前言
上一篇文章对入Moon整体结构进行了分析,基本了解了Moon代码结构组成,接下来的文章会对于Moon中各个部分的细节进行分析与了解,分析的版本是Moon的v0.9.0。
具体分析
首先通过了一个实例来看看Moon的实现效果,具体代码如下:
<div id="app">
<p>{{msg}}</p>
</div>
<script src="./moon.js"></script>
<script>
new Moon({
el: '#app',
data: {
msg: 'hello world'
}
});
</script>
上面是Moon中最基本的用法,接下来就分析下Moon中具体的处理。
Moon构造函数
function Moon(opts) {
// Moon构造函数传入的options,即上例中{el: '#app', data: {msg: 'hello world'}}
this.$opts = opts || {};
var self = this;
// 编号,此时id=0
this.$id = id++;
// 实例名称,上例中不存在name属性,故此为root
this.$name = this.$opts.name || "root";
// data属性
this.$data = this.$opts.data || {};
// render函数,上例中无render,故此为noop
this.$render = this.$opts.render || noop;
// hooks, 就是生命周期函数需写在hooks对象中
this.$hooks = this.$opts.hooks || {};
// methods属性是否存在,存在就会调用initMethods(顾名思义该方法是初始化方法的)
var methods = this.$opts.methods;
if (methods !== undefined) {
initMethods(self, methods);
}
// 新增$events、$dom(虚拟DOM)属性
this.$events = {};
this.$dom = {};
// 观察对象
this.$observer = new Observer(this);
this.$destroyed = true;
this.$queued = false;
// 计算属性computed是否存在,存在就调用initComputed初始化计算属性
var computed = this.$opts.computed;
if (computed !== undefined) {
initComputed(this, computed);
}
// 初始化
this.init();
}
上面实例中不存在methods以及computed,所以主要的处理是observer对象的创建以及init方法的执行
Observer:观察对象构造函数
function Observer(instance) {
// Moon实例,每个Moon实例都会存在一个Observer实例
this.instance = instance;
// 计算属性缓存对象
this.cache = {};
// 计算属性的setter
this.setters = {};
this.clear = {};
this.target = null;
// 依赖map
this.map = {};
}
init():初始化
Moon.prototype.init = function () {
log("======= Moon =======");
// 调用callhook函数
callHook(this, 'init');
// 如果el属性存在,就调用mount函数
if (this.$opts.el !== undefined) {
this.mount(this.$opts.el);
}
};
callHook函数
var callHook = function (instance, name) {
// 查看hook对象中是否存在指定的生命周期方法,存在就调用该方法
var hook = instance.$hooks[name];
if (hook !== undefined) {
hook.call(instance);
}
};
mount
Moon.prototype.mount = function (el) {
// 获取挂载点DOM对象
this.$el = document.querySelector(el);
this.$destroyed = false;
// el不存在报错
if ("development" !== "production" && this.$el === null) {
error("Element " + this.$opts.el + " not found");
}
// 在$el中定义私有属性__moon__代表当前Moon实例
this.$el.__moon__ = this;
// 不存在模板,就会获取$el的标签及其包含的内容
// 上面的实例中不存在template属性,所以此处是<div id="app"><p>{{msg}}</p></div>
this.$template = this.$opts.template || this.$el.outerHTML;
// 如果render是noop函数,表示是默认render函数,就会调用Moon.compile
if (this.$render === noop) {
this.$render = Moon.compile(this.$template);
}
// build函数,就是创建虚拟DOM并
this.build();
// mounted生命周期函数是否存在,存在就调用mounted
callHook(this, 'mounted');
};
Moon.compile
Moon.compile = function (template) {
// 调用compile函数,将template html转换成render函数
return compile(template);
};
build
Moon.prototype.build = function () {
// 调用render函数创建dom节点
var dom = this.render();
var old = null;
// 如果存在实例的$dom不存在,old表示就是挂载点的DOM节点
if (this.$dom.meta !== undefined) {
old = this.$dom;
} else {
old = this.$el;
this.$dom = dom;
}
// 调用patch函数
this.patch(old, dom, this.$el.parentNode);
};
render
Moon.prototype.render = function () {
// 实际上就是调用$render函数,创建虚拟DOM节点
// h是Moon中定义的函数
return this.$render(h);
};
本篇旨在Moon构造函数的过程,此时只要知道render函数的作用即可,就是创建虚拟DOM,针对上面的实例,render函数的结果是:
从上面的结果中可以看到,render函数实际上是将不同类型的node都创建成指定格式的对象,现在只需要知道这个对象的组成属性有,具体后面会专门分析:
- children:子节点对应的virtual dom
- meta:元信息
- props:属性
- type:标签名
- val:值
patch
// 具体该函数的功能是处理虚拟DOM,将改变更新到真实DOM中
Moon.prototype.patch = function (old, vnode, parent) {
};
之后会针对虚拟DOM与真实DOM之间有个详细分析,本次具体去了解。
总结
Moon构造函数创建Moon实例添加的属性主要有:
- $opts:参数对象
- $id:id编号
- $name:实例名称
- $data:数据中心
- $render:render函数,根据html转换成虚拟DOM
- $hooks:生命周期对象
- $dom:虚拟DOM对象
Moon构造函数的处理流程如下:
属性添加–>初始化所有方法—> 观察对象创建—>初始化计算属性---->生命周期函数init处理---->获取el挂载点DOM---->获取template---->根据template构建render函数–>根据render函数创建Virtual DOM---->Virtual DOM与真实DOM之间的处理—>调用mounted生命周期函数
在整个Moon创建的过程中,还有几处不甚明了:
- methods的处理
- computed的处理
- Observer的监听机制
在下篇文章中会对于methods以及computed等相关做具体的分析。