当前位置: 首页 > news >正文

前端组件库自定义主题切换探索-03-webpack-theme-color-replacer webpack 同时替换多个颜色改造

接上一篇《前端组件库自定义主题切换探索-02-webpack-theme-color-replacer webpack 的实现逻辑和原理-02》
这篇我们来开始改造,让这个插件最终能达到我们的目的:

首先修改plugin.config.js

插件首先要在vue.config.js引用注册,因此先对这里做改造。这里我们指定了四种颜色,primary,danger,warning,other,然后生成配置数据,数据格式如下

{primary: {matchColors: getAntdSerials(colorTypes[type]), // colors array for extracting css file, support rgb and hsl.fileName: `css/${type}-colors-[contenthash:8].css`, // optional. output css file name, suport [contenthash] and [hash].configVar: `tc_cfg_${type}` + Math.random().toString().slice(2),
}
}

后面的代码逻辑都会根据对象的键名(比如primary)来读取每个键名下面的配置来做批量操作

最终代码如下:

const ThemeColorReplacer = require("./webpack-theme-color-replacer/src/index")
// const ThemeColorReplacer = require("webpack-theme-color-replacer")
const generate = require("@ant-design/colors/lib/generate").defaultconst getAntdSerials = (color) => {// 淡化(即less的tint)const lightens = new Array(9).fill().map((t, i) => {return ThemeColorReplacer.varyColor.lighten(color, i / 10)})// console.log("lightens", lightens)const colorPalettes = generate(color)// console.log("colorPalettes", colorPalettes)const rgb = ThemeColorReplacer.varyColor.toNum3(color.replace("#", "")).join(",")// console.log("rgb", rgb)const matchColors = lightens.concat(colorPalettes).concat(rgb)// console.log("matchColors", matchColors)return matchColors
}
const getRandomString = () => Math.random().toString().slice(2)
const colorTypes = {primary: "#1890ff",danger: "#F5222D",warning: "#F2A830",other: "#35964f",
}
const option = {}
for (const type in colorTypes) {option[type] = {matchColors: getAntdSerials(colorTypes[type]), // colors array for extracting css file, support rgb and hsl.fileName: `css/${type}-colors-[contenthash:8].css`, // optional. output css file name, suport [contenthash] and [hash].configVar: `tc_cfg_${type}` + getRandomString(),}
}
const createThemeColorReplacerPlugin = () => new ThemeColorReplacer({option
})module.exports = createThemeColorReplacerPlugin

在这个验证测试代码过程中,也遇到了不少问题,这里为了省事点,我们就不赘述中间的曲折,直接上改好的代码,后面再附加一些注意的地方

第二个改scr下面的index.js

这个文件主要是根据option生成LC_THEME_CONFIG变量里面的内容,关键代码是

      const { option } = this.handler.optionsfor (const i in option) {webpackDefineConfig[i] = option[i].configVar}new webpack.DefinePlugin({LC_THEME_CONFIG: JSON.stringify(webpackDefineConfig)}).apply(compiler)

完整的代码如下:

"use strict"
var Handler = require("./handler")var webpack = require("webpack")class ThemeColorReplacer {constructor(options) {this.handler = new Handler(options)}getBinder(compiler, event) {return compiler.hooks? compiler.hooks[event].tapAsync.bind(compiler.hooks[event], "ThemeColorReplacer"): compiler.plugin.bind(compiler, event)}apply(compiler) {const webpackDefineConfig = {}const { option } = this.handler.optionsfor (const i in option) {webpackDefineConfig[i] = option[i].configVar}new webpack.DefinePlugin({LC_THEME_CONFIG: JSON.stringify(webpackDefineConfig)}).apply(compiler)compiler.hooks.thisCompilation.tap("ThemeColorReplacer", (compilation) => {compilation.hooks.processAssets.tapAsync({name: "ThemeColorReplacer",stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS},(compilationAssets, callback) => {this.handler.handle(compilation)callback()})})}
}ThemeColorReplacer.varyColor = require("../client/vary-color")module.exports = ThemeColorReplacer

第三改造index.js直接引用的Handler.js

这里需要改造的关键地方是

        const { option } = this.options// let { injectToHtml } = this.optionsfor (const i in option) {const { fileName, matchColors, configVar } = option[i]const output = this.assetsExtractor.extractAssets(compilation.assets, matchColors, i)// console.log("handle output", output)// console.log("Extracted theme color css content length: " + output.length)const outputName = compilation.getPath(replaceFileName(fileName, output), {})this.emitSource(compilation, outputName, new wpSources.RawSource(output))// console.log("fileName", fileName)// console.log("matchColors", matchColors)// 记录动态的文件名,到每个入口js// console.log("outputName", outputName)this.addToEntryJs(outputName, compilation, output, matchColors, configVar)}

另外,其他地方需要传递matchColors和configVar,之前因为只改一种颜色,所以是直接用最外面传递进来的,现在要改变多个则需要在for循环里面传递,最终修改代码如下:

"use strict"
var webpack = require("webpack")
var AssetsExtractor = require("./assets-extractor")
var replaceFileName = require("./replace-file-name")
var LineReg = /\n/g
var wpSources = webpack.sources
if (!wpSources) {wpSources = require("webpack-sources") // for webpack 4
}
module.exports = class Handler {constructor(options) {this.options = {isJsUgly: !(process.env.NODE_ENV === "development" || process.argv.find(arg => arg.match(/\bdev/))),...options}this.assetsExtractor = new AssetsExtractor(this.options.isJsUgly, this.options.changeSelector)}// Add Webpack5 SupportemitSource(compilation, name, source) {console.log("emitSource name", name)var exists = compilation.assets[name]if (compilation.updateAsset) { // webpack.version[0] >= '5'if (exists) compilation.updateAsset(name, source)else compilation.emitAsset(name, source)} else {if (exists) delete compilation.assets[name]compilation.assets[name] = source}}handle(compilation) {// Add to assets for outputconst { option } = this.options// let { injectToHtml } = this.optionsfor (const i in option) {const { fileName, matchColors, configVar } = option[i]const output = this.assetsExtractor.extractAssets(compilation.assets, matchColors, i)// console.log("handle output", output)// console.log("Extracted theme color css content length: " + output.length)const outputName = compilation.getPath(replaceFileName(fileName, output), {})this.emitSource(compilation, outputName, new wpSources.RawSource(output))// console.log("fileName", fileName)// console.log("matchColors", matchColors)// 记录动态的文件名,到每个入口js// console.log("outputName", outputName)this.addToEntryJs(outputName, compilation, output, matchColors, configVar)}}// 自动注入js代码,设置css文件名addToEntryJs(outputName, compilation, cssCode, matchColors, configVar) {const onlyEntrypoints = {entrypoints: true,errorDetails: false,modules: false,assets: false,children: false,chunks: false,chunkGroups: false}const entrypoints = compilation.getStats().toJson(onlyEntrypoints).entrypointsObject.keys(entrypoints).forEach(entryName => {const entryAssets = entrypoints[entryName].assetsfor (let i = 0, l = entryAssets.length; i < l; i++) {const assetName = entryAssets[i].name || entryAssets[i]if (assetName.slice(-3) === ".js" && assetName.indexOf("manifest.") === -1) { //const assetSource = compilation.assets[assetName]if (assetSource && !assetSource._isThemeJsInjected) {const cSrc = this.getEntryJs(outputName, assetSource, cssCode, matchColors, configVar)// cSrc._isThemeJsInjected = truethis.emitSource(compilation, assetName, cSrc)break}}}})}getConfigJs(outputName, cssCode, matchColors, configVar) {const config = { url: outputName, colors: matchColors }if (this.options.injectCss) {config.cssCode = cssCode.replace(LineReg, "")}return "\n(typeof window=='undefined'?global:window)." + configVar + "=" + JSON.stringify(config) + ";\n"}getEntryJs(outputName, assetSource, cssCode, matchColors, configVar) {const ConcatSource = wpSources.ConcatSourceconst CachedSource = wpSources.CachedSourceconst configJs = this.getConfigJs(outputName, cssCode, matchColors, configVar)if (assetSource instanceof CachedSource) { // CachedSource没有node方法,会报错return new CachedSource(concatSrc(assetSource._source || assetSource.source(), configJs))}return concatSrc(assetSource, configJs)function concatSrc(assetSource, configJs) {if (assetSource instanceof ConcatSource) {assetSource.add(configJs)return assetSource} else {return new ConcatSource(assetSource, configJs)}}}
}

这里需要注意的是,在addToEntryJs函数里面,需要吧cSrc._isThemeJsInjected = true去掉。这里是之前插件为了做缓存用的,在测试中发现,加上这一行,window里面最多挂两个tc_cfg_变量,
在这里插入图片描述
在原本的判断里面,执行cSrc._isThemeJsInjected = true时,assetSource._isThemeJsInjected 也会变为true,因为this.getEntryJs返回的是一个浅拷贝,且和assetSource同源,从下面的代码可以看到数据的来源
在这里插入图片描述
在这里插入图片描述
数据最终通过webpack处理而来,至于为什么是浅拷贝,是同源的,没有得去深究。

replace-file-name 不需要更改,源代码即可。

第四需要改的是assets-extractor.js

(原插件是AssetsExtractor,这里为了符合开发规范改了名字)文件。这里没有大改的地方,而是将参数改为从调用的地方传递,而不是直接取自option,改动后代码如下:

var Extractor = require("./extractor")
var cssLoaderRegDev = /\bn?(?:exports|___CSS_LOADER_EXPORT___)\.push\(\[module\.id?, \\?"(.+?\})(?:\\?\\n)?(?:[\\n]*\/\*#\s*sourceMappingURL=.+?\*\/)?\\?", \\?"\\?"(?:\]\)|,\s*\{)/g// css-loader:  n.exports=t("FZ+f")(!1)).push([n.i,"\n.payment-type[data-v-ffb10066] {......}\n",""])
var cssLoaderRegUgly = /\.push\(\[\w+\.i,['"](.+?\})[\\rn]*['"],['"]['"](?:\]\)|,\{)/g
var CssExtReg = /\.css$/i; var JsExtReg = /\.js$/i
function assetToStr(asset) {var src = asset.source() || ""return src.toString()
}
const extractAsset = function (fn, asset, matchColors, isJsUgly, changeSelector) {const src = assetToStr(asset)var cssSrcs = []var CssCodeReg = isJsUgly ? cssLoaderRegUgly : cssLoaderRegDevsrc.replace(CssCodeReg, (match, $1) => {cssSrcs = cssSrcs.concat(Extractor(changeSelector, $1, matchColors))})// console.log("cssSrcs", cssSrcs.filter(item=>item))return cssSrcs.filter(item => item)
}
function extractAll(assets, matchColors, isJsUgly, changeSelector, type) {// console.log("extractAll matchColors", matchColors)var cssSrcs = []Object.keys(assets).map(fn => {// 原本每修改一点源码,都需要对整个项目的assets翻一遍css,影响性能。// 故改为在asset上缓存上一次的结果,对没发生变化的asset直接取缓存(已发生变化的asset已经是新对象,无缓存)。// console.log("fn", fn)const asset = assets[fn]// console.log("asset._themeCssCache", asset._themeCssCache)let cssRules = ""if (asset._themeCssCache && asset._themeCssCache[type]) {cssRules = asset._themeCssCache[type]} else {cssRules = extractAsset(fn, asset, matchColors, isJsUgly, changeSelector)}// asset._themeCssCache || extractAsset(fn, asset, matchColors, isJsUgly, changeSelector)// console.log("cssRules", cssRules)if (asset._themeCssCache) {asset._themeCssCache[type] = cssRules} else {asset._themeCssCache = {[type]: cssRules}}cssSrcs = cssSrcs.concat(cssRules)})// console.log("cssSrcs", cssSrcs)// console.log("cssSrcs.filter(item => item)", cssSrcs.filter(item => item))return cssSrcs
}module.exports = function AssetsExtractor(isJsUgly, changeSelector) {this.extractAssets = function (assets, matchColors, type) {// console.log("assets", assets)var srcArray = this.extractToArray(assets, matchColors, type)// console.log("srcArray", srcArray)// 外部的css文件。如cdn加载的var output = dropDuplicate(srcArray).join("\n")return output}this.extractToArray = function (assets, matchColors, type) {var srcArray = extractAll(assets, matchColors, isJsUgly, changeSelector, type)// console.log("srcArray------------------------------------------------------", srcArray)if (srcArray.length === 0 && !this._uglyChanged) {// 容错一次this._uglyChanged = trueisJsUgly = !isJsUgly// 清空缓存Object.keys(assets).map(fn => assets[fn]._themeCssCache = 0)srcArray = extractAll(assets, matchColors, isJsUgly, changeSelector, type)}return srcArray}
}function dropDuplicate(arr) {var map = {}var r = []for (var s of arr) {if (!map[s]) {r.push(s)map[s] = 1}}return r
}

这里需要注意的一点是,asset._themeCssCache 需要按照外面传递的类型来存储,否则后面的颜色不生效

        const asset = assets[fn]// console.log("asset._themeCssCache", asset._themeCssCache)let cssRules = ""if (asset._themeCssCache && asset._themeCssCache[type]) {cssRules = asset._themeCssCache[type]} else {cssRules = extractAsset(fn, asset, matchColors, isJsUgly, changeSelector)}// asset._themeCssCache || extractAsset(fn, asset, matchColors, isJsUgly, changeSelector)// console.log("cssRules", cssRules)if (asset._themeCssCache) {asset._themeCssCache[type] = cssRules} else {asset._themeCssCache = {[type]: cssRules}}

第五需要改的是assets-extractor.js直接引用的extractor.js

这里需要改动的是将matchColors从外部直接传入,而不是从option取用
改动后代码如下

var extractorCss = require("./css-extractor")
const testCssCode = function (cssCode, matchColors) {var matchColorRegs = matchColors // ['#409EFF', '#409eff', '#53a8ff', '#66b1ff', '#79bbff', '#8cc5ff', '#a0cfff', '#b3d8ff', '#c6e2ff', '#d9ecff', '#ecf5ff', '#3a8ee6', '#337ecc'].map(c => new RegExp(c.replace(/\s/g, "").replace(/,/g, ",\\s*") + "([\\da-f]{2})?(\\b|\\)|,|\\s)", "i")) // 255, 255,3for (var colorReg of matchColorRegs) {if (colorReg.test(cssCode)) return true // && !ExclueCssReg.test(cssCode)}return false
}
module.exports = function Extractor(changeSelector, src, matchColors) {return extractorCss(src, changeSelector).map(function (css) {var rules = css.rules.filter(cssCode => testCssCode(cssCode, matchColors))if (!rules.length) return ""return css.selector + "{" + rules.join(";") + "}"})
}

第六个需要改的是extractor.js直接引用的css-extractor.js

同样是需要改动传参方式,最终代码如下

// \n和备注
var regLfRem = /\\\\?n|\n|\\\\?t|\\\\?r|\/\*[\s\S]+?\*\//gvar SpaceReg = /\s+/g
var TrimReg = /(^|,)\s+|\s+($)/g // 前空格,逗号后的空格; 后空格
var SubCssReg = /\s*>\s*/g // div > a 替换为 div>a
var DataUrlReg = /url\s*\([\\'"\s]*data:/ // url("data:image/svg+xml;base64,PHN2")
var QuotReg = /\\+(['"])/g
// var ExclueCssReg = /(?:scale3d|translate3d|rotate3d|matrix3d)\s*\(/i;
module.exports = function extractCss(src, changeSelector) {src = src.replace(regLfRem, "")var ret = []var nameStart; var nameEnd; var cssEnd = -1while (true) {nameStart = cssEnd + 1nameEnd = src.indexOf("{", nameStart)cssEnd = findCssEnd(src, nameEnd)if (cssEnd > -1 && cssEnd > nameEnd && nameEnd > nameStart) {var cssCode = src.slice(nameEnd + 1, cssEnd)if (cssCode.indexOf("{") > -1) { // @keyframesvar rules = extractCss(cssCode, changeSelector)} else {rules = getRules(cssCode)}if (rules.length) {var selector = src.slice(nameStart, nameEnd)selector = selector.replace(TrimReg, "$1")selector = selector.replace(SubCssReg, ">")selector = selector.replace(SpaceReg, " ") // linesvar p = selector.indexOf(";") // @charset utf-8;if (p > -1) {selector = selector.slice(p + 1)}// 改变选择器if (changeSelector) {var util = {rules: rules,changeEach: changeEach}selector = changeSelector(selector.split(",").sort().join(","), util) || selector}ret.push({ selector, rules: rules })}} else {break}}return ret// 查找css尾部,兼容 @keyframes {10%{...}}function findCssEnd(src, start) {var level = 1var cssEnd = startwhile (true) {cssEnd++var char = src[cssEnd]if (!char) {return -1} else if (char === "{") {level++} else if (char === "}") {level--if (level === 0) {break}}}return cssEnd}function changeEach(selector, surfix, prefix) {surfix = surfix || ""prefix = prefix || ""return selector.split(",").map(function (s) {return prefix + s + surfix}).join(",")}
}function getRules(cssCode) {var rules = cssCode.split(";")var ret = []for (var i = 0; i < rules.length; i++) {var rule = rules[i].replace(/^\s+|\s+$/, "")if (!rule) continueif (rule.match(DataUrlReg)) {rule += ";" + rules[i + 1]rule = rule.replace(QuotReg, "$1")i++}ret.push(rule.replace(SpaceReg, " "))}return ret
}

现在src下面的文件更改完了,我们先去改调用处的vue文件。

第七是路由文件theme-example.vue

<template><basic-container><div><a-button type="primary">主色-primary</a-button><a-button type="danger">报错色-danger</a-button><span class="my-theme-color">测试自定义主题色</span><span class="my-warning-color">测试自定义警告色</span><span class="my-other-color">测试自定义其他颜色</span></div><setting-drawer ref="settingDrawer"/></basic-container>
</template>
<script lang="ts">
import BasicContainer from "../../components/layouts/basic-container2.vue"
import { Component, Vue } from "vue-property-decorator"
import SettingDrawer from "../../../packages/setting-drawer"@Component({components: {BasicContainer,SettingDrawer},
})
export default class ThemeExample extends Vue {}
</script>
<style scoped lang="less">
.my-theme-color{color: #1890ff;
}
.my-warning-color{color: #F2A830;
}
.my-other-color{color: #35964f;
}
</style>

第八是setting-drawer.vue,至于basic-container2.vue,就是一个容器文件,先不去管

<template><div class="setting-drawer"><div class="setting-drawer-index-content"><div :style="{ marginTop: '24px' }"><h3 class="setting-drawer-index-title">切换颜色列表</h3><div><a-tooltip class="setting-drawer-theme-color-colorBlock" v-for="(item, index) in colorList" :key="index"><template slot="title">{{ item.key }}</template><a-tag :color="item.color" @click="changeColor(item.color,index)"><a-icon type="check" v-if="item.color === color"></a-icon><a-icon type="check" style="color: transparent;" v-else></a-icon></a-tag></a-tooltip></div></div></div></div>
</template><script>
import { updateTheme, colorList } from "./settingConfig"export default {data () {return {colorList,color: "",}},methods: {changeColor (color, index) {updateTheme({primary: color,danger: this.colorList[index + 1] ? this.colorList[index + 1].color : this.colorList[0].color,warning: this.colorList[index + 2] ? this.colorList[index + 2].color : (this.colorList[index + 1] ? this.colorList[0].color : this.colorList[1].color),other: this.colorList[index + 3] ? this.colorList[index + 3].color : (this.colorList[index + 2] ? this.colorList[index + 2].color : this.colorList[index + 1] ? this.colorList[0].color : this.colorList[2].color),})},}
}
</script>

第九需要改动的是setting-drawer.vue直接引用的settingConfig.js

import themeColor from "./themeColor.js"
const colorList = [{key: "薄暮", color: "#F5222D"},{key: "火山", color: "#FA541C"},{key: "日暮", color: "#FAAD14"},{key: "明青", color: "#13C2C2"},{key: "极光绿", color: "#52C41A"},{key: "拂晓蓝(默认)", color: "#1890FF"},{key: "极客蓝", color: "#2F54EB"},{key: "酱紫", color: "#722ED1"},{key: "浅紫", color: "#9890Ff"}
]const updateTheme = (changeColors) => {// no-undefthemeColor.changeColor(changeColors).finally(() => {})
}export { updateTheme, colorList }

第十需要改动的是settingConfig.js直接引用的themeColor.js

import client from "../../../config/webpack-theme-color-replacer/client"
import generate from "@ant-design/colors/lib/generate"export default {/*** 获取变化的颜色* @param color* @returns {T[]}*/getAntdSerials (color) {// 淡化(即less的tint)const lightens = new Array(9).fill().map((t, i) => {return client.varyColor.lighten(color, i / 10)})// colorPalette变换得到颜色值// console.log("lightens", lightens)const colorPalettes = generate(color)// console.log("colorPalettes", colorPalettes)const rgb = client.varyColor.toNum3(color.replace("#", "")).join(",")// console.log("rgb", rgb)return lightens.concat(colorPalettes).concat(rgb)},/*** 改变颜色* @param changeColors* @returns {Promise<unknown>|Promise<unknown>}*/changeColor (changeColors) {const options = {}for (const i in changeColors) {options[i] = {newColors: this.getAntdSerials(changeColors[i]),changeUrl (cssUrl) {return `/${cssUrl}` // while router is not `hash` mode, it needs absolute path}}}return client.changer.changeColor(options)}
}

这里的关键改动是根据我们的需要组装插件需要的数据源,关键代码如下:

    for (const i in changeColors) {options[i] = {newColors: this.getAntdSerials(changeColors[i]),changeUrl (cssUrl) {return `/${cssUrl}` // while router is not `hash` mode, it needs absolute path}}}

第十一需要改动的是theme-color-changer.js
这里的关键代码是将themeColorConfig按照类型存储,和按照类型从LC_THEME_CONFIG和window中取用数据,关键代码如下:

    for (const i in option) {if (!themeColorConfig[i]) {// eslint-disable-next-line no-undefconsole.log("LC_THEME_CONFIG[i]", LC_THEME_CONFIG[i])// eslint-disable-next-line no-undefthemeColorConfig[i] = win()[LC_THEME_CONFIG[i]]const later = retry(i)// 重试直到themeColorConfig加载console.log("later", later)if (later) return later}const { oldColors, newColors, cssUrl, changeUrl } = option[i]oldColorsObj[i] = oldColors || themeColorConfig[i].colors || []newColorsObj[i] = newColors || []const cssUrlValue = themeColorConfig[i].url || cssUrlcssUrlObj[i] = changeUrl ? changeUrl(cssUrlValue) : cssUrlValue // url可能被changeUrl改变}

这里的option就是注册插件时传递的option,即代码const createThemeColorReplacerPlugin = () => new ThemeColorReplacer({
option
})。
另外 oldColorsObj ,newColorsObj ,cssUrlObj 也需要改为对象形式,需要按照键名存储

其他改动后的完整代码如下:

const _urlColors = {} // {[url]: {id,colors}}
const themeColorConfig = {}module.exports = {_tryNum: 0,_tryNumObj: {},changeColor: function (option) {const _this = thisconst oldColorsObj = {}const newColorsObj = {}const cssUrlObj = {}console.log("wen()", win())// eslint-disable-next-line no-undefconsole.log("LC_THEME_CONFIG", LC_THEME_CONFIG)console.log("option", option)for (const i in option) {if (!themeColorConfig[i]) {// eslint-disable-next-line no-undefconsole.log("LC_THEME_CONFIG[i]", LC_THEME_CONFIG[i])// eslint-disable-next-line no-undefthemeColorConfig[i] = win()[LC_THEME_CONFIG[i]]const later = retry(i)// 重试直到themeColorConfig加载console.log("later", later)if (later) return later}const { oldColors, newColors, cssUrl, changeUrl } = option[i]oldColorsObj[i] = oldColors || themeColorConfig[i].colors || []newColorsObj[i] = newColors || []const cssUrlValue = themeColorConfig[i].url || cssUrlcssUrlObj[i] = changeUrl ? changeUrl(cssUrlValue) : cssUrlValue // url可能被changeUrl改变}console.log("themeColorConfig", themeColorConfig)return new Promise(function (resolve, reject) {const optionKeys = Object.keys(option || {})const isSameArrReturn = optionKeys.every(key => isSameArr(oldColorsObj[key], newColorsObj[key])) // 判断是不是所有的都相同// console.log("isSameArrReturn", isSameArrReturn)if (isSameArrReturn) {resolve()} else {for (const i in option) {const last = _urlColors[cssUrlObj[i]]if (last) {// 之前已替换过oldColorsObj[i] = last.colors}if (!isSameArr(oldColorsObj[i], newColorsObj[i])) {setCssText(last, cssUrlObj[i], oldColorsObj[i], newColorsObj[i], i, resolve, reject)}}}})function retry(type) {if (!themeColorConfig[type]) {if (_this._tryNumObj[type] < 9) {_this._tryNumObj[type] = _this._tryNumObj[type] + 1return new Promise(function (resolve, reject) {setTimeout(function () {resolve(_this.changeColor(option))}, 100)})} else {themeColorConfig[type] = {}}}}function setCssText(last, url, oldColors, newColors, type, resolve, reject) {// console.log("last=", last, ",url=", url, ",oldColors=", oldColors, ",newColors=", newColors, ",type=", type,)let elStyle = last && document.getElementById(last.id)if (elStyle && last.colors) {setCssTo(elStyle.innerText)last.colors = newColorsresolve()} else {// 第一次替换const id = "css_" + type + (+new Date())console.log("第一次替换")console.log("id", id)elStyle = document.querySelector(option.appendToEl || "body").appendChild(document.createElement("style"))// console.log("elStyle", elStyle)elStyle.setAttribute("id", id)// console.log("url", url)_this.getCssString(url, function (cssText) {// console.log("cssText", cssText)setCssTo(cssText)_urlColors[url] = { id: id, colors: newColors }resolve(cssText)}, reject)}function setCssTo(cssText) {cssText = _this.replaceCssText(cssText, oldColors, newColors)elStyle.innerText = cssText}}},replaceCssText: function (cssText, oldColors, newColors) {oldColors.forEach(function (color, t) {// #222、#222223、#22222350、222, 255,3 => #333、#333334、#33333450、211,133,53、hsl(27, 92.531%, 52.745%)const reg = new RegExp(color.replace(/\s/g, "").replace(/,/g, ",\\s*") + "([\\da-f]{2})?(\\b|\\)|,|\\s)", "ig")cssText = cssText.replace(reg, newColors[t] + "$1$2") // 255, 255,3})return cssText},getCssString: function (url, resolve, reject) {// console.log("url", url)const xhr = new XMLHttpRequest()xhr.onreadystatechange = function () {if (xhr.readyState === 4) {if (xhr.status === 200) {resolve(xhr.responseText)} else {reject(xhr.status)}}}xhr.onerror = function (e) {reject(e)}xhr.ontimeout = function (e) {reject(e)}xhr.open("GET", url)xhr.send()},
}
function win() {return typeof window === "undefined" ? global : window
}
function isSameArr(oldColors, newColors) {if (oldColors.length !== newColors.length) {return false}for (let i = 0, j = oldColors.length; i < j; i++) {if (oldColors[i] !== newColors[i]) {return false}}return true
}

到这里就结束了,vary-color.js不需要改动。然后我们运行后看下效果(注意每次改动均需重启项目)

颜色批量替换演示

我们可以看到,页面上成功同时替换了4种颜色。到此这个测试完成。但是我们在页面上实现让用户自定义各种颜色的期望能实现吗?
答案是No!!
虽然这不是我们想要的答案,但是还是要接受目前的现实。下面讲下原因:
1、webpack-theme-color-replacer 这个插件只会根据颜色色号去替换,也就是说如果用户自己在主题色和危险色上选了相同的颜色,那么出现的结果会是项目上的主题色和危险色都被同时更改,并且分不开了,除非用户重置。而且如果我们修改css查找正则,比如只找css属性名带primary的,又会导致没有用ant-design样式的颜色不会被替换
2、其他的比如圆角等的修改,估计会波及无辜。因为比如 border-radius: 2px,这样的如果按照目前插件的替换逻辑,只会替换2px,会殃及一大片无辜的样式,就算按照规则改成替换border-radius: 2px整个字符,也会殃及不少的无辜样式。所以并不现实。

所以,好想赶紧升级到vue3啊,可以很爽快的使用css变量来实现这些逻辑!!

http://www.lryc.cn/news/21529.html

相关文章:

  • Redis高级-主从复制相关操作
  • SPI总线设备驱动模型
  • 开发同事辞职,接手到垃圾代码怎么办?
  • gRPC简介
  • 《MySQL系列-InnoDB引擎25》表-InnoDB逻辑存储结构
  • YOLOv8之C2f模块——与YOLOv5的C3模块对比
  • 动态规划实例——换零钱的方法数(C++详解版)
  • linux c
  • 第十三章 系统错误消息 - 一般系统错误消息 S - Z
  • 移动web基础
  • MyBatis和MyBatis_Plus有什么区别【面试常考题】
  • 华为OD机试用Python实现 -【统一限载货物数最小值】(2023-Q1 新题)
  • Vue入门小练习
  • Oracle-09-集合运算符篇
  • 获取浏览器(服务端)请求中特定的Cookie
  • c++11 标准模板(STL)(std::unordered_set)(九)
  • python实战应用讲解-【实战应用篇】文件操作(附python示例代码)
  • OpenCV-Python系列(二)—— 图像处理(灰度图、二值化、边缘检测、高斯模糊、轮廓检测)
  • ccc-台大林轩田机器学习基石-hw1
  • hadoop03-MapReduce【尚硅谷】
  • 测牛学堂:软件测试python学习之异常处理
  • 图神经网络--图神经网络
  • React useCallback如何使其性能最大化?
  • 长尾关键词使用方法,通过什么方式挖掘长尾关键词?
  • 【网络编程套接字(一)】
  • shell脚本入门
  • 【经典蓝牙】 蓝牙HFP层协议分析
  • 互联网摸鱼日报(2023-02-26)
  • 关于程序员中年危机的一个真实案例
  • 【fly-iot飞凡物联】(2):如何从0打造自己的物联网平台,使用开源的技术栈搭建一个高性能的物联网平台,目前在设计阶段。