一个普通的写网页的人如何过渡到ES6 (二) - Browserify 打包 - Tricky's Blog

一个普通的写网页的人如何过渡到ES6 (二) - Browserify 打包

上一节中,我们已经能够稍微的使用 es6 的语法来写一个很简单的页面了,知道了在 es6 的语法中如何去引入一个 jquery.js

其实我们还有个问题没解决,如何引入我们自己写的 .js 文件?

这是我们上一节中的项目目录结构:

TIM图片20171010210333.png

现在我们要做一个新的效果,把输出 "hello world" 改成 "hello" + x,其中的 x 由另外一个单独的文件 b.js 来控制。

新建 b.js

let words = "cat";
export {words};

这个文件,或者说是模块,会输出一个 words 的变量。
接下来修改一下原来的 a.js

import $ from 'jquery';
import {words} from './b.js';

$(function(){
    $('#mybtn').click(function(){
        alert('hello'+words);
    });
});

然后通过 babel+browserify 处理一下 a.js

会发现运行 browserify 的时候报错了,原因是 b.js 中的 export 关键字有问题。

上一节里面,我对 browserify 的理解是,它会帮我干掉那些 require 的方法。

但实际上,它是做了什么呢?怎么达到干掉 require 的效果的呢?我在官方的使用手册里找到了如下原文:

Browserify starts at the entry point files that you give it and searches for any require() calls it finds using static analysis of the source code's abstract syntax tree.

For every require() call with a string in it, browserify resolves those module strings to file paths and then searches those file paths for require() calls recursively until the entire dependency graph is visited.

Each file is concatenated into a single javascript file with a minimal require() definition that maps the statically-resolved names to internal IDs.

大概的意思就是说,无论我们项目代码中写的 require() 逻辑有多复杂,Browserify 都会根据某种算法,把所有 require() 参数里面所写的脚本文件,拼接到一起成为一个独立的脚本文件。

怎么理解呢?其实很好理解,上一节中我们写了一个 a.js,在里面又引入了 jquery.js,它最终生成的 a_es5_killRequire.js 实际上包含了两个脚本的内容,

打开这个文件,你就会发现代码真的非常 “a.js + jquery.js”:

(甚至连 jquery 文件本身的注释都被包含进来了233333)

那为什么不能说是简单的拼接文件,而是说要根据某种算法呢?因为设想一下,如果 a.jsb.js 都各自引入了 jquery.js,那是不是最后生成的文件等于 a.js + jquery.js + b.js + jquery.js 呢?

实际上是不会的,只会生成 a.js + b.js + jquery.js
所以,这种算法能够帮我们避免一些尴尬的情况。

这个根据某种算法拼接文件的过程,大家喜欢称之为 打包
而打包生成的文件,大家喜欢称之为 bundle,很形象。

也就是说,browserify 只关心如何打包文件,它并不关心你的脚本是使用什么语法。

回过头分析一下上面遇到的问题,发现 browserify 在引入 b.js 的时候,没办法识别 es6 语法的 export,所以就报错了。

我头脑里的第一个想法就是把 babel b.js -o b_es5.js ,把 b.js 给先给处理成 es5 语法。

然后修改一下 a.js 中的引用:

import $ from 'jquery';
import {words} from './b_es5.js';

$(function(){
    $('#mybtn').click(function(){
        alert('hello'+words);
    });
});

接下来再对 a.js 进行 babel + browserify ,没报错了!

打开浏览器,点击按钮,效果如图:

好像真的弄懂了 browserifybabel 了有木有,这两个东西各司其职。

但是,如果我还有 c.jsd.jse.js …… 总共 n 个自己写的 js。
那岂不是每一个文件我都得执行一次 babel 命令? n 个文件可就是执行 n 次了。

太麻烦了。这个时候,就得用到 browserifybabel 的私生子 babelify

还是一样先安装: npm install babelify

需要注意的是,babelify 是 browserify 的一款插件。

它的作用就是,在把代码交给 browserify 去打包之前,先把所有代码处理成 es5 的语法。

使用方法如下:

browserify a.js -o a_bundle.js -t [ babelify --presets[env]]

在这个例子中,我把最后编译出来的文件叫做 a_bundle.js,是为了和前面作区分,所以在 index.html 里面也需要作相应的修改:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>try es6</title>
    <script src="a_bundle.js"></script>
</head>
<body>
    <button id="mybtn">click me to hello</button>
</body>
</html>

在浏览器中打开页面,十分成功。一句命令就能解决原来 babel + browserify 的繁杂操作。

需要注意的是上面那句命令的语法,在 -t 参数之后跟了 [ babelify --presets[env]]
这后面的东西和上一节在讲 babel 的时候十分相像,而且还使用了一个叫做 env 的预设。

十分重要的提醒: --presets 和后面的 [env] 之间不能有空格

也就是对于我们来说,以后没必要安装 babel 这个命令,但是需要安装相应的预设,想要成功运行上面的代码就必须安装 babel-preset-env

总结

使用 browserifybabelify 插件可以解决编译 es6 的问题,同时还能打包文件,还无需关心被打包的文件是否已经编译成 es5,再也不需要对每一个文件都额外执行一次 babel

//a 引用 b_es5,b 引用 c_es5
babel a.js -o a_es5.js
babel b.js -o b_es5.js
babel c.js -o c_es5.js
browserify a_es5.js -o a_bundle.js

// 等同于下面代码
//a 引用 b,b 引用 c
browserify a.js -o a_bundle.js -t [ babelify --presets[env]]

  JavaScript ES6