React18 + typescript 配 less

187 阅读1分钟

差不多疯了,严重高估我自己,本以为周六一天能搞定UI,结果装了个React18之后,发现好多东西都不一样了,记录一下。 这篇是怎么配less的。

一些版本:

{
    "dependencies": {
        "react": "^18.2.0",
        "webpack": "^5.64.4",
        "typescript": "^4.9.5",
    },
    "devDependencies": {
        "less": "^4.2.0",
        "less-loader": "^11.1.3"
    }
}

首先 npm run eject 暴露配置。 在 config/webpack.config.js 里改:

// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
const lessRegex = /\.less$/;                // to support less
const lessModuleRegex = /\.module\.less$/;  // to support module.less

找sassRegex 和 sassModuleRegex 在哪里,copy,然后改成less

 {
  test: lessRegex,
  exclude: sassModuleRegex,
  use: getStyleLoaders(
    {
      importLoaders: 3,
      sourceMap: isEnvProduction
        ? shouldUseSourceMap
        : isEnvDevelopment,
      modules: {
        mode: 'icss',
      },
    },
    'less-loader'
  ),
  // Don't consider CSS imports dead code even if the
  // containing package claims to have no side effects.
  // Remove this when webpack adds a warning or an error for this.
  // See https://github.com/webpack/webpack/issues/6571
  // sideEffects: true,
},
// Adds support for CSS Modules, but using SASS
// using the extension .module.scss or .module.sass
{
  test: lessModuleRegex,
  use: getStyleLoaders(
    {
      importLoaders: 3,
      sourceMap: isEnvProduction
        ? shouldUseSourceMap
        : isEnvDevelopment,
      modules: {
        mode: 'local',
        getLocalIdent: getCSSModuleLocalIdent,
      },
    },
    'less-loader'
  ),
},

这个时候,应该已经能support 这种语法了 import 'xx.less'; import 'xx.module.less' 但是通常我们还想做到像vue的<style lang="less" scoped>的效果,所以我们还需要让ts认识xx.module.less。找到react-app-env.d.ts,模仿'*.module.sass' 写一个for less。

declare module '*.module.less' {
  const classes: {[key:string]: string}; 
  export default classes;
}

搞定,不会报错了。

如果在browser里面你的className={${styles.myLessStyle}} 编译出来是 undefined,请继续改webpack.config.js 吧。先找到这一段

{
  test: lessModuleRegex,
  use: getStyleLoaders(
    {
      importLoaders: 3,
      sourceMap: isEnvProduction
        ? shouldUseSourceMap
        : isEnvDevelopment,
      modules: {
        mode: 'local',
        getLocalIdent: getCSSModuleLocalIdent,
      },
    },
    'less-loader'
  ),
},

getLocalIdent 就是用来定义你编译出来的class名字。它在这里const getCSSModuleLocalIdent = require("react-dev-utils/getCSSModuleLocalIdent");。跟进源代码看看,发现它并不支持less。如果你的项目不用重新npm install,直接改掉就行。如果你跟我一样,因为用jekins build package 的时候规定必须重新npm install,那就把"react-dev-utils/getCSSModuleLocalIdent"copy 到webpack.config.js,改它

const getLessModuleLocalIdent = (
  context,
  localIdentName,
  localName,
  options
) => {
  // Use the filename or folder name, based on some uses the index.js / index.module.(css|scss|sass) project style
  const fileNameOrFolder = context.resourcePath.match(
    /index\.module\.(css|scss|sass|less)$/
  )
    ? '[folder]'
    : '[name]';
  // Create a hash based on a the file location and class name. Will be unique across a project, and close to globally unique.
  const hash = loaderUtils.getHashDigest(
    path.posix.relative(context.rootContext, context.resourcePath) + localName,
    'md5',
    'base64',
    5
  );
  // Use loaderUtils to find the file or folder name
  const className = loaderUtils.interpolateName(
    context,
    fileNameOrFolder + '_' + localName + '__' + hash,
    options
  );
  // Remove the .module that appears in every classname when based on the file and replace all "." with "_".
  return className.replace('.module_', '_').replace(/\./g, '_');
};

然后test: lessModuleRegexgetLocalIdent改成getLessModuleLocalIdent。好,Fixed liao leh!

{
  test: lessModuleRegex,
  use: getStyleLoaders(
    {
      importLoaders: 3,
      sourceMap: isEnvProduction
        ? shouldUseSourceMap
        : isEnvDevelopment,
      modules: {
        mode: 'local',
        getLocalIdent: getLessModuleLocalIdent,
      },
    },
    'less-loader'
  ),
},

再试试

import styles from 'xx.module.less'; 

const myComponent = () => {
    return <div className={`${styles.myLessStyle}`}></div>
}

export default myComponent

这个div的class 应该会被翻译成 xx_myLessStyle_一串哈希码