Vue 超詳細手記

Reaper622發表於2018-11-01

Vue

Vue例項屬性

名稱 內容
$data 傳入的data內容,修改可引起頁面內容變化
$props 父元件傳入子元件
$el 繫結的html的節點
$options 新建Vue例項物件時穿進去的內容,修改optins的內容不會引起頁面變化,有render方法,在頁面重新渲染時會呼叫
$root 整個Vue物件的根節點,app.$root === app
$children 元件內部的元素
$slots 插槽
$scopedSlots 區域化插槽
$refs 模板的引用
$isServer 服務端渲染時可以用來判斷
$watch 監聽內容,與寫在例項中效果相同,但通過$watch寫的監聽要返回的unWatch方法結束監聽
$on 監聽事件是否被觸發,可以獲取emit傳遞的引數
$emit 觸發事件,監聽哪個物件就要通過哪個事件觸發,可以傳值
$once 與on相似,但只被觸發一次
$forceUpdate 強制元件重新渲染
$set 給新值賦值並且頁面會渲染
$delete 刪除值
$nextTick 非同步操作

Vue 生命週期

生命週期 介紹 可呼叫次數
beforeCreate 還未繫結Dom,不能進行dom操作**(不要放入ajax請求)**
created 還未繫結Dom,不能進行dom操作
beforeMount el只為html中寫的節點,還未繫結,說明還未渲染(未呼叫render方法)
mounted el繫結為渲染後的節點,說明之前進行了渲染(已呼叫render方法),例項已經建立完成
beforeUpdate 更新的物件時模板,需要虛擬DOM的重洗渲染和補丁修改,但是如果更改的資料在模板中沒有使用,就不會進行更新
updated 資料更改導致虛擬DOM重新渲染和打補丁,注意要避免在這個鉤子函式中運算元據
activated
deactivated
beforeDestory 例項銷燬之前呼叫。在這一步,例項仍然完全可用,this仍能獲取到例項。一般在這一步中進行:銷燬定時器、解綁全域性事件、銷燬外掛物件等操作
destoryed Vue 例項銷燬後呼叫。呼叫後,Vue 例項指示的所有東西都會解繫結,所有的事件監聽器會被移除,所有的子例項也會被銷燬

生命週期圖示

Computed 的特性

  • computed內建快取,在頁面重新渲染時如果computed的值沒有發生變化便不會再重新渲染,提高效能
  • computed可以做設定操作,但不建議用computed做設定操作,有一定概率導致重複計算

Wacth 的特性

  • watch可以用來監聽資料的變化
  • 第一次渲染watch並不會渲染出內容,如果要初次渲染加上handler方法
  • handler 只監聽監聽物件引用的變化,屬性的變化並不會呼叫handler方法
  • immediate屬性可以用來確認是否以當前的初始值執行handler的函式
  • 將deep屬性設定為true就可以監聽物件屬性的變化
  • 建議不要再watch內修改監聽的值和屬性

Vue指令

指令名 指令作用
v-text 標籤裡面要顯示的文字內容,內容較多時不建議使用
v-html 將html程式碼片段展示為html標籤
v-show 根據內容的Boolean值來顯示或不顯示內容,原理是設定display:none;
v-if 與v-show不同的是如果判斷為false將不會放在文件流裡,如果頻繁切換會動態增刪節點,會造成效能問題 搭配指令有 v-else v-else-if
v-for 迴圈,用法為 v-for="item in arr",item為陣列內的值或物件,arr為儲存值的陣列,如果要拿到順序則寫為v-for="(item, index) in arr" 遍歷物件可用 v-for="(val, key,index) in obj"的方式,val為值,key為對應的鍵,index為對應的索引值即順序,使用v-for時需要:key=""來繫結一個唯一識別
v-on 監聽事件,可以監聽到$emit觸發的事件
v-model 雙向繫結資料,可進行實時更改,一般用於input,一般輸入之後會變為字串
如果想讓值為數字,在v-model後加.number修飾符
如果想去掉首位的空格,在v-model後加.trim修飾符
如果不想實時更改,而是失焦後更改,在v-model後加.lazy修飾符
v-pre 不解析內部內容,即內部內容保留源格式
v-cloak 一般直接在頁面引入vue.js程式碼時使用,在vue程式碼完成之前讓內容隱藏,vue載入完成後再展示
v-once 資料繫結內容只執行一次,一般為靜態內容使用,節省效能開銷,不會做虛擬DOM對比

元件

定義元件

首先寫一個元件內容

const component = {
    templete: '<div>This is compoent</div>'
}
複製程式碼

然後我們用Vue例項的方法引入元件

