一文詳解Vue3中的script setup語法糖

2022-11-22 22:00:47

前端(vue)入門到精通課程:進入學習
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API偵錯工具:

script setup 語法糖

組合式 API:setup()

基本使用

Vue 3 的 Composition API 系列裡,推出了一個全新的 setup 函數,它是一個元件選項,在建立元件之前執行,一旦 props 被解析,並作為組合式 API 的入口點。【學習視訊分享:、】

setup 選項是一個接收 propscontext 的函數,我們進行討論。此外,我們將 setup 返回的所有內容都暴露給元件的其餘部分 (計算屬性、方法、生命週期勾點等等) 以及元件的模板。

<script>
// 這是一個基於 TypeScript 的 Vue 元件
import { defineComponent } from 'vue'

export default defineComponent({
  setup(props, context) {
    // 在這裡宣告資料,或者編寫函數並在這裡執行它

    return {
      // 需要給 `<template />` 用的資料或函數,在這裡 `return` 出去
    }
  },
})

</script>
登入後複製

新的 setup 選項是在元件建立之前, props 被解析之後執行,是組合式 API 的入口。

注意:
setup 中你應該避免使用 this,因為它不會找到元件範例。setup 的呼叫發生在 data property、computed property 或 methods 被解析之前,所以它們無法>在 setup 中被獲取。

在新增了setup的script標籤中,我們不必宣告和方法,這種寫法會自動將所有頂級變數、函數,均會自動暴露給模板(template)使用
這裡強調一句 「暴露給模板,跟暴露給外部不是一回事

TIP:說的通俗一點,就是在使用 Vue 3 生命週期的情況下,整個元件相關的業務程式碼,都可以放在 setup 裡執行。

因為在 setup 之後,其他的生命週期才會被啟用,我們對比一下Vue2的Vue3生命週期的變化

元件生命週期

關於 Vue 生命週期的變化,可以從下表直觀地瞭解:

Vue 2 生命週期Vue 3 生命週期執行時間說明
beforeCreatesetup元件建立前執行
createdsetup元件建立後執行
beforeMountonBeforeMount元件掛載到節點上之前執行
mountedonMounted元件掛載完成後執行
beforeUpdateonBeforeUpdate元件更新之前執行
updatedonUpdated元件更新完成之後執行
beforeDestroyonBeforeUnmount元件解除安裝之前執行
destroyedonUnmounted元件解除安裝完成後執行
errorCapturedonErrorCaptured當捕獲一個來自子孫元件的異常時啟用勾點函數

可以看到 Vue 2 生命週期裡的 beforeCreatecreated ,在 Vue 3 裡已被 setup 替代。

script setup 語法糖

它是 Vue3 的一個新語法糖,在 setup 函數中。所有 ES 模組匯出都被認為是暴露給上下文的值,幷包含在 setup() 返回物件中。相對於之前的寫法,使用後,語法也變得更簡單。

自動註冊屬性和方法無需返回,直接使用

1.<script setup> 語法糖並不是新增的功能模組,它只是簡化了以往的組合API(compositionApi)的必須返回(return)的寫法,並且有更好的執行時效能。

2.在 setup 函數中:所有 ES 模組匯出都被認為是暴露給上下文的值,幷包含在 setup() 返回物件中。相對於之前的寫法,使用後,語法也變得更簡單。

你不必擔心setup語法糖的學習成本,他是組合式API的簡化,並沒有新增的知識點。你只需要瞭解一些用法和細微的不同之處,甚至比之前寫setup()還要順手!

使用方式也很簡單,只需要在 script 標籤加上 setup 關鍵字即可

<script setup>
</script>
登入後複製

元件核心 API 的使用

元件自動註冊

在 script setup 中,引入的元件可以直接使用,無需再通過components進行註冊,並且無法指定當前元件的名字,它會自動以檔名為主,也就是不用再寫name屬性了。

範例

<template>
	<Child />
</template>

<script setup>
import Child from '@/components/Child.vue'
</script>
登入後複製

定義元件的 props

defineProps ----> [用來接收父元件傳來的 props] 程式碼示列

通過defineProps指定當前 props 型別,獲得上下文的props物件。

範例:

<script setup>
  import { defineProps } from 'vue'

  const props = defineProps({
    title: String,
  })
</script>
<!-- 或者 -->
<script setup> 
    import { ref,defineProps } from 'vue';
    
    type Props={ 
        msg:string 
    }
    defineProps<Props>(); 
</script>
登入後複製

定義 emit

defineEmit ----> [子元件向父元件事件傳遞]

