Vue 全家桶,深刻Vue 的世界

內容簡介:

  1. Vue 實例上的屬性
  2. Vue 生命週期
  3. Vue 數據綁定
  4. computed 計算屬性
  5. watch 監聽器
  6. Vue 組件
  7. Vue 組件 extend
  8. Vue 組件高級屬性
  9. Vue 的render
  10. Vue-router
  11. Vux

Vue 實例上的屬性

組件樹html

  • $parent :用來訪問組件實例的父實例
  • $root : 用來訪問當前組件樹的根實例
  • $children :用來訪問當前組件實例的直接子組件實例
  • $refs :用來訪問ref指令的子組件

DOM訪問前端

  • $el :用來掛載當前組件實例的dom元素
  • $els :用來訪問$el元素中使用了v-el指令的DOM元素

數據訪問vue

  • $data :用來訪問組件實例觀察的數據對象
  • $options :用來訪問組件實例化時的初始化選項對象

DOM方法的使用git

  • $appendTo(elementOrSelector, callback) :將el所指的DOM元素插入目標元素
  • $before(elementOrSelector, callback) :將el所指的DOM元素或片斷插入目標元素以前
  • $after(elementOrSelector, callback) :將el所指的DOM元素或片斷插入目標元素以後
  • $remove(callback) :將el所指的DOM元素或片斷從DOM中刪除
  • $nextTick(callback) :用來在下一次DOM更新循環後執行指定的回調函數
// vue 的 渲染過程是異步的<template> <div id="app"> <p>{{text}}</p> </div></template><script>export default { data() { return { text: 0 }; } mounted(){ setInterval(()=> { this.text +=1; this.text +=1; this.text +=1; this.text +=1; this.text +=1; },1000) }}</script>

能夠看到text值的變化是0 5 10 15 … 而並無出現 0 1 2 3 … 這樣連續的變化github

Vue 全家桶,深刻Vue 的世界

 

event方法的使用vue-router

1.監聽vuex

  • $on(event, callback) :監聽實例的自定義事件
  • $once(event, callback) :同上,但只能觸發一次
  • $watch(property,callback(new, old)) : 監聽屬性的變化,拿到變化先後的值
// 第一種寫法watch: { text(new, old) { console.log(`${new}:${old}`); }}// 第二種寫法const unWatch = this.$watch('text',(new,old)=> console.log(`${new}:${old}`);})// 2秒後銷燬 unWatchsetTimeout(()=> { unWatch();},2000)// 兩種寫法的結果同樣,只是第二種須要在組件銷燬手動銷燬$watch

2.觸發數組

  • $dispatch(event,args) :派發事件,先在當前實例觸發,再沿父鏈一層層向上,對應的監聽函數返回false中止
  • $broadcast(event,args) :廣播事件,遍歷當前實例的$children,若是對應的監聽函數返回false,就中止
  • $emit(event, args) :觸發事件

3.刪除瀏覽器

  • $off(event, callback) :刪除時間監聽

4.其餘緩存

  • $forceUpdate() :強制組件刷新
  • $set(ele,attr,value) :給對象設置屬性
  • $delete(ele,attr,value) :刪除對象屬性
