翼度科技»论坛 编程开发 JavaScript 查看内容

vue3如何避免样式污染的方法示例

5

主题

5

帖子

15

积分

新手上路

Rank: 1

积分
15
众所周知,在vue中使用scoped可以避免父组件的样式渗透到子组件中。使用了scoped后会给html增加自定义属性
  1. data-v-x
复制代码
,同时会给组件内CSS选择器添加对应的属性选择器
  1. [data-v-x]
复制代码
。本文讲一下vue是如何给CSS选择器添加对应的属性选择器
  1. [data-v-x]
复制代码
。注:本文中使用的vue版本为
  1. 3.4.19
复制代码
  1. @vitejs/plugin-vue
复制代码
的版本为
  1. 5.0.4
复制代码

先看个demo
代码如下:
  1. <template>
  2.   <div class="block">hello world</div>
  3. </template>

  4. <style scoped>
  5. .block {
  6.   color: red;
  7. }
  8. </style>
复制代码
经过编译后,上面的demo代码就会变成下面这样:
  1. <template>
  2.   <div data-v-c1c19b25 class="block">hello world</div>
  3. </template>

  4. <style>
  5. .block[data-v-c1c19b25] {
  6.   color: red;
  7. }
  8. </style>
复制代码
从上面的代码可以看到在div上多了一个
  1. data-v-c1c19b25
复制代码
自定义属性,并且css的属性选择器上面也多了一个
  1. [data-v-c1c19b25]
复制代码

那有人就会好奇,为什么生成这样的代码就可以避免样式污染呢?
  1. .block[data-v-c1c19b25]
复制代码
:这里面包含两个选择器。
  1. .block
复制代码
是一个类选择器,表示class的值包含
  1. block
复制代码
  1. [data-v-c1c19b25]
复制代码
是一个属性选择器,表示存在
  1. data-v-c1c19b25
复制代码
自定义属性的元素。
所以只有class包含
  1. block
复制代码
,并且存在
  1. data-v-c1c19b25
复制代码
自定义属性的元素才能命中这个样式,这样就能避免样式污染。并且由于在同一个组件里面生成的
  1. data-v-x
复制代码
值是一样的,所以在同一组件内多个html元素只要class的值包含
  1. block
复制代码
,就可以命中
  1. color: red
复制代码
的样式。
接下来我将通过debug的方式带你了解,vue是如何在css中生成.block[data-v-c1c19b25]这样的属性选择器。

@vitejs/plugin-vue

还是一样的套路启动一个debug终端。这里以
  1. vscode
复制代码
举例,打开终端然后点击终端中的
  1. +
复制代码
号旁边的下拉箭头,下拉中点击
  1. Javascript Debug Terminal
复制代码
就可以启动一个
  1. debug
复制代码
终端。

假如
  1. vue
复制代码
文件编译为
  1. js
复制代码
文件是一个毛线团,那么他的线头一定是
  1. vite.config.ts
复制代码
文件中使用
  1. @vitejs/plugin-vue
复制代码
的地方。通过这个线头开始
  1. debug
复制代码
我们就能够梳理清楚完整的工作流程。


vuePlugin函数

我们给上方图片的
  1. vue
复制代码
函数打了一个断点,然后在
  1. debug
复制代码
终端上面执行
  1. yarn dev
复制代码
,我们看到断点已经停留在了
  1. vue
复制代码
函数这里。然后点击
  1. step into
复制代码
,断点走到了
  1. @vitejs/plugin-vue
复制代码
库中的一个
  1. vuePlugin
复制代码
函数中。我们看到简化后的
  1. vuePlugin
复制代码
函数代码如下:
  1. function vuePlugin(rawOptions = {}) {
  2.   return {
  3.     name: "vite:vue",
  4.     // ...省略其他插件钩子函数
  5.     transform(code, id, opt) {
  6.       // ..
  7.     }
  8.   };
  9. }
复制代码
  1. @vitejs/plugin-vue
复制代码
是作为一个
  1. plugins
复制代码
插件在vite中使用,
  1. vuePlugin
复制代码
函数返回的对象中的
  1. transform
复制代码
方法就是对应的插件钩子函数。vite会在对应的时候调用这些插件的钩子函数,vite每解析一个模块都会执行一次
  1. transform
复制代码
钩子函数。更多vite钩子相关内容查看官网。
我们这里只需要看
  1. transform
复制代码
钩子函数,解析每个模块时调用。
由于解析每个文件都会走到
  1. transform
