手动搭webpack + React Hooks + TypeScript + Antd(五)

992 阅读1分钟
前章见

手动搭webpack + React Hooks + TypeScript + Antd(一)

手动搭webpack + React Hooks + TypeScript + Antd(二)

手动搭webpack + React Hooks + TypeScript + Antd(三)

手动搭webpack + React Hooks + TypeScript + Antd(四)

本章主要引入antd theme多主题的配置

安装antd

yarn add antd

antd引入一般需要做按需加载,如果不按需加载那么js css过大,如果不按需加载那么打包的结果大小👇

即便通过`css-minimizer-webpack-plugin`压缩后依然过大

安装babel-plugin-import

yarn babel-plugin-import -D

babel-plugin-import中文介绍

babel.config.js plugin  配置添加

const plugins = [
      ['react-hot-loader/babel'],

+     ['import',
+      {
+        libraryName: 'antd',
+        style: true,
+      }
+    ]

];

src/pages/about/index.tsx

+  import { Button } from 'antd';

...

+  <Button type="primary">primary</Button>
+  <Button>Button</Button>

...

run的时候报错,因为style配置为true的时候会将antd的css一并引入

但是我们在配置less loader的时候并没有配置node_modules中的less

build/webpack.dev.js

webpack.dev.js也引入MiniCssExtractPlugin

module: {
  rules: [
  ...

+    {
+        test: /\.less$/,
+        include: /node_modules/,  // antd less和主题不进行module
+        use: [
+          MiniCssExtractPlugin.loader,
+          "css-loader",
+          {
+            loader: "less-loader",
+            options: {
+              lessOptions: {
+                javascriptEnabled: true,
+              },
+            },
+          },
+        ],
+      },

  ...
  ]
}

antd主题适配

分为静态适配和动态主题设置

  • 静态适配很简单
方法一(推荐)

lessOptions中加入modifyVars 具体颜色适配、主题变量见antd官网

build/webpack.dev.js

lessOptions: {
    javascriptEnabled: true,
+      modifyVars: {
+          '@primary-color': '#1DA57A',
+      },
},
此方法按需加载仍然适用,打包的css体积也较小,好一般静态配置到这里就结束了.

方法二(不推荐) 新建一个less引入官方提供的 less 样式入口文件

在assets下新建style文件夹,新建theme文件夹,里面新建 blue.variables.less red.variables.less之类的文件 然后分别写入各自的主题变量

src/assets/style/theme/red.variables.less

@import '~antd/dist/antd.less';  // 引入官方提供的 less 样式入口文件
@primary-color: #f00; // 全局主色

// ...
App.tsx中引入即可
import '@assets/style/theme/red.variables.less';

(注意此种方法不能按需加载,配置了babel-plugin-import的style也没用)那为什么要在这里大费周章的探索多主题适配,是因为我们甲方爸爸要做多主题适配...

注意:如果在样式less里面引入antd一定不要将其module了,不然antd组件样式无效而且就算是分开配置其css-loader也无效,因为在src的less中引入的antd不会走webpack中css-loader的include:/node_modules/这个配置

所以antd的样式需要这么配置,直接将variables.less不module,才能达到效果

build/webpack.dev.js

include: /node_modules|variables\.less/, // antd less和主题不进行module

多主题配置因为主题是后台配置,需要发送请求才能知道当前用户配置的主题颜色,所以需要将所有主题以及antd先全部打包出来,获取到用户配置主题后通过link标签append的方式引入

去掉babel-plugin-import的style配置,因为完全打包所以按需加载没用

build/config.js   写入所有主题配置文件

const THEMES_PATH = {
    'theme.blue': './src/assets/style/theme/blue.variables.less',
    'theme.red': './src/assets/style/theme/red.variables.less',
    // ...
};

module.exports = {
   cssLoaderConfig,
+  THEMES_PATH,
};

build/webpack.common.js   修改entry入口

entry: {
    main: './src/index.tsx',
    ...config.THEMES_PATH, // 将antd css主题全部打包 再通过append style标签
},

