Vue
Vue是一個前端js框架和媳,由尤雨溪開發(fā)湖员,是個人項目
Vue近幾年來特別的受關(guān)注耗拓,三年前的時候angularJS霸占前端JS框架市場很長時間,接著react框架橫空出世荧恍,因為它有一個特性是虛擬DOM,從性能上碾軋angularJS屯吊,這個時候送巡,vue1.0悄悄的問世了,它的優(yōu)雅盒卸,輕便也吸引了一部分用戶骗爆,開始收到關(guān)注,16年中旬蔽介,VUE2.0問世摘投,這個時候vue不管從性能上,還是從成本上都隱隱超過了react虹蓄,火的一塌糊涂犀呼,這個時候,angular開發(fā)團隊也開發(fā)了angular2.0版本薇组,并且更名為angular外臂,吸收了react、vue的優(yōu)點律胀,加上angular本身的特點宋光,也吸引到很多用戶,目前已經(jīng)迭代到5.0了累铅。
學(xué)習(xí)vue是現(xiàn)在前端開發(fā)者必須的一個技能跃须。
前端js框架到底在干嘛,為什么要用
js框架幫助開發(fā)者寫js邏輯代碼娃兽,在開發(fā)應(yīng)用的時候js的功能劃分為如下幾點:
渲染數(shù)據(jù)
操作dom(寫一些效果)
操作cookie等存儲機制api
在前端開發(fā)中菇民,如何高效的操作dom、渲染數(shù)據(jù)是一個前端工程師需要考慮的問題投储,而且當數(shù)據(jù)量大第练,流向較亂的時候,如何正確使用數(shù)據(jù)玛荞,操作數(shù)據(jù)也是一個問題
而js框架對上述的幾個問題都有自己趨于完美的解決方案娇掏,開發(fā)成本降低。高性能高效率勋眯。唯一的缺點就是需要使用一定的成本來學(xué)習(xí)婴梧。
Vue官網(wǎng)介紹
vue是漸進式JavaScript框架
“漸進式框架”和“自底向上增量開發(fā)的設(shè)計”是Vue開發(fā)的兩個概念
Vue可以在任意其他類型的項目中使用下梢,使用成本較低,更靈活塞蹭,主張較弱孽江,在Vue的項目中也可以輕松融匯其他的技術(shù)來開發(fā),并且因為Vue的生態(tài)系統(tǒng)特別龐大番电,可以找到基本所有類型的工具在vue項目中使用
特點:易用(使用成本低)岗屏,靈活(生態(tài)系統(tǒng)完善,適用于任何規(guī)模的項目)漱办,高效(體積小这刷,優(yōu)化好,性能好)
Vue是一個MVVM的js框架娩井,但是,Vue 的核心庫只關(guān)注視圖層洞辣,開發(fā)者關(guān)注的只是m-v的映射關(guān)系
與AngularJS的對比
Vue的很多api率碾、特性都與angularJS相似,其實是因為Vue在開發(fā)的時候借鑒了很多AngularJS中的特點屋彪,而AngularJS中固有的缺點,在Vue中已經(jīng)解決绒尊,也就是青出于藍而勝于藍畜挥,Vue的學(xué)習(xí)成本比AngularJS低很多,因為復(fù)雜性就低
AngularJS是強主張的婴谱,而Vue更靈活
Vue的數(shù)據(jù)流是單向的蟹但,數(shù)據(jù)流行更清晰
Angular里指令可以是操作dom的,也可以封裝一段結(jié)構(gòu)邏輯代碼谭羔,例如:廣告展示模塊
Vue中的指令只是操作dom的华糖,用組件來分離結(jié)構(gòu)邏輯
AngularJS的性能比不上Vue
Vue的使用
Vue不支持IE8,因為使用了ES5的很多特性
可以直接通過script標簽來引入vue.js瘟裸,有開發(fā)版本和生產(chǎn)版本客叉,開發(fā)版本一般我們在開發(fā)項目的時候引入,當最后開發(fā)完成上線的時候引入生產(chǎn)版本话告,開發(fā)版本沒有壓縮的兼搏,并且有很多提示,而生產(chǎn)版本全部刪掉了
在Vue中提供了一個腳手架(命令行工具)可以幫我們快速的搭建基于webpack的開發(fā)環(huán)境...
Vue的實例
每一個應(yīng)用都有一個根實例沙郭,在根實例里我們通過組件嵌套來實現(xiàn)大型的應(yīng)用
也就是說組件不一定是必須的佛呻,但是實例是必須要有的
在實例化實例的時候我們可以傳入一個;配置項病线,在配置項中設(shè)置很多屬性方法可以實現(xiàn)復(fù)雜的功能
在配置中可以設(shè)置el的屬性,el屬性代表的是此實例的作用范圍
在配置中同過設(shè)置data屬性來為實例綁定數(shù)據(jù)
mvc/mvvm
mvc 分為三層宙枷,其實M層是數(shù)據(jù)模型層兄淫,它是真正的后端數(shù)據(jù)在前端js中的一個映射模型,他們的關(guān)系是:數(shù)據(jù)模型層和視圖層有映射關(guān)系暖眼,model改變,view展示也會更改紊撕,當view產(chǎn)生用戶操作或會反饋給controller罢荡,controller更改model,這個時候view又會進行新的數(shù)據(jù)渲染
[圖片上傳失敗...(image-c89b70-1566368488005)]
這是純純的MVC的模式对扶,但是很多框架都會有一些更改
前端mvc框架区赵,如angularjs,backbone:
[圖片上傳失敗...(image-d287dc-1566368488005)]
會發(fā)現(xiàn)浪南,用戶可以直接操作controller(例如用戶更改hash值笼才,conrtoller直接監(jiān)聽hash值變化后執(zhí)行邏輯代碼,然后通知model更改)
控制器可以直接操作view络凿,如果骡送,讓某一個標簽獲得進入頁面獲得焦點,不需要model來控制絮记,所以一般會直接操作(angularJS摔踱,指令)
view可以直接操作model (數(shù)據(jù)雙向綁定)
MVP:
view和model不能直接通信,所有的交互都由presenter來做怨愤,其他部分的通信都是雙向的
view較薄 派敷,presenter較為厚重
MVVM:
MVVM和MVP及其相似,只是view和viewmodel的通信是雙向綁定撰洗,view的操作會自動的像viewmodel通過
v-for
這是一個指令篮愉,只要有v-的就是指令(directive 操作dom )
在vue中可以通過v-for來循環(huán)數(shù)據(jù)的通知循環(huán)dom,語法是item in/of items差导,接收第二個參數(shù)是索引 (item,index) of items,還可以循環(huán)鍵值對试躏,第一個參數(shù)是value,第二個是key设褐,第三個依然是索引
v-on
在vue中還有v-on來為dom綁定事件颠蕴,在v-on:后面加上要綁定的事件類型,值里可以執(zhí)行一些簡單javascript表達式:++ -- = ...
可以將一些方法設(shè)置在methods里助析,這樣就可以在v-on:click的值里直接寫方法名字可以裁替,默認會在方法中傳入事件對象,當寫方法的時候加了()就可以傳參貌笨,這個時候如果需要事件對象弱判,那就主動傳入$event
v-on綁定的事件可以是任意事件,v-on:可以縮寫為@
為什么在 HTML 中監(jiān)聽事件?
你可能注意到這種事件監(jiān)聽的方式違背了關(guān)注點分離 (separation of concern) 這個長期以來的優(yōu)良傳統(tǒng)。但不必擔(dān)心锥惋,因為所有的 Vue.js 事件處理方法和表達式都嚴格綁定在當前視圖的 ViewModel 上昌腰,它不會導(dǎo)致任何維護上的困難开伏。實際上,使用 v-on 有幾個好處:
- 掃一眼 HTML 模板便能輕松定位在 JavaScript 代碼里對應(yīng)的方法遭商。
- 因為你無須在 JavaScript 里手動綁定事件固灵,你的 ViewModel 代碼可以是非常純粹的邏輯,和 DOM 完全解耦劫流,更易于測試巫玻。
- 當一個 ViewModel 被銷毀時,所有的事件處理器都會自動被刪除祠汇。你無須擔(dān)心如何自己清理它們仍秤。
模板語法
在vue中,我們使用mustache插值({{}})來將數(shù)據(jù)渲染在模板中
使用v-once指令可以控制只能插入一次值可很,當數(shù)據(jù)變化的時候诗力,模板對應(yīng)的視圖不更新
使用v-html指令可以解析html格式的數(shù)據(jù)
在html標簽屬性里不能使用mustache插值,這個時候給元素添加動態(tài)屬性的時候使用v-bind來綁定屬性我抠,可以縮寫成:
在使用v-bind綁定class和內(nèi)聯(lián)樣式的時候苇本,vue做了一些優(yōu)化,可以使用對象語法和數(shù)組的語法來控制
防止表達式閃爍:
-
v-cloak
給模板內(nèi)的元素添加v-cloak屬性后菜拓,元素在vue沒有加載完的時候就有這個屬性瓣窄,當vue加載完成后這個屬性就消失了,所以我們可以給這個屬性設(shè)置css樣式為隱藏
<style>
[v-cloak]{
visibility: hidden;
}
</style>
-
v-text/v-html
v-text會指定將模板內(nèi)元素的textContent屬性替換為指令值所代表的數(shù)據(jù)纳鼎,也可以用于防止閃爍
v-html可以解析標簽康栈,更改元素的innerHTML,性能比v-text較差 -
v-pre
跳過元素和其子元素的編譯過程喷橙,可以用來顯示mustache
vue-resource
這是一款vue的插件,可以用來進行數(shù)據(jù)交互登舞,支持的請求方式:GET/POST/JSONP/OPTIONS...
這個插件官方宣布不在更新維護贰逾,也就是說盡量不要使用
計算屬性、監(jiān)聽
有的時候我們需要在模板中使用數(shù)據(jù)a菠秒,這個時候就需要用到表達式疙剑,但是有的地方我們需要對a數(shù)據(jù)進行一些簡單的處理后才能使用,那么我們就會在表達式中寫一些js邏輯運算
```
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
```
這樣我們的維護就會非常困難践叠,也不便于閱讀
那め我們就可以在methods里設(shè)置一個方法言缤,在模板的表達式中使用這個方法
```
<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在組件中
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
```
但是這個時候,只要vm中有數(shù)據(jù)變化禁灼,這個變化的數(shù)據(jù)可能和我們關(guān)注的數(shù)據(jù)無關(guān)管挟,但是vm都會重新渲染模板,這個時候表達式中的方法就會重新執(zhí)行弄捕,大大的影響性能
這個時候其實我們可以使用監(jiān)聽器里完成:
在vm實例中設(shè)置watch屬性僻孝,在里面通過鍵值對來設(shè)置一些監(jiān)聽导帝,鍵名為數(shù)據(jù)名,值可以是一個函數(shù)穿铆,這個函數(shù)在數(shù)據(jù)改變之后才會執(zhí)行您单,兩個參數(shù)分別是性格前的值和更改后的值
a: function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
}
值還可以是一個方法名字,當數(shù)據(jù)改變的時候這個方法會執(zhí)行
當數(shù)據(jù)為object的時候荞雏,object的鍵值對改變不會被監(jiān)聽到(數(shù)組的push等方法可以),這個時候需要設(shè)置深度監(jiān)聽:
c: {
deep:true,
handler:function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
}
},
監(jiān)聽的handler函數(shù)前面的這幾種寫法都是在數(shù)據(jù)變化的時候才會執(zhí)行虐秦,初始化的時候不會執(zhí)行,但是如果設(shè)置immediate為true就可以了
num:{
immediate:true,
handler:function(val){
this.nums = val*2
}
}
我們在回到上面的問題凤优,用監(jiān)聽器加上immediate屬性就可以做到該效果悦陋,但是大家可以看到的是邏輯稍稍有點復(fù)雜
watch還可以通過實例對象直接使用:vm.$watch,返回一個取消監(jiān)聽的函數(shù)别洪,這個函數(shù)執(zhí)行之后會取消監(jiān)聽
我們一般都會用到一個叫計算屬性的東西來解決:
計算屬性就是在實例配置項中通過computed來為vm設(shè)置一個新的數(shù)據(jù)叨恨,而這個新數(shù)據(jù)會擁有一個依賴(一條已經(jīng)存在的數(shù)據(jù)),當依賴發(fā)送變化的時候挖垛,新數(shù)據(jù)也會發(fā)送變化
與方法的方式相比痒钝,它性能更高,計算屬性是基于它們的依賴進行緩存的痢毒。計算屬性只有在它的相關(guān)依賴發(fā)生改變時才會重新求值送矩。相比之下,每當觸發(fā)重新渲染時哪替,調(diào)用方法將總會再次執(zhí)行函數(shù)栋荸。
與watch相比,寫起來簡單凭舶,邏輯性更清晰晌块,watch一般多用于,根據(jù)數(shù)據(jù)的變化而執(zhí)行某些動作帅霜,而至于這些動作是在干什么其實無所謂匆背,而計算屬性更有針對性,根據(jù)數(shù)據(jù)變化而更改另一個數(shù)據(jù)
計算屬性也擁有g(shù)etter和setter身冀,默認寫的是getter钝尸,設(shè)置setter執(zhí)行可以當此計算屬性數(shù)據(jù)更改的時候去做其他的一些事情,相當于watch這個計算屬性
xm:{
get:function(){//getter 當依賴改變后設(shè)置值的時候
return this.xing+'丶'+this.ming
},
set:function(val){//setter 當自身改變后執(zhí)行
this.xing = val.split('丶')[0]
this.ming = val.split('丶')[1]
}
}
過濾器
vue中可以設(shè)置filter(過濾器)來實現(xiàn)數(shù)據(jù)格式化搂根,雙花括號插值和 v-bind 表達式中使用
vue1.0的有默認的過濾器珍促,但是在2.0的時候全部給去掉了
所以在vue中如果想要使用過濾器就需要自定義
自定義的方法有兩種:全局定義和局部定義,全局定義的過濾器在任意的實例剩愧、組件中都可以使用猪叙,局部定義就是在實例、組件中定義,只能在這個實例或組件中使用
-
全局定義
Vue.filter(name,handler)
name是過濾器的名字沐悦,handler是數(shù)據(jù)格式化處理函數(shù)成洗,接收的第一個參數(shù)就是要處理的數(shù)據(jù),返回什么數(shù)據(jù)藏否,格式化的結(jié)果就是什么
在模板中通過 | (管道符) 來使用,在過濾器名字后面加()來傳參瓶殃,參數(shù)會在handler函數(shù)中第二個及后面的形參來接收
<p>{{msg | firstUpper(3,2)}}</p>
Vue.filter('firstUpper',function (value,num=1,num2) {
console.log(num2)
return value.substr(0,num).toUpperCase()+value.substr(num).toLowerCase()
})
-
局部定義
在實例、組件的配置項中設(shè)置 filters副签,鍵名為過濾器名遥椿,值為handler
filters:{
firstUpper:function (value,num=1,num2) {
console.log(num2)
return value.substr(0,num).toUpperCase()+value.substr(num).toLowerCase()
}
}
注意:
過濾器只能在mustache插值、v-bind里使用淆储,其他的指令等地方都不能用
作業(yè):自定義類似于angularJS中的currency冠场、order、filter過濾器
條件渲染
在Vue中可以使用v-if來控制模板里元素的顯示和隱藏本砰,值為true就顯示碴裙,為false就隱藏
v-if控制的是是否渲染這個節(jié)點
當我們需要控制一組元素顯示隱藏的時候,可以用template標簽將其包裹点额,將指令設(shè)置在template上舔株,等等vm渲染這一組元素的時候,不會渲染template
當有else分支邏輯的時候还棱,可以給該元素加上v-else指令來控制载慈,v-else會根據(jù)上面的那個v-if來控制,效果與v-if相反珍手,注意办铡,一定要緊挨著
還有v-else-if指令可以實現(xiàn)多分支邏輯
<input type="text" v-model="mode">
<template v-if="mode=='A'">
<h1>1.title</h1>
<p>我的第一個P標簽</p>
</template>
<template v-else-if="mode=='B'">
<h1>2.title</h1>
<p>我的第二個P標簽</p>
</template>
<template v-else-if="mode=='C'">
<h1>3.title</h1>
<p>我的第三個P標簽</p>
</template>
<template v-else>
<p>不好意思,輸入有誤</p>
</template>
需要注意的另一個地方是:Vue 會盡可能高效地渲染元素琳要,通常會復(fù)用已有元素而不是從頭開始渲染寡具。這樣確實能使Vue變得更快,性能更高稚补,但是有的時候我們需要讓實例去更新dom而不是復(fù)用童叠,就需要給dom加上不同的key屬性,因為vue在判斷到底渲染什么的時候孔厉,包括哪些dom可以復(fù)用,都會參考key值帖努,如果dom表現(xiàn)基本一致撰豺,符合復(fù)用的條件,但是key值不同拼余,依然不會復(fù)用
Vue還提供了v-show指令污桦,用法和v-if基本一樣,控制的是元素的css中display屬性匙监,從而控制元素的顯示和隱藏 凡橱, 不能和v-else配合使用,且不能使用在template標簽上小作,因為template不會渲染,再更改它的css屬性也不會渲染稼钩,不會生效
v-if vs v-show
v-if 是“真正”的條件渲染顾稀,因為它會確保在切換過程中條件塊內(nèi)的事件監(jiān)聽器和子組件適當?shù)乇讳N毀和重建。
v-if 也是惰性的:如果在初始渲染時條件為假坝撑,則什么也不做——直到條件第一次變?yōu)檎鏁r静秆,才會開始渲染條件塊。
相比之下巡李,v-show 就簡單得多——不管初始條件是什么抚笔,元素總是會被渲染,并且只是簡單地基于 CSS 進行切換侨拦。
一般來說殊橙,v-if 有更高的切換開銷,而 v-show 有更高的初始渲染開銷狱从。因此膨蛮,如果需要非常頻繁地切換,則使用 v-show 較好矫夯;如果在運行時條件很少改變鸽疾,則使用 v-if 較好。
mixin
在Vue中训貌,我們可以通過定義多個mixin來實現(xiàn)代碼抽離復(fù)用制肮,便于維護,提升頁面的邏輯性
要注意的是:data屬性不要使用mixin递沪,因為從邏輯上來說豺鼻,每一個實例、組件的數(shù)據(jù)都應(yīng)該是獨立的
一個mixin其實就是一個純粹的對象款慨,上面掛載著抽離出來的配置儒飒,在某一個實例中,通過mixins選項(數(shù)組)導(dǎo)入后檩奠,此實例就擁有導(dǎo)入的mixin的配置
且導(dǎo)入的配置不會覆蓋原有的桩了,而是合并到一起
虛擬dom
頻繁且復(fù)雜的dom操作通常是前端性能瓶頸的產(chǎn)生點,Vue提供了虛擬dom的解決辦法
虛擬的DOM的核心思想是:對復(fù)雜的文檔DOM結(jié)構(gòu)埠戳,提供一種方便的工具井誉,進行最小化地DOM操作。這句話整胃,也許過于抽象颗圣,卻基本概況了虛擬DOM的設(shè)計思想
(1) 提供一種方便的工具,使得開發(fā)效率得到保證
(2) 保證最小化的DOM操作,使得執(zhí)行效率得到保證
也就是說在岂,虛擬dom的框架/工具都是這么做的:
- 根據(jù)現(xiàn)有的真實dom來生成一個完整的虛擬dom樹結(jié)構(gòu)
- 當數(shù)據(jù)變化奔则,或者說是頁面需要重新渲染的時候,會重新生成一個新的完整的虛擬dom
- 拿新的虛擬dom來和舊的虛擬dom做對比(使用diff算法)蔽午,易茬。得到需要更新的地方之后,更新內(nèi)容
這樣的話祠丝,就能大量減少真實dom的操作,提高性能
組件化
模塊化就是將系統(tǒng)功能分離成獨立的功能部分的方法疾呻,一般指的是單個的某一種東西,例如js写半、css
而組件化針對的是頁面中的整個完整的功能模塊劃分,組件是一個html、css蜕该、js萤悴、image等外鏈資源,這些部分組成的一個聚合體
優(yōu)點:代碼復(fù)用拌倍,便于維護
劃分組件的原則:復(fù)用率高的,獨立性強的
組件應(yīng)該擁有的特性:可組合眼俊,可重用遮晚,可測試,可維護
組件
在vue中法绵,我們通過Vue.extend來創(chuàng)建Vue的子類,這個東西其實就是組件
也就是說Vue實例和組件的實例有差別但是差別不帶,因為畢竟一個是父類一個是子類
一般的應(yīng)用喷市,會擁有一個根實例,在根實例里面都是一個一個的組件
因為組件是要嵌入到實例或者父組件里的附迷,也就是說惧互,組件可以互相嵌套,而且喇伯,所有的組件最外層必須有一個根實例喊儡,所以組件分為:全局組件和局部組件
全局組件在任意的實例、父級組件中都能使用稻据,局部組件只能在創(chuàng)建自己的父級組件或者實例中使用
組件通過不同的注冊方法成為全局艾猜、局部組件
創(chuàng)建組件:
Vue.extend(options)
全局注冊:
var App = Vue.extend({
template:"<h1>hello world</h1>"
})
Vue.component('my-app',App)
簡便寫法:
// 創(chuàng)建組件構(gòu)造器和注冊組件合并一起
Vue.component('hello',{//Vue會自動的將此對象給Vue.extend
template:"<h1>hello</h1>"
})
組件通過template來確定自己的模板,template里的模板必須有根節(jié)點,標簽必須閉合
組件的屬性掛載通過:data方法來返回一個對象作為組件的屬性捻悯,這樣做的目的是為了每一個組件實例都擁有獨立的data屬性
局部注冊:
new Vue({
el:"#app",
components:{
'my-app':App
}
})
簡便寫法:
data:{},
components:{
'hello':{
template:"<h1>asdasdasdasdasdas</h1>"
}
}
在實例或者組件中注冊另一個組件匆赃,這個時候,被注冊的組件只能在注冊它的實例或組件的模板中使用今缚,一個組件可以被多個組件或?qū)嵗?/p>
注意瀏覽器規(guī)則
因為vue在解析模板的時候會根據(jù)某些html的規(guī)則算柳,例如,在table里只能放tr,td,th..姓言,如果放入組件不會解析 這個時候我們可以放入tr使用is方式來標識這個tr其實是組件
<table id="app">
<tr is="hello"></tr>
</table>
template
我們可以在html的某個地方通過template標簽來定義組件的模板瞬项,在組件的template屬性中通過選擇器指定對應(yīng)的template標簽內(nèi)容就可以了,注意蔗蹋,需要給template標簽加id來指定
<template id="my-hello">
<div>
<h1>hello world</h1>
<p>hahahah</p>
</div>
</template>
//組件中
template:"#my-hello"
is切換
在實例、組件的模板中的某一個標簽上囱淋,可以通過is屬性來指定為另一個目標的組件纸颜,這個時候我們一般會使用component標簽來占位、設(shè)置is屬性來指定目標組件
<component :is="type"></component>
//組件中
data:{
type:'aaa'
},
components:{
'aaa':{template:"<h1>AAAAAAAAAAAAA</h1>"},
'bbb':{template:"<h1>BBBBBBBBBBBBB</h1>"}
}
組件嵌套
應(yīng)用中劃分的組件可能會很多绎橘,為了更好的實現(xiàn)代碼復(fù)用,所以必然會存在組件的嵌套關(guān)系
組件設(shè)計初衷就是要配合使用的唠倦,最常見的就是形成父子組件的關(guān)系:組件 A 在它的模板中使用了組件 B称鳞。
prop 傳遞數(shù)據(jù)
組件實例的作用域是孤立的,父組件不能直接使用子組件的數(shù)據(jù),子組件也不能直接使用父組件的數(shù)據(jù)
父組件在模板中使用子組件的時候可以給子組件傳遞數(shù)據(jù)
<bbb money="2"></bbb>
子組件需要通過props屬性來接收后才能使用
'bbb':{
props:['money']
如果父組件傳遞屬性給子組件的時候鍵名有'-'稠鼻,子組件接收的時候?qū)懗尚●劮宓哪J?/p>
<bbb clothes-logo='amani' clothes-price="16.58"></bbb>
////
props:['clothesLogo','clothesPrice']
我們可以用 v-bind 來動態(tài)地將 prop 綁定到父組件的數(shù)據(jù)冈止。每當父組件的數(shù)據(jù)變化時,該變化也會傳導(dǎo)給子組件
單向數(shù)據(jù)流
Prop 是單向綁定的:當父組件的屬性變化時候齿,將傳導(dǎo)給子組件熙暴,但是反過來不會。這是為了防止子組件無意間修改了父組件的狀態(tài)慌盯,來避免應(yīng)用的數(shù)據(jù)流變得難以理解周霉。
另外,每次父組件更新時亚皂,子組件的所有 prop 都會更新為最新值俱箱。這意味著你不應(yīng)該在子組件內(nèi)部改變 prop。如果你這么做了灭必,Vue 會在控制臺給出警告狞谱。
在兩種情況下,我們很容易忍不住想去修改 prop 中數(shù)據(jù):
Prop 作為初始值傳入后禁漓,子組件想把它當作局部數(shù)據(jù)來用跟衅;
Prop 作為原始數(shù)據(jù)傳入,由子組件處理成其它數(shù)據(jù)輸出播歼。
對這兩種情況伶跷,正確的應(yīng)對方式是:
定義一個局部變量,并用 prop 的值初始化它:
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
//定義一個計算屬性秘狞,處理 prop 的值并返回:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
注意在 JavaScript 中對象和數(shù)組是引用類型撩穿,指向同一個內(nèi)存空間,如果 prop 是一個對象或數(shù)組谒撼,在子組件內(nèi)部改變它會影響父組件的狀態(tài)食寡。
prop驗證
我們可以為組件的 prop 指定驗證規(guī)則。如果傳入的數(shù)據(jù)不符合要求廓潜,Vue 會發(fā)出警告抵皱。這對于開發(fā)給他人使用的組件非常有用
驗證主要分為:類型驗證善榛、必傳驗證、默認值設(shè)置呻畸、自定義驗證
props:{
//類型驗證:
str:String,
strs:[String,Number],
//必傳驗證
num:{
type:Number,
required:true
},
//默認數(shù)據(jù)
bool:{
type:Boolean,
// default:true,
default:function(){
return true
}
},
//自定義驗證函數(shù)
nums:{
type:Number,
validator: function (value) {
return value %2 == 0
}
}
}
當父組件傳遞數(shù)據(jù)給子組件的時候移盆,子組件不接收,這個數(shù)據(jù)就會掛載在子組件的模板的根節(jié)點上
slot
vue里提供了一種將父組件的內(nèi)容和子組件的模板整合的方法:內(nèi)容分發(fā)伤为,通過slot插槽來實現(xiàn)
- 匿名插槽
<aaa>abc</aaa>
template:"<h1><slot></slot></h1>"
在父組件中使用子組件的時候咒循,在子組件標簽內(nèi)部寫的內(nèi)容,在子組件的模板中可以通過<slot></slot>來使用
- 具名插槽
父組件在子組件標簽內(nèi)寫的多個內(nèi)容我們可以給其設(shè)置slot屬性來命名绞愚,在子組件的模板通過通過使用帶有name屬性的slot標簽來放置對應(yīng)的slot叙甸,當slot不存在的時候,slot標簽內(nèi)寫的內(nèi)容就出現(xiàn)
<my-button>提交</my-button>
<my-button>重置</my-button>
<my-button></my-button>
template:"<button><slot>按鈕</slot></button>"
transition
Vue提供了transition組件來幫助我們實現(xiàn)過渡效果位衩,依據(jù)就是在控制元素顯示隱藏的時候為dom在指定的時刻添加上對應(yīng)的類名
而我們只要在這些類名里寫上對應(yīng)的css樣式
在進入/離開的過渡中裆蒸,會有 6 個 class 切換(v代表的是transition的name屬性的值)。
v-enter:定義進入過渡的開始狀態(tài)糖驴。在元素被插入時生效僚祷,在下一個幀移除。
v-enter-active:定義過渡的狀態(tài)贮缕。在元素整個過渡過程中作用辙谜,在元素被插入時生效,在 transition/animation 完成之后移除感昼。這個類可以被用來定義過渡的過程時間筷弦,延遲和曲線函數(shù)。
v-enter-to: 2.1.8版及以上 定義進入過渡的結(jié)束狀態(tài)抑诸。在元素被插入一幀后生效 (于此同時 v-enter 被刪除)烂琴,在 transition/animation 完成之后移除。
v-leave: 定義離開過渡的開始狀態(tài)蜕乡。在離開過渡被觸發(fā)時生效奸绷,在下一個幀移除。
v-leave-active:定義過渡的狀態(tài)层玲。在元素整個過渡過程中作用号醉,在離開過渡被觸發(fā)后立即生效,在 transition/animation 完成之后移除辛块。這個類可以被用來定義過渡的過程時間畔派,延遲和曲線函數(shù)。
v-leave-to: 2.1.8版及以上 定義離開過渡的結(jié)束狀態(tài)润绵。在離開過渡被觸發(fā)一幀后生效 (于此同時 v-leave 被刪除)线椰,在 transition/animation 完成之后移除。
如果有多個元素需要用transition-group包裹尘盼,并且需要有key值做標記
animate.css:
引入animate.css之后憨愉,按照下面的寫法:
<transition
leave-active-class="animated fadeOut"
enter-active-class="animated slideInLeft">
<p v-if="isShow" class="box"></p>
</transition>
渲染函數(shù)和jsx
在vue中我們可以不用template來指定組件的模板烦绳,而是用render函數(shù)來創(chuàng)建虛擬dom結(jié)構(gòu),用這種方法優(yōu)點就是性能高配紫,缺點就是使用成本高径密,代碼可讀性較低,可以使用jsx來在render函數(shù)中創(chuàng)建躺孝,這樣既提高了性能享扔,又減少了成本
但是,我們在使用了vue-cli腳手架之后植袍,因為腳手架中有對template標簽轉(zhuǎn)換虛擬dom的處理惧眠,所以,不需要使用jsx奋单,我們也能高效的轉(zhuǎn)換為createElement形式
Vue里組件的通信
通信:傳參、控制(A操控B做一個事件)猫十、數(shù)據(jù)共享
模式:父子組件間览濒、非父子組件
-
父組件可以將一條數(shù)據(jù)傳遞給子組件,這條數(shù)據(jù)可以是動態(tài)的拖云,父組件的數(shù)據(jù)更改的時候贷笛,子組件接收的也會變化
子組件被動的接收父組件的數(shù)據(jù),子組件不要再更改這條數(shù)據(jù)了
-
父組件如果將一個引用類型的動態(tài)數(shù)據(jù)傳遞給子組價的時候,數(shù)據(jù)會變成雙向控制的宙项,子組件改數(shù)據(jù)的時候父組件也能接收到數(shù)據(jù)變化,因為子組件改的時候不是在改數(shù)據(jù)乏苦,而是在改數(shù)據(jù)里的內(nèi)容,也就是說引用類型數(shù)據(jù)的地址始終沒有變化尤筐,不算改父組件數(shù)據(jù)
父子間數(shù)據(jù)共享(雙向控制)
-
父組件可以將一個方法傳遞給子組件汇荐,子組件調(diào)用這個方法的時候,就可以給父組件傳遞數(shù)據(jù)
父組件被動的接收子組件的數(shù)據(jù)
-
父組件可以將一個事件綁定在子組件的身上盆繁,這個事件的處理程序是父組件某一個方法掀淘,當子組件觸發(fā)自己的這個被綁定的事件的時候躺坟,相當于觸發(fā)了父組件的方法
父組件被動的接收子組件的數(shù)據(jù)
-
在組件間可以用過ref形成ref鏈觅彰,組件還擁有一個關(guān)系鏈(
children,$root),通過這兩種鏈跃闹;理論來說垄懂,任意的兩個組件都可以互相訪問易猫,互相進行通信
任意組件通信猩系,用的少...
-
event bus 事件總線 小天使 專注于非父子組件的通信寻仗,其實父子組件也可以使用员寇,只是沒有必要
在B組件的某個鉤子函數(shù)為event_bus綁定一個事件安寺,事件的處理程序是B想做的事情
在A組件的某一個操作里厕妖,觸發(fā)event_bus綁定的事件
- 大量組件間數(shù)據(jù)共享的時候 vuex
組件的生命周期
每一個組件或者實例都會經(jīng)歷一個完整的生命周期,總共分為三個階段:初始化挑庶、運行中叹放、銷毀
實例饰恕、組件通過new Vue() 創(chuàng)建出來之后會初始化事件和生命周期,然后就會執(zhí)行beforeCreate鉤子函數(shù)井仰,這個時候埋嵌,數(shù)據(jù)還沒有掛載ね,只是一個空殼俱恶,無法訪問到數(shù)據(jù)和真實的dom雹嗦,一般不做操作
掛載數(shù)據(jù),綁定事件等等合是,然后執(zhí)行created函數(shù)了罪,這個時候已經(jīng)可以使用到數(shù)據(jù),也可以更改數(shù)據(jù),在這里更改數(shù)據(jù)不會觸發(fā)updated函數(shù)聪全,在這里可以在渲染前倒數(shù)第二次更改數(shù)據(jù)的機會泊藕,不會觸發(fā)其他的鉤子函數(shù),一般可以在這里做初始數(shù)據(jù)的獲取
接下來開始找實例或者組件對應(yīng)的模板难礼,編譯模板為虛擬dom放入到render函數(shù)中準備渲染娃圆,然后執(zhí)行beforeMount鉤子函數(shù),在這個函數(shù)中虛擬dom已經(jīng)創(chuàng)建完成蛾茉,馬上就要渲染,在這里也可以更改數(shù)據(jù)讼呢,不會觸發(fā)updated,在這里可以在渲染前最后一次更改數(shù)據(jù)的機會谦炬,不會觸發(fā)其他的鉤子函數(shù)悦屏,一般可以在這里做初始數(shù)據(jù)的獲取
接下來開始render,渲染出真實dom键思,然后執(zhí)行mounted鉤子函數(shù)础爬,此時,組件已經(jīng)出現(xiàn)在頁面中吼鳞,數(shù)據(jù)幕帆、真實dom都已經(jīng)處理好了,事件都已經(jīng)掛載好了,可以在這里操作真實dom等事情...
當組件或?qū)嵗臄?shù)據(jù)更改之后赖条,會立即執(zhí)行beforeUpdate失乾,然后vue的虛擬dom機制會重新構(gòu)建虛擬dom與上一次的虛擬dom樹利用diff算法進行對比之后重新渲染,一般不做什么事兒
當更新完成后纬乍,執(zhí)行updated碱茁,數(shù)據(jù)已經(jīng)更改完成,dom也重新render完成仿贬,可以操作更新后的虛擬dom
當經(jīng)過某種途徑調(diào)用$destroy方法后纽竣,立即執(zhí)行beforeDestroy,一般在這里做一些善后工作,例如清除計時器蜓氨、清除非指令綁定的事件等等
組件的數(shù)據(jù)綁定聋袋、監(jiān)聽...去掉后只剩下dom空殼,這個時候穴吹,執(zhí)行destroyed幽勒,在這里做善后工作也可以
vue-cli腳手架
現(xiàn)在使用前端工程化開發(fā)項目是主流的趨勢,也就是說港令,我們需要使用一些工具來搭建vue的開發(fā)環(huán)境啥容,一般情況下我們使用webpack來搭建,在這里我們直接使用vue官方提供的顷霹,基于webpack的腳手架工具:vue-cli
安裝方法:
# 全局安裝 vue-cli
npm install --global vue-cli
# 創(chuàng)建一個基于 webpack 模板的新項目
vue init webpack my-project
//init之后可以定義模板的類型
# 安裝依賴咪惠,走你
cd my-project
npm install
npm run dev
模板類型:
simple 對應(yīng)的是一個超級簡單的html文件
webpack 在配置的時候可以選擇是否需要vue-router
注意的是,模板創(chuàng)建的時候會詢問使用需要使用ESLINT來標準化我們的代碼
在腳手架中淋淀,開發(fā)目錄是src文件夾遥昧,build負責(zé)打包的,config是負責(zé)配置(內(nèi)置服務(wù)器的端口朵纷、proxy代理)炭臭,static是靜態(tài)目錄,test是測試
src中main.js是入口文件柴罐,在里面創(chuàng)建了一個根實例徽缚,根實例的模板就是根組件App的模板憨奸,其他的組件都在根組件里面進行嵌套實現(xiàn)革屠。
每一個組件都是一個單文件組件,這種文件會被webpack利用vue-loader的工具進行編譯
template部分負責(zé)寫組件的模板內(nèi)容排宰,script中創(chuàng)建組件似芝。style里寫組件的樣式
assets目錄也是靜態(tài)目錄,在這個目標中的文件我們使用相對路徑引入,而static目錄中的文件使用絕對地址來引入
在style上添加scoped能使這個style里的樣式只作用于當前的組件板甘,不加scoped就是全局樣式
習(xí)慣于在App.vue根組件的style里寫全局樣式党瓮,而每個組件的style最好都是局部的
配置sass編譯環(huán)境
vue-cli沒有內(nèi)置sass編譯,我們需要自己修改配置
下載對應(yīng)工具:node-sass(4.0.0) sass-loader
在build目錄下的webpack.base.conf.js中的module.rule里添加如下配置
{
test: /\.scss$/,
loader:'style-loader!css-loader!sass-loader'
}
- 在需要使用scss代碼的組件的style標簽中添加 lang='scss'
vue-router
現(xiàn)在的應(yīng)用都流行SPA應(yīng)用(single page application)
傳統(tǒng)的項目大多使用多頁面結(jié)構(gòu)盐类,需要切換內(nèi)容的時候我們往往會進行單個html文件的跳轉(zhuǎn)寞奸,這個時候受網(wǎng)絡(luò)、性能影響在跳,瀏覽器會出現(xiàn)不定時間的空白界面枪萄,用戶體驗不好
單頁面應(yīng)用就是用戶通過某些操作更改地址欄url之后,動態(tài)的進行不同模板內(nèi)容的無刷新切換猫妙,用戶體驗好瓷翻。
Vue中會使用官方提供的vue-router插件來使用單頁面,原理就是通過檢測地址欄變化后將對應(yīng)的路由組件進行切換(卸載和安裝)
簡單路由實現(xiàn):
- 引入vue-router,如果是在腳手架中齐帚,引入VueRouter之后妒牙,需要通過Vue.use來注冊插件
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
- 創(chuàng)建router路由器
new Router(options)
- 創(chuàng)建路由表并配置在路由器中
var routes = [
{path,component}//path為路徑,component為路徑對應(yīng)的路由組件
]
new Router({
routes
})
- 在根實例里注入router,目的是為了讓所有的組件里都能通過this.
route來使用路由的相關(guān)功能api
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
利用router-view來指定路由切換的位置
使用router-link來創(chuàng)建切換的工具湘今,會渲染成a標簽,添加to屬性來設(shè)置要更改的path信息饥伊,且會根據(jù)當前路由的變化為a標簽添加對應(yīng)的router-link-active/router-link-exact-active(完全匹配成功)類名
<router-link to="main">main</router-link>
<router-link to="news">news</router-link>
.router-link-active{
color:red;
}
多級路由:
在創(chuàng)建路由表的時候象浑,可以為每一個路由對象創(chuàng)建children屬性,值為數(shù)組琅豆,在這個里面又可以配置一些路由對象來使用多級路由愉豺,注意:一級路由path前加'/'
const routes = [
{path:'/main',component:AppMain},
{path:'/news',component:AppNews,children:[
{path:'inside',component:AppNewsInside},
{path:'outside',component:AppNewsOutside}
]},
]
二級路由組件的切換位置依然由router-view來指定(指定在父級路由組件的模板中)
<router-link to='inside'>inside</router-link>
<router-link to='outside'>outside</router-link>
<router-view></router-view>
默認路由和重定向:
當我們進入應(yīng)用,默認像顯示某一個路由組件茫因,或者當我們進入某一級路由組件的時候想默認顯示其某一個子路由組件蚪拦,我們可以配置默認路由:
{path:'',component:Main}
當我們需要進入之后進行重定向到其他路由的時候,或者當url與路由表不匹配的時候:
{path:'',redirect:'/main'}
///...放在最下面
{path:'**',redirect:'/main'},
命名路由
我們可以給路由對象配置name屬性冻押,這樣的話驰贷,我們在跳轉(zhuǎn)的時候直接寫name:main就會快速的找到此name屬性對應(yīng)的路由,不需要寫大量的urlpath路徑了
動態(tài)路由匹配
有的時候我們需要在路由跳轉(zhuǎn)的時候跟上參數(shù)洛巢,路由傳參的參數(shù)主要有兩種:路徑參數(shù)括袒、queryString參數(shù)
路由參數(shù)需要在路由表里設(shè)置
{path:'/user/:id',component:User}
上面的代碼就是給User路由配置接收id的參數(shù),多個參數(shù)繼續(xù)在后面設(shè)置
在組件中可以通過this.$route.params來使用
queryString參數(shù)不需要在路由表設(shè)置接收稿茉,直接設(shè)置锹锰?后面的內(nèi)容,在路由組件中通過this.$route.query接收
router-link
<router-link> 組件支持用戶在具有路由功能的應(yīng)用中(點擊)導(dǎo)航漓库。 通過 to 屬性指定目標地址恃慧,默認渲染成帶有正確鏈接的 <a> 標簽,可以通過配置 tag 屬性生成別的標簽.渺蒿。另外痢士,當目標路由成功激活時,鏈接元素自動設(shè)置一個表示激活的 CSS 類名茂装。
router-link的to屬性怠蹂,默認寫的是path(路由的路徑),可以通過設(shè)置一個對象少态,來匹配更多
:to='{name:"detail",params:{id:_new.id},query:{content:_new.content}}'
name是要跳轉(zhuǎn)的路由的名字城侧,也可以寫path來指定路徑,但是用path的時候就不能使用params傳參况增,params是傳路徑參數(shù)赞庶,query傳queryString參數(shù)
replace屬性可以控制router-link的跳轉(zhuǎn)不被記錄\
active-class屬性可以控制路徑切換的時候?qū)?yīng)的router-link渲染的dom添加的類名
編程式導(dǎo)航
有的時候需要在跳轉(zhuǎn)前進行一些動作,router-link直接跳轉(zhuǎn),需要在方法里使用$router的方法
router.push = router-link:to
router.replace = router-link:to.replace
router.go() = window.history.go
路由模式
路由有兩種模式:hash歧强、history澜薄,默認會使用hash模式,但是如果url里不想出現(xiàn)丑陋hash值摊册,在new VueRouter的時候配置mode值為history來改變路由模式肤京,本質(zhì)使用H5的histroy.pushState方法來更改url,不會引起刷新茅特,但是需要后端進行路由的配置
路由鉤子
在某些情況下忘分,當路由跳轉(zhuǎn)前或跳轉(zhuǎn)后、進入白修、離開某一個路由前妒峦、后,需要做某些操作兵睛,就可以使用路由鉤子來監(jiān)聽路由的變化
全局路由鉤子:
router.beforeEach((to, from, next) => {
//會在任意路由跳轉(zhuǎn)前執(zhí)行肯骇,next一定要記著執(zhí)行,不然路由不能跳轉(zhuǎn)了
console.log('beforeEach')
console.log(to,from)
//
next()
})
//
router.afterEach((to, from) => {
//會在任意路由跳轉(zhuǎn)后執(zhí)行
console.log('afterEach')
})
單個路由鉤子:
只有beforeEnter祖很,在進入前執(zhí)行笛丙,to參數(shù)就是當前路由
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
路由組件鉤子:
beforeRouteEnter (to, from, next) {
// 在渲染該組件的對應(yīng)路由被 confirm 前調(diào)用
// 不!能假颇!獲取組件實例 `this`
// 因為當守衛(wèi)執(zhí)行前胚鸯,組件實例還沒被創(chuàng)建
},
beforeRouteUpdate (to, from, next) {
// 在當前路由改變,但是該組件被復(fù)用時調(diào)用
// 舉例來說笨鸡,對于一個帶有動態(tài)參數(shù)的路徑 /foo/:id姜钳,在 /foo/1 和 /foo/2 之間跳轉(zhuǎn)的時候,
// 由于會渲染同樣的 Foo 組件镜豹,因此組件實例會被復(fù)用傲须。而這個鉤子就會在這個情況下被調(diào)用蓝牲。
// 可以訪問組件實例 `this`
},
beforeRouteLeave (to, from, next) {
// 導(dǎo)航離開該組件的對應(yīng)路由時調(diào)用
// 可以訪問組件實例 `this`
}
命名視圖
有時候想同時(同級)展示多個視圖趟脂,而不是嵌套展示,例如創(chuàng)建一個布局例衍,有 sidebar(側(cè)導(dǎo)航) 和 main(主內(nèi)容) 兩個視圖昔期,這個時候命名視圖就派上用場了。你可以在界面中擁有多個單獨命名的視圖佛玄,而不是只有一個單獨的出口硼一。如果 router-view 沒有設(shè)置名字,那么默認為 default梦抢。
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
一個視圖使用一個組件渲染般贼,因此對于同個路由,多個視圖就需要多個組件。確保正確使用 components 配置(帶上 s):
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,//默認的哼蛆,沒有name的router-view
a: Bar,
b: Baz
}
}
]
})
prop將路由與組件解耦
在組件中接收路由參數(shù)需要this.$route.params.id,代碼冗余蕊梧,現(xiàn)在可以在路由表里配置props:true
{path:'detail/:id',component:AppNewsDetail,name:'detail',props:true}
在路由自己中可以通過props接收id參數(shù)去使用了
props:['id']
Axios 數(shù)據(jù)交互工具
vue官方宣布在2.0版本中不再對Vue-resource進行維護了,推薦使用axios工具
注意腮介,axios默認配置不會設(shè)置session-cookie肥矢,需要進行配置
axios.defaults.withCredentials = true
詳細請看文檔
響應(yīng)式原理
因為vue是mvvm的框架,所以當數(shù)據(jù)變化的時候叠洗,視圖會立即更新甘改,視圖層產(chǎn)生操作后會自動通知vm來更改model,所以我們可以實現(xiàn)雙向數(shù)據(jù)綁定灭抑,而其中的原理就是實例會將設(shè)置的data逐個遍歷利用Object.defineProperty給數(shù)據(jù)生成getter和setter十艾,當數(shù)據(jù)變化地方時候setter會監(jiān)聽到并且通知對應(yīng)的watcher工具進行邏輯運算會更新視圖
vuex借鑒了flux和redux的思想,但是flux和redux是獨立且完整的架構(gòu)腾节,vuex是耦合與vue框架的疟羹,所以使用成本要比flux、redux低
聲明式渲染
在vue中禀倔,我們可以先在vue實例中聲明數(shù)據(jù)榄融,然后通過{{}}等方式渲染在dom中
Vuex
Vuex是vue官方的一款狀態(tài)管理工具,什么是狀態(tài)呢救湖?我們在前端開發(fā)中有一個概念:數(shù)據(jù)驅(qū)動愧杯,頁面中任意的顯示不同,都應(yīng)該有一條數(shù)據(jù)來控制鞋既,而這條數(shù)據(jù)又叫做state力九,狀態(tài)。
在vue中邑闺。組件間進行數(shù)據(jù)傳遞跌前、通信很頻繁,而父子組件和非父子組件的通信功能也比較完善陡舅,但是抵乓,唯一困難的就是多組件間的數(shù)據(jù)共享,這個問題由vuex來處理
Vuex的使用:
- 創(chuàng)建store:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//可以設(shè)置store管理的state/getter靶衍,mutations,actions
const store = new Vuex.Store({
})
- 設(shè)置state
state就是一個純對象灾炭,上面有一些狀態(tài)掛載,而且一個應(yīng)用應(yīng)該只有一個數(shù)據(jù)源:單一狀態(tài)樹、唯一數(shù)據(jù)源
import state from './modules/state'
//可以設(shè)置store管理的state/getter颅眶,mutations,actions
const store = new Vuex.Store({
state
})
- 在根實例里配置store
這樣蜈出,我們就可以在任意的組件中通過this.$store來使用關(guān)于store的api
import store from './store'
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
- 在組件中使用state
因為在組件中可以通過this.store.state來使用state中管理的數(shù)據(jù)
data(){
return {
num:this.$store.state.num
}
},
但是我們發(fā)現(xiàn),這樣使用的話涛酗,當state的數(shù)據(jù)更改的時候铡原,vue組件并不會重新渲染,不會觸發(fā)組件的相關(guān)生命周期函數(shù)
也就是說偷厦,如果想要在組件中響應(yīng)式的使用的時候,我們需要通過計算屬性(computed)來使用
computed:{
num(){
return this.$store.state.num
}
}
這樣的寫法很無趣燕刻,而且如果使用的狀態(tài)較多會產(chǎn)生冗余的感覺沪哺,所以vuex提供了mapState輔助函數(shù),幫助我們在組件中獲取并使用vuex的store中保存的狀態(tài)
所以我們可以這樣寫:
computed:mapState(['num']),
但是如果組件中已經(jīng)有了num這個數(shù)據(jù)了酌儒,而state中的數(shù)據(jù)名字也叫num就會照成沖突辜妓,這個時候我們可以在組件使用state的時候,給狀態(tài)起個別名:
computed:mapState({
// _num:'num',//鍵名為別名忌怎,值字符串代表的是真正的狀態(tài)
_num(state){//方法名為別名籍滴,函數(shù)體里還可以對真正的狀態(tài)做出一些處理
return state.num
}
}),
但是,有的時候我們在組件中還有自己的業(yè)務(wù)邏輯需要用到計算屬性:
computed:{
a(){
return num+1
},
...mapState({
// _num:'num',//鍵名為別名榴啸,值字符串代表的是真正的狀態(tài)
_num(state){//方法名為別名孽惰,函數(shù)體里還可以對真正的狀態(tài)做出一些處理
return state.num
}
}),
},
- getters
有的時候,我們需要根據(jù)state中的某一個狀態(tài)派生出一個新的狀態(tài)鸥印,例如勋功,我們state中有一個num,在某些組件中需要用到是num的二倍的一個狀態(tài)库说,我們就可以通過getters來創(chuàng)建
const getters = {
doublenum(state){
return state.num*2
}
}
創(chuàng)建了之后狂鞋,在組件中通過this.$store.getters來獲取里面的數(shù)據(jù)
當然vuex也提供了mapGetters輔助函數(shù)來幫助我們在組件中使用getters里的狀態(tài),且潜的,使用的方法和mapState一模一樣
- 使用mutations更改state
我們不能直接在組件中更改state:this.$store.state.num=2,而是需要使用mutations來更改骚揍,mutations也是一個純對象,里面包含很多更改state 的方法啰挪,這些方法的形參接收到state信不,在函數(shù)體里更改,這時亡呵,組件用到的數(shù)據(jù)也會更改抽活,實現(xiàn)響應(yīng)式。
但是我們也不能直接調(diào)用mutations 的方法锰什,需要使用this.$store.commit來調(diào)用下硕,第一個參數(shù)為調(diào)用的方法名,第二げ參數(shù)為傳遞參數(shù)
const mutations = {
increment(state){
state.num++
}
}
vuex提供了mapMutations方法來幫助我們在組件中調(diào)用mutations 的方法,使用方法和mapState歇由、mapGetters一樣
- 使用actions來處理異步操作
Action 類似于 mutation卵牍,不同在于:
Action 提交的是 mutation果港,而不是直接變更狀態(tài)沦泌。
Action 可以包含任意異步操作。
也就是說辛掠,如果有這樣的需求:在一個異步處理之后谢谦,更改狀態(tài)释牺,我們在組件中應(yīng)該先調(diào)用actions,來進行異步動作回挽,然后由actions調(diào)用mutation來更改數(shù)據(jù)
const actions = {
[CHANGE_NUM]({commit}){
alert(1)
setTimeout(() => {
let num = Math.floor(Math.random()*10)
//調(diào)用mitations的方法
commit(CHANGE_NUM,num)
}, 1000);
}
}
``
如上没咙,actions的方法中可以進行異步的動作,且形參會接收store千劈,從中取出commit方法用以調(diào)用mutations的方法
在組件中通過this.$store.dispatch方法調(diào)用actions的方法
當然也可以使用mapMutations來輔助使用
組件使用數(shù)據(jù)且通過異步動作更改數(shù)據(jù)的一系列事情:
1.生成store,設(shè)置state
2.在根實例中注入store
3.組件通過計算屬性或者mapState來使用狀態(tài)
4.用戶產(chǎn)生操作祭刚,調(diào)用actions的方法,然后進行異步動作
5.異步動作之后墙牌,通過commit調(diào)用mutations的方法
6.mutations方法被調(diào)用后涡驮,更改state
7.state中的數(shù)據(jù)更新之后,計算屬性重新執(zhí)行來更改在頁面中使用的狀態(tài)
8.組件狀態(tài)被更改...創(chuàng)建新的虛擬dom......
9.組件的模板更新之后重新渲染在dom中
vuex的使用:
目前市場上有兩種使用vuex的情況喜滨,
第一種:將需要共享捉捅、需要管理的狀態(tài)放入vuex中管理,也就是說在必要時使用
第二種:將所有的數(shù)據(jù)都交由vuex管理虽风,由vuex來承擔(dān)更多的責(zé)任棒口,組件變得更輕量級,視圖層更輕
---
##### 自定義指令
在實現(xiàn)回到頂部功能的時候辜膝,我們寫了一個backTop組件无牵,接下來需要通過監(jiān)聽window.scroll事件來控制這個組件顯示隱藏
因為可能會有其他的組件會用到這樣的邏輯,所以將此功能做成一個自定義指令:
根據(jù)滾動的距離控制一個數(shù)據(jù)為true還是為false(v-scroll-show)
問題:
唯一需要注意的是厂抖,在指令的鉤子函數(shù)中我們可以訪問到el合敦,也就是使用指令的標簽,但是我們不能直接更改value(指令的值所代表的數(shù)據(jù))
所以我們使用引用類型來進行地址的傳遞來解決這個問題
接下來有寫了一個v-back-top指令验游,就是將回到頂部功能做成一個指令充岛,哪個組件或者dom需要使用到回到頂部,就加上這個指令就可以耕蝉,設(shè)置不同的參數(shù)來控制在不同的情況下觸發(fā)
##### Vue的組件庫
組件庫就是通用組件的集合
pc:element-ui iview
mobile: mint-ui
##### nextTick
當我們在使用一些插件的時候崔梗,經(jīng)常需要在dom更新完成后進行必要操作,但是在vue中提供的api只有updated鉤子函數(shù)垒在,而在這個函數(shù)里蒜魄,任意數(shù)據(jù)的變化導(dǎo)致的dom更新完成都會觸發(fā),所以很可能會造成無關(guān)數(shù)據(jù)的影響场躯,而使用監(jiān)聽的話只能監(jiān)聽到數(shù)據(jù)的變化谈为,此時dom還沒有更新,我們只能強行使用setTimeout來處理
這里推薦大家使用nextTick全局方法:
在下次 DOM 更新循環(huán)結(jié)束之后執(zhí)行延遲回調(diào)踢关。在修改數(shù)據(jù)之后立即使用這個方法伞鲫,獲取更新后的 DOM。
eq:
getBillBoards(){
axios.get(this.$root.config.host+'mz/v4/api/billboard/home',{
params:{__t:Date.now()}
}).then(res => {
console.log(res.data.data.billboards)
this.billboards = res.data.data.billboards
//當數(shù)據(jù)更新签舞,dom循環(huán)完成后秕脓,執(zhí)行回調(diào)
Vue.nextTick(function () {
new Swiper('.app-home-banner',{
loop:true
})
})
})
}
##### keep-alive
在component組件柒瓣、router-view外面包裹上keep-alive的話,就會對組件進行緩存吠架,當切換回來的時候芙贫,組件會立即渲染,理論來說傍药,切換組件的時候其實會把上一個組件銷毀磺平,使用了keep-alive則不會
設(shè)置include、exclude屬性控制有選擇的緩存
include匹配到的組件會被緩存拐辽,exclude匹配到的不會被緩存
值可以為逗號隔開的字符串include = 'a,b';正則:include = '/a|b/';數(shù)組:include=['a','b']
使用keep-alive緩存的組件連帶它的子組件們都會擁有activated褪秀、deactivated鉤子函數(shù),會在切換回來和要切換出去的時候觸發(fā)
比如薛训,main做了緩存媒吗,但是main的banner我們希望每次都去重新獲取數(shù)據(jù),所以就在banner的activated里獲取數(shù)據(jù)
### Vue試題分析
1. v-for可以實現(xiàn)數(shù)據(jù)遍歷顯示乙埃,不僅可以遍歷數(shù)組闸英,也可以遍歷對象,還可以從數(shù)值中取值:
v-for='n in 10' n會打印1-10
2. vue的生命周期鉤子:
通用:beforeCreate/created/beforeMount/mounted/beforeUpdate/updated/beforeDestroy/destroyed
路由守衛(wèi):beforeRouteEnter/beforeRouteUpdate (2.2 新增)/beforeRouteLeave
keep-alive:activated/deactivated
3. v-if v-show
v-if是真正的條件渲染介袜,會確保在切換中條件塊內(nèi)的事件監(jiān)聽甫何、子組件都會適當?shù)谋讳N毀和重建
v-show總是將節(jié)點渲染在dom中,只是基于css:display來控制節(jié)點的顯示和隱藏
v-if有更高的切換開始遇伞,v-show有更高的初始渲染開銷
v-if是惰性的辙喂,初始條件為假,就不會渲染
4. axios相關(guān)
axios請求的時候不會帶上cookie鸠珠,不會影響帶寬巍耗,可以通過withCredentials:true來設(shè)置
對axios 的請求頭進行設(shè)置:
axios.defaults.headers = {'Content-Type':'...'}
vue2.0不在更新維護vue-resource,官方推薦使用axios
axios攔截器可以攔截請求和響應(yīng)渐排,在then炬太、catch之前攔截
6. 組件實例的作用域是孤立的,意味著不能(不應(yīng)該)在子組件模板里直接引用父組件的數(shù)據(jù)驯耻,要讓子組件使用父組件數(shù)據(jù)的話亲族,需要通過props來將父組件的數(shù)據(jù)傳遞給子組件,子組件不能也不應(yīng)該修改父組件傳入的數(shù)據(jù)可缚,但是可以通過傳入引用類型的數(shù)據(jù)來實現(xiàn)數(shù)據(jù)共享
7.為了讓組件可以組合霎迫,我們需要一種方式來混合父組件的內(nèi)容與子組件自己的模板。這個過程被稱為內(nèi)容分發(fā) (即 Angular 用戶熟知的“transclusion”)帘靡。Vue.js 實現(xiàn)了一個內(nèi)容分發(fā) API知给,參照了當前 Web Components 規(guī)范草案,使用特殊的 <slot> 元素作為原始內(nèi)容的插槽测柠。
a-template:
<p>hello world</p>
<b>
<h1>hello world</h1>
</b>
b-template:
<slot></slot>
....
8. 如果把切換出去的組件保存在內(nèi)存中炼鞠,保留狀態(tài)避免重新渲染缘滥,可以使用keep-alive
include exclude
9. 注冊方式:
全局:Vue.component(name,Vue.extend({}))
局部:{ components:{name:Vue.extend({})} }
10. 事件總線實現(xiàn)非父子組件通信
//創(chuàng)建bus
let bus = new Vue()
//a
new Vue({
template:'...',
mounted(){
bus.$on('emit-a',function(){
alert(1)
})
}
})
//b
new Vue({
template:'...',
methods:{
emitA(){
bus.$emit('emit-a')
}
}
})
//當b組件的emitA方法被調(diào)用的時候轰胁,A組件就會執(zhí)行alert(1)
11. methods和計算屬性的區(qū)別
假設(shè)我們有一個數(shù)據(jù)為num谒主,還希望擁有一個數(shù)據(jù)為doublenum,而且希望doublenum的值永遠都是num的二倍
方法:
* 因為是直接顯示在模板中赃阀,也就是說霎肯,我們可以來一個doublenum的方法,這個方法返回num的二倍榛斯,將這個方法放到模板中的某個地方執(zhí)行 {{doublenum()}}
但是观游,當無關(guān)的例如一個str的數(shù)據(jù)更改的時候,組件會重新創(chuàng)建虛擬dom樹驮俗,與上一次的虛擬dom樹對比之后重新渲染懂缕,這個時候在重新渲染模板的時候doublenum函數(shù)會被再次的調(diào)用,造成不必要的性能浪費
* 創(chuàng)建一個doublenum數(shù)據(jù)王凑,使其初始值為num的二倍搪柑,然后利用watch來監(jiān)聽這兩個數(shù)據(jù),在改變的時候更改對應(yīng)的數(shù)據(jù)索烹,但是需要初始的為doublenum賦值為num的二倍工碾,如果num是動態(tài)獲取到的,doublenun賦值會更繁瑣
* computed計算數(shù)據(jù)百姓,我們可以利用computed來創(chuàng)建一條新的doublenum數(shù)據(jù)渊额。并且設(shè)置它的getter和setter,并與num建立關(guān)系,且computed會緩存垒拢,在重新渲染的時候旬迹,不會重新執(zhí)行g(shù)etter和setter
computed:{
doublenum:{
get(){
return this.num*2
},
set(val){
this.num = val/2
}
}
}
12. 綁定class的對象語法和數(shù)組語法
<a :class="{a:true,b:false,c:1}"> => </a> => <a class='a c'></a>
data(){
return {
c:'c'
}
}
<a :class = '["a","b",c]'></a> => </a> => <a class='a b c'></a>
13.
new Vue({
el:"#example-3",
methods:{
say(str){
alert(str)
}
}
})
14. 單向數(shù)據(jù)流
prop是單向綁定的,父組件屬性變化求类,傳遞給子組件舱权,但是,子組件數(shù)據(jù)變化仑嗅,不能直接傳遞給父組件宴倍,也就是數(shù)據(jù)的流行是從父組件流向子組件的,為了防止子組件修改父組件的數(shù)據(jù)(會讓應(yīng)用的數(shù)據(jù)流變的更難開發(fā)仓技、更新鸵贬、維護)
使用了vuex工具的時候,store中數(shù)據(jù)在組件中使用的過程也是單向數(shù)據(jù)流脖捻,state->vue component->actions->mutations->state->vue component
15. this.$router.push/replace({name:'user',params:{userId:1})
this.$router.push/replace({path:'/register',query:{plan:private})
##### key相關(guān)
當數(shù)據(jù)改變之后阔逼,vue會創(chuàng)建新的虛擬dom來和原來的虛擬dom做對比,在創(chuàng)建新的虛擬的dom的時候地沮,會根據(jù)key來查找在原來的虛擬dom中有沒有某個部分嗜浮,如果原來的有羡亩,這次的也需要,就會實現(xiàn)復(fù)用危融,而且在做diff對比的時候畏铆,如果有key會加快對比的查找速度,提高性能
盡量循環(huán)的時候不要將key設(shè)置為數(shù)組的索引吉殃,因為當刪除某一個元素的時候辞居,就會導(dǎo)致刪除位置下面的所有元素的key值都與上一次虛擬dom的key值不同,導(dǎo)致復(fù)用失敗蛋勺,這個時候我們最好使用關(guān)鍵的唯一的瓦灶,例如id這樣的數(shù)據(jù)作為key
如果數(shù)據(jù)變化只是值的變化而不是條數(shù)和位置的變化,可以使用索引作為key
##### Vue.use()
Vue.use會查找插件對象里的install方法去執(zhí)行,并且給install方法里傳入Vue對象
var a = {
install(Vue){
Vue.component("my-a",{...})
}
}
Vue.use(a)
##### 進入域后根據(jù)不同的情況顯示不同的頁面(PC/MOBILE)
很多情況下抱完,一個應(yīng)用會有PC和移動端兩個版本贼陶,而這兩個版本因為差別大,內(nèi)容多巧娱,所以不能用響應(yīng)式開發(fā)但是單獨開發(fā)碉怔,而域名只有一個,用戶進入域后直接返回對應(yīng)設(shè)備的應(yīng)用家卖,做法主要有兩種:
1. 前端判斷并跳轉(zhuǎn)
進入一個應(yīng)用或者一個空白頁面后眨层,通過navigator.userAgent來判斷用戶訪問的設(shè)備類型,進行跳轉(zhuǎn)
2. 后端判斷并響應(yīng)對應(yīng)的應(yīng)用
用戶地址欄進入域的時候上荡,服務(wù)器能接收到請求頭上包含的userAgent信息趴樱,判斷之后返回對應(yīng)應(yīng)用
---
function foo(){// 第16行
getName = function(){console.log(1)}
return this
}
foo.getName = function(){console.log(2)}
foo.prototype.getName = function(){console.log(3)}
var getName = function(){console.log(4)}
function getName(){console.log(5)}
foo.getName()//2
//foo是一個函數(shù),也可以說是一個對象酪捡,所以它也可以掛載一些屬性和方法叁征,18行在其上掛載了一個getName方法
//執(zhí)行的結(jié)果是
getName()//4
//21行有一個全局函數(shù),全局函數(shù)聲明提前后被20行的getName覆蓋逛薇,所以輸出4
foo().getName()//1
//foo()執(zhí)行完成后捺疼,將全局的getName也就是window.getName給更改后返回this,而在這里this執(zhí)行的就是window永罚,所以最后執(zhí)行的就是window.getName啤呼,所以輸出1
getName()//1
//在上面已經(jīng)更改全局的getName,所以依然是1
new foo.getName()//2
//new 操作符在實例化構(gòu)造器的時候呢袱,會執(zhí)行構(gòu)造器函數(shù)官扣,也就是說,foo.getName會執(zhí)行羞福,輸出2
new foo().getName()//3
//new操作符的優(yōu)先級較高惕蹄,所以會先new foo()得到一個實例,然后再執(zhí)行實例的getName方法,這個時候,實例的構(gòu)造器里沒有g(shù)etName方法卖陵,就會執(zhí)行構(gòu)造器原型上的getName方法
new new foo().getName()//3
//先執(zhí)行new foo()得到一個實例渔隶,然后在new 這個實例的getName方法,這個時候會執(zhí)行這個方法懈凹,所以輸出3
//除了本地對象的方法务豺,其他的函數(shù)都能new
---