import Vue from 'vue'
Vue.component('ComponentName',component); //定義了一個名為name的元件
//元件名用大寫開頭並用駝峰命名 (類的思想)
複製程式碼

使用時:

第一種情況-直接在tempelate內使用:

new Vue({
    el:'#root',
    tempelate:'<component-name></component-name>' //使用時使用小寫,並且單詞之間用-連線
})
複製程式碼

第二種情況-重新命名使用:

new Vue({
    el:'#root',
    tempelate:'<new-name-component></new-name-component>',
    //使用時使用小寫,並且單詞之間用-連線
    components: {
        newNameComponent: component
    }
})
複製程式碼

在元件內部繫結資料-data

const component = {
    templete: '<div>{{text}}</div>',
    data(){  //必須要使用函式返回值的方式
        return{
            text:'hello'
        }
    }
}
複製程式碼

使用一個return新物件的方法可以防止多個元件動態繫結至一個相同的值,例如v-model的場景就會出現相關的問題

元件內部傳遞資料-props

const component = {
    templete: `
		<div>
			<span v-show="active">see me if active</span>
			<span>{{propOne}}</span>  //prop與data相似都是繫結在this上,可以直接使用
		</div>
	`
    data(){  //必須要使用函式返回值的方式
        return{
            text:'hello'
        }
    },
    props: {
        active: Boolean,//向元件傳遞一個布林值,key為active
        propOne: String  //駝峰式命名
        
    }
}
複製程式碼

使用時

new Vue({
    el:'#root',
    tempelate:`
			<new-component :active="true" prop-one="123"></new-component>  //需要用v-bind解析傳入的值
			<new-component :active="false" prop-one="456"></new-component> //在prop中為駝峰式的命名在使用時使用小寫並且用-連線的方式(非強制,只是規範)
		`,
    //使用時使用小寫,並且單詞之間用-連線
    components: {
        NewComponent: component
    }
})
複製程式碼

props資料的一些設定

const component = {
    props: {
        active: {
            type: Boolean, //設定為布林值
            required: true, //這個屬性為必需屬性
            default: true, //設定預設值為true,如果有了default就不需要required
            validator (value) {   //可用來檢測輸入的值是否符合要求,可用來自定義檢測
            	return typeof value === 'Boolean' 
        	}
        },
        propOne: {
            type: Object,
            default() {   //如果預設值為一個物件,則應該用函式返回一個物件的形式
                return {
                    
                }
            }
        }
    },
    .....
}
複製程式碼

元件的extend

extend例項內的值

當我們新建一個Vue例項時,它內部都是一些預設的屬性,如果我們想使用我們已經定義過的元件的一些屬性內容我們可以用下列程式碼:

... 已建立元件component

const ComponentVue = Vue.extend(component);  //前提是component沒有required為true的props

new ComponentVue({
    el: '#root',
})
複製程式碼

但如果我們需要用到props呢?

我們可以用下面的方式:

... 已建立元件component

const ComponentVue = Vue.extend(component); 

new ComponentVue({
    el: '#root',
    propsData: {   //用propsdata來向原元件的props傳值才能使用
    	propOne: 'xxx',
	}
})
複製程式碼

extend的mounted

會先執行原元件的mounted,再執行extend例項的mountend

元件內如何實現v-model雙向繫結

const component = {
    props: ['value'],
    template: `
		<div>
			<input type="text" @input="handleInput" v-model="value" >
		</div>
	`,
    methods: {
        handleInput(e) {
            this.$emit('input', e.target.value); //向父元件觸發input事件
        }
    }
}

//在Vue例項中

new Vue({
    components: {
        CompOne: component
    },
    el: '#root',
    data () {
        return {
            value: '123'
        }
    }
    template: `
		<div>
			<comp-one :value="value" @input="value = arguments[0]"></comp-one>   //arguments陣列即為通過$emit後面傳遞的引數
		</div>
	`
})
複製程式碼

我們還可以通過下面的功能實現

const component = {
    model: {   
        prop: 'value1', //為要傳的值
        event: 'change' //為觸發的事件
    },
    props: ['value1'],
    template: `
		<div>
			<input type="text" @input="handleInput" v-model="value1">
		</div>
	`,
    methods: {
        handleInput(e) {
            this.$emit('input', e.target.value); //向父元件觸發input事件
        }
    }
}

//在Vue例項中

new Vue({
    components: {
        CompOne: component
    },
    el: '#root',
    data () {
        return {
            value: '123'
        }
    }
    template: `
		<div>
			<comp-one v-model="value"></comp-one>   //arguments陣列即為通過$emit後面傳遞的引數
		</div>
	`
})
複製程式碼

元件高階屬性

插槽 slot

