vueJs 源码解析 (三) 具体代码
vueJs 源码解析 (三) 具体代码
在之前的文章中提到了 vuejs 源码中的 架构部分,以及 谈论到了 vue 源码三要素 vm、compiler、watcher 这三要素,那么今天我们就从这三要素逐步了解清楚
好了,话不多说, let's do it
在这之前,我们需要 对上文中讲到的 vuejs 的源码是 flow 写法的问题进行一个简化。 毕竟还有有工具是可以解决的。
可以用babel-plugin-transform-flow-strip-types去转化下即可。
1、 npm install --save-dev babel-plugin-transform-flow-strip-types
2、 .babelrc 文件中
{
"plugins": ["transform-flow-strip-types"]
}
具体转换方法见 github地址
一、 instance 实例化入口 核心代码
/src/core/instance/index.js
import { initMixin } from './init' // 实例化 混合
import { stateMixin } from './state' // 各类数据应用 混合
import { renderMixin } from './render' // render 函数等 混合
import { eventsMixin } from './events' // 例如 父子组件的 emit on 事件
import { lifecycleMixin } from './lifecycle' // 这个暂时比较模糊,后面的文章更新
import { warn } from '../util/index' // warn 报错工具 在控制台经常会看到的 vue 的warn
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue) // 这里就是判断当前 this 的 prototype 是否是 Vue
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
一、 instance 实例化入口 核心代码 之 init.js
核心代码区块一:
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options);
} else {
vm.$options = mergeOptions(resolveConstructorOptions(vm.constructor),
options || {}, vm);
}
解析:
判断传入的值当中是否有组件,如果有则先实例化组件。
核心代码区块二:
vm._self = vm;
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');
解析:
看字面上的意思是分别一次 实例化了 生命周期、事件、渲染函数 、回调钩子 ‘beforeCreate’、
依赖注入、状态、 提供 、回调钩子 ‘created’、
对,看到这里我们还是很奇怪这些东西是干嘛的?
那么下面我们继续依次往下看:
一、实例化生命周期 initLifecycle
vm.$parent = parent;
vm.$root = parent ? parent.$root : vm;
vm.$children = [];
vm.$refs = {};
vm._watcher = null;
vm._inactive = null;
vm._directInactive = false;
vm._isMounted = false;
vm._isDestroyed = false;
vm._isBeingDestroyed = false;
看上去是增加了一系列的 属性。但是还是不知道这个有什么用。不着急,我们继续往下看。
二、实例化生命周期 initEvents
export function initEvents(vm) {
vm._events = Object.create(null);
vm._hasHookEvent = false;
// init parent attached events
const listeners = vm.$options._parentListeners;
if (listeners) {
updateComponentListeners(vm, listeners);
}
}
再走到这步的时候就会发现, vm.$options 这个对象 频频出现在我们的视野中,如果每次都不能理解的话。就会遇到 理解障碍。 那么 我们就需要 做一个 最简单的测试用例。 (实际的把 vue 跑起来)
三、new Vue({ options }) Vue 最简单的测试用例
我们在created 钩子下打印 this 对象 部分结果如下
console.log(this)
// console.log(this)
_events: {}
_hasHookEvent :false
_inactive :null
_isBeingDestroyed :false
_isDestroyed :false
_isMounted: true
_isVue: true
这里我们就能够看到比较清晰的一些属性了 在 init.js 中的第一步 initLifecycle.js 中定义的
// console.log(this)
_events: {} // 事件对象集合
_hasHookEvent :false // 是否有钩子事件
_inactive :null // 未知
_isBeingDestroyed :false // 是否要销毁
_isDestroyed :false // 是否已经销毁
_isMounted: true // 是否已挂载
_isVue: true // 是否是 Vue 对象
这里我们就再回到 initLifecycle.js 源码 中去看
const options = vm.$options;
let parent = options.parent;
if (parent && !options.abstract) {
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent;
}
parent.$children.push(vm);
}
vm.$parent = parent;
vm.$root = parent ? parent.$root : vm;
vm.$children = []; // 这里定义的 children 数字对象未知是什么意思
vm.$refs = {}; // 这里定义的 refs 对象依然未知
走到这一步 大概对 vm.$options 这个对象 有个初步的了解,但是还有部分依然是未知。好了,我们继续往下走。
四、 我回到 第二步 实例化生命周期 initEvents
export function initEvents(vm) {
vm._events = Object.create(null);
vm._hasHookEvent = false;
// init parent attached events
const listeners = vm.$options._parentListeners;
if (listeners) {
updateComponentListeners(vm, listeners);
}
}
给 vm 对象 新增了一个 _events 对象 , 并且会去判断 vm.$options 是否有父级的事件监听。
propsData : undefined
_componentTag : "App"
_parentElm : null
_parentListeners : undefined
_parentVnode : VNode {tag: "vue-component-1-Apps-test", data: {…}, children: undefined, text: undefined, elm: div.test11, …}
_refElm : null
_renderChildren : undefined
_parentListeners 值为空。 这里我们做一个大胆的假设: 是否有组件的引用的时候这个值就会发生改变。 那么下面我们简单的测试下。新增一个基础组件.
// 验证失败:这个值依然还是为 空
// 但是弄清楚了一个问题:
this.$root // 为根对象
this.$root.$parent // 根对象的 父属性一定是为空
this.$root.$children // 根对象的 子代属性一定是 一个数组。
// 如果你引用的 组件
this.$root.$children[0] // 为第一个组件
this.$root.$children[1] // 为第二个组件
...
// 以此类推
好,我们接着往下看
五、 initRender(vm)
第五步: 比较核心的 渲染功能。
今天先到这里... 先下个班,回家继续整理
vueJs 源码解析 (三) 具体代码的更多相关文章
- ReactiveCocoa源码解析(三) Signal代码的基本实现
上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见<ReactiveSwift源码解析之Bag容器>.本篇博客我们就来聊一下信号量,也就是Signal的的几种状 ...
- ReactiveSwift源码解析(三) Signal代码的基本实现
上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见<ReactiveSwift源码解析之Bag容器>.本篇博客我们就来聊一下信号量,也就是Signal的的几种状 ...
- VueJs 源码解析 (四) initRender.Js
vueJs 源码解析 (四) initRender.Js 在之前的文章中提到了 vuejs 源码中的 架构部分,以及 谈论到了 vue 源码三要素 vm.compiler.watcher 这三要素,那 ...
- Celery 源码解析三: Task 对象的实现
Task 的实现在 Celery 中你会发现有两处,一处位于 celery/app/task.py,这是第一个:第二个位于 celery/task/base.py 中,这是第二个.他们之间是有关系的, ...
- Mybatis源码解析(三) —— Mapper代理类的生成
Mybatis源码解析(三) -- Mapper代理类的生成 在本系列第一篇文章已经讲述过在Mybatis-Spring项目中,是通过 MapperFactoryBean 的 getObject( ...
- React的React.createRef()/forwardRef()源码解析(三)
1.refs三种使用用法 1.字符串 1.1 dom节点上使用 获取真实的dom节点 //使用步骤: 1. <input ref="stringRef" /> 2. t ...
- Spring源码解析 - springMVC核心代码
一.首先来讲解下springMVC的底层工作流程 1.首先我们重点放在前端控制器(DispatcherServlet) 其类图: 因为从流程图看,用户的请求最先到达就是DispatcherServle ...
- Vue源码解析-调试环境-代码目录和运行构建
目录 前言 1 代码结构 1.1 octotree插件 1.2 vue工程项目目录 1.3 主要代码目录src compiler core platforms server sfc shared 2 ...
- Spring源码解析三:IOC容器的依赖注入
一般情况下,依赖注入的过程是发生在用户第一次向容器索要Bean是触发的,而触发依赖注入的地方就是BeanFactory的getBean方法. 这里以DefaultListableBeanFactory ...
随机推荐
- eclipse 中启动Tomcat超时了错误
修改E:\eclipse\eclipse\workspace\.metadata\.plugins\org.eclipse.wst.server.core\servers.xml 将 start-ti ...
- MongoDB 搭建文件存储的方案
用云的话,节省你开发成本,快速上线,数据比较安全.缺点是一旦用了他们的,形成习惯以后,数据想迁移就会比较麻烦,你会越来越依赖,而且规模上去以后价格并不低.早年自己做的话,你需要实现分布式文件系统,这个 ...
- 记录一次网站漏洞修复过程(三):第二轮处理(拦截SQL注入、跨站脚本攻击XSS)
在程序编写的时候采用参数化的SQL语句可以有效的防止SQL注入,但是当程序一旦成型,再去修改大量的数据库执行语句并不是太现实,对网页表单上输入进行校验是易于实现的方法.在webForm 页面中开启校验 ...
- C# Redis实战(四)
四.写入数据 在C# Redis实战(三)中我们已经配置好了web.config程序,并且能通过C#代码来读取和管理以上配置信息. 接下来,就可以进行Redis的数据写入了.Redis中可以用Stor ...
- Oracle GoldenGate实现数据库同步
前言:最近刚好在弄数据库同步,网上查了些资料再加上自己整理了一些,做个分享! 一.GoldenGate的安装 1.安装包准备 数据库版本:Oracle Database 11g Release 2(1 ...
- EasyUI Parser 解析器
Parser(解析器)应用场景 1,自动调用parser 只要我们书写相应的class,easyui就能成功的渲染页面,这是因为解析器在默认情况下,会在dom加载完成的时候($(docunment). ...
- Excel中choose函数的使用方法
你还在为Excel中choose函数的使用方法而苦恼吗,今天小编教你Excel中choose函数的使用方法,让你告别Excel中choose函数的使用方法的烦恼. 经验主要从四方面对Excel函数进行 ...
- PHP之取得当前时间函数方法
PHP之取得当前时间函数方法 PHP之取得当前时间函数方法文章提供了php的几种获取当前时间的函数,date,time等,同时告诉我如何解决时区问题.php教程取得当前时间函数文章提供了php的几种获 ...
- java项目和java-web项目中文件和文件夹的含义
1. java项目 .project:是工程构建配置文件 .classpath:保存的是项目所用的外部引用包的路径 .settings:记录项目配置变化的记录文件夹 src:sourcefolder项 ...
- java编程基础知识及常见例题
⒈标识符: 只能包含数字.字母.下划线.$,并且不能以数字开头.语义直观规范 驼峰法则: 如:方法名.变量名采用驼峰法则 帕斯卡命名法: 如: 类.接口.枚举采用帕斯卡命名法包名:网址倒写,com.网 ...