How to use VUE router with native HTML elements as components? - vue.js

I am in this situation:
old project, npm/vue compiler not available
using vue in vanilla JS flavour
using router, that needs components
don't want to use smelly injected strings, that are really bad for code readability
How can I use pieces of html as components for vanilla JS vue?

I found a totally undocumented solution for making vanilla JS components with native HTML:
1 Pure HTML (no smelly injected strings):
<div id="components" style="display:none"><!--hide component code-->
<div id="component-category-index"><!--component 1 source code-->
CATEGORY INDEX COMPONENT
</div>
<div id="component-article-show"><!--component 2 source code-->
ARTICLE SHOW COMPONENT
</div>
</div>
2 vanilla JS Vue initialization:
const vue=new Vue({
el: '#vue-app',
....your vue blabla.....
router:new VueRouter({
routes :[
{ path: '/article', component: { template: document.getElementById('component-article-show').outerHTML } },
{ path: '/categories', component: { template: document.getElementById('component-category-index').outerHTML } }
]
})
})
pros:
use vanilla JS vue with full components functionalities
keep vue "style-component-logic" separated but on the same file, without
hard breaking that clean and simple pattern
cons:
all HTML of the page must be on the same file, so this solution is OK for smaller SPA or single pages with VUE.

Related

How to make Vue 3 application without CLI / Webpack / Node

