vue3.md 18 KB

Vue2 与 Vue3 的区别

Vue3由于完全由TS进行重写,在应用中对类型判断的定义和使用有很强的表现。同一对象的多个键返回值必须通过定义对应的接口(interface)来进行类型定义。要不然在 ESLint 时都会报错。

vue2 的双向数据绑定是利用 ES5 的一个 API Object.definePropert()对数据进行劫持 结合 发布订阅模式的方式来实现的。Vue3 中使用了 es6ProxyAPI 对数据代理。

Vue3支持碎片(Fragments)

Vue2 与 Vue3 最大的区别: Vue2 使用Options API而 Vue3 使用的Composition API Composition API 中,所有的逻辑可以集中在一个名为 setup() 的函数内定义,并根据功能进行分组和模块化。

声明响应式状态的方式

ref() reactive() shallowRef() shallowReactive()

<script>
import { ref, reactive, shallowRef, shallowReactive, onMounted } from 'vue';
 
export default {
  setup() {
    const message = ref('Hello, Vue 3!');
    const count = ref(0)
    const obj = ref({
      foo: 'foo',
      bar: 'bar',
    })
    const array = ref([1, 2, 3])
    return {
      message,
      count,
      obj,
      array
    }
  }
};

// Ref 可以持有任何类型的值,包括深层嵌套的对象、数组或者 JavaScript 内置的数据结构,比如 Map。
// Ref 会使它的值具有深层响应性。这意味着即使改变嵌套对象或数组时,变化也会被检测到:

const obj = ref({
  nested: { count: 0 },
  arr: ['foo', 'bar']
})

function mutateDeeply() {
  // 以下都会按照期望工作
  obj.value.nested.count++
  obj.value.arr.push('baz')
}

// 和 ref() 不同,浅层 ref 的内部值将会原样存储和暴露,并且不会被深层递归地转为响应式。只有对 .value 的访问是响应式的。
// shallowRef() 常常用于对大型数据结构的性能优化或是与外部的状态管理系统集成。
const state = shallowRef({ count: 1 })

// 不会触发更改
state.value.count = 2

// 会触发更改
state.value = { count: 2 }

//  shallowRef() 和 shallowReactive() 来绕开深度响应。浅层式 API 创建的状态只在其顶层是响应式的,对所有深层的对象不会做任何处理。这使得对深层级属性的访问变得更快,但代价是,我们现在必须将所有深层级对象视为不可变的,并且只能通过替换整个根状态来触发更新:


const shallowArray = shallowRef([
  /* 巨大的列表,里面包含深层的对象 */
])

// 这不会触发更新...
shallowArray.value.push(newObject)
// 这才会触发更新
shallowArray.value = [...shallowArray.value, newObject]

// 这不会触发更新...
shallowArray.value[0].foo = 1
// 这才会触发更新
shallowArray.value = [
  {
    ...shallowArray.value[0],
    foo: 1
  },
  ...shallowArray.value.slice(1)
]

reactive() API。与将内部值包装在特殊对象中的 ref 不同,reactive() 将使对象本身具有响应性:Vue 能够拦截对响应式对象所有属性的访问和修改,以便进行依赖追踪和触发更新 主要用于创建整个对象(包括嵌套对象和数组)的响应式状态,它可以递归地转换对象的所有属性。

返回值:reactive 函数返回一个代理对象,这个对象上的所有属性都是响应式的,可以直接读取和修改,无需.value。

const state = reactive(
  count: 0,
  nested: {
    bar: 2
  }
)

<button @click="state.count++">
  {{ state.count }}
</button>

// shallowReactive() 与 reactive() 类似,但只将对象本身转为响应式,而不将嵌套的属性转为响应式。
const state = shallowReactive({
  count: 0,
  nested: {
    bar: 2
  }
})
// 更改状态自身的属性是响应式的
state.count++

// ...但下层嵌套对象不会被转为响应式
isReactive(state.nested) // false

// 不是响应式的
state.nested.bar++
</script>


## 生命周期钩子变化

js Vue2 ~~~~~~~~~~~ vue3 beforeCreate -> setup() created -> setup() // 实例没有创建,不能访问上下文 beforeMount -> onBeforeMount mounted -> onMounted beforeUpdate -> onBeforeUpdate updated -> onUpdated beforeDestroy -> onBeforeUnmount destroyed -> onUnmounted activated -> onActivated deactivated -> onDeactivated

vue

{{ message }}

``` ## 性能提升 ``` vue3 => { 编译阶段 源码体积 响应式系统 } • diff 算法优化 • 静态提升 • 事件监听缓存 • SSR 优化 ``` ```vue

静态文本

静态文本

{{ message }}

静态文本

...

静态文本

``` ### 1.diff 算法 ``` vue3 在 diff 算法中相比 vue2 增加了静态标记 关于这个静态标记,其作用是为了会发生变化的地方添加一个 flag 标记,下次发 生变化的时候直接找该地方进行比较 ``` ### 2.静态提升 ``` Vue3 中对不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复 用 这样就免去了重复的创建节点,大型应用会受益于这个改动,免去了重复的创建操 作,优化了运行时候的内存占用 ``` ```vue 你好
{{ message }}
``` ### 3.事件监听缓存 ``` 未开启事件监听器缓存: ``` ```vue

