本篇文章,我们简单的介绍几个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概述中我们也提到过,platformDirectives
和baseDirectives
会对部分内置指令进行处理。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
数组中添加一个对象,对象里保存的name
和value
。
// DOM propsif (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 ???} ?}}
首先会重置oldProps
中props
上不存在的属性。然后遍历props
中的属性,如果key
值textContent
或innerHTML
,则清除children
的内容。
如果key === ‘value‘
这里应该对input
、select
等标签的特殊处理。否则,直接设置elm.textContent = cur
,以此来改变文本内容。
v-html
v-html
和v-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
原文地址:http://www.cnblogs.com/dhsz/p/7697741.html