本文主要记录 https://github.com/CyanSalt/commas 重构过程中对于 Vue 3 的一些体验
0. 生态对 ES Module 的支持稀烂
尽管 Node.js 14 已经正式支持了 ES Module,但相关的工具链对于 .mjs
的支持依然不佳。点名批评:
- webpack@4:需要手动为
.mjs
指定javascript/auto
类型的解析。v5 版本似乎支持 ES Module,但可能也需要开启实验性特性(暂未测试) - electron:内置的 Node 禁用了命令行参数
--experiment-module
,按照核心团队的说法(issue 找不到了),引入 Node 14 时也将会出于安全性考虑禁用 ES Module 功能 - typescript:不支持
.mjs
文件的类型检查,即使开了checkJs
编译选项。这导致依赖 typescript 的 VSCode 等对于.mjs
的支持也很差(缺少自动补全、智能提示等功能,通过import
的类型推导总是any
)
1. 代理对象对于常量的影响
问题描述
举一个栗子
import { reactive, watchEffect } from 'vue'
const MY_CONSTANT = {
foo: 1,
bar: 2,
}
const state = reactive({})
state.mapping = MY_CONSTANT
watchEffect(() => {
console.log(state.mapping === MY_CONSTANT) // false
})
可以看到,通过 reactive 对象获取的属性会和原本的对象不相同,这是因为 reactive 对象会对访问的属性也做深层处理,也就是说
import { isProxy } from 'vue'
isProxy(state.mapping) // true
解决方案
像上面的这种常量的情况,可以通过
import { markRaw } from 'vue'
state.mapping = markRaw(MY_CONSTANT)
来规避。如果需要依赖属性的 reactive 特性,可以
import { watchEffect, toRaw } from 'vue'
watchEffect(() => {
console.log(toRaw(state.mapping) === MY_CONSTANT) // true
})
2. 浅层代理和深层代理混用的问题
问题描述
考虑一个使用浅层代理的情况:
import { shallowReactive } from 'vue'
const state = shallowReactive({
list: [],
})
state.list.push(data) // will not trigger reactivity mutations
对 shallowReactive
对象修改时,后续传入的数据将不会被响应式系统监管。通常来说如果是某个类的实例,通过这种方式来保存引用是安全的。但是
import { readonly } from 'vue'
function useList() {
return readonly(state.list)
}
看起来是一个更加安全的对外提供 state.list
的方式,但是注意 readonly
是深层的,这会导致 state.list
在执行 useList
时被响应式系统监管。
解决方案
一种方式是使用 shallowReadonly
import { shallowReadonly } from 'vue'
function useList() {
return shallowReadonly(state.list)
}
但通常来说,不使用 shallowReactive
而是使用 markRaw
可能更好
import { reactive, markRaw } from 'vue'
const state = reactive({
list: markRaw([]),
})
state.list.push(data) // will not trigger reactivity mutations