vue2源碼解析---v-model雙向數據綁定

什么是v-model

v-model 是 Vue 中的一個指令,用于實現表單元素與 Vue 實例中數據的雙向綁定。這意味著當表單元素的值發生變化時,Vue 實例中的數據也會隨之更新

工作原理

生成ast樹

本質上是語法糖 結合了v-bind和v-on兩個指令
示例代碼

new Vue({el: '#app',data () {return {msg: 'Hello, msg'}},template: `<input v-model="msg" />`
})

源碼解析

processElement方法中調用processAttrs來處理標簽上面解析的各種屬性

export function processElement (element: ASTElement,options: CompilerOptions
) {// ...省略代碼processAttrs(element)return element
}

進入processAttrs這個方法中 用于構建抽象的語法樹

export const dirRE = process.env.VBIND_PROP_SHORTHAND? /^v-|^@|^:|^\.|^#/: /^v-|^@|^:|^#/
const argRE = /:(.*)$/
function processAttrs (el) {const list = el.attrsListlet i, l, name, rawName, value, modifiers, syncGen, isDynamicfor (i = 0, l = list.length; i < l; i++) {name = rawName = list[i].namevalue = list[i].valueif (dirRE.test(name)) {el.hasBindings = true// modifiers省略代碼if (bindRE.test(name)) {// v-bind省略代碼} else if (onRE.test(name)) {// v-on省略代碼} else {// normal directivesname = name.replace(dirRE, '')// parse arg//先使用dirRE正則表達式把v-model字符串中的v-前綴去掉,//此時name的值就變成了model//它又使用了argRE正則表達式來匹配指令參數//示例 // const template = `<input v-model:value="msg" />`// 匹配到的指令參數//const arg = 'value'const argMatch = name.match(argRE)let arg = argMatch && argMatch[1]isDynamic = falseif (arg) {name = name.slice(0, -(arg.length + 1))if (dynamicArgRE.test(arg)) {arg = arg.slice(1, -1)isDynamic = true}}addDirective(el, name, rawName, value, arg, isDynamic, modifiers, list[i])if (process.env.NODE_ENV !== 'production' && name === 'model') {checkForAliasModel(el, value)}}} else {// ...省略代碼}}
}

處理完畢后進入調用addDirective方法,給ast對象添加directives屬性

export function addDirective (el: ASTElement,name: string,rawName: string,value: string,arg: ?string,isDynamicArg: boolean,modifiers: ?ASTModifiers,range?: Range
) {(el.directives || (el.directives = [])).push(rangeSetItem({name,rawName,value,arg,isDynamicArg,modifiers}, range))el.plain = false
}

生成的ast樹如下所示

const ast = {type: 1,tag: 'input',attrsList: [{ name: 'v-model', value: 'msg' }],attrsMap: {'v-model': 'msg'},directives: [{ name: 'model', rawName: 'v-model', value: 'msg' }]
}

codegen階段

codegen代碼生成階段,會在genData方法中調用genDirectives來處理指令

export function genData (el: ASTElement, state: CodegenState): string {let data = '{'const dirs = genDirectives(el, state)if (dirs) data += dirs + ','// ...省略代碼return data
}function genDirectives (el: ASTElement, state: CodegenState): string | void {const dirs = el.directivesif (!dirs) returnlet res = 'directives:['let hasRuntime = falselet i, l, dir, needRuntimefor (i = 0, l = dirs.length; i < l; i++) {dir = dirs[i]needRuntime = trueconst gen: DirectiveFunction = state.directives[dir.name]if (gen) {// compile-time directive that manipulates AST.// returns true if it also needs a runtime counterpart.needRuntime = !!gen(el, dir, state.warn)}if (needRuntime) {hasRuntime = trueres += `{name:"${dir.name}",rawName:"${dir.rawName}"${dir.value ? `,value:(${dir.value}),expression:${JSON.stringify(dir.value)}` : ''}${dir.arg ? `,arg:${dir.isDynamicArg ? dir.arg : `"${dir.arg}"`}` : ''}${dir.modifiers ? `,modifiers:${JSON.stringify(dir.modifiers)}` : ''}},`}}if (hasRuntime) {return res.slice(0, -1) + ']'}
}

與其他指令不同 state.directives,這個屬性是在CodegenState類的構造函數中被處理的

export class CodegenState {options: CompilerOptions;warn: Function;transforms: Array<TransformFunction>;dataGenFns: Array<DataGenFunction>;directives: { [key: string]: DirectiveFunction };maybeComponent: (el: ASTElement) => boolean;onceId: number;staticRenderFns: Array<string>;pre: boolean;constructor (options: CompilerOptions) {this.options = options// ...省略代碼this.directives = extend(extend({}, baseDirectives), options.directives)// ...省略代碼}
}

directives中 v-model中

export default function model (el: ASTElement,dir: ASTDirective,_warn: Function
): ?boolean {warn = _warnconst value = dir.valueconst modifiers = dir.modifiersconst tag = el.tagconst type = el.attrsMap.typeif (process.env.NODE_ENV !== 'production') {// inputs with type="file" are read only and setting the input's// value will throw an error.if (tag === 'input' && type === 'file') {warn(`<${el.tag} v-model="${value}" type="file">:\n` +`File inputs are read only. Use a v-on:change listener instead.`,el.rawAttrsMap['v-model'])}}if (el.component) {genComponentModel(el, value, modifiers)// component v-model doesn't need extra runtimereturn false} else if (tag === 'select') {genSelect(el, value, modifiers)} else if (tag === 'input' && type === 'checkbox') {genCheckboxModel(el, value, modifiers)} else if (tag === 'input' && type === 'radio') {genRadioModel(el, value, modifiers)} else if (tag === 'input' || tag === 'textarea') {genDefaultModel(el, value, modifiers)} else if (!config.isReservedTag(tag)) {genComponentModel(el, value, modifiers)// component v-model doesn't need extra runtimereturn false} else if (process.env.NODE_ENV !== 'production') {warn(`<${el.tag} v-model="${value}">: ` +`v-model is not supported on this element type. ` +'If you are working with contenteditable, it\'s recommended to ' +'wrap a library dedicated for that purpose inside a custom component.',el.rawAttrsMap['v-model'])}// ensure runtime directive metadatareturn true
}

最后代碼生成階段

function genDefaultModel (el: ASTElement,value: string,modifiers: ?ASTModifiers
): ?boolean {// ...省略代碼addProp(el, 'value', `(${value})`)addHandler(el, event, code, null, true)// ...省略代碼
}

● addProp:調用addProp是為了給ast添加一個value的props屬性。
● addHandler:調用addHandler是為了給ast添加一個事件監聽,至于到底監聽什么事件取決于v-model作用于什么標簽。
所以處理之后多了props和events屬性

export function genData (el: ASTElement, state: CodegenState): string {let data = '{'// directiveconst dirs = genDirectives(el, state)if (dirs) data += dirs + ','// ...省略代碼// DOM propsif (el.props) {data += `domProps:${genProps(el.props)},`}// event handlersif (el.events) {data += `${genHandlers(el.events, false)},`}// ...省略代碼return data
}

總結

其實學源碼學到后續感覺有點懵 然后就是復習js 感覺紅寶書講的挺多的 馬上也要期末考試了 希望期末平穩度過。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/445109.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/445109.shtml
英文地址,請注明出處:http://en.pswp.cn/news/445109.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

php收集的精典代碼

1. οncοntextmenu"window.event.return&#xff06;#118aluefalse" 將徹底屏蔽鼠標右鍵 <table border οncοntextmenureturn(false)><td>no</table> 可用于Table 2. <body onselectstart"return false"> 取消選取、防止復制…

python外卷(7)--glob

glob模塊1.glob.glob()2.對比os.listdir()glob是python自帶的一個操作文件的模塊&#xff0c;可用于查找 指定路徑 中 匹配的 文件。1.glob.glob() 下面是一個測試文件路徑&#xff1a; (base) pppp-System-Product-Name:~/Desktop/test_glob$ tree . ├── a │ ├── 1…

Sublime Text 2配置強大的IDE開發環境,運行java

Sublime Text 2是我無意中發現的、據說十分強大的、便捷的編輯器&#xff0c;許多程序員都投入到Sublime Text 2的懷抱中。 1 配置java開發環境的方法如下&#xff1a; 在jdk安裝目錄下的bin文件夾下新建一個bat格式的文件&#xff0c;文件命為javacexec.bat。 如果是在Wind…

thinkphp的快捷方法實例化對象

D、F、S、C、L、A、I 他們都在functions.php這個文件家 下面我分別說明一下他們的功能 D&#xff08;&#xff09; 加載Model類 M&#xff08;&#xff09; 加載Model類 A&#xff08;&#xff09; 加載Action類 L&#xff08;&#xff09; 獲取語言定義 C&#xff08;&#xf…

Python外卷(8)--pdist, squareform

pdist, squareform1.pdist, squareform使用例子2.通過矩陣的四則運算實現上述pdist, squareformscipy.spatial.distance 距離計算庫中有兩個函數&#xff1a;pdist, squareform&#xff0c;用于計算樣本對之間的歐式距離&#xff0c;并且將樣本間距離用方陣表示出來。&#xff…

模擬進程調度

功能 data.h #ifndef _Data_h_ #define _Data_h_#include <stdio.h> #include <stdlib.h> #include <string.h>#define ElemType PCB #define Status int #define OK 1 #define ERROR 0 #define TimeSlice 1 #define Infinity 10 //INT_MAX#define NAME_M…

gdb調試多進程和多線程命令

1. 默認設置下&#xff0c;在調試多進程程序時GDB只會調試主進程。但是GDB&#xff08;>V7.0&#xff09;支持多進程的 分別以及同時 調試&#xff0c;換句話說&#xff0c;GDB可以同時調試多個程序。只需要設置follow-fork-mode(默認值&#xff1a;parent)和detach-on-fork…

python外卷(10)--取整

"取整"那些事1.python 內置函數1.1int()--向下取整1.2round()--四舍五入2.math模塊取整函數2.1 math.floor()--向下取整2.2 math.ceil()--向上取整2.3 math.modf()--分別取小數部分和整數部分3.numpy模塊取整函數3.1 numpy.floor()--向下取整3.2 numpy.ceil()--向上取…

模擬銀行家算法

介紹 data.h #ifndef _Data_h_ #define _Data_h_#include <stdio.h> #include <stdlib.h> #include <string.h>#define ElemType PCB #define Status int #define true 1 #define false 0 #define OK 1 #define ERROR 0 #define RESOURCE_NUM …

Lua 與 C混合編程 .

本文通過程序實例說明C調用lua腳本和lua調用C的方法: 先建立一個 test.c文件: #include <stdio.h> #include <stdlib.h> #include "lua.h" #include "lualib.h" #include "lauxlib.h" #pragma comment(lib, "lua5.1.lib&qu…

模擬固定分區分配

介紹 data.h #ifndef _Data_h_ #define _Data_h_#include <stdio.h> #include <stdlib.h> #include <string.h> #define LIST_INIT_SIZE 10 #define LISTINCREMENT 2 #define true 1 #define false 0 #define PCBType PCB #define Status int…

Linux下的lua和boost c++的搭建和安裝

先下載lua &#xff0c;boost c http://www.lua.org/versions.html#5.2 http://www.boost.org/ http://sourceforge.net/projects/luabind/ 1. 安裝lua [rootlocalhost ~]#tar zxvf lua-5.1.2.tar.gz -C /usr/local [rootlocalhost ~]# cd /usr/local/ [rootlocalhost lo…

模擬基本分頁存儲

介紹 data.h #ifndef _Data_h_ #define _Data_h_#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h>#define LIST_INIT_SIZE 10 #define LISTINCREMENT 2 #define true 1 #define false 0 #define PCBType PC…

常用正則表達式和shell命令列表

取當前目錄下普通文件的后綴名列表&#xff1a; ls -l | awk /^-/{print $NF} |awk -F. {print $NF}|awk !/^$/ 匹配0和正整數的正則表達式&#xff08;除0以外&#xff0c;其它數字不能以0開頭&#xff09;&#xff1a; (^0$)|(^[0-9]\d*$) 匹配中文字符的正則表達式&#xff…

無限踩坑系列(7)-Latex使用Tips

Latex常用命令1.latex注釋2.圖片左邊對齊3.字母頭上加聲調4.腳注5.公式中加空格6.字體加粗體7.公式換行8.\textsuperscript{*}9.\begin{itemize}10.\operatorname{trace}11.\noindent12.\textcircled{}圓圈數字一些TIPs1.latex注釋 單行使用百分號%注釋 多行使用如下命令,在編…

在CentOS6.2下安裝DNS服務軟件Bind并快速配置簡單實例

[實踐Ok]在CentOS6.2下安裝DNS并快速配置實例&#xff0c;共八步&#xff0c;心路歷程如下&#xff1a;背景介紹&#xff1a;在日常的開發中&#xff0c;往往會在測試機和外網的Http的Url實際接口是不一樣的&#xff0c;在測試機一個Url地址&#xff0c;在外網中又是一個地址。…

模擬動態分區分配

介紹 list.h #ifndef _List_h_ #define _List_h_#include "Data.h"//******* 鏈表 *******// Status InitLinkList(LinkList *L); void PCBAssign(PCBType *e1, PCBType e2); Status GetElemt_L(LinkList L,int i,PCBType *e); Status ListIn…

python模塊(4)-Collections

collections1.collection.counter(list)2.collections.defaultdict()3.collection.dequecollections是Python內建的一個集合模塊&#xff0c;提供了許多有用的集合類。collections在python官方文檔中的解釋是High-performance container datatypes1.collection.counter(list) …

js知識點匯總

1.本門課的作用&#xff08;JavaScript的作用&#xff09;所有基于Web的程序開發基礎 2.一種計算機客戶端腳本語言&#xff0c;主要在Web瀏覽器解釋執行。 3.瀏覽器中Javascript&#xff0c;用于與用戶交互&#xff0c;以及實現頁面中各種動態特效 4.在HTML文件中&#xff0…

redis——內存概述

Redis通過自己的方法管理內存,&#xff0c;主要方法有zmalloc(),zrealloc()&#xff0c; zcalloc()和zfree(), 分別對應C中的malloc(), realloc()、 calloc()和free()。相關代碼在zmalloc.h和zmalloc.c中。 Redis自己管理內存的好處主要有兩個&#xff1a;可以利用內存池等手段…