写起来感觉都是老三套,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)的更多相关文章

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

    我是这样计划的,写完这个还写一篇数据变动时,VNode是如何更新的,顺便初探一下diff算法. 至于vue-router.vuex等插件源码,容我缓一波好吧,vue看的有点伤. 其实在之前讲其余内置指 ...

  2. Vue源码后记-其余内置指令(3)

    其实吧,写这些后记我才真正了解到vue源码的精髓,之前的跑源码跟闹着玩一样. go! 之前将AST转换成了render函数,跳出来后,由于仍是字符串,所以调用了makeFunction将其转换成了真正 ...

  3. Vue源码后记-钩子函数

    vue源码的马拉松跑完了,可以放松一下写点小东西,其实源码讲20节都讲不完,跳了好多地方. 本人技术有限,无法跟大神一样,模拟vue手把手搭建一个MVVM框架,然后再分析原理,只能以门外汉的姿态简单过 ...

  4. Vue源码后记-其余内置指令(1)

    把其余的内置指令也搞完吧,来一个全家桶. 案例如下: <body> <div id='app'> <div v-if="vIfIter" v-bind ...

  5. Vue源码后记-vFor列表渲染(2)

    这一节争取搞完! 回头来看看那个render代码,为了便于分析,做了更细致的注释: (function() { // 这里this指向vue对象 下面的所有方法默认调用Vue$3.prototype上 ...

  6. Vue源码后记-vFor列表渲染(1)

    钩子函数比较简单,没有什么意思,这一节搞点大事情 => 源码中v-for的渲染过程. vue的内置指令包含了v-html.v-if.v-once.v-bind.v-on.v-show等,先从一个 ...

  7. Vue源码后记-其余内置指令(2)

    -- 指令这个讲起来还有点复杂,先把html弄上来: <body> <div id='app'> <div v-if="vIfIter" v-bind ...

  8. Vue源码后记-vFor列表渲染(3)

    这一节肯定能完! 经过DOM字符串的AST转化,再通过render变成vnode,最后就剩下patch到页面上了. render函数跑完应该是在这里: function mountComponent( ...

  9. vue源码逐行注释分析+40多m的vue源码程序流程图思维导图 (diff部分待后续更新)

    vue源码业余时间差不多看了一年,以前在网上找帖子,发现很多帖子很零散,都是一部分一部分说,断章的很多,所以自己下定决定一行行看,经过自己坚持与努力,现在基本看完了,差ddf那部分,因为考虑到自己要换 ...

随机推荐

  1. Mybatis学习(三)XML配置文件之mybatis-config.xml

    1.MyBatis的配置文件结构 1.1 properties 这些是外部化的,可替代的属性,这些属性也可以配置在典型的 Java 属性配置文件中,或者通过 properties 元素的子元素来传递. ...

  2. [js高手之路] 设计模式系列课程 - 迭代器(1)

    迭代器是指通过一种形式依次遍历数组,对象,或者类数组结构中的每个元素. 常见的有jquery中的each方法, ES5自带的forEach方法. 下面我们就来自定义一个类似jquery或者ES5的迭代 ...

  3. html tip实现

    一.介绍before/after CSS中的before和after伪类选择器早在CSS2时就被引入,改属性被所有主流浏览器所支持了.before和after顾名思义,分别指的是伪元素在元素前/后添加 ...

  4. 方法--printStackTrace()

    java抛出异常的方法有很多,其中最常用的两个: System.out.println(e),这个方法打印出异常,并且输出在哪里出现的异常,不过它和另外一个e.printStackTrace()方法不 ...

  5. gRPC官方快速上手学习笔记(c#版)

    上手前准备工作 支持操作系统:windows.OS X.Linux.实例采用.net..net core sdk. The .NET Core SDK command line tools. The ...

  6. Linux入门之常用命令(13) date

    在linux shell编程中,经常用到日期的加减运算 以前都是自己通过expr函数计算,很麻烦 其实date命令本身提供了日期的加减运算 非常方便.例如:得到昨天的时间date +%Y%m%d -- ...

  7. 51 nod 1521 一维战舰 时间复杂度O(n),同 Codeforces 567D. One-Dimensional Battle Ships 有详细注释

    题目:51nod: 题目Codeforces: 题目注意到两个战舰不能挨在一起就可以了. // 每一段 struct node{ int left; // 段的左端点 int right; // 段的 ...

  8. uva11401

    题目大意:计算从1,2,3,...,n中选出3个不同的整数,使得以它们为边长可以构成三角形的个数. 思路:用一般的方法需要三重循环,时间复杂度为O(n^3),肯定超时,因此可用数学的方法对问题进行分析 ...

  9. CPU工作方式、多核心、超线程技术详解[转贴]

    CPU是一台电脑的灵魂,决定电脑整体性能.现在的主流CPU都是多核的,有的运用了多线程技术(Hyper-threading,简称HT).多核可能还容易理解些,相信不少玩家都能说出个所以然.但超线程是个 ...

  10. Java高级工程师进阶路线

    第一部分:宏观方面 一. JAVA.要想成为JAVA(高级)工程师肯定要学习JAVA.一般的程序员或许只需知道一些JAVA的语法结构就可以应付了.但要成为JAVA(高级) 工程师,您要对JAVA做比较 ...