1、介绍
Webpack 是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。
2、安装
-
全局安装
以下的 NPM 安装方式,将使 webpack 在全局环境下可用:
npm install -g webpack
不推荐全局安装 webpack。这会将您项目中的 webpack 锁定到指定版本,并且在使用不同的 webpack 版本的项目中,可能会导致构建失败。
-
本地安装
//安装最新版本
npm install --save-dev webpack
//安装特定版本
npm install --save-dev webpack@<version>
对于大多数项目,我们建议本地安装。这可以使我们在引入破坏式变更(breaking change)的依赖时,更容易分别升级项目。
-
安装操作
首先我们创建一个目录,初始化 npm,以及在本地安装 webpack:
mkdir webpack-demo && cd webpack-demo
npm init -y
npm install --save-dev webpack
2、Webpack使用
-
简单使用webpack
现在我们将创建以下目录结构和内容
src/index.js
function component() {
var element = document.createElement('div');
element.innerHTML = 'Hello, webpack';
return element;
}
document.body.appendChild(component());
dist/index.html
<html>
<head>
<title>Getting Started</title>
</head>
<body>
<script src="bundle.js"></script>
</body>
</html>
执行 webpack,会将我们的脚本作为入口起点,然后输出为 bundle.js
./node_modules/.bin/webpack src/index.js dist/bundle.js
在浏览器中打开 index.html,如果一切访问都正常,你应该能看到以下文本:'Hello webpack'。
-
使用配置文件webpack.config.js
大多数项目会需要很复杂的设置,这就是为什么 webpack 要支持配置文件。这比在终端(terminal)中输入大量命令要高效的多,所以让我们创建一个取代以上使用 CLI 选项方式的配置文件:webpack.config.js
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
现在,让我们再次执行构建,通过使用我们的新配置:
./node_modules/.bin/webpack --config webpack.config.js
NPM 脚本(NPM Scripts)
考虑到用 CLI 这种方式来运行本地的 webpack 不是特别方便,我们可以设置一个快捷方式。在 package.json 添加一个 npm 脚本(npm script):
package.json
现在,可以使用 npm run build 命令,来替代我们之前用到的较长命令。注意,使用 npm 的 scripts,我们可以通过模块名,来引用本地安装的 npm 包,而不是写出完整路径。这是大多数基于 npm 的项目遵循的标准,允许我们直接调用 webpack,而不是去调用 ./node_modules/.bin/webpack。
现在运行以下命令,然后看看你的脚本别名是否正常运行:
npm run build
2、React和ES6以及JSX语法的支持
-
ES6以及JSX语法的支持
Babel其实是几个模块化的包,其核心功能位于称为babel-core的npm包中,webpack可以把其不同的包整合在一起使用,对于每一个你需要的功能或拓展,你都需要安装单独的包(用得最多的是解析Es6的babel-preset-es2015包和解析JSX的babel-preset-react包)
安装Babel依赖包
// npm一次性安装多个依赖模块,模块之间用空格隔开
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
.babelrc
{
"presets": ["react", "es2015"]
}
babel 在webpack.config.js 中进行配置
React的支持
安装 React 和 React-DOM
npm install --save react react-dom
使用React和ES6的语法更新index.js
注意这里我没有直接渲染到 document.body上,而是特别选择了 id 为 root 的 :
ReactDOM.render(<Hello />, document.getElementById('root'));
React官方不推荐将组件渲染到 document.body 上,因为这个节点很可能会改变,比如动态添加一个 <script> 标签等,这将使 React 的 DOM diff计算变得更加困难。
import ReactDOM from 'react-dom';
import React, {Component} from 'react';
class Hello extends Component{
render() {
return (
<div>
Hello, webpack
</div>
);
}
}
ReactDOM.render(<Hello />, document.getElementById('root'));
在body中增加一个div标签
<html>
<head>
<title>Getting Started</title>
</head>
<body>
<div id='root'></div>
<script src="bundle.js"></script>
</body>
</html>
运行
3、通过 loader 引入任何其他类型的文件
-
加载 CSS
为了从 JavaScript 模块中 import 一个 CSS 文件,你需要在 module 配置中 安装并添加 style-loader 和 css-loader:
npm install --save-dev style-loader css-loader
加载css在webpack.config.js 中进行配置
我们尝试一下,通过在项目中添加一个新的 style.css 文件,并将其导入到我们的 index.js 中:
src/style.css
.hello {
color: red;
}
src/index.js
运行
CSS module
CSS modules 的技术就意在把JS的模块化思想带入CSS中来,通过CSS模块,所有的类名,动画名默认都只作用于当前模块。Webpack从一开始就对CSS模块化提供了支持,在CSS loader中进行配置后,你所需要做的一切就是把”modules“传递到所需要的地方,然后就可以直接把CSS的类名传递到组件的代码中,且这样做只对当前组件有效,不必担心在不同的模块中使用相同的类名造成冲突。
CSS module在webpack.config.js 中进行配置
src/index.js
运行
-
加载图片
使用 file-loader,我们可以轻松地将这些内容混合到 CSS 中:
npm install --save-dev file-loader
加载图片在webpack.config.js 中进行配置
增加一张test.png图片
src/style.css
运行
4、webpack plugin
-
HtmlWebpackPlugin
作用
1、html文件中引入的外部资源如script、link动态添加每次compile后的hash,防止引用缓存的外部文件问题
2、可以生成创建html入口文件,比如单页面可以生成一个html文件入口,配置N个html-webpack-plugin可以生成N个页面入口
安装
npm install --save-dev html-webpack-plugin
创建src/index.tmpl.html,并删除dist/index.html
src/index.tmpl.html
<html>
<head>
<title>Getting Started</title>
</head>
<body>
<div id='root'></div>
</body>
</html>
webpack.config.js配置
运行
了解更多 HtmlWebpackPlugin 插件
如果你想要了解更多 HtmlWebpackPlugin 插件提供的全部功能和选项,那么你就应该多多熟悉HtmlWebpackPlugin 仓库。
你还可以看一下 html-webpack-template,除了默认模板之外,还提供了一些额外的功能。
-
clean-webpack-plugin
你可能已经注意到,由于过去的指南和代码示例遗留下来,导致我们的 /dist 文件夹相当杂乱。webpack 会生成文件,然后将这些文件放置在 /dist 文件夹中,但是 webpack 无法追踪到哪些文件是实际在项目中用到的。
通常,在每次构建前清理 /dist 文件夹,是比较推荐的做法,因此只会生成用到的文件。让我们完成这个需求。
安装
npm install clean-webpack-plugin --save-dev
webpack.config.js配置
现在执行 npm run build,再检查 /dist 文件夹。如果一切顺利,你现在应该不会再看到旧的文件,只有构建后生成的文件!
5、开发常用配置
-
webpack-dev-server
webpack-dev-server 为你提供了一个简单的 web 服务器,并且能够实时重新加载(live reloading)。
安装
npm install --save-dev webpack-dev-server
webpack.config.js配置
localhost:8080 下建立服务,并启用gzip压缩
package.json配置npm script
运行
-
source map
介绍
当 webpack 打包源代码时,可能会很难追踪到错误和警告在源代码中的原始位置。例如,如果将三个源文件(a.js, b.js 和 c.js)打包到一个 bundle(bundle.js)中,而其中一个源文件包含一个错误,那么堆栈跟踪就会简单地指向到 bundle.js。这并通常没有太多帮助,因为你可能需要准确地知道错误来自于哪个源文件。
为了更容易地追踪错误和警告,JavaScript 提供了 source map 功能,将编译后的代码映射回原始源代码。如果一个错误来自于 b.js,source map 就会明确的告诉你。
devtool选项
以下选项是开发的理想选择:
eval- 每个模块都用eval()和执行//@ sourceURL。这很快。主要的缺点是它不会正确显示行号,因为它被映射到折叠代码而不是原始代码(没有来自加载器的源地图)。
eval-source-map- 每个模块都执行,eval()并将SourceMap作为DataUrl添加到eval()。最初它很慢,但它提供了快速的重建速度,并产生真实的文件。行号被正确映射,因为它被映射到原始代码。它产生最好的质量SourceMaps进行开发。
cheap-eval-source-map- 类似于eval-source-map每个模块都执行eval()。它是“便宜的”,因为它没有列映射,它只映射行号。它忽略来自Loader的SourceMaps,并且仅显示与devtool类似的折叠代码eval。
cheap-module-eval-source-map- cheap-eval-source-map但是,与此类似,处理来自加载程序的源地图以获得更好的结果。然而,Loader Source Maps简化为每行一个映射。
对小到中型的项目中,eval-source-map是一个很好的选项,再次强调你只应该开发阶段使用它。
cheap-module-eval-source-map方法构建速度更快,但是不利于调试,推荐在大型项目考虑时间成本时使用。
webpack.config.js 配置
-
代码分离及优化
介绍
- OccurenceOrderPlugin :为组件分配ID,通过这个插件webpack可以分析和优先考虑使用最多的模块,并为它们分配最小的ID
- UglifyJsPlugin:压缩JS代码;
- ExtractTextPlugin:用于将 CSS 从主应用程序中分离
安装
OccurenceOrder 和 UglifyJS plugins 都是内置插件,你需要做的只是安装其它非内置插件
npm install --save-dev extract-text-webpack-plugin
webpack.config.js配置
运行
运行后将css文件分离到style.css文件中
运行后js文件被压缩
-
缓存
介绍
通过使用 output.filename 进行文件名替换,可以确保浏览器获取到修改后的文件。[hash] 替换可以用于在文件名中包含一个构建相关(build-specific)的 hash,但是更好的方式是使用 [chunkhash] 替换,在文件名中包含一个 chunk 相关(chunk-specific)的哈希。
webpack.config.js 配置
-
生产环境构建
现在,我们把 scripts 重新指向到新配置。我们将 npm start 定义为开发环境脚本,并在其中使用 webpack-dev-server,将 npm run build 定义为生产环境脚本
创建webpack.config.prod.js文件
webpack.config.prod.js
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: {
loader: "css-loader",
options: {
modules: true,
minimize: true
}
}
})
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
]
},
plugins: [
new ExtractTextPlugin("[name].[contenthash].css"),
new CleanWebpackPlugin([__dirname + "/dist"]),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin(),
new HtmlWebpackPlugin({
template: __dirname + "/src/index.tmpl.html",//new 一个这个插件的实例,并传入相关的参数
minify: {
removeComments: true, //是否去掉注释
collapseWhitespace: true, //是否去掉空格
minifyJS: true, //是否压缩html里的js(使用uglify-js进行的压缩)
minifyCSS: true, //是否压缩html里的css(使用clean-css进行的压缩)
},
})
],
};
webpack.config.js去除一些配置
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist')
},
devtool: 'eval-source-map',
module: {
rules: [{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
{
loader: "style-loader"
}, {
loader: "css-loader",
options: {
modules: true
}
}
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: __dirname + "/src/index.tmpl.html",//new 一个这个插件的实例,并传入相关的参数
minify: {
removeComments: true, //是否去掉注释
collapseWhitespace: true, //是否去掉空格
minifyJS: true, //是否压缩html里的js(使用uglify-js进行的压缩)
minifyCSS: true, //是否压缩html里的css(使用clean-css进行的压缩)
},
})
],
devServer: {
//一切服务都启用gzip 压缩:
compress: true,
port: 8080
},
};
package.json配置
最后
本篇文章将涵盖webpack安装、使用、plugin、开发中常用的配置、缓存等等,我在边写边跟着构建,并将每一步构建生成git记录,代码传送门,如果有疑问的地方欢迎在下面评论或者私信给我。
如果您觉得有帮助,请给我点个👍。