d40: vue杂项问题
导航
-
- Vue 中使用 HTML 原生属性的问题
-
el-dialog
与父组件交互的异步问题
-
- 动态路由机制中关于根路由的小问题
-
ref
的dep
特性
1. Vue 中使用 HTML 原生属性的问题
在 Vue 项目中,我们可能会不小心使用 HTML 原生事件属性,例如:
<div onclick="profileVisible = true">点击我</div>
这看起来没什么问题,但实际上,这种方式存在很大的隐患。当 onclick
被触发时,浏览器会在 全局作用域(即 window
对象)下执行这句 JS 代码。它会寻找一个名为 window.profileVisible
的变量并将其设置为 true
,而完全不知道 Vue 组件实例的存在,更不知道你定义在组件 data
中的那个 profileVisible
。
Vue 指令与原生 HTML 属性的对比
特性 | @click (Vue 指令) | onclick (原生 HTML 属性) |
---|---|---|
执行上下文 | Vue 组件实例 (this ) | 全局作用域 (window ) |
响应式 | 能触发 Vue 的响应式更新 | 不能触发 Vue 的响应式更新 |
数据访问 | 可以访问 data , props , computed 等 | 只能访问全局变量 |
方法调用 | 可以调用 methods 中定义的方法 | 只能调用全局函数 |
推荐用法 | 在 Vue 项目中必须使用 | 在 Vue 项目中应避免使用 |
结论
在 Vue 项目中,始终 应该使用 Vue 的事件处理指令(如 @click
, @input
, @submit
等)来处理 DOM 事件,而不是原生的 HTML 事件属性。
2. el-dialog
与父组件交互的异步问题
在使用 el-dialog
组件时,我们可能会遇到子组件与父组件交互的异步问题。例如,我们有以下代码:
<LinkPermissionDialogv-model:open="openLinkPermission":role="linkedRole":permissions="linkedPermissions":all-permissions="allPermissions"@confirm="submitCheckedPermission"@close="openLinkPermission = false"/>
当我们试图向子组件传输 allPermissions
等数据,并在 handleOpen
方法中将 allPermissions
处理为树时,发现子组件生成的树还是上一次的数据。这是因为 el-dialog
的 open
事件在 DOM 更新前触发,而父组件异步拉取新权限后才将 props.permissions
改掉。因此,当 handleOpen
同步执行时,新数据还没下来,我们处理的其实是上一次的数据。
解决方案
此时,应该使用 watch
监听 allPermissions
的变化,并进行同步:
watch(() => props.allPermissions,() => {tableData.value = handleTree(props.allPermissions);},{ immediate: true } // 立即执行一次
);
3. 动态路由机制中关于根路由的小问题
在动态路由机制中,我们可能会遇到一些关于根路由的小问题。例如,原来的动态路由机制会将无 component
的组件(即目录)用 LayoutView
来兜底。这样做的问题是,如果出现目录嵌套目录的情况,就会对 Layout
进行重复渲染。
解决方案
这个问题其实很好解决,只需要将目录的 component
置为 null
,并整体上添加 LayoutView
作为根路由即可:
/*** 获取路由列表*/
export const buildRoutes = async (menus) => {const children = buildNestedRoutes(menus);const route = {path: '/',redirect: '/index',component: LayoutView,children: children,meta: {}};return route;
};
在这个过程中,我们意识到了重复定义相同的根路由可能会带来一些问题。Vue Router 的路由匹配机制是通过路径 + 其他属性(如 name
、component
)来判断路由是否“重复”的。为了避免奇奇怪怪的隐式覆盖问题,最好统一一个根路由:
/*** 加入到 Layout 中的静态路由*/
export const staticChildren = [{path: 'index',component: () => import('@/views/home/index.vue'),name: 'Index',meta: {title: '首页',hidden: false,}}
];/*** 获取路由列表*/
export const buildRoutes = async (menus) => {const dynamicChildren = buildNestedRoutes(menus);// 合并静态路由和动态路由const allChildren = mergeChildren(staticChildren, dynamicChildren);// 创建根路由const rootRoute = {path: '/',component: LayoutView,name: 'Layout',redirect: '/index',children: allChildren,meta: {}};// 防止重复添加if (router.hasRoute('Layout')) router.removeRoute('Layout');return rootRoute;
};
我们可以通过 map
来简单地去重,这里就不展示了。
4. ref
的 dep
特性
在使用 ref
时,我们可能会遇到一些关于 dep
特性的问题。例如,我们有以下代码:
export const AllPages = {pageNum: 1,pageSize: 1000000
};
现在有两段类似的代码:
queryParams.value = { pageNum: 1, pageSize: 10};
queryParams.value = AllPages;
我们将其用于对 createdAt
字段进行重置:
<el-form-item label="创建时间"><el-date-picker v-model="queryParams.createdAt" type="date" placeholder="起始日期" clearable/>
</el-form-item>
第一段代码顺利完成了,但是第二段代码还残留着之前的 createdAt
数据,这是为什么呢?
原因分析
在 queryParams
中,我们一开始没有 createdAt
这个字段。而在第一次添加 createdAt
后,dep
列表里会添加这个字段的槽位。我们对其重新赋值 { pageNum: 1, pageSize: 10}
后,Vue 的 proxy
会保留引用关系。因为 createdAt
实际上不存在,所以悄悄地对其赋值为 undefined
。queryParams.value = AllPages;
这一步相当于把 ref
重新赋值了一个普通对象,这个对象里面又没有关于 createdAt
的信息,v-model
绑定的路径直接断了(变成访问一个非响应式的、根本不存在的属性),DatePicker
内部监听不到“从有到无”的变动,于是面板里依旧显示旧值。
解决方案
将 AllPages
对象展开 { ...AllPages }
,这样就和 { pageNum: 1, pageSize: 10}
等价了。
以上就是我们在使用 Vue.js 过程中遇到的一些常见问题及其解决方案。希望这些内容能对大家有所帮助。
注意事项
- 在 Vue 项目中,始终 使用 Vue 的事件处理指令(如
@click
,@input
,@submit
等)来处理 DOM 事件,而不是原生的 HTML 事件属性。 - 使用
watch
监听props
的变化,以解决子组件与父组件交互的异步问题。 - 在动态路由机制中,避免重复定义相同的根路由,统一一个根路由可以避免很多问题。
- 注意
ref
的dep
特性,在重新赋值时要确保对象的结构一致,避免出现绑定路径断开的问题。