Love at first Vue

37
Love at first Vue * Vue (pronounced /vjuː/, like view) *

Transcript of Love at first Vue

Love at first Vue* Vue (pronounced /vjuː/, like view)

*

So What’s Vue.js?

SO WHAT’S VUE.JS

It's a Open-source progressive framework for building user interfaces.

Its focused on the view layer only.

It has a Virtual DOM and composable, reactive components.

Its core is small and works well with companion libraries for routing, global state, and HTTP requests.

It's light, fast, and flexible.

First release Feb. 2014

2.0 release (stable) in Oct. 2016

~45.9k stars on Github

~ 410,497 downloads in the last month (npm only)

Small learning curve and semantic

Pre-processor agnostic (Jade/Pug, Scss, Stylus)

Server Side Rendering

Stable, maintaned and tested

Single File Vue Components

● Imported as ES6 module.

● Collocation of Template, Logic and Style.

● Use what you already know:

HTML, CSS and JavaScript.

● Component-scoped CSS

(no conflicts)

<template lang="pug"> .my-component h1 {{ msg }} other-component</template>

<script> import OtherComponent from './OtherComponent.vue'

export default { components: { OtherComponent }, data () { return { msg: 'Hello Vue.js' } } }</script>

<style lang="stylus" scoped> font-stack = Helvetica, sans-serif primary-color = #333

.my-component color primary-color font-family font-stack</style>

Ecosystem

Getting Started

GETTING STARTED

The easiest way to try out Vue.js is using the Codepen example. Feel free to fork it and follow along as we go through some basic examples. Or, you can simply create an .html file and include Vue with:

<script src="//unpkg.com/vue"></script>

Getting started

Vue-cliA simple CLI for scaffolding Vue.js projects.

$ npm i -g vue-cli # Install vue-cli if you haven't already

$ vue init daliborgogic/vue-simple-boilerplate # Create a new project based on this template

$ cd vue-simple-boilerplate # Navigate into your new project folder

$ npm i -g live-server # Install live-server if you haven't already

$ live-server # Run live-server and open it in your browser

Fork It And Make Your Own

You can fork this repo to create your own boilerplate, and use it with vue-cli:

$ vue init username/repo my-project

Getting started

Declarative Rendering

HTML

<div id="app">

{{ message }}

</div>

JS

new Vue({

el: '#app',

data: {

message: 'Welcome!'

}

})

Getting started

Binding

HTML

<div id="app">

<span v-bind:title="message">

Hover your mouse over me for a few seconds to see my dynamically bound title!

</span>

</div>

JS

new Vue({

el: '#app',

data: {

message: 'You loaded this page on ' + new Date()

}

})

The v-bind attribute is called a directive. Directives are prefixed with v-.They apply special reactive behavior to the rendered DOM.

Getting started

Conditionals

HTML

<div id="app">

<p v-if="seen">Now you see me</p>

</div>

JS

new Vue({

el: '#app',

data: {

seen: true

}

})

We can bind data to not only text and attributes, but also the structure of the DOM.

Getting started

Loops

HTML

<div id="app">

<ol>

<li v-for="todo in todos">

{{ todo.text }}

</li>

</ol>

</div>

JS

new Vue({

el: '#app',

data: {

todos: [

{ text: 'Learn JavaScript' },

{ text: 'Learn Vue' },

{ text: 'Build something awesome' }

]

}

})

Getting started

Handling User InputTo let users interact with your app, we can use the v-on directive to attach event listeners that invoke methods on our Vue instances:

Note in the method we simply update the state of our app without touching the DOM - all DOM manipulations are handled by Vue.

HTML

<div id="app">

<p>{{ message }}</p>

<button v-on:click="reverseMessage">Reverse Message</button>

</div>

JS

new Vue({

el: '#app',

data: {

message: 'Hello Vue.js!'

},

methods: {

reverseMessage: function () {

this.message = this.message.split('').reverse().join('')

}

}

})

Getting started

Two-way binding

HTML

<div id="app">

<p>{{ message }}</p>

<input v-model="message">

</div>

JS

