# 学习理解Vue3

# createApp

createApp的源码

const createApp = ((...args) => {
    const app = ensureRenderer().createApp(...args);
    if ((process.env.NODE_ENV !== 'production')) {
        injectNativeTagCheck(app);
        injectCompilerOptionsCheck(app);
    }
    const { mount } = app;
    app.mount = (containerOrSelector) => {
        const container = normalizeContainer(containerOrSelector);
        if (!container)
            return;
        const component = app._component;
        if (!isFunction(component) && !component.render && !component.template) {
            // __UNSAFE__
            // Reason: potential execution of JS expressions in in-DOM template.
            // The user must make sure the in-DOM template is trusted. If it's
            // rendered by the server, the template should not contain any user data.
            component.template = container.innerHTML;
        }
        // clear content before mounting
        container.innerHTML = '';
        const proxy = mount(container, false, container instanceof SVGElement);
        if (container instanceof Element) {
            container.removeAttribute('v-cloak');
            container.setAttribute('data-v-app', '');
        }
        return proxy;
    };
    return app;
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

createApp接收一个组件作为参数,然后调用ensureRenderer方法

function ensureRenderer() {
    return (renderer ||
        (renderer = createRenderer(rendererOptions)));
}
function createRenderer(options) {
  return baseCreateRenderer(options);
}
1
2
3
4
5
6
7

renderer是渲染器,ensureRenderer的作用是确保渲染器存在,如不存在则创建一个渲染器。

rendererOptions是渲染器的一些配置

const rendererOptions = extend({ patchProp }, nodeOps);
const nodeOps = {
    insert: (child, parent, anchor) => {
        parent.insertBefore(child, anchor || null);
    },
    remove: child => {
        const parent = child.parentNode;
        if (parent) {
            parent.removeChild(child);
        }
    },
    createElement: (tag, isSVG, is, props) => {
        const el = isSVG
            ? doc.createElementNS(svgNS, tag)
            : doc.createElement(tag, is ? { is } : undefined);
        if (tag === 'select' && props && props.multiple != null) {
            el.setAttribute('multiple', props.multiple);
        }
        return el;
    },
    createText: text => doc.createTextNode(text),
  // ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

baseCreateRenderer

function baseCreateRenderer(options, createHydrationFns) {
  //...
  return {
    render,
    hydrate,
    createApp: createAppAPI(render, hydrate)
  };
}
1
2
3
4
5
6
7
8

baseCreateRenderer 函数返回的对象中,包含render渲染函数、hydrate用于服务端渲染的注水函数、createApp函数。

# app.use

Vue 核心库packages/runtime-core/src/apiCreateApp.ts

// Vue 3 源码 - apiCreateApp.ts (简化版)
import { App, Plugin } from './apiApp' // 导入 App 类型和 Plugin 类型

export function createApp(...args) {
  const installedPlugins = /* @__PURE__ */ new WeakSet(); // 存储已安装插件的集合
  // 创建 Vue 应用实例
  const app = {
    use(plugin: Plugin, ...options: any[]) {
      // 检查插件是否已经安装过,防止重复安装
      if (installedPlugins.has(plugin)) {
        warn$1(`Plugin has already been applied to target app.`);
      }
      // 如果插件是一个对象,调用它的 install 方法
      else if (plugin && typeof plugin.install === 'function') {
        installedPlugins.add(plugin);
        plugin.install(app, ...options);
      }
      else if (typeof plugin === 'function') {
        // 如果插件是一个函数,直接调用,并传递应用实例和其他参数
        installedPlugins.add(plugin);
        plugin(app, ...options)
      } else if (true) {
      warn$1(`A plugin must either be a function or an object with an "install" function.`);
      }
      // 将插件添加到已安装集合中,防止重复安装
      this._installedPlugins.add(plugin)
      return app
    }
  }
  return app
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# Composition API

# 被 ref() 包装 的变量

const readersNumber = ref(100);
console.log(readersNumber); // 
1
2

打印结果:

image-20230221155644672

# reactive源码

# 被reactive() 包装的变量

const obj = reactive({
  name: 'zs',
  age: 18
})
1
2
3
4

打印结果:

image-20210422110445665
setup(prop,context){
	console.log(prop)
  console.log(context)
}
1
2
3
4

打印结果:

image-20210420114211526

context:

image-20210420114335981
function reactive(target) {
  if (isReadonly(target)) {
    return target;
  }
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap
  );
}
1
2
3
4
5
6
7
8
9
10
11
12

我们来看createReactiveObject

  • target: 要创建代理的目标对象。

  • isReadonly2: 一个标志,表示是否是只读的代理。

  • baseHandlers: 基本操作的处理器对象。

  • collectionHandlers: 集合类型操作的处理器对象。

  • proxyMap: 用于缓存已创建的代理对象的 Map。

function createReactiveObject(target, isReadonly2, baseHandlers, collectionHandlers, proxyMap) {
  // 首先,检查 target 是否是一个对象,如果不是对象,则直接返回 target
  if (!isObject(target)) {
    {
      console.warn(`value cannot be made reactive: ${String(target)}`);
    }
    return target;
  }
  // 检查 target 是否已经有 __v_raw 属性,并且是否是只读的响应式对象。如果是,则直接返回 target
  if (target["__v_raw"] && !(isReadonly2 && target["__v_isReactive"])) {
    return target;
  }
  // 检查 proxyMap 中是否已经存在 target 对应的代理对象,如果存在,则直接返回缓存的代理对象
  const existingProxy = proxyMap.get(target);
  if (existingProxy) {
    return existingProxy;
  }
  // 获取 target 的类型,使用 getTargetType 函数
  // 返回一个枚举值。targetType 的类型可能是 0(INVALID,无效)、1(COMMON,普通对象)、2(COLLECTION,集合类型)。
  const targetType = getTargetType(target);
  if (targetType === 0 /* INVALID */) {
    return target;
  }
  const proxy = new Proxy(
    target,
    targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers
  );
  proxyMap.set(target, proxy);
  return proxy;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

这里的baseHandlers和collectionHandlers对应上面的 mutableHandlers 和 mutableCollectionHandlers

const mutableHandlers = {
  get: get$1,
  set: set$1,
  deleteProperty,
  has: has$1,
  ownKeys
};
const get$1 = /* @__PURE__ */ createGetter();
const set$1 = /* @__PURE__ */ createSetter();

const mutableCollectionHandlers = {
  get: /* @__PURE__ */ createInstrumentationGetter(false, false)
};
function createInstrumentationGetter(isReadonly, shallow) {
  const instrumentations = shallow ? isReadonly ? shallowReadonlyInstrumentations : shallowInstrumentations : isReadonly ? readonlyInstrumentations : mutableInstrumentations;
  return (target, key, receiver) => {
    if (key === "__v_isReactive") {
      return !isReadonly;
    } else if (key === "__v_isReadonly") {
      return isReadonly;
    } else if (key === "__v_raw") {
      return target;
    }
    return Reflect.get(
      hasOwn(instrumentations, key) && key in target ? instrumentations : target,
      key,
      receiver
    );
  };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

再看createGetter方法

function createGetter(isReadonly2 = false, shallow = false) {
  return function get2(target, key, receiver) {
    // 首先,对一些特殊属性进行处理,__v_isReactive 用于判断对象是否是reactive对象
    if (key === "__v_isReactive") {
      return !isReadonly2;
    } else if (key === "__v_isReadonly") {
      return isReadonly2;
    } else if (key === "__v_isShallow") {
      return shallow;
    } else if (key === "__v_raw" && receiver === (isReadonly2 ? shallow ? shallowReadonlyMap : readonlyMap : shallow ? shallowReactiveMap : reactiveMap).get(target)) {
      return target;
    }
    const targetIsArray = isArray(target);
    if (!isReadonly2) {
      if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
        return Reflect.get(arrayInstrumentations, key, receiver);
      }
      if (key === "hasOwnProperty") {
        return hasOwnProperty;
      }
    }
    const res = Reflect.get(target, key, receiver);
    if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
      return res;
    }
    if (!isReadonly2) {
      track(target, "get", key);
    }
    if (shallow) {
      return res;
    }
    if (isRef(res)) {
      return targetIsArray && isIntegerKey(key) ? res : res.value;
    }
    if (isObject(res)) {
      return isReadonly2 ? readonly(res) : reactive(res);
    }
    return res;
  };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# shallowReactive

function shallowReactive(target) {
  return createReactiveObject(
    target,
    false,
    shallowReactiveHandlers,
    shallowCollectionHandlers,
    shallowReactiveMap
  );
}
const shallowReactiveHandlers = /* @__PURE__ */ extend(
  {},
  mutableHandlers,
  {
    get: shallowGet,
    set: shallowSet
  }
);
const shallowGet = /* @__PURE__ */ createGetter(false, true);
const shallowSet = /* @__PURE__ */ createSetter(true);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# readonly

function readonly<T extends object>(
  target: T
): DeepReadonly<UnwrapNestedRefs<T>>
1
2
3

对象内部的所有嵌套属性都将是只读的,有递归效应

如果不想要递归,请使用shallowReadonly

/**
 * Creates a readonly copy of the original object. Note the returned copy is not
 * made reactive, but `readonly` can be called on an already reactive object.
 */
function readonly(target) {
    return createReactiveObject(target, true, readonlyHandlers, readonlyCollectionHandlers, readonlyMap);
}
1
2
3
4
5
6
7

再来看一下readonly的处理

const readonlyHandlers = {
    get: readonlyGet,
    set(target, key) {
        if ((process.env.NODE_ENV !== 'production')) {
            console.warn(`Set operation on key "${String(key)}" failed: target is readonly.`, target);
        }
        return true;
    },
    deleteProperty(target, key) {
        if ((process.env.NODE_ENV !== 'production')) {
            console.warn(`Delete operation on key "${String(key)}" failed: target is readonly.`, target);
        }
        return true;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

而readonlyGet

const readonlyGet = /*#__PURE__*/ createGetter(true);
1

题外话,/*#__PURE__*/是什么意思,有什么用呢?

实际上是polyfill转换之后自动加上的。具体可以看这篇文章 (opens new window)

继续往下看

function createGetter(isReadonly = false, shallow = false) {
    return function get(target, key, receiver) {
        if (key === "__v_isReactive" /* IS_REACTIVE */) {
            return !isReadonly;
        }
        else if (key === "__v_isReadonly" /* IS_READONLY */) {
            return isReadonly;
        }
        else if (key === "__v_raw" /* RAW */ &&
            receiver ===
                (isReadonly
                    ? shallow
                        ? shallowReadonlyMap
                        : readonlyMap
                    : shallow
                        ? shallowReactiveMap
                        : reactiveMap).get(target)) {
            return target;
        }
        const targetIsArray = isArray(target);
        if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
            return Reflect.get(arrayInstrumentations, key, receiver);
        }
        const res = Reflect.get(target, key, receiver);
        if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
            return res;
        }
        if (!isReadonly) {
            track(target, "get" /* GET */, key);
        }
        if (shallow) {
            return res;
        }
        if (isRef(res)) {
            // ref unwrapping - does not apply for Array + integer key.
            const shouldUnwrap = !targetIsArray || !isIntegerKey(key);
            return shouldUnwrap ? res.value : res;
        }
        if (isObject(res)) {
            // Convert returned value into a proxy as well. we do the isObject check
            // here to avoid invalid value warning. Also need to lazy access readonly
            // and reactive here to avoid circular dependency.
            return isReadonly ? readonly(res) : reactive(res);
        }
        return res;
    };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

这里createGetter返回了一个get函数,结合上面,createGetter就是重写了Proxy的get方法

# computed源码

computed 函数接受三个参数:getterOrOptions、debugOptions、isSSR。其中:

  • getterOrOptions 可以是一个函数(只有 getter)或包含 get 和 set 方法的对象。
  • debugOptions 是调试选项对象,包含 onTrack 和 onTrigger 回调函数。
  • isSSR 表示是否在服务端渲染环境中。
function computed(getterOrOptions, debugOptions, isSSR = false) {
  let getter;
  let setter;
  // 如果 getterOrOptions 是一个函数,说明只有 getter,将其赋给 getter 变量,并且为 setter 变量赋一个警告函数,用于提醒用户 computed 属性是只读的。
  const onlyGetter = isFunction(getterOrOptions);
  if (onlyGetter) {
    getter = getterOrOptions;
    setter = () => {
      console.warn("Write operation failed: computed value is readonly");
    } ;
  } else {
    getter = getterOrOptions.get;
    setter = getterOrOptions.set;
  }
  const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR);
  if (debugOptions && !isSSR) {
    cRef.effect.onTrack = debugOptions.onTrack;
    cRef.effect.onTrigger = debugOptions.onTrigger;
  }
  return cRef;
}

class ComputedRefImpl {
  constructor(getter, _setter, isReadonly, isSSR) {
    this._setter = _setter;
    this.dep = void 0;
    this.__v_isRef = true;
    this["__v_isReadonly"] = false;
    this._dirty = true;
    this.effect = new ReactiveEffect(getter, () => {
      if (!this._dirty) {
        this._dirty = true;
        triggerRefValue(this);
      }
    });
    this.effect.computed = this;
    this.effect.active = this._cacheable = !isSSR;
    this["__v_isReadonly"] = isReadonly;
  }
  get value() {
    const self = toRaw(this);
    trackRefValue(self);
    if (self._dirty || !self._cacheable) {
      self._dirty = false;
      self._value = self.effect.run();
    }
    return self._value;
  }
  set value(newValue) {
    this._setter(newValue);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

# toRaw()返回原始数据

const obj = toRaw({
  name: 'zs',
  age: 18,
});
console.log(obj); // {name: "zs", age: 18}
1
2
3
4
5

# toReadonly()包装的变量

toReadonly也会将变量包装成一个Proxy对象

# v-memo

v-memo内部由_withMemo方法实现

// 没有v-memo
_createElementVNode('ul', null, [
  ( _openBlock(true),
  _createElementBlock(
    _Fragment,
    null,
    _renderList(_ctx.list, (item, idx) => {
      return (
        _openBlock(),
        _createElementBlock('li', { key: idx }, _toDisplayString(item.id), 1 /* TEXT */)
      );
    }),
    128 /* KEYED_FRAGMENT */
  ))
]);
// 加上v-memo
_withMemo(
  [_ctx.refreshFlag],
  () => (
    _openBlock(),
    // ...
    _createElementBlock('ul', null, [
      (_openBlock(true),
       // ......这一段是一样的
      _createElementBlock(
        _Fragment,
        null,
        _renderList(_ctx.list, (item, idx) => {
          return (
            _openBlock(),
            _createElementBlock('li', { key: idx }, _toDisplayString(item.id), 1 /* TEXT */)
          );
        }),
        128 /* KEYED_FRAGMENT */
      ))
      // ......
    ])
  ), // ...
  _cache,
  0
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

来看withMemo的源码

// cache是vue组件运行时的缓存数据
function withMemo(memo, render, cache, index) {
  const cached = cache[index];
  if (cached && isMemoSame(cached, memo)) {
    return cached;
  }
  // 执行 render 函数获取新的结果,并命名为ret
  const ret = render();
  // 将当前的 memoization 结果存入 ret.memo,使用 slice() 创建一个新的数组以防止引用问题
  ret.memo = memo.slice();
  // 将新结果存入cache,并返回表达式右边的值
  return cache[index] = ret;
}
// 接收两个参数:cached.memo 是缓存中的 memoization 结果,memo 是新计算的 memoization 结果。
function isMemoSame(cached, memo) {
  const prev = cached.memo;
  // 比较二者的长度是否相同,如果不同直接返回 false。
  if (prev.length != memo.length) {
    return false;
  }
  // 遍历比较两个 memoization 结果的每个元素
  for (let i = 0; i < prev.length; i++) {
    // 如果有任何一个元素不相同(通过 hasChanged 函数判断),则返回 false。
    if (hasChanged(prev[i], memo[i])) {
      return false;
    }
  }
  if (isBlockTreeEnabled > 0 && currentBlock) {
    currentBlock.push(cached);
  }
  return true;
}
// Object.is判断两个值是否相同
const hasChanged = (value, oldValue) => !Object.is(value, oldValue);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

关于Object.is的文章可以看这篇 (opens new window)

# 组件通信

# script setup内如何获取props

可以使用defineProps函数来获取props

<script setup>
import { defineProps } from 'vue'

const props = defineProps({
  message: String
})

console.log(props.message) // 获取父组件传递的message属性值
</script>
1
2
3
4
5
6
7
8
9

# 常见问题

# setup内操作dom

https://juejin.cn/post/6913730913142734861

# async setup

解决 (opens new window)

setup() {
  const users = ref([]);
  onMounted(async () => {
    const res = await axios.get("https://jsonplaceholder.typicode.com/users");
    users.value = res.data;
    console.log(res);
  });
  return {
    users,
  };
},
1
2
3
4
5
6
7
8
9
10
11

# @vue/composition-api

# How to use?

// main.js
import Vue from 'vue'
import VueCompositionAPI from '@vue/composition-api'

Vue.use(VueCompositionAPI)
1
2
3
4
5

reactive包裹空对象新增属性没有响应性 issues (opens new window)

"reactive" doesn't work well with Array #219 (opens new window)

Vue.observable on an array does not really work #9499 (opens new window)

// 点击按钮后lis不会增加,而tabs会正常增加
setup() {
  let lis = reactive([]);
  let tabs = ref([]);
  function handleClick() {
    lis = [1, 2, 3];
    tabs.value = [1, 22, 3];
    // lis.push(1, 2, 3);
  }
  return { lis, tabs, handleClick };
},
1
2
3
4
5
6
7
8
9
10
11

为了解决数组响应式的问题,

修改数组全部使用

let arr = reactive([])
arr.push()
arr.splice(0) // 清空数组
1
2
3

# 指令解析

<form @submit.prevent="onSubmit">...</form>
1

v-bind:class.sync="value" 会被解析为:

{
  "name": "bind",
  "arg": "class",
  "modifiers": ["sync"]
}
1
2
3
4
5