行业知识
web worker性能优化,vxe-table提高表格列拖拽和列宽改变等操作的性能,具体实现详解
感兴趣的可以去MDN官网,查看Web Worker API,Web开发技术-Web API接口参考-Web Worker API
Web Worker API
1、 Web Worker 使得在一个独立于 Web 应用程序主执行线程的后台线程中运行脚本操作成为可能。这样做的好处是可以在独立线程中执行费时的处理任务,使主线程(通常是 UI 线程)的运行不会被阻塞/放慢。
2、Worker 是一个使用构造函数创建的对象(例如 Worker()),它运行一个具名 JavaScript 文件——该文件包含将在 worker 线程中运行的代码。
3、可以在 worker 线程中运行任何你喜欢的代码,有一些例外:你不能直接在 worker 线程中操作 DOM 元素,或使用 window 对象中的某些方法和属性
4、数据通过消息系统在 worker 和主线程之间发送——双方都使用 postMessage() 方法发送消息,并通过 onmessage 事件处理程序响应消息(消息包含在 message 事件的 data 属性中)。数据是复制的,而不是共享的。
以简单的方式理解web work:
1、创建 Worker:在 JavaScript 中,使用 new Worker() 函数创建一个 Web Worker。需要传递一个 JavaScript 文件的 URL 作为参数,该文件将被用作 Worker 的脚本
2、启动 Worker:调用 worker.postMessage() 方法向 Worker 发送消息。可以通过该方法传递任意类型的数据给 Worker。Worker 可以通过 onmessage 事件监听来接收这些消息
3、Worker 执行:Worker 在后台线程中执行脚本。Worker 的代码在独立的全局作用域中运行,并且无法访问 DOM 和其他浏览器 API,因此它不会干扰主线程的运行
4、处理消息:Worker 可以通过 onmessage 事件监听来接收主线程发送的消息。通过 event.data 属性可以获取到接收到的数据。Worker 可以在接收到消息后执行相应的操作,并使用 postMessage() 方法向主线程发送处理结果。
5、结束 Worker:当不再需要 Worker 时,可以调用 worker.terminate() 方法来终止 Worker 的执行
//主线程var worker = new Worker('work.js')worker.onmessage = e=>{console.log(e.data)} //Worker线程//做一些耗时的操作self.postMessage('worker'+result) 关于self:
在 Web Worker 中,self 是一个指向 Worker 自身的全局对象。可以将 self 用作替代 this 关键字来引用当前 Worker 线程的全局对象。通过 self 对象,Worker 可以访问一些全局属性和方法,以及与主线程之间的通信接口。如:self.location、self.close()、self.postMessage()
1、主线程—表格列宽改变
resizableChange({ column }) { const copyColumns = JSON.stringify(this.newColumns) const columnData = JSON.stringify(column) return new Promise((resolve, reject) => { //1、主线程实例化了一个新的 Worker 对象,并指定了 Worker 脚本文件的 URL 为 '/worker.js' const worker = new Worker('/worker.js') //2、主线程设置了 Worker 对象的 onmessage 和 onerror 事件监听器,分别用于接收来自 Worker 线程的消息和处理错误 worker.onmessage = e => { const { result } = e.data resolve(result) //当不再需要 Worker 时,可以调用 worker.terminate() 方法来终止 Worker 的执行 worker.terminate() } worker.onerror = e => { reject(e) //当不再需要 Worker 时,可以调用 worker.terminate() 方法来终止 Worker 的执行 worker.terminate() } setTimeout(() => { reject(new Error('worker请求超时!')) //当不再需要 Worker 时,可以调用 worker.terminate() 方法来终止 Worker 的执行 worker.terminate() }, 3000) //通过 worker.postMessage() 向 Worker 发送了一个包含 method、copyColumns 和 columnData 的消息 worker.postMessage({ method: 'ClassAResizableChange', copyColumns: copyColumns, column: columnData }) }) .then(result => { this.newColumns = result setTimeout(() => { this.$nextTick(() => { this.$refs.vxeTable.scrollTo(this.scrollLeft) }) }, 500) }) .catch(error => { console.error(error) }) }, 2、主线程—表格列拖拽
mounted() { this.$nextTick(() => { this.columnDrop() }) }, updated() { this.$nextTick(() => { this.columnDrop() }) }, columnDrop() { const wrapperTr = document.querySelector('.body--wrapper>.vxe-table--header .vxe-header--row') this.sortable = Sortable.create(wrapperTr, { animation: 220, delay: 0, filter: '.vxe-header--column .vxe-cell', draggable: '.vxe-header--column', easing: 'cubic-bezier(1, 0, 0, 1)', onStart: () => {}, onEnd: evt => { const { oldIndex, newIndex } = evt const empty = 2 const copyColumns = JSON.stringify(this.newColumns) return new Promise((resolve, reject) => { //1、主线程实例化了一个新的 Worker 对象,并指定了 Worker 脚本文件的 URL 为 '/worker.js' const worker = new Worker('/worker.js') //2、主线程设置了 Worker 对象的 onmessage 和 onerror 事件监听器,分别用于接收来自 Worker 线程的消息和处理错误 worker.onmessage = e => { const { result } = e.data resolve(result) //当不再需要 Worker 时,可以调用 worker.terminate() 方法来终止 Worker 的执行 worker.terminate() } worker.onerror = e => { reject(e) //当不再需要 Worker 时,可以调用 worker.terminate() 方法来终止 Worker 的执行 worker.terminate() } setTimeout(() => { reject(new Error('worker请求超时!')) //当不再需要 Worker 时,可以调用 worker.terminate() 方法来终止 Worker 的执行 worker.terminate() }, 3000) //通过 worker.postMessage() 向 Worker 发送了一个包含 method、copyColumns 和 oldIndex, newIndex, empty 的消息 worker.postMessage({ method: 'ClassAColumnDrop', copyColumns, oldIndex, newIndex, empty }) }) .then(result => { this.newColumns = result const data = { styleJson: this.newColumns, tableId: this.vxeTableId, } this.savStyle(data).then(res => { if (this.scrollLeft === undefined) { return } setTimeout(() => { this.$nextTick(() => { this.$refs.vxeTable.scrollTo(this.scrollLeft) }) }, 500) }) }) .catch(error => { console.error(error) }) }, }) }, 3、worker线程
新建work.js,放在public或static等静态文件夹下,public/work.js
关于work.js为什么要放在public或static静态文件夹下:
worker.js 脚本文件需要被浏览器访问到,并且在主线程中通过 new Worker() 方法来创建一个新的 Worker 线程。因此,需要将 worker.js 放置于可以被浏览器访问到的位置。
在 Vue.js 项目中,public 和 static 文件夹都属于静态资源目录,这些文件夹下的内容不会被 webpack
处理,而是直接复制到输出目录中。因此,将 worker.js 放置于 public 或 static
文件夹下,可以让浏览器能够直接访问到这个文件,并在主线程中使用 new Worker(‘/worker.js’) 来创建一个新的
Worker 实例。
需要注意的是,如果将 worker.js 放置于其他目录下,可能会导致浏览器无法访问到该文件,从而无法创建 Worker
线程。同时,如果放置的位置不当,还可能影响应用程序的性能或安全性。因此,在将 worker.js 放置于 public 或 static
文件夹下时,需要注意设置正确的访问路径和访问权限
//使用 self.onmessage 监听主线程发送的消息,并根据消息中的 method 执行不同的处理函数。self.onmessage = function (event) { const { method, copyColumns } = event.data let result = [] const parsedColumns = JSON.parse(copyColumns) switch (method) { case 'ClassAResizableChange': result = parseClassAResizableChange(parsedColumns, event.data.column) break case 'ClassAColumnDrop': result = parseClassAColumnDrop(parsedColumns, event.data.oldIndex, event.data.newIndex, event.data.empty) break } //Worker 线程处理完毕后,通过 self.postMessage() 将处理结果发送回主线程 self.postMessage({ result })} function parseClassAResizableChange(columns, column) { const parsedColumn = JSON.parse(column) const resizableDataMap = new Map() resizableDataMap.set(parsedColumn.field, { resizeWidth: parsedColumn.resizeWidth, }) for (let i = 0; i function parseClassAColumnDrop(columns, oldIndex, newIndex, empty) { const oldItem = columns[oldIndex - empty] columns.splice(oldIndex - empty, 1) columns.splice(newIndex - empty, 0, oldItem) columns.sort((a, b) => b.freeze - a.freeze) return columns}