鉤子函數(shù)的生命周期
根實(shí)例,在初始化時會調(diào)用很多方法,這些方法被稱為鉤子函數(shù)
生命周期:beforeCreate created beforeMount mounted beforeUpdate updated beforeDestory destoryed
beforeCreate(){},此時只有內(nèi)部方法
此方法一般用不到
create(){}
獲取ajax,初始化操作
beforeMount(){}編譯前的操作,也沒有什么實(shí)際意義
一定要保證有編譯的元素el,否則會中斷
掛載el,也可以使用
new Vue().mount('#app')
如果有實(shí)例template屬性,會用模板替換掉外部html,只要有此屬性,app中的內(nèi)容沒有任何意義,template中只能有一個根元素。
let vm=new Vue(){
el:"#app",
template:"</div>{{a}}</div>",
data:{a:1
}}
之前第三天的時候又提到過,DOM中也有一個tempelate標(biāo)簽,是沒有任何意義,用來包裹元素的。
mounted(){} 操作DOM
用編譯好的el替換掉el,然后調(diào)用mounted()
真實(shí)dom渲染完了可以操作dom了
beforeUpdate(){}
當(dāng)數(shù)據(jù)變化的時候,調(diào)用beforeUpdate()
updated(){}
一般用watch來替換,watch能夠更準(zhǔn)確的知道是誰更新了。
beforedestory(){}銷毀前,可以清除定時器,或清除事件綁定
destoried(){}銷毀后
watch,computed以及方法全部被銷毀
實(shí)例上的方法
this.$data vm上的數(shù)據(jù)
this.$watch 監(jiān)控
this.$el 當(dāng)前el元素
this.$set 后加的屬性實(shí)現(xiàn)響應(yīng)式的變化
this.$options 獲取vm中所有的屬性
this.$nextTick(){} 異步方法,可以等待dom完成后獲取vm,是當(dāng)前代碼全部執(zhí)行完畢后,執(zhí)行
另外dom渲染是異步的,想要數(shù)據(jù)變化后想獲取真實(shí)dom中的內(nèi)容,必須使用nextTick
this.$refs 所有refs的集合,假如有一個標(biāo)簽 ref="myp" 可以直接通過this.$ refs.myp。ref 被用來給DOM元素或子組件注冊引用信息。如果dom元素不是通過v-for循環(huán)出來的,只能獲取一個,通過v-for則可以獲取多個,輸出為數(shù)組
組件
可以將很復(fù)雜的頁面分割成若干個獨(dú)立組件,每個組件包含自己的邏輯和樣式
減少邏輯復(fù)雜度,實(shí)現(xiàn)了代碼的重用
頁面級組件:1、一個頁面是一個組件
2、將可復(fù)用的部分抽離出來,作為基礎(chǔ)組件
優(yōu)點(diǎn)
- 提高開發(fā)效率
- 方便重復(fù)使用
- 便于協(xié)同開發(fā)
- 更容易被管理和維護(hù)
Vue的組件
一個自定義的標(biāo)簽Vue就會把它看成一個組件
div p span a header section都是W3C規(guī)定的,不是自定義的標(biāo)簽,但是Vue可以給它們賦予一定的意義
全局組件
聲明一次,可以在任何地方使用
一般使用局部組件,寫插件的時候使用全局組件
之前Vue.filter("函數(shù)名",函數(shù))全局過濾器
全局組件的基本形式
一個對象可以看成是一個組件
Vue.component('polarbear',{
//一個對象可以看成一個組件
template:'<div>{{msg}}</div>',
//只能是一個根元素
//相當(dāng)于用這個模板替換這個標(biāo)簽
//組件名不要帶有大寫,多個單詞用-
//只要組件名和定義名字相同是可以的(首字母可以大寫)
//html采用的短橫線隔開命名法 js中轉(zhuǎn)駝峰也是可以的
data(){
//組件中的數(shù)據(jù)必須是函數(shù)類型的,返回一個實(shí)例作為數(shù)組的實(shí)例
return {msg:'大白白熊'}
}
})
組件名
組件名不要帶有大寫,有多個單詞用-
只要組件名和定義名字相同是可以大寫的,但是只有首字母可以大寫
html采用的短橫線隔開命名法,js的轉(zhuǎn)駝峰法也可以
template屬性
DOM中的template是一個沒有意義的標(biāo)簽,用來框東西
這里的template會給出一個DOM模板,替換標(biāo)簽中的內(nèi)容
只能是一個根元素
template:'<div>{{msg}}</div>'
data
組件中的函數(shù)必須是函數(shù)類型的,并且返回一個實(shí)例作為數(shù)組的實(shí)例
DOM
<div id="app">
<polarbear>大白熊</polarbear>
</div>
雖然DOM中是大白熊,但是polarbear的template屬性給出了內(nèi)容為大白白熊,所以頁面中的內(nèi)容會被替換
局部組件
必須告訴這個組件是屬于誰的
三步驟
- 創(chuàng)建這個組件
- 注冊這個組件
- 引用這個組件
創(chuàng)建組件
和創(chuàng)建Vue的過程相似,但是要記住組件的data是函數(shù),需要返回一個形式與Vue data形式相同的對象
let polarbear={
template:'<div>大白白熊</div>',
data(){
return {msg:'大白白熊'}
}
};
let penguin={
template:'<div>企鵝</div>'
};
- 如果組件共用了數(shù)據(jù),數(shù)據(jù)會同時刷新,而組件的特點(diǎn)就是獨(dú)立性,所以最好不要
- 子組件不能直接使用父組件的數(shù)據(jù)
- 組件理論上可以無限嵌套
注冊組件
在Vue實(shí)例中添加一個components屬性
let vm=new Vue(
{
el:"#app",
data:{
a:1
},
components:{
polarbear,
penguin
//它們的parent是vm
//不能跨作用域調(diào)用vm的data,組件相互獨(dú)立不能直接跨作用域 vm的實(shí)例也是一個組件
//也擁有生命周期函數(shù)
},
}
)
- 在哪里注冊組件,這些組件的父組件就是誰
- 但是組件是不能調(diào)用父組件的數(shù)據(jù)的,polarbear和penguin都不能調(diào)用a
- 這些組件也有生命周期函數(shù)
DOM的寫法
除了直接在DOM中寫,也可以在Vue實(shí)例中添加
template:'<polarbear><penguin></penguin></polarbear>'
注意以上這種寫法是不會顯示企鵝的,因?yàn)閜olarbear會替換掉標(biāo)簽之中的所有內(nèi)容包括penguin
組件之間引用
如果要在一個組件中使用另一個組件
- 首先必須要保證使用的組件真實(shí)存在
- 在需要引用這個組件的實(shí)力上通過components注冊這個組件
- 組件需要在父級的模板中通過標(biāo)簽的
形式引入
let littlebear={
template:"<div>小白熊</div>"
};
let polarbear={
template:'<div>白熊<littlebear></littlebear></div>',
components:{littlebear}
};
let bigbear={
template:'<div>大白熊<polarbear></polarbear></div>',
components:{polarbear}
};
let vm=new Vue(
{
el:"#app",
components:{bigbear},
template:"<bigbear></bigbear>"
}
最終顯示結(jié)果是大白熊 白熊 小白熊
除了標(biāo)簽的簡單引用,想要數(shù)據(jù)之間的引用就需要使用其他的方法了
父傳子
父親定義好數(shù)據(jù),通過屬性的方式傳遞給孩子
以父親給孩子零花錢為例
- 首先父親中要有money這個數(shù)據(jù)
- 孩子通過DOM獲取
DOM中數(shù)據(jù)傳遞
<div id="app">
父親給了{(lán){money}}
<child :m="money/2"></child>
<!--m是屬于child的屬性,1是屬于父級的,當(dāng)前組件的屬性=父級的值-->
<!--用冒號把值變成屬性,不加冒號,money就變成字符串了,key:value,這里永遠(yuǎn)是字符串-->
</div>
這里有標(biāo)簽的相關(guān)知識,在一個標(biāo)簽中
- key:value形式,前面的key是當(dāng)前組件的屬性,后面value是父級標(biāo)簽的值
- 如果不使用冒號的方式,傳遞的值會變?yōu)樽址袷剑褂妹疤枺禃優(yōu)閷傩缘男问?br>
如果上文中
:m="money"改為m="money"
那么m是字符串money
props屬性
props中的數(shù)據(jù)可以是數(shù)組,也可以是對象
但是不能和data中的名字相同,校驗(yàn)時不能阻斷代碼的執(zhí)行,只是進(jìn)行警告
單向數(shù)據(jù)流
prop使得父子prop之間形成了單向下行綁定,父級prop的更新會向下流動到子組件中。
prop:['m']
會在當(dāng)前子組件上聲明一個m屬性,值是父親的
m不能直接寫m必須寫成字符串格式
m可以直接用,但是不能改,只能通過data或者compute計(jì)算新的值
computed:{
money(){
return this.m
}}
這時
child:{
template:'<div>兒子 {{money}}</div>',
//props:['m'],
computed:{
money(){
return this.m
}
}
顯示結(jié)果為兒子獲得50
props校驗(yàn)功能
可以使用對象的形式進(jìn)行校驗(yàn)
type
校驗(yàn)屬性的類型
type可以師原生構(gòu)造函數(shù)中的一個
String,Number,Boolean,Array,Object,Date,Function,Symbol
如果傳遞的屬性類型不正確,只會warn并不會報(bào)錯
default
默認(rèn)值,如果沒有傳數(shù)據(jù)過來,自動取默認(rèn)值
required
當(dāng)這個屬性為true時,此屬性必須傳遞,不傳就報(bào)warn,不能和default同時使用
不傳遞的時候顯示會是false
validator(val){}
自定義校驗(yàn)器
第一個參數(shù)是當(dāng)前傳遞的值
返回true則表示通過,false為不通過
但是false也只是warn,不報(bào)錯
props:{
//屬性名和data中的名字不能相同,校驗(yàn)時不能阻斷代碼的執(zhí)行,只是進(jìn)行警告
m:{
//校驗(yàn)屬性的類型,如果不帶冒號,得到的是字符串
type:[String,Boolean,Function,Object,Arrey,Number],
//對象的形式可以進(jìn)行校驗(yàn)
//默認(rèn)值,如果不傳的話,自動取默認(rèn)值
default:0,
//required:true
//此屬性必須傳遞,不傳就報(bào)warn,現(xiàn)在{{money}}顯示為false,但是不能和default同時使用
validator(val){
//第一個參數(shù)是當(dāng)前傳遞的值
//返回true表示通過
//false表示不通過
//依然只是warn,自定義校驗(yàn)器,用的不是很多
return val>300
}
}
}
全部程序
<body>
<div id="app">
{{money}}
<child :m="money/2"></child>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm=new Vue( //parent組件
{
el:"#app",
data:{
money:100
},
components:{
child:{
template:'<div>兒子 {{money}}</div>',
computed:{
money(){
return this.m
}
},
props:{
m:{
type:[String,Boolean,Function,Object,Arrey,Number],
default:0,
//required:true
validator(val){
return val>300
}
}
}
}
}
)
</script>
</body>
發(fā)布訂閱模型
發(fā)布emit 訂閱 on
說白了就是觸發(fā)和綁定
利用這兩個事件,可以創(chuàng)建一對多的關(guān)系
實(shí)例,創(chuàng)建一個{日常行為:['睡覺','游泳','吃海豹']}
function polarbear(){
this._events={}
//下劃線表示是私有的
//{日常行為:['睡覺','游泳','吃海豹']}
}
let pb=new polarbear()
on
為polarbear綁定on,emit事件
因?yàn)槭撬袑?shí)例共用的,所以放在原型上
polarbear.prototype.on=function(eventName,callback){
if(this._events[eventName]){
//不是第一次
//不能用點(diǎn),點(diǎn)是返回字符
this._events[eventName].push(callback)
}
else{
this._events[eventName]=[callback]
}
}
為日常行為綁定相關(guān)事件
let sleep=(who)=>{console.log(who+"睡覺")}
let swim=(who)=>{console.log(who+"游泳")}
let eat=(who)=>{console.log(who+"吃海豹")}
pb.on('日常行為',sleep);
pb.on('日常行為',swim);
pb.on('日常行為',eat);
emit
原型綁定
polarbear.prototype.emit=function(eventName,...args){
if(this._events[eventName])
{
this._events[eventName].forEach(cb=>cb(...args)))
//cb.apply(this,args)
//展開數(shù)組
//只運(yùn)行數(shù)組第一項(xiàng)
}
}
這個過程是將emit后面的值賦給前面的事件
因?yàn)閰?shù)的數(shù)量不確定,使用ES6中的...args,將剩余的參數(shù)作為數(shù)組args存儲
pb.emit('日常行為','我','熊大','熊二');
最終運(yùn)行結(jié)果 我睡覺 我游泳 我吃海豹,如果想要每一個都運(yùn)行,還是要取args.length去做
子傳父
通過事件,子觸發(fā)父的事件,傳遞數(shù)據(jù)
兒子不能改變父親的值,但是可以告訴他讓他改
父親綁定一些事件,兒子觸發(fā)這個事件,將參數(shù)傳遞過去,單向數(shù)據(jù)流,父親數(shù)據(jù)刷新,兒子也隨之刷新
子傳父超復(fù)雜,饒了180個彎
- 首先既然觸發(fā)父的事件,那父親里必須有這個事件
- 然后得把這個事件綁定到子組件上
- 綁定后還要觸發(fā)這個事件,讓父親的方法執(zhí)行
- 那觸發(fā)這個事件還要在子組件上定義一個事件
事件綁定兒子上
<div id="app">
父親給{{money}}
兒子得<child :m="money" @childmessage="changemoney"></child>
</div>
changemoney是父親的事件,childmessage屬于兒子
相當(dāng)于child.on('childmessage',changemoney)
父親事件定義
methods:{
changemoney(val){
this.money=val
}
}
兒子上的事件
template:'<div><button @click="getmoney()">多要錢</button></div>'
觸發(fā)兒子自定義事件,父親的方法被執(zhí)行
getmoney(){
//觸發(fā)自己的自定義事件,讓父親的方法執(zhí)行
this.$emit('childmessage',800)
}
將800的值傳遞給childmessage,通過觸發(fā)childmessage事件,執(zhí)行父組件的changemoney,最終money被改變?yōu)?00
全部程序
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">
父親給{{money}}
兒子得<child :m="money" @childmessage="changemoney"></child>
<!--child.on('childmessage',changemoney),把事件綁定在兒子上-->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!--父親綁定一些事件,兒子觸發(fā)這個事件,將參數(shù)傳遞過去,單向數(shù)據(jù)流,父親數(shù)據(jù)刷新,兒子就刷新-->
<!--兒子不能改父親的值,但是可以告訴他,讓他改-->
<script>
let vm=new Vue(
{
el:"#app",
data:{money:400},
components:{
child:{
template:'<div><button @click="getmoney()">多要錢</button></div>',
props:['m'],
methods:{
getmoney(){
//觸發(fā)自己的自定義事件,讓父親的方法執(zhí)行
this.$emit('childmessage',800)
}
}
}
},
methods:{
changemoney(val){
this.money=val
}
}
}
)
</script>
</body>
</html>
.sync的用法 語法糖
.sync修飾符
是一種雙向綁定
但是雙向綁定會帶來一定維護(hù)的問題
sync修飾符相當(dāng)于將emit處的方法名改變?yōu)閡pdate:m
<child :m.sync="money"></child>
<!--相當(dāng)于<child :m="money" @update:m="val=>{this.money=val}"></child>-->
然后再子組件
getmoney(){
this.$emit('update:m',800)
}
模態(tài)框?qū)嵗?/h1>
這個實(shí)例是點(diǎn)擊主界面上的按鈕彈出警告框,然后點(diǎn)擊警告框上的關(guān)閉按鈕,關(guān)閉這個警告窗
需要利用bootstrap的警告框,子組件向父組件傳遞事件,父組件向子組件傳遞數(shù)據(jù)
- 父組件中定義flag屬性,true的時候顯示警告窗,false時不顯示
- 將flag傳遞到子組件
- 子組件關(guān)閉按鍵按下時觸發(fā)自定義事件執(zhí)行父組件的改變flag的方法
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<div id="app">
<button @click="clickparent">彈</button>
<modal :show="flag" @close="fn"></modal>
<!--:show="flag"這是父傳子,@close="fn"是子傳父-->
<!--如果show的值為true則顯示,如果是false則隱藏 @close對應(yīng)的函數(shù)是點(diǎn)擊關(guān)閉按鈕時觸發(fā)的函數(shù)-->
<!--<dialog :show="" @close="fn"></dialog>-->
</div>
<template id="dialog">
<div class="container" v-show="show">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div class="alert alert-danger" style="height:200px" role="alert">
<button class="pull-right" @click="closealert">關(guān)閉</button>
</div>
</div>
</div>
</div>
</template>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let modal={
template:'#dialog',
props:['show'],//接收父組件的屬性
methods:{
closealert(){
this.$emit('close')
}
}
}
let vm=new Vue({
el:"#app",
components:{
modal//名字不能叫dialog,原生已經(jīng)占用了這個名字
},
data:{
flag:false
},
methods:
{
clickparent(){
this.flag=true
},
fn(){
this.flag=false
}
}
})
</script>
</html>