Vue 系列之:defineProps、defineEmits、...

defineProps

用于接收父組件傳遞的屬性值。

父組件:

<!-- 父組件 -->
<template><Child1 str="字符串" :num="num" />-----------------<Child2 str="字符串" :num="num" />
</template><script setup>
import { ref } from 'vue';
import Child1 from './views/Child1.vue';
import Child2 from './views/Child2.vue';const num = ref(18)setInterval(() => {num.value++
}, 1000);
</script>

子組件1:

<!-- Child1 -->
<template><div><p>{{ str }}</p><!-- 兩種寫法都可以 --><p>{{ props.str }}</p><p>{{ num }}</p><p>{{ obj }}</p><p>{{ fun }}</p><!-- 會報錯 --><!-- v-model cannot be used on a prop, because local prop bindings are not writable. --><!-- <input v-model="str" /> --><!-- 在 input 框中輸入值并不會引起 str 的變化 --><input v-model="newStr" /></div>
</template><script setup>
import { ref } from 'vue';const props = defineProps({str: String,/*** 通過 defineProps 定義的 props 是響應式的。* 這意味著當父組件傳遞給子組件的 prop 值發生變化時,* 子組件中的相應 prop 也會自動更新,* 并且任何依賴于這些 props 的計算屬性、偵聽器(watchers)或模板都會更新。*/num: {type: Number,default: 0},obj: {type: Object,default: () => {return {}}},fun: {type: Function,default: null}
});console.log("child1 created props:", props, typeof props); // Proxy 對象
console.log("child1 created str:", props.str, typeof props.str); // 字符串
console.log("child1 created num:", props.num);
console.log("child1 created obj:", props.obj, typeof props.obj); // object
console.log("child1 created fun:", props.fun); // objectconst newStr = ref(props.str)
console.log("child2 newStr:", newStr.value)
</script>

子組件2:

<!-- Child2 -->
<template><div><p>{{ str }}</p><!-- 這里就不能這樣寫了,會報錯 --><!-- <p>{{ props.str }}</p> --><p>{{ num }}</p><p>{{ obj }}</p><p>{{ fun }}</p></div>
</template><script setup>
import { ref, computed } from 'vue';const { str, num, obj, fun } = defineProps({str: String,num: {type: Number,default: 0},obj: {type: Object,default: () => {return {}}},fun: {type: Function,default: null}
});console.log("child2 created str:", str);
console.log("child2 created num:", num);
console.log("child2 created obj:", obj);
console.log("child2 created fun:", fun);/*** 在 Vue 3 中,通過 defineProps 定義的 props 是只讀的,不能直接修改。* 這是為了確保數據流保持單向,即父組件傳遞給子組件的數據不會被子組件意外地改變。* 非要修改只能使用計算屬性或者創建一個響應式變量*/
// str = '改變字符串' // 會報錯const changeStr1 = computed(() => {return `使用計算屬性改變${str}`
})
console.log("child2 changeStr1:", changeStr1.value);const changeStr2 = ref(str)
changeStr2.value = `使用響應式變量改變${str}`
console.log("child2 changeStr2:", changeStr2.value);
</script>

defineProps() 返回的是一個 Proxy 對象,它既不是 ref 對象,也不是 reactive 對象。

defineProps() 返回的對象的屬性值是普通數據類型或普通對象,也不是 ref/reactive 對象。

defineEmits

用在子組件中。表面上看它的作用似乎是用于子組件調用父組件的方法。

更準確的說法是用來定義子組件可以發出的事件,父組件可以通過監聽這些事件來響應子組件的行為。

它實際上是在告訴父組件:當‘我’使用 emit 的時候,你父組件需要做出相應的響應,你想怎么響應是你父組件自己的事情。

<!-- 子組件 -->
<template><button @click="handleClick">子組件按鈕</button>
</template><script setup>
// 定義可以發出的事件
const emit = defineEmits(['update'])function handleClick() {// 發出 'update' 事件給父組件emit('update', '我是參數')
}
</script>

<!-- 父組件 -->
<template><!-- 監聽子組件的 'update' 事件 --><Child1 @update="handleUpdate" />
</template><script setup>
import Child1 from './views/Child1.vue';function handleUpdate(params) {// 父組件做出響應:打印參數值console.log("params:", params);
}
</script>

defineExpose

用在子組件中,用于暴露子組件實例的方法和數據。它可以讓父組件通過 ref 獲取子組件的特定方法或數據。

<!-- 子組件 -->
<template>子組件
</template><script setup>
import { ref } from 'vue';const str = ref('子組件字符串')
const fun = function () {console.log("子組件方法觸發...");
}
const other = '其他'defineExpose({str, // ES6 簡化寫法num: 18,fun: fun
})
</script>

