Vue3.x 笔记

date
Mar 24, 2021
slug
vue3.x-note
status
Published
tags
Vue3.x
Vuejs
summary
vue3.x 尝鲜笔记,内含vue&react对比,以及vue3.x的改进点
type
Post

Vue & React 设计思路对比

vue: options => composition
react: class => hook
用法相似,但底层设计千差万别
 
vue 只在初始化的时候执行一次,后续通过响应式更新:响应式 + vdom
  1. 响应式:数据变了通知变化
  1. vdom 数据变了并不知道具体变化的地方需要diff算出变化
  1. 二者一个推一个拉,相互配合
  1. 如何配合?响应式主动通知变化,vdom 是被动计算
  1. vue1 中是纯响应式,问题在于响应式对象太大后导致页面卡顿,vue2 引入vdom
 
react 每次改变都会重新执行 usestate :vdom
  1. react 没有响应式,纯vdom,计算diff
  1. vdom 对象太大后导致diff时间过长超过16.6ms页面卡顿

Vue Options/Composition Api

Options Api

写法固定 先写data,method... 当业务逻辑越来越复杂,可维护性越来越差,解决方式提取业务组件、mixins(坑:命名冲突,不好追踪变量来源)
所有的配置式写法都有这种苦恼,所有的东西必须按照某种规则提前写好,代码量越来越多,难以维护。
好处就很明显,简单易懂;不好之处就是不灵活

Composition Api

import { ref, reactive } from 'vue'

export default {
    setup() {
        let val = ref('')
        let todos = reactive([
            { id: 1, title: '1' },
            { id: 2, title: '2' },
        ])
        
        const addTodo = () => {
            todos.push({
                id: todos.length,
                title: val.value
            })
            
            val.value = ''
        }
        
        return {
            val,
            todos,
            addTodo
        }
    
    }
}
缺点:
1. 难看
2. return 一旦数据量大,也会导致上下反复查阅代码(在script标签中写 setup,即可)
<script setup>
import { ref, reactive } from 'vue'
// import useTodo from './useTodo'

// let { val, todo, addTodo } = useTodo 

let val = ref('')
let todos = reactive([
    { id: 1, title: '1' },
    { id: 2, title: '2' },
])

const addTodo = () => {
    todos.push({
        id: todos.length,
        title: val.value
    })
    
    val.value = ''
}
<script>
// useTodo.js
import { ref, reactive } from 'vue'

export default () => {
    let val = ref('')
    let todos = reactive([
        { id: 1, title: '1' },
        { id: 2, title: '2' },
    ])
    
    const addTodo = () => {
        todos.push({
            id: todos.length,
            title: val.value
        })
        
        val.value = ''
    }
    return { val, todo, addTodo }
}
优点:
1. 通过 import 导入的依赖方便做tree-shaking。按照options的写法,build的时候虽然可以读取每个文件来查看是否使用computed但是build速度会很慢,而composition只需要查看import即可选择是否打包 computed的代码
2. 方便复用、组合逻辑,更彻底的面向对象。而组合优于继承
3. 组件任意拆分后组合类似 hook

Vue Complier 到底做了什么

把jsx、template转化成js、html能在runtime运行(浏览器)

jsx & template

  1. jsx 就是用js输出html,足够灵活;代价就是优化空间较小
  1. template不够灵活,要遵循template语法来写,优化空间较大 例子:vue3 diff: 11 /* TEXT, CLASS, PROPS */ 11 就是通过位运算将 {{ name }}、:class、:name 通过用位运算组合算出;这也是template的好处,虽然限制多但是可以通过代码打上各种各样的标记来进行优化(diff优化);vue2中没有这项diff优化,在vue3中 _createVNode("div", null, "谢谢") =》<div>谢谢</div> 这一项会被当作静态内容,不进行diff真正做到了按需diff

虚拟 dom

  1. 避免直接操作dom,在操作前通过diff找出真正需要更新的dom树节点而更新,并不是更新整棵树
  1. vdom除了性能更牛逼的是将view层变成对象(json)后可以通过http拉取,轻松实现跨端。不关注不同端的渲染逻辑只在乎数据(结构),因为对象(json)是基础数据结构所有端都支持
  1. vue2只做了双端预判,vue3的diff算法:最长递增子序列 + 双端预判
  1. vdom在vue和react中的区别? react:vdom是被动计算; vue:响应式主动通知变化,vdom是被动计算;具体是什么?根据组件划分,组件之间通过响应式通知,组件内部通过vdom用diff计算

性能优化手段

  1. complier期的优化非常重要,如果能在build的时候把 let a = new Array(6).fill(1).filter(v => v>0); console.log(a) 转成 console.log([1, 1, 1, 1, 1, 1])让 runtime直接执行console.log([1, 1, 1, 1, 1, 1])那么性能肯定是大幅度提升的。vue3优化也是借鉴了这个思想,思想来源 facebook 的 prepack.io 项目
  1. vue complier 层作出的优化是:h 函数新增三个参数:patchflag dynamicprops isBlockNode 更新组件的时候加快render速度

setup

setup(props, { slots, attrs, emit }) {
    return {
      // ...  
    }
}
emit 用于响应事件
const state = reactive({
    name: '11'
})

state.name += 1
return {
    state
}
// 模版里直接取 {{ state.name }}
const nameRef = ref('alex')
nameRef.value += 1

return {
    nameRef
}
// 模版里直接取 {{ nameRef }}, 模版解析的时候会自动判断这个值是不是ref是的话直接取value
俩者的区别在于 ref生成的是一个 { value: 'alex' } 响应式对象
const computedNameRef = computed(() => {
    return nameRef.value + '2'
})
watchEffect(() => {
    console.log(nameRef.value)
})
// reactive、ref值变化后做一些逻辑
// 大部分的时候执行dom、ajax等副作用函数

为什么建议在 vue3+ts 中使用 jsx 开发?

  1. prop的类型会在传错的时候编译器直接抛出错误,而template不行,需要来回切换查看
  1. jsx有动态控制组件渲染的能力,而template不行,所有的东西必须静态写死
  1. 唯一不好的点就是sfc中可以写样式,但是jsx中得使用cssinjs比较繁琐

提示

vue3 setup 中的 return 相当于 render,所以参考react中的一些render规则不要写引起data或者props变化的逻辑从而导致re-render

© i7eo 2017 - 2022