alb*_*rtR 16 css shadow-dom reactjs webpack webpack-style-loader
我正在尝试修改使用 shadow root 创建的 web 组件的样式。
我看到样式被添加到head标签中,但它对shadow root封装没有影响。
我需要的是加载所有组件的样式并使它们在shadow root.
这是创建 Web 组件的一部分:
索引.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import { App } from './App';
import './tmp/mockComponent.css'; // This is the styling i wish to inject
let container: HTMLElement;
class AssetsWebComponent extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
const { shadowRoot } = this;
container = document.createElement('div');
shadowRoot.appendChild(container);
ReactDOM.render(<App />, container);
}
}
window.customElements.define('assets-component', AssetsWebComponent);
Run Code Online (Sandbox Code Playgroud)
App.ts // 常规反应组件
import React from 'react';
import './App.css';
import { MockComponent } from './tmp/mockComponent'
export const App: React.FunctionComponent = (props) => {
return (
<MockComponent />
);
};
Run Code Online (Sandbox Code Playgroud)
webpack.config.ts
// @ts-ignore
const path = require('path');
const common = require('./webpack.common.ts');
const merge = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = merge(common, {
mode: 'development',
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: 'style-loader',
options: {
insert: function (element) {
const parent = document.querySelector('assets-component');
***This what i want, to inject inside the shadowRoot but it
never steps inside since the shadowRoot not created yet***
if (parent.shadowRoot) {
parent.shadowRoot.appendChild(element);
}
parent.appendChild(element);
},
},
},
'css-loader',
],
},
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader',
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './src/template.html',
}),
],
devtool: 'inline-source-map',
});
Run Code Online (Sandbox Code Playgroud)
由于MockComponent内部可以有更多的组件,所以我依靠 Webpack 将所有样式注入到shadow root. 我正在使用style-loader注入样式,但效果不佳。
我做错了什么,或者这个解决方案有什么替代方案。
原来你只需要'css-loader',所以你应该完全删除'style-loader'及其选项。所以在webpack.config.ts 中:
module: {
rules: [
{test:/\.css$/, use:'css-loader'}
]
}
Run Code Online (Sandbox Code Playgroud)
然后你想导入一个我们从 css-loader 获得的样式字符串,并在你附加到容器之前的影子根的样式元素中使用它。所以在index.tsx 中:
......
import style from './tmp/mockComponent.css';
......
constructor() {
super();
this.attachShadow({ mode: 'open' });
const { shadowRoot } = this;
container = document.createElement('div');
styleTag = document.createElement('style');
styleTag.innerHTML = style;
shadowRoot.appendChild(styleTag);
shadowRoot.appendChild(container);
ReactDOM.render(<App />, container);
}
......
Run Code Online (Sandbox Code Playgroud)
'style-loader' 给你带来问题的原因是它是为了找到在最终的 html 文件中自己注入 css 样式标签的位置,但是你已经知道通过 JS 将它放在你的 shadowDOM 中的哪个位置,所以你只需要'css加载器'。
style-loader此目的这一切都取决于执行顺序。你的index.tsx被执行后style-loader::insert。但影子根之前必须存在。
最简单的方法是修改index.html.
这是一个完整的示例:
./src/index.html
...
<body>
<div id="root"></div>
<script>
<!-- This gets executed before any other script. -->
document.querySelector("#root").attachShadow({ mode: 'open' })
</script>
</body>
...
Run Code Online (Sandbox Code Playgroud)
./webpack.config.js
var HtmlWebpackPlugin = require('html-webpack-plugin');
const cssRegex = /\.css$/i
const customStyleLoader = {
loader: 'style-loader',
options: {
insert: function (linkTag) {
const parent = document.querySelector('#root').shadowRoot
parent.appendChild(linkTag)
},
},
}
module.exports = {
module: {
rules: [
{
test: cssRegex,
use: [
customStyleLoader,
'css-loader'
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
};
Run Code Online (Sandbox Code Playgroud)
./src/index.js
import "./style.css";
const root = document.getElementById('root').shadowRoot
// Create a new app root inside the shadow DOM to avoid overwriting stuff (applies to React, too)
const appRoot = document.createElement("div")
appRoot.id = "app-root"
root.appendChild(appRoot)
appRoot.textContent = "This is my app!"
Run Code Online (Sandbox Code Playgroud)
./src/style.css
#app-root {
background: lightgreen;
}
Run Code Online (Sandbox Code Playgroud)
构建并提供您的应用程序后,您应该看到带有绿色背景的“这是我的应用程序”,所有内容都在您的影子根中。
我想添加我的解决方案来解决这个问题。它基于 @josef-wittmann 的想法,但允许将样式添加到自定义元素的任何新实例,而不仅仅是一个根元素。
索引.html
...
<body>
<script src="main.js"></script>
<my-custom-element></my-custom-element>
</body>
...
Run Code Online (Sandbox Code Playgroud)
./webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: [
{
loader: "style-loader",
options: {
insert: require.resolve("./src/util/style-loader.ts"),
},
},
"css-loader",
],
},
{
test: /\.tsx?$/,
exclude: /node_modules/,
loader: "ts-loader",
options: {
configFile: "tsconfig.app.json",
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
],
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
};
Run Code Online (Sandbox Code Playgroud)
./src/util/style-loader.ts
const styleTags: HTMLLinkElement[] = [];
export const getStyleTags = () => styleTags.map(node => node.cloneNode(true));
export default function (linkTag) {
styleTags.push(linkTag);
}
Run Code Online (Sandbox Code Playgroud)
./src/index.ts
import "./style.css";
import { styleTags } from "./util/style-loader";
customElements.define(
"my-custom-element",
class extends HTMLElement {
async connectedCallback() {
this.attachShadow({ mode: "open" });
const myElement = document.createElement("div");
myElement.innerHTML = "Hello";
this.shadowRoot?.append(...styleTags, myElement );
}
}
);
Run Code Online (Sandbox Code Playgroud)
./src/style.css
div {
background: lightgreen;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3476 次 |
| 最近记录: |