复制代码
钩子函数中,但是我们只关注
  1. index.vue
复制代码
文件是如何解析的,所以我们给
  1. transform
复制代码
钩子函数打一个条件断点。如下图:

然后点击Continue(F5),
  1. vite
复制代码
服务启动后就会走到
  1. transform
复制代码
钩子函数中打的断点。我们可以看到简化后的
  1. transform
复制代码
钩子函数代码如下:
  1. function transform(code, id, opt) {
  2.   const { filename, query } = parseVueRequest(id);
  3.   if (!query.vue) {
  4.     return transformMain(
  5.       code,
  6.       filename,
  7.       options.value,
  8.       this,
  9.       ssr,
  10.       customElementFilter.value(filename)
  11.     );
  12.   } else {
  13.     const descriptor = getDescriptor(filename);
  14.     if (query.type === "style") {
  15.       return transformStyle(
  16.         code,
  17.         descriptor,
  18.         Number(query.index || 0),
  19.         options.value
  20.       );
  21.     }
  22.   }
  23. }
复制代码
首先调用
  1. parseVueRequest
复制代码
函数解析出当前要处理的文件的
  1. filename
复制代码
  1. query
复制代码
,在debug终端来看看此时这两个的值。如下图:

从上图中可以看到
  1. filename
复制代码
为当前处理的vue文件路径,
  1. query
复制代码
的值为空数组。所以此时代码会走到
  1. transformMain
复制代码
函数中。

transformMain函数

将断点走进
  1. transformMain
复制代码
函数,在我们这个场景中简化后的
  1. transformMain
复制代码
函数代码如下:
  1. async function transformMain(code, filename, options) {
  2.   const { descriptor } = createDescriptor(filename, code, options);

  3.   const { code: templateCode } = await genTemplateCode(
  4.     descriptor
  5.     // ...省略
  6.   );

  7.   const { code: scriptCode } = await genScriptCode(
  8.     descriptor
  9.     // ...省略
  10.   );

  11.   const stylesCode = await genStyleCode(
  12.     descriptor
  13.     // ...省略
  14.   );

  15.   const output = [scriptCode, templateCode, stylesCode];
  16.   let resolvedCode = output.join("\n");
  17.   return {
  18.     code: resolvedCode,
  19.   };
  20. }
复制代码
首先调用
  1. createDescriptor
复制代码
函数根据当前vue文件的code代码字符串生成一个
  1. descriptor
复制代码
对象,简化后的
  1. createDescriptor
复制代码
函数代码如下:
  1. const cache = new Map();

  2. function createDescriptor(
  3.   filename,
  4.   source,
  5.   { root, isProduction, sourceMap, compiler, template }
  6. ) {
  7.   const { descriptor, errors } = compiler.parse(source, {
  8.     filename,
  9.     sourceMap,
  10.     templateParseOptions: template?.compilerOptions,
  11.   });
  12.   const normalizedPath = slash(path.normalize(path.relative(root, filename)));
  13.   descriptor.id = getHash(normalizedPath + (isProduction ? source : ""));
  14.   cache.set(filename, descriptor);
  15.   return { descriptor, errors };
  16. }
复制代码
首先调用
  1. compiler.parse
复制代码
方法根据当前vue文件的code代码字符串生成一个
  1. descriptor
复制代码
对象,此时的
  1. descriptor
复制代码
对象主要有三个属性
  1. template
复制代码
  1. scriptSetup
复制代码
  1. style
复制代码
,分别对应的是vue文件中的
  1.  <template>
复制代码
模块、
  1. <template setup>
复制代码
模块、
  1.  <style> 
复制代码
模块。
然后调用
  1. getHash
复制代码
函数给
  1. descriptor
复制代码
对象生成一个
  1. id
复制代码
属性,
  1. getHash
复制代码
函数代码如下:
  1. import { createHash } from "node:crypto";
  2. function getHash(text) {
  3.   return createHash("sha256").update(text).digest("hex").substring(0, 8);
  4. }
复制代码
从上面的代码可以看出id是根据vue文件的路径调用node的
  1. createHash
复制代码
加密函数生成的,这里生成的id就是scoped生成的自定义属性
  1. data-v-x
复制代码
中的
  1. x
复制代码
部分。
然后在
  1. createDescriptor
复制代码
函数中将生成的
  1. descriptor
复制代码
对象缓存起来,关于
  1. descriptor
复制代码
对象的处理就这么多了。
接着在
  1. transformMain
