本文主要记录 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