webpack 入门
Posted 时倾
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了webpack 入门相关的知识,希望对你有一定的参考价值。
什么是 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
以上是关于webpack 入门的主要内容,如果未能解决你的问题,请参考以下文章