前言
笔者最近一直在使用 electron 开发一个可视化工具 Nowa,里面的技术栈是
webpack2
babili
react
electron
electron-builder
使用过 electron 的人都知道,打出来的包是很大的,因为electron 内置了 Node & Chromium, 所以啥都还没干,打出来的应用安装包就有几十兆了。
无法在 electron 上做文章,那么只好在 webpack 打包程序代码的过程中捣鼓了。以前打包应用的时候,程序里会有 node_modules
文件夹。这次任务就是干掉这个文件夹。
目录结构
大家会发现这里居然有两个 package.json
!! 其实主要是因为 electron-builder 的 Two package.json Structure 的设置。把打包需要的依赖与开发依赖完全分开,纯粹打包你想要的东西,所以设置了 app 文件夹放这些。
electron-builder 只会对 app 文件夹进行打包,换句话说,这里面有多少东西就会打包多少内容。
所以我们可以想法设法减少不必要的东西。比如这里没有任何依赖, node_modules
是空的!
打包过程干货
结合 electron 的特殊环境,webpack 编译过程有很多文章可以做。
1、 考虑 electron 的 Chromium & Node 版本
在 webpack 打包的时候,我们抛弃低版本浏览器的那些兼容,因为我们只用 Chromium,所以不必要的会增加编译输出的 preset 就不要了,比如 loose
,和一些 shim。
而且高版本的 node 已经支持一些 es6 的属性了,我们真的需要降级到 es5 么?当然不是的。
A、 修改 babel 配置
推荐使用 babel-preset-env
设置。这个 preset 主要可以设置项目当前的环境,适时进行引入新特性,如果对其没有其他配置,就相当于使用了 babel-preset-latest
。
// .babelrc
{
"presets": [
["env", {
"targets": {
"electron": 1.6,
},
"loose": false,
"modules": false,
"useBuiltIns": true
}],
"stage-2",
"react"
],
"plugins": [
]
}
demo 里面设置了 targets
是 electron 1.6 版本,如果不嫌麻烦的话,可以根据当前的 electron 的 node 和 chrome 版本进行分别设置。
比如:
"targets": {
"node": 7.4,
"chrome": 56
}
B、 更换压缩方案
通常我们以前的打包方案是这样子的:
ES2015+ code -> Babel -> Babili/Uglify -> Minified ES5 Code
现在,我们可以不用降级这么多,使用一个工具babili(不要看成bilibili),它是 babel 的压缩工具。
babili 的打包方案是这样的:
ES2015+ code -> Babili -> Minified ES2015+ Code
它不会编译成 es5 的版本,而是对当前版本进行压缩。这简直就是 electron 的绝配啊。
为了能在 webpack 中使用,我们需要引入一个插件 babili-webpack-plugin
。 这个是使用于生产环境的,所以我们 webpack 生产环境配置中可以这样引入:
// webpack.prod.config.js
const BabiliPlugin = require('babili-webpack-plugin');
module.exports = {
...,
plugins: [
...,
new BabiliPlugin()
]
}
2、 对 main 端代码进行打包
通常我们可能不对 main 端进行打包,我之前做的项目就没打包,main 端的依赖全部都合入安装包去了。如果 main 端依赖很大的话,那真是灾难。
实际上 main 端也能进行打包,与 renderer 端一样,输出到 app
目录,这样 node_modules 就空了。
然而,如果有引入第三方的 native node 模块的话,笔者没有尝试过是否能行得通,猜测很可能还是要放到 node_modules
里面保险。有尝试过的看官请留言。
对 main & renderer 端打包代码的时候,要注意设置 webpack 的 target
字段。
// renderer.webpack.config
{
target: 'electron-renderer'
}
// main.webpack.config
{
target: 'electron-main'
}
webpack 的 target 默认是 web
。如果你没有进行更改的话,renderer 端就无法使用 node 模块了。
main 端注意事项
对 main 端打包的条件是有些条件的。
如果说您使用了
remote.require(xxx)
的方式在 renderer 端引入了 main 端需要的模块,那么您需要在 app 目录下放该模块。如果在 main 端调用了
child_process
的方法去执行放在 app 文件夹里面的js文件,而这些脚本依赖了非 node 原生模块的时候,请把这些模块安装到 app 里面的 node_modules 里面。
main 端遇到的问题
main 端打包容易碰到如下问题:
依赖中出现 #!/usr/bin/env node
这样的语句或者包含了 *.node
的脚本,这个使用您需要使用一些特殊的 loader 进行处理。
{
test: /\.js$/,
include: /node_modules/,
loader: 'shebang-loader'
},
{
test: /\.node$/,
include: /node_modules/,
loader: 'node-loader'
}
3、合适的renderer 端构建方案
笔者在renderer 端构建采用了 DLL(动态链接库)方案, 也是 webpack 官方比较推荐的方案。它可以快速的提升构建速度,特别是明显的提升第一次启动的速度。在生产环境就不要使用它了,因为 dll 文件的体积比较大。
css 要使用 ExtractTextPlugin
与 js 代码分离开来,不要合并,不要合并,因为文件体积同样比较大。
4、 注意 electron 版本号 和 electron-builder 版本号
使用新的 electron 版本打包出来的安装包会比旧版本大几兆,其实很容易理解。
使用不同版本的 electron-builder 打包出来的也不同。大于 13.* 版本的打包出来的安装包同样大几兆。
几兆到底是几兆呢? demo 的例子实测是 3~5 MB。如果大家不care这几兆的话其实无所谓。
为了减小安装包体积,笔者真是无所不用其极。
如果大家有更好的打包方式,请评论回复。
参考
https://github.com/chentsulin/electron-react-boilerplatehttps://github.com/chentsulin/electron-react-boilerplate
http://babeljs.io/docs/plugins/preset-env/
https://babeljs.io/blog/2016/08/30/babili