亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

vue動(dòng)態(tài)綁定v-model屬性名方式

 更新時(shí)間:2022年08月16日 09:58:03   作者:crayon-shin-chan  
這篇文章主要介紹了vue動(dòng)態(tài)綁定v-model屬性名方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

vue動(dòng)態(tài)綁定v-model屬性名

1.目標(biāo)

首先配置列,根據(jù)配置渲染表單,每個(gè)表單項(xiàng)綁定配置中的屬性

2.方案

<template v-for="(item) in showQueryColumns" >
    <el-col :key="item.prop" :xs = "24" :sm = "12" :md="12" :lg = "12" :xl = "6">
        <!--字符串類型-->
        <el-form-item v-if="item.type==='string'" :label="$t(item.i18n)" :prop="item.prop">
             <el-input v-model="form[item.prop]" clearable></el-input>
        </el-form-item>
    </el-col>
 </template>
  • v-model綁定的必須是屬性,可以使用方括號(hào),取指定對(duì)象的屬性

親測(cè)有效

vue雙向綁定原理(v-model)

之前有整理過Vue響應(yīng)式原理,響應(yīng)式主要的效果是數(shù)據(jù)改變了就會(huì)引起頁(yè)面修改。關(guān)于v-model我們也不陌生,vue的雙向綁定指令,頁(yè)面修改會(huì)引起數(shù)據(jù)修改,數(shù)據(jù)修改頁(yè)面也會(huì)跟著改變。我們直到數(shù)據(jù)->頁(yè)面是由vue的響應(yīng)式原理實(shí)現(xiàn)的,那么該怎么做到頁(yè)面->數(shù)據(jù)的修改呢?

表單綁定

v-model一般我們是在表單元素上進(jìn)行使用,因?yàn)橐晥D能影響數(shù)據(jù),本質(zhì)上是這個(gè)視圖需要可交互,因此表單是實(shí)現(xiàn)這一交互的前提。表單的使用是以<input>、<textarea>、<select>為核心。具體的使用細(xì)節(jié)這里就不一一細(xì)說了。

這里我們從模板解析開始分析,vue對(duì)v-model做了什么操作。

這里我們來看一些綁定在input上的v-model都經(jīng)歷了什么。

// 普通輸入框
<input type="text" v-model="value1">

AST樹的解析

模版的編譯階段,會(huì)調(diào)用var ast = parse(template.trim(), options)生成AST樹,parse函數(shù)的起他細(xì)節(jié)這里不展開分析,我們只說模板屬性上的解析processAttrs函數(shù)。

vue模板屬性有兩部分組成,一部分是指令,另一部分是普通的html標(biāo)簽屬性。對(duì)于指令,出去v-on和v-bind,其他普通指令會(huì)執(zhí)行addDirective過程。

// 處理模板屬性
function processAttrs(el) {
? var list = el.attrsList;
? var i, l, name, rawName, value, modifiers, syncGen, isDynamic;
? for (i = 0, l = list.length; i < l; i++) {
? ? name = rawName = list[i].name; // v-on:click
? ? value = list[i].value; // doThis
? ? if (dirRE.test(name)) { // 1.針對(duì)指令的屬性處理
? ? ? ···
? ? ? if (bindRE.test(name)) { // v-bind分支
? ? ? ? ···
? ? ? } else if(onRE.test(name)) { // v-on分支
? ? ? ? ···
? ? ? } else { // 除了v-bind,v-on之外的普通指令
? ? ? ? ···
? ? ? ? // 普通指令會(huì)在AST樹上添加directives屬性
? ? ? ? addDirective(el, name, rawName, value, arg, isDynamic, modifiers, list[i]);
? ? ? ? if (name === 'model') {
? ? ? ? ? checkForAliasModel(el, value);
? ? ? ? }
? ? ? }
? ? } else {
? ? ? // 2. 普通html標(biāo)簽屬性
? ? }
? }
}

在AST產(chǎn)生階段對(duì)事件指令v-on的處理是為AST樹添加events屬性。類似的,普通指令會(huì)在AST樹上添加directives屬性,我們可以看一下addDirective函數(shù)

