构建相同的组件Vue3 vs Vue2

发布于3/4/2020 来自:「前端知否」微信公众号

随着Vue3即将发布,许多人都在想”Vue2与Vue3有何不同?”

尽管我们之前已经写过有关重大变化的文章,但实际上并没有真正深入地了解我们的代码将如何变化。因此,为了显示这些更改,我们将在Vue2和Vue3中构建一个简单的表单组件。

在本文中,您将了解Vue2和Vue3之间的主要编程差异,并逐步成为一名更好的开发人员。

如果您想知道如何构建第一个Vue3应用程序,请查阅Vue3 Composition API教程及示例

让我们开始吧!

创建模板

对于大多数组件,Vue2和Vue3中的代码将非常相似。但是,Vue3支持Fragments,这意味着组件可以具有多个根节点。

在渲染列表中的组件时,可以减少不必要的包装div元素,这特别有用。在这种情况下,我们将为两个版本的Form组件保留一个根节点。

<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中,我们的响应式数据都包装在一个响应​​式状态变量中,因此我们需要访问该状态变量以获取我们的值。

<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>

设置数据

这是一个重大的区别:Vue2 Options API与Vue3 Composition API。

Options API将我们的代码分为不同的属性:数据,计算属性,方法等。但是,Composition API允许我们按功能而不是属性的类型对代码进行分组

假设对于表单组件,我们只有两个数据属性:用户名和密码。

Vue2代码:

export default {
props: {
title: String
},

data() {
return {
username: '',
password: ''
}
}
}

在Vue 3.0中,我们必须通过使用新的setup()方法进行更多的工作,在该方法中应进行所有组件初始化

另外,为了使开发人员能够更好地控制数据响应,我们可以直接访问Vue的 reactive API。

创建响应性数据包含三个步骤:

  • 从Vue导入reactive
  • 使用reactive方法声明我们的数据
  • 让我们的设置方法返回响应性数据,以便我们的模板可以访问它

代码如下:

import { reactive } from 'vue'

export default {
props: {
title: String
},

setup() {
const state = reactive({
username: '',
password: ''
})

return { state }
}
}

然后,在模板中,我们可以使用state.usernamestate.password访问它们

在Vue2与Vue3中创建方法

Vue2 Options API有一个单独的方法部分。在其中,我们可以定义所有方法并以所需的任何方式组织它们。

export default {
props: {
title: String
},

data() {
return {
username: '',
password: ''
}
},

methods: {
login() {
// login method
}
}
}

Vue3 Composition API中的setup方法也可以处理方法。它的工作方式与声明数据有些类似。我们必须先声明我们的方法,然后返回它,以便组件的其他部分可以访问它。

export default {
props: {
title: String
},

setup() {
const state = reactive({
username: '',
password: ''
})

const login = () => {
// login method
}

return {
login,
state
}
}
}

生命周期钩子

在Vue2中,我们可以直接在组件选项中设置生命周期钩子。

export default {
props: {
title: String
},

data() {
return {
username: '',
password: ''
}
},

mounted() {
console.log('component mounted')
},

methods: {
login() {
// login method
}
}
}

现在有了Vue3 Composition API,几乎所有内容都在setup()方法内部。这包括mounted生命周期钩子。

但是,默认情况下不包括生命周期钩子,我们必须导入onMounted方法,作为Vue3中调用的方法。这与之前导入reactive相同。

然后,在setup方法中,可以通过给onMounted方法传递函数参数来使用它。

import { reactive, onMounted } from 'vue'

export default {
props: {
title: String
},

setup() {
// ..

onMounted(() => {
console.log('component mounted')
})

// ...
}
}

计算属性

让我们添加一个计算属性,将我们的用户名转换为小写字母。

为了在Vue2中完成此操作,我们需要添加computed字段到我们的options对象中。我们可以像这样定义我们的属性:

export default {
// ..
computed: {
lowerCaseUsername () {
return this.username.toLowerCase()
}
}
}

Vue3的设计允许开发人员导入他们使用的内容。本质上,他们不希望开发人员必须包含从未使用过的东西,这在Vue2中正成为一个日益严重的问题。

因此,要在Vue3中使用计算属性,我们首先必须将computed导入到组件中。

然后,类似于我们之前创建响应式数据的方式,我们可以使一条响应式数据成为这样的计算值:

import { reactive, onMounted, computed } from 'vue'

export default {
props: {
title: String
},

setup() {
const state = reactive({
username: '',
password: '',
lowerCaseUsername: computed(() => state.username.toLowerCase())
})

// ...
}
}

访问Props

访问Props带来了Vue2和Vue3之间的重要区别。

在Vue2中,我们可以轻松访问P。让我们添加一个简单的示例,例如在加载完成钩子上打印出标题prop:

mounted() {
console.log('title: ' + this.title)
}

但是在Vue3中,我们不再使用this来访问Props,发出事件和获取属性。相反,setup()方法采用两个参数:

  • props -对组件的props的不变访问
  • context -Vue3公开的选定属性(emit,slot,attrs

使用props参数,上面的代码将如下所示:

setup(props) {
// ...

onMounted(() => {
console.log('title: ' + props.title)
})

// ...
}

发出事件

同样,在Vue2中发出事件非常简单,但是Vue3使您可以更好地控制如何访问属性/方法。

假设在我们的情况下,我们想在按下“提交”按钮时向父组件发出一个登录事件。

Vue2代码只需要调用this.$ emit并传入我们的payload对象即可。

login() {
this.$emit('login', {
username: this.username,
password: this.password
})
}

但是,在Vue3中,我们现在知道这不再意味着同一件事,因此我们必须做不同的事情。

幸运的是,上下文对象公开了emit方法。

我们要做的就是将context添加为setup()方法的第二个参数。我们将解构上下文对象,以使我们的代码更简洁。

然后,我们只需要调用emit发送事件即可。像以前一样,emit方法采用两个参数:

  • 事件名称
  • 与事件一起传递的payload对象
setup(props, { emit }) {
// ...

const login = () => {
emit('login', {
username: state.username,
password: state.password
})
}

// ...
}

最终的Vue2与Vue3代码

我们已经完成了。如您所见,Vue2和Vue3中的所有概念都是相同的,但是我们访问属性的某些方式已经有所改变。

总的来说,我认为Vue3将帮助开发人员编写更有组织的代码,尤其是在大型项目中。这主要是因为Composition API允许我们按照特定功能将代码组织在一起,甚至可以将功能提取到单独文件中,然后根据需要将其导入组件中。

在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>

最后

希望本教程有助于重点介绍Vue代码在Vue3中不同表现形式。编码愉快!