vue-worker把復雜的web worker封裝起來,提供一套非常簡明的api接口,使用的時候可以說像不接觸worker一樣方便。那么具體怎么使用呢?
安裝
npm i -S vue-worker
注冊
import Vue from 'vue'import VueWorker from 'vue-worker'import App from 'App.vue'
Vue.use(VueWorker)
new Vue({
el: '#app',
render: h => h(App) })
注冊之后,你可以像this.$store一樣使用this.$worker
使用
export default {
name: 'worker-test', data() {
return {
worker: null,
}
},
mounted() {
// 通過this.$worker.run這個方法,跑起一個worker,
// worker是在另外的線程里面跑的,所以可以在run的第一個參數函數里面執行一個非常大計算的操作
// run方法像Promise一樣提供.then和.catch,then的參數就是run第一個參數函數的返回值
this.worker = this.$worker.run(n => n + 10, [2])
.then(res => console.log(res))
.catch(e => console.log(e)) }, destroyed() {
// 通過賦值null的方式,釋放掉worker引用,這樣就可以關閉worker,這是作者在github上說的
this.worker = null
},
}
API介紹
下面來詳細介紹一下vue-worker的幾個api,也就是方法method。
.run(fun, [...args])
上面已經看到了這個方法,而且也有注釋說明。注意第二個參數是一個數組,數組的個數和第一個參數fun的形參個數是一樣的。
你可能會有一個疑問:直接在fun函數體內使用當前變量不就好了么?js本身的全局變量特性在這里不能用?這是因為worker是在另外一個線程中運行,跟當前頁面內的js腳本不是在同一個線程,不共享內存空間,所以直接在fun函數體里面使用另外一個線程的變量是找不到的,所以要通過函數參數的形式進行傳遞。而傳遞的實質,是使用了worker的postMessage方法,把第二個參數當做postMessage的內容,具體你可以閱讀這里的源碼。
run是一次性的,跑完這次,worker線程就會被關掉。想要持久化worker,可以使用下面的create來創建。
.create([...actions])
這個方法讓你創建一個worker對象(注意不是worker實例,你無法通過該對象直接操作worker,這個實例僅僅是一個js object,提供了幾個屬性接口)。
actions是一個數組,數組的每個元素是一個含有兩個屬性的對象:
export default {
name: 'worker-test',
data() {
return {
worker: null,
}
},
created() {
this.worker = this.$worker.create([
{
message: 'pull-data',
func(data) {
data.forEach(...)
return data
},
},
{
message: 'run-task',
func(id) {
//...
},
}
])
},
mounted() {
let data = ...
this.worker.postMessage('pull-data', [data])
.then(res => console.log(res))
},
destroyed() {
this.worker = null
},
}
實際上.run方法是create方法和postMessage方法的合體,一次性把兩個方法的事都做了。
.postMessage(messageid, [...args])
這個在上面的代碼里面已經演示了。它不是this.$worker的方法,而是通過this.$worker.create之后得到的object的一個方法。使用這個方法跟worker原生的方法很像,當然,這里的messageid就是上面actions數組里面的某個對象的message字段對應的那個。而args就是你要傳遞的數據。
你可能又會問了,這里的[..args]是一個參數,還是說里面的元素才是參數。其實很簡單,[...args]被用作了.apply的第二個參數:func.apply(null, [...args]),所以,...args對應的就是func的參數。
.postAll([...args])
這里的postAll和上面的postMessage一樣,是create之后的那個object的一個方法,而不是this.$worker的,所以使用的時候,也只能用在create之后。
它的參數是一個數組,但是這個數組的元素有三種形式,一種是不傳,一種是string:messageid,另一種是{message, [...func_args]}。其實都很好理解。
不傳
代表所有的actions都執行一次postMessage。
[string:messageid]
代表對應的messageid的那個action被執行postMessage。
[{message, [...func_args]}]
給指定的messageid傳參數。
export default {
name: 'worker-test',
data() {
return {
worker: null,
}
},
created() {
this.worker = this.$worker.create([
{
message: 'pull-data',
func(data) {
data.forEach(...)
return data
},
},
{
message: 'run-task',
func() {
//...
},
}
])
},
mounted() {
// 1. 不傳
this.worker.postAll().then([res1, res2] => {})
// 2. 字符串形式
let data = ...
this.worker.postAll(['run-task']).then([res] => {}) // 僅'run-task'被postMessage
// 3. 對象形式(混合形式)
this.worker.postAll([
'run-task',
{
message: 'pull-data',
args: [data],
},
]).then([res1, res2] => {})
},
destroyed() {
this.worker = null
},
}
比較難把握的就是,這里所有的傳入都要采用數組的形式,理解上需要稍微思考下。
.register(action || [...actions])
同理,也是在<worker>的object對象上的方法。當你使用create之后,發現你的worker任務不夠用,要追加一個action或多個,那么可以使用register來追加。action(s)和create是一模一樣的。
.unregister(message || [...messages])
和register有點像,意思是當你某一個任務不想要了,可以通過unregister來取消這個任務。參數和register不一樣,直接使用messageid作為參數即可。
export default {
name: 'worker-test',
data() {
return {
worker: null,
}
},
created() {
this.worker = this.$worker.create([
{
message: 'pull-data',
func(data) {
data.forEach(...)
return data
},
},
{
message: 'run-task',
func() {
//...
},
}
])
},
mounted() {
// 1. 不傳
this.worker.postAll().then([res1, res2] => {})
// 2. 字符串形式
let data = ...
this.worker.postAll(['run-task']).then([res] => {}) // 僅'run-task'被postMessage
// 3. 對象形式(混合形式)
this.worker.postAll([
'run-task',
{
message: 'pull-data',
args: [data],
},
]).then([res1, res2] => {
// 注意,這里then里面執行的是在主js線程里面執行的,所以可以直接用this.worker
this.worker.unregister('run-task')
// 當你注銷掉了,那么下回你在post到run-task這個任務消息時,就啥都不會發生了
})
},
destroyed() {
this.worker = null
},
}
關閉worker
在最前面的代碼里面已經提到了,插件的作者指出,你是沒辦法拿到worker原始實例的,所以也就無法調用worker.terminate()或者在worker線程內部執行self.close()來關閉worker。create方法創建的不是worker實例,所以它內部有,但是沒有暴露出來。所以插件沒有關閉worker的方法,你直接把worker對象釋放掉即可。我翻閱了源碼,發現它只在調用run方法時才使用close,執行完run之后worker會被close,但是如果你使用create創建的worker,是不會被close的它會一直存在,直到你關閉瀏覽器。
原理
web worker是通過一個瀏覽器提供的Worker對象來創建的,創建的時候要傳入指定的javascript文件作為worker線程的執行腳本。worker線程內的腳本有一些限制,比如只能拿到window.navigator的信息,不能拿到完整的window對象。重點是,這里我們沒有提供一個js文件傳入worker線程,vue-worker是怎么做到的呢?它利用了Blob來創建一個可執行的二進制上下文,在通過這個上下文來調用我們傳入的function,就好像在內存中虛擬了一個內容是我們傳入的function的js文件一樣。具體的源碼可以看這里

