1. 程式人生 > 實用技巧 >Vue3.0(B站李南江)

Vue3.0(B站李南江)

參考:

  https://www.yuque.com/gdnnth/vue-v3/xvrc7e

  https://www.jianshu.com/p/4725441aff5f

  https://blog.csdn.net/wanghuan1020/article/details/109362810

一、 Vue3.0六大兩點

  1.   Performance:效能比Vue2.x快1.2~2倍
  2.   Treeshakingsupport:按需編譯,體積比Vue2.x更小
  3.   CompositionAPI:組合API(類似ReactHooks)
  4.   BetterTypeScriptsupport:更好的Ts支援
  5.   CustomRendererAPI:暴露了自定義渲染API
  6.   Fragment,Teleport(Protal),Suspense:更先進的元件

二、VUE3.0是如何變快的

  1.diff方法優化:http://vue-next-template-explorer.netlify.app/

    • Vue2中的虛擬dom是進行全量的對比
    • Vue3新增了靜態標記(PatchFlag)

    在與上次虛擬節點進行對比時候,只對比帶有patchflag的節點

    並且可以通過flag的資訊得知當前節點要對比的具體內容

    在建立虛擬dom的時候,會根據DOM中的內容會不會發生變化,新增靜態標記

  2.hoistStatic靜態提升

    • Vue2中無論元素是否參與更新,每次都會重新建立,然後再渲染
    • Vue3中對不參與更新的元素,會做靜態提升,只會被建立一次,在渲染時直接複用即可

  

  3.cacheHandlers事件偵聽快取

    • 預設情況下onClick會被視為動態繫結,所以每次都會去追蹤它的變化
    • 但是因為是同一個函式,所以沒有追蹤變化,直接快取起來複用即可
    • 在Vue3中的diff演算法中,只有存在靜態標記的節點才會進行追蹤,事件偵聽快取本質上是去除了不必要的diff比較

  

  4.SSR渲染

    • 當有大量靜態的內容時候,這些內容會被當做純字串推進一個Buffer裡面,即使存在動態的繫結,會通過模板插值嵌入進去。這樣會比通過虛擬dom來渲染快上很多。
    • 當靜態內容達到一定量級時候,會使用_createStaticVNode方法在客戶端dom來渲染一個staticnode,這些靜態node,會被直接innerHtml,就不需要建立物件,然後根據物件渲染。

三、VUE3.0的建立

  1.VUE-CLI

1 npm install -g @vue/cli
2 vue create projectName
3 cd projectName
4 npm run serve

  2.Webpack

git clone https://github.com/vuejs/vue-next-webpack-preview.git projectName
cd projectName
npm install
npm run dev

  3.Vite

  Vite是Vue作者開發的一款意圖取代webpack的工具

  其實現原理是利用ES6的import會發送請求去載入檔案的特性,攔截這些請求,做一些預編譯,省去webpack冗長的打包時間

// 1. 安裝Vite
npm install -g create-vite-app
// 2. 建立Vue3專案
create-vite-app projectName
// 3. 安裝依賴執行專案
cd projectName
npm install
npm run dev

四、CompositionAPI

<template>
    <div>
        <p>{{ count }}</p>
        <button @click="myFun">按鈕</button>
    </div>
</template>

<script>
// ref函式只能監聽簡單型別的變化,不能監聽複雜型別的變化(物件,陣列),監聽複雜型別引入並使用reactive
import { ref } from 'vue'
export default {
  name: 'App',
    // setup 函式是組合api的入口函式,執行時間在beforeCreate和created之間
    setup() {
        // 定義了一個count變數,初始值為0
        // 該變數發生改變後,Vue會自動更新UI
        let count = ref(0)
        // 在組合api中,如果定義方法,不需要定義到methods,直接定義即可
        function myFun() {
            count.value += 1    // 修改值,count.value而不是count
        }
        /**
         * 注意點:
         * 在組合api中定義的變數/方法,要想在外界使用,必須通過return { xxx, xxx } 暴露出去
         */
        return { count, myFun }
    },
}
</script>

  1.setup注意點

    • 由於在執行setup函式的時候,還沒有執行created生命週期方法,所以在setup函式中,是無法使用data和methods
    • 由於不能再setup函式中使用data和methods,所以Vue為了避免我們的錯誤使用,它直接將setup函式中的this修改成了undefined
    • setup函式只能是同步的,不能是非同步的,async setup() {} 錯誤使用

  2.ref函式

