Skip to content

Instantly share code, notes, and snippets.

@relyky
Created November 19, 2024 07:37

Revisions

  1. relyky created this gist Nov 19, 2024.
    42 changes: 42 additions & 0 deletions AppMain.lazy.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,42 @@
    // 與 webpack.config.lazy.js 搭配,以進行動態拆分。
    import React, { lazy, Suspense, LazyExoticComponent, useMemo } from "react"

    type ComponentType = LazyExoticComponent<React.ComponentType<any>>;

    //## 所有作業都需先“註冊”
    const funcPool = new Map<string, ComponentType>();
    funcPool.set("HomeIndex", lazy(() => import(/* webpackChunkName: "HomeIndex" */'./Home/HomeIndex')));
    funcPool.set("DEMO001", lazy(() => import(/* webpackChunkName: "DEMO001" */'./Demo/DEMO001/AppForm')));
    funcPool.set("DEMO002", lazy(() => import(/* webpackChunkName: "DEMO002" */'./Demo/DEMO002/AppForm')));
    //※ 上面的 webpackChunkName 註解資訊將在 bundle 時送至 webpack.config.js 對應的 chunk 參數 [name] 以用於 chunk.bundle.js 取名。

    export default function AppMain(props: {
    funcId: string
    }) {

    //## 依參數 funcId 取得 AppCtx
    const AppCtx = useMemo(() => funcPool.get(props.funcId) ?? funcPool.get('HomeIndex')
    , [funcPool, props.funcId])

    console.log('App.AppForm.v1', { props })
    return (
    <div>
    <h1>AppForm</h1>
    <a href="/">Home</a>&nbsp;&nbsp;
    <a href="/Demo/DEMO001">DEMO001</a>&nbsp;&nbsp;
    <a href="/Demo/DEMO002">DEMO002</a>&nbsp;&nbsp;
    <main>
    <Suspense fallback={<Spinner />}>
    <AppCtx />
    </Suspense>
    </main>
    </div>
    )
    }

    const Spinner: React.FC = () => (
    <div className="text-center bg-light text-primary p-4 ">
    <i className="fa fa-cog fa-spin fa-5x"></i>
    <p>載入中</p>
    </div>
    )
    74 changes: 74 additions & 0 deletions webpack.config.lazy.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,74 @@
    /*
    * 單進入點與動態 lazy 載入。
    * 應用於 "webpack": "^5.88.2", "webpack-cli": "^5.1.4", "react": "^18.3.1", "react-dom": "^18.3.1",
    * 需挑配 React lazy 指令進行動態拆分。
    */

    //const HtmlWebPackPlugin = require("html-webpack-plugin");
    const path = require('path');
    const TerserPlugin = require('terser-webpack-plugin');

    module.exports = [{
    context: __dirname,
    entry: {
    app: './src/app.tsx',
    },
    output: {
    path: path.resolve(__dirname, '../N48MvcReactLab/Scripts/bundle/'),
    filename: '[name].bundle.js',
    publicPath: '/Scripts/bundle/',
    chunkFilename: '[name].bundle.js'
    },
    module: {
    rules: [
    {
    test: /\.?js$/,
    exclude: /node_modules/,
    use: {
    loader: "babel-loader",
    options: {
    presets: ["@babel/preset-env", "@babel/preset-react"],
    },
    },
    },
    {
    test: /\.tsx?$/,
    use: 'ts-loader',
    exclude: /node_modules/
    },
    {
    test: /\.css$/,
    use: [
    "style-loader",
    "css-loader",
    ],
    },
    ],
    },
    resolve: {
    extensions: ['.tsx', '.ts', '.js'],
    alias: {
    hooks: path.resolve(__dirname, 'src/hooks/'),
    }
    },
    optimization: {
    // minimize: true, // 預設當執行[webpack --mode production]模式指令時會觸發[minimizer]動作。
    minimizer: [
    /* Remove Comment, ref→https://webpack.js.org/plugins/terser-webpack-plugin/ */
    new TerserPlugin({
    terserOptions: {
    output: {
    comments: false,
    },
    },
    extractComments: false,
    }),
    ],
    }
    //plugins: [
    // new HtmlWebPackPlugin({
    // template: "./src/index.html",
    // filename: "./index.html"
    // })
    //]
    }];
    110 changes: 110 additions & 0 deletions webpack.config.multi-entries.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,110 @@
    /*
    * 多個進入點與分拆共用模組。
    * 應用於 "webpack": "^5.88.2", "webpack-cli": "^5.1.4"
    * 但共用模組不是傳統的函式庫提供固定的參數,就算沒有用到也放著。
    * 本例 webpack 拆出來的共用模組 shared, common, highorder 其內容是有用到才放入,bundle 混淆後識別代碼名稱也會動態換掉。
    */

    const path = require("path");
    const TerserPlugin = require('terser-webpack-plugin'); // 用以不產生 LICENSE 宣告檔
    const Dotenv = require('dotenv-webpack'); // 用以取得 .env 環境參數

    module.exports = {
    mode: "production", // development | production
    entry: {
    common: {
    import: './src/common/index.tsx',
    dependOn: ['shared'],
    },
    highorder: {
    import: './src/highorder/index.tsx',
    dependOn: ['shared', 'common'],
    },
    home: {
    import: "/src/home/app.tsx", //default: "/src/index.js"
    dependOn: ['shared','common', 'highorder'],
    },
    demo001: {
    import: '/src/Demo/DEMO001/app.tsx',
    dependOn: ['shared', 'common', 'highorder'],
    },
    demo002: {
    import: '/src/Demo/DEMO002/app.tsx',
    dependOn: ['shared', 'common', 'highorder'],
    },
    demo003: {
    import: '/src/Demo/DEMO003/app.tsx',
    dependOn: ['shared', 'common', 'highorder'],
    },
    shared: 'lodash', // 動態共享模組
    },
    output: {
    path: path.resolve(__dirname, "../N48MvcReactTpl/Scripts/bundle"), // output folder, default:"./dist"
    publicPath: "/",
    filename: "[name].bundle.js",
    },
    optimization: {
    runtimeChunk: 'single',
    minimize: true,
    minimizer: [
    new TerserPlugin({
    extractComments: false, // 不產生 LICENSE 宣告檔
    //exclude: /common|highorder/, // 不進行混淆
    }),
    ],
    splitChunks: {
    cacheGroups: {
    // 動態共享模組 shared: 將所有在 node_modules 中的程式碼打包到 shared.bundle.js。
    shared: {
    name: 'shared',
    test: /[\\/]node_modules[\\/]/,
    chunks: 'all',
    enforce: true,
    },
    //// 動態共享模組 common: 將 src\highorder 等共用中的程式碼打包到 common.bundle.js。
    //common: {
    // name: 'common',
    // test: /[\\/]src[\\/](highorder|tools|hooks|atoms|shared)[\\/]/,
    // chunks: 'all',
    // enforce: true,
    //},
    },
    },
    },
    resolve: {
    extensions: ['.tsx', '.ts', '.js']
    },
    module: {
    rules: [
    {
    test: /\.?js$/,
    exclude: /node_modules/,
    use: {
    loader: "babel-loader",
    options: {
    presets: ["@babel/preset-env", "@babel/preset-react"],
    },
    },
    },
    {
    test: /\.tsx?$/,
    use: 'ts-loader',
    exclude: /node_modules/
    },
    {
    test: /\.css$/,
    use: [
    "style-loader",
    "css-loader",
    ],
    },
    ],
    },
    plugins: [
    new Dotenv({
    path: './.env',
    systemvars: true,
    defaults: true
    })
    ],
    };