Vue一文學(xué)會(huì)?
Vue大家都知道就是一個(gè)國(guó)內(nèi)非常流行的框架柳琢,最近因?yàn)檫^(guò)了許久沒(méi)用Vue對(duì)于Vue的許多早已淡忘衡怀,所以目前正在復(fù)習(xí)Vue順便記錄一下筆記够庙,以后忘了也可以進(jìn)行查證靠娱,因?yàn)檫@是根據(jù)自己的性格所寫(xiě)的筆記嗽元,可能大家會(huì)看不懂敛纲,如果看不懂請(qǐng)盡量不要試著看,可以去看看其他的博主寫(xiě)的文章剂癌!
起步
如果需要知道怎么使用webpack手動(dòng)搭建vue環(huán)境的麻煩傳送
那么我們還是使用Vue的官方cli進(jìn)行構(gòu)建Vue程序 npm i -g @vue/cli
然后我們 vue create -n suiyue_m
-n選項(xiàng)你隨意加不加淤翔,是用來(lái)表示不初始化git的
然后就是一個(gè)選項(xiàng)欄
我之前已經(jīng)保存過(guò)一次選項(xiàng)了所以大家可以看看,我這里在選擇一次
選擇最后一項(xiàng)按回車(chē)鍵進(jìn)入二次選擇
空格鍵選擇
大家先跟著我的選擇選吧珍手,一般就是這樣办铡,最多就是多一個(gè)typescript什么的之類(lèi),然后我們直接敲回車(chē)
之后這里又有一個(gè)選項(xiàng)
意思是要不要開(kāi)啟歷史模式琳要,什么區(qū)別呢寡具?
就是路由的時(shí)候
默認(rèn)是啟用的(因?yàn)閅是大寫(xiě),n是小寫(xiě))稚补,啟用此歷史模式就是
example url: http://www.xxx.com/user/xiaoming/info
不啟用就是熟悉的
http://www.xxx.com/#/user/xiaoming/info (應(yīng)該沒(méi)記錯(cuò))
(啟用此模式需要配置服務(wù)器因?yàn)橥@樣發(fā)送http請(qǐng)求的話(huà)會(huì)請(qǐng)求對(duì)應(yīng)服務(wù)器對(duì)應(yīng)的文件夾下的index.html所以需要進(jìn)行配置,具體配置請(qǐng)移步Vue官方文檔,不放鏈接了厦坛,因?yàn)槲彝四莻€(gè)鏈接了)
所以我們選擇N五垮,主要是懶得配置服務(wù)器了
下面就是配置css預(yù)處理了,我喜歡用less大家隨意按照自己喜歡的擅長(zhǎng)的那種進(jìn)行選擇杜秸,下一個(gè)選項(xiàng)就是
語(yǔ)法檢測(cè)eslint然后我默認(rèn)就選擇第一個(gè)了放仗,代表eslint用來(lái)檢測(cè)語(yǔ)法沒(méi)有錯(cuò)誤,其他的不了解也不使用
下一個(gè)默認(rèn)也選擇第一個(gè)代表在保存時(shí)進(jìn)行語(yǔ)法檢查
這個(gè)的意思大意就是Babel撬碟,PostCss诞挨,ESLint這些配置的配置文件(個(gè)人理解,錯(cuò)誤勿怪)存放在一個(gè)單獨(dú)的文件還是跟package.json文件放在一起
那么就選擇第一項(xiàng)了,放在一個(gè)獨(dú)立的文件中
最后一個(gè)就是是否保存這次的選擇,然后因?yàn)槲冶4媪怂杂腥齻€(gè)選項(xiàng)大家隨意保存不保存谨设,如果敲y的話(huà)會(huì)讓你輸入這個(gè)preset的名字,默認(rèn)是不保存的堰乔,然后就開(kāi)始下載依賴(lài)什么的了
最后想說(shuō)的就是建議大家使用yarn或者pnpm這等之類(lèi)的npm包管理工具,因?yàn)樗俣瓤鞓侵魇褂玫氖莥arn
大家可以使用
npm i -g yarn
進(jìn)行安裝然后我們直接運(yùn)行Vue服務(wù)
默認(rèn)是不會(huì)打開(kāi)瀏覽器自動(dòng)運(yùn)行的需要大家手動(dòng)打開(kāi)瀏覽器查看頁(yè)面,好了關(guān)于vue-cli的用法就結(jié)束了
接下來(lái)就是Hello Vue的實(shí)現(xiàn)了,我們先更改一些配置文件
在文件夾的根目錄新建一個(gè)vue.config.js文件蜈敢,插入一下這些內(nèi)容將vue服務(wù)的端口改為3000,讓它自動(dòng)打開(kāi)瀏覽器汽抚,至于熱更新本身就熱更新了
然后我們重新
yarn serve
然后就會(huì)看到我們熟悉的一幕了因?yàn)?000端口跑了react的項(xiàng)目所以緩存了圖標(biāo)
然后接下來(lái)就是Hello Vue了扶认,直接打開(kāi)App.vue
將template下面改成這樣
就是一個(gè)Hello Vue了
那么因?yàn)槲沂菑?fù)習(xí)我就想到什么寫(xiě)什么了
進(jìn)階
-
計(jì)算屬性與監(jiān)聽(tīng)
計(jì)算屬性殊橙,類(lèi)似于一個(gè)Vue屬性但是計(jì)算屬性是實(shí)時(shí)進(jìn)行計(jì)算出來(lái)的,計(jì)算屬性可以依賴(lài)Vue的屬性狱从,當(dāng)Vue的屬性改變時(shí)膨蛮,計(jì)算屬性會(huì)同步的改變,類(lèi)似于
然后其實(shí)下面就是一個(gè)計(jì)算屬性永遠(yuǎn)不會(huì)更新的死節(jié)
因?yàn)橄旅娴倪@個(gè)計(jì)算屬性并沒(méi)有依賴(lài)任何Vue的屬性導(dǎo)致此計(jì)算屬性永遠(yuǎn)也不會(huì)更新季研,其實(shí)此計(jì)算屬性的效果也可以同樣使用methods進(jìn)行替代敞葛,類(lèi)似于
然后就是此方法與使用計(jì)算屬性的不同之處在于,當(dāng)頁(yè)面被觸發(fā)渲染時(shí)計(jì)算屬性所依賴(lài)的屬性改變時(shí)才會(huì)重新計(jì)算求值与涡,但是函數(shù)每一次頁(yè)面重新渲染時(shí)都會(huì)進(jìn)行重新的一次計(jì)算求值
監(jiān)聽(tīng)
使用屬性名作為函數(shù)名惹谐,每一次當(dāng)對(duì)應(yīng)的屬性值發(fā)生改變時(shí)會(huì)進(jìn)行調(diào)用對(duì)應(yīng)的函數(shù)(不進(jìn)行詳細(xì)的截圖了)
class與style的綁定
綁定class
使用v-bind指令綁定class傳入一個(gè)對(duì)象,屬性名決定類(lèi)名驼卖,是否指定此類(lèi)名決定值的真或者是假氨肌,同樣的我們也可以將class后面的屬性提出為一個(gè)對(duì)象指定變量的方式
同樣的class后面也可以指定為一個(gè)函數(shù)或者一個(gè)計(jì)算屬性要求是返回對(duì)象形式的
class綁定數(shù)組語(yǔ)法
渲染為
同時(shí)也可以在數(shù)組中使用三元運(yùn)算符來(lái)指定對(duì)應(yīng)的類(lèi)名
渲染為
最后就是還可以在數(shù)組之中使用對(duì)象來(lái)進(jìn)行混入
(對(duì)組件傳遞class類(lèi)名會(huì)默認(rèn)綁定到此組件的根元素身上)
style綁定
(在強(qiáng)調(diào)一遍,vue中指令后面的引號(hào)中的字符串會(huì)被當(dāng)做js代碼進(jìn)行執(zhí)行)
同樣的因?yàn)閟tyle是一個(gè)對(duì)象我們可以將這個(gè)對(duì)象取出直接指定變量的名字進(jìn)行綁定也可以類(lèi)class的綁定方法
style數(shù)組綁定法類(lèi)對(duì)象方法
指令
v-if
使用v-if后面的表達(dá)式為true時(shí)才會(huì)渲染對(duì)應(yīng)的html元素,否則不會(huì)渲染(使用的是上dom樹(shù)和下dom樹(shù))
我這里用了一下v-if
<template>
<div id="app">
<h1 v-if="isShow">Hello World</h1>
<input type="button" value="點(diǎn)我上樹(shù)下樹(shù)" @click="shangxiashu">
<router-view/>
</div>
</template>
<script>
export default {
data(){
return {
isShow:true
}
},
methods:{
shangxiashu(){
this.isShow = !this.isShow;
}
}
}
</script>
通過(guò)isShow控制h1標(biāo)簽的上樹(shù)與下樹(shù),這里使用的v-if是直接從dom樹(shù)上面刪除的,如果需要頻繁的操作dom需要使用另外一個(gè)指令(暫時(shí)忘了)
vue還提供了一個(gè)指令與此指令配套,就是v-else,我們稍微修改代碼
<template>
<div id="app">
<h1 v-if="isShow">Hello World</h1>
<h2 v-else>h2 ---> Hello World</h2>
<input type="button" value="點(diǎn)我切換元素" @click="shangxiashu">
<router-view/>
</div>
</template>
<script>
export default {
data(){
return {
isShow:true
}
},
methods:{
shangxiashu(){
this.isShow = !this.isShow;
}
}
}
</script>
你會(huì)發(fā)現(xiàn)使用vue能夠讓你用極少的代碼來(lái)操作dom
vue2.1新增了一個(gè)v-else-if指令
v-else 必須添加到v-if或者v-else-if后面否則無(wú)法被識(shí)別
我們繼續(xù)修改代碼
<template>
<div id="app">
<h1 v-if="isShow <=6">Hello World</h1>
<h2 v-else-if="isShow >6 && isShow<=10">h2 ---> Hello World</h2>
<h3 v-else>h3 ---> Hello World</h3>
<input type="button" value="點(diǎn)我切換元素" @click="shangxiashu">
<router-view/>
</div>
</template>
<script>
export default {
data(){
return {
isShow:1
}
},
methods:{
shangxiashu(){
this.isShow++;
}
}
}
</script>
我們直接將isShow改變?yōu)閿?shù)字通過(guò)每一個(gè)區(qū)間的不同渲染不同的元素
v-show --> v-show指令只是單純的切換標(biāo)簽的display屬性,所以并不是真正的銷(xiāo)毀一個(gè)dom元素
v-if初始化加載開(kāi)銷(xiāo)低,每次切換開(kāi)銷(xiāo)高 v-show初始化開(kāi)銷(xiāo)高,但是切換開(kāi)銷(xiāo)低
用法一般是: 只需要渲染一兩次的dom元素可以使用v-if,需要頻繁顯示隱藏的一些效果需要v-show
v-for
<template>
<div id="app">
<ul>
<li v-for="(item,index) in items" :key="index">
{{ item }}
</li>
</ul>
<router-view/>
</div>
</template>
<script>
export default {
data(){
return {
isShow:1,
items:[
"Foo","Bar"
]
}
},
methods:{
shangxiashu(){
this.isShow++;
}
}
}
</script>
可以使用of替換in,這樣更像迭代器
v-for="item of items"
遍歷數(shù)組有兩個(gè)參數(shù)
(item,index)遍歷對(duì)象有三個(gè)參數(shù) (value,name,index)
數(shù)組更新檢測(cè)
data中數(shù)組的某一些方法也會(huì)觸發(fā)vue的視圖更新
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
當(dāng)data中的數(shù)組通過(guò)上面的方法數(shù)據(jù)發(fā)生改變時(shí)同樣會(huì)出發(fā)虛擬dom的更新進(jìn)而刷新視圖
(上述方法統(tǒng)稱(chēng)為變異方法,就是會(huì)改變調(diào)用這些方法的數(shù)組)
由于JavaScript的某些限制,vue無(wú)法監(jiān)控兩類(lèi)數(shù)組的變化
- items[0] = "Xiao Ming" 直接修改某一位置的值
- items.length = 99 修改數(shù)組的長(zhǎng)度
為了解決第一類(lèi)問(wèn)題,可以使用下面的兩種方法
- Vue.set(items,0,"XiaoMing")
- items.splice(0,1,"XiaoMing")
解決第二類(lèi)問(wèn)題的方法
items.splice(newLength)
(我這里直接寫(xiě)的items,大家一定要使用vue的實(shí)例打點(diǎn)調(diào)用)
由于JavaScript的限制vue無(wú)法檢測(cè)對(duì)象屬性的添加和刪除,例如當(dāng)Vue對(duì)象實(shí)例化之后可以為這個(gè)實(shí)例化對(duì)象vm(viewModel)繼續(xù)添加屬性但是這時(shí)候添加的屬性vue是無(wú)法動(dòng)態(tài)監(jiān)控到的(此屬性沒(méi)有與vm進(jìn)行綁定)
解決方案:
Vue.set(obj,newProp,newPropValue))
(這代表是Vue這個(gè)對(duì)象的靜態(tài)方法,不是實(shí)例化vm的方法,實(shí)例化vm也有一個(gè)此方法為$set()這個(gè)方法是全局set方法的別名)
v-on
v-on:事件名.事件修飾符 --> 一個(gè)簡(jiǎn)單的使用
<template>
<div id="app">
<h1>{{counter}}</h1>
<input type="button" value="click me" v-on:click="counter++">
<router-view/>
</div>
</template>
<script>
export default {
data(){
return {
counter:0
}
},
methods:{
shangxiashu(){
this.isShow++;
}
},
mounted(){
window.items = this.items;
}
}
</script>
我們除了直接在v-on后面書(shū)寫(xiě)語(yǔ)句外我們還可以書(shū)寫(xiě)方法(methods中定義的)
<template>
<div id="app">
<h1>{{counter}}</h1>
<input type="button" value="click me" v-on:click="add">
<router-view/>
</div>
</template>
<script>
export default {
data(){
return {
counter:0
}
},
methods:{
shangxiashu(){
this.isShow++;
},
add:function(){
// this代表vue的實(shí)例
this.counter++;
}
},
mounted(){
window.items = this.items;
}
}
</script>
一樣可以實(shí)現(xiàn)點(diǎn)擊按鈕+1的操作,然后我們除了可以使用方法外我們還可以給這個(gè)方法進(jìn)行傳參,類(lèi)似于
<template>
<div id="app">
<h1>{{counter}}</h1>
<input type="button" value="click me" v-on:click="add(99999999999)">
<router-view/>
</div>
</template>
<script>
export default {
data(){
return {
counter:0
}
},
methods:{
shangxiashu(){
this.isShow++;
},
add:function(num){
num = num || 1;
// this代表vue的實(shí)例
this.counter += num;
}
},
mounted(){
window.items = this.items;
}
}
</script>
有時(shí)候我們可能需要訪(fǎng)問(wèn)原聲事件對(duì)象的一些屬性,我們也可以將$event這個(gè)屬性傳遞給方法
<template>
<div id="app">
<h1>{{counter}}</h1>
<input type="button" value="click me" v-on:click="add($event)">
<router-view/>
</div>
</template>
<script>
export default {
data(){
return {
counter:0
}
},
methods:{
shangxiashu(){
this.isShow++;
},
add:function(e,num){
num = num || 1;
// this代表vue的實(shí)例
this.counter += num;
// 當(dāng)前事件的事件對(duì)象
console.log(e);
}
},
mounted(){
window.items = this.items;
}
}
</script>
事件修飾符
按照vue官方的說(shuō)法就是事件中就是純粹的邏輯而不去處理dom事件的細(xì)節(jié)所以我們可以使用事件修飾符做一些操作事件細(xì)節(jié)的事情如:阻止默認(rèn)事件,阻止事件冒泡等等
比如說(shuō)我這里有一個(gè)a標(biāo)簽我這個(gè)a標(biāo)簽點(diǎn)擊之后就會(huì)跳轉(zhuǎn)到baidu去
<template>
<div id="app">
<h1>{{counter}}</h1>
<a target="_blank">點(diǎn)我+1</a>
<router-view/>
</div>
</template>
那我想干啥呢?我想要這個(gè)a標(biāo)簽點(diǎn)擊之后counter+1,所以我就這樣寫(xiě)
<template>
<div id="app">
<h1>{{counter}}</h1>
<a target="_blank" v-on="add">點(diǎn)我+1</a>
<router-view/>
</div>
</template>
<script>
export default {
data(){
return {
counter:0
}
},
methods:{
shangxiashu(){
this.isShow++;
},
add:function(e,num){
num = num || 1;
// this代表vue的實(shí)例
this.counter += num;
// 當(dāng)前事件的事件對(duì)象
console.log(e);
}
},
mounted(){
window.items = this.items;
}
}
</script>
那么你就會(huì)發(fā)現(xiàn)點(diǎn)擊了這個(gè)a標(biāo)簽之后counter+1后又跳轉(zhuǎn)到baidu去了,這就是所謂的默認(rèn)事件,顧名思義就是a標(biāo)簽本來(lái)就是表示一個(gè)鏈接的點(diǎn)擊之后自然是要跳轉(zhuǎn)到對(duì)應(yīng)的地址去,所以我們可以通過(guò)
prevent
事件修飾符阻止默認(rèn)事件,我們可以試試
<template>
<div id="app">
<h1>{{counter}}</h1>
<a target="_blank" v-on:click.prevent="add">點(diǎn)我+1</a>
<router-view/>
</div>
</template>
<script>
export default {
data(){
return {
counter:0
}
},
methods:{
shangxiashu(){
this.isShow++;
},
add:function(e,num){
num = num || 1;
// this代表vue的實(shí)例
this.counter += num;
// 當(dāng)前事件的事件對(duì)象
console.log(e);
}
},
mounted(){
window.items = this.items;
}
}
</script>
這樣之后不管你怎么點(diǎn)擊a標(biāo)簽也只會(huì)響應(yīng)click事件不會(huì)跳轉(zhuǎn)頁(yè)面了
常用的修飾符
- stop 阻止冒泡
- prevent 阻止默認(rèn)事件
- capture 捕獲階段執(zhí)行事件
- self 大致理解為捕獲或者冒泡到這個(gè)元素的事件不會(huì)執(zhí)行,只有直接出發(fā)此元素的事件才會(huì)被執(zhí)行
- once 代表只執(zhí)行一次
- passive 這個(gè)東西代表立即觸發(fā)默認(rèn)事件,同時(shí)會(huì)使prevent無(wú)效
按鍵修飾符 --> 可以通過(guò)按鍵修飾符綁定keyboard事件時(shí)監(jiān)聽(tīng)某一個(gè)鍵
- enter
- tab
- delete (捕獲“刪除”和“退格”鍵)
- esc
- space
- up
- down
- left
- right
這是一個(gè)使用按鍵修飾符的小例子
<template>
<div id="app">
<h1>{{counter}}</h1>
<a target="_blank" v-on:click.prevent="add">點(diǎn)我+1</a>
<br />
<input type="text" placeholder="回車(chē)+10w" v-on:keyup.enter="keyHandle">
<router-view/>
</div>
</template>
<script>
export default {
data(){
return {
counter:0
}
},
methods:{
shangxiashu(){
this.isShow++;
},
add:function(e,num){
num = num || 1;
// this代表vue的實(shí)例
this.counter += num;
// 當(dāng)前事件的事件對(duì)象
console.log(e);
},
keyHandle(){
this.counter+=100000;
}
},
mounted(){
window.items = this.items;
}
}
</script>
就輕松實(shí)現(xiàn)了按下特定的鍵做某些特定的事情了
同時(shí)我們也可以自定義按鍵修飾符
我們可以使用
Vue.config.keyCodes.w= 87
定義一個(gè)w的按鍵修飾符鍵盤(pán)碼為87
<template>
<div id="app">
<h1>{{counter}}</h1>
<a target="_blank" v-on:click.prevent="add">點(diǎn)我+1</a>
<br />
<input type="text" placeholder="回車(chē)+10w" v-on:keyup.w="keyHandle">
<router-view/>
</div>
</template>
<script>
export default {
mounted(){
Vue.config.keyCodes.w= 87
},
data(){
return {
counter:0
}
},
methods:{
shangxiashu(){
this.isShow++;
},
add:function(e,num){
num = num || 1;
// this代表vue的實(shí)例
this.counter += num;
// 當(dāng)前事件的事件對(duì)象
console.log(e);
},
keyHandle(){
console.log("按下了w")
}
},
mounted(){
window.items = this.items;
}
}
</script>
效果顯而易見(jiàn),同時(shí)vue2.1新增了系統(tǒng)修飾鍵(個(gè)人覺(jué)得就是幾個(gè)特殊的鍵)
- .ctrl
- .alt
- .shift
- .meta 鍵盤(pán)上的有win圖標(biāo)的鍵,mac就是花鍵
所以我們可以這樣玩
<template>
<div id="app">
<h1>{{counter}}</h1>
<a target="_blank" v-on:click.prevent.ctrl="add">按住ctrl點(diǎn)擊+1</a>
<router-view/>
</div>
</template>
<script>
export default {
mounted(){
Vue.config.keyCodes.w= 87
},
data(){
return {
counter:0
}
},
methods:{
shangxiashu(){
this.isShow++;
},
add:function(e,num){
num = num || 1;
// this代表vue的實(shí)例
this.counter += num;
}
},
mounted(){
window.items = this.items;
}
}
</script>
這樣的話(huà)就是不按住ctrl鍵不會(huì)響應(yīng)點(diǎn)擊事件的,還是一個(gè)非常親名的功能
我這樣就可以制作一個(gè)按住alt+c清楚輸入的內(nèi)容的一個(gè)輸入框
<template>
<div id="app">
<h1>{{counter}}</h1>
<a target="_blank" v-on:click.prevent.ctrl="add">按住ctrl點(diǎn)擊+1</a>
<br />
<input type="text" v-on:keyup.alt.67="clear($event)">
<router-view/>
</div>
</template>
<script>
export default {
mounted(){
Vue.config.keyCodes.w= 87
},
data(){
return {
counter:0
}
},
methods:{
shangxiashu(){
this.isShow++;
},
add:function(e,num){
num = num || 1;
// this代表vue的實(shí)例
this.counter += num;
},
clear(e){
e.target.value = "";
}
},
mounted(){
console.log(1);
window.items = this.items;
}
}
</script>
時(shí)候我們就可以驕傲的說(shuō)我們的輸入框有快捷鍵了
這里說(shuō)一下上面綁定keyup時(shí)使用的是.alt.67
后面的67是鍵盤(pán)上的c鍵的鍵盤(pán)碼我們綁定事件時(shí)也可以直接將鍵盤(pán)碼點(diǎn)上去,然后就是
v-on有一個(gè)縮寫(xiě) 比如v-on:click.prevent.ctrl
--->@:click.prevent.ctrl
可以直接縮寫(xiě)為@
vue2.5新增了一個(gè)用于鍵盤(pán)事件的修飾符
- exact
我這里直接就將官方的說(shuō)明粘過(guò)來(lái)了,一看就懂
<!-- 即使 Alt 或 Shift 被一同按下時(shí)也會(huì)觸發(fā) -->
<button @click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的時(shí)候才觸發(fā) -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<!-- 沒(méi)有任何系統(tǒng)修飾符被按下的時(shí)候才觸發(fā) -->
<button @click.exact="onClick">A</button>
vue2.2新增了鼠標(biāo)的按鍵修飾符
- .left
- .right
- .middle
那么我們就可以實(shí)現(xiàn)一個(gè)右鍵點(diǎn)擊+1的操作了
<template>
<div id="app">
<h1>{{counter}}</h1>
<a target="_blank" v-on:click.prevent.right.exact="add">點(diǎn)擊右鍵+1(以使用exact不要按其他的鍵否則無(wú)效)</a>
<br />
<input type="text" v-on:keyup.alt.67="clear($event)">
<router-view/>
</div>
</template>
<script>
export default {
mounted(){
Vue.config.keyCodes.w= 87
},
data(){
return {
counter:0
}
},
methods:{
shangxiashu(){
this.isShow++;
},
add:function(e,num){
num = num || 1;
// this代表vue的實(shí)例
this.counter += num;
},
clear(e){
e.target.value = "";
}
},
mounted(){
console.log(1);
window.items = this.items;
}
}
</script>
那么這時(shí)候就發(fā)現(xiàn)只有右鍵點(diǎn)擊才能夠+1了(而左鍵點(diǎn)擊又坑爹的跳轉(zhuǎn)了),并且當(dāng)我們按住ctrl之類(lèi)的系統(tǒng)修飾鍵是無(wú)效的
v-model --> 數(shù)據(jù)的雙向綁定
我們快速實(shí)現(xiàn)一個(gè)表單認(rèn)證
<template>
<div id="app">
<div>
<form action="#">
<p>
<input type="text" v-model="username" placeholder="please type username">
</p>
<p>
<input type="password" v-model="userpwd" placeholder="please type password">
</p>
<input type="submit" value="sumit" @click.prevent="submit">
</form>
</div>
<router-view/>
</div>
</template>
<script>
export default {
data() {
return {
username: "",
userpwd: ""
};
},
methods: {
submit() {
console.log(`username:${this.username}--->password:${this.userpwd}`);
}
}
};
</script>
同時(shí)此指令還有一些修飾符就如v-on一樣
- .lazy 默認(rèn)使用input中的input事件進(jìn)行雙向綁定,使用此修飾符可將雙向綁定的事件改為change(當(dāng)輸入框失去焦點(diǎn)時(shí)更新)
- .number 默認(rèn)輸入的任何字符都是字符串,如果你需要讓用戶(hù)輸入的數(shù)字轉(zhuǎn)換成number類(lèi)型可加
- .trim 可以自動(dòng)去除用戶(hù)輸入字符串中的首尾空格
其他指令介紹
(v-if,v-model,v-for,v-on)
v-text --> 指令用于綁定后面值到元素的innerText中
<template>
<div id="app">
<div v-text="msg"></div>
<router-view/>
</div>
</template>
<script>
export default {
data() {
return {
msg:"<h1>Hello Vue</h1>"
};
},
methods: {
}
};
</script>
這樣就完成了綁定,這個(gè)指令會(huì)替換掉原本元素中的內(nèi)容(元素中的所有內(nèi)容都會(huì)被替換包括子元素)
你會(huì)發(fā)現(xiàn)這個(gè)指令解析出來(lái)就是一個(gè)單純的字符串
v-html
這個(gè)指令可以解決上面的問(wèn)題,v-text中的內(nèi)容無(wú)論是什么都會(huì)被解析為字符串
v-html與v-text類(lèi)似,但是會(huì)將類(lèi)似于html的代碼解析為html嵌入元素中
<template>
<div id="app">
<div v-html="msg">
<p>lfjlsfsd </p>
</div>
<router-view/>
</div>
</template>
<script>
export default {
data() {
return {
msg:"<h1>Hello Vue</h1>"
};
},
methods: {
}
};
</script>
上述兩個(gè)指令都會(huì)替換掉元素中的所有內(nèi)容,但是很靈活可以通過(guò)函數(shù)或者一些簡(jiǎn)單的表達(dá)式動(dòng)態(tài)的求值,同時(shí)替換元素中的所有內(nèi)容也算是一個(gè)缺點(diǎn),我們可以使用vue插值表達(dá)式就是雙大括號(hào){{}}進(jìn)行動(dòng)態(tài)解析某些值,插值表達(dá)式不會(huì)替換原宿所有內(nèi)容,解析后的值就會(huì)替換在插值表達(dá)式所在的位置,插值表達(dá)式計(jì)算出的值為純字符串
<template>
<div id="app">
<div>
{{msg}}
</div>
<router-view/>
</div>
</template>
<script>
export default {
data() {
return {
msg:"<h1>Hello Vue</h1>"
};
},
methods: {
}
};
</script>
有一種情況我們可以預(yù)見(jiàn)
<template>
<div id="app">
<div>
<h1>Hello {{msg}}</h1>
</div>
<router-view/>
</div>
</template>
<script>
export default {
data() {
return {
msg:"Vue"
};
},
methods: {
}
};
</script>
這時(shí)候渲染是沒(méi)有任何問(wèn)題的
但是當(dāng)我們進(jìn)入調(diào)試模式調(diào)低網(wǎng)速時(shí)
這個(gè)地方更改為slow 3G
無(wú)語(yǔ)了,我這里是vue cli服務(wù)的情況
模擬不了那種情況,我就直接說(shuō)了吧,大家應(yīng)該能夠明白是什么情況吧
當(dāng)網(wǎng)速慢的時(shí)候我們寫(xiě)的插值表達(dá)式可能就會(huì)原樣輸出(就是該咋樣咋樣的輸出,因?yàn)檫@個(gè)時(shí)候網(wǎng)速慢vue.js這個(gè)文件可能還沒(méi)有請(qǐng)求過(guò)來(lái),因?yàn)榭隙ㄊ莾?yōu)先載入html文件再?gòu)膆tml文件中的script標(biāo)簽中載入vuejs文件)這個(gè)時(shí)候就暴露了,等到vuejs請(qǐng)求下來(lái)才會(huì)更新這些插值表達(dá)式,但是這樣就有點(diǎn)坑了,不是很美觀(guān),所以vue提供了一個(gè)指令用來(lái)控制此等情況
v-cloak
這個(gè)指令就非常簡(jiǎn)單,就是插值表達(dá)式暴露到屏幕的時(shí)候可以為對(duì)應(yīng)的元素暴露一個(gè)v-cloak屬性
(我這個(gè)真的不行,模擬不了
從圖片中可以看出,這是服務(wù)端渲染html結(jié)束然后返回給客戶(hù)端的,這應(yīng)該是服務(wù)端渲染的(個(gè)人猜測(cè),但是8,9了))
就是當(dāng)有了這個(gè)v-cloak屬性時(shí)我們就可以寫(xiě)類(lèi)似的樣式
因?yàn)檫@個(gè)屬性在插值表達(dá)式解析完畢之后會(huì)自動(dòng)去除(removeAttr..)
v-once
顧名思義不說(shuō)了
(就是當(dāng)這個(gè)元素第一次渲染之后,之后的數(shù)據(jù)變化觸發(fā)render方法更新視圖時(shí)這個(gè)元素不會(huì)在被刷新了永遠(yuǎn)也不會(huì)了)
<template>
<div id="app">
<div>
<h1 v-once>Hello {{msg}}</h1>
<input type="button" value="改變msg的值" @click="changeValue">
</div>
<router-view/>
</div>
</template>
<script>
export default {
data() {
return {
msg:"Vue"
};
},
methods: {
changeValue(){
console.log(1)
this.msg += "zzz"
console.log(this.msg);
}
}
};
</script>
<style lang="less">
div[v-cloak]{
display: none;
}
</style>
你就會(huì)發(fā)現(xiàn)無(wú)論你怎么點(diǎn)擊按鈕,試圖也不會(huì)刷新因?yàn)槌跏蓟芷谝呀?jīng)執(zhí)行過(guò)一次render了,所以以后不會(huì)刷新視圖了,哪怕數(shù)據(jù)怎么發(fā)生變化
除非你直接人肉修改此選項(xiàng)
<template>
<div id="app">
<div>
<h1 v-once ref="aa">Hello {{msg}}</h1>
<input type="button" value="改變msg的值" @click="changeValue">
</div>
<router-view/>
</div>
</template>
<script>
export default {
data() {
return {
msg:"Vue"
};
},
methods: {
changeValue(){
console.log(1)
this.msg += "zzz"
console.log(this.msg);
// 暴力修改
this.$refs.aa.innerText = "Hello World"
}
}
};
</script>
<style lang="less">
div[v-cloak]{
display: none;
}
</style>
哈哈,一般是不會(huì)這么做的,因?yàn)檫@是不被vue提倡的
v-pre
跳過(guò)這個(gè)元素和它的子元素的編譯過(guò)程酌畜≡跚簦可以用來(lái)顯示原始 Mustache 標(biāo)簽。跳過(guò)大量沒(méi)有指令的節(jié)點(diǎn)會(huì)加快編譯桥胞。
例子就是
<template>
<div id="app">
<div>
<h1 v-pre ref="aa" v-text="這是一個(gè)測(cè)試的文字">Hello {{msg}}</h1>
<p>你看到Vue算我輸</p>
<input type="button" value="test" @click="changeValue">
</div>
<router-view/>
</div>
</template>
<script>
export default {
data() {
return {
msg:"Vue"
};
},
methods: {
changeValue(){
console.log(1)
this.msg += "zzz"
console.log(this.msg);
// 暴力修改
console.log(this.$refs.aa);
}
}
};
</script>
<style lang="less">
div[v-cloak]{
display: none;
}
</style>
就像于Vue不會(huì)管這個(gè)元素了,這個(gè)元素的所有都跟普通html一樣了,這些在vue中認(rèn)識(shí)的指令也無(wú)用了,瀏覽器不能認(rèn)識(shí)的一切都不會(huì)起作用了
v-slot
這個(gè)東西后期完善更新,需要先了解組件
(date:2019-06-23 13:21:54)
組件基礎(chǔ)
聲明一個(gè)組件的最簡(jiǎn)單的方法就是通過(guò)Vue.component方法,然后就是我想說(shuō)的就是因?yàn)関ue-cli聲明組件用的是另外一種方法所以我們只有新建一個(gè)文件夾不用打包工具來(lái)寫(xiě)了,我這里直接去node_modules文件夾下的vue中將dist目錄下的vuejs復(fù)制過(guò)來(lái)的,然后直接新建一個(gè)index.html文件夾如下
大家像我這樣寫(xiě)完這個(gè)index.html之后使用file協(xié)議在瀏覽器中打開(kāi)這個(gè)文件就會(huì)發(fā)現(xiàn)我之前所說(shuō)的這種情況了,現(xiàn)在我們將網(wǎng)絡(luò)改為slow 3G然后刷新,反正你就是會(huì)看到插值表達(dá)式會(huì)一閃而過(guò)就這個(gè)情況
算了其他的不多說(shuō)了,我們首先看一下最基礎(chǔ)的定義組件
<body>
<div id="app">
{{msg}}
<hello-c></hello-c>
</div>
<script src="../lib/vue.js"></script>
<script>
Vue.component("hello-c",{
template:"<div>hello 組件</div>"
})
new Vue({
data:{
msg:"Hello Vue"
}
}).$mount("#app");
</script>
</body>
然后我們就可以在瀏覽器中看見(jiàn)hello組件了,這樣便意味著我們已經(jīng)自己制作了一個(gè)組件了,大家可能在制作組件的時(shí)候有一點(diǎn)不爽的就是為什么template寫(xiě)html代碼沒(méi)有代碼提示什么都要手寫(xiě)就是不爽對(duì)吧,所以我們可以使用這種方法定義template
<body>
<div id="app">
{{msg}}
<hello-c></hello-c>
</div>
<template id="hello-c">
<div>
<h1>hello組件</h1>
</div>
</template>
<script src="../lib/vue.js"></script>
<script>
Vue.component("hello-c",{
template:"#hello-c"
})
new Vue({
data:{
msg:"Hello Vue"
}
}).$mount("#app");
</script>
</body>
我們可以直接將組件的模板寫(xiě)在template標(biāo)簽中,這樣就又有了我們熟悉的代碼提示了(是不是非常棒),同時(shí)我們自定義的組件就跟新建Vue的實(shí)例一樣大多數(shù)關(guān)于組件的方法都會(huì)有比如說(shuō)data啊methods啊都有
這樣就繼承了Vue的大多數(shù)屬性
<body>
<div id="app">
{{msg}}
<Counter></Counter>
</div>
<template id="counter">
<div>
<button type="button" @click="count++">你一共點(diǎn)擊了我{{count}}次</button>
</div>
</template>
<script src="../lib/vue.js"></script>
<script>
Vue.component("Counter",{
template:"#counter",
data(){
return {
count:0
}
}
})
new Vue({
data:{
msg:"Hello Vue"
}
}).$mount("#app");
</script>
</body>
組件的生命周期
我覺(jué)得關(guān)于Vue組件的生命周期只需要一張圖就可以明白了
直接就將Vue官方的一張圖fetch過(guò)來(lái)了
- beforeCreate() 在組件創(chuàng)建之前此時(shí)無(wú)法訪(fǎng)問(wèn)vue實(shí)例(this)
- created() 創(chuàng)建完成之后已經(jīng)可以訪(fǎng)問(wèn)this實(shí)例和數(shù)據(jù)
- beforeMount() 在組件掛載到視圖之前
- mounted() 組件已經(jīng)掛載到視圖上以后
- beforeUpdate() 改變數(shù)據(jù)觸發(fā)視圖更新之前
- updated() 視圖已經(jīng)更新后
- beforeDestroy() 組件將要被銷(xiāo)毀之前(下樹(shù),display:none不算,是銷(xiāo)毀)
- destroyed() 組件銷(xiāo)毀之后
這就是Vue組件的生命周期了
<body>
<div id="app">
{{msg}}
<Counter></Counter>
</div>
<template id="counter">
<div>
<button type="button" @click="count++">你一共點(diǎn)擊了我{{count}}次</button>
</div>
</template>
<script src="../lib/vue.js"></script>
<script>
Vue.component("Counter",{
template:"#counter",
data(){
return {
count:0
}
},
beforeCreate(){
console.log("beforeCreate");
},
created(){
console.log("created");
},
beforeMount(){
console.log("beforeMount");
},
mounted(){
console.log("mounted");
},
beforeUpdate(){
console.log("beforeUpdate");
},
updated(){
console.log("updated");
},
beforeDestroy(){
console.log("beforeDestroy");
},
destroyed(){
console.log("destroyed");
}
})
new Vue({
data:{
msg:"Hello Vue"
}
}).$mount("#app");
</script>
</body>
關(guān)于組件的生命周期就到這里了
組件的傳參
那么我們使用一個(gè)自定義組件肯定是需要?jiǎng)討B(tài)的顯示某些內(nèi)容的,這時(shí)候我們就需要傳遞一些參數(shù)過(guò)去了,那么組件之間是怎么傳參的呢?
通過(guò)props就可以實(shí)現(xiàn)組件之間的參數(shù)傳遞
我們看一下
<body>
<div id="app">
<h1>{{count}}</h1>
<!--
第一個(gè)count為props中聲明的可被接收的props,值為父組件中的count
這里使用v-bind將不然count中的內(nèi)容不會(huì)被當(dāng)做js代碼解析
v-bind:count 縮寫(xiě)為 :count 縮寫(xiě) :
-->
<input type="button" value="點(diǎn)我+1" @click="count++">
<Counter :count="count"></Counter>
</div>
<template id="counter">
<div>
<button type="button" @click="count++">你一共點(diǎn)擊了我{{count}}次</button>
</div>
</template>
<script src="../lib/vue.js"></script>
<script>
Vue.component("Counter",{
template:"#counter",
// props屬性列舉了可被接收的參數(shù)
props:['count']
})
new Vue({
data:{
count:0
}
}).$mount("#app");
</script>
</body>
那么vue是不建議也不可以在子組件中直接操作props的,因?yàn)榇藀rops為父組件傳遞過(guò)來(lái)的,子組件無(wú)論怎么改變此props的值,當(dāng)父組件觸發(fā)虛擬DOM刷新頁(yè)面時(shí)都會(huì)重新傳遞props意味著子組件改變的props并不會(huì)長(zhǎng)久的儲(chǔ)存下去,所以Vue提供了一個(gè)類(lèi)似于自定義事件的這種機(jī)制
<body>
<div id="app">
<h1>{{count}}</h1>
<!--
第一個(gè)count為props中聲明的可被接收的props,值為父組件中的count
這里使用v-bind將不然count中的內(nèi)容不會(huì)被當(dāng)做js代碼解析
v-bind:count 縮寫(xiě)為 :count 縮寫(xiě) :
-->
<input type="button" value="點(diǎn)我+1" @click="count++">
<!-- 通過(guò)在父組件中使用v-on指令監(jiān)聽(tīng)自定義事件名 -->
<Counter :count="count" @mclick="count++"></Counter>
</div>
<template id="counter">
<div>
<!-- 使用$emit觸發(fā)一個(gè)自定義事件的事件名 -->
<button type="button" @click="$emit('mclick')">你一共點(diǎn)擊了我{{count}}次</button>
</div>
</template>
<script src="../lib/vue.js"></script>
<script>
Vue.component("Counter",{
template:"#counter",
// props屬性列舉了可被接收的參數(shù)
props:['count']
})
new Vue({
data:{
count:0
}
}).$mount("#app");
</script>
</body>
兄弟們看注釋吧,代碼結(jié)合注釋這還不懂
(廢話(huà)一大堆:說(shuō)明不適合自學(xué)啊,這里說(shuō)一下我是自學(xué)的前端開(kāi)發(fā)的,不是不愿意去培訓(xùn)機(jī)構(gòu),相反其實(shí)還是挺想去的,只是因?yàn)榧彝サ脑?最主要是沒(méi)錢(qián)啊)所以只能苦逼的自學(xué)了,其實(shí)我想說(shuō)的是不管是去培訓(xùn)機(jī)構(gòu)還是自學(xué)還是在學(xué)校我想說(shuō)的就一點(diǎn),你永遠(yuǎn)也不可能會(huì)一直有老師帶著你,技術(shù)是在不斷的迭代的,誰(shuí)也不知道明天又有哪種框架哪種解決方案火了,就比如webassembly一樣,誰(shuí)知道那天會(huì)火說(shuō)不定到時(shí)候的前端真的可以說(shuō)又是一場(chǎng)革命了,你想到時(shí)候的前端有接近原生的代碼執(zhí)行效率,無(wú)論是開(kāi)發(fā)網(wǎng)站還是配合electron或者nw開(kāi)發(fā)跨平臺(tái)的桌面應(yīng)用或者搭配react native或uni-app或weex等等開(kāi)發(fā)移動(dòng)端的軟件,毫無(wú)疑問(wèn)的一點(diǎn)就是以后的前端誰(shuí)也說(shuō)不準(zhǔn),所以我覺(jué)得自學(xué)反而是好的,自學(xué)的過(guò)程中雖然很難但反而是最容易學(xué)通學(xué)透的因?yàn)槲覀儧](méi)有人為我們解答問(wèn)題,每一個(gè)問(wèn)題都是需要自己去研究解決的,可以說(shuō)每一個(gè)人都在學(xué)習(xí),不過(guò)有的人是在學(xué)校學(xué)習(xí)有的人在工作的時(shí)候?qū)W習(xí),或者說(shuō)每個(gè)人一直都在學(xué)習(xí),比如你剛出生你不學(xué)走路,不學(xué)說(shuō)話(huà),等等...)
咳咳扯遠(yuǎn)了,總而言之大家可以想象一下以后的前端是一個(gè)怎么樣的情況,現(xiàn)在webassembly已經(jīng)支持c/c++/rust語(yǔ)言編譯為asm.js這種編譯的JavaScript代碼進(jìn)行在瀏覽器端執(zhí)行了
我們接著說(shuō)一下,子組件通過(guò)自定義事件將自己的需求暴露給父組件,由父組件負(fù)責(zé)數(shù)據(jù)的更新(值是由父組件傳遞下來(lái)的,不父組件維護(hù)誰(shuí)維護(hù))
那么我想要求子組件點(diǎn)擊按鈕不跟父組件的按鈕一樣了,我要點(diǎn)擊子組件的按鈕我想+多少就多少應(yīng)該怎么辦呢?
那么我們繼續(xù)完善代碼
<body>
<div id="app">
<h1>{{count}}</h1>
<!--
第一個(gè)count為props中聲明的可被接收的props,值為父組件中的count
這里使用v-bind將不然count中的內(nèi)容不會(huì)被當(dāng)做js代碼解析
v-bind:count 縮寫(xiě)為 :count 縮寫(xiě) :
-->
<input type="button" value="點(diǎn)我+1" @click="count++">
<!-- 通過(guò)在父組件中使用v-on指令監(jiān)聽(tīng)自定義事件名 -->
<!-- 這里的$event就是傳遞過(guò)來(lái)的參數(shù) -->
<Counter :count="count" @mclick="count+=$event"></Counter>
</div>
<template id="counter">
<div>
<!-- 使用$emit觸發(fā)一個(gè)自定義事件的事件名 -->
<!-- 添加一個(gè)輸入框,輸入多少就+多少的count -->
<p><input type="text" v-model.number="sCount"></p>
<!-- 可以通過(guò)第二個(gè)參數(shù)給父組件的handle傳遞參數(shù) -->
<button type="button" @click="$emit('mclick',sCount)">你一共點(diǎn)擊了我{{count}}次</button>
</div>
</template>
<script src="../lib/vue.js"></script>
<script>
Vue.component("Counter",{
template:"#counter",
// props屬性列舉了可被接收的參數(shù)
props:['count'],
data(){
return {
sCount:0
}
}
})
new Vue({
data:{
count:0
},
methods:{
addHandle(num){
this.count+=num;
}
}
}).$mount("#app");
</script>
</body>
這時(shí)候我們就可以通過(guò)自定義事件傳參的方式,來(lái)實(shí)現(xiàn)這個(gè)需求了,如果傳遞了多個(gè)參數(shù)請(qǐng)?jiān)诟附M件中@自定義事件后傳入一個(gè)函數(shù)接收多個(gè)參數(shù)
這里我又想實(shí)現(xiàn)一個(gè)自定義的輸入框,但是當(dāng)我輸入v-model的時(shí)候我發(fā)現(xiàn)這個(gè)指令失效了?
<body>
<div id="app">
<h1>{{msg}}</h1>
<custom-input v-model="msg"></custom-input>
</div>
<template id="cinput">
<input type="text">
</template>
<template id="counter">
<div>
<!-- 使用$emit觸發(fā)一個(gè)自定義事件的事件名 -->
<!-- 添加一個(gè)輸入框,輸入多少就+多少的count -->
<p><input type="text" v-model.number="sCount"></p>
<!-- 可以通過(guò)第二個(gè)參數(shù)給父組件的handle傳遞參數(shù) -->
<button type="button" @click="$emit('mclick',sCount,'hello')">你一共點(diǎn)擊了我{{count}}次</button>
</div>
</template>
<script src="../lib/vue.js"></script>
<script>
Vue.component("Counter",{
template:"#counter",
// props屬性列舉了可被接收的參數(shù)
props:['count'],
data(){
return {
sCount:0
}
}
})
Vue.component("custom-input",{
template:"#cinput",
props:['value']
})
new Vue({
data:{
count:0,
msg:"Hello"
},
methods:{
addHandle(num,c){
console.log(c);
this.count+=num;
}
}
}).$mount("#app");
因?yàn)槲覀儾荒苤苯咏o自定義組件綁定v-model而是應(yīng)該給組件里面的元素綁定,但是如果我們給子組件中的input標(biāo)簽綁定v-model的話(huà)就會(huì)出現(xiàn)(因?yàn)殡p向綁定的值是父親的,但是與之綁定的視圖確實(shí)兒子的)父組件一但刷新props重新賦值為父組件的傳遞過(guò)來(lái)的值了,這個(gè)時(shí)候應(yīng)該怎么班呢?我們就應(yīng)該自己實(shí)現(xiàn)一個(gè)v-model了
通過(guò)打印發(fā)現(xiàn)我們是能夠得到v-model綁定的msg值的
然后我們就需要思考如何讓這個(gè)值與之產(chǎn)生關(guān)聯(lián),首先無(wú)論如何我們肯定需要先v-bind先單向?qū)?shù)據(jù)與視圖建立聯(lián)系才行,通過(guò)v-bind我們已經(jīng)能夠使input標(biāo)簽顯示msg的值了
然后我記得v-model的時(shí)候我有提到過(guò),v-model的雙向數(shù)據(jù)綁定是通過(guò)元素的input事件進(jìn)行綁定的可以通過(guò)lazy修飾符改為change事件,所以這個(gè)時(shí)候我要干什么大家應(yīng)該都知道了吧
<body>
<div id="app">
<h1>{{msg}}</h1>
<custom-input v-model="msg"></custom-input>
</div>
<template id="cinput">
<input type="text" :value="value" @input="$emit('input', $event.target.value)">
</template>
<template id="counter">
<div>
<!-- 使用$emit觸發(fā)一個(gè)自定義事件的事件名 -->
<!-- 添加一個(gè)輸入框,輸入多少就+多少的count -->
<p><input type="text" v-model.number="sCount"></p>
<!-- 可以通過(guò)第二個(gè)參數(shù)給父組件的handle傳遞參數(shù) -->
<button type="button" @click="$emit('mclick',sCount,'hello')">你一共點(diǎn)擊了我{{count}}次</button>
</div>
</template>
<script src="../lib/vue.js"></script>
<script>
Vue.component("Counter",{
template:"#counter",
// props屬性列舉了可被接收的參數(shù)
props:['count'],
data(){
return {
sCount:0
}
}
})
Vue.component("custom-input",{
template:"#cinput",
props:['value'],
created(){
console.log(this.value);
}
})
new Vue({
data:{
count:0,
msg:"Hello"
},
methods:{
addHandle(num,c){
console.log(c);
this.count+=num;
}
}
}).$mount("#app");
</script>
</body>
使用v-on綁定input事件在代碼中emit一個(gè)input事件到自定義組件身上,這樣就可以被v-model接收到了,從而完成雙向數(shù)據(jù)綁定,懂了嗎?
Vue提供了一個(gè)自定義組件用于動(dòng)態(tài)顯示我們的自定義組件的
<body>
<div id="app">
<h1>{{msg}}</h1>
<!--
真的不想寫(xiě)注釋了,但是我還是要寫(xiě),vue指令后面引號(hào)中的字符會(huì)被當(dāng)做js代碼解析就像執(zhí)行eval一樣
is屬性制定了一個(gè)要顯示的自定義組件名稱(chēng)
-->
<input type="button" value="點(diǎn)我切換組件" @click="type == 'custom-input' ? type = 'Counter' : type = 'custom-input'">
<component :is="type"></component>
</div>
<template id="cinput">
<input type="text" :value="value" @input="$emit('input', $event.target.value)">
</template>
<template id="counter">
<div>
<!-- 使用$emit觸發(fā)一個(gè)自定義事件的事件名 -->
<!-- 添加一個(gè)輸入框,輸入多少就+多少的count -->
<p><input type="text" v-model.number="sCount"></p>
<!-- 可以通過(guò)第二個(gè)參數(shù)給父組件的handle傳遞參數(shù) -->
<button type="button" @click="$emit('mclick',sCount,'hello')">你一共點(diǎn)擊了我{{count}}次</button>
</div>
</template>
<script src="../lib/vue.js"></script>
<script>
Vue.component("Counter",{
template:"#counter",
// props屬性列舉了可被接收的參數(shù)
props:['count'],
data(){
return {
sCount:0
}
}
})
Vue.component("custom-input",{
template:"#cinput",
props:['value'],
beforeDestroy(){
console.log('beforeDestroy')
},
destroyed(){
console.log("destroyed")
}
})
new Vue({
data:{
count:0,
msg:"Hello",
type:"custom-input"
},
methods:{
addHandle(num,c){
console.log(c);
this.count+=num;
}
}
}).$mount("#app");
</script>
</body>
component標(biāo)簽用于控制渲染我們的組件通過(guò)is屬性動(dòng)態(tài)渲染不同的組件已達(dá)到更好的交互效果,這里組件的切換是上樹(shù)下樹(shù)級(jí)別的
因?yàn)榻M件執(zhí)行了生命周期中的destroyed方法
(那么關(guān)于Vue的基礎(chǔ)就已經(jīng)說(shuō)完了恳守,接下來(lái)就是Vue深入組件的一個(gè)部分了)
深入了解組件
組件注冊(cè)
首先關(guān)于組件命名的要求考婴,按照Vue官方所說(shuō)的那樣組件命名有兩種方式
第一種:custom-input 就是字母之間或者組件名之中必須出現(xiàn)一個(gè)橫線(xiàn)用以區(qū)分原生標(biāo)簽
第二種:首字母大寫(xiě) CustomInput 駝峰命名法,大家隨意喜歡那種都是可以的
然后就是到目前為止我們注冊(cè)組件還是使用的是Vue.component這種注冊(cè)組件的方式是全局注冊(cè)的催烘,全局注冊(cè)的組件無(wú)論在哪一種層級(jí)還是哪一個(gè)Vue實(shí)例都是可以使用的沥阱,接下來(lái)給大家說(shuō)說(shuō)如何局部注冊(cè)組件,局部注冊(cè)的組件只能在當(dāng)前注冊(cè)的組件中使用
<body>
<div id="app">
{{msg}}
<!-- 這里Vue會(huì)自動(dòng)將后面首字母大寫(xiě)的字母改為小寫(xiě)并且加上橫線(xiàn) -->
<Custom-div></Custom-div>
</div>
<script src="./lib/vue.js"></script>
<script>
var CustomInput = {
template:`
<div>我是自定義input組件</div>
`
}
var CustomDiv = {
template:`
<div>我是自定義div組件</div>
`,
components:{
CustomInput
}
}
var vm = new Vue({
el:"#app",
data() {
return {
msg:"Hello Vue"
}
},
components:{
// 鍵值對(duì)相同可以直接寫(xiě)一個(gè)在這里
CustomDiv
}
})
</script>
</body>
然后我們?cè)谶@里一共定義了兩個(gè)組件其中一個(gè)組件注冊(cè)為另一個(gè)組件的子組件伊群,且這個(gè)組件注冊(cè)到Vue實(shí)例上考杉,那么我們接下來(lái)直接在html中訪(fǎng)問(wèn)這個(gè)CustomInput組件看看會(huì)是什么情況,你會(huì)發(fā)現(xiàn)Vue不出意外的報(bào)錯(cuò)了
<div id="app">
{{msg}}
<!-- 這里Vue會(huì)自動(dòng)將后面首字母大寫(xiě)的字母改為小寫(xiě)并且加上橫線(xiàn) -->
<Custom-div></Custom-div>
<Custom-input></Custom-input>
</div>
那么我們接下來(lái)將這個(gè)組件放到customdiv組件中試試
<body>
<div id="app">
{{msg}}
<!-- 這里Vue會(huì)自動(dòng)將后面首字母大寫(xiě)的字母改為小寫(xiě)并且加上橫線(xiàn) -->
<Custom-div></Custom-div>
</div>
<script src="./lib/vue.js"></script>
<script>
var CustomInput = {
template: `
<h1>我是自定義Input組件</h1>
`
}
var CustomDiv = {
// 子組件放在html代碼中
template: `
<div>
<p>我是自定義div組件</p>
<Custom-input></Custom-input>
</div>
`,
components: {
CustomInput
}
}
var vm = new Vue({
el: "#app",
data() {
return {
msg: "Hello Vue"
}
},
components: {
// 鍵值對(duì)相同可以直接寫(xiě)一個(gè)在這里
CustomDiv
}
})
</script>
</body>
那么我們發(fā)現(xiàn)不出意外組件已經(jīng)正常渲染了在岂,因?yàn)閂ue實(shí)例的template已經(jīng)掛在到div#app上了奔则,所以我們的子組件一定要寫(xiě)在template上而不是那個(gè)標(biāo)簽中的內(nèi)部
Prop
HTML 中的特性名是大小寫(xiě)不敏感的,所以瀏覽器會(huì)把所有大寫(xiě)字符解釋為小寫(xiě)字符蔽午。這意味著當(dāng)你使用 DOM 中的模板時(shí)易茬,camelCase (駝峰命名法) 的 prop 名需要使用其等價(jià)的 kebab-case (短橫線(xiàn)分隔命名) 命名:
Vue.component('blog-post', {
// 在 JavaScript 中是 camelCase 的
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>'
}
<!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-title="hello!"></blog-post>
那么這個(gè)限制在字符串模板是沒(méi)有限制的,前面也說(shuō)了template屬性可以給一個(gè)選擇器用來(lái)定位html template標(biāo)簽中的內(nèi)容,這就是所謂的DOM模板,字符串模板就是我下面這樣template之后直接手寫(xiě)這個(gè)模板,但是你也知道的沒(méi)有代碼提示純手寫(xiě)賊難受
<body>
<div id="app">
{{msg}}
<!-- 這里Vue會(huì)自動(dòng)將后面首字母大寫(xiě)的字母改為小寫(xiě)并且加上橫線(xiàn) -->
<Custom-div></Custom-div>
</div>
<script src="./lib/vue.js"></script>
<script>
var CustomInput = {
template: `
<h1>我是自定義Input組件</h1>
`,
props:["mData"],
// 字符串模板中不需要m-data
mounted() {
console.log(this.mData);
},
}
var CustomDiv = {
// 子組件放在html代碼中
template: `
<div>
<p>我是自定義div組件</p>
<Custom-input mData="Hello"></Custom-input>
</div>
`,
components: {
CustomInput
}
}
var vm = new Vue({
el: "#app",
data() {
return {
msg: "Hello Vue"
}
},
components: {
// 鍵值對(duì)相同可以直接寫(xiě)一個(gè)在這里
CustomDiv
}
})
</script>
</body>
那么關(guān)于props首先要了解的就是組件中props屬性的類(lèi)型,前面我們寫(xiě)的props都是數(shù)組的形式出現(xiàn)的就像我上面貼的代碼一樣,其實(shí)props屬性還可以是一個(gè)對(duì)象,且看下方代碼
<body>
<div id="app">
<!-- 使用v-bind -->
<my-component :username="username" :password="password" :age="age" ></my-component>
</div>
<template id="mcom">
<div>
我接受的username是{{username}} 我接受的password是{{password}} <br>
我接受的age是{{age}}
</div>
</template>
<script src="./lib/vue.js"></script>
<script>
var vm = new Vue({
el: "#app",
data() {
return {
username:"小鋼炮",
password:"xiaogangpao",
// 故意寫(xiě)一個(gè)字符串
age:"18"
}
},
components:{
// 我這里只是把對(duì)象直接寫(xiě)在這里的,不會(huì)看不懂吧
myComponent:{
template:"#mcom",
props:{
// 那么這里就有三個(gè)props需要傳遞過(guò)來(lái)
// 名稱(chēng)就是鍵及老,類(lèi)型就是值
username:String,
password:String,
age:Number
}
}
}
})
</script>
</body>
那么我這里故意將age設(shè)置為字符串不出所料的是果真是報(bào)錯(cuò)了,出乎意料的是值還是顯示出來(lái)了
那么我將age重新改為number類(lèi)型的時(shí)候報(bào)錯(cuò)就消失了
就不貼代碼了,這是應(yīng)該知道怎么改的
那么我們可以直接這樣傳遞對(duì)象
<my-component :obj="mobj"></my-component>
<script>
var vm = new Vue({
el: "#app",
data() {
return {
mobj: {
username: "小鋼炮",
password: "xiaogangpao",
// 故意寫(xiě)一個(gè)字符串
age: 18
}
}
},
components: {
// 我這里只是把對(duì)象直接寫(xiě)在這里的抽莱,不會(huì)看不懂吧
myComponent: {
template: "#mcom",
props: ["obj"],
mounted() {
console.log(this.obj)
},
}
}
})
</script>
然后可能我們需要傳遞對(duì)象的屬性進(jìn)去,那么vue就提供了一個(gè)非常簡(jiǎn)便的方法,那么可能我們正常需要這樣寫(xiě)
<my-component :username="mobj.username" :password="mobj.password" :age="mobj.age" ></my-component>
那么Vue就提供了一個(gè)簡(jiǎn)單的方法快速通過(guò)props傳遞一個(gè)對(duì)象的所有屬性
<my-component v-bind="mobj" ></my-component>
<script>
var vm = new Vue({
el: "#app",
data() {
return {
mobj: {
username: "小鋼炮",
password: "xiaogangpao",
// 故意寫(xiě)一個(gè)字符串
age: 18
}
}
},
components: {
// 我這里只是把對(duì)象直接寫(xiě)在這里的,不會(huì)看不懂吧
myComponent: {
template: "#mcom",
// 這里就可以接收所有對(duì)象的屬性
props: ["username","password","age"],
mounted() {
// 這里就可以進(jìn)行打印了
console.log(this.username,this.password,this.age)
},
}
}
})
</script>
大家記住,這種方法一定要在元素中props中寫(xiě)對(duì)象的屬性,不要傻乎乎的寫(xiě)對(duì)象的名字在哪里啊
單向數(shù)據(jù)流
大家應(yīng)該也知道我之前有說(shuō)過(guò)從父組件傳遞下來(lái)的props是不能進(jìn)行修改的,因?yàn)槟呐履憬邮盏膒rops如何修改,當(dāng)父組件觸發(fā)虛擬DOM時(shí)props又會(huì)進(jìn)行重新傳遞導(dǎo)致子組件重新渲染新的props值,這就是所謂的數(shù)據(jù)的單向流動(dòng),即數(shù)據(jù)總是自頂向下一層一層傳遞的(可能這時(shí)候大家就會(huì)想了,如果層級(jí)非常多的話(huà)豈不是從頂級(jí)組件傳遞下來(lái)會(huì)有那么幾個(gè)組件只負(fù)責(zé)props傳遞數(shù)據(jù)到目標(biāo)組件上,那么這個(gè)問(wèn)題我們需要使用vuex進(jìn)行解決,后面說(shuō)),這樣我們就非常清楚數(shù)據(jù)的一個(gè)流動(dòng),并且如果子組件修改了props那么數(shù)據(jù)的流向也不是很明確了,那么如果確實(shí)需要修改props呢?這里有兩種解決方案,要么就是直接將傳遞過(guò)來(lái)的props綁定到自己的data上
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
另一種方法呢就是直接通過(guò)計(jì)算屬性依賴(lài)這個(gè)props的值計(jì)算我們需要的值
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
兩種方法各有各的應(yīng)用場(chǎng)景,這里就不深入了解了
注意在 JavaScript 中對(duì)象和數(shù)組是通過(guò)引用傳入的骄恶,所以對(duì)于一個(gè)數(shù)組或?qū)ο箢?lèi)型的 prop 來(lái)說(shuō)食铐,在子組件中改變這個(gè)對(duì)象或數(shù)組本身將會(huì)影響到父組件的狀態(tài)。
props驗(yàn)證
這個(gè)驗(yàn)證跟react的prop-types
庫(kù)非常相似
props: {
// 基礎(chǔ)的類(lèi)型檢查 (`null` 和 `undefined` 會(huì)通過(guò)任何類(lèi)型驗(yàn)證)
// username:String,
// 多個(gè)可能的類(lèi)型
// age:[String,Number],
age: {
type: Number,
// 指定默認(rèn)值
default: 18
},
password: {
// 指定類(lèi)型
type: String,
// 必須傳遞
required: true
},
// 自定義驗(yàn)證函數(shù)
username: {
validator: function (value) {
// 這個(gè)值必須匹配下列字符串中的一個(gè)
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
// 也可以是一個(gè)帶有默認(rèn)值的對(duì)象
/* obj:{
type:Object,
// 對(duì)象或者數(shù)組的默認(rèn)值必須從一個(gè)factory function(工廠(chǎng)函數(shù))中取
default(){
return ["a",18,"c"]
}
} */
},
那么這個(gè)時(shí)候定義之后我們回到瀏覽器刷新就會(huì)發(fā)現(xiàn)
報(bào)錯(cuò)了,但是值也取到了,那么我們修改一下這個(gè)函數(shù)
// 自定義驗(yàn)證函數(shù)
username: {
validator: function (value) {
// 這個(gè)值必須匹配下列字符串中的一個(gè)
return ['小鋼炮', 'warning', 'danger'].indexOf(value) !== -1
}
}
這樣就ok了
注意那些 prop 會(huì)在一個(gè)組件實(shí)例創(chuàng)建之前進(jìn)行驗(yàn)證僧鲁,所以實(shí)例的屬性 (如 data虐呻、computed 等) 在 default 或 validator 函數(shù)中是不可用的。
類(lèi)型檢查
type 可以是下列原生構(gòu)造函數(shù)中的一個(gè):
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
額外的寞秃,type 還可以是一個(gè)自定義的構(gòu)造函數(shù)斟叼,并且通過(guò) instanceof 來(lái)進(jìn)行檢查確認(rèn)。例如春寿,給定下列現(xiàn)成的構(gòu)造函數(shù):
function Person (firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
//你可以使用:
Vue.component('blog-post', {
props: {
author: Person
}
})
//來(lái)驗(yàn)證 author prop 的值是否是通過(guò) new Person 創(chuàng)建的朗涩。
如果有這么一種情況,我們的組件是用來(lái)發(fā)布出去的,那么不知道用戶(hù)會(huì)怎么樣的去傳遞屬性,那么如果假如說(shuō)我使用我這個(gè)組件,我傳遞了一個(gè)我根本就沒(méi)有在props中定義過(guò)的屬性會(huì)怎么樣呢?
<body>
<div id="app">
<!-- 直接使用v-bind等于你需要傳遞所有屬性的對(duì)象 -->
<my-component v-bind="mobj" class="red" data-data-picker="activated"></my-component>
</div>
<template id="mcom">
<div class="black">
Hello my
</div>
</template>
<script src="./lib/vue.js"></script>
<script>
var vm = new Vue({
el: "#app",
data() {
return {
mobj: {
username: "小鋼炮",
password: "xiaogangpao",
age: 18
}
}
},
components: {
myComponent: {
template: "#mcom",
props: {
// 基礎(chǔ)的類(lèi)型檢查 (`null` 和 `undefined` 會(huì)通過(guò)任何類(lèi)型驗(yàn)證)
// username:String,
// 多個(gè)可能的類(lèi)型
// age:[String,Number],
age: {
type: Number,
// 指定默認(rèn)值
default: 18
},
password: {
// 指定類(lèi)型
type: String,
// 必須傳遞
required: true
},
// 自定義驗(yàn)證函數(shù)
username: {
validator: function (value) {
// 這個(gè)值必須匹配下列字符串中的一個(gè)
return ['小鋼炮', 'warning', 'danger'].indexOf(value) !== -1
}
}
// 也可以是一個(gè)帶有默認(rèn)值的對(duì)象
/* obj:{
type:Object,
// 對(duì)象或者數(shù)組的默認(rèn)值必須從一個(gè)factory function(工廠(chǎng)函數(shù))中取
default(){
return ["a",18,"c"]
}
} */
},
mounted() {
console.log(this.username, this.password, this.age)
},
}
}
})
</script>
那么你會(huì)發(fā)現(xiàn),上面我的這個(gè)自定義組件的根元素有一個(gè)已有的類(lèi)名,我在使用的時(shí)候傳遞了兩個(gè)沒(méi)有定義的屬性過(guò)去,我們看看html架構(gòu)是個(gè)什么情況
你會(huì)發(fā)現(xiàn)沒(méi)有被props定義的屬性都會(huì)直接繼承到組件的唯一根元素身上,如果你不想組件的根元素繼承這些屬性的話(huà),你可以在定義組件的時(shí)候加上
inheritAttrs: false
那么我這里加一下,刷新的時(shí)候發(fā)現(xiàn)
我們沒(méi)有被props接收的屬性都被忽略了,class合并了后面有說(shuō)原因,那么我們使用了這個(gè)選項(xiàng)之后就沒(méi)辦法使用傳遞過(guò)來(lái)的屬性了嗎?其實(shí)Vue將沒(méi)有被props接收的自定義屬性都封裝到了一個(gè)組件實(shí)例的$attrs屬性上,我們修改打印一下
mounted() {
console.log(this.$attrs)
},
<body>
<div id="app">
<base-input
v-model="username"
required
placeholder="type your username"
></base-input>
</div>
<template id="baseInput">
<!-- 將所有的$attrs屬性都傳遞到props上 -->
<label>
<input type="text"
v-bind="$attrs"
:value="value"
@input="$emit('input',$event.target.value)"
>
</label>
</template>
<script src="./lib/vue.js"></script>
<script>
var vm = new Vue({
el: "#app",
data() {
return {
username:"suiuye"
}
},
components: {
baseInput:{
template:"#baseInput",
inheritAttrs:false,
props:["value"]
}
}
})
</script>
</body>
Vue官方說(shuō)過(guò)的一句話(huà)還是非常哲學(xué)的,對(duì)哲學(xué):
這個(gè)模式允許你在使用基礎(chǔ)組件的時(shí)候更像是使用原始的 HTML 元素,而不會(huì)擔(dān)心哪個(gè)元素是真正的根元素,沒(méi)錯(cuò)你會(huì)發(fā)現(xiàn)上面的baseInput這個(gè)自定義的組件我完全當(dāng)成input再用,因?yàn)閷傩允裁吹亩际怯玫膇nput的屬性
注意 inheritAttrs: false 選項(xiàng)不會(huì)影響 style 和 class 的綁定绑改。意思就是說(shuō)加了這個(gè)屬性,class和style還是會(huì)被合并,不會(huì)被這個(gè)屬性影響,我上面就是一個(gè)活生生的例子
自定義事件
不同于組件和 prop谢床,事件名不存在任何自動(dòng)化的大小寫(xiě)轉(zhuǎn)換。而是觸發(fā)的事件名需要完全匹配監(jiān)聽(tīng)這個(gè)事件所用的名稱(chēng).
(事件名不存在組件名那樣的自動(dòng)轉(zhuǎn)換大寫(xiě)字母的名稱(chēng)加橫線(xiàn),事件名必須$emit()什么事件名就必須@什么事件名稱(chēng))
借用Vue官方的一句話(huà)
(不同于組件和 prop厘线,事件名不會(huì)被用作一個(gè) JavaScript 變量名或?qū)傩悦锻龋跃蜎](méi)有理由使用 camelCase 或 PascalCase 了。并且 v-on 事件監(jiān)聽(tīng)器在 DOM 模板中會(huì)被自動(dòng)轉(zhuǎn)換為全小寫(xiě) (因?yàn)?HTML 是大小寫(xiě)不敏感的)造壮,所以 v-on:myEvent 將會(huì)變成 v-on:myevent——導(dǎo)致 myEvent 不可能被監(jiān)聽(tīng)到覆履。因此,我們推薦你始終使用 kebab-case 的事件名。)
意思就是在template中@的事件名如果有大寫(xiě)字母會(huì)被自動(dòng)轉(zhuǎn)換成小寫(xiě)導(dǎo)致事件永遠(yuǎn)不會(huì)被監(jiān)聽(tīng)到,所以可以說(shuō)是自定義事件必須使用短橫線(xiàn)命名法類(lèi)似于 CustomEvent ---> custom-event ,除非是字符串模板否則可以說(shuō)是必須使用短橫線(xiàn)命名法
自定義組件的v-model --> vue2.2新增特性
比如說(shuō)我們自定義一個(gè)文本輸入框
<body>
<div id="app">
<!-- 這里將msg傳入了組件內(nèi) -->
<!-- <base-input v-model="msg"></base-input> <br> -->
<base-input v-model="msg"></base-input>
{{msg}}
</div>
<script src="../lib/vue.js"></script>
<script>
Vue.component("base-input",{
model:{
// 監(jiān)聽(tīng)根元素的value
prop:'value',
// 當(dāng)監(jiān)聽(tīng)根元素的input事件,如果改為change就會(huì)無(wú)效,因?yàn)橄旅娌](méi)有$emit(change),這里寫(xiě)什么下面就要emit什么
event:'input'
},
props:['value'],
template:`
<input type="text" :value="value" @change="$emit('input',$event.target.value)" />
`
})
new Vue({
data: {
msg:"Hello Vue"
},
methods: {
}
}).$mount("#app");
</script>
</body>
這樣我們就自定義了一個(gè)input輸入框,但是個(gè)人感覺(jué)好像沒(méi)什么用處似的,有的時(shí)候我們可能很想監(jiān)聽(tīng)我們的自定義組件的原聲事件,那么v-model提供了一個(gè)修飾符
- .native --> 用于綁定原生事件
可以這么認(rèn)為硝全,native就是一個(gè)把組件變回原生DOM的一種方法栖雾,給vue組件綁定事件的時(shí)候,一定要加上native伟众,如果是普通元素就不需要,默認(rèn)vue會(huì)將原生事件綁定到組件的根元素上,如果根元素不支持此事件就會(huì)靜默的失敗,例如
<div id="app">
<base-input @focus.native="onFocus"></base-input>
{{msg}}
</div>
<script src="../lib/vue.js"></script>
<script>
Vue.component("base-input",{
// 我們就可以直接將$listeners屬性綁定到對(duì)應(yīng)的元素上
template:`
<div>
<input type="text" v-on="$listeners" />
</div>
`
})
因?yàn)閐iv獲取不了焦點(diǎn)那么綁定事件就會(huì)靜默失敗,所以vue官方提供了一個(gè)$listeners屬性,我們可以將我們定義的處理函數(shù)添加到這個(gè)屬性上然后綁定到一個(gè)特定的元素上,下面是vue官方的一個(gè)演示
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
computed: {
inputListeners: function () {
var vm = this
// `Object.assign` 將所有的對(duì)象合并為一個(gè)新對(duì)象
return Object.assign({},
// 我們從父級(jí)添加所有的監(jiān)聽(tīng)器
this.$listeners,
// 然后我們添加自定義監(jiān)聽(tīng)器析藕,
// 或覆寫(xiě)一些監(jiān)聽(tīng)器的行為
{
// 這里確保組件配合 `v-model` 的工作
input: function (event) {
vm.$emit('input', event.target.value)
}
}
)
}
},
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on="inputListeners"
>
</label>
`
})
vue2.3新增的一個(gè)修飾符
- .sync
這是一個(gè)v-bind的修飾符用于將props進(jìn)行雙向綁定
<div id="app">
<base-input v-bind:value.sync="msg"></base-input>
{{msg}}
</div>
<script src="../lib/vue.js"></script>
<script>
Vue.component("base-input",{
props:['value'],
template:`
<input type="text"
:value="value"
@input="$emit('update:value', $event.target.value)"
/>
`
// 這里的update:是一個(gè)固定寫(xiě)法后面跟著你要更新的屬性名
// 我這里要更新input的value屬性就寫(xiě)value
})
插槽
關(guān)于插槽就要說(shuō)一下前面的v-slot指令了,前面的v-slot就是跟插槽有關(guān)的,那么我們需要先理解一下什么是插槽,先看一下下面的一個(gè)小例子
<div id="app">
<base-div></base-div>
</div>
<script src="../lib/vue.js"></script>
<script>
Vue.component("base-div",{
template:`<div></div>`
})
我這樣聲明使用這個(gè)組件是沒(méi)有什么問(wèn)題的,但是如果我要在組件中添加某些內(nèi)容呢,肯定是這樣的!
<base-div>
Hello World
</base-div>
然后我們就慢慢等待這頁(yè)面打印出Hello World但是,頁(yè)面真的能夠打印出來(lái)嗎?
那么就會(huì)發(fā)現(xiàn)實(shí)際的渲染結(jié)果可以說(shuō)是差強(qiáng)人意,什么都沒(méi)有,相信大家都會(huì)犯這樣一個(gè)錯(cuò)誤吧(我都犯過(guò))總是把我們自定義的組件當(dāng)成html元素一樣往里面插入內(nèi)容甚至,有時(shí)候插入子組件也插入在這里面,這肯定是不對(duì)的,正確的做法是
//清空組件中的內(nèi)容
<base-div></base-div>
//模板中寫(xiě)Hello World
template:`<div>Hello World</div>`
最終的渲染結(jié)果就非常正確了,那么我們肯定希望能夠在組件標(biāo)簽中直接插入內(nèi)容了,方便啊,還有就是非常直觀(guān),因?yàn)槟阋粋€(gè)組件包括子組件還有內(nèi)容全在DOM模板或者字符串模板中,那么我們定位組件的時(shí)候都必須在DOM模板或者字符串模板中進(jìn)行編輯,這樣的話(huà)乍一看html代碼會(huì)非常懵逼,我們維護(hù)的時(shí)候就必須一層一層的去尋找模板查看了,大家應(yīng)該懂這個(gè)意思吧?
所以Vue提供了插槽的特性,插槽就可以讓我們直接渲染組件標(biāo)簽中的內(nèi)容,我們立馬嘗試吧
然后我們繼續(xù)在組件標(biāo)簽中添加Hello World內(nèi)容,然后我們只需要在組件模板中添加一個(gè)標(biāo)簽就行了
Vue.component("base-div",{
template:`
<div>
//只需要添加一個(gè)slot標(biāo)簽就行了
<slot></slot>
</div>
`
})
然后我們就會(huì)驚奇的發(fā)現(xiàn)內(nèi)容成功被渲染出來(lái)了
這就是插槽了,就是提供了一個(gè)接口用于防止組件標(biāo)簽中的內(nèi)容,關(guān)于插槽的默認(rèn)值,某些情況下我們總希望我們沒(méi)有給插槽數(shù)據(jù)的時(shí)候插槽能夠渲染一個(gè)默認(rèn)的值,如下
這樣的渲染是常有的,我們可能就是希望組件按鈕的值能夠動(dòng)態(tài)的改變,但是我們還是希望當(dāng)我們不傳遞任何值的話(huà)能夠出現(xiàn)一個(gè)默認(rèn)情況,那么我們可以這樣做
那么插槽其實(shí)是可以訪(fǎng)問(wèn)其所在層級(jí)的內(nèi)容的,例如
插槽訪(fǎng)問(wèn)數(shù)據(jù)是有作用域的,插槽的作用域其實(shí)就相當(dāng)于自定義組件的作用于,你就當(dāng)那么自定義組件標(biāo)簽就一個(gè)div,那么作用域就是外層父親了,這里插槽的作用域就是外邊的#app,插槽無(wú)法訪(fǎng)問(wèn)同組件中的內(nèi)容,例如這里我傳遞一些數(shù)據(jù)進(jìn)去
<body>
<div id="app">
<button-submit :mdataa="mdata">
<!-- 這里訪(fǎng)問(wèn)不了button-submit組件的作用域 -->
<!-- 失敗 -->
{{mdataa}}
</button-submit>
</div>
<script src="../lib/vue.js"></script>
<script>
Vue.component("button-submit", {
props:["mdataa"],
// 如果沒(méi)有傳遞值,就會(huì)是提交,如果傳遞了那么是什么就是什么
template: `
<div>
<button type="button">
<slot>提交</slot>
</button?>
{{mdataa}}
</div>
`
})
new Vue({
data: {
msg: "Hello Vue",
mdata:"這是數(shù)據(jù),傳遞的props插槽無(wú)法訪(fǎng)問(wèn)"
}
}).$mount("#app");
</script>
</body>
像這個(gè)例子
Vue毫不客氣的報(bào)錯(cuò)并且不會(huì)獲得任何數(shù)據(jù)
具名插槽
聽(tīng)名字就知道就是有具體名字的插槽了,什么意思呢?具名插槽有什么用?
有時(shí)我們需要向一個(gè)組件中傳遞多個(gè)插槽(一個(gè)slot就是一個(gè)插槽),那么肯定這樣是不對(duì)的
<body>
<div id="app">
<button-submit :mdataa="mdata">
<!-- 這里訪(fǎng)問(wèn)不了button-submit組件的作用域 -->
<!-- 失敗 -->
我
</button-submit>
</div>
<script src="../lib/vue.js"></script>
<script>
Vue.component("button-submit", {
props:["mdataa"],
// 如果沒(méi)有傳遞值,就會(huì)是提交,如果傳遞了那么是什么就是什么
template: `
<div>
<button type="button">
<slot>提交</slot>
<slot>提交</slot>
<slot>提交</slot>
<slot>提交</slot>
</button?>
{{mdataa}}
</div>
`
})
new Vue({
data: {
msg: "Hello Vue",
mdata:"這是數(shù)據(jù),傳遞的props插槽無(wú)法訪(fǎng)問(wèn)"
}
}).$mount("#app");
</script>
</body>
這樣的話(huà)就相當(dāng)于重復(fù)了幾次插槽的內(nèi)容而已,完全無(wú)法分隔開(kāi)各插槽之間的內(nèi)容,這個(gè)時(shí)候具名插槽的作用就來(lái)到了,我們可以這樣書(shū)寫(xiě)代碼實(shí)現(xiàn)不同插槽之間的分隔
<body>
<div id="app">
<base-layout>
header
nav
section
footer
</base-layout>
</div>
<template id="base-layout">
<div>
<!--
對(duì)于一下組件我們可能需要往不同的標(biāo)簽中插入不同的內(nèi)容
所以我們?cè)诿總€(gè)標(biāo)簽中放一個(gè)插槽進(jìn)去,但是這樣根本無(wú)法實(shí)現(xiàn)我們的需求
這樣只會(huì)讓一個(gè)插槽重復(fù)四次罷了
-->
<header>
<slot></slot>
</header>
<nav>
<slot></slot>
</nav>
<section>
<slot></slot>
</section>
<footer>
<slot></slot>
</footer>
</div>
</template>
<body>
<div id="app">
<base-layout>
<!--
為了對(duì)應(yīng)插槽的具體名字我們需要使用DOM模板
模板使用v-slot指令,參數(shù)就是對(duì)應(yīng)插槽的名字
這個(gè)指令是下面這樣寫(xiě),跟平常的指令不一樣
-->
<template v-slot:header>
header
</template>
<template v-slot:nav>
nav
</template>
<template v-slot:section>
section
</template>
<template v-slot:footer>
footer
</template>
</base-layout>
</div>
<template id="base-layout">
<div>
<!--
對(duì)于一下組件我們可能需要往不同的標(biāo)簽中插入不同的內(nèi)容
所以我們?cè)诿總€(gè)標(biāo)簽中放一個(gè)插槽進(jìn)去,但是這樣根本無(wú)法實(shí)現(xiàn)我們的需求
這樣只會(huì)讓一個(gè)插槽重復(fù)四次罷了,所以插槽提供了一個(gè)name屬性用于指定具體的名字
-->
<header>
<slot name="header"></slot>
</header>
<nav>
<slot name="nav"></slot>
</nav>
<section>
<slot name="section"></slot>
</section>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<script src="../lib/vue.js"></script>
<script>
Vue.component("base-layout", {
template: "#base-layout"
})
new Vue({
data: {
msg: "Hello Vue",
mdata: "這是數(shù)據(jù),傳遞的props插槽無(wú)法訪(fǎng)問(wèn)"
}
}).$mount("#app");
</script>
</body>
這樣我們就分隔開(kāi)了每一個(gè)插槽,這就是具名插槽了,那么其實(shí)默認(rèn)的
<slot></slot>
也是有名字的叫做default,我們也可以指令v-slot:default或者不指定名字的標(biāo)簽當(dāng)會(huì)傳入到默認(rèn)的插槽中,如果沒(méi)有提供默認(rèn)插槽那么就會(huì)放棄對(duì)應(yīng)默認(rèn)插槽中的內(nèi)容
關(guān)于具名插槽就介紹到此了,接下來(lái)說(shuō)一下作用于插槽
作用域插槽
簡(jiǎn)單來(lái)說(shuō)就是讓插槽中的內(nèi)容訪(fǎng)問(wèn)對(duì)應(yīng)組件中的內(nèi)容
<body>
<div id="app">
<base-layout>
<!-- 但是插槽中訪(fǎng)問(wèn)不了此數(shù)據(jù),訪(fǎng)問(wèn)的是父親#app中的數(shù)據(jù)所以會(huì)報(bào)錯(cuò) -->
{{username}}
</base-layout>
</div>
<template id="base-layout">
<div>
<!-- 這里是能夠訪(fǎng)問(wèn)自己組件中的數(shù)據(jù)的 -->
{{username}}
<slot></slot>
</div>
</template>
<script src="../lib/vue.js"></script>
<script>
Vue.component("base-layout", {
template: "#base-layout",
data(){
return {
username:"suiyue"
}
}
})
new Vue({
data: {
msg: "Hello Vue",
mdata: "這是數(shù)據(jù),傳遞的props插槽無(wú)法訪(fǎng)問(wèn)"
}
}).$mount("#app");
</script>
</body>
這就代表著默認(rèn)插槽的作用域與其所在的組件同級(jí)(不是父子是同級(jí))所以無(wú)法訪(fǎng)問(wèn)其所在組件的一些數(shù)據(jù)
那么我們就需要這么玩了
<body>
<div id="app">
<base-layout>
<!-- 但是插槽中訪(fǎng)問(wèn)不了此數(shù)據(jù),訪(fǎng)問(wèn)的是父親#app中的數(shù)據(jù)所以會(huì)報(bào)錯(cuò) -->
<!-- 需要使用DOM模板標(biāo)簽接受插槽props -->
<!--
通過(guò)v-slot參數(shù)為對(duì)應(yīng)的插槽名稱(chēng) 等號(hào)后面的制定一個(gè)變量用于接受插槽props
所有的插槽props都會(huì)變成屬性綁定到此變量上
-->
<template v-slot:default = "my_props" >
{{my_props.username}}
</template>
</base-layout>
</div>
<template id="base-layout">
<div>
<!-- 這里是能夠訪(fǎng)問(wèn)自己組件中的數(shù)據(jù)的 -->
{{username}}
<!--
使用v-bind指令將username屬性綁定在特定的插槽上
通過(guò)這樣的方式傳遞數(shù)據(jù)給插槽,這樣的方式叫做插槽props
-->
<!-- 不用v-bind后面是字符串 -->
<slot :username="username"></slot>
</div>
</template>
<script src="../lib/vue.js"></script>
<script>
Vue.component("base-layout", {
template: "#base-layout",
data(){
return {
username:"suiyue"
}
}
})
new Vue({
data: {
msg: "Hello Vue",
mdata: "這是數(shù)據(jù),傳遞的props插槽無(wú)法訪(fǎng)問(wèn)"
}
}).$mount("#app");
</script>
</body>
這樣我們就成功的將數(shù)據(jù)fetch到了,以上就是作用于插槽的使用講解了
上述作用于插槽在提供組件中如果沒(méi)有具名插槽時(shí)可以簡(jiǎn)化書(shū)寫(xiě)為
還可以進(jìn)一步簡(jiǎn)化
上述簡(jiǎn)寫(xiě)方法只能在組件中沒(méi)有具名插槽中使用,如果組件中有具名插槽還請(qǐng)不要使用簡(jiǎn)寫(xiě),否則語(yǔ)法會(huì)無(wú)效并且Vue會(huì)發(fā)出警告,切記
解構(gòu)插槽prop
作用域插槽的內(nèi)部工作原理是將你的插槽內(nèi)容包括在一個(gè)傳入單個(gè)參數(shù)的函數(shù)里
function (slotProps) {
// 插槽內(nèi)容
}
所以我們可以直接對(duì)插槽進(jìn)行解構(gòu),像這樣
瀏覽器正常渲染輸出,同時(shí)我們可以在解構(gòu)時(shí)重命名插槽
我們?cè)O(shè)置可以直接給插槽賦默認(rèn)值
vue2.6+ 新增動(dòng)態(tài)插槽名
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
</base-layout>
就是可以通過(guò)一個(gè)變量或者計(jì)算屬性函數(shù)等等動(dòng)態(tài)計(jì)算插槽名字
具名插槽的縮寫(xiě),跟v-on和v-bind一樣具名插槽也是有縮寫(xiě)的,具名插槽的縮寫(xiě) v-slot:header ---> #header v-slot:縮寫(xiě)為#,跟其他指令的縮寫(xiě)一樣該縮寫(xiě)只有當(dāng)插槽有參數(shù)才有用,這意味著一下語(yǔ)法是無(wú)效的
<!-- 這樣會(huì)觸發(fā)一個(gè)警告 -->
<current-user #="{ user }">
{{ user.firstName }}
</current-user>
我們必須明確制定他的參數(shù)才行
<current-user #default="{ user }">
{{ user.firstName }}
</current-user>
異步組件
在大型應(yīng)用中,我們可能需要將應(yīng)用分割成小一些的代碼塊凳厢,并且只在需要的時(shí)候才從服務(wù)器加載一個(gè)模塊账胧。為了簡(jiǎn)化,Vue 允許你以一個(gè)工廠(chǎng)函數(shù)的方式定義你的組件先紫,這個(gè)工廠(chǎng)函數(shù)會(huì)異步解析你的組件定義治泥。Vue 只有在這個(gè)組件需要被渲染的時(shí)候才會(huì)觸發(fā)該工廠(chǎng)函數(shù),且會(huì)把結(jié)果緩存起來(lái)供未來(lái)重渲染.
這一個(gè)簡(jiǎn)單的例子演示了如何異步加載組件,你可以在此函數(shù)中執(zhí)行任何異步的請(qǐng)求最后通過(guò)resolve傳遞給vue
斷斷續(xù)續(xù)才發(fā)現(xiàn)居然寫(xiě)了這么多,所以我決定拆分開(kāi)來(lái)寫(xiě)了,因?yàn)関ue還有差不多這么一篇文章的知識(shí)還沒(méi)有接觸,所以我決定將另外的一些知識(shí)什么的都寫(xiě)在下一篇文章中,同時(shí)發(fā)現(xiàn)自己寫(xiě)得有那么一點(diǎn)點(diǎn)的亂,我決定最后來(lái)一個(gè)總結(jié)吧
最后來(lái)一個(gè)總結(jié)
- 計(jì)算屬性 -->obj
需要依賴(lài)于組件的data才能夠進(jìn)行數(shù)據(jù)的更新,如果data不更新,計(jì)算屬性永遠(yuǎn)不更新
- 監(jiān)聽(tīng) --->obj
監(jiān)聽(tīng)的屬性為函數(shù)名,每一次這個(gè)屬性的改變都會(huì)當(dāng)做參數(shù)傳給對(duì)應(yīng)的函數(shù),然后自定義邏輯
- class與style的綁定
對(duì)象法 class: key --> 類(lèi)名 value-->bool(決定是否綁定此類(lèi)名)
style: key --->樣式名(駝峰) value---> 樣式的值
數(shù)組法 class: 每一個(gè)變量都表示類(lèi)名,值為bool
style -->每一個(gè)變量必須是一個(gè)對(duì)象,key-value代表樣式名和樣式值
同時(shí)也可以使用三元運(yùn)算符,或者直接引用data中的對(duì)象或者數(shù)組等等
- 指令
- v-if
- v-else-if (必須在v-if后,否則無(wú)效)
- v-else (必須在上面兩個(gè)指令后,否則無(wú)效)
- v-show
- v-for
- v-on --> @
-->事件修飾符
- stop
- prevent
- capture
- self
- once
- passive
-->按鍵修飾符 - enter
- tab
- delete (捕獲“刪除”和“退格”鍵)
- esc
- space
- up
- down
- left
- right
-->系統(tǒng)修飾鍵 - .ctrl
- .alt
- .shift
- .meta
--->2.5新增鍵盤(pán)事件修飾符 - .exact
--->2.2新增鼠標(biāo)按鍵修飾符 - .left
- .right
- .middle
--->組件的修飾符 - .native
- v-bind --> (:)
-->修飾符
- .sync
<div id="app">
<base-input v-bind:value.sync="msg"></base-input>
{{msg}}
</div>
<script src="../lib/vue.js"></script>
<script>
Vue.component("base-input",{
props:['value'],
template:`
<input type="text"
:value="value"
@input="$emit('update:value', $event.target.value)"
/>
`
// 這里的update:是一個(gè)固定寫(xiě)法后面跟著你要更新的屬性名
// 我這里要更新input的value屬性就寫(xiě)value
})
- v-model
--->修飾符
- .lazy
- .number
- .trim
v-text
v-html
v-choak
v-once
v-pre
v-slot
數(shù)組更新檢測(cè)
data中數(shù)組的某一些方法也會(huì)觸發(fā)vue的視圖更新
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
- 組件的生命周期
- beforeCreate()
- created()
- beforeMount()
- mounted()
- beforeUpdate()
- updated()
- beforeDestroy()
- destroyed()
- 自定義事件
通過(guò)組件內(nèi)$emit一個(gè)事件,外部通過(guò)v-on接受事件
- 組件的全局和局部注冊(cè)
- 組件prop
- 單向數(shù)據(jù)流
- props驗(yàn)證
- 插槽
- 具名插槽
- 作用域插槽
- 解構(gòu)插槽prop
- vue2.6+ 動(dòng)態(tài)插槽名 v-slot:[] v-bind:[]
好了差不多就是這樣了,接下來(lái)我會(huì)寫(xiě)得非常簡(jiǎn)潔了,下一章見(jiàn)!
最后如果你有什么學(xué)習(xí)困惑或者對(duì)于前端有什么想跟我探討的可以加下QQ群聯(lián)系我 , 78484------5854