當我們在Vue例項呼叫的元件內部直接寫入html元素時,它是不會顯示的,因為我們沒有告訴元件要放在哪裡顯示,這時就需要 插槽

//元件內
const component = {
    template: `
		<div>
			<slot></slot> //為內容新增插槽位置,沒有宣告插槽位置的元素都會放在這個裡面
		</div>
	`
}

//例項中
new Vue({
    components: {
        CompOne: component
    },
    el: '#root',
    template: `
		<div>
			<comp-one>
				<span>我會被放在slot中</soan>
			</comp-one>  
		</div>
	`
})
複製程式碼

具名插槽

//元件內
const component = {
    template: `
		<div>
			<div class="header">
				<slot name="header"></slot> 	
			</div>	
			<div class="body">
				<slot name="body"></slot>
			</div>
		</div>
	`
}

//例項中
new Vue({
    components: {
        CompOne: component
    },
    el: '#root',
    template: `
		<div>
			<comp-one>
				<span slot="header">我會被放在slot name為header的插槽中</soan>
				<span slot="body">我會被放在slot name為body的插槽中</soan>
			</comp-one>  
		</div>
	`
})
複製程式碼

作用域插槽 讓插槽內繫結的資料為元件的資料

使用方法為:

//元件內
template: `
		<div>
			<slot value="456" anothervalue="content"></slot>
		</div>
	`

//例項中
template:`
	<div>
			<comp-one><span slot-scope="props">{{props.value}}  {{props.anothervalue}}</span></comp-one>   //slot-scope 的物件即為元件內slot傳遞值的物件
	</div>
`
複製程式碼

或者可以使用元件內部data的值,它並不影響本地data的使用:

//元件內
template: `
		<div>
			<slot :value="value" anothervalue="content"></slot>  //注意要使用v-bind形式
		</div>
	`,
data() {
    return{
        value="this is data value"
    }    
}

//例項中
template:`
	<div>
			<comp-one><span slot-scope="props">{{props.value}}  {{props.anothervalue}}</span></comp-one>   //slot-scope 的物件即為元件內slot傳遞值的物件
	</div>
`
複製程式碼

跨級資料交流

如何越級拿到vue元件例項呢?

使用 provide屬性!

//子元件
const ChildComponent = {
    template: '<div>child component</div>',
    inject: ['grandfather']  //獲取例項向外提供的物件
}

//元件
const component = {
    name: 'comp',
    component: {
        ChildComponent //宣告子元件
    },
    template: `
		<div>
			<child-component />  //如果為空元件可以這樣書寫
		</div>
	`,
    
}

//例項
new Vue({
    components: {
        CompOne: component
    },
    provide (){
        return{
          grandfather: this  //將自己這個例項作為物件向外提供,僅為節點下的節點(子節點 & 子節點的子節點 $ ......)
        } //使用函式返回值的方式的原因與data相同
    },
    ...
})
複製程式碼

但一般provide只提供一次值,不會實現model模式,若要實現需要給provide的值提供獲得方法,方法如下:

provide (){
        return{
          const data = {}  //先設定一個空物件
    
    Object.defineProperty(data,'value',{
        get: () => this.value, //沒次使用值時其實是用了這個get方法
        enumerable: true //可讀屬性
    })
        } 
    }
複製程式碼

這種方法雖然是實現了跨級model,但屬於一種hack方法,官方不建議使用。

Render方法

render方法是我們渲染的一個方法,它內部會返回一個createElement方法,這個方法是Vue給我們提供的建立節點的方法,使用render方法我們就可以不使用template

//元件
const component = {
    name: 'comp',
    props: ['props1'],
    render (createElement) {
        return createElement('div', {
            //data資料 data: this.data形式
        },[   //注意要傳遞陣列
            this.$slots.default  //無名為default,具名slot將default改為名字即可
            this.props1 //傳遞prop值
        ]) 
    }
}

//例項
new Vue({
    components: {
        CompOne: component
    },
    render( createElement ) {  //不一定為createElement,可以用其他字母表示,但下面的方法名要保持一致
        return createElement(
            'comp-one',{
                ref: 'comp'     
            },[            //注意節點內在建立節點要傳遞一個陣列
            createElement(
                'span',{
                    ref: 'span',
                    slot: 'header'  //可以指定要渲染的slot插槽位置
                },this.value)
        ])  //Vue提供的建立節點的函式
    }
})
複製程式碼

Render方法下的事件監聽,on 和 nativeOn

on形式的監聽,需要例項和組建中都使用on

//元件
const component = {
    name: 'comp',
    props: ['props1'],
    render (createElement) {
        return createElement('div', {
            on: {
                click: () => {this.$emit('click')} //監聽click事件,向父級觸發click事件
            }
        },[   //注意要傳遞陣列
            this.$slots.default  //無名為default,具名slot將default改為名字即可
            this.props1 //傳遞prop值
        ]) 
    }
}

