初探Vue3.0--響應式api理解~

掘金的第一篇文章獻給vue3.0,html

vue3.0的正式版發佈有必定的時間了,忙於其餘,雖然好奇但沒有進行比較和學習,終於不負本身「指望」,打敗了「懶惰」,學習相關熟悉的技術新版本,其實入手仍是比較簡單的(坐等打臉),本文主要是筆者本身在學習過程當中的探索;vue

組合api(Composition API)

最大的感覺,組合api是將具備獨立功能的函數封裝組合在一塊兒,提升複用性; 後來實際封裝了相關功能,變量較多的狀況下,設計高內聚的功能是很是必要的react

demo 學生和教師顯示

分別用vue2.x以及vue3.x進行了學生列表和老師列表的展現,其代碼功能分部圖以下圖所示es6

vue2.x 學生和老師展現代碼功能圖 vue 2.x
api

vue 3.x! 學生和老師展現代碼功能圖 其中不一樣顏色表示頁面中兩個功能的代碼分佈;markdown

  • 對於vue2.x而言,主要分爲三個部分
    • data中的數據定義
    • methods中的方法定義
    • 視圖使用
  • 對於Vue3.x而言
    • 學生、老師中的變量和功能定義
    • setup中引用功能
    • 視圖使用

根據顏色而言,其實vue2.x的功能相對而言是比較分散的,而vue3.0中的功能和數據定義能夠定義在一個函數中,功能比較集中;所以在進行組合性api的是時候是須要思考如何設計是比較合理的;vue3.x在功能上可以實現高度的一個複用,想到vue.mixin的方法,實現代碼的複用;數據結構

響應式數據定義

響應式數據是實現mvvm很是重要的一個部分,在vue3.x中,在支持data方式定義響應式的數據外,還提供不少可自定義的api;app

在響應式的數據中值得注意的部分 深層次響應仍是淺層次響應;dom

reactive

定義響應式數據的一種方式,經過 Proxy包裝傳入的變量,藉助Proxy提供的set和get方法,實現響應式數據的邏輯異步

  • vue3中提供實現響應式數據的方法
  • vue2中響應式數據是經過 O b j e c . d e f i n e P r o p e r t y \color{red}{Objec.defineProperty} 來實現的,而在vue3中數據經過es6的 P r o x y \color{red}{Proxy} 來實現的,

reactive使用

setup(props) {
     let state =reactive({
        msg:{
          name:'mafe',
          age:24, 
        },
        time: new Date(), //只能進行 賦值更新
        list:[
          {name:'mfy'},
          {name:'mfy1'},
          {name:'mfy2'},
        ]
     })
     function changeName(){
       //定義的是對象可以直接修改賦值
       state.msg.name = '333'
        //沒法更改state中time的賦值對象 
       // state.time.setDate(state.time.getDate()+1)
       let newDate =new Date(state.time.getTime())
       newDate.setDate(state.time.getDate()+1) 
       state.time = newDate ;//不是對象類型 只能修改數據內容
     }
     return  {
       state,
       changeName
     }
  }

複製代碼

reactive定義數據

reactive 使用注意事項

  • reactive定義的基礎類型數據
    • 使用只能經過state.time = XXX 賦值的方式進行更改;
    • 得到到的只能是當前的值,好比time初始使用new Date() 進行賦值,獲得的time的原型並不是是Date的,再次修改time的值的時候不能經過setDate賦值;
  • reactive定義複雜數據類型
    • 對象的每一層都會被包裝成一個Proxy的代理,用於頁面的響應式數據構建;
    • 更改對象中的某一個屬性的值都會所以視圖的響應;

ref

也是定義響應式數據的一種,一般用來定義基礎類型數據,以及獲取dom元素的內容 reactive 一般用於定義對象,在咱們實際的使用過程當中是很是不便捷的,咱們僅僅是想要某個變量實現響應式,ref是對簡單的值進行監聽;

ref使用

ref的本質仍是reactive,系統會自動根據咱們傳入的值轉換成ref(xxx)->reactive({value:xx})

setup(props) {
    // 傳遞一個值 
     let age = ref(33);
     function changeAge(){ 
       age.value +=1;
     }
     return{
       age,
       changeAge
     }
  }
複製代碼

ref 定義的內容:

ref的注意點

  • 在Vue中使用ref的值不用經過value獲取;
  • 在js中使用ref的值必需要用value獲取;
  • 在setup 中定義的變量是沒法在method中獲取的;

triggerRef 更新變量

ref定義對象類型時候更新問題 ref定義的對象類型,若是是隻想更改某一層的變化 須要調用triggerRef進行觸發更新

// 只想更改ref中某一層的變化
  msg.value.a = 'myy'
  //增長triggerRef 引發視圖的更新 
  // Vue3 只提供了triggerRef 沒有提供reactRef相關的
  triggerRef(msg)
