一简肴、什么是Vue.js
1. vue是一種數(shù)據(jù)驅(qū)動的前端框架
this.msg="我愛你"
贮缅,通過改變數(shù)據(jù)榨咐,然后自動渲染到綁定的DOM節(jié)點(diǎn)上
2. jQuery就是一種結(jié)構(gòu)驅(qū)動的前端框架
$(#app).text('你真好')
,先獲取結(jié)構(gòu),然后在修改數(shù)據(jù)來更新結(jié)構(gòu)
二谴供、搭建環(huán)境
首先保證你的電腦上有node和npm块茁,版本越新越好
-
npm install -g vue-cli
全局安裝腳手架工具,安裝的時候可以指定版本 -
vue init webpack myProject
用webpack工具初始化vue項目桂肌,myProject是項目名稱数焊,可以自己命名 -
cd myProject
進(jìn)入創(chuàng)建的vue項目目錄 -
npm install
安裝項目所需要的依賴,也就是安裝一些必要的插件 -
npm run dev
開始運(yùn)行項目
三崎场、Vue核心知識點(diǎn)
1. Vue實例
1.1
vue實例被創(chuàng)建時佩耳,它會自動的將data中的屬性,methods中的方法綁定到Vue實例對象上,也就是Vue實例代理了data對象上的所有屬性
調(diào)用時:vm.msg = "hello world"
1.2
data中存在的屬性才是響應(yīng)式的谭跨,新添加的不算
比如在瀏覽器端添加:vm.b = 1
,那么b的變化不會對頁面進(jìn)行渲染
1.3
Object.freeze(),會阻止修改現(xiàn)有的屬性干厚,響應(yīng)系統(tǒng)無法再追蹤變化
var data = {msg: 1} ; Object.freeze(data)
1.4
Vue實例還暴露了一些有用的實例屬性和方法,有前綴
el
vm.$data
vm.$props……
3. Vue模板語法
2.1 動態(tài)參數(shù)
<a v-bind:[attrName] = 'url'></a>
如果data中有一個屬性attrName值為 href,那么這個綁定v-bind:href = 'url'
v-on:[eventName] = 'doSomething'></a>
同樣,在data中有eventName的值為focus函數(shù)時污呼,v-on:focus = 'doSomething'
注意:
2.1.1. 動態(tài)參數(shù)值是字符串類型裕坊,值為null,用于解除綁定燕酷,其他任何非字符串類型的值都會觸發(fā)一個警告
2.1.2<a v-bind:['foo' + bar] = 'value'></a>
無效,要使用沒有空格和引號的表達(dá)式籍凝,或者用計算屬性代替
2.2 修飾符
.prevent
告訴v-on指令對于觸發(fā)的事件調(diào)用event.preventDefault()
<form v-on:submit.prevent = 'onSubmit'></form>
3. 計算屬性周瞎、方法和偵聽器
3.1 計算屬性
計算屬性有緩存,依賴改變才會重新計算
<div id="app">
{{fullName}}
{{age}}
</div>
<script>
var app = new Vue({
el: '#app',
data: {
firstName: 'Dell',
lastName: 'Lee',
age: 28
},
computed: {
//這個計算屬性依賴于this.firstName this.firstName這兩個變量饵蒂,只要他們不發(fā)生
//改變声诸,那么這個計算屬性就不會進(jìn)行重新計算
//vm.age = 27,頁面重新渲染,但計算屬性不會重新計算
//vm.firstName = 'Mike',會重新計算
fullName () {
console.log('打印了一次')
return this.firstName + ' ' + this.firstName
}
}
</script>
3.1.1 計算屬性中的 getter和setter
computed: {
fullName: {
//自動執(zhí)行退盯,獲取 fullName 的值
//并且 get 依賴的變量發(fā)生改變時彼乌,get就會重新進(jìn)行計算
get () {
return this.firstName + " " + this.lastName
},
//當(dāng)重新設(shè)置fullName的值的時候,set函數(shù)就會執(zhí)行 vm.fullName = 'Mike Wang'
//value就是 vm.fullName = 'Mike Wang'
//set函數(shù)里渊迁,重新設(shè)置 fullName 改變了firstName lastName慰照,觸發(fā)了get 進(jìn)行計算
set (value) {
var arr = value.split(' ')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
}
3.2 方法
methods: {
//沒有緩存,只要頁面上有內(nèi)容變動琉朽,就會執(zhí)行
fullName () {
console.log('打印了一次')
return this.firstName + ' ' + this.lastName
}
},
3.3 偵聽器
watch: {
//有緩存毒租,只有監(jiān)聽的屬性變量發(fā)生改變,才會執(zhí)行函數(shù)內(nèi)的內(nèi)容
firstName () {
console.log('打印了一次')
return this.fullName = this.firstName + ' ' + this.lastName
},
lastName () {
console.log('打印了一次')
return this.fullName = this.firstName + ' ' + this.lastName
}
},
注意: 計算屬性和偵聽器
- 計算屬性的鍵名是計算出來的箱叁,所以鍵名是一個新的名稱墅垮,在data和props中都是不存在的,計算屬性一般可以監(jiān)聽多個值的變化
- watch偵聽器就是已有值發(fā)生變化的時候執(zhí)行的操作,所以偵聽器的鍵名是data或者props中已經(jīng)存在的
4.class與style的綁定
4.1 class的綁定
<style>
.divStyle{
background-color: red;
widows: 100px;
height: 100px;
}
.borderStyle{
border: 10px solid black;
}
.btnBackground{
background: red;
}
.active{
background-color: yellow;
widows: 100px;
height: 100px;
}
.error{
border: 10px solid red;
}
</style>
<div id="app">
綁定class對象語法:對象鍵是類名耕漱,值是布爾值 <br>
意思就是isActive是true算色,那么就綁定類名為divStyle的這個類
<div v-bind:class= '{divStyle:isActive,borderStyle:isBorder}'>1223454</div>
<button v-bind:class = '{btnBackground:isBackground}' v-on:click='changeColor'>哈哈,快點(diǎn)我</button>
綁定class數(shù)組語法:數(shù)組中的成員直接對應(yīng)類名
意思是綁定的類由 activeClass 這個變量來決定孤个,若為空剃允,就綁定空,若是active齐鲤,就綁定類名為active的這個類
<div v-bind:class='[activeClass,errorClass]'>hhhhhh</div>
</div>
<script>
var app = new Vue({
el:'#app',
data:{
isActive: true,
isBorder: true,
isBackground: true,
activeClass: 'active',
errorClass: 'error'
},
methods:{
changeColor(){
this.isBackground = !this.isBackground
}
}
})
</script>
4.2 style的綁定
綁定內(nèi)聯(lián)樣式:鍵代表的是style的屬性(color,size屬性等)斥废,值就是屬性值啦
切記: 在vue中,只要是大寫字母给郊,就會轉(zhuǎn)變?yōu)? -小寫
fontSize ----> font-size
<div id="app">
<div v-bind:style="{'color':color,'fontSize':fontSize}">你丫真傻啊</div>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.6/vue.min.js"></script>
<script>
var app = new Vue({
el:'#app',
data:{
color:'red',
fontSize: '20px'
},
})
</script>
5. 條件渲染
5.1 v-if
v-if綁定的變量若為true牡肉,則該元素就會出現(xiàn)在DOM中,并且在頁面中渲染出來淆九;若為false统锤,則將該元素從DOM中移除
5.1.1 v-if使用
<div id="app">
<div v-if='show'>{{msg}}</div>
</div>
<script>
var app = new Vue({
el: '#app',
data:{
show: false,
msg: 'hello world'
},
methods: {
}
})
</script>
5.1.2 v-if v-else-if v-else的使用
<div id="app">
v-if v-else-if v-else一定要連在一起寫,中間不能有其他標(biāo)簽分隔開
<div v-if='this.show === "a"'>this is A</div>
<div v-else-if='this.show === "b"'>this is B</div>
<div v-else>this is others</div>
</div>
<script>
var app = new Vue({
el: '#app',
data:{
show: 'a',
// msg: 'hello world'
},
methods: {
}
})
</script>
5.1.3 key值的使用
Vue 會盡可能高效地渲染元素炭庙,通常會復(fù)用已有元素而不是從頭開始渲染饲窿,所以如果是完全一樣的文本框,vue會完全借用上一個文本框焕蹄,如果不想復(fù)用逾雄,可用不同的key值區(qū)分。
注意:元素復(fù)用并不局限與文本框,其他元素都會復(fù)用的鸦泳,這會極大的提高Vue的渲染效率
<div id="app">
<div v-if='show'>
用戶名:<input type="text" key="userName">
</div>
<div v-else>
lalala: <input type="text" key="userName"> //會復(fù)用
密碼名:<input type="text" key="passwordName"> //不會復(fù)用
</div>
</div>
<script>
var app = new Vue({
el: '#app',
data:{
show: true
},
})
</script>
5.2 v-show
v-show綁定的變量無論是true還是false银锻,它只是控制這個DOM元素的display:'none'這個style屬性而已,這個元素一直在DOM中
6.列表渲染
6.1數(shù)組渲染
這種是工作中常用的循環(huán)做鹰,要加上key值击纬,可以提高vue的性能,理想的 key 值是每項都有的唯一 id钾麸。key不建議使用index
注意:
- Vue有一組觀察數(shù)組的變異方法更振,所以他們也會觸發(fā)視圖更新
方法如下:
- push()----在數(shù)組末尾加上一項
vm.list.push({id:'004',text:'啦啦啦'})
- pop()----將數(shù)組的最后一個元素移除
- shift()----刪除數(shù)組的第一個元素
- unshift()----在數(shù)組的第一個元素位置添加一個元素
- splice()----可以添加或者刪除數(shù)組中的一個或多個元素—返回刪除的元素
三個參數(shù):
- 表示開始操作的位置
- 要操作的長度,為0喂走,就是不刪除
- 為可選參數(shù)殃饿,可以把要添加的元素或者數(shù)組對象寫在這里
arr.splice(4,1,{ll:true})
- sort()----排序
- reverse()----數(shù)組反轉(zhuǎn)
當(dāng)我們想要修改數(shù)組中的元素的時候谋作,不能通過下標(biāo)的方式進(jìn)行改變芋肠,只能通過Vue提供的幾個數(shù)組變異方法來實現(xiàn)。
比如想要在數(shù)組添加一項內(nèi)容:
vm.list[4]={id: '005',text:'hello world'}
其實list數(shù)組中已經(jīng)添加上了這一項遵蚜,只是無法在頁面中渲染出來
比如在數(shù)組第二項后面添加一項內(nèi)容:
vm.list.splice(1,0,{id:'001',text:'第三項'})
當(dāng)我們想要修改數(shù)組中的元素的時候帖池,還可以通過改變數(shù)組的引用,就是對數(shù)組進(jìn)行重新賦值
當(dāng)我們想要修改數(shù)組中的元素的時候吭净,還可以通過set語法
Vue.set(vm.list,1,{id:'007',text: '你真傻'})
或者vm.$set(vm.list,1,{id:'007',text: '你真傻'})
,中間數(shù)字為操作元素的index值
<div id="app">
<div v-for='(item,index) of list' :key = 'item.id'>{{item.text}}----{{index}}</div>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
list: [{
id: '001',
text: 'xiaoliu'
},{
id: '002',
text: 'xiaoxv'
},{
id: '003',
text: 'xiaofang'
}]
}
})
</script>
如果我們想要通過一個list循環(huán)兩項睡汹,比如div和span
兩個循環(huán)完全獨(dú)立,不符合預(yù)期
<div id="app">
<div v-for='(item,index) of list' :key='item.id'>{{item.text}}----{{index}}</div>
<span v-for='(item,index) of list' :key='item.id'>{{item.text}}----{{index}}</span>
</div>
外面包一層div,效果可以寂殉,但我們并不想要包裹的div出現(xiàn)在DOM中
<div id="app">
<div v-for='(item,index) of list' :key='item.id'>
<div>{{item.text}}----{{index}}</div>
<span>{{item.text}}----{{index}}</span>
</div>
</div>
滿足預(yù)期囚巴,且在DOM中不會出現(xiàn)template標(biāo)簽
<div id="app">
<template v-for='(item,index) of list' :key='item.id'>
<div>{{item.text}}----{{index}}</div>
<span>{{item.text}}----{{index}}</span>
</template>
</div>
6.2 對象渲染
參數(shù)順序:拿到value key index 的寫法 v---k---i 外開
<div id="app">
<div v-for = '(item,key,index) of userInfo'>{{key}}: {{item}}--{{index}}</div>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
userInfo: {
name: 'Dell',
sex: 'man',
age: 18
}
}
})
</script>
注意:
對象里面要想添加某一項,用點(diǎn)語法是行不通的
比如:vm.userInfo.address = 'ZhengZhou'
,在頁面中渲染不出來
- 通過改變對象引用友扰,就是為對象重新賦值的方式來改變對象內(nèi)容
- 通過set語法
Vue.set(vm.userInfo,'address', 'beijing')
或者vm.$set(vm.userInfo,'adress','beijing')
7.組件使用中的一些細(xì)節(jié)
7.1 table中的問題
正常情況下如此
<div id="root">
<table>
<tbody>
<tr><td>1</td></tr>
<tr><td>2</td></tr>
<tr><td>3</td></tr>
</tbody>
</table>
</div>
每一行都引入一個組件彤叉,但是渲染的時候發(fā)現(xiàn)渲染出來的 row 并不在 tbody 標(biāo)簽里面
因為 tbody 標(biāo)簽里面只能寫 tr,其他標(biāo)簽他識別不了
<div id="root">
<table>
<tbody>
<row></row>
<row></row>
<row></row>
</tbody>
</table>
</div>
<script>
Vue.component('row',{
template: '<tr><td>this is a row</td></tr>'
})
new Vue({
el: '#root',
})
</script>
使用is屬性可以解決這個問題
<table>
<tbody>
<tr is="row"></tr>
<tr is="row"></tr>
<tr is="row"></tr>
</tbody>
</table>
比如:這幾種情況最好也不要直接寫組件,用is屬性來實現(xiàn)
<ul>
<li is="row"></li>
</ul>
<ol>
<li is="row"></li>
</ol>
<section>
<option is="row"></option>
</section>
7.2 組件中的data
組件都是可以復(fù)用的村怪,所以組件中data也得是一個獨(dú)立的對象秽浇,那么組件之間的data才不會相互干擾
<div id="root">
<table>
<tbody>
<tr is="row"></tr>
<tr is="row"></tr>
<tr is="row"></tr>
</tbody>
</table>
</div>
<script>
Vue.component('row',{
data () {
return {
content: 'this is a row'
}
},
template: '<tr><td>{{content}}</td></tr>'
})
new Vue({
el: '#root',
data: {
}
})
</script>
7.3 ref的使用
ref在dom標(biāo)簽上使用,指向的是這個dom節(jié)點(diǎn)
<div id="root">
<div ref="hello"
@click = 'handleClick'
>
hello world
</div>
</div>
<script>
new Vue({
el: '#root',
data: {
},
methods: {
handleClick () {
console.log(this.$refs.hello)//就是指向的上面的div節(jié)點(diǎn)
console.log(this.$refs.hello.innerHTML)//hello world
}
}
})
</script>
ref在一個組件上使用甚负,實際上是對這個組件的一個引用
<div id="root">
<counter ref="one" @change = 'sumNumber'></counter>
<counter ref="two" @change = 'sumNumber'></counter>
<div>{{sum}}</div>
</div>
<script>
Vue.component('counter',{
template: '<div @click="handleClick">{{number}}</div>',
data () {
return {
number: 0
}
},
methods: {
handleClick () {
this.number ++
this.$emit('change',this.number)
}
}
})
new Vue({
el: '#root',
data: {
sum: 0
},
methods: {
sumNumber (number) {
this.sum = this.$refs.one.number + this.$refs.two.number
}
}
})
</script>
8. 父子組件間數(shù)據(jù)傳遞
8.1 父組件向子組件傳遞數(shù)據(jù)
注意:
- 父組件向子組件傳值時柬焕,傳值參數(shù)前最好加上v-bind,以下面為例:
count="1"
就是傳遞的字符串1
:count="1"
就是數(shù)字1梭域,
不加:斑举,那么傳遞參數(shù)就是參數(shù)后的那個值
加:,那么傳遞參數(shù)就是引號里面的js表達(dá)式- 子組件接收到父組件傳過來的值病涨,是不能修改的富玷,不要在子組件中修改props中的值,vue中有單向數(shù)據(jù)流的概念,即父組件向子組件傳值時凌彬,父組件中可以隨意修改所要傳遞地數(shù)據(jù)沸柔,子組件不能反過來修改父組件傳遞來的數(shù)據(jù)
如果你在子組件中修改了props:
- 如果傳過來的是基本類型,那么會有警告铲敛,但在頁面中正常渲染
- 如果是引用類型(對象褐澎,數(shù)組),vue會檢測不到變化伐蒋,頁面中也不會渲染
如何修改傳遞過來的值工三?
- 把傳遞過來的值賦值給子組件data的一個屬性里,就可以修改啦
<div id="root">
<counter :count = "1"></counter>
<counter :count = "2"></counter>
</div>
<script>
var counter = { //局部組件先鱼,需要在父組件中注冊
props: ['count'],
template: '<div @click = "handleClick">{{this.count}}</div>',
methods: {
handleClick () {
this.count ++
}
}
}
var vm = new Vue({
el: '#root',
data: {
},
components: {
counter
}
})
</script>
//正確寫法
var counter = { //局部組件俭正,需要在父組件中注冊
props: ['count'],
data () {
return {
number: this.count
}
},
template: '<div @click = "handleClick">{{number}}</div>',
methods: {
handleClick () {
this.number ++
}
}
}
8.2 子組件向父組件傳遞數(shù)據(jù)
<div id="root">
<counter :count = "1" @change = 'numberChange'></counter>
<counter :count = "2" @change = 'numberChange'></counter>
<div>{{total}}</div>
</div>
<script>
var counter = { //局部組件,需要在父組件中注冊
props: ['count'],
data () {
return {
number: this.count
}
},
template: '<div @click = "handleClick">{{number}}</div>',
methods: {
handleClick () {
this.number = this.number + 2
this.$emit('change',2) // 2是步長
}
}
}
var vm = new Vue({
el: '#root',
data: {
total: 3
},
components: {
counter,
},
methods: {
numberChange (step) {
this.total += step
}
}
})
</script>
8.3 組件參數(shù)校驗與非props特性
以下是props的參數(shù)校驗的幾種寫法焙畔,以及參數(shù)意思
props特性和非props特性
- props特性就是父組件傳遞過去掸读,子組件有接收,接收后這個特性就不會出現(xiàn)在DOM結(jié)構(gòu)中
- 非props特性就是父組件傳遞過去宏多,子組件沒有接收儿惫,會在DOM結(jié)構(gòu)中出現(xiàn)(了解)
<div id="root">
<child content="hello world"></child>
</div>
<script>
Vue.component('child',{
// props: ['content'],
props: {
// content: [ String, Number ]
content: {
type: String,
required: true,//要求父組件必須向子組件傳content,不傳會有提示
default: 'default value',//如果不傳內(nèi)容伸但,那么這個內(nèi)容就默認(rèn)顯示
validator (value) {//校驗器肾请,value就是傳過來的數(shù)據(jù)
return (value.length < 5)
}
}
},
template: '<div>{{content}}</div>'
})
var vm = new Vue({
el: '#root',
data: {
},
})
</script>
8.3 自定義事件和原生事件
- 自定義事件:子組件在父組件中使用時,直接綁定在子組件上的事件就是自定義事件更胖,必須經(jīng)過子組件的觸發(fā)才能執(zhí)行
- 原生事件:直接在子組件里的模板上綁定的事件铛铁,子組件引入后是可以直接使用的
- 怎么在父組件的子組件里直接綁定原生事件,不用子組件的再次觸發(fā)呢却妨?
直接在綁定的自定義事件后加上修飾符
.native
<div id="root">
<child @click = 'handleClick'></child> //在這里綁定的是自定義事件饵逐,必須經(jīng)過子組件的觸發(fā)才能執(zhí)行
</div>
<script>
Vue.component('child', {
template: '<div @click = "handleChildClick">click</div>',//這里綁定的是原生事件
methods: {
handleChildClick () {
alert('child click')
this.$emit('click')//觸發(fā)自定義事件
}
}
})
var vm = new Vue({
el: '#root',
methods: {
handleClick () {
alert('click')
}
}
})
</script>
<div id="root">
<child @click.native = 'handleClick'></child>
</div>
<script>
Vue.component('child', {
template: '<div @click = "handleChildClick">click</div>'//這里綁定的是原生事件
})
var vm = new Vue({
el: '#root',
methods: {
handleClick () {
alert('click')
}
}
})
</script>
9. 非父子組件之間的傳值
bus/總線/發(fā)布訂閱模式/觀察者模式
步驟:
- 建立總線
Vue.prototype.bus = new Vue()
- 在子組件中觸發(fā)
this.bus.$emit('change',this.selfContent)
- 在子組件mounted生命周期鉤子函數(shù)中監(jiān)聽
this.bus.$on('change',(value)=>{})
value就是傳過來的值,可以進(jìn)行操作
<div id="root">
<child content="Dell" ></child>
<child content="Lee"></child>
</div>
<script>
Vue.prototype.bus = new Vue()
Vue.component('child',{
props: {
content: String
},
data () {
return {
selfContent: this.content
}
},
template: '<div @click = "handleClick">{{selfContent}}</div>',
methods: {
handleClick () {
this.bus.$emit('change',this.selfContent)
}
},
mounted () {
this.bus.$on('change',(value)=>{
// alert(this.content)
this.selfContent = value
})
}
})
var vm = new Vue({
el: '#root'
})
</script>
10. 插槽
10.1 沒有插槽前的傳值方式
父組件向子組件傳值管呵,采用props傳值梳毙,可以傳少量的值,但如果數(shù)值較多的話捐下,代碼可讀性會很低
<div id="root">
<child content="<p>Dell</p>"></child>
</div>
<script>
Vue.component('child',{
props: ['content'],
// template: "<div><p>hello</p>{{content}}</div>"http://content無法轉(zhuǎn)義
template: `<div>
<p>hello</p>
<div v-html='this.content'></div> //可以轉(zhuǎn)義账锹,但是content外包了一個div
</div>`
})
var vm = new Vue({
el: '#root'
})
</script>
插槽
<div id="root">
<child>
<p>Dell</p>
</child>
</div>
<script>
Vue.component('child',{
template: `<div>
<p>hello</p>
<slot>默認(rèn)數(shù)據(jù)</slot>//父組件中間沒有傳值的時候默認(rèn)顯示
</div>`
})
var vm = new Vue({
el: '#root'
})
</script>
具名插槽,可以在父組件中插入多個模塊的內(nèi)容
<div id="root">
<child>
<div class="header" slot="header">header</div>
<div class="footer" slot="footer">footer</div>
</child>
</div>
<script>
Vue.component('child',{
template: `<div>
<slot name="header"></slot>
<p>hello</p>
<slot name="footer"></slot>
</div>`
})
var vm = new Vue({
el: '#root'
})
</script>
作用域插槽
- 執(zhí)行邏輯:
- 父組件調(diào)用子組件時坷襟,向子組件傳遞了一個插槽
- 子組件通過slot向父組件傳遞數(shù)據(jù)奸柬,比如:
:item = item
- 插槽是作用域插槽,插槽必須寫在template里面婴程,同時聲明從子組件接收的數(shù)據(jù)都放在props里面
- 在template里面寫上模板的信息廓奕,以什么方式進(jìn)行展示
- 什么時候用作用域插槽?
子組件做循環(huán)或者有一部分的DOM結(jié)構(gòu)要由外部傳過來的時候
<div id="root">
<child></child>
</div>
<script>
Vue.component('child',{
data () {
return {
list: [1,2,3,4]
}
},
template: `<div>
<ul>
<li v-for='item of list'>{{item}}</li>
</ul>
</div>`
})
var vm = new Vue({
el: '#root'
})
</script>
作用域插槽改寫
<div id="root">
<child>
<template slot-scope='props'>
<h1>{{props.item}}</h1>
</template>
</child>
</div>
<script>
Vue.component('child',{
data () {
return {
list: [1,2,3,4]
}
},
template: `<div>
<ul>
<slot v-for='item of list' :item = item></slot>
</ul>
</div>`
})
var vm = new Vue({
el: '#root'
})
</script>
11. 動態(tài)組件和v-once指令
- 動態(tài)組件
動態(tài)組件中is屬性根據(jù)綁定的組件名的不同會動態(tài)的切換組件
- v-once 使用
子組件中加上v-once,也就是第一次執(zhí)行的時候就把子組件放入內(nèi)存桌粉,下次直接復(fù)用即可蒸绩,所以如果子組件內(nèi)容不變的話,加上v-once會提高vue性能
<div id="root">
<component :is = 'type'></component>
<child-one v-if='type === "child-one"'></child-one>
<child-two v-if='type === "child-two"'></child-two>
<button @click='handleClick'>change</button>
</div>
<script>
Vue.component('child-one',{
template: '<div v-once>child-one</div>'
})
Vue.component('child-two',{
template: '<div v-once>child-two</div>'
})
var vm = new Vue({
el: '#root',
data: {
type: 'child-one'
},
methods: {
handleClick () {
this.type = this.type === 'child-one'? 'child-two':'child-one'
}
}
})
</script>
12.Vue動畫
12.1 css過渡動畫
過渡動畫原理:
- 在需要過渡的元素外面包裹上transition標(biāo)簽铃肯,那么vue執(zhí)行代碼的時候就會對被包裹的元素進(jìn)行解析
- 以緩動出現(xiàn)為例:
- 動畫開始前患亿,vue剛開始解析代碼,就會給transition包裹的標(biāo)簽(div)加上
v-enter , v-enter-active
兩個類(注意可以給transition加name押逼,那么v就換成name名稱)- 動畫開始時步藕,去掉
fade-enter
這個類,添加fade-enter-to
這個類- 動畫結(jié)束時挑格,所有的類都去掉
- 我們可以根據(jù)這些類的添加和刪除為這些類添加一些樣式咙冗,來做出動畫效果
代碼如下:
<style>
/*緩動出現(xiàn)動畫 */
.v-enter {
opacity: 0; /*將初始狀態(tài)的透明度設(shè)為0*/
}
.v-enter-active {
transition: opacity 1s; /*transition檢測到opacity的變化,1s內(nèi)完成*/
}
/*緩慢消失動畫 */
.v-leave-to {
opacity: 0;/*將最終狀態(tài)變?yōu)?*/
}
.v-leave-active {
transition: opacity 1s;
}
</style>
<div id="root">
<transition>
<div v-if='show'>hello world</div>
</transition>
<button @click='handleButton'>切換</button>
</div>
<script>
var vm = new Vue({
el: '#root',
data: {
show: true
},
methods: {
handleButton() {
this.show = !this.show
}
}
})
</script>
12.2 在Vue中使用animate.css庫
Vue中使用keyframes
v-enter-active, v-leave-active
在動畫整個過程都是存在的漂彤,所以可以在這里面寫效果vue提供的原生類名太長雾消,想換類名怎么辦?
可以在
transition
里面自己設(shè)置類名 比如:enter-active-class='enter'
leave-active-class='leave'
显歧,那么這兩個類就可以這樣簡寫啦
<style>
@keyframes bounce-in {
0% {
transform: scale(0)
}
50% {
transform: scale(1.5)
}
100% {
transform: scale(1)
}
}
.v-enter-active {
transform-origin: left center;/*設(shè)置元素變形的原點(diǎn)帘睦,左邊線的中點(diǎn)*/
animation: bounce-in 1s;
}
.v-leave-active {
transform-origin: left center;
animation: bounce-in 1s reverse;
}
</style>
<div id="root">
<transition>
<div v-if='show'>hello world</div>
</transition>
<button @click='handleButton'>切換</button>
</div>
<script>
var vm = new Vue({
el: '#root',
data: {
show: true
},
methods: {
handleButton() {
this.show = !this.show
}
}
})
</script>
使用animate.css庫
如何使頁面出現(xiàn)以及刷新的時候也出現(xiàn)動畫呢咕宿?在transition中加入屬性
appear
,類appear-active-class='animated swing'
<link rel="stylesheet" href="../animate.css">
<div id="root">
<transition
appear
enter-active-class = 'animated swing'
leave-active-class='animated shake'
appear-active-class= 'animated swing'
>
<div v-if='show'>hello world</div>
</transition>
<button @click='handleButton'>切換</button>
</div>
<script>
var vm = new Vue({
el: '#root',
data: {
show: true
},
methods: {
handleButton() {
this.show = !this.show
}
}
})
</script>
12.3 在vue中同時使用animate.css庫和transition
注意: animate.css動畫默認(rèn)時間是1s,所以如果transition和animate時間不一致枢析?
怎么辦疚脐?
- 加
type="transition"
用來指定以誰的時間為準(zhǔn)- 自定義時間
:duration="5000"
<style>
.v-enter,.v-leave-to{
opacity: 0;
}
.v-enter-active,
.v-leave-active{
transition: opacity 3s;
}
</style>
<div id="root">
<transition
//type="transition"
//:duration="5000" 動畫執(zhí)行時間為5s
:duration="{enter: 5000,leave: 10000}" //可以設(shè)置進(jìn)出場動畫的時間
appear
enter-active-class = 'animated swing v-enter-active'
leave-active-class='animated shake v-leave-active'
appear-active-class='animated swing'
>
<div v-if='show'>hello world</div>
</transition>
<button @click='handleButton'>切換</button>
</div>
12.4 js 動畫
js動畫鉤子:
- before-enter: 動畫開始執(zhí)行前就執(zhí)行啦
- enter: 動畫開始的時候執(zhí)行
- after-enter: 動畫結(jié)束時執(zhí)行
離開動畫與進(jìn)入動畫一樣before-leave,leave,after-leave
<div id="root">
<transition
name="fade"
@before-enter="handleBeforeEnter"
@enter = "handleEnter"
@after-enter = "handleAfterEnter"
>
<div v-if='show'>hello world</div>
</transition>
<button @click='handleButton'>切換</button>
</div>
<script>
var vm = new Vue({
el: '#root',
data: {
show: true
},
methods: {
handleButton() {
this.show = !this.show
},
handleBeforeEnter (el) {//動畫開始執(zhí)行前調(diào)用
// alert('beforeenter')
el.style.color="red"http://el就是指transition里面包裹的元素
},
handleEnter (el,done) {//動畫開始執(zhí)行時調(diào)用
// alert('enter')
setTimeout(() => {
el.style.color="green"
},2000)
setTimeout(()=>{
done()//只有調(diào)用done這個回調(diào)函數(shù)之后奶躯,才會執(zhí)行after-enter這個鉤子
},4000)
},
handleAfterEnter (el) {
el.style.color= "#000"
}
}
})
</script>
12.5 Velocity動畫庫的使用
<div id="root">
<transition
name="fade"
@before-enter="handleBeforeEnter"
@enter = "handleEnter"
@after-enter = "handleAfterEnter"
>
<div v-if='show'>hello world</div>
</transition>
<button @click='handleButton'>切換</button>
</div>
<script>
var vm = new Vue({
el: '#root',
data: {
show: true
},
methods: {
handleButton() {
this.show = !this.show
},
handleBeforeEnter (el) {
el.style.opacity= 0
},
handleEnter (el,done) {
Velocity(el,{opacity: 1},{duration: 1000})
//在1s之內(nèi)把el的opacity從0變到1
},
handleAfterEnter (el) {
}
}
})
</script>
12.6 Vue中多個元素或組件的過渡
- 多個元素的過渡
注意:多個元素的過渡买喧,每個元素要加一個key,如果不加vue中會進(jìn)行dom復(fù)用馋袜,就沒有動畫效果啦
<style>
.v-enter,.v-leave-to{
opacity: 0
}
.v-enter-active,.v-leave-active {
transition: opacity 1s
}
</style>
<div id="root">
<transition mode="out-in">
<div v-if='show' key="hello">hello world</div>
<div v-else key="bye">bye world</div>
</transition>
<button @click='handleButton'>切換</button>
</div>
<script>
var vm = new Vue({
el: '#root',
data: {
show: true
},
methods: {
handleButton() {
this.show = !this.show
}
}
})
</script>
- 多個組件的過渡
<style>
.v-enter,.v-leave-to{
opacity: 0
}
.v-enter-active,.v-leave-active {
transition: opacity 1s
}
</style>
<div id="root">
<transition mode="out-in">
<child v-if='show'></child>
<child-one v-else></child-one>
</transition>
<button @click='handleButton'>切換</button>
</div>
<script>
Vue.component('child',{
template: '<div>child</div>'
})
Vue.component('child-one',{
template: '<div>child-one</div>'
})
var vm = new Vue({
el: '#root',
data: {
show: true
},
methods: {
handleButton() {
this.show = !this.show
}
}
})
</script>
多個組件可以轉(zhuǎn)化為動態(tài)組件
<style>
.v-enter,.v-leave-to{
opacity: 0
}
.v-enter-active,.v-leave-active {
transition: opacity 1s
}
</style>
<div id="root">
<transition mode="out-in">
<component :is="type"></component>
</transition>
<button @click='handleButton'>切換</button>
</div>
<script>
Vue.component('child',{
template: '<div>child</div>'
})
Vue.component('child-one',{
template: '<div>child-one</div>'
})
var vm = new Vue({
el: '#root',
data: {
type: "child"
},
methods: {
handleButton() {
this.type = this.type === "child"?"child-one":"child"
}
}
})
</script>
12.7 vue中的列表過渡
原理:
<transition-group> <div v-for="item of list" :key="item.id">{{item.title}}</div> </transition-group>
把循環(huán)的每一項都變成了
<transition> <div>{{item.title}}</div> </transition>
在進(jìn)行渲染
<style>
.v-enter,.v-leave-to {
opacity: 0
}
.v-enter-active,.v-leave-active {
transition: opacity 1s
}
</style>
<div id="root">
<transition-group>
<div v-for="item of list" :key="item.id">{{item.title}}</div>
</transition-group>
<button @click="handleAddOne">addOne</button>
</div>
<script>
var count = 0
var vm = new Vue({
el: '#root',
data: {
list: []
},
methods: {
handleAddOne () {
this.list.push({
title: "xxx我又想你" + count + "次",
id: count++
})
}
}
})
</script>
12.8 vue動畫封裝
<div id="root">
<fade :show="show">
<div>hello world</div>
</fade>
<fade :show="show">
<div>I love you</div>
</fade>
<button @click='handleButton'>切換</button>
</div>
<script>
Vue.component('fade',{
props: ['show'],
template:
`<transition @before-enter="handleBeforeEnter"
@enter="handleEnter"
>
<slot v-if="show"></slot>
</transition>
`,
methods: {
handleBeforeEnter (el) {
el.style.color = "red"
},
handleEnter (el,done) {
setTimeout(()=>{
el.style.color = "green"
done()
},2000)
}
}
})
13. Vue-router的學(xué)習(xí)
基本寫法:vue本質(zhì)上是一個單頁面應(yīng)用简逮,頁面間跳轉(zhuǎn)就是組件之間的跳轉(zhuǎn)
13.1 vue-router的使用步驟(vue-cli的情況下)
- 在src目錄下建立router目錄旨巷,目錄下創(chuàng)建index.js
- 在index.js中引入
import Vue from 'vue'; import Router from 'vue-router'
- 使用Router
Vue.use(Router)
因為vue-router是一個插件巨缘,vue中使用插件就要Vue.use()
- 定義路由
import Home from '@/pages/home/Home'
import City from '@/pages/city/City'
export const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/city',
name: 'City',
component: City
},
]
- 創(chuàng)建Router實例,對路由進(jìn)行配置
const router = new Router({
routes, //routes: routes
mode: 'history', //默認(rèn)是hash模式(路徑中有#,seo不好)采呐,改成history若锁,可以把路徑中#去掉,不過這樣寫刷新頁面后會出現(xiàn)404斧吐,還需在后端配置
base: /base/, //會在routes設(shè)置的路徑前面加上/base/(可以自定義)又固,用于區(qū)分一
//些頁面,注意/base/不是必須的煤率,去掉它頁面還會正常顯示
})
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/home/Home'
import City from '@/pages/city/City'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/city',
name: 'City',
component: City
},
]
})
四仰冠、vue-router學(xué)習(xí)
Ⅰ. vue-router在項目中的使用
當(dāng)項目比較大時,可以將route拆分成兩個文件
routes.js
import Todo from '../views/todo/todo.vue'
import Login from '../views/login/login.vue'
export default [
{
path: '/',
redirect: '/app'
},
{
path: '/app',
component: Todo
},
{
path: '/login',
component: Login
}
]
router.js
import Vue from 'vue'
import Router from 'vue-router'
import routes from './routes'
Vue.use(Router)
const router = new Router({ // 不推薦這樣做蝶糯,這樣做傳出去的就一個router洋只,做服務(wù)端渲染的時候會內(nèi)存溢出
routes,
// mode: 'history', // 可以去掉hash路由的#
// base: '/base/', // 在routes配置的路前都加上/base/, 里面可以是任何內(nèi)容,
linkActiveClass: 'active-class', // 可以自定義router-link a 標(biāo)簽里面的類名
linkExactActiveClass: 'exact-active-class' // 精確匹配路徑
})
export default router
router.js另一種寫法:(做服務(wù)端渲染的時候要使用這種方法,要不然會內(nèi)存溢出)
import Vue from 'vue'
import Router from 'vue-router'
import routes from './routes'
Vue.use(Router)
export default () => { // 這樣每次傳出去的就是一個新router
return new Router({
routes: routes
})
}
// 在入口文件中引入
import creatRouter from './config/router'
const router = creatRouter()
然后在項目入口文件中引入router.js识虚,在實例中注冊好肢扯,將router-view放到app.vue中就好啦
Ⅱ、router對象(new Router)中的一些配置項
1. mode: history
作用:將URL中難看的#
去掉
分析:
- 為什么vue-router跳轉(zhuǎn)要有hash和history兩種模式呢担锤?
vue是單頁面應(yīng)用鹃彻,所以vue-router的核心就在于---改變路由的同時不會向后端發(fā)送請求
hash模式:hash(#)是URL 的錨點(diǎn),代表的是網(wǎng)頁中的一個位置妻献,單單改變#后的部分蛛株,瀏覽器只會滾動到相應(yīng)位置,不會重新加載網(wǎng)頁育拨,也就是說 #是用來指導(dǎo)瀏覽器動作的谨履,對服務(wù)器端完全無用,HTTP請求中也不會不包括#熬丧;同時每一次改變#后的部分笋粟,都會在瀏覽器的訪問歷史中增加一個記錄,使用”后退”按鈕析蝴,就可以回到上一個位置
history模式:HTML5 History API提供了一種功能害捕,能讓開發(fā)人員在不刷新整個頁面的情況下修改站點(diǎn)的URL,就是利用 history.pushState API 來完成 URL 跳轉(zhuǎn)而無須重新加載頁面
注意:但當(dāng)用戶直接在用戶欄輸入地址并帶有參數(shù)時:
Hash模式:xxx.com/#/id=5 請求地址為 xxx.com,沒有問題闷畸;
History模式: xxx.com/id=5 請求地址為 xxx.com/id=5尝盼,如果后端沒有對應(yīng)的路由處理,就會返回404錯誤
所以佑菩,我們使用history模式的時候盾沫,一定要在后臺進(jìn)行配置
我們在開發(fā)環(huán)境時,使用devServer啟動服務(wù)殿漠,所以可以在devServer中配置
historyApiFallback: {
index: '/index.html'
},
2. base: '/base/'
---配置base項赴精,base項的值可以任意寫
作用:在routes配置的路前都加上/base/, 里面可以是任何內(nèi)容,可以用來標(biāo)記一些特殊的頁面绞幌。
注意:這個base配置的值在url中不是必須的蕾哟,去掉url還是正常顯示的
3. linkActiveClass 和 linkExactActiveClass
作用:可以改變router-link
形成的 a 標(biāo)簽中的兩個類名
最初類名:
<a data-v-06ebb29e="" href="/base/app" class="router-link-exact-active router-link-active">app</a>
配置自定義類名:
linkActiveClass: 'active-class',
linkExactActiveClass: 'exact-active-class' // 精確匹配路徑
自定義后的類名:
<a data-v-06ebb29e="" href="/base/app" class="exact-active-class active-class">app</a>
問題:這兩個類有什么作用?他們兩個有什么區(qū)別莲蜘?
作用:用來給激活的鏈接加樣式
區(qū)別:
-
linkActiveClass
: 全局配置 <router-link> 的默認(rèn)“激活 class 類名” -
linkExactActiveClass
: 全局配置 <router-link> 精確激活的默認(rèn)的 class
舉例:頁面上兩個鏈接:login谭确,login exact, login 路徑是/login
, login exact的路徑是login/exact
那么當(dāng)我們激活 login exact 的時候,頁面上的類顯示:
<a data-v-06ebb29e="" href="/base/login" class="active-class">login</a>
<a data-v-06ebb29e="" href="/base/login/exact" class="exact-active-class active-class">login exact</a>
4. scrollBehavior
的配置
作用:配置路由頁面的滾動行為
說明:
- to from 都是路由對象菇夸,to將要跳轉(zhuǎn)的路由琼富,from就是當(dāng)前還未跳轉(zhuǎn)的路由
- savedPosition就是要跳轉(zhuǎn)的頁面以前滾動保留下的位置,如存在庄新,跳轉(zhuǎn)后到保留的位置鞠眉,不存在薯鼠,就到頁面頂端唄
- 當(dāng)然不同頁面的位置可以有to from定制
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}
5. parseQuery 和 stringifyQuery
作用:url中的經(jīng)常會有查詢參數(shù)(?后面的一串)械蹋,我們需要把它們轉(zhuǎn)化為json對象才能使用出皇,其實vue會幫我們轉(zhuǎn)化它們,不過如果我們有一些特殊的需求的話哗戈,可以用他們配置
parseQuery (query) {
// 把查詢參數(shù)(string)轉(zhuǎn)化為json對象
},
stringifyQuery (obj) {
// 把對象轉(zhuǎn)化為字符串
}
6. fallback: true
作用:當(dāng)瀏覽器不支持 history.pushState 控制路由是否應(yīng)該回退到 hash 模式郊艘。默認(rèn)值為 true。
如果你不想退回 hash 模式唯咬,那寫成 false 纱注,改成false之后,那么vue單頁應(yīng)用變成多頁應(yīng)用胆胰,每次router-link跳轉(zhuǎn)都會去后端拿數(shù)據(jù)狞贱,比較耗時
所以一般不要改成false
Ⅲ、routes(路由)配置的的要點(diǎn)
1. name
的配置與作用
{
path: '/app',
component: Todo,
name: 'app' // 與path無關(guān)蜀涨,可以定義任何名字r
}
作用:路由跳轉(zhuǎn)router-link
中可以使用
比如:下面它們是等效的
<router-link to="/app">app</router-link>
<router-link :to="{name: 'app'}">app</router-link>
2. meta
的配置與作用
作用:保存一下當(dāng)前路由的一些信息的瞎嬉,我們再寫html頁面時,head標(biāo)簽里面的meta里面保存的信息厚柳,我們稱他們?yōu)轫撁娴脑葱畔⒀踉妫@些信息有利于我們處理seo,根據(jù)description里面的內(nèi)容來排列他們的搜索結(jié)果,而在vue組件里面别垮,我們沒有辦法在組件中寫這些東西便监,所以可以在路由里面的meta里面寫我們需要的東西
meta: {
title: 'this is an app',
description: 'asddffg'
}
3. children
的配置和使用
作用:當(dāng)前路由的子路由
注意:當(dāng)前路由可能不止一個子路由,所以children是一個數(shù)組宰闰,在routes配置好之后茬贵,在當(dāng)前路由需要的位置引入router-view即可
children: [
{
path: 'test',
component: Login
}
]
4. 路由參數(shù)的配置
作用:通過向像跳轉(zhuǎn)的組件傳參數(shù),來改變組件的行為(類似于props)
分為id和query
// 在routes中配置參數(shù)
{
path: '/app/:id', // 配置參數(shù)
component: Todo,
name: 'app',
meta: {
title: 'this is an app',
description: 'asddffg'
}
// 向要跳轉(zhuǎn)的路由傳參數(shù)(這個參數(shù)一般都是一個變量)
<router-link to="/app/123">app</router-link>
//接收到參數(shù)的路由怎么使用參數(shù)移袍?
mounted () {
console.log(this.$route) // this.$route包含著路由匹配成功后路由中的所有信息
console.log(this.$route.params.id) // 拿到了id值
console.log(this.$route.query) // 拿到了query值(query不用在routes設(shè)置,傳參的時候有query, 就能拿到)
}
注意:在同一個路由下面this.$route
在哪一個組件中都是一樣的
this.$route對象:
使用場景:當(dāng)我們定義路徑的時候老充,有一個列表請求(比如商品列表)葡盗,我們可以通過傳參的方式拿到單個商品的id,進(jìn)而去請求這個商品的詳細(xì)內(nèi)容(類似于props啡浊,都是通過傳來的數(shù)據(jù)觅够,來改變當(dāng)前組件的某些行為)
5. props
的配置
注意:盡量使用props方法,少使用$route
巷嚣,使組件與路由解耦喘先,提高組件的復(fù)用率
-
props: true
布爾模式
作用:可以把要傳遞給要跳轉(zhuǎn)路由的參數(shù)轉(zhuǎn)化成props傳遞
好處:在組件中使用$route
會使之與對應(yīng)路由高度耦合,該組件只能在特定url(路由)下使用廷粒,組件單獨(dú)使用時this.$route
會不匹配窘拯,拿不到id
的值红且。使用props: true
則可以解決這個問題,通過props: ['id']
可以接收到外部傳來的id
的值涤姊,這其實就是一種解耦暇番。
上面的代碼就可以改成下面這樣:
// 在routes中配置參數(shù)
{
path: '/app/:id', // 配置參數(shù)
props: true,
component: Todo,
name: 'app',
meta: {
title: 'this is an app',
description: 'asddffg'
}
// 向要跳轉(zhuǎn)的路由傳參數(shù)(這個參數(shù)一般都是一個變量)
<router-link to="/app/123">app</router-link>
//接收到參數(shù)的路由怎么使用參數(shù)?
props: ['id']
mounted () {
console.log(this.id)
}
// 注意:如果是命名視圖思喊,props要包一層
components:{
default: Todo,
a: login
},
props: {
default: true,
a: true
}
-
props
對象模式
作用:如果 props 是一個對象壁酬,它會被按原樣設(shè)置為組件屬性。當(dāng) props 是靜態(tài)的時候有用
props: {
id: '456' // 不會變化
}
-
props
函數(shù)模式
props: (route) => ({ query: route.query.a, id: route.query.b })
接收route參數(shù)恨课,返回一個對象
Ⅳ舆乔、vue-router的一些高級用法
1. 命名視圖
作用:在router-view
上設(shè)置好名字,這樣可以根據(jù)我們的需求在同一個路由下顯示不同的組件
使用場景:可以同時展示多個視圖剂公,而不用嵌套希俩。例如創(chuàng)建一個布局,有 sidebar
(側(cè)導(dǎo)航) 和 main
(主內(nèi)容) 兩個視圖诬留,這個時候命名視圖就派上用場了斜纪。你可以在界面中擁有多個單獨(dú)命名的視圖,而不是只有一個單獨(dú)的出口文兑。如果 router-view
沒有設(shè)置名字盒刚,那么默認(rèn)為 default
。
配置和使用:
// 在routes中配置
{
path: '/login',
components: {
default: Login,
a: Todo
}
}
// 在組件中使用
<transition name="fade">
<router-view></router-view>
</transition>
<Footer></Footer>
<router-view name='a'></router-view>
2. vue-router
之導(dǎo)航守衛(wèi)
路由守衛(wèi)
全局守衛(wèi)
---對所有的路由有效果绿贞,只要路由跳轉(zhuǎn)因块,就會觸發(fā)
- 全局前置守衛(wèi)
作用:對一些頁面進(jìn)行校驗,比如:驗證一些頁面是需要用戶登錄才可以顯示的
router.beforeEach((to, from, next) => {
console.log('before each invoked')
if (to.fullPath === '/app') {
next('/login')
} else {
next()
}
})
- 全局解析守衛(wèi)
router.beforeResolve((to, from, next) => {
console.log('before resolved invoked')
next()
})
- 全局后置鉤子
router.afterEach((to, from) => {
console.log('after each invoked')
})
路由內(nèi)守衛(wèi)
---只對該路由有效果
- 路由獨(dú)享的守衛(wèi)
// 在routes中配置
beforeEnter: (to, from, next) => {
console.log('before enter route')
next()
}
組件內(nèi)守衛(wèi)
---對本組件進(jìn)入籍铁,離開涡上,以及組件的復(fù)用有效果
-
beforeRouteEnter
無法拿到this
,可以在next方法里面執(zhí)行一個回調(diào)拿到當(dāng)前組件
beforeRouteEnter (to, from, next) {
console.log('befor route enter')
next(vm => {
console.log(vm.id)
})
}
-
beforeRouteUpdate
執(zhí)行:同一個組件在不同的路由下面顯示的時候拒名。比如:/app/123和/app/456
可以拿到this
beforeRouteUpdate (to, from, next) {
console.log('before route update')
next()
}
注意:當(dāng)我們在兩個相似路徑(/app/123和/app/456
)跳轉(zhuǎn)時吩愧,第二次還會不會觸發(fā)mounted
鉤子呢?
不會增显,當(dāng)我們進(jìn)入/app/123
時雁佳,mounted
鉤子執(zhí)行,這時跳到/app/456
鉤子同云,mounted
鉤子就不再執(zhí)行糖权。所以我們不要把根據(jù)id
變化而變化的數(shù)據(jù)寫在mounted
里面,可以寫在beforeRouteUpdate
里面炸站,也可以寫在watch
里面
-
beforeRouteLeave
使用場景:控制頁面離開行為星澳。比如你修改了一個表單,還沒有保存旱易,現(xiàn)在你要跳到其他頁面禁偎,可以在這里給你設(shè)置一個提醒
beforeRouteLeave (to, from, next) {
console.log('before route leave')
if (global.confirm('are you sure?')) {
next()
}
}
3. 異步組件實現(xiàn)按需加載
作用:每次加載頁面腿堤,就要把所有的業(yè)務(wù)邏輯代碼加載一下,若是項目比較大届垫,勢必會大大影響頁面的初始加載速度释液,若是app.js小于1mb以下,就沒有必要使用異步組件啦
異步組件不僅可以在路由中使用装处,在頁面組件中中只要引入組件的地方都可以使用
// 在路由routes 引入
component: () => import('../views/todo/todo.vue')
component: () => import('../views/login/login.vue')
// 安裝 `babel-plugin-syntax-dynamic-import` 插件
npm i babel-plugin-syntax-dynamic-import
// 在 `babelrc`中配置
"plugins": [
"transform-vue-jsx", // 解析 jsx 語法
"syntax-dynamic-import" // 異步加載組件
]
五误债、vuex的學(xué)習(xí)
Ⅰ、vuex在項目中的使用
// 安裝
npm install vuex --save
// 在 src 目錄下建立store目錄妄迁,建立store.js 文件
// store.js 文件配置
import 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
// 在入口文件中引入 store 寝蹈,注冊
// 因為vue是一個樹形結(jié)構(gòu),只有把 store 注冊到入口文件根節(jié)點(diǎn)上登淘,下面那些子節(jié)點(diǎn)才都可以拿到 store對象
在組件中使用store
拿到state中的值
computed: {
count () {
return this.$store.state.count
}
}
// 通過mutations修改state中的值
mounted () {
let i = 0
console.log(this.$store)
setInterval(() => {
this.$store.commit('updateCount', i++)
}, 1000)
}
在大型項目或者可擴(kuò)展項目中可采用一下目錄結(jié)構(gòu)箫老,把每個模塊都拆分出來形成單獨(dú)的目錄
Ⅱ、getters的使用
作用:getters的作用就是對state中的原始數(shù)據(jù)做一些處理和封裝黔州,可以讓我們在組件中更好的使用耍鬓,相當(dāng)于組件中的computed
很多時候,后端提供我們的數(shù)據(jù)我們不能直接使用流妻,當(dāng)然我們可以拿到數(shù)據(jù)在組件中通過computed進(jìn)行處理牲蜀,但是很多頁面都需要處理數(shù)據(jù),每個頁面都寫computed就會造成代碼冗余绅这,維護(hù)也困難涣达,getters可以幫我們解決這個問題
export default {
fullName (state) {
return `${state.firstName} ${state.lastName}`
}
}
Ⅲ、map 語法在state和getters中的使用
作用:使組件中屬性方法和store中模塊的屬性方法形成一個映射证薇,可以直接把state和getters中的值拿過來
注意:mapState和mapGetters
是訪問度苔,拿到數(shù)據(jù),所以在組件的計算屬性 computed
里面混入
// 首先安裝一個轉(zhuǎn)義器包浑度,是Babel用來翻譯最新的語法的
npm i babel-present-stage-1 -D
// 在 .babelrc 文件中配置
{
"presets": [
"env", // babel-present-env 主要對javascript最新的語法糖進(jìn)行編譯寇窑,并不負(fù)責(zé)轉(zhuǎn)譯javascript新增的api和全局對象
"stage-1" // babel-preset-stage-1,轉(zhuǎn)義器包箩张,要配合env使用疗认,包含一些插件,可以識別最新語法
],
"plugins": [
"transform-vue-jsx", // 解析 jsx 語法
"syntax-dynamic-import" // 異步加載組件
]
}
// 現(xiàn)在就可以在組件中使用 對象展開運(yùn)算符 啦
import { mapState, mapGetters } from 'vuex'
...mapState(['count']) // 映射 this.count 為 store.state.count
...maoState({
counter: 'count'
})
...mapState({
counter: (state) => state.counte // 通過函數(shù)可以對拿到的數(shù)據(jù)做一些處理
})
...mapGetters(['fullName'])
Ⅳ伏钠、Vuex之mutations和actions
1. mutations
代碼演示:
// mutations.js
export default { // 只有兩個參數(shù)
updateCount (state, num) {
state.count = num
},
firstName (state, num) {
state.firstName = num
}
}
// 多個數(shù)值的話
export default {
updateCount (state, {num, num2}) {
console.log(num2)
state.count = num
},
firstName (state, num) {
state.firstName = num
}
}
// 組件中
this.$store.commit('updateCount', { // payload,載荷
num: i++,
num2: 2
})
注意:state
中的值只能通過mutations
中修改,其實在組件中可以通過
this.$store.state.count = 2
也是可以修改的谨设,但是這樣修改的話熟掂,多人協(xié)作的話就會很困難,數(shù)據(jù)不利于維護(hù)扎拣。
所以我們一般會限制組件中直接修改 state
的值赴肚,做法是:
在開發(fā)環(huán)境下素跺,給 store
實例對象加上嚴(yán)格模式,這樣的話誉券,我們?nèi)绻诮M件中修改 state
指厌,會有警告
const isDev = process.env.NODE_ENV === 'development'
export default () => {
return new Vuex.Store({
strict: isDev, // 在開發(fā)環(huán)境的時候使用
state: defaultState,
mutations: mutations,
getters
})
}
2. actions
作用:處理異步操作,或者一個動作要多次修改mutations踊跟,往往也用actions封裝一下
export default {
updateCountAsync (ctx, data) { // context 上下文踩验, store 實例具有相同方法和屬性的 context 對象
setTimeout(() => {
ctx.commit('updateCount', {
num: data.num
})
}, data.time)
}
}
// 在組件中調(diào)用 actions,用 dispatch 方法
this.$store.dispatch('updateCountAsync', {
num: 5,
time: 2000
})
3. mapMutatios 和 mapActions
的用法
注意:這兩個map是操作商玫,所以要寫在 methods
里面
代碼如下:
methods: {
...mapActions(['updateCountAsync']), // 將 `this.updateCountAsync()` 映射為 `this.$store.dispatch('updateCountAsync')`
...mapMutations(['updateCount'])
}
// 現(xiàn)在代碼就可以改了
this.$store.dispatch('updateCountAsync', { // store 對象的方法
num: 5,
time: 2000
})
this.updateCountAsync({ // 組件自身的方法
num: 5,
time: 2000
})
Ⅴ箕憾、Vuex之模塊(module)
由于使用單一狀態(tài)樹,應(yīng)用的所有狀態(tài)會集中到一個比較大的對象拳昌。當(dāng)應(yīng)用變得非常復(fù)雜時袭异,store 對象就有可能變得相當(dāng)臃腫。
為了解決以上問題炬藤,Vuex 允許我們將 store 分割成模塊(module)御铃。每個模塊擁有自己的 state、mutation沈矿、action上真、getter、甚至是嵌套子模塊——從上至下進(jìn)行同樣方式的分割
1. 模塊中的 state
// 在 store.js 中的store對象中配置如下:
modules: {
a: {
state: {
text: 'a'
}
},
b: {
state: {
text: 'b'
}
}
}
// 在組件中調(diào)用
computed: {
textA () {
return this.$store.state.a.text
}
}
2. 模塊中的 mutations
注意: vuex默認(rèn)的會把全部的mutation都放在全局的命名空間中细睡,所以在不同的模塊也可以直接調(diào)用谷羞。
如果我們想把各個模塊的mutation就在自己的模塊,因為太多的mutation都放在全局溜徙,容易引發(fā)命名上的沖突湃缎,怎么辦?
我們可以給模塊加一個命名空間 namespaced: true,
modules: {
// namespaced: true,
a: {
state: {
text: 'a'
},
mutations: {
updateText (state, text) {
state.text = text
}
}
}
}
// 組件中使用
methods: {
...mapMutations([ 'updateText'])
}
mounted () {
this.updateText('123')
}
為模塊加上命名空間蠢壹,如何調(diào)用嗓违?
methods: {
...mapMutations([ 'a/updateText'])
}
mounted () {
this['a/updateText']('123')
}
3. 模塊中的 getters
modules: {
a: {
namespaced: true,
state: {
text: 'a'
},
mutations: {
updateText (state, text) {
state.text = text
}
},
getters: {
textPlus (state) {
console.log(state.text)
return state.text + 1
}
}
}
}
// 在項目中使用
computed: {
// ...mapGetters(['a/textPlus']), // this['a/textPlus']調(diào)用
...mapGetters({
fullName: 'fullName',
textPlus: 'a/textPlus' // 可以直接在模板中使用 {{textPlus}}
})
}
默認(rèn)情況下,模塊內(nèi)部的 action图贸、mutation 和 getter 是注冊在全局命名空間的蹂季,當(dāng)我們加上命名空間后,變成局部的疏日,此時我們?nèi)绾文玫饺值?state 和 getter 呢偿洁?
getters: {
textPlus (state, getters, rootState, rootGetters) {
// state 當(dāng)前模塊的state對象
// getters 當(dāng)前模塊的 getters 對象
// rootState rootGetters 全局的,不僅可以取到根模塊的值沟优,其他模塊也可以
// return state.text + rootState.count
return state.text + rootState.b.count
}
}
4. 模塊中的 actions
modules: {
a: {
namespaced: true,
state: {
text: 'a'
},
mutations: {
updateText (state, text) {
state.text = text
}
},
getters: {
textPlus (state, getters, rootState, rootGetters) {
console.log(state.text)
return state.text + rootState.b.text
}
},
actions: {
add (ctx) { // 上下文
ctx.commit('updateText', 8)
}
}
}
// 使用
methods: {
...mapActions(['updateCountAsync', 'a/add']),
...mapMutations(['updateCount', 'a/updateText'])
}
mounted () {
this['a/add']()
}
但是我們一般都用 ES6 的結(jié)構(gòu)賦值簡化
// 此時調(diào)用的 updateText 在 a 命名空間下涕滋,如果我們想要調(diào)取全局的mutation怎么辦?
actions: {
add ({ state, commit, rootState }) {
commit('updateText', rootState.count)
}
}
// 首先調(diào)用的mutation全局里面存在挠阁,加上參數(shù) {root:true}
actions: {
add ({ state, commit, rootState }) {
commit('updateCount', { num: '56789' }, { root: true })
}
}
// 模塊的actions調(diào)用其他模塊的mutation
// b 模塊沒有加命名空間
b: {
state: {
text: 'b'
},
actions: {
testAction ({ commit }) {
commit('a/updateText', 'test action')
}
}
}
methods: {
...mapActions(['updateCountAsync', 'a/add', 'testAction']),
...mapMutations(['updateCount', 'a/updateText'])
}
mounted () {
this.testAction()
}
// b 模塊加上命名空間呢
b: {
namespaced: true,
state: {
text: 'b'
},
actions: {
testAction ({ commit }) {
commit('a/updateText', 'test action', { root: true })
}
}
}
methods: {
...mapActions(['updateCountAsync', 'a/add', 'b/testAction']),
...mapMutations(['updateCount', 'a/updateText'])
}
mounted () {
this['b/testAction']()
}
5. 動態(tài)注冊模塊
// 在有store對象的位置宾肺,一般在入口文件里溯饵,注冊
const store = createStore()
store.registerModule('c', {
state: {
text: 'ccc'
}
})
store.unregisterModule('c') // 解綁一個model
// 使用方法與其他一樣
Ⅵ、 熱重載
當(dāng)我們修改store里面的代碼時锨用,我們發(fā)現(xiàn)都是刷新整個頁面進(jìn)行更新的, 在我們做webapp的時候丰刊,狀態(tài)經(jīng)常是變化的,如果因為修改了一下 store 中的數(shù)據(jù)增拥,刷新整個頁面啄巧,使得之前的操作記錄也會消失,浪費(fèi)時間
熱重載
export default () => {
const store = new Vuex.Store({
strict: isDev, // 在開發(fā)環(huán)境的時候使用
state: defaultState,
mutations: mutations,
getters,
actions
})
if (module.hot) {
module.hot.accept([
'./state/state',
'./mutations/mutation',
'./getters/getter',
'./actions/actions'
], () => {
const newState = require('./state/state').default
const newMutations = require('./mutations/mutation').default
const newGetters = require('./getters/getter').default
const newActions = require('./actions/actions').default
store.hotUpdate({
state: newState,
mutations: newMutations,
getters: newGetters,
actions: newActions
})
})
}
return store
}
Ⅶ跪者、vuex之其他一些API和配置
1. store.watch
第一個函數(shù)的返回值發(fā)生變化時棵帽,第二個函數(shù)就會執(zhí)行
store.watch((state) => state.count + 1, (newCount) => {
console.log(`newCount: ${newCount}`)
})
2. store.subscribe
(常用于插件)
監(jiān)聽mutations,調(diào)用的話渣玲,可以得到哪一個mutation變化逗概,以及變化的數(shù)據(jù)
store.subscribe((mutation, state) => {
console.log(mutation.type)
console.log(mutation.payload)
})
3. store.subscribeAction
(常用于插件)
store.subscribeAction((action, state) => {
console.log(action.type)
console.log(action.payload)
})
4. vuex插件的制作
plugins 在vuex初始化的時候就已經(jīng)執(zhí)行,所以可以在里面使用上面subscribe 和 subscribeAction
訂閱一些內(nèi)容忘衍,來進(jìn)行一些操作
export default () => {
const store = new Vuex.Store({
strict: isDev, // 在開發(fā)環(huán)境的時候使用
state: defaultState,
mutations: mutations,
getters,
actions,
plugins: [
(store) => {
console.log('my plugin invoked')
}
]
})