Vue源码后记-更多options参数(2)

vue

  写起来感觉都是老三套,AST => render => VNode => patch

  之前是把AST弄完了,对事件和过滤器处理如图:

  render函数也只看这两部分的转换吧!

  首先是el.events,该属性在genData中被处理,这个在之前讲过了!不过,前面没有modifiers,所以,这里可以再看看:

    // name => click

// handler => {value:'click',modifiers:{self:true,number:true}}

function genHandler(name, handler) {

// code...

if (!handler.modifiers) {

// 无modifiers

}

// 处理modifiers

else {

var code = '';

var genModifierCode = '';

var keys = [];

for (var key in handler.modifiers) {

// 修饰符部分代码集合

if (modifierCode[key]) {

genModifierCode += modifierCode[key];

// 键盘按键简化

if (keyCodes[key]) {

keys.push(key);

}

}

// number,trim等

else {

keys.push(key);

}

}

if (keys.length) {

// 处理特殊按键的keyCode

code += genKeyFilter(keys);

}

// Make sure modifiers like prevent and stop get executed after key filtering

if (genModifierCode) {

code += genModifierCode;

}

var handlerCode = isMethodPath ?

handler.value + '($event)' :

isFunctionExpression ?

("(" + (handler.value) + ")($event)") :

handler.value;

return ("function($event){" + code + handlerCode + "}")

}

}

function genKeyFilter(keys) {

return ("if(!('button' in $event)&&" + (keys.map(genFilterCode).join('&&')) + ")return null;")

}

function genFilterCode(key) {

var keyVal = parseInt(key, 10);

if (keyVal) {

return ("$event.keyCode!==" + keyVal)

}

var alias = keyCodes[key];

return ("_k($event.keyCode," + (JSON.stringify(key)) + (alias ? ',' + JSON.stringify(alias) : '') + ")")

}

  函数的前半部分会对一些特殊按键的修饰符做处理,包括鼠标按键、键盘按键,以及阻止冒泡与默认事件等。

  其中:

  genModifierCode返回: 

  稍微普及一下target、currentTarget的区别,主要是currentTarget始终指向事件绑定的对象,而target指向事件触发的对象。

  code返回:

  这里强行返回一个number字符串真是奇怪啊!

  完事后,将genModifierCode拼接到code后面,最后return的时候由于handler.value是一个内置事件click,所以handlerCode拼接为click($event)。

  完整的return字符串为:

    "function($event){if(!('button' in $event)&&_k($event.keyCode,"number"))return null;if($event.target !== $event.currentTarget)return null;click($event)}"

  格式化一下:

    (function($event) {

if (!('button' in $event) && _k($event.keyCode, "number")) return null;

if ($event.target !== $event.currentTarget) return null;

click($event)

})

  至此,事件的render代码处理完毕,作为on属性添加到el上。

  后面会接着处理子节点,由于是表示式,所以直接会被包裹在_s函数中,最后整个AST返回一个_c,参数为tag、data、children,详细过程之前搞过,这里不重复了。

  直接把render => VNode => patch一起看了吧!

  由于proxy的关系,vnode = render.call(vm._renderProxy, vm.$createElement)这个代码的执行过程太长了,不如直接看render函数:

    (function() {

with(this) {

return _c('div', {

attrs: {

"id": "app"

}

}, [_c('div', {

on: {

"click": function($event) {

if (!('button' in $event) && _k($event.keyCode, "number")) return null;

if ($event.target !== $event.currentTarget) return null;

click($event)

}

}

}, [_v("\n" + _s(_f("filter")(computedValue)) + "\n")])])

}

})

  这里唯一比较特殊的是那个filter。

  这里从内到外首先是执行_f函数,这个之前没见过,主要是处理filter:

    Vue.prototype._f = resolveFilter;

function resolveFilter(id) {

return resolveAsset(this.$options, 'filters', id, true) || identity

}

function resolveAsset(options, type, id, warnMissing) {

/* istanbul ignore if */

if (typeof id !== 'string') {

return

}

var assets = options[type];

// 返回了定义的filter函数

if (hasOwn(assets, id)) {

return assets[id]

}

// 驼峰处理

var camelizedId = camelize(id);

if (hasOwn(assets, camelizedId)) {

return assets[camelizedId]

}

var PascalCaseId = capitalize(camelizedId);

if (hasOwn(assets, PascalCaseId)) {

return assets[PascalCaseId]

}

// fallback to prototype chain

var res = assets[id] || assets[camelizedId] || assets[PascalCaseId];

// warning...

return res

}

  该函数返回了本地定义的fiter函数,即options中的那个函数,然后传入变量computedValue,这个变量在一开始的init中定义了一个Watcher监视,在这里进行调用获取值,执行后面的表达式得到数值2。

  总的来说,filter与computed结合执行过程就是:

    (function /*filter*/ (value) {

if (!value) {

return;

}

return value * 4;

})(function /*computed*/ () {

return this.value * 2

})()

  下面是on事件,好像没啥讲的,直接把整个on对象按在了data属性里返回一个VNode,看看patch阶段怎么处理那个number字符串。

  我发现我用错了,number这个是用在v-model里面的!好吧,这里会把不认识的字符串默认当成keyCode来处理,所以number会被强行作为判断条件,不过不会报错,也无所谓了。

  简单讲下这里的click事件:

    "click": function($event) {

if (!('button' in $event) && _k($event.keyCode, "number")) return null;

if ($event.target !== $event.currentTarget) return null;

click($event)

}

  这里有一个_k函数,判断两个参数是否相等:

    Vue.prototype._k = checkKeyCodes;

function checkKeyCodes(eventKeyCode, key, builtInAlias) {

var keyCodes = config.keyCodes[key] || builtInAlias;

if (Array.isArray(keyCodes)) {

return keyCodes.indexOf(eventKeyCode) === -1

} else {

return keyCodes !== eventKeyCode

}

}

  第二个判断点击事件是否只发生在事件绑定对象上,最后执行click事件的回调函数。

  写得好丑啊!

以上是 Vue源码后记-更多options参数(2) 的全部内容, 来源链接: utcz.com/z/379486.html

回到顶部