所以這里我使用Vue2和Vue3開發(fā)一個(gè)簡單的表格組件來展示一下Vue2和Vue3開發(fā)組件的區(qū)別
創(chuàng)建一個(gè) template
組件來說,大多代碼在Vue2和Vue3都非常相似。Vue3支持碎片(Fragments),就是說在組件可以擁有多個(gè)根節(jié)點(diǎn)
這種新特性可以減少很多組件之間的div包裹元素。在開發(fā)vue的時(shí)候书妻,我們會發(fā)現(xiàn)每一個(gè)組件都會有個(gè)div元素包裹著。就會出現(xiàn)很多層多余的div元素。碎片(Fragments)解決了這個(gè)問題凝赛。
<template>
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
</template>
Vue2 表格template
<template>
<div class='form-element'>
<h2> {{ title }} </h2>
<input type='text' v-model='username' placeholder='Username' />
<input type='password' v-model='password' placeholder='Password' />
<button @click='login'>
Submit
</button>
<p>
Values: {{ username + ' ' + password }}
</p>
</div>
</template>
在Vue3的唯一真正的不同在于數(shù)據(jù)獲取。Vue3中的反應(yīng)數(shù)據(jù)(Reactive Data
)是包含在一個(gè)反應(yīng)狀態(tài)(Reactive State
)變量中坛缕∧沽裕— 所以我們需要訪問這個(gè)反應(yīng)狀態(tài)來獲取數(shù)據(jù)值。
<template>
<div class='form-element'>
<h2> {{ state.title }} </h2>
<input
type='text'
v-model='state.username'
placeholder='Username'
/>
<input
type='password'
v-model='state.password'
placeholder='Password'
/>
<button @click='login'>
Submit
</button>
<p>
Values: {{ state.username + ' ' + state.password }}
</p>
</div>
</template>
建立數(shù)據(jù) data
這里就是Vue2與Vue3 最大的區(qū)別 — Vue2使用選項(xiàng)類型API(Options API)
對比Vue3合成型API(Composition API)
舊的選項(xiàng)型API在代碼里分割了不同的屬性(properties):data赚楚,computed屬性毙沾,methods,等等宠页。新的合成型API能讓我們用方法(function)來分割左胞,相比于舊的API使用屬性來分組寇仓,這樣代碼會更加簡便和整潔。
現(xiàn)在我們來對比一下Vue2寫法和Vue3寫法在代碼里面的區(qū)別烤宙。
Vue2 - 這里把兩個(gè)數(shù)據(jù)放入data屬性中
export default {
props: {
title: String
},
data () {
return {
username: '',
password: ''
}
}
}
在Vue3.0遍烦,我們就需要使用一個(gè)新的setup()
方法,此方法在組件初始化構(gòu)造的時(shí)候觸發(fā)门烂。
為了可以讓開發(fā)者對反應(yīng)型數(shù)據(jù)有更多的控制乳愉,我們可以直接使用到 Vue3 的反應(yīng)API(reactivity API)
。
使用以下三步來建立反應(yīng)性數(shù)據(jù):
1.從vue引入reactive
2.使用reactive()
方法來聲名我們的數(shù)據(jù)為反應(yīng)性數(shù)據(jù)
3.使用setup()
方法來返回我們的反應(yīng)性數(shù)據(jù)屯远,從而我們的template可以獲取這些反應(yīng)性數(shù)據(jù)
上一波代碼蔓姚,讓大家更容易理解是怎么實(shí)現(xiàn)的。
import { reactive } from 'vue'
export default {
props: {
title: String
},
setup () {
const state = reactive({
username: '',
password: ''
})
return { state }
}
}
這里構(gòu)造的反應(yīng)性數(shù)據(jù)就可以被template使用慨丐,可以通過state.username和state.password獲得數(shù)據(jù)的值坡脐。
Vue2 對比 Vue3的 methods 編寫
Vue2 的選項(xiàng)型API是把methods分割到獨(dú)立的屬性區(qū)域的。我們可以直接在這個(gè)屬性里面添加方法來處理各種前端邏輯
export default {
props: {
title: String
},
data () {
return {
username: '',
password: ''
}
},
methods: {
login () {
// 登陸方法
}
}
}
**Vue3 **的合成型API里面的setup()
方法也是可以用來操控methods的房揭。創(chuàng)建聲名方法其實(shí)和聲名數(shù)據(jù)狀態(tài)是一樣的备闲。— 我們需要先聲名一個(gè)方法然后在setup()
方法中返回(return)
捅暴, 這樣我們的組件內(nèi)就可以調(diào)用這個(gè)方法了恬砂。
export default {
props: {
title: String
},
setup () {
const state = reactive({
username: '',
password: ''
})
const login = () => {
// 登陸方法
}
return {
login,
state
}
}
}
生命周期鉤子 — Lifecyle Hooks
在 Vue2,我們可以直接在組件屬性中調(diào)用Vue的生命周期的鉤子蓬痒。以下使用一個(gè)組件已掛載(mounted)
生命周期觸發(fā)鉤子泻骤。
export default {
props: {
title: String
},
data () {
return {
username: '',
password: ''
}
},
mounted () {
console.log('組件已掛載')
},
methods: {
login () {
// login method
}
}
}
現(xiàn)在 Vue3 的合成型API里面的setup()
方法可以包含了基本所有東西。生命周期的鉤子就是其中之一梧奢!
但是在 Vue3 生周期鉤子不是全局可調(diào)用的了狱掂,需要另外從vue中引入。和剛剛引入reactive
一樣亲轨,生命周期的掛載鉤子叫onMounted
引入后我們就可以在setup()
方法里面使用onMounted
掛載的鉤子了趋惨。
import { reactive, onMounted } from 'vue'
export default {
props: {
title: String
},
setup () {
// ..
onMounted(() => {
console.log('組件已掛載')
})
// ...
}
}
計(jì)算屬性 - Computed Properties
我們一起試試添加一個(gè)計(jì)算屬性來轉(zhuǎn)換username成小寫字母。
在 Vue2 中實(shí)現(xiàn)惦蚊,我們只需要在組件內(nèi)的選項(xiàng)屬性中添加即可
export default {
// ..
computed: {
lowerCaseUsername () {
return this.username.toLowerCase()
}
}
}
Vue3 的設(shè)計(jì)模式給予開發(fā)者們按需引入需要使用的依賴包器虾。這樣一來就不需要多余的引用導(dǎo)致性能或者打包后太大的問題。Vue2就是有這個(gè)一直存在的問題
所以在 Vue3 使用計(jì)算屬性蹦锋,我們先需要在組件內(nèi)引入computed
使用方式就和反應(yīng)性數(shù)據(jù)(reactive data
)一樣曾撤,在state中加入一個(gè)計(jì)算屬性:
import { reactive, onMounted, computed } from 'vue'
export default {
props: {
title: String
},
setup () {
const state = reactive({
username: '',
password: '',
lowerCaseUsername: computed(() => state.username.toLowerCase())
})
// ...
}
接收 Props
接收組件props參數(shù)傳遞這一塊為我們帶來了Vue2和Vue3之間最大的區(qū)別≡畏啵—this在vue3中與vue2代表著完全不一樣的東西挤悉。
在 Vue2,this代表的是當(dāng)前組件,不是某一個(gè)特定的屬性装悲。所以我們可以直接使用this訪問prop屬性值昏鹃。就比如下面的例子在掛載完成后打印處當(dāng)前傳入組件的參數(shù)title。
mounted () {
console.log('title: ' + this.title)
}
但是在 Vue3 中诀诊,this無法直接拿到props屬性洞渤,emit events(觸發(fā)事件)和組件內(nèi)的其他屬性。不過全新的setup()
方法可以接收兩個(gè)參數(shù)
- props - 不可變的組件參數(shù)
- context - Vue3 暴露出來的屬性(emit属瓣,slots载迄,attrs)
所以在 Vue3 接收與使用props就會變成這樣:
setup (props) {
// ...
onMounted(() => {
console.log('title: ' + props.title)
})
// ...
}
事件 - Emitting Events
在** Vue2** 中自定義事件是非常直接的,但是在 Vue3 的話抡蛙,我們會有更多的控制的自由度护昧。
舉例,現(xiàn)在我們想在點(diǎn)擊提交按鈕時(shí)觸發(fā)一個(gè)login
的事件粗截。
在 Vue2 中我們會調(diào)用到this.$emit
然后傳入事件名和參數(shù)對象
login () {
this.$emit('login', {
username: this.username,
password: this.password
})
}
但是在 Vue3中惋耙,我們剛剛說過this
已經(jīng)不是和vue2代表著這個(gè)組件了,所以我們需要不一樣的自定義事件的方式
在setup()
中的第二個(gè)參數(shù)content對象中就有emit
熊昌,這個(gè)是和this.$emit
是一樣的绽榛。那么我們只要在setup()
接收第二個(gè)參數(shù)中使用分解對象法取出emit
就可以在setup
方法中隨意使用了。
setup (props, { emit }) {
// ...
const login = () => {
emit('login', {
username: state.username,
password: state.password
})
}
// ...
}
我覺得 Vue3 給我們前端開發(fā)者帶來了全新的開發(fā)體驗(yàn)婿屹,更好的使用彈性灭美,可控度也得到了大大的提升。如果你是一個(gè)學(xué)過或者接觸過 React 然后現(xiàn)在想使用Vue的話昂利,應(yīng)該特別興奮届腐,因?yàn)楹芏嗍褂梅绞蕉己蚏eact非常相近了 !
全新的合成式API(Composition API)
可以提升代碼的解耦程度 —— 特別是大型的前端應(yīng)用页眯,效果會更加明顯梯捕。還有就是按需引用的有了更細(xì)微的可控性厢呵,讓項(xiàng)目的性能和打包大小有更好的控制
Vue2
<template>
<div class='form-element'>
<h2> {{ title }} </h2>
<input type='text' v-model='username' placeholder='Username' />
<input type='password' v-model='password' placeholder='Password' />
<button @click='login'>
Submit
</button>
<p>
Values: {{ username + ' ' + password }}
</p>
</div>
</template>
<script>
export default {
props: {
title: String
},
data () {
return {
username: '',
password: ''
}
},
mounted () {
console.log('title: ' + this.title)
},
computed: {
lowerCaseUsername () {
return this.username.toLowerCase()
}
},
methods: {
login () {
this.$emit('login', {
username: this.username,
password: this.password
})
}
}
}
</script>
Vue3
<template>
<div class='form-element'>
<h2> {{ state.title }} </h2>
<input type='text' v-model='state.username' placeholder='Username' />
<input type='password' v-model='state.password' placeholder='Password' />
<button @click='login'>
Submit
</button>
<p>
Values: {{ state.username + ' ' + state.password }}
</p>
</div>
</template>
<script>
import { reactive, onMounted, computed } from 'vue'
export default {
props: {
title: String
},
setup (props, { emit }) {
const state = reactive({
username: '',
password: '',
lowerCaseUsername: computed(() => state.username.toLowerCase())
})
onMounted(() => {
console.log('title: ' + props.title)
})
const login = () => {
emit('login', {
username: state.username,
password: state.password
})
}
return {
login,
state
}
}
}
</script>