vue3.0改進


vue3.0的設計目標

  • 更小
  • 更快
  • 加強TypeScript支持
  • 加強API設計一致性
  • 提高自身可維護性
  • 開放更多底層功能

具體可以從以下方面來理解

1,壓縮包體積更小

當前最小化並被壓縮的 Vue 運行時大小約為 20kB(2.6.10 版為 22.8kB)。Vue 3.0捆綁包的大小大約會減少一半,即只有10kB!

2,Object.defineProperty -> Proxy

Object.defineProperty是一個相對比較昂貴的操作,因為它直接操作對象的屬性,顆粒度比較小。將它替換為es6的Proxy,在目標對象之上架了一層攔截,代理的是對象而不是對象的屬性。這樣可以將原本對對象屬性的操作變為對整個對象的操作,顆粒度變大。

javascript引擎在解析的時候希望對象的結構越穩定越好,如果對象一直在變,可優化性降低,proxy不需要對原始對象做太多操作。

3,Virtual DOM 重構

vdom的本質是一個抽象層,用javascript描述界面渲染成什么樣子。react用jsx,沒辦法檢測出可以優化的動態代碼,所以做時間分片,vue中足夠快的話可以不用時間分片。

傳統vdom的性能瓶頸:

  • 雖然 Vue 能夠保證觸發更新的組件最小化,但在單個組件內部依然需要遍歷該組件的整個 vdom 樹。
  • 傳統 vdom 的性能跟模版大小正相關,跟動態節點的數量無關。在一些組件整個模版內只有少量動態節點的情況下,這些遍歷都是性能的浪費。
  • JSX 和手寫的 render function 是完全動態的,過度的靈活性導致運行時可以用於優化的信息不足

那為什么不直接拋棄vdom呢?

  • 高級場景下手寫 render function 獲得更強的表達力
  • 生成的代碼更簡潔
  • 兼容2.x

vue的特點是底層為Virtual DOM,上層包含有大量靜態信息的模版。為了兼容手寫 render function,最大化利用模版靜態信息,vue3.0采用了動靜結合的解決方案,將vdom的操作顆粒度變小,每次觸發更新不再以組件為單位進行遍歷,主要更改如下

  • 將模版基於動態節點指令切割為嵌套的區塊
  • 每個區塊內部的節點結構是固定的
  • 每個區塊只需要以一個 Array 追蹤自身包含的動態節點

vue3.0將 vdom 更新性能由與模版整體大小相關提升為與動態內容的數量相關

4, 更多編譯時優化

  • Slot 默認編譯為函數:父子之間不存在強耦合,提升性能
  • Monomorphic vnode factory:參數一致化,給它children信息,
  • Compiler-generated flags for vnode/children types

5,選用Function_based API

為什么撤銷 Class API ?

1,更好地支持TypeScript

  • Props 和其它需要注入到 this 的屬性導致類型聲明依然存在問題
  • Decorators 提案的嚴重不穩定使得依賴它的方案具有重大風險

2,除了類型支持以外 Class API 並不帶來任何新的優勢

3,vue中的UI組件很少用到繼承,一般都是組合,可以用Function-based API

Function_based API示例如下

const App = {
    setup () {
        // data
        const count = value(0)
        // computed
        const plusOne = computed(()=>count.value + 1)
        // method
        const increment = () => {count.value++}
        // watch
        watch(() => count.value*2, v =>console.log(v))
        // lifecycle
        onMounted(() => console.log('mounted'))
        // 暴露給模板或者渲染函數
        return {count}

    }
}

 

 

 

1,vue3.0將組件的邏輯都寫在了函數內部,setup()會取代vue2.x的data()函數,返回一個對象,暴露給模板,而且只在初始化的時候調用一次,因為值可以被跟蹤。

2,新的函數api:const count = value(0)

value是一個wrapper,是一個包裝對象,會包含數字0,可以用count.value來獲取這個值。在函數返回的時候會關心是value wrapper,一旦返回給模版,就不用關心了。

優點:即使count包含的是基本類型,例如數字和字符串,也可以在函數之間來回傳遞,當用count.value取值的時候會觸發依賴,改值的時候會觸發更新。

3,計算屬性返回的也是這個值的包裝。

4,onMounted生命周期函數直接注入。

Function-based API 對比Class-based API有以下優點

1,對typescript更加友好,typescript對函數的參數和返回值都非常好,寫Function-based API既是javascript又是typescript,不需要任何的類型聲明,typescript可以自己做類型推導。

2,靜態的import和export是treeshaking的前提,Function-based API中的方法都是從全局的vue中import進來的。

3,函數內部的變量名和函數名都可以被壓縮為單個字母,但是對象和類的屬性和方法名默認不被壓縮(為了防止引用出錯)。

4,更靈活的邏輯復用。

目前如果我們要在組件之間共享一些代碼,則有兩個可用的選擇:mixins 和作用域插槽( scoped slots),但是它們都存在一些缺陷:

1,mixins 的最大缺點在於我們對它實際上添加到組件中的行為一無所知。這不僅使代碼變得難以理解,而且還可能導致名稱與現有屬性和函數發生沖突。

2,通過使用作用域插槽,我們確切地知道可以通過 v-slot 屬性訪問了哪些屬性,因此代碼更容易理解。這種方法的缺點是我們只能在模板中訪問它,並且只能在組件作用域內使用。

高階組件在vue中比較少,在react中引入是作為mixins的替代品,但是比mixins更糟糕,高階組件可以將多個組件進行包裝,子組件通過props接收數據,多個高階組件一起使用,不知道數據來自哪個高階組件,存在命名空間的沖突。而且高階組件嵌套得越多,額外的組件實例就越多,造成性能損耗。

下面以一個鼠標位置偵聽的案例演示vue3.0中的邏輯復用

new Vue({
    template: '<div>Mouse position: x {{x}} / y {{y}}</div>',
    data () {
        const {x, y} = useMousePosition()
        return {
            x,
            y
        }
    }
})

function useMousePosition () {
    const x = value(0)
    const y = value(0)
    const update = e => {
        x.value = e.pageX
        y.value = e.pageY
    }
    onMounted(() => {
        window.addEventListener('mousemove', update)
    })
    onUnmounted(() => {
        window.removeEventListener('mousemove', update)
    })
    return {x, y}
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM