vue3 大海賊時代發佈了

Vue3

vue3 大海賊時代發佈了,學習一下這一方面的知識,大佬,我我我我我,還學的動。個人肩膀寬,磚頭搬得動 vue

optionsAPI -> composition API

vue-composition-api-rfc.netlify.appreact

舉個簡單的例子webpack

  1. 聲明變量
const { reactive } = Vue
var App = {
  template: `
    <div>
         {{message}}
    </div>`,
  setup() {
  	const state = reactive({message: "Hello World!!!"})
	return {
		...state
	}
  }
}
Vue.createApp().mount(App, '#app')
複製代碼
  1. 雙向綁定
const { reactive } = Vue
let App = {
  template: `
    <div>
        <input v-model="state.value"/>{{state.value}}
    </div>`,
  setup() {
    const state = reactive({ value: '' })
    return { state }
  }
}
Vue.createApp().mount(App, '#app')

複製代碼
  • setup

    被詬病得地方,內容要寫在這個地方。setup 其實是一個組件的入口,它運行在組件被實例化時候,props 屬性被定義以後,實際上等價於 vue2 版本的 beforeCreate 和 Created 這兩個生命週期git

  • reactive

    建立一個響應式得狀態,幾乎等價於 vue2.x 中的 Vue.observable() API,爲了不於 rxjs 中得 observable 混淆進行了重命名github

  1. 觀察屬性
import { reactive, watchEffect } from 'vue'

const state = reactive({
  count: 0,
})

watchEffect(() => {
  document.body.innerHTML = `count is ${state.count}`
})
return {...state}
複製代碼

watchEffect 和 2.x 中的 watch 選項相似,可是它不須要把被依賴的數據源和反作用回調分開。組合式 API 一樣提供了一個 watch 函數,其行爲和 2.x 的選項徹底一致。web

  1. ref

    vue3 容許用戶建立單個的響應式對象api

const App = {
  template: `
      <div>
        {{value}}
      </div>`,
  setup() {
    const value = ref(0)
    return { value }
  }
}
Vue.createApp().mount(App, '#app')
複製代碼
  1. 計算屬性
setup() {
  const state = reactive({
    count: 0,
    double: computed(() => state.count * 2),
   })

  function increment() {
    state.count++
  }

  return {
    state,
    increment,
  }
},
複製代碼
  1. 生命週期的變動數組

    vue2 vue3
    beforeCreate setup
    created setup
    beforeMount onBeforeMount
    mounted onMounted
    beforeUpdate onBeforeUpdate
    updated onUpdated
    beforeDestroy onBeforeUnmount
    destroyed onUnmounted
    errorCaptured onErrorCaptured

生命週期使用舉例:緩存

import { onMounted } from 'vue'

export default {
  setup() {
    onMounted(() => {
      console.log('component is mounted!')
    })
  },
}
複製代碼

performance 優化

  • 重構了虛擬 DOM,保持兼容性,使 dom 脫離模板渲染,提高性能
  • 優化了模板編譯過程,增長 patchFlag,遍歷節點的時候,會跳過靜態節點
  • 高效的組件初始化
  • 組件 upload 的過程性能提高 1.3~2 倍
  • SSR 速度提高 2~3 倍

vue3.0如何實現的 domdiff和vDOM的優化服務器

  1. 編譯模板的靜態標記

舉例:

<div id="app">
    <p>週一呢</p>
    <p>明天就週二了</p>
    <div>{{week}}</div>
</div>
複製代碼

在vue2會被解析成一下代碼

function render() {
  with(this) {
    return _c('div', {
      attrs: {
        "id": "app"
      }
    }, [_c('p', [_v("週一呢")]), _c('p', [_v("明天就週二了")]), _c('div', [_v(
      _s(week))])])
  }
}
複製代碼

能夠看出,兩個p標籤是徹底靜態的,以致於在後續的渲染中,其實沒有任何變化的,可是在vue2.x中依然會使用_c新建成一個vdom,在diff的時候仍然須要去比較,這樣就形成了必定量的性能消耗

在vue3中

import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"

export function render(_ctx, _cache) {
  return (_openBlock(), _createBlock("div", { id: "app" }, [
    _createVNode("p", null, "週一呢"),
    _createVNode("p", null, "明天就週二了"),
    _createVNode("div", null, _toDisplayString(_ctx.week), 1 /* TEXT */)
  ]))
}

複製代碼

只有當_createVNode的第四個參數不爲空的時候,這時,纔會被遍歷,而靜態節點就不會被遍歷到

同時發現了在vue3最後一個非靜態的節點編譯後:出現了/* TEXT */,這是爲了標記當前內容的類型以便進行diff,若是不一樣的標記,只須要去比較對比相同的類型。這就不會去浪費時間對其餘類型進行遍歷了

export const enum PatchFlags {
  
  TEXT = 1,// 表示具備動態textContent的元素
  CLASS = 1 << 1,  // 表示有動態Class的元素
  STYLE = 1 << 2,  // 表示動態樣式(靜態如style="color: red",也會提高至動態)
  PROPS = 1 << 3,  // 表示具備非類/樣式動態道具的元素。
  FULL_PROPS = 1 << 4,  // 表示帶有動態鍵的道具的元素,與上面三種相斥
  HYDRATE_EVENTS = 1 << 5,  // 表示帶有事件監聽器的元素
  STABLE_FRAGMENT = 1 << 6,   // 表示其子順序不變的片斷(沒懂)。 
  KEYED_FRAGMENT = 1 << 7, // 表示帶有鍵控或部分鍵控子元素的片斷。
  UNKEYED_FRAGMENT = 1 << 8, // 表示帶有無key綁定的片斷
  NEED_PATCH = 1 << 9,   // 表示只須要非屬性補丁的元素,例如ref或hooks
  DYNAMIC_SLOTS = 1 << 10,  // 表示具備動態插槽的元素
}

