一、組件內的 data 為什么總是函數形式?
我們試着先做一個計數器案例,把 data 的返回形式修改成一個對象。具體的代碼如下:
<template> <div> <button @click="num++">+</button> {{num}} <button @click="num--">-</button> </div> </template> <script> const retData = { num:0 } export default { data(){ return retData } } </script>
運行結果,看着是正常的,並無異常。但我們都知道,vue 項目中,之所以采用組件形式,就是為了重復多次使用,所以我們多次使用我們的組件試試。
神奇的效果發生了,當我們改變第一個組件的數值時,第二個組件的數據也被修改。修改第二個組件的時候,第一個組件的數據也同步更新。

原因:同一個組件被復用多次,會創建多個實例,如果data是一個對象的話,這些實例用的是同一個構造函數,指針指向的就會是同一個地方,就導致了兩個組件的數據會同時更新。
為了保證組件內的數據各自獨立,不會相互影響,要求每個組件的data必須是函數形式,目的就是把數據放入一個新對象內,這樣就不會出現上述問題了。
說這的主要意義就是告訴我們,在 Vue3.x 中的 data 選項總是為函數形式,返回響應式數據。
Vue2.x VS Vue3.x 實例創建

二、函數式組件的變化
在Vue3.x中,functional:true 組件選項被移除。vue3.x 不推薦使用函數式組件。
有些小伙伴就會驚嘆,媽呀,俺都不知道函數式組件是啥,你說得再簡單我也不懂吶,所以就講講函數式組件干啥的,原來的 functional 屬性放哪?
2.1、在Vue2.x中的函數式組件:
函數式組件也是組件的一種類型,主要用來定義那些沒有響應數據,也不需要任何生命周期鈎子函數,只 props 來接收傳遞來的數據。
類型1:基於模板的函數式組件
<template functional > <div> 函數式組件內容 </div> </template>
類型2:組件注冊
Vue.componenet('fun-comp':{ functional:true, props:{ msg:{ type:String, default:'組件數據' } }, render:(h,context)=>{ return h('div',['組件內容','++',context.props.msg]) } })
類型3:中間件實現 render 方法
// FunComps.js 文件代碼 export default { functional:true, props:{ render:{ type:Function }, params:{} }, render:(h,ctx)=>{ return ctx.props.render(h,ctx.props.params) } } //組件調用 <fun-comp :render="renderHandle" :params="['111','222']" />
renderHandle 這個函數,在外層可以任意控制,這樣不但節省開銷,而且復用性也很高。
2.2、在Vue3.x中的函數式組件:
在 SFC 中不能使用 functional 特性聲明是函數式組件,移除了 functional:true 特性。
// 新建一個 FunComp.vue 文件 <script> import { h } from "vue" function Footer(props,context){ return h(`h${props.level}`,context.attrs , context.slots ) } Footer.props = ['level'] export default Footer </script> //使用函數式組件 <template> <FunComp level="1.0.0" >Vue3.x函數式組件內容</FunComp> </template> <script > import FunComp from '../../components/FunComp.vue' export default{ component:{ FunComp }, } </script>
這下應該清楚到底移除的屬性在哪了!接着看看異步組件有什么改變。。。
三、異步組件的變化
Vue3.x 異步組件要求使用 defineAsyncComponent 方法創建。
由於 Vue3 中函數式組件必須定義為純函數,所以異步組件有如下變化:
- 必須明確地使用 defineAsyncComponent 方法包裹
- component 選項已經被重命名為 loader
- loader 函數不再接收 resolve 和 reject 回調方法 。且必須返回一個 Promise 對象 。
3.1、不帶配置的異步組件對比
在 Vue2.x中異步組件使用:
{ path:'/', component: ()=> import("@/view/home/index") }
在 Vue3.x 中異步組件使用:
import { defineAsyncComponent } from 'vue' { path:"/", component:defineAsyncComponent(()=>{ import("@/view/home/index.vue") }) }
3.2、帶配置的異步組件
const asyncPageWithOptions = defineAsyncComponent({ loader:()=> import("../view/index/index.vue"), delay:200, timeout:3000, errorComponent:ErrorComponent, loadingComponent:LoadingComponent })
loader 選項是以前的 component 選項。
四、自定義組件白名單的變化
Vue3.x中,自定義元素檢測發生在模板編譯時,如果要添加 vue 之外的自定義元素,需要在編譯器選項中設置 isCustomElement 選項。
使用構建工具時,模板都會用 vue-loader 預編譯,在 vite.config.js 中配置它提供的 vueCompilerOption 即可:
import { defineConfig,vueCompilerOption } from 'vite' import vue from '@vitejs/plugin-vue' // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue()], vueCompilerOptions:{ isCustomElement:tag => tag==='pie-chart' } })
此時遇到 pie-chart 元素時,直接跳過,不對其進行編譯。
五、動態組件
vue3.x 中設置動態組件時,is 屬性只能用於 component 標簽上。
使用方法:
<component :is="currentView"></component> //currentView 是一個表達式
讓多個組件使用同一個掛載點,並動態切換組件。
5.1、動態組件的緩存
好多時候多個組件來回切換時,組件的實例都是重新創建的,而我們需要保存它的狀態,此時就需要緩存動態組件。
在Vue2.x中:
<keep-alive> <component :is="currentView"></component> </keep-alive>
在 Vue3.x中:keep-alive 必須使用在 router-view內部
<router-view> <keep-alive> <component :is="currentView"></component> </keep-alive> </router-view>