In the process of Monibuca development, in order to facilitate access to the interface of each plug-in, we need to display the customized interface of all plug-ins together. We need to implement the following functions: in order to easily access the interface of each plug-in, we need to display all the custom interface of the plug-in together. We need to implement the following functions:

  1. The plug-in interface can be dynamically loaded and switched on the main screen
  2. Parameters can be passed into the plug-in interface.
  3. Display the plug-in interface quickly and smoothly.

The options are:

  1. Use iframe to load the interface of each plug-in
  2. Dynamic compilation using VUE
  3. Compile to WebComponent using Vuecli

Among them, scheme 1 is the worst choice. Iframe has various disadvantages, and the current trend is to try not to use iframe. Scheme 2 is a feasible scheme, but its defect is that it cannot make use of the compilation features of VUE and requires manual management of front-end resources, which is not conducive to engineering. Scheme 3 is the best scheme, which can perfectly avoid all the shortcomings of the above two schemes.

Scheme used three technologies: cli.vuejs.org/guide/build…

Of course, the process of adopting Plan 3 was not smooth sailing. One of the biggest problems is CSS style loading. Due to the nature of WebComponent, the CSS inside a WebComponent is completely isolated from the outside. So you need to load the CSS separately. In our project, we use the UI framework of iView, so we need to load the CSS file of iView to display normally.

First attempt: Dynamically add a link label

In vue file, if you want to obtain DOM elements, you must wait until the Mounted function to operate, so there is a period of interface display confusion.

let linkTag = document.createElement('link');
linkTag.href = "iview.css";
linkTag.setAttribute('rel'.'stylesheet');
linkTag.setAttribute('type'.'text/css');
el.appendChild(linkTag);
Copy the code

Second attempt: use import mode

More elegant than the first method is to write import in the style tag of the vue file

<style>
@import url("/iview.css")
</style>
Copy the code

This lets you load your CSS files dynamically. But the downside is that every time the WebComponent is loaded, the CSS file is reloaded and the page is still out of place for a while. So how do you avoid loading CSS files every time you render a component?

Third attempt: dynamically inject CSS objects

For a deeper understanding of the WebComponent style mechanism, open github.com/w3c/webcomp… Check the official version. Dig through CSS and find this example:

import styles from "styles.css";
document.adoptedStyleSheets = [...document.adoptedStyleSheets, styles];
Copy the code

After trying, the import notation inside the VUE file failed to construct the CSSStyleSheet object, which was placed in the HTML of the parent page

 <script type="module">
 import styles from "iview.css";
 window.iviewCSS = styles
 </script>
Copy the code

Attempts to operate through the global variable window. The result was also a failure. The error message means that the MIME header of the loaded file must be javascript, and I returned stylesheet.

So I came up with an idea, and since I can’t import it directly, why don’t I just build a CSSStyleSheet object by hand? The first attempt to pass the parent page’s Document. stylesheets directly into the WebComponent reported an error: you must use a CSSStyleSheet object with a constructor, WTF. Wicg. Making. IO/construct – s… To use a constructed CSSStyleSheet, you must use js new.

var style = new CSSStyleSheet()
Copy the code

To shadowRoot. AdoptedStyleSheets that external CSS file how incoming to CSSStyleSheet object? According to the document, loading the CSS file has been blocked.

CSSStyleSheet(options)
When called, execute these steps:
Construct a new CSSStyleSheet object sheet with the following properties:

location set to the base URL of the associated Document for the current global object

No parent CSS style sheet.

No owner node.

No owner CSS rule.

title set to the title attribute of options.

Set alternate flag if the alternate attribute of options is true, otherwise unset the alternate flag.

Set origin-clean flag.

Set constructed flag.

Constructor document set to the associated Document for the current global object.

If the media attribute of options is a string, create a MediaList object from the string and assign it as sheet’s media. Otherwise, serialize a media query list from the attribute and then create a MediaList object from the resulting string and setIt is sheet's media. If the disabled attribute of options istrue.setSheet's disabled flag. Return sheet.Copy the code

In the end, the problem was solved in a rough way.

const appStyle = new CSSStyleSheet();
const appCSSs = document.styleSheets;
for (var i = 0; i < appCSSs.length; i++) {
    for (var j = 0; j < appCSSs[i].cssRules.length; j++) { appStyle.insertRule(appCSSs[i].cssRules[j].cssText); }}Copy the code

We walked through all the style rules of the parent page, manually filling the CSSStyleSheet object by calling insertRule. This contains the CSS rules of the CSS file loaded in the link tag. Then we assign to shadowRoot. The end result is that the WebComponent no longer needs to import CSS to download CSS, and the page is rendered instantly.