看了很多webpack的教程,大多是上来就讲一堆配置,一堆插件的使用。这种文章看起来有点类似于官方文档,或者新华字典。我想回归初心,换一种方式,基于实际使用出发,一步一步介绍webpack。
从一个最简单的例子开始,这个例子只为了描述最简单的webpack功能,实现一个最简单的需求:在浏览器上显示一段文字。
传统方式
首先我们有一个index.html
,这个html中只是引入了index.js
,a.js
和b.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
。
这种使用方式虽然简单,但是会有潜在的问题:
- 浏览器在加载时,会先加载html文件,然后根据HTML里面的script标签去依次加载每个js文件。这样对于每个js文件,浏览器都会向服务器发送一次请求。如果引入的文件数很多,那么发送的请求次数就会过多,对服务器造成一定的压力。而且,单个文件可能并不大,相对于浏览器对每次请求都需要建立链接、断开链接的成本来说很不划算。要解决这个问题,就需要打包,将多个js文件打包成单个。
- 不能严格保证js文件的加载顺序,比如
index.js
加载完了,但它所依赖的a.js
还没加载。当然,这个问题可以用require.js
解决。 - 不同script标签引入的js代码,会污染全局作用域,比如
a.js
中声明的a就直接挂载到了window上,其他文件中如果再声明a变量,就会有冲突。这个问题可以用立即执行函数的方式解决。
虽然有办法解决,但总感觉不是那么完美,治标而不治本。回归到js的运行环境上,这都是因为js代码需要在浏览器中运行。如果是在node环境中运行,那可以直接使用CommonJS规范,每个文件就是一个模块,各个模块之间的作用域是独立的,通过require可以解决模块依赖和加载问题。甚至还可以在node中利用ES6的模块机制,也同样可以解决这个问题。显然,这种写代码的方式要简单很多,但只能在node环境下。而webpack的一个重要作用,就是可以让你这种方式写出来的代码能在浏览器中运行。
webpack方式
a.js
和b.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。
- 新建一个文件夹,在这个文件夹中
npm init
,初始化。 - 安装
webpack
和webpack-cli
,运行npm install webpack webpack-cli -D
。webpack-cli
为webpack提供了命令行工具,让我们可以直接在命令行中使用webpack - 建立src文件夹,将
a.js
,b.js
和index.js
存放在src文件夹下。这个文件夹存放的是原始文件 - 建立dist文件夹,用来存放编译后的文件,也就是打包后的单个文件
-
将
index.html
放到src文件夹下,这时引用的不是index.js
,而是打包后的位于dist目录下的bundle.js
文件<script type="text/javascript" src="../dist/bundle.js"></script>
-
配置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.js
中import
了a.js
和b.js
,所以webpack在打包时会同时将a.js
和b.js
引入。从这里可以看到,只用告诉webpack入口文件即可,所有的依赖文件webpack会自己寻找和解决。 - output是告诉webpack,打包后的文件放哪里。path指定了打包后的文件路径,filename指定了打包后的文件名。综合起来,打包后的文件就是dist目录下的
bundle.js
- entry是打包的入口文件,也就是告诉webpack打包哪个文件,这里指定的是
-
在
package.json
中的script下做个配置:"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack" },
- 直接运行
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能做的事情还有很多,将在后面一步步继续学习。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。