侧边栏壁纸
博主头像
MicroMatrix博主等级

曲则全,枉则直,洼则盈,敝则新,少则得,多则惑。是以圣人抱一为天下式。不自见,故明;不自是,故彰;不自伐,故有功;不自矜,故长。夫唯不争,故天下莫能与之争。古之所谓“曲则全”者,岂虚言哉!诚全而归之。

  • 累计撰写 80 篇文章
  • 累计创建 21 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

esbuild项目css样式隔离

蜗牛
2023-12-27 / 0 评论 / 0 点赞 / 14 阅读 / 5920 字 / 正在检测是否收录...

前言

由于最近使用到esbuild这个打包工具编写插件,而我的插件需要编写样式。在实际的运用过程中,我发现插件的样式类名可能和被使用项目中的一些类名发生冲突。这个时候我就想借鉴一下vue中出现的样式类scoped 的概念。

具体思路

首先呢,要想实现样式类的隔离 那么就要了解下属性选择器。下面就是很简单的一句解释。

CSS 属性选择器匹配那些具有特定属性或属性值的元素。

其实呢就是dom上有个属性然后配合着标签或者类名来实现的。例如:

/* 存在 title 属性的 <a> 元素 */
a[title] {
  color: purple;
}

/* 存在 href 属性并且属性值匹配"https://example.org"的 <a> 元素 */
a[href="https://example.org"]
{
  color: green;
}

那么vue就相当于你自定义一个属性data-v-sjdgdf 然后类名后面跟上这个属性就能实现样式隔离

实际操作

esbuild 只是一个打包工具,所以我们第一步就是要获取插件中的dom元素,由于我是采用了jsx的方式编写插件,具体可以参考我的文章:基于esbuild搭建组件开发框架**。**第二步就是处理编写的css样式文件,然后将类名后面加上随机生成的字符串。

随机字符串

const generateString = (len = 6) => {
  const characters = `abcdefghijklmnopqrstuvwxyz0123456789`;
  let result = '';
  for (let i = 0; i < len; i++) {
    result += characters.charAt(Math.floor(Math.random() * characters.length));
  }
  return result;

}
const generateStr = generateString()

生成一个6位长度的随机字符串。

Dom元素打包编辑

我原先是使用babel来转义jsx的语法的。所以这里不介绍涉及此处的插件,具体的看我前面提到的文章那里面有缺失的插件。

pnpm install @babel/types -w -D

这个是读取dom节点的插件。然后我们编写dom属性添加插件

const types = require("@babel/types");
const addProperty = () => ({
  visitor: {
    JSXOpeningElement(path) {
      const { name, attributes } = path.node;
      let flag = attributes.some(
        (attr) =>
          attr.type === "JSXAttribute" &&
          attr.name.name === "className"
      )

      if (flag) {
        // 添加属性到元素
        path.node.attributes.push(
          types.jsxAttribute(
            types.jsxIdentifier(`data-v-${generateStr}`),
            types.stringLiteral("")
          )
        );
      }
    },
  },
})

module.exports.addProperty = addProperty

代码很简单,里面也有注释,就不详细解释了。

然后加入到之前写的jsxTransform解析插件里面去,具体放在此处

......
const { addProperty } = require("./cssAttribute");
build.onLoad({ filter: /(?:\.jsx|\.tsx)$/ }, async (args) => {
	const jsx = await fs.promises.readFile(args.path, "utf8");
    const result = babel.transformSync(jsx, {
      plugins: [plugin, addProperty()], presets: [presetEnv, presetTypescript], filename: args.path
    });
    return { contents: result.code };
});
......

这里完成后重新打包文件就能看到页面上的dom节点处会出现data-v-随机字符串。

css样式文件处理

同样的借助插件解析css文件

pnpm install postcss -w -D

然后编写如下的插件,也是很简单的插件内容

const postcss = require('postcss');
const cssScopedPlugin = () => ({
  name: "css-scoped-plugin",
  setup(build) {
    const fs = require("fs");
    const plugin = {
      postcssPlugin: 'css-parser',
      Rule(node) {
        node.selectors = node.selectors.map((selector) => `${selector}[data-v-${generateStr}]`)
      }
    }

    build.onLoad({ filter: /(?:\.css|\.scss)$/ }, async (args) => {
      const cssFile = await fs.promises.readFile(args.path, "utf8");
      let result = await postcss([plugin]).process(cssFile, { from: undefined })
      return {
        contents: result.css,
        loader: 'css'
      };
    });
  },
});

之后载入到esbuild的文件插件属性中,下面是部分代码

const { cssScopedPlugin } = require("../plugins/cssAttribute")
context({
plugins:[cssScopedPlugin(), cssPlugin.sassPlugin(), svgBuilder()]
})

这里我放在了sass文件解析插件的前面,因为我发现在后面貌似不起作用。可能是我的项目有BUG啥的。

到这里就完成了css样式隔离的问题,实际效果如下

6dddfa9583f0cac9b69fb.png

0

评论区