Vue3.2語法糖使用總結

Vue3.2語法糖使用總結

一. 概述

Vue2時期,組件裏定義的各類變量、方法、計算屬性等是分別存放到datamethodscomputed等選項裏,這樣編寫的代碼不便於後期的查閱,查找一個業務邏輯需要在各個選項來回切換。vue3.0組合式APIsetup函數的推出就是爲了解決這個問題,它讓我們的邏輯關注點更加集中,語法也更加精簡,但是當我們在使用vue3.0的語法就構建組件的時候,總是需要把外面定義的方法變量必須要return出去才能在<template>,比較麻煩一些. vue3.2語法糖的出現以及一些新增的API,讓我們的代碼進一步簡化。

什麼是語法糖?

語法糖(英語:Syntactic sugar)是由英國計算機科學家彼得·蘭丁發明的一個術語,指計算機語言中添加的某種語法,這種語法對語言的功能沒有影響,但是更方便程序員使用。語法糖讓程序更加簡潔,有更高的可讀性。

Vue3.2語法糖

來看下vue3.0vue3.2的單文件組件(SFC,即.vue 文件)的結構對比

  • vue3.0組件
<template>
    <div>
    </div>
</template>
<script>
export default {
    components: {
    },
    props: {
    },
    setup () {
        return {}
    }
}
</script>
<style lang="scss" scoped>
</style>
  • vue3.2組件
<template>
    <MyTestVue :title="title" @click="changeTitle" />
</template>
<script setup>
import MyTestVue from './MyTest.vue';
import { ref } from 'vue';
const title = ref('測試一下')
const changeTitle = () => {
    title.value = 'Hello,World'
}
</script>
<style lang="scss" scoped>
</style>
  1. 對比vue3.0vue3.2版本的組件模板,最主要的變化是3.2中沒有了setup函數,而是把它放在了script標籤中。

  2. 我們定義的屬性和方法也不用在return中返回,直接就可以用在模板語法中
    ...

    這些是直觀的變化,接下來我們學習具體的用法。

二.使用介紹

1.組件註冊

vue3.0中使用組件,需要使用 components 選項來顯式註冊:

<script>
import ComponentA from './ComponentA.js'

export default {
  components: {
    ComponentA
  },
  setup() {
    // ...
  }
}
</script>

vue3.2 <script setup> 的單文件組件中,導入的組件可以直接在模板中使用,組件會自動註冊,並且無需指定當前組件的名字,它會自動以文件名爲主,也就是不用再寫name屬性了。

<script setup>
import ComponentA from './ComponentA.vue'
</script>

<template>
  <ComponentA />
</template>

2.Props 聲明

vue3.0中,prop可以使用props選項來聲明

<script>
export default {
  props: ['foo'],
  // 或者用這種方式指類型與默認值
  // props: {
  //   foo:{
  //     type: String,
  //     default: ''
  //   },
  // },
  setup(props) {
    // setup() 接收 props 作爲第一個參數
    console.log(props.foo)
  }
}
</script>

vue3.2組件中,props可以使用defineProps()宏來聲明

<script setup>
const props = defineProps(['foo'])
// 或者
const propsOther = defineProps({
  title: String,
  likes: Number
})

console.log(props.foo)
</script>

注意事項:所有的 props 都遵循着單向綁定原則,props 因父組件的更新而變化,自然地將新的狀態向下流往子組件,而不會逆向傳遞,這意味着你不應該在子組件中去更改一個 prop。

3.計算屬性

我們一般使用計算屬性來描述依賴響應式狀態的複雜邏輯。說白了就是這個計算屬性的值依賴於其他響應式屬性的值,依賴的屬性發生變化,那麼這個計算屬性的值就會進行重新計算。

<script setup>
import { ref, computed } from 'vue'

const firstName = ref('John')
const lastName = ref('Doe')

const fullName = computed({
  // getter
  get() {
    return firstName.value + ' ' + lastName.value
  },
  // setter
  set(newValue) {
    // 注意:我們這裏使用的是解構賦值語法
    [firstName.value, lastName.value] = newValue.split(' ')
  }
})
</script>

當調用fullName.value = 'John Doe'時,setter會被調用,而firstNamelastName會被更新,在vue3.2中我們可以直接在<template>標籤中使用它,不在需要return返回。

  • 不要在計算函數中做異步請求或者更改 DOM!
  • 一個計算屬性僅會在其響應式依賴更新時才重新計算,如果他依賴的是個非響應式的依賴,及時其值發生變化,計算屬性也不會更新。
  • 相比於方法而言,計算屬性值會基於其響應式依賴被緩存,一個計算屬性僅會在其響應式依賴更新時才重新計算

4. watch

