create-react-app craco多页面配置记录

2,482 阅读1分钟

项目目录

> build
> public
> src
  > common
  > views
    > index
      index.tsx
      index.html
      index.less
    > demo
      demo.tsx
      demo.html
      demo.less
craco.config.js
webpack.tools.js

craco.config.js

const CracoLessPlugin = require('craco-less');
const webpack = require('webpack');
const {
    allSitePath,
    removeFromArrayByCondition,
    htmlPlugin,
    resolveApp,
    resolveModule,
} = require('./webpack.tools');


// 是否是线上环境
const isProd = process.env.NODE_ENV === 'production';

// 线上环境又分沙盒和正式线上
const buildEnv = process.env.IS_BUILD_ONLINE === 'true' ? 'online' : 'sandbox';

const publicPathMap = {
    'sandbox': 'aaa',
    'online': 'bbb',
};

// env 配置
// env设置必须放到 webpack 配置合并之前

// jsx
process.env.DISABLE_NEW_JSX_TRANSFORM = 'true';
if (isProd) {

    // 关掉webpack内联运行时的脚本
    process.env.INLINE_RUNTIME_CHUNK = 'false';

    // 关掉souremap
    process.env.GENERATE_SOURCEMAP = 'false';
}


module.exports = {
    webpack: {
        configure: (webpackConfig, {env, paths}) => {
            const isDev = env === 'development';
            // 修改基础paths
            paths.appHtml = resolveApp('src/views/index/index.html');
            paths.appIndexJs = resolveModule(resolveApp, 'src/views/index/index', paths);

            // 修改入口
            webpackConfig.entry = allSitePath(isDev, paths);

            // 修改出口
            webpackConfig.output.filename = isDev ? 'js/[name].bundle.js' : 'js/[name].[contenthash:8].js';
            webpackConfig.output.publicPath = isDev ? '/' : (publicPathMap[buildEnv] || '');

            // 删除插件 HtmlWebpackPlugin 和 ManifestPlugin
            let plugins = webpackConfig.plugins;
            removeFromArrayByCondition(plugins, item => {
                return item.constructor.name === 'HtmlWebpackPlugin' || item.constructor.name === 'ManifestPlugin';
            });
            // 增加插件
            plugins = [
                // html
                ...htmlPlugin(isDev, paths),
                // 环境变量
                new webpack.DefinePlugin({
                    'IS_BUILD_ONLINE': JSON.stringify(process.env.IS_BUILD_ONLINE),
                }),
                // 编译进度
                new webpack.ProgressPlugin(),
                ...plugins,
            ];
            webpackConfig.plugins = plugins;
            return webpackConfig;
        },
    },
    babel: {
        plugins: [
            'babel-plugin-transform-typescript-metadata',
            ['@babel/plugin-proposal-decorators', {'legacy': true}],
            ['@babel/plugin-proposal-class-properties', {'loose': true}],
            'react-hot-loader/babel',
        ],
        presets: [
            '@babel/preset-typescript',
        ],
    },
    plugins: [
        {
            plugin: CracoLessPlugin,
            options: {
                lessLoaderOptions: {
                    lessOptions: {
                        modifyVars: {'@primary-color': '#1DA57A'},
                        javascriptEnabled: true,
                    },
                },
            },
        },
    ],
};

webpack.tools.js


const path = require('path');
const fs = require('fs');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const glob = require('glob');


const appDirectory = fs.realpathSync(process.cwd());

const resolveApp = relativePath => path.resolve(appDirectory, relativePath);

// 可以通过命令控制只打包哪些模块
function getEntryFilesFormCommandLine(paths) {
    const argvs = process.argv;
    const flagIndex = argvs.indexOf('--page');
    let entryFiles = null;
    if (flagIndex !== -1) {
        entryFiles = argvs.slice(flagIndex + 1).map(item => {
            return `${paths.appSrc}/views/${item}`;
        });
    }
    return entryFiles;
}

// 查看文件是否存在
// 支持文件的后缀类型为 paths.moduleFileExtensions
function resolveModule(resolveFn, filePath, paths) {
    const extension = paths.moduleFileExtensions.find(extension =>
        fs.existsSync(resolveFn(`${filePath}.${extension}`))
    );
    if (extension) {
        return resolveFn(`${filePath}.${extension}`);
    }
    // 默认js
    return resolveFn(`${filePath}.js`);
}


// 根据指定条件从数组中删除某一项
// 用于删除webpack plugins中原有插件
function removeFromArrayByCondition(arr, condition, setValue = (arr3, ii, vv) => {
    arr3[ii] = vv;
}) {
    condition = (!condition || typeof condition !== 'function') ? () => false : condition;
    let count = 0;
    for (let i = 0; i < arr.length; i++) {
        if (condition(arr[i], i)) {
            count++;
        } else {
            count > 0 && (setValue(arr, i - count, arr[i]));
        }
    }
    for (let i = 0; i < count; i++) {
        arr.pop();
    }
    return arr;
}

// 多页面工具函数, 拿到所有的入口文件
function allSitePath(isEnvDevelopment, paths) {
    let entryFiles = getEntryFilesFormCommandLine(paths);
    !entryFiles && (entryFiles = glob.sync(paths.appSrc + '/views/*'));

    // eslint-disable-next-line prefer-const
    let map = {};
    entryFiles.forEach(item => {
        const filename = item.substring(item.lastIndexOf('/') + 1);
        // const filePath = `${item}/${filename}.tsx`;
        const filePath = resolveModule(resolveApp, `${item}/${filename}`, paths);
        map[filename] = [
            'react-hot-loader/patch',
            isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient'),
            filePath,
        ].filter(Boolean);
    });
    return map;
}

// 重写htmlPlugin,返回所有模块的hmlt plugin
function htmlPlugin(isEnvDevelopment, paths) {
    const fileNameLists = Object.keys(
        allSitePath(isEnvDevelopment, paths)
    );

    // eslint-disable-next-line prefer-const
    let arr = [];
    fileNameLists.forEach(item => {
        const filename = item.substring(item.lastIndexOf('/') + 1);

        arr.push(
            new HtmlWebpackPlugin(
                Object.assign(
                    {},
                    {
                        inject: true,
                        filename: item + '.html',
                        chunks: [item],
                        template: path.resolve(paths.appSrc, `views/${filename}/${filename}.html`),
                    },
                    isEnvDevelopment
                        ? undefined
                        : {
                            minify: {
                                removeComments: true,
                                collapseWhitespace: true,
                                removeRedundantAttributes: true,
                                useShortDoctype: true,
                                removeEmptyAttributes: true,
                                removeStyleLinkTypeAttributes: true,
                                keepClosingSlash: true,
                                minifyJS: true,
                                minifyCSS: true,
                                minifyURLs: true,
                            },
                        }
                )
            )
        );
    });
    return arr;
}


module.exports = {
    allSitePath,
    removeFromArrayByCondition,
    htmlPlugin,
    resolveApp,
    resolveModule,
};


  • 可通过--page [模块名]单独打包某一些模块,提升开发效率

  • 如果想单独编译 index

yarn start --page index
  • 如果想单独编译 indexdemoother
yarn start --page index demo other