使用defineEmit定義當前元件含有的事件,並通過返回的上下文去執行 emit。

程式碼示列

<script setup>
  import { defineEmits } from 'vue'
  const emit = defineEmits(['change', 'delete'])
  
</script>
登入後複製

父子元件通訊

defineProps 用來接收父元件傳來的 props ; defineEmits 用來宣告觸發的事件。

//父元件
<template>
	<Child @getChild="getChild" :title="msg" />
</template>

<script setup>
import { ref } from 'vue'
import Child from '@/components/Child.vue'
const msg = ref('parent value')
const getChild = (e) => {
	// 接收父元件傳遞過來的資料
	console.log(e); // child value
}
</script>
登入後複製
//子元件
<template>
	<div @click="toEmits">Child Components</div>
</template>

<script setup>
// defineEmits,defineProps無需匯入,直接使用
const emits = defineEmits(['getChild']);
const props = defineProps({
	title: {
		type: String,
		defaule: 'defaule title'
	}
});

const toEmits = () => {
	emits('getChild', 'child value') // 向父元件傳遞資料
}

// 獲取父元件傳遞過來的資料
console.log(props.title); // parent value
</script>
登入後複製

子元件通過 defineProps 接收父元件傳過來的資料,子元件通過 defineEmits 定義事件傳送資訊給父元件

useSlots()useAttrs()

獲取 slots 和 attrs

注:useContext API 被棄用,取而代之的是更加細分的 api。

可以通過useContext從上下文中獲取 slots 和 attrs。不過提案在正式通過後,廢除了這個語法,被拆分成了useAttrsuseSlots

  • useAttrs:見名知意,這是用來獲取 attrs 資料,但是這和 vue2 不同,裡面包含了 class屬性方法

<template>
    <component v-bind='attrs'></component>
</template>
<srcipt setup>
   const attrs = useAttrs();
<script>
登入後複製
  • useSlots: 顧名思義,獲取插槽資料。

使用範例:

// 舊
<script setup>
  import { useContext } from 'vue'

  const { slots, attrs } = useContext()
</script>

// 新
<script setup>
  import { useAttrs, useSlots } from 'vue'

  const attrs = useAttrs()
  const slots = useSlots()
</script>
登入後複製

defineExpose API

defineExpose ----> [元件暴露出自己的屬性]

傳統的寫法,我們可以在父元件中,通過 ref 範例的方式去存取子元件的內容,但在 script setup 中,該方法就不能用了,setup 相當於是一個閉包,除了內部的 template模板,誰都不能存取內部的資料和方法。

<script setup> 的元件預設不會對外部暴露任何內部宣告的屬性。
如果有部分屬性要暴露出去,可以使用 defineExpose

注意:目前發現defineExpose暴露出去的屬性以及方法都是 unknown 型別,如果有修正型別的方法,歡迎評論區補充。

如果需要對外暴露 setup 中的資料和方法,需要使用 defineExpose API。範例

//子元件

<template>
	{{msg}}
</template>

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

let msg = ref("Child Components");
let num = ref(123);

// defineExpose無需匯入,直接使用
defineExpose({
	msg,
	num
});
</script>
登入後複製
//父元件
<template>
	<Child ref="child" />
</template>

<script setup>
import { ref, onMounted } from 'vue'
import Child from '@/components/Child.vue'

let child = ref(null);

onMounted(() => {
	console.log(child.value.msg); // Child Components
	console.log(child.value.num); // 123
})
</script>
登入後複製

定義響應變數、函數、監聽、計算屬性computed

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

const count = ref(0); //不用 return ,直接在 templete 中使用

const addCount=()=>{ //定義函數,使用同上 
    count.value++; 
} 

//建立一個唯讀的計算屬性 ref:
const plusOne = computed(() => count.value + 1)

// 建立一個可寫的計算屬性 ref
const plusOne = computed({
get: () => count.value + 1, 
set: (val) => { count.value = val - 1 } 
})


//定義監聽,使用同上 //...some code else 
watchEffect(()=>console.log(count.value)); 
</script>
登入後複製

watchEffect和watch區別

1、watch是惰性執行,也就是隻有的值發生變化的時候才會執行,但是watchEffect不同,每次程式碼載入watchEffect都會執行(忽略watch第三個引數的設定,如果修改設定項也可以實現立即執行)

2、watch需要傳遞監聽的物件,watchEffect不需要

3、watch只能監聽響應式資料:ref定義的屬性和reactive定義的物件,如果直接監聽reactive定義物件中的屬性是不允許的,除非使用函數轉換一下

