静态文本
静态文本
{{ message }}
静态文本
...静态文本
Vue3
由于完全由TS
进行重写,在应用中对类型判断的定义和使用有很强的表现。同一对象的多个键返回值必须通过定义对应的接口(interface
)来进行类型定义。要不然在 ESLint 时都会报错。
vue2
的双向数据绑定是利用 ES5
的一个 API Object.definePropert()
对数据进行劫持 结合 发布订阅
模式的方式来实现的。Vue3
中使用了 es6
的 ProxyAPI
对数据代理。
Vue3
支持碎片(Fragments
)
Vue2 与 Vue3 最大的区别: Vue2 使用Options API
而 Vue3 使用的Composition API
Composition API 中,所有的逻辑可以集中在一个名为 setup() 的函数内定义,并根据功能进行分组和模块化。
ref() reactive() shallowRef() shallowReactive()
Vue2 ~~~~~~~~~~~ vue3
beforeCreate -> setup()
created -> setup() // 实例没有创建,不能访问上下文
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
activated -> onActivated
deactivated -> onDeactivated
vue3 => {
编译阶段
源码体积
响应式系统
}
• diff 算法优化
• 静态提升
• 事件监听缓存
• SSR 优化
静态文本
静态文本
{{ message }}
静态文本
...静态文本
vue3 在 diff 算法中相比 vue2 增加了静态标记
关于这个静态标记,其作用是为了会发生变化的地方添加一个 flag 标记,下次发
生变化的时候直接找该地方进行比较
Vue3 中对不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复
用
这样就免去了重复的创建节点,大型应用会受益于这个改动,免去了重复的创建操
作,优化了运行时候的内存占用
你好
未开启事件监听器缓存:
```
onClick属性直接绑定到组件实例的_ctx.onClick方法。每次渲染时,都会创建一个新的事件处理器引用。
开启事件监听器缓存:
```
```
在开启了事件监听器缓存的情况下,Vue.js会尝试复用已创建的事件处理器以提高性能。这里使用了闭包来缓存事件处理函数,_cache[1] 是一个闭包缓存变量,如果之前已经为 onClick 创建过处理函数,则从缓存 _cache 中获取,否则新建一个处理函数并赋值给 _cache[1]。这样,在后续的渲染过程中,如果_ctx.onClick方法没有变化,就会复用同一个事件处理器,从而避免不必要的内存分配和DOM操作。
总结来说,开启事件监听器缓存是为了优化性能,减少无谓的DOM更新以及内存消耗。
```
### 4.SSR 优化
```
当静态内容大到一定量级时候,会用 createStaticVNode 方法在客户端去生成一
个 static node,这些静态 node,会被直接 innerHtml,就不需要创建对象,然后根
据对象渲染
```
相比 Vue2,Vue3 整体体积变小了,除了移出一些不常用的 API,再重要的是 Tree
shanking
任何一个函数,如 ref、reavtived、computed 等,仅仅在用到的时候才打包,没
用到的模块都被摇掉,打包的整体体积变小
Tree shanking的作用
1、减少程序体积(更小)
2、减少程序执行时间(更快)
3、便于将来对程序架构进行优化(更友好)
<script>
// Object.defineProperty 只能遍历对象属性进行劫持
function observe(obj) {
if (typeof obj !== 'object' || obj == null) {
return
}
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key])
})
}
// Proxy 直接可以劫持整个对象,并返回一个新对象,我们可以只操作新的对象达到响应式目的
function reactive(obj) {
if (typeof obj !== 'object' && obj != null) {
return obj
}
// Proxy 相当于在对象外层加拦截
const observed = new Proxy(obj, {
get(target, key, receiver) {
const res = Reflect.get(target, key, receiver)
console.log(`获取${key}:${res}`)
return res
},
set(target, key, value, receiver) {
const res = Reflect.set(target, key, value, receiver)
console.log(`设置${key}:${value}`)
return res
},
deleteProperty(target, key) {
const res = Reflect.deleteProperty(target, key)
console.log(`删除${key}:${res}`)
return res
}
})
return observed
}
// Proxy 可以直接监听数组的变化(push、shift、splice)
const obj = [1,2,3]
const proxtObj = reactive(obj)
obj.push(4) // ok
// Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等,
// Proxy 不兼容 IE,也没有 polyfill, defineProperty 能支持到 IE9
// 这是 Object.defineProperty 不具备的
// 正因为 defineProperty 自身的缺陷,导致 Vue2 在实现响应式过程需要实现其他的方法辅助(如重写数组方法、增加额外 set、delete 方法)
// 数组重写
const originalProto = Array.prototype
const arrayProto = Object.create(originalProto)['push', 'pop', 'shift', 'unshift', 'splice', 'reverse', 'sort']
.forEach(method => {
arrayProto[method] = function () {
originalProto[method].apply(this.arguments)
dep.notice()
}
});
// set、delete
Vue.set(obj,'bar','newbar')
Vue.delete(obj,'bar')
</script>
由于 vuex 4 对 typescript 的支持度较低,所以状态管理弃用了 vuex 而采取了 pinia. pinia 的作者是 Vue 核心团队成员
尤大好像说 pinia
可能会代替 vuex
,所以请放心使用。
Pinia 与 Vuex 的区别:
id
是必要的,它将所使用 store 连接到 devtools。new Vuex.Store(...)
(vuex3),createStore(...)
(vuex4)。函数返回对象
。没有 mutations
,不用担心,state 的变化依然记录在 devtools 中。
# 安装
yarn add pinia@next
main.ts 中增加
# 引入
import { createPinia } from "pinia"
# 创建根存储库并将其传递给应用程序
app.use(createPinia())
在 src
文件夹下新增 store
文件夹,接在在 store 中新增 main.ts
store
, main.ts :import { defineStore } from 'pinia'
export const useMainStore = defineStore({
id: 'main',
state: () => ({
name: '超级管理员',
}),
})
组建中获取 store :
<template>
<div>{{mainStore.name}}</div>
</template>
<script setup lang="ts">
import { useMainStore } from "@/store/main"
const mainStore = useMainStore()
</script>
Pinia 中的 getter 与 Vuex 中的 getter 、组件中的计算属性具有相同的功能
store
=> main.ts
import { defineStore } from 'pinia'
export const useMainStore = defineStore({
id: 'main',
state: () => ({
name: '超级管理员',
}),
// getters
getters: {
nameLength: (state) => state.name.length,
},
})
组件中使用:
<template>
<div>用户名:{{ mainStore.name }}<br />长度:{{ mainStore.nameLength }}</div>
<hr/>
<button @click="updateName">修改store中的name</button>
</template>
<script setup lang="ts">
import { useMainStore } from '@/store/main'
const mainStore = useMainStore()
const updateName = ()=>{
// $patch 修改 store 中的数据
mainStore.$patch({
name: '名称被修改了,nameLength也随之改变了'
})
}
</script>
/* $patch 方法在 Vue.js 或者与 Vue 生态相关的状态管理库(如 Vuex 或 Pinia)中常用于局部更新或合并现有状态。这个方法允许你以部分更新的方式修改 store 中的状态,而无需替换整个状态对象。
在 Pinia(Vue3 的状态管理库)中,store.$patch 方法用来直接对 store 状态进行部分更新,例如: */
这里与
Vuex
有极大的不同,Pinia
仅提供了一种方法来定义如何更改状态的规则,放弃mutations
只依靠Actions
,这是一项重大的改变。
Pinia
让 Actions
更加的灵活:
action
调用store
的 action
中调用store
实例上调用同步
或异步
可以 $patch
方法直接更改状态属性
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
},
},
})
在 Vue2 中,我们用过 mixin 去复用相同的逻辑
<script>
export const MoveMixin = {
data() {
return {
x: 0,
y: 0,
};
},
methods: {
handleKeyup(e) {
console.log(e.code);
// 上下左右 x y
switch (e.code) {
case "ArrowUp":
this.y--;
break;
case "ArrowDown":
this.y++;
break;
case "ArrowLeft":
this.x--;
break;
case "ArrowRight":
this.x++;
break;
}
},
},
mounted() {
window.addEventListener("keyup", this.handleKeyup);
},
unmounted() {
window.removeEventListener("keyup", this.handleKeyup);
},
};
</script>
然后在组件中使用
<template>
<div>
Mouse position: x {{ x }} / y {{ y }}
</div>
</template>
<script>
import mousePositionMixin from './mouse'
export default {
mixins: [mousePositionMixin]
}
</script>
使用单个 mixin 似乎问题不大,但是当我们一个组件混入大量不同的 mixins 的时 候 mixins: [mousePositionMixin, fooMixin, barMixin, otherMixin] 会存在两个非常明显的问题: • 命名冲突 • 数据来源不清晰 现在通过 Compositon API 这种方式改写上面的代码
<script>
import { onMounted, onUnmounted, reactive } from "vue";
export function useMove() {
const position = reactive({
x: 0,
y: 0,
});
const handleKeyup = (e) => {
console.log(e.code);
// 上下左右 x y
switch (e.code) {
case "ArrowUp":
position.y--;
break;
case "ArrowDown":
position.y++;
break;
case "ArrowLeft":
position.x--;
break;
case "ArrowRight":
position.x++;
break;
}
};
onMounted(() => {
window.addEventListener("keyup", handleKeyup);
});
onUnmounted(() => {
window.removeEventListener("keyup", handleKeyup);
});
return { position };
}
</script>
<template>
<div>
Mouse position: x {{ x }} / y {{ y }}
</div>
</template>
<script>
// toRefs将state对象的属性分别转换成了独立的ref对象,这样在模板中可以直接通过.value访问它们的值,而且更改.value会触发视图更新。
import { useMove } from "./useMove";
import { toRefs } from "vue";
position = {x, y}
torefs() => ref(x) ref(y)
export default {
setup() {
const { position } = useMove();
const { x, y } = toRefs(position);
return {
x,
y,
};
},
};
</script>