实践结果
package.json
{
"name": "webpack_dev_test_better",
"version": "1.0.0",
"devDependencies": {
"css-loader": "^3.4.2",
"file-loader": "^5.1.0",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0",
"less": "^3.11.1",
"less-loader": "^5.0.0",
"style-loader": "^1.1.3",
"url-loader": "^3.0.0",
"webpack": "^4.42.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3"
}
}
entry.js(js模块热替换的代码示例)
import {
status} from './test.js'
console.log('status:'+status);
if (module.hot) {
// test.js模块热更新开启,替换后触发回调(其它模块不会重新打包构建)。
module.hot.accept('./test.js', function() {
console.log('js HMR success');
console.log('status:'+status);
});
}
webpack.config.js
const {
resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry:["./src/js/entry.js","./src/index.html"],
output:{
path:resolve(__dirname,'build'),
filename:'js/built.js'
},
module:{
rules:[
{
test:/\.less$/,
use:['style-loader','css-loader','less-loader']
},
{
test:/\.css$/,
use:['style-loader','css-loader']
},
{
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
esModule:false,
outputPath: 'images'
}
},
{
test: /\.html$/,
loader: 'html-loader'
},
{
exclude: /\.(html|js|css|less|jpg|png|gif)/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]',
outputPath: 'media'
}
}
]
},
plugins:[
new HtmlWebpackPlugin({
template:'./src/index.html'
})
],
mode:'development',
devServer: {
contentBase: resolve(__dirname, 'build'),// 项目构建后路径
compress: true,// 启动gzip压缩
port: 3000,// 端口号
open: true,// 自动打开浏览器
hot: true // 启动模块热替换HMR
},
// 开发环境优选:'eval-source-map',生产环境视情况而定(源代码是否要隐藏?调试是否要更友好?)。
// eval:内联source-map,速度最快。source-map:定位源代码报错位置,调试最优。
devtool: 'eval-source-map'
};
实践准备
创建项目:webpack_dev_test_better
初始化
npm init
npm i webpack webpack-cli -D
// 上篇博客编译打包功能涉及到的所有依赖
npm i css-loader file-loader html-loader html-webpack-plugin less less-loader style-loader url-loader -D
- src/js/entry.js:空文件
- webpack.config.js:复制开发环境打包这篇博客产生的webpack.config.js文件
const {
resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry:"./src/js/entry.js",
output:{
path:resolve(__dirname,'build'),
filename:'js/built.js'
},
module:{
rules:[
{
test:/\.less$/,
use:['style-loader','css-loader','less-loader']
},
{
test:/\.css$/,
use:['style-loader','css-loader']
},
{
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
esModule:false,
outputPath: 'images'
}
},
{
test: /\.html$/,
loader: 'html-loader'
},
{
exclude: /\.(html|js|css|less|jpg|png|gif)/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]',
outputPath: 'media'
}
}
]
},
plugins:[
new HtmlWebpackPlugin({
template:'./src/index.html'
})
],
mode:'development'
};
实践过程
一:自动构建(devServer)【代码修改后】
1.解决问题
- 任意一个模块发生变化,都需要再次手动执行 打包、刷新浏览器 等重复步骤。
2.解决方案
- webpack配置devServer(热部署)
注意:热部署功能并不能监听webpack.config.js文件,当修改了webpack配置,新配置要想生效,必须重启webpack服务。
3.操作示例
- 下载devServer
npm i webpack-dev-server -D
- 配置devServer:webpack.config.js
module.exports = {
...,
mode:'devlopment',
devServer: {
contentBase: resolve(__dirname, 'build'),// 项目构建后路径
compress: true,// 启动gzip压缩
port: 3000,// 端口号
open: true// 自动打开浏览器
}
}
- 启动devServer
// 内存中编译打包,不会有build输出
npx webpack-dev-server
- 启动结果:
- 自动编译打包,并未输出build文件夹。
- 自动启动默认浏览器并访问http://localhost:3000/,呈现与浏览器打开build/index.html一致。
- 代码修改后,浏览器自动刷新,并呈现修改后的代码逻辑。
二:加快构建(HMR)【代码修改后】
1.解决问题
- devServer普通配置启动后,一个模块发生变化,所有模块都要重新打包,构建速度慢。
2.解决方案
- 启动devServer的HMR功能(hot module replacement 模块热替换)。
作用:一个模块发生变化,只会重新打包这一个模块,极大提速构建速度。
3.操作示例
不同模块类型对HMR功能的支持情况不同,下面实践测试不同类型模块的支持情况及其配置。
- 样式文件:配置后可以使用HMR功能(开发环境下使用的style-loader内部实现了)。
module.exports = {
entry:["./src/js/entry.js","./src/index.html"],
...,
mode:'devlopment',
devServer: {
contentBase: resolve(__dirname, 'build'),
compress: true,
port: 3000,
open: true,
// 实践证明:
// 1.配置后,样式类型模块成功HMR.
// 2.但会让html文件热部署失效,需把index.html配置为entry)
hot: true // 启动模块热替换HMR。
}
}
- js文件:默认不能使用HMR功能。需要在需要支持HMR功能的js文件(不能是入口文件)中添加支持HMR功能的代码。
- 证明:默认不支持HMR
- 证明:在src/js/entry.js中添加代码后支持src/js/test.js文件的HMR
- 证明:默认不支持HMR
// entrt.js
import {
status} from './test.js'
console.log('status:'+status);
//let status2 = 1;
// 实验证明1:下段代码放在test.js中不能触发HMR。
if (module.hot) {
// test.js模块热更新开启,替换后触发回调(其它模块不会重新打包构建)。
module.hot.accept('./test.js', function() {
console.log('js HMR success');
console.log('status:'+status);
});
// 实验证明2:入口文件entry.js不能支持HMR。
//module.hot.accept('./entry.js', function() {
// console.log('js HMR success');
// console.log('status:'+status2);
//});
}
- html文件:默认不能也不需要HMR功能。
三:方便调试(source-map)【代码报错后】
1.解决问题
- 打包前后代码千差万别,打包后代码出错很难定位到打包前代码的出错位置,不方便调试。
2.解决方案
- 配置devtool:所有可选配置项为 [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
3.操作示例
-1.报错源文件:src/js/test.js
export let status = 6;
status.fnTest();
- 2.不使用source-map时的报错信息与报错文件,受到打包后的干扰。
- 3.配置source-map
module.exports = {
...,
// 开发环境优选:'eval-source-map',生产环境视情况而定(源代码是否要隐藏?调试是否要更友好?)。
// eval:内联source-map,速度最快。source-map:定位源代码报错位置,调试最优。
devtool: 'eval-source-map'
}
- 4.使用source-map后的报错信息与报错文件,不受打包后的干扰,准确定位报错源文件及源文件的报错行。