當前為vue3的基礎知識點,為總結b站某視訊的知識文章,在剛開始學習時自我儲存在語雀,現在分享到部落格。
目前找不到原視訊文章地址了!!!要有兄弟看到原文地址:歡迎在下面評論!
Vue3新的特性
- Composition API(組合API)
- setup配置
- ref與reactive
- watch與watchEffect
- provide與inject
- 新的內建元件
- Fragment
- Teleport
- Suspense
- 其他改變
- 新的生命週期鉤子
- data 選項應始終被宣告為一個函式
- 移除keyCode支援作為 v-on 的修飾符
建立Vue3.0工程
1.使用 vue-cli 建立
官方文件:https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create
## 檢視@vue/cli版本,確保@vue/cli版本在4.5.0以上 vue --version ## 安裝或者升級你的@vue/cli npm install -g @vue/cli ## 建立 vue create vue_test ## 啟動 cd vue_test npm run serve
常用 Composition API
官方文件: https://v3.cn.vuejs.org/guide/composition-api-introduction.html
1.拉開序幕的setup
- 理解:Vue3.0中一個新的配置項,值為一個函式。
- setup是所有Composition API(組合API)“ 表演的舞臺 ”。
- 元件中所用到的:資料、方法等等,均要配置在setup中。
- setup函式的兩種返回值:
- 若返回一個物件,則物件中的屬性、方法, 在模板中均可以直接使用。(重點關注!)
- 若返回一個渲染函式:則可以自定義渲染內容。(瞭解)
<template> <h1>測試setup,拿資料</h1> <h3>姓名:{{ name }}</h3> <h3>年齡:{{ age }}</h3> <button @click="sayHello">打招呼</button> </template> <script> // 渲染函式 要匯入這個 import { h } from '@vue/runtime-core'; export default { name: "TestSetup", //此處只是測試setup, 不考慮響應式的問題 setup() { //資料 let name = "張三", age = 15; //方法 function sayHello() { alert(`我叫${name},我${age}歲了,你好!`); } //返回一個物件 (常用) return { name, age, sayHello, }; //返回一個函式(渲染函式--瞭解) // return ()=> h('h1','返回一個函式(渲染函式)') }, }; </script> <style> </style>
5.注意點:
-
- 儘量不要與Vue2.x配置混用
- Vue2.x配置(data、methos、computed...)中可以訪問到setup中的屬性、方法。
- 但在setup中不能訪問到Vue2.x配置(data、methos、computed...)。
- 如果有重名, setup優先。
- setup不能是一個async函式,因為返回值不再是return的物件, 而是promise, 模板看不到return物件中的屬性。(後期也可以返回一個Promise例項,但需要Suspense和非同步元件的配合)
2.ref函式
- 作用: 定義一個響應式的資料
- 語法:
const xxx = ref(initValue)
- 建立一個包含響應式資料的引用物件(reference物件,簡稱ref物件)。
- JS中運算元據:
xxx.value
- 模板中讀取資料: 不需要.value,直接:
<div>{{xxx}}</div>
- 備註:
- 接收的資料可以是:基本型別、也可以是物件型別。
- 基本型別的資料:響應式依然是靠
Object.defineProperty()
的get
與set
完成的。 - 物件型別的資料:內部 “ 求助 ” 了Vue3.0中的一個新函式——
reactive
函式。
<template> <h1>學習ref</h1> <h3>姓名:{{ name }}</h3> <h3>年齡:{{ age }}</h3> <button @click="alterName">修改姓名</button> </template> <script> // 響應式 ref import { ref } from '@vue/reactivity'; export default { name: "testRef", setup() { //資料 let name = ref("張三"), age = 15, obj = ref({ type:'前端工程師', salary:'5K' }); //方法 function alterName() { //成功 name.value = "修改姓名" //物件 obj.value.type = "修改工作" obj.value.salary = "60k" //修改失敗 age ="修改年齡" } //返回一個物件 (常用) return { name, age, alterName, }; }, }; </script> <style> </style>
3.reactive函式
- 作用: 定義一個物件型別的響應式資料(基本型別不要用它,要用
ref
函式) - 語法:
const 代理物件= reactive(源物件)
接收一個物件(或陣列),返回一個代理物件(Proxy的例項物件,簡稱proxy物件) - reactive定義的響應式資料是“深層次的”。
- 內部基於 ES6 的 Proxy 實現,通過代理物件操作源物件內部資料進行操作。
<template> <h1>學習reactive</h1> <h3>工作:{{ obj.type }}</h3> <h3>薪水:{{ obj.salary }}</h3> <h3>愛好:{{ list }}</h3> <button @click="alterName">修改姓名</button> </template> <script> /* 響應式 reactive 物件型別 */ import { reactive } from "@vue/reactivity"; export default { name: "testRef", setup() { //資料 let obj = reactive({ type: "前端工程師", salary: "5K", }), list = reactive(['吃飯','睡覺','打豆豆']); //方法 function alterName() { //物件 obj.type = "修改工作"; obj.salary = "60k"; //陣列 list[0] = '出去玩' } //返回一個物件 (常用) return { obj, list, alterName, }; }, }; </script> <style> </style>
4.reactive對比ref
- 從定義資料角度對比:
- ref用來定義:基本型別資料。
- reactive用來定義:物件(或陣列)型別資料。
- 備註:ref也可以用來定義物件(或陣列)型別資料, 它內部會自動通過
reactive
轉為代理物件。
- 從原理角度對比:
- ref通過
Object.defineProperty()
的get
與set
來實現響應式(資料劫持)。 - reactive通過使用Proxy來實現響應式(資料劫持), 並通過Reflect操作源物件內部的資料。
- ref通過
- 從使用角度對比:
- ref定義的資料:運算元據需要
.value
,讀取資料時模板中直接讀取不需要.value
。 - reactive定義的資料:運算元據與讀取資料:均不需要
.value
。
5.setup的兩個注意點
- setup執行的時機
- 在beforeCreate之前執行一次,this是undefined。
- setup的引數
- props:值為物件,包含:元件外部傳遞過來,且元件內部宣告接收了的屬性。
- context:上下文物件
- attrs: 值為物件,包含:元件外部傳遞過來,但沒有在props配置中宣告的屬性, 相當於
this.$attrs
。 - slots: 收到的插槽內容, 相當於
this.$slots
。 - emit: 分發自定義事件的函式, 相當於
this.$emit
。
6.元件傳值,插槽,自定義事件
程式碼例項
//父頁面 <template> <Demo @hello="showHelloMsg" msg="你好啊" school="尚矽谷"> <template v-slot:qwe> <span>尚矽谷</span> </template> <template v-slot:asd> <span>尚矽谷</span> </template> </Demo> </template> <script> import Demo from './components/Demo' export default { name: 'App', components:{Demo}, setup(){ function showHelloMsg(value){ alert(`你好啊,你觸發了hello事件,我收到的引數是:${value}!`) } return { showHelloMsg } } } </script> //元件 <template> <h1>一個人的資訊</h1> <h2>姓名:{{person.name}}</h2> <h2>年齡:{{person.age}}</h2> <button @click="test">測試觸發一下Demo元件的Hello事件</button> </template> <script> import {reactive} from 'vue' export default { name: 'Demo', //接收引數 props:['msg','school'], //宣告接收 自定義事件 emits:['hello'], //setup 的引數 setup(props,context){ // console.log('---setup---',props) //接收引數 // console.log('---setup---',context) //上下文 // console.log('---setup---',context.attrs) //相當與Vue2中的$attrs // console.log('---setup---',context.emit) //觸發自定義事件的。 console.log('---setup---',context.slots) //插槽 //資料 let person = reactive({ name:'張三', age:18 }) //方法 function test(){ context.emit('hello',666) } //返回一個物件(常用) return { person, test } } } </script>
7.計算屬性與監視
computed函式
- 與Vue2.x中computed配置功能一致
- 寫法
<template> <h1>一個人的資訊</h1> 姓:<input type="text" v-model="person.firstName"> <br> 名:<input type="text" v-model="person.lastName"> <br> <span>全名:{{person.fullName}}</span> <br> 全名:<input type="text" v-model="person.fullName"> </template> <script> //匯入 computed 計算屬性 import {reactive,computed} from 'vue' export default { name: 'Demo', setup(){ //資料 let person = reactive({ firstName:'張', lastName:'三' }) //計算屬性——簡寫(沒有考慮計算屬性被修改的情況) /* person.fullName = computed(()=>{ return person.firstName + '-' + person.lastName }) */ //計算屬性——完整寫法(考慮讀和寫) person.fullName = computed({ get(){ return person.firstName + '-' + person.lastName }, set(value){ const nameArr = value.split('-') person.firstName = nameArr[0] person.lastName = nameArr[1] } }) //返回一個物件(常用) return { person } } } </script>
watch函式
- 與Vue2.x中watch配置功能一致
- 兩個小“坑”:
- 監視reactive定義的響應式資料時:oldValue無法正確獲取、強制開啟了深度監視(deep配置失效)。
- 監視reactive定義的響應式資料中某個屬性時:deep配置有效。
<template> <h2>當前求和為:{{sum}}</h2> <button @click="sum++">點我+1</button> <hr> <h2>當前的資訊為:{{msg}}</h2> <button @click="msg+='!'">修改資訊</button> <hr> <h2>姓名:{{person.name}}</h2> <h2>年齡:{{person.age}}</h2> <h2>薪資:{{person.job.j1.salary}}K</h2> <button @click="person.name+='~'">修改姓名</button> <button @click="person.age++">增長年齡</button> <button @click="person.job.j1.salary++">漲薪</button> </template> <script> import {ref,reactive,watch} from 'vue' export default { name: 'Demo', setup(){ //資料 let sum = ref(0) let msg = ref('你好啊') let person = reactive({ name:'張三', age:18, job:{ j1:{ salary:20 } } }) //情況一:監視ref所定義的一個響應式資料 /* watch(sum,(newValue,oldValue)=>{ console.log('sum變了',newValue,oldValue) },{immediate:true}) */ //情況二:監視ref所定義的多個響應式資料 /* watch([sum,msg],(newValue,oldValue)=>{ console.log('sum或msg變了',newValue,oldValue) },{immediate:true}) */ /* 情況三:監視reactive所定義的一個響應式資料的全部屬性 1.注意:此處無法正確的獲取oldValue 2.注意:強制開啟了深度監視(deep配置無效) */ /* watch(person,(newValue,oldValue)=>{ console.log('person變化了',newValue,oldValue) },{deep:false}) //此處的deep配置無效 */ //情況四:監視reactive所定義的一個響應式資料中的某個屬性 /* watch(()=>person.name,(newValue,oldValue)=>{ console.log('person的name變化了',newValue,oldValue) }) */ //情況五:監視reactive所定義的一個響應式資料中的某些屬性 /* watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{ console.log('person的name或age變化了',newValue,oldValue) }) */ //特殊情況 /* watch(()=>person.job,(newValue,oldValue)=>{ console.log('person的job變化了',newValue,oldValue) },{deep:true}) //此處由於監視的是reactive素定義的物件中的某個屬性,所以deep配置有效 */ //返回一個物件(常用) return { sum, msg, person } } } </script>
watch監視ref資料的說明
<template> <h2>當前求和為:{{sum}}</h2> <button @click="sum++">點我+1</button> <hr> <h2>當前的資訊為:{{msg}}</h2> <button @click="msg+='!'">修改資訊</button> <hr> <h2>姓名:{{person.name}}</h2> <h2>年齡:{{person.age}}</h2> <h2>薪資:{{person.job.j1.salary}}K</h2> <button @click="person.name+='~'">修改姓名</button> <button @click="person.age++">增長年齡</button> <button @click="person.job.j1.salary++">漲薪</button> </template> <script> import {ref,reactive,watch} from 'vue' export default { name: 'Demo', setup(){ //資料 let sum = ref(0) let msg = ref('你好啊') let person = ref({ name:'張三', age:18, job:{ j1:{ salary:20 } } }) console.log(person) watch(sum,(newValue,oldValue)=>{ console.log('sum的值變化了',newValue,oldValue) }) /* person 為 reactive 函式的物件 deep:true 需要開啟深度監視 person.value 直接監視 reactive 函式的物件 可以參考上面 情況三 */ watch(person,(newValue,oldValue)=>{ console.log('person的值變化了',newValue,oldValue) },{deep:true}) //返回一個物件(常用) return { sum, msg, person } } } </script>
watchEffect函式
- watch的套路是:既要指明監視的屬性,也要指明監視的回撥。
- watchEffect的套路是:不用指明監視哪個屬性,監視的回撥中用到哪個屬性,那就監視哪個屬性。
- watchEffect有點像computed:
- 但computed注重的計算出來的值(回撥函式的返回值),所以必須要寫返回值。
- 而watchEffect更注重的是過程(回撥函式的函式體),所以不用寫返回值。
<template> <h2>當前求和為:{{sum}}</h2> <button @click="sum++">點我+1</button> <hr> <h2>當前的資訊為:{{msg}}</h2> <button @click="msg+='!'">修改資訊</button> <hr> <h2>姓名:{{person.name}}</h2> <h2>年齡:{{person.age}}</h2> <h2>薪資:{{person.job.j1.salary}}K</h2> <button @click="person.name+='~'">修改姓名</button> <button @click="person.age++">增長年齡</button> <button @click="person.job.j1.salary++">漲薪</button> </template> <script> import {ref,reactive,watch,watchEffect} from 'vue' export default { name: 'Demo', setup(){ //資料 let sum = ref(0) let msg = ref('你好啊') let person = reactive({ name:'張三', age:18, job:{ j1:{ salary:20 } } }) //監視watch('監視屬性','回撥','配置') /* watch(sum,(newValue,oldValue)=>{ console.log('sum的值變化了',newValue,oldValue) },{immediate:true}) */ //不用指明監視哪個屬性,監視的回撥中用到哪個屬性,那就監視哪個屬性 watchEffect(()=>{ const x1 = sum.value const x2 = person.job.j1.salary console.log('watchEffect所指定的回撥執行了') }) //返回一個物件(常用) return { sum, msg, person } } } </script>
8.生命週期
2.0與3.0對比
vue2 | vue3 |
|
setup()
|
|
setup()
|
|
|
|
|
|
|
|
|
|
|
|
|
程式碼例項
<template> <h2>當前求和為:{{sum}}</h2> <button @click="sum++">點我+1</button> </template> <script> //匯入生命週期 import {ref,onBeforeMount,onMounted, onBeforeUpdate,onUpdated, onBeforeUnmount,onUnmounted} from 'vue' export default { name: 'Demo', setup(){ console.log('---setup---') //資料 let sum = ref(0) //通過組合式API的形式去使用生命週期鉤子 //先執行組合式 之後 配置項的形式 生命週期 onBeforeMount(()=>{ console.log('---onBeforeMount---') }) onMounted(()=>{ console.log('---onMounted---') }) onBeforeUpdate(()=>{ console.log('---onBeforeUpdate---') }) onUpdated(()=>{ console.log('---onUpdated---') }) onBeforeUnmount(()=>{ console.log('---onBeforeUnmount---') }) onUnmounted(()=>{ console.log('---onUnmounted---') }) //返回一個物件(常用) return {sum} }, //通過配置項的形式使用生命週期鉤子 //先執行組合式 之後 配置項的形式 生命週期 //#region 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---') }, //#endregion } </script>