Vue 3 体验报告

详细文档参见 https://composition-api.vuejs.org/zh/api.html

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

3. emits 是一个非常有用的特性

4. 总是把非声明的语句放在 watchEffect

5. 严格执行命名约定


Powered by Sairin