快速掌握Vue3部分特性


  經過了漫長的迭代,Vue 3.0 終於在 2020-09-18 發布了,帶了翻天覆地的變化,使用了 Typescript 進行了大規模的重構,帶來了 Composition API RFC 版本,類似 React Hook 一樣的寫 Vue,可以自定義自己的 hook ,讓使用者更加的靈活,接下來總結一下 vue 3.0 帶來的部分新特性。

一、Vue2 與 Vue3 的對比

  • 對 TypeScript 支持不友好(所有屬性都放在了 this 對象上,難以推倒組件的數據類型)
  • 大量的 API 掛載在 Vue 對象的原型上,難以實現 TreeShaking。
  • 架構層面對跨平台 dom 渲染開發支持不友好
  • CompositionAPI。受 ReactHook 啟發
  • 更方便的支持了 jsx
  • Vue 3 的 Template 支持多個根標簽,Vue 2 不支持
  • 對虛擬 DOM 進行了重寫、對模板的編譯進行了優化操作...

二、setup函數

  setup() 函數是 vue3 中,專門為組件提供的新屬性。它為我們使用 vue3 的 Composition API 新特性提供了統一的入口, setup 函數會在 beforeCreate 之后、created 之前執行, vue3 也是取消了這兩個鈎子,統一用 setup 代替, 該函數相當於一個生命周期函數,vue 中過去的 data,methods,watch 等全部都用對應的新增 api 寫在 setup()函數中

setup(props, context) { context.attrs context.slots context.parent context.root context.emit context.refs return {} }
  • props: 用來接收 props 數據
  • context 用來定義上下文, 上下文對象中包含了一些有用的屬性,這些屬性在 vue 2.x 中需要通過 this 才能訪問到, 在 setup() 函數中無法訪問到 this,是個 undefined
  • 返回值: return {}, 返回響應式數據, 模版中需要使用的函數

三、reactive函數

  reactive() 函數接收一個普通對象,返回一個響應式的數據對象, 想要使用創建的響應式數據也很簡單,創建出來之后,在 setup 中 return 出去,直接在 template 中調用即可

<template> {{name}} // test
<template>

<script lang="ts"> import { defineComponent, reactive, ref, toRefs } from 'vue'; export default defineComponent({ setup(props, context) { let state = reactive({ name: 'test' }); return state } }); </script>

四、ref函數

  ref() 函數用來根據給定的值創建一個響應式的數據對象,ref() 函數調用的返回值是一個對象,這個對象上只包含一個 value 屬性, 只在 setup 函數內部訪問 ref 函數需要加.value

<template>
    <div class="mine"> {{count}} // 10
    </div>
</template>

<script lang="ts"> import { defineComponent, ref } from 'vue'; export default defineComponent({ setup() { const count = ref<number>(10) // 在js 中獲取ref 中定義的值, 需要通過value屬性
 console.log(count.value); return { count } } }); </script>

  在 reactive 對象中訪問 ref 創建的響應式數據

<template>
    <div class="mine"> {{count}} -{{t}} // 10 -100
    </div>
</template>

<script lang="ts"> import { defineComponent, reactive, ref, toRefs } from 'vue'; export default defineComponent({ setup() { const count = ref<number>(10) const obj = reactive({ t: 100, count }) // 通過reactive 來獲取ref 的值時,不需要使用.value屬性
 console.log(obj.count); return { ...toRefs(obj) } } }); </script>

  isRef() 函數用來判斷某個值是否為 ref() 創建出來的對象

 setup(props, context) { const name: string = 'vue'
    const age = ref<number>(18) console.log(isRef(age)); // true
    console.log(isRef(name)); // false
    return { age, name } }

  toRefs() 函數可以將 reactive() 創建出來的響應式對象,轉換為普通的對象,只不過,這個對象上的每個屬性節點,都是 ref() 類型的響應式數據

五、computed()

  該函數用來創造計算屬性,和過去一樣,它返回的值是一個 ref 對象。里面可以傳方法,或者一個對象,對象中包含 set()、get()方法

1、創建只讀的計算屬性

