目前我们开发的代码,为了运行需要有两个操作:
操作一:npm run build,编译相关的代码;
操作二:通过live server或者直接通过浏览器,打开index.html代码,查看效果;
这个过程经常操作会影响我们的开发效率,我们希望可以做到,当文件发生变化时,可以自动的完成 编译 和 展示;
为了完成自动编译,webpack提供了几种可选的方式:
webpack给我们提供了watch模式:
方式一:在导出的配置中,添加 watch: true;
方式二:在启动webpack的命令中,添加 –watch的标识;
方式二是在package.json的 scripts 中添加一个 watch 的脚本。
上面的方式可以监听到文件的变化,但是事实上它本身是没有自动刷新浏览器的功能的:
安装webpack-dev-server
npm i webpack-dev-server -D
修改配置文件,告知 dev server,从什么位置查找文件:
module.exports = {
// 其他配置均省略
// 热部署(热更新)的配置
devServer: {
// 如果需要的资源没有在webpack里面加载到,会去contentBase指定的文件夹里面寻找
contentBase: "./public",
},
// 打包的是node环境 还是 web 环境
target: "web"
}
什么是HMR呢?
HMR通过如下几种方式,来提高开发的速度:
如何使用HMR呢?
修改webpack的配置:
浏览器可以看到如下效果:
但是你会发现,当我们修改了某一个模块的代码时,依然是刷新的整个页面:
这是因为我们需要去指定哪些模块发生更新时,进行HMR;
有一个问题:在开发其他项目时,我们是否需要经常手动去写入 module.hot.accpet相关的API呢?
那么HMR的原理是什么呢?如何可以做到只更新一个模块中的内容呢?
HMR Socket Server,是一个socket的长连接:
默认值是localhost;如果希望其他地方也可以访问,可以设置为 0.0.0.0;
port设置监听的端口,默认情况下是8080
open是否打开浏览器:
compress是否为静态文件开启gzip compression:
默认值是false,可以设置为true;
proxy是我们开发中非常常用的一个配置选项,它的目的设置代理来解决跨域访问的问题:
比如我们的一个api请求是 http://localhost:8888,但是本地启动服务器的域名是 http://localhost:8000,这 个时候发送网络请求就会出现跨域的问题;
那么我们可以将请求先发送到一个代理服务器,代理服务器和API服务器没有跨域的问题,就可以解决我们的跨 域问题了;
我们可以进行如下的设置
target:表示的是代理到的目标地址,比如 /api-hy/moment会被代理到 http://localhost:8888/api-mhy/moment
pathRewrite:默认情况下,我们的 /api-hy 也会被写入到URL中,如果希望删除,可以使用pathRewrite;
secure:默认情况下不接收转发到https的服务器上,如果希望支持,可以设置为false;
changeOrigin:它表示是否更新代理后请求的headers中host地址
module.exports = {
devServer:{
// 配置 proxy 代理 解决跨域问题
// 只适用于开发阶段
// 相当于请求交给本地服务器发送给远程的服务器
// 然后请求到的数据在给页面的请求
proxy: {
// 配置映射关系
// 相当于在项目中请求 /api地址 就是请求后面配置的地址
"/api": {
target: "http://www.baidu.com",
// 路径重写 将 /api开头的请求地址的 /api 给替换为 "" 空字符串
pathRewrite: {
"^/api": ""
},
// 默认不支持 转发到https的服务器上,如果需要,设置 secure为false
secure: false,
// 是否更新代理后请求的headers中的host地址
changeOrigin: true
}
},
}
}
这个 changeOrigin官方说的非常模糊,通过查看源码我发现其实是要修改代理请求中的headers中的host属性:
resolve用于设置模块如何被解析:
webpack能解析三种文件路径:
绝对路径 :由于已经获得文件的绝对路径,因此不需要再做进一步解析。
相对路径
模块路径
在 resolve.modules中指定的所有目录检索模块;
如果是一个文件:
如果是一个文件夹:
会在文件夹中根据 resolve.mainFiles配置选项中指定的文件顺序查找;
extensions是解析到文件时自动添加扩展名:
另一个非常好用的功能是配置别名alias
目前我们所有的webpack配置信息都是放到一个配置文件中的:webpack.config.js
那么,在启动时如何可以区分不同的配置呢?
我们之前编写入口文件的规则是这样的:./src/index.js,但是如果我们的配置文件所在的位置变成了 config 目录, 我们是否应该变成 …/src/index.js呢
context的作用是用于解析入口(entry point)和加载器(loader):
官方说法:默认是当前路径(但是经过我测试,默认应该是webpack的启动目录)
另外推荐在配置中传入一个值;
这里我们创建三个文件:
将公共代码和生产环境的配置以及开发时的配置分开,然后使用webpack-merge插件进行配置文件的合并。
webpack.common.js
// 公共的环境
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { DefinePlugin } = require("webpack");
// 引入Vue-loader的插件 帮助loader做一些事情
const { VueLoaderPlugin } = require("vue-loader/dist/index");
module.exports = {
entry: './src/index.js',
output: {
path: path.join(__dirname, '../build'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.css$/,
use: [
"style-loader",
"css-loader",
"postcss-loader"
]
},
{
test: /\.less$/,
use: [
"style-loader",
"css-loader",
"postcss-loader",
"less-loader"
]
},
// 处理图片
{
test: /\.(png|jpe?g|JPG)$/,
type: "asset",
generator: {
filename: "image/[name]-[hash:6][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 40 * 1024
}
}
},
{
test: /\.(eot|ttf|woff2?)$/,
type: 'asset/resource',
generator: {
// 这里使用的是filename属性 别忘了!!!!!
filename: 'font/[name]-[hash:6][ext]'
}
},
// 对js代码进行转换
{
test: /\.js$/,
loader: "babel-loader",
},
// 配置 .vue文件的加载打包规则
{
test: /\.vue$/,
loader: "vue-loader"
}
]
},
plugins: [
new HtmlWebpackPlugin({
// 要注意:相对路径是从项目根路径出发的,!!!!!!!!!!!
template: "./public/index.html",
title: "babel 学习!"
}),
new DefinePlugin({
BASE_URL: "'./'",
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false
}),
new VueLoaderPlugin()
],
// resolve 模块解析
// 用于设置模块如何被解析
resolve: {
// 默认值就是 node_modules
modules: ["node_modules"],
// 文件拓展名 默认值有js mjs json wasm
extensions: [".js", ".ts", ".json", ".vue"],
// 如果加载的模块是一个文件夹 会自动去文件夹里面的 index.xx 文件来加载
mainFiles: ["index"],
// 配置路径的别名
alias: {
// 在路径的地方使用的js,就代表后面的这个路径
"js": path.resolve(__dirname, "../src/js")
}
},
// 打包的是node环境 还是 web 环境
target: "web"
}
webpack.prod.js
// 生产环境
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyPlugin = require("copy-webpack-plugin");
// 合并当前环境和公共的common环境 使用 webpack-merge插件
const { merge } = require('webpack-merge')
// 导入 公共环境的配置
const commonConfig = require('./webpack.common');
module.exports = merge(commonConfig, {
mode: 'production',
devtool: 'eval',
plugins: [
new CleanWebpackPlugin(),
new CopyPlugin({
patterns: [
{
from: "public",
globOptions: {
ignore: "**/index.html"
}
}
]
}),
]
}
)
webpack.dev.js
// 开发环境
// 合并当前环境和公共的common环境 使用 webpack-merge插件
const { merge } = require('webpack-merge')
// 导入 公共环境的配置
const commonConfig = require('./webpack.common');
module.exports = merge(commonConfig, {
mode: 'development',
devtool: 'source-map',
// 热部署(热更新)的配置
devServer: {
// 如果需要的资源没有在webpack里面加载到,会去contentBase指定的文件夹里面寻找
contentBase: "../public",
// 开启HMR 热模块替换 (最好配置一下target属性,跟DevServer同级)
hot: true,
// 设置ip地址 主机
host: "127.0.0.1",
// 端口号
port: 8800,
// 是否在服务启动时打开浏览器
open: true,
// 开启 gzip压缩 传输速率提高(一般html这种文件不会压缩)
compress: true,
// 配置 proxy 代理 解决跨域问题
// 只适用于开发阶段
// 相当于请求交给本地服务器发送给远程的服务器
// 然后请求到的数据在给页面的请求
proxy: {
// 配置映射关系
// 相当于在项目中请求 /api地址 就是请求后面配置的地址
"/api": {
target: "http://www.baidu.com",
// 路径重写 将 /api开头的请求地址的 /api 给替换为 "" 空字符串
pathRewrite: {
"^/api": ""
},
// 默认不支持 转发到https的服务器上,如果需要,设置 secure为false
secure: false,
// 是否更新代理后请求的headers中的host地址
changeOrigin: true
}
},
}
}
)