複製代碼

unRef返回其原始值

若是參數爲 ref,則返回內部值,不然返回參數自己。這是 val = isRef(val) ? val.value : val

let age = ref(33);
console.log(age)
console.log(unref(age))
複製代碼

ref 和reactive的擴展講解

ref 和reactive的區別

  • 在template中使用ref類型的數據,vue會自動幫咱們計算.value
  • 在template中使用reactive類型的數據,vue不會自動幫咱們計算.value

vue 是如何決定是否自動添加.value

Vue在解析數據以前,會自動判斷這個數據是不是ref類型的,若是是,則自動添加上.value,若是不是,則不會自動添加

vue 是如何判斷當前數據是不是ref類型的

  • 經過當前按數據的__v_isRef來進行判斷的,
  • 若是有私有的屬性,而且取值爲true,那麼就表明是一個ref類型的數據
console.log("判斷是不是ref類型數據",isRef(age))
 console.log("判斷是不是isReactive類型數據",isReactive(age))
複製代碼

監聽層級問題

不管是ref仍是reactive的都是遞歸監聽的過程,也就是不管咱們嵌套了多少層級,其每一層的每一個數據都會發生監聽;

遞歸監聽

reactive

let state= reactive({
      a:'2',
      gf:{
        b:'b',
        f:'c',
        c:{
          a:3
        }
      }
    })
  // 多個層級 每一個層級內容改變的時候,都會改變;
    function changeValue(){
      console.log(state)
      console.log(state.gf)
    }
複製代碼

打印後能夠發如今reactive中定義的對象,發現對象中的每一層對象都是經過Proxy進行代理

ref

let msg = ref({
       a:'2',
        gf:{
          b:'b',
          f:'c',
            c:{
            a:3
          }
        }
    }) 
 function changeRef(){
    //直接更改ref 
    console.log(msg.value)
    console.log(msg.value.gf) 
 }
複製代碼

非遞歸監聽

只監聽對象的第一層,

  • 只有第一層被包裝,第二層未被包裝;
  • 第一層的數據發生改變,第一層的ui也會變化
  • 第二層的數據修改,可是不會更新ui
  • shallowRef Vue監聽的是.value的變化,並非第一層的變化

利用shallowRef 和shallowReactive可以改變當前對象的監聽層級

shallowReactive

let state = shallowReactive({
      a:'2',
      gf:{
        b:'b',
        f:'c',
        c:{
          a:3
        }
      }
    })
  function changeValue(){
      console.log(state)
      console.log(state.gf)
    }
複製代碼

第二層的對象不會包裝成響應式內容

shallowRef

let msg = shallowRef({
       a:'2',
        gf:{
          b:'b',
          f:'c',
            c:{
            a:3
          }
        }
    })
function changeRef(){ 
      console.log(msg)
      console.log(msg.value)
      console.log(msg.value.gf) 
    }
複製代碼

注意內容:

  • 經過ref定義的內容會被reactive包裝成一個.value的對象,所以這個對象的第一層的就是value
  • 當訪問msg.value的時候,其實已經訪問它的值了,天然是沒有內容的;

readonly

只讀屬性的修改,使用readonly防止更改響應式對象

存在一組對象,咱們只能讀取,可是不能更改,所以可使用readonly進行包裹;

  • 對於對象深度包裹,遞歸到最底層的變量
let state = readonly({name:'mfy',age:34,attr:{weight:45,height:1.88}})
  function changeReadonly(){
      state.age = 2; //修改第一層 
      state.attr.weight = 2; //修改深層
      console.log(state)
      console.log(isReadonly(state)) 
    }
複製代碼

點擊進行修改時候

readonly定義的對象和reactive數據結構的區別

const obj = reactive({name:'33'})
  console.log(obj)
  let state = readonly({name:'mfy',age:34,attr:{weight:45,height:1.88}})
  console.log(state)
複製代碼

  • reactive定義的參數在Proxy函數的set支持傳入四個參數
  • readonly只能收到兩個參數,在set中直接進行警告提示

shallowReadonly

shallowRef以及shallowReactive的性質同樣,只能控制第一層的數據是不可修改的;

let state = shallowReadonly({name:'mfy',age:34,attr:{weight:45,height:1.88}})
console.log(state)
 function changeReadonly(){
   state.age = 2; //修改第一層
   state.attr.weight = 2; //修改第二層
   console.log(state) 
   console.log(state.attr)
}
複製代碼

  • 第二層數據的對象發生變化
  • 視圖沒法進行更新,由於第二層的數據不是響應式的
  • shllowReadonly 非遞歸包裝,只包裝第一層數據,第二層將不會進行包裝

不得不比較const

