1. 程式人生 > 其它 >vue3的新特性

vue3的新特性

vue3的新特性

不知不覺的VUE3.0釋出已經很久了。

在B站上看了教學視屏 瞬間勾起的我濃厚的學習興趣。

https://www.bilibili.com/video/BV1yK4y1M7Fz?p=8&spm_id_from=pageDriver

Vue 3全新的Web開發構建工具Vite

Vite是Vue的作者尤雨溪開發的Web開發構建工具,它是一個基於瀏覽器原生ES模組匯入的開發伺服器,在開發環境下,利用瀏覽器去解析import,在伺服器端按需編譯返回,完全跳過了打包這個概念,伺服器隨啟隨用。同時不僅對Vue檔案提供了支援,還支援熱更新,而且熱更新的速度不會隨著模組增多而變慢。在生產環境下使用Rollup打包。

Vite具有以下特點:

快速的冷啟動即時熱模組更新(HMR,Hot Module Replacement)真正按需編譯Vite是在推出Vue 3的時候開發的,目前僅支援Vue 3.x,這意味著與Vue 3不相容的庫也不能與Vite一起使用。

與Vue CLI類似,Vite也提供用npm或者yarn來生成專案結構的方式。選擇一個目錄,開啟命令提示視窗,依次執行下面的命令構建腳手架專案,並啟動專案。

npm init vite-app <project-name>

cd <project-name>

npm install

npm run dev

如果使用yarn,則依次執行下面的命令:

yarn create vite-app <project-name>

cd <project-name>

yarn

yarn dev

由於Vite使用了瀏覽器原生的ES模組匯入功能,但IE 11並不支援ES的模組匯入,因此基於Vite開發專案,瀏覽器不能使用IE11,其他主流的瀏覽器均支援ES模組的模組功能。

我建立了一個專案 結構如下

可以發現,Vite生成的腳手架專案的目錄結構與Vue CLI生成的專案目錄結構很類似,確實是這樣的,而且開發方式也基本相同。不過Vite專案的預設配置檔案是vite.config.js,而不是vue.config.js。

package.json檔案的內容如下所示:

{
  "name": "webapp-vue3",
  "version": "0.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vite build"
  },
  "dependencies": {
    "vue": "^3.0.4"
  },
  "devDependencies": {
    "vite": "^1.0.0-rc.13",
    "@vue/compiler-sfc": "^3.0.4"
  }
}

這裡與Vue CLI不同地方在這裡:

"dev": "vite",

"build": "vite build"

體驗了一波非常棒,基本上都是秒啟動,改動程式碼以後也是秒更新。

Vue 3採用Proxy資料劫持

  1. Vue 2.x 利用 Object.defineProperty(),並且把內部解耦為 Observer, Dep, 並使用 Watcher 相連
  2. Vue 在 3.x 版本之後改用 Proxy 進行實現

那麼他們之前有什麼優缺點呢?

1、Object.defineProperty() 的問題主要有三個:

  • 不能監聽陣列的變化
  • 必須遍歷物件的每個屬性
  • 必須深層遍歷巢狀的物件
  • 不能監聽陣列的變化

    陣列的這些方法是無法觸發set的:push, pop, shift, unshift,splice, sort, reverse.
    Vue 把會修改原來陣列的方法定義為變異方法 (mutation method)
    非變異方法 (non-mutating method):例如 filter, concat, slice 等,它們都不會修改原始陣列,而會返回一個新的陣列。
    Vue 的做法是把這些方法重寫來實現陣列的劫持。
  • 必須遍歷物件的每個屬性
    使用 Object.defineProperty() 多數要配合 Object.keys() 和遍歷,,於是多了一層巢狀

Object.keys(obj).forEach(key => {
  Object.defineProperty(obj, key, {
    // ...
  })
})

必須深層遍歷巢狀的物件
當一個物件為深層巢狀的時候,必須進行逐層遍歷,直到把每個物件的每個屬性都呼叫 Object.defineProperty() 為止。 Vue 的原始碼中這樣的邏輯----walk 方法.

2、Proxy:
Proxy 物件用於定義基本操作的自定義行為(如屬性查詢,賦值,列舉,函式呼叫等)。

  • 針對物件:針對整個物件,而不是物件的某個屬性
  • 支援陣列:不需要對陣列的方法進行過載,省去了眾多 hack
  • 巢狀支援: get 裡面遞迴呼叫 Proxy 並返回
  • 其他
    針對物件
    不需要對 keys 進行遍歷。這解決Object.defineProperty() 的第二個問題.Proxy 是針對整個 obj 的。所以 obj 內部包含的所有的 key ,都可以走進 set。(省了一個 Object.keys() 的遍歷)
let obj = {
  name: 'Eason',
  age: 30
}
let handler = {
  get (target, key, receiver) {
    console.log('get', key)
    return Reflect.get(target, key, receiver)
  },
  set (target, key, value, receiver) {
    console.log('set', key, value)
    return Reflect.set(target, key, value, receiver)
  }
}
let proxy = new Proxy(obj, handler)
proxy.name = 'Zoe' // set name Zoe
proxy.age = 18 // set age 18

另外 Reflect.get 和 Reflect.set 可以理解為類繼承裡的 super,即呼叫原來的方法

巢狀支援
Proxy 也是不支援巢狀的,這點和 Object.defineProperty() 是一樣的。因此也需要通過逐層遍歷來解決。Proxy 的寫法是在 get 裡面遞迴呼叫 Proxy 並返回

