preface

Third-party JS plug-ins are often used in daily development. For some function plug-ins not involved in display, only need to introduce a JS file, but for some interface plug-ins, such as the wheel, rich text editor, etc., often need to introduce a SEPARATE CSS file to make it normal display. But because CSS has coverage issues, even if it follows certain specifications (such as BEM), it cannot be ignored.

The versatility of plug-ins can be further improved if only one JS file can be introduced and the plug-in style is completely isolated from the main application. Shadow DOM is definitely a very attractive choice (see 👉Using Shadow DOM for those who are not familiar with it), which fits your needs very well.

In addition, Shadow DOM can be integrated with the MVVM framework, which is also covered in this article.

Why Preact

The popularity of MVVM frameworks has, to some extent, influenced the way front-end developers think. Instead of manipulating the DOM in a dictatorial way, we hand it over to the framework, which greatly improves development efficiency.

If you have a hammer in your hand, everything looks like a nail. The MVVM framework can of course also be used to develop interface level JS plug-ins, which would even make things easier.

JS plug-ins are generally lightweight. Preact works better than React. Preact is a lightweight alternative to React, only 3kB in size, and has the same API as React (according to the React website). More information about its advantages and differences with React can be found on the official website, which will not be repeated here.

The development process

Preact provides scaffolding tools and can also be integrated with other build tools such as Webpack, Rollup, and more. For reference: preactjs.com/guide/v10/g… . Rollup😊 is strongly recommended here

The idea of how to combine Preact and Shadow DOM is similar to that of constructing Shadow DOM components using Webpack 🔧 written by the author. The difference is that we no longer need to separate HTML parts, but can achieve all in JS with the help of JSX. That’s one of the reasons we chose Preact.

In React or Vue projects, it is common practice to select a root node and mount the root component to it. As follows:

import * as ReactDOM from 'react-dom'

ReactDOM.render(<App />.document.querySelector('#app'))
Copy the code

The Shadow DOM is also a DOM node, so all we need to do is mount the root component onto the Shadow DOM. The following is an example of using Preact:

import { h, render } from 'preact'

const shadowHost = document.createElement("div");
document.body.appendChild(shadowHost);

const shadow = shadowHost.attachShadow({ mode: "open" });
const shadowRoot = document.createElement("div");
shadowRoot.id = "shadow-root";

shadow.appendChild(shadowRoot);

render(<App />, shadowRoot);
Copy the code

The relationship between the root component App and Shadow DOM is simplified as follows:

Shadow host is the normal DOM node on which Shadow DOM is attached, and Shadow Root is the Root node on which the Root component is mounted.

Once you’ve done that, you can solve the styling problem.

In Shadow DOM, you can directly add the style tag node, and it will only take effect in Shadow DOM. External styles will not take effect in Shadow DOM, perfect for style isolation. In this vein, simply extract the CSS text, inject it into the Style tag, and attach the style tag to the Shadow DOM.

For simple styles, template strings may suffice, but when styles become numerous, building tools can reduce a lot of work. Take Rollup as an example and use rollup-plugin-postcss to complete:

rollup.config.js

import postcss from "rollup-plugin-postcss";

export default {
  plugins: [
    postcss({
      include: "src/styles/**".inject: false.minimize: true,})]};Copy the code

Set the inject value to false to prevent CSS from injecting into the head tag, so that directly importing CSS files will produce CSS text strings, and set the minimize value to true to enable text compression and reduce packing volume. The introduction is as follows:

import styleText from "./styles/index.scss";

const shadowHost = document.createElement("div");
shadowHost.id = shadowTargetId;
document.body.appendChild(shadowHost);

const shadow = shadowHost.attachShadow({ mode: "open" });

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

const shadowRoot = document.createElement("div");
shadowRoot.id = "shadow-root";

shadow.appendChild(style);
shadow.appendChild(shadowRoot);

render(<App />, shadowRoot);

Copy the code

Now you can develop plugins in Shadow DOM just like you would in a normal React application!

Q&A

  1. Component selection

Preact can use most Components in the React ecosystem directly, however, many of them use Styled Components, which cannot be used directly in Shadow DOM. For Styled Components, the way it works is to inject the style tag into , and since the Shadow DOM isolates external styles, this will not work. If you want to take full advantage of the React ecosystem, you should consider components that require a separate style file.

  1. Event listeners

There is nothing to worry about with React composite events. But sometimes you need to listen for events outside the Shadow DOM, such as clicking on the outside area to close the popover component inside the Shadow DOM. Since the target of all events in Shadow DOM is the Shadow Root node, the event. Target can only be used to determine the SOURCE of UI events from Shadow DOM, but not the specific element.

– End-