接到新項目,需要用vue3讓我瞅瞅有什麼新特性?
highlight: an-old-hope
1.Vue3簡介
-
相關閲讀:
- Vue3 中文文檔 https://vue3js.cn/docs/zh/
- Vue3 設計理念 https://vue3js.cn/vue-composition/
2.vue3--你知道哪些
2.1 性能的提升
-
打包大小減少41%
-
初次渲染快55%, 更新渲染快133%
-
內存減少54%
......
2.源碼的升級
-
使用Proxy代替defineProperty實現響應式
-
重寫虛擬DOM的實現和Tree-Shaking
......
3.擁抱TypeScript
- Vue3可以更好的支持TypeScript
4.新的特性
-
Composition API(組合API)
- setup配置
- ref與reactive
- watch與watchEffect
- provide與inject
- ......
-
新的內置組件
- Fragment
- Teleport
- Suspense
-
其他改變
- 新的生命週期鈎子
- data 選項應始終被聲明為一個函數
- 移除keyCode支持作為 v-on 的修飾符
- ......
5. Composition API 的優勢
-
Options API 存在的問題
使用傳統OptionsAPI中,新增或者修改一個需求,就需要分別在data,methods,computed裏修改 。
-
Composition API 的優勢
我們可以更加優雅的組織我們的代碼,函數。讓相關功能的代碼更加有序的組織在一起。
3.常用 Composition API
3.1 setup
理解:Vue3.0中一個新的配置,值為一個函數
-
組件中所用到的:數據、方法等等,均要配置在setup中
-
setup函數的兩種返回值:
- 若返回一個對象,則對象中的屬性、方法, 在模板中均可以直接使用。(重點關注!)
- 若返回一個渲染函數:則可以自定義渲染內容。(瞭解)
-
steup 執行時機
- 在beforeCreate之前執行一次,this是undefined。
-
setup的參數
-
props:值為對象,包含:組件外部傳遞過來,且組件內部聲明接收了的屬性。
-
context:上下文對象
- attrs: 值為對象,包含:組件外部傳遞過來,但沒有在props配置中聲明的屬性, 相當於
this.$attrs
。 - slots: 收到的插槽內容, 相當於
this.$slots
。 - emit: 分發自定義事件的函數, 相當於
this.$emit
。
- attrs: 值為對象,包含:組件外部傳遞過來,但沒有在props配置中聲明的屬性, 相當於
-
-
注意點:
-
儘量不要與Vue2.x配置混用
- Vue2.x配置(data、methos、computed...)中可以訪問到setup中的屬性、方法。
- 但在setup中不能訪問到Vue2.x配置(data、methos、computed...)。
- 如果有重名, setup優先。
-
setup不能是一個async函數,因為返回值不再是return的對象,而是promise,模板看不到return對象中的屬性。(後期也可以返回一個Promise實例,但需要Suspense和異步組件的配合)
-
3.2 ref
-
作用:定義一個響應式的數據
-
語法:
const xxx = ref(initValue)
- 創建一個包含響應式數據的引用對象(reference對象,簡稱ref對象)
- JS中操作數據:
xxx.value
- 模板中讀取數據: 不需要.value,直接:
<div>{{xxx}}</div>
-
其他
- 接收的數據可以是:基本類型、也可以是對象類型。
- 基本類型的數據:響應式依然是靠
Object.defineProperty()
的get
與set
完成的。 - 對象類型的數據:內部 “ 求助 ” 了Vue3.0中的一個新函數——
reactive
函數(也就是ES6的Proxy 代理)。
3.3 reactive函數
-
作用: 定義一個
對象類型
的響應式數據(基本類型不要用它,要用ref
函數) - 語法:
const 代理對象= reactive(源對象)
接收一個對象(或數組),返回一個代理對象(Proxy的實例對象,簡稱proxy對象) - reactive定義的響應式數據是“深層次的”。
- 內部基於 ES6 的 Proxy 實現,通過代理對象操作源對象內部數據進行操作
3.4 computed函數
-
配置與vue2.x中computed配置功能一致
-
寫法
js import {computed} from 'vue' setup(){ ... //計算屬性——簡寫(沒有考慮寫的情況) let fullName = computed(()=>{ return person.firstName + '-' + person.lastName }) //計算屬性——完整(考慮讀和寫) let fullName = computed({ get(){ return person.firstName + '-' + person.lastName }, set(value){ const nameArr = value.split('-') person.firstName = nameArr[0] person.lastName = nameArr[1] } }) }
3.5 watch函數
-
與vue2.x中的watch配置功能一致
-
支持三個參數
-
兩個小坑
- 監視
reactive
定義的響應式數據時:oldValue
無法正確獲取,強制開啟嘗試監視 - 監視
reactive
內某個屬性時:deep配置有效,(如對象類型有意義,基本數據類型無意義)
js import {ref,reactive,watch} from "vue" setup(){ 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) },) /* 情況三:監視reactive定義的響應式數據 若watch監視的是reactive定義的響應式數據,則無法正確獲得oldValue!! 若watch監視的是reactive定義的響應式數據,則強制開啟了深度監視 */ watch(person,(newValue,oldValue)=>{ console.log('person變化了',newValue,oldValue) },{immediate:true,deep:false}) //此處的deep配置不再奏效 //情況四:監視reactive定義的響應式數據中的某個屬性 watch(()=>person.job,(newValue,oldValue)=>{ console.log('person的job變化了',newValue,oldValue) },{immediate:true,deep:true}) //情況五:監視reactive定義的響應式數據中的某些屬性 watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{ console.log('person的job變化了',newValue,oldValue) },{immediate:true,deep:true}) //特殊情況 watch(()=>person.job,(newValue,oldValue)=>{ console.log('person的job變化了',newValue,oldValue) },{deep:true}) //此處由於監視的是reactive素定義的對象中的某個屬性,所以deep配置有效 }
- 監視
3.6 watchEffect函數
-
watch的套路是:既要指明監視的屬性,也要指明監視的回調
-
watchEffect
的套路是:不用指明監視哪個屬性,監視的回調中用到哪個屬性,那就監視哪個屬性。 -
watchEffect
有點像computed):- 但
computed
注重的計算出來的值(回調函數的返回值),所以必須要寫返回值。 - 而watchEffect更注重的是過程(回調函數的函數體),所以不用寫返回值。
js //watchEffect所指定的回調中用到的數據只要發生變化,則直接重新執行回調。 watchEffect(()=>{ const x1 = sum.value const x2 = person.age console.log('watchEffect配置的回調執行了') })
- 但
3.7 hook函數
- 什麼是hook?—— 本質是一個函數,把setup函數中使用的Composition API進行了封裝。
- 類似於vue2.x中的mixin。缺點是組件的data,methods會覆蓋mixins裏的同名data,methods
- 自定義hook的優勢: 複用代碼, 讓setup中的邏輯更清楚易懂。
- Vue3 hook 庫Get Started | VueUse
案例 本地圖片轉 Base64
```js import { onMounted } from 'vue'
type Options = { el: string }
type Return = {
Baseurl: string | null
}
export default function (option: Options): Promise
return new Promise((resolve) => { onMounted(() => { const file: HTMLImageElement = document.querySelector(option.el) as HTMLImageElement; file.onload = ():void => { resolve({ Baseurl: toBase64(file) }) }
})
const toBase64 = (el: HTMLImageElement): string => { const canvas: HTMLCanvasElement = document.createElement('canvas') const ctx = canvas.getContext('2d') as CanvasRenderingContext2D canvas.width = el.width canvas.height = el.height ctx.drawImage(el, 0, 0, canvas.width,canvas.height) console.log(el.width); return canvas.toDataURL('image/png')
} })
} ```
使用
js
import useBase64 from './hooks'
useBase64({el:'#img'}).then(res=>{
console.log(res)
})
3.8 toRef
-
作用:創建一個 ref 對象,其value值指向另一個對象中的某個屬性。
-
語法:
const name = toRef(obj, 'bar')
-
應用: 要將響應式對象中的某個屬性單獨提供給外部使用時。
```js
```
3.9 toRefs
-
擴展:
toRefs
與toRef
功能一致,但可以批量創建多個 ref 對象,語法:toRefs(person)
-
應用:批量創建ref對象主要 是方便我們解構使用
```js import { reactive, toRefs } from 'vue' const obj = reactive({ foo: 1, bar: 1 })
let { foo, bar } = toRefs(obj)
foo.value++ console.log(foo, bar); ```
4 . 其他 Composition API
4.1shallowReactive 與 shallowRef
-
shallowReactive
:只處理對象最外層屬性的響應式(淺響應式) -
shallowRef
:只處理基本數據類型的響應式, 不進行對象的響應式處理。 -
什麼時候使用?
-
如果有一個對象數據,結構比較深,但變化 時只是外層屬性變化==>
shallowReactive
-
如果有一個對象數據,後續功能不會修改該對象中的屬性,而是生新的對象來替換===> shallowRef
-
4.2 readonly 與shallowReadonly
- readonly: 讓一個響應式數據變為只讀的(深只讀)。
- shallowReadonly:讓一個響應式數據變為只讀的(淺只讀)。
- 應用場景: 不希望數據被修改時。
4.3 toRaw 與 markRaw
-
toRaw
- 作用:將一個由
reactive
生成的 響應式對象 轉為 普通對象 - 使用場景:用於讀取響應式對象=>對應的普通對象,對這個普通對象的所有操作,不會引起頁面的更新
- 作用:將一個由
-
markRaw
-
作用:標記一個對象,使用其永遠不會再成為響應式對象
-
應用場景:
- 有些值不應被設置為響應式的,例如複雜的第三方類庫
- 當渲染具有不可變數據源的大列表時,跳過響應式轉換可以提高性能
-
4.4 customRef
-
作用:創建一個自定義的ref、並對其依賴項跟蹤和更新觸發進行顯示控制
-
實現防抖效果:
js <template> <input type="text" v-model="keyword"> <h3>{{keyword}}</h3> </template> <script> import {ref,customRef} from 'vue' export default { name:'Demo', setup(){ // let keyword = ref('hello') //使用Vue準備好的內置ref //自定義一個myRef function myRef(value,delay){ let timer //通過customRef去實現自定義 return customRef((track,trigger)=>{ return{ get(){ track() //告訴Vue這個value值是需要被“追蹤”的 return value }, set(newValue){ clearTimeout(timer) timer = setTimeout(()=>{ value = newValue trigger() //告訴Vue去更新界面 },delay) } } }) } let keyword = myRef('hello',500) //使用程序員自定義的ref return { keyword } } } </script>
4.5 provide 與 inject
-
作用:實現
祖與後代
組件間通信 (如是父子
之間通信 建議使用props) -
套路: 父組件有一個
provide
選項來提供數據,後代組件有一個inject
選項來開始使用這些數據 -
具體寫法:
注意:如果傳遞普通的值 是不具有響應式的 需要通過ref reactive 添加響應式
-
祖組件中:
js setup(){ ...... let car = reactive({name:'奔馳',price:'40萬'}) provide('car',car) //可以用來傳遞響影數據 ...... }
0. 後代組件中:js setup(props,context){ ...... const car = inject('car') return {car} ...... }
4.6響應式數據的判斷
- isRef: 檢查一個值是否為一個 ref 對象
- isReactive: 檢查一個對象是否是由
reactive
創建的響應式代理 - isReadonly: 檢查一個對象是否是由
readonly
創建的只讀代理 - isProxy: 檢查一個對象是否是由
reactive
或者readonly
方法創建的代理
5.語法糖
5.1 setup
```js
```
-
在
<script setup>
包裹 的任何在內部聲明的頂級綁定(包括變量,函數聲明和導入)都可以直接在模板中使用 -
setup()
函數返回的值 ,引用在模板中引用時會自動展開js <script setup> // variable const msg = 'Hello!' // functions function log() { console.log(msg) } </script> <template> <div @click="log">{{ msg }}</div> </template>
-
使用組件
建議PascalCase 命名的組件標籤以保持一致性,它有助於區分原生自定義元素
js <script setup> import MyComponent from './MyComponent.vue' </script> <template> <MyComponent /> //不使用my-Component是因為區別是否為原生元素 </template>
-
動態組件
在setup的語法糖的便攜下,由於組件被引用為變量而不是在字符串鍵下注冊,因此
:is
在內部使用動態組件時應該使用動態綁定<script setup>
:js <script setup> import Foo from './Foo.vue' import Bar from './Bar.vue' </script> <template> <component :is="Foo" /> <component :is="someCondition ? Foo : Bar" /> </template>
-
組件之間的傳值
要聲明具有完整類型推斷支持的選項
props
,emits
我們可以使用defineProps
和defineEmits
API,它們在內部自動可用<script setup>
:js <script setup> const props = defineProps({ foo: String }) const emit = defineEmits(['change', 'delete']) // setup code </script>
-
頂層 await
頂層
await
可以在裏面使用<script setup>
。生成的代碼將編譯為async setup()
:js <script setup> const post = await fetch(`/api/post/1`).then((r) => r.json()) </script>
-
useSlots()
&
useAttrs()- 插槽
useSlots()
==setupContext.slots
- useAttrs() 相當於
setupContext.attrs
您可以直接在模板
$slots
中訪問它們。$attrs
在您確實需要它們的極少數情況下,請分別使用useSlots
和useAttrs
助手: - 插槽
-
對外暴露屬性 (defineExpose)
在vue3.x的setup語法糖中定義的變量默認不會暴露出去,需要使用defineExpose({}) 來暴露組件內部屬性給父組件使用為了在