mksz608-Vue3源码解析,打造自己的Vue3框架

第1章 课程导读


第2章 框架设计前瞻 - 框架设计中的一些基本概念

2-1前言

2-2编程范式之命令式编程

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <div>
        <p></p>
    </div>
</div>
<script>
    const divEl = document.querySelector("#app");
    // divEl.innerHTML='hello world'
    const subDivEl = divEl.querySelector('div');
    const pEl = subDivEl.querySelector('p')
    const msg = 'hello world'
    pEl.innerHTML = msg
</script>
</body>

</html>

2-3编程范式之声明式编程

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<template>
    <div>{{msg}}</div>
</template>
</body>
</html>

2-4命令式VS声明式


2-5企业应用的开发与设计原则

2-6为什么说框架的设计过程其实是一个不断取舍的过程

2-7vue中的html是真实的html吗

2-8什么是运行时


2-9什么是编译时

2-10运行时编译时


2-11什么是副作用

2-12Vue3框架设计概述


操作响应式对象,产生副作用,副作用里更新html,html被编译器编译为render函数,render函数在运行时渲染

2-13扩展所谓良好的TypeScript支持是如何提供的

类型约束定义不好,传递错误类型也不报错,就是对ts支持不好

2-14总结

第3章 Vue 3源码结构 - 搭建框架雏形

3-1前言

3-2探索源码设计Vue3源码设计大解析


3-3创建测试实例在Vue源码中运行测试实例

npm i -g pnpm
pnpm i --ignore-scripts
# 使用git clone下来,否则build会报错
npm run build

<!-- package/vue/examples/imooc/reactive.html起个服务来运行 -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../../../dist/vue.global.js"></script>
</head>
<body>
<div id="app"></div>
<script>
    const {reactive, effect} = Vue

    const obj = reactive({
        name: '张三'
    })

    effect(() => {
        document.querySelector('#app').innerText = obj.name
    })

    setTimeout(() => {
        obj.name = '李四'
    }, 2000)
</script>
</body>
</html>

3-4跟踪解析运行行为为vue开启SourceMap


3-5授人以鱼如何针对源码进行debugger

3-6授人以渔如何阅读源码

没进的代码就当不存在

3-7开始搭建自己的框架创建vue-next-mini

npm init -y

3-8为框架进行配置导入ts

tsc -init

tsconfig.json

{
  // 编译器配置
  "compilerOptions": {
    // 根目录
    "rootDir": ".",
    // 严格模式标志
    "strict": true,
    // 指定类型的脚本如何从给定的模块说明符查找文件
    "moduleResolution": "node",
    "esModuleInterop": true,
    // js语言版本
    "target": "ES5",
    // 允许未读取局部变量
    "noUnusedLocals": false,
    // 允许未读取参数
    "noUnusedParameters": false,
    // 允许解析json
    "resolveJsonModule": true,
    // 支持语法迭代
    "downlevelIteration": true,
    // 允许使用隐式的any类型(有助于简化ts复杂度)
    "noImplicitAny": false,
    // 模块化
    "module": "ESNext",
    // 转换为js时,从ts中删除所有注释
    "removeComments": false,
    // 禁用sourceMap
    "sourceMap": false,
    "lib": [
      "ESNext",
      "DOM"
    ]
  },
  // 入口
  "include": [
    "packages/*/src"
  ]
}

3-9引入代码格式化工具prettier让你的代码结构更加规范

.prettierrc

// .prettierrc
{
  // 去掉末尾分号
  "semi": false,
  // 双引号变单引号
  "singleQuote": true,
  // 多少个字符才换行
  "printWidth": 80,
  // 对象最后一个属性不加逗号
  "trailingComma": "none",
  // 省略箭头函数小括号
  "arrowParens": "avoid"
}

3-10模块打包器rollup

{
  "devDependencies": {
    "@rollup/plugin-node-resolve": "13.3.0",
    "@rollup/plugin-commonjs": "22.0.1",
    "@rollup/plugin-typescript": "8.3.4"
  }
}
// rollup.config.js
import typescript from '@rollup/plugin-typescript'
import nodeResolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'

/**
 * 默认导出一个数组, 数组的每个对象都是一个单独的导出文件配置
 */
