閱讀目錄
1.vue屬性和方法
每個Vue例項都會代理其 data物件裡所有的屬性。
如下程式碼:
var data = { a: 1 }; var vm = new Vue({ data: data }); console.log(vm); console.log(vm.a === data.a); // true // 設定屬性也會影響到原始資料 vm.a = 2; console.log(data.a); // 2 // 反之 data.a = 3; console.log(vm.a); // 3 //除了data屬性,Vue例項還暴露了一些有用的例項屬性與方法。這些屬性與方法都有字首$, 以便與代理的data屬性區分。 var data = { a: 1 } var vm = new Vue({ el: '#container1', data: data }) console.log(vm.$data === data) // true console.log(vm.$el === document.getElementById('container1')) // true data.a = 5; // $watch 是一個例項方法 vm.$watch('a', function (newVal, oldVal) { // 這個回撥將在 `vm.a` 改變後呼叫 console.log(newVal); console.log(oldVal); })
1-1. data 必須是函式
通過Vue構造器傳入的各種選項大多數都可以在元件裡用。 data 是一個例外,它必須是函式。 如下程式碼Vue 會停止,並在控制檯會報錯。
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <component1></component1> </div> </body> <script src="./vue.js"></script> <script type="text/javascript"> var data = { counter: 0 }; // 全域性註冊 Vue.component('component1', { template: '<span>{{ message }}</span>', data: { message: 'hello' } }); new Vue({ el: '#container1' }) </script> </html>
data是函式解決該方案
程式碼如下:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <component1></component1> <component1></component1> <component1></component1> </div> </body> <script src="./vue.js"></script> <script type="text/javascript"> var data = { counter: 0 }; // 全域性註冊 Vue.component('component1', { template: '<button v-on:click="counter += 1">{{ counter }}</button>', // data是一個函式,vue不會報錯,但是我們返回給每個元件的實列引用了同一個data物件 data: function() { return data } }); new Vue({ el: '#container1' }) </script> </html>
由於這三個元件共享了同一個 data , 因此增加一個 counter 會影響所有元件!這不對。我們可以通過為每個元件返回全新的 data 物件來解決這個問題:
程式碼如下:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <component1></component1> <component1></component1> <component1></component1> </div> </body> <script src="./vue.js"></script> <script type="text/javascript"> // 全域性註冊 Vue.component('component1', { template: '<button v-on:click="counter += 1">{{ counter }}</button>', // data是一個函式,vue不會報錯,但是我們返回給每個元件的實列引用了同一個data物件 data: function() { return { counter: 0 } } }); new Vue({ el: '#container1' }) </script> </html>
現在每個 counter 都有它自己內部的狀態了.
2.理解元件的通訊。
一般情況父子元件是這樣的關係,元件A在它的模板中使用了元件B,他們之間必然需要相互通訊,父元件要給子元件傳遞資料,子元件需要將它內部發生的事情告知父元件,為了保證父子元件的解耦,可維護性及可重用性。在vue.js中,父元件通過props向下傳遞資料給子元件,子元件通過events給父元件傳送訊息。
2-1 使用props傳遞資料
不能在子元件的模板內直接引用父元件的資料,要讓子元件使用父元件的資料,我們需要通過子元件的props選項。
如下程式碼:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <child message="hello!"></child> </div> </body> <script src="./vue.js"></script> <script type="text/javascript"> // 全域性註冊 Vue.component('child', { // 宣告props props: ['message'], template: '<span>{{ message }}</span>' }); new Vue({ el: '#container1' }) </script> </html>
結果在頁面上會列印 hello。
注意: HTML特性是不區分大小寫的,所以當使用的不是字串模板,camelCased(駝峰式) 命名的prop需要轉換為相對應的 kebab-case(短橫線隔開式)命名:
如下程式碼:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <!-- kebab-case in HTML--> <child my-message="hello!"></child> </div> </body> <script src="./vue.js"></script> <script type="text/javascript"> // 全域性註冊 Vue.component('child', { // 宣告props props: ['myMessage'], template: '<span>{{ myMessage }}</span>' }); new Vue({ el: '#container1' }) </script> </html>
2-2 理解動態prop
在模板中,要動態地繫結父元件的資料到子模板的props,使用v-bind,每當父元件的資料變化時,該變化會傳遞給子元件。
<div id="container1"> <input v-model='parentMsg' /> <br /> <!-- kebab-case in HTML--> <child v-bind:my-message="parentMsg"></child> </div>
使用 v-bind 的縮寫語法通常更簡單:
<child :my-message="parentMsg"></child>
程式碼如下:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <input v-model='parentMsg' /> <br /> <!-- kebab-case in HTML--> <child v-bind:my-message="parentMsg"></child> </div> </body> <script src="./vue.js"></script> <script type="text/javascript"> new Vue({ el: '#container1', data: { parentMsg: 'Message' }, components: { child: { props: ['myMessage'], template: '<span>{{myMessage}}</span>' } } }) </script> </html>
3.理解自定義事件
父元件使用props傳遞資料給子元件,但是如果子元件需要把資料傳回去的話,就需要自定義事件了;
3-1 使用v-on繫結自定義事件
每個vue例項都實現了事件介面,即:
1. 使用 $on(eventName) 監聽事件
2. 使用 $emit(eventName) 觸發事件
注意: $on 和 $emit 不是 addEventListener 和 dispatchEvent的別名。且 父元件可以在使用元件的地方直接用 v-on 來監聽子元件觸發的事件。
不能用$on偵聽子元件丟擲的事件,而必須在模板裡直接用v-on繫結,就像以下的例子:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <p> {{ total }} </p> <button-counter v-on:increment="incrementTotal"></button-counter> <button-counter v-on:increment="incrementTotal"></button-counter> </div> </body> <script src="./vue.js"></script> <script type="text/javascript"> Vue.component('button-counter', { template: '<button v-on:click="increment">{{ counter }}</button>', data: function() { return { counter: 0 } }, methods: { increment: function() { this.counter += 1; this.$emit('increment'); } }, }) new Vue({ el: '#container1', data: { total: 0 }, methods: { incrementTotal: function() { this.total += 1; } } }) </script> </html>
上面程式碼: 初始化時候 例項化設定 data: {total: 0}, 設定total為0, 子元件button-counter 預設為0, 當點選子元件的時候呼叫 increment方法,當前的counter自增1, 然後在子元件觸發 $emit('increment')事件,當使用 v-on:increment 會監聽到事件後,會呼叫父元件的incrementTotal方法,因此父元件也自增1.
上面程式碼中 子元件已經和它外部完全解耦了。它所做的只是報告自己的內部事件,至於父元件是否關心則與它無關。
4.理解使用自定義事件的表單輸入元件
自定義事件可以用來建立自定義的表單輸入元件,使用v-modal來進行資料雙向繫結。比如如下程式碼:
<input v-modal="something" />
上面的程式碼是下面的語法糖;如下程式碼:
<input v-bind:value="something" v-on:input="something=$event.target.value" />
因此在建立元件中時,相當於下面的簡寫;如下程式碼:
<custom-input v-bind:value="something" v-on:input="something=arguments[0]"></custom-input>
所以要讓元件的v-model 生效,必須滿足下面的條件:
1. 接受一個value屬性。
2. 在有新的value時觸發input事件。
如下測試程式碼:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <currency-input v-model="price"></currency-input> </div> </body> <script src="./vue.js"></script> <script> </script> <script type="text/javascript"> Vue.component('currency-input', { template: '\ <span>\ $\ <input\ ref="input"\ v-bind:value="value"\ v-on:input="updateValue($event.target.value)"\ >\ </span>\ ', props: ['value'], methods: { // 不是直接更新值,而是使用此方法來對輸入值進行格式化和位數限制 updateValue: function (value) { var formattedValue = value // 刪除兩側的空格符 .trim() // 保留 2 小數位 .slice(0, value.indexOf('.') + 3) // 如果值不統一,手動覆蓋以保持一致 if (formattedValue !== value) { this.$refs.input.value = formattedValue } // 通過 input 事件發出數值 this.$emit('input', Number(formattedValue)) } } }); new Vue({ el: '#container1', data: { price: 0 } }) </script> </html>
5.單個slot
<slot>標籤中的任何內容都被視為 備用內容。備用內容在子元件的作用域內編譯,並且只有在宿主元素為空,且沒有插入的內容時才顯示備用內容。
如果<slot>標籤中有內容的話,就顯示該內容。
比如 my-component 元件有如下程式碼:
<div class="content">
<h2>this is a component</h2>
<slot>如果沒有分發內容,則顯示slot中的內容</slot>
<p>asdsadsdad</p>
</div>
父元件有如下程式碼:
<div id="container1"> <my-component> <h1>Hello Vue.js</h1> </my-component> <my-component></my-component> </div>
渲染後的結果為:
<div id="container1"> <div class="content"> <h2>this is a component</h2> <h1>Hello Vue.js</h1> <p>asdsadsdad</p> </div> <div class="content"> <h2>this is a component</h2> 如果沒有分發內容,則顯示slot中的內容 <p>asdsadsdad</p> </div> </div>
所有測試例項程式碼如下:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <my-component> <h1>Hello Vue.js</h1> </my-component> <my-component></my-component> </div> <template id="myComponent"> <div class="content"> <h2>this is a component</h2> <slot>如果沒有分發內容,則顯示slot中的內容</slot> <p>asdsadsdad</p> </div> </template> </body> <script src="./vue.js"></script> <script type="text/javascript"> Vue.component('my-component', { template: '#myComponent' }) new Vue({ el: '#container1' }) </script> </html>
6.具名slot
<slot> 元素可以用一個特殊的屬性 name 來配置如何分發內容。多個 slot 可以有不同的名字。具名 slot 將匹配內容片段中有對應 slot 特性的元素。
如果沒有預設的 slot ,這些找不到匹配的內容片段將被拋棄。
比如:假如有一個 my-component 元件,它的模板為:
<template id="myComponent"> <div class='content'> <header> <slot name='header'></slot> </header> <main> <slot></slot> </main> <footer> <slot name='footer'></slot> </footer> </div> </template>
父元件的模板如下:
<div id="container1"> <h1 slot="header">這裡可能是一個頁面標題</h1> <p>主要內容的一個段落</p> <p>另一個主要段落</p> <p slot='footer'>這裡是底部資訊</p> </div>
頁面渲染的結果如下:
<div id="container1"> <h1>這裡可能是一個頁面標題</h1> <p>主要內容的一個段落</p> <p>另一個主要段落</p> <p>這裡是底部資訊</p> </div>
所有的程式碼如下:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <h1 slot="header">這裡可能是一個頁面標題</h1> <p>主要內容的一個段落</p> <p>另一個主要段落</p> <p slot='footer'>這裡是底部資訊</p> </div> <template id="myComponent"> <div class='content'> <header> <slot name='header'></slot> </header> <main> <slot></slot> </main> <footer> <slot name='footer'></slot> </footer> </div> </template> </body> <script src="./vue.js"></script> <script type="text/javascript"> Vue.component('my-component', { template: '#myComponent' }) new Vue({ el: '#container1' }) </script> </html>
7.理解作用域插槽(2.1.0新增的)
在slot分發中,無論是單分發還是具名分發,都是父元件替換子元件的資料,或者沒有替換,用子元件預設的資料。 但是通過設定作用域槽,就可以改變這種狀況,讓子元件可以在父元件進行分發時獲取自己的資料,至於是什麼資料,由子元件決定,這樣就能解耦了。
作用域槽通過slot的一個自定義的屬性,官方給出的DEMO是text,但也可以是其他,值為暴露的資料。 這個自定義屬性已經存放在子元件的prop物件裡了。等待著被父元件獲取。
怎麼獲取呢? 在父元件的模板裡,使用一個Vue自帶的特殊元件<template> ,並在該元件上使用scope屬性,值是一個臨時的變數,存著的是由子元件傳過來的prop物件,獲得由子傳過來的prop物件。這時候,父元件就可以訪問子元件在自定義屬性上暴露的資料了。
如下程式碼:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <parent-component></parent-component> </div> </body> <script src="./vue.js"></script> <script type="text/javascript"> // 子元件 Vue.component('child-component', { template: '<ul><slot name="child-ul" v-for="item in rets" v-bind:text="item.name"></slot></ul>', data: function() { return { rets: [ {name: '我是蘋果'}, {name: '我是香蕉'}, {name: '我是橘子'} ] } } }); // 父元件 Vue.component('parent-component', { template: '<child-component><template scope="props" slot="child-ul"><li>{{props.text}}</li></template></child-component>' }) new Vue({ el: '#container1' }) </script> </html>
頁面渲染後的程式碼如下:
<div id="container1"> <ul> <li>我是蘋果</li> <li>我是香蕉</li> <li>我是橘子</li> </ul> </div>
8.理解動態元件
通過使用保留的<component>元素,動態地繫結到它的 is 特性,我們可以讓多個元件使用同一個掛載點,並動態的切換。
keep-alive: 如果把切換出去的元件留在記憶體中,可以保留它的狀態或避免重新渲染,為此我們可以新增一個 keep-alive指令引數。
如下實現的tab切換程式碼:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <h3>動態元件</h3> <template id="tab-01"> <div>this is tab01</div> </template> <template id='tab-02'> <div>this is tab02</div> </template> <template id="tab-03"> <div>this is tab03</div> </template> <div id="container1"> <!-- 導航欄 --> <ul> <li> <a href="javascript:void(0)" @click="toggleTabs(tab01Text);">{{ tab01Text }}</a> </li> <li> <a href="javascript:void(0)" @click="toggleTabs(tab02Text);">{{ tab02Text }}</a> </li> <li> <a href="javascript:void(0)" @click="toggleTabs(tab03Text);">{{ tab03Text }}</a> </li> </ul> <!-- 點選導航後要切換的內容 --> <div class='content' style='height: 200px'> <!-- 如果把切換出去的元件保留在記憶體中,可以保留它的狀態或避免重新渲染。為此可以新增一個 keep-alive 指令引數 --> <keep-alive> <component :is="currentView"></component> </keep-alive> </div> </div> </body> <script src="./vue.js"></script> <script type="text/javascript"> var tab01 = Vue.extend({ template: '#tab-01' }); var tab02 = Vue.extend({ template: '#tab-02' }); var tab03 = Vue.extend({ template: '#tab-03' }); // 新建vue例項 var newVue = new Vue({ el: '#container1', data: { tab01Text: "tab01", // 選單一 tab02Text: "tab02", // 選單二 tab03Text: "tab03", // 選單三 currentView: "tab01" // 預設選中的導航欄 }, // 區域性註冊元件 components: { tab01: tab01, tab02: tab02, tab03: tab03, }, methods: { // 繫結tab的切換事件 toggleTabs: function(tabText) { this.currentView = tabText; } } }) </script> </html>