复制代码
函数中会分别以
  1. descriptor
复制代码
对象为参数执行
  1. genTemplateCode
复制代码
  1. genScriptCode
复制代码
  1. genStyleCode
复制代码
函数,分别得到编译后的render函数、编译后的js代码、编译后的style代码。
编译后的render函数如下图:

从上图中可以看到template模块已经编译成了render函数
编译后的js代码如下图:

从上图中可以看到script模块已经编译成了一个名为
  1. _sfc_main
复制代码
的对象,因为我们这个demo中script模块没有代码,所以这个对象是一个空对象。
编译后的style代码如下图:

从上图中可以看到style模块已经编译成了一个import语句。
最后就是使用换行符
  1. \n
复制代码
  1. templateCode
复制代码
  1. scriptCode
复制代码
  1. stylesCode
复制代码
拼接起来就是vue文件编译后的js文件啦,如下图:

想必细心的同学已经发现有地方不对啦,这里的style模块编译后是一条import语句,并不是真正的css代码。这条import语句依然还是import导入的
  1. index.vue
复制代码
文件,只是加了一些额外的query参数。
  1. ?vue&type=style&index=0&lang.css
复制代码
:这个query参数表明当前import导入的是vue文件的css部分。
还记得前面讲过的
  1. transform
复制代码
钩子函数吗?vite每解析一个模块都会执行一次
  1. transform
复制代码
钩子函数,这个import导入vue文件的css部分,当然也会触发
  1. transform
复制代码
钩子函数的执行。

第二次执行transform钩子函数

当在浏览器中执行vue文件编译后的js文件时会触发
  1. import "/Users/xxx/index.vue?vue&type=style&index=0&lang.css"
复制代码
语句的执行,导致再次执行
  1. transform
复制代码
钩子函数。
  1. transform
复制代码
钩子函数代码如下:
  1. function transform(code, id, opt) {
  2.   const { filename, query } = parseVueRequest(id);
  3.   if (!query.vue) {
  4.     return transformMain(
  5.       code,
  6.       filename,
  7.       options.value,
  8.       this,
  9.       ssr,
  10.       customElementFilter.value(filename)
  11.     );
  12.   } else {
  13.     const descriptor = getDescriptor(filename);
  14.     if (query.type === "style") {
  15.       return transformStyle(
  16.         code,
  17.         descriptor,
  18.         Number(query.index || 0),
  19.         options.value
  20.       );
  21.     }
  22.   }
  23. }
复制代码
由于此时的
  1. query
复制代码
中是有
  1. vue
复制代码
字段,所以
  1. !query.vue
复制代码
的值为false,这次代码就不会走进
  1. transformMain
复制代码
函数中了。在
  1. else
复制代码
代码在先执行
  1. getDescriptor
复制代码
函数拿到
  1. descriptor
复制代码
对象,
  1. getDescriptor
复制代码
函数代码如下:
  1. function getDescriptor(filename) {
  2.   const _cache = cache;
  3.   if (_cache.has(filename)) {
  4.     return _cache.get(filename);
  5.   }
  6. }
复制代码
我们在第一次执行
  1. transformMain
复制代码
函数的时候会去执行
  1. createDescriptor
复制代码
函数,他的作用是根据当前vue文件的code代码字符串生成一个
  1. descriptor
复制代码
对象,并且将这个
  1. descriptor
复制代码
对象缓存起来了。在
  1. getDescriptor
复制代码
函数中就是将缓存的
  1. descriptor
复制代码
对象取出来。
由于
  1. query
复制代码
中有
  1. type=style
复制代码
,所以代码会走到
  1. transformStyle
复制代码
函数中。

transformStyle函数

接着将断点走进
  1. transformStyle
复制代码
函数,代码如下:
  1. async function transformStyle(code, descriptor, index, options) {
  2.   const block = descriptor.styles[index];
  3.   const result = await options.compiler.compileStyleAsync({
  4.     ...options.style,
  5.     filename: descriptor.filename,
  6.     id: `data-v-${descriptor.id}`,
  7.     source: code,
  8.     scoped: block.scoped,
  9.   });

  10.   return {
  11.     code: result.code,
  12.   };
  13. }
复制代码
从上面的代码可以看到
  1. transformStyle
复制代码
函数依然不是干活的地方,而是调用的
  1. @vue/compiler-sfc
复制代码
包暴露出的
  1. compileStyleAsync
复制代码
函数。
在调用
  1. compileStyleAsync