export default [
    {
        // 入口文件
        input: 'packages/vue/src/index.ts',
        // 打包出口
        output: [
            // 导出 iife 模式的包
            {
                // 开启sourcemap
                sourcemap: true,
                // 导出文件地址
                file: './packages/vue/dist/vue.js',
                // 生成包的格式
                format: 'iife',
                // 变量名
                name: 'Vue'
            }
        ],
        // 插件
        plugins: [
            // ts
            typescript({
                sourceMap: true
            }),
            // 模块导入的路径补全
            nodeResolve(),
            // 转 commonjs 为 esm
            commonjs()
        ]
    }
]

npm i --save-dev tslib@2.4.0 typescript@4.7.4

3-11初见框架雏形配置路径映射

{
  // 编译器配置
  "compilerOptions": {
    // ...
    // 设置快捷导入
    "baseUrl": ".",
    "paths": {
      "@vue/*": [
        "packages/*/src"
      ]
    }
  }
}

3-12总结

第4章 响应系统 - 响应系统的核心设计原则

4-1前言

4-2JS的程序性

4-3如何让你的程序变得更加聪明

4-4vue2的响应性核心APIObjectdefineProperty

4-5ObjectdefineProperty在设计层的缺陷


4-6vue3的响应性核心APIproxy

4-7proxy的最佳拍档Reflect拦截js对象操作


reflect.get的第三个值改变this指向

4-8总结

第5章 响应系统 - 初见 reactivity 模块

5-1前言

5-2源码阅读reactive的响应性跟踪Vue3源码实现逻辑


进入该方法

用服务器或liveserver打开,否则没有createGetter等方法



5-3源码阅读reactive的响应性跟踪Vue3源码实现逻辑

依赖收集建立起map和set的关联

track是依赖收集, trigger是依赖触发
deps存放了set
核心依赖触发

5-4框架实现构建reactive函数获取proxy实例

// packages/vue/src/index.ts
// 入口文件进行导出
export {reactive} from '@vue/reactivity'
// packages/reactivity/src/reactive.ts
export {reactive} from './reactive'
// packages/reactivity/src/reactive.ts
import {mutableHandlers} from './baseHandlers'

// k:原始对象 -- v:响应式对象
export const reactiveMap = new WeakMap<object, any>()

function createReactiveObject(
    target: object,
    baseHandlers: ProxyHandler<any>,
    proxyMap: WeakMap<object, any>) {
    // 从缓存中 根据对象获取对应的代理对象
    const existingProxy = proxyMap.get(target)
    if (existingProxy) {
        return existingProxy
    }

    // 创建代理对象
    const proxy = new Proxy(target, baseHandlers)

    // 缓存代理对象
    proxyMap.set(target, proxy)

    return proxy
}

export function reactive(target: object) {
    // 创建响应式对象
    return createReactiveObject(target, mutableHandlers, reactiveMap)
}
// packages/reactivity/src/baseHandlers.ts
/**
 * interface ProxyHandler<T extends object> {
 *     apply?(target: T, thisArg: any, argArray: any[]): any;
 *     construct?(target: T, argArray: any[], newTarget: Function): object;
 *     defineProperty?(target: T, p: string | symbol, attributes: PropertyDescriptor): boolean;
 *     deleteProperty?(target: T, p: string | symbol): boolean;
 *     get?(target: T, p: string | symbol, receiver: any): any;
 *     getOwnPropertyDescriptor?(target: T, p: string | symbol): PropertyDescriptor | undefined;
 *     getPrototypeOf?(target: T): object | null;
 *     has?(target: T, p: string | symbol): boolean;
 *     isExtensible?(target: T): boolean;
 *     ownKeys?(target: T): ArrayLike<string | symbol>;
 *     preventExtensions?(target: T): boolean;
 *     set?(target: T, p: string | symbol, value: any, receiver: any): boolean;
 *     setPrototypeOf?(target: T, v: object | null): boolean;
 * }
 */
export const mutableHandlers: ProxyHandler<object> = {}
<!--packages/vue/examples/reactivity/reactivity.html-->
<!DOCTYPE html>
<html lang='en'>
<head>
    <meta charset='UTF-8'>
    <title>Title</title>
    <script src='../../dist/vue.js'></script>
</head>
<body>

<script>
    const {reactive} = Vue

    console.log(reactive)

    const obj = reactive({
        name: '张三'
    })

    console.log(obj)
</script>
</body>
</html>

5-5框架实现什么是WeakMap它和Map有什么区别


vue中使用weakmap防止内存占用过多

5-6框架实现createGettercreateSetter

// packages/reactivity/src/baseHandlers.ts
import {track, trigger} from './effect'

