webpack
是一个用于现代 JavaScript 应用程序的静态模块打包工具,它有什么作用呢,又是基于什么原因出现的呢?
让我们来看一下传统的MVC模式下的前端代码组织方式。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>测试</title>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</head>
<body>
<script src="./src/index.js"></script>
</body>
</html>
当我们引入一个外部库(jQuery),然后又在页面引入了自己编写的 js 文件,index.html 可能是上面写的这样。这样会有什么问题呢?
- 无法直接体现,脚本的执行依赖于外部库。
- 如果依赖不存在,或者引入顺序错误,应用程序将无法正常运行。
- 如果依赖被引入但是并没有使用,浏览器将被迫下载无用代码。
基于以上问题,webpack
提出了一种新的代码组织方式。那就是用一个 package.json
文件来描述项目用到的外部 js 文件(依赖),并且把这些文件全部放在 'node_modules' 目录之下,而自己开发的代码则放在 'src' 目录之下。这样一来,就可以清楚地看到,哪些是外部库,哪些是自己的代码,修改起来也方便。
但 webpack
解决的不仅仅是代码组织的问题,它可以通过 Tree Shaking、压缩等技术去除项目里未使用或者重复的代码,从而达到减少输出文件体积,加快加载速度的目的。而且提供了热模块替换,有助于大大提高开发效率。
1. 入口
module.exports = {
// entry: './src/index.js',
entry: {
index: './src/index.js',
print: './src/print.js',
}
}
配置中的 'entry' 字段用于配置入口文件,可以使用字符串(单入口),也可以使用对象(多入口)。'webpack' 会根据入口文件去查找依赖,然后把所有用到的依赖一起打包输出。
webpack
默认只对 js
打包,html
、css
、image
之类的不会打包,这些要打包的话就需要引入其他的插件,而 html-webpack-plugin
就是实现对html
文件打包,它的作用就是根据修改的 .vue
文件打包生成新的 /dist/index.html
,并且在里面自动引入对应的 js
文件。
【注】html-webpack-plugin
插件打包生成的 index.html
默认是压缩的(就是所有代码写在一行,没有分行),然后 js
文件是在 <head>
标签里面引入,而不是在 <body>
结束之前,不过它会使用 defer="defer"
属性阻止 js
阻塞页面。
2. 自动编译
每次修改都运行 npm run build
进行编译是一件很麻烦的事,webpack
可以有3种方法帮助你实现自动编译。
2.1 使用 watch mode(观察模式)
很简单,就是添加一个脚本命令,然后使用 npm run watch
运行即可。
"scripts": {
"watch": "webpack --watch",
},
2.2 使用 webpack-dev-server
这个是最常使用的,也是我们日常默认使用的方式。
需要安装插件,然后添加运行脚本。
// 安装
npm install webpack-dev-server --save-dev
// 运行
"scripts": {
"start": "webpack serve --open",
}
2.3 使用 webpack-dev-middleware
这个方法很少使用,它需要借助 express
,而且需要配置 server.js
文件。
【注】以上方法只是自动编译文件,但是不会自动刷新浏览器页面。
3. 构建性能
3.1 通用环境
无论是开发环境还是生产环境都适用。
1】把 webpack
、Node.js
、npm
、yarn
这些升级到最新版本,能够建立更高效的模块树以及提高解析速度。
2】把 loader
只应用在必要的地方,在 rule
规则里面通过 include
或 exclude
字段配置文件。
3】尽量少地使用 loader
和 plugin
。
4】使用 DllPlugin
为更改不频繁的代码生成单独的编译结果。
5】在多页面应用程序中使用 SplitChunksPlugin
。
3.2 开发环境
1】使用 webpack
的 watch mode
(监听模式),而不使用其他工具来 watch
文件和调用 webpack
。
2】使用 webpack-dev-server
、webpack-dev-middleware
插件来在内存中(而不是写入磁盘)编译和 serve
资源来提高性能。
3】不需要的时候可以不生成 source-map
文件。
4】避免在生产环境下才会用到的工具,比如一些压缩插件。
5】最小化 entry chunk
。
3.3 生产环境
如果需要保留调试,使用 source-map
代替 inline-source-map
。
4. 生产环境
为了管理方便,可以针对不同的环境书写不同的配置文件,比如 webpack.dev.js
、webpack.prod.js
、webpack.common.js
,然后通过 webpack-merge
包进行合并。
当拆分了不同环境的配置文件之后,可以通过如下的脚本来运行。
"scripts": {
"build": "webpack --config webpack.prod.js",
"start": "webpack serve --open --config webpack.dev.js",
},
【注】'webpack-merge' 包的功能跟 Object.assign(a, b)
类似,但是 webpack-merge
会合并相同的选项,而后者是直接覆盖。
5. 懒加载
对于某些需要事件触发才去调用的 js
库或文件,可以通过 import
方法引入(在写代码的时候就已经处理,而不是打包或者运行的时候。也就是说,不是在 .vue
文件的开头直接 import
,而是在点击的时候再 import
),这样在一开始的时候就不会加载。