一、組件
1.props屬性
1)props屬性
組件可以嵌套使用,叫做父子組件。
那麼父組件經常要給子組件傳遞數據這叫做父子組件通信。
父子組件的關係可以總結爲 props 向下傳遞,事件向上傳遞。
父組件通過 props 給子組件下發數據,子組件通過事件給父組件發送消息。<div id="app"> <!-- 使用子組件 --> <!-- 通過綁定屬性,將父組件的數據設置到子組件標籤中 --> <!-- msg2 代表的是一個自定義屬性名,可任意 msg 代表的是父組件中data屬性中的msg數據 --> <child :msg2="msg" :b="greetTxt"></child> </div> </body> <script type="text/javascript"> /* 定義子組件 */ Vue.component('child',{ /* 子組件中想用父組件傳遞的數據,需要通過props屬性聲明 */ props:["msg2",'b'], template:"<h3>Hello -- {{msg2}} --- {{b}}</h3>" }); /* 父組件 */ new Vue({ el:"#app", data:{ msg:"來自父組件的消息", greetTxt:"你好啊~Child" } });
2)props校驗
子組件在接收父組件傳入數據時, 可以進行props校驗,來確保數據的格式和是否必傳。可以指定一下屬性:
1) type: 指定數據類型 String Number Object …注意不能使用字符串數組,只能是對象大寫形式
2) required: 指定是否必輸
3) default: 給默認值或者自定義函數返回默認值
4) validator: 自定義函數校驗兩種形式:
數組(不支持數據校驗)
props:[‘參數1’,‘參數2’…]
對象(支持數據校驗)
props:{
參數1:
參數2:
}<div id="app"> <child :a="msg" :b="greetTxt" :d="num" ></child> </div> </body> <script type="text/javascript"> /* 定義子組件 */ Vue.component('child',{ /* props校驗 */ props:{ // 要求數據是數值型 // a:String, // 可以是數值型 和 字符串類型 b:[Number, String], // 必須設置屬性 /* c:{ required:true }, */ // 設置默認值;如果數據未被傳遞,則顯示默認結果 c:{ type:Number, default:100 }, // 數組/對象的默認值應當由一個工廠函數返回 e:{ default:function(){ return 1; } }, // 自定義驗證函數 d:{ validator:function(value){ return value > 1; } }, // 自定義驗證函數 a:{ validator:function(value){ return value.length > 10; } } }, template:"<h3>Hello -- {{a}} -- {{b}} -- {{c}} -- {{e}} -- {{d}}</h3>" }); /* 父組件 */ new Vue({ el:"#app", data:{ msg:"來自父組件的消息", greetTxt:"你好啊~Child", num:10 } }); </script>
2.非props屬性
引用子組件時,非定義的props屬性,自動合併到子組件上**,class和style也會自動合併。**
<div id="app"> <child style="font-size: 20px;" class="active" ></child> </div> </body> <script type="text/javascript"> /* 定義子組件 */ Vue.component('child',{ template:"<div style='color:red' class='bg'>Hello</div>" }); /* 父組件 */ new Vue({ el:"#app", }); </script>
3.自定義事件
自定義事件
父組件給子組件傳值使用props屬性, 那麼需要子組件更新父組件時,要使用自定義事件emit:
emit主動觸發: $emit(事件名,傳入參數)
實現步驟: 1.定義父組件
定義父組件中的數據和方法 2.定義子組件
定義子組件的模板和方法 3.在使用子組件的標籤時,綁定自定義事件
<child @update-count=“changeCount” >
說明:
update-count 代表的是 自定義事件
changeCount 代表的是自定義事件被觸發時,調用的函數 4.在子組件的方法中,手動觸發自定義事件
$emit(事件名,傳遞的參數);主動掛載
自定義事件不僅可以綁定在子組件,也可以直接掛載到父組件,使用emit觸發。<div id="app"> 數量:{{count}} <hr > <child @update-count="changeCount" ></child> </div> <hr > <div id="root"> 數量:{{count}} </div> </body> <script type="text/javascript"> /* 定義子組件 */ Vue.component('child',{ template:"<button @click='update'>子組件Child</button>", methods:{ update:function(){ console.log("子組件的方法..."); // 觸發自定義事件 this.$emit("update-count","Hello"); } } }); /* 父組件 */ new Vue({ el:"#app", data:{ count:0 }, methods:{ changeCount:function(a){ console.log("父組件的方法..."); console.log("子組件中傳遞的數據:" + a); // 更新data屬性中的數據 this.count++; } } }); var root = new Vue({ el:"#root", data:{ count:0 } }); // 主動掛載 root.$on("update-count",function(){ console.log("主動掛載..."); // 更新父組件的數據 this.count++; }); // 手動觸發 root.$emit("update-count","HAHAH"); </script>
二、插槽分發
1.slot插槽
父子組件使用時,有時需要將父元素的模板跟子元素模板進行混合,這時就要用到slot插槽進行內容分發,簡單理解就是在子模板中先佔個位置等待父組件調用時進行模板插入。
在子組件模板中使用**標籤定義插槽位置**,標籤中可以填寫內容,當父組件不傳入內容時顯示此內容。<div id="app"> <child> <h3>我是父組件</h3> <h3>我是父組件2</h3> </child> </div> <hr > <!-- 組件模板 --> <template id="child-template"> <div> <slot><div>我是默認內容,父組件不傳入時我顯示</div></slot> <div>我是子組件</div> <div>Hello</div> <!-- 定義slot插槽進行佔位 --> <!-- <slot>我是默認內容,父組件不傳入時我顯示</slot> --> <slot><div>我是默認內容,父組件不傳入時我顯示</div></slot> </div> </template> </body> <script type="text/javascript"> /* 定義子組件 */ Vue.component('child',{ template:"#child-template", }); /* 父組件 */ new Vue({ el:"#app", data:{ count:0 } }); </script>
2.具名插槽
具名插槽slot, 就是給插槽起個名字。
在子組件定時可以定定義多個插槽,同時通過name屬性指定一個名字
如:,父組件引用時使用< slot=‘header’>進行插槽選擇。<div id="app"> <child> <h3 slot="zhangsan">我是張三</h3> <h3 slot="lisi">我是李四</h3> </child> </div> <hr > <!-- 組件模板 --> <template id="child-template"> <div> <slot name="zhangsan"></slot> <div>我是子組件</div> <div>Hello</div> <slot name="lisi"></slot> </div> </template> </body> <script type="text/javascript"> /* 定義子組件 */ Vue.component('child',{ template:"#child-template", }); /* 父組件 */ new Vue({ el:"#app", data:{ count:0 } }); </script>
3.slot-scope
作用域插槽slot-scope,父組件通過插槽混入父組件的內容, 子組件也可以通過slot作用域向插槽slot內部傳入數據
使用方式:,父組件通過進行引用。<div id="app"> <child> <!-- 使用template標籤將插槽對應的數據包裹起來,並設置slot-scope的屬性值,屬性值是一個變量名,自定義 --> <!-- <template slot-scope="props"> <h3 >我是張三 -- {{props.msg}}</h3> </template> --> <!-- 在2.5+之後,可以不侷限於<template>, 任何元素都可以,同時可以使用解構賦值的方式進行數據解析。 --> <!-- <h3 slot-scope="props">我是張三 -- {{props.msg}}</h3> --> <!-- 解構賦值 --> <h3 slot-scope="{msg,txt}">我是張三 -- {{msg}}-- {{txt}}</h3> </child> </div> <hr > <!-- 組件模板 --> <template id="child-template"> <div> <div>我是子組件</div> <div>Hello</div> <!-- 定義插槽時,傳遞數據 --> <slot msg="你好啊張三" txt="哈哈"></slot> </div> </template> </body> <script type="text/javascript"> /* 定義子組件 */ Vue.component('child',{ template:"#child-template", }); /* 父組件 */ new Vue({ el:"#app", data:{ count:0 } }); </script>
三、動態組件
1.動態組件
使用標籤的is屬性,動態綁定多個組件到一個掛載點**,通過改變is綁定值,切換組件。**
實現步驟: 1. 定義父組件,設置data數據 2. 定義多個子組件 3. 定義導航(a標籤),併爲導航綁定點擊事件,修改data屬性中數據的值 4. 使用is屬性綁定父組件的數據 <component :is="page"></component> page 代表的是父組件中data屬性定義的數據 <div id="app"> / <a href="#" @click.prevent="page='index'">首頁</a> / <a href="#" @click.prevent="page='news'">新聞頁</a> / <a href="#" @click.prevent="page='login'">登錄頁</a> <hr > <component :is="page"></component> </div> </body> <script type="text/javascript"> Vue.component('index', { template:'<h5>首頁</h5>' }); Vue.component('news', { template:'<h5>新聞頁</h5>' }); Vue.component('login', { template:'<h5>登陸頁</h5>' }); /* 父組件 */ new Vue({ el:"#app", data:{ page:"login" } }); </script>
2.keep-alive
如果把切換出去的組件保留在內存中,可以保留它的狀態或避免重新渲染。爲此可以添加一個 keep-alive 指令。
用生命週期中的mounted(掛載)鉤子函數進行組件渲染監聽,當組件第一次被渲染後就保存在內存中,下次切換不會被重新渲染。<div id="app"> / <a href="#" @click.prevent="page='index'">首頁</a> / <a href="#" @click.prevent="page='news'">新聞頁</a> / <a href="#" @click.prevent="page='login'">登錄頁</a> <hr > <!-- 把切換出去的組件保留在內存中 --> <keep-alive> <component :is="page"></component> </keep-alive> </div> </body> <script type="text/javascript"> Vue.component('index', { template:'<h5>首頁</h5>', mounted: function () { console.log('掛載...首頁'); } }); Vue.component('news', { template:'<h5>新聞頁</h5>', mounted: function () { console.log('掛載...新聞頁'); } }); Vue.component('login', { template:'<h5>登陸頁</h5>', mounted: function () { console.log('掛載...登錄頁'); } }); /* 父組件 */ new Vue({ el:"#app", data:{ page:"login" } }); </script>
3.refs屬性
使用ref 給每個組件起一個固定的名字,方便後續直接引用操作,在父組件中使用$refs訪問子組件。
<div id="app"> <child ref="ref1"></child> <hr> <child ref="ref2"></child> </div> </body> <script type="text/javascript"> Vue.component('child', { template:'<button @click="count++">{{count}}</button>', data:function(){ return {count:0}; } }); /* 父組件 */ var app = new Vue({ el:"#app", }); </script>
四、數據處理
1.watch屬性
在Vue組件中,使用watch屬性來監聽數據的變化,同時可以指定監聽那個屬性。
watch:{**
監聽的數據:function(newValue,oldValue){
}
}**<div id="app"> {{uname}} -- {{upwd}} <hr > <p> firstName: <input type="text" v-model="firstName"> </p> <p> lastName: <input type="text" :value="lastName" @input="changeLastName"> </p> <h4>{{fullName}}</h4> </div> </body> <script type="text/javascript"> var app = new Vue({ el:"#app", data:{ uname:"zhangsan", upwd:"123456", firstName:"Hello", lastName:"Kitty", fullName:"Hello Kitty" }, // watch屬性 監聽數據變化,可以得知數據變化前的值與變化後的結果 watch:{ uname:function(v1,v2){ console.log(v1+","+v2); }, upwd:function(newValue,oldValue){ console.log("改變後的值:" + newValue + ",改變後的值:" + oldValue); }, // 監聽值的變化 firstName:function(newValue,oldVlue){ this.fullName = newValue + " " + this.lastName; }, lastName:function(newValue,oldVlue){ this.fullName = this.firstName + " " + newValue; } }, methods:{ changeLastName:function(e){ // 給數賦值 this.lastName = e.target.value } } }); </script>
2.$watch
除了在組件內部使用watch也可以使用內部命令watch第一個參數是需要監聽的屬性,第二個是回調函數用法和watch一樣。
需要取消監聽只需拿到監聽對象的引用,這個引用是返回一個函數對象,執行該對象就可以取消監聽。
同時監聽多個屬性,可以不指定屬性<div id="app"> {{uname}} </div> </body> <script type="text/javascript"> var app = new Vue({ el:"#app", data:{ uname:"zhangsan", upwd:'123' }, }); // 使用$watch監聽指定數據 /* var unwacth = app.$watch("uname",function(v1,v2){ console.log(v1+","+v2) }); */ // 取消監聽 (返回一個函數對象,執行該對象就可以取消監聽) // unwacth(); // 監聽所有數據 app.$watch(function(){ return this.uname +"-" + this.upwd; },function(newValue, oldValue) { console.log('newValue:【'+newValue+'】 oldValue:【'+oldValue+'】'); }); </script>
3.computed屬性
computed計算屬性用於定義比較複雜的屬性計算
注:如果是computed屬性計算得到的結果,使用時不需要加括號,與定義在data中的屬性使用方法一致
computed和methods區別:
計算屬性使用computed定義, 方法使用methods定義
計算屬性使用時不加括號執行符
計算屬性是基於它們的依賴進行緩存的,計算屬性只有在它的相關依賴發生改變時纔會重新求值。否則返回之前計算好的值,性能更高!<div id="app"> <p> firstName: <input type="text" v-model="firstName"> </p> <p> lastName: <input type="text" v-model="lastName"> </p> <!-- 如果是computed屬性計算得到的結果,使用時不需要加括號,與定義在data中的屬性使用方法一致 --> <h4>{{fullName2}}</h4> <!-- 如果是methods中定義的方法,使用時需要添加括號調用該方法 --> <h4>{{fullName()}}</h4> </div> </body> <script type="text/javascript"> var app = new Vue({ el:"#app", data:{ firstName:"Hello", lastName:"Kitty" }, // 計算數據 computed:{ fullName2:function(){ return this.firstName + " " + this.lastName; } }, methods:{ fullName:function(){ return this.firstName + " " + this.lastName; } } }); </script>
4.getter和setter
<div id="app"> <p> fullName: <input type="text" v-model="fullName" /> </p> <h4>firstName: {{firstName}}</h4> <h4>lastName: {{lastName}}</h4> </div> </body> <script type="text/javascript"> var app = new Vue({ el:"#app", data:{ firstName:"Hello", lastName:"Kitty" }, // 計算數據 computed:{ fullName:{ // get 取值 get:function(){ return this.firstName + " " + this.lastName; }, // set 賦值 set:function(value){ console.log(value); var news = value.split(" "); this.firstName = news[0]; this.lastName = news[1]; } } } }); </script>
五、生命週期
1.生命週期
常用的生命週期鉤子函數有:
1) created: 實例創建完成後被立即調用
2) mounted: 實例掛載完成後被立即調用
3) beforeUpdate: 實例需要渲染之前調用
4) updated: 實例更新後調用
5) destroyed: Vue 實例銷燬後調用<div id="app"> {{uname}} </div> </body> <script type="text/javascript"> var app = new Vue({ el:"#app", data:{ uname:"admin" }, created: function () { console.log('實例創建...'); }, mounted:function () { console.log('實例掛載...'); }, beforeUpdate:function () { console.log('實例將要更新...'); }, updated:function () { console.log('實例已更新...'); }, destroyed:function(){ console.log('實例卸載...'); } }); // 銷燬實例 // app.$destroy(); </script>
2.鉤子函數
el: 指令所綁定的元素,可以用來直接操作 DOM 。
binding: 一個對象,包含以下屬性:
- name: 指令名,不包括
v-
前綴。- value: 指令的綁定值, 例如:
v-my-directive="1 + 1"
, value 的值是2
。- oldValue: 指令綁定的前一個值,僅在
update
和componentUpdated
鉤子中可用。無論值是否改變都可用。- expression: 綁定值的表達式或變量名。 例如
v-my-directive="1 + 1"
, expression 的值是"1 + 1"
。- arg: 傳給指令的參數。例如
v-my-directive:foo
, arg 的值是"foo"
。- modifiers: 一個包含修飾符的對象。 例如:
v-my-directive.foo.bar
, 修飾符對象 modifiers 的值是{ foo: true, bar: true }
。vnode: Vue 編譯生成的虛擬節點。
oldVnode: 上一個虛擬節點,僅在
update
和componentUpdated
鉤子中可用。<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue</title> <script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script> </head> <body> <div id="app" v-runoob:hello.a.b="message"> </div> <script> Vue.directive('runoob', { bind: function (el, binding, vnode) { var s = JSON.stringify el.innerHTML = 'name: ' + s(binding.name) + '<br>' + 'value: ' + s(binding.value) + '<br>' + 'expression: ' + s(binding.expression) + '<br>' + 'argument: ' + s(binding.arg) + '<br>' + 'modifiers: ' + s(binding.modifiers) + '<br>' + 'vnode keys: ' + Object.keys(vnode).join(', ') } }) new Vue({ el: '#app', data: { message: '哈士奇' } }) </script> </body> </html>
六、自定義指令
1.自定義指令
除了默認設置的核心指令 (v-model 和 v-show),Vue 也允許註冊自定義指令。
兩種定義方式:全局指令和局部指令1.定義指令
全局指令
Vue.directive(“指令名”,{
//inserted 鉤子函數,當綁定元素插入到DOM調用
inserted:function(el){
// el 代表的是當前綁定指令的元素對象
}
});
局部指定
- 使用指令
v-指令名 注:使用directive定義,第一個參數爲指令名,使用時加上v-前綴才能生效。inserted屬性指當綁定元素插入到DOM時調用。
<div id="app"> <input type="text" v-focus v-aa > <input type="text" v-demo:ab.a.b="1+1"> </div> </body> <script type="text/javascript"> /* 定義全局指令 */ Vue.directive("focus",{ //當綁定元素插入到DOM調用 inserted:function(el){ // el 代表的是當前綁定指令的元素對象 el.focus(); // 聚焦 } }); Vue.directive('demo',{ bind: function (el,binding) { console.log(el); console.log(binding); } }); var app = new Vue({ el:"#app", data:{ uname:"admin" }, /* 局部指令 */ directives:{ aa:function(el){ console.log(el); el.value="Hello" } } }); // 銷燬實例 // app.$destroy(); </script>
2.實現圖片懶加載
谷歌圖片的加載做得非常優雅,在圖片未完成加載前,用隨機的背景色佔位,圖片加載完成後才直接渲染出來,用自定義指令可以非常方便的實現這個功能。
<div id="app"> <div class="item" v-for="item in imgs" v-img="item.url"></div> </div> </body> <script type="text/javascript"> /* 定義全局自定義指令v-img */ Vue.directive("img",{ bind:function(el,binding){ console.log(el); console.log(binding); //生成隨機顏色 var color=parseInt(Math.random()*0xFFFFFF).toString(16); //設置當前元素的背景,提前進行佔位等待圖片加載 el.style.background="#"+color; //setTimeout模擬圖片加載的延時情況 setTimeout(function() { //得到圖片的路徑 var url=binding.value; //得到圖片對象 var img=new Image(); //設置img對象的人src屬性 img.src=url; //將img對象設置到div(el對象)中 el.appendChild(img); },Math.random()*100+500);//隨機延時 } }); var app=new Vue({ el:"#app", data:{ //定義模擬數據 imgs:[ {url:'img/1.jpg'}, {url:'img/2.jpg'}, {url:'img/3.jpg'}, {url:'img/4.jpg'} ] } }); </script>
3.過濾器
Vue允許自定義過濾器,可被用作一些常見的文本格式化。
過濾器可以用在兩個地方:mustache 插值和 v-bind 表達式 (後者從 2.1.0+ 開始支持)。
過濾器應該被添加在 JavaScript 表達式的尾部,由“管道”符指示:
{{數據 | 過濾器}}
多個過濾器可以串聯
{{數據 | 過濾器 | 過濾器2}} 定義過濾器
全局過濾器
Vue.filter(“uppercase”,function(value){
return value.toUpperCase();
});
局部過濾器
filters:{
length:function(value){
return value.length;
}
}<div id="app"> <!-- 使用過濾器 --> {{msg}} --{{msg | uppercase}}--{{msg | length}}<br> {{msg | uppercase | test}}<br> {{msg | test01("-->","<--")}} </div> </body> <script type="text/javascript"> /* 定義全局過濾器 */ Vue.filter("uppercase",function(value){ return value.toUpperCase(); }); var app=new Vue({ el:"#app", data:{ msg:"Hello" }, /* 定義局部過濾器 */ filters:{ length:function(value){ return value.length; }, test:function(value){ return value+":"+value.length; }, test01:function(value,begin,end){ return begin + value + end; } } }); </script>