let obj = {
  info: {
    name: 'eason',
    blogs: ['webpack', 'babel', 'cache']
  }
}
let handler = {
  get (target, key, receiver) {
    console.log('get', key)
    // 遞迴建立並返回
    if (typeof target[key] === 'object' && target[key] !== null) {
      return new Proxy(target[key], handler)
    }
    return Reflect.get(target, key, receiver)
  },
  set (target, key, value, receiver) {
    console.log('set', key, value)
    return Reflect.set(target, key, value, receiver)
  }
}
let proxy = new Proxy(obj, handler)
// 以下兩句都能夠進入 set
proxy.info.name = 'Zoe'
proxy.info.blogs.push('proxy')

其它方面

優勢:Proxy 的第二個引數可以有 13 種攔截方法,比 Object.defineProperty() 要更加豐富,Proxy 作為新標準受到瀏覽器廠商的重點關注和效能優化,相比之下 Object.defineProperty() 是一個已有的老方法。
劣勢:Proxy 的相容性不如 Object.defineProperty() (caniuse 的資料表明,QQ 瀏覽器和百度瀏覽器並不支援 Proxy,這對國內移動開發來說估計無法接受,但兩者都支援 Object.defineProperty()),不能使用 polyfill 來處理相容性

Vue 3採用Proxy資料劫持

Vue3 的新特性(二) —— Composition-Api

Composition API : 一組低侵入式的、函式式的 API,它使我們能夠更靈活地「組合」元件的邏輯。

Composition API 的靈感來自於 React Hooks ,是比 mixin 更強大的存在。它可以提高程式碼邏輯的可複用性,從而實現與模板的無關性;同時函式式的程式設計使程式碼的可壓縮性更強。另外,把 Reactivity 模組獨立開來,意味著 Vue3.0 的響應式模組可以與其他框架相組合。

如上圖,在較大元件的編寫中, Composition-Api 可以把複雜元件的邏輯抽地更緊湊,而且可以將公共邏輯進行抽取。

1. reactive()

reactive() 函式接收一個普通的物件,返回出一個響應式物件。

在Vue2.x的版本中,我們只需要在 data() 中定義一個數據就能將它變為響應式資料,在 Vue3.0 中,需要用 reactive 函式或者 ref 來建立響應式資料。

setup() {
    // 建立響應式物件
    const state = reactive({
        count:0
    });

    // 將響應式物件return出去,暴露給模板使用
    return state;
}

使用響應式物件

<p>當前的count的值為:{{count}}</p>

<button @click="count++">點選增加count<  button>

2. ref()

ref() 函式可以根據給定的來建立一個響應式的資料物件,返回值是一個物件,且只包含一個 .value 屬性。

  • 用 ref 建立響應式物件

setup() {
    // 建立響應式物件
    const count = ref(0);

    return {
        count
    }
}

使用響應式物件

<p>當前的count的值為:{{count}}</p>

<button @click="count++">點選增加count</button>

ref 的注意事項

在 setup() 函式內,由 ref() 建立的響應式資料返回的是物件,所以需要用 .value 來訪問;

而在 setup() 函式外部則不需要 .value ,直接訪問即可。

可以在 reactive 物件中訪問 ref() 函式建立的響應式資料。

新的 ref() 會覆蓋舊的 ref() 。

watchEffect()

watchEffect() 會立即執行傳入的函式,並響應式偵聽其依賴,並在其依賴變更時重新執行該函式。

const count = ref(0)

// 初次直接執行,打印出 0
watchEffect(() => console.log(count.value))

setTimeout(() => {
  // 被偵聽的資料發生變化,觸發函式打印出 1
  count.value++
}, 1000)

停止偵聽

watchEffect() 使用時返回一個函式,當執行這個返回的函式時,就停止偵聽

const stop = watchEffect(() => {
  /* ... */
})

// 停止偵聽
stop()

watch()

composition-api 中的 watch 和 Vue2.x 中是一樣的,watch 需要偵聽資料,並執行它的偵聽回撥。預設情況下初次渲染不執行。

  • watchwatchEffect 的不同

    1. watch 初次渲染不執行
    2. watch 偵聽的更具體
    3. watch 可以訪問偵聽資料變化前後的值
// 偵聽一個 getter
const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  }
)

// 直接偵聽一個 ref
const count = ref(0)
watch(count, (count, prevCount) => {
  /* ... */
})

watch 偵聽多個數據源

在偵聽多個數據源時,把引數以陣列的形式給 watch

watch([ref1, ref2], ([newRef1, newRef2],   [prevRef1, prevRef2]) => {
  /* ... */
})

setup() 函式

在學習 Composition-Api 之前,我們需要先了解一下 setup() 函式。 setup() 是 Vue3 中的新增內容。它為基於 Composition API 的新特性提供了統一的入口。

在Vue3中,定義 methodswatchcomputeddata資料 等都放在了 setup() 函式中

注意:在 setup() 函式中訪問不到Vue的 this 例項

1. 執行時機

setup()函式會在created()生命週期之前執行。

import { ref } from "vue";
// setup 在init執行的
export default {
  name: 'HelloWorld',
  setup () {
    console.log('setup')
    const name = ref('啊sir')
    // onMounted(()=>{
    //   alert('1111')
    // })
    return {
      name
    }
  },
  mounted () {
    console.log('mounted')
  },
  beforeCreate () {
    console.log('beforeCreate')
  },
  created () {
    console.log('created')
  }
}

2. 接收props資料(由於setup沒有this,所以訪問內部屬性只能傳遞引數)

setup() 函式的第一個引數是 props ,元件接收的 props 資料可以在 setup() 函式內訪問到。

3. context上下文物件

contextsetup() 的第二個引數,它是一個上下文物件,可以通過 context 來訪問Vue的例項 this

setup(props,context) {
    console.log(this)
    console.log(context)
}

如果這篇文章對您有幫助,您可以打賞我

技術交流QQ群:15129679