const也是定義的變量不能在修改,可是對於對象除外;

  • const 和 readonly的區別
  • const 賦值保護 不能給變量從新賦值
  • readonly 屬性保護,不能給屬性從新賦值

toRef

在實際的使用中,咱們可能不只僅是進行一整個屬性進行監聽,有多是單個的屬性進行監聽,所以toRef等給咱們提供了固定屬性進行監聽;

let obj1 = {age:34,name:'mfy'} 
  let state2 = toRef(obj1,'name')
 
  function changeRef(){
     state2.value = 333
     console.log('obj1',obj1)
     console.log('state2',state2) 
   }
複製代碼

toRef修改的值不只僅使定義的值發生了改變,其原始值也發生了改變;

  • 利用toRef將某一個對象的屬性變成響應式的,此時修改是會影響到原始值
  • 若是響應數據是經過toRef變化的,是不會觸發頁面更新的

ref和toRef

  • ref
    • 複製,修改響應式數據不會影響到之前的數據
    • 數據發生改變,ui會自動更新
  • toRef
    • 引用 修改響應式數據會影響到之前懟數據
    • 數據發生改變,ui不會自動更新

toRefs

將響應式對象轉化爲普通對象,其中結果對象的每一個 property 都是指向原始對象相應 propertyref

定義多個屬性的響應式

let state3 = toRefs(obj1,'name','age')   
複製代碼

轉化reactive定義的響應式

let obj = {age:34,name:'mfy'} 
 let state =reactive(obj);
 console.log(state) 
 let state2 = toRefs(state)
 console.log(state2) 
複製代碼

打印出來statestate2

state2中的引用和原始數據類型以及經過reactive定義的類型都是屬於同一引用關係;即修改objstatestate2中的任何一個元素屬性值,都會發生改變;

let obj = {age:34,name:'mfy'} 
   let state =reactive(obj);
   console.log(state) 
   let state2 = toRefs(state)
   console.log(state2) 
  
   function changeRef(){  
     //修改值 
     obj.age = 232323;
     console.log('state2',state2)
     console.log('state',state)
     console.log('obj',obj)

   }
複製代碼

使用場景

使用reactive定義的對象是不具備響應式的,所以在使用的時候沒法進行結構出來使用,而toRefs能夠定義每一個屬性都是響應式的,

function useFeatureX() {
  const state = reactive({
    foo: 1,
    bar: 2
  }) 
  // 返回時轉換爲ref
  return toRefs(state)
}
export default {
  setup() {
    // 能夠在不失去響應性的狀況下破壞結構
    const { foo, bar } = useFeatureX() 
    return {
      foo,
      bar
    }
  }
}
複製代碼

總結內容

找到原始數據

被定義的響應式數據,如何找到原始數據呢?

toRaw

reactive或者ref中獲得原始數據,主要是作一些不想被監聽的事情(提高性能),每次修改都會被追蹤,都會被更新UI界面,可是是很是消耗性能的,因此有一些操做咱們不須要追蹤,不須要更新ui界面,此時能夠經過 toRaw方法拿到原始數據,對原始數據進行修改,這樣不會被追蹤,不會更新ui界面

追蹤reactive的特性進行定義響應式

//默認狀況下不是一個響應式數據
   let obj = {age:34,name:'mfy'}
   let state  =reactive(obj); 
   //把建立時候的參數分解出來
   let obj2 = toRaw (state); // 和obj是一個樣子
   function changeRef(){ 
     //引用修改的內容,頁面不會自動更新 只有經過修改包裝的方法才能更新 
      obj2.name = 'myy'  
      //state和obj是引用關係,其中的一個值更改時候,另一個值也會更改
      console.log(state)
      console.log(obj) 
      console.log(obj2) 
   }
複製代碼

  • toRaw獲取到的變量,是一個引用的關係
  • 修改每個值的是時候,都會影響到所依賴的值

追蹤ref定義的數據

對於ref定義的值,必需要準確的告訴toRaw.value 若是經過toRaw 獲取ref的數據,必須明確告訴ref.value的值 通過Vue處理以後,.value中保存的是當初建立時傳入的那個原始數據;

let obj = {age:34,name:'mfy'} 
   let state  =ref(obj); 
   //把建立時候的參數分解出來
   let obj2 = toRaw(state.value); // 必須指定定義的ref內部變量的value
   function changeRef(){ 
      obj2.name = 'myy'   
      console.log('state',state)
      console.log('obj',obj) 
      console.log('obj2',obj2) 
   }
複製代碼

markRaw

markRaw 永遠不想被追蹤

let obj = {age:34,name:'mfy'}
   //告訴這個對象 永遠不能被監聽
   obj = markRaw(obj) 
   let state =reactive(obj);  
   function changeRef(){
     state.name ='mfy';//監聽到這個的變化
     console.log(state)
   }
複製代碼

