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、主线程—表格列宽改变

      <vxe-table      
        border
        :align="'center'"      
        resizable     
        @resizable-change="resizableChange"
      >
  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 < columns.length; i++) {
    const column = columns[i]
    const resizableColumn = resizableDataMap.get(column.field)
    if (resizableColumn) {
      column.width = resizableColumn.resizeWidth
    }
  }
  return columns
}
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
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值