vue3已经出来2年了,但是在实际工作中却很少用到,很多前端的同事也在跃跃欲试在工作中使用vue3,作为相对保守派的我是不敢轻易尝试的,但是技术的革新是时代发展的需要,我们闭上双眼无视时代的进步,那么进步就不存在了吗?所以唯心主义是不成立的,是早晚要被淘汰的。基于不被淘汰的目的我也大胆的尝试了一把vue3的开发模式。当然工作中的代码是不能外泄的,下面示例我们仅以开源的element ui的基础上进行尝试,在这些尝试中给我的思考及启发分享给大家,如有不完善或者错误的地方,还请各位同僚不吝赐教,一起共建良好且高效的开发环境。
官网地址
确保你安装了最新版本的 Node.js,然后在命令行中运行以下命令 (不要带上 > 符号)
装不上的话,看看自己是否安装了科学上网的工具,或者防火墙之类的东西。
npm init vue@latest
这一指令将会安装并执行 create-vue,它是 Vue 官方的项目脚手架工具。你将会看到一些诸如 TypeScript 和测试支持之类的可选功能提示
✔ Project name: … <your-project-name>
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit testing? … No / Yes
✔ Add Cypress for both Unit and End-to-End testing? … No / Yes
✔ Add ESLint for code quality? … No / Yes
✔ Add Prettier for code formatting? … No / Yes
Scaffolding project in ./<your-project-name>...
Done.
为了让项目简单,这里我都选的no
安装地址
npm install element-plus --save
找到项目中的main.js文件。(注意此处是全局引入到项目中插件的地方,由于我们项目中只引入了element-plus,所以可以全局替换。
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
一切配置好后,我们就可以直接在项目中使用插件了。
首先我们知道vue3的主要标志是组合式api,也就是setup()函数。
所以在组件中我们就需要创建一个setup()。
如下:
<template>
</template>
<script>
export default {
setup() {
},
};
</script>
<style scoped>
</style>
但是我们看到element-plus上长这样
<template>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
</el-table>
</template>
<script lang="ts" setup>
const tableData = [
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
]
</script>
很多像我这样的小伙伴,可能就会迷惑了,这样抄行吗?
答案是可行的,但是个人经过学习发现这样抄并不好。
首先:后面我们越抄代码越乱,不易于维护。
其次:和vue差距太大了,我们不太习惯。
为了习惯我们可以将上面的代码改成这样。
<template>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
</el-table>
</template>
<script>
import { reactive, toRefs } from "vue";
export default {
setup() {
const state = reactive({
tableData: [
{
date: "2016-05-03",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-02",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-04",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-01",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
],
});
return toRefs(state);
},
};
</script>
<style scoped>
</style>
发现上面的代码并没有报错。
由此我们认为上面的代码是完全可行的。此处我们就用到了vue3的reactive, toRefs两个API。
表格实在太简单,那么我们是不是可以在表格的基础上加个搜索呢?
是的
一个可以校验的表单长这个样子。
<template>
<el-form
ref="ruleFormRef"
:model="ruleForm"
:rules="rules"
label-width="120px"
class="demo-ruleForm"
:size="formSize"
status-icon
>
<el-form-item label="Activity name" prop="name">
<el-input v-model="ruleForm.name" />
</el-form-item>
<el-form-item label="Activity zone" prop="region">
<el-select v-model="ruleForm.region" placeholder="Activity zone">
<el-option label="Zone one" value="shanghai" />
<el-option label="Zone two" value="beijing" />
</el-select>
</el-form-item>
<el-form-item label="Activity count" prop="count">
<el-select-v2
v-model="ruleForm.count"
placeholder="Activity count"
:options="options"
/>
</el-form-item>
<el-form-item label="Activity time" required>
<el-col :span="11">
<el-form-item prop="date1">
<el-date-picker
v-model="ruleForm.date1"
type="date"
label="Pick a date"
placeholder="Pick a date"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col class="text-center" :span="2">
<span class="text-gray-500">-</span>
</el-col>
<el-col :span="11">
<el-form-item prop="date2">
<el-time-picker
v-model="ruleForm.date2"
label="Pick a time"
placeholder="Pick a time"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-form-item>
<el-form-item label="Instant delivery" prop="delivery">
<el-switch v-model="ruleForm.delivery" />
</el-form-item>
<el-form-item label="Activity type" prop="type">
<el-checkbox-group v-model="ruleForm.type">
<el-checkbox label="Online activities" name="type" />
<el-checkbox label="Promotion activities" name="type" />
<el-checkbox label="Offline activities" name="type" />
<el-checkbox label="Simple brand exposure" name="type" />
</el-checkbox-group>
</el-form-item>
<el-form-item label="Resources" prop="resource">
<el-radio-group v-model="ruleForm.resource">
<el-radio label="Sponsorship" />
<el-radio label="Venue" />
</el-radio-group>
</el-form-item>
<el-form-item label="Activity form" prop="desc">
<el-input v-model="ruleForm.desc" type="textarea" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm(ruleFormRef)"
>Create</el-button
>
<el-button @click="resetForm(ruleFormRef)">Reset</el-button>
</el-form-item>
</el-form>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
const formSize = ref('default')
const ruleFormRef = ref<FormInstance>()
const ruleForm = reactive({
name: 'Hello',
region: '',
count: '',
date1: '',
date2: '',
delivery: false,
type: [],
resource: '',
desc: '',
})
const rules = reactive<FormRules>({
name: [
{ required: true, message: 'Please input Activity name', trigger: 'blur' },
{ min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },
],
region: [
{
required: true,
message: 'Please select Activity zone',
trigger: 'change',
},
],
count: [
{
required: true,
message: 'Please select Activity count',
trigger: 'change',
},
],
date1: [
{
type: 'date',
required: true,
message: 'Please pick a date',
trigger: 'change',
},
],
date2: [
{
type: 'date',
required: true,
message: 'Please pick a time',
trigger: 'change',
},
],
type: [
{
type: 'array',
required: true,
message: 'Please select at least one activity type',
trigger: 'change',
},
],
resource: [
{
required: true,
message: 'Please select activity resource',
trigger: 'change',
},
],
desc: [
{ required: true, message: 'Please input activity form', trigger: 'blur' },
],
})
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
console.log('submit!')
} else {
console.log('error submit!', fields)
}
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
}
const options = Array.from({ length: 10000 }).map((_, idx) => ({
value: `${idx + 1}`,
label: `${idx + 1}`,
}))
</script>
好烦,又是一大坨。
对此我们是不是应该和上面那个表格融合一下呢?
经过改造后长这个样子:
<template>
<el-form
ref="ruleFormRef"
:model="ruleForm"
:rules="rules"
label-width="120px"
class="demo-ruleForm"
:size="formSize"
status-icon
>
<el-form-item label="Activity name" prop="name">
<el-input v-model="ruleForm.name" />
</el-form-item>
<el-form-item label="Activity zone" prop="region">
<el-select v-model="ruleForm.region" placeholder="Activity zone">
<el-option label="Zone one" value="shanghai" />
<el-option label="Zone two" value="beijing" />
</el-select>
</el-form-item>
<el-form-item label="Activity count" prop="count">
<el-select-v2
v-model="ruleForm.count"
placeholder="Activity count"
:options="options"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm(ruleFormRef)"
>Create</el-button
>
<el-button @click="resetForm(ruleFormRef)">Reset</el-button>
</el-form-item>
</el-form>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
</el-table>
</template>
<script>
import { reactive, ref, toRefs } from "vue";
export default {
setup() {
const submitForm = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid, fields) => {
if (valid) {
console.log("submit!");
} else {
console.log("error submit!", fields);
}
});
};
const resetForm = (formEl) => {
if (!formEl) return;
formEl.resetFields();
};
const state = reactive({
// 表格数据
tableData: [
{
date: "2016-05-03",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-02",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-04",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-01",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
],
// 表单数据
ruleForm: {
name: "Hello",
region: "",
count: "",
},
// 表单校验
rules: {
name: [
{
required: true,
message: "Please input Activity name",
trigger: "blur",
},
{
min: 3,
max: 5,
message: "Length should be 3 to 5",
trigger: "blur",
},
],
region: [
{
required: true,
message: "Please select Activity zone",
trigger: "change",
},
],
count: [
{
required: true,
message: "Please select Activity count",
trigger: "change",
},
],
},
formSize: "default",
// 数字字典
options: Array.from({ length: 10000 }).map((_, idx) => ({
value: `${idx + 1}`,
label: `${idx + 1}`,
})),
ruleFormRef:ref(null),
submitForm, //提交
resetForm,//还原
});
return toRefs(state);
},
};
</script>
<style scoped>
</style>
上面虽然省了一些字段,但是其实已经简化整合了好多东西了。
比如:
其实所有字段都是可以存在reactive;
ruleFormRef:ref(null),可以直接使用在vue标签上的ref,并且数据中已经具备了dom树上的所有内容。
也就是在此处我们也是可以把resetForm函数改写也是可以正常使用的:
改写如下:(不再通过触发事件传值了,而是直接使用state里面的值)。
function resetForm () {
if (!state.ruleFormRef) return;
state.ruleFormRef.resetFields();
};
细心的小伙伴发现此处我们把箭头函数改成了普通函数,这是因为const会有执行顺序的问题。
如果想要避免这个问题,我们可以把const改成var。(这里涉及到了js的预编译问题)
然后代码就变成了这个样子:
<template>
<el-form
ref="ruleFormRef"
:model="ruleForm"
:rules="rules"
label-width="120px"
class="demo-ruleForm"
:size="formSize"
status-icon
>
<el-form-item label="Activity name" prop="name">
<el-input v-model="ruleForm.name" />
</el-form-item>
<el-form-item label="Activity zone" prop="region">
<el-select v-model="ruleForm.region" placeholder="Activity zone">
<el-option label="Zone one" value="shanghai" />
<el-option label="Zone two" value="beijing" />
</el-select>
</el-form-item>
<el-form-item label="Activity count" prop="count">
<el-select-v2
v-model="ruleForm.count"
placeholder="Activity count"
:options="options"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm(ruleFormRef)"
>Create</el-button
>
<el-button @click="resetForm()">Reset</el-button>
</el-form-item>
</el-form>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
</el-table>
</template>
<script>
import { reactive, ref, toRefs } from "vue";
export default {
setup() {
var submitForm = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid, fields) => {
if (valid) {
console.log("submit!");
} else {
console.log("error submit!", fields);
}
});
};
var resetForm = () => {
if (!state.ruleFormRef) return;
state.ruleFormRef.resetFields();
};
var state = reactive({
// 表格数据
tableData: [
{
date: "2016-05-03",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-02",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-04",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-01",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
],
// 表单数据
ruleForm: {
name: "Hello",
region: "",
count: "",
},
// 表单校验
rules: {
name: [
{
required: true,
message: "Please input Activity name",
trigger: "blur",
},
{
min: 3,
max: 5,
message: "Length should be 3 to 5",
trigger: "blur",
},
],
region: [
{
required: true,
message: "Please select Activity zone",
trigger: "change",
},
],
count: [
{
required: true,
message: "Please select Activity count",
trigger: "change",
},
],
},
formSize: "default",
// 数字字典
options: Array.from({ length: 10000 }).map((_, idx) => ({
value: `${idx + 1}`,
label: `${idx + 1}`,
})),
ruleFormRef:ref(null),
submitForm, //提交
resetForm,//还原
});
return toRefs(state);
},
};
</script>
<style scoped>
</style>
上面的内容,我们承认函数已经不受编译顺序影响了,但是这也影响了我们新增const,let的初衷,为了使用const,let,我们还是直接要按顺序来的,如果想放到state下面,那么就推荐使用function。这也就回到了js的本源。
就像这样:
<template>
<el-form
ref="ruleFormRef"
:model="ruleForm"
:rules="rules"
label-width="120px"
class="demo-ruleForm"
:size="formSize"
status-icon
>
<el-form-item label="Activity name" prop="name">
<el-input v-model="ruleForm.name" />
</el-form-item>
<el-form-item label="Activity zone" prop="region">
<el-select v-model="ruleForm.region" placeholder="Activity zone">
<el-option label="Zone one" value="shanghai" />
<el-option label="Zone two" value="beijing" />
</el-select>
</el-form-item>
<el-form-item label="Activity count" prop="count">
<el-select-v2
v-model="ruleForm.count"
placeholder="Activity count"
:options="options"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm(ruleFormRef)"
>Create</el-button
>
<el-button @click="resetForm">Reset</el-button>
</el-form-item>
</el-form>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
</el-table>
</template>
<script>
import { reactive, ref, toRefs } from "vue";
export default {
setup() {
const submitForm = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid, fields) => {
if (valid) {
console.log("submit!");
} else {
console.log("error submit!", fields);
}
});
};
const state = reactive({
// 表格数据
tableData: [
{
date: "2016-05-03",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-02",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-04",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-01",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
],
// 表单数据
ruleForm: {
name: "Hello",
region: "",
count: "",
},
// 表单校验
rules: {
name: [
{
required: true,
message: "Please input Activity name",
trigger: "blur",
},
{
min: 3,
max: 5,
message: "Length should be 3 to 5",
trigger: "blur",
},
],
region: [
{
required: true,
message: "Please select Activity zone",
trigger: "change",
},
],
count: [
{
required: true,
message: "Please select Activity count",
trigger: "change",
},
],
},
formSize: "default",
// 数字字典
options: Array.from({ length: 10000 }).map((_, idx) => ({
value: `${idx + 1}`,
label: `${idx + 1}`,
})),
ruleFormRef:ref(null),
submitForm, //提交
resetForm,//还原
});
function resetForm (){
if (!state.ruleFormRef) return;
state.ruleFormRef.resetFields();
};
return toRefs(state);
},
};
</script>
<style scoped>
</style>
其实讲到上面还不过瘾
我们还发现setup的返回其实还可以通过解构的方法这样写的。
<script>
import { reactive, ref, toRefs } from "vue";
export default {
setup() {
const submitForm = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid, fields) => {
if (valid) {
console.log("submit!");
} else {
console.log("error submit!", fields);
}
});
};
const state = reactive({
// 表格数据
tableData: [
{
date: "2016-05-03",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-02",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-04",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-01",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
],
// 表单数据
ruleForm: {
name: "Hello",
region: "",
count: "",
},
// 表单校验
rules: {
name: [
{
required: true,
message: "Please input Activity name",
trigger: "blur",
},
{
min: 3,
max: 5,
message: "Length should be 3 to 5",
trigger: "blur",
},
],
region: [
{
required: true,
message: "Please select Activity zone",
trigger: "change",
},
],
count: [
{
required: true,
message: "Please select Activity count",
trigger: "change",
},
],
},
formSize: "default",
// 数字字典
options: Array.from({ length: 10000 }).map((_, idx) => ({
value: `${idx + 1}`,
label: `${idx + 1}`,
})),
ruleFormRef:ref(null),
submitForm, //提交
// resetForm,//还原
});
function resetForm (){
if (!state.ruleFormRef) return;
state.ruleFormRef.resetFields();
};
return{...toRefs(state),resetForm} ;
},
};
</script>
好了,今天就到这啦