什么是 webpack
webpack 是一个模块打包机,将依据文件间的依赖关系对其进行动态剖析,而后将这些模块按指定规定生成动态资源
当 webpack 处理程序时,它会递归地构建一个依赖关系图(dependency graph),其中蕴含应用程序须要的每个模块,而后将所有这些模块打包成一个或多个 bundle
次要承当如下性能
* 打包:将多个文件打包成一个文件,缩小服务器压力和下载带宽
* 转换:将预编译语言 转换成 浏览器辨认的语言
* 优化:性能优化
webpack 特点
- 代码拆分
webpack 有两种组织模块的依赖形式,同步、异步。异步依赖将作为宰割点,造成一个新的块;在优化了依赖树之后,每一个异步区块都将作为一个文件被打包。
- 智能解析
webpack 有一个智能解析器,简直能够解决任何第三方库。无论它们的模块模式是 CommonJS、AMD 还是一般的 JS 文件;甚至在加载依赖的时候,容许应用动静表达式 require(“./templates/” + name + “.jade”)。
- 疾速运行
webpack 应用异步 I/O、多级缓存进步运行效率,使得 webpack 以难以令人相信的速度 疾速增量编译。
装置
- 全局装置
sudo npm i webpack -g
- 部分装置:在曾经 npm 初始化的我的项目 根目录执行
npm i webpack -D
揭示:webpack4.x 版本须要额定装置 webpack-cli:
npm i webpack-cli -D
模块交互 runtime、manifest
- 在应用 webpack 构建的典型应用程序或站点中,有三种次要的代码类型:
- 你或你的团队编写的源码。
- 你的源码会依赖的任何第三方的 library 或 “vendor” 代码。
- webpack 的 runtime 和 manifest,治理所有模块的交互
- runtime
runtime 蕴含:在模块交互时,连贯模块所需的加载和解析逻辑;包含浏览器中的已加载模块的连贯,以及懒加载模块的执行逻辑
- manifest
当编译器 (compiler) 开始执行、解析、映射应用程序时,它会保留所有模块的具体要点,这个数据汇合称为 “Manifest”
当实现打包并发送到浏览器时,会在运行时通过 manifest 来解析、加载模块
- runtime 和 manifest 治理模块的交互
在浏览器运行时,runtime 和 manifest 用来连贯模块化的应用程序的所有代码
无论你抉择哪种模块语法,那些 import 或 require 语句当初都曾经转换为 __webpack_require__
办法,此办法指向模块标识符(module identifier)
通过应用 manifest 中的数据(每个模块的具体要点:映射、依赖等),runtime 将可能查问模块标识符,检索出背地对应的模块
入口 entry
- 作用
通知 webpack 从哪个文件开始构建,这个文件将作为 webpack 依赖关系图的终点
- 配置 单入口
// webpack.config.js
module.exports = {entry: './app/main.js'};
// webpack.config.js
module.exports = {
entry: {main: './app/main.js'}
};
- 配置 多入口
// 场景一:拆散 应用程序(app) 和 第三方库(vendor) 入口
module.exports = {
entry: {
app: './app/main.js',
vendors: './vendors/main.js'
}
};
// 场景二:多页面应用程序,通知 webpack 须要 3 个独立拆散的依赖图
module.exports = {
entry: {
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js'
}
};
进口 output
- 作用
通知 webpack 在哪里输入构建后的包、包的名称等
- 配置 单进口
// webpack.config.js
module.exports = {
entry: __dirname + "/app/main.js",
output: {
filename: 'main.js',
path: __dirname + '/dist'
}
};
// 写入到硬盘:./dist/main.js
- 配置 多进口
// webpack.config.js
module.exports = {
entry: {
app: './src/app.js',
vendors: './src/vendors.js'
},
output: {filename: '[name].js',
path: \_\_dirname + '/dist'
}
}
// 写入到硬盘:./dist/app.js, ./dist/vendors.js
注:“__dirname”是 node.js 中的一个全局变量,它指向以后执行脚本所在的目录。
生成 Source Maps
解决的问题:大部分源码(各种函数库和框架)都要通过转换,能力投入生产环境,这使得理论运行的代码不同于开发代码,可能会很难追踪到 error(谬误) 和 warning(正告) 在源代码中的原始地位。为了更容易地追踪 error 和 warning,JavaScript 提供了 source maps 性能,能够将编译后的代码映射回原始源代码。
通过 webpack 的 devtools
就能够在打包时为咱们生成 source maps
。devtools 能够抉择一种源映射款式,不同的 source map(资源映射)会决定代码中谬误的显示方式(打包后代码、生成后代码、转换过代码、源代码等),会影响 构建(build)、从新构建(rebuild) 的速度。
整个 source map 作为一个独自的文件生成。它为 bundle 增加了一个援用正文,以便开发工具晓得在哪里能够找到它
开发环境的几种常见的 source map
- eval-source-map
构建速度:–、从新构建速度:+、生产环境:no、显示原始源代码
- cheap-eval-source-map
构建速度:+、从新构建速度:++、生产环境:no、转换过的代码(仅限行)
- cheap-module-eval-source-map【举荐】
构建速度:0、从新构建速度:++、生产环境:no、原始源代码(仅限行)
- none【举荐】(生产环境中 常见的 source map)
构建速度:+++、从新构建速度:+++、生产环境:yes、打包后代码
如何抉择
不同的 devtool 的设置,会导致不同的性能差别。
- “eval” 具备最好的性能,但并不能帮忙你转译代码。
- 如果你能承受稍差一些的 mapping 品质,能够应用 cheap-source-map 选项来进步性能
- 应用 eval-source-map 配置进行增量编译
- 在大多数状况下,cheap-module-eval-source-map 是最好的抉择
// webpack.config.js
module.exports = {
devtool: 'eval-source-map', // 生成 source maps
entry: __dirname + "/app/main.js", // 入口
output: {
filename: "bundle.js",
path: __dirname + "/public",
}
}
除了应用
devtool
之外,您还能够间接应用SourceMapDevToolPlugin
/EvalSourceMapDevToolPlugin
,因为它具备更多抉择。切勿同时应用devtool
和插件,因为devtool
在外部增加了插件,因而您最终将插件利用了两次。
构建本地服务器
想不想让你的浏览器监听你的代码的批改,并主动刷新显示批改后的后果,其实 Webpack
提供一个可选的本地开发服务器,这个本地服务器基于 node.js 构建,能够实现你想要的这些性能。
// webpack.config.js
module.exports = {
devtool: 'eval-source-map', // 生成 source maps
entry: __dirname + "/app/main.js", // 入口
output: {
filename: "bundle.js",
path: __dirname + "/public",
},
devServer: {
contentBase: "./public",// 本地服务器所加载的页面所在的目录
historyApiFallback: true,// 不跳转
inline: true // 当源文件扭转时是否主动刷新页面
}
}
loader
通过应用不同的 loader
,webpack
有能力调用内部的脚本或工具,实现对不同格局的文件的解决,比如说剖析转换 scss 为 css,或者把下一代的 JS 文件(ES6,ES7)转换为古代浏览器兼容的 JS 文件,对 React 的开发而言,适合的 Loaders 能够把 React 的中用到的 JSX 文件转换为 JS 文件。
应用形式
- 内联(不罕用)
// 在我的项目文件中,import 语句时应用
import Styles from 'style-loader!css-loader?modules!./styles.css';
- CLI(不罕用)
webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'
// 会对 .jade 文件应用 jade-loader,对 .css 文件应用 style-loader 和 css-loader
- 在 Webpack.config.js 配置
loader 个性
- 简直所有 loader 都 须要装置, 但 不须要 在 webpack 配置文件中通过
require
引入 - 逆向编译,链式传递
module.exports = {
module: {
rules: [{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}]
}
};
css 文件编译程序顺次为:postcss-loader —> css-loader —> style-loader。编译过程中,第一个 loader 的值 传递给下一个 loader,顺次传递;最初一个 loader 编译实现后,将预期值传递给 webpack。
编译 css
webpack 提供两个工具解决样式表,css-loader
和 style-loader
,二者解决的工作不同,css-loader
使你可能应用相似 @import
和 url(...)
实现 require()
的性能,style-loader
将所有的计算后的款式退出页面中,二者组合在一起使你可能把样式表嵌入 webpack 打包后的 JS 文件中。
Sass
和 Less
是对原生 CSS 的拓展,它们容许你应用相似于 variables
, nesting
, mixins
, inheritance
等不存在于 CSS 中的个性来写 CSS,CSS 预处理器能够这些非凡类型的语句转化为浏览器可辨认的 CSS 语句。
// 装置 loader
npm install --save-dev style-loader css-loader postcss-loader autoprefixer
module.exports = {
// ...
module: {
rules: [{
test: /\.css$/,
use: ['style-loader', 'css-loader', "postcss-loader"]
}]
}
};
CSS modules 是把 JS 的模块化思维带入 CSS 中来,通过 CSS 模块,所有的类名,动画名默认都只作用于以后模块。通过 webpack 在 CSS loader 中进行配置,就能够把 CSS 的类名传递到组件的代码中,无效防止了全局净化。
// webpack.config.js
module.exports = {
// ...
module: {
rules: [{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {modules: true,}
},
'postcss-loader'
]
}]
}
};
// postcss.config.js
module.exports = {
plugins: [require('autoprefixer')
]
}
babel
Babel 是一个编译 JavaScript 的平台,它能够编译代码帮你达到以下目标:
- 让你能应用最新的 JavaScript 代码(ES6,ES7…),而不必管新规范是否被以后应用的浏览器齐全反对;
- 让你能应用基于 JavaScript 进行了拓展的语言,比方 React 的 JSX;
装置
Babel 是几个模块化的包,其外围性能位于称为 babel-core
的 npm 包中,webpack 能够把其不同的包整合在一起应用,对于每一个你须要的性能或拓展,你都须要装置独自的包(用得最多的是解析 Es6 的 babel-env-preset
包和解析 JSX 的 babel-preset-react
包)。
npm install --save-dev babel-core babel-loader babel-preset-env babel-preset-react
配置
// webpack.config.js
module.exports = {
// ...
module: {
rules: [{test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader",
options: {
presets: ["env", "react"]
}
},
exclude: /node_modules/
}]
}
};
思考到 babel 具备十分多的配置选项,在 webpack.config.js
文件中配置使得文件显得太简单,因而会把 babel 的配置选项独自放在 “.babelrc” 的配置文件中(webpack 会主动调用 .babelrc
里的 babel 配置选项)
// webpack.config.js
module.exports = {
// ...
module: {
rules: [{test: /(\.jsx|\.js)$/,
use: {loader: "babel-loader",},
exclude: /node_modules/
}]
}
};
//.babelrc
{"presets": ["react", "env"]
}
插件 plugin
插件(Plugins)是用来拓展 Webpack 性能的,它们会在整个构建过程中失效,执行相干的工作。
Loaders 和 Plugins 经常被弄混,然而他们其实是齐全不同的货色,能够这么来说,loaders 是在打包构建过程中用来解决源文件的(JSX,Scss,Less..),一次解决一个,插件并不间接操作单个文件,它间接对整个构建过程其作用。
- 作用
能够解决各种工作,从打包优化和压缩,始终到从新定义环境中的变量
- plugin 应用
有些插件须要独自装置,有些插件是 webpack 内置插件,不须要独自装置。
所有的插件都 须要 在 webpack 配置文件中通过 require
引入,在 plugins 关键字局部增加该插件的一个实例(plugins 是一个数组)
npm i html-webpack-plugin -D
// webpack 配置
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
- plugin 分析
webpack 插件是一个具备 apply 属性的 JavaScript 对象
apply 属性会被 webpack compiler 调用,并且 compiler 对象可在整个编译生命周期拜访
// ConsoleLogOnBuildWebpackPlugin.js
const pluginName = 'ConsoleLogOnBuildWebpackPlugin';
class ConsoleLogOnBuildWebpackPlugin {apply(compiler) {
compiler.hooks.run.tap(pluginName, compilation => {console.log("webpack 构建过程开始!");
});
}
}
模式 mode
- 作用
通知 webpack 应用的是哪种模式,进行相应模式的内置优化
- 应用
// webpack 配置
module.exports = {mode: 'production'};
// CLI 参数中
webpack --mode=production
- 两种模式的区别
development
:会将 process.env.NODE_ENV 的值设为 development。默认启用 NamedChunksPlugin 和 NamedModulesPlugin 插件production
: 会将 process.env.NODE_ENV 的值设为 production。默认启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin 插件
// mode: development
module.exports = {
+ mode: 'development'
- plugins: [- new webpack.NamedModulesPlugin(),
- new webpack.DefinePlugin({"process.env.NODE_ENV": JSON.stringify("development") }),
- ]
}
// mode: production
module.exports = {
+ mode: 'production',
- plugins: [- new UglifyJsPlugin(/* ... */),
- new webpack.DefinePlugin({"process.env.NODE_ENV": JSON.stringify("production") }),
- new webpack.optimize.ModuleConcatenationPlugin(),
- new webpack.NoEmitOnErrorsPlugin()
- ]
}
- 在 webpack 中辨别两种 模式
if(process.env.NODE_ENV === 'development'){// 开发环境} else {// 生产环境}
target
- webpack 可能为 多种环境 或 target 构建编译(编译后代码 的运行环境)
默认值:web
常见值见 API