onClick属性直接绑定到组件实例的_ctx.onClick方法。每次渲染时,都会创建一个新的事件处理器引用。

开启事件监听器缓存:

在开启了事件监听器缓存的情况下,Vue.js会尝试复用已创建的事件处理器以提高性能。这里使用了闭包来缓存事件处理函数,_cache[1] 是一个闭包缓存变量,如果之前已经为 onClick 创建过处理函数,则从缓存 _cache 中获取,否则新建一个处理函数并赋值给 _cache[1]。这样,在后续的渲染过程中,如果_ctx.onClick方法没有变化,就会复用同一个事件处理器,从而避免不必要的内存分配和DOM操作。

总结来说,开启事件监听器缓存是为了优化性能,减少无谓的DOM更新以及内存消耗。

### 4.SSR 优化
当静态内容大到一定量级时候,会用 createStaticVNode 方法在客户端去生成一
个 static node,这些静态 node,会被直接 innerHtml,就不需要创建对象,然后根
据对象渲染
你好
... // 很多个静态属性
{{ message }}



### 源码体积


相比 Vue2,Vue3 整体体积变小了,除了移出一些不常用的 API,再重要的是 Tree 
shanking
任何一个函数,如 ref、reavtived、computed 等,仅仅在用到的时候才打包,没
用到的模块都被摇掉,打包的整体体积变小


Tree shanking的作用 1、减少程序体积(更小) 2、减少程序执行时间(更快) 3、便于将来对程序架构进行优化(更友好)


### 响应式系统

## 状态管理 pinia

> 由于 vuex 4 对 typescript 的支持度较低,所以状态管理弃用了 vuex 而采取了 pinia. pinia 的作者是 Vue 核心团队成员

尤大好像说 `pinia` 可能会代替 `vuex`,所以请放心使用。

### 安装 pinia

Pinia 与 Vuex 的区别:

- `id` 是必要的,它将所使用 store 连接到 devtools。
- 创建方式:`new Vuex.Store(...)`(vuex3),`createStore(...)`(vuex4)。
- 对比于 vuex3 ,state 现在是一个`函数返回对象`。
- 没有 `mutations`,不用担心,state 的变化依然记录在 devtools 中。

bash

安装

yarn add pinia@next


main.ts 中增加

js

引入

import { createPinia } from "pinia"

创建根存储库并将其传递给应用程序

app.use(createPinia())


在 `src` 文件夹下新增 `store` 文件夹,接在在 store 中新增 `main.ts`

### 创建 `store`, main.ts :

js import { defineStore } from 'pinia'

export const useMainStore = defineStore({ id: 'main', state: () => ({

name: '超级管理员',

}), })


组建中获取 store :

js

{{mainStore.name}}


### getters 用法介绍

> Pinia 中的 getter 与 Vuex 中的 getter 、组件中的计算属性具有相同的功能

`store` => `main.ts`

js import { defineStore } from 'pinia'

export const useMainStore = defineStore({ id: 'main', state: () => ({

name: '超级管理员',

}), // getters getters: {

nameLength: (state) => state.name.length,

}, })


组件中使用:

js

用户名:{{ mainStore.name }}
长度:{{ mainStore.nameLength }}

修改store中的name

/* $patch 方法在 Vue.js 或者与 Vue 生态相关的状态管理库(如 Vuex 或 Pinia)中常用于局部更新或合并现有状态。这个方法允许你以部分更新的方式修改 store 中的状态,而无需替换整个状态对象。

在 Pinia(Vue3 的状态管理库)中,store.$patch 方法用来直接对 store 状态进行部分更新,例如: */


![](https://files.mdnice.com/user/16854/ab70ded8-aa34-456a-9044-ac560ff5d2d4.gif)

### actions

> 这里与 `Vuex` 有极大的不同,`Pinia` 仅提供了一种方法来定义如何更改状态的规则,放弃 `mutations` 只依靠 `Actions`,这是一项重大的改变。

`Pinia` 让 `Actions` 更加的灵活:

- 可以通过组件或其他 `action` 调用
- 可以从其他 `store` 的 `action` 中调用
- 直接在 `store` 实例上调用
- 支持`同步`或`异步`
- 有任意数量的参数
- 可以包含有关如何更改状态的逻辑(也就是 vuex 的 mutations 的作用)
- 可以 `$patch` 方法直接更改状态属性

ts import { defineStore } from 'pinia'

export const useMainStore = defineStore({ id: 'main', state: () => ({

name: '超级管理员',

}), getters: {

nameLength: (state) => state.name.length,

}, actions: {

async insertPost(data: string) {
  // 可以做异步
  // await doAjaxRequest(data);
  this.name = data
},

}, })



## composition API 与 options API

在 Vue2 中,我们用过 mixin 去复用相同的逻辑

然后在组件中使用

Mouse position: x {{ x }} / y {{ y }}

使用单个 mixin 似乎问题不大,但是当我们一个组件混入大量不同的 mixins 的时
候
mixins: [mousePositionMixin, fooMixin, barMixin, otherMixin]
会存在两个非常明显的问题:
• 命名冲突
• 数据来源不清晰
现在通过 Compositon API 这种方式改写上面的代码

Mouse position: x {{ x }} / y {{ y }}
```