内容简介:在上一篇文章尽管webpack4新增了对wasm文件的编译支持,在wasm模块编写完成之后将其与webpack结合的过程中发现,wasm模块无法被正确加载。在对@ne_fe/gis编译输出文件的检查之后,有了新的解决方案直接在前端工程中引入@ne_fe/gis并使用的话,控制台会输出错误信息
在上一篇文章 WebAssembly应用到前端工程(上)—— webassembly模块的编写 中,完成了@ne_fe/gis模块的编写与发布。然而webassembly在当前以webpack4为主要编译 工具 的实际工程应用中依然存在问题。
尽管webpack4新增了对wasm文件的编译支持,在wasm模块编写完成之后将其与webpack结合的过程中发现,wasm模块无法被正确加载。在对@ne_fe/gis编译输出文件的检查之后,有了新的解决方案 wasm-module-webpack-plugin 。
出现的问题
直接在前端工程中引入@ne_fe/gis并使用的话,控制台会输出错误信息
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后发现这样一句话
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加载失败。
babel
webpack不能处理js的fetch语句,导致了上面问题的出现,那么只有一种方法能够处理fetch语句,那就是babel。
下面来编写一个babel插件处理fetch加载wasm的情况
// 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中使用
// 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进行编译,控制台输出
argument.right[ i ] /gps.wasm state.file.opts.filename C:\dir\test\node_modules\@ne_fe\gis\dist\index.js
最终获得了fetch所加载的wasm文件名与fetch语句所在文件。
webpack
在上一步中获取到了fetch语句加载的wasm文件名与fetch语句所在文件。
为了将wasm文件输出到webpack编译结果中,需要添加webpack插件。经修改之后,整个结合wasm与webpack的插件如下
// 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模块的编写以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。