1. 元件簡介
元件(Component)是 Vue.js 最強大的功能之一,元件可以擴充套件 HTML 元素,封裝可重用的程式碼。
元件:為了拆分Vue例項的程式碼量,以不同的元件來劃分不同的功能模組,需要什麼樣的功能,可以去呼叫對應的元件。
模組化和元件化的區別:
◊ 模組化:是從程式碼邏輯的角度進行劃分的;方便程式碼分層開發,保證每個功能模組的職能單一。
◊ 元件化:是從UI介面的角度進行劃分的;前端的元件化,方便UI元件的重用。
2. 註冊元件
2.1 全域性元件
註冊全域性元件語法格式:
Vue.component(tagName, options)
其中,tagName 為元件名,options 為配置選項。
註冊元件後呼叫方式:
<tagName></tagName>
所有例項都能用全域性元件。
元件名定義方式:PascalCase和kebab-case。在元件命名時可以採用PascalCase或kebab-case,但在DOM中只能使用kebab-case。
PascalCase示例:
<div id="app"> <my-component></my-component> </div> <script> Vue.component('MyComponent', { template: '<div>標題</div>' }); var vm = new Vue({ el: "#app" }); </script>
kebab-case示例:
<div id="app"> <my-component></my-component> </div> <script> Vue.component('my-component', { template: '<div>標題</div>' }); var vm = new Vue({ el: "#app" }); </script>
<div id="app"> <home></home> </div> <script> Vue.component("home", { template: "<div>{{text}}</div>", data: function () { return { text: "主頁" }; } }); new Vue({ el: "#app" }); </script>
<div id="app"> <home></home> </div> <script> var homeTpl = Vue.extend({ template: "<div>{{text}}</div>", data: function () { return { text: "主頁" }; } }); Vue.component('home', homeTpl); new Vue({ el: "#app" }); </script>
使用template標籤:
<div id="app"> <home></home> </div> <template id="tpl"> <div>{{text}}</div> </template> <script> Vue.component("home", { template: "#tpl", data: function () { return { text: "主頁" }; } }); new Vue({ el: "#app" }); </script>
2.2 區域性元件
區域性元件只能在當前Vue示例中使用。
<div id="app"> <home></home> </div> <script> new Vue({ el: "#app", components: { "home": { template: "<div>{{text}}</div>", data: function () { return { text: "主頁" }; } } } }); </script>
2.3 Vue.extend
2.3.1 基本使用
<div id="app"> <home></home> </div> <script> var home = Vue.extend({ template: "<div>標題</div>" }); Vue.component("home", home); new Vue({ el: "#app" }); </script>
2.3.2 引數data
data:在 Vue.extend() 中必須是函式。
<body> <task></task> <script> var task = Vue.extend({ template:"<div>{{ taskName }}</div>", data:function(){ return { taskName:"任務名稱" } } }); new task().$mount("task"); </script> </body>
2.3.3 使用$mount
mount:掛載,將vue例項掛靠在某個dom元素上的一個過程。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>libing.vue</title> <script src="node_modules/vue/dist/vue.min.js"></script> </head> <body> <div id="app"></div> <script> var home = Vue.extend({ template: "<div>標題</div>" }); new home().$mount("#app"); </script> </body> </html>
3. 元件通訊
3.1 props:父元件向子元件傳遞資料
prop 是元件用來傳遞資料的自定義特性,在元件上註冊自定義屬性。
prop特性註冊成為元件例項的屬性。
props
:父元件向子元件傳遞資料。
一個元件預設可以擁有任意數量的 prop,任何值都可以傳遞給任何 prop。
3.1.1 靜態props
示例:
<div id="app"> <home text="主頁"></home> </div> <script> var homeTpl = Vue.extend({ props:["text"], template: "<div>{{text}}</div>" }); Vue.component('home', homeTpl); new Vue({ el: "#app" }); </script>
3.1.2 動態props
使用 v-bind 動態繫結 props 的值到父元件的資料中。每當父元件的資料變化時,該變化也會傳導給子元件。
<div id="app"> <home v-bind:text="text"></home> </div> <script> var homeTpl = Vue.extend({ props: ["text"], template: "<div>{{text}}</div>" }); Vue.component('home', homeTpl); new Vue({ el: "#app", data: { text: "主頁" } }); </script>
由於HTML Attribute不區分大小寫,當使用DOM模板時,camelCase的props名稱要轉為kebab-case。
<div id="app"> <home warning-text="提示資訊"></home> </div> <script> Vue.component('home', { props: ['warningText'], template: '<div>{{ warningText }}</div>' }); var vm = new Vue({ el: "#app" }); </script>
傳遞的資料可以是來自父級的動態資料,使用指令v-bind來動態繫結props的值,當父元件的資料變化時,也會傳遞給子元件。
<div id="app"> <home v-bind:warning-text="warningText"></home> </div> <script> Vue.component('home', { props: ['warningText'], template: '<div>{{ warningText }}</div>' }); var vm = new Vue({ el: "#app", data: { warningText: '提示資訊' } }); </script>
注:prop 是單向傳遞,當父元件的屬性變化時,將傳遞給子元件,但是不會反過來。這是為了防止子元件無意修改了父元件的狀態。
3.1.3 props驗證
為元件的 prop 指定驗證要求,如果有一個需求沒有被滿足,則 Vue 會在控制檯中警告。
Vue.component('my-component', { props: { // 基礎的型別檢查 (`null` 匹配任何型別) propA: Number, // 多個可能的型別 propB: [String, Number], // 必填的字串 propC: { type: String, required: true }, // 帶有預設值的數字 propD: { type: Number, default: 100 }, // 帶有預設值的物件 propE: { type: Object, // 物件或陣列且一定會從一個工廠函式返回預設值 default: function () { return { message: 'hello' } } }, // 自定義驗證函式 propF: { validator: function (value) { // 這個值必須匹配下列字串中的一個 return ['success', 'warning', 'danger'].indexOf(value) !== -1 } } } });
型別檢查:type可以是下列原生建構函式中的一個:String、Number、Boolean、Array、Object、Date、Function、Symbol,也可以是一個自定義的建構函式,並且通過 instanceof 來進行檢查確認。
示例:
<div id="app"> <parent-component></parent-component> </div> <template id="child-component1"> <h2>{{ message }}</h2> </template> <template id="child-component2"> <h2>{{ message }}</h2> </template> <template id="parent-component"> <div> <child-component1></child-component1> <child-component2></child-component2> </div> </template> <script> Vue.component('parent-component', { template: '#parent-component', components: { 'child-component1': { template: '#child-component1', data() { return { message: '子元件1' }; } }, 'child-component2': { template: '#child-component2', data() { return { message: '子元件2' }; } } } }); var vm = new Vue({ el: "#app" }); </script>
示例:
<div id="app"> <todo :todo-data="taskList"></todo> </div> <template id="tpl-todo-item"> <li>{{ id }} - {{ text }}</li> </template> <template id="tpl-todo-list"> <ul> <todo-item v-for="item in todoData" :id="item.id" :text="item.text"></todo-item> </ul> </template> <script> // 構建一個子元件 var todoItem = Vue.extend({ template: "#tpl-todo-item", props: { id: { type: Number, required: true }, text: { type: String, default: '' } } }) // 構建一個父元件 var todoList = Vue.extend({ template: "#tpl-todo-list", props: { todoData: { type: Array, default: [] } }, // 區域性註冊子元件 components: { todoItem: todoItem } }) // 註冊到全域性 Vue.component('todo', todoList) new Vue({ el: "#app", data: { taskList: [{ id: 1, text: 'New' }, { id: 2, text: 'InProcedure' }, { id: 3, text: 'Done' } ] } }); </script>
3.2 自定義事件:子元件向父元件傳遞資料
每一個Vue例項都實現事件介面:
$on(eventName)
:監聽事件
$emit(eventName)
:觸發事件
子元件需要向父元件傳遞資料時,子元件用$emit(eventName)
來觸發事件,父元件用$on(eventName)
來監聽子元件的事件。
<div id="app"> <searchbar></searchbar> </div> <template id="tpl-search-form"> <div class="input-group form-group" style="width: 500px;"> <input type="text" class="form-control" placeholder="請輸入查詢關鍵字" v-model="keyword" /> <span class="input-group-btn"> <input type="button" class="btn btn-primary" value="查詢" @click="search"> </span> </div> </template> <template id="tpl-search-bar"> <searchform @onsearch="search"></searchform> </template> <script> // 構建一個子元件 var searchform = Vue.extend({ template: "#tpl-search-form", data: function () { return { keyword: 'libing' }; }, methods: { search: function () { this.$emit('onsearch', this.keyword); } } }); // 構建一個父元件 var searchbar = Vue.extend({ template: "#tpl-search-bar", components: { searchform: searchform }, methods: { search(keyword) { console.log(keyword); } } }) // 註冊到全域性 Vue.component('searchbar', searchbar); new Vue({ el: "#app" }); </script>
購物車示例:
<div id="app"> <shoppingcart :shopppingcarts="products" @calc="getTotal"></shoppingcart> <div>總計:{{ totalPrice }}</div> </div> <template id="shoppingcart"> <table> <tr> <th>商品ID</th> <th>商品名稱</th> <th>單價</th> <th>數量</th> </tr> <tr v-for="item in shopppingcarts"> <td>{{ item.ID }}</td> <td>{{ item.ProductName }}</td> <td>{{ item.UnitPrice }}</td> <td><input type="text" v-model="item.Quantity" @change="calcTotal" /></td> </tr> </table> </template> <script> var shoppingcart = Vue.extend({ template: "#shoppingcart", props: ["shopppingcarts"], methods: { calcTotal: function () { this.$emit("calc"); } } }); new Vue({ el: "#app", components: { shoppingcart: shoppingcart }, data: { totalPrice: 100, products: [{ ID: 1, ProductName: "手機", UnitPrice: 1000, Quantity: 2 }, { ID: 2, ProductName: "電腦", UnitPrice: 5000, Quantity: 5 }] }, methods: { getTotal() { console.log(new Date()); this.totalPrice = 0; this.products.forEach(product => { this.totalPrice += product.UnitPrice * product.Quantity; }); } }, mounted() { //當vue執行完畢之後,去執行函式 this.getTotal(); } }); </script>