编号 | 名称 | 类型 |
1 | webpack | 1.任务管理器: Gulp / Grunt是一种任务管理工具,能够优化前端工作流程。比如自动刷新页面、combo、压缩css、js、编译less等等。简单来说,就是使用Gulp/Grunt,然后配置你需要的插件,就可以把以前需要手工做的事情让它帮你做了。 2.模块化解决方案 Seajs/Requirejs是一种在线"编译" 模块的方案,相当于在页面上加载一个 CMD/AMD 解释器。这样浏览器就认识了 define、exports、module 这些东西。也就实现了模块化。 Browserify / Webpack / Rollup是一个预编译模块的方案,相比于上面 ,这个方案更加智能。 |
2 | browserify | |
3 | rollup | |
4 | gulp | |
5 | grunt | |
6 | seajs | |
7 | requirejs |
在开始webpack之前,可以先了解AMD/CMD/CJS/ES6的区别
Webpack Overview
Webpack是模块打包器:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。
在面对复杂的业务需求和前端性能优化的场景时,webpack能够很好地解决如下问题:
前端模块化工具
异步模块加载及管理
样式(Style)预处理:Scss、Less、Postcss等
样式(Style)后处理:autoprefixer、img资源内联、原子css、css module
ES6语法、TypeScript、CoffeeScript支持
专有代码处理:JSX、*.vue
外部依赖模块:Handlerbars等模板、gzip、text、json等
公共模块提取
开模模式自动刷新浏览器
长效缓存处理
代码压缩混淆
概念
入口
入口(entry)指示webpack应使用哪些模块作为构建其内部依赖图的开始。处理完每个依赖后将结果输出到bundles文件中。
分别来看几种不同的入口配置方式。
单入口语法
用法:entry: string | Arrray
。当entry被配置为数组时,将创建 多个主入口(multi-main-entry)
。在需要将多个依赖一起注入并将其依赖导向到一个 chunk
时,可采用传入数组的方式。
module.exports = { entry: './path/to/my/entry/file.js',};// 完整语法module.exports = { entry: { main: './src/app.js', }};
对象语法
用法:entry: {[entryChunkName: string]: string | Array}
。对象语法是扩展性最好的配置方式。
// webpack.config.js// 分离应用程序【app】和第三方库【vendor】入口// [TODO] 为了支持提供更佳vendor分离能力的DllPlugin,考虑移除该场景。module.exports = { entry: { app: './src/app.js', vendros: './src/vendors.js', }};
多页面应用程序
告知webpack有多个独立的依赖图;
优化点:通过使用
CommonsChunkPlugin
为每个页面中相同的依赖构建共享的bundle。
module.exports = { entry: { pageOne: './src/pageOne/index.js', pageTwo: './src/pageTwo/index.js', pageThree: './src/pageThree/index.js' }};
输出
控制webpack如何向硬盘输出打包结果。常见的配置属性有filename和path,filename配置输出的bundle资源名称,path指定资源输出目录。
输出单独的bundle.js
module.exports = { output: { filename: 'bundle.js', path: '/home/project/public/assets' }};
创建多个输出文件
使用入口名称生成每个bundle的名称:
filename: '[name].js'
;使用内部chunk id生成每个bundle的名称:
filename: '[id].bundle.js'
;使用每次构建过程中唯一的hash值:
filename: '[name].[hash].bundle.js'
;使用基于每个chunk内容的hash值:
filenname: '[chunkhash].bundle.js'
;
module.exports = { entry: { app: './src/app.js', search: './src/search.js' }, output: { filename: '[name].js', path: __dirname + 'dist' }};
loader
loader用于对模块的源码进行转换。常见的loaders
支持三种配置方式:
配置
内联
CLI
loader特性
编号 | |
1 | 支持链式传递 |
2 | 前一个loader的返回值可作为下一个loader的输入 |
3 | 可以是同步的,也可以是异步的 |
4 | loader运行在node.js环境中,能执行任何可能的操作 |
5 | loader接收查询参数,用于对loader进行配置;也可使用options对象进行配置 |
6 | loader能够产生额外的任意文件 |
7 | 插件可以为loader带来更多特性 |
8 | 在package.json中定义loader字段,可将普通npm模块导出为loader(如何编写一个loader) |
配置
在webpack.config.js文件中进行配置,通过module.rules可配置多个loader。
每个rule分为三个部分:
condition
result
nested rule
module.exports = { module: { rules: [ { test: /\.css$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader', options: { modules: true } } ] } ] }};
内联
可以在 import
语句或任何等效于 "import" 的方式中指定 loader。使用 !
将资源中的 loader 分开。分开的每个部分都相对于当前目录解析。
import Styles from 'style-loader!css-loader?modules!./styles.css';
CLI
在webpack打包时传递参数配置实现对资源的转换
# 以下命令使用jade-loader处理 .jade文件,使用style-loader 和 css-loader 处理.css文件webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'
插件
插件是webpack的支柱功能。webpack自身也是构建在插件系统之上。插件的目的在于解决loader无法实现的事情。
webpack 插件是一个具有 apply 属性的 JavaScript 对象。apply 属性会被 webpack compiler 调用,并且 compiler 对象可在整个编译生命周期访问。
常见插件
插件示例
compiler hook 的 tap 方法的第一个参数,应该是驼峰式命名的插件名称。建议为此使用一个常量,以便它可以在所有 hook 中复用。
const pluginName = 'ConsoleLogOnBuildWebpackPlugin';class ConsoleLogOnBuildWebpackPlugin { apply(compipler) { compiler.hooks.run.tap(pluginName, compilation => { console.log('webpack 构建开始!'); }); }}
配置示例
更完整示例(后台回复完整实例)
模块
在模块化编程中,开发者将程序分解成离散功能块(discrete chunks of functionality),并称之为模块。
ES2015 import
CommonJS require
AMD define 和 require
css/sass/less 中的 @import
样式(url(...))或者HTML文件()中的图片链接
模块解析
resolver 是一个库(library),用于帮助找到模块的绝对路径。
所依赖的模块可以是来自应用程序代码或第三方的库(library)。resolver 帮助 webpack 找到 bundle 中需要引入的模块代码,这些代码在包含在每个 require
/import
语句中。当打包模块时,webpack
使用 enhanced-resolve 来解析文件路径。
webpack中的解析规则
使用enhanced-resolve,webpack能够解析三种文件路径。
绝对路径
已经为绝对路径,则无需进一步处理。
import '/home/project/src/file';import 'C:\\Users/dadange/file';
相对路径
这种情况下,import或者require的资源文件所在的目录被认为是上下文目录。在解析时会添加上下文路径(context path)以生成模块绝对路径(absolute path)。
import '../src/file';import './file';
模块路径
模块路径模式下,将在resolve.modules指定的目录内搜索,可通过resolve.alias来替换初始模块路径。
import 'module';import 'module/lib/file';
指定为具体文件路径:如果路径具有文件扩展名,则被直接打包;否则使用
resolve.extensions
选项作为文件扩展名来解析,此选项告知解析器在解析过程中哪些扩展名被接收(例如:.js
,.jsx
)。若指向文件夹:
-
如果文件夹下包含
package.json
文件,则先在package.json
中查找符合resolve.mainFields
配置字段的第一个结果,并确定文件路径;若
package.json
文件不存在或者package.json
未配置一个resolve.mainFields
有效路径,则在import/require
目录下查找一个匹配的文件;
manifest
Runtime
在浏览器运行时,webpack 用来连接模块化的应用程序的所有代码。
runtime 包含:在模块交互时,连接模块所需的加载和解析逻辑。包括浏览器中的已加载模块的连接,以及懒加载模块的执行逻辑。
Manifest
一旦你的应用程序中,形如
index.html
文件、一些 bundle 和各种资源加载到浏览器中,会发生什么?你精心安排的/src
目录的文件结构现在已经不存在,所以 webpack 如何管理所有模块之间的交互呢当编译器(compiler)开始执行、解析和映射应用程序时,它会保留所有模块的详细要点。这个数据集合称为 "Manifest",当完成打包并发送到浏览器时,会在运行时通过 Manifest 来解析和加载模块。无论你选择哪种模块语法,那些
import
或require
语句现在都已经转换为__webpack_require__
方法,此方法指向模块标识符(module identifier)。通过使用 manifest 中的数据,runtime 将能够查询模块标识符,检索出背后对应的模块。
构建目标
因服务器和浏览器代码均可使用JavaScript编写,所以webpack提供了多种构建目标,targets列表。要设置
target
属性,在webpack中配置即可。
// webpack.config.jsmodule.exports = { target: 'web' // webpack构建目标默认为web,可省略};
构建多个Target
尽管 webpack 不支持向
target
传入多个字符串,但是可以通过打包两份分离的配置来创建同构库。
var path = require('path');var serverConfig = { target: 'node', output: { path: path.resolve(__dirname, 'node-dist'), filename: 'lib.node.js' }, // ...};var clientConfig = { target: 'web', output: { path: path.resolve(__dirname, 'client-dist'), filename: 'client.js' } // ...};module.exports = [serverConfig, clientConfig];