秒轉Vue3教程

20210609103414.jpg

1. Vue3的介紹

Vue.js 設計的初衷就包括能夠被漸進式地採用 ,因此Vue3支持2中的大部分特性 (遷移指南)。javascript

Vue3相比於Vue2有哪些進步?

巧黑板,實際開發沒有多大用,面試極大機率問道。💪html

e331220d4440436f8b7eabddc6e4a841.jpeg

Vue3新增特性:

  • 性能vue

    • 虛擬DOM的重寫java

    • 打包大小減小( tree shaking)react

    • 初次渲染快面試

    • 內存使用減小(compositionAPI)npm

  • 新增語法(compositionAPI)markdown

    • ref和reactiveapp

    • computed和watch框架

    • 新的生命週期函數

    • 自定義函數-Hooks函數(像React Hooks)

  • 其它新贈特性

    • Teleport 瞬移組件
    • Suspense 異步加載組件
    • 全局API的修改和優化
    • Custom renderer
  • 任何的修改都已經移到全局的實例上去

    全局配置

    • Vue.config 改爲 app.config

    全局註冊類API

    • Vue.component 改成 app.component

    • Vue.directive 改成 app.directive

    行爲擴展類API

    • Vue.mixin 改成 app.mixin

    • Vue.use 改成 app.use

  • 更好的Typescript支持

Vue2遇到的難題

Vue2的組件 API 設計所面對的核心問題之一就是如何組織邏輯,以及如何在多個組件之間抽取和複用邏輯。基於 Vue 2 目前的 API 咱們有一些常見的邏輯複用模式,但都或多或少存在一些問題。這些模式包括:

  • Mixins
    • 命名衝突
    • 不清楚暴露出來變量的做用
    • 重用到其餘component常常會遇到問題
  • 高階組件 (Higher-order Components, aka HOCs)
  • Renderless Components (基於 scoped slots / 做用域插槽封裝邏輯的組件)

整體來講,以上這些模式存在如下問題:

  • 模版中的數據來源不清晰

    ​ 舉例來講,當一個組件中使用了多個 mixin 的時候,光看模版會很難分清一個屬性究竟是來自哪個 mixin。HOC 也有相似的問題。

  • 命名空間衝突

    ​ 由不一樣開發者開發的 mixin 沒法保證不會正好用到同樣的屬性或是方法名。HOC 在注入的 props 中也存在相似問題。

  • 性能

    ​ HOC 和 Renderless Components 都須要額外的組件實例嵌套來封裝邏輯,致使無謂的性能開銷。

Vue3解決的問題

Function-based API 受 React Hooks 的啓發,提供了一個全新的邏輯複用方案,且不存在上述問題。使用基於函數的 API,咱們能夠將相關聯的代碼抽取到一個 "composition function"(組合函數)中 —— 該函數封裝了相關聯的邏輯,並將須要暴露給組件的狀態以響應式的數據源的方式返回出來。這裏是一個用組合函數來封裝鼠標位置偵聽邏輯的例子:

function useMouse() {
  const x = ref(0)
  const y = ref(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 }
}

// 在組件中使用該函數
const Component = {
  setup() {
    const { x, y } = useMouse()
    // 與其它函數配合使用
    const { z } = useOtherLogic()
    return { x, y, z }
  },
  template: `<div>{{ x }} {{ y }} {{ z }}</div>`
}
複製代碼

從以上例子中能夠看到:

  • 暴露給模版的屬性來源清晰(從函數返回)
  • 返回值能夠被任意重命名,因此不存在命名空間衝突
  • 沒有建立額外的組件實例所帶來的性能損耗
類型問題:

Vue3本來指望Class類解決類型推倒問題,最後依然存在類型問題.最後採用了自然對類型很友好的基於函數的API,由於基於函數API在使用TS或是原生JS時寫出來的代碼幾乎一摸同樣

打包尺寸:

基於函數的 API 每個函數均可以被 ES export 被單獨引入,主要這樣對tree-shaking很友好

沒有使用的API打包的時候會被移除,基於函數API有更好的壓縮效率,可是Class的屬性和方法名不行

2. 體驗Vue3

安裝三種方式:

  1. 在頁面上以 CDN 包的形式導入。
  2. 使用 npm 安裝它。
  3. 使用官方的 CLI 來構建一個項目。
