深入探討Vue.js組件和組件通信
基本是按照官網(wǎng)的 Guide 全部梳理了一遍:http://vuejs.org/guide/index.html 這里我們以一個 Todo List 應(yīng)用為例來把相關(guān)的只是都串起來,這篇里面的全部代碼都在github上 https://github.com/lihongxun945/vue-todolist
Vue 實例
一個 Vue 應(yīng)用是由一個 root vue instance 引導(dǎo)啟動的,而 Vue instance 是這么創(chuàng)建的:
var vm = new Vue({
// options
})
一個 instance 實際上就是 MVVM 中的一個 VM。 傳入的配置對象中data里的所有屬性都會被掛載到 instance上,而為了避免命名沖突,Vue 內(nèi)置方法都會以 $ 開頭的屬性掛載到 instance 上。
instance 從創(chuàng)建到銷毀會經(jīng)歷如下生命周期:
在初始化的時候大致經(jīng)過三步:
•綁定數(shù)據(jù)監(jiān)聽,即對 data 的監(jiān)聽
•編譯模板
•插入document或者替換對應(yīng)dom
# Vue 基本語法
數(shù)據(jù)綁定
Vue 使用的是一種 類 mastache 語法。常用綁定語法分這么幾類:
•mastache 語法,比如 {{ data }} {{ data | filter}}
•v-bind 綁定屬性,比如 v-bind: href, v-bind:class
•v-on 綁定事件, 比如 v-on:click, v-on:submit
其中 v-* 都是 directive
例子:
<div v-bind:class="[classA, isB ? classB : '']">
屬性計算
Vue 支持一個很有意思的屬性計算語法,可以指定一個屬性由其他屬性計算出來,這樣就不用通過 $watch 來實現(xiàn)了:
var vm = new Vue({
el: '#example',
data: {
a: 1
},
computed: {
// a computed getter
b: function () {
// `this` points to the vm instance
return this.a + 1
}
}
})
## 流程控制和列表相關(guān)的語法 包括 `v-if`, `v-show`, `v-else`, `v-for`
表單
雙向數(shù)據(jù)綁定:
<input type="text" v-model="message" placeholder="edit me">
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
## 動畫 動畫的實現(xiàn)方式和 Angular 以及 React 都是一樣的,都是通過添加和刪除 class 來實現(xiàn)的。 # Component
組件的基本用法
Component 的定義包括兩部分:
1 創(chuàng)建component類:
var Profile = Vue.extend({
template: "<div> Lily </div>"
});
2 注冊一個 tagname:
Vue.component("me-profile", Profile);
這樣我們就可以通過 tagname 來使用這么組件了:
<div id="todo"> <my-profile></my-profile> <form v-on:submit="add" v-on:submit.prevent> <input type="text" v-model="input"/> <input type="submit" value='add' /> </form> ... </div>
Vue.component("me-profile", Profile); 屬于全局注冊,如果只是在某一個頁面內(nèi)使用,可以通過局部注冊的方式:
var vm = new Vue({
el: "#todo",
components: {
"my-profile": Profile
},
...
}
其中因為我們的 Vue 實例是綁定在 todo 元素上的,所以如果把 my-profile 放在這個元素外面是無效的,只有放在這個里面才會被 Vue 的這個實例引導(dǎo)初始化。
注意事項:
Vue 構(gòu)造函數(shù)可以傳的參數(shù)基本都可以用在 Vue.extend 上,但是對 el 和 data 兩個參數(shù)需要注意,為了避免不同實例間共享同一個對象,總是要通過 function 返回一個新的對象比較靠譜:
var MyComponent = Vue.extend({
data: function () {
return { a: 1 }
}
})
因為參數(shù)都一樣,其實他們倆就是同一個東西,不過一個是組件,一個是用來引導(dǎo)Vue啟動的。
模板注意事項
因為 Vue 就是原生的DOM,所以有些自定義標(biāo)簽可能不符合DOM標(biāo)準(zhǔn),比如想在 table 中自定義一個 tr,如果直接插入 my-component 不符合規(guī)范,所以應(yīng)該這樣寫:
<table> <tr is="my-component"></tr> </table>
Props 傳遞數(shù)據(jù)
在 Vue 中每個組件都是獨立的,不能也不應(yīng)該直接訪問父類的data。所以我們通過 props 來向子組件傳遞數(shù)據(jù),是不是和 React 的方式很像?
不同于 React,在 Vue 中子組件需要先聲明自己的 props 才行:
var Profile = Vue.extend({
props: ["name"],
template: `
<h2>{{name}}'s Todo List</h2>
<h4>{{name}} is a good girl</h4>
`
});
然后我們可以在使用 Profile 的時候這樣傳遞參數(shù):
<my-profile name='Lily'></my-profile>
這種是通過字面量傳遞參數(shù),所以傳遞的值一定是字符串。還有一種方式是動態(tài)傳參,通過 v-bind 來傳遞參數(shù),可以雙向綁定數(shù)據(jù)或者傳非字符串參數(shù):
<my-profile v-bind:name=‘input'></my-profile>
v-bind 如果是一個字符串,則是綁定父組件的data中對應(yīng)的字段,比如上面就是雙向綁定了 input 的值。如果是一個數(shù)字則就是綁定了一個數(shù)字。
Vue 還可以顯式指定單向還是雙向的數(shù)據(jù)綁定:
<!-- default, one-way-down binding --> <child :msg="parentMsg"></child> <!-- explicit two-way binding --> <child :msg.sync="parentMsg"></child> <!-- explicit one-time binding --> <child :msg.once="parentMsg"></child>
Props 校驗
一個好的組件總是應(yīng)該先驗證參數(shù)是否正確,另外可能還需要設(shè)置一些參數(shù)的默認(rèn)值:
var Profile = Vue.extend({
input: {
type: String
}
});
父子組件通信
上面講到的 props 其實就是父組件向子組件傳遞消息的一種方式。
在子組件中有一個 this.$parent 和 this.$root 可以用來方法父組件和根實例。不過,現(xiàn)在我們應(yīng)該避免這么做。因為組件本身就是為了封裝獨立的邏輯,如果又去直接訪問父組件的數(shù)據(jù)就破壞了組件的封裝性。
所以我們應(yīng)該還是應(yīng)該通過父組件向子組件傳遞 props 的方式來通信。
當(dāng)然 props 其實只能做回調(diào)。在 React 中就探討過這個問題,React 的做法就是通過 props 來做,傳一個回調(diào)函數(shù)給子組件。其實我不是很喜歡這種把回調(diào)函數(shù)傳來傳去的方式,我更喜歡的是事件的方式。Vue 中子組件可以通過通過事件和父組件進行通信的。向父組件發(fā)消息是通過 this.$dispatch,而向子組件發(fā)送消息是通過 this.$boardcast,這里都是向所有的父親和孩子發(fā)送消息,但是一旦執(zhí)行一個回調(diào)之后就會停止,除非這個回調(diào)函數(shù)顯式返回了 true。
我們把之前的Todo List拆成不同的組件來實現(xiàn),這樣可以體驗下如何進行組件的雙向通信,我們拆分出兩個組件,分別是 List 和 Form 。
Form 負(fù)責(zé)處理用戶輸入,并在提交表單的時候向父組件發(fā)送一個 add 消息,代碼如下:
var Form = Vue.extend({
props: {
username: {
type: String,
default: "Unnamed"
}
},
data: function() {
return {
input: "",
};
},
template: `
<h1>{{username}}'s Todo List</h1>
<form v-on:submit="add" v-on:submit.prevent>
<input type="text" v-model="input"/>
<input type="submit" value='add' />
</form>
`,
methods: {
add: function() {
this.$dispatch("add", this.input); //這里就是向父組件發(fā)送消息
this.input = "";
}
}
});
List 只負(fù)責(zé)展示列表和處理用戶勾選操作,它接收到 add 消息之后會在自己上添加一個條目:
var List = Vue.extend({
template: `
<ul>
<li v-for='todo in list'>
<label v-bind:class="{ done : todo.done }" >
<input type="checkbox" v-model="todo.done"/>
{{todo.title}}
</label>
</li>
</ul>`,
props: {
initList: {
type: Array
}
},
data: function() {
return {
list: []
}
},
events: {
add: function(input) {
if(!input) return false;
this.list.unshift({
title: input,
done: false
});
}
}
});
然后,因為這是兩個組件,當(dāng)然需要一個 Vue 實例來引導(dǎo)啟動,我們的實例如下:
var vm = new Vue({
el: "#todo",
components: {
"todo-form": Form,
"todo-list": List
},
events: {
add: function(input) {
this.$broadcast("add", input);
}
}
});
注意,其實 Form 和 List 在邏輯上是平級的組件,所以他們沒有父子關(guān)系,他們共同都是 vm 的孩子。這里 vm 接到 Form 的消息之后會轉(zhuǎn)發(fā)給 List。
html 代碼就更簡單了:
<div id="todo"> <todo-form username='Lily'></todo-form> <todo-list></todo-list> </div>
Slot
通過 Slot 可以實現(xiàn)把父組件渲染出來的HTML插入到子組件中,目前還不清楚什么時候會需要這樣做,而且這么做對子組件的侵入性太大。
動態(tài)切換組件
這個功能感覺有點多余,感覺很多情況下我們應(yīng)該是通過邏輯代碼來實現(xiàn)切換,而不是通過Vue內(nèi)置的動態(tài)組件來切換。不過用來實現(xiàn)一個類似 tab 切換的功能還是很方便的。
我們這里給 Todo List 增加一個 about 頁面。那么首先我們需要把 vm 改成一個組件,這個組件叫 Todo,它就是整個 Todo 頁面:
var Todo = Vue.extend({
template: `
<div id="todo">
<todo-form username='Lily'></todo-form>
<todo-list></todo-list>
<slot>not show</slot>
</div>
`,
components: {
"todo-form": Form,
"todo-list": List
},
events: {
add: function(input) {
this.$broadcast("add", input);
}
}
});
其實改動就第一行。
然后我們需要創(chuàng)建一個 About 組件:
var About = Vue.extend({
template: `
<div id="about">
<p>About Todo List V0.1.0</p>
<p>Content here</p>
</div>`
});
接下來是重點了,我們要創(chuàng)建一個實例 vm,這vm要負(fù)責(zé)切換這兩個頁面:
var vm = new Vue({
el: "body",
data: {
currentView: "todo"
},
components: {
"todo": Todo,
"about": About
}
});
這里我們定義了一個 currentView 字段,當(dāng)然可以是任意名稱,然后通過特殊的 component 標(biāo)簽來進行組件切換:
<component :is="currentView"></component> <ul> <li><label><input type="radio" name='page' value='todo' v-model='currentView'> Home</label></li> <li><label><input type="radio" name='page' value='about' v-model='currentView'> About</label></li> </ul>
上面的代碼有兩處需要注意:
•通過 component這個特殊標(biāo)簽,然后用 :is 屬性來進行組件的切換。
•radio 通過雙向綁定來修改 currentView 字段,從而實現(xiàn)點擊之后就可以進行切換。
數(shù)據(jù)綁定的實現(xiàn)原理
Vue 把雙向綁定稱作 reactive,可以翻譯為響應(yīng)式數(shù)據(jù)綁定。內(nèi)部是通過 ES5 定義的 getter 和 setter 方法實現(xiàn)的,所以不支持 IE8 及以下瀏覽器,這種實現(xiàn)方式有兩個容易犯錯的地方:
•如果在 data 上直接添加和刪除屬性是無法被檢測到的,一般刪除是不會的,但是可能會動態(tài)添加,這個時候應(yīng)該通過 vm.$set(“name”, value) 的方式來添加。
•無法檢測到對象內(nèi)部的變化,也就是只能檢測 data 的屬性變化,如果 data.a 是一個對象,那么 data.a.b = 1 這種變化是無法被檢測到的。這種情況下應(yīng)該創(chuàng)建一個新的對象并賦值給 data.a 就行了。
異步更新機制
Vue 對DOM的更新是異步的! 這個異步是在一個異步隊列中進行的,不過這個異步隊列會在當(dāng)前的 Event Loop 中執(zhí)行完,所以如果修改了 Data 立刻去DOM中做查詢操作是不對的,這個時候DOM還沒有更新,正確的做法是這樣做:
vm.msg = 'new message' // change data
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
vm.$el.textContent === 'new message' // true
})
或者這樣:
vm.$nextTick(function () {
this.$el.textContent === 'new message' // true
})
花了半天時間才看完組件,下面應(yīng)該去看一下另一個重點: Directive
本文已被整理到了《Vue.js前端組件學(xué)習(xí)教程》,歡迎大家學(xué)習(xí)閱讀。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
ElementUI實現(xiàn)el-form表單重置功能按鈕
本文主要介紹了Element使用el-form時,點擊重置按鈕或者取消按鈕時會實現(xiàn)表單重置效果,具有一定的參考價值,感興趣的可以了解一下2021-07-07
vue?使用el-table循環(huán)輪播數(shù)據(jù)列表的實現(xiàn)
這篇文章主要介紹了vue?使用el-table循環(huán)輪播數(shù)據(jù)列表的實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-04-04
vue3項目如何使用樣式穿透修改elementUI默認(rèn)樣式
這篇文章主要介紹了vue3項目使用樣式穿透修改elementUI默認(rèn)樣式,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-03
Vue+ElementUi實現(xiàn)點擊表格鏈接頁面跳轉(zhuǎn)和路由效果
這篇文章主要介紹了Vue+ElementUi實現(xiàn)點擊表格中鏈接進行頁面跳轉(zhuǎn)和路由,本文結(jié)合實例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-03-03
vue.js中使用微信掃一掃解決invalid signature問題(完美解決)
這篇文章主要介紹了vue.js中使用微信掃一掃解決invalid signature問題(推薦),本文通過實例代碼給出解決方法,代碼簡單易懂非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04

