1. 程式人生 > 程式設計 >詳解vue v-model

詳解vue v-model

1. v-model原理

vue中v-model是一個語法糖,所謂的語法糖就是對其他基礎功能的二次封裝而產生的功能。簡單點說,v-model本身就是父元件對子元件狀態以及狀態改變事件的封裝。其實現原理上分為兩個部分:

通過props設定子元件的狀態
通過監聽子元件發出的事件改變父元件的狀態,從而影響子元件的props值
通過以上兩個部分,實現了父元件的狀態和子元件狀態進行了繫結的效果。

1.1 demo

v-model使用示例

<!DOCTYPE html>
<html>
 <head>
 <meta charset="utf-8" />
 <title>v-model示例</title>
 <script type="text/javascript" src="vue.js"></script>
 </head>

 <body>
 <div id="app">
 <div>這裡是父元件的狀態:</div>
 <div style="margin-bottom: 15px;">{{content}}</div>
 <Child v-model="content"></Child>
 </div>

 <template id="input">
 <div>
 <div>這裡是子元件的輸入區域:</div>
 <input :value="value" @input="contentChange" />
 </div>
 </template>

 <script type="text/javascript">
 var Child = {
 template: "#input",props: {
 value: {
 type: String,required: true
 }
 },methods: {
 contentChange(value){
 this.$emit("input",value.target.value);
 }
 }
 };

 var vueInstance = new Vue({
 el: "#app",components: {Child},data: {
 content: ""
 }
 })
 </script>
 </body>
</html>

在瀏覽器中開啟上述html頁面,可以看到實時效果:在子元件中的input框中輸入內容可以在父元件區域實時顯示,達到了子元件中狀態和父元件狀態實時繫結的效果。

2. 修改v-model預設監聽的事件和設定prop的名稱

v-model指令預設是在子元件上設定的prop名稱是value,預設監聽子元件上的input事件,在上面的demo上,如果我們修改子元件contentChange函式中發出的事件名稱,在父元件中就無法實時獲取到子元件的輸入。

Vue中提供了通過在子元件上定義model屬性來修改這兩個引數名稱的功能,不過該功能需要在版本2.2以上才能使用,如下demo所示:

2.1 demo

<!DOCTYPE html>
<html>
 <head>
 <meta charset="utf-8" />
 <title>v-model示例</title>
 <script type="text/javascript" src="vue.js"></script>
 </head>

 <body>
 <div id="app">
 <div>這裡是父元件的狀態:</div>
 <div style="margin-bottom: 15px;">{{content}}</div>
 <Child v-model="content"></Child>
 </div>

 <template id="input">
 <div>
 <div>這裡是子元件的輸入區域:</div>
 <input :value="content" @input="contentChange" />
 </div>
 </template>

 <script type="text/javascript">
 var Child = {
 template: "#input",model: {
 prop: "content",event: "contentChanged"
 },props: {
 content: {
 type: String,methods: {
 contentChange(value){
 this.$emit("contentChanged",data: {
 content: ""
 }
 })
 </script>
 </body>
</html>

3. Vue中對v-model指令處理分析

基於Vue2.0版本,分析我們在標籤上寫上v-model屬性到vue元件實現響應的流程。

3.1 解析部分

3.1.1 在將HTML解析稱AST時,會解析HTML中標籤的屬性

function processAttrs(el){
 ...
 name = name.replace(dirRE,'')
 // parse arg
 const argMatch = name.match(argRE)
 if (argMatch && (arg = argMatch[1])) {
 name = name.slice(0,-(arg.length + 1))
 }
 addDirective(el,name,value,arg,modifiers)
 ...
}

提取指令的名稱,v-model的指令名稱name為model,然後新增到例項的指令中

3.1.2 將指令相關內容新增到例項指令中

export function addDirective (
 el: ASTElement,name: string,value: string,arg: ?string,modifiers: ?{ [key: string]: true }
) {
 (el.directives || (el.directives = [])).push({ name,modifiers })
}

在例項的指令屬性中新增相應的指令,這樣就實現了從html上的屬性到Vue例項上指令格式的轉換

3.2 指令設定部分

在將html解析稱AST之後,例項對應的directives屬性上就有了我們設定的v-model相關的值,包括引數值value,name是model

3.2.1 呼叫指令的建構函式

function genDirectives (el: ASTElement): string | void {
 const dirs = el.directives
 if (!dirs) return
 let res = 'directives:['
 let hasRuntime = false
 let i,l,dir,needRuntime
 for (i = 0,l = dirs.length; i < l; i++) {
 dir = dirs[i]
 needRuntime = true
 const gen = platformDirectives[dir.name] || baseDirectives[dir.name]
 if (gen) {
 // compile-time directive that manipulates AST.
 // returns true if it also needs a runtime counterpart.
 needRuntime = !!gen(el,warn)
 }
 ...
}

在v-model指令的建構函式中會根據tag的種類進行不同的建立函式進行建立,如果我們自定義指令需要在子元件上新增屬性,也需要在這個函式裡面進行操作

3.2.2 普通tag下的v-model指令構造過程

function genDefaultModel 
 el: ASTElement,modifiers: ?Object
): ?boolean {
 ...
 addProp(el,'value',isNative ? `_s(${value})` : `(${value})`)
 addHandler(el,event,code,null,true)
 ...
}
  • addProp在el上設定一個名稱為value的prop,同時設定其值
  • addHandler在el上設定事件處理函式

3.3 指令響應變化部分

3.3.1 createPatchFunction統一處理指令的鉤子函式
createPatchFunction函式返回一個patch函式,在patch處理過程中,會呼叫指令的鉤子函式,包括:

  • bind
  • inserted
  • update
  • componentUpdated
  • unbind

4. 總結

4.1 編譯過程

  1. 從html上解析所設定的指令
  2. 通過gen*函式將指令設定到AST上
  3. 呼叫指令的建構函式,設定指令需要在編譯時期處理的事情

4.2 初始化過程

通過在patch函式中,呼叫統一的鉤子函式,觸發指令的鉤子函式,實現相應的功能

以上就是詳解vue v-model的詳細內容,更多關於vue v-model的資料請關注我們其它相關文章!