脚本宝典收集整理的这篇文章主要介绍了vue - 响应式原理梳理(二),脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
vue实例 初始化 完成以后,接下来就要进行 挂载。
vue实例挂载,即为将vue实例对应的 template模板,渲染成 Dom节点。
通过原型方法 $mount方法 来挂载vue实例。
// 挂载Vue实例
Vue$3.prototype.$mount = function(el, hydrating) {
// el为dom元素对应的选择器表达式,根据选择器表达式,获取dom元素
el = el && query(el);
...
// this->Vue实例,或者是组件实例对象
var options = this.$options;
// 解析模板,将模板转换为render渲染函数
if(!options.render) {
// 一般是组件实例的构造函数的options中会直接有template这个属性
var template = options.template;
if(template) {
if(typeof template === 'string') {
if(template.charAt(0) === '#') {
template = idToTemplate(template);
/* istanbul ignore if */
if("development" !== 'production' && !template) {
warn(
("Template element not found or is empty: " + (options.template)),
this
);
}
}
} else if(template.nodeType) {
template = template.innerHTML;
} else {
{
warn('invalid template option:' + template, this);
}
return this
}
} else if(el) {
// 获取dom节点的outerHTML
template = getOuterHTML(el);
}
// template,模板字符串
if(template) {
/* istanbul ignore if */
if("development" !== 'production' && config.performance && mark) {
mark('compile');
}
// 将html模板字符串编译为渲染函数
var ref = compileToFunctions(template, {
// 换行符
shouldDecodeNewlines : shouldDecodeNewlines,
// 分割符
delimiters : options.delimiters,
// 注释
comments : options.comments
}, this);
// 获取渲染函数
var render = ref.render;
// 获取静态渲染函数
var staticRenderFns = ref.staticRenderFns;
// 将渲染函数添加到Vue实例对象的配置项options中
options.render = render;
// 将静态渲染函数添加到Vue实例对象的配置项options中
options.staticRenderFns = staticRenderFns;
/* istanbul ignore if */
if("development" !== 'production' && config.performance && mark) {
mark('compile end');
measure(((this._name) + " compile"), 'compile', 'compile end');
}
}
}
return mountComponent.call(this, el, hydrating)
};
// 挂载vue实例
function mountComponent(vm, el, hydrating) {
vm.$el = el;
...
updateComponent = function() {
vm._update(vm._render(), hydrating);
};
// 给Vue实例或者是组件实例创建一个监听器, 监听updateComponent方法
vm._watcher = new Watcher(vm, updateComponent, noop);
...
return vm;
}
watcher 对象在构建过程中,会作为观察者模式中的 Observer,会被添加到 响应式属性的dep对象的依赖列表 中。
当响应式属性发生变化时,会通知依赖列表中的watcher对象进行更新。
此时,watcher 对象执行 updateComponent 方法,重新渲染 dom节点。
在 挂载vue实例 时, 会为 vue实例 构建一个 监听者watcher。
// vm => 当前监听者对应的vue实例
// expOfFn => 需要监听的表达式
// cb => 监听回调方法
// options => 选项,比如deep、immediate
// vm.$watch('message', function(newValue) {...})
var Watcher = function Watcher(vm, expOrFn, cb, options) {
this.vm = vm;
vm._watchers.push(this);
...
// 监听器订阅的Dep对象实例
this.deps = [];
this.newDeps = [];
// 存储监听器已经订阅的Dep对象实例的id,防止重复订阅。
// 每一个Dep对象实例都有一个ID
this.depIds = new _Set();
this.newDepIds = new _Set();
// 监听器监听的表达式
this.expression = expOrFn.toString();
// 初始化getter,getter用于收集依赖关系
if(typeof expOrFn === 'function') {
// expOfFn 为 函数
// 对应:给vue实例建立watcher
this.getter = expOrFn;
} else {
// epOfFn 为 字符串
// 对应:在vue实例中建立监听
// 比如 vm.$watch('message', function() {...})
this.getter = parsePath(expOrFn);
}
this.value = this.lazy
? undefined
: this.get();
};
// 执行watcher的getter方法,用于收集响应式属性dep对象 和 watcher的依赖关系
// 在vue实例挂载过程中, getter = updateComponent
Watcher.prototype.get = function get() {
// 将Dep.target设置为当前Watcher对象实例
pushTarget(this);
var value;
var vm = this.vm;
...
value = this.getter.call(vm, vm);
...
return value
};
// watcher 更新
Watcher.prototype.update = function update() {
...
this.get()
...
};
// 将watcher添加到dep属性的依赖列表中
Watcher.prototype.addDep = function addDep(dep) {
var id = dep.id;
if(!this.newDepIds.has(id)) {
this.newDepIds.add(id);
this.newDeps.push(dep);
if(!this.depIds.has(id)) {
dep.addSub(this);
}
}
};
在挂载vue实例时,watcher对象会在构建过程中会执行 updateComponent 方法。
执行 updateComponent 方法分为两个过程:
执行 render 方法时,会触发响应式属性的 getter 方法,将 watcher 添加到 dep对象的依赖列表中。
render 方法是 vue实例 在挂载时由 template 编译成的一个 渲染函数。
@H_304_409@tempalte:
<div id="app">
{{message}}
</div>
render:
// 执行render, 需要读取响应式属性message,触发message的getter方法
(function anonymous() {
with(this){return _c('div',{attrs:{"id":"app"}},[_v(_s(message))])}
})
// _s, 将this.message转化为字符串
// _v, 生成文本节点对应的VNode
// _c, 生成'div'元素节点对应的Vnode
render 函数返回 VNode节点 , 用于 渲染Dom节点。
在执行过程中,如果需要读取 响应式属性,则会触发 响应式属性 的 getter。
在 getter 方法中, 将 watcher 对象添加到 响应式属性dep对象的依赖列表 中。
修改 响应式属性时,会触发响应式属性的 setter 方法。此时,响应式属性的 dep对象执行 notify 方法,遍历自己的 依赖列表subs, 逐个通知subs中的 watcher 去更新。
watcher 对象执行 update 方法,重新调用 render 方法生成 Vnode节点树,然后 对比新旧Vnode节点树 的不同,更新dom树。
响应式属性 的原理:
以上是脚本宝典为你收集整理的vue - 响应式原理梳理(二)全部内容,希望文章能够帮你解决vue - 响应式原理梳理(二)所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。