在vue中为什么你添加的属性,打印都有,视图却不更新?

在 Vue 中,如果在运行时动态给 data 添加新的属性,Vue 无法自动追踪这个新属性的变化。这是因为 Vue 使用的是 Object.defineProperty 进行数据劫持,而 Object.defineProperty 只能在对象初始化时对已有的属性进行监听,因此动态添加的属性不会被 Vue 侦测到,也不会触发视图的更新。

发生什么?

假设你在一个组件的 data 中没有定义 newProperty,然后你想动态添加它:

this.$data.newProperty = 'newValue';
console.log(this.newProperty); // 'newValue'

在上面的例子中,你虽然可以通过 $data 访问到 newProperty,但是 Vue 无法响应它的变化,界面也不会更新。

解决办法:

Vue 提供了两种方法解决这个问题:

  1. 使用 Vue.set() 方法:可以动态地将属性添加到响应式对象中,并确保它是响应式的。
Vue.set(this.someObject, 'newProperty', 'newValue');
  1. this.$set() 方法:与 Vue.set() 类似,适用于实例内的响应式数据。
this.$set(this.someObject, 'newProperty', 'newValue');

示例:

data() {
  return {
    person: {
      name: 'John'
    }
  };
},
mounted() {
  // 动态添加一个属性,Vue 无法追踪
  this.person.age = 25;
  
  // 正确的方式,Vue 会追踪到新属性的变化
  this.$set(this.person, 'age', 25);
}

Vue 源码分析:

在 Vue 源码中,Vue 初始化响应式数据时,会通过 initData() 函数遍历 data 对象上的属性,并调用 defineReactive() 方法。这个方法使用 Object.defineProperty 将属性定义为 getter 和 setter,从而使得 Vue 可以劫持数据的读取和更新。

然而,Object.defineProperty 不能对新增的属性进行监听。这就是为什么在运行时动态添加属性不会触发响应的原因。因此,Vue 提供了 set 方法,它使用 defineReactive 手动为新增属性设置响应式劫持。

场景:

动态给 data 添加属性常见于异步请求完成后,或者组件渲染过程中,需要给对象动态添加数据时。比如,一个用户表单可能开始时只有 nameemail 字段,但在某些交互下需要动态添加 phone 字段。

### 使用 `vue-print-nb` 打印 `el-table` 常见问题解决方案 #### 一、Table 序号打印错乱问题 当使用 `vue-print-nb` 打印 `el-table` 时,如果发现序号打印错乱,可以通过调整 CSS 样式来解决问题。具体方法如下: 通过设置 `.el-table .el-table__cell.is-hidden > * { visibility: visible; font-size: 12px; }` 可以确保隐藏的内容在打印时可见[^1]。 ```css ::v-deep .el-table .el-table__cell.is-hidden > * { visibility: visible; font-size: 12px; } ``` --- #### 二、表格边框显示问题 在打印过程中,由于默认情况下浏览器会保留原有样式的边框效果,因此需要手动覆盖样式以确保边框正常显示。可以尝试以下方式解决此问题: 重新定义打印区域内的表格样式,例如添加 border 属性,并将其应用到目标组件上[^2]。 ```css @media print { .el-table td, .el-table th { border: 1px solid #ebeef5 !important; } .el-table::before, .el-table--border, .el-table--group { border-color: #ebeef5 !important; } } ``` --- #### 三、表格打印全问题 对于某些情况下的宽屏数据表,在实际打印时可能会因为页面宽度限制而无法完全展示所有列的数据。这是由于浏览器计算的屏幕像素比例与物理打印机分辨率存在差异所致[^3]。 一种有效的办法是动态调整每列宽度或者固定整体布局尺寸,从而适应 A4 或其他标准纸张大小的要求。以下是实现代码片段的一个例子: ```javascript methods: { handlePrint() { this.$nextTick(() => { const tableWidth = document.querySelector('.el-table').offsetWidth; const colGroups = document.querySelectorAll('.el-table>colgroup>col'); let totalWidth = 0; Array.from(colGroups).forEach((item) => { item.style.width = `${(parseInt(item.getAttribute('width')) / tableWidth) * 793}px`; totalWidth += parseInt(item.style.width); }); console.log(`Total adjusted width is ${totalWidth}`); }); }, }, ``` 上述逻辑中,我们先获取整个表格的实际宽度,再按比例分配给各个子项的新宽度值(假设最终输出介质为A4横向模式下最大可用空间约为793px)。这样即使原始设计超出常规范围也能较好适配硬件设备特性需求。 --- #### 四、分页及自定义页脚处理 为了支持多页文档中的连续记录以及统计汇总功能,则需引入额外机制完成分割操作并附加相应信息至各独立区块底部位置处[^4]。 下面是一个简单的 Vue 模板实例演示如何利用循环渲染技术生成多个小型局部视图单元供后续组合拼接成完整的报告形式文件结构: ```html <template> <div id="print-area"> <!-- 动态创建若干个表格 --> <table v-for="(chunk, index) in paginatedData" :key="index" class="my-table"> <thead> <tr> <th v-for="(header, hIndex) in headers" :key="'h' + hIndex">{{ header }}</th> </tr> </thead> <tbody> <tr v-for="(row, rowIndex) in chunk" :key="'r' + rowIndex"> <td v-for="(cell, cellIndex) in row" :key="'c' + cellIndex">{{ cell }}</td> </tr> </tbody> <tfoot> <tr> <td colspan="{{headers.length}}">Page {{ index + 1 }} of {{ totalPages }}</td> </tr> </tfoot> </table> </div> </template> <script> export default { data() { return { allRows: [], // 完整数据集 pageSize: 20, // 每页条目数量 }; }, computed: { paginatedData() { const result = []; for (let i = 0; i < this.allRows.length; i += this.pageSize) { result.push(this.allRows.slice(i, i + this.pageSize)); } return result; }, totalPages() { return Math.ceil(this.allRows.length / this.pageSize); }, }, }; </script> ``` 在此基础上还可以进一步扩展更多高级选项比如自动累加总计数值等功能模块满足特定业务场景下的复杂报表生成功能要求。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sherry Tian

打赏1元鼓励作者

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值