在組合式API中,我們可以使用watch函數在每次響應式狀態發生變化時觸發回調函數,watch的第一個參數可以是不同形式的“數據源”:它可以是一個ref(包括計算屬性)、一個響應式對象、一個 getter 函數、或多個數據源組成的數組:
watch()是懶執行的:僅當數據源變化時,纔會執行回調,例如:

<script setup>
import { ref,watch } from 'vue';

const props = defineProps({
    title: String,
    itemList: {
        type: Array,
        default: () => [{
            text: 'title',
            value: 0
        }]
    }
})

watch(() => props.itemList.length,(newValue,oldValue) => {
    console.log('newValue===',newValue);
    console.log('oldValue===',oldValue);
})
</script>

這裏監聽props.itemList.length,當傳入的itemList數量發生變化時,後面的回調方法會被調用。當然wacth()還有第三個可選參數:否開啓深監聽(deep), 如果這裏這樣寫:

<script setup>
import { ref,watch } from 'vue';
...
watch(() => props.itemList,(newValue,oldValue) => {
    console.log('newValue===',newValue);
    console.log('oldValue===',oldValue);
})
</script>

當傳入的itemList數量發生改變時,回調函數不會觸發,正確的寫法是加上其第三個參數deep:true

<script setup>
import { ref,watch } from 'vue';
...
watch(() => props.itemList,(newValue,oldValue) => {
    console.log('newValue===',newValue);
    console.log('oldValue===',oldValue);
},{deep:true})
</script>

watch也可以同時監聽多個屬性:

<script setup>
import { ref,watch } from 'vue';

const props = defineProps({
    title: String,
    itemList: {
        type: Array,
        default: () => [{
            text: 'title',
            value: 0
        }]
    }
})
// 同時監聽多個屬性
watch(() => [props.itemList,props.title],(newValue,oldValue) => {
    console.log('newValue===',newValue);
    console.log('oldValue===',oldValue);
},{deep:true})
  
</script>

5. watchEffect()

watch()的懶執行不同的是,watchEffect()會立即執行一遍回調函數,如果這時函數產生了副作用,Vue會自動追蹤副作用的依賴關係,自動分析出響應源。上面的例子可以重寫爲:

<script setup>
  ...
watchEffect(() => {
    console.log('itemList===',props.itemList.length);
    console.log('title===',props.title);
})
</script>  

這個例子中,回調會立即執行。在執行期間,它會自動追蹤props.itemList.length作爲依賴(和計算屬性的行爲類似)。每當傳入的itemList.length變化時,回調會再次執行。

如果要清除watchEffect()的的監聽,只需要顯示的調用watchEffect()的返回函數就可以了,例如:

<script setup>
  ...
const stopEffect = watchEffect(() => {
    console.log('itemList===',props.itemList.length);
    console.log('title===',props.title);
})
stopEffect()
</script>

watch 只追蹤明確偵聽的數據源。它不會追蹤任何在回調中訪問到的東西。另外,僅在數據源確實改變時纔會觸發回調。我們能更加精確地控制回調函數的觸發時機。
watchEffect,則會在副作用發生期間追蹤依賴。它會在同步執行過程中,自動追蹤所有能訪問到的響應式屬性。

6.組件的事件調用

6.1 子組件調用父組件的方法

vue3.0中如果我們的子組件觸發父組件的方法,我們的做法:

子組件
<script>
export default {
  emits: ['inFocus', 'submit'],
  setup(props, ctx) {
    ctx.emit('submit',params)
  }
}
// 或者將可以將emit解構使用
export default {
    setup(props,{emit}) {
    emit('submit',params)
  }
}
</script>
父組件
<template>
    <Children @submit="submitHandel"/>
  </div>
</template>

<script>
export default {
  name: 'TodoItem',
  setup(props, { emit }) {
    const submitHandel = () => {
      console.log('子組件調用了父組件的submitHandel方法');
    }
    return {
      submitHandel,
    }
  }
};
</script>

vue3.2語法糖中,子組件要觸發的事件需要顯式地通過 defineEmits() 宏來聲明

子組件
<script setup>
const emit = defineEmits(['inFocus', 'submit'])

function buttonClick(parmas) {
  emit('submit', parmas)
}
</script>
父組件
<template>
    <Children @submit="submitHandel"/>
  </div>
</template>

<script setup>
  const submitHandel = () => {
    console.log('子組件調用了父組件的submitHandel方法');
  }
};
</script>
6.2 父組件調用子組件的方法或是屬性

vue3.0中如果父組件觸發子組件的方法或是屬性,直接在return函數中返回就可以,數據都是默認隱式暴露給父組件的。

<script>
// 子組件
setup(props, { emit }) {
  const isShow = ref(false)
  // 父組件調用這個方法
  const showSubComponent = () => {
    isShow.value = !isShow.value
  }
  return {
      // return 返回
      showSubComponent,
    }
  }
