# 学习理解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;
});
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);
}
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),
// ...
}
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)
};
}
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
}
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); //
2
打印结果:
# reactive源码
# 被reactive() 包装的变量
const obj = reactive({
name: 'zs',
age: 18
})
2
3
4
打印结果:
setup(prop,context){
console.log(prop)
console.log(context)
}
2
3
4
打印结果:
context:
function reactive(target) {
if (isReadonly(target)) {
return target;
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap
);
}
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;
}
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
);
};
}
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;
};
}
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);
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>>
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);
}
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;
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
而readonlyGet
const readonlyGet = /*#__PURE__*/ createGetter(true);
题外话,/*#__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;
};
}
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);
}
}
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}
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
);
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);
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>
2
3
4
5
6
7
8
9
# 常见问题
# setup内操作dom
https://juejin.cn/post/6913730913142734861
# async setup
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,
};
},
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)
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 };
},
2
3
4
5
6
7
8
9
10
11
为了解决数组响应式的问题,
修改数组全部使用
let arr = reactive([])
arr.push()
arr.splice(0) // 清空数组
2
3
# 指令解析
<form @submit.prevent="onSubmit">...</form>
v-bind:class.sync="value" 会被解析为:
{
"name": "bind",
"arg": "class",
"modifiers": ["sync"]
}
2
3
4
5