场景背景:基于pdfjs实现PDF预览,动态创建canvas渲染PDF页面,需要存储页面附属信息,很多人会混淆
canvas.dataset.page和Vue组件内data(){}定义的变量。这两者看似都能存数据,但存储位置、特性、适用场景完全不同,一旦用错容易产生诡异bug。
一、核心概念简介
- DOM
data-*自定义属性 & datasetdata-*是HTML原生标准,允许开发者在HTML标签上挂载自定义信息。浏览器自动在DOM元素上提供dataset对象,专门读写所有data-开头的属性。 特点:数据挂载在DOM标签上,属于浏览器原生能力,和Vue框架无关。
示例DOM:
<canvas data-page="1" data-page-height="841.89"></canvas>
通过 canvas.dataset 读写:
// 赋值
canvas.dataset.page = 1
canvas.dataset.pageHeight = 841.89
// 读取
console.log(canvas.dataset.page) // "1" 字符串类型
console.log(canvas.dataset.pageHeight) // "841.89"
-
Vue data 响应式变量 在Vue组件
data(){}中定义的变量,保存在JS内存中的Vue组件实例 上,Vue对它做了响应式劫持。 特点:变量修改可以被Vue监听,自动触发模板更新、watch、computed。不会出现在HTML标签中。export default {
data() {
return {
scale: 1.2, // PDF全局缩放
currentPage: 1, // 当前浏览页码
}
}
}
二、详细对比表
| 对比项 | DOM data-* / dataset | Vue data 响应式变量 |
|---|---|---|
| 存储位置 | DOM元素节点(HTML标签上),浏览器Elements面板可以直接看到 | JS内存,Vue组件实例;DOM看不到 |
| 是否原生 | Web标准原生API,不依赖Vue、pdfjs | Vue框架提供的响应式能力,脱离Vue不能使用 |
| 响应式 | ❌ 无响应式。修改dataset,Vue无法感知,不会触发watch、页面更新 | ✅ 响应式。修改变量,Vue自动监听,触发watch、模板渲染 |
| 数据类型 | 全部为字符串。数字写入会自动转为字符串,存对象需要JSON序列化 | 支持任意JS类型:数字、布尔、数组、对象、函数 |
| 生命周期 | 和DOM绑定:DOM元素被删除,dataset数据跟着消失 | 和组件实例绑定:组件销毁,变量才释放,DOM删除不影响变量 |
| 典型用途 | 标记DOM自身附属信息;配合querySelector选择器查找DOM |
保存业务全局状态,驱动页面逻辑、模板渲染 |
三、代码实战(PDF预览场景,贴合我们之前pdfjs代码)
场景说明
我们用pdfjs渲染PDF,动态创建<canvas>画布渲染每一页PDF。
-
每个canvas需要记录:属于第几页、PDF原始页面高度 → 使用
dataset -
组件全局状态:缩放比例、PDF文档对象、当前页码 → 使用Vue data
代码关键点说明
canvas.dataset.page = num信息绑定到canvas DOM上,后续使用querySelector通过data-page选择器快速找到对应页面画布。canvas删除,这个页码标记自动消失,不需要手动维护数组。this.scale放在Vue data scale是全局状态,所有页面共用一份。使用Vuewatch监听scale变化,自动重绘PDF页面。如果scale放到dataset,修改scale时需要遍历所有canvas逐个更新dataset,维护成本极高。
四、适用场景选择指南
✅ 什么时候使用 DOM dataset(data-*)
- 数据属于单个DOM元素自身的附属信息,DOM销毁数据就不再需要;
- 需要通过CSS选择器
querySelector,根据标记查找DOM节点; - 原生JS操作DOM,脱离Vue环境也需要读取该信息;
- 多同类DOM(多个canvas),每个DOM有独立标识。
适合:canvas的页码
data-page、页面原始高度data-page-height、每个签章框的id等DOM标记。
❌ 不推荐dataset场景
- 需要监听数据变化,自动执行业务逻辑;
- 全局共享状态,多个DOM共用同一个值;
- 存储对象、数组等复杂数据(dataset只能存字符串,需要JSON序列化)。
✅ 什么时候使用 Vue data 响应式变量
- 全局业务状态,组件内多处共享;
- 数据变更后,需要自动触发逻辑:
watch、computed、模板渲染; - 存储复杂类型:对象、数组、PDF文档实例;
- 驱动模板渲染,控制元素
v-if、v-for。
适合:缩放scale、pdfDoc文档对象、总页数totalPages、当前页码currentPage、loading加载状态。
❌ 不推荐放在Vue data场景
- 单纯用来标记DOM,需要通过选择器查找DOM;
- 数据是每个DOM实例独有的,DOM删除后数据无用(需要手动维护数组,容易内存泄漏)。
五、常见坑点
坑1:dataset的值永远是字符串
canvas.dataset.page = 5
console.log(typeof canvas.dataset.page) // string "5"
// 如果需要数字,必须手动转换
const pageNum = Number(canvas.dataset.page)
坑2:同名互不干扰,极易混淆
// Vue data变量
this.page = 1
// DOM dataset属性
canvas.dataset.page = 2
this.page 和 canvas.dataset.page 完全独立,互不覆盖。写代码时不要当成同一个变量。
坑3:修改dataset,watch不会触发
// 修改dataset,Vue完全感知不到,watch不执行
canvas.dataset.page = 3
dataset没有响应式,不能靠修改dataset驱动页面更新。
坑4:不要把DOM专属信息放到data数组维护
不推荐写法:
data() {
return {
pageHeightMap: { 1: 841, 2: 595 }
}
}
每次新增/删除canvas,都需要手动增删pageHeightMap。canvas销毁如果忘记清理数组,造成内存泄漏。页面高度属于canvas自身信息,放到dataset更合理。
六、一句话总结
data-* / dataset:贴在HTML标签上的标签,用来标记DOM,原生能力,无响应式; Vue data变量:保存在JS内存里的业务状态,Vue提供响应式,用来驱动组件逻辑和界面渲染。
在PDF canvas场景:canvas属于哪个页码,这是画布本身的标签 → dataset;PDF全局缩放,是整个预览组件的业务状态 → Vue data。