<template> <div id="app"> <p>{{obj.a}}</p> </div></template><script>export default { data() { return { obj:{} }; } mounted(){ let i = 0; setInterval(()=> { i++; // 第一種 this.obj.a = i ; // obj.a沒有定義,vue是沒法監聽到這個屬性的變化,因此頁面的值也不會變化,這時能夠用$forceUpdate進行強制渲染,固然不推薦這種用法 this.$forceUpdate(); // 第二種 this.$set(this.obj,'a',i); },1000) }}</script>

Vue 生命週期

vue 官方生命週期

Vue 全家桶,深刻Vue 的世界

 

render (h) { throw new TypeError('render error') // console.log('render function invoked') // render 在beforeMount 和 mounted之間執行 // return h('div', {}, this.text) // 虛擬DOM},renderError (h, err) { return h('div', {}, err.stack)},errorCaptured () {// 會向上冒泡,而且正式環境可使用}

若是要修改data裏面的值,最先只能放到create生命週期中

Vue 數據綁定

<template> <div id="app"> <p>{{isActive?'active':'notActive'}}</p> <p>{{arr.join(' ')}}</p> <p>{{Date.now()}}</p> <p v-html="html"></p> <div :class="{ active: isActive }" :style="[styles, styles2]" ></div> <div :class="[isActive? 'active':'']"></div> <ul> <li v-for="(item,index) in arr" :key="index">{{item}}</li> </ul> // 單個checkbox <input type="checkbox" v-model="a"> {{a}} <br/> // 多個checkbox 愛好:<input type="checkbox" v-model="b" value="游泳"> 游泳 <input type="checkbox" v-model="b" value="游泳"> 登山 <input type="checkbox" v-model="b" value="游泳"> 睡覺 性別:<input type="radio" v-model="c" value="男"> 男 <input type="radio" v-model="c" value="女"> 女 // 只綁定一次 <p v-once="a"></p> </div></template><script>export default { data() { return { isActive: false, arr: [1, 2, 3], html: '<span>123</span>', styles: { color: 'red', appearance: 'none' }, styles2: { color: 'black' }, a: false, b:[], // 能夠拿到checkbox 的 value c:'' // 性別 }; }}</script>

v-model 的修飾符

來自官網的例子:

1. .number

若是想自動將用戶的輸入值轉爲數值類型,能夠給 v-model 添加 number 修飾符:

<input v-model.number="age" type="number">

這一般頗有用,由於即便在 type=」number」 時,HTML 輸入元素的值也總會返回字符串。

2. .trim

若是要自動過濾用戶輸入的首尾空白字符,能夠給 v-model 添加 trim 修飾符:

<input v-model.trim="msg">

3. .lazy

在默認狀況下, v-model 在每次 input 事件觸發後將輸入框的值與數據進行同步 。你能夠添加 lazy 修飾符,從而轉變爲使用 change 事件進行同步(當輸入框失去焦點):

<!-- 在「change」時而非「input」時更新 --><input v-model.lazy="msg" >

數組和對象的注意事項

數組

因爲 JavaScript 的限制,Vue 不能檢測如下變更的數組:

  • 當你利用索引直接設置一個項時,例如:vm.items[indexOfItem] = newValue
  • 當你修改數組的長度時,例如:vm.items.length = newLength
var vm = new Vue({ data: { items: ['a', 'b', 'c'] }})vm.items[1] = 'x' // 不是響應性的vm.items.length = 2 // 不是響應性的

爲了解決第一類問題,如下兩種方式均可以實現和 vm.items[indexOfItem] = newValue 相同的效果,同時也將觸發狀態更新:

// Vue.setVue.set(vm.items, indexOfItem, newValue)// Array.prototype.splicevm.items.splice(indexOfItem, 1, newValue)

你也可使用 vm.$set 實例方法,該方法是全局方法 Vue.set 的一個別名:

vm.$set(vm.items, indexOfItem, newValue)

爲了解決第二類問題,你可使用 splice:

vm.items.splice(newLength)

對象

Vue 不能檢測對象屬性的添加或刪除:

var vm = new Vue({ data: { a: 1 }})// `vm.a` 如今是響應式的vm.b = 2// `vm.b` 不是響應式的

對於已經建立的實例,Vue 不能動態添加根級別的響應式屬性。可是,可使用 Vue.set(object, key, value) 方法向嵌套對象添加響應式屬性。例如,對於:

var vm = new Vue({ data: { userProfile: { name: 'Anika' } }})

你能夠添加一個新的 age 屬性到嵌套的 userProfile 對象:

Vue.set(vm.userProfile, 'age', 27)

你還可使用 vm.$set 實例方法,它只是全局 Vue.set 的別名:

vm.$set(vm.userProfile, 'age', 27)

有時你可能須要爲已有對象賦予多個新屬性,好比使用 Object.assign() 或 _.extend() 。在這種狀況下,你應該用兩個對象的屬性建立一個新的對象。因此,若是你想添加新的響應式屬性,不要像這樣:

Object.assign(vm.userProfile, { age: 27, favoriteColor: 'Vue Green'})

你應該這樣作:

vm.userProfile = Object.assign({}, vm.userProfile, { age: 27, favoriteColor: 'Vue Green'})

computed 計算屬性

計算屬性的使用

<template> <div id="app"> <p>{{name}}</p> </div></template><script>export default { data() { return { firstName: 'Fin', lastName: 'Get', }; }, computed: { name() { return `${this.firstName}${this.lastName}` } }}</script>

雙向綁定的計算屬性與Vuex

// vuex state是沒法直接修改的,官方給出了 v-model 的解決方案<input v-model="message">computed: { message: { get () { return this.$store.state.obj.message }, set (value) { this.$store.commit('updateMessage', value) } }}

若是在方法或者生命週期中使用了計算屬性,則必須設置一個set

watch 監聽器

watch 簡單使用

<div id="demo">{{ fullName }}</div>var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar', fullName: 'Foo Bar' }, watch: { // watch 方法最初綁定的時候,它是不會執行的,只有變化了纔會執行 firstName: function (val) { this.fullName = val + ' ' + this.lastName }, lastName: function (val) { this.fullName = this.firstName + ' ' + val } }})watch: { // 聲明一個handler,這樣在初始化時就會執行一次 handler firstName: { handler(val) { this.fullName = val + ' ' + this.lastName }, immediate: true }}

監聽對象屬性的變化

<div id="demo">{{ obj.a }}</div><input v-model="obj.a" />var vm = new Vue({ el: '#demo', data: { obj: { a: '123' } }, watch: { obj: { handler() { console.log('obj.a changed'); }, immediate: true, deep: true // 若是不加這一句,在輸入框中輸入值,並不會打印 obj.a changed } }})// 這樣寫就能監聽到屬性值的變化watch: { 'obj.a': { handler() { console.log('obj.a changed'); } }}

Vue 組件

Vue 組件中的data爲何必須是函數

官網解釋

在Vue組件中data必須是函數,可是在 new Vue() 中data能夠是一個對象

Vue.component('MyComponent', { template: '<div>this is a component</div>', data() { return {} // 返回一個惟一的對象,不要和其餘組件共用一個對象進行返回 },})

上面定義了一個 MyComponent 組件,在這裏咱們能夠把這個組件當作一個構造函數。在其餘頁面引入,並註冊組件時,其實是對這個構造函數的一個引用。當在模板中正真使用組件時相似於實例化了一個組件對象。

// 模擬一下let MyComponent = function() { // 定義一個構造函數}MyComponent.prototype.data = { name: 'component', age: 0}// 實例化組件對象let componentA = new MyComponent();let componentB = new MyComponent();componentA.data.name === componentB.data.name; // truecomponentA.data.age = 4;componentB.data.name;

能夠看出,兩個實例組件對象的data是如出一轍的,一個改變也會致使另外一個改變,這在實際開發中是不符合組件式思想的。

// 模擬一下let MyComponent = function() { // 定義一個構造函數}// 這樣就好了 寫成函數,函數有本身的做用域,不會相互影響MyComponent.prototype.data = function() { return { name: 'component', age: 0 }}

用 Vue.use() 定義全局組件

// 定義一個 button 組件// button.vue<template> <div class="button"> 按鈕 </div></template><script></script>// button.jsimport ButtonComponent from './button.vue';const Button={ install:function (Vue) { Vue.component('Button',ButtonComponent) }}export default Button;// main.jsimport Button from './component/button.js';Vue.use(Button);

完成上面的步驟就能夠在全局使用button組件了,其實最重要的 Vue.component('Button',ButtonComponent) , Vue.use(Button) 會執行install方法,也能夠直接在 main.js 使用 Vue.component() 註冊全局組件。

props

<template> <div class="button"> 按鈕 </div></template><script>export default { props: ['msg'], // 沒有任何限制 // 輸入限制 props: { // 基礎的類型檢查 (`null` 匹配任何類型) propA: Number, // 多個可能的類型 propB: [String, Number], // 必填的字符串 propC: { type: String, required: true }, // 帶有默認值的數字 propD: { type: Number, default: 100 }, // 帶有默認值的對象 propE: { type: Object, // 對象或數組且必定會從一個工廠函數返回默認值 default: function () { return { message: 'hello' } } }, // 自定義驗證函數 propF: { validator: function (value) { // 這個值必須匹配下列字符串中的一個 return ['success', 'warning', 'danger'].indexOf(value) !== -1 } } }}</script>

子組件是不能直接修改props的。

Vue組件之間的通訊問題能夠看這裏…

Vue 組件 extend

使用 Vue.extend 就是構造了一個Vue構造函數的「子類」。它的參數是一個 包含組件選項的對象 ,其中 data 選項必須是函數。

import Vue from 'vue'// 一個包含組件選項的對象const compoent = { props: { active: Boolean, propOne: String }, template: ` <div> <input type="text" v-model="text"> <span v-show="active">see me if active</span> </div> `, data () { return { text: 0 } }, mounted () { // 這個mounted先打印 console.log('comp mounted'); }}// 建立一個「子類」const CompVue = Vue.extend(compoent);// 實例化一個「子類」new CompVue({ el: '#root', propsData: { // 這裏若是用props,組件內是拿不到值的 propOne: 'xxx' }, data: { text: '123' }, mounted () { console.log('instance mounted'); }})const component2 = { extends: component, // 繼承於 component data(){ return { text: 1 } }, mounted () { this.$parent.text = '111111111'; // 能夠改變父組件的值 console.log('comp2 mounted') }}new Vue({ name: 'Root', el: '#root', mounted () { console.log(this.$parent.$options.name) }, components: { Comp: componet2 }, data: { text: 23333 }, template: ` <div> <span>{{text}}</span> <comp></comp> </div> `})

Vue 組件高級屬性

Vue 組件插槽

一般咱們會向一個組件中傳入一些自定義的內容,這個時候就能夠用到插槽。插槽內能夠包含任何模板代碼,包括HTML或者是一個組件。

// 定義一個帶插槽的組件const component = { name: 'comp', template: ` <div> <slot></slot> </div> `}new CompVue({ el: '#root', components:{ Comp }, template: ` <div> <comp> <p>這裏的內容顯示在插槽內</p> </comp> </div> `}

具名插槽

官網連接: https://cn.vuejs.org/v2/guide/components-slots.html

<div class="container"> <header> <!-- 咱們但願把頁頭放這裏 --> <slot name="header"></slot> </header> <main> <!-- 咱們但願把主要內容放這裏 --> <slot name="main"></slot> </main> <footer> <!-- 咱們但願把頁腳放這裏 --> <slot name="footer"></slot> </footer></div>

具名插槽的使用:

第一種:在一個父組件的 <template> 元素上使用 slot 特性

<base-layout> <template slot="header"> <h1>Here might be a page title</h1> </template> <template slot="main"> <p>A paragraph for the main content.</p> <p>And another one.</p> </template> <template slot="footer"> <p>Here's some contact info</p> </template></base-layout>

第二種:直接在普通元素上使用

<base-layout> <h1 slot="header">Here might be a page title</h1> <div slot="main"> <p>A paragraph for the main content.</p> <p>And another one.</p> </div> <p slot="footer">Here's some contact info</p></base-layout>

插槽的默認內容

在插槽中能夠設置一個默認內容,若是用戶沒有設置新的內容,則會顯示默認內容

<button> <slot>提交</slot></button>

做用域插槽

2.1.0+ 新增 在 2.5.0+, slot-scope 再也不限制在 <template> 元素上使用,而能夠用在插槽內的任何元素或組件上。

const component = { name: 'comp', template: ` <div> <slot value="456" name="finget"></slot> </div> `}new CompVue({ el: '#root', components:{ Comp }, template: ` <div> <comp> <p slot-scope="props">{{props.value}} {{props.name}}</p> // 456 finget </comp> </div> `}

provide/inject 跨級組件交互

2.2.0 新增

這對選項須要一塊兒使用,以容許一個祖先組件向其全部子孫後代注入一個依賴,不論組件層次有多深,並在起上下游關係成立的時間裏始終生效。

// 父級組件提供 'foo'var Provider = { provide: { foo: 'bar' }, // ...}// 子組件注入 'foo'var Child = { inject: ['foo'], created () { console.log(this.foo) // => "bar" } // ...}

若是是注入一個父級組件內部的值,provide須要做爲一個函數,相似於data

const component = { name: 'comp', inject: ["value"] template: ` <div>子組件 {{value}}</div> `}new CompVue({ el: '#root', data() { return { value: '123' } } components:{ Comp }, provide() { // 這裏若是隻是一個對象的話是沒法拿到this.value的 return { value: this.value } }, template: ` <div> <comp></comp> <input type="text" v-model="value"> </div> `}

若是要監聽父級組件的屬性值的變化,從而自動更新子組件的值,須要手動實現監聽

const component = { name: 'comp', inject: ["data"] template: ` <div>子組件 {{data.value}}</div> `}...provide() { const data = {} // 這是vue雙向綁定的基礎 Object.defineProperty(data,"value",{ get: () => this.value, enumerable: true }) return { data }},...

Vue 的render

Vue模板的解析: https://finget.github.io/2018/05/31/mvvm-vue/

Vue-router

router構建選項

重定向:

{ path: '/', redirect: '/app'}

History 模式:

const router = new VueRouter({ mode: 'history', routes: [...]})

vue-router 默認 hash 模式 —— 使用 URL 的 hash 來模擬一個完整的 URL,因而當 URL 改變時,頁面不會從新加載。

不過這種模式要玩好,還須要後臺配置支持。由於咱們的應用是個單頁客戶端應用,若是後臺沒有正確的配置,當用戶在瀏覽器直接訪問 http://oursite.com/user/id 就會返回 404,這就很差看了。

給個警告頁:

const router = new VueRouter({ mode: 'history', routes: [ { path: '*', component: NotFoundComponent } ]})

base

const router = new VueRouter({ mode: 'history', base: '/base/', routes: [ { path: '/hello', component: hello } ]})

當訪問 localhost:8080/hello 會變成 localhost:8080/base/hello ,全部的路由路徑都會加上 /base ,固然手動刪除 /base 仍是能夠打開頁面

linkActiveClass 和 linkExactActiveClass

<router-link to="/app">app</router-link><router-link to="/login">login</router-link>

router-link 在頁面中會渲染成 a 標籤,點擊以後會添加兩個類名: router-link-exact-active 和 router-link-active

const router = new VueRouter({ linkActiveClass: 'active-link', linkExactActiveClass: 'exact-active-link'})

這至關因而從新命名了兩個類名。

二者的不一樣點:

<router-link to="/login">login</router-link><router-link to="/login/exact">login exact</router-link>

上面這兩個路由有一部分 /login 是相同的,在點擊了 login exact 路由調轉到 /login/exact 後:

/login 上還保留了 router-link-active 類名

Vue 全家桶,深刻Vue 的世界

 

scrollBehavior

使用前端路由,當切換到新路由時,想要頁面滾到頂部,或者是保持原先的滾動位置,就像從新加載頁面那樣。

注意: 這個功能只在支持 history.pushState 的瀏覽器中可用。

const router = new VueRouter({ scrollBehavior(to, form, savedPosition){ if (savedPosition) { return savedPosition } else { return { x: 0, y: 0 } } }, routes: [...]})

scrollBehavior 方法接收 to 和 from 路由對象。第三個參數 savedPosition 當且僅當 popstate 導航 (經過瀏覽器的 前進/後退 按鈕觸發) 時纔可用。

parseQuery 和 stringifyQuery

提供自定義查詢字符串的解析/反解析函數。覆蓋默認行爲。

const router = new VueRouter({ parseQuery (query) { console.log(query) }, stringifyQuery (obj) { console.log(obj) }})

fallback

當瀏覽器不支持 history.pushState 控制路由是否應該回退到 hash 模式。默認值爲 true。

在 IE9 中,設置爲 false 會使得每一個 router-link 導航都觸發整頁刷新。它可用於工做在 IE9 下的服務端渲染應用,由於一個 hash 模式的 URL 並不支持服務端渲染。

const router = new VueRouter({ fallback: true})

路由元信息

官網例子:

const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, children: [ { path: 'bar', component: Bar, // a meta field meta: { requiresAuth: true } } ] } ]})

那麼如何訪問這個 meta 字段呢?

首先,咱們稱呼 routes 配置中的每一個路由對象爲 路由記錄。路由記錄能夠是嵌套的,所以,當一個路由匹配成功後,他可能匹配多個路由記錄

例如,根據上面的路由配置, /foo/bar 這個 URL 將會匹配父路由記錄以及子路由記錄。

一個路由匹配到的全部路由記錄會暴露爲 $route 對象 (還有在導航守衛中的路由對象) 的 $route.matched 數組。所以,咱們須要遍歷 $route.matched 來檢查路由記錄中的 meta 字段。

下面例子展現在全局導航守衛中檢查元字段:

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() // 確保必定要調用 next() }})

命名視圖

在一個路由下展現多個視圖組件,用的並很少

// 在這個頁面中要分別展現三個視圖<router-view></router-view> // 默認的<router-view name="a"></router-view> // 視圖a<router-view name="b"></router-view> // 視圖bconst router = new VueRouter({ routes: [ { path: '/', components: { // 加s default: Foo, // 對應默認router-view a: Bar, // name = "a" b: Baz // name = "b" } } ]})

導航守衛

路由改變時,按順序觸發的鉤子函數

全局守衛

const router = new VueRouter({ ... })router.beforeEach((to, from, next) => { console.log('before each invoked'); next();})router.beforeResolve((to, from, next) => { console.log('before resolve invoked'); next();})

每一個守衛方法接收三個參數:

  • to: Route : 即將要進入的目標 路由對象
  • from: Route : 當前導航正要離開的 路由對象
  • next: Function : 必定要調用該方法來 resolve 這個鉤子。執行效果依賴 next 方法的調用參數。
  • next() : 進行管道中的下一個鉤子。若是所有鉤子執行完了,則導航的狀態就是 confirmed (確認的)。
  • next(false) : 中斷當前的導航。若是瀏覽器的 URL 改變了 (多是用戶手動或者瀏覽器後退按鈕),那麼 URL 地址會重置到 from 路由對應的地址。
  • next('/') 或者 next({ path: '/' }) : 跳轉到一個不一樣的地址。當前的導航被中斷,而後進行一個新的導航。你能夠向 next 傳遞任意位置對象,且容許設置諸如 replace: true、name: 'home' 之類的選項以及任何用在 router-link 的 to prop 或 router.push 中的選項。
  • next(error) : (2.4.0+) 若是傳入 next 的參數是一個 Error 實例,則導航會被終止且該錯誤會被傳遞給 router.onError() 註冊過的回調。

確保要調用 next 方法,不然鉤子就不會被 resolved 。

路由對象

一個路由對象 (route object) 表示當前激活的路由的狀態信息,包含了當前 URL 解析獲得的信息,還有 URL 匹配到的路由記錄 (route records)。

路由對象是不可變 (immutable) 的,每次成功的導航後都會產生一個新的對象。

路由對象屬性:

  • $route.path
  • 類型: string
  • 字符串,對應當前路由的路徑,老是解析爲絕對路徑,如 「/foo/bar」。
  • $route.params
  • 類型: Object
  • 一個 key/value對象,包含了動態片斷和全匹配片斷,若是沒有路由參數,就是一個空對象。
  • $route.query
  • 類型: Object
  • 一個 key/value 對象,表示 URL 查詢參數。例如,對於路徑 /foo?user=1,則有 $route.query.user == 1,若是沒有查詢參數,則是個空對象。
  • $route.hash
  • 類型: string
  • 當前路由的 hash 值 (帶 #) ,若是沒有 hash 值,則爲空字符串。
  • $route.fullPath
  • 類型: string
  • 完成解析後的 URL,包含查詢參數和 hash 的完整路徑。
  • $route.matched
  • 類型: Array 一個數組,包含當前路由的全部嵌套路徑片斷的路由記錄 。路由記錄就是 routes 配置數組中的對象副本 (還有在 children 數組)。
const router = new VueRouter({ routes: [ // 下面的對象就是路由記錄 { path: '/foo', component: Foo, children: [ // 這也是個路由記錄 { path: 'bar', component: Bar } ] } ]})

當 URL 爲 /foo/bar, $route.matched 將會是一個包含從上到下的全部對象 (副本)。

  • $route.name
  • 當前路由的名稱,若是有的話。(查看命名路由)
  • $route.redirectedFrom
  • 若是存在重定向,即爲重定向來源的路由的名字

全局後置鉤子

router.afterEach((to, from) => { console.log('after each invoked');})

路由獨享的守衛

const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, beforeEnter: (to, from, next) => { // ... } } ]})

組件內的守衛

const Foo = { template: `...`, beforeRouteEnter (to, from, next) { // 在渲染該組件的對應路由被 confirm 前調用 // 不!能!獲取組件實例 `this` // 由於當守衛執行前,組件實例還沒被建立 }, beforeRouteUpdate (to, from, next) { // 在當前路由改變,可是該組件被複用時調用 // 舉例來講,對於一個帶有動態參數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候, // 因爲會渲染一樣的 Foo 組件,所以組件實例會被複用。而這個鉤子就會在這個狀況下被調用。 // 能夠訪問組件實例 `this` }, beforeRouteLeave (to, from, next) { // 導航離開該組件的對應路由時調用 // 能夠訪問組件實例 `this` }}

beforeRouteEnter 守衛 不能 訪問 this,由於守衛在導航確認前被調用,所以即將登場的新組件還沒被建立。

不過,你能夠經過傳一個回調給 next來訪問組件實例。在導航被確認的時候執行回調,而且把組件實例做爲回調方法的參數。

beforeRouteEnter (to, from, next) { next(vm => { // 經過 `vm` 訪問組件實例 })}

完整的導航解析流程

  1. 導航被觸發。
  2. 在失活的組件裏調用離開守衛。
  3. 調用全局的 beforeEach 守衛。
  4. 在重用的組件裏調用 beforeRouteUpdate 守衛 (2.2+)。
  5. 在路由配置裏調用 beforeEnter 。
  6. 解析異步路由組件。
  7. 在被激活的組件裏調用 beforeRouteEnter 。
  8. 調用全局的 beforeResolve 守衛 (2.5+)。
  9. 導航被確認。
  10. 調用全局的 afterEach 鉤子。
  11. 觸發 DOM 更新。
  12. 用建立好的實例調用 beforeRouteEnter 守衛中傳給 next 的回調函數。

異步路由

在路由文件中,直接import全部組件勢必形成頁面首次渲染時間變長,異步路由,當進入對應的路由才加載對應的頁面。

const router = new VueRouter({ routes: [ { path: '/foo', component: () => import('../view/...'), } ]})

這種寫法須要安裝 syntax-dynamic-import ,並在 .babelrc 進行配置

// .babelrc{ "plugins": ["syntax-dynamic-import"]}

Vux

如下內容來自 官網:https://vuex.vuejs.org/zh/

簡單使用vuex

// store.jsimport Vuex from 'vuex'import Vue from 'vue'Vue.use(Vuex)const store = new Vuex.Store({ state: { count: 0 }, mutations: { updateCount(state, num) { state.count = num } }})export default store// main.jsimport Vue from 'vue'import App from './App'import store from './store/store.js'Vue.config.productionTip = false/* eslint-disable no-new */new Vue({ el: '#app', store, // 掛載 components: { App }, template: '<App/>'})// 任意組件mounted(){ console.log(this.$store) let i = 1 setInterval(() => { this.$store.commit('updateCount', i++) })},computed: { count() { return this.$store.state.count }}
Vue 全家桶,深刻Vue 的世界

 

核心概念

State

Vuex 使用單一狀態樹——是的,用一個對象就包含了所有的應用層級狀態。至此它便做爲一個「惟一數據源 (SSOT)」而存在。這也意味着,每一個應用將僅僅包含一個 store 實例。單一狀態樹讓咱們可以直接地定位任一特定的狀態片斷,在調試的過程當中也能輕易地取得整個當前應用狀態的快照。

大白話: state就至關因而個全局對象,經過 Vue.use(Vuex) 全局註冊了vuex以後,在任意組件中能夠用 this.$store.state 拿到該對象

Vuex的狀態存儲是響應式的,從store實例中讀取狀態最簡單的方法就是在計算屬性中返回某個狀態。

computed: { count() { return this.$store.state.count }}

當 state 中的 count 變化時,自動會更新 computed ,從而改變相關 DOM

mapState 輔助函數

當一個組件須要獲取多個狀態時候,將這些狀態都聲明爲計算屬性會有些重複和冗餘。爲了解決這個問題,咱們可使用 mapState 輔助函數幫助咱們生成計算屬性,讓你少按幾回鍵:

// 在單獨構建的版本中輔助函數爲 Vuex.mapStateimport { mapState } from 'vuex'export default { // ... computed: mapState({ // 箭頭函數可以使代碼更簡練 count: state => state.count, // 傳字符串參數 'count' 等同於 `state => state.count` countAlias: 'count', // 爲了可以使用 `this` 獲取局部狀態,必須使用常規函數 不能用箭頭函數 countPlusLocalState (state) { return state.count + this.localCount } })}

當映射的計算屬性的名稱與 state 的子節點名稱相同時,咱們也能夠給 mapState 傳一個字符串數組。

computed: mapState([ // 映射 this.count 爲 store.state.count 'count'])// 經常使用操做computed: { ...mapState(['count'])}// 換一個變量名computed: { ...mapState({ count1 : 'count', count2 : state => state.count })}

Getter

Getter就是vuex種state的computed,經過state派生出新的state,並且它會被緩存起來,只有依賴的state發生變化纔會從新計算

export default { fullName(state) { // 默認接收state做爲第一個參數 return `${state.firstName}${state.lastName}` }}

mapGetters 輔助函數

getter的使用和state相似,能夠把它當作state來用。

import { mapGetters } from 'vuex'export default { // ... computed: { // 使用對象展開運算符將 getter 混入 computed 對象中 ...mapGetters([ 'doneTodosCount', 'anotherGetter', // ... ]) }}

若是想給getter換個名字,方法和state同樣,不重複

Mutation

Mutation必須是同步的

更改 Vuex 的 store 中的狀態的惟一方法是提交 mutation。Vuex 中的 mutation 很是相似於事件:每一個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler)。這個回調函數就是咱們實際進行狀態更改的地方,而且它會接受 state 做爲第一個參數:

const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment (state) { // 變動狀態 state.count++ } }})

你不能直接調用一個 mutation handler。這個選項更像是事件註冊:「當觸發一個類型爲 increment 的 mutation 時,調用此函數。」要喚醒一個 mutation handler,你須要以相應的 type 調用 store.commit 方法:

store.commit('increment')

提交載荷(傳參)

你能夠向 store.commit 傳入額外的參數,即 mutation 的 載荷(payload):

// ...mutations: { increment (state, n) { state.count += n }}store.commit('increment', 10)

在大多數狀況下,載荷應該是一個 對象 ,這樣能夠包含多個字段而且記錄的 mutation 會更易讀:

// ...mutations: { increment (state, payload) { state.count += payload.amount }}store.commit('increment', { amount: 10})

對象風格的提交方式

提交 mutation 的另外一種方式是直接使用包含 type 屬性的對象:

store.commit({ type: 'increment', amount: 10})

當使用對象風格的提交方式,整個對象都做爲載荷傳給 mutation 函數,所以 handler 保持不變:

mutations: { increment (state, payload) { state.count += payload.amount }}

使用常量替代 Mutation 事件類型

使用常量替代 mutation 事件類型在各類 Flux 實現中是很常見的模式。這樣可使 linter之類的工具發揮做用,同時把這些常量放在單獨的文件中可讓你的代碼合做者對整個 app 包含的 mutation 一目瞭然:

// mutation-types.jsexport const SOME_MUTATION = 'SOME_MUTATION'// store.jsimport Vuex from 'vuex'import { SOME_MUTATION } from './mutation-types'const store = new Vuex.Store({ state: { ... }, mutations: { // 咱們可使用 ES2015 風格的計算屬性命名功能來使用一個常量做爲函數名 [SOME_MUTATION] (state) { // mutate state } }})

在組件中提交 Mutation

你能夠在組件中使用 this.$store.commit('xxx') 提交 mutation ,或者使用 mapMutations 輔助函數將組件中的 methods 映射爲 store.commit 調用(須要在根節點注入 store)。

import { mapMutations } from 'vuex'export default { // ... methods: { ...mapMutations([ 'increment', // 將 `this.increment()` 映射爲 `this.$store.commit('increment')` // `mapMutations` 也支持載荷: 'incrementBy' // 將 `this.incrementBy(amount)` 映射爲 `this.$store.commit('incrementBy', amount)` ]), ...mapMutations({ add: 'increment' // 將 `this.add()` 映射爲 `this.$store.commit('increment')` }) }}

Action

Action 能夠包含異步操做

Action跟Mutation相似,Action是調用 commit 方法,提交 mutation 的。

const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } }})

Action 函數接受一個與 store 實例具備相同方法和屬性的 context 對象,所以你能夠調用 context.commit 提交一個 mutation ,或者經過 context.state 和 context.getters 來獲取 state 和 getters 。

實踐中,咱們會常常用到 ES2015 的 參數解構 來簡化代碼(特別是咱們須要調用 commit 不少次的時候):

actions: {// {commit} = context 解構出來 increment ({ commit }) { commit('increment') }}

實際代碼:

Vue 全家桶,深刻Vue 的世界

 

在組件中分發 Action

你在組件中使用 this.$store.dispatch('xxx') 分發 action ,或者使用 mapActions 輔助函數將組件的 methods 映射爲 store.dispatch 調用(須要先在根節點注入 store):

import { mapActions } from 'vuex'export default { // ... methods: { ...mapActions([ 'increment', // 將 `this.increment()` 映射爲 `this.$store.dispatch('increment')` // `mapActions` 也支持載荷: 'incrementBy' // 將 `this.incrementBy(amount)` 映射爲 `this.$store.dispatch('incrementBy', amount)` ]), ...mapActions({ add: 'increment' // 將 `this.add()` 映射爲 `this.$store.dispatch('increment')` }) }}

嚴格模式

開啓嚴格模式,僅需在建立 store 的時候傳入 strict: true:

const store = new Vuex.Store({ // ... strict: true})

在嚴格模式下,不管什麼時候發生了狀態變動且不是由 mutation 函數引發的,將會拋出錯誤。這能保證全部的狀態變動都能被調試工具跟蹤到。

開發環境與發佈環境

不要在發佈環境下啓用嚴格模式!嚴格模式會深度監測狀態樹來檢測不合規的狀態變動——請確保在發佈環境下關閉嚴格模式,以免性能損失。

相似於插件,咱們可讓構建工具來處理這種狀況:

const store = new Vuex.Store({ // ... strict: process.env.NODE_ENV !== 'production'})

原文連接:https://finget.github.io/2018/06/28/vue-family/?utm_source=tuicool&utm_medium=referral 若有侵權請聯繫刪除,謝謝

相關文章
相關標籤/搜索