复制代码
函数的时候有三个参数需要注意:
  1. source
复制代码
  1. id
复制代码
  1. scoped
复制代码
  1. source
复制代码
字段的值为
  1. code
复制代码
,值是当前css代码字符串。
  1. id
复制代码
字段的值为
  1. data-v-${descriptor.id}
复制代码
,是不是觉得看着很熟悉?没错他就是使用
  1. scoped
复制代码
后vue帮我们自动生成的html自定义属性
  1. data-v-x
复制代码
和css选择属性选择器
  1. [data-v-x]
复制代码

其中的
  1. descriptor.id
复制代码
就是在生成
  1. descriptor
复制代码
对象时根据vue文件路径加密生成的id。
  1. scoped
复制代码
字段的值为
  1. block.scoped
复制代码
,而
  1. block
复制代码
的值为
  1. descriptor.styles[index]
复制代码
。由于一个vue文件可以写多个style标签,所以
  1. descriptor
复制代码
对象的
  1. styles
复制代码
属性是一个数组,分包对应多个style标签。我们这里只有一个
  1. style
复制代码
标签,所以此时的
  1. index
复制代码
值为0。
  1. block.scoped
复制代码
的值为style标签上面是否有使用
  1. scoped
复制代码

直到进入
  1. compileStyleAsync
复制代码
函数之前代码其实一直都还在
  1. @vitejs/plugin-vue
复制代码
包中执行,真正干活的地方是在
  1. @vue/compiler-sfc
复制代码
包中。

@vue/compiler-sfc

接着将断点走进
  1. compileStyleAsync
复制代码
函数,代码如下:
  1. function compileStyleAsync(options) {
  2.   return doCompileStyle({
  3.     ...options,
  4.     isAsync: true,
  5.   });
  6. }
复制代码
从上面的代码可以看到实际干活的是
  1. doCompileStyle
复制代码
函数。

doCompileStyle函数

接着将断点走进
  1. doCompileStyle
复制代码
函数,在我们这个场景中简化后的
  1. doCompileStyle
复制代码
函数代码如下:
  1. import postcss from "postcss";

  2. function doCompileStyle(options) {
  3.   const {
  4.     filename,
  5.     id,
  6.     scoped = false,
  7.     postcssOptions,
  8.     postcssPlugins,
  9.   } = options;
  10.   const source = options.source;
  11.   const shortId = id.replace(/^data-v-/, "");
  12.   const longId = `data-v-${shortId}`;
  13.   const plugins = (postcssPlugins || []).slice();

  14.   if (scoped) {
  15.     plugins.push(scopedPlugin(longId));
  16.   }

  17.   const postCSSOptions = {
  18.     ...postcssOptions,
  19.     to: filename,
  20.     from: filename,
  21.   };
  22.   let result;
  23.   try {
  24.     result = postcss(plugins).process(source, postCSSOptions);
  25.     return result.then((result) => ({
  26.       code: result.css || "",
  27.       // ...省略
  28.     }));
  29.   } catch (e: any) {
  30.     errors.push(e);
  31.   }
  32. }
复制代码
  1. doCompileStyle
复制代码
函数中首先使用
  1. const
复制代码
定义了一堆变量,我们主要关注
  1. source
复制代码
  1. longId
复制代码

其中的
  1. source
复制代码
为当前css代码字符串,
  1. longId
复制代码
为根据vue文件路径加密生成的id,值的格式为
  1. data-v-x
复制代码
。他就是使用
  1. scoped
复制代码
后vue帮我们自动生成的html自定义属性
  1. data-v-x
复制代码
和css选择属性选择器
  1. [data-v-x]
复制代码

接着就是判断
  1. scoped
复制代码
是否为true,也就是style中使用有使用scoped。如果为true,就将
  1. scopedPlugin
复制代码
插件push到
  1. plugins
复制代码
数组中。从名字你应该猜到了这个plugin插件就是用于处理css scoped的。
最后就是执行
  1. result = postcss(plugins).process(source, postCSSOptions)
复制代码
拿到经过
  1. postcss
复制代码
转换编译器处理后的css。
可能有的小伙伴对
  1. postcss
复制代码
不够熟悉,我们这里来简单介绍一下。
  1. postcss
复制代码
是 css 的 transpiler(转换编译器,简称转译器),它对于 css 就像 babel 对于 js 一样,能够做 css 代码的分析和转换。同时,它也提供了插件机制来做自定义的转换。
在我们这里主要就是用到了
  1. postcss