經過markRaw定義的屬性再次被定義成響應式的時候,不管怎樣對state進行賦值,是沒法更改其內容的;

Composition API 的入口 (setup)

setupvue3.x中加入的一個新的生命週期,也是組合api的入口,因爲在執行 setup 時還沒有建立組件實例,所以在 setup 選項中沒有this。這意味着,除了props 以外,你將沒法訪問組件中聲明的任何屬性——本地狀態、計算屬性或方法。

執行時間

setup ->beforeCreate -> created

  • 因爲setup在執行的時候是在beforeCreate以前,所以是沒法進行獲取到this
  • setup中只能執行同步的,不能是異步的
setup(props) { 
    //定義數據
    let age = ref(33) 
    
     //定義函數
    function changeAge(){
      age.value = 333
    }
    // 必須將變量暴露出去才能進行使用
    return{
      age,
      changeAge
    } 
  }
複製代碼

setup中生命週期

在setup的函數中,其內部提供了不少的可進行兼容的方法

setup響應式數據監控方法

computed

用於使用計算屬性,能夠直接獲取到當前拼接出來的值

let firstName = ref("馬");
let lastName = ref("fyy") 
let fullName = computed(()=> firstName.value + lastName.value)
console.log(fullName)  
複製代碼

若想要直接修改computed的變量,可使用set的方法,將依賴的屬性的值進行修改;

let age = ref(12)
 let ageCom = computed({
      get: () => age.value + 1,
      set: val => {
        age.value = val - 1
      }
 })
複製代碼

watchEffect

在響應式地跟蹤其依賴項時當即運行一個函數,並在更改依賴項時從新運行它。

let firstName = ref("馬");
let lastName = ref("fyy") 
let fullName = computed(()=> firstName.value + lastName.value) 
watchEffect(() => { 
  console.log('%c 內容發生改變了','color:yellow',fullName.value)
})
function changeName(){
  firstName.value = 'Ming'+Math.random()   
}
複製代碼


每次watchEffect裏面的內容發生改變的時候都會觸發;

watch

watch API 與選項式 API this.$watch (以及相應的 watch 選項) 徹底等效。watch 須要偵聽特定的 data 源,並在單獨的回調函數中反作用。默認狀況下,它也是惰性的——即,回調是僅在偵聽源發生更改時調用。 與 watchEffect 比較,watch 容許咱們:

  • 惰性地執行反作用;
  • 更具體地說明應觸發偵聽器從新運行的狀態;
  • 訪問偵聽狀態的先前值和當前值。

偵聽一個數據源

const state = reactive({ count: 0 }) 
 watch(
   ()=>state.count,
   (count,preCount)=>{
     console.log('%c count的值發生修改了','color:yellow','count='+count,' preCount='+preCount)
   }
 ) 
function changeCount(){
  state.count +=1  
}

複製代碼

偵聽多個數據源

let name = ref('myy')
     let age = ref(22)
     watch([name,age],([name,age],[prevName,prevAge])=>{
       console.log("%c --姓名和年齡更改--",'color:yellow')
       console.log("%c 姓名更改",'color:green','prevName='+prevName,' name='+name )
       console.log("%c 年齡更改",'color:green','prevAge='+prevAge,' age='+age )
     })
     function changeCount(){
       name.value = 'myyyyy'
       age.value +=1  
     }
複製代碼

setup 中定義的參數

基礎類型 (最準確的來講 是使用ref定義的基礎類型)

  • setup中定義的參數,只能在setup的做用域下進行修改
  • 在外部的method中進行修改,則會報錯
  • 在setup暴露出去後,其暴露的就是當前變量的值;
changeValue(){ 
      console.log(this.isShowAddModal) //false
      this.isShowAddModal.value =3
    }
複製代碼

複雜數據類型(使用reactive)

在外部的method中進行修改數據 可以修改爲功

changeValue(){ 
    console.log(this.heorData)
    this.heorData.list=[] 
  }
//setup內部的監聽函數是否會觸發
  watch(heorData,()=>{
    console.log("watch:: heroData--數據修改了",heorData)
  }) 
複製代碼

調用setup內部暴露的函數,在setup內部是能夠被檢測到的

  • ref定義的參數,沒法在setup外部進行參數的修改
  • reactive定義的參數可以進行修改,而且可以被watch監聽到

總結

本身經過文檔+視頻+實際操做對Vue3.x有了一點點了解,有些內容其實不是不夠深刻的,比較好奇的點會經過本身想要了解的內容去實際操做一波;

  • Composition API 的使用
  • 響應式數據的定義
    • 引用類型、基礎類型定義
    • 遞歸響應和非遞歸響應數據的定義
    • 只讀響應數據
  • setup函數的使用

參考文檔

官方文檔api b站vue3.0入門講解視頻

相關文章
相關標籤/搜索