詳細(xì)教程:在Vue2和Vue3中構(gòu)建相同的組件
過(guò)去,我們看過(guò)很多重大變革即將到來(lái)的文章,但是并沒(méi)有真正深入研究代碼將如何變化。
為了展示這些變化,小芯將在Vue2和Vue3中構(gòu)建一個(gè)簡(jiǎn)單的表單組件。
在本文的最后,將介紹Vue2和Vue3之間的主要編程差異,幫助大家了解,逐漸成為更好的開(kāi)發(fā)人員。
如果想知道如何建立第一個(gè)Vue3應(yīng)用程序,請(qǐng)查看初學(xué)者的Vue3Alpha應(yīng)用程序指南。
我們開(kāi)始吧!
創(chuàng)建模板
對(duì)于大多數(shù)組件,Vue2和Vue3中的代碼即使不完全相同,也是非常相似的。但是,Vue3支持片段,這意味著組件可以有多個(gè)根節(jié)點(diǎn)。
這在呈現(xiàn)列表中的組件以刪除不必要的包裝器div元素時(shí)特別有用。但是,在本例中,表單組件的兩個(gè)版本都將只保留一個(gè)根節(jié)點(diǎn)。
- <template>
- <div>
- <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>
唯一真正的區(qū)別是如何訪(fǎng)問(wèn)數(shù)據(jù)。在Vue3中,響應(yīng)式數(shù)據(jù)都封裝在一個(gè)響應(yīng)狀態(tài)變量中,所以需要訪(fǎng)問(wèn)這個(gè)狀態(tài)變量來(lái)獲得值。
- <template>
- <div>
- <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è)置數(shù)據(jù)
這就是主要的區(qū)別——Vue2選項(xiàng)API對(duì)戰(zhàn)Vue3組合API。
選項(xiàng) API將代碼分成不同的屬性:數(shù)據(jù)、計(jì)算屬性、方法等等。同時(shí),組合API允許根據(jù)函數(shù)而不是屬性類(lèi)型對(duì)代碼進(jìn)行分組。
對(duì)于表單組件,只有兩個(gè)數(shù)據(jù)屬性:一個(gè)username和一個(gè)password。
Vue2代碼看起來(lái)是這樣的——只需在數(shù)據(jù)屬性中放入兩個(gè)值。
- exportdefault {
- props: {
- title: String
- },
- data () {
- return {
- username: '',
- password: ''
- }
- }
- }
在Vue3.0中,必須投入更多精力來(lái)使用一個(gè)新的setup()方法,所有的組件初始化都應(yīng)該在這個(gè)方法中進(jìn)行。
另外,為了讓開(kāi)發(fā)人員更好地控制響應(yīng),可以直接訪(fǎng)問(wèn)Vue的響應(yīng)API。
創(chuàng)建響應(yīng)式數(shù)據(jù)需要三個(gè)步驟:
- 從vue中導(dǎo)入reactive
- 使用響應(yīng)式方法聲明數(shù)據(jù)
- 使用setup方法返回響應(yīng)式數(shù)據(jù)以便模板可以訪(fǎng)問(wèn)
代碼看起來(lái)有點(diǎn)像這樣。
- import {reactive } from 'vue'
- export default {
- props: {
- title: String
- },
- setup () {
- const state = reactive({
- username: '',
- password: ''
- })
- return { state }
- }
- }
接著在模板中,可以使用諸如state.username和state.password來(lái)訪(fǎng)問(wèn)
在Vue2和Vue3中創(chuàng)建方法
Vue2選項(xiàng)API有一個(gè)單獨(dú)的方法部分。這部分可以定義所有的方法,并以任何想要的方式進(jìn)行組織。
- exportdefault {
- props: {
- title: String
- },
- data () {
- return {
- username: '',
- password: ''
- }
- },
- methods: {
- login () {
- // login method
- }
- }
- }
Vue3組合API中的setup方法也可以處理。它的工作原理與聲明數(shù)據(jù)有些類(lèi)似——必須先聲明方法,然后返回,以便組件的其他部分可以訪(fǎng)問(wèn)它。
- exportdefault {
- props: {
- title: String
- },
- setup () {
- const state = reactive({
- username: '',
- password: ''
- })
- const login = () => {
- // login method
- }
- return {
- login,
- state
- }
- }
- }
生命周期掛鉤
Vue2可以直接從組件選項(xiàng)中訪(fǎng)問(wèn)生命周期掛鉤。以下示例代碼將等待掛載的事件。
- exportdefault {
- props: {
- title: String
- },
- data () {
- return {
- username: '',
- password: ''
- }
- },
- mounted () {
- console.log('component mounted')
- },
- methods: {
- login () {
- // login method
- }
- }
- }
現(xiàn)在有了Vue3 組合API,幾乎所有東西都在setup()方法中,包括掛載的生命周期掛鉤。
然而,生命周期掛鉤在默認(rèn)情況下并不包括在內(nèi),因此必須導(dǎo)入在Vue3中被稱(chēng)為onmount的方法。這看起來(lái)與前面導(dǎo)入reactive是一樣的。
然后,在setup方法中,通過(guò)傳遞函數(shù)來(lái)使用onmount方法。
- import {reactive, onMounted } from 'vue'
- export default {
- props: {
- title: String
- },
- setup () {
- // ..
- onMounted(() => {
- console.log('component mounted')
- })
- // ...
- }
- }
computed屬性
添加一個(gè)computed屬性,將用戶(hù)名轉(zhuǎn)換為小寫(xiě)字母。
為了在Vue2中實(shí)現(xiàn)這一點(diǎn),向選項(xiàng)對(duì)象添加一個(gè)computed字段。從這里,可以像這樣定義屬性…
- exportdefault {
- // ..
- computed: {
- lowerCaseUsername () {
- return this.username.toLowerCase()
- }
- }
- }
Vue3的設(shè)計(jì)允許開(kāi)發(fā)人員導(dǎo)入他們使用的東西,并且項(xiàng)目中沒(méi)有不必要的包。本質(zhì)上,他們不希望開(kāi)發(fā)人員包含從未使用過(guò)的東西,這而這在Vue2中已經(jīng)成為一個(gè)日益嚴(yán)重的問(wèn)題。
因此,要在Vue3中使用computed屬性,首先必須將computed導(dǎo)入到組件中。
然后,與之前創(chuàng)建響應(yīng)式數(shù)據(jù)的方式類(lèi)似,將一段響應(yīng)式數(shù)據(jù)變成一個(gè)計(jì)算值,如下所示:
- import {reactive, onMounted, computed } from 'vue'
- export default {
- props: {
- title: String
- },
- setup () {
- const state = reactive({
- username: '',
- password: '',
- lowerCaseUsername: computed(()=> state.username.toLowerCase())
- })
- // ...
- }
訪(fǎng)問(wèn)Props
訪(fǎng)問(wèn)道具帶來(lái)了Vue2和Vue3之間的一個(gè)重要區(qū)別——this意味著完全不同的東西。
在Vue2中,this幾乎總是指向組件,而不是特定的屬性。雖然這使事情表面上很容易,但它使類(lèi)型支持成為一種痛苦。
然而,可以很容易地訪(fǎng)問(wèn)props——只需添加一個(gè)小例子,比如在掛載掛鉤期間打印出title prop:
- mounted() {
- console.log('title: ' + this.title)
- }
但是在Vue3中不再使用它來(lái)訪(fǎng)問(wèn)props、觸發(fā)事件和獲取屬性。相反,setup()方法有兩個(gè)參數(shù):
- props - 對(duì)組件props的不可變?cè)L問(wèn)
- context - Vue3公開(kāi)的上下文選擇屬性(emit、slot、attrs)
使用props參數(shù),上面的代碼將如下所示。
- mounted() {
- console.log('title: ' + this.title)
- }
觸發(fā)事件
類(lèi)似地,在Vue2中觸發(fā)事件非常簡(jiǎn)單,但是Vue3對(duì)如何訪(fǎng)問(wèn)屬性/方法提供了更多控制。
本例希望在按下“Submit”按鈕時(shí)觸發(fā)父組件的一個(gè)登錄事件。
Vue2代碼只需要調(diào)用this.$emit并傳入有效負(fù)載對(duì)象。
- login (){
- this.$emit('login', {
- username: this.username,
- password: this.password
- })
- }
然而在Vue3中,this現(xiàn)在含義不同了,得做出一點(diǎn)改變。
幸運(yùn)的是,文本對(duì)象公開(kāi)了emit,提供了與this.$emit相同的東西
所要做的就是添加context作為setup方法的第二個(gè)參數(shù)。將對(duì)c文本對(duì)象進(jìn)行析構(gòu),使代碼更簡(jiǎn)潔。
然后,只需調(diào)用emit來(lái)觸發(fā)事件。與前面一樣,emit方法有兩個(gè)參數(shù):
- 事件名稱(chēng)
- 與事件一起傳遞的有效負(fù)載對(duì)象
- setup(props, { emit }) {
- // ...
- const login = () => {
- emit('login', {
- username: state.username,
- password: state.password
- })
- }
- // ...
- }
Vue2和Vue3的最后代碼
太棒了!已經(jīng)到了最后階段。
Vue2和Vue3中的所有概念都是相同的,但是訪(fǎng)問(wèn)屬性的一些方式發(fā)生了一點(diǎn)變化。
總的來(lái)說(shuō),筆者認(rèn)為Vue3將幫助開(kāi)發(fā)人員編寫(xiě)更有組織的代碼——特別是在大型代碼庫(kù)中。這主要是因?yàn)榻M合API允許根據(jù)特定的特性將代碼分組,甚至可以將功能提取到它們自己的文件中,并根據(jù)需要將它們導(dǎo)入到組件中。
下面是Vue2中的表單組件代碼。
- <template>
- <div>
- <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>
- <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>
希望本教程能夠幫助了解Vue代碼在Vue3中的一些不同之處。