本篇是对3-1的补充,使用setup语法糖,以及eslint之后的bug修复。
单向数据流
可以使用eslint来主动识别是否编码过程违背了单向数据流的思想,若有问题,编译时可能出现Unexpected mutation of "props" prop
的报错。
业务描述
Navbar中有一个avatar,鼠标悬浮出现下拉框,点击个人中心后使用抽屉形式的表单来做个人信息的修改。
具体设计如下:
父子组件通信实现
我的想法是Navbar作为一个父组件(总控制器),有Avatar和Drawer两个子组件,子组件管理自己的状态,当有对其他组件有影响的状态的改变要通知父组件。
Navbar.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <div class="navbar-wrapper"> <Avatar @handleDrawer="handleDrawer" /> </div> <Drawer :show="showDrawer" @handleDrawer="handleDrawer" /> </template>
<script setup lang="ts"> import Avatar from './avatar.vue' import Drawer from './drawer.vue' const showDrawer = ref(false)
const handleDrawer = (flag: boolean) => { console.log('flag=', flag) showDrawer.value = flag } </script>
|
Avatar.vue中图标的hover状态只与自身有关,点击“个人中心”-openMyInfo方法会触发Drawer状态改变,使用emit告知父组件状态改变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <template> <el-dropdown class="avatar-container right-menu-item hover-effect" trigger="hover"> <div class="avatar-wrapper"> <img src="user-avatar" /> </div> <template #dropdown> <el-dropdown-menu> <el-dropdown-item @click="openMyInfo" @mouseenter="handleHover(1, true)" @mouseleave="handleHover(1, false)" > <div class="left-img"> <img v-if="!state.hoverInfo" src="@/assets/img/my-info.png" alt="" /> <img v-else src="@/assets/img/my-info-active.png" alt="" /> </div> <span>个人中心</span> </el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> </template>
<script setup lang="ts"> import FormInstance from 'element-plus' import { ElMessage, ElMessageBox, ElNotification, ElDrawer } from 'element-plus' const state = reactive({ hoverInfo: false, })
const handleHover = (flag: Number, action: any) => { state.hoverInfo = action } const myEmit = defineEmits(['handleDrawer']) const openMyInfo = () => { myEmit('handleDrawer', true) } </script>
|
Drawer.vue
这里需要注意的是,我一开始直接将 props.show 赋予 v-model,结果出现了vue/no-mutating-props
问题,查阅资料后发现, 是直接对 prop 的内容进行了修改,违反了单向数据流原则。
原因:实质上,在子组件内,我们并不可以直接将 prop 的变量应用于子组件深层次组件的 v-model 上(因为 v-model 会隐含的对 prop 值更新。
解决方式:将 props.show 通过中间变量进行 v-model 化,再把中间变量传入子组件深层次组件的 v-model 。下面的计算属性showDrawer就是这里的中介。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <template> <el-drawer ref="drawerRef" v-model="showDrawer" :before-close="handleClose" > <el-tabs v-model="drawActive" type="card"> <el-tab-pane label="修改密码" name="first"> <el-form :model="pwdForm" :rules="pwdRules" ref="pwdFormRef"> <el-form-item label="新密码"> <el-input v-model="pwdForm.password" type="password"/> </el-form-item> <div class="drawer__footer"> <el-button @click="cancelForm">取消</el-button> <el-button type="primary" @click="Sumbit(pwdFormRef)"></el-button> </div> </el-form> </el-tab-pane> </el-tabs> </el-drawer> </template>
<script setup lang="ts"> import FormInstance from 'element-plus' import { ElMessage, ElNotification, ElDrawer } from 'element-plus' import { updateUserPwd } from '@/api/users'
const props = defineProps({ show: { type: Boolean, default: false } }) const myEmit = defineEmits(['handleDrawer']) const showDrawer = computed({ get: () => props.show, set: () => {} })
const handleClose = () => { myEmit('handleDrawer', false) }
const cancelForm = () => { myEmit('handleDrawer', false) } </script>
|