复制代码
提供的插件机制来完成css scoped的自定义转换,调用
  1. postcss
复制代码
的时候我们传入了
  1. source
复制代码
,他的值是style模块中的css代码。并且传入的
  1. plugins
复制代码
插件数组中有个
  1. scopedPlugin
复制代码
插件,这个自定义插件就是vue写的用于处理css scoped的插件。
在执行
  1. postcss
复制代码
对css代码进行转换之前我们在debug终端来看看此时的css代码是什么样的,如下图:

从上图可以看到此时的css代码还是和我们源代码是一样的,并没有css选择属性选择器
  1. [data-v-x]
复制代码

scopedPlugin插件
  1. scopedPlugin
复制代码
插件在我们这个场景中简化后的代码如下:
  1. const scopedPlugin = (id = "") =&gt; {
  2.   return {
  3.     postcssPlugin: "vue-sfc-scoped",
  4.     Rule(rule) {
  5.       processRule(id, rule);
  6.     },
  7.     // ...省略
  8.   };
  9. };
复制代码
这里的id就是我们在
  1. doCompileStyle
复制代码
函数中传过来的
  1. longId
复制代码
,也就是生成的css选择属性选择器
  1. [data-v-x]
复制代码
中的
  1. data-v-x
复制代码

在我们这个场景中只需要关注
  1. Rule
复制代码
钩子函数,当
  1. postcss
复制代码
处理到选择器开头的规则就会走到
  1. Rule
复制代码
钩子函数。
我们这里需要在使用了scoped后给css选择器添加对应的属性选择器
  1. [data-v-x]
复制代码
,所以我们需要在插件中使用
  1. Rule
复制代码
钩子函数,在处理css选择器时手动给选择器后面塞一个属性选择器
  1. [data-v-x]
复制代码

  1. Rule
复制代码
钩子函数打个断点,当
  1. postcss
复制代码
处理到我们代码中的
  1. .block
复制代码
时就会走到断点中。在debug终端看看
  1. rule
复制代码
的值,如下图:

从上图中可以看到此时
  1. rule.selector
复制代码
的值为
  1. .block
复制代码
,是一个class值为
  1. block
复制代码
的类选择器。

processRule函数

将断点走进
  1. processRule
复制代码
函数中,在我们这个场景中简化后的
  1. processRule
复制代码
函数代码如下:
  1. import selectorParser from "postcss-selector-parser";

  2. function processRule(id: string, rule: Rule) {
  3.   rule.selector = selectorParser((selectorRoot) => {
  4.     selectorRoot.each((selector) => {
  5.       rewriteSelector(id, selector, selectorRoot);
  6.     });
  7.   }).processSync(rule.selector);
  8. }
复制代码
前面我们讲过
  1. rule.selector
复制代码
的值为
  1. .block
复制代码
,通过重写
  1. rule.selector
复制代码
的值可以将当前css选择器替换为一个新的选择器。在
  1. processRule
复制代码
函数中就是使用
  1. postcss-selector-parser
复制代码
来解析一个选择器,进行处理后返回一个新的选择器。
  1. processSync
复制代码
方法的作用为接收一个选择器,然后在回调中对解析出来的选择器进行处理,最后将处理后的选择器以字符串的方式进行返回。
在我们这里
  1. processSync
复制代码
方法接收的选择器是字符串
  1. .block
复制代码
,经过回调函数处理后返回的选择器字符串就变成了
  1. .block[data-v-c1c19b25]
复制代码

我们接下来看
  1. selectorParser
复制代码
回调函数中的代码,在回调函数中会使用
  1. selectorRoot.each
复制代码
去遍历解析出来的选择器。
为什么这里需要去遍历呢?
答案是css选择器可以这样写:
  1. .block.demo
复制代码
,如果是这样的选择器经过解析后,就会被解析成两个选择器,分别是
  1. .block
复制代码
  1. .demo
复制代码

在each遍历中会调用
  1. rewriteSelector
复制代码
函数对当前选取器进行重写。

rewriteSelector函数

将断点走进
  1. rewriteSelector
复制代码
函数,在我们这个场景中简化后的代码如下:
  1. function rewriteSelector(id, selector) {
  2.   let node;
  3.   const idToAdd = id;

  4.   selector.each((n) => {
  5.     node = n;
  6.   });

  7.   selector.insertAfter(
  8.     node,
  9.     selectorParser.attribute({
  10.       attribute: idToAdd,
  11.       value: idToAdd,
  12.       raws: {},
  13.       quoteMark: `"`,
  14.     })
  15.   );
  16. }
