前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Webpack5构造React多页面应用

Webpack5构造React多页面应用

作者头像
winty
发布2021-01-07 18:07:23
3.6K0
发布2021-01-07 18:07:23
举报
文章被收录于专栏:前端Q前端Q

来源 | https://github.com/zhedh/react-multi-page-app/

介绍

react-multi-page-app是一个基于webpack5构造的react多页面应用。

为什么建造多页面应用:

  • 多个页面之间业务互不关联,页面之间并没有共享的数据
  • 多个页面使用同一个一个服务,使用通用的组件和基础库

建造多页面应用的好处:

  • 保留了传统单页应用的开发模式:支持补充打包,你可以把每个页面看成是一个单独的单页应用
  • 独立部署:每个页面相互独立,可以单独部署,解压缩项目的复杂性,甚至可以在不同的页面选择不同的技术栈
  • 减少包的体积,优化加载渲染流程

快速上手

克隆

代码语言:javascript
复制
git clone https://github.com/zhedh/react-multi-page-app.git

安装依赖

代码语言:javascript
复制
yarn install

开发

代码语言:javascript
复制
yarn start

http:// localhost:8000 / page1

打包

代码语言:javascript
复制
yarn build

简易建设流程

npm初始化

代码语言:javascript
复制
yarn init

约定目录

代码语言:javascript
复制

|____README.md
|____package.json
|____src
| |____utils
| |____components
| |____pages
| | |____page2
| | | |____index.css
| | | |____index.jsx
| | |____page1
| | | |____index.css
| | | |____index.jsx

webpack配置

安装webpack

yarn add -D可以使用npm i --save-dev替代

代码语言:javascript
复制
yarn add -D webpack webpack-cli

创建配置文件

代码语言:javascript
复制
touch webpack.config.js

入口配置

代码语言:javascript
复制

module.exports = {
  entry: {
    page1: "./src/pages/page1/index.jsx",
    page2: "./src/pages/page2/index.jsx",
    // ...
  },
};

输出配置

代码语言:javascript
复制

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插件

代码语言:javascript
复制
yarn add -D babel-loader @babel/core @babel/preset-env
代码语言:javascript
复制

module.exports = {
  ...
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  },
}

css编译

安装装载机

代码语言:javascript
复制
yarn add -D style-loader css-loader
代码语言:javascript
复制

module.exports = {
  ...
  module: {
    ...
    rules: [
      {
        test: /\.css$/i,
        use: [
          'style-loader',
          'css-loader'
        ],
      },
    ]
  },
}

html插件配置

安装html -webpack-plugin

代码语言:javascript
复制
yarn add -D html-webpack-plugin
代码语言:javascript
复制

module.exports = {
  ...
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'page1/index.html',
      chunks: ['page1']
    }),
    new HtmlWebpackPlugin({
      filename: 'page2/index.html',
      chunks: ['page2']
    }),
  ],
}

页面编辑

第1页

index.jsx

代码语言:javascript
复制
import "./index.css";

document.querySelector("body").append("PAGE1");

index.css

代码语言:javascript
复制

body {
  color: blue;
}

第2页

index.jsx

代码语言:javascript
复制
import "./index.css";

document.querySelector("body").append("PAGE2");

index.css

代码语言:javascript
复制

body {
  color: green;
}

打包

执行

代码语言:javascript
复制
webpack

输出dist包产物如下:

代码语言:javascript
复制

├── page1
│   ├── index.html
│   └── index.js
└── page2
    ├── index.html
    └── index.js

完整的配置文件

webpack.config.js

代码语言:javascript
复制

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

代码语言:javascript
复制

{
  "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

流程优化

分离开发生产环境

新建配置目录,并创建配置文件

代码语言:javascript
复制

mkdir config
cd config
touch webpack.base.js
touch webpack.dev.js
touch webpack.prod.js
代码语言:javascript
复制

├── webpack.base.js
├── webpack.dev.js
└── webpack.prod.js

基础配置

webpack.base.js

代码语言:javascript
复制

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"],
    }),
  ],
};

开发配置

  • 安装webpack-merge,用于合并webpack配置信息
代码语言:javascript
复制
yarn add -D webpack-merge
  • 安装webpack-dev-server,用于启动开发服务
代码语言:javascript
复制
yarn add -D webpack-dev-server
  • 开发配置如下

webpack.dev.js

代码语言:javascript
复制

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

代码语言:javascript
复制

{
  "scripts": {
    "start": "webpack serve --mode development --env development --config config/webpack.dev.js"
  },
}
  • 启动
代码语言:javascript
复制
yarn start
  • 预览

http:// localhost:8000 / page1 http:// localhost:8000 / page2

生产配置

配置如下

webpack.prod.js

代码语言:javascript
复制

const { merge } = require('webpack-merge')
const base = require('./webpack.base')

module.exports = merge(base, {
    mode: 'production',
})

配置打包命令

package.json

代码语言:javascript
复制