// 添加directives屬性
function addDirective (el,name,rawName,value,arg,isDynamicArg,modifiers,range) {
? ? (el.directives || (el.directives = [])).push(rangeSetItem({
? ? ? name: name,
? ? ? rawName: rawName,
? ? ? value: value,
? ? ? arg: arg,
? ? ? isDynamicArg: isDynamicArg,
? ? ? modifiers: modifiers?? ?// 模板中添加的修飾符,如:.lazy、.number、.trim
? ? }, range));
? ? el.plain = false;
? }

最終AST樹上會(huì)多處一個(gè)屬性對(duì)象

// AST
{
? directives: {
? ? {
? ? ? rawName: 'v-model',
? ? ? value: 'value',
? ? ? name: 'v-model',
? ? ? modifiers: undefined
? ? }
? }
}

render函數(shù)生成

render函數(shù)生成階段,generate邏輯,其中g(shù)enData會(huì)對(duì)模版的各個(gè)屬性進(jìn)行處理,最終返回拼接好的字符串模板,而對(duì)指令的處理會(huì)進(jìn)入genDirectives函數(shù)

在genDirectives函數(shù)中,會(huì)拿到之前AST樹中的directives對(duì)象,并遍歷解析指令對(duì)象,最終以'directives:['包裹的字符串返回。

// directives render字符串的生成
? function genDirectives (el, state) {
? ? // 拿到指令對(duì)象
? ? var dirs = el.directives;
? ? if (!dirs) { return }
? ? // 字符串拼接
? ? var res = 'directives:[';
? ? var hasRuntime = false;
? ? var i, l, dir, needRuntime;
? ? for (i = 0, l = dirs.length; i < l; i++) {
? ? ? dir = dirs[i];
? ? ? needRuntime = true;
? ? ? // 對(duì)指令ast樹的重新處理
? ? ? var gen = 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 = true;
? ? ? ? res += "{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) + ']'
? ? }
? }

這里有一句關(guān)鍵代碼var gen = state.directives[dir.name],這里的的dir.name為model,這個(gè)model回去執(zhí)行對(duì)應(yīng)的model函數(shù)。我們來看一下model函數(shù)的邏輯。