import { computed, defineComponent, ref } from 'vue'; export default defineComponent({ setup(props, context) { const age = ref(18) // 根據 age 的值,創建一個響應式的計算屬性 readOnlyAge,它會根據依賴的 ref 自動計算並返回一個新的 ref
    const readOnlyAge = computed(() => age.value++) // 19
    return { age, readOnlyAge } } });

2、通過 set()、get()方法創建一個可讀可寫的計算屬性

import { computed, defineComponent, ref } from 'vue'; export default defineComponent({ setup(props, context) { const age = ref<number>(18) const computedAge = computed({ get: () => age.value + 1, set: value => age.value + value }) // 為計算屬性賦值的操作,會觸發 set 函數, 觸發 set 函數后,age 的值會被更新
    age.value = 100
    return { age, computedAge } } });

六、 watch() 函數

  watch 函數用來偵聽特定的數據源,並在回調函數中執行副作用。默認情況是懶執行的,也就是說僅在偵聽的源數據變更時才執行回調。

1、監聽用 reactive 聲明的數據源

<script lang="ts"> import { computed, defineComponent, reactive, toRefs, watch } from 'vue'; interface Person { name: string, age: number } export default defineComponent({ setup(props, context) { const state = reactive<Person>({ name: 'vue', age: 10 }) watch( () => state.age, (age, preAge) => { console.log(age); // 100
        console.log(preAge); // 10
 } ) // 修改age 時會觸發watch 的回調, 打印變更前后的值
    state.age = 100
    return { ...toRefs(state) } } }); </script>

2、監聽用 ref 聲明的數據源

<script lang="ts"> import { defineComponent, ref, watch } from 'vue'; interface Person { name: string, age: number } export default defineComponent({ setup(props, context) { const age = ref<number>(10); watch(age, () => console.log(age.value)); // 100 // 修改age 時會觸發watch 的回調, 打印變更后的值
    age.value = 100
    return { age } } }); </script>

3、同時監聽多個值

<script lang="ts"> import { computed, defineComponent, reactive, toRefs, watch } from 'vue'; interface Person { name: string, age: number } export default defineComponent({ setup(props, context) { const state = reactive<Person>({ name: 'vue', age: 10 }) watch( [() => state.age, () => state.name], ([newName, newAge], [oldName, oldAge]) => { console.log(newName); console.log(newAge); console.log(oldName); console.log(oldAge); } ) // 修改age 時會觸發watch 的回調, 打印變更前后的值, 此時需要注意, 更改其中一個值, 都會執行watch的回調
    state.age = 100 state.name = 'vue3'
    return { ...toRefs(state) } } }); </script>

4、stop 停止監聽

  在 setup() 函數內創建的 watch 監視,會在當前組件被銷毀的時候自動停止。如果想要明確地停止某個監視,可以調用 watch() 函數的返回值即可,語法如下

import { computed, defineComponent, reactive, toRefs, watch } from 'vue'; interface Person { name: string, age: number } export default defineComponent({ setup(props, context) { const state = reactive<Person>({ name: 'vue', age: 10 }) const stop = watch( [() => state.age, () => state.name], ([newName, newAge], [oldName, oldAge]) => { console.log(newName); console.log(newAge); console.log(oldName); console.log(oldAge); } ) // 修改age 時會觸發watch 的回調, 打印變更前后的值, 此時需要注意, 更改其中一個值, 都會執行watch的回調
    state.age = 100 state.name = 'vue3' setTimeout(()=> { stop() // 此時修改時, 不會觸發watch 回調
      state.age = 1000 state.name = 'vue3-' }, 1000) // 1秒之后講取消watch的監聽

    return { ...toRefs(state) } } });

七、新的生命周期函數

  新版的生命周期函數,可以按需導入到組件中,且只能在 setup() 函數中使用, 但是也可以在 setup 外定義, 在 setup 中使用