{
  "scripts": {
    "start": "webpack serve --mode development --env development --config config/webpack.dev.js",
    "build": "webpack --config config/webpack.prod.js"
  },
}

打包

代码语言:javascript
复制
yarn build

♡反应

以page1页面为例

约定目录

代码语言:javascript
复制

├── page1
│   ├── app.jsx
│   ├── index.jsx
│   └── index.css
└── page2
    ├── app.js
    ├── index.jsx
    └── index.css

安装反应

代码语言:javascript
复制
yarn add react react-dom

代码如下

app.js

代码语言:javascript
复制

import React from 'react'

function App() {
  return (
    <div id="page1">
      我是PAGE1,Hello World
    </div>
  )
}

export default App

index.js

代码语言:javascript
复制

import React from 'react'
import ReactDOM from 'react-dom'
import App from './app'
import './index.css'

ReactDOM.render(<App />, document.getElementById('root'))

index.css

代码语言:javascript
复制

body{
  background-color: #ccc;
}

#page1 {
  color: rebeccapurple;
}

添加反应编译

webpack.base.js

代码语言:javascript
复制

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

代码语言:javascript
复制

module.exports = {
  // ...
  resolve: {
    extensions: ['.js', '.jsx', '.json']
  }
}

添加html样式模版,挂载React DOM

代码语言:javascript
复制

cd src
touch template.html

template.html

代码语言:javascript
复制

<!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

代码语言:javascript
复制

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'
    }),
  ],
}

安装依赖

代码语言:javascript
复制
yarn add @babel/preset-react @babel/plugin-proposal-class-properties

ass

以page1首先

将index.css变更为index.scss

index.scss

代码语言:javascript
复制

body {
  background-color: #ccc;

  #page1 {
    color: rebeccapurple;
  }
}

添加scss编译

webpack.base.js

代码语言:javascript
复制

module.exports = {
  // ...
  module: {
    // ...
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          'style-loader',
          'css-loader',
          'resolve-url-loader',
          'sass-loader'
        ]
      },
    ]
  },
  // ...
}

安装依赖

代码语言:javascript
复制
yarn add -D resolve-url-loader sass-loader

到此,一个完整的React多页面应用构建完成,查看完整代码react-multi-page-app

入口配置和模版自动匹配

为了不用每次补充页面都要添加入口页面配置,我们将入口配置改成自动匹配

入口文件自动匹配

代码语言:javascript
复制
cd config
touch webpack.util.js

webpack.util.js

代码语言:javascript
复制

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

代码语言:javascript
复制

const { setEntry } = require('./webpack.util')

module.exports = {
  entry: setEntry,
}

拆分React依赖,将React单独打包出一个bundle,作为公共依赖帖子各个页面

webpack.util.js

代码语言:javascript
复制

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
}

html模版自动匹配,并约会反应包

以page1为例子,约会页面自定义模版目录约定如下

代码语言:javascript
复制

├── app.jsx
├── index.html
├── index.jsx
└── index.scss

index.html

代码语言:javascript
复制

<!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

代码语言:javascript
复制

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

代码语言:javascript
复制

const { setEntry, setHtmlPlugin } = require('./webpack.util')

module.exports = {
  plugins: [
    ...setHtmlPlugin(),
  ]
}

安装相关依赖

代码语言:javascript
复制
yarn add -D html-webpack-plugin glob

配置优化

清除之前打包文件

webpack.base.js

代码语言:javascript
复制

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  plugins: [
    new CleanWebpackPlugin(),
  ]
}
代码语言:javascript
复制
yarn add -D clean-webpack-plugin

分离并压缩css

webpack.base.js

代码语言:javascript
复制

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

代码语言:javascript
复制

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
}
代码语言:javascript
复制
yarn add -D mini-css-extract-plugin optimize-css-assets-webpack-plugin
代码语言:javascript
复制

在 package.json 配置 sideEffects,避免 webpack Tree Shaking 移除.css、.scss 文件
package.json

```json
{
  "sideEffects": [
    "*.css",
    "*.scss"
  ]
}

至此,项目配置完成

项目源码

完整代码:https://github.com/zhedh/react-multi-page-app/,喜欢给个star

问题与解答

无法读取未定义的属性“ createSnapshot”

报错:UnhandledPromiseRejectionWarning:TypeError:无法读取未定义的属性'createSnapshot'

原因:因为同时运行2个不同版本的webpack。我们项目中没有安装webpack-cli,webpack会进行交替使用的webpack-cli,webpack5和webpack-cli3不兼容

解决:升级版本webpack-cli3到webpack-cli4或在项目中安装最新版本的webpack-cli4

参考:https : //github.com/

本文完〜

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-01-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端Q 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 介绍
  • 快速上手
  • 简易建设流程
    • npm初始化
      • 约定目录
        • webpack配置
          • 打包
            • 完整的配置文件
            • 流程优化
              • 分离开发生产环境
                • ♡反应
                  • ass
                    • 入口配置和模版自动匹配
                      • 配置优化
                      • 项目源码
                      • 问题与解答
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档