react-multi-page-app 是一个基于 webpack5 搭建的 react 多页面应用。
为什么搭建多页面应用:
搭建多页面应用的好处:
clone
git clone https://github.com/zhedh/react-multi-page-app.git
安装依赖
yarn install
开发
yarn start
http://localhost:8000/page1
打包
yarn build
yarn init
|____README.md
|____package.json
|____src
| |____utils
| |____components
| |____pages
| | |____page2
| | | |____index.css
| | | |____index.jsx
| | |____page1
| | | |____index.css
| | | |____index.jsx
安装 Webpack
yarn add -D 可以使用 npm i --save-dev 替代
yarn add -D webpack webpack-cli
创建配置文件
touch webpack.config.js
入口配置
module.exports = {
entry: {
page1: "./src/pages/page1/index.jsx",
page2: "./src/pages/page2/index.jsx",
// ...
},
};
输出配置
module.exports = {
entry: {
page1: "./src/pages/page1/index.jsx",
page2: "./src/pages/page2/index.jsx",
// ...
},
output: {
path: path.resolve(__dirname, "./dist"),
filename: "[name]/index.js",
},
};
js|jsx 编译
安装 babel 插件
yarn add -D babel-loader @babel/core @babel/preset-env
module.exports = {
...
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
},
}
css 编译
安装 loader
yarn add -D style-loader css-loader
module.exports = {
...
module: {
...
rules: [
{
test: /\.css$/i,
use: [
'style-loader',
'css-loader'
],
},
]
},
}
html 插件配置
安装 html-webpack-plugin
yarn add -D html-webpack-plugin
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
filename: 'page1/index.html',
chunks: ['page1']
}),
new HtmlWebpackPlugin({
filename: 'page2/index.html',
chunks: ['page2']
}),
],
}
page1
index.jsx
import "./index.css";
document.querySelector("body").append("PAGE1");
index.css
body {
color: blue;
}
page2
index.jsx
import "./index.css";
document.querySelector("body").append("PAGE2");
index.css
body {
color: green;
}
执行
webpack
输出 dist 包产物如下:
├── page1
│ ├── index.html
│ └── index.js
└── page2
├── index.html
└── index.js
webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: {
page1: "./src/pages/page1/index.jsx",
page2: "./src/pages/page2/index.jsx",
},
output: {
path: path.resolve(__dirname, "./dist"),
filename: "[name]/index.js",
},
module: {
rules: [
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
{
test: /\.m?jsx$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
filename: "page1/index.html",
chunks: ["page1"],
// chunks: ['page1', 'page1/index.css']
}),
new HtmlWebpackPlugin({
filename: "page2/index.html",
chunks: ["page2"],
}),
],
};
package.json
{
"name": "react-multi-page-app",
"version": "1.0.0",
"description": "React 多页面应用",
"main": "index.js",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.12.9",
"@babel/preset-env": "^7.12.7",
"babel-loader": "^8.2.2",
"css-loader": "^5.0.1",
"html-webpack-plugin": "^4.5.0",
"style-loader": "^2.0.0",
"webpack": "^5.9.0",
"webpack-cli": "^4.2.0"
}
}
去 github 查看简易版完整代码react-multi-page-app
新建 config 目录,并创建配置文件
mkdir config
cd config
touch webpack.base.js
touch webpack.dev.js
touch webpack.prod.js
├── webpack.base.js
├── webpack.dev.js
└── webpack.prod.js
基础配置
webpack.base.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: {
page1: "./src/pages/page1/index.jsx",
page2: "./src/pages/page2/index.jsx",
},
output: {
path: path.resolve(__dirname, "./dist"),
filename: "[name]/index.js",
},
module: {
rules: [
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
filename: "page1/index.html",
chunks: ["page1"],
}),
new HtmlWebpackPlugin({
filename: "page2/index.html",
chunks: ["page2"],
}),
],
};
开发配置
yarn add -D webpack-merge
yarn add -D webpack-dev-server
webpack.dev.js
const { merge } = require("webpack-merge");
const path = require("path");
const base = require("./webpack.base");
module.exports = merge(base, {
mode: "development",
devtool: "inline-source-map",
target: "web",
devServer: {
open: true,
contentBase: path.join(__dirname, "./dist"),
historyapiFallback: true, //不跳转
inline: true, //实时刷新
hot: true, // 开启热更新,
port: 8000,
},
});
package.json
{
"scripts": {
"start": "webpack serve --mode development --env development --config config/webpack.dev.js"
},
}
yarn start
http://localhost:8000/page1
http://localhost:8000/page2
生产配置
webpack.prod.js
const { merge } = require('webpack-merge')
const base = require('./webpack.base')
module.exports = merge(base, {
mode: 'production',
})
package.json
{
"scripts": {
"start": "webpack serve --mode development --env development --config config/webpack.dev.js",
"build": "webpack --config config/webpack.prod.js"
},
}
yarn build
以page1页面为例
├── page1
│ ├── app.jsx
│ ├── index.jsx
│ └── index.css
└── page2
├── app.js
├── index.jsx
└── index.css
yarn add react react-dom
app.js
import React from 'react'
function App() {
return (
<div id="page1">
我是PAGE1,Hello World
</div>
)
}
export default App
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './app'
import './index.css'
ReactDOM.render(<App />, document.getElementById('root'))
index.css
body{
background-color: #ccc;
}
#page1 {
color: rebeccapurple;
}
webpack.base.js
module.exports = {
module: {
// ...
rules: [
// ...
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-react', '@babel/preset-env'],
plugins: ['@babel/plugin-proposal-class-properties']
}
}
}
]
},
// ...
}
webpack.base.js
module.exports = {
// ...
resolve: {
extensions: ['.js', '.jsx', '.json']
}
}
cd src
touch template.html
template.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
webpack.base.js
module.exports = {
plugins: [
new HtmlWebpackPlugin({
filename: 'page1/index.html',
chunks: ['page1'],
template: './src/template.html'
}),
new HtmlWebpackPlugin({
filename: 'page2/index.html',
chunks: ['page2'],
template: './src/template.html'
}),
],
}
yarn add @babel/preset-react @babel/plugin-proposal-class-properties
以 page1 为例
index.scss
body {
background-color: #ccc;
#page1 {
color: rebeccapurple;
}
}
webpack.base.js
module.exports = {
// ...
module: {
// ...
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
'style-loader',
'css-loader',
'resolve-url-loader',
'sass-loader'
]
},
]
},
// ...
}
yarn add -D resolve-url-loader sass-loader
到此,一个完整的 React 多页面应用搭建完成,查看完整代码react-multi-page-app
为了不用每次新增页面都要新增入口页面配置,我们将入口配置改成自动匹配
cd config
touch webpack.util.js
webpack.util.js
const glob = require('glob')
function setEntry() {
const files = glob.sync('./src/pages/**/index.jsx')
const entry = {}
files.forEach(file => {
const ret = file.match(/^\.\/src\/pages\/(\S*)\/index\.jsx$/)
if (ret) {
entry[ret[1]] = {
import: file,
}
}
})
return entry
}
module.exports = {
setEntry,
}
webpack.base.js
const { setEntry } = require('./webpack.util')
module.exports = {
entry: setEntry,
}
webpack.util.js
function setEntry() {
const files = glob.sync('./src/pages/**/index.jsx')
const entry = {}
files.forEach(file => {
const ret = file.match(/^\.\/src\/pages\/(\S*)\/index\.jsx$/)
if (ret) {
entry[ret[1]] = {
import: file,
dependOn: 'react_vendors',
}
}
})
// 拆分react依赖
entry['react_vendors'] = {
import: ['react', 'react-dom'],
filename: '_commom/[name].js'
}
return entry
}
以 page1 为例子,引入页面自定义模版目录约定如下
├── app.jsx
├── index.html
├── index.jsx
└── index.scss
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>页面1</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
如果匹配不到自定义模版,会匹配默认模版,配置如下:
webpack.util.js
function getTemplate(name) {
const files = glob.sync(`./src/pages/${name}/index.html`)
if (files.length > 0) {
return files[0]
}
return './src/template.html'
}
function setHtmlPlugin() {
const files = glob.sync('./src/pages/**/index.jsx')
const options = []
files.forEach(file => {
const ret = file.match(/^\.\/src\/pages\/(\S*)\/index\.jsx$/)
if (ret) {
const name = ret[1]
options.push(new HtmlWebpackPlugin({
filename: `${name}/index.html`,
template: getTemplate(name),
chunks: ['react_vendors', name,]
}))
}
})
return options
}
module.exports = {
setEntry,
setHtmlPlugin
}
webpack.base.js
const { setEntry, setHtmlPlugin } = require('./webpack.util')
module.exports = {
plugins: [
...setHtmlPlugin(),
]
}
yarn add -D html-webpack-plugin glob
webpack.base.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
plugins: [
new CleanWebpackPlugin(),
]
}
yarn add -D clean-webpack-plugin
webpack.base.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
// 'style-loader',
MiniCssExtractPlugin.loader,
'css-loader',
'resolve-url-loader',
'sass-loader'
]
},
]
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name]/index.css',
}),
new OptimizeCSSPlugin({
cssProcessorOptions: {
safe: true
}
})
]
}
html 中注入 css
webpack.util.js
function setHtmlPlugin() {
const files = glob.sync('./src/pages/**/index.jsx')
const options = []
files.forEach(file => {
const ret = file.match(/^\.\/src\/pages\/(\S*)\/index\.jsx$/)
if (ret) {
const name = ret[1]
options.push(new HtmlWebpackPlugin({
filename: `${name}/index.html`,
template: getTemplate(name),
chunks: ['react_vendors', name, '[name]/index.css']
}))
}
})
return options
}
yarn add -D mini-css-extract-plugin optimize-css-assets-webpack-plugin
在 package.json 配置 sideEffects,避免 webpack Tree Shaking 移除.css、.scss 文件
package.json
```json
{
"sideEffects": [
"*.css",
"*.scss"
]
}
至此,项目配置完成
完整代码react-multi-page-app,喜欢给个star
Cannot read property 'createSnapshot' of undefined
报错:UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'createSnapshot' of undefined
原因:因为同时运行 2 个不同版本的 webpack。我们项目中没有安装 webpack-cli,webpack 会默认使用全局的 webpack-cli,webpack5 和 webpack-cli3 不兼容
解决:升级全局 webpack-cli3 到 webpack-cli4 或在项目中安装最新版本的 webpack-cli4
如何使用webpack构建多页面应用,这是一个我一直在想和解决的问题。网上也给出了很多的例子,很多想法。猛一看,觉得有那么点儿意思,但仔细看也就那样。使用webpack这个构建工具,可以使我们少考虑很多的问题。
图片常见的类型有jp(e)g,png,gif,包括现在普遍使用的svg以及webp。svg作为矢量图形,有一定的使用场景,而webp作为未来web开发的趋势,有一定的使用场景,比如:爱奇艺中的轮播图(carousel)中就是用webp,但其他的见到的不多。
利用 vue-cli 搭建的项目大都是单页面应用项目,对于简单的项目,单页面就能满足要求。但对于有多个子项目的应用,如果创建多个单页面
多页面vue应用中,在请求接口之前统一加上Loading作为未请求到数据之前的过渡展示。由于多页面未使用vuex做状态管理,只在入口统一注册了bus,所以此例子使用eventbus做事件通信。
公司有专门的审批系统,我现在做的后台管理系统需要接入,移动端和PC端都要有一个页面来展示业务信息。后端不给开俩项目(也确实没必要),所以打算用多页面来解决,PC和移动端放到一个项目里,然后打包到各自的文件夹
现在很多脚手架基本上都是单页面入口的应用,比如create-react-app等,整个的入口就是src/index.js。即使是Next.js,可以使用next export,导出成一个个单独的 html 页面。但在编译时,依然是全量编译,作为本身是为同构直出量身定制的服务端框架
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!