Web Components learning (1)

1. What are web components

When developing a project, why not write native JS by hand, but use a very popular front-end framework today, for many reasons, such as:

  • good ecology
  • data driven view
  • Modular
  • Componentization
  • wait

Web Components was born to solve "componentization". It is a componentization natively supported by browsers, and it can run in browsers without relying on any libraries, dependencies, and packaging tools.

The componentization of Vue and React is not real componentization. Although the actual componentization code is written when writing the code, it is no longer componentization after compilation.

For example, in an application developed with Vue + ElementUI, the components of ElementUI all start with el, such as el <el-button>, but the tags displayed on the page after compilation are no longer <el-button> tags .

This is somewhat similar to CSS preprocessors (like Sass, Less), those variables compiled in the development phase (eg $color: red;) are not real variables, but fake variables. After compilation, there is no concept of variables, so it is difficult to communicate with JS.

For example, there is a requirement to provide users with an input box on the page. What color the user enters (such as red, #ff00000), the website will become the theme color of the corresponding color, but after we get the user input, there is no change Assign them to Sass variables. Because the Sass code has become CSS code after compilation, there are no Sass variables, for example color: $color; compiled ascolor: red;。

So at this time, a variable that is natively supported by the browser and can be run without compilation is needed, so CSS variables appear ( , --color: red;) color: var(--color), which can communicate with JS very conveniently, because it is a native variable at the browser level .
In the same way, the componentization of the framework is not a real standard. Each company uses its own componentization standard, which leads to the split of the ecology. Moreover, the componentization of these frameworks can only be realized by compiling, and it is very dependent on This framework is a symbiotic relationship, just like when using Vue, files with suffixes ending with .vue have no way to run in the browser. You must download Node, Webpack, vue-loader and other tools for packaging, but still can’t Run without the installation package of the Vue framework.

Generally speaking, browser vendors will absorb the merits of some very popular front-end frameworks, and then promote them to become standards, and implement these functions natively in browsers. The most classic is jQuery's $() selector

"It's the 21st century, and jQuery is still mentioned?"

Although the rise of Vue and React in recent years has exacerbated the decline of jQuery, there are still more than 66 million websites in the world using jQuery. At the same time, jQuery has left a legacy that has had a profound impact on the industry. W3C follows the $()function Implements querySelector() the and querySelectorAll()methods.

Ironically, it was the emergence of these two native methods that greatly accelerated the decline of jQuery, because they replaced one of jQuery's most commonly used functions: quick selection of DOM elements.

So will the componentization natively supported by browsers replace the popular libraries or frameworks?

So is this really the case? It's not.

Web Components and today's very popular MVVM framework are a coexistence relationship, not a mutually exclusive relationship, just like Sass variables and CSS variables, the two can complement each other perfectly, not to say that CSS variables cannot be used Use Sass variables.

Furthermore, we use those MVVM frameworks not just for their componentization functions. Although componentization is a very important function, there are also page routing, data binding, modularization, and CSS preprocessing. browser, virtual DOM, Diff algorithm, and various huge ecological functions.

What Web Components needs to solve is just such a function of componentization.

React and Web Components were born to solve different problems. Web Components provide powerful encapsulation for reusable components, while React provides a declarative solution to keep the DOM and data in sync. The two are intended to complement each other. As a developer, you are free to choose to use React with Web Components, or Web Components with React, or both.
—— "Web Components - React"

We see Vue and Web Components as primarily complementary technologies. Vue provides excellent support for using and creating custom elements. Whether you are integrating custom elements into an existing Vue application, or using Vue to build and distribute custom elements is convenient.
—— "Vue and Web Components | Vue.js"

Web Components is a componentization solution natively supported by browsers that allows you to create new custom, packageable, and reusable HTML markup. It can run directly in the browser without loading any external modules.

Why does it appear? Because the early component ecology was chaotic, there were various frameworks and libraries with their own specifications, which made it difficult for a team to switch frameworks. In order to solve this differentiation and unify the Web component model, the Web Components specification emerged. The goal is to provide an authoritative, browser-understood way to create components.

Why learn it? To sum it up in one sentence is to look back on history and look forward to the future.

In 2011, the concept of Web Components was proposed, and React was born

In 2013, Chrome and Opera jointly proposed the V0 version of the Web Components specification, and React was open source.

Vue was born in 2014

In 2016, Web Components advanced to the V1 version

Due to issues such as browser compatibility and mainstream framework development efficiency, it is hardly used now, but we can learn its ideas, and maybe it will become useful in the future?

Web Components consists of three technologies

Custom Elements

It is possible to create a custom label. According to the specification, the name of a custom element must contain a hyphen "-", which is used to distinguish it from a native HTML element.

<body>
    <user-card></user-card>
    <script>
        class UserCard extends HTMLElement {
      
      
            constructor() {
      
      
                super();
                
                var el = document.createElement('p');
                el.classList.add('name');
                el.innerText = 'User Name';
                
                this.append(el);
            }
        }
        window.customElements.define('user-card', UserCard);
    </script>
</body>

insert image description here

class UserButton extends HTMLButtonElement {
    
    
    constructor() {
    
    
    	super();
    }
}

customElements.define('user-button', UserButton, {
    
     extends: "button" });
<button is="user-button">

</button>

Using life cycle callback function
In the constructor of custom element, you can specify multiple different callback functions, which will be called during different life periods of the element:

  • connectedCallback: Called when the custom element is inserted into the document DOM for the first time.
  • disconnectedCallback: Called when the custom element is removed from the document DOM.
  • adoptedCallback: Called when the custom element is moved to a new document.
  • attributeChangedCallback: Called when the custom element adds, deletes, or modifies its own attributes.

Shadow DOM

Often write video, audio and other html elements in the control bar or module, but where do these modules come from and what to implement

insert image description here
insert image description here

insert image description here
insert image description here
Hidden a bit deep and hard to spot.

So what is shadow DOM

  1. The existence of the shadow dom mainly solves the problem of maintaining its own boundaries when the dom tree is established. It's a bit like Vue's scope guarantees that it will not be invaded or polluted by external modifications.
  2. Shadow dom hides the corresponding dom information and can still render it in the html document. But the dom information cannot be obtained through ordinary js methods
  3. Shadow dom event capture complies with regular dom events, and is still transmitted inside the shadow dom. At the same time, it also follows event bubbling, and transmits events to the dom of the entire document.

Create a shadow tree
Create a shadow tree root node through createShadowRoot

<!DOCTYPE html>
<html>
<head>
	<title>影子dom</title>
	<link rel="dns-prefetch" href="//dfhs.tanx.com">
	<style>
		.box {
      
      
			height: 80px;
			width: 80px;
			background-color: red;
		}
	</style>
</head>
<body>
	<div id="box" class="box"></div>
</body>
<script>
	var $box = document.getElementById('box');
	var shadowRoot = $box.createShadowRoot(); // 获得root
	//var showRoot = $box.webkitGetShadowRoot() // webkit 支持
	var children = document.createElement('div');
	children.setAttribute('style', 'height: 40px; width: 40px; background-color: blue');
	shadowRoot.appendChild(children);
</script>
</html>


insert image description here
When adding css to shadow tree nodes, class or element selection cannot be used to add, otherwise it will have no effect

<!DOCTYPE html>
<html>
<head>
	<title>影子dom</title>
	<link rel="dns-prefetch" href="//dfhs.tanx.com">
	<style>
		.box {
      
      
			height: 80px;
			width: 80px;
			background-color: red;
		}
		.children {
      
      
			height: 40px;
			width: 40px;
			background-color: blue;
		}
		div {
      
      
			height: 40px;
			width: 40px;
			background-color: blue;
		}
	</style>
</head>
<body>
	<video src="test.mp4" height="200px" controls></video>
	<audio src="mp3.mp3" controls></audio>
	<canvas></canvas>
	<div id="box" class="box"></div>
</body>
<script>
		var $box = document.getElementById('box');
		var shadowRoot = $box.createShadowRoot(); // 获得root
		//var showRoot = $box.webkitGetShadowRoot() // webkit 支持
		var children = document.createElement('div');
		children.setAttribute('class', 'children');
		shadowRoot.appendChild(children);
</script>
</html>

insert image description here
When selecting dom through class, you need to put style into the shadow node

<script>
	var $box = document.getElementById('box');
	var shadowRoot = $box.createShadowRoot(); // 获得root
	//var showRoot = $box.webkitGetShadowRoot() // webkit 支持
	var children = document.createElement('div');
	children.setAttribute('class', 'children')
	shadowRoot.innerHTML += '<style>.children { height: 40px; width: 40px; background-color: blue;}</style>';
	shadowRoot.appendChild(children);
</script>

The shadow DOM cannot be obtained directly.
The dom node cannot be obtained directly through the js conventional method.

	var $box = document.getElementById('box');
	var shadowRoot = $box.createShadowRoot(); // 获得root
	//var showRoot = $box.webkitGetShadowRoot() // webkit 支持
	var children = document.createElement('div');
	children.setAttribute('class', 'children');
	children.setAttribute('id', 'children');
	shadowRoot.appendChild(children);

	// 获得影子dom
	// 通过id
	var getShadowRootById = document.getElementById('children');
	console.log(getShadowRootById)
	// 通过节点选择
	console.log('---------------')
	var getShadowRootByDomBox = document.body.firstChild.nextSibling; // 获得到box
	//var getShadowRootByDom = getShadowRootByDomBox.firstChild
	var getShadowRootByDom = getShadowRootByDomBox.firstElementChild;
	console.log(getShadowRootByDom)

insert image description here
Shadow dom event binding
element obtained when createElement, add addEventListener event

	var $box = document.getElementById('box');
	var shadowRoot = $box.createShadowRoot(); // 获得root
	//var showRoot = $box.webkitGetShadowRoot() // webkit 支持
	var children = document.createElement('div');
	children.setAttribute('class', 'children')
	shadowRoot.innerHTML += '<style>.children { height: 40px; width: 40px; background-color: blue;}</style>';
	shadowRoot.appendChild(children);

	children.addEventListener('click', function(e) {
    
    
		console.log(e)
	})

Use the select attribute of the content element to match the target content to the specified position in the template, and the target content can only be in the shadow element

<!DOCTYPE html>
<html>
<head>
	<title>影子dom</title>
	<link rel="dns-prefetch" href="//dfhs.tanx.com">
	<style>
		.box {
      
      
			height: 160px;
			width: 160px;
			background-color: red;
		}
		.children {
      
      
			height: 80px;
			width: 80px;
			background-color: blue;
		}
		.test-content {
      
      
			background-color: yellow;
		}
	</style>
</head>
<body>
	<div id="box" class="box">
		<div class="test-content">我接着测试</div>
	</div>
	<template class="root-tlp">
		<style>
			.test-ctn {
      
      
				color: white;
			}
		</style>
		<div>
			<div class="test-ctn" id="test">测试</dt>
		</div>
		<content select=".test-content"></content>
	</template>
</body>
<script>
	var $box = document.getElementById('box');
	var shadowRoot = $box.createShadowRoot(); // 获得root
	var children = document.createElement('div');
	var template = document.querySelector('.root-tlp');
	shadowRoot.appendChild(document.importNode(template.content, true));
	document.addEventListener('click', function() {
      
      
		console.log('test-content')
	})
</script>
</html>


insert image description here
CSS selectors:
:host, :host(), :host-context()
:host

<template class="root-tlp">
		<style>
			.test-ctn {
      
      
				color: white;
			}
			:host {
      
      
  				font-weight: bold;
			}
		</style>
		<div>
			<div class="test-ctn" id="test">测试</dt>
		</div>
		<content select=".test-content"></content>
	</template>

insert image description here
:host() selector, select the shadow dom host element

<body>
	<div id="box" class="box">
		<div class="test-content">我接着测试</div>
	</div>
	<template class="root-tlp">
		<style>
			.test-ctn {
      
      
				color: white;
			}
			:host {
      
      
  				font-weight: bold;
			}
			:host(.box) {
      
      
				color: blue;
			}
		</style>
		<div>
			<div class="test-ctn" id="test">测试</dt>
		</div>
		<content select=".test-content"></content>
	</template>
</body>

insert image description here
:host-context() is used with descendant selector expressions to select only instances of a custom element inside a specific ancestor

<!DOCTYPE html>
<html>
<head>
	<title>影子dom</title>
	<link rel="dns-prefetch" href="//dfhs.tanx.com">
	<style>
		.box {
      
      
			height: 160px;
			width: 160px;
		}
		.children {
      
      
			height: 80px;
			width: 80px;
		}
	</style>
</head>
<body>
	<div id="box" class="test">
		<div class="box-content" id="box-content">
			<div class="box-ctn">213</div>
		</div>
	</div>
	<template class="root-tlp">
		<style>
			.test-ctn {
      
      
				color: white;
			}
			:host {
      
      
  				font-weight: bold;
			}
			:host(.box-content) {
      
      
				color: blue;
				background-color:red;
			}
			:host-context(.test) {
      
      
				height:  300px;
				background-color: blueviolet
			}
		</style>
		<div>
			<div class="test-ctn" id="test">测试</dt>
		</div>
		<content select=".box-ctn"></content>
	</template>
</body>
<script>
	var $box = document.getElementById('box-content');
	var shadowRoot = $box.createShadowRoot(); // 获得root
	var children = document.createElement('div');
	var template = document.querySelector('.root-tlp');
	shadowRoot.appendChild(document.importNode(template.content, true));
</script>
</html>

insert image description here

templates and slots

It is possible to write markup templates that are not displayed in the rendered page. They can then be reused multiple times as the basis for custom element structures.

  1. Will not be rendered until used.
  2. It has no effect on the rest of the page until it is used, scripts will not run, images will not load, and audio will not play.
<body>
    <p>会影响外部样式</p>
    <template id="my-paragraph">
        <style>
            p{
      
      color: red;}
        </style>
        <p>My paragraph</p>
    </template>
    <my-paragraph></my-paragraph>
    <script>
        customElements.define('my-paragraph',
            class extends HTMLElement {
      
      
                constructor() {
      
      
                    super();
                    let template = document.getElementById('my-paragraph');
                    let templateContent = template.content.cloneNode(true);

                    this.appendChild(templateContent);
                }
        })
    </script>
</body>

insert image description here
The use of slots:

<body>
    <style>
        p{
      
      color: blueviolet;}
    </style>
    
    
    <p>会影响外部样式</p>
    <template id="my-paragraph">
        <style>
            p{
      
      color: red;}
        </style>
        <p>My paragraph</p>
        <slot name="my-text">My default text</slot>
    </template>
    
    
    <my-paragraph>
        <p slot="my-text">slot text</p>
    </my-paragraph>
    
    
    <script>
        customElements.define('my-paragraph',
            class extends HTMLElement {
      
      
                constructor() {
      
      
                    super();
                    let template = document.getElementById('my-paragraph');
                    let templateContent = template.content.cloneNode(true);
                    this.attachShadow({
      
      mode: 'open'}).appendChild(templateContent);
                }
        })
    </script>
</body>

insert image description here
Below is a selection of fun and high-value component libraries on the market to experience the following Web Components:

  • css-doodle : literal translation - css doodle
  • fancy-components : Literal translation - fancy component library, unfortunately there is no official document

Two, css-doodle

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>css-doodle</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/css-doodle/0.6.1/css-doodle.min.js"></script>
  <style>
    html, body {
      
      
      height: 100%;
      margin: 0;
      overflow: hidden;
    }
  </style>
</head>
<body>
  <css-doodle>
    :doodle {
      @grid: 20 / 100vmax;
      background: #12152f;
    }

    ::after {
      content: '\@hex(@rand(0x2500, 0x257f))';
      font-size: 5vmax;
      color: hsla(@rand(360), 70%, 70%, @rand(.9))
    }
  </css-doodle>
</body>
</html>

insert image description here

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>css-doodle</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/css-doodle/0.6.1/css-doodle.min.js"></script>
  <style>
    html, body {
      
      
      width: 100%;
      height: 100%;
      margin: 0;
      overflow: hidden;
      background: #011627;
      display: grid;
      place-items: center;
      /* 公众号:前端学不动 搜:居中篇 */
    }
  </style>
</head>
<body>
  <css-doodle>
    :doodle {
      @grid: 1x1x100 / 100vmin;
      animation: r 23s linear infinite
    }

    @size: 100% 50%;
    position: absolute;
    top: 25%;
    transform: rotate(@r(360deg));
    perspective: @r(100px, 200px);

    ::after {
      content: '';
      position: absolute;
      @size: @r(.5vmin, 5vmin);
      color: @p(#fdfffc, #2ec4b6, #e71d36, #ff9f1c);
      background: currentColor;
      box-shadow: @m2(0 0 1.2vmin currentColor);
      animation: cycle @r(2s) linear infinite;
      --trans: scaleX(@r(1, 5)) translateZ(@r(10vmin, 20vmin));
      transform: rotateY(0) @var(--trans)
    }
    :empty::after { display: none }

    @keyframes cycle {
      to {
        transform: rotateY(@p(-1turn, 1turn)) @var(--trans)
      }
    }

    @keyframes r {
      to { transform: rotate(1turn) }
    }
  </css-doodle>
</body>
</html>

insert image description here

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>css-doodle</title>
  <script src="https://unpkg.com/[email protected]/css-doodle.min.js"></script>
  <style>
    html, body {
      
      
      width: 100%;
      height: 100%;
      margin: 0;
      overflow: hidden;
      background: radial-gradient(#459dc1, #070729);
      display: grid;
      place-items: center;
      /* 公众号:前端学不动 搜:居中篇 */
    }
  </style>
</head>
<body>
  <css-doodle>
    <!-- css-doodle.com -->
    :doodle {
      @grid: 80x1 / 100vw 100vh;
      @min-size: 100px;
      filter: url(#filter);
      animation: r 23s linear infinite
    }

    @size: 100% 50%;
    position: absolute;
    top: 25%;
    transform: rotate(@r(360deg));
    perspective: 130px;

    ::after {
      content: '';
      position: absolute;
      @size: @r(10px);
      background: #fff;
      box-shadow: @m3(0 0 calc(.5vmin + 5px) #fff);
      animation: cycle @r(2s, 8s) linear infinite;
      animation-delay: -@r(100s);
      --trans: scaleX(@r(.1, 5)) translateZ(105px);
      transform: rotateY(0) @var(--trans)
    }

    @keyframes cycle {
      to {
        transform: rotateY(@p(-1turn, 1turn)) @var(--trans)
      }
    }

    @keyframes r {
      to { transform: rotate(@p(-1turn, 1turn)) }
    }
  </css-doodle>
  
  <svg style="width: 0; height: 0;">
    <filter id="filter">
      <feGaussianBlur in="SourceGraphic" stdDeviation="5" result="blur">
      </feGaussianBlur>
      <feColorMatrix in="blur" mode="matrix" values="
          1 0 0 0 0
          0 1 0 0 0
          0 0 1 0 0
          0 0 0 18 -7
        "
        result="goo"  
      >
      </feColorMatrix>
      <feBlend in="SourceGraphic" in2="goo"></feBlend>
    </filter>
  </svg>
</body>
</html>

insert image description here

三、fancy-components

https://github.com/fancy-components/fancy-components

<!DOCTYPE html>
<html lang="en">
<head>
  <title>fancy-components</title>
  <style>
    * {
      
       padding: 0; margin: 0; }
    html, body {
      
       height: 100%; }
    body {
      
      
      display: grid;
      place-items: center;
      background: #3f2766;
    }
    fc-3d-btn {
      
      
      --color: #6e50a6;
      --shadow-color: rgba(255, 255, 255, .4);
      --inset-shadow-color: #315;
      --inset-shadow-color-active: rgba(49, 23, 7, .9);
      --cover-color: rgba(0, 0, 0, .4);
    }
  </style>
</head>
<body>
  <div>
    <!-- html 只支持小写标签,不支持驼峰命名法 -->
    <fc-input white placeholder="Username"></fc-input>
    <br />
    <fc-input white disabled value="fancy components" placeholder="Username"></fc-input>
    <br />
    <fc-btn>fancy-components</fc-btn>
    <br />
    <fc-warp-btn></fc-warp-btn>
    <br />
    <fc-3d-btn></fc-3d-btn>
    <br />
    <fc-underline-btn></fc-underline-btn>
    <br />
    <fc-pixel-btn></fc-pixel-btn>
    <br />
    <fc-parentheses-btn></fc-parentheses-btn>
    <br />
    <fc-round-btn></fc-round-btn>
    <br />
    <fc-arrow-btn></fc-arrow-btn>
    <br />
    <fc-bubbles click>
      <fc-parentheses-btn>撒花</fc-parentheses-btn>
    </fc-bubbles>
  </div>

  <script type="module">
    import {
      
       FcTypingInput } from 'http://unpkg.com/fancy-components'
    import {
      
       FcDblWarpBtn } from 'http://unpkg.com/fancy-components'
    import {
      
       FcWarpBtn } from 'http://unpkg.com/fancy-components'
    import {
      
       Fc3DBtn } from 'http://unpkg.com/fancy-components'
    import {
      
       FcUnderlineBtn } from 'http://unpkg.com/fancy-components'
    import {
      
       FcPixelBtn } from 'http://unpkg.com/fancy-components'
    import {
      
       FcParenthesesBtn } from 'http://unpkg.com/fancy-components'
    import {
      
       FcRoundBtn } from 'http://unpkg.com/fancy-components'
    import {
      
       FcArrowBtn } from 'http://unpkg.com/fancy-components'
    import {
      
       FcBubbles } from 'http://unpkg.com/fancy-components'

    // 注册组件
    // 可以传递一个重命名组件名的字符串,必须是小写,且用 `-` 连接
    // 不传参数默认组件名就是 fc-typing-input
    new FcTypingInput('fc-input')
    new FcDblWarpBtn('fc-btn')
    new FcWarpBtn()
    new Fc3DBtn()
    new FcUnderlineBtn()
    new FcPixelBtn()
    new FcParenthesesBtn()
    new FcRoundBtn()
    new FcArrowBtn()
    new FcBubbles()
  </script>
</body>
</html>

insert image description here

Fourth, use the Web Components component library in the scaffolding

Used in Vue 2

npm i -g @vue/cli
vue create vue2-app
cd vue2-app
npm i fancy-components
npm run serve

// src\main.js
import Vue from 'vue'
import App from './App.vue'
import {
    
     FcBubbles } from 'fancy-components'

// 禁用 no-new 校验规则
/* eslint-disable no-new */
new FcBubbles()

Vue.config.productionTip = false

new Vue({
    
    
  render: h => h(App)
}).$mount('#app')


<!-- src\App.vue -->
<template>
  <div id="app">
    <fc-bubbles click><img alt="Vue logo" src="./assets/logo.png"></fc-bubbles>
    <!-- <FcBubbles click><img alt="Vue logo" src="./assets/logo.png"></FcBubbles> -->
  </div>
</template>
...


The status of Web Components native components is the same as that of HTML tags. Uppercase camelcase components will be treated as Vue components. Native components should be the same as HTML tags, and should not be written in camelcase. The same is true in the React framework.

Using the Web Components console in the old version of Vue CLI may issue a warning. The reason is that Vue judges the native component as a Vue component, and the warning component is not registered. The solution is to configure Vue to ignore the native component ignoredElements:

Vue.config.ignoredElements = [
 // 正则匹配
 /^fc-/,
 // 或字符串
 'css-coodle'
]

Used in Vue 3

vue create vue3-app
cd vue3-app
npm i fancy-components
npm run serve

// src\main.js
import {
    
     createApp } from 'vue'
import App from './App.vue'
import {
    
     FcBubbles } from 'fancy-components'

/* eslint-disable no-new */
new FcBubbles()

createApp(App).mount('#app')


<!-- src\App.vue -->
<template>
  <fc-bubbles click><img alt="Vue logo" src="./assets/logo.png"></fc-bubbles>
  <HelloWorld msg="Welcome to Your Vue.js App"/>
</template>

It is the same as Vue2 in use, but it will actually report an error:

[Vue warn]: Failed to resolve component: fc-bubbles
If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.

The reason is the same as the Vue2 application created by the old version of Vue CLI, the solution is still to configure to ignore native components (custom elements), refer to: Vue and Web Components

// vue.config.js
module.exports = {
    
    
  chainWebpack: config => {
    
    
    config.module
      .rule('vue')
      .use('vue-loader')
      .tap(options => ({
    
    
        ...options,
        compilerOptions: {
    
    
          // 将所有 fc- 开头的标签名都视为自定义元素
          isCustomElement: tag => tag.startsWith('fc-')
        }
      }))
  }
}


Restart the application, the warning is gone, but the click still does not take effect. Open the Element panel and find that the click attribute of the component has not been added, but other attributes such as click1 can be added. This may be because Vue3 thinks click is a keyword that cannot be added directly. , the test found that you only need to change click to uppercase Click to add it.

<fc-bubbles Click><img alt="Vue logo" src="./assets/logo.png"></fc-bubbles>

Using Web Components component library in Vite

# npm 6.x
npm create vite@latest vite-vue-app
√ Select a framework: » vue
√ Select a variant: » vue

cd vite-vue-app
npm install
npm i fancy-components
npm run dev

// src/main.js
import {
    
     createApp } from 'vue'
import App from './App.vue'
import {
    
     FcBubbles } from 'fancy-components'

new FcBubbles()

createApp(App).mount('#app')


<!-- src\App.vue -->
<!-- 注意 Click 大写 -->
<fc-bubbles Click><img alt="Vue logo" src="./assets/logo.png" /></fc-bubbles>

Also configure ignored custom elements:

// vite.config.js
import {
    
     defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
    
    
  plugins: [
    vue({
    
    
      template: {
    
    
        compilerOptions: {
    
    
          // 将所有 fc- 开头的标签名都视为自定义元素
          isCustomElement: tag => tag.startsWith('fc-')
        }
      }
    })
  ]
})


No reboot is required to take effect.

insert image description here

Guess you like

Origin blog.csdn.net/woyebuzhidao321/article/details/129430021