以下均在 Webpack 4.42.1 版本环境下
mkdir webpack-simple
cd webpack-simple
npm init
npm i --save-dev webpack webpack-cli
touch index.html
touch webpack.config.js
mkdir src
cd src
touch main.js
在 index.html 中引入 bundle.js
<script src="./dist/bundle.js"></script>
在 main.js 中写上如下代码
console.info('hello webpack');
webpack.config.js 如下
const path = require('path'); // node path 模块用来处理文件路径
module.exports = {
entry: {
app: './src/main.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist') // __dirname 为当前文件路径,dist 为绝对路径
}
}
执行「webpack」命令,在根目录「dist」文件夹下就会看到一个「bundle.js」文件
打开文件后你会发现代码量很多,实际上我们却只写了一行代码,这其中前面的代码都是 webpack 生成的「runtime」和「mainfest」代码,后面会讲到。
用浏览器打开 index.html,F12 能看到打印输出了 “hello webpack”
有 html 还不够,我们还得写样式呀,现在在 src 目录下新建 style.css 文件
body {
background: pink;
}
在 main.js 中引入 style.css
import './style.css'
console.info('hello webpack')
现在我们再次执行「webpack」命令,会发现报下面的错误
这是因为「webpack」只能对「js」文件进行打包编译,碰到「css」文件它识别不出来,这个时候我们就要加上「loader」来让它识别「css」文件。
执行 “npm i --save-dev style-loader css-loader”
修改「webpack.config.js」文件如下
const path = require('path')
module.exports = {
entry: {
app: './src/main.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
// 匹配 css 文件
test: /\.css$/,
/*
先使用 css-loader 处理,返回的结果交给 style-loader 处理。
css-loader 将 css 内容存为 js 字符串,并且把 background,@font-face 等引用的图片,
字体文件交给指定的 loader 打包
*/
use: ['style-loader', 'css-loader']
}
]
}
}
再次执行「webpack」命令,就不会报错了,打开「index.html」就能看到添加样式成功了。引入的图片、字体、html等,都有相应的 loader 来编译。
rules: [
{
/*
使用 babel 编译 ES6 / ES7 / ES8 为 ES5 代码
使用正则表达式匹配后缀名为 .js 的文件
*/
test: /\.js$/,
// 排除 node_modules 目录下的文件,npm 安装的包不需要编译
exclude: /node_modules/,
/*
use 指定该文件的 loader,值可以是字符串或者数组。
这里先使用 eslint-loader 处理,返回的结果交给 babel-loader 处理。loader 的处理顺序是从最后一个到第一个
eslint-loader 用来检查代码,如果有错误,编译的时候会报错
babel-loader 用来编译 js 文件
*/
use: ['babel-loader', 'eslint-loader']
},
{
// 匹配 html 文件
test: /\.html$/,
/*
使用 html-loader,将 html 内容存为 js 字符串,比如当遇到
import htmlString from './template.html';
template.html 的文件内容会被转成一个 js 字符串,合并到 js 文件里
*/
use: 'html-loader'
},
{
// 匹配 css 文件
test: /\.css$/,
/*
先使用 css-loader 处理,返回的结果交给 style-loader 处理。
css-loader 将 css 内容存为 js 字符串,并且把 background,@font-face 等引用的图片,
字体文件交给指定的 loader 打包,类似上面的 html-loader,用什么 loader 同样在 loaders 对象中定义,等会下面就会看到
*/
use: ['style-loader', 'css-loader']
},
{
/*
匹配各种格式的图片和字体文件
上面 html-loader 会把 html 中 标签的图片解析出来,文件名匹配到这里的 test 的正则表达式,
css-loader 引用的图片和字体同样会匹配到这里的 test 条件
*/
test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,
/*
使用 url-loader,它接受一个 limit 参数,单位为字节(byte)
当文件体积小于 limit 时,url-loader 把文件转为 Data URI 的格式内联到引用的地方
当文件大于 limit 时,url-loader 会调用 file-loader,把文件储存到输出目录,并把引用的文件路径写成输出后的路径
比如 views/foo/index.html 中
会被编译成
而
会被编译成
*/
use: [
{
loader: 'url-loader',
options: {
limit: 10000
}
}
]
}
]
这时候如果我们修改「webpack.config.js」中「output」的「filename」的名字,「index.html」中的名字也要修改,有没有办法在编译的时候,自动生成「index.html」并且自动引入「entry」文件?
「html-webpack-plugin」就是用来做这个的
安装
npm i --save-dev html-webpack-plugin
修改「webpack.config.js」
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: {
app: './src/main.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
// 匹配 css 文件
test: /\.css$/,
/*
先使用 css-loader 处理,返回的结果交给 style-loader 处理。
css-loader 将 css 内容存为 js 字符串,并且把 background,@font-face 等引用的图片,
字体文件交给指定的 loader 打包
*/
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
/*
template 参数指定入口 html 文件路径,插件会把这个文件交给 webpack 去编译,
webpack 按照正常流程,找到 loaders 中 test 条件匹配的 loader 来编译,那么这里 html 就是匹配的 html-loader
html-loader 编译后产生的字符串,会由 html-webpack-plugin 储存为 html 文件到输出目录,默认文件名为 index.html
可以通过 filename 参数指定输出的文件名
html-webpack-plugin 也可以不指定 template 参数,它会使用默认的 html 模版
*/
template: './index.html'
})
]
}
删除「index.html」中引入「bundle.js」的脚本,执行「webpack」命令,成功后可以看到在「dist」目录下多了一个「index.html」,并且已经引入了「bundle.js」,用浏览器打开的效果跟刚才是一样的。
在「main.js」中打印一个错误日志,并重新运行「wenpack」编译,用浏览器打开「index.html」
import './style.css'
console.info('hello webpack')
console.error('这是一个错误')
我们看到,浏览器只会提示我们「bundle.js」中有错误,但是定位不到原始文件的位置,这个时候我们要使用「source map」,在「webpack.config.js」中增加配置
module.exports = {
entry: {
app: './src/main.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
devtool: 'inline-source-map',
...
}
重现编译,刷新浏览器,我们就能看到精确定位代码位置
当我们在「main.js」中加入包含「ES6」语法的代码时,运行「npm run build」编译时并没有报错
function printArr(){
const arr = Array(5).fill('6');
arr.map(item => {
console.log(item)
})
};
printArr();
这是因为 webpack 4 依赖包中使用「terser-webpack-plugin」替代了之前一直使用的「uglifyjs-webpack-plugin」作为它的内置插件,并做代码压缩处理.
每次要编译代码时,手动运行 webpack 就会变得很麻烦。
「webpack-dev-server」 提供了一个简单的 web 服务器,并且能够实时重新加载(live reloading)。
npm i --save-dev webpack-dev-server
webpack.config.js 加入配置
module.exports = {
devServer: {
contentBase: './dist'
}
}
以上配置告知 webpack-dev-server,在 localhost:8080 下建立服务,将 dist 目录下的文件,作为可访问文件。
让我们添加一个 script 脚本,可以直接运行开发服务器(dev server):
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --open"
}
现在执行「npm run dev」,浏览器会自动打开页面,修改「main.js」的内容
console.log('hello webpack-dev-server')
我们可以看到浏览器上的内容已经实时更新了,并不用再刷新浏览器来查看结果
模块热替换(Hot Module Replacement 或 HMR)是 webpack 提供的最有用的功能之一。它允许在运行时更新各种模块,而无需进行完全刷新。
启用此功能实际上相当简单。而我们要做的,就是更新 webpack-dev-server 的配置,和使用 webpack 内置的 HMR 插件。
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack'); // +
module.exports = {
entry: {
app: './src/main.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
hot: true // +
},
module: {
rules: [
{
// 匹配 css 文件
test: /\.css$/,
/*
先使用 css-loader 处理,返回的结果交给 style-loader 处理。
css-loader 将 css 内容存为 js 字符串,并且把 background,@font-face 等引用的图片,
字体文件交给指定的 loader 打包,类似上面的 html-loader,用什么 loader 同样在 loaders 对象中定义,等会下面就会看到
*/
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
}),
new webpack.NamedModulesPlugin(), // +
new webpack.HotModuleReplacementPlugin() // +
]
}
新建三个文件「webpack.base.conf.js」「webpack.dev.conf.js」「webpack.prod.conf.js」
// webpack.base.conf.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack');
module.exports = {
entry: {
app: './src/main.js'
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
// 匹配 css 文件
test: /\.css$/,
/*
先使用 css-loader 处理,返回的结果交给 style-loader 处理。
css-loader 将 css 内容存为 js 字符串,并且把 background,@font-face 等引用的图片,
字体文件交给指定的 loader 打包,类似上面的 html-loader,用什么 loader 同样在 loaders 对象中定义,等会下面就会看到
*/
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
})
]
}
// webpack.dev.conf.js
const webpack = require('webpack')
const merge = require('webpack-merge'); // 合并配置
const common = require('./webpack.base.conf');
module.exports = merge(common,{
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist'
}
})
// webpack.prod.conf.js
const webpack = require('webpack');
const merge = require('webpack-merge');
const common = require('./webpack.base.conf');
module.exports = merge(common,{
mode: 'production',
})
// package.json
"scripts": {
"start": "webpack-dev-server --inline --progress --config webpack.dev.conf.js",
"build": "webpack --config webpack.prod.conf.js"
}
先引入「lodash」
npm i --save lodash
main.js 中添加如下代码, 并执行「npm run build」命令
function component() {
var element = document.createElement('div')
element.innerHTML = _.join(['hello', 'webpack'], '')
return element
}
document.body.appendChild(component())
在「dist」目录下,我们能看到「app.bundle.js」和「index.html」两个文件,但是打开「bundle.js」会发现我们「main.js」中引入的「lodash」也打包进去了,这样在后续引入多个第三方包后会使得我们「app.bundle」文件越来越大,所以我们要把第三方插件抽离出来,统一打包到一个文件中去
1、第三方插件分离
module.exports = {
optimization: {
splitChunks: {
// 替代 webpack.optimize.CommonsChunkPlugin
cacheGroups: {
vendors: {
chunks: "all",
test: /[\\/]node_modules[\\/]/,
name: "vendors",
minChunks: 1,
minSize: 30000,
priority: 10 // 设置优先级
}
}
}
}
}
此时在「dist」目录下会多一个「vendors.cd5da2e8640d819f8b41.js」文件,打开就会发现里面是「lodash」的代码