umi 项目增加支持放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录,就比如 vue-cli 的 assetDir

简介:最近需要做一个后台系统,时间又比较充足,antd 和 Ant Design Pro 都已经升级到了 v4 ,于是就果断尝鲜了一波,遇到的问题也比较多,比较难解决的就是类似 vue-cli 的 assetDir 的问题,现把解决方案整理如下。

提示: 不要配置 config.optimization.splitChunks ,会导致 umi 项目运行失败。

1.项目主要依赖

package.json

{
  "dependencies": {
    "@ant-design/icons": "^4.0.0",
    "@ant-design/pro-layout": "^5.0.16",
    "@ant-design/pro-table": "2.3.4",
    "antd": "4.3.3",
    "axios": "^0.19.2",
    "braft-editor": "^2.3.9",
    "braft-polyfill": "^0.0.2",
    "classnames": "^2.2.6",
    "cropperjs": "^1.5.7",
    "font-awesome": "^4.7.0",
    "js-cookie": "^2.2.1",
    "js-md5": "^0.7.3",
    "lodash": "^4.17.11",
    "moment": "^2.25.3",
    "omit.js": "^1.0.2",
    "path-to-regexp": "2.4.0",
    "prop-types": "^15.7.2",
    "qs": "^6.9.0",
    "react": "^16.8.6",
    "react-cropper": "^1.3.0",
    "react-dom": "^16.8.6",
    "react-helmet-async": "^1.0.4",
    "umi": "^3.1.2",
    "umi-request": "^1.0.8",
    "use-merge-value": "^1.0.1"
  }
}

2.项目结构

    ├── config                 # 配置文件,包含umi内置功能和插件的配置以及webpack和babel的配置。
    │   ├── config.dev.js      # 开发环境变量
    │   ├── config.js          # 配置文件
    │   ├── config.prod.js     # 生产环境变量
    │   └── defaultSettings.js # 网站和antd的配置
    ├── public                 # 静态文件,不变化的logo和icon等
    ├── src                    # 主代码文件
        ├── assets             # 静态文件,网站需要的图片,字体等
        │
        ├── components         # jsx组件
        ├── global.jsx         # 全局js
        ├── global.less        # 全局样式
        ├── icons              # 自定义svg图标
        ├── layouts            # 网站基本布局,权限校验
        ├── locales            # 语言配置
        ├── models             # store数据仓库
        ├── pages              # 网站页面
        ├── routes             # 网站路由文件
        │
        ├── service-worker.js  # pwa文件
        ├── services           # api接口
        │
        ├── styles             # 全局样式
        │
        └── utils              # 工具,包含请求库,本地存储等

3.主要修改 chainWebpack 配置 (defaultSettings 和 routes 文件可以根据自己项目删除)

config/config.js

// https://umijs.org/config/
import { defineConfig } from "umi";
import defaultSettings from "./defaultSettings";
import routes from "../src/routes";

const path = require("path");
const CompressionWebpackPlugin = require("compression-webpack-plugin");
const isEnvProduction = process.env.NODE_ENV === "production";
const isEnvDevelopment = process.env.NODE_ENV === "development";
const resolve = (dir) => path.join(__dirname, dir);
const assetDir = "static";

const config = defineConfig({
  history: { type: "hash" },
  publicPath: "./",
  hash: true,
  antd: {},
  dva: {
    hmr: true,
  },
  chainWebpack(config, { env, webpack, createCSSRule }) {
    // 修改js,js chunk文件输出目录
    config.output
      .filename(assetDir + '/js/[name].[hash:8].js')
      .chunkFilename(assetDir + '/js/[name].[contenthash:8].chunk.js')

    // 修改css输出目录
    config.plugin("extract-css").tap(() => [
      {
        filename: `${assetDir}/css/[name].[contenthash:8].css`,
        chunkFilename: `${assetDir}/css/[name].[contenthash:8].chunk.css`,
        ignoreOrder: true,
      },
    ]);

    // 修改图片输出目录
    config.module
      .rule("images")
      .test(/\.(png|jpe?g|gif|webp|ico)(\?.*)?$/)
      .use("url-loader")
      .loader(require.resolve("url-loader"))
      .tap((options) => {
        const newOptions = {
          ...options,
          name: assetDir + "/img/[name].[hash:8].[ext]",
          fallback: {
            ...options.fallback,
            options: {
              name: assetDir + "/img/[name].[hash:8].[ext]",
              esModule: false,
            },
          },
        };
        return newOptions;
      });

    // 修改svg输出目录
    config.module
      .rule("svg")
      .test(/\.(svg)(\?.*)?$/)
      .use("file-loader")
      .loader(require.resolve("file-loader"))
      .tap((options) => ({
        ...options,
        name: assetDir + "/img/[name].[hash:8].[ext]",
      }));

    // 修改fonts输出目录
    config.module
      .rule("fonts")
      .test(/\.(eot|woff|woff2|ttf)(\?.*)?$/)
      .use("file-loader")
      .loader(require.resolve("file-loader"))
      .tap((options) => ({
        ...options,
        name: assetDir + "/fonts/[name].[hash:8].[ext]",
        fallback: {
          ...options.fallback,
          options: {
            name: assetDir + "/fonts/[name].[hash:8].[ext]",
            esModule: false,
          },
        },
      }));

    // 添加gzip压缩
    config.when(isEnvProduction, (config) => {
      config
        .plugin("compression-webpack-plugin")
        .use(CompressionWebpackPlugin, [
          {
            filename: "[path].gz[query]",
            algorithm: "gzip",
            test: new RegExp("\\.(js|css)$"),
            threshold: 10240,
            minRatio: 0.8,
          },
        ]);
    });
  },
  // 生产环境去除console日志打印
  terserOptions: {
    compress: {
      drop_console: isEnvProduction,
    },
  },
  locale: {
    // default zh-CN
    default: "zh-CN",
    // default true, when it is true, will use `navigator.language` overwrite default
    antd: true,
    baseNavigator: true,
  },
  dynamicImport: {
    loading: "@/components/PageLoading/index",
  },
  targets: {
    ie: 11,
  },
  // umi routes: https://umijs.org/docs/routing
  routes,
  // Theme for antd: https://ant.design/docs/react/customize-theme-cn
  theme: {
    // ...darkTheme,
    "primary-color": defaultSettings.primaryColor,
  },
  // @ts-ignore
  title: defaultSettings.title,
  ignoreMomentLocale: true,
  manifest: {
    basePath: "/",
  },
  devServer: {
    open: true,
    port: 8367,
  },
});

export default config;

4.打包后输出的 dist 目录

├── asset-manifest.json
├── favicon.ico
├── index.html
└── static
    ├── css
    ├── fonts
    ├── img
    └── js

参考链接

1.https://github.com/umijs/umi/blob/master/packages/bundler-webpack/src/getConfig/getConfig.ts

问题来源

1.https://github.com/umijs/umi/issues/4490

Logo

前往低代码交流专区

更多推荐