<!-- 父組件 -->
<template><Child1 ref="child1" /><button @click="handleClick">父組件按鈕</button>
</template><script setup>
import { onMounted, ref } from 'vue';
import Child1 from './views/Child1.vue';const child1 = ref(null)console.log("created str:", child1.value.str); // 第 15 行,報錯onMounted(() => {console.log("mounted str:", child1.value.str); // 子組件字符串
})function handleClick() {console.log("str:", child1.value.str); // 子組件字符串console.log("num:", child1.value.num); // 18child1.value.fun() // 子組件方法觸發...console.log("other:", child1.value.other); // undefined
}
</script>

為什么第 15 行會報錯?

因為在 setup 函數執行時,子組件還沒有被掛載到 DOM 上,組件的實例也沒有準備好。因此,此時 child1.valuenull

為什么 child1.value.other 是 undefined?

因為子組件中沒有通過 defineExpose 來暴露 other

defineAsyncComponent

異步組件。可以理解為延遲加載或按需加載。比如當一個大型頁面中包含很多個子組件,如果一次性加載所有內容勢必會導致頁面渲染速度過慢,這時候就可以對那些不需要第一時間就加載的、或者需要經過某些操作后才加載的子組件使用異步組件。

同步組件寫法:

<!-- App.vue -->
<template><button @click="handleClick">加載子組件</button><SyncComponent v-if="show" />
</template><script setup>
import { ref } from 'vue';
import SyncComponent from "./views/Child1.vue";const show = ref(false)function handleClick() {show.value = true
}
</script>

異步組件寫法:

<!-- App.vue -->
<template><button @click="handleClick">加載子組件</button><AsyncComponent v-if="show" />
</template><script setup>
import { defineAsyncComponent, ref } from 'vue';const AsyncComponent = defineAsyncComponent(() => import('./views/Child1.vue'))
const show = ref(false)function handleClick() {show.value = true
}</script>

檢查控制臺發現:無論同步組件還是異步組件,頁面元素都沒有渲染子組件內容。

在這里插入圖片描述

那么他們有什么區別?

區別是你可以在控制臺的 Network 中發現:

  • 同步組件會在頁面初始化時一次性加載 App.vue 和 Child1.vue

  • 異步組件在頁面初始化時只加 App.vue,當點擊按鈕時再加載 Child1.vue。


知道了 defineAsyncComponent 的作用,下面詳細介紹 defineAsyncComponent 的用法:

defineAsyncComponent 方法接收一個返回 Promise 的加載函數:

