如何修改Webpack样式加载器的插入方法?

问题描述 投票:0回答:1

Webpack 样式加载器的默认行为是注入样式标签作为 head 标签的最后一个子标签。但是,我想修改默认的插入方法,以便将 css 注入到影子根中。我查看了 docs 并测试了提供的代码,但它对我不起作用。

{
      loader: "style-loader",
      options: {
          insert: function insertIntoTarget(element, options) {
             var parent = options.target || document.head;
              parent.appendChild(element);
              },
      },
}

这里的一些上下文是我正在开发一个 chrome 扩展,并且我的入口点之一具有注入到活动选项卡上的项目。然而,为了不干扰页面预先存在的样式,我在“内容”入口点动态创建了一个shadow-root,并尝试在shadow-root而不是head中加载CSS。

下面是我当前的 webpack 配置文件:

//webpack.config.js
const path = require('path');
const CopyPlugin = require('copy-webpack-plugin');
const HtmlPlugin = require('html-webpack-plugin');

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const tailwindcss = require('tailwindcss')
const autoprefixer = require('autoprefixer')


module.exports = {
  entry: {
    popup: path.resolve('src/popup/index.tsx'),
    background: path.resolve('src/background/background.ts'),
    content: path.resolve('src/content/index.tsx'),
  },
  module: {
    rules: [
      {
        use: 'ts-loader',
        test: /\.tsx?$/,
        exclude: /node_modules/,
      },
      {
        test: /\.css$/i,
        use: [
          {
            loader: 'style-loader',
            options: {
              insert: function insertIntoTarget(element, options) {
                var parent = options.target || document.head;
                parent.appendChild(element);
              },
            }
          },
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
            },
          },
          {
            loader: 'postcss-loader', // postcss loader needed for tailwindcss
            options: {
              postcssOptions: {
                ident: 'postcss',
                plugins: [tailwindcss, autoprefixer],
              },
            },
          },
        ],
      },
      {
        type: 'assets/resource',
        test: /\.(png|jpg|jpeg|gif|woff|woff2|tff|eot|svg)$/,
      },
    ]
  },
  plugins: [
    new CleanWebpackPlugin({
      cleanStaleWebpackAssets: false
    }),
    new CopyPlugin({
      patterns: [{
        from: path.resolve('src/static'),
        to: path.resolve('dist')
      }]
    }),
    ...getHtmlPlugins([
      'popup',
      'content'
    ])
  ],
  resolve: {
    extensions: ['.tsx', '.js', '.ts']
  },
  output: {
    filename: '[name].js',
    path: path.join(__dirname, 'dist')
  },
  optimization: {
    splitChunks: {
      chunks(chunk) {
        return chunk.name !== 'content';
      },
    }
  }
}

function getHtmlPlugins(chunks) {
  return chunks.map(chunk => new HtmlPlugin({
    title: 'React Extension',
    filename: `${chunk}.html`,
    chunks: [chunk]
  }))
}

下面是我的入口点,content/index.tsx:

import React from "react";
import { createRoot } from "react-dom/client";
import ActiveTabContextProvider from "../context/ActiveTabContextProvider";
import Content from "./content";
import '../assets/tailwind.css'


function init() {
    const appContainer = document.createElement('div')
    appContainer.id = "shadow-root-parent";
    if (!appContainer) {
        throw new Error("Can not find AppContainer");
    }
    const shadowRoot = appContainer.attachShadow({ mode: 'open' });
        const root = createRoot(shadowRoot);
    document.body.appendChild(appContainer);
    root.render(
        <React.StrictMode>
            <ActiveTabContextProvider>
                <Content />
            </ActiveTabContextProvider>
        </React.StrictMode>
    );
}

init();

根据 style-loader 文档 style-loader 选项对象有一个插入字段,可以采用自定义函数来定位要注入的 html 元素对象:

{
      loader: "style-loader",
      options: {
          insert: function insertIntoTarget(element, options) {
             var parent = options.target || document.head;
              parent.appendChild(element);
              },
      },
},

我对它的工作原理有点困惑。我什么时候将参数

document.getElementById('shadow-root-parent').shadowRoot
传递给函数?在这个样式加载器运行之前,shadow-root html 元素是否可用?

webpack css-loader webpack-style-loader postcss-loader
1个回答
0
投票

我发现最有效的解决方案是为插入字段编写一个快速函数:

function insertInShadow(element) {
  if (window.location.pathname === '/') {
    const appContainer = document.createElement('div');
    appContainer.id = 'my-shadow-root';
    const shadowRoot = appContainer.attachShadow({ mode: 'open' });
    shadowRoot.appendChild(element);
    document.body.appendChild(appContainer);
  } else {
    document.head.appendChild(element)
  }
}

然后我可以在选项中添加此功能:

{
    loader: 'style-loader',
    options: {
       insert: insertInShadow,
    }
}

if 语句检查

window.location.pathname
可以知道样式加载器正在应用到哪个块。这样我们就可以有条件地将样式加载到 head 或 body 中。

插入函数中的

document.getElementByID
从未起作用的原因是样式加载器在index.tsx中的代码创建shadowRoot之前运行。所以我们可以更改这里的代码来查找由 style-loader 的 insert 创建的元素:

function init() {
    const appContainer = document.getElementById('active-recall-shadow-root').shadowRoot;
    if (!appContainer) {
        throw new Error("Can not find AppContainer");
    }
    
    const root = createRoot(appContainer)
    root.render(
        <React.StrictMode>
            <ActiveTabContextProvider>
                <Content />
            </ActiveTabContextProvider>
        </React.StrictMode>
    );
}
© www.soinside.com 2019 - 2024. All rights reserved.