React + webpack 多页面应用

先上目录图

依赖和插件

{
  "name": "webpack-multiple-app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "jest --watchAll",
    "build": "webpack --config ./config/webpack.prod.js",
    "dev": "webpack-dev-server --config ./config/webpack.dev.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@types/jquery": "^3.5.16",
    "@types/swiper": "^6.0.0",
    "animate.css": "^4.1.1",
    "aos": "^2.3.4",
    "axios": "^1.4.0",
    "javascript-obfuscator": "^4.0.2",
    "jquery": "^3.7.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^5.3.4",
    "swiper": "^10.0.4",
    "wowjs": "^1.1.3"
  },
  "devDependencies": {
    "@babel/core": "^7.22.9",
    "@babel/plugin-syntax-jsx": "^7.22.5",
    "@babel/plugin-transform-react-jsx": "^7.22.5",
    "@babel/plugin-transform-runtime": "^7.22.9",
    "@babel/preset-env": "^7.22.9",
    "@babel/preset-react": "^7.22.5",
    "@babel/preset-typescript": "^7.22.5",
    "@babel/runtime": "^7.22.6",
    "@jest/globals": "^29.6.1",
    "@types/jest": "^29.5.3",
    "autoprefixer": "^10.4.14",
    "babel-loader": "^9.1.3",
    "cache-loader": "^4.1.0",
    "core-js": "^3.31.1",
    "css-loader": "^6.8.1",
    "file-loader": "^6.2.0",
    "html-loader": "^4.2.0",
    "html-webpack-plugin": "^5.5.3",
    "html-withimg-loader": "^0.1.16",
    "javascript-obfuscator": "^4.0.2",
    "jest": "^29.6.1",
    "less": "^4.1.3",
    "less-loader": "^11.1.3",
    "mini-css-extract-plugin": "^2.7.6",
    "postcss-loader": "^7.3.3",
    "postcss-preset-env": "^9.0.0",
    "progress-bar-webpack-plugin": "^2.1.0",
    "speed-measure-webpack-plugin": "^1.5.0",
    "style-loader": "^3.3.3",
    "terser-webpack-plugin": "^5.3.9",
    "thread-loader": "^4.0.2",
    "ts-jest": "^29.1.1",
    "ts-loader": "^9.4.4",
    "ts-node": "^10.9.1",
    "typescript": "^5.1.6",
    "url-loader": "^4.1.1",
    "webpack": "^5.88.1",
    "webpack-bundle-analyzer": "^4.9.0",
    "webpack-cli": "^5.1.4",
    "webpack-dev-server": "^4.15.1",
    "webpack-merge": "^5.9.0",
    "webpack-obfuscator": "^3.5.1",
    "webpackbar": "^5.0.2"
  }
}

使用webpack的多入口来打包后生成多页面

webpack公共配置

//webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const SpeedMeasureWebpackPlugin = require("speed-measure-webpack-plugin");
const WebpackBar = require("webpackbar");
const ProgressBarWebpackPlugin = require("progress-bar-webpack-plugin");
const fs = require("fs");

let t = new Date();
let y = t.getFullYear() + "";
let m = t.getMonth() + 1 + "";
let d = t.getDate() + "";
let h = t.getHours() + "";
let s = t.getMinutes() + "";
var Version = y + m + d + h + s;
let entry = {};
let HtmlTemplate = [];
const dirs = fs.readdirSync(path.resolve(__dirname, "../src/pages"));

dirs.map((item) => {
  fs.readdirSync(path.resolve(__dirname, `../src/pages/${item}`)).map((item2) => {
    if (path.extname(item2)=='.js') {
      let name = item2.substring(0, item2.lastIndexOf("."));
      entry[name] = path.resolve(__dirname, `../src/pages/${item}/${name}.js`);
       console.log(entry);
    }
  });
});

for (const key in entry) {
  if (Object.hasOwnProperty.call(entry, key)) {
    console.log(key);
    HtmlTemplate.push(
      new HtmlWebpackPlugin({
        template: path.resolve(__dirname, `../src/public/index.html`),
        filename: `./${key}.html`,
        chunks: [key],
        hash: true,
        inject: true,
        minify: {
          removeComments: true, //移出注释
          collapseWhitespace: true,
        },
      })
    );
  }
}