unction model (el,dir,_warn) {
? ? warn$1 = _warn;
? ? // 綁定的值
? ? var value = dir.value;
? ? var modifiers = dir.modifiers;
? ? var tag = el.tag;
? ? var type = el.attrsMap.type;
? ? {
? ? ? // 這里遇到type是file的html,如果還使用雙向綁定會(huì)報(bào)出警告。
? ? ? // 因?yàn)镕ile inputs是只讀的
? ? ? if (tag === 'input' && type === 'file') {
? ? ? ? warn$1(
? ? ? ? ? "<" + (el.tag) + " v-model=\"" + value + "\" type=\"file\">:\n" +
? ? ? ? ? "File inputs are read only. Use a v-on:change listener instead.",
? ? ? ? ? el.rawAttrsMap['v-model']
? ? ? ? );
? ? ? }
? ? }
? ? //組件上v-model的處理
? ? if (el.component) {
? ? ? genComponentModel(el, value, modifiers);
? ? ? // component v-model doesn't need extra runtime
? ? ? return false
? ? } else if (tag === 'select') {
? ? ? // select表單
? ? ? genSelect(el, value, modifiers);
? ? } else if (tag === 'input' && type === 'checkbox') {
? ? ? // checkbox表單
? ? ? genCheckboxModel(el, value, modifiers);
? ? } else if (tag === 'input' && type === 'radio') {
? ? ? // radio表單
? ? ? genRadioModel(el, value, modifiers);
? ? } else if (tag === 'input' || tag === 'textarea') {
? ? ? // 普通input,如 text, textarea
? ? ? genDefaultModel(el, value, modifiers);
? ? } else if (!config.isReservedTag(tag)) {
? ? ? genComponentModel(el, value, modifiers);
? ? ? // component v-model doesn't need extra runtime
? ? ? return false
? ? } else {
? ? ? // 如果不是表單使用v-model,同樣會(huì)報(bào)出警告,雙向綁定只針對(duì)表單控件。
? ? ? warn$1(
? ? ? ? "<" + (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 metadata
? ? //?
? ? return true
? }

我們可以看到對(duì)于v-model的處理,在這一步上會(huì)根據(jù)使用場(chǎng)景處理調(diào)用不同的處理。單是對(duì)每種類型對(duì)應(yīng)的事件處理響應(yīng)機(jī)制也不同。因此我們需要針對(duì)不同的表單控件生成不同的render函數(shù),所以需要產(chǎn)生不同的AST屬性。model針對(duì)不同類型的表單控件有不同的處理分支。我們來看普通input標(biāo)簽的處理,genDefalutModel分支。

function genDefaultModel (el,value,modifiers) {
? ? var type = el.attrsMap.type;
? ? // v-model和v-bind值相同值,有沖突會(huì)報(bào)錯(cuò)
? ? {
? ? ? var value$1 = el.attrsMap['v-bind:value'] || el.attrsMap[':value'];
? ? ? var typeBinding = el.attrsMap['v-bind:type'] || el.attrsMap[':type'];
? ? ? if (value$1 && !typeBinding) {
? ? ? ? var binding = el.attrsMap['v-bind:value'] ? 'v-bind:value' : ':value';
? ? ? ? warn$1(
? ? ? ? ? binding + "=\"" + value$1 + "\" conflicts with v-model on the same element " +
? ? ? ? ? 'because the latter already expands to a value binding internally',
? ? ? ? ? el.rawAttrsMap[binding]
? ? ? ? );
? ? ? }
? ? }
? ? // modifiers存貯的是v-model的修飾符。
? ? var ref = modifiers || {};
? ? // lazy,trim,number是可供v-model使用的修飾符
? ? var lazy = ref.lazy;
? ? var number = ref.number;
? ? var trim = ref.trim;
? ? var needCompositionGuard = !lazy && type !== 'range';
? ? // lazy修飾符將觸發(fā)同步的事件從input改為change
? ? var event = lazy ? 'change' : type === 'range' ? RANGE_TOKEN : 'input';
? ? var valueExpression = '$event.target.value';
? ? // 過濾用戶輸入的首尾空白符
? ? if (trim) {
? ? ? valueExpression = "$event.target.value.trim()";
? ? }
? ? // 將用戶輸入轉(zhuǎn)為數(shù)值類型
? ? if (number) {
? ? ? valueExpression = "_n(" + valueExpression + ")";
? ? }
? ? // genAssignmentCode函數(shù)是為了處理v-model的格式,允許使用以下的形式: v-model="a.b" v-model="a[b]"
? ? var code = genAssignmentCode(value, valueExpression);
? ? if (needCompositionGuard) {
? ? ? // ?保證了不會(huì)在輸入法組合文字過程中得到更新
? ? ? code = "if($event.target.composing)return;" + code;
? ? }
? ? // ?添加value屬性
? ? addProp(el, 'value', ("(" + value + ")"));
? ? // 綁定事件
? ? addHandler(el, event, code, null, true);
? ? if (trim || number) {
? ? ? addHandler(el, 'blur', '$forceUpdate()');
? ? }
? }
function genAssignmentCode (value,assignment) {
? // 處理v-model的格式,v-model="a.b" v-model="a[b]"
? var res = parseModel(value);
? if (res.key === null) {
? ? // 普通情形
? ? return (value + "=" + assignment)
? } else {
? ? // 對(duì)象形式
? ? return ("$set(" + (res.exp) + ", " + (res.key) + ", " + assignment + ")")
? }
}

該函數(shù)主要邏輯是兩個(gè)部分,一部分是針對(duì)修飾符產(chǎn)生不同的事件處理字符串,二是為v-model產(chǎn)生的AST樹,添加屬性和事件相關(guān)的屬性。其中最核心的兩行代碼是:

// ?添加value屬性
addProp(el, 'value', ("(" + value + ")"));
// 綁定事件屬性
addHandler(el, event, code, null, true);

addHandler函數(shù)會(huì)為AST樹添加事件相關(guān)的屬性,addProp會(huì)為AST樹添加props屬性。最終AST樹新增了兩個(gè)屬性。

到這里我們會(huì)發(fā)現(xiàn),通過genDirective處理后,原先的AST樹新增了兩個(gè)屬性。所以在字符串生成階段同樣需要處理props和event的分支

function genData$2 (el, state) {
? var data = '{';
? // 已經(jīng)分析過的genDirectives
? var dirs = genDirectives(el, state);
? // 處理props
? if (el.props) {
? ? data += "domProps:" + (genProps(el.props)) + ",";
? }
? // 處理事件
? if (el.events) {
? ? data += (genHandlers(el.events, false)) + ",";
? }
}

最終render函數(shù)的結(jié)果為:

"_c('input',
{
? directives:[{
? ? ?name:"model",
? ? ?rawName:"v-model",
? ? ?value:(message),
? ? ?expression:"message"
? ?}],
? ?attrs:{"type":"text"},
? ?domProps:{"value":(message)},
? ?on:{
? ? ?"input":function($event){
? ? ? ?if($event.target.composing)
? ? ? ? ?return;message=$event.target.value
? ? ?}
? ?}
})"

總結(jié)

如果到這里比較迷糊的話,我們來整理一下整體的流程

  • 在生成AST階段,處理到屬性時(shí)進(jìn)入processAttrs,在該函數(shù)中判斷該屬性是否為指令,是指令判斷是不是v-on、v-bind,如果都不是就進(jìn)入addDirective
  • 通過addDirective函數(shù)為AST樹上添加了directives中的一個(gè)對(duì)象
  • 然后根據(jù)AST樹,生成render函數(shù)過程中,需要在genData中調(diào)用genDirectives,進(jìn)入指令處理流程。
  • genDirectives拿到指令對(duì)象后,會(huì)遍歷指令對(duì)象,使用state.directives[dir.name]對(duì)指令對(duì)象進(jìn)行解析和處理。這里的dir.name是model。會(huì)調(diào)用一個(gè)model函數(shù),在該函數(shù)中根據(jù)v-model的應(yīng)用標(biāo)簽類型,處理成不同的AST屬性。input輸入框類型,會(huì)調(diào)用genDefaultModel函數(shù),在其中做類型判斷,修飾符處理,然后通過addProp為AST添加props屬性,addHandler會(huì)為AST語法樹的events屬性中添加對(duì)應(yīng)事件監(jiān)聽。
  • 最后根據(jù)AST語法樹生成render函數(shù)時(shí),綁定的屬性會(huì)以props的形式存在domProps中,另一個(gè)是以事件的形式存儲(chǔ)input事件,并保留在on屬性中

patch真實(shí)節(jié)點(diǎn)

當(dāng)我們的render函數(shù)生成以后,執(zhí)行render函數(shù),生成對(duì)應(yīng)的vnode。 

有了新的vnode以后需要執(zhí)行patchVnode。前面得到的指令相關(guān)的信息會(huì)保留在vnode的data屬性里,所以對(duì)屬性的處理也會(huì)走針對(duì)指令處理的函數(shù)incokeCreateHooks。

在該函數(shù)中對(duì)指令的處理包括:

  • 判斷vnode data上存在domProps屬性,調(diào)用 updateDOMProps更新input標(biāo)簽的value值
  • 調(diào)用updateAttrs函數(shù),根據(jù)attrs屬性更新節(jié)點(diǎn)的屬性值
  • 判斷vnode data上存在on屬性,調(diào)用updateDomListeners為dom添加事件監(jiān)聽

總結(jié):所以v-model語法糖最終是通過監(jiān)聽表單控件自身的某些事件(不同類型的標(biāo)簽會(huì)有不同的監(jiān)聽事件類型,后面會(huì)列出來),去影響自身的value值。等同于

<input type="text" :value="message" @input="(e) => {this.message = e.target.value}">

不同的表單控件綁定的事件總結(jié)

上面我們講到v-model其實(shí)是一個(gè)語法糖,他本質(zhì)包含了兩個(gè)操作:

  • v-bind綁定了一個(gè)value屬性
  • v-on指令給當(dāng)前元素綁定對(duì)應(yīng)事件,默認(rèn)是input事件

原理說過了我們這里來整理一下,表單綁定v-model不同的控件到底是綁定了什么事件。

change事件

  • select
  • checkbox
  • radio

input事件

這是默認(rèn)時(shí)間,當(dāng)不是上面三種表單元素時(shí),會(huì)解析成input事件,比如text、number等input元素和textarea

組件使用v-model

由上面的原理我們可以看出來組件上使用v-model也是類似的流程,本質(zhì)上是子父組件通信的語法糖??匆粋€(gè)使用案例:

var child = {
? ? template: '<div><input type="text" :value="value" @input="emitEvent">{{value}}</div>',
? ? methods: {
? ? ? emitEvent(e) {
? ? ? ? this.$emit('input', e.target.value)
? ? ? }
? ? },
? ? props: ['value']
? }
?new Vue({
? ?data() {
? ? ?return {
? ? ? ?message: 'test'
? ? ?}
? ?},
? ?components: {
? ? ?child
? ?},
? ?template: '<div id="app"><child v-model="message"></child></div>',
? ?el: '#app'
?})

父組件上使用v-model,子組件默認(rèn)會(huì)利用名為value的prop和名為input的事件。

源碼這里就不詳細(xì)說了,子組件的vnode會(huì)為data.props添加data.model.value,并且給data.on添加data.model.callback。因此父組件的語法糖本質(zhì)上可以修改為

<child :value = "message" @input="function(e){message = e}"</child>

顯然,這種寫法就是事件通信的寫法,這個(gè)過程又回到對(duì)事件指令的分析過程了。因此我們可以很明顯的意識(shí)到,組件使用v-model本質(zhì)上還是一個(gè)子父組件通信的語法糖。 

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。 

相關(guān)文章

  • Vue解決移動(dòng)端彈窗滾動(dòng)穿透問題

    Vue解決移動(dòng)端彈窗滾動(dòng)穿透問題

    這篇文章主要介紹了Vue解決移動(dòng)端彈窗滾動(dòng)穿透問題的方法,幫助大家更好的理解和使用vue框架,感興趣的朋友可以了解下
    2020-12-12
  • Vue中多個(gè)元素、組件的過渡及列表過渡的方法示例

    Vue中多個(gè)元素、組件的過渡及列表過渡的方法示例

    這篇文章主要介紹了Vue中多個(gè)元素、組件的過渡及列表過渡的方法示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-02-02
  • Vue如何實(shí)現(xiàn)組件間通信

    Vue如何實(shí)現(xiàn)組件間通信

    組件間通信簡(jiǎn)單來說就是組件間進(jìn)行數(shù)據(jù)傳遞。就像我們?nèi)粘5拇螂娫挘褪峭ㄓ嵉囊环N方式,你把話說給我聽,我把話說給你聽,說的話就是數(shù)據(jù)。電話就是通訊方式的一種。無論是 Vue 還是 React ,都得進(jìn)行組件間通信。
    2021-05-05
  • Vue使用鼠標(biāo)在Canvas上繪制矩形

    Vue使用鼠標(biāo)在Canvas上繪制矩形

    這篇文章主要介紹了Vue使用鼠標(biāo)在Canvas上繪制矩形,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • vue通過watch對(duì)input做字?jǐn)?shù)限定的方法

    vue通過watch對(duì)input做字?jǐn)?shù)限定的方法

    本篇文章主要介紹了vue通過watch對(duì)input做字?jǐn)?shù)限定的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • vue中組件如何使用vue-quill-editor

    vue中組件如何使用vue-quill-editor

    這篇文章主要介紹了vue中組件如何使用vue-quill-editor問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • 淺談vue3在項(xiàng)目中的邏輯抽離和字段顯示

    淺談vue3在項(xiàng)目中的邏輯抽離和字段顯示

    本文主要介紹了vue3在項(xiàng)目中的邏輯抽離和字段顯示,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • 利用vue寫todolist單頁(yè)應(yīng)用

    利用vue寫todolist單頁(yè)應(yīng)用

    有很多關(guān)于vue的todolist小程序,這篇文章主要介紹了 用vue寫todolist單頁(yè)應(yīng)用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • vue3.x ref()語法糖賦值方式

    vue3.x ref()語法糖賦值方式

    這篇文章主要介紹了vue3.x ref()語法糖賦值方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • Vue中響應(yīng)式系統(tǒng)實(shí)現(xiàn)原理圖文講解

    Vue中響應(yīng)式系統(tǒng)實(shí)現(xiàn)原理圖文講解

    Vue的響應(yīng)式實(shí)現(xiàn)是借助Object.defineProperty通過重寫getter和setter方法來進(jìn)行的數(shù)據(jù)劫持,Vue3通過Proxy代理攔截對(duì)象中任意屬性的變化,通過Reflect反射對(duì)源對(duì)象的屬性進(jìn)行操作,然后再在get里收集依賴在set里派發(fā)更新
    2023-03-03

最新評(píng)論