vue3.x已經發布了這麼久,相關的生態也慢慢起來了,包括vite這個新的打包工具,在vue3.0學習過程當中有一些實用性的api對比,但願能在開發中給你們作個示範,準確的使用對應的api去完成咱們的項目開發前端
要特別說明一下的就是,setup
函數代替了 beforeCreate
和 created
兩個生命週期函數,所以咱們能夠認爲它的執行時間在beforeCreate
和 created
之間vue
Vue2 | Vue3 |
---|---|
beforeCreate | setup |
created | setup |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestory | onBeforeUnmount |
destoryed | onUnmounted |
瞭解過vue3的小夥伴兒都知道,如今使用都會用到setup函數,關於在setup函數操做數據,咱們用例子說明會好一點react
reactive
方法是用來建立一個響應式的數據對象,該API也很好地解決了Vue2經過 defineProperty
實現數據響應式的缺陷vuex
用法很簡單,只需將數據做爲參數傳入便可api
<template> <div id="app"> <!-- 4. 訪問響應式數據對象中的 count --> {{ state.count }} </div> </template> <script> // 1. 從 vue 中導入 reactive import {reactive} from 'vue' export default { name: 'App', setup() { // 2. 建立響應式的數據對象 const state = reactive({count: 3}) // 3. 將響應式數據對象state return 出去,供template使用 return {state} } } </script>
在介紹 setup
函數時,咱們使用了 ref
函數包裝了一個響應式的數據對象,這裏表面上看上去跟 reactive
好像功能如出一轍啊,確實差很少,由於 ref
就是經過 reactive
包裝了一個對象 ,而後是將值傳給該對象中的 value
屬性,這也就解釋了爲何每次訪問時咱們都須要加上 .value
性能優化
咱們能夠簡單地把 ref(obj)
理解爲這個樣子 reactive({value: obj})
app
<script> import {ref, reactive} from 'vue' export default { name: 'App', setup() { const obj = {count: 3} const state1 = ref(obj) const state2 = reactive(obj) console.log(state1) console.log(state2) } } </script>
注意: 這裏指的 .value
是在 setup
函數中訪問 ref
包裝後的對象時才須要加的,在 template
模板中訪問時是不須要的,由於在編譯時,會自動識別其是否爲 ref
包裝過的函數
ref
和 reactive
呢?建議:工具
String
、Nmuber
、Boolean
等)或單值對象(相似像 {count: 3}
這樣只有一個屬性值的對象)使用 ref
Object
、Array
)使用 reactive
ref
,vue3.x咱們要獲取元素標籤怎麼辦呢?<template> <div> <div ref="el">div元素</div> </div> </template> <script> import { ref, onMounted } from 'vue' export default { setup() { // 建立一個DOM引用,名稱必須與元素的ref屬性名相同 const el = ref(null) // 在掛載後才能經過 el 獲取到目標元素 onMounted(() => { el.value.innerHTML = '內容被修改' }) // 把建立的引用 return 出去 return {el} } } </script>
獲取元素的操做一共分爲如下幾個步驟:性能
ref
屬性設置一個值,假設爲 el
setup
函數中調用 ref
函數,值爲 null
,並賦值給變量 el
,這裏要注意,該變量名必須與咱們給元素設置的 ref
屬性名相同el
返回(return)出去補充:設置的元素引用變量只有在組件掛載後才能訪問到,所以在掛載前對元素進行操做都是無效的
固然若是咱們引用的是一個組件元素,那麼得到的將是該組件的實例對象
toRef
是將某個對象中的某個值轉化爲響應式數據,其接收兩個參數,第一個參數爲 obj
對象;第二個參數爲對象中的屬性名
<script> // 1. 導入 toRef import {toRef} from 'vue' export default { setup() { const obj = {count: 3} // 2. 將 obj 對象中屬性count的值轉化爲響應式數據 const state = toRef(obj, 'count') // 3. 將toRef包裝過的數據對象返回供template使用 return {state} } } </script>
上面又有個ref,又有個toRef,不是衝突了嗎?兩個有不同的功效:
<template> <p>{{ state1 }}</p> <button @click="add1">增長</button> <p>{{ state2 }}</p> <button @click="add2">增長</button> </template> <script> import {ref, toRef} from 'vue' export default { setup() { const obj = {count: 3} const state1 = ref(obj.count) const state2 = toRef(obj, 'count') function add1() { state1.value ++ console.log('原始值:', obj); console.log('響應式數據對象:', state1); } function add2() { state2.value ++ console.log('原始值:', obj); console.log('響應式數據對象:', state2); } return {state1, state2, add1, add2} } } </script>
ref
是對原數據的一個拷貝,不會影響到原始值,同時響應式數據對象值改變後會同步更新視圖toRef
是對原數據的一個引用,會影響到原始值,可是響應式數據對象值改變後會不會更新視圖
將傳入的對象裏全部的屬性的值都轉化爲響應式數據對象,該函數支持一個參數,即 obj
對象
<script> // 1. 導入 toRefs import {toRefs} from 'vue' export default { setup() { const obj = { name: '前端印象', age: 22, gender: 0 } // 2. 將 obj 對象中屬性count的值轉化爲響應式數據 const state = toRefs(obj) // 3. 打印查看一下 console.log(state) } } </script>
返回的是一個對象,對象裏包含了每個包裝事後的響應式數據對象
聽這個API的名稱就知道,這是一個淺層的 reactive
,難道意思就是本來的 reactive
是深層的唄,沒錯,這是一個用於性能優化的API
<script> <template> <p>{{ state.a }}</p> <p>{{ state.first.b }}</p> <p>{{ state.first.second.c }}</p> <button @click="change1">改變1</button> <button @click="change2">改變2</button> </template> <script> import {shallowReactive} from 'vue' export default { setup() { const obj = { a: 1, first: { b: 2, second: { c: 3 } } } const state = shallowReactive(obj) function change1() { state.a = 7 } function change2() { state.first.b = 8 state.first.second.c = 9 console.log(state); } return {state} } } </script>
首先咱們點擊了第二個按鈕,改變了第二層的 b
和第三層的 c
,雖然值發生了改變,可是視圖卻沒有進行更新;
當咱們點擊了第一個按鈕,改變了第一層的 a
時,整個視圖進行了更新;
由此可說明,shallowReactive
監聽了第一層屬性的值,一旦發生改變,則更新視圖
這是一個淺層的 ref
,與 shallowReactive
同樣是拿來作性能優化的,配合triggerRef
,調用它就能夠立馬更新視圖,其接收一個參數 state
,即須要更新的 ref
對象
shallowReactive
是監聽對象第一層的數據變化用於驅動視圖更新,那麼 shallowRef
則是監聽 .value
的值的變化來更新視圖的
<template> <p>{{ state.a }}</p> <p>{{ state.first.b }}</p> <p>{{ state.first.second.c }}</p> <button @click="change">改變</button> </template> <script> import {shallowRef, triggerRef} from 'vue' export default { setup() { const obj = { a: 1, first: { b: 2, second: { c: 3 } } } const state = shallowRef(obj) console.log(state); function change() { state.value.first.b = 8 state.value.first.second.c = 9 // 修改值後當即驅動視圖更新 triggerRef(state) console.log(state); } return {state, change} } } </script>
toRaw
方法是用於獲取 ref
或 reactive
對象的原始數據的
<script> import {reactive, toRaw} from 'vue' export default { setup() { const obj = { name: '前端印象', age: 22 } const state = reactive(obj) const raw = toRaw(state) console.log(obj === raw) // true } } </script>
上述代碼就證實了 toRaw
方法從 reactive
對象中獲取到的是原始數據,所以咱們就能夠很方便的經過修改原始數據的值而不更新視圖來作一些性能優化了
注意: 補充一句,當toRaw
方法接收的參數是ref
對象時,須要加上.value
才能獲取到原始數據對象
markRaw
方法能夠將原始數據標記爲非響應式的,即便用 ref
或 reactive
將其包裝,仍沒法實現數據響應式,其接收一個參數,即原始數據,並返回被標記後的數據。即便咱們修改了值也不會更新視圖了,即沒有實現數據響應式
<template> <p>{{ state.name }}</p> <p>{{ state.age }}</p> <button @click="change">改變</button> </template> <script> import {reactive, markRaw} from 'vue' export default { setup() { const obj = { name: '前端印象', age: 22 } // 經過markRaw標記原始數據obj, 使其數據更新再也不被追蹤 const raw = markRaw(obj) // 試圖用reactive包裝raw, 使其變成響應式數據 const state = reactive(raw) function change() { state.age = 90 console.log(state); } return {state, change} } } </script>
watchEffect
它與 watch
的區別主要有如下幾點:
<script> import {reactive, watchEffect} from 'vue' export default { setup() { const state = reactive({ count: 0, name: 'zs' }) watchEffect(() => { console.log(state.count) console.log(state.name) /* 初始化時打印: 0 zs 1秒後打印: 1 ls */ }) setTimeout(() => { state.count ++ state.name = 'ls' }, 1000) } } </script>
沒有像 watch
方法同樣先給其傳入一個依賴,而是直接指定了一個回調函數
當組件初始化時,將該回調函數執行一次,自動獲取到須要檢測的數據是 state.count
和 state.name
根據以上特徵,咱們能夠自行選擇使用哪個監聽器
咱們都知道在Vue2的任何一個組件中想要獲取當前組件的實例能夠經過 this
來獲得,而在Vue3中咱們大量的代碼都在 setup
函數中運行,而且在該函數中 this
指向的是undefined
,那麼該如何獲取到當前組件的實例呢?這時能夠用到另外一個方法,即 getCurrentInstance
評論中反饋:在生產環境就沒有用,沒法獲得ctx上下文
<template> <p>{{ num }}</p> </template> <script> import {ref, getCurrentInstance} from 'vue' export default { setup() { const num = ref(3) const instance = getCurrentInstance() console.log(instance) return {num} } } </script>
instance
中重點關注 ctx
和 proxy
屬性,這兩個纔是咱們想要的 this
。能夠看到 ctx
和 proxy
的內容十分相似,只是後者相對於前者外部包裝了一層 proxy
,由此可說明 proxy
是響應式的
在Vue2中使用 Vuex,咱們都是經過 this.$store
來與獲取到Vuex實例,但上一部分說了本來Vue2中的 this
的獲取方式不同了,而且咱們在Vue3的 getCurrentInstance().ctx
中也沒有發現 $store
這個屬性,那麼如何獲取到Vuex實例呢?這就要經過 vuex
中的一個方法了,即 useStore
// store 文件夾下的 index.js import Vuex from 'vuex' const store = Vuex.createStore({ state: { name: '前端印象', age: 22 }, mutations: { …… }, …… }) // example.vue <script> // 從 vuex 中導入 useStore 方法 import {useStore} from 'vuex' export default { setup() { // 獲取 vuex 實例 const store = useStore() console.log(store) } } </script>
而後接下來就能夠像以前同樣正常使用 vuex
了