Vuejs核心代码解析之编译阶段2(三)
最近比较懒,工作也比较忙。这节拖后较久。 声明本人非大大,有错误请指出。
这节说下只处理文本节点的情况。
例子:
html:
<div id="output">
{{show}}
</div>
js:
var vm = new Vue({
data:{
show:'init'
}
});
vm.$mount('#output');
其实就是把{{show}}对应替换成相应的值。
简化版vue,可以看这里,现在还有点复杂,直接看src比较好。
简化版 ,跪求几个star, (hi star)
之后还会进一步整理。
#编译执行阶段.
function compositeLinkFn (vm, el) {
// cache childNodes before linking parent, fix #657
var childNodes = _.toArray(el.childNodes)
// link
if (nodeLinkFn) nodeLinkFn(vm, el)
if (childLinkFn) childLinkFn(vm, childNodes)
}
因无nodeLinkFn 直接执行 childLinkFn:
function makeChildLinkFn (linkFns) {
return function childLinkFn (vm, nodes, host) {
var node, nodeLinkFn, childrenLinkFn
for (var i = 0, n = 0, l = linkFns.length; i < l; n++) {
node = nodes[n]
nodeLinkFn = linkFns[i++]
childrenLinkFn = linkFns[i++]
// cache childNodes before linking parent, fix #657
var childNodes = _.toArray(node.childNodes)
if (nodeLinkFn) {
nodeLinkFn(vm, node, host)
}
if (childrenLinkFn) {
childrenLinkFn(vm, childNodes, host)
}
}
}
}
为初始化的时候返回的linker
linkFns:
//初始化遍历后的结果
[function textNodeLinkFn(vm, el) { ... }, null]
依次执行 linkFns 内函数 :(先本身,后子)
本例只有一个文本节点(本身), 就是 textNodeLinkFn:
function makeTextNodeLinkFn (tokens, frag) {
return function textNodeLinkFn (vm, el) {
var fragClone = frag.cloneNode(true)
var childNodes = _.toArray(fragClone.childNodes)
var token, value, node
for (var i = 0, l = tokens.length; i < l; i++) {
token = tokens[i]
value = token.value
if (token.tag) {
node = childNodes[i]
vm._bindDir(token.type, node, token.descriptor, token.def)
}
}
_.replace(el, fragClone)
}
}
上节的连接器: tokens 与 frag 为:
//frag 为 fragment 有三文本节点data分别为:
1. "↵ "
2. " "
3. "↵ "
//tokens 为一个数组, 数组内三个对象:
1.{value:"↵ "}
2. {
html: false,
tag: true,
type: "text",
value: "show",
def:{
bind:function(){..},
update:function(){..}
},
descriptor:{
expression: "show",
raw: "show"
}
}
3.{value:"↵ "}
遍历tokens, 对tag为true的进行处理(也就是{{show}}):
if (token.tag) {
node = childNodes[i]
vm._bindDir(token.type, node, token.descriptor, token.def)
}
执行bindDir:
创建指令
exports._bindDir = function (name, node, desc, def, host) {
this._directives.push(
new Directive(name, node, this, desc, def, host)
)
}
指令构造:
function Directive (name, el, vm, descriptor, def, host) {
// public
this.name = name
this.el = el
this.vm = vm
// copy descriptor props
this.raw = descriptor.raw
this.expression = descriptor.expression
this.arg = descriptor.arg
// private
this._descriptor = descriptor
this._host = host
this._locked = false
this._bound = false
// init
this._bind(def)
}
执行_bind:
p._bind = function (def) {
_.extend(this, def)
this._watcherExp = this.expression
if (this.bind) {
//指令def中的bind方法
// after this.attr = 'nodeValue'
this.bind()
}
if (this.update || this.twoWay) {
// wrapped updater for context
var dir = this
//dir内update方法
var update = this._update = this.update
? function (val, oldVal) {
if (!dir._locked) {
dir.update(val, oldVal)
}
}
: function () {}
var watcher = this.vm._watchers[this.raw]
// v-repeat always creates a new watcher because it has
// a special filter that's bound to its directive
// instance.
if (!watcher) {
// this.raw = 'show'
watcher = this.vm._watchers[this.raw] = new Watcher(
this.vm,
this._watcherExp,
update, // callback
{
twoWay: this.twoWay,
deep: this.deep
}
)
} else {
watcher.addCb(update)
}
this._watcher = watcher
if (this._initValue != null) {
watcher.set(this._initValue)
} else if (this.update) {
this.update(watcher.value)
}
}
this._bound = true
}
其他比较简单主要看 new Watcher:
function Watcher (vm, expression, cb, options) {
this.vm = vm
vm._watcherList.push(this)
this.expression = expression
this.cbs = [cb]
this.id = ++uid // uid for batching
this.active = true
options = options || {}
this.deep = !!options.deep
this.user = !!options.user
this.deps = []
this.newDeps = []
// parse expression for getter/setter
//expression = 'show' , options.twoWay = undefined
//对表达式进行处理,
var res = expParser.parse(expression, options.twoWay)
this.getter = res.get
this.setter = res.set
//获取第一节初始化中Oberver中的值,也就是vm.data,也就是init
this.value = this.get()
}
this.value = this.get() 获取data的值,初始化已给data添加访问器,所以获取直接触发可以获取到'init':
p.get = function () {
this.beforeGet()
var vm = this.vm
var value
try {
value = this.getter.call(vm, vm)
} catch (e) {
if (config.warnExpressionErrors) {
_.warn(
'Error when evaluating expression "' +
this.expression + '":\n ' + e
)
}
}
// "touch" every property so they are all tracked as
// dependencies for deep watching
if (this.deep) {
traverse(value)
}
value = _.applyFilters(value, this.readFilters, vm)
this.afterGet()
//this.value = value = 'init'
return value
}
最后更新值 this.update(watcher.value):
//p._bind
// 上面的返回值(p.get )value = watcher.value = 'init'
// this.attr dir.bind 执行结果 'nodeValue'
update: function (value) {
this.el[this.attr] = _.toString(value)
}
this.el 就是 fragment中的node, 最后replace 结束。。。
//textNodeLinkFn
{{show}} => 'init'
_.replace(el, fragClone)
有兴趣可以看下peel-vue,地址在上面。thx
</>