function createGetter() {
    return function get(target: object, key: string | symbol, receiver: object) {
        const res = Reflect.get(target, key, receiver)

        // 依赖收集 收集触发更新的fn,到时set的时候调用
        track(target, key)

        return res
    }
}

const get = createGetter()

function createSetter() {
    return function set(
        target: object,
        key: string | symbol,
        value: unknown,
        receiver: object
    ) {
        const res = Reflect.set(target, key, value, receiver)

        // 触发依赖
        trigger(target, key, value)

        return res
    }
}

const set = createSetter()

export const mutableHandlers: ProxyHandler<object> = {
    get,
    set
}
// packages/reactivity/src/effect.ts
/**
 * 收集依赖
 * @param target
 * @param key
 */
export function track(target: object, key: unknown) {
    console.log('track: 收集依赖')
}

/**
 * 依赖触发
 * @param target
 * @param key
 * @param newVal
 */
export function trigger(target: object, key: unknown, newVal: unknown) {
    console.log('trigger: 触发依赖')
}
<!--packages/vue/examples/reactivity/reactivity.html-->
<!DOCTYPE html>
<html lang='en'>
<head>
    <meta charset='UTF-8'>
    <title>Title</title>
    <script src='../../dist/vue.js'></script>
</head>
<body>

<script>
    const {reactive} = Vue

    console.log(reactive)

    const obj = reactive({
        name: '张三'
    })

    console.log(obj)

    // 触发依赖收集
    console.log(obj.name)
    // 依赖触发
    obj.name = '李四'
</script>
</body>
</html>

5-7热更新的开发时提升开发体验

5-8框架实现构建effect函数生成ReactiveEffect实例

// packages/reactivity/src/effect.ts
export class ReactiveEffect<T = any> {
    constructor(public fn: () => T) {

    }

    run() {
        activeEffect = this
        // 触发effect回调
        return this.fn()
    }
}

/**
 * 让传入的函数发生作用
 * @param fn
 */
export function effect<T = any>(fn: () => T) {
    const _effect = new ReactiveEffect(fn)

    // 第一次fn执行
    _effect.run()
}

// 当前触发的effect
export let activeEffect: ReactiveEffect | undefined

// ...
// packages/vue/src/index.ts
// 入口文件进行导出
export {reactive, effect} from '@vue/reactivity'
// packages/reactivity/src/reactive.ts
export {reactive} from './reactive'
export {effect} from './effect'
<!--packages/vue/examples/reactivity/reactivity.html-->
<!DOCTYPE html>
<html lang='en'>
<head>
    <meta charset='UTF-8'>
    <title>Title</title>
    <script src='../../dist/vue.js'></script>
</head>
<body>
<div id='app'></div>
<script>
    const {reactive, effect} = Vue

    // console.log(reactive)

    const obj = reactive({
        name: '张三'
    })

    effect(() => {
        document.querySelector('#app').innerText = obj.name
    })
</script>
</body>
</html>

5-9框架实现tracktrigger


每一个对象的每个属性都有可能有对应的effect函数,所以我们要为每个属性创建effect关联

5-10框架实现构建track依赖收集函数

// packages/reactivity/src/effect.ts
/**
 * 收集依赖
 * @param target
 * @param key
 */
export function track(target: object, key: unknown) {
    // 当前不存在执行函数, return
    if (!activeEffect) return
    // 从targetMap中读取depsMap
    let depsMap = targetMap.get(target)
    // 如果targetMap中没有
    if (!depsMap) {
        // 缓存到targetMap
        targetMap.set(target, (depsMap = new Map()))
    }

    // 绑定 proxy.attribute -- effect_fn
    depsMap.set(key, activeEffect)

    console.log(depsMap)
}

5-11框架实现构建trigger触发依赖

// packages/reactivity/src/effect.ts
// ...

/**
 * 依赖触发
 * @param target
 * @param key
 * @param newVal
 */
export function trigger(target: object, key: unknown, newVal: unknown) {
    // 拿到对象对应的depsMap
    const depsMap = targetMap.get(target)

    if (!depsMap) {
        return
    }

    // 拿到对象属性对应的effect
    const effect = depsMap.get(key) as ReactiveEffect

    if (!effect) {
        return
    }

    // 触发依赖
    effect.fn()
}
<!--packages/vue/examples/reactivity/reactivity.html-->
<!DOCTYPE html>
<html lang='en'>
<head>
    <meta charset='UTF-8'>
    <title>Title</title>
    <script src='../../dist/vue.js'></script>
