1. 程式人生 > >v-text、v-html、v-cloak、v-pre.md

v-text、v-html、v-cloak、v-pre.md

spa rcu 定義 回調函數 undefined serve 用法 roo 重置

本篇文章,我們簡單的介紹幾個Vue內置指令的實現。

v-text

v-text的用法很簡單,以下兩個表達式的作用相同。

<span v-text="msg"></span>
<span>{{msg}}</span>

和所有普通指令一樣,在生成ast時,v-text會被解析到el.directives中。

但在生成render函數的過程中,在解析屬性時,

我們會首先解析指令。genDirectives方法中有如下一段代碼:

const gen: DirectiveFunction = 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, dir, warn)
}
if (needRuntime) {
  hasRuntime = true
  res += `...`
}

directives概述中我們也提到過,platformDirectivesbaseDirectives會對部分內置指令進行處理。v-text就是其中之一。

最終的gen函數如下所示:

export default function text (el: ASTElement, dir: ASTDirective) {
  if (dir.value) {
    addProp(el, ‘textContent‘, `_s(${dir.value})`)
  }
}

該函數返回的是undefined,所以needRuntime最終是false,所以該結果不會添加到res上。

addProp

的定義如下:

export function addProp (el: ASTElement, name: string, value: string) {
  (el.props || (el.props = [])).push({ name, value })
}

它會給el.props數組中添加一個對象,對象裏保存的namevalue

// DOM props
if (el.props) {
  data += `domProps:{${genProps(el.props)}},`
}

最終,會添加到domProps對應的數組中。上面例子中的span,最終生成的render函數如下:

_c(‘span‘,{domProps:{"textContent":_s(msg)}})

patch過程中的處理,和其他data中的數據一樣,是通過鉤子函數處理的。

function updateDOMProps (oldVnode: VNodeWithData, vnode: VNodeWithData) {
  if (!oldVnode.data.domProps && !vnode.data.domProps) {
    return
  }
  let key, cur
  const elm: any = vnode.elm
  const oldProps = oldVnode.data.domProps || {}
  let props = vnode.data.domProps || {}
  // clone observed objects, as the user probably wants to mutate it
  if (props.__ob__) {
    props = vnode.data.domProps = extend({}, props)
  }

  for (key in oldProps) {
    if (props[key] == null) {
      elm[key] = ‘
    }
  }
  for (key in props) {
    cur = props[key]
    if (key === ‘textContent|| key === ‘innerHTML‘) {
      if (vnode.children) vnode.children.length = 0
      if (cur === oldProps[key]) continue
    }

    if (key === ‘value‘) {
      // store value as _value as well since
      // non-string values will be stringified
      elm._value = cur
      // avoid resetting cursor position when value is the same
      const strCur = cur == null ? : String(cur)
      if (shouldUpdateValue(elm, vnode, strCur)) {
        elm.value = strCur
      }
    } else {
      elm[key] = cur
    }
  }
}

首先會重置oldPropsprops上不存在的屬性。然後遍歷props中的屬性,如果keytextContentinnerHTML,則清除children的內容。

如果key === ‘value‘這裏應該對inputselect等標簽的特殊處理。否則,直接設置elm.textContent = cur,以此來改變文本內容。

v-html

v-htmlv-text的用法和處理流程基本完全一樣,唯一的區別就是最終v-html設置的elm.innerHTML = cur

用法示例如下:

<span v-html="msg"></span>

v-cloak

這個指令用的比較少,不懂的人看完官方文檔的說明可能還是稀裏糊塗的。它的ast生成和上面講的普通指令一樣,在genDirectives時,baseDirectives中包含了cloak,但最終返回的gen是一個空函數。最終它也不會添加到directives數組中,之後也就沒有了對它的處理。

因為我們的模板再編譯的過程中,頁面中是會顯示Mustache 標簽的。該指令就是在模板編譯之後,被刪除。我們可以添加[v-cloak] { display: none },來防止用戶感知到Mustache 標簽 出現。

v-pre

v-pre表示該會跳過該標簽及其子元素的編譯。

在編譯模板時的start回調函數中,有如下片段:

if (!inVPre) {
  processPre(element)
  if (element.pre) {
    inVPre = true
  }
}
 if (inVPre) {
   processRawAttrs(element)
 } else {
   ...
 }

processPre函數會獲取element上的v-pre屬性,如果有則設置element.pre = true,同時設置inVPre = true

接下來的處理,會走進processRawAttrs函數。else塊內對各種指令、屬性等的處理,都不會執行。

function processRawAttrs (el) {
  const l = el.attrsList.length
  if (l) {
    const attrs = el.attrs = new Array(l)
    for (let i = 0; i < l; i++) {
      attrs[i] = {
        name: el.attrsList[i].name,
        value: JSON.stringify(el.attrsList[i].value)
      }
    }
  } else if (!el.pre) {
    // non root node in pre blocks with no attributes
    el.plain = true
  }
}

這裏是對屬性的處理,如果el.attrsList不為空數組,則直接循環el.attrsList上的屬性添加到el.attrs上。否則,如果當前元素沒有設置v-pre指令(是設置v-pre元素的子元素),則設置el.plain = true

因為我們不編譯的是整個子樹,而不是單個元素。Vue中就是通過inVPre來標示的,我們parse的整個過程就是入棧出棧,當子元素都編譯完,會走到當前元素的end處理,此時再設置inVPre = false,來結束不編譯的內容。

v-text、v-html、v-cloak、v-pre.md