import { defineAsyncComponent } from 'vue'const AsyncComp = defineAsyncComponent(() => {return new Promise((resolve, reject) => {// ...從服務器獲取組件resolve(/* 獲取到的組件 */)})
})

ES6 模塊動態導入也會返回一個 Promise,所以也可以這樣寫:

import { defineAsyncComponent } from 'vue'const AsyncComp = defineAsyncComponent(() =>import('./components/MyComponent.vue')
)

異步組件會將接收到的 props 和插槽傳給內部組件,所以你可以使用這個異步的包裝組件無縫地替換原始組件,同時實現延遲加載。


異步操作不可避免地會涉及到加載和錯誤狀態,因此 defineAsyncComponent 也支持在高級選項中處理這些狀態:

import LoadingComponent from './views/Loading.vue';
import ErrorComponent from './views/Error.vue';const AsyncComp = defineAsyncComponent({// 加載函數loader: () => import('./views/Child1.vue'),// 加載異步組件時使用的組件loadingComponent: LoadingComponent,// 展示加載組件前的延遲時間,默認為 200msdelay: 2000,// 加載失敗后展示的組件errorComponent: ErrorComponent,// 如果提供了一個 timeout 時間限制,并超時了,// 也會顯示加載失敗后展示的組件,默認值是:Infinitytimeout: 3000
})

注意:delay: 2000 并不是指等待 2s 后才開始加載異步組件,而是指在異步組件開始加載后,等待 2s 再顯示 loadingComponent。

當使用服務器端渲染時還可以配置:在空閑時進行激活、在可見時激活、自定義策略等等…這里不做拓展,詳情可以直接訪問官網。


Suspense

[s??spens]

Suspense 是一個包裹異步組件的容器組件,用來處理異步組件加載期間的 UI 狀態。

Suspense 組件有兩個插槽:

  • #default:默認插槽,這個插槽用于放置異步組件。當異步組件加載完成后,#default 插槽中的內容將被渲染。

  • #fallback:備用插槽,當異步組件正在加載時,#fallback 插槽中的內容會被渲染。

父組件:

<!-- 父組件 -->
<template><div>我是父組件內容</div><Suspense><AsyncComponent /><template #fallback><!-- <h1>正在加載中...</h1> --><LoadingComponent /></template></Suspense>
</template><script setup>
import LoadingComponent from "./views/LoadingComponent.vue";
import { defineAsyncComponent } from 'vue';
const AsyncComponent = defineAsyncComponent(function () {return new Promise(resolve => {setTimeout(() => {resolve(import('./views/AsyncComponent.vue'))}, 5000);});
})
</script>

異步子組件:

<!-- 異步子組件 -->
<template><div>我是異步子組件</div>
</template><script setup>
import { onMounted } from 'vue';
onMounted(() => {console.log("子組件 onMounted 執行");
})
</script>

備用組件 LoadingComponent:

<!-- 備用組件 -->
<template><div>加載中...</div>
</template>

在這里插入圖片描述

5 秒后頁面內容替換:

在這里插入圖片描述


注意點:#default 和 #fallback 兩個插槽都只允許一個直接子節點。

<template #fallback> Loading... </template>
<template #fallback> <LoadingComponent /> 
</template>
<template #fallback> <h1>正在加載中...</h1>
</template>

都是可以的,但是

<template #fallback>哈哈<h1>正在加載中...</h1>
</template>

就會報錯。因為它有兩個直接子節點。

Suspense 組件會觸發三個事件:pendingfallbackresolve

  • pending 事件是在進入掛起狀態時觸發。

  • fallback 事件是在 #fallback 插槽的內容顯示時觸發。

  • resolve 事件是在 #default 插槽完成獲取新內容時觸發。

<template><div>我是父組件內容</div><Suspense @pending="handlePending" @fallback="handleFallback" @resolve="handleResolve"><AsyncComponent /><template #fallback><h1>正在加載中...</h1></template></Suspense>
</template><script setup>
import { defineAsyncComponent } from 'vue';
const AsyncComponent = defineAsyncComponent(function () {return new Promise(resolve => {setTimeout(() => {resolve(import('./views/AsyncComponent.vue'))}, 5000);});
})let num = 1setInterval(() => {console.log(num++);
}, 1000)function handlePending() {console.log("pending...");
}
function handleFallback() {console.log("fallback...");
}
function handleResolve() {console.log("resolve...");
}
</script>

在這里插入圖片描述


defineAsyncComponent 的實際作用:

手摸手教你利用defineAsyncComponent實現長頁面按需加載組件

路由懶加載:

const routes = [{path: '/dashboard',component: defineAsyncComponent(() => import('./views/Dashboard.vue'))},{path: '/profile',component: defineAsyncComponent(() => import('./views/Profile.vue'))}
];

通過 Vue Router 的懶加載機制,只有在用戶訪問特定路由時,相關頁面組件才會被加載。

拓展:CommonJS 的 require() 也可以實現路由懶加載。

defineOptions

在 Vue 3.3 及之后的版本中,defineOptions 是一個新引入的宏(macro),它允許開發者在 <script setup> 語法糖中聲明組件的選項(options)。

這個特性解決了之前需要額外編寫一個非 setup 的<script>標簽來配置選項的問題。

// 設置組件名并禁止屬性繼承
defineOptions({name: 'MyComponent', // 組件名稱inheritAttrs: false        // 禁止屬性繼承
});

注意:

可以在<script setup>之外使用<script>標簽來配置選項,但是<script setup>和普通<script>中的 setup() 函數不能同時用來定義響應式數據。

例如:

<template><div>{{ name }}{{ age }}</div>
</template><script setup>
import { ref } from 'vue';
const name = ref('張三');
</script><script>
import { ref } from 'vue';
export default {setup() {const age = ref(10);return { age };}
}
</script>

同時使用了<script setup>和 setup(),則頁面不會按預期展示。

<template><div>{{ name }}{{ age }}</div>
</template><script setup>
import { ref } from 'vue';
const name = ref('張三');
const age = ref(10);
</script><script>
export default {// 這里可以配置選項式 API 的內容,比如 props、emits、components 等props: {// 示例 props 配置someProp: String},emits: ['someEvent'],components: {// 示例組件配置// SomeComponent}
}
</script>

在這個示例里,<script setup>負責定義響應式數據和邏輯,普通<script>負責配置選項式 API 的內容。這樣就能把兩種語法風格結合起來使用。

defineComponent

從 API 名稱來看,意思是定義一個組件,是 Vue 3 中引入的一個輔助函數,主要用于 TypeScript 項目中。它允許你在定義組件選項時獲得更好的類型推斷和 IDE(如 VSCode)中的自動補全功能。通過使用 defineComponent,IDE 能夠識別這是一個 Vue 組件,并據此提供 Vue 特有的 API 提示和類型檢查。

什么意思呢?

在 Vue2 中,我們會習慣這樣寫:

export default {//...
}

這個時候,對于開發工具而言,{} 只是一個普通的 Object 對象,開發工具不會對一個普通對象做任何特殊的處理。

但是增加一層 defineComponet 的話:

export default defineComponent({//...
})

你實際上就是在告訴開發工具,我使用的是 Vue3,你需要給我一些 Vue3 相關的自動提示。這樣在你寫代碼的時候,開發工具會給出更多的一些自動提示幫你補全代碼。

核心源碼:

var Vue = (function (exports) {// 定義組件function defineComponent(options) {return isFunction(options) ? { setup: options, name: options.name } : options;}exports.defineComponent = defineComponent;  
}({}));

參數 options 是一個選項對象或 setup 函數。

什么是選項對象?

Vue2 中寫的:

export default {data() {// ...},methods: {// ...}
}

這些就是選項對象。

Vue3中defineComponent 的作用詳解

Vue 中的 defineComponent

Vue3源碼解析-defineComponent

defineSlots

與 defineComponent 類似,輔助功能,用于類型檢查和 IDE 的自動補全,主要用于 TypeScript 環境下。

defineCustomElement

defineCustomElement 作用是定義一個自定義元素。在 Vue 中我們可以自己寫 .vue 文件封裝組件,那么它存在的意義是什么呢?

在 Vue 3 中,defineCustomElement 是一種特殊的 API,它允許你將 Vue 組件轉換為 Web Components,這樣它們就能在任何現代瀏覽器中作為原生的自定義 HTML 元素使用,而不僅僅是在 Vue 應用中使用。與常規的 Vue 組件不同,使用 defineCustomElement 創建的組件可以獨立于 Vue 環境運行,也可以在非 Vue 項目中使用。

這個方法接收的參數和 defineComponent 完全相同。但它會返回一個繼承自 HTMLElement 的自定義元素構造器:

import { defineCustomElement } from 'vue'const MyVueElement = defineCustomElement({// 這里是同平常一樣的 Vue 組件選項props: {},emits: {},template: `...`,// defineCustomElement 特有的:注入進 shadow root 的 CSSstyles: [`/* inlined css */`]
})// 注冊自定義元素
// 注冊之后,所有此頁面中的 `<my-vue-element>` 標簽都會被升級
customElements.define('my-vue-element', MyVueElement)

有興趣的可以看看 Web Component 的相關知識 【zh-CN】。可以將 Web Components 簡單理解為一個自定義的 HTML 標簽。

用的很少,就不做具體研究。

Vue 與 Web Components

Vue3中defineCustomElement的使用

defineModel

僅在 3.4+ 中可用

v-model

Vue 系列之:自定義雙向數據綁定

defineModel 就是簡化 v-model 實現過程

下面使用 defineModel 來實現雙向數據綁定的例子:

父組件:

<!-- 父組件 -->
<template><div><p>Count in parent: {{ count }}</p><Children v-model="count" /></div>
</template><script setup>
import { ref, watch } from 'vue'
import Children from './Children.vue';const count = ref(1)watch(count, (newVal, oldVal) => {console.log(newVal, oldVal, typeof newVal) // number 類型
})  
</script>

子組件:

<!-- 原子組件代碼 -->
<template><div><!-- 這里不能直接 v-model="modelValue" 會報編譯錯誤v-model cannot be used on a prop, because local prop bindings are not writable.--><el-select v-model="selectValue" @change="handleChange"><el-option label="選項1" :value="1" /><el-option label="選項2" :value="2" /><el-option label="選項3" :value="3" /></el-select></div>
</template><script setup>
import { ref, defineProps, defineEmits } from 'vue'const props = defineProps({modelValue: Number
})const emit = defineEmits(['update:modelValue'])const selectValue = ref(props.modelValue)function handleChange() {console.log('選項變化了');emit('update:modelValue', selectValue.value);
}
</script>
<!-- 使用 defineModel 的子組件代碼 -->
<template><div><el-select v-model="selectValue" @change="handleChange"><el-option label="選項1" :value="1" /><el-option label="選項2" :value="2" /><el-option label="選項3" :value="3" /></el-select></div>
</template><script setup>
import { ref, defineProps, defineEmits } from 'vue'// const props = defineProps({
//     modelValue: Number
// })// const emit = defineEmits(['update:modelValue'])// const selectValue = ref(props.modelValue)// function handleChange() {
//     console.log('選項變化了');
//     emit('update:modelValue', selectValue.value);
// }const selectValue = defineModel()function handleChange() {console.log('選項變化了:', selectValue.value);// emit('update:modelValue', selectValue.value);
}
</script>

非常簡單!

defineModel 就是封裝了之前的實現過程:在子組件內定義了一個叫 selectValueref 變量(當然也可以取別的變量名)和名字叫 modelValue 的 props,并且 watch 了 props 中的 modelValue。當父組件改變 modelValue 的值后會同步更新 selectValue 變量的值;當子組件改變 selectValue 變量的值后會調用 update:modelValue 事件,父組件收到這個事件后就會更新父組件中對應的變量值。

defineModel 中的 type 和 default

默認情況就使用:

const model = defineModel();

如果想定義類型:

const model = defineModel({ type: String })

類型 + 默認值:

const model = defineModel({ type: String, default: "張三" });

自定義屬性名:

<!-- 父組件 -->
<Children v-model:aa="count"></Children>
// 子組件
const model = defineModel('aa', { type: String, default: "張三" })

綁定多個屬性:

<!-- 父組件 -->
<Children v-model:name="myName" v-model:age="myAge"></Children>
// 子組件
const model1 = defineModel('name')
const model2 = defineModel('age', { type: Number, default: 8 })

setup() 和 <script setup> 的區別

編譯

在編譯時 setup() 難以進行深度靜態分析, 因為它的返回值是動態的(比如返回的對象可能包含運行時才能確定的屬性或方法)。

<script setup>是編譯時語法糖,它的頂層綁定(變量、函數、import 等)是直接暴露給模板的,編譯器可以明確知道哪些內容會被模板使用,從而進行更多優化,例如更好的 Tree-shaking:未在模板中使用的代碼可以被標記并移除。

總結:

Vue 編譯器可以對<script setup>語法糖內部的代碼進行靜態分析,從而進行更多的編譯時優化,減少運行時的開銷,提高組件的渲染性能。因此在性能優化上 setup() 不如<script setup>

上下文

setup() 函數通過參數 props 和 context 來訪問組件的屬性和上下文。

  • props 就是 Vue2 中組件中的 props,指父組件傳遞來的參數

  • context 有三個屬性 attrs slots emit 分別對應 Vue2 中的 attrs 屬性、slots 插槽、$emit 事件

子組件接收父組件傳遞的值:

setup():

<script>
export default {props: {num: {type: Number,default: 1}},setup (props) {console.log(props)}
}
</script>

<script setup>:

<script setup>
import { defineProps } from 'vue'
const props = defineProps({num: {type: Number,default: 1}
})
</script>

子組件給父組件傳值:

setup():

<script>
export default {setup (props, context) {const sendNum = () => {context.emit('submit', 1200)}return { sendNum }}
}
</script>

<script setup>:

<script setup>
import { defineProps, defineEmits } from 'vue'
const emit = defineEmits(['submit'])
const sendNum = () => {emit('submit', 1000)
}
</script>

<script setup>使用 defineProps 和 defineEmits 宏來訪問組件的屬性和觸發自定義事件,不需要手動接收 props 和 context。

return

setup() 函數是一個標準的組件選項(Component Option),由 Vue 運行時直接解析。需顯式返回對象,其屬性暴露給模板。

即:setup() 中的內容需要顯式地 return 才能在模板中訪問(屬性和方法都需要 return):

<template><div><p>{{ message }}</p><button @click="changeMessage">測試</button></div>
</template><script>
import { ref } from 'vue';
export default {setup() {const message = ref('Hello, Vue 3!');const changeMessage = () => {message.value = 'Message changed!';};return { message, changeMessage };}
};
</script>

例如上面這段代碼,如果沒有 return,則功能無法實現。

如果使用 <script setup> 則不需要 return:

<template><div><p>{{ message }}</p><button @click="changeMessage">測試</button></div>
</template><script setup>
import { ref } from 'vue';
const message = ref('Hello, Vue 3!');
const changeMessage = () => {message.value = 'Message changed!';
};
</script>

這是因為:

編譯器會對 <script setup>塊進行靜態分析和轉換,生成等效的 setup() 函數。編譯后的代碼會提取頂層變量(包括 import 的組件),形成 setup() 的返回對象。

即:<script setup> 是 setup() 的語法糖,在 <script setup> 中定義的變量和方法會自動暴露給模板,無需手動 return。

expose

父組件:

<template><div><Children1 ref="child1" /><Children2 ref="child2" /><button @click="handleClick">按鈕</button></div>
</template><script setup>
import { ref } from 'vue';
import Children1 from './Children1.vue';
import Children2 from './Children2.vue';
const child1 = ref(null);
const child2 = ref(null);
const handleClick = () => {console.log("child1:", child1);console.log("child1 message:", child1.value.message);console.log("child1 obj:", child1.value.obj);console.log("child1 fn:", child1.value.fn);console.log("----------");console.log("child2:", child2);console.log("child2 message:", child2.value.message);console.log("child2 obj:", child2.value.obj);console.log("child2 fn:", child2.value.fn);
}
</script>

setup() 子組件:

<template><div>setup() 子組件</div>
</template><script>
import { reactive, ref } from 'vue';
export default {setup() {const message = ref('Hello, Vue 3!');const obj = reactive({ name: '張三', age: 10 })const fn = () => {console.log("子組件方法");}return { message, obj }}
}
</script>

<script setup> 子組件:

<template><div><script setup> 子組件</div>
</template><script setup>
import { reactive, ref } from 'vue';
const message = ref('Hello, Vue 3!');
const obj = reactive({ name: '張三', age: 10 })
const fn = () => {console.log("子組件方法");
}
</script>

在這里插入圖片描述

可以發現:setup() 會向父組件暴露所有 return 的屬性和方法,而<script setup>就不會,<script setup>語法糖只會對外暴露手動 defineExpose 的內容。

其他

其他的一些使用細節上的區別:

注冊組件:

setup():需要手動注冊

<script>
import Hello from '@/components/HelloWorld'
export default {components: {Hello}
}
</script>

<script setup>:不需要手動注冊

<script setup>
import Hello from '@/components/HelloWorld'
</script>

自定義指令:

setup():

<template><h1 v-onceClick>使用了setup函數</h1>
</template>
<script>export default {directives: {onceClick: {mounted (el, binding, vnode) {console.log(el)}}},
}
</script>

<script setup>:

不需要顯式注冊,但他們必須遵循 vNameOfDirective 這樣的命名規范。

<template><h1 v-once-Directive>使用了script setup</h1>
</template>
<script setup>
const vOnceDirective = {beforeMount: (el) => {console.log(el)}
}
</script>

setup 函數特點

4、setup 函數在 beforeCreate 鉤子函數之前執行

export default {setup() {console.log("setup");},beforeCreate() {console.log("beforeCreate");},created() {console.log("created");},mounted() {console.log("mounted");}
}// setup
// beforeCreate
// created
// mounted

setup(props, context) 詳細說明

執行順序

setup() 是組件中使用組合式 API 的入口點,它在組件實例創建之前執行,在 beforeCreate 和 created 生命周期鉤子之前調用。

注:有說法認為 Vue3 中沒有 beforeCreate 和 created 鉤子函數,這是不準確的。

  • 組合式 API 中確實沒有 beforeCreate 和 created 鉤子函數,他們的功能被 setup 取代;

  • 但是在選項式 API 中他們依然存在。

<template>
</template><script>
export default {beforeCreate() {console.log("beforeCreate");},created() {console.log("created");},setup() {console.log("setup");}
}
</script>

在這里插入圖片描述
執行順序:setup——beforeCreate——created

props 參數

特性:

  1. 是響應式的,包含組件接收的所有 prop,當父組件更新 props 時會自動更新

  2. 不能使用 ES6 解構,否則會失去響應性

  3. 如果需要解構,可以使用 toRefs 或 toRef 保持響應性

特性 1 舉例:

<!--父組件-->
<template><div><Children name="張三" :age="10" /></div>
</template><script>
import Children from './Children.vue';
export default {components: { Children }
}
</script>
<!--子組件-->
<template><div><p>{{ name }}</p><p>{{ age }}</p></div>
</template><script>
export default {props: {name: String,// age: Number 沒有接收 age 屬性},setup(props) {console.log("props:", props);console.log("name:", props.name);console.log("age:", props.age);}
}
</script>

在這里插入圖片描述
組件沒有接收 age 屬性,所以 props 參數中的 age 為 undefined。


特性 2、3 舉例:

<!--父組件-->
<template><div><Children :name="name" :age="age" @change="handleChange" /></div>
</template><script>
import { ref } from 'vue';
import Children from './Children.vue';
export default {components: { Children },setup() {const name = ref('張三');const age = ref(10);const handleChange = () => {age.value++}return { name, age, handleChange };}
}
</script>
<!--子組件-->
<template><div style="margin-left: 50px;"><p>{{ props.name }}</p><p>{{ props.age }}</p><p>{{ age }}</p><button @click="handleClick">按鈕</button></div>
</template><script>
import { toRefs } from 'vue';export default {props: {name: String,age: Number},setup(props, context) {const { age } = propsconst age1 = props.ageconst { age: age2 } = toRefs(props)const handleClick = () => {context.emit('change');setTimeout(() => {console.log("props.age:", props.age);console.log("age:", age);console.log("age1:", age1);console.log("age2.value:", age2.value);console.log("age2:", age2);})};return { props, age, handleClick }}
}
</script>

初始頁面:
在這里插入圖片描述
點擊一次按鈕后:
在這里插入圖片描述
在這里插入圖片描述
可以看到:

const { age } = propsconst age1 = props.age 丟失了響應性,

props.ageconst { age: age2 } = toRefs(props) 保留了響應性。

拓展:

使用 toRefs(props) 創建的 age2 是一個 ref 對象,它包含了多個內部屬性:

屬性類型說明
__v_isRefboolean標識這是一個 ref 對象,值為 true
_defaultValueany默認值
_keystring對應的 props 鍵名,這里是 “age”
_objectobject指向原始的 props 響應式對象
_valueany當前存儲的 age 的值(與 value 相同)
depSet<ReactiveEffect>存儲依賴該 ref 的副作用(effect)
valueany訪問或修改

context 參數

context 是一個普通對象(非響應式),包含組件的三個屬性:

  • attrs

    • 包含所有未在 props 中聲明的 attribute

    • 相當于 Vue 2 中的 this.$attrs

    • 非響應式

    • 示例:

      setup(props, { attrs }) {console.log(attrs.class) // 訪問 class 屬性
      }
      
  • slots

    • 包含所有插槽內容的對象

    • 相當于 Vue 2 中的 this.$slots

    • 非響應式

    • 示例:

      setup(props, { slots }) {const defaultSlot = slots.default() // 獲取默認插槽內容return () => h('div', defaultSlot)
      }
      
  • emit

    • 用于觸發自定義事件的函數

    • 相當于 Vue 2 中的 this.$emit

    • 示例:

      setup(props, { emit }) {const handleClick = () => {emit('change', 'new value')}return {handleClick}
      }
      

當然也可以不使用解構寫法:

setup(props, context) {console.log(context.attrs.class)const defaultSlot = context.slots.default()const handleClick = () => {context.emit('change', 'new value')}
}

返回值

setup() 可以返回一個對象,該對象的屬性/函數將被暴露給模板使用,也可以返回一個渲染函數:

返回對象:

import { ref } from 'vue'setup() {const count = ref(0)return {count,increment: () => count.value++}
}

返回渲染函數:

import { h, ref } from 'vue'setup() {const count = ref(0)return () => h('div', count.value)
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/79213.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/79213.shtml
英文地址,請注明出處:http://en.pswp.cn/web/79213.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

windows服務器部署Gitlab

代碼托管,如果對工具功能要求不高,Gitea也可以滿足需要,只是功能相對比較簡單。 通常GltLab是部署在linux服務器上的,windows版本已經不維護了。不過現在windows10 11已經可以實現部署了,一個是windows本機部署linux虛擬機(windows商店直接安裝或者其他虛擬機平臺都可以)…

剖析 FFmpeg:從基本功能到過濾器,實現音視頻處理的靈活性

目錄 1.解復用2 解碼2.1 音頻解碼2.2 視頻解碼 3 修飾3.1 avio3.2 重采樣 4 過濾器4.1 過濾器基本知識4.2 簡單過濾器4.3 復雜濾鏡圖 1.解復用 解復用就是把容器中的媒體流分離出來&#xff0c;方便我們對媒體流處理。 step1&#xff1a;對媒體文件上下文初始化 AVFormatCont…

kafka學習筆記(四、生產者、消費者(客戶端)深入研究(三)——事務詳解及代碼實例)

1.事務簡介 Kafka事務是Apache Kafka在流處理場景中實現Exactly-Once語義的核心機制。它允許生產者在跨多個分區和主題的操作中&#xff0c;以原子性&#xff08;Atomicity&#xff09;的方式提交或回滾消息&#xff0c;確保數據處理的最終一致性。例如&#xff0c;在流處理中…

Missashe計網復習筆記(隨時更新)

Missashe計算機網絡復習筆記 前言&#xff1a;這篇筆記用于博主對計網這門課所學進行記錄和總結&#xff0c;也包括一些個人的理解。正在更新當中…… 第一章 計算機網絡體系結構 考綱內容 (一) 計算機網絡概述 計算機網絡的概念、組成與功能;計算機網絡的分類; 計算機網絡…

PVP鼠標推薦(deepseek)

下面有不懂的自行百度查找&#x1f44d; ?? 以下是幾款在 雙擊性能&#xff08;DBC&#xff09; 和 拖拽點擊&#xff08;DC&#xff09; 方面表現優秀的游戲鼠標推薦&#xff0c;結合了硬件性能、微動壽命以及玩家口碑&#xff1a; 1. 羅技 G102/G203 Lightsync 特點&#…

ABP vNext + EF Core 實戰性能調優指南

ABP vNext EF Core 實戰性能調優指南 &#x1f680; 目標 本文面向中大型 ABP vNext 項目&#xff0c;圍繞查詢性能、事務隔離、批量操作、緩存與診斷&#xff0c;系統性地給出優化策略和最佳實踐&#xff0c;幫助讀者快速定位性能瓶頸并落地改進。 &#x1f4d1; 目錄 ABP vN…

為啥大模型一般將kv進行緩存,而q不需要

1. 自回歸生成的特點 大模型&#xff08;如 GPT 等&#xff09;在推理時通常采用自回歸生成的方式&#xff1a; 模型逐個生成 token&#xff0c;每次生成一個新 token 時&#xff0c;需要重新計算注意力。在生成第 t 個 token 時&#xff0c;模型需要基于前 t-1 個已生成的 t…

3DGS-slam:splatam公式

配套講解視頻&#xff1a;https://www.bilibili.com/video/BV1ZgfBYdEpg/?spm_id_from333.1387.homepage.video_card.click&vd_sourced4c3e747c32049ddd90dcce17208f4e0 1、多維高斯分布公式: 對于多維&#xff08;多變量&#xff09;高斯分布&#xff0c;概率密度函數的…

從Dockerfile 構建docker鏡像——保姆級教程

從Dockfile開始 dockerfile簡介開始構建1、編輯dockerfile2、構建鏡像3、拉取鏡像4、推送到鏡像倉庫 鏡像的優化1、優化的基本原則2、多階段構建 dockerfile簡介 開始構建 1、編輯dockerfile # 使用官方的 Python 3.8 鏡像作為基礎鏡像 FROM python:3.8-slim# 設置工作目錄 …

開元類雙端互動組件部署實戰全流程教程(第2部分:控制端協議拆解與機器人邏輯調試)

作者&#xff1a;那個寫了個機器人結果自己被踢出房間的開發者 游戲邏輯房間結構參考界面 從這張圖我們能看出&#xff0c;該組件按功能結構細分為多個房間&#xff0c;每個房間底注、準入標準不同&#xff0c;對應的控制模塊也有層級區分。常規來說&#xff0c;一個“互動房間…

[特征工程]機器學習-part2

1 特征工程概念 特征工程:就是對特征進行相關的處理 一般使用pandas來進行數據清洗和數據處理、使用sklearn來進行特征工程 特征工程是將任意數據(如文本或圖像)轉換為可用于機器學習的數字特征,比如:字典特征提取(特征離散化)、文本特征提取、圖像特征提取。 特征工程步驟…

[數據庫之十一] 數據庫索引之聯合索引

執行數據庫查詢時&#xff0c;通常查詢條件是多對個屬性進行判斷和約束&#xff0c;對于這種類型的查詢&#xff0c;如果存在多個索引則使用多個索引&#xff0c;或者使用建立在多屬性搜索碼上的索引&#xff0c;這樣能提高查詢效率。 一、使用多個單碼索引 假設數據表 instruc…

增強學習(Reinforcement Learning)簡介

增強學習&#xff08;Reinforcement Learning&#xff09;簡介 增強學習是機器學習的一種范式&#xff0c;其核心目標是讓智能體&#xff08;Agent&#xff09;通過與環境的交互&#xff0c;基于試錯機制和延遲獎勵反饋&#xff0c;學習如何選擇最優動作以最大化長期累積回報。…

PaddlePaddle 和PyTorch選擇與對比互斥

你遇到的錯誤信息如下&#xff1a; RuntimeError: (PreconditionNotMet) Tensors dimension is out of bound.Tensors dimension must be equal or less than the size of its memory.But received Tensors dimension is 8, memorys size is 0.[Hint: Expected numel() * Size…

vison transformer vit 論文閱讀

An Image is Worth 16x16 Words 20年的論文看成10年的哈斯我了 [2010.11929] 一張圖像勝過 16x16 個單詞&#xff1a;用于大規模圖像識別的轉換器 --- [2010.11929] An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale 為什么transformer好訓練&am…

依賴關系-根據依賴關系求候選碼

關系模式R&#xff08;U, F&#xff09;, U{}&#xff0c;F是R的函數依賴集&#xff0c;可以將屬性分為4類&#xff1a; L: 僅出現在依賴集F左側的屬性 R: 僅出現在依賴集F右側的屬性 LR: 在依賴集F左右側都出現的屬性 NLR: 在依賴集F左右側都未出現的屬性 結論1: 若X是L類…

SAP note 3565626 : Baltimore CyberTrust 根證書即將過期

SAP note 3565626 &#xff1a; Baltimore CyberTrust 根證書即將過期 20250512 2025年5月9日 癥狀 您已收到來? SAP Integration Suite/Cloud Integration 服務的通知郵件&#xff0c; 建議 Baltimore CyberTrust 根證書將于 2025 年 5 ? 12 ? 過期&#xff0c;其中 Balt…

算法精講:字母異位詞分組問題剖析

算法精講:字母異位詞分組問題剖析 一、引言 在算法的學習與實踐中,字符串相關的問題一直是重點和難點。今天我們要深入探討的“字母異位詞分組”問題,不僅考驗對字符串操作的理解,還涉及到數據結構的巧妙運用。通過解決這個問題,我們能進一步提升算法思維和代碼實現能力。…

【每日八股】復習 Redis Day7:應知應會的 33 條 Redis 基礎八股文

應知應會的 33 條 Redis 基礎八股文 今天對 Redis 八股文進行收官總結&#xff0c;共收錄了 33 條基礎八股文。 文章目錄 應知應會的 33 條 Redis 基礎八股文Redis 持久化簡述 Redis 持久化的兩種策略&#xff1f;AOF 的三種持久化策略&#xff1f;AOF 磁盤重寫機制&#xf…

k8s之探針

探針介紹&#xff1a; 編排工具運行時&#xff0c;雖說pod掛掉會在控制器的調度下會重啟&#xff0c;出現pod重啟的時候&#xff0c;但是pod狀態是running,無法真實的反應當時pod健康狀態&#xff0c;我們可以通過Kubernetes的探針監控到pod的實時狀態。 Kubernetes三種探針類…