</head>
<body>
<div id='app'></div>
<script>
    const {reactive, effect} = Vue

    const obj = reactive({
        name: '张三'
    })

    effect(() => {
        document.querySelector('#app').innerText = obj.name
    })

    setTimeout(() => {
        // 因为effect传入fn是更新dom的,所以触发依赖后视图更新了
        obj.name = '李四'
    }, 2000)
</script>
</body>
</html>

5-12总结单一依赖的reactive

5-13功能升级响应数据对应多个effect

只有第二个effect发生改变
因为当前一个key(对象属性)只能有一个effect_fn

5-14框架实现构建Dep模块处理一对多的依赖关系

// packages/reactivity/src/dep.ts
import {ReactiveEffect} from './effect'

export type Dep = Set<ReactiveEffect>

export const createDep = (effects?: ReactiveEffect[]): Dep => {
    const dep = new Set<ReactiveEffect>(effects)

    return dep
}
// packages/reactivity/src/effect.ts
import {createDep, Dep} from './dep'
import {isArray} from '@vue/shared'

// ...

/**
 * 收集依赖
 * @param target
 * @param key
 */
export function track(target: object, key: unknown) {
    // 当前不存在执行函数, return
    if (!activeEffect) return
    // 从targetMap中读取depsMap
    let depsMap = targetMap.get(target)
    // 如果targetMap中没有
    if (!depsMap) {
        // 缓存到targetMap
        targetMap.set(target, (depsMap = new Map()))
    }

    // 尝试获取dep
    let dep = depsMap.get(key)
    if (!dep) {
        // k: obj.attribute -- v: dep<effect>
        depsMap.set(key, (dep = createDep()))
    }

    trackEffects(dep)
}

/**
 * 利用dep依次跟踪指定key的所有effect
 */
export function trackEffects(dep: Dep) {
    dep.add(activeEffect!)
}

/**
 * 依赖触发
 * @param target
 * @param key
 * @param newVal
 */
export function trigger(target: object, key: unknown, newVal: unknown) {
    // 拿到对象对应的depsMap
    const depsMap = targetMap.get(target)

    if (!depsMap) {
        return
    }

    // 拿到对象属性对应的effect
    const dep: Dep | undefined = depsMap.get(key)

    if (!dep) {
        return
    }

    // 触发依赖
    triggerEffects(dep)
}

/**
 * 依次触发dep中保存的依赖
 */
export function triggerEffects(dep: Dep) {
    const effects = isArray(dep) ? dep : [...dep]
    // 依次触发
    for (let effect of effects) {
        triggerEffect(effect)
    }
}

/**
 * 触发指定依赖
 * @param effect
 */
export function triggerEffect(effect: ReactiveEffect) {
    effect.run()
}

5-15reactive函数的局限性

基本数据类型传入我们的reactive报错
解构之后失去响应性
vue3需要使用ref来给基本数据类型实现响应性

5-16总结

第6章 响应系统 - ref 的响应性

6-1前言

6-2源码阅读ref复杂数据类型的响应性


isShallow可以简单看作是否为基本类型
如果是对象,依然还是使用reactive来实现响应性

6-3源码阅读ref复杂数据类型的响应性



此时activeEffect不再有值

6-4框架实现ref函数-构建复杂数据类型的响应性

// packages/reactivity/src/ref.ts
import {createDep, Dep} from './dep'
import {toReactive} from './reactive'
import {activeEffect, track, trackEffects} from './effect'

export interface Ref<T = any> {
    value: T
}

/**
 * 是否为ref
 * @param r
 */
function isRef(r: any): r is Ref {
    // !! 强行转为布尔值
    return !!(r && r.__v_isRef === true)
}

export function trackRefValue(ref) {
    if (activeEffect) {
        trackEffects(ref.dep || (ref.dep = createDep()))
    }
}

class RefImpl<T> {

    private _value: T

    public dep?: Dep = undefined

    public readonly __v__isRef = true

    constructor(value: T, public readonly __v_isShallow: boolean) {
        // 如果是基本类型就不做处理, 不是则转为reactive
        this._value = __v_isShallow ? value : toReactive(value)
    }

    get value() {
        // 依赖收集
        trackRefValue(this)
        return this._value
    }

    set value(newVal) {
    }
}

function createRef(rawValue: unknown, shallow: boolean) {
    if (isRef(rawValue)) {
        return rawValue
    }

    return new RefImpl(rawValue, shallow)
}

