一、初始化项目
在命令行中敲入如下命令:
mkdir Webpack-react && cd Webpack-react && npm init -y
然后你就可以在你的当前路径下看到一个叫 Webpack-Vue 的文件夹,里面有一个包含默认信息的 package.json 文件,打开并修改这个文件的一些内容。
然后我们在项目文件夹中创建以下几个文件夹:
- dist
- src
- build
其中,dist 用于存放 Webpack 打包后的项目文件、src 用于存放你的源代码文件、build 用于存放 Webpack 打包相关的配置文件。
在 src 下,创建入口文件 index.js。
Linux 下创建的命令:
touch ./src/index.js
在根目录下创建 index.html 文件,内容如下:
<!DOCTYPE html>
<html>
<head>
<title>Webpack Vue Demo</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="root"></div>
</body>
</html>
这将用于作为我们应用的模板,打包的 js 文件会在 Webpack 插件的处理下插入到这个文件中。
其他配置性文件根据你自己的喜好来添加了,比如 .gitignore 文件等。
二、安装 Webpack
要使用 Webpack,第一步当然是先安装。使用以下命令进行安装:
npm i webpack webpack-cli -D
然后你就可以看到你的项目文件夹中多了一个 node_modules 文件夹,然后 package.json 文件中多了一个 devDependencies 属性。里面包含了安装的依赖名称和依赖版本,现在暂时还只有 webpack 和 webpack-cli。
三、配置最基本的 Webpack
这一节我们将着手配置一个具有最基本打包功能的项目,从 src/index.js 开始对项目进行打包。
为了项目结构更加科学合理,我们把所有的 Webpack 配置相关的文件都存放在了 build 目录中。
进入 build 文件夹,然后创建以下几个文件:
- webpack.base.conf.js
- webpack.dev.conf.js
- webpack.prod.conf.js
- build.js
在 Linux 中,可以敲入如下命令快速创建:
cd build/ && touch webpack.base.conf.js webpack.dev.conf.js webpack.prod.conf.js build.js
其中,webpack.base.conf.js 是最基础的打包配置,是开发环境和生产环境都要用到的配置。webpack.dev.conf.js 就是在开发环境要使用的配置。webpack.prod.conf.js 就是在生产环境要使用的配置了。build.js 是通过 Node 接口进行打包的脚本。
接下来我们在对应的文件中写入最基本的配置信息。
(1) webpack.base.conf.js
先写最基本的配置信息:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
bundle: path.resolve(__dirname, '../src/index.js')
},
output: {
path: path.resolve(__dirname, '../dist'),
filename: '[name].[hash].js'
},
module: {
rules: [
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../index.html')
})
]
};
(2) webpack.dev.conf.js
同样写入最基本的配置信息:
const merge = require('webpack-merge');
const path = require('path');
const baseConfig = require('./webpack.base.conf');
module.exports = merge(baseConfig, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: path.resolve(__dirname, '../dist'),
open: true
}
});
(3) webpack.prod.conf.js
继续写入最基础的配置:
const merge = require('webpack-merge');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const path = require('path');
const baseConfig = require('./webpack.base.conf');
module.exports = merge(baseConfig, {
mode: 'production',
devtool: 'source-map',
module: {
rules: []
},
plugins: [
new CleanWebpackPlugin(['dist/'], {
root: path.resolve(__dirname, '../'),
verbose: true,
dry: false
})
]
});
注意到我们上面引用了两个新的依赖,需要先进行安装才能使用:
npm i webpack-merge clean-webpack-plugin webpack-dev-server html-webpack-plugin -D
(4) build.js
这个脚本用于构建生产环境,开发环境基于 webpack-dev-server 搭建,不写脚本。
接下来,写入我们的打包脚本,通过 Node 调用 Webpack 进行打包。
const webpack = require('webpack');
const config = require('./webpack.prod.conf');
webpack(config, (err, stats) => {
if (err || stats.hasErrors()) {
// 在这里处理错误
console.error(err);
return;
}
// 处理完成
console.log(stats.toString({
chunks: false, // 使构建过程更静默无输出
colors: true, // 在控制台展示颜色
modules: false,
children: false,
chunkModules: false
}));
console.log('编译完成!')
});
这样做的好处是可以利用 Node 做一些其他的事情,另外当 Webpack 配置文件不在项目文件夹根部时方便调用。
(5) npm scripts
配置 npm scripts 能够使我们更方便的使用打包命令。
在 package.json 文件的 scripts 属性中,写入如下两条:
"build": "node build/build.js",
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js"
基本的配置写完了,我们测试一下打包效果,在 src/index.js 中写入如下代码:
先下载react npm i react react-dom -S
import React from 'react';
import ReactDOM from 'react-dom';
// import less from './less'
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { number: 0 };
this.decrease = this.decrease.bind(this);
this.increase = this.increase.bind(this);
}
// 加1
increase() {
let self = this;
self.setState({ number: self.state.number + 1 })
}
// 减一
decrease() {
let self = this;
self.setState({ number: self.state.number - 1 })
}
render() {
return (
<div>
<input type="button" value="减1" onClick={this.decrease} />
<span> {this.state.number} </span>
<input type="button" value="加1" onClick={this.increase} />
</div>)
}
}
ReactDOM.render(<Counter />, document.getElementById('root'))
随后我们还要配置别名,将 resolve.alias 配置为如下对象:
{
'@': path.resolve(__dirname, '../src'),
}
我们在 JavaScript 文件中引入依赖的时候,也可以方便地使用 @ 来代替 src,省去了写文件路径的麻烦。
我们顺便添加一个 resolve.extensions 属性,方便我们引入依赖或者文件的时候可以省略后缀:
extensions: ['*', '.js', '.jsx', '.json', '.less', '.css'],
extensions 属性是一个数组。这样配置之后,我们在 JavaScript 文件中 import JavaScript 文件、json 文件和 react 单文件组件、less/css文件都可以省略后缀。
以上几步都很重要,最好不要省略。
我们现在还只完成了最基本的打包功能,react 还不能使用,接下来我们将这个项目变得更加强大。
四、引入一些基础的 Loader
为了方便开发,我们需要引入一些 Loader,以简化我们的写法以及使我们的代码兼容更多的环境。
这一部分可以根据 Webpack 的文档来写,因为都是一些基本的东西,配置起来也不难。
(1)babel-loader
为了使我们的 JavaScript 代码兼容更多环境,我们需要使用 babel-loader。
配置方法:
首先安装 babel-loader、babel-preset-env 和 babel-core。需要注意的是,如果你的 babel-loader 是 7.x 版本的话,你的 babel-core 必须是 6.x 版本;如果你的 babel-loader 是 8.x 版本的话,你的 babel-core 必须是 7.x 版本。如果不这样的话,Webpack 会报错。
安装命令如下:
npm i babel-loader@7 babel-core babel-preset-env babel-plugin-transform-runtime babel-preset-stage-0 babel-preset-react -D
然后在 webpack.base.conf.js 的 module.rules 中新增如下对象:
{
test: /\.(js|jsx)$/,
use: 'babel-loader',
exclude: /node_modules/
}
我们还需要添加一个配置文件(.babelrc)在根目录下:
{
"presets": ["env", "stage-0", "react"],
"plugins": ["transform-runtime"]
}
这就是 babel-preset-env 的作用,帮助我们配置 babel。我们只需要告诉它我们要兼容的情况(目标运行环境),它就会自动把代码转换为兼容对应环境的代码。
如果你需要兼容IE需要另一位插件babel-polyfill
先下载插件npm i babel-polyfill -S
然后在你的src/index.js文件中的最上面引入(第一行,一定要第一行引入)
improt ‘babel-polyfill’
即可。
(2)file-loader
这个用于将字体文件、图片文件进行模块化。
首先安装 file-loader:
npm i file-loader -D
然后在 webpack.base.conf.js 中添加如下配置到 module.rules:
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}
当然可以简化配置信息,把两个 test 正则合并到一处。
接下来我们配置 vue-loader。
(3) less-loader
为了使用 Vue 单文件组件,我们需要对 .vue 文件进行处理,使用 vue-loader。
首先安装 style-loader、css-loader、less-loader 和 postcss-loader,后者是添加样式前缀的。
命令:
npm i style-loader css-loader less-loader postcss-loader -D
然后我们配置 webpack.base.conf.js,写入以下代码到该文件的 module.rules 属性当中:
{
test: /\.css$/,
use: ["style-loader","css-loader", "less-loader", 'postcss-loader']
}
接下来我们验证一下 react 单文件组件是否可用。
之前我们已经安装了 react 和 react-dom ,也修改了 src/index.js 文件了
运行命令 npm start 就可以看到截图如下了
到这里,我们的项目已经可以使用 react 单文件组件进行开发了,但是还没有完,我们还有一些任务要做。
五、优化 CSS 代码
这里我们使用 postcss 的 autoprefixer 插件为我们的 css 代码自动添加前缀以适应不同的浏览器。
首先安装依赖:
npm i postcss-loader autoprefixer -D
然后修改 module.rules 中的 css 配置项,修改之后如下:
{
test: /\.css$/,
use: ['style-loader', "css-loader", "less-loader", 'postcss-loader']
}
然后在我们项目的根目录下新增配置文件 postcss.config.js,内容如下:
module.exports = {
plugins: [
require('autoprefixer')
]
}
代表我们将要使用 autoprefixer 插件。
之后我们就可以愉快地写代码了,可以自己验证一下是否自动添加了前缀,这里不再赘述。
六、开启热更新
Webpack 4 开启热更新相对容易,具体步骤如下:
修改 webpack.dev.conf.js,在 devServer 属性中设置 hot 的值为 true,这就代表开启了热更新。但是只这样做还不够,需要我们添加一个插件,继续修改 webpack.dev.conf.js。
设置其 plugins 属性如下:
const webpack = require('webpack');
// 在文件头部引入 webpack 依赖
[
new webpack.HotModuleReplacementPlugin()
]
这就开启了 css 热更新(因为 vue-style-loader 封装了 style-loader,热更新开箱即用),但是 JavaScript 热更新还不能用,每次修改代码我们都会刷新浏览器,所以我们需要继续配置。
为了使得 JavaScript 模块也能进行 HMR,我们需要在我们的 入口文件(index.js/main.js) 的底部添加如下代码:
if (module.hot) {
module.hot.accept();
}
接下来就可以进行 HMR 了。
七、第三方库单独打包
每次我们对项目进行打包时,我们都会把引用的第三方依赖给打包一遍,比如 Vue、Vue-Router、React 等等。但是这些库的代码基本都是不会变动的,我们没必要每次打包都构建一次,所以我们最好将这些第三方库提取出来单独打包,这样有利于减少打包时间。
官方插件是 DllPlugin(个人推荐)。网上有人推荐另一个插件 —— autodll-webpack-plugin,也很好用(但是不推荐)。
下面是它的配置方法:
首先安装:
npm i autodll-webpack-plugin -D
然后在 webpack.base.conf.js 中引入:
const AutoDllPlugin = require('autodll-webpack-plugin');
然后在 plugins 属性中添加这个插件:
new AutoDllPlugin({
inject: true, // will inject the DLL bundle to index.html
debug: true,
filename: '[name]_[hash].js',
path: './dll',
entry: {
vendor: ['react', 'react-router', 'antd']
}
})
inject 为 true,插件会自动把打包出来的第三方库文件插入到 HTML。filename 是打包后文件的名称。path 是打包后的路径。entry 是入口,vendor 是你指定的名称,数组内容就是要打包的第三方库的名称,不要写全路径,Webpack 会自动去 node_modules 中找到的。
每次打包,这个插件都会检查注册在 entry 中的第三方库是否发生了变化,如果没有变化,插件就会使用缓存中的打包文件,减少了打包的时间,这时 Hash 也不会变化。
下面重点讲下dllPlugin
1.在原来的打包配置文件基础上面,我们需要新建一个webpack.dll.conf.js文件。
配置如下:
// webpack.dll.conf.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
mode: 'production',
entry: {
vendor: [
'react',//提前打包一些基本不怎么修改的文件 一般都是放生产环境要用到的第三方库
'react-dom',
//'antd',
]
},
output: {
path: path.resolve(__dirname, '../static/dll/js'), //放在项目的static/dll/js目录下面
filename: '[name]_[hash].dll.js', //打包文件的名字
library: '[name]_library' //可选 暴露出的全局变量名
// vendor.dll.js中暴露出的全局变量名。
// 主要是给DllPlugin中的name使用,
// 故这里需要和webpack.DllPlugin中的`name: '[name]_library',`保持一致。
},
plugins: [
new webpack.DllPlugin({
path: path.join(__dirname, '../static/dll', '[name]-manifest.json'), //生成上文说到清单文件,放在static/dll文件下面,这个看你自己想放哪里了。
name: '[name]_library'
}),
]
};
2.在webpack.prod.conf.js文件需要做如下修改,在plugins下面加入如下代码:add-asset-html-webpack-plugin
// +++ 这个依赖需要 npm 下
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
// +++
plugins: [
//+++
//这个是基于webpack的,要引入webpack
new webpack.DllReferencePlugin({
context: path.resolve(__dirname, '..'),
manifest: require('../static/vendor-manifest.json')
}),
new AddAssetHtmlPlugin({
filepath: path.resolve(__dirname,'../static/js/*.dll.js')
}),
//+++
]
3.修改package.json中的 “scripts” 添加一个命令
"build:dll": "webpack --config build/webpack.dll.conf.js",
4.执行
npm run build:dll //这个命令在最初执行一次之后,之后发布都不需要再重复执行了,除非webpack.dll.conf.js里面的依赖文件有升级。
//发布之前的打包
npm run build
八、提取共同代码:
使用 splitChucksPlugin 插件,这是 Webpack 自带的,不用安装第三方依赖。
使用方法:
在 webpack.base.conf.js 的 plugins 属性中添加如下插件对象;
new webpack.optimize.SplitChunksPlugin()
这代表你将使用默认的提取配置来提取你的公共代码,如果你不想使用默认配置,请给插件构造函数传入配置对象.
具体怎么配置,请参考冷星大神的博客 —— webpack4——SplitChunksPlugin使用指南,里面关于配置项的作用介绍得很清楚很详细。
九、抽取 CSS 到单文件
这个功能的配置方法在 Vue Loader 官网交代得很清楚了。
使用的是 mini-css-extract-plugin 插件,首先安装:
npm i mini-css-extract-plugin -D
然后在配置文件头部引入:
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
然后当你要抽取 CSS 的时候(比如生产环境打包) ,你就把原来配置文件中的所有 style-loader 替换为 MiniCssExtractPlugin.loader,其他的什么 css-loader、less-loader 等等都不要动。
注意:建议写在生产环境配置中
最后,修改 plugins 选项,插入如下插件:
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
打包之后,你会发现所有的 CSS 代码都被抽取到了一个单独的 CSS 文件当中。
十、图片压缩
使用:配置image-webpack-loader
npm 一下
npm i image-webpack-loader -D
然后配置module.rules
{
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: { // 这里的options选项参数可以定义多大的图片转换为base64
name: '[name].[ext]',
limit: 20000, // 表示小于20kb的图片转为base64,大于20kb的是路径
outputPath: 'images' //定义输出的图片文件夹
}
},
{ //压缩图片要在file-loader之后使用
loader: 'image-webpack-loader',
options: {
bypassOnDebug: true
}
}
]
}
十一、输出目录整理
到这步,基本上是重头到尾OK了,但是有点忍不了,npm run build后的dist文件夹好乱,js/css/font/html/img全在一起,mmp什么情况及,强迫症完全忍受不了
1.设置webpack.base.conf.js中的 output.filename 赋值 ./js/[name].[hash].js
2.设置font字体的输出路径 webpack.base.conf.js 中的 module.rules 中
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: './fonts/[name]-[hash].[ext]'//这个是重点
}
}
]
},
3.设置图片输出位置
{
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: { // 这里的options选项参数可以定义多大的图片转换为base64
name: '[name].[ext]',
limit: 20000, // 表示小于50kb的图片转为base64,大于50kb的是路径
outputPath: './images' //定义输出的图片文件夹
}
},
{ //压缩图片要在file-loader之后使用
loader: 'image-webpack-loader',
options: {
bypassOnDebug: true
}
}
]
},
4.设置css输出路径
注意点,修改 MiniCssExtractPlugin 中的配置
new MiniCssExtractPlugin({
filename: "./css/[name].css",//这里是重点
chunkFilename: "[id].css"
})
5.设置我们之前单独打包的第三方库的输出
注意点,修改AddAssetHtmlPlugin 中的配置
new AddAssetHtmlPlugin({
filepath: path.resolve(__dirname,'../static/dll/js/*.dll.js'),
publicPath: "./js",//这个是index.html模版引入的路径,一般都配合outputPath用
outputPath: './js'//这个是第三方库打包后的输出路径,一般都配合publicPath用
}),
来源: