一、Vue3 比 Vue2 有什么优势?
- 性能更好
- 体积更小
- 更好的 ts 支持
- 更好的代码组织
- 更好的逻辑抽离
- 更多新功能
二、Vue2 和 Vue3 生命周期区别
App.vue 父组件:
<template> <div> <life-cycles :msg="msg" v-if="flag" /> <button @click="changeHandler">change msg</button> <button @click="changeFlagHandler">change flag</button> </div> </template> <script> import LifeCycles from "./components/LifeCycles.vue"; export default { data() { return { msg: "hello vue3", flag: true, }; }, methods: { changeHandler() { this.msg = "hello vue3" + Date.now(); }, changeFlagHandler() { this.flag = !this.flag; }, }, components: { LifeCycles }, }; </script>
1、Options API 生命周期
LiftCycles.vue 子组件:
- Vue2.x 的形式
- 点击按钮进行 组件更新 和 组件销毁(查看控制台输出内容)
<template> <p>生命周期 {{ msg }}</p> </template> <script> export default { name: "LiftCycles", props: { msg: String, }, beforeCreate() { console.log("beforeCreate"); }, created() { console.log("created"); }, beforeMount() { console.log("beforeMount"); }, mounted() { console.log("mounted"); }, beforeUpdate() { console.log("beforeUpdate"); }, updated() { console.log("updated"); }, beforeUnmount() { console.log("beforeUnmount"); }, unmounted() { console.log("unmounted"); }, }; </script>
2、Composition API 生命周期
- beforeDestroy 改为 beforeUnmount
- destroyed 改为 unmounted
- 其他沿用 Vue2 的生命周期
<template> <p>生命周期 {{ msg }}</p> </template> <script> import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, } from "vue"; export default { name: "LiftCycles", props: { msg: String, }, // 等于 beforeCreate 和 created setup() { console.log("setup"); onBeforeMount(() => { console.log("onBeforeMounted"); }); onMounted(() => { console.log("onMounted"); }); onBeforeUpdate(() => { console.log("onBeforeUpdate"); }); onUpdated(() => { console.log("onUpdated"); }); onBeforeUnmount(() => { console.log("onBeforeUnmount"); }); onUnmounted(() => { console.log("onUnmounted"); }); }, }; </script>
三、如何理解 Composition API 和 Options API
Options API 对比 Composition API:
1、Composition API 带来了什么
- 更好的代码组织
- 更好的逻辑复用
- 更好的类型推导
2、Composition API 和 Options API 如何选择?
- 不建议共用,容易引起代码混乱
- Composition API 用于复杂的业务情况
- Options API 用于简单的业务情况
3、如何选择
- 不建议乱用,会引起混乱
- 小型项目、业务逻辑简单,用 Options API
- 中大型项目、逻辑复杂,用 Composition API
4、别误解 Composition API
- Composition API 属于高阶技巧,不是基础必会
- Composition API 是为解决复杂业务逻辑而设计
- Composition API 就像 Hooks 在 React 中的地位
四、如何理解 ref、toRef 和 toRefs
1、ref
- 生成值类型的响应式数据
- 可以用于 reactive,也可以用于模板(不需要 .value)
- 用 .value 去修改值
- 所有的 ref 变量,尽量使用 xxxRef 的格式命名,便于区分
- ref 用来定义响应式的值类型
- reactive 用来定义响应式的引用类型
- ref 定义的值类型可在 reactive 和 模板中直接使用(不需要 .value)
- 修改值的时候要使用 .value
<template> <p>ref demo {{ ageRef }} {{ state.name }}</p> </template> <script> import { ref, reactive } from "vue"; export default { name: "Ref", setup() { const ageRef = ref(21); // 值类型 响应式 const nameRef = ref("杂货铺"); const state = reactive({ name: nameRef, }); setTimeout(() => { console.log("ageRef", ageRef.value); ageRef.value = 18; // .value 修改值 nameRef.value = "前端杂货铺"; }, 1000); return { ageRef, state, }; }, }; </script>
2、ref 扩展(获取模板的dom元素)
RefTemplate.vue 组件
- ref 本身的意思就是一个引用,给它传什么,它就是指向什么
- 传一个 DOM 当然就指向 DOM 了
<template> <p ref="elemRef">我是一行文字</p> </template> <script> import { ref, onMounted } from "vue"; export default { name: "RefTemplate", setup() { const elemRef = ref(null); onMounted(() => { console.log('ref template', elemRef.value.innerHTML, elemRef.value); }) return { elemRef, }; }, }; </script>
3、toRef
- 针对一个响应式对象(reactive)的 prop(属性)
- 创建一个 ref,具有响应式
- 两者保持引用关系
toRef.vue 组件
- toRef(对象, "属性") 修改响应式对象的属性
- 改变 ageRef 时, state.age 也会改变
- 改变 state.age 时,ageRef 也会改变
<template> <p>toRef demo - {{ ageRef }} - {{ state.name }} - {{ state.age }}</p> </template> <script> import { reactive, toRef } from "@vue/reactivity"; export default { name: "ToRef", setup() { const state = reactive({ age: 20, name: "杂货铺", }); // toRef 如果用于普通对象(非响应式对象),产出的结果不具备响应式 // const state = { // age: 20, // name: '杂货铺' // } // 修改响应式对象(reactive)的一个属性(age) const ageRef = toRef(state, "age"); setTimeout(() => { state.age = 25; }, 1000); setTimeout(() => { ageRef.value = 30; // 用 .value 修改值 }, 2000); return { state, ageRef, }; }, }; </script>
4、toRefs
- 将响应式对象(reactive 封装)转换为普通对象
- 对象的每个 prop 都是对应的 ref
- 两者保持引用关系
toRefs 组件
- toRefs,将响应式对象变为普通对象(仍然具有响应式)
- 对象的每个属性都是对应的 ref
<template> <p>toRefs demo {{ ageRef }} {{ nameRef }}</p> </template> <script> import { toRefs, reactive } from "vue"; export default { name: "ToRefs", setup() { const state = reactive({ age: 20, name: "杂货铺", }); // 将响应式对象,变为普通对象 const stateAsRefs = toRefs(state); // 每个属性,都是 ref 对象 const { age: ageRef, name: nameRef } = stateAsRefs; setTimeout(() => { state.age = 25 }, 1000) return { ageRef, nameRef, }; }, }; </script>
或者这么写(推荐):
- 直接返回这个普通对象
- 注意此时模板内容也发生了变化,直接写对象里面的属性
<template> <p>toRefs demo {{ name }} {{ age }}</p> </template> <script> import { toRefs, reactive } from "vue"; export default { name: "ToRefs", setup() { const state = reactive({ age: 20, name: "杂货铺", }); // 将响应式对象,变为普通对象 const stateAsRefs = toRefs(state); setTimeout(() => { state.age = 25 }, 1000) return stateAsRefs }, }; </script>
五、ref、toRef 和 toRefs 的最佳使用方式
- 用 reactive 做对象的响应式,用 ref 做值类型的响应式
- setup 中返回 toRefs(state),或者 toRefs(state, ‘xxx’)
- ref 的变量命名都用 xxxRef
- 合成函数返回响应式对象时,使用 toRefs
xxx.js 文件
- 定义函数和响应式对象
- 返回时转为 ref
import { toRefs, reactive } from "vue" function useFeatureX() { const state = reactive({ x: 1, y: 2 }) // 逻辑运行状态,省略 N 行 // 返回时转换为 ref return toRefs(state) } export default useFeatureX
xxx.vue 组件
- 使用时直接写对象的属性就可以
export default { setup() { // 可以在不是去响应式的情况下破坏结构 const { x, y } = useFeatureX() return { x, y } } }
不积跬步无以至千里 不积小流无以成江海