自定义主题方案

最近团队正在开发一个组件库,其中遇到一个问题是:在不同业务中使用时,需要根据 UI 定制不同的主题色。

如何实现?

参考 Antd 的主题方案,在构建时,将主题色变量传递给 lessLoader 中 modifyVars 属性,通过 less 变量实现颜色控制。这其中也有很多细节,我们展开聊一下。
首先,我们项目使用 umi 构建,需要配置 .umirc(当然如果你使用 webpack 也基本类似,下面不再特别说明)

// .umirc 配置
const theme = require('./config/share').read('myTheme')
...
const lessLoader = {
  modifyVars: {
    ...theme,
    '@base-font-size': '12px',
  },
  javascriptEnabled: true,
}

export default {
  ...
  lessLoader,
  ...
}
复制代码

config/share 主要是用于处理主题变量的 less 文件,包括处理文件依赖,相当是一个 plugin:

const lessJs = require('less-vars-to-js')

exports.read = function (fileName) {
  const { code } = loadLessWithImports(`src/themes/${fileName}.less`)
  return lessJs(code)
}

/*
 * 解析 less 文件中依赖的 less 变量,合并输出
 */
function loadLessWithImports(entry) {
  const input = readFileSync(entry, 'utf8')
  const importRegExp = /^@import\s+['"]([^'"]+)['"];$/gm
  // 判断是否有 @import 引入
  const imports = getRegexpMatches(importRegExp, input).map((match) => {
    const importPath = match[1]
   
    return {
      match,
      path: importPath,
      ...loadLessWithImports(importPath),
    }
  })

  return {
    code: imports.reduceRight( // imports 数组倒序合并,否则默认的 less 文件变量会将自定义变量覆盖
      (acc, { code }) =>
        replaceSubstring(acc, code),
      input
    ),
    imports: imports.reduce(
      (acc, { path, imports: nestedImports }) => [...acc, ...nestedImports, path],
      []
    ),
  }
}

/*
* 获取匹配的 less 文件
*/
function getRegexpMatches(regexp, text) {
  var matches = []
  var lastIndex = regexp.lastIndex
  var match

  do {
    match = regexp.exec(text)
    if (match) {
      matches.push(match)
    } // prevent infinite loop (only regular expressions with `global` flag retain the `lastIndex`)
  } while (match && regexp.global) // don't leak `lastIndex` changes

  regexp.lastIndex = lastIndex

  return matches
}
复制代码

其中主要方法是 loadLessWithImports,因为整体的思路比较清晰,主要是如何实现文件的解析。它接受 less 文件路径作为参数,并通过正则解析 @import 依赖文件,并递归调用解析。

以上 : )

参考链接

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享