export function ref(value?: unknown) {
    return createRef(value, false)
}
// packages/reactivity/src/reactive.ts 
import {isObject} from '@vue/shared'

// ...

/**
 * 如果是对象,就转为reactive
 * @param value
 */
export const toReactive = <T extends unknown>(value: T): T =>
    isObject(value) ? reactive(value as object) : value
// packages/reactivity/src/reactive.ts
export {reactive} from './reactive'
export {effect} from './effect'
export {ref} from './ref'
// packages/shared/src/index.ts
// ...

/**
 * 判断是不是对象
 * @param val
 */
export const isObject = (val: unknown) => val !== null && typeof val === 'object'
// packages/vue/src/index.ts
// 入口文件进行导出
export {reactive, effect, ref} from '@vue/reactivity'
<!--packages/vue/examples/reactivity/ref.html-->
<!DOCTYPE html>
<html lang='en'>
<head>
    <meta charset='UTF-8'>
    <title>Title</title>
    <script src='../../dist/vue.js'></script>
</head>
<body>
<div id='app'>
</div>
<script>
    const {ref, effect} = Vue

    const obj = ref({
        name: '张三'
    })

    effect(() => {
        document.querySelector('#app').innerText = obj.value.name
    })

    setTimeout(() => {
        // 因为effect传入fn是更新dom的,所以触发依赖后视图更新了
        obj.value.name = '李四'
    }, 2000)
</script>
</body>
</html>

6-5总结ref复杂数据类型的响应性

6-6源码阅读ref简单数据类型的响应性



ref是主动触发get和set, 自身不具备响应性

6-7框架实现ref函数-构建简单数据类型的响应性

// packages/shared/src/index.ts
// ...

/**
 * 是否有改变
 */
export const hasChanged = (value: any, oldValue: any): boolean => !Object.is(value, oldValue)
// packages/reactivity/src/ref.ts

// ...

/**
 * 依赖触发
 */
export function triggerRefValue(ref) {
    if (ref.dep) {
        triggerEffects(ref.dep)
    }
}

class RefImpl<T> {

    private _value: T
    private _rawValue: T

    public dep?: Dep = undefined

    public readonly __v__isRef = true

    constructor(value: T, public readonly __v_isShallow: boolean) {
        // 保存当前的value作为初始值
        this._rawValue = value
        // 如果是基本类型就不做处理, 不是则转为reactive
        this._value = __v_isShallow ? value : toReactive(value)
    }

    get value() {
        // 依赖收集
        trackRefValue(this)
        return this._value
    }

    set value(newVal) {
        // 如果值有变化
        if (hasChanged(newVal, this._rawValue)) {
            this._rawValue = newVal
            this._value = toReactive(newVal)
            triggerRefValue(this)
        }
    }
}

6-8总结ref简单数据类型响应性

6-9总结

第7章 响应系统 - watch && computed

7-1开篇

7-2源码阅读computed的响应性跟踪Vue3源码实现逻辑


set是空函数
脏变量
computed的getter会作为reactive的fn
(传了第二个参数创建reactiveEffect),该参数是匿名函数,逻辑: 如果脏变量为假,则触发依赖

7-3源码阅读computed的响应性跟踪Vue3源码实现逻辑

get收集依赖时, 当脏状态为真才会更新为假脏, 然后触发收集
如果不是脏状态,没有触发依赖收集,也就相当于用以前的数据,使用了缓存
reactiveEffect确实有第二个构造参数-调度器
如果有调度器就执行调度器, 如果没有就执行run

7-4框架实现构建ComputedRefImpl读取计算属性的值

// packages/reactivity/src/computed.ts
import {isFunction} from '@vue/shared'
import {Dep} from './dep'
import {ReactiveEffect} from './effect'
import {trackRefValue} from './ref'

export class ComputedRefImpl<T> {
    public dep?: Dep = undefined
    private _value!: T

    public readonly effect: ReactiveEffect<T>

    public readonly __v_isRef = true

    constructor(getter) {
        this.effect = new ReactiveEffect(getter)
        this.effect.computed = this
    }

    get value() {
        trackRefValue(this)
        // 执行getter
        this._value = this.effect.run()
        return this._value
    }
}