4、watchEffect如果監聽reactive定義的物件是不起作用的,只能監聽物件中的屬性。

reactive

返回一個物件的響應式代理。

<script setup>
import { reactive, onUnmounted } from 'vue'

const state = reactive({
    counter: 0
})
// 定時器 每秒都會更新資料
const timer = setInterval(() => {
    state.counter++
}, 1000);

onUnmounted(() => {
    clearInterval(timer);
})
</script>
<template>
    <div>{{state.counter}}</div>
</template>
登入後複製

使用ref也能達到我們預期的'counter',並且在模板中,vue進行了處理,我們可以直接使用counter而不用寫counter.value.

ref和reactive的關係:

ref是一個{value:'xxxx'}的結構,value是一個reactive物件

ref 暴露變數到模板

曾經的提案中,如果需要暴露變數到模板,需要在變數前加入export宣告:

export const count = ref(0)
登入後複製

不過在新版的提案中,無需export宣告,編譯器會自動尋找模板中使用的變數,只需像下面這樣簡單的宣告,即可在模板中使用該變數

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

const counter = ref(0);//不用 return ,直接在 templete 中使用

const timer = setInterval(() => {
    counter.value++
}, 1000)

onUnmounted(() => {
    clearInterval(timer);
})
</script>
<template>
    <div>{{counter}}</div>
</template>
登入後複製

其他 Hook Api

  • useCSSModule:CSS Modules 是一種 CSS 的模組化和組合系統。vue-loader 整合 CSS Modules,可以作為模擬 scoped CSS。允許在單個檔案元件的setup中存取CSS模組。此 api 本人用的比較少,不過多做介紹。

  • useCssVars: 此 api 暫時資料比較少。介紹v-bind in styles時提到過。

  • useTransitionState: 此 api 暫時資料比較少。

  • useSSRContext: 此 api 暫時資料比較少。

支援 async await 非同步

注意在vue3的原始碼中,setup執行完畢,函數 getCurrentInstance 內部的有個值會釋放對 currentInstance 的參照,await 語句會導致後續程式碼進入非同步執行的情況。所以上述例子中最後一個 getCurrentInstance() 會返回 null,建議使用變數儲存第一個 getCurrentInstance() 返回的參照.

<script setup>
  const post = await fetch(`/api/post/1`).then((r) => r.json())
</script>
登入後複製

<script setup> 中可以使用頂層 await。結果程式碼會被編譯成 async setup()

<script setup>
const post = await fetch(`/api/post/1`).then(r => r.json())
</script>
登入後複製

另外,await 的表示式會自動編譯成在 await 之後保留當前元件範例上下文的格式。

注意
async setup() 必須與 Suspense 組合使用,Suspense 目前還是處於實驗階段的特性。我們打算在將來的某個釋出版本中開發完成並提供檔案 - 如果你現在感興趣,可以參照 看它是如何工作的。

定義元件其他設定

設定項的缺失,有時候我們需要更改元件選項,在setup中我們目前是無法做到的。我們需要在上方再引入一個 script,在上方寫入對應的 export即可,需要單開一個 script。

<script setup> 可以和普通的 <script> 一起使用。普通的 <script> 在有這些需要的情況下或許會被使用到:

  • 無法在 <script setup> 宣告的選項,例如 inheritAttrs 或通過外掛啟用的自定義的選項。
  • 宣告命名匯出。
  • 執行副作用或者建立只需要執行一次的物件。

在script setup 外使用export default,其內容會被處理後放入原元件宣告欄位。

<script>
// 普通 `<script>`, 在模組範圍下執行(只執行一次)
runSideEffectOnce()

// 宣告額外的選項
  export default {
    name: "MyComponent",
    inheritAttrs: false,
    customOptions: {}
  }
</script>
<script setup>
    import HelloWorld from '../components/HelloWorld.vue'
    // 在 setup() 作用域中執行 (對每個範例皆如此)
    // your code
</script>
<template>
  <div>
    <HelloWorld msg="Vue3 + TypeScript + Vite"/>
  </div>
</template>
登入後複製

注意:Vue 3 SFC 一般會自動從元件的檔名推斷出元件的 name。在大多數情況下,不需要明確的 name 宣告。唯一需要的情況是當你需要 <keep-alive> 包含或排除或直接檢查元件的選項時,你需要這個名字。

(學習視訊分享:、)

以上就是一文詳解Vue3中的script setup語法糖的詳細內容,更多請關注TW511.COM其它相關文章!