复制代码
  1. rewriteSelector
复制代码
函数中each遍历当前
  1. selector
复制代码
选择器,给
  1. node
复制代码
赋值。将断点走到each遍历之后,我们在debug终端来看看
  1. selector
复制代码
选择器和
  1. node
复制代码
变量。如下图:

在这里
  1. selector
复制代码
是container容器,
  1. node
复制代码
才是具体要操作的选择器节点。
比如我们这里要执行的
  1. selector.insertAfter
复制代码
方法就是在
  1. selector
复制代码
容器中在一个指定节点后面去插入一个新的节点。这个和操作浏览器DOM API很相似。
我们再来看看要插入的节点,
  1. selectorParser.attribute
复制代码
函数的作用是创建一个attribute属性选择器。在我们这里就是创建一个
  1. [data-v-x]
复制代码
的属性选择器,如下图:

所以这里就是在
  1. .block
复制代码
类选择器后面插入一个
  1. [data-v-c1c19b25]
复制代码
的属性选择器。
我们在debug终端来看看执行
  1. insertAfter
复制代码
函数后的
  1. selector
复制代码
选择器,如下图:

将断点逐层走出,直到
  1. processRule
复制代码
函数中。我们在debug终端来看看此时被重写后的
  1. rule.selector
复制代码
字符串的值是什么样的,如下图

原来
  1. rule.selector
复制代码
的值为
  1. .block
复制代码
,通过重写
  1. rule.selector
复制代码
的值可以将
  1. .block
复制代码
类选择器替换为一个新的选择器,而这个新的选择器是在原来的
  1. .block
复制代码
类选择器后面再塞一个
  1. [data-v-c1c19b25]
复制代码
属性选择器。

总结

这篇文章我们讲了当使用scoped后,vue是如何给组件内CSS选择器添加对应的属性选择器
  1. [data-v-x]
复制代码
。主要分为两部分,分别在两个包里面执行。

  • 第一部分为在
    1. @vitejs/plugin-vue
    复制代码
    包内执行。

    • 首先会根据当前vue文件的路径进行加密算法生成一个id,这个id就是添加的属性选择器
      1. [data-v-x]
      复制代码
      中的
      1. x
      复制代码

    • 然后就是执行
      1. transformStyle
      复制代码
      函数,这个
      1. transformStyle
      复制代码
      并不是实际干活的地方,他调用了
      1. @vue/compiler-sfc
      复制代码
      包的
      1. compileStyleAsync
      复制代码
      函数。并且传入了
      1. id
      复制代码
      1. code
      复制代码
      (css代码字符串)、
      1. scoped
      复制代码
      (是否在style中使用
      1. scoped
      复制代码
      )。

  • 第二部分在
    1. @vue/compiler-sfc
    复制代码
    包执行。

      1. compileStyleAsync
      复制代码
      函数依然不是实际干活的地方,而是调用了
      1. doCompileStyle
      复制代码
      函数。
      1. doCompileStyle
      复制代码
      函数中,如果
      1. scoped
      复制代码
      为true就向
      1. plugins
      复制代码
      数组中插入一个
      1. scopedPlugin
      复制代码
      插件,这个是vue写的
      1. postcss
      复制代码
      插件,用于处理css scoped。然后使用
      1. postcss
      复制代码
      转换编译器对css代码进行转换。
      1. postcss
      复制代码
      处理到选择器开头的规则就会走到
      1. scopedPlugin
      复制代码
      插件中的
      1. Rule
      复制代码
      钩子函数中。在
      1. Rule
      复制代码
      钩子函数中会执行
      1. processRule
      复制代码
      函数。
      ata-v-x]
      1. 中的
      复制代码
      x`。
    • 然后就是执行
      1. transformStyle
      复制代码
      函数,这个
      1. transformStyle
      复制代码
      并不是实际干活的地方,他调用了
      1. @vue/compiler-sfc
      复制代码
      包的
      1. compileStyleAsync
      复制代码
      函数。并且传入了
      1. id
      复制代码
      1. code
      复制代码
      (css代码字符串)、
      1. scoped
      复制代码
      (是否在style中使用
      1. scoped
      复制代码
      )。

到此这篇关于vue3是如何避免样式污染的的文章就介绍到这了,更多相关vue3 样式污染内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

来源:https://www.jb51.net/javascript/3267958cw.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具