module.exports = {
  //入口文件的配置项
  entry,
  //出口文件的配置项
  output: {
    path: path.resolve(__dirname, "../build"),
    filename: "./js/[name].js?v=" + Version,
  },

 resolve: {
    //省略后缀
    extensions: [".js",".jsx", ".less"], //后缀名 可以根据需要自由增减
    alias: {
      "@": path.resolve(__dirname, "../src"),
    },
    symlinks: false,
  },
  cache: {
    type: "filesystem", // 使用文件缓存
  },
  //模块:例如解读CSS,图片如何转换,压缩
  module: {
    rules: [
      {
        test: /\.ts$/, //test 指定的是规则生效的文件
        use: [
          "cache-loader",
          {
            // 配置加载器
            loader: "babel-loader",
            // 设置babel
            options: {
              presets: [
                [
                  "@babel/preset-env", // 指定环境的插件,
                  {
                    // 配置位置
                    targets: {
                      // 要兼容的目标浏览器
                      chrome: "88",
                    },
                    corejs: "3", // 指定corejs的版本
                    useBuiltIns: "usage", //使用corejs版本为3,"usage"表示按需加载
                  },
                ],
              ],
            },
          },
          "ts-loader",
        ], // 要使用的loader
        exclude: /node_modeules/, // 要排除的文件
        include: path.resolve(__dirname, "../src"),
      },
      {
        test: /\.jsx$/, //已作为js扩展名这样类型的文件
        exclude: /node_modules/, //排除node_modules文件夹
        include: path.resolve(__dirname, "../src"),
        use: {
          loader: "babel-loader", //转换成es5
          options: {
            presets: ["@babel/preset-react"], //设置编译的规则
            plugins: [
              // 设置编译的插件
              ["@babel/plugin-transform-runtime"], //设置编译的规则
            ],
          },
        },
      },
      {
        test: /\.js$/, //已作为js扩展名这样类型的文件
        exclude: /node_modules/, //排除node_modules文件夹
        include: path.resolve(__dirname, "../src"),
        use: [
          {
            loader: "babel-loader", //转换成es5
            options: {
              presets: ["@babel/preset-env"], //设置编译的规则
              plugins: [
                // 设置编译的插件
                ["@babel/plugin-transform-runtime"], //设置编译的规则
              ],
            },
          },
        ],
      },
      {
        //匹配哪些文件
        test: /\.less$/,
        exclude: /node_modeules/,
        include: path.resolve(__dirname, "../src"),
        
        //使用哪些loader进行处理
        use: [
          MiniCssExtractPlugin.loader,
          "cache-loader",
          "css-loader",
          {
           
            loader: "postcss-loader",
            options: {
             postcssOptions: {
                plugins: ["postcss-preset-env"],
              },
            },
          },
          "less-loader",
        ],
      },
      {
        test: /\.(png|jpe?g|gif|jpg|webp)$/,
        type: "asset/resource",
        include: path.resolve(__dirname, "../src"),
        generator: {
          filename: "../images/[name][ext]",
          outputPath: "./images",
        },
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024, // 小于10kb的图片会被base64处理
          },
        },
      },
      {
        test: /\.(mp3|mp4)$/i,
        include: path.resolve(__dirname, "../src"),
        type: "asset/resource",
        generator: {
          filename: "./media/[name][ext]",
        },
      },
      {
        test: /\.(ttf|eot|woff2)$/i,
        type: "asset/resource",
        include: path.resolve(__dirname, "../src"),
        generator: {
          filename: "fonts/[name][ext]",
        },
      },
      {
        // 处理html中的img
        test: /\.html$/,
        include: path.resolve(__dirname, "../src"),
        generator: {
          filename: "./images/[name][ext]",
        },
        use: [
          {
            loader: "html-withimg-loader",
          },
        ],
      },

      {
        // 打包其他资源
        exclude: /\.(css|ts|js|jsx|html|json|less|png|jpg|gif|ttf|eot|woff2|svg|bin|mp3|mp4)$/,
        loader: "file-loader",
        options: {
          outputPath: "./file",
          name: "[name].[ext]",
        },
      },
    ],
  },
  performance: {
    hints: "warning", // 枚举
    maxAssetSize: 30000000, // 整数类型(以字节为单位)
    maxEntrypointSize: 50000000, // 整数类型(以字节为单位)
    assetFilter: function (assetFilename) {
      // 提供资源文件名的断言函数
      return assetFilename.endsWith(".css") || assetFilename.endsWith(".js");
    },
  },

  //插件,用于生产模版和各项功能
  plugins: [
    ...HtmlTemplate,
    new MiniCssExtractPlugin({
      filename: "./css/[name].css?v=" + Version, //输出的文件名字
    }),

    new SpeedMeasureWebpackPlugin(),
    new WebpackBar(),
    new ProgressBarWebpackPlugin(),
  ],
};

 webpack 开发配置

