Vue3.2單文件組件setup的語法糖與新特性總結(jié)
前言
滿滿的干貨,建議收藏慢慢看,可以當作Vue3.0的學習資料。
在vue2.0時期,組件里定義的各類變量、方法、計算屬性等是分別存放到data、methods、computed...選項里,這樣編寫的代碼不便于后期的查閱(查找一個業(yè)務邏輯需要在各個選項來回切換)。setup函數(shù)的推出就是為了解決這個問題,讓新手開發(fā)者更容易上手...
setup語法糖
setup是Vue3.0后推出的語法糖,并且在Vue3.2版本進行了大更新,像寫普通JS一樣寫vue組件,對于開發(fā)者更加友好了;按需引入computed、watch、directive等選項,一個業(yè)務邏輯可以集中編寫在一起,讓代碼更加簡潔便于瀏覽。
一、基本用法
只需在<script>里添加一個setup屬性,編譯時會把<script setup></script>里的代碼編譯成一個setup函數(shù)
<script setup>
console.log('hello script setup')
</script>普通的<script>只會在組件被首次引入的時候執(zhí)行一次,<script setup>里的代碼會在每次組件實例被創(chuàng)建的時候執(zhí)行。
二、data和methods
<script setup>里聲明的變量和函數(shù),不需要return暴露出去,就可以直接在template使用
<script setup>
import { ref, reactive } from 'vue'
// 普通變量
const msg = 'Hello!'
// 響應式變量
let num = ref(1111) // ref聲明基本類型變量
const obj = reactive({ // reactive聲明對象類型變量,如Object、Array、Date...
key: 'this is a object'
})
// 函數(shù)
function log() {
console.log(msg) // Hello
console.log(num.value) // 1111(可根據(jù)input輸入值而改變)
console.log(obj.key) // this is a object
}
</script>
<template>
<h1>{{ msg }}</h1>
<p>{{obj.key}}</p>
<input v-model="num" type="text" />
<button @click="log">打印日志</button>
</template>三、計算屬性computed
<script setup>
import { ref, computed } from 'vue'
let count = ref(0)
const countPlus = computed(()=>{
return count.value+1
})
</script>
<template>
<h1>計數(shù):{{ countPlus }}</h1>
</template>四、監(jiān)聽器watch、watchEffect
1、watch監(jiān)聽器除了使用方式有區(qū)別之外,其他的與vue2.0沒啥變化
<script setup>
import { ref, reactive, watch } from 'vue'
// 監(jiān)聽ref
let count = ref(0)
watch(count, (newVal, oldVal)=> {
console.log('修改后', newVal)
console.log('修改前', oldVal)
})
// 監(jiān)聽reactive屬性
const obj = reactive({
count: 0
})
watch(
()=> obj.count, // 一個函數(shù),返回監(jiān)聽屬性
(newVal, oldVal)=> {
console.log('修改后', newVal)
console.log('修改前', oldVal)
},
{
immediate: true, // 立即執(zhí)行,默認為false
deep: true // 深度監(jiān)聽,默認為false
}
)
const onChange = function(){
count.value++
obj.count++
}
</script>
<template>
<button @click="onChange">改變count</button>
</template>2、watchEffect
watchEffect是Vue3.0新增的一個監(jiān)聽屬性的方法,它與watch的區(qū)別在于watchEffect不需要指定監(jiān)聽對象,回調(diào)函數(shù)里可直接獲取到修改后的屬性的值
<script setup>
import { ref, reactive, watchEffect } from 'vue'
let count = ref(0)
const obj = reactive({
count: 0
})
setTimeout(()=>{
count.value++
obj.count++
}, 1000)
watchEffect(()=> {
console.log('修改后的count', count.value)
console.log('修改前的obj', obj.value)
})
</script>五、自定義指令directive
以 vNameOfDirective 的形式來命名本地自定義指令,可以直接在模板中使用
<script setup>
// 導入指令可重命名
// import { myDirective as vMyDirective } from './MyDirective.js'
// 自定義指令
const vMyDirective = {
beforeMount: (el) => {
// 在元素上做些操作
}
}
</script>
<template>
<h1 v-my-directive>This is a Heading</h1>
</template>六、import導入的內(nèi)容可直接使用
1、導入的模塊內(nèi)容,不需要通過 methods 來暴露它
// utils.js
export const onShow = function(name) {
return 'my name is ' + name
}// Show.vue
<script setup>
import { onShow } from './utils.js'
</script>
<template>
<div>{{ onShow('jack') }}</div>
</template>2、導入外部組件,不需要通過components注冊使用
// Child.vue
<template>
<div>I am a child</div>
</template>// Parent.vue
<script setup>
import Child from './Child.vue'
</script>
<template>
<child></child>
</template>七、聲明props和emits
在 <script setup> 中必須使用 defineProps 和 defineEmits API 來聲明 props 和 emits ,它們具備完整的類型推斷并且在 <script setup> 中是直接可用的
// Child.vue
<script setup>
// 聲明props
const props = defineProps({
info: {
type: String,
default: ''
}
})
// 聲明emits
const $emit = defineEmits(['change'])
const onChange = function() {
$emit('change', 'child返回值')
}
</script>
<template>
<h1>信息:{{ info }}</h1>
<button @click="onChange">點擊我</button>
</template>// Parent.vue
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const msg = ref('hello setup !') // 響應式變量
const onAction = function(event) {
console.log(event) // child返回值
}
</script>
<template>
<child :info="msg" @change="onAction"></child>
</template>如果使用了 Typescript,使用純類型聲明來聲明 prop 和 emits 也是可以的。
八、父組件獲取子組件的數(shù)據(jù)
父組件要想通過ref獲取子組件的變量或函數(shù),子組件須使用defineExpose暴露出去
// Child.vue
<script setup>
import { ref, defineExpose } from 'vue'
const info = ref('I am child')
const onChange = function() {
console.log('Function of child')
}
// 暴露屬性
defineExpose({
info,
onChange
})
</script>
<template>
<h1>信息:{{ info }}</h1>
<button @click="onChange">點擊我</button>
</template>// Parent.vue
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const childRef = ref()
const onAction = function() {
console.log(childRef.value.info) // I am child
console.log(childRef.value.onChange()) // Function of child
}
</script>
<template>
<child ref="childRef"></child>
<button @click="onAction">獲取子值</button>
</template>九、provide和inject傳值
無論組件層次結(jié)構(gòu)有多深,父組件都可以通過provide 選項來其所有子組件提供數(shù)據(jù),子組件通過inject接收數(shù)據(jù)
// Parent.vue
<script setup>
import { ref, provide } from 'vue'
import Child from './Child.vue'
const msg = ref('Hello, my son')
const onShow = function() {
console.log('I am your parent')
}
provide('myProvide', {
msg,
onShow
})
</script>
<template>
<child></child>
</template>// Child.vue
<script setup>
import { inject } from 'vue'
const provideState = inject('myProvide') // 接收參數(shù)
const getData = function() {
console.log(provideState.msg) // Hello, my son
console.log(provideState.onShow()) // I am your parent
}
</script>
<template>
<button @click="getData">獲取父值</button>
</template>十、路由useRoute和useRouter
<script setup>
import { useRoute, useRouter } from 'vue-router'
const $route = useRoute()
const $router = userRouter()
// 路由信息
console.log($route.query)
// 路由跳轉(zhuǎn)
$router.push('/login')
</script>十一、對await異步的支持
<script setup> 中可以使用頂層 await。結(jié)果代碼會被編譯成 async setup()
<script setup>
const post = await fetch(`/api/post/1`).then(r => r.json())
</script>十二、nextTick
// 方式一
<script setup>
import { nextTick } from 'vue'
nextTick(()=>{
console.log('Dom已更新!')
})
</script>// 方式二
<script setup>
import { nextTick } from 'vue'
await nextTick() // nextTick是一個異步函數(shù),返回一個Promise實例
// console.log('Dom已更新!')
</script>十三、全局屬性globalProperties
// main.js里定義
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// 定義一個全局屬性$global
app.config.globalProperties.$global = 'This is a global property.'
app.mount('#app')// 組件內(nèi)使用
<script setup>
import { getCurrentInstance } from 'vue'
// 獲取vue實例
const { proxy } = getCurrentInstance()
// 輸出
console.log(proxy.$global) // This is a global property.
</script>十四、生命周期
setup()里訪問組件的生命周期需要在生命周期鉤子前加上“on”,并且沒有beforeCreate和created生命周期鉤子
因為
setup是圍繞beforeCreate和created生命周期鉤子運行的,所以不需要顯式地定義它們。換句話說,在這些鉤子中編寫的任何代碼都應該直接在setup函數(shù)中編寫。

