先看一下Demo的页面效果
虽然它是一个简单的增删改,但是我把它当作一个标准的页面来做。
目录结构:
User:当前的页面.
compoent:拆分出来的组件,
type:User页面的公共类型声明
mitt:vue3中用来做兄弟传参
UI库:elelment-Plus
插件:安装Volar(把之前的vetur给禁用掉)vue3用到ts语法,不然到处都是报错.
所有的组件都用的是 <script lang="ts" setup>语法(也推荐大家这么用,这个语法糖很甜)
type-->index.ts
因为 添加,编辑,和列表渲染都用的是这样的数据结构所以就把它单独封装起来。
当你需要修改User这个页面的bug的时候,首先打开的肯定是index文件,当你看到一个这样的template时,bug已经解决一半了,因为它做到了所见即所得,结构非常的清晰,我们在页面看到的是三部分"按钮","列表","新增/编辑".而他的模板部分正好也是三部分。
在vue3中模板可以有多个根节点,这样大大减少了标签套娃的现象。
使用了<script setup>引入的子组件不需要注册,直接在模板中使用
所有的api都需要在'vue'中引入
import {...} from "vue"
ref
ref:声明一个响应式的变量,在<script setup>中修改或者使用都是通过它的.value属性。在template模板中直接引用就可以。
const a = ref(0)
a.value++
{{a}}
defineProps
defineProps 用来接收父组件传过来的变量,它是一个函数,我们需要在它的参数对象中把传过来的变量'配置'一下才能使用。在script中访问title.value,template中直接访问,有时候我们想解构props,这样会让他失去响应式 可以用toRefs它会让解构出来的变量具有响应式。
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
title: {
type: String,
default: "新增",
}
})
//template
{{title}}
const { visible, title } = toRefs(props)
//script
Mitt.emit("addData", { form, title: props.title })解构前
Mitt.emit("addData", { form, title: title.value })//解构后
这里需要注意一个小细节就是element-Plus中的dialog组件控制它显隐的属性是v-model,这导致了父组件传过来的props不能直接使用,这里用computed计算属性可以解决,
<el-dialog v-model="dialogShow" :title="title" width="30%" :before-close="handleCancel">
const dialogShow = computed(() => visible.value)
defineEmits
defineEmits用来接收父组件传过来的函数(给父组件传值),子组件向父组件传值主要是父组件传递一个函数给子组件,在子组件通过emit触发这个函数,并把参数(通信的值)传递过去。这里我们可以理解父组件传过来的函数需要用,defineEmits接收一下,下面在需要的时候,用它的返回值,直接调用,传值就可以了。因为这个emit多次被调用所以我对它封装了一下。
const emit = defineEmits(["isShow"])
//触发父组件事件
function emitIsShow(visible="false",title="新增"){
emit("isShow", {
visible,
title,
})
}
onMounted
vue3的生命周期和vue2的生命周期几乎时没有变化的,除了前面多了个on以外,我们发现大部分组合式api的特点就是,所有的逻辑都交给它的回调函数去处理,生命周期也一样。
onMounted(() => {
//从编辑页打开弹窗
Mitt.on('editData', (row: User) => {
emitIsShow(true, "编辑")
Object.assign(form, row)
})
})
reactive
reactive声明一个响应式对象,它声明的对象不能被直接赋值替换,可以通过,Object.assign(old, new)给他赋一个新值,当修改他单个属性时可以直接修改。
const formData: User = {
name: "",
age: "",
hobby: ""
}
const form = reactive(formData)
const newForm = {name:"李雷",age:18,hobby:"吃饭"}
Object.assign(form, newForm)//替换
form.name="李雷"//修改
watch
watch它监听对象时需要用一个函数包装一下,监听单个变量则不需要
watch(() => form.hobby, (newV) => {
if (newV) hobbyMsg.value = newV
})
const sum = ref(0)
watch(sum, (newV,oldV) => {
....
})
mitt
当我们在Add组件填写完表单点击确认就会把值传给List组件,这里有两种情况,编辑保存,和新增保存,所以用title来区分,这时需要用到兄弟传参,vue3中不能new Vue()。所以需要用到一个插件mitt
npm install mitt -S
yarn mitt
安装完插件在User文件夹下建一个mitt.js文件 用的时候和$Bus一样
mitt.js
import Event from 'mitt'
export default new Event()
Add.vue
import Mitt from "../mitt.js"
Mitt.emit("addData", { form, title: title.value })
List.vue
Mitt.on("addData", (data: intData) => {
if (data.title == "新增") {
tableData.value.push({
...data.form,
id: Math.floor(Math.random() * 1000) + 100
})
} else {
tableData.value.forEach((el: User, index: number) => {
if (el.id == data.form.id) {
tableData.value.splice(index, 1, { ...data.form })
}
});
}
})
在ts文件中不能引入js文件所以要找到项目根目录下的.d.ts文件,添加一个模块声明即可。
declare module '*.js'
Demo完整代码:点我跳转仓库地址