目前我们开发的代码,为了运行需要有两个操作:
npm run build
,编译相关的代码;查看效果
;这个过程经常操作会影响我们的开发效率,我们希望可以做到,当文件发生变化时,可以自动的完成 编译
和 展示
;
为了完成自动编译,webpack提供了几种可选的方式:
上面的方式可以监听到文件的变化
,但是事实上它本身是没有自动刷新浏览器的功能
的:
live reloading
(实时重新加载)的功能;安装webpack-dev-server
npm install webpack-dev-server -D
修改配置文件,启动时加上serve参数:
module.exports = {
devServer: {}
}
// package.json
{
"scripts": [
"serve": "webpack serve --config wk.config.js"
]
}
webpack-dev-server 在编译之后不会写入到任何输出文件,而是将 bundle 文件保留在内存中:
memfs
(memory-fs webpack自己写的)什么是HMR呢?
Hot Module Replacement
,翻译为模块热替换
;HMR通过如下几种方式,来提高开发的速度:
如何使用HMR呢?
修改webpack的配置:
module.exports = {
devServer: {
hot: true
}
}
浏览器可以看到如下效果:
但是你会发现,当我们修改了某一个模块的代码时,依然是刷新的整个页面:
if (module.hot) {
module.hot.accept("./utils.js", () => {
console.log("util更新了")
})
}
有一个问题:在开发其他项目时,我们是否需要经常手动去写入 module.hot.accpet
相关的API呢?
事实上社区已经针对这些有很成熟的解决方案了:
host设置主机地址:
localhost 和 0.0.0.0 的区别:
localhost
:本质上是一个域名,通常情况下会被解析成127.0.0.1;127.0.0.1
:回环地址(Loop Back Address),表达的意思其实是我们主机自己发出去的包,直接被自己接收;
0.0.0.0
:监听IPV4上所有的地址,再根据端口找到不同的应用程序;
port
设置监听的端口,默认情况下是8080
open
是否打开浏览器:
compress
是否为静态文件开启gzip compression:
proxy是我们开发中非常常用的一个配置选项,它的目的设置代理来解决跨域访问
的问题:
我们可以进行如下的设置:
这个 changeOrigin官方说的非常模糊,通过查看源码我发现其实是要修改代理请求中的headers中的host属性:
historyApiFallback是开发中一个非常常见的属性,它主要的作用是解决SPA页面在路由跳转之后,进行页面刷新时,返回404 的错误。
boolean值:默认是false
object类型的值,可以配置rewrites属性:
事实上devServer中实现historyApiFallback功能是通过connect-history-api-fallback库的:
目前我们所有的webpack配置信息都是放到一个配置文件中的:webpack.config.js
某些配置是在开发环境需要使用的
,某些配置是在生产环境需要使用的
,当然某些配置是在开发和生成环境都会使用的;对配置进行划分
,方便我们维护和管理;那么,在启动时如何可以区分不同的配置呢?
两个不同的配置文件
,开发和生产时,分别加载不同的配置文件即可;入口配置文件
,通过设置参数来区分它们;"script": {
"build": "webpack --config ./config/common.config --env production",
"serve": "webpack serve --config ./config/common.config"
}
我们之前编写入口文件的规则是这样的:./src/index.js
,但是如果我们的配置文件所在的位置变成了 config 目录,我们是否应该变成 …/src/index.js呢?
context
;context的作用是用于解析入口
(entry point)和加载器
(loader):
启动目录
)module.exports = {
context: path.resolve(__dirname, "./"),
entry: "../src/index.js"
}
module.exports = {
context: path.resolve(__dirname, "../"),
entry: "./src/index.js"
}
这里我们创建三个文件:
webpack.dev.conf.js
const { merge } = require("webpack-merge")
const commonConfig = require("./webpack.comm.config")
module.exports = merge(commonConfig, {
mode: "development",
devServer: {
hot: true,
// host: "0.0.0.0",
// port: 8888,
// open: true
// compress: true
}
})
webpack.comm.conf.js
const path = require("path")
const { VueLoaderPlugin } = require("vue-loader/dist/index")
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { DefinePlugin } = require("webpack")
module.exports = {
entry: "./src/main.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "../build")
},
resolve: {
extensions: [".js", ".json", ".vue", ".jsx", ".ts", ".tsx"],
alias: {
utils: path.resolve(__dirname, "../src/utils")
}
},
module: {
rules: [
{
test: /\.css$/,
use: [ "style-loader", "css-loader", "postcss-loader" ]
},
{
test: /\.less$/,
use: [ "style-loader", "css-loader", "less-loader", "postcss-loader" ]
},
{
test: /\.(png|jpe?g|svg|gif)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 60 * 1024
}
},
generator: {
filename: "img/[name]_[hash:8][ext]"
}
},
{
test: /\.js$/,
use: [
{
loader: "babel-loader"
}
]
},
{
test: /\.vue$/,
loader: "vue-loader"
}
]
},
plugins: [
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
title: "电商项目",
template: "./index.html"
}),
new DefinePlugin({
BASE_URL: "'./'",
coder: "'abc'",
counter: "123"
})
]
}
webpack.prod.conf.js
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
const { merge } = require("webpack-merge")
const commonConfig = require("./webpack.comm.config")
module.exports = merge(commonConfig, {
mode: "production",
output: {
clean: true
},
plugins: [
new CleanWebpackPlugin()
]
})