做爲 attribute 和 property 的 value 及 Vue.js 的相關處理

attribute 和 property 是 Web 開發中,比較容易混淆的概念,而對於 value,因其特殊性,更易困惑,本文嘗試作一下梳理和例證html

attribute 和 property 的概念

簡單的說,attribute 是元素標籤的屬性,property 是元素對象的屬性,例如:vue

<input id="input" value="test value">
<script>
let input = document.getElementById('input');
console.log(input.getAttribute('value')); // test value
console.log(input.value); // test value
</script>
複製代碼
  • input 的 value attribute 是經過標籤裏的 value="test value" 定義的,能夠經過 input.getAttribute('value') 獲取,能夠經過 input.setAttribute('value', 'New Value') 更新
  • input 的 value property 可經過 input.value 獲取和更新,初始值是與 attribute 中的賦值一致的

attribute 和 property 的綁定

若是在最開始的時候,更新 attribute value 的值,property 的值也會隨之改變node

可是更新 property value 的值(在文本框輸入或給 input.value 賦新值 ),attribute 的值不會隨之改變,並且此時再更新 attribute 的值,property 的值也再也不隨之改變,如此動畫所示,也可訪問此頁面嘗試進行操做web

這實際上是髒值標記(dirty value flag)在起做用,dirty value flag 的初始值爲 false,即 attribute value 的更新默認會改變對應的 property value,可是一旦用戶交互修改了 property value,dirty value flag 的值就變爲 true,即attribute value 的更新就不會改變對應的 property value 了npm

因此在實際項目中,咱們通常都是在處理做爲 property 的 valuebash

Vue.js 對 value 的處理

通常狀況使用 :value

Vue.js 的 v-bind,通常狀況下是在處理 attribute,若是要做爲 property 處理的話,須要加上 .propapp

不過 v-bind:value 卻大都默認爲處理 property 值,由於被強制轉化了,例如:dom

<input id="input" :value="'test value'" >
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let input = new Vue({
  el: '#input',
  mounted () {
    console.log(this.$el.getAttribute('value')); // null
    console.log(this.$el.value); // test value
    console.log(this._vnode.data) // {attrs: {id: "input"}, domProps: {value: "test value"}}
  }
});
</script>
複製代碼

可見,Vue.js 將 value 做爲 VNode 的 data 中的 domProps 的屬性,而不是 attrs 的屬性,因此掛載後會成爲做爲 property 的 valueide

在 Vue.js 源碼中,強制轉化 property 的處理以下:動畫

// src/compiler/parser/index.js
function processAttrs (el) {
...
        if ((modifiers && modifiers.prop) || (
          !el.component && platformMustUseProp(el.tag, el.attrsMap.type, name)
        )) {
          addProp(el, name, value, list[i], isDynamic)
        } else {
          addAttr(el, name, value, list[i], isDynamic)
        }
複製代碼

其中 platformMustUseProp 在 web 平臺的定義以下:

// src/platforms/web/util/attrs.js
const acceptValue = makeMap('input,textarea,option,select,progress')
export const mustUseProp = (tag: string, type: ?string, attr: string): boolean => {
  return (
    (attr === 'value' && acceptValue(tag)) && type !== 'button' ||
    (attr === 'selected' && tag === 'option') ||
    (attr === 'checked' && tag === 'input') ||
    (attr === 'muted' && tag === 'video')
  )
}
複製代碼

由上可知,類型不爲 button 的 input, textarea, option, select, progress 的 value 會強制做爲 property,而不須要設置爲 :value.prop

例如 textarea 標籤,其自己其實並不支持 value attribute,因此如下代碼中的 value 的值並不會顯示在多行文本框中

<textarea value="test value"></textarea>
複製代碼

可是在 Vue.js 中, 如下代碼能成功綁定到 value property 並顯示在多行文本框中

<textarea :value="'test value'"></textarea>
複製代碼

特殊狀況使用 :value.prop

以上 Vue.js 源碼須要注意的還有,強制做爲 property, 還要知足 !el.component,即不爲動態組件,由於動態組件的 el.component 的值爲其 is attribute 的值

即動態組件的的 v-bind 默認都是做爲 attribute的,若是要做爲 property,就要使用 .prop,例如:

<div id="app">
  <component :is="element" :value.prop="'test value'"></component>
  <button @click="change">Change</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let app = new Vue({
  el: '#app',
  data: {
    element: 'input'
  },
  methods: {
    change () {
      this.element = 'input' === this.element ? 'textarea' : 'input';
    }
  }
});
</script>
複製代碼

若是以上 component 中,刪除 :value.prop 的 .prop,切換到 textarea 時,其值就不會顯示在多行文本框中,能夠在此頁面點擊切換標籤查看

總結

  • 做爲 attribute 和 property 的 value 的綁定關係會在用戶交互更新值後失效
  • Vue.js 通常使用 :value 便可讓 value 做爲 property
  • Vue.js 動態模版須要使用 :value.prop 纔可以讓 value 做爲 property
相關文章
相關標籤/搜索