Skip to content
javascript
// webpack.common.js
// 公共配置 不区分环境
const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackBar = require('webpackbar');
const { PROJECT_PATH, isDev } = require('../constant');

// 获取对应的css相关的loader 执行顺序 从后往前
const getCssLoader = (importLoaders, module) => {
    return [
        // 将css内容转化为style标签插入到index.html中的head里 是css处理的最后一步
        'style-loader',
        // 打包css 解析@import等语句
        {
            loader: "css-loader",
            options: {
                modules: module,
                sourceMap: isDev,
                importLoaders
            }
        },
        // 对css做一些预处理
        // 比如postcss-preset-env 可以将最新的 CSS 语法转换为目标环境的浏览器能够理解的 CSS 语法
        // postcss-normalize 从 browserslist 中自动导入所需要的 normalize.css 内容。
        {
            loader: 'postcss-loader',
            options: {
                sourceMap: isDev,
            },
        }]
}


module.exports = {
    // 指定打包入口 本例中从src下的index.tsx开始打包
    entry: {
        app: path.resolve(PROJECT_PATH, "./src/index.tsx"),
    },
    // 指定打包输出的地方
    output: {
        // prod环境打包时 需要生成hash值 用于缓存
        filename: `js/[name]${isDev ? '' : '.[hash:8]'}.js`,
        // 输出到./dist目录下
        path: path.resolve(PROJECT_PATH, "./dist")
    },
    resolve: {
        // 指定可以解析的文件后缀
        extensions: ['.tsx', '.ts', '.js', '.json'],
        // 指定路径别名 如@代表./src   @/components => ./src/component
        alias: {
            '@': path.resolve(PROJECT_PATH, './src'),
        },
    },
    module: {
        rules: [
            {
                // 普通css文件
                test: /\.css$/,
                use: getCssLoader(1)
            },
            {
                // less文件
                test: /\.less$/,
                exclude: /\.module\.less$/,
                include: /node_modules/,
                use: [
                    // 先调用less-loader编译less文件为css
                    ...getCssLoader(2, false),
                    {
                        loader: "less-loader",
                        options: {
                            sourceMap: isDev,
                            lessOptions: {
                                javascriptEnabled: true,
                                // 做一些全局变量的修改 如这里的antd的主题色
                                modifyVars: {
                                    '@brand-primary': '#ff6611',
                                    '@brand-primary-tap': '#ff6611'
                                }
                            }
                        }
                    }
                ]
            },
            {
                // 模块化的less文件 (文件名含有module字段)
                test: /\.module\.less$/,
                use: [
                    // 对应loader的option里的module设置为true
                    ...getCssLoader(2, true),
                    {
                        loader: "less-loader",
                        options: {
                            sourceMap: isDev,
                            lessOptions: {
                                javascriptEnabled: true,
                            }
                        }
                    }
                ]
            },
            // 图片文件
            {
                test: [/\.jpe?g$/, /\.png$/],
                use: [
                    {
                        // 使用url-loader进行解析图片等文件 小图片还可以转化base64编码
                        loader: 'url-loader',
                        options: {
                            limit: 10 * 1024,
                            name: '[name].[contenthash:8].[ext]',
                            // 输出到assets下的image文件夹下
                            outputPath: 'assets/images',
                        },
                    },
                ],
            },
            // tsx文件或者js文件 调用babel-loader进行转化
            // babel自带ts解析器 所以这里不需要使用tsc进行编译
            {
                test: /\.(tsx?|js)$/,
                loader: 'babel-loader',
                options: { cacheDirectory: true },
                // node-modules文件夹下的不编译
                exclude: /node_modules/,
            },
        ]
    },
    plugins: [
        // 自动往index.html文件中写入内容
        new htmlWebpackPlugin({
            template: path.resolve(PROJECT_PATH, './public/index.html'),
            filename: "index.html",
            cache: false,
            // 一些压缩配置
            minify: isDev ? false : {
                removeAttributeQuotes: true,
                collapseWhitespace: true,
                removeComments: true,
                collapseBooleanAttributes: true,
                collapseInlineTagWhitespace: true,
                removeRedundantAttributes: true,
                removeScriptTypeAttributes: true,
                removeStyleLinkTypeAttributes: true,
                minifyCSS: true,
                minifyJS: true,
                minifyURLs: true,
                useShortDoctype: true,
            }
        }),
        // 每次打包前清空./dist文件夹的内容
        new CleanWebpackPlugin(),
        // 将./public文件夹下的内容 复制到./dist/public里面
        new CopyPlugin({
            patterns: [
                {
                    context: path.resolve(PROJECT_PATH, './public'),
                    from: '*',
                    to: path.resolve(PROJECT_PATH, './dist'),
                    toType: 'dir',
                },
            ],
        }),
        // 显示打包进度
        new WebpackBar({
            name: isDev ? '正在启动' : '正在打包',
            color: '#fa8c16',
        }),
    ],
    optimization: {
        splitChunks: {
            chunks: 'all',
            name: false,
        },
    },
}
javascript
// webpack.dev.js
// 开发环境的特殊配置
const { merge } = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
const { SERVER_PORT, SERVER_HOST } = require('../constant');
const webpack = require("webpack");

module.exports = merge(commonConfig, {
    mode: 'development',
    // 指定dev Server相关的配置
    devServer: {
        host: SERVER_HOST,
        port: SERVER_PORT,
        stats: 'errors-only', // 终端仅打印 error
        clientLogLevel: 'silent', // 日志等级
        compress: true, // 是否启用 gzip 压缩
        // open: true, // 打开默认浏览器
        hot: true, // 热更新,
        disableHostCheck: true
    },
    // devtool: 'eval-source-map',
    // 与HMR有关
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
    ]
})
javascript
// webpack.prod.js
// 生产环境的配置 主要是指定mode: production
const { merge } = require('webpack-merge');
const commonConfig = require('./webpack.common.js');

module.exports = merge(commonConfig, {
    mode: 'production',
})
javascript
// constant.js
// 一些常量
const path = require('path');

const PROJECT_PATH = path.resolve(__dirname, '../');
const PROJECT_NAME = path.parse(PROJECT_PATH).name;

const isDev = process.env.NODE_ENV !== 'production'; //从环境变量中读当前的打包环境

const SERVER_HOST = '0.0.0.0';
const SERVER_PORT = '3000';

module.exports = {
    PROJECT_PATH,
    PROJECT_NAME,
    isDev,
    SERVER_HOST,
    SERVER_PORT
}