前言
之前一直听说 webpack 的大名,可惜未曾得见。今天有空,初步了解了 Webpack 工作方式后,一步步的开始学习使用 Webpack,并把其中的关键点记录下来
这不是一个教程,是工作当中用到的时候才写的,所有看起来会有点乱
环境配置
- 需要用到 node 环境,建议依赖node的环境版本>=6.11.5
- 进入项目初始化 npm init,生成package.json
- 正常安装 webpack 之外,webpack4 需要再单独安一个 webpack-cli
npm init
npm install --save-dev webpack webpack-cli
webpack.config.js
1、创建 webpack.config.js
let path = require('path');
let webpack = require('webpack');
module.exports = {
mode: "development", // 模式配置 development || production
entry: '', // 入口文件
output: {}, // 出口文件
module: {}, // 处理对应模块
plugins: [], // 对应的插件
devServer: {}, // development 下开发服务器
}
2、启动静态服务器
如果文件被更新,代码将被重新编译
npm install --save-dev webpack-dev-server
// webpack.config.js
devServer: {
contentBase: './dist', // 静态文件根目录
hot: true, // 模块热替换
overlay: true, // 浏览器页面上显示错误
stats: "errors-only", // 控制编译的时候shell上的输出内容 minimal || normal || verbose
compress: true // 服务器返回浏览器的时候是否启动gzip压缩
},
module.exports = {
plugins: [
new webpack.NamedModulesPlugin(),
// 热替换,热替换不是刷新
new webpack.HotModuleReplacementPlugin()
],
}
//package.json
"start": "webpack-dev-server --open",
js中的内容修改保存后,在不刷新页面的情况下直接修改掉,这样就实现了热更新
3、Source Maps 调试
开发中方便的调试能极大的提高开发效率,不过通过打包后的文件,很难找到出错了的地方,Source Maps 就是来帮我们解决这个问题的
// webpack.config.js
devtool: 'inline-source-map' // 有多种选项
4、Html模板和清空 dist
使用hash值之后,每次打包的文件都不一样,dist里面的文件会越来越多,需要每次打包前清空文件夹
文件都打包好了,但是我们在使用的时候不能在dist目录下去创建一个html文件,然后去引用打包后的js吧,这不合理,实际开发中也不会这样
我们需要实现html打包功能,可以通过一个模板实现打包出引用好路径的html来
npm install --save-dev html-webpack-plugin clean-webpack-plugin
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'app', 'index.tmpl.html'),// 源模板文件
filename:'index.html', //生成的html存放路径,相对于 path
minify:{ //压缩HTML文件
removeComments:true, //移除HTML中的注释
collapseWhitespace:false //删除空白符与换行符
},
//chunks:['index', 'vendor'], // 多入口的html文件用chunks这个参数来区分
})
]
5、多入口文件
entry: {
index: './src/index.js',
login: './src/login.js'
},
output: {
// [name]就可以将出口文件名和入口文件名一一对应
filename: '[name]-[hash].js', // 打包后会生成index.js和login.js文件
path: path.resolve('dist')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
chunks: ['index'] // 对应关系,index.js对应的是index.html
}),
new HtmlWebpackPlugin({
template: './src/login.html',
filename: 'login.html',
chunks: ['login'] // 对应关系,login.js对应的是login.html
})
]
6、加载 CSS
css文件需要额外的loader支持
npm install --save-dev style-loader css-loader
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [ // 从右向左解析
'style-loader',
'css-loader'
]
}
]
}
};
// 在入口文件 main.js 加入:
import './style.css';
打包后的css文件是以行内样式style的标签写进打包后的html页面
7、拆分 css
全部打包到一个文件,太多会影响加载速度,需要把文件拆分
npm install --save-dev mini-css-extract-plugin
// webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); // CSS文件单独提取出来
module: {
rules: [
{
test: /\.css$/,
use: [
// 将 css 用 link 的方式引入就不再需要 style-loader
MiniCssExtractPlugin.loader,
"css-loader"
],
include: path.resolve(__dirname, 'app'), // 限制范围,提高打包速度
exclude: /node_modules/
},
]
}
new MiniCssExtractPlugin({
filename: `css/[name]-[contenthash].css`,
}),
添加 CSS3 前缀
通过 postcss 中的 autoprefixer 可以实现将 CSS3 中的一些需要兼容写法的属性添加响应的前缀,这样省去我们不少的时间
npm install --save-dev postcss-loader autoprefixer
在项目根目录下创建一个 postcss.config.js 文件
// postcss.config.js
module.exports = {
plugins: [require('autoprefixer')] // 引用该插件即可了
}
// webpack.config.js
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}
8、加载图片
npm install --save-dev file-loader url-loader
// webpack.config.js
module: {
rules: [
{
test: /\.(png|svg|jpg|gif)$/,
//use: ['file-loader']
use: [
{
// url-loader内置了file-loader
loader: 'url-loader',
options: {
limit: 8192, // 小于8k的图片自动转成base64格式,并且不会存在实体图片
outputPath: 'images/' // 图片打包后存放的目录
}
}
]
},
]
]
// main.js
import Icon from './icon.png';
页面中经常会用到img标签,img引用的图片地址也需要一个loader来帮我们处理好
npm install --save-dev html-withimg-loader
module.exports = {
module: {
rules: [
{
test: /\.(htm|html)$/,
use: 'html-withimg-loader'
}
]
}
}
这样再打包后的html文件下img就可以正常引用图片路径了
svg图片都可以通过file-loader来解析,样式中引入了这类格式的图标或者图片都没有问题了,img如果也引用svg格式的话,配合上面写好的html-withimg-loader就都没有问题了
9、加载字体
file-loader 和 url-loader 可以接收并加载任何文件,然后将其输出到构建目录
// webpack.config.js
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}
// style.css
@font-face {
font-family: 'MyFont';
src: url('./Omnes_Light.otf') format('woff2');
font-weight: 600;
font-style: normal;
}
10、加载数据
NodeJS,JSON 支持实际上是内置
导入 CSV、TSV 和 XML
npm install --save-dev csv-loader xml-loader
//webpack.config.js
{
test: /\.(csv|tsv)$/,
use: [
'csv-loader'
]
},
{
test: /\.xml$/,
use: [
'xml-loader'
]
}
// main.js
// 所导入的 Data 变量将包含可直接使用的已解析 JSON
import Data from './data.xml'; // JSON, CSV, TSV, XML
11、模块热替换
css的hash命名必须使用contenthash,保证即使css文件所处的模块里就算其他文件内容改变,只要css文件内容不变,那么不会重复构建
css 如果使用style-loader将样式添加到js文件中,在编写样式的时候可以享受热更新的效果,如果将样式提取,需要手动刷新。
方法二:在入口文件中引入html文件,并使用raw-loader对html文件进行处理,实现html热更新
html自动更新
1、安装raw-loader
npm install --save-dev raw-loader
2、在webpack.config.js中配置raw-loader
rules: [
......
{
test: /\.(htm|html)$/,
use: [
'raw-loader'
]
}
......
]
3、在入口文件index.js文件中引入index.html文件
import '../template/index.html'
12、转义 ES6
在实际开发中,经常使用 ES6 去写代码,这样会提高我们写代码的速度,不过为了向后兼容低版本浏览器,需要 Babel 转换成兼容的代码
npm install -save-dev babel-core babel-loader babel-preset-env babel-preset-stage-0 babel-preset-react
代码不仅仅包含ES6还有之后的版本和那些仅仅是草案的内容,所以我们可以通过一个.babelrc文件来配置一下
// .babelrc
{
"presets": ["env", "stage-0"] // 从右向左解析
}
// webpack.config.js
module.exports = {
module: {
rules: [
{
test:/\.js$/,
use: 'bable-loader',
include: /src/, // 只转化src目录下的js
exclude: /node_modules/ // 排除掉node_modules,优化打包速度
}
]
}
}
13、构建开发和生产环境-分离配置文件
npm install --save-dev webpack-merge
新建 webpack.common.js、webpack.dev.js、webpack.prod.js
把 webpack.config.js 文件内容分为公共代码、开发代码、生产代码
14、压缩提取出的css,解决分离出的js重复问题
npm install --save-dev optimize-css-assets-webpack-plugin
// webpack.prod.js
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin');
plugins: [
new OptimizeCSSPlugin({
cssProcessorOptions: {safe: true}
}),
]
15、集中拷贝静态资源
npm install --save-dev copy-webpack-plugin
// webpack.prod.js
const copyWebpackPlugin = require('copy-webpack-plugin');
new copyWebpackPlugin([{
from:__dirname+'/src/public',//打包的静态资源目录地址
to:'./public' //打包到dist下面的public
}]),
16、报错
1、Cannot use [chunkhash] or [contenthash] for chunk in '[name]-[chunkhash].js' (use [hash] instead)
解决方法:
- 如果是开发环境,将配置文件中入口文件的 chunkhash 替换为 hash
- 如果是生产环境,不要使用参数 --hot
2、Refusing to install package with name "react" under a package also called "react".
解决方法:package.json name 的值不能跟npm的包名相同,同时把 package-lock.json 的 name 修改
3、img 和 css 的图片路径不对
如果是在css文件里引入的如背景图之类的图片,就需要指定一下相对路径
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
/*
* 复写 css 文件中资源路径
* 因为 css 文件中的外链是相对与 css 的,
* 我们抽离的 css 文件在可能会单独放在 css 文件夹内
* 引用其他如 img/a.png 会寻址错误
*/
publicPath: '../'
}
},
{ loader: "css-loader" },
{ loader: "postcss-loader" }
],
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
}
]
}