页面中也不需要引入antd.css或者less了,直接引入需要的组件即可,babel.config.js中的style也注释掉

less-loader中的modifyVars也去掉,也无效了,手写的主题颜色已经将其覆盖了

然后run start,发现页面有antd的样式

查看页面才发现把blue和red的css全引入了,生成的样式js也全引入了,这还得了!!

new HtmlWebpackPlugin加入下面代码,不自动引入打包的样式css和js

+   const config = require('./config');
new HtmlWebpackPlugin({
     template: 'public/index.html',
+    excludeChunks: [...Object.keys(config.THEMES_PATH)],
}),
即回到正常无antd模式

要手动引入主题,需要将其打包出的css, run build试试

但怎么将其引入呢...

HtmlWebpackPlugin加入templateParameters在html中查看所有打包的内容

...
       template: 'public/index.html',
       excludeChunks: [...Object.keys(config.THEMES_PATH)],
+      templateParameters(compilation, assets, options) {
+        return {
+          compilation,
+          webpack: compilation.getStats().toJson(),
+          htmlWebpackPlugin: {
+            files: assets,
+            options,
+          },
+        };
      },
...

在html中使用ejs语法

<div id="root"></div>
    <p></p>
    <div style="border: 1px solid #f00">
    <% for(var key in webpack) { %>
    <span style="margin-right: 10px;"><%= key %></span>
    <% } %>
</div>

<div style="border: 1px solid #000">
    <P><span style="color: #f00;">publicPath:</span> <%= JSON.stringify(webpack.publicPath) %></P>
    <P><span style="color: #f00;">outputPath: </span><%= JSON.stringify(webpack.outputPath) %></P>
    <P><span style="color: #f00;">assetsByChunkName: </span><%= JSON.stringify(webpack.assetsByChunkName) %></P>
    <P><span style="color: #f00;">hash: </span><%= JSON.stringify(webpack.hash) %></P>
    <P><span style="color: #f00;">entrypoints: </span><%= JSON.stringify(webpack.entrypoints) %></P>
    <P><span style="color: #f00;">namedChunkGroups: </span><%= JSON.stringify(webpack.namedChunkGroups) %></P>
    <P><span style="color: #f00;">filteredModules: </span><%= JSON.stringify(webpack.filteredModules) %></P>
</div>

重新run start在页面中可以看到所有的webpack的内容

那么在这里我们需要assetsByChunkName

在html中添加如下代码

public/index.html

....
<div id="root"></div>

<script type="text/javascript">
    (function() {
      var assetsByChunkNameString = '<%= JSON.stringify(webpack.assetsByChunkName) %>';
      var assetsByChunkName = JSON.parse(assetsByChunkNameString);
      var THEME = {};
      for(var key in assetsByChunkName) {
        if (/theme/.test(key)) {
          var theme = key.replace(/theme\.(\w+)/, '$1');
          if (Object.prototype.toString.call(assetsByChunkName[key]) === '[object Array]') THEME[theme] = assetsByChunkName[key][0];
          else if(typeof assetsByChunkName[key] === 'string') THEME[theme] = assetsByChunkName[key];
        }
      }
      var xhr = new XMLHttpRequest();
      xhr.onreadystatechange = function handleDone() {
        if (xhr.readyState === XMLHttpRequest.DONE) {
          var theme = 'orange';
          if (xhr.status === 200) {
            var res = JSON.parse(xhr.responseText || '{}');
            theme = {
              '0': 'red', 
              '1': 'blue',
            }[res.data] || theme;
          }
          var cssUrl = THEME[theme];
          var head = document.getElementsByTagName('head')[0];
          var link = document.createElement('link');
          link.type='text/css';
          link.rel = 'stylesheet';
          link.href = cssUrl;
          head.appendChild(link);
          link = null;
        }
      };  
      xhr.open('GET', 'urlxxxxx', true);
      xhr.send();
    })();
  </script>

...
页面即显示正常

run build 后打开也没有问题