学习webpack:从最简单的webpack说起

看了很多webpack的教程,大多是上来就讲一堆配置,一堆插件的使用。这种文章看起来有点类似于官方文档,或者新华字典。我想回归初心,换一种方式,基于实际使用出发,一步一步介绍webpack。

从一个最简单的例子开始,这个例子只为了描述最简单的webpack功能,实现一个最简单的需求:在浏览器上显示一段文字。

传统方式

首先我们有一个index.html,这个html中只是引入了index.jsa.jsb.js

<!Doctype html>
<html>
  <head>
      <meta name="charset" content="utf-8">
      <title>webpack</title>
  </head>
  <body>
    <script type="text/javascript" src="./a.js"></script>
    <script type="text/javascript" src="./b.js"></script>
    <script type="text/javascript" src="./index.js"></script>
  </body>
</html>

a.js中声明了变量var a = 1;,在b.js中声明了变量var b = 2;。由于这种方式下,作用域是共享的,a和b相当于都是挂载在window上,所以在index.js中可以直接访问到a和b的值。

index.js中,我们创建一个DOM并挂载到HTML上:

var dom = document.createElement('p');
dom.innerHTML = `a=${a}; b=${b}`;
document.body.appendChild(dom);

最后,在浏览器上就会显示a=1; b=2

这种使用方式虽然简单,但是会有潜在的问题:

  1. 浏览器在加载时,会先加载html文件,然后根据HTML里面的script标签去依次加载每个js文件。这样对于每个js文件,浏览器都会向服务器发送一次请求。如果引入的文件数很多,那么发送的请求次数就会过多,对服务器造成一定的压力。而且,单个文件可能并不大,相对于浏览器对每次请求都需要建立链接、断开链接的成本来说很不划算。要解决这个问题,就需要打包,将多个js文件打包成单个。
  2. 不能严格保证js文件的加载顺序,比如index.js加载完了,但它所依赖的a.js还没加载。当然,这个问题可以用require.js解决。
  3. 不同script标签引入的js代码,会污染全局作用域,比如a.js中声明的a就直接挂载到了window上,其他文件中如果再声明a变量,就会有冲突。这个问题可以用立即执行函数的方式解决。

虽然有办法解决,但总感觉不是那么完美,治标而不治本。回归到js的运行环境上,这都是因为js代码需要在浏览器中运行。如果是在node环境中运行,那可以直接使用CommonJS规范,每个文件就是一个模块,各个模块之间的作用域是独立的,通过require可以解决模块依赖和加载问题。甚至还可以在node中利用ES6的模块机制,也同样可以解决这个问题。显然,这种写代码的方式要简单很多,但只能在node环境下。而webpack的一个重要作用,就是可以让你这种方式写出来的代码能在浏览器中运行。

webpack方式

a.jsb.js分别作为2个模块,通过ES6的export导出变量a和b,在index.js中通过import引入:

//a.js
export var a = 1;

//b.js
export var b = 2;

//index.js
import {a} from './a.js';
import {b} from './b.js';
var dom = document.createElement('p');
dom.innerHTML = `a=${a}; b=${b}`;
document.body.appendChild(dom);

最后,我们希望用webpack,将其打包成一个单独的文件,直接挂载到index.html中。从零开始,安装webpack。

  1. 新建一个文件夹,在这个文件夹中npm init,初始化。
  2. 安装webpackwebpack-cli,运行npm install webpack webpack-cli -Dwebpack-cli为webpack提供了命令行工具,让我们可以直接在命令行中使用webpack
  3. 建立src文件夹,将a.js,b.jsindex.js存放在src文件夹下。这个文件夹存放的是原始文件
  4. 建立dist文件夹,用来存放编译后的文件,也就是打包后的单个文件
  5. index.html放到src文件夹下,这时引用的不是index.js,而是打包后的位于dist目录下的bundle.js文件

    <script type="text/javascript" src="../dist/bundle.js"></script>
  6. 配置webpack。webpack的配置就是在根目录下直接新建一个webpack.config.js,配置如下:

    const path = require('path');
    
    module.exports = {
      entry: './src/index.js',
      output: {
        path: path.join(__dirname, 'dist'),
        filename: 'bundle.js'
      }
    }
    • entry是打包的入口文件,也就是告诉webpack打包哪个文件,这里指定的是index.js。由于在index.jsimporta.jsb.js,所以webpack在打包时会同时将a.jsb.js引入。从这里可以看到,只用告诉webpack入口文件即可,所有的依赖文件webpack会自己寻找和解决。
    • output是告诉webpack,打包后的文件放哪里。path指定了打包后的文件路径,filename指定了打包后的文件名。综合起来,打包后的文件就是dist目录下的bundle.js
  7. package.json中的script下做个配置:

    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "webpack" 
      },
  8. 直接运行npm run build,node就会自动执行webpack,这时在dist目录下就可以看到生成的js文件(只有一个),将index.html放到浏览器中,就会看到最后显示的效果。

不止于此

从上面的例子看到,使用了webpack之后,我们解决了传统方式里面遇到的各种问题。当然,webpack能做的,远不止这些。比如在写代码时,可能还会有这些需求:

  • 代码转换:将 TypeScript 编译成JavaScript、将 SCSS 编译成 CSS等。
  • 文件优化:压缩JavaScript、CSS、HTML 代码,压缩合并图片等。
  • 代码分割:提取多个页面的公共代码,提取首屏不需要执行部分代码让其异步记在。
  • 模块合并:在采用模块化的项目里会有很多个模块和文件,需要通过构建功能将模块分类合并成一个文件。
  • 自动刷新:监听本地源代码变化,自动重新构建、刷新浏览器。
  • 代码校验:在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过。
  • 自动发布:更新代码后,自动构建出线上发布代码并传输给发布系统。

我们需要一个工具来帮我们解决这些问题,完成整个构建流程。使用构建工具的目的,是为了让我们写代码更加方便,可以用更新的特性而不用过多关心浏览器的兼容问题;让我们可以省去很多机械重复性的工作,比如修改代码后浏览器会自动刷新,提高我们的开发效率。

当然,在webpack之前,已经有很多优秀的构建工具了,比如grunt、gulp等。称webpack是当下最流行的构建工具毫不为过。webpack强大不仅在其本身,还在于很多基于webpack的插件,提供了一个强大的生态系统。webpack能做的事情还有很多,将在后面一步步继续学习。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章