1. cdn
<script src="https://unpkg.com/vue@next"></script>
複製代碼
2. npm
// 最新穩定版
npm install vue@next
複製代碼
3. 命令行工具 (CLI)
// 最新穩定版
yarn global add @vue/cli
// 而後在Vue項目中運行(更新相關插件)
vue upgrade --next
// 建立Vue3項目跟2同樣
vue create vue3_demo
複製代碼
4. Vite:
// yarn
yarn create @vitejs/app <project-name>‘
cd <project-name>
yarn
yarn dev
複製代碼

hello,word!!!

<script src="https://unpkg.com/vue@next"></script>
<body>
    <div id="app">
        {{msg}} == {{count}}
    </div>
</body>
<script> // 沒用compositionAPI跟傳統寫法同樣 const CountApp = { data() { return { msg: 'hello,word', count: 0 } }, mounted() { setInterval(() => { this.count++ }, 1000) } } // 聲明式渲染 Vue.createApp(CountApp).mount('#app') </script>
複製代碼

CompositionAPI:

const { ref, computed, watch, onMounted } = Vue
const App = {
  template: ` <div> <span> 鐵蛋兒很帥!!!{{ count }}</span> <span> 你也是這麼認爲的!!!! {{ plusOne }}</span> <button @click="increment">點贊+++</button> </div> `,
  setup() {
    // 響應的數據
    const count = ref(0)
    // 計算屬性
    const plusOne = computed(() => count.value + 1)
    // method方法
    const increment = () => { count.value++ }
    // 觀察
    watch(() => count.value * 2, val => {
      console.log(`count * 2 is ${val}`)
    })
    // 生命週期
    onMounted(() => {
      console.log(`mounted`)
    })
    // 雙向綁定的數據必須返回
    return {
      count,
      plusOne,
      increment
    }
  }
}
// 聲明式渲染
Vue.createApp(App).mount('#app')
複製代碼

3. 響應式對象

1. setup函數

setup函數是組件邏輯的地方,它在組件實例被建立時,初始化 props 以後調用。

// setup() 會接收到初始的 props 做爲參數:
const MyComponent = {
  props: {
    name: String
  },
  setup(props) {
    console.log(props.name)
  }
}
複製代碼

注意:

傳進來的 props 對象雖然是響應式的能夠被看成數據源去觀測,可是後續props 發生變更時它也會被框架內部同步更新,因此不要直接修改。(會致使警告)

組件狀態 :

setup() 能夠返回一個對象相似data,這個對象上的屬性將會被暴露給模版的渲染上下文:

const MyComponent = {
  props: {
    name: String
  },
  setup(props) {
    return {
      msg: `hello ${props.name}!`
    }
  },
  template: `<div>{{ msg }}</div>`
}
複製代碼

2. ref

建立一個能夠在 setup() 內部被管理的值,可使用 ref 函數。

ref函數返回的是隻有一個value屬性的包裝對象(主要是用來包裝原始類型的數據)。

import { ref } from 'vue'

const MyComponent = {
  setup(props) {
    const msg = ref('hello')
    const appendName = () => {
      msg.value = `hello ${props.name}`
    }
    return {
      msg,
      appendName
    }
  },
  // 當包裝對象被暴露給模版渲染上下文,或是被嵌套在另外一個響應式對象中的時候,它會被自動展開爲內部的值。
  template: `<div @click="appendName">{{ msg }}</div>`
}

// 這樣包裝對象的值就能夠直接修改了
console.log(msg.value) // 'hello'
msg.value = 'tiedan' // 修改成tiedan
複製代碼

爲何須要包裝對象?

咱們知道在 JavaScript 中,原始值類型如 string 和 number 是隻有值,沒有引用的。若是在一個函數中返回一個字符串變量,接收到這個字符串的代碼只會得到一個值,是沒法追蹤原始變量後續的變化的。

所以,包裝對象的意義就在於提供一個讓咱們可以在函數之間以引用的方式傳遞任意類型值的容器。

3. reactive

