Shadow DOM已经比较成熟了,基本上所有的现代浏览器都已经支持了,可以参考Can I Use。在React中使用shadow DOM也不是什么新鲜事,也有一些关于shadow DOM的第三方库,但是在CSS方面,几乎都是使用了styled components的方式。我个人不太喜欢这种css和js混在一起方式,而且这种方式下也没法使用scss了。

开始看了下style-laoder的文档,里面有个选项insert,可以传入一个function,这样你可以在里面进行你想要的操作,比如找到shadow DOM的shadow root,然后把你的style插入进去:

rules: [
  // Other webpack modules
  ...
  {
    test: /\.css$/,
    use: [
      {
        loader: require.resolve('style-loader'),
        options: {
          insertInto: function () {
            return document.getElementById('root').shadowRoot;
          },
        },
      },
      // Other css loaders (if any)
      ...
    ],
  },
  ...
],

很遗憾,如果你的shadow root是动态添加的,那么这个方法也行不通。

另外回到原点,既然style可以动态注入,那么我们是不是也可以用类似css module的方式,把scss当做字符串引入进来,然后append到shadow root上?sass-to-string正好可以做到,不如试试看。

首先配置webpack:

rules: [
  ...
  {
    test: /\.styles.scss$/,
    exclude: /node_modules/,
    use: [
      'sass-to-string',
      {
        loader: 'sass-loader',
        options: {
          sassOptions: {
            outputStyle: 'compressed'
          }
        }
      }
    ]
  },
  ...
],

然后创建单独的popup.styles.scss:

$img-width: 1.2rem;

.popup {
  position: absolute;
  bottom: 20px;
  left: 10px;
  z-index: 3;
}

img {
  width: $img-width;
}

再在入口文件中引入css:

import styleText from './popup-info.scss';

const style = document.createElement('style');
style.appendChild(document.createTextNode(styleText));

const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.appendChild(style);
// ...

至此,基本解决了style独立并且能够使用sass的问题。

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.