2.1.什麼是ref?
  ref和reactive一樣,也是用來實現響應式資料的方法

  由於reactive必須傳遞一個物件,所以導致在企業開發中,如果我們只想讓某個變數實現響應式的時候會非常麻煩,

  所以Vue3就給我們提供了ref方法,實現對簡單值的監聽
2.2.ref本質
  ref底層的本質其實還是reactive,系統會自動根據我們給ref傳入的值將它轉換成ref(xx)->reactive({value:xx})
2.3.ref注意點
  在Vue中使用ref的值不用通過value獲取

  在JS中使用ref的值必須通過value獲取

3.reactive

<template>
  <div>
    <p>{{time}}</p>
    <button @click="myFn">按鈕</button>
  </div>
</template>

<script>
/*
    reactive的用法與ref的用法相似,也是將資料變成響應式資料,當資料發生變化時UI也會自動更新。
    不同的是ref用於基本資料型別,而reactive是用於複雜資料型別,比如物件和陣列
*/
// toRefs解構
import { reactive,toRefs } from 'vue'
export default {
  name: 'App',
  setup() {
    //   賦值
    let state = reactive({
      time: new Date(),
    })

    function myFn() {
    /*
        reactive中傳遞的引數必須是json物件或者陣列,如果傳遞了其他物件(比如new Date()),在預設情況下修改物件,
        介面不會自動更新,如果也需要具有響應式,可以通過重新賦值的方式實現
    */
      const newTime = new Date(state.time.getTime())
      newTime.setDate(newTime.getDate() + 1)
      state.time = newTime
      // state.time.setDate(state.time.getDate() + 1) ,頁面不更新
        console.log(state)    // reactive將傳遞的物件包裝成了proxy物件
    }

    return {
      ...toRefs(state),
      myFn,
    }
  },
}
</script>

3.1.什麼是reactive?

    •  reactive是Vue3中提供的實現響應式資料的方法
    •  在Vue2中響應式資料是通過defineProperty來實現的,而在Vue3中響應式資料是通過ES6的Proxy來實現的

3.2.reactive注意點

    •  reactive引數必須是物件(json/array)
    •  如果給reactive傳遞了其他物件
    •  預設情況下修改物件,介面不會自動更新
    • 如果想更新,可以通過重新賦值的方式

四、相關 API

  1.isRef和isReactive ,判斷一個數據是ref型別還是reactive型別

import { ref, reactive, isRef, isReactive } from 'vue'
export default {
  name: 'App',
    setup() {
        let age = ref(18)
        // let age = reactive({
          //      value: 18
          // })
        function myFun() {
            console.log(isReactive(age))
            console.log(isRef(age))
            age.value += 2
        }
        return {
            age,
            myFun
        }
    }
}

  2.只讀:readonly,shallowReadonly,isReadonly

    • readonly:用於建立一個只讀的資料,並且是遞迴只讀
    • shallowReadonly:用於建立一個只讀資料,但是不是遞迴只讀的
    • isReadonly:對於readonly和shallowReadonly建立的資料,返回結果均為true
    • const和readonly的區別:

      const:賦值保護,不能給變數重新賦值

      readonly:屬性保護,不能給屬性重新賦值

import { readonly, isReadonly, shallowReadonly } from 'vue'
export default {
  name: 'App',
  setup() {
    // 用於建立一個只讀的資料,並且是遞迴只讀
    let state = readonly({
      name: 'lnj',
      attr: {
        age: 18,
        height: 1.88,
      },
    })

    function myFn() {
      state.name = '知播漁'
      state.attr.age = 666
      state.attr.height = 1.66
      console.log(state)
      console.log(isReadonly(state))
    }

    return {
      state,
      myFn,
    }
  },
}