const { merge } = require("webpack-merge");
const commonConfiguration = require("./webpack.common.js");

const path = require("path");
const app = require("express")();
// let prot = Math.floor(Math.random()*4000+1000)
module.exports = merge(commonConfiguration, {
  mode: "development",
  devServer: {
    static: {
      directory: path.join(process.cwd(), "build"),
    },  
    host: "localhost",
    compress: true,
    port: 4100,
    hot: true, // 开启热更新,
  },
});

webpack生产配置  打包自动删除的插件调不好,索性自己写一个

const { merge } = require("webpack-merge");
const commonConfiguration = require("./webpack.common.js");
const TerserPlugin = require("terser-webpack-plugin");
const {resolve} = require('path');
const WebpackObfuscator = require('webpack-obfuscator');
const {ClearBuildDir} = require('./webpackTools.ts')
// 打包删除build目录 
try {
  ClearBuildDir(resolve(__dirname,'../build'))
} catch (error) {
  console.log('build文件夹正被使用')
}
module.exports = merge(commonConfiguration, {
  mode: "production",
  // 去除死代码
  optimization: {
    usedExports: true,
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          mangle: true, // 开启变量名混淆
          compress: {
            drop_console: true,
            drop_debugger: false,
            pure_funcs: ["console.log"],
          },
        },
      }),
    ],
  },
  plugins: [
    new WebpackObfuscator ({
      rotateStringArray: true ,// 打乱Unicode数组顺序
      compact: true,
      deadCodeInjection: true,
      debugProtection: true,
      disableConsoleOutput: true,
      identifierNamesGenerator: 'hexadecimal',
  }, [])
  ],
});

打包自动删除 build

const fs = require('fs');
const path = require('path');
function ClearBuildDir(folderPath) {
  if (fs.existsSync(folderPath)) {
    fs.readdirSync(folderPath).forEach((file) => {
      const curPath = path.join(folderPath, file);
      if (fs.lstatSync(curPath).isDirectory()) {
        ClearBuildDir(curPath);
      } else {
        fs.unlinkSync(curPath);
      }
    });
    fs.rmdirSync(folderPath);
  }
}

module.exports = { ClearBuildDir }

首页 index.js

import ReactDOM from "react-dom/client";
import React from "react";
import MapRouter from "../../router/mapRouter";

import "./index.less";


ReactDOM.createRoot(document.getElementById("root")).render(<MapRouter />);

首页 IndexPage.jsx

import React from "react";
import Header from "../../components/Header/Header";
import Footer from "../../components/Footer/Footer";
export default function IndexPage(props) {
   return (
    <div>
      <Header/>
      <button  onClick={() => {props.history.push("/about"); }} >
        JumpAbout
      </button>
      <Footer/>
    </div>
  );
}

路由配置MapRouter.js

import React from "react";
import { HashRouter, Switch, Route, Redirect } from "react-router-dom";
import IndexPage from "../pages/Index/IndexPage";
import AboutPage from "../pages/About/AboutPage";
export default function MapRouter() {
  return (
    <HashRouter>
      <Switch>
        <Route path="/index" component={IndexPage}></Route>
        <Route path="/about" component={AboutPage}></Route>
        <Redirect from="/" to="/index" exact></Redirect>
      </Switch>
    </HashRouter>
  );
}

仅供参考 如果有错误的地方还请各位大佬不吝赐教,拜谢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值