I am trying to make Vue 3 application but without CLI and Webpack.
There is no official documentation yet. On CDN are many versions (vue.cjs.js, vue.cjs.prod.js, vue.esm-browser.js, vue.esm-bundler.js, vue.global.js, vue.runtime.global.js...).
Which one to pick? And how to mount application, old way does not work. There are many online examples how works new Composition API but none how to start project without CLI / Webpack.
Link to Vue 3 CDN:
<script src="https://unpkg.com/vue#3/dist/vue.global.js"></script>
In body:
<div id="app">
</div>
<script type="module">
import app from './app.js'
const {createApp} = Vue;
createApp(app).mount('#app');
</script>
In app.js is simple component:
export default {
name: 'Test',
setup() {
const title = "Hello";
return {
title
};
},
template: `
<div>
<h1>{{title}}</h1>
</div>
`,
};
Instead of one component, app.js can be a container for other components.
I made simple Vue 3 QuickStart template so anyone can see how this works.
Template is in SPA-like style and contains 4 sample pages, 4 components, routing and store. It uses only Vue.js from CDN, everything else is hand made ;)
Note: This is not library, it's just demo code so anyone can see how to mount Vue 3 application and use Composition API in simple scenario.
Online demo: http://vue3quickstart.rf.gd/
GitHub: https://github.com/SaleCar/Vue3-QuickStart
Found in docs: https://vuejs.org/guide/quick-start.html#without-build-tools
Without Build Tools
To get started with Vue without a build step, simply copy the following code into an HTML file and open it in your browser:
<script src="https://unpkg.com/vue#3"></script>
<div id="app">{{ message }}</div>
<script>
Vue.createApp({
data() {
return {
message: 'Hello Vue!'
}
}
}).mount('#app')
</script>
The above example uses the global build of Vue where all APIs are exposed under the global Vue variable.
While the global build works, we will be primarily using ES modules syntax throughout the rest of the documentation for consistency. In order to use Vue over native ES modules, use the following HTML instead:
<script type="importmap">
{
"imports": {
"vue": "https://unpkg.com/vue#3/dist/vue.esm-browser.js"
}
}
</script>
<div id="app">{{ message }}</div>
<script type="module">
import { createApp } from 'vue'
createApp({
data() {
return {
message: 'Hello Vue!'
}
}
}).mount('#app')
</script>
Notice how we can import directly from 'vue' in our code - this is made possible by the <script type="importmap"> block, leveraging a native browser feature called Import Maps. Import maps are currently only available in Chromium-based browsers, so we recommend using Chrome or Edge during the learning process. If your preferred browser does not support import maps yet, you can polyfill it with es-module-shims.
You can add entries for other dependencies to the import map - just make sure they point to the ES modules version of the library you intend to use.
Not for production
The import-maps-based setup is meant for learning only - if you intend to use Vue without build tools in production, make sure to check out the Production Deployment Guide.
In addition, as Evan You recommended, Vite(https://madewithvuejs.com/vite) is a good alternative to #vue/cli and webpack. It's still CLI like but more lightweight I think. Fast and supports SFC.

What is the purpose of 'main.js' and 'App.vue' in a Vue.js application?

I don't understand the exact purpose of each file.
Suppose I want to add authentication code. Where should I place it, in main.js or App.vue?
I believe you might be missing on some of the basics behind the structure of Vue.js and where and/or how to put in functionality like authentication. It might be worth going through their introduction again to solidify your knowledge.
To answer more directly, when you run a Vue.js application you need to have a basic HTML page (like index.html) as an entry point and the initialisation for your Vue.js application loaded in a <script> in that page.
When you write a Vue.js application you can choose to do it in pure JavaScript, in TypeScript or in the .vue component format which combines the HTML, CSS and JavaScript you need to define components.
The vue format is not run directly. It has to be transpiled into plain JavaScript by the Vue-CLI/builder and packed using a packager like Webpack first and then loaded by your entry point. Luckily, the Vue.js CLI handles nearly all of this process so you can get on with building.
File App.vue
This is typically the root of your application defined in Vue.js Component file format. It's usually something that defines the template for your page:
<template>
<div id="app">
<SideBar />
<router-view v-if="loaded" />
</div>
</template>
<script>
import SideBar from "./pages/SideBar";
export default {
components: { SideBar },
computed: {
loaded() {
return this.$store.state.loadState == "loaded";
}
}
};
</script>
File main.js
This is usually the JavaScript file that will initialise this root component into a element on your page. It is also responsible for setting up plugins and third-party components you may want to use in your app:
import Vue from "vue";
import { store } from "./store/store";
import router from "./router";
import App from "./App.vue";
new Vue({
router,
store,
render: h => h(App)
}).$mount("#app");
File index.html
The index page provides your entry point in HTML providing an element for Vue.js to load into and imports file main.js to initialise your application.
<!-- The HTML element that hosts the App.vue component -->
<div id="app"></div>
<!-- Built files will be auto injected -->
<script type="text/javascript" src="main.js"></script>
On another note, a decent place to put your authentication logic is in the router where you can add navigation guards to restrict access to pages based on the current authentication state and send your users to a login page:
// GOOD
router.beforeEach((to, from, next) => {
if (!isAuthenticated) next('/login')
else next()
})
I don't think you specifically need an index.html in your project. Provided your main.js has an import that references you main Vue page, e.g.:
import App from "./App.vue";
and then renders it.
new Vue({
...
render: (h) => h(App),
...
The .vue file is a special Vue.js CLI project feature allowing you to write Vue.js apps or Vue.js components in more convenient way. You write your app/component in the .vue file and Vue.js CLI transforms it into code that works in a browser.
The main.js in Vue.js CLI project is starting the instance of the app. The index.html file in Vue.js CLI project is handled automatically (it's located in the 'public' folder).
The right place to start with Vue.js CLI is: Instant Prototyping

Can i import single file component using Vue and Vue Router CDN?

I'm currently using Vue and Vue Router CDN. I want to import a single file component (user.html) to my index.html with Vue router. But when I click on "Go to user" the data didn't display. I read a few guides about Vue router but they used NPM or CIL instead of Vue CDN.
Index.html
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- use router-link component for navigation. -->
<!-- specify the link by passing the `to` prop. -->
<!-- `<router-link>` will be rendered as an `<a>` tag by default -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
<router-link to='/User.html'>Go to User</router-link>
</p>
<!-- route outlet -->
<!-- component matched by the route will render here -->
<router-view></router-view>
</div>
<script>
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
const User = { template: '#test'}
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar },
{
path:'/User.html', component: User
}
]
const router = new VueRouter({
routes
})
const app = new Vue({
router
}).$mount('#app')
</script>
User.html
<template id = "test">
fsjdfjdfldskjflkd
</template>
I think there's been a misunderstanding. As far as I can tell you're trying to load User.html in the way you would load a .vue file, but that doesn't work unless you're using Webpack to build your project since the .vue filetype is something that the vue-loader project is responsible for parsing.
Vue Router CDN means you're not building with Webpack, so loading a component like you're doing is impossible. You can configure Vue Router to serve a User.html file, but that file can't be a template component since in CDN mode Vue Router has no idea what files are on your server, nor can it simply retreive them.
So you need to do one of the following three options:
Option 1: Start using Webpack for your project
This is what I recommend you doing. You'll find that your project will perform better and will be easier to develop using it.
Option 2: Use the proper template syntax for CDN
This is for example:
var mytemplate = `<div>
<h1>This is my template</h1>
</div>`
Vue.component('mycomp1', {
template: mytemplate
});
Vue.component('mycomp2', {
template: `
<div>
Hello, {{ name }}!
</div>
`,
props: ['name'],
});
You can't load other files as templates when you're not building using Webpack. Webpack puts them into your single page application for you, Vue Router has no idea what's inside User.html nor can Vue use it as a template. Vue Router can be told to redirect to a fully functional User.html website page, but not to just use it as a template.
Option 3: Use Ajax requests to fetch the template file
This is something I STRONGLY URGE YOU NOT TO DO, but for the sake of completeness, you can use the CDN version if you fetch the contents of User.html file using an Ajax request and create a component from that.
I really, really recommend that you stop using the CDN version and instead go for a Webpack based solution, give in to the dark side! Or make it even simpler and use Nuxt.js instead since it's easier for beginners to use.

Is the way I'm writing my Vue components correct?

Here's the way I am currently writing my Vue components. E.g.
<template>
<NavBar></NavBar>
<div class="Footer">
<div class="left">
<p>I'm a cool footer on the Left!</p>
</div>
<div class="middle">
</div>
<div class="right">
<p>I'm a cool footer on the Right!</p>
</div>
</div>
</template>
<script>
import NavBar from './NavBar.vue';
export default {
name: 'Footer',
components: {
NavBar
}
data () {
return {
}
},
methods: {
}
}
My question is should I be writing my components like this instead? And if so, what is the difference?
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
new Vue({
el: '#example'
})
The official guide explains the difference here: https://v2.vuejs.org/v2/guide/single-file-components.html
The short explanation is that the <template>... syntax is used for single-file components (e.g. my-component.vue) whereas the vue.component... syntax is used alongside the new Vue({... declaration.
Full quote from the guide:
In many Vue projects, global components will be defined using Vue.component, followed by new Vue({ el: '#container' }) to target a container element in the body of every page.
This can work very well for small to medium-sized projects, where JavaScript is only used to enhance certain views. In more complex projects however, or when your frontend is entirely driven by JavaScript, these disadvantages become apparent:
Global definitions force unique names for every component
String templates lack syntax highlighting and require ugly slashes
for multiline HTML
No CSS support means that while HTML and JavaScript are modularized
into components, CSS is conspicuously left out
No build step restricts us to HTML and ES5 JavaScript, rather than
preprocessors like Pug (formerly Jade) and Babel
All of these are solved by single-file components with a .vue extension, made possible with build tools such as Webpack or Browserify.

How to Access Vue-Loader Components in an HTML File

I would like to use the modular style and file format of Vue Loader (i.e., where I have a template section, script section and style section in each .vue file).
What I can't figure out how to do (or if it is even possible to do) is use my custom templates in an html file.
For instance, in the App.vue file I can use the following code:
<template>
<div id="app">
<message>Hello there</message>
</div>
</template>
This will work to display a custom message component on the home page.
What I would like to do instead is use my custom components in html files. For instance, in the index.html file to use the following code:
<div id="app">
<message>Hello there</message>
</div>
Any idea how I can do this? Thanks.
NOTE: I am new to Vue Loader and semi-new to Vue (so I apologize in advance if the answer to this question is obvious).
There are many ways you can compile a single file component and then use that component in a web page.
Use vue-cli
Vue released a command line interface tool called vue-cli that can initialize projects and build components with zero configuration. One option to build a component that you can use in your page is to use vue build.
vue build MyComponent.vue --prod --lib MyComponent
This will compile a script that exposes MyComponent. If you include that script in your page and then add it globally,
Vue.component(MyComponent)
That component will be available to you in any of your Vues.
Make a plugin
Here is a sample of a very basic framework for making a plugin.
myPluginDefinition.js
window.MyPlugin= {};
MyPlugin.install = function (Vue) {
Vue.component('my-component', require('./my-component.vue'));
}
webpack.config.js
module.exports = {
entry: "./myPluginDefinition.js",
output: {
path: __dirname+'/dist',
filename: "MyPlugin.js"
},
module: {
loaders: [
{
test: /\.vue$/,
loader: 'vue-loader',
}
]
}
};
This will build a file called MyPlugin.js that will contain each of the single file components that you include in the install function. Include the script on your page and then call
Vue.use(MyPlugin)
and you will have all of your components.
Use a custom webpack configuration
There are many ways you could configure webpack to build your single file components. You could build them all into a single file or build them separately. I suggest if you want to use one of these options you ask a separate question.
Actually you can do this easily by:
register your component :
Vue.component('message', {
template: '<div>A custom component!</div>'
});
then comment the render function in your Vue instance like so:
new Vue({
el: '#app',
// render: h => h(App)
})
after that you will be able to render your message Tag like this:
<div id="app">
<message></message>
</div>
Edit :
if you don't want to use this way you can define it in your view instance:
new Vue({
el: '#app',
// render: h => h(App)
components: {
message: {
template: `
<h1>Hello World</h1>
`
}
}
})
Import desired component definition object and pass it to options.components
<template>
<some-component></some-component>
</template>
<style>...</style>
<script>
import SomeComponent from 'path/to/some-component.vue';
export default {
components: {
// ES2015 shorthand for SomeComponent: SomeComponent
SomeComponent
}
}
</script>
That leverages local component registration
Both the default export and SomeComponent are component definition objects.