new Vue({

el: '#app',

data: {

message: 'Hello Vue.js!'

}

})

Getting started

Componentsallows us to build large-scale applications composed of small, Self-contained and often reusable components. A component is essentially a Vue instance with pre-defined options. Registering a component in Vue is straightforward:

JS

Vue.component('todo-item', {

template: '<li>This is a todo</li>'

})

Now you can compose it in another component’s template:

HTML

<ol>

<todo-item></todo-item>

</ol>

Getting started

But this would render the same text for every todo. We should be able to pass data from the parent scope into child components. Let’s modify the component definition to make it accept a prop:

JS

Vue.component('todo-item', {

props: ['todo'],

template: '<li>{{ todo.text }}</li>'

})

Getting started

Now we can pass the todo into each repeated component using v-bind:

HTML

<div id="app">

<ol>

<todo-item v-for="item in groceryList" v-bind:todo="item"></todo-item>

</ol>

</div>

JS

Vue.component('todo-item', {

props: ['todo'],

template: '<li>{{ todo.text }}</li>'

})

new Vue({

el: '#app',

data: {

groceryList: [

{ text: 'Vegetables' },

{ text: 'Cheese' },

{ text: 'Whatever else humans are supposed to eat' }

]

}

})

Getting started

TODO list example

HTML<div id="todo">

<input type="text" placeholder="Add new task" @keyup.enter="addItem"/>

<ul>

<li v-for="item in items">

<span :class="{'done': item.done}">{{item.text}}</span>

<button @click="toggleItem(item)">{{item.done ? 'Not done': 'Done'}}</button>

<button @click="deleteItem(item)">Delete</button>

</li>

</ul>

</div>

JSnew Vue ({ el: '#todo', data: { items: [] }, methods: { addItem (e) { this.items.push({ text: e.target.value, done: false }) e.target.value = '' }, toggleItem (item) { item.done = !item.done }, deleteItem (item) { this.items.splice(this.items.indexOf(item), 1) } }})

CSS.done { text-decoration: line-thorough;}

RoutingSingle Page Application (SPA)

1. Entire app loaded on a single page2. No page reload on UI navigation3. Dynamically update UI with ajax-fetched data

Dynamic page-level component// routes templates

const NotFound = {template: '<p>404</p>'}

const Home = {template: '<p>Home page</p>'}

const About = {template: '<p>About page</p>'}

// routes mapping

let routes = {

'/': Home,

'/about': About

}

// vue instance

new Vue({

el: '#app',

data: {

currentRoute: window.location.pathname

},

computed: {

ViewComponent () {

return routes[this.currentRoute] || Notound

}

},

render (h) {

return h(this.ViewComponent)

}

})

Vue Router

1. Dynamic route matching (patterns)2. HTML5 history API (states)3. Lazy-loading (async components)4. Nested routes5. Navigation guards (authentication, preloading...)

Vue CLIThe simplest possible Vue setup in a single HTML file

$ npm i -g vue-cli # Install vue-cli if you haven't already

$ vue init simple # Create a new project based on this template

$ cd simple # Navigate into your new project folder

$ npm i -g live-server # Install live-server if you haven't already

$ live-server # Run live-server and open it in your browser

Current available templates include:

webpack - A full-featured Webpack + vue-loader setup with hot reload, linting, testing & css extraction.webpack-simple - A simple Webpack + vue-loader setup for quick prototyping.browserify - A full-featured Browserify + vueify setup with hot-reload, linting & unit testing.browserify-simple - A simple Browserify + vueify setup for quick prototyping.simple - Vue setup in a single HTML file

VUE CLI

Vue router and preloading example

$ npm i -g vue-cli

$ vue init webpack-simple app

$ cd app

$ npm i

$ npm run dev

app

/node_modules

/src

/assets

Logo.png

App.vue

Main.js

.babelrc

index.html

package.json

README.md

webpack.config.js

Webpack simple

$ npm i vue-router -S

App.vue

<template lang="pug">

#app

//- caches the inactive component instances withouth destroying them

keep-alive

//- render current route template

router-view

</template>

<script>