返回一個沒有包裝的響應對象(主要用於非原始類型的數據

import { reactive } from 'vue'

const object = reactive({
  count: 0
})

object.count++
複製代碼

4. toRefs

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

說人話就是: 響應式對象轉爲普通對象用它能夠再變成響應式的

const { reactive, toRefs } = Vue
 const MyComponent = {
   setup() {
     const obj = reactive({
       count: 0
     })
     const inc = () => {
       obj.count++
     }
     return {
       // 
       // 若是直接用ES6擴展運算符,會取消雙向數據綁定的特性,使用toRefs(),轉爲響應式數據
       ...toRefs(obj),
       inc
     }
   },
   template: `<h1 @click="inc">{{ count }}</h1>`
 }
複製代碼

5. Watchers

watchEffect

自動收集依賴源,依賴源更新時從新執行自身

  1. 當即執行,沒有惰性,頁面的首次加載就會執行。

  2. 自動檢測內部代碼,代碼中有依賴就會執行。

  3. 不須要傳遞要偵聽的內容, 會自動感知代碼依賴。

const { watchEffect, ref } = Vue

Vue.createApp({
  setup() {
    const userId = ref(0)
    // 只要數據數據發生改變就觸發
    watchEffect(() => console.log(userId.value))
    setTimeout(() => {
      userId.value = 1
    }, 1000)
    return {
      userId
    }
  }

}).mount('#app')
複製代碼

watch

顯式指定依賴源,依賴源更新時執行回調函數

  1. 具備必定的惰性lazy 第一次頁面展現的時候不會執行,只有數據變化的時候纔會執行。
  2. 參數能夠拿到當前值和原始值。
  3. 能夠偵聽多個數據的變化。
// 偵聽一個 getter
const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  }
)

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

// 偵聽多個數據源
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
  /* ... */
})
複製代碼

兩着的區別 :

  • watchEffect和computed比較像,都是經過執行反作用函數獲取要監聽的數據源,而watch是經過第一參數獲取要監聽的數據源(能夠監聽多個)
  • watch的參數有3個(數據源,反作用函數,配置),watchEffect只有兩個(反作用函數,配置)
  • watch的反作用函數接收的參數比watchEffect多兩個(新舊值)

相同點 :

  • watch 與 watchEffect共享中止偵聽(源碼都是調用doWatch方法只是傳的參數不一樣)

  • 相同的反作用刷新時機

  • 都能經過返回的中止函數中止監聽

4. 生命週期

  • 除去 beforeCreatecreated以外,在咱們的 setup 方法中,有9箇舊的生命週期鉤子,咱們能夠在setup方法中訪問

    • onBeforeMount——掛載開始前調用

    • onMount——掛載後調用

    • onBeforeUpdate——當響應數據改變,且從新渲染前調用

    • onUpdated——從新渲染後調用

    • onBeforeUnmount——Vue實例銷燬前調用

    • onUnmounted——實例銷燬後調用

    • onActivated——當keep-alive組件被激活時調用

    • onDeactivated——當keep-alive組件取消激活時調用

    • onErrorCaptured——從子組件中捕獲錯誤時調用

  • 改變兩個銷燬生命週期更具備語義化

    • beforeDestory 換成 beforeUnmount
    • destoryed 換成 unmounted
  • 新增兩個方便調試生命週期

    • onRenderTracked
    • onRenderTriggered

5. Teleport瞬移組件

​ 一個常見的場景是建立一個包含全屏模式的組件。在大多數狀況下,你但願模態框的邏輯存在於組件中,可是模態框的快速定位就很難經過 CSS 來解決,或者須要更改組件組合

​ 當在初始的 HTML 結構中使用這個組件時,咱們能夠看到一個問題——模態框是在深度嵌套的 div 中渲染的,而模態框的 position:absolute 以父級相對定位的 div 做爲引用。

​ Teleport 提供了一種乾淨的方法,容許咱們控制在 DOM 中哪一個父節點下渲染了 HTML,而沒必要求助於全局狀態或將其拆分爲兩個組件。

6. Suspense異步組件

Suspense組件用於在等待某個異步組件解析時顯示後備內容

應用實例:

  • 在頁面加載以前顯示加載動畫
  • 顯示佔位符內容
  • 處理延遲加載的圖像

定義異步:

app.component('component-a', {
        setup() {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve({
                        result: "異步組件"
                    });
                }, 2000)
            })
        },
        template: `<h1>{{result}}</h1>`,         
 })
複製代碼

Suspense組件應用:

<div id="app">
  <Suspense>
    <template #default>
      <component-a></component-a>
    </template>
    <template #fallback>
      <h1>Loadding...</h1>
    </template>
  </Suspense>
</div>
複製代碼

以上就是我對Vue3的簡單總結

相關文章
相關標籤/搜索