一、Vue3 与 Vue2 的区别
-
新加了组件:
Fragment、Teleport、Suspense。 -
语法
v-on的.native修饰符被移除,不在支持。 -
Vue3的template中,不再需要唯一的根元素,如果这么使用报错可能是ESLint没有配置好或版本太低不支持这种写法。<template> <div>123</div> <div>456</div> </template> -
全局导入方式调整
// vue - main.js import { createApp } from 'vue' createApp(App).use(store).use(router).mount('#app') // vuex import { createStore } from 'vuex' export default createStore({ ... }) // vue-router import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' const routes: Array<RouteRecordRaw> = [] export default createRouter({ history: createWebHistory(process.env.BASE_URL), routes }) ... -
Vue3支持Vue2生命周期的使用 -
生命周期对比
Vue2 Vue3 含义 区别 beforeCreate() setup() 创建前, data和el并未初始化Vue3中被setup()替代created() setup() 创建完毕, data数据完成初始化,el没有Vue3中被setup()替代beforeMount() onBeforeMount() 挂载前, data和el完成初始化mounted() onMounted() 挂载结束,完成挂载,渲染页面 beforeUpdate() onBeforeUpdate() 更新前 updated() onUpdated() 更新完成 beforeDestroy() onBeforeUnmount() 销毁前 Vue3中被改名destroyed() onUnmounted() 销毁完成 Vue3中被改名errorCaptured() onErrorCaptured() 异常捕获 onRenderTracked() 虚拟 dom重新渲染Vue3新增,只有在调试的时候有用,生产环境会忽略onRenderTriggered() 虚拟 dom重新渲染,输出更详细准确的数据Vue3新增,只有在调试的时候有用,生产环境会忽略 -
以前自带的方法可以直接使用,现在需要通过
import引入之后才可以进行使用import { ref, reactive, onMounted, onUpdated, onUnmounted, onBeforeMount, onBeforeUpdate, onBeforeUnmount ... } from 'vue'
二、setup 函数
-
setup函数中,不能使用this,this指向的是undefined。 -
setup只会在组件初始化的时候执行一次,执行顺便对比:beforeCreate () { console.log('---- beforeCreate') }, created () { console.log('---- created') }, setup () { console.log('---- setup') } // 输出顺序: // ---- setup // ---- beforeCreate // ---- created -
参数分析
setup(props ,context) { }-
props为一个对象,内部包含了父组件传递过来的所有prop数据。 -
context对象包含了attrs、slots、emit属性。
-
-
属性、函数的优先级、生命周期的调用先后
-
一样的属性与函数,
Vue3的会覆盖Vue2的。 -
同一个生命周期函数
Vue3的先调用,Vue2的后调用,不会覆盖Vue2的。
<template> <div @click="touchButton">{{ sex }}</div> </template> <script> import { defineComponent, ref, onMounted } from 'vue' export default defineComponent({ // vue2 data() { return { sex:'男' } }, // vue2 mounted () { console.log('mounted - vue2') }, // vue3 setup() { // 比 vue2 优先调用 onMounted(() => { console.log('onMounted - vue3') }) // 优先使用这个,会覆盖 vue2 中的 touchButton 方法 function touchButton () { console.log('touchButton - vue3') } // 优先使用这个,会覆盖 vue2 中的 sex 值 const sex = ref('女') return { sex, touchButton } }, // vue2 methods: { touchButton () { console.log('touchButton - vue2') } } }) </script> -
三、ref 函数(响应式数据 方式一)
-
在
setup中使用时,要加.value,模板中使用的时候不需要加.value -
包装简单数据类型
<template> <!-- 模板中使用 --> <div @click="touchButton">{{ sex }}</div> </template> <script> import { defineComponent, ref } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const sex = ref('女') // 点击按钮 function touchButton () { // 修改值时,需要加上 .value sex.value = '男' } // 定义的属性或函数需要 return 出去才会生效 return { sex, touchButton } } }) </script> -
包装复杂数据类型
<template> <!-- 模板中使用 --> <div @click="touchButton">{{ user.job.salary }}</div> </template> <script> import { defineComponent, ref } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const user = ref({ id: 1, name: 'dzm' }) // 中途添加字段 user.value.job = { salary: 10 } // 点击按钮 function touchButton () { // 修改值时,需要加上 .value user.value.job.salary += 1 } // 定义的属性或函数需要 return 出去才会生效 return { user, touchButton } } }) </script>
四、reactive 函数(响应式数据 方式二)
-
ref和reactive都是用来定义响应式数据的,reactive推荐去定义复杂数据类型,ref推荐定义基本类型。-
ref包装的简单数据(例如:number),可以被监听,可以被修改。 -
reactive包装简单数据(例如:number),会报警告,且不能直接被修改。 -
ref包装的复杂数据(例如:json),不可以被监听,可以新增、修改字段。 -
reactive包装的复杂数据(例如:json),可以被监听,可以新增、修改字段。
-
-
下面
十一、中有介绍reactive与shallowReactive函数的使用区别。<template> <!-- 模板中使用 --> <div @click="touchButton">{{ user.name }}</div> </template> <script> import { defineComponent, reactive } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const user = reactive({ id: 1, name: 'dzm' }) // 点击按钮 function touchButton () { // 修改值 user.name = 'xyq' } // 返回 return { user, touchButton } } }) </script>
五、toRef 与 toRefs 函数
-
toRef与toRefs的使用区别-
toRef: 将一个数据变成响应式数据。 -
toRefs: 将多个数据变成响应式数据。
-
-
reactive定义的对象,如果直接用...解构,就会失去响应式的特点,然而使用toRef() 或 toRefs()解构,则可以保留响应式特点。总结:解构之后还具有响应式。let { 属性1, 属性2 } = toRefs(响应式数据)<template> <!-- 模板中使用 --> <div @click="touchButton">{{ user.name }}</div> </template> <script> import { defineComponent, toRef, toRefs, reactive } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const user = reactive({ id: 1, name: 'dzm' }) // 通过 toRef 取值,保留响应式特点(通过 key 单个取值) // let name = toRef(user, 'name') // 通过 toRefs 取值,保留响应式特点 let { name } = toRefs(user) // 点击按钮 function touchButton () { // 修改值 name.value = 'xyq' } // 返回 return { user, name, touchButton } } }) </script>
六、computed 函数
-
computed支持get、set写法,普通写法只用到了get。-
普通写法:返回值就是计算属性的值
import { computed } from 'vue' const 计算属性名 = computed(() => { return 响应式数据相关计算 }) -
高阶写法:
get返回的是计算属性的值,set监听计算属性的变化(v-model绑定计算属性)import { computed } from 'vue' const 计算属性名 = computed(() => { // 取值 get: () => { } // 赋值 set: (val) => { } })
-
-
普通写法
<template> <!-- 模板中使用 --> <div @click="touchButton">{{ newAge }}</div> </template> <script> import { defineComponent, ref, computed } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const age = ref(1) // 计算方法 const newAge = computed(() => { return age.value * 10 }) // 点击按钮 function touchButton () { // 修改值 age.value += 1 } // 返回 return { age, newAge, touchButton } } }) </script> -
高阶写法(
get、set组合使用)<template> <!-- 模板中使用 --> <div @click="touchButton">{{ newAge }}</div> </template> <script> import { defineComponent, ref, computed } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const age = ref(1) // 计算方法 const newAge = computed({ // 取值 get: () => { return age.value * 10 }, // 赋值 set: (val) => { age.value = val / 10 } }) // 点击按钮 function touchButton () { // 修改值 age.value += 1 } // 返回 return { age, newAge, touchButton } } }) </script>
七、watch 函数
-
监听深层次对象时,新旧对象的值都会发生变化,
watch是惰性的,页面第一次加载时不触发watch函数,只有监听的数据发生变化时,才会触发watch函数。-
使用
deep为深层次监听 -
使用
immediate为第一次直接触发,默认第一次不触发
-
-
监听格式
watch(监听参数, 新旧值变化回调, 配置)// 监听普通属性 watch(age, (newVal, oldVal) => { console.log('变化了') }) // 监听 json 数据 watch(json, (newVal, oldVal) => { console.log('变化了') }, { immediate: true, deep: true }) // 监听多个数据 watch([age, json], (newVal, oldVal) => { console.log('变化了') }, { immediate: true, deep: true }) -
使用案例
<template> <!-- 模板中使用 --> <div @click="touchButton">{{ age }}</div> </template> <script> import { defineComponent, ref, reactive, watch } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const age = ref(1) const user = reactive({ id: 1, name: 'dzm' }) // 监听普通数据 watch(age, (newVal, oldVal) => { console.log('Age 变化:', newVal, oldVal) }) // 监听深层次数据 watch(user, (newVal, oldVal) => { console.log('User 变化:', newVal, oldVal) }, { immediate: true, deep: true }) // 监听多个数据 watch([age, user], (newVal, oldVal) => { console.log('Age User 变化:', newVal, oldVal) }, { immediate: true, deep: true }) // 点击按钮 function touchButton () { // 修改值 age.value += 1 user.id += 1 } // 返回 return { age, touchButton } } }) </script>
八、watchEffect 函数(高级监听)
-
watchEffect是非惰性的,一开始就会自动调用一次,可配置执行时机,还可以监听某个值之前,可以先执行一个函数。 -
在页面第一次加载时就会触发,并且会一直监听追踪内部使用的响应式数据,只要追踪的响应式数据发生变化,
watchEffect都会运行。 -
也可以监听多个参数,只是不能监听对象,因为他无法监测对象内部的变化,可能是watchEffect 无法实现深度监听吧(具体原因还不清楚)。
-
普通监听使用
<template> <!-- 模板中使用 --> <div @click="touchButton">{{ age }}</div> <div @click="touchStop">停止监听</div> </template> <script> import { defineComponent, ref, reactive, watchEffect } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const age = ref(1) const user = reactive({ id: 1, name: 'dzm' }) // 普通监听使用 const stop = watchEffect(() => { console.log(age.value, user.id) }) // 点击按钮 function touchButton () { // 修改值 age.value += 1 user.id += 1 } // 停止监听 function touchStop () { stop() } // 返回 return { age, touchButton, touchStop } } }) </script> -
高级监听使用
<template> <!-- 模板中使用 --> <div @click="touchButton">{{ age }}</div> <div @click="touchStop">停止监听</div> </template> <script> import { defineComponent, ref, reactive, watchEffect } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const age = ref(1) const user = reactive({ id: 1, name: 'dzm' }) // 监听值变化之前,会先执行一下函数,无论什么位置,都会先执行函数,在执行其他内容,只有在监听值发生变化时,才会触发 const stop = watchEffect((oninvalidata) => { // 在函数前面输入一下 console.log('oninvalidata - 前', age.value, user.id) // 先执行这个函数,可以用来提前处理一些需要的东西 oninvalidata(() => { console.log('before') }) // 在函数后面输入一下 console.log('oninvalidata - 后', age.value, user.id) }, { // 执行时机: pre(组件更新前)、sync(强制效果始终同步)、post(组件更新后执行) flush: 'post', // dom 加载完毕后执行 // 数据发生变化时,比上面的 oninvalidata 函数还会先调用 onTrigger(e) { console.log('onTrigger') // console.log(e) // 数据变化信息,可以在这里拿到新旧值、变化对象、变化类型、变化key // debugger // 用于 debugger 调试 } }) // 这里是上面监听的输出结果: // 1、oninvalidata - 前 1 1 // 2、oninvalidata - 后 1 1 // 3、onTrigger // 4、onTrigger // 5、before // 6、oninvalidata - 前 2 2 // 7、oninvalidata - 后 2 2 // 第 1、2 行是初始化默认调用的第一次 // 第 3、4 行是监听到值变化之后调用的,如果多个数据同时发生改变,每个数据都会调用一遍 // 第 5、6、7 行是监听到变化之后调用的,如果多个数据同时发生改变,只触发一次 // 点击按钮 function touchButton () { // 修改值 age.value += 1 user.id += 1 } // 停止监听 function touchStop () { stop() } // 返回 return { age, touchButton, touchStop } } }) </script>
九、emit 函数 与 emits 自定义事件注册
-
子组件中所有自定义事件都需要在
emits中进行注册,不然会报警告:[Vue warn]: Extraneous non-emits event listeners (success1, success2) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. If the listener is intended to be a component custom event listener only, declare it using the "emits" option. -
子组件自定义事件
<template> <div @click="touchButton1">点击按钮1</div> <div @click="touchButton2">点击按钮2</div> </template> <script> export default { // 申明自定义事件 emits: ['success1', 'success2'], setup (props, context) { // 点击按钮1 function touchButton1 () { context.emit('success1') } // 点击按钮2 function touchButton2 () { context.emit('success2') } // 返回 return { touchButton1, touchButton2 } } } </script> -
父组件接受事件
<template> <Temp @success1="touchButton1" @success2="touchButton2"></Temp> </template> <script> import Temp from './Temp.vue' import { defineComponent } from 'vue' export default defineComponent({ components: { Temp }, setup () { // 点击按钮1 function touchButton1 () { console.log('点击按钮1') } // 点击按钮2 function touchButton2 () { console.log('点击按钮2') } // 返回 return { touchButton1, touchButton2 } } }) </script>
十、readonly 与 shallowReadonly 函数
-
readonly为深只读,shallowReadonly为浅只读。
十、reactive 与 shallowReactive 函数
-
reactive为深劫持,shallowReactive为浅减持。 -
按上面
九、的同类型数据,被reactive劫持的数据,修改任何层级的数据都会刷新页面,被shallowReactive劫持的数据,修改深层级的数据都不会刷新页面,只有第一层次的会刷新页面。
十一、toRaw 函数
-
toRaw: 将一个reactive生成的响应式对象转换为普通对象。<script> import { defineComponent, reactive, toRaw } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const user = reactive({ id: 1, name: 'dzm', job: { salary: 30 } }) // 点击按钮 function touchButton () { console.log(user) // Proxy {id: 1, name: 'dzm', job: {…}} const user1 = toRaw(user) // 将一个 reactive 生成的响应式对象转换为普通对象 console.log(user1) // {id: 1, name: 'dzm', job: {…}} } // 返回 return { user, touchButton } } }) </script>
十二、markRaw 函数
-
markRaw:标记一个对象,使其永远不再成为响应式的数据<template> <!-- 模板中使用 --> <div @click="touchButton1">新增数据</div> <div @click="touchButton2">叠加数据 {{ user.job && user.job.salary }}</div> </template> <script> import { defineComponent, reactive, markRaw } from 'vue' export default defineComponent({ setup () { // 定义响应式属性 const user = reactive({ id: 1, name: 'dzm' }) // 新增数据 function touchButton1 () { // 通过 reactive 包装后,默认增加数据,是支持响应式的 // user.job = { salary: 10 } // 但是增加的数据通过 markRaw 包装之后,是不支持响应式的 user.job = markRaw({ salary: 10 }) } // 增加数值 function touchButton2 () { // user.job.salary 无论是支持或不支持响应式,值都会变化 // 但是不支持响应式,则值变页面不变,支持响应式,则值变页面也变 if (user.job) { user.job.salary += 1 } // 无论支持与否,输出值是在增加变化的 console.log(user.job.salary) } // 返回 return { user, touchButton1, touchButton2 } } }) </script>
十三、其他的后续会持续更新
- 但是基本掌握上面的也就能进入正常开发了,其他情况的现查现用就行了!