本文是我新开的坑的第一篇文章,这个坑就是vue3,接下来我会围绕着vue3进行一系列的动做,包括但不限于:html
关于源码解析,网站已经上线,vue3源码解析,最佳实践,网站是逐行代码形式的解析,更多关注于源码,而在掘金上分享的文章则相似于总结,会用更复合一篇文章的结构来写。若是你想持续跟进vue3源码,能够打开前面的网站关注我。vue
那么,开始!react
vue3最大的变化莫过于其对于响应式原理的重构,以及其新发布的composition api
,本文聚焦于前者,来深度剖析一下vue3中响应式究竟是怎么实现的。api
咱们以reactive
API 为例,数组
const Comp = {
setup() {
const state = reactive({
a: 'jokcy'
})
return () => {
return <input value={state.a} onChange={(e) => state.a = e.targent.value} /> } } } 复制代码
咱们看上面的例子,这个例子很简单,建立了一个组件,他有一个响应式的数据对象,而后render里面的input
的value绑定的是state.a
以及他的onChange
会修改state.a
。这是很是简单且直观的一个数据绑定的例子,而这个逻辑能实现的根本缘由,是咱们在调用state.a = xxx
的时候,vue会从新渲染咱们return
的render函数,来更新节点函数
而篇文章就是要来看一下,咱们经过reactive
建立的对象,到底有什么魔力,可以帮咱们完成这个任务。网站
其实自己 API 是很简单的,传入一个对象,返回一个 reactive 对象,建立的方法是createReactiveObject
ui
export function reactive(target: object) {
// if trying to observe a readonly proxy, return the readonly version.
if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
return target;
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers
);
}
复制代码
function createReactiveObject( target: Target, isReadonly: boolean, baseHandlers: ProxyHandler<any>, collectionHandlers: ProxyHandler<any> ) {
// 前面都是一些对象是否已经proxy等的判断逻辑,这里就不展现了
const observed = new Proxy(
target,
collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers
);
def(
target,
isReadonly ? ReactiveFlags.READONLY : ReactiveFlags.REACTIVE,
observed
);
return observed;
}
复制代码
那么这里最重要的就是new Proxy
了,能够说理解 vue3 的响应式原理过程就是理解这个proxy
建立的过程,而了解这个过程,主要就是看第二个参数,在这里就是collectionHandlers
或者baseHandlers
,大部分是后者,前者主要针对,Set、Map 等。spa
那么咱们就来看baseHandlers
:代理
export const mutableHandlers: ProxyHandler<object> = {
get,
set,
deleteProperty,
has,
ownKeys,
};
复制代码
可见 vue 代理了这几个操做,那么咱们一个个看这几个操做作了啥:
function get(target: object, key: string | symbol, receiver: object) {
// ...内部key的货足
// 关于数组的一些特殊处理
const targetIsArray = isArray(target);
if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
return Reflect.get(arrayInstrumentations, key, receiver);
}
// 获取请求值
const res = Reflect.get(target, key, receiver);
// ...若是是内部值的获取则直接返回res
if (!isReadonly) {
track(target, TrackOpTypes.GET, key);
}
// 返回的一些处理
return res;
}
复制代码
这里的重点就在于track(target, TrackOpTypes.GET, key);
,这个是咱们正常获取值的时候都会执行到的代码:
export function track(target: object, type: TrackOpTypes, key: unknown) {
if (!shouldTrack || activeEffect === undefined) {
return;
}
let depsMap = targetMap.get(target);
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()));
}
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
if (!dep.has(activeEffect)) {
dep.add(activeEffect);
activeEffect.deps.push(dep);
if (__DEV__ && activeEffect.options.onTrack) {
activeEffect.options.onTrack({
effect: activeEffect,
target,
type,
key,
});
}
}
}
复制代码
好了,重点来了,咱们逐行分析。
第一个if
,就是根据环境变量的shouldTrack
来判断是否要进行跟踪,若是你已经看过个人源码解析中的processComponent
的章节,那么你如今应该就是豁然开朗的感受。由于在执行processComponent
里面的setup
的时候,咱们特意关闭了track
,而那时候就是把shouldTrack
改成了false
。
let depsMap = targetMap.get(target)
这行,targetMap
是一个 map,用来记录全部的响应对象。以后若是目前没有记录该对象,那么就从新记录。
这个 target 对应的也是一个 map,他会对每一个 key 创建一个 set。
最后要记录此次的 effect 了,这里所谓的effect
是什么呢?就是当前正在调用这个对象的函数。在咱们的例子里面,就是return回去的render
函数,这个函数在执行的时候会调用state.a
因此会进入proxy
对于get
的代理,这个时候 proxy 就调用了track
,那么这时候的activeEffect
就是这个 render 方法。这里的effect
就是,当state.a
改动的时候,咱们须要从新执行该 render 方法来进行渲染。
那么他是何时被设置的呢?在mount
章节的时候咱们提到了,在执行render
方法的时候,咱们执行这样一句代码instance.update = effect(function componentEffect()...)
,就是在这里调用的effect
方法里面,把activeEffect
记录为componentEffect
,而这个componentEffect
里面则运行了render
方法。
另外这个getHandler
里面有句代码也挺有意思的:
if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res);
}
复制代码
在获取属性的时候,若是返回的值是对象的时候,才会对其执行reactive
,这也就是延迟处理,并且readonly
的话是不执行reactive
的。
OK,到这里咱们已经知道了在执行render
函数的时候,由于咱们调用了state.a
因此这个函数就至关于依赖state.a
,这在vue3里面被称为effect
。
那么下一篇文章,咱们就来说一讲,这些effect
在state.a
变更的时候是如何被调用的,敬请期待。