VUE家族系列:
01、component元件
1.1、component基礎知識
元件是可以複用的Vue模組,是一個獨立的,有自己的檢視、樣式CSS、ViewMoel業務邏輯,結構的完整模組。當成自定義元素,可以在任意Vue中、模板、其他元件中使用。一個複雜的頁面、系統可以拆分為多個元件,獨立開發和維護,可複用、更清晰。
?註冊元件:Vue.component( id, [definition] )
註冊全域性元件,id
為元件的名稱,也作為元素名,引數和Vue引數選項物件基本一致。
data
必須是函式:透過函式返回物件,避免不同元件例項共享data
資料。template
模板,字串模板,或者<template>
選擇器,不能用真實Dom元素。- 必須有根元素:元件的模板必須有一個有效的根元素。
?props 引數:透過props
定義元件引數(Array<string> | Object
),引數都會作為元件元素的attribute
特性使用,透過vm.$props
獲取元件引數。
?自定義事件:在子元件中,申明並觸發自定義事件 $emit( event-name, […args] )
,透過vm.$listeners
獲取元件的所有監聽事件。(emit /iˈmɪt/ 發射,發出 )
- 響應事件(父元件):
v-on:event-name
,這裡的處理函式建議只繫結函式名,便於接收引數。 - 修飾符
.native
繫結元件根元素的原生事件,v-on:change.native="func"
- 繫結事件監聽器,在元件內特定元素上繫結所有監聽事件:
v-on="$listeners"
,vm.$listeners
可獲取元件上的所有監聽事件。
?<slot>
插槽</slot>
,在元件中設定一個插槽,來接收元件元素的標籤內的內容,透過vm.$slots
獲取元件的插槽。
<div id="app9">
<uinfo v-for="(u,i) in users" v-bind:u="u" type="鑽石會員" v-on:remove="users.splice(i,1)"></uinfo>
</div>
<script>
// 註冊一個全域性元件
Vue.component("uinfo", {
data: function () {
return { count: 0 }
}, //元件的data必須是函式返回物件
props: ['u', 'type'],
template: `<div class="uinfo">
<span>{{u.name}}</span> ?<input v-model.number="u.age" type="number"> <i>{{type}}</i>
<span>“{{u.summary}}”</span>
<button v-on:click="$emit('remove')">刪除</button>
</div>` //透過模板字元的方式實現多行文字,IE是不支援的。
});
let app9 = new Vue({
el: "#app9",
data: {
users: [
{ name: "張三", age: 14, summary: "垂死病中驚坐起,趕快下樓做核酸" },
{ name: "李四", age: 30, summary: "仰天大笑出門去,下樓排隊做核酸" }]
}
})
</script>
?注意元件命名:
小寫+連字元
(kebab-case),包括元件名稱(作為自定元素標籤)、元件引數prop(作為自定義元素的 attribute),以及自定義事件名。
- 小寫遵循W3C規範,連字元避免和HTML衝突(包括以後新的HTML標籤名)。
- HTML 中的 attribute 名是大小寫不敏感的,瀏覽器會把所有大寫字元解釋為小寫字元。
⚠️ 如果使用字元模板,就不歸瀏覽器管了,就沒有命名的限制問題了!
1.2、註冊元件
- 全域性註冊:
Vue.component( id, {選項})
,全域性元件,任何地方都可以使用。 - 區域性註冊:
components:{id:{選型}}
選項註冊元件,只能在當前註冊的Vue環境內使用,不可繼承。 - 擴充套件Vue類:
Vue.extend({選項})
,建立一個Vue的擴充套件類,透過new()
建立元件例項,也是一種元件複用方式。
<div id="app10">
<div>
<sexbox v-bind:data="uinfo" age="99" required>性別({{uinfo.sex}}):</sexbox>
<user-box :data="uinfo"></user-box>
</div>
</div>
<template id="userSexTemplate">
<div>
<slot></slot>
<label v-for="(value,name) in dic">
<!-- v-bind="$attrs" 用來繫結元件上設定的屬性:age="99" required -->
<input type="radio" v-model="data.sex" name="sex" :value="name" v-bind="$attrs">
{{value}}
</label>
</div>
</template>
<template id="userBoxTemplate">
<div>姓名:<input type="text" v-model="data.name">
<sexbox :data="data">性別:</sexbox>
</div>
</template>
<script>
// 性別選擇元件
let comSex = {
data: function () { return { dic: { male: '男', female: "女", other: '其他' } } },
props: ['data'],
template: '#userSexTemplate'
}
// 使用者資訊元件,引入了性別元件
let UserVue = Vue.extend({
props: ['data'],
components: { 'sexbox': comSex },
template: `#userBoxTemplate`,
})
//vue 應用
let app10 = new Vue({
el: "#app10",
data: { uinfo: { name: '核算', sex: 'male' } },
components: { 'user-box': UserVue, 'sexbox': comSex }
})
// app10 = new UserVue({el:'#app10'})
</script>
1.3、is 動態元件
透過is
特性設定(或v-bind:is
繫結)元件名稱,動態的申明一個元件。
元件使用申明方式:
<user-info></user-info>
:常規自定義元素方式申明。<component is="user-info"></component>
:component元件元素申明,透過is
設定元件名稱,:is
就可以動態繫結元件了。<tr is="user-info"></tr>
:其他HTML元素+is
申明元件,該元素會被元件內容替換掉。這主要是為了解決有些HTML元素中只能包含特定的子元素,如<ul>
、<table>
。
<keep-alive>
<component is="user-info" v-bind:u="users[0]"></component>
</keep-alive>
<table>
<tr is="user-info" v-bind:u="users[0]"></tr>
</table>
?keep-alive 快取元件:用<keep-alive>
包裹動態元件,配合元件特性:is
使用,用來快取失活的動態元件,避免元件失活後狀態丟失。
1.4、Props引數
透過props
定義引數(Array<string> | Object )
,引數都會作為元件元素的attribute
特性使用,透過vm.$props
獲取元件引數。
?Prop物件驗證:除了使用陣列設定多個引數,還可以用一個物件來申明多個引數及引數規則,用於引數合法性驗證。⚠️注意驗證的執行是在元件例項建立之前進行的,此時data
、computed
都還不可用。
- 每個引數可以指定多個驗證型別。
- 每個引數可以定義型別
type
、必填require
、預設值default
,以及驗證器函式validator
。
<div id="app11">
<sex-box v-bind:data="uinfo" type="vip" age="99" required class="form-item">性別:</sex-box>
</div>
<script>
function User(name = "", sex = "") {
this.name = name; this.sex = sex;
}
// 一個性別選擇元件
let sexBox = {
data: function () {
return {
dic: { male: '男', female: "女", other: '其他' },
utype: this.type, //使用prop引數為初始值
}
},
inheritAttrs: false, //元件根元素不繼承Attribute(不含style、class)
props: {
data: [Object, User], //User為自定義構造器
type: String,
age: {
type: [String, Number],
required: true,
default: 18,
validator: function (value) { return value > 0 && value < 100 } /* 自定義驗證器 */
}
},
// props: ['data', 'type', 'age'],
template: `<div><slot></slot>
<label v-for="(value,name) in dic">
<input type="radio" v-model="data.sex" name="sex" :value="name" v-bind="$attrs">
{{value}}</label> //{{type}}</div>`,
}
let app11 = new Vue({
el: "#app11",
data: {uinfo: new User("張三", 'male')},
components: {'sex-box': sexBox,}
})
</script>
?引數繫結:引數作為自定義元件元素的attribute
使用,推薦v-bind:prop
繫結賦值,除是非字串值,繫結是支援表示式的。
- 如果要傳入一個物件的所有 property,直接
v-bind="obj"
即可。
?引數的單向傳遞:引數的值是單向的向下傳遞的,子元件不可更改。
- 父元件的資料變更會觸發子元件所有引數
prop
的重新整理。 - 如果傳入的是一個引用型別(陣列、物件),這個是共享的,會影響父元件狀態。
?非Props引數的Attribute:對於非Prop引數的Attribute,預設會都應用到元件根元素上,如上面示例中的age="99" required class="form-item"
- 替換:除了
class
、style
會合並,其他如果衝突會替換掉元件內部的Attribute
。 inheritAttrs
不繼承:可在元件選項裡設定inheritAttrs:false
取消根元素的Attribute
繼承。- 主動繼承:元件內部其他元素可以用
v-bind="$attrs"
,來主動繼承非Prop的Attribute特性(不含class、style)。
1.5、#slot插槽
插槽可以用來插入任何內容,包括文字、HTML、其他元件。結合具名插槽(name)、prop繫結,可以實現更為開放、靈活的元件。
申明插槽 :在元件內部透過<slot></slot>
申明一個插槽,整個<slot></slot>
會被替換為元件元素標籤的內容(InnerHTML)。- 後備內容/預設值:
<slot>預設顯示內容</slot>
,在沒有提供內容時使用後備內容。
<div id="app13">
<com-search-box>
<p>{{search.title[0]}}</p>
<template v-slot:default> <!--效果同上-->
<p>{{search.title[0]}}</p>
</template>
</com-search-box>
</div>
<script>
let comSearchBox = {
template: `<div><slot>搜尋</slot> <input><button><b>?</b><span>搜尋</span></button></div>`,
}
let app13 = new Vue({
el: "#app13",
data: { search: { title: ['百度搜尋', '學術搜尋', '圖片搜尋'] } },
components: { 'com-search-box': comSearchBox }
})
</script>
?具名插槽,有名字的VIP插槽,name
特性取名,用於需要多個插槽的場景。沒命名的插槽<slot>
預設名稱為"default
"--預設插槽。
- 必須透過一個模板
<template>
來使用,用slot引數v-slot:name
=#name
指定插槽的名字。無<template>
、不指定名字的內容用於預設插槽。 - 縮寫
#
=v-slot:
,只能用於帶引數的v-slot
:v-slot:default
。 - 插槽名可以用動態引數,
[data-name]
:<template v-slot:['default']>
?作用域插槽:插槽內容訪問子元件內部資料
父級不能訪問子元件內部的資料,為了可以讓插槽內容可以訪問到子元件內部的資料,於是有了作用域插槽,主要就是2個步驟。
❶ 元件內部把資料繫結在插槽<slot>
的特性Attribute上 <slot v-bind:data="btnKey">
,稱為插槽的Prop,在父級作用域可以訪問。
❷ 在元件<template>
上引用插槽prop :<template v-slot="soldData">
,可取一個新名字。當然這裡也可以用當前作用域的繫結資料。
<div id="app13">
<com-search-box>
<template #header v-slot:header> <p>{{search.title[1]}}</p> </template>
<template v-slot="soldData">
<b>?</b><span>{{soldData.data[1]}}</span>
</template>
</com-search-box>
</div>
<script>
let comSearchBox = {
data: function () { return { 'btnKey': ['搜尋', 'search'] } },
template: `<div>
<slot name="header"></slot> <input>
<button> <slot v-bind:data="btnKey">{{btnKey[0]}}</slot> </button>
</div>`,
}
let app13 = new Vue({
el: "#app13",
data: { search: { title: ['百度搜尋', '學術搜尋', '圖片搜尋'] } },
components: { 'com-search-box': comSearchBox }
})
</script>
?todo-list(代辦列表)的示例:馬上掘金 | codepen
1.6、元件樹關係
屬性 | 描述 |
---|---|
$refs | 透過過 ref attribute 註冊的所有 DOM 元素和元件例項的一個引用物件。當 ref 和 v-for 一起使用的時候,其引用為一個陣列 |
$parent | 父例項,根例項的 $parent 為null |
$root | 組建樹的根例項,根例項的 $root 為自己 |
$children | 子元件例項陣列Array<Vue instance> |
當我們構建一顆樹形結構(如檔案目錄樹)的元件時,會遞迴迴圈引用,造成元件的迴圈依賴。
- 全域性註冊元件。
- 在
beforeCreate
手動注入子元件。 - 設定webpack非同步
import
引入元件。
1.7、總結:元件通訊
透過元件關係樹也是可以的,不過不推薦,耦合性太高。
02、可複用性 & 組合
2.1、[mixins]混入(CtrlV)元件程式碼
混入(mixins)可以靈活複用元件中的程式碼,可惜只能用於組建內,也僅支援Vue的選項。步驟:
- 定義混入物件:
let cmixin={}
;- 元件中使用:
mixins:[cmixin]
,混入物件中的內容。
?程式碼混入(合併)規則:
- 選項合併:如果混入時存在程式碼衝突,則會先遞迴合併,然後本地優先的策略。
- 混入的鉤子,合併為一個陣列,依次呼叫。
- 全域性混入
Vue.mixin( mixin )
,應用於後面所有的Vue物件。 - 混入優先順序:原生程式碼優先 > 區域性混入 > 全域性混入。
- 自定義混入合併策略:
Vue.config.optionMergeStrategies
let cmixin = {
data: { message: "hello world!" },
methods: { say: function () { console.log(this.message) } }
}
let app1 = new Vue({
el: "#app1",
data:{name:'sam'},
mixins: [cmixin],
//...cmixin, //不支援合併,會覆蓋已有屬性值
created: function () { this.say() }
})
?用ES6的展開運算子
...
,也能達到類似的複用目的,不過不支援合併策略。
2.2、v-自定義指令
指令是對Dom元素的擴充套件,具有一定的行為特徵(鉤子函式)。註冊的自定義指令,需要用指令的鉤子函式來觸發指令行為,一個指令支援多個鉤子函式,不同鉤子函式(觸發點)可設定不同的行為。如果不指定鉤子函式,就是全都要,都會觸發呼叫。
?註冊方式:全域性、區域性
- 全域性註冊:
Vue.directive( id, [definition] )
,注意順序,先定義後使用。 - 區域性註冊-選項:
directives:{ id: { } }
。
?指令命名:參考HTML命名規範,小寫+連字元,正式指令名稱會加上v-
。
?指令函式引數:(el, binding, vnode, oldVnode)
- el:繫結的Dom元素。
- binding:指令物件,包含指令名稱
name
、引數arg
、修飾符modifiers
、屬性值value
、上一次的屬性值oldValue
、指令表示式expression
。這裡的值可用來做變化判斷,以最佳化指令效能。 - vnode:虛擬Dom元素
- oldVnode:上一個虛擬Dom元素
?使用:<input v-id:arg.ky='value'>
構子函式 | 描述 |
---|---|
bind | 只呼叫一次,指令第一次繫結到元素時呼叫。 |
inserted | 被繫結元素插入父節點時呼叫 |
update | 所在元件的 VNode 更新時呼叫 |
componentUpdated | 所在元件的 VNode 更新後呼叫 |
unbind | 只呼叫一次,指令與元素解綁時呼叫 |
?自定義指令-驗證器表單輸入:值為需要驗證的資料,其他驗證引數check-reg、check-error用自定屬性申明,指令用在表單元素後面的一個用於提示錯誤的元素上。
<div id="app1">
<p>
<label>使用者名稱:<input type="text" v-model="userName" maxlength="40" size="40" placeholder="請輸入使用者名稱"></label>
<span v-check.required="userName" check-reg="^\w{4,6}$" check-error="使用者名稱必須是4-6位的字母數字"></span>
</p>
<p>
<label>使用者名稱:<input type="text" v-model="email" maxlength="40" size="40" placeholder="請輸入郵箱"></label>
<span v-check="email" check-reg="^\w+@\w+\.\w+$" check-error="郵箱格式不合法"></span>
</p>
</div>
<script>
// 驗證器(必填修飾符),正則,錯誤資訊,check,check-reg,check-error
let mixinDirectivesValidator = {
directives: {
check: { //check指令:v-check
//繫結指令時觸發
bind(el, bind) {
//先讀取check的配置屬性資訊,暫存備用
el.text = el.getAttribute('check-error');
el.reg = new RegExp(el.getAttribute('check-reg'));
el.style.color = 'red';
el.style.fontSize = '0.8em';
if (bind.modifiers.required) el.innerText = ' * 必填';
},
//更新檢視時觸發
update: function (el, bind) {
if (bind.value === bind.oldValue) return;
el.innerText = '';
if (bind.modifiers.required) el.innerText = ' * 必填';
if (bind.value && !el.reg.test(bind.value)) el.innerText = el.text;
}
}
}
}
let app1 = new Vue({
el: "#app1",
data: { userName: '', email: '' },
mixins: [mixinDirectivesValidator]
})
</script>
2.3、| 插值過濾器
過濾器就是一個函式,在{{文字插值}}
、v-bind="表示式"
繫結表示式後面使用,對繫結的值進行過濾(再加工)處理。支援鏈式的過濾使用,支援引數,常用於格式化顯示。在V2、V3中,過濾器被逐漸弱化,更推薦用表示式或計算屬性。
- 全域性過濾器:
Vue.filter( id, func(value,...arg )
,第一個引數為管道符號前面的繫結值。 - 區域性過濾器-
filters{id:func(value,...arg)}
選項,(❗多了個s
) - 使用:用管道符號連線,
{{文字插值 | filter1 | filter2(arg)}}
<style>
#app2 *{
font-family:'Courier New', Courier, monospace;
}
</style>
<div id="app2">
<ul>
<li v-for="a in arr">{{a | fixedLength(3,' ')}}: {{a|fixedLength(4)| money|money|money}}</li>
</ul>
</div>
<script>
//全域性過濾器,數字固定長度
Vue.filter("fixedLength", function (value, length,char='0') {
return (Array(length).join(char) + value).slice(-length);
})
let app2 = new Vue({
el: "#app2",
data: { arr: [1, 2, 3, 44, 55, '5K'] },
//具備過濾器定義
filters: {
money: function (v) { return '¥' + v; }
}
})
</script>
2.4、Vue外掛開發
外掛就是一個函式或者包含install()
方法的物件,目的是實現封裝複用。透過Vue.use()
來安裝,給Vue提供全域性的擴充套件能力,如新增全域性的指令、方法、元件,混入選項等,很多Vue的UI元件都是這麼處理的。
install(Vue,...args)
:外掛應該提供的安裝函式,第一個引數就是Vue構造器,就可以呼叫Vue的靜態方法做一些處理。後面的引數就是外掛自己用的引數了。Vue.use(外掛, ...args)
:註冊外掛,第一個引數為外掛,後面就是外掛需要的引數選項了。
// 封裝外掛
export let SuperPlugin = {
install(Vue, ...args) {
console.log('安裝外掛SuperPlugin', args);
//安裝一個UI元件
Vue.component('super-btn', { template: '<button>Super<slot></slot></button>' });
//新增全域性靜態方法
Vue.$get = function (url) { /*ajax get*/ };
Vue.$post = function (url, data) { /*ajax post*/ };
//新增例項方法
Vue.prototype.$myMethod = function () { };
//混入全域性的選項
Vue.mixin({ created: function () {console.log('建立元件') } });
}
}
// 註冊外掛
import { SuperPlugin } from './super-plugin.js'
Vue.use(SuperPlugin, 1, 2, 3);
©️版權申明:版權所有@安木夕,本文內容僅供學習,歡迎指正、交流,轉載請註明出處!原文編輯地址-語雀