1. 程式人生 > 實用技巧 >快速掌握Vue3部分特性

快速掌握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>

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