为什么要搭建本地服务器?

Webpack watch

webpack-dev-server
- cnpm install --save-dev webpack-dev-server
- 注意,脚本是 "serve": "webpack serve"

webpack-dev-middleware 【了解】

webpack-dev-middleware的使用

认识模块热替换(HMR)

开启HMR

框架的HMR

React的HMR
- cnpm install -D @pmmmwh/react-refresh-webpack-plugin react-refresh

Vue的HMR
- cnpm install vue-loader vue-template-compiler -D

HMR的原理

HMR的原理图

webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "./build")
},
devServer: {
hot: true
},
module: {
rules: [
{
test: /\.jsx?$/i,
use: "babel-loader"
},
{
test: /\.vue$/i,
use: "vue-loader"
},
{
test: /\.css/i,
use: [
"style-loader",
"css-loader"
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: "./index.html"
}),
new ReactRefreshWebpackPlugin(),
new VueLoaderPlugin()
]
}
package.json
{
"name": "webpack_devserver",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack",
"watch": "webpack --watch",
"serve": "webpack serve"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.12.17",
"@babel/preset-env": "^7.12.17",
"@babel/preset-react": "^7.12.13",
"@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
"babel-loader": "^8.2.2",
"css-loader": "^5.0.2",
"html-webpack-plugin": "^5.2.0",
"react-refresh": "^0.9.0",
"style-loader": "^2.0.0",
"vue-loader": "^15.9.6",
"vue-template-compiler": "^2.6.12",
"webpack": "^5.23.0",
"webpack-cli": "^4.5.0",
"webpack-dev-server": "^3.11.2"
},
"dependencies": {
"express": "^4.17.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"vue": "^2.6.12",
"webpack-dev-middleware": "^4.1.0"
}
}
index.js
import "./math";
import React from 'react';
import ReactDom from 'react-dom';
import ReactApp from './App.jsx';
import Vue from 'vue';
import VueApp from './App.vue';
console.log("Hello 哈哈哈");
console.log("abc");
if (module.hot) {
module.hot.accept("./math.js", () => {
console.log("math模块发生了更新~");
});
}
ReactDom.render(<ReactApp/>, document.getElementById("app"));
new Vue({
render: h => h(VueApp)
}).$mount("#root");
App.vue
<template>
<div id="app">
<h2 class="title">{{ message }}</h2>
</div>
</template>
<script>
export default {
data() {
return {
message: "Hello 哈哈哈",
};
},
};
</script>
<style scoped>
.title {
color: blue;
}
</style>
App.jsx
import React, { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
this.state = {
message: "Hello React",
};
}
render() {
return (
<div>
<h2>{this.state.message}</h2>
</div>
);
}
}
export default App;
babel.config.js
module.exports = {
presets: [
["@babel/preset-env"],
["@babel/preset-react"],
],
plugins: [
["react-refresh/babel"]
]
}
output的publicPath

devServer的publicPath

devServer的contentBase

hotOnly、host配置

port、open、compress


Proxy代理

changeOrigin的解析


historyApiFallback

resolve模块解析

确实文件还是文件夹

extensions和alias配置

目录结构

webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "./build"),
},
devServer: {
hot: true,
hotOnly: true,
compress: true,
contentBase: path.resolve(__dirname, "./why"),
watchContentBase: true,
proxy: {
"/why": {
target: "http://localhost:8888",
pathRewrite: {
"^/why": ""
},
secure: false,
changeOrigin: true
}
},
historyApiFallback: {
rewrites: [
{from: /abc/, to: "/index.html"}
]
}
},
resolve: {
extensions: ['.wasm', '.mjs', '.js', '.json', '.jsx', '.ts', '.vue'],
alias: {
"@": path.resolve(__dirname, "./src"),
"pages": path.resolve(__dirname, "./src/pages")
}
},
module: {
rules: [
{
test: /\.jsx?$/i,
use: "babel-loader"
},
{
test: /\.vue$/i,
use: "vue-loader"
},
{
test: /\.css/i,
use: [
"style-loader",
"css-loader"
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: "./index.html"
}),
new ReactRefreshWebpackPlugin(),
new VueLoaderPlugin()
]
}
index.js
import axios from 'axios';
import React from 'react';
import ReactDom from 'react-dom';
import ReactApp from './App.jsx';
import Vue from 'vue';
import VueApp from './App.vue';
import "./math";
console.log("Hello Coderwhy");
console.log("abc");
if (module.hot) {
module.hot.accept("./math.js", () => {
console.log("math模块发生了更新~");
});
}
ReactDom.render(<ReactApp/>, document.getElementById("app"));
new Vue({
render: h => h(VueApp)
}).$mount("#root");
axios.get("http://localhost:8080/why/moment").then(res => {
console.log(res);
}).catch(err => {
console.log(err);
});