vue源码的马拉松跑完了,可以放松一下写点小东西,其实源码讲20节都讲不完,跳了好多地方。

  本人技术有限,无法跟大神一样,模拟vue手把手搭建一个MVVM框架,然后再分析原理,只能以门外汉的姿态简单过一下~

  想到什么写什么了,这节就简单说说钩子函数吧!

  vue中的钩子函数主要包含初始化的beforeCreated/created,Virtual Dom更新期间的beforeUpdate/updated,页面渲染期间的beforeMount/mounted,组件销毁期间的beforeDestroy/destroyed等等。

  vue框架会在合适的时间点调用对应的钩子函数,调用过程其实很简单,看一下源码的函数就明白了:

    // vm为当前vue实例
// hook为钩子函数名称
function callHook(vm, hook) {
// 获取对相应的钩子函数内容
var handlers = vm.$options[hook];
if (handlers) {
for (var i = 0, j = handlers.length; i < j; i++) {
try {
// 直接调用
handlers[i].call(vm);
} catch (e) {
handleError(e, vm, (hook + " hook"));
}
}
}
// 钩子函数事件emit
if (vm._hasHookEvent) {
vm.$emit('hook:' + hook);
}
}

  其中vm.$option中包含了vm中所有的钩子函数已经其他一些配置,根据对应的hook字符串取出来然后用call执行。

  可以从一个简单的例子跑一遍:

    <body>
<div id='app'>
{{message}}
</div>
</body>
<script src='./vue.js'></script>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
},
beforeCreate: function() {
console.log('beforeCreate');
}
});
</script>

  样本案例还是之前跑源码的,只不过在这里额外加了一个钩子函数,随便选了一个beforeCreate。

  跑源码我写过,对象会以options对象的形式与baseOptions进行合并处理,如下:

    Vue.prototype._init = function() {
// code... vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
); // code...
}

  其options就是new Vue传进来的那个对象,mergeOptions如下:

    function mergeOptions(parent, child, vm) {
// code...
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key);
}
} function mergeField(key) {
var strat = strats[key] || defaultStrat;
options[key] = strat(parent[key], child[key], vm, key);
}
return options
}

  其余的属性都跳过,目前主关注钩子函数的处理。函数内部会遍历child(即传进来的对象),然后根据键名取对应strats对象的函数对值进行处理。

  可以简略看一下strats对象:,基本上包含所有可能的键,无论是钩子函数、值、计算属性、方法等,所有的参数会被处理。

  而beforeCreate参数被分类为钩子函数,会调用mergeHook函数,源码如下:

    // parentVal => undefined
// childVal => function(){...}
function mergeHook(parentVal, childVal) {
return childVal ?
parentVal ?
parentVal.concat(childVal) :
Array.isArray(childVal) ?
childVal : [childVal] :
parentVal
}

  这个函数同时也处理props属性,所以接受两个参数。如果是钩子函数,则parentVal为undefined,这个超长三元表达式稍微解说一下:

  childVal有没有?有啊,好,那parentVal呢?没,那childVal是不是数组啊?不是,是个函数。好,包起来,弄成数组返回吧!

  于是,最终钩子函数被包装了这样:

  

  现在,回到最开始那个callHook函数,就能跑得通了。handlers就是这个数组,for循环遍历,取出函数,调用call方法执行钩子函数代码,看情况emit一下,返回。

  

  钩子函数的作用我就不解释了,这个网上随便查都有,我就简单说说为什么要在created中调用ajax初始化数据,一起来看看源码:

    function initMixin(Vue) {
// code... initLifecycle(vm);
initEvents(vm);
initRender(vm);
callHook(vm, 'beforeCreate');
initInjections(vm); // resolve injections before data/props
initState(vm);
initProvide(vm); // resolve provide after data/props
callHook(vm, 'created'); // code...
}

  在beforeCreate阶段,vue仅仅完成了参数合并,以及在vm上添加了大量空属性,并没有实际开始初始化操作,这时候如果调用ajax,并调用this.data你会发现:

  对的,压根就没有这个属性。

  但是,在之后的initState初始化中(有兴趣可以回头看下我的流水账呀),会将data属性解析并添加到vm上,并且已经有对应的响应式方法了。

  所以说,这个时候页面还没有渲染,非常适合进行数据初始化。

  

  背不来网上的生命周期,自己画个图:

  

  

  

Vue源码后记-钩子函数的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

    写起来感觉都是老三套,AST => render => VNode => patch 之前是把AST弄完了,对事件和过滤器处理如图: render函数也只看这两部分的转换吧! 首先是 ...

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

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

  9. 大白话Vue源码系列(03):生成render函数

    阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...

随机推荐

  1. java-枚举一些字典信息的例子

    一个典型的枚举应用的例子 package opstools.vtm.dictionary.enums; import opstools.framework.view.ResourceValue; /* ...

  2. [转]Xcode的快捷键及代码格式化

    Xcode比较常用的快捷键,特别是红色标注的,很常用.1. 文件CMD + N: 新文件CMD + SHIFT + N: 新项目CMD + O: 打开CMD + S: 保存CMD+OPt+S:保存所有 ...

  3. 再起航,我的学习笔记之JavaScript设计模式25(迭代器模式)

    迭代器模式 概念介绍 迭代器模式(Iterator): 在不暴露对象内部结构的同时,可以顺序地访问聚合对象内部的元素. 迭代器 程序中的循环是一种利器,循环语句也使我们程序开发更简洁高效,但是有时一遍 ...

  4. 前后端分离,接口API,契约

    前后端分离了,然后呢? http://icodeit.org/2015/06/whats-next-after-separate-frontend-and-backend/ Swagger - 前后端 ...

  5. 动易CMS-搜索结果页显示自定义字段

    最终的页面: 步骤: 1.搜索标签代码 <input id="keyword" type="text" class="text" on ...

  6. mysql技能提升篇 - Sqlyog高级应用

    mysql作为绝大部分公司使用的数据库,自然是牛牛牛! 每个人都能设计数据库,都能从删库到跑路.但是,如何做到更好,更快,更准地建立你的mysql数据库,这是个值得关注的问题(尽管很多人已经去搞大数据 ...

  7. Redis学习——Redis持久化之RDB备份方式保存数据

    从这一个介绍里面知道,redis比memcache作为缓存数据库强大的地方,一个是支持的数据类型比较多,另一个就是redis持久化功能. 下面就介绍Redis的持久化之RDB! 一:什么是redis的 ...

  8. mybatis typehandler

    建立TypeHandler 我们知道java有java的数据类型,数据库有数据库的数据类型,那么我们在往数据库中插入数据的时候是如何把java类型当做数据库类型插入数据库,在从数据库读取数据的时候又是 ...

  9. 基于MyBatis3.0.6的基本操作介绍

    每 一 个 MyBatis 的 应 用 程 序 都 以 一 个 SqlSessionFactory 对 象 的 实 例 为 核 心 .SqlSessionFactory本身是由SqlSessionFa ...

  10. 『诡异的』VL10B创建外向交货单出错解决全过程

    一直觉得SAP STO的业务模式配置起来还是挺简单的,无非就是关联一下采购单与交货单的关系,以及相应工厂的装运数据,其他像主数据的设置也没有什么特别的.相比ICS模式,它少了IDOC的配置,所以还是很 ...