export default {

name: 'app'

}

</script>

main.js

import Vue from 'vue'

import VueRouter from 'vue-router'

import App from './App.vue'

Vue.use(VueRouter)

const Home = resolve => require(['./views/Home.vue'], resolve)

const About = resolve => require(['./views/About.vue'], resolve)

const NotFound = resolve => require(['./views/NotFound.vue'], resolve)

const router = new VueRouter({

mode: 'history',

routes: [

{path: '/', component: Home},

{path: '/about/:name?', component: About},

{path: '*', component: NotFound}

]

})

const app = new Vue({

el: '#app',

router,

render: h => h(App)

})

Router mode

● Hash - uses the URL hash for routing (hashbang urls /#!/)● History - uses HTML5 History API (semantic urls)● Abstract - servre-side with Node.js (all javascript environments)

Server config - nginx

location / {

try_files $uri $uri/ index.html

}

Lazy Loading RoutesAsync components to only load them when the route is visited:

const Foo = resolve => require([‘./Foo.vue’], resolve)const Foo = () => System.import(‘./Foo.vue’)

Built-in ComponentCaches inactive components withouth destroy them:

<keep-alive></keep-alive>

$ npm i vuex -S

$ npm i vuex-router-sync@next -S

store.js

import Vue from 'vue'

import Vuex from 'vuex'

Vue.use(Vuex)

const state = {

isPreloading: false

}

const mutations = {

beginPreload (state) {

state.isPreloading = true

},

endPreload (state) {

state.isPreloading = false

}

}

const actions = {

beginPreload: ({commit}) => commit('beginPreload'),

endPreload: ({commit}) => commit('endPreload')

}

const getters = {

isPreloading: state => state.isPreloading

}

export default new Vuex.Store({

state,

getters,

actions,

mutations

})

preloader.vue

<template lang="pug">

.preloader(:class="{'hidden': !this.$store.getters.isPreloading}") Loading...

</template>

<style scoped>

.preloader {

...

opacity: 1;

visibility: visible;

}

.preloader.hidden {

opacity: 0;

visibility: hidden;

}

</style>

App.vue

<template lang="pug">

#app

navigation

preloader

keep-alive

router-view

</template>

<script>

import Navigation from './partials/Nav.vue'

import Preloader from './partials/Preloader.vue'

export default {

name: 'app',

components: {Navigation, Preloader}

}

</script>

main.js

...

import store from './store'

import {sync} from 'vuex-router-sync'

const router = new VueRouter({

...

{

path: '/about/:name?',

component: About,

meta: {

preload: true

}

}

...

})

router.beforeEach((to, from, next) => {

if (to.matched.some(m => m.meta.preload)) {

store.dispatch('beginPreload')

next()

} else {

store.dispatch('endPreload')

next()

}

})

sync(store, router)

const app = new Vue({

el: '#app',

router,

store,

render: h => h(App)

})

Navigation Guards

Guard navigations either by redirecting it or canceling it:globally / per-route / in-component

router.beforeEach((to, from, next) => {})

router.afterEach((to, from, next) => {})

● to: Route : the target Route Object being navigated to.● from: Route : the current route being navigated away from.● next: Function : this function must be called to resolve the hook.

Route Meta Fields

Attach abstract information to each route:

● Authentication● Preloading● Authorized Referrer

meta: { requiresAuth: true }

router.beforeEach((to, from, next) => { if (to.matched.some(record => record.meta.requiresAuth)) { // this route requires auth, check if logged in // if not, redirect to login page. if (!auth.loggedIn()) { next({ path: '/login', query: { redirect: to.fullPath } }) } else { next() } } else { next() // make sure to always call next() }})

About.vue

<template lang="pug">

div

h1 About {{name}}

</template>

<script>

export default {

name: 'about',

computed: {

name () {

return this.$route.params.name || 'anonymous'

}

},

activated () {

setTimeout(() => {

this.$nextTick(() => {

this.$store.dispatch('endPreload')

})

}, 3000)

}

}

</script>

Github:

https://github.com/daliborgogic/vue-routing-preloading

Thanks!