//例項
new Vue({
    components: {
        CompOne: component
    },
    render( createElement ) {  //不一定為createElement,可以用其他字母表示,但下面的方法名要保持一致
        return createElement(
            'comp-one',{
                ref: 'comp',
                on: {
                    click: this.handleClick //點選元件會受到元件傳遞的click事件,並觸發handleClick方法 
                }
            },[            //注意節點內在建立節點要傳遞一個陣列
            createElement(
                'span',{
                    ref: 'span'
                },this.value)
        ])  //Vue提供的建立節點的函式
    },
    methods: {
        handleClick() {
            //do something....
        }
})
複製程式碼

nativeOn 有個很大的便捷,那就是我們只需要在例項中宣告而元件中可以不用宣告

//元件
const component = {
    name: 'comp',
    props: ['props1'],
    render (createElement) {
        return createElement('div', {
        },[   //注意要傳遞陣列
            this.$slots.default  //無名為default,具名slot將default改為名字即可
            this.props1 //傳遞prop值
        ]) 
    }
}

//例項
new Vue({
    components: {
        CompOne: component
    },
    render( createElement ) {  //不一定為createElement,可以用其他字母表示,但下面的方法名要保持一致
        return createElement(
            'comp-one',{
                ref: 'comp',
                nativeOn: {
                    click: this.handleClick //nativeOn會自動繫結到元件根節點的dom上面
                }
            },[            //注意節點內在建立節點要傳遞一個陣列
            createElement(
                'span',{
                    ref: 'span'
                },this.value)
        ])  //Vue提供的建立節點的函式
    },
    methods: {
        handleClick() {
            //do something....
        }
})
複製程式碼

template其實是render方法的一種編譯方式

V-router

router,顧名思義即為路由,vue框架可以讓我們在前端實現路由功能,這個功能對WebApp這種應用來說是必不可少的。

使用路由之前需要先安裝模組,npm install vue-router

一般我們將路由分為兩個檔案,router.js 和 routes.js,一個用來存放路由資訊,另一個用來寫路由邏輯功能

//routes.js
import Todo from '../views/todo/todo.vue'  //引入要展示的頁面
import Login from '../views/login/login.vue'

export default [
    {
        path: '/', //網頁的路徑
        component: Todo //指定輸入上方路徑後顯示什麼頁面
    },
    {
        path: '/login',
        compoent: Login
    }
]
複製程式碼
//router.js
import Router from 'vue-router'  //引入vue-router模組
import routes from './routes' //引入我們寫的路由資訊


//每次引入路由檔案時會返回一個路由物件
export default () => {
    return new Router({
        routes
    })
}
複製程式碼

為了實現跳轉,我們還需要用router-view來實現對路由跳轉過後的展示

// app.vue
<template>
  <div id="app">
    <div id="cover"></div>
    <Header></Header>
    <router-view></router-view>  //這部分來進行跳轉,頁面內容展示在這部分
    <!-- <todo></todo> -->
    <Footer></Footer>
  </div>
</template>
複製程式碼

我們也可以用路由來實現首頁重定向,在routes.js寫入這部分內容

//routes.js
import Todo from '../views/todo/todo.vue'  //引入要展示的頁面
import Login from '../views/login/login.vue'

export default [
    {
      path: '/',  
      redirect: '/app' //當輸入預設路徑時重定向到app頁面
    },
    {
        path: '/app', //網頁的路徑
        component: Todo //指定輸入上方路徑後顯示什麼頁面
    },
    {
        path: '/login',
        compoent: Login
    }
]
複製程式碼

此時我們會注意到網頁上的url是 http://localhost:8080/#/app,他預設會傳一個hash,我們可以更改這個東西需要更改router.js的內容

import Router from 'vue-router'  //引入vue-router模組
import routes from './routes' //引入我們寫的路由資訊


//每次引入路由檔案時會返回一個路由物件
export default () => {
    return new Router({
        routes,
        mode:'history'  //mode改為 'hash'即為hash方式
    })
}
複製程式碼

此時在此執行我就可以發現那個#消失了

我們還可以通過base屬性設計基路徑

import Router from 'vue-router'  //引入vue-router模組
import routes from './routes' //引入我們寫的路由資訊


//每次引入路由檔案時會返回一個路由物件
export default () => {
    return new Router({
        routes,
        mode:'history',
        base: '/base/'  //設定基路徑為base,通過vue-router的跳轉都會加入基路徑
    })
}
複製程式碼

此時我們再訪問localhost:8080發現他的路徑跳轉為了

localhost:8080/base/app

但基路徑並不是強制的,我們把base去掉訪問依然可以訪問到app

還有兩個屬性使我們要配合router-link使用的

//app.vue
<template>
  <div id="app">
    <div id="cover"></div>
    <Header></Header>
    <router-link to="/app">app</router-link>
    <router-link to="/login">login</router-link>
    <router-view></router-view>
    <!-- <todo></todo> -->
    <Footer></Footer>
  </div>
</template>
複製程式碼

我們在app.vue中加入兩個router-link(與a標籤類似,原理是通過a標籤實現的),to的內容即為他們要跳轉的路由,然後我們可以全域性設定連結的樣式

export default () => {
    return new Router({
        routes,
        mode:'history',
        linkActuveCLass:'active-link', //只要有一部分被啟用會加上
        linkExactActiveClass: 'exact-active-link' //當前路徑完全匹配才加上
    
    })
}
複製程式碼

可能會有點難理解,那我們這樣想,你當前有一個/app的頁面,展示的是app.vue,然後app下面有個子頁面是childApp.vue,路徑是/app/childApp,當我們在localhost:8080/app/childApp的路徑下時,此時routerlink to 為childApp的連結的樣式為'active-link' 和'exact-active-link',而app的routerlink的樣式為'active-link' 而不會有'exact-active-link'

路徑的對映

我們把路徑輸入url時,按下回車,會想伺服器傳送一個請求,如果此時我們沒有在伺服器中,新增路由對映時,瀏覽器會返回我們一個錯誤資訊

Cannot GET /xxx //xxx即為url的內容

//webpack.config.client.js
const devServer = {
    port: 8000,
    host: '0.0.0.0',
    overlay: {
      errors: true,
    },
    historyApiFallback: {
        index: 'index.html'      //加上這個我們輸入url後重新整理頁面還是會顯示出內容
    },
    hot: true
  }

複製程式碼

頁面的跳轉時的滾動保留

我們有時在跳轉頁面時,不想回到上一級時已經返回到了頂部而不是跳轉之前的位置,我們可以在Router裡設定一個scrollBehavior

export default () => {
    return new Router({
        routes,
        mode:'history',
        scrollBehavior (to, from, savedPosition){ //to -> 跳轉的路由 from -> 當前的路由即跳轉的起始點 savedPosition -> 儲存當前滾動條滾動的位置 
            if(savedPosition) {  //如果是有滾動距離的,返回到之前的頁面位置
                return savedPosition
            } else { //否則,返回頂部
                return {x:0 , y:0}
            }
        }
    
    })
}
複製程式碼

配置路由的引數

這部分主要講路由方面的引數

name

import Todo from '../views/todo/todo.vue'
import Login from '../views/login/login.vue'

export default [
  {
    path: '/',
    redirect: '/app' //當輸入預設路徑時重定向到app頁面
  },
  {
    path: '/app',
    component: Todo,
    name: 'app'  //給當前的路由設定一個姓名,可以用來跳轉,與路徑和元件名無強制聯絡
  },
  {
    path: '/login',
    compoent: Login
  }
]

複製程式碼

然後在routerlink上可以進行通過name跳轉

<router-link :to="{name:'app'}"> </router-link>  //傳入物件,讓Vue來解析它
複製程式碼

之後就可以進行跳轉了,不必每次都要寫路徑

meta

用來存放一些元資訊

import Todo from '../views/todo/todo.vue'
import Login from '../views/login/login.vue'

export default [
  {
    path: '/',
    redirect: '/app' //當輸入預設路徑時重定向到app頁面
  },
  {
    path: '/app',
    component: Todo,
    meta: {
          title:'this is app',  //與html的meta同樣的效果
          description: 'author Reaper Lee'
      }
  },
  {
    path: '/login',
    compoent: Login
  }
]
複製程式碼

children

用來寫當前路徑下的子元件

import Todo from '../views/todo/todo.vue'
import Login from '../views/login/login.vue'

export default [
  {
    path: '/app',
    component: Todo,
   children: [ //注意是陣列
       {
           path: 'child', //路徑資訊,與父級相同
           component: Child 
       }
   ]
  }
]
複製程式碼

但注意這個只會在 父元件即(Todo)下的routerview展示,不會和Todo搶佔同一個routerview

路由的Transition

transition顧名思義就是路由的過度,即為一次過渡動畫,我們使用時要配合routerview使用

//app.vue
<template>
  <div id="app">
    <router-link to="/app">app</router-link>
    <router-link to="/login">login</router-link>
      <transition name="fade"> //使用fade的動畫作為過渡
          <router-view></router-view>
      </transition>
  </div>
</template>

<style>
    //定義這個fade動畫
    .fade-enter-active, .fade-leave-active {
        transition: opacity .5s;
    }
    .fade-enter, .fade-leave-to {
        opacity: 0;
    }
</style>
複製程式碼

路由傳參

我們可以通過路由傳遞一些引數,這個會在我們比如檢視一個物品的詳情頁等場景使用會比較多。具體方法如下

// routes.js
export default [
  {
    path: '/app/:id', //這裡我們就宣告瞭一個路由上的引數
    component: Todo,
    
  }
]
複製程式碼

此時我們直接訪問/app 是不會有內容的

但當我們訪問/app/xxxx (xxxx為要傳遞的引數),此時我們就可以正常的顯示頁面,我們獲取這個引數可以在父元件中使用this.$route來獲取引數

this.$router.params.id 就可以拿到路徑中的引數了,id是我們在上面的path中給的引數名為id,要與引數名保持一致
複製程式碼

還有一種更強大的方法,那就是把路徑中的引數作為元件的props傳遞給元件

// routes.js
export default [
  {
    path: '/app/:id', //這裡我們就宣告瞭一個路由上的引數
    props:true, //這裡我們設定為true即可讓引數作為props傳遞給元件
    component: Todo
  }
]
複製程式碼

然後在元件中我們需要把引數的props宣告

//todo.vue
export default {
    props: ['id']  //宣告一個名為 id 的props資料
}
複製程式碼

然後我們就可以在元件中拿到這個引數了!而且通過props傳遞的引數我們可以更好的使用,所以如果真的需要通過路徑傳引數我們儘可能使用props。

我們有時會遇見在主頁要使用多個router-view來展示元件的情況,此時我們需要給一些router-view提供name屬性

//app.vue
<template>
  <div id="app">
    <router-link to="/app">app</router-link>
    <router-link to="/login">login</router-link>
    <router-view />   //內部不放內容可以寫成空標籤形式
    <router-view name="a" />
  </div>
</template>
複製程式碼

與此同時,我們也要修改路由資訊的JavaScript檔案的一些內容

//routes.js
export default [
  {
    path: '/app/:id', //這裡我們就宣告瞭一個路由上的引數
      components:{ //注意因為要展示多個元件,所以我們這裡定義為components
          default: App   //在沒有給name屬性的router-view中展示的元件
          a: Another  //在name屬性為a的router-view中展示
      } ,  
    
  }
]
複製程式碼

路由導航守衛

當我們觸發一個導航時,全域性前置守衛按照穿件的順序呼叫,守衛是非同步執行的。我們可以通過守衛來進行一些攔截,比如只有登入之後才能進入的一些頁面或者使用的一些功能。

全域性:路由守衛有三個鉤子函式
函式名 作用
beforeEach 一般在這個守衛方法中進行全域性攔截,比如必須滿足某種條件(使用者登入等)才能進入路由的情況
beforeResolve 和beforeEach類似,區別是在導航被確認之前,同時在所有元件內守衛和非同步路由元件被解析之後,解析守衛就被呼叫
afterEach 在所有路由跳轉結束的時候呼叫這些鉤子不會接受 next 函式也不會改變導航本身
beforeEnter 可直接定義在路由配置上,和beforeEach方法引數、用法相同,呼叫位置在beforeEach 和 beforeResolve

大體的用法如下

//index.js
import createRouter from './config/router' //先匯入我們寫的路由資訊

const router = createRouter()

router.beforeEach((to, from, next) >= { //to -> 跳轉的路由 from -> 當前的路由即跳轉的起始點,next為此次跳轉的函式,呼叫他才能執行跳轉
    if(如果沒有登入){
        next('/login')  //跳轉到登入頁面
    }else if(沒有註冊) {
        next({path:'/register',replace:true});  //我們也可以傳遞一個物件,replace設定為true就不會放在history的棧堆內
	}
})

router.beforeResolve((to, from, next) >= { 
    //do something
    next();
})

router.beforeEach((to, from) >= { 
    //do something
    next();
})
複製程式碼

在路由上設定beforeEnter

//router.js
export default [
  {
    path: '/app',
    component: Todo,
    name: 'app', // 給當前的路由設定一個姓名,可以用來跳轉,與路徑和元件名無強制聯絡
    meta: {
      title: 'this is app', // 與html的meta同樣的效果
      description: 'author Reaper Lee'
    },
      beforeEnter (to,from,next) { //只有在進入路由之前呼叫
          //dosomething
      }
  }
]
複製程式碼
元件內路由守衛
函式名 作用
beforeRouteEnter 在渲染該元件的對應路由前呼叫,用法與引數和beforeEach類似, next需要被主動呼叫 ,此時例項並未被建立,不能使用this
beforeRouteUpdate 在當前路由改變,並且元件被複用時呼叫,此時例項已經建立,this可以訪問例項,next需要被主動呼叫,不能傳回撥
beforeRouteLeave 導航離開該元件的對應路由時呼叫。可以訪問元件例項this,next需要被主動呼叫,不能傳回撥
//comp.vue
export default {
    beforeRouteEnter (to, from, next) {
        //在這裡我們因為沒有例項,所以不能用this,但我們要用獲取的引數,就在next裡使用回撥函式
        next( vm => {
            //在這裡可以拿到引數
        })
    },
    beforeRouteUpdate (to,from, next){
      	next()  
    },
    beforeRouteLeave (to, from, next){ //我們可以通過下面的程式碼來實現退出時的提示確認
        if(flobal.confirm('are you sure to leave?')){ 
            next()
        }
    }
    data() {
        return {
            
        }
    }
}
複製程式碼

非同步元件

我們路由如果有非常多,一次性通過webpack把全部打包進去會導致js檔案變得異常的大,並且初次載入的時間會變得非常長,顯然是不可取的。我們可以讓對應不同的路由只載入那一部分的元件的程式碼。

使用這部分功能我們需要安裝一個babel的外掛

npm i babel-plugin-syntax-dynamic-import -D

之後在 .babelrc檔案中我們寫入

"plugins": [
    "syntax-dynamic-import"
  ]
複製程式碼
//routes.js
//注意我們不在開頭import元件

export default [
  {
    path: '/app',
    component:() => { import('元件的路徑')},  //注意要用一個函式
  }
]
複製程式碼

VueX

VueX 是基於Vue框架的一個狀態管理工具

Vue 超詳細手記

官網的介紹是:

Vuex 是一個專為 Vue.js 應用程式開發的狀態管理模式。它採用集中式儲存管理應用的所有元件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。

加入VueX

npm install vuex -S

之後我麼你在專案的資料夾裡建一個新的資料夾為stroe,並在內部建立stroe.js,他將作為整個資料儲存的入口

//store.js
import Vuex from 'vuex'
import Vue from 'vue'

Vue.use(Vuex)

export default () => {
	return	new Vuex.store({
            state:{  //儲存的物件
                count: 0
            },
            mutations:{  //對state進行操作的方法
                updateCount(state,num){ //第一個引數都為state,第二個引數為你可以傳的引數
                    state.count = num;
                }
            }
    })
}


複製程式碼

我們需要在index.js引入Vuex

//index.js
import Vuex from 'vuex'
import createStore from './store/store'

const store = createStore()

new Vue({
  router,
  store,
  render: (h) => h(App)
}).$mount(root)
複製程式碼

到這裡之後我們就已經把Vuex引入了

使用Vuex

//app.vue
<script>
    export default {
        components:{
        },
        mounted () {
            this.$router //我們就可以通過this.$store拿到所需的資料
            this.$store.commit('updateCount',引數) //我們使用store的mutaitions時需要使用 commit方法,第一個引數
        }
    }
</script>
複製程式碼

state

我們可以把state當做vue元件的data資料來使用

//state.js
export default {
    
}
複製程式碼

我們也要在store裡引入

//store.js
import defaultState from './state/state'

export default () => {
    return new Vuex.Store({
        state: defaultState,
        mutations: {
            updateCount(state,num){
                state.count = num;
            }
        }
    })
}
複製程式碼

我們也可以把mutations也單獨寫個檔案

//mutations.js
export default {
    updateCount (state, num){
        state.count = num;
    }
}
//store.js
import Vuex from 'vuex'

import defaultState from './state/state'
import mutations from './mutations/mutations'

export default () => {
    return new Vuex.Store({
        state: defaultState,
        mutations: mutations
    })
}

複製程式碼

getters

我們可以把一些要直接在頁面用的資料放在getters,然後可以直接方便使用

比如我們在state.js裡寫入了我們的名字資訊

// state.js
export default {
    count: 0,
    firstName: 'Reaper',
    lastName: 'Lee'
}
複製程式碼

我們在getters裡寫入獲取全名的方法

//getters.js
export default {
    fullName (state) {
        return `${state.firstName} ${state.lastName}`
    }
}
複製程式碼

然後我們可以通過Vuex提供的方法快速使用getters

//app.vue
<template>
	<p>
        {{fullName}}
    </p>
</template>


<script>
    export default {
        computed: {
            fullName () {
                return this.$store.getters.fullName
            }
        }
    }
</script>
複製程式碼

mutations

我們一般把修改state值的一些方法放在mutation中,然後在元件中通過呼叫修改state

注意,mutation 只能傳遞兩個引數,第一個是state物件,第二個是傳參的物件,單個引數可以單獨放入,如果要傳遞多個引數,則要放在一個物件中

其實我們在元件中可以直接使用this.$store.state來進行修改,但這樣不夠規範,如果想防止這種情況就在例項化時將strict屬性設定為true

export default () => {
  return new Vuex.Store({
    strict:true
  })
}

複製程式碼

actions

mutations只能同步操作,不能寫入非同步的程式碼,如果要執行非同步操作,必須要寫入actions中

例如我們要根據傳入的引數延時修改資料

//actions.js
export default {
    updateCountAsync (store, data) {
        setTimeout(() => {
            store.commit('updateCount',data.num) //觸發mutations的修改事件
        }, data.time)
    }
}
複製程式碼

我們在vue檔案中觸發actions的方法與mutations有所不同,觸發mutation使用commit,觸發action使用dispatch

//app.vue
export default {
	...
    mounted() {
    	this.$store.dispatch('updateCountAsync',{ num:5,time:2000})
	}
}
複製程式碼

模組

我們有時呼叫store要存在多種場景,需要劃分作用域,這時我們就要使用到Vuex的模組

//store.js
export default () => {
  return new Vuex.Store({
    modules: {
      a: {
         namespaced: true, //使用這個在不同的模組中可以使用相同命名的mutations
        state: {
          text :1
        },
          mutations: {
              updateText(state,text){
                  state.text = text;
              }
          },
          getters: {
          textPlus(state,getters,rootState){ //第二個引數是getter方法,第三個引數是全域性state
          return state.text + 1
      		}
      	  },
      actions:{
          add({state,commit,rootState}){
              commit('updateText',rootState.count,{root:true}) //使用{root:true} 就可以讓rootState為全域性
          }
      }
      },
      b: {
        text: 2
      }
    }
  })
}

複製程式碼

在vue檔案呼叫時

//app.vue
computed: {
    textA() {
        return this.$store.state.a.text //呼叫a模組的text值
    },
    textB() {
        return this.$store.state.b.text //呼叫b模組的text值
    }
}
複製程式碼

Vuex熱更替

我們在使用vuex時會發現,每當我們修改vuex內的內容再儲存時,vue不會熱更替顯示內容,而是會重新整理一下,WTF,這麼好用的功能難道Vuex用不了:horse:?

當然不是,我們只需要在store.js加入部分程式碼即可

//store.js

import defaultState from './state/state'
import mutations from './mutations/mutations'
import getters from './getters/getters'
import actions from './actions/actions'

export default () => {
    const store = new Vuex.Store({
     	state: defaultState,
    	mutations: mutations,
    	getters: getters,
        actions: actions,
    })
        
        if (module.hot) {
        module.hot.accept([
        	'./state/state',
            './mutations/mutations',
            './actions/actions',
            './getters/getters'
        ],() => {
            const newState = require('./state/state').default,
            const newMutations = require('./mutations/mutations').default
            const newGetters = require('./getters/getters').default
            const newActions = require('./actions/actions').default
            
            store.hotUpdate({
                state:newState,
                mutations:newMutations,
                actions:newActions,
                getters:newGetters
            })
        })
    }
    
    return store
}
複製程式碼

一些更多的有關Vuex的API 可以檢視

服務端渲染

服務端渲染的優勢:

  • 更好的 SEO,由於搜尋引擎爬蟲抓取工具可以直接檢視完全渲染的頁面。
  • 更快的內容到達時間(time-to-content),特別是對於緩慢的網路情況或執行緩慢的裝置。無需等待所有的 JavaScript 都完成下載並執行,才顯示伺服器渲染的標記,所以你的使用者將會更快速地看到完整渲染的頁面。通常可以產生更好的使用者體驗,並且對於那些「內容到達時間(time-to-content)與轉化率直接相關」的應用程式而言,伺服器端渲染(SSR)至關重要。

但同時也要注意一些權衡之處:

  • 開發條件所限。瀏覽器特定的程式碼,只能在某些生命週期鉤子函式(lifecycle hook)中使用;一些外部擴充套件庫(external library)可能需要特殊處理,才能在伺服器渲染應用程式中執行。
  • 涉及構建設定和部署的更多要求。與可以部署在任何靜態檔案伺服器上的完全靜態單頁面應用程式(SPA)不同,伺服器渲染應用程式,需要處於 Node.js server 執行環境。
  • 更多的伺服器端負載。在 Node.js 中渲染完整的應用程式,顯然會比僅僅提供靜態檔案的 server 更加大量佔用 CPU 資源(CPU-intensive - CPU 密集),因此如果你預料在高流量環境(high traffic)下使用,請準備相應的伺服器負載,並明智地採用快取策略。

相關文章