vue.js composition api
表单是前端开发中最棘手的部分之一,您可能会在其中发现很多混乱的代码。
Vue.js 2之类的基于组件的框架在提高前端代码的可伸缩性方面做了很多工作,但是形式问题仍然存在。
在本教程中,我将向您展示新的Vue.js Composition API(属于Vue 3)将如何使您的表单代码更简洁,更具可伸缩性。
目录:
- 为什么表单代码经常很烂
- Vue Composition API
- 将Composition API添加到Vue 2项目
- 创建表单输入组件
- 设置表格
- 设置输入
- 输入验证器功能
- 添加验证器
- 显示错误
- 重用代码
为什么表单代码经常很烂
Vue等基于组件的框架的关键设计模式是组件组合。这种模式告诉我们将应用程序的功能抽象为隔离的,单一目的的组件,这些组件与道具和事件通信状态。
但是,在这种模式下不能很好地抽象表单,因为表单的功能和状态并不明确地属于任何一个组件,因此将其分离通常会导致解决的许多问题。
表单代码经常吸引Vue应用程序的另一个重要原因是,直到Vue 2为止,Vue都不具备在组件之间重用代码的强大方法。这在表单中很重要,因为表单输入通常明显不同,但在功能上有许多相似之处。
Vue 2提供的代码重用的主要方法是mixin,我认为这是公然的反模式。
Vue Composition API
Composition API是使用Vue.js定义组件的新方法,它将成为Vue 3的核心功能。它现在也可以在Vue 2中作为插件使用。
这个新的API旨在解决我提到的一些问题(不仅在形式上,而且在前端应用程序体系结构的任何方面)。
Composition API不能替代经典的Vue API,而是可以在需要时使用的东西。正如您将在本文中看到的那样,创建干净且可伸缩的表单代码是一个完美的用例。
将Composition API添加到Vue 2项目
由于我是在Vue 3发布之前编写本教程的,所以让我们将Composition API作为插件添加到Vue 2项目中。
我们将从创建一个新的Vue CLI项目开始(只是我们需要的仅是裸露的功能-无需路由器,Vuex等),然后使用NPM安装Composition API插件。
$ vue create composition-api-form
$ cd composition-api-form
$ npm i -S @vue/composition-api
接下来,让我们在main.js中将插件添加到我们的Vue实例中。
src / main.js
import Vue from "vue";
import App from "./App.vue";
import VueCompositionApi from "@vue/composition-api";
Vue.use(VueCompositionApi);
new Vue({
render: h => h(App)
}).$mount('#app');
创建表单输入组件
为了使这个例子简单,我们将创建一个仅包含两个输入的表单-名称和电子邮件。让我们将它们创建为自己的独立组件。
$ touch src/components/InputName.vue
$ touch src/components/InputEmail.vue
现在,让我们以典型的方式设置InputName组件模板,其中包括带有v-model
指令的HTML输入元素,该指令与该组件创建双向绑定。
src / components / InputName.vue
<template>
<div>
<label>
Name
<input type="text" v-model="input" name="name" />
</label>
</div>
</template>
<script>
export default {
name: 'InputName'
}
</script>
设置表格
让我们暂时保留输入并设置表单。您可以将其创建为单独的组件以使其可重用,但是为了简化本教程,我将在App组件模板中声明它。
我们将添加novalidate
属性,以使浏览器知道我们将提供自定义验证。我们还将侦听submit
表单的事件,阻止其自动提交,并使用onSubmit
我们稍后将声明的方法来处理该事件。
然后,我们将添加InputName
和InputEmail
组件和绑定本地状态值name
,并email
分别给他们。
src / App.vue
<template>
<div id="app">
<form novalidate @submit.prevent="onSubmit">
<InputName v-model="name" />
<InputEmail v-model="email" />
<button type="submit">Submit</button>
</form>
</div>
</template>
<script>
import InputName from "@/components/InputName";
import InputEmail from "@/components/InputEmail";
export default {
name: 'App',
components: {
InputName,
InputEmail
}
}
</script>
现在让我们使用Composition API定义表单功能。我们将setup
在组件定义中添加一个方法,在其中定义两个状态变量,name
并email
使用ref
Composition API 的方法。该方法将需要从Composition API包中导入。
然后,我们将声明一个onSubmit
函数来处理表单提交。我不会指定任何功能,因为它与本教程无关。
最后,我们需要返回两个状态变量和从setup
函数创建的方法,以便组件模板可以访问它们。
src / App.vue
...
import { ref } from "@vue/composition-api";
export default {
name: "App",
setup () {
const name = ref("");
const email = ref("");
function onSubmit() {
// submit to backend or whatever you like
console.log(name.value, email.value);
}
return {
name,
email,
onSubmit
}
},
...
}
设置输入
接下来,我们将定义InputName
组件的功能。
由于父窗体正在v-model
与此组件一起使用,因此声明一个prop value
将是双向绑定的一半很重要。
让我们创建一个setup
函数。道具与上下文对象一样传递到此方法中,使我们可以访问组件实例方法。我们可以解构第二个参数并获得emit
方法。我们将需要它来完成v-model
双向绑定的另一半,即以反应方式发出输入的新值。
在此之前,让我们声明一个状态变量input
,该变量将绑定到我们在模板中声明的输入HTML元素。
该变量的值将是我们将从待定义的组合函数中返回的值useInputValidator
。此函数将处理所有常见的验证逻辑。
我们将value
prop 传递给此方法,第二个参数将是一个回调函数,该函数返回经过验证的输入值。让我们使用此回调将此输入作为事件发出并履行v-model
合同。
src / components / InputName.vue
import useInputValidator from "@/features/useInputValidator";
export default {
name: "InputName",
props: {
value: String
},
setup (props, { emit }) {
const { input } = useInputValidator(
props.value,
value => emit("input", value)
);
return {
input
}
}
}
输入验证器功能
现在创建useInputValidator
合成函数。为此,我们将首先创建一个features
文件夹,然后为其创建模块文件。
$ mkdir src/features
$ touch src/features/useInputValidator.js
在模块文件中,我们将导出一个函数。我们刚刚看到它将需要两个参数- value
从父窗体接收的prop(我们将其调用startVal
)和一个回调方法(将被调用)onValidate
。
请记住,此函数需要返回一个input
状态变量,因此让我们继续进行声明,并分配一个ref
使用prop提供的值初始化的。
在input
从函数返回值之前,让我们看一下它的值并onValidate
使用输入作为参数调用回调。
src /功能/useInputValidator.js
import { ref, watch } from "@vue/composition-api";
export default function (startVal, onValidate) {
let input = ref(startVal);
watch(input, value => {
onValidate(value);
});
return {
input
}
}
添加验证器
下一步是添加验证器功能。对于InputName
组件,我们只有一个验证规则-一个minLength,确保输入的字符数为三个或更多。尚未创建的InputEmail
组件将需要电子邮件验证。
现在,我们将validators.js
在src
文件夹中的JavaScript实用程序模块中创建这些验证器。在实际的项目中,您可能会改用第三方库。
我不会详细介绍验证器功能,但是需要注意两点:
- 这些是返回函数的函数。这种架构允许我们通过传递成为闭包一部分的参数来自定义验证。
- 每个验证器返回的函数总是返回一个字符串(错误消息),或者
null
在没有错误的情况下返回。
src / validators.js
const minLength = min => {
return input => input.length < min
? `Value must be at least ${min} characters`
: null;
};
const isEmail = () => {
const re = /\S+@\S+\.\S+/;
return input => re.test(input)
? null
: "Must be a valid email address";
}
export { minLength, isEmail };
回到composition函数中,我们希望使用组件定义所需的验证,因此让我们首先在功能配置文件中添加另一个参数,该参数validators
应该是验证函数的数组。
在input
观察器内部,我们现在将处理验证功能。让我们使用map
验证器数组的方法,将输入的当前值传递给每个验证器方法。
返回值将被捕获到新的状态变量中,errors
我们还将返回到使用方组件。
src /功能/useInputValidator.js
export default function (startVal, validators, onValidate) {
const input = ref(startVal);
const errors = ref([]);
watch(input, value => {
errors.value = validators.map(validator => validator(value));
onValidate(value);
});
return {
input,
errors
}
}
最后返回InputName
组件,我们现在将为方法提供所需的三个参数useInputValidator
。记住,第二个参数现在是一个验证器数组,因此让我们就地声明一个数组并传递minLength
,我们将通过从验证器文件导入来获取该数组。
minLength
是工厂函数,因此我们调用传递要指定的最小长度的函数。
现在,我们还从合成函数返回了两个对象- input
和errors
。setup
为了从组件的渲染上下文中获取可用性,这两种方法都将从方法中返回。
src / components / InputName.vue
...
import { minLength } from "@/validators";
export default {
...
setup (props, { emit }) {
const { input, errors } = useInputValidator(
props.value,
[ minLength(3) ],
value => emit("input", value)
);
return {
input,
errors
}
}
}
那是我们将添加到该组件的最后一个功能。不过,在继续之前,请花点时间了解一下此代码比我们使用mixin所看到的代码更具可读性,这一点很重要。
一方面,我们清楚地看到了状态变量的声明和修改位置,而无需浏览到单独的mixin模块文件。另一方面,我们不必担心局部变量和组合函数之间的名称冲突。
显示错误
转到InputName
组件的模板,我们现在可以显示一系列潜在的错误。让我们将其委托给一个名为的演示组件ErrorDisplay
。
src / components / InputName.vue
<template>
<div>
<label>
Name
<input type="text" v-model="input" name="name" />
</label>
<ErrorDisplay :errors="errors" />
</div>
</template>
<script>
...
import ErrorDisplay from "@/components/ErrorDisplay";
export default: {
...
components: {
ErrorDisplay
}
}
</script>
的功能ErrorDisplay
太琐碎,无法在此处显示。
重用代码
这就是我们基于Composition API的表单的基本功能。本教程的目的是创建干净且可伸缩的表单代码,我想向您证明,我们已经完成了第二个自定义输入的定义,从而做到了这一点InputEmail
。
如果达到了本教程的目标,那么在没有我的评论的情况下,您可以毫无困难地理解它!
src / components / InputEmail
<template>
<div>
<label>
Email
<input type="email" v-model="input" name="email" />
</label>
<ErrorDisplay v-if="input" :errors="errors" />
</div>
</template>
<script>
import useInputValidator from "@/features/useInputValidator";
import { isEmail } from "@/validators";
import ErrorDisplay from "./ErrorDisplay";
export default {
name: "InputEmail",
props: {
value: String
},
setup (props, { emit }) {
const { input, errors } = useInputValidator(
props.value,
[ isEmail() ],
value => emit("input", value)
);
return {
input,
errors
}
},
components: {
ErrorDisplay
}
}
</script>