在上一篇文章 WebAssembly應用到前端工程(上)—— webassembly模塊的編寫中,完成了@ne_fe/gis模塊的編寫與發佈。然而webassembly在當前以webpack4爲主要編譯工具的實際工程應用中依然存在問題。
儘管webpack4新增了對wasm文件的編譯支持,在wasm模塊編寫完成以後將其與webpack結合的過程當中發現,wasm模塊沒法被正確加載。在對@ne_fe/gis編譯輸出文件的檢查以後,有了新的解決方案 wasm-module-webpack-plugin。前端
直接在前端工程中引入@ne_fe/gis並使用的話,控制檯會輸出錯誤信息node
index.js?558c:1 GET http://localhost:8080/gps.wasm 404 (Not Found)
Uncaught (in promise) TypeError: Incorrect response MIME type. Expected 'application/wasm'.
查看所引用的@ne_fe/gis/dist/index.js後發現這樣一句話webpack
var Pn="undefined"!=typeof location?location.pathname.split("/"):[];Pn.pop(),(Cn?WebAssembly.instantiateStreaming(fetch(Pn.join("/")+"/gps.wasm"),o):fetch(Pn.join("/")+"/gps.wasm").then(e=>e.arrayBuffer()).then(e=>WebAssembly.instantiate(e,o)))
出錯緣由時因爲fetch直接從根路徑下直接獲取wasm文件,但文件並無生成或移動,webpack並不能處理fetch所加載的文件,最終致使wasm加載失敗。git
webpack不能處理js的fetch語句,致使了上面問題的出現,那麼只有一種方法可以處理fetch語句,那就是babel。
下面來編寫一個babel插件處理fetch加載wasm的狀況github
// babel-plugin.js module.exports = function() { return { visitor: { CallExpression(path, state) { if(path.node.callee.name === 'fetch'){ const argument = JSON.parse(JSON.stringify(path.node.arguments[0])); for (const i in argument.right) { if (i === 'value' && argument.right[i].endsWith('.wasm')) { console.log('argument.right[ i ]', argument.right[ i ], 'state.file.opts.filename', state.file.opts.filename); } } } }, } } };
在webpack中使用web
// webpack.config.js const path = require('path'); const BabelPlugin = require('./babel-plugin'); module.exports = { module: { rules: [ ... { test: /\.js$/, loader: 'babel-loader', include: [ path.join(process.cwd(), './node_modules/@ne_fe/gis') ], options: { plugins: [ BabelPlugin ], }, }, ... ], }, plugins: [ ... ], };
啓動webpack進行編譯,控制檯輸出npm
argument.right[ i ] /gps.wasm state.file.opts.filename C:\dir\test\node_modules\@ne_fe\gis\dist\index.js
最終得到了fetch所加載的wasm文件名與fetch語句所在文件。segmentfault
在上一步中獲取到了fetch語句加載的wasm文件名與fetch語句所在文件。
爲了將wasm文件輸出到webpack編譯結果中,須要添加webpack插件。經修改以後,整個結合wasm與webpack的插件以下promise
// event-listener.js const EventEmitter = require('events').EventEmitter; class Events extends EventEmitter { constructor(prop) { super(prop); this.data = {}; } } const events = new Events(); events.on('wasm', data => { if (!events.data[data.wasmRefFileName]) { events.data[data.wasmRefFileName] = {}; events.data[data.wasmRefFileName][data.wasmRefPath] = data.wasmDir; } else { if (!events.data[data.wasmRefFileName][data.wasmRefPath]) { events.data[data.wasmRefFileName][data.wasmRefPath] = data.wasmDir; } } }); module.exports = events;
// babel-plugin.js const eventEmitter = require('./event-listener'); const pathInternal = require('path'); module.exports = function() { return { visitor: { CallExpression(path, state) { if(path.node.callee.name === 'fetch'){ const argument = JSON.parse(JSON.stringify(path.node.arguments[0])); for (const i in argument.right) { if (i === 'value' && argument.right[i].endsWith('.wasm')) { eventEmitter.emit('wasm', { wasmRefPath: argument.right[i], wasmRefFileName: state.file.opts.filename, wasmDir: pathInternal.parse(state.file.opts.filename).dir, }); } } } }, } } };
// webpack-plugin const eventEmitter = require('./event-listener'); const path = require('path'); class WasmPlugin { apply(compiler) { compiler.plugin('emit', function(compilation, callback) { for (const i in eventEmitter.data) { for (const j in eventEmitter.data[i]) { const filePath = path.join(eventEmitter.data[ i ][ j ], '.' + j); const content = compiler.inputFileSystem._readFileSync(filePath); const stat = compiler.inputFileSystem._statSync(filePath); const wasmRefPath = j; const wasmName = wasmRefPath.substring(1, wasmRefPath.length); compilation.assets[wasmName] = { size() { return stat.size; }, source() { return content; }, }; } } callback(); }); } } module.exports = WasmPlugin;
event-listener的做用是爲了保存babel-plugin中獲取到的wasm相關信息而後在webpack插件執行的時候使用,webpack-plugin將獲取到的wasm文件輸出到正確路徑。瀏覽器
涉及到的技術主要爲commonjs模塊機制、babel插件編寫與webpack插件編寫。
能夠參考wasm-module-webpack-plugin或@ne_fe/gis,歡迎start。
儘管webassembly的出現對前端開發高性能瀏覽器應用有了重大的做用,webpack4也新增了對webassembly的支持,可是目前以webpack編譯爲主的前端工程對webassembly的支持依然不友好,開發難度不小,但願之後有更好的解決方案。
上一篇:WebAssembly應用到前端工程(上)—— webassembly模塊的編寫