複製代碼

若是存在兩種類型,那麼只須要對這兩個值對應的patchflag進行位暈眩 如:TEXTPROPS

TEXT: 1 ,PROPRS: 1<<3 => 8

那麼對1和8進行按位與運算獲得=>9
複製代碼

2.事件儲存

綁定的事件會緩存在緩存中

<div id="app">
  <button @click="handleClick">週五啦</button>
</div>

複製代碼

通過轉換=>

import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from "vue"

export function render(_ctx, _cache) {
  return (_openBlock(), _createBlock("div", { id: "app" }, [
    _createVNode("button", {
      onClick: _cache[1] || (_cache[1] = ($event, ...args) => (_ctx.handleClick($event, ...args)))
    }, "週五啦")
  ]))
}

複製代碼

在代碼中能夠看出在綁定點擊事件的時候,會生成並緩存了一個內聯函數在cache中,變成了一個靜態的節點

  1. 靜態提高
<div id="app">
    <p>週一了</p>
    <p>週二了</p>
    <div>{{week}}</div>
    <div :class="{red:isRed}">週三呢</div>
</div>
複製代碼

轉換成=>

import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"

const _hoisted_1 = { id: "app" }
const _hoisted_2 = /*#__PURE__*/_createVNode("p", null, "週一了", -1 /* HOISTED */)
const _hoisted_3 = /*#__PURE__*/_createVNode("p", null, "週二了", -1 /* HOISTED */)

export function render(_ctx, _cache) {
  return (_openBlock(), _createBlock("div", _hoisted_1, [
    _hoisted_2,
    _hoisted_3,
    _createVNode("div", null, _toDisplayString(_ctx.week), 1 /* TEXT */),
    _createVNode("div", {
      class: {red:_ctx.isRed}
    }, "週三呢", 2 /* CLASS */)
  ]))
}
複製代碼

在這裏能夠看出來將一些靜態的節點放放在了render函數的外部,這樣就避免了每次render都會去生成一次靜態節點


提供了tree shaking

  • 打包的時候自動去除沒用到的 vue 模塊

更好的 ts 支持

  • 類型定義提示
  • tsx 支持
  • class 組件的支持

全家桶修改

vite 的使用,放棄原來vue2.x使用的 webpack

github.com/vitejs/vite

  1. 開發服務器啓動後不須要進行打包操做
  2. 能夠自定義開發服務器:const {createSever} = require('vite')
  3. 熱模塊替換的性能和模塊數量無關,替換變快,即時熱模塊替換
  4. 生產環境和 rollup 捆綁

源碼修改

vue2和vue3響應式對比

vue2.x 使用的是defineProperty,有兩個難解決的問題

  1. 只能作第一層屬性的響應,再往深處就沒法實現了
  2. 數組問題:defineProperty沒法檢測數組長度的變化,準確的是說,是沒法檢測經過改變length的方法而增長的長度沒法檢測到
length的屬性被初始化成爲了
enumberable: false
configurable: false
writable: true
因此說直接去刪除或者修改length屬性是不行的
var a = [1,2,3]
Object.defineProperty(a,'length',{
   enumberable: true,
configurable: true,
writable: true ,
})
=> Uncaught TypeError: Cannot redefine property: length
複製代碼

vue3 使用的是Proxy和Reflect,直接代理整個對象

function reactive(data) {
    if (typeof data !== 'object' || data === null) {
        return data
    }
    const observed = new Proxy(data, {
        get(target, key, receiver) {
            // Reflect有返回值不報錯
            let result = Reflect.get(target, key, receiver)

            // 多層代理
            return typeof result !== 'object' ? result : reactive(result) 
        },
        set(target, key, value, receiver) {
            effective()
            // proxy + reflect
            const ret = Reflect.set(target, key, value, receiver)
            return ret
        },

        deleteProperty(target,key){
            const ret = Reflect.deleteProperty(target,key)
            return ret
        }

    })
    return observed
}
複製代碼

總結:

  1. Object.defineProperty 只能劫持對象的屬性,而 Proxy 是直接代理對象,因爲 Object.defineProperty 只能對屬性進行劫持,須要遍歷對象的每一個屬性。而 Proxy 能夠直接代理對象。
  2. Object.defineProperty 對新增屬性須要手動進行 Observe, 因爲 Object.defineProperty 劫持的是對象的屬性,因此新增屬性時,須要從新遍歷對象,對其新增屬性再使用 Object.defineProperty 進行劫持。 也正是由於這個緣由,使用 Vue 給 data 中的數組或對象新增屬性時,須要使用 vm.$set 才能保證新增的屬性也是響應式的。
  3. Proxy 支持 13 種攔截操做,這是 defineProperty 所不具備的新標準性能紅利
  4. Proxy 做爲新標準,長遠來看,JS 引擎會繼續優化 Proxy,但 getter 和 setter 基本不會再有針對性優化。
  5. Proxy 兼容性差 目前並無一個完整支持 Proxy 全部攔截方法的 Polyfill 方案
相關文章
相關標籤/搜索