3.遞迴監聽與非遞迴監聽

遞迴監聽
  預設情況下,無論是通過ref還是reactive都是遞迴監聽

  每一層都包裝成了一個proxy物件

  遞迴監聽存在的問題

  如果資料量比較大,非常消耗效能

非遞迴監聽
  shallowReactive
  非遞迴監聽下,第一層被包裝成了proxy
  這意味著:只有第一層的資料發生改變,才會觸發UI介面的更新


  shallowRef
  如果是通過shallowRef建立資料,那麼Vue監聽的是.value的變化,並不是第一層的變化
  如果想在修改其內部資料後觸發介面的更新,可以呼叫triggerRef方法


  應用場景
  一般情況下使用ref和reactive即可
  只有在需要監聽的資料量比較大的時候,我們才使用shallowRef/shallowReactive

<template>
  <div>
    <p>{{ state.a }}</p>
    <p>{{ state.gf.b }}</p>
    <p>{{ state.gf.f.c }}</p>
    <p>{{ state.gf.f.s.d }}</p>
    <button @click="myFun">按鈕</button>
  </div>
</template>

<script>
import { ref, shallowRef, triggerRef } from 'vue'
export default {
  name: 'App',
    setup() {
      // shallowRef 本質上還是 shallowReative
      // shallowRef(10) -> shallowReactive({ value: 10 })
      // 所以如果是通過 shallowRef 建立的資料,它監聽的是 .value 的變化
      let state = shallowRef({
        a: 'a',
        gf: {
          b: 'b',
          f: {
            c: 'c',
            s: {
              d: 'd'
            }
          }
        }
      })

      function myFun() {
        /**
         * triggerRef
         * 注意點:
         *  + Vue3 只提供了 triggerRef 方法,沒有提供 triggerReactive 方法
         *  + 所以如果是 reactive 型別的資料,那麼是無法主動觸發介面更新的
         */
        // state.value.a = 1
        // state.value.gf.b = 2
        state.value.gf.f.c = 3
        state.value.gf.f.s.d = 4 // 如果想在修改該資料時,觸發介面的更新,可以呼叫 triggerRef 方法主動觸發
        triggerRef(state)

        /**
         * shallowRef
         * 注意點:
         *  + 如果是通過 shallowRef 建立資料,那麼 Vue監聽的是 .value 的變化,因為底層本質上.value才是第一層
         */
        // state.value = {
        //   a: '1',
        //   gf: {
        //     b: '2',
        //     f: {
        //       c: '3',
        //       s: {
        //         d: '4'
        //       }
        //     }
        //   }
        // }
        console.log(state)
        console.log(state.value.gf)
        console.log(state.value.gf.f)
        console.log(state.value.gf.f.s)
      }

      return {
        state,
        myFun
      }
    }
}

</script>

  4.toRef與toRefs

    • ref->複製,修改響應式資料不會影響以前的資料
    • toRef->深拷貝引用,修改響應式資料會影響以前的資料
    • ref->資料發生改變,介面就會自動更新
    • toRef->資料發生改變,介面也不會自動更新
    • toRefs->解構

  

  5.customRef函式

<template>
  <div>
    <p>{{age}}</p>
    <button @click="myFun">按鈕</button>
  </div>
</template>

<script>
  /**
   *  custemRef返回一個 ref 物件,可以顯式的控制依賴追蹤和觸發響應
   */
  import { ref, customRef } from 'vue'
  function myRef(value) {
  return customRef((track, trigger) => {
    // track -> 追蹤  trigger -> 觸發
    return {
      get() {
        track() // 告訴 Vue 這個資料是需要追蹤變化的
        console.log('get', value)
        return value
      },
      set(newValue) {
        console.log('set', newValue)
        value = newValue
        trigger() // 告訴 Vue 觸發介面更新
      }
    }
  })
}

export default {
    name: 'App',
    setup() {
      // let age = ref(18) // reactive({value: 18})
      let age = myRef(18)
      function myFun() {
        age.value += 1
      }
      return { age, myFun }
    }
}
</script>