-
Vue
源码解析:深入响应式原理(中)
Directive
Vue
指令类
型很多,
限于篇幅,
我们不会把所有指令的解析过程都介绍一遍
,
这里结合前面的例子只介绍
v-text
指令的解析过程,
其他
指令的解析过
程也大同小异。
前面我们提到了
Vue
实例创建的生命周期,
在给
data
添加
Observer
之后,
有一个过程是调用
e
方法对模板进行编译。
compile
方
法的源码定义如下:
源码目录:
src/instance/in
ternal/-->
ype._compile = function (el)
{
var options = this.$$options
// transclude and init element
// transclude can potentially
replace original
// so we need to
keep reference; this step also injects
// the template and caches the original attributes
// on the container node and
replacer node.
var original = el
el = transclude(el, options)
this._initElement(el)
// handle v-pre on root node (#2026)
if (pe === 1 && getAttr(el, 'v-pre') !== null) {
return
}
// root is always compiled per-
instance, because
// container
attrs and props can be different every time.
var contextOptions = this._context
&& this._context.$$options
var
rootLinker = compileRoot(el, options,
contextOptions)
// resolve slot
distribution
resolveSlots(this,
options._content)
// compile and
link the rest
var contentLinkFn
var ctor = uctor
//
component compilation can be cached
// as long as it's not using inline-template
if (options._linkerCachable) {
contentLinkFn =
if (!contentLinkFn) {
contentLinkFn = = compile(el, options)
}
}
// link phase
// make sure to link
root with prop scope!
var
rootUnlinkFn = rootLinker(this, el, this._scope)
var contentUnlinkFn = contentLinkFn
? contentLinkFn(this, el)
: compile(el, options)(this, el)
// register composite unlink
function
// to be called during
instance destruction
this._unlinkFn
= function () {
rootUnlinkFn()
// passing destroying: true to
avoid searching and
// splicing
the directives
contentUnlinkFn(true)
}
// finally replace original
if (e)
{
replace(original, el)
}
this._isCompiled =
true
this._callHook('compiled')
}
我们可以通过下图来看一下这个方法编译的主要流程:
这个过程通过
el = transclude(el, option)
方法把
template
编译成一段
document
fragment
,拿到
el
对象。而指令解析部分就是通过
compile(el, options)
方法实现的。接下来我们看一下
compile
方法的实现,它的源码定义如下:
<
!-
源码目录:
src/compiler/-->
export function compile (el,
options, partial) {
// link function
for the node itself.
var nodeLinkFn =
partial || !options._asComponent
?
compileNode(el, options)
: null
// link function for the childNodes
var childLinkFn =
!(nodeLinkFn && al) &&
!isScript(el) &&
ldNodes()
? compileNodeList(odes, options)
: null
/**
* A composite linker function to be
called on a already
* compiled piece
of DOM, which instantiates all directive
* instances.
*
* @param {Vue} vm
*
@param {Element|DocumentFragment} el
* @param {Vue} [host] - host vm of transcluded
content
* @param {Object} [scope] -
v-for scope
* @param {Fragment}
[frag] - link context fragment
*
@return {Function|undefined}
*/
return function compositeLinkFn (vm,
el, host, scope, frag) {
// cache
childNodes before linking parent, fix #657
var childNodes = toArray(odes)
// link
var dirs =
linkAndCapture(function compositeLinkCapturer () {
if (nodeLinkFn) nodeLinkFn(vm,
el, host, scope, frag)
if
(childLinkFn) childLinkFn(vm, childNodes, host,
scope, frag)
}, vm)
return makeUnlinkFn(vm, dirs)
}
}
compile
方法主要通过
compileNode(el, options)
方法完
成节点的解析,
如果节点拥有子节点,
则调用
< br>
compileNodeList(odes, options)
方法完成子节点的解析。
compileNodeList
方法其实就是遍历子节点,递归调用
compileNode
方法。因为
DOM
元素本身就是树结构,这种递归
方法也就是常见的树的深度遍历方法,
这样就可以完成整个
DOM
树节点的解析。
接下来我们看一下
compileNode
方法的实现,
它的源码定义如下:
<
/p>
源码目录:
src/compil
er/-->
function compileNode (node,
options) {
var type = pe
if (type === 1 && !isScript(node)) {
return compileElement(node, options)
} else if (type === 3 && ()) {
return compileTextNode(node, options)
} else {
return null
}
}
compileNode
方法对节点的
nodeType
做判断,如果是一个非
script
普通的元素(
div
、
p
等);则调用
compileElement(node, options)
方
法解析;如果是一个非空的文本节点,则调用
compileTextNode(node, options)
方法解析。我们在前面的例子中解析的是非空文本节点
count:
{{times}}
,这实际上是
v-text
指令,它的解析是通过
compileTextNode
方法实现的。接下来我们看一下
compileTextNode
方法,它的源码定
义如下:
源码目录:
src/compiler/
-->
function compileTextNode (node,
options) {
// skip marked text nodes
if (node._skip) {
return removeText
}
var
tokens = parseText(ext)
if (!tokens)
{
return null
}
// mark adjacent text nodes as
skipped,
// because we are using ext
to compile
// all adjacent text nodes
together. This fixes
// issues in IE
where sometimes it splits up a single
// text node into multiple ones.
var
next = bling
while (next && pe === 3)
{
next._skip = true
next = bling
}
var frag
= DocumentFragment()
var el, token
for (var i = 0, l = i < l; i++) {
token = tokens[i]
el
=
? processTextToken(token,
options)
: TextNode()
Child(el)
}
return
makeTextNodeLinkFn(tokens, frag, options)
}
compileTextNode
方法首先调用了
parseText
方法对
ext
做解析。主要通过正则表达式解析
count: {{times}}
部分,我们看一下
解析结果,如下图所示:
解析后的
tokens
是一个数组,
数组的每个元素则是一个
Object
。
如果是
count:
这样的普通文本,
则返回的对象只有
value
字段;
如果是
{{times}}
这样的插值,则返回的对象包含
ht
ml
、
onTime
、
tag
、
value
等字段。
接下来创建
document
fragment
,遍历
tokens
创建
DOM
节点插入到这个
fragment
中。在遍历过程中,如果
token
无
tag
字段,则调
用
TextNode()
方法创建
DOM
节点;否则调用
proces
sTextToken(token, options)
方法创建
DOM
节点和扩
展
token
对象。我们看一下调用后的结果,如下图所示:
-
-
-
-
-
-
-
-
-
上一篇:selenium自动化项目总体框架
下一篇:财务管理相关证书