export function computed(getterOrOptions) {
    let getter

    const onlyGetter = isFunction(getterOrOptions)

    if (onlyGetter) {
        getter = getterOrOptions
    }

    const cRef = new ComputedRefImpl(getter)

    return cRef
}
// packages/reactivity/src/effect.ts 
export class ReactiveEffect<T = any> {
    computed?: ComputedRefImpl<T>

    constructor(public fn: () => T) {
    }

    run() {
        activeEffect = this
        // 触发effect回调
        return this.fn()
    }
}

// ...
// packages/shared/src/index.ts
// ...

/**
 * 是不是函数
 */
export const isFunction = (val: unknown): val is Function => typeof val === 'function'
// packages/reactivity/src/reactive.ts
export {reactive} from './reactive'
export {effect} from './effect'
export {ref} from './ref'
export {computed} from './computed'
// packages/vue/src/index.ts
// 入口文件进行导出
export {reactive, effect, ref, computed} from '@vue/reactivity'
<!--packages/vue/examples/reactivity/computed.html-->
<!DOCTYPE html>
<html lang='en'>
<head>
    <meta charset='UTF-8'>
    <title>Title</title>
    <script src='../../dist/vue.js'></script>
</head>
<body>
<div id='app'></div>
<script>
    const {reactive, effect, computed} = Vue

    const obj = reactive({
        name: '张三'
    })

    const computedObj = computed(() => {
        return '姓名: ' + obj.name
    })

    effect(() => {
        document.querySelector('#app').innerText = computedObj.value
    })

    setTimeout(() => {
        obj.name = '李四'
    }, 2000)
</script>
</body>
</html>

7-5框架实现computed的响应性初见调度器处理脏的状态

// packages/reactivity/src/computed.ts
import {isFunction} from '@vue/shared'
import {Dep} from './dep'
import {ReactiveEffect} from './effect'
import {trackRefValue, triggerRefValue} from './ref'

export class ComputedRefImpl<T> {
    public dep?: Dep = undefined
    private _value!: T

    public readonly effect: ReactiveEffect<T>

    public readonly __v_isRef = true

    // 脏状态
    // 为true需要重新执行effect.run
    public _dirty = true

    constructor(getter) {
        this.effect = new ReactiveEffect(getter, () => {
            if (!this._dirty) {
                this._dirty = true
                triggerRefValue(this)
            }
        })
        this.effect.computed = this
    }

    get value() {
        trackRefValue(this)
        if (this._dirty) {
            // 执行getter
            this._dirty = false
            this._value = this.effect.run()
        }
        return this._value
    }
}

// ...
// packages/reactivity/src/effect.ts
// ...

export class ReactiveEffect<T = any> {
    computed?: ComputedRefImpl<T>

    constructor(
        public fn: () => T,
        public scheduler: EffectScheduler | null = null) {
    }
}

export function triggerEffect(effect: ReactiveEffect) {
    if (effect.scheduler) {
        effect.scheduler()
    } else {
        effect.run()
    }
}

7-6框架实现computed的缓存性

vue3的计算属性是有缓存的,多次执行只会触发一次


多次执行计算属性的依赖收集,导致死循环

// packages/reactivity/src/effect.ts
// ...

export function triggerEffects(dep: Dep) {
    const effects = isArray(dep) ? dep : [...dep]
    // 依次触发
    for (let effect of effects) {
        if (effect.computed) {
            triggerEffect(effect)
        }
    }

    for (const effect of effects) {
        if (!effect.computed) {
            triggerEffect(effect)
        }
    }
}


第一次执行getter,将computed设置为false,后面再getter不会触发依赖

7-7总结computed计算属性

7-8源码阅读响应性的数据监听器watch跟踪源码实现逻辑




immediate是默认自动执行一次

7-9源码阅读响应性的数据监听器watch跟踪源码实现逻辑



7-10框架实现深入scheduler调度系统实现机制


 上一篇
mksz330 - 下一代前端开发语言 TypeScript从零重构axios mksz330 - 下一代前端开发语言 TypeScript从零重构axios
第1章 课程介绍 第2章 初识 Typescript【初次体验】02-01 安装 TypeScriptnpm i -g typescript 02-02 编写第一个 TypeScript 程序tsc xxx.ts node xxx.js
2023-10-08
下一篇 
拉勾大前端高薪训练营 拉勾大前端高薪训练营
01.Part 1 · JavaScript 深度剖析02.Part 2 · 前端工程化实战02.模块二 模块化开发与规范化标准05.任务五:webpack源码01.内容概述 02.打包后文件分析 key: ‘src/inedx.js’,
2023-10-06
  目录