前言

笔者最近一直在使用 electron 开发一个可视化工具 Nowa,里面的技术栈是

  • webpack2

  • babili

  • react

  • electron

  • electron-builder

使用过 electron 的人都知道,打出来的包是很大的,因为electron 内置了 Node & Chromium, 所以啥都还没干,打出来的应用安装包就有几十兆了。

无法在 electron 上做文章,那么只好在 webpack 打包程序代码的过程中捣鼓了。以前打包应用的时候,程序里会有 node_modules 文件夹。这次任务就是干掉这个文件夹。

目录结构

ele0

大家会发现这里居然有两个 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这几兆的话其实无所谓。

为了减小安装包体积,笔者真是无所不用其极。

如果大家有更好的打包方式,请评论回复。

参考

本文固定链接: http://www.js-code.com/node-js/node-js_36328.html