import { defineComponent, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onErrorCaptured, onMounted, onUnmounted, onUpdated } from 'vue'; export default defineComponent({ setup(props, context) { onBeforeMount(()=> { console.log('beformounted!') }) onMounted(() => { console.log('mounted!') }) onBeforeUpdate(()=> { console.log('beforupdated!') }) onUpdated(() => { console.log('updated!') }) onBeforeUnmount(()=> { console.log('beforunmounted!') }) onUnmounted(() => { console.log('unmounted!') }) onErrorCaptured(()=> { console.log('errorCaptured!') }) return {} } });

八、Template refs

  通過 refs 來回去真實 dom 元素, 這個和 react 的用法一樣,為了獲得對模板內元素或組件實例的引用,我們可以像往常一樣在 setup()中聲明一個 ref 並返回它

1、還是跟往常一樣,在 html 中寫入 ref 的名稱

2、在steup 中定義一個 ref,然后在 steup 中返回 ref的實例

3、onMounted 中可以得到 ref的RefImpl的對象, 通過.value 獲取真實dom

<template>
  <!--第一步:還是跟往常一樣,在 html 中寫入 ref 的名稱-->
  <div class="mine" ref="elmRefs">
    <span>1111</span>
  </div>
</template>

<script lang="ts"> import { defineComponent, onMounted, ref } from 'vue'; export default defineComponent({ setup(props, context) { // 獲取真實dom
    const elmRefs = ref<null | HTMLElement>(null); onMounted (() => { console.log(elmRefs.value); // 得到一個 RefImpl 的對象, 通過 .value 訪問到數據
 }) return { elmRefs } } }); </script>

九、vue 的全局配置

  通過 vue 實例上 config 來配置,包含 Vue 應用程序全局配置的對象。您可以在掛載應用程序之前修改下面列出的屬性:

const app = Vue.createApp({}) app.config = {...}

  為組件渲染功能和觀察程序期間的未捕獲錯誤分配處理程序。錯誤和應用程序實例將調用處理程序

app.config.errorHandler = (err, vm, info) => {}

  可以在應用程序內的任何組件實例中訪問的全局屬性,組件的屬性將具有優先權。這可以代替 Vue 2.xVue.prototype 擴展:

app.config.globalProperties.$http = 'xxxxxxxxs'

  可以在組件用通過 getCurrentInstance() 來獲取全局 globalProperties 中配置的信息,getCurrentInstance 方法獲取當前組件的實例,然后通過 ctx 屬性獲得當前上下文,這樣我們就能在 setup 中使用 router 和 vuex,通過這個屬性我們就可以操作變量、全局屬性、組件屬性等等

setup( ) { const { ctx } = getCurrentInstance(); ctx.$http }

十、vue 3.x 完整組件模版結構

   一個完整的 vue 3.x 完整組件模版結構包含了:組件名稱、 props、components、setup(hooks、computed、watch、methods 等)

<template>
  <div class="mine" ref="elmRefs">
    <span>{{name}}</span>
    <br>
    <span>{{count}}</span>
    <div>
      <button @click="handleClick">測試按鈕</button>
    </div>

    <ul>
      <li v-for="item in list" :key="item.id">{{item.name}}</li>
    </ul>
  </div>
</template>

<script lang="ts"> import { computed, defineComponent, getCurrentInstance, onMounted, PropType, reactive, ref, toRefs } from 'vue'; interface IState { count: 0, name: string, list: Array<object> } export default defineComponent({ name: 'demo', // 父組件傳子組件參數
 props: { name: { type: String as PropType<null | ''>, default: 'vue3.x' }, list: { type: Array as PropType<object[]>, default: () => [] } }, components: { /// TODO 組件注冊
 }, emits: ["emits-name"], // 為了提示作用
 setup (props, context) { console.log(props.name) console.log(props.list) const state = reactive<IState>({ name: 'vue 3.0 組件', count: 0, list: [ { name: 'vue', id: 1 }, { name: 'vuex', id: 2 } ] }) const a = computed(() => state.name) onMounted(() => { }) function handleClick () { state.count ++
      // 調用父組件的方法
      context.emit('emits-name', state.count) } return { ...toRefs(state), handleClick } } }); </script>

  這樣簡單介紹了解下,使用的時候再深入研究學習。


免責聲明!

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



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