目录
- 总体来说
- 性能提升
- 树摇(Tree shaking)
- 碎片化节点(Fragment)
- 传送门 (Teleport)
- Suspense
- 更好的TypeScript支持
- Composition API
- 响应式原理
- 开发时差异
- 0. setup
- 1. 生命周期调用
- 2. 响应式(数据)API
- 3.计算属性computed
- 4.侦听器watch
- 5. 获取DOM元素refs
- 6. 路由使用, VUEX ,VUE挂载
- 7. 自定义指令处理技巧
总体来说性能提升
- 重写了虚拟DOM的实现(跳过静态节点,只处理动态节点)
- update性能提高1.3~2倍
- 服务端渲染速度提高了2~3倍
树摇(Tree shaking)
可以将无用模块“剪辑”掉,仅打包需要的
原理:
- ES6 Module引入进行静态分析,故而编译的时候正确判断到底加载了那些模块
- 静态分析程序流,判断那些模块和变量未被使用或者引用,进而删除对应代码
参考:介绍一下 tree shaking 及其工作原理
碎片化节点(Fragment)
在 vue2.x 中,每个组件只能有一个根,所以,写每个组件模板时都要套一个父元素。
在 vue3 中,为了更方便的书写组件模板,新增了一个类似 dom 的标签元素 ,也就是 vue3 中组件可以拥有多个根了。
这样做的好处在于:减少标签层级, 减小内存占用。
传送门 (Teleport)
可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。
Suspense
用于协调对组件树中嵌套的异步依赖的处理。
如果在渲染时遇到异步依赖项 (异步组件和具有 async setup() 的组件),它将等到所有异步依赖项解析完成时再显示默认插槽。
https://cn.vuejs.org/api/built-in-components.html#suspense
更好的TypeScript支持Composition API
组合式API,替换原有的 Options API
根据逻辑相关性组织代码,提高可读性和可维护性
更好的重用逻辑代码(避免mixins混入时命名冲突的问题)
兼容了VUE2中的写法,原有Options API依然可以延用
响应式原理
不再基于 Object.defineProperty而是基于ES6中的Proxy
开发时差异
0. setup
组合式API以setup函数作为组件的入口
是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。当同时使用 SFC 与组合式 API 时该语法是默认推荐。相比于普通的
语法,它具有更多优势:
- 更少的样板内容,更简洁的代码。
- 能够使用纯 TypeScript 声明 props 和自定义事件。
- 更好的运行时性能 (其模板会被编译成同一作用域内的渲染函数,避免了渲染上下文代理对象)。
- 更好的 IDE 类型推导性能 (减少了语言服务器从代码中抽取类型的工作)。
// setup 函数的第一个参数是组件的 props // 没有vue2.x中的this指向vue对象了export default { props: { title: String }, setup(props) { console.log(props.title) }}
1. 生命周期调用
// 在vue中解构出方法import { onMounted, onUpdated } from 'vue'onMounted(() => { ...})onUpdated(() => { ...})
beforeCreate -> 使用 setup()
created -> 使用 setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
errorCaptured -> onErrorCaptured
2. 响应式(数据)API
ref
接受一个参数值并返回一个响应式且可改变的 ref 对象
- ref 对象拥有一个指向内部值的单一属性 .value
- 当ref在模板中使用的时候,它会自动解套,无需在模板内额外书写 .value
import { ref } from "vue";export default { setup() { let Num = ref(0), isShow = ref(false); return { Num, isShow }; }};
reactive
- 接收一个普通对象然后返回该普通对象的响应式代理等同于 2.x 的 Vue.observable()
- 响应式转换是“深层的”:会影响对象内部所有嵌套的属性
使用toRefs(state)
可将其创建的传为Refs类型,方便在模板中直接取值,而不用.value
import { ref, reactive } from "vue";export default { props: { title: String }, setup() { let state = reactive({ Num: 0, arr: [1, 2] }); let change = () => { state.arr[0] = state.arr[0] + 1; state.name = "flytree"; }; return { state, change }; }};
相关的一些方法:
unref / toRef / toRefs / isRef / isProxy / isReactive / isReadonly
3.计算属性computed
传入一个 getter 函数,返回一个默认不可手动修改的 ref 对象
const count = ref(1);const plusOne = computed(() => count.value + 1);console.log(plusOne.value); //2plusOne.value++; //错误!
或者传入一个拥有 get 和 set 函数的对象,创建一个可手动修改的计算状态
const count = ref(1);const plusOne = computed({ get: () => count.value + 1, set: val => { count.value = val - 1; }});plusOne.value = 1;console.log(count.value); //0
4.侦听器watch
watchEffect
立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数
export default { props: { title: String, }, setup(props) { watchEffect(() => { console.log(`title is: ` + props.title); }); }};
watch
- watch API 完全等效于 2.x this.$watch
- watch 需要侦听特定的数据源,并在回调函数中执行副作用
- 默认情况是懒执行的,也就是说仅在侦听的源变更时才执行回调侦听单个数据源
点击查看代码
// 侦听器的数据源可以是一个拥有返回值的 getter 函数,也可以是 refconst state = reactive({ count: 0 })watch( () => state.count, (count, prevCount) => { /* ... */ });const count = ref(0)watch(count, (count, prevCount) => { /* ... */});import { ref, reactive, toRefs, computed, watch } from "vue";export default { setup() { .... let ratio = ref("--"); watch(state, (state, preState) => { let total = state.supNum + state.oppNum; ratio.value = total === 0 ? "--" : ((state.supNum / total) * 100).toFixed(2) + "%"; }); return { ..., ratio }; }};
5. 获取DOM元素refs
模板的Refs
当使用组合式 API 时,reactive refs 和 template refs 的概念已经是统一的
点击查看代码
import { ref, onMounted } from 'vue'; export default { setup() { const root = ref(null); onMounted(() => { console.log(root.value); }); return { root }; } }6. 路由使用, VUEX ,VUE挂载
先要解构出方法,再去创建对应路由,store或vue对象,其它一样
Vue Router 4.x
import { createRouter, createWebHashHistory } from 'vue-router';import routes from './routes';const router = createRouter({ history: createWebHashHistory(), routes});router.beforeEach(async (to, from, next) => { ... })export default router;
Vue Router 3.x
import VueRouter from 'vue-router'import routes from './routes';const router = new VueRouter({ routes})export default router;
VUEX 4.x
import { createStore, createLogger } from 'vuex';export default createStore({ state: {}, mutations: {}, actions: {}, plugins: [createLogger()]})
VUEX 3.x
import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)const store = new Vuex.Store({ state: {}, state: {}, mutations: {}, actions: {},})export default store
实例创建
VUE 3.x
import { createApp } from 'vue';const app = createApp(App);app.use(store);app.use(router);app.mount('#app');
VUE 2.x
import Vue from 'vue'new Vue({ router, store, render: h => h(App)}).$mount('#app')
7. 自定义指令处理技巧
export default function directive(app) { app.directive('xxx', { // 指令首次绑定到元素且在安装父组件之前...「等同于bind」 beforeMount(el, binding, vnode, prevVnode) { // binding:数据对象 // + arg:传给指令的参数 v-xxx:n -> arg:"n" // + modifiers:修饰符对象 v-xxx.stop -> modifiers:{stop:true} // + value:指令绑定的值 v-xxx="1+1" -> value:2 // + oldValue:之前绑定的值 }, // 安装绑定元素的父组件时...「等同于inserted」 mounted() {}, // 在包含组件的VNode更新之前... beforeUpdate() {}, // 在包含组件的VNode及其子VNode更新后...「等同于componentUpdated」 updated() {}, // 在卸载绑定元素的父组件之前... beforeUnmount() {}, // 指令与元素解除绑定且父组件已卸载时...「等同于unbind」 unmounted() {} });};// main.jsimport { createApp} from 'vue';import App from './App.vue';import directive from './directive';const app = createApp(App);directive(app);app.mount('#app');