写在开头
- 不为了追寻潮流而学习某个技术, 本人仅做最基础的入门与实践讲解
- 欢迎收藏前端生活社区:
https://qianduan.life
- 想要加入资源群和前端交流群可以看文末
WebAssembly 是什么,可以吃吗?
官网介绍:
- WebAssembly 是由主流浏览器厂商组成的 W3C 社区团体 制定的一个新的规范
- WebAssembly/wasm WebAssembly 或者 wasm 是一个可移植、体积小、加载快并且兼容 Web 的全新格式
webAssembly 的特点
高效
- WebAssembly 有一套完整的语义,实际上 wasm 是体积小且加载快的二进制格式,其目标就是充分发挥硬件能力以达到原生执行效率
安全
- WebAssembly 运行在一个沙箱化的执行环境中,甚至可以在现有的 JavaScript 虚拟机中实现。在 web 环境中,WebAssembly 将会严格遵守同源策略以及浏览器安全策略。
开放
- WebAssembly 设计了一个非常规整的文本格式用来、调试、测试、实验、优化、学习、教学或者编写程序。可以以这种文本格式在 web 页面上查看 wasm 模块的源码。
标准
- WebAssembly 在 web 中被设计成无版本、特性可测试、向后兼容的。WebAssembly 可以被 JavaScript 调用,进入 JavaScript 上下文,也可以像 Web API 一样调用浏览器的功能。当然,WebAssembly 不仅可以运行在浏览器上,也可以运行在非 web 环境下。
正式开始(要凑字数, 理解)
- Node.js 有 C ++ 插件,Addon 模块,还能调用 C# 插件,go 插件, 还能跟他们通信
- 我 show 一段我之前写的代码吧. 用子进程调起 c# 插件, 然后通信.(其实这个是我写的当时 windows 平台的截图插入到自研的文本编辑器中, 桌面软件)
`// Node.js 主进程中调起子进程
await screen_window();
//function screen_window
import {execFile} from 'child_process';
import path from 'path';
import ipcSend from '../main/utils/ipcSender';
function screen_window() {return new Promise((resolve, reject) => {const screen_window = execFile(path.resolve($dirname, '../screenshot/PrintScr.exe'));
screen_window.on('exit', function(code) {if (code === 1) {ipcSend.insertImage();
}
resolve();});
});
}
export default screen_window;`
既然 Node.js 有拓展能力,那么浏览器环境呢?必然也需要
- 当时为了处理一个 speex 格式的音频在 H5 中实现动态播放,我封装了一个 7000 行的库,里面大量的 8 进制 … 泪
- 不过最后帮助到了很多人, 希望对你有用
https://github.com/JinJieTan/speex-in-h5
- 处理音视频的时候,多考虑下各种插件,webAssembly…
什么情况需要考虑到使用webAssembly
?
- 首先给大家一个链接,
https://www.wasm.com.cn/demo/Tanks/
,这是坦克!,Unity 教程
中的一个游戏 导出成 WebAssembly 的游戏. - 很流程,丝滑般顺畅
怎么理解 webAssembly 这个技术?
- 写到这里,还是很多人不理解,到底什么是 webAssembly 啊!
- Node.js 可以直接运行 C# C++ 代码吗?当然不可以,只能调用操作系统能力,或者 + 中间层或者其他方式调用。
- 浏览器可以运行 C++ 代码,rust 吗?当然也不可以(如果可以的话,你告诉我,我把这里改了)
- 那么,webAssembly 模块必然是要被编译成浏览器可以识别的语言,然后被 JS 调用,可以看成 C ++ ADDON 一样的形式吧,我个人理解
在这里,我要强调一件事
- Electron 中,虽然分主进程和渲染进程,但是主进程阻塞,同样会阻塞渲染进程,GC 也是会阻塞的。回收 300MB,需要 1S 还是多久,忘记了,去年做过实验
- 那么浏览器中,这种调用 webAssembly 模块的情况下,理论上如果 webAssembly 模块被阻塞了,那么 JS 主解析线程也是会阻塞的。
可是我在国外网站上看到的内容是说:每个 WebAssembly 线程都在 Web Worker 中运行,相当于跟 JS 主解析线程是分开的,不会阻塞 JS 主线程的解析
- 最近看到又新引入的 SharedArrayBuffer 和原子操作使开发人员能跨多个线程使用共享的内存了。这样以来就能实现更细粒度的并发算法,避免过于硬核引发不适,可以自行跳转
https://hacks.mozilla.org/2017/06/a-crash-course-in-memory-management/
如何编写 webAssembly 模块
- 将下面这段代码复制到浏览器控制台 就可以运行了
WebAssembly.compile(new Uint8Array(`
00 61 73 6d 01 00 00 00 01 0c 02 60 02 7f 7f 01
7f 60 01 7f 01 7f 03 03 02 00 01 07 10 02 03 61
64 64 00 00 06 73 71 75 61 72 65 00 01 0a 13 02
08 00 20 00 20 01 6a 0f 0b 08 00 20 00 20 00 6c
0f 0b`.trim().split(/[\s\r\n]+/g).map(str => parseInt(str, 16))
)).then(module => {const instance = new WebAssembly.Instance(module)
const {add, square} = instance.exports
console.log('2 + 4 =', add(2, 4))
console.log('3^2 =', square(3))
console.log('(2 + 5)^2 =', square(add(2 + 5)))
})``
- 输出结果:
这里应该大家能看出来,webAssembly 模块,其实就是二进制文件
- 你编写的 webAssembly 模块,无论是什么语言,他最终应该是一段二进制文件,然后被前端通过 ajax 获取
- 如何编译:
https://www.wasm.com.cn/getting-started/developers-guide/
如何加载 / 运行 webAssembly 模块?
-
在未来计划中,WebAssembly 模块可以使用 ES6 模块 (使用
<script type="module">
) 加载,WebAssembly 目前只能通过 JavaScript 来加载和编译。基础的加载,只需要 3 步:- 获取 .wasm 二进制文件,将它转换成类型数组或者 ArrayBuffer
- 将二进制数据编译成一个 WebAssembly.Module
- 使用 imports 实例化这个 WebAssembly.Module,获取 exports。
使用 webAssembly 模块示例:
- 首先定义加载 webAssembly 的功能函数:
`/**
* @param {String} path wasm 文件路径
* @param {Object} imports 传递到 wasm 代码中的变量
*/
function loadWebAssembly (path, imports = {}) {return fetch(path)
.then(response => response.arrayBuffer())
.then(buffer => WebAssembly.compile(buffer))
.then(module => {imports.env = imports.env || {}
// 开辟内存空间
imports.env.memoryBase = imports.env.memoryBase || 0
if (!imports.env.memory) {imports.env.memory = new WebAssembly.Memory({ initial: 256})
}
// 创建变量映射表
imports.env.tableBase = imports.env.tableBase || 0
if (!imports.env.table) {
// 在 MVP 版本中 element 只能是 "anyfunc"
imports.env.table = new WebAssembly.Table({initial: 0, element: 'anyfunc'})
}
// 创建 WebAssembly 实例
return new WebAssembly.Instance(module, imports)
})
}`
- 然后外部调用, 传入存放这个 WebAssembly 模块的资源接口
`loadWebAssembly('path/to/math.wasm')
.then(instance => {const { add, square} = instance.exports
// ...
})`
- 只要通过请求获取到了它,然后处理后,就可以获取到 exports 出来的内容了
想要更深入了解的,可以参考下 webAssembly 和 ffmpeg 实现前端转码
- 导出一个入口函数到上层 js
- 传入函数参数来控制 ffmpeg 命令行参数
- 通过虚拟文件系统传入输入文件以及获取输出文件
-
https://zhuanlan.zhihu.com/p/27910351
使用方法:
`self.importScripts('ffmpeg.js');
onmessage = function(e) {console.log('ffmpeg_run', ffmpeg_run);
var files = e.data;
console.log(files);
ffmpeg_run({arguments: ['-i', '/input/' + files[0].name, '-b:v', '64k', '-bufsize', '64k', '-vf', 'showinfo', '-strict', '-2', 'out.mp4'],
files: files,
}, function(results) {console.log('result',results);
self.postMessage(results[0].data, [results[0].data]);
});
}`
- 涉及到转码,压缩这些事情的时候,就要多考虑 webAssembly 了,当然 node.js 本身命令传参调用 ffmpeg 也可以,像图片压缩这种事情,Node.js 也做不好的,就算上了 C ++ 插件也不行,CPU 吃得很,并发稍微上去点,CPU 就打到百分百.