// 使用方式
<script setup>
import { onMounted } from 'vue'
onMounted(()=> {
console.log('onMounted')
})
</script>十五、與普通的script標簽一起使用
<script setup> 可以和普通的 <script> 一起使用。普通的 <script> 在有這些需要的情況下或許會被使用到:
- 無法在
<script setup>聲明的選項,例如inheritAttrs或通過插件啟用的自定義的選項; - 聲明命名導出,
<script setup>定義的組件默認以組件文件的名稱作為組件名; - 運行副作用或者創(chuàng)建只需要執(zhí)行一次的對象。
<script>
// 普通 <script>, 在模塊范圍下執(zhí)行(只執(zhí)行一次)
runSideEffectOnce()
// 聲明額外的選項
export default {
name: 'ComponentName', // 組件重命名
inheritAttrs: false,
customOptions: {}
}
</script>
<script setup>
// 在 setup() 作用域中執(zhí)行 (對每個實例皆如此)
</script>十六、v-memo新指令
該指令與v-once類似,v-once是只渲染一次之后的更新不再渲染,而v-memo是根據(jù)條件來渲染。該指令接收一個固定長度的數(shù)組作為依賴值進行記憶比對,如果數(shù)組中的每個值都和上次渲染的時候相同,則該元素(含子元素)不刷新。
1、應用于普通元素或組件;
<template> <-- 普通元素 --> <div v-memo="[valueA, valueB]"> ... </div> <-- 組件 --> <component v-memo="[valueA, valueB]"></component> </template> <script setup> import component from "../compoents/component.vue" </script>
當組件重新渲染的時候,如果 valueA 與 valueB 都維持不變,那么對這個 <div> 以及它的所有子節(jié)點的更新都將被跳過。
2、結(jié)合v-for使用
v-memo 僅供性能敏感場景的針對性優(yōu)化,會用到的場景應該很少。渲染 v-for 長列表 (長度大于 1000) 可能是它最有用的場景:
<template>
<div v-for="item in list" :key="item.id" v-memo="[item.id === selected]">
<p>ID: {{ item.id }} - selected: {{ item.id === selected }}</p>
<p>...more child nodes</p>
</div>
</template>當selected發(fā)生變化時,只有item.id===selected的該項重新渲染,其余不刷新。
style新特性
Vue3.2版本對單文件組件的style樣式進行了很多升級,如局部樣式、css變量以及樣式暴露給模板使用等。
一、局部樣式
當 <style> 標簽帶有 scoped attribute 的時候,它的 CSS 只會應用到當前組件的元素上:
<template>
<div class="example">hi</div>
</template>
<style scoped>
.example {
color: red;
}
</style>二、深度選擇器
處于 scoped 樣式中的選擇器如果想要做更“深度”的選擇,也即:影響到子組件,可以使用 :deep() 這個偽類:
<style scoped>
.a :deep(.b) {
/* ... */
}
</style>通過
v-html創(chuàng)建的 DOM 內(nèi)容不會被作用域樣式影響,但你仍然可以使用深度選擇器來設置其樣式。
三、插槽選擇器
默認情況下,作用域樣式不會影響到 <slot/> 渲染出來的內(nèi)容,因為它們被認為是父組件所持有并傳遞進來的。使用 :slotted 偽類以確切地將插槽內(nèi)容作為選擇器的目標:
<style scoped>
:slotted(div) {
color: red;
}
</style>四、全局選擇器
如果想讓其中一個樣式規(guī)則應用到全局,比起另外創(chuàng)建一個 <style>,可以使用 :global 偽類來實現(xiàn):
<style scoped>
:global(.red) {
color: red;
}
</style>五、混合使用局部與全局樣式
你也可以在同一個組件中同時包含作用域樣式和非作用域樣式:
<style> /* global styles */ </style> <style scoped> /* local styles */ </style>
六、支持CSS Modules
<style module> 標簽會被編譯為 CSS Modules 并且將生成的 CSS 類鍵暴露給組件:
1、 默認以$style 對象暴露給組件;
<template>
<p :class="$style.red">
This should be red
</p>
</template>
<style module>
.red {
color: red;
}
</style>2、可以自定義注入module名稱
<template>
<p :class="classes.red">red</p>
</template>
<style module="classes">
.red {
color: red;
}
</style>七、與setup一同使用
注入的類可以通過 useCssModule API 在 setup() 和 <script setup> 中使用:
<script setup>
import { useCssModule } from 'vue'
// 默認, 返回 <style module> 中的類
const defaultStyle = useCssModule()
// 命名, 返回 <style module="classes"> 中的類
const classesStyle = useCssModule('classes')
</script>八、動態(tài) CSS
單文件組件的 <style> 標簽可以通過 v-bind 這一 CSS 函數(shù)將 CSS 的值關聯(lián)到動態(tài)的組件狀態(tài)上:
<script setup>
const theme = {
color: 'red'
}
</script>
<template>
<p>hello</p>
</template>
<style scoped>
p {
color: v-bind('theme.color');
}
</style>參考文獻:
Vue3.2 setup語法糖、Composition API、狀態(tài)庫Pinia歸納總監(jiān)
總結(jié)
到此這篇關于Vue3.2單文件組件setup的語法糖與新特性的文章就介紹到這了,更多相關Vue3.2單文件組件setup語法糖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
vue中 router.beforeEach() 的用法示例代碼
導航守衛(wèi)主要是通過跳轉(zhuǎn)或取消的方式守衛(wèi)導航,本文通過示例代碼講解vue中 router.beforeEach() 的用法,感興趣的朋友跟隨小編一起看看吧2023-12-12
vue實現(xiàn)通過手機號發(fā)送短信驗證碼登錄的示例代碼
本文主要介紹了vue實現(xiàn)通過手機號發(fā)送短信驗證碼登錄的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-05-05

