当下公司在做一个媒体门户网站,后台由另一家公司使用java开发并提供接口,本人负责开发后台页面,使用的是vue-element-admin开发
下面说一下问题场景,在开发过程中有一个后台管理员角色页面,其中包含一个表单dialog,在其中使用了el-tree组件,相关 代码结构如下:
<div class="filter-container">
<el-button class="filter-item" style="margin-left: 10px;" v-waves @click="handleCreate" type="primary" icon="el-icon-edit">新增角色
</el-button>
</div>
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" width="50%">
<el-form :rules="rules" ref="dataForm" :model="temp" label-position="top" label-width="90px"
style='width: 400px; margin-left:50px;'>
<el-form-item label="选择权限" prop="sysPermission">
<el-tree ref="tree" :data="sysPermission" :props="formProps" show-checkbox
@check-change="handleCheckChange" node-key="id"></el-tree>
</el-form-item>
</el-form>
</el-dialog>
相关的js如下:
export default {
name: 'sysRoleList',
data() {
return {
tableKey: 0,
list: null,
total: null,
listLoading: true,
formLoading: true,
listQuery: {
page: 1,
limit: 20,
importance: undefined,
title: undefined,
type: undefined,
sort: '+id'
},
dialogFormVisible: false,
dialogStatus: 'update',
textMap: {
update: '编辑角色',
create: '新增角色'
},
rules: {
sysRoleName: [{required: true, message: '必须填写角色名称', trigger: 'blur'}]
},
// 表单数据
temp: {
id: '',
sysPermissionList: [],
sysRoleName: ''
},
currentKeys: [],
// 表单权限字段映射
formProps: {
label: 'sysPermissionName'
},
sysPermission: {}
}
},
created() {
// 列表数据
// this.getList()
// 获取所有权限
// this.findAllSysPermission()
},
methods: {
/*
* todo:checkbox状态变更监听
* */
handleCheckChange(data, checked, indeterminate) {
const idObj = {id: data.id}
this.temp.sysPermissionList.push(idObj)
},
resetTemp() {
this.temp = {
id: '',
sysRoleName: '',
sysPermissionList: []
}
},
handleCreate() {
this.resetTemp()
this.dialogStatus = 'create'
this.currentKeys = []
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
this.$refs.tree.setCheckedKeys(this.currentKeys)
})
},
handleUpdate(row) {
this.resetTemp()
this.dialogStatus = 'update'
this.dialogFormVisible = true
this.temp = Object.assign({}, row) // copy obj
this.currentKeys = []
row.sysPermissionList.forEach((value, index) => {
this.currentKeys.push(value[0])
})
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
this.$refs.tree.setCheckedKeys(this.currentKeys)
})
}
}
}
需求:
需要在每次编辑数据的时候触发<el-tree>的方法setCheckedKeys
问题:
之前没有把this.$refs.tree.setCheckedKeys()写在this.$nextTick的callback之中,因此会提示:
TypeError: Cannot read property 'setCheckedKeys' of undefined
解决方法:
把dialog组件内的其他需要执行方法的组件方法写到this.$nextTick的callback之中
原因如下(官方):
可能你还没有注意到,Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作上非常重要。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部尝试对异步队列使用原生的 Promise.then 和 MessageChannel,如果执行环境不支持,会采用 setTimeout(fn, 0) 代替。
例如,当你设置 vm.someData = ‘new value’ ,该组件不会立即重新渲染。当刷新队列时,组件会在事件循环队列清空时的下一个“tick”更新。多数情况我们不需要关心这个过程,但是如果你想在 DOM 状态更新后做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员沿着“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们确实要这么做。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。
深入响应式原理
个人理解:
vue这么做是因为频繁的更新dom是特别耗费性能的,所以搞了一个批处理更新,把所有的update操作放到任务队列中,等主线程中执行栈的所有同步任务执行完毕,系统就会读取任务队列
一个比较典型的场景,created回调里是无法直接通过this.$refs获取到用ref命名的子组件的,只有通过$nextTick才能访问到。还有比如dialog里有一个步骤条组件,在每次打开对话框都想触发步骤1的动作。如果直接写step=0;step=1;是不会有变化的,因为整个函数执行完之前DOM都不会刷新。把step=1放到$nextTick里就可以了