</script>

父組件中通過ref獲取到子組件,並對子組件暴露的方法進行訪問

父組件
<template>
  <div class="todo-list">
    <TodoItemVue :itemList="itemList" @clickItemHandel="clickItemHandel" ref="todoItemVueRef" />
  </div>
</template>
<script>
  import { ref } from 'vue';
  export default {
  setup(props, { emit }) {
    //獲取子組件ref
    const todoItemVueRef = ref(null)
    // 調用子組件的方法
    const callItemFuncHandel = () => {
        todoItemVueRef.value.showSubComponent()
    }
    return {
     todoItemVueRef
    }
  }
};
</script>

vue3.2語法中,父組件的調用方式相同,子組件通過defineExpose()將方法或是屬性暴露出去

子組件
<script setup>
const isShow = ref(false)
// 父組件調用這個方法
const showSubComponent = () => {
    isShow.value = !isShow.value
}
// 通過defineExpose將方法暴露出去
defineExpose({
    showSubComponent
})
</script> 
父組件
<template>
  <div class="todo-list">
    <TodoItemVue :itemList="itemList" @clickItemHandel="clickItemHandel" ref="todoItemVueRef" />
  </div>
</template>
<script setup>
  import { ref } from 'vue';
  //獲取子組件ref
  const todoItemVueRef = ref(null)
  // 調用子組件的方法
  const callItemFuncHandel = () => {
      todoItemVueRef.value.showSubComponent()
  }
</script>

7.Vuex的使用

vue3.0vue3.2中創建Vuex沒有區別,只不過在<template>模板中使用Vuex的store有細微差別。

import { createStore } from 'vuex';
import { ADD_ITEM_LIST, REDUCE_ITEM_LIST, CHANGE_ITEM_LIST_ASYNC } from './constants';

export default createStore({
  state: {
    itemList: [
      { text: 'Learn JavaScript', done: true },
      { text: 'Learn Vue', done: false },
      { text: 'Build something awesome', done: false },
    ],
  },
  getters: {
    doneItemList: (state) => state.itemList.filter((todo) => todo.done),
  },
  mutations: {
    // 使用ES2015風格的計算屬性命名功能 來使用一個常量作爲函數名
    [ADD_ITEM_LIST](state, item) {
      console.log('增加數據', item);
      state.itemList.push(item);
    },
    [REDUCE_ITEM_LIST](state) {
      console.log('減少數據');
      state.itemList.pop();
    },
  },
  actions: {
    [CHANGE_ITEM_LIST_ASYNC]({ commit, state }, todoItem) {
      /// 模擬網絡請求
      setTimeout(() => {
        commit(ADD_ITEM_LIST, todoItem);
        console.log('state===', state);
      }, 1000);
    },
  },
  modules: {
  },
});

vue3.0中我們一般在return中對store.state進行解構,然後可以直接在<template>中使用state中的值

<template>
  <div class="todo-item">
    <ol>
      <li v-for="(item,index) in itemList" :key="index" class="todos" @click="clickItem(index)">
        {{ item.text }}
      </li>
    </ol>
  </div>
</template>
<script>
  export default {
  name: 'TodoItem',
  setup(props, { emit }) {
    return {
      // 對store.state進行解構
      ...store.state,
      clickItem,
      count,
      isShow,
      showSubComponent,
    }
  }
};
</script>

vue3.2中沒有了return,需要我們顯示的獲取要使用的stare的值

<template>
  <div class="todo-item">
    <ol>
      <li v-for="(item,index) in itemList" :key="index" class="todos" @click="clickItem(index)">
        {{ item.text }}
      </li>
    </ol>
  </div>
</template>
<script setup>
import { useStore } from 'vuex';
const store = useStore()
// 獲取後在<template>中使用
const itemList = store.state.itemList
</script>

8. <style>中的 v-bind

<style>中的 v-bind: 用於在 SFC <style> 標籤中啓用組件狀態驅動的動態 CSS 值

<script setup>
import { ref, watchEffect } from 'vue';
const color = ref('black')
const callChangeColorHandel = () => {
  if(color.value === 'black') {
    color.value = 'red'
  }else {
    color.value = 'black'
  }
}
</script>
<style lang="scss" scoped>
.todo-list {
  color: v-bind(color);
}
</style>

觸發callChangeColorHandel 函數,在<style>中的v-bind指令可以動態綁定的響應式狀態。

三. 總結

整體來說,setup語法糖的引入簡化了使用Composition API時冗長的模板代碼,也就是讓代碼更加簡潔,可讀性也更高。並且官方介紹vue3.2在界面渲染的速度以及內存的使用量上都進行了優化,本文只是對setup語法糖的常用方式進行了總結,更多vue3.2新特性可以去官方文檔查看。

一些參考:

Vue3.2

Vuex

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章