找回密码
 会员注册
查看: 13|回复: 0

WebComponent探索之旅_UTF_8

[复制链接]

2万

主题

0

回帖

7万

积分

超级版主

积分
75043
发表于 2024-9-30 08:14:53 | 显示全部楼层 |阅读模式
Web Component 是什么?简介Web Components 是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的 web 应用中使用它们。--Web Components | MDN[1]关于它的其它介绍:组件是前端的发展方向,现在流行的 React 和 Vue 都是组件框架。而 Web Component 相比第三方框架,原生组件简单直接,符合直觉,不用加载任何外部模块,代码量小。目前,已经发展的比较成熟,并用于生产环境。组件化开发使得我们可以将页面切割成模块便于封装和开发,而 Web Component 在此基础上,可以将每个组件渲染在独立的 DOM 树中,天然支持模块间样式和逻辑的隔离。特性这里会对 Web Component 的相关属性做一个简单介绍。Web Component 特性完整代码:https://codesandbox.io/s/snowy-darkness-jmdip7Custom Elements一组 Javascript API,允许您定义 Custom Elements 及其行为,然后在您的用户界面中按照需要使用它们。window.customElements//custombuttonclassCustomButtonextendsHTMLElement{constructor(){super();constbutton=document.createElement("button");button.innerText=this.getAttribute("name")||"custombutton";button.disabled=true;this.appendChild(button);}}window.customElements.define("custom-button",CustomButton);window.customElements.get该方法用来获取自定义组件的构造函数,接收一个参数,即声明的自定义组件的 name,返回构造函数。constgetCustomConstructorBefore=customElements.get('custom-button');//getCustomConstructorBeforebefore:undefinedconsole.log('getCustomConstructorBeforebefore:',getCustomConstructorBefore);customElements.define("custom-button",CustomButton);constgetCustomConstructorAfter=customElements.get('custom-button');//getCustomConstructorAfterafter:CustomButton(){}console.log('getCustomConstructorAfterafter:',getCustomConstructorAfter);window.customElements.upgradecustomElements upgrade() 方法升级节点子树中文档的所有包含 shadow dom 的(亲测,可以不包含 shadow dom)自定义元素,甚至在它们连接到主文档之前。接收一个参数,即一个自定义组件节点,无返回值。//先创建自定义标签constel=document.createElement("spider-man");classSpiderManextendsHTMLElement{}//后声明构造函数customElements.define("spider-man",SpiderMan);//falseconsole.log(elinstanceofSpiderMan);//建立自定义标签与构造函数之间的绑定关系customElements.upgrade(el);//trueconsole.log(elinstanceofSpiderMan);window.customElements.whenDefined该方法用来检测并提供自定义组件被定义声明完毕的时机,接收一个参数,即自定义元素的 name,返回值是一个 Promise,若提供的 name 无效,则触发 Promise 的 catch,可以用来判断是否定义了同名的 Custom Element。Custom Element 重复定义的报错:我们可以用这个方法来捕获重复定义的报错,最推荐 define 之前先 get 一下~customElements.whenDefined('custom-button').then(()=>{customElements.define('custom-button',CustomButton);}).catch((err)=>{console.log(err,'err-----')});捕获的报错信息:生命周期Custom Elements提供了一些生命周期函数,使得我们能在自定义元素在 DOM 中的行为变化后执行相关逻辑。ConnectedCallback:当自定义元素第一次被连接到文档 DOM 时调用(类似组件 Mounted);attributeChangedCallback:当自定义元素的一个属性被增加、移除或更改时被调用,需要配合静态方法 observedAttributes 来使用,设置只有注册在 observedAttributes 中的属性才被监听;disconnectedCallback:当自定元素与文档 DOM 断开连接时调用(类似 Unmount);adoptedCallback:当自定义元素被移动到新文档时被调用;示例代码:classCustomButtonextendsHTMLElement{constructor(){super();constbutton=document.createElement("button");button.innerText="custombutton";button.addEventListener("click",this.changeAttribute.bind(this));consttextSpan=document.createElement("span");textSpan.innerText=this.getAttribute("text")||"default";textSpan.className="info";this.appendChild(button);this.appendChild(textSpan);}connectedCallback(){console.log("自定义button被连接到DOM啦~");}//observedAttributes,定义特定属性变化时,触发attributeChangedCallback函数//未定义的属性改变,则不会触发回调staticgetobservedAttributes(){return["style","text"];}//与observedAttributes结合使用attributeChangedCallback(name,oldValue,newValue){if(name==="text"&oldValue!==newValue&this.querySelector(".info")){this.querySelector(".info").innerText=newValue;}}changeAttribute(){this.setAttribute("text","sfsdfd");}disconnectedCallback(){console.log("自定义button与DOM断开连接啦~");}adoptedCallback(){//创建一个iframe的document,并移动进去console.log("自定义button移动到新文档啦~");}clickToRemove(){//从DOM中移除自身this.parentNode.removeChild(this);}}window.customElements.define("custom-button",CustomButton);Shadow DOM一组 Javascript API,可以将封装的“Shadow DOM”树附加到元素(与主文档分开呈现),并控制其关联的功能。通过这种方式,您可以拥有一个天然的沙箱环境,保持元素的功能私有,实现样式和脚本的隔离,不用担心与文档的其他部分发生冲突。示例代码:classCustomShadowDOMextendsHTMLElement{constructor(){super();//创建一个shadowDOMconstshadow=this.attachShadow({mode:"open"});constinfo=document.createElement("span");conststyle=document.createElement("style");constcss="span {color:red;}";style.type="text/css";style.appendChild(document.createTextNode(css));info.setAttribute("class","info");info.textContent=this.getAttribute("text")||"default";//shadow可以创建一个不受外部影响,切拥有内部js运行逻辑,拥有独立的//css的自定义元素(也就是webcomponent),shadow.appendChild(style);shadow.appendChild(info);}}window.customElements.define("custom-shadow-dom",CustomShadowDOM);并非只能在 CustomElement 下使用,即便是普通的 HTMLElement 也能使用这一技术来实现样式保护,详见 :Using shadow DOM - Web Components | MDN[2]。Shadow DOM 与 Light DOM 不能共存,若两者同时存在则通常情况下 Light DOM 不会被渲染。HTML Template 和 元素使您可以编写呈现页面中显示的标记模版。可以作为自定义元素结构的基础被多次重用,也就是我们常做的组件复用。 元素是一种保护客户端内容机制,该内容在加载页面时不会呈现,但随后可以在运行时使用 JavaScript 实例化。将模板视为一个可存储在文档中以便后续使用的内容片段,虽然解析器在加载页面时确实会处理 template 元素的内容,但这样做只是为了确保内容有效,元素不会被渲染。下面我们来定义一个人员信息卡片的template,组件的样式应该与代码封装在一起,只对自定义元素生效,不影响外部的全局样式,可以把样式写在 template 里面。示例代码: //UserTemplate.jsclassUserCardextendsHTMLElement{constructor(){super();vartemplateElem=document.getElementById("userCardTemplate");varcontent=templateElem.content.cloneNode(true);content.querySelector(".image").setAttribute("src",this.getAttribute("image"));content.querySelector(".name").innerText=this.getAttribute("name");content.querySelector(".email").innerText=this.getAttribute("email");content.querySelector(".age").innerText=this.getAttribute("age");this.appendChild(content);}}window.customElements.define("user-card",UserCard);运行效果:使用模版我们只能传递一些文本变量,这很有局限性,于是 Web Components 引入了(插槽)的概念来增加编码的灵活度。我们可以使用 slot 来实现基于模版的部分自定义内容(标签、样式)的渲染,slot 插槽需要在 Shadow DOM 里才能生效。classUserCardextendsHTMLElement{constructor(){super();vartemplateElem=document.getElementById("userCardTemplate");varcontent=templateElem.content.cloneNode(true);content.querySelector(".image").setAttribute("src",this.getAttribute("image"));content.querySelector(".name").innerText=this.getAttribute("name");content.querySelector(".email").innerText=this.getAttribute("email");content.querySelector(".age").innerText=this.getAttribute("age");constshadowRoot=this.attachShadow({mode:'open'}).appendChild(content);}}window.customElements.define("user-card",UserCard);所有特性的完整 Demo :https://codesandbox.io/s/snowy-darkness-jmdip7file=/index.html兼容性支持程度、存在问题、对应版本的浏览器在市场中的占比(不支持 IE)。Custom ElementsShadow DOMHTML templatesWeb Component 的使用实践React 与 Web Component 结合自定义标签 custom-buttonWeb Component & React 完整代码:https://codesandbox.io/s/sleepy-euclid-00crlifile=/src/CustomButton.tsx:256-261//customButton.tsxclassCustomButtonextendsHTMLElement{constructor(){super();this.shadow=this.attachShadow({mode:"open"});conststyle=document.createElement("style");style.textContent=`span {display:block;color:black};`;this.shadow.appendChild(style);constchild=document.createElement("span");child.className="shadowInfo";child.textContent="default";this.shadow.appendChild(child);}connectedCallback(){if(this.querySelector(".shadowInfo")){this.querySelector(".shadowInfo").textContent=this.getAttribute("text");}}//observedAttributes,定义特定属性变化时,触发attributeChangedCallback函数staticgetobservedAttributes(){return["text"];}//与observedAttributes结合使用attributeChangedCallback(name,oldValue,newValue){if(name==="text"&oldValue!==newValue){this.shadow.querySelector(".shadowInfo").innerText=newValue;}}}window.customElements.define("custom-button",CustomButton);在组件中引入constMain=(props)=>{const[text,setText]=useState("custombutton");return(setText("muzishuiji")}>changetext点击按钮changetext);};exportdefaultMain;运行效果: 已关注 关注 重播 分享 赞 关闭观看更多更多字节前端 ByteFE已关注分享点赞在看已同步到看一看写下你的评论分享视频,时长00:020/000:00/00:02 切换到横屏模式 继续播放进度条,百分之0播放00:00/00:0200:02 倍速播放中 0.5倍 0.75倍 1.0倍 1.5倍 2.0倍 超清 流畅 您的浏览器不支持 video 标签 继续观看 Web Component 探索之旅 观看更多转载,Web Component 探索之旅字节前端 ByteFE已关注分享点赞在看已同步到看一看写下你的评论 视频详情 Vue 与 Web Component 结合Vue 提供了一个和定义一般 Vue 组件几乎完全一致的 defineCustomElement 方法来支持创建自定义元素。这个方法接收的参数和 defineComponent 完全相同。但它会返回一个继承自 HTMLElement 的自定义元素构造器:import{defineCustomElement}from"vue";constMyVueElement=defineCustomElement({//普通vue组件选项});customElements.define("my-vue-element",MyVueElement);defineCustomElement这个 API 允许开发者创建 Vue 驱动的 UI 组件库,这些库可以与任何框架一起使用,或者根本没有框架。Web Component 在「飞书项目」的插件体系的尝试创建一个 React 工程,接下来根据示例代码操作,插件的 js 资源通过网络请求加载,css 资源通过 string 引入。Web Component 在「飞书项目」的插件体系的尝试完整代码:https://codesandbox.io/s/goofy-gauss-pmi46ifile=/src/App.tsx主工程的代码示例App.jsimport'./pluginTemplate';import'./App.css';import{useEffect,useState}from"react";functionApp(){const[userId]=useState('123456789');const[count,setCount]=useState(0);useEffect(()=>{consttimer=setInterval(()=>{setCount(pre=>pre+1);},1000);return()=>{clearInterval(timer)}},[]);return( 我是一个依赖人员卡片的业务方 这里有一个定时器:{count}宿主环境的a标签 );}exportdefaultApp;关键角色 pluginTemplate.js//偷个懒,cssText就不请求了importcssContentfrom"./cssContent";classPluginTemplateextendsHTMLElement{constructor(){super();this.shadow=this.attachShadow({mode:"open"});//给插件搞一个容器constcontainer=document.createElement("div");container.id="root";//如果有modal需要作为getPopupcontainercontainer.style.position="relative";//container.innerHTML=divText;this.shadow.appendChild(container);//插件的style设置不会影响到外部conststyle=document.createElement("style");style.innerText="body{backgrount-color:red;}#root{--semi-shadow-elevated:001pxrgba(0,0,0,.3),04px14pxrgba(0,0,0,.1);}";conststyleNode=document.createTextNode(cssContent.toString());style.appendChild(styleNode);this.shadow.appendChild(style);}connectedCallback(){//动态插入插件的打包资源constscript=document.createElement("script");script.type="text/javascript";fetch("https://raw.githubusercontent.com/muzishuiji/demo_resource/main/web_component/bundle.js").then((response)=>{response.text().then((data)=>{script.innerText=`(function(){let__temp_exports__={};with({exports:__temp_exports__}){console.log(document);${data.replace(/[\r\n]*/[/*](=#sourceMap"\r\n]*/[/*").*[\r\n]*/g,"")};}return__temp_exports__;})();`;this.shadow.appendChild(script);});}).catch((err)=>{console.log(err,"err-----");});//引入一个会导致栈溢出的script//consterrScript=document.createElement("script");//errScript.type="text/javascript";//errScript.innerText=(function(){//functionsum(n){//if(n===1){//return1;//}//returnn+sum(n-1);//}//sum(1e5);//})();//this.shadow.appendChild(errScript);}//observedAttributes,定义特定属性变化时,触发attributeChangedCallback函数staticgetobservedAttributes(){return["userId"];}}if(!window.customElements.get("plugin-template")){window.customElements.define("plugin-template",PluginTemplate);}constant.js 的 cssContent//这里是插件资源的css,生产环境会通过网络请求获取//在这里偷个懒不发请求了,存成一个StringexportconstcssContent=`#root{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:-apple-system,BlinkMacSystemFont,SegoeUI,Roboto,Oxygen,Ubuntu,Cantarell,FiraSans,DroidSans,HelveticaNeue,sans-serif;margin:0}code{font-family:source-code-pro,Menlo,Monaco,Consolas,CourierNew,monospace}#root{--semi-transition_duration-slowest:0ms;--semi-transition_duration-slower:0ms;--semi-transition_duration-slow:0ms;--semi-transition_duration-normal:0ms;--semi-transition_duration-fast:0ms;--semi-transition_duration-faster:0ms;--semi-transition_duration-fastest:0ms;--semi-transition_duration-none:0ms;--semi-transition_function-linear:linear;--semi-transition_function-ease:ease;--semi-transition_function-easeIn:ease-in;--semi-transition_function-easeOut:ease-out;--semi-transition_function-easeInIOut:ease-in-out;--semi-transition_delay-none:0ms;--semi-transition_delay-slowest:0ms;--semi-transition_delay-slower:0ms;--semi-transition_delay-slow:0ms;--semi-transition_delay-normal:0ms;--semi-transition_delay-fast:0ms;--semi-transition_delay-faster:0ms;--semi-transition_delay-fastest:0ms;--semi-transform_scale-none:scale(1,1);--semi-transform_scale-small:scale(1,1);--semi-transform_scale-medium:scale(1,1);--semi-transform_scale-large:scale(1,1);--semi-transform-rotate-none:rotate(0deg);--semi-transform_rotate-clockwise90deg:rotate(90deg);--semi-transform_rotate-clockwise180deg:rotate(180deg);--semi-transform_rotate-clockwise270deg:rotate(270deg);--semi-transform_rotate-clockwise360deg:rotate(360deg);--semi-transform_rotate-anticlockwise90deg:rotate(-90deg);--semi-transform_rotate-anticlockwise180deg:rotate(-180deg);--semi-transform_rotate-anticlockwise270deg:rotate(-270deg);--semi-transform_rotate-anticlockwise360deg:rotate(-360deg)}#root,#root.semi-always-light{--semi-amber-0:254,251,235;--semi-amber-1:252,245,206;--semi-amber-2:249,232,158;--semi-amber-3:246,216,111;--semi-amber-4:243,198,65;--semi-amber-5:240,177,20;--semi-amber-6:200,138,15;--semi-amber-7:160,102,10;--semi-amber-8:120,70,6;--semi-amber-9:80,43,3;--semi-black:0,0,0;--semi-blue-0:234,245,255;--semi-blue-1:203,231,254;--semi-blue-2:152,205,253;--semi-blue-3:101,178,252;--semi-blue-4:50,149,251;--semi-blue-5:0,100,250;--semi-blue-6:0,98,214;--semi-blue-7:0,79,179;--semi-blue-8:0,61,143;--semi-blue-9:0,44,107;--semi-cyan-0:229,247,248;--semi-cyan-1:194,239,240;--semi-cyan-2:138,221,226;--semi-cyan-3:88,203,211;--semi-cyan-4:44,184,197;--semi-cyan-5:5,164,182;--semi-cyan-6:3,134,152;--semi-cyan-7:1,105,121;--semi-cyan-8:0,77,91;--semi-cyan-9:0,50,61;--semi-green-0:236,247,236;--semi-green-1:208,240,209;--semi-green-2:164,224,167;--semi-green-3:125,209,130;--semi-green-4:90,194,98;--semi-green-5:59,179,70;--semi-green-6:48,149,59;--semi-green-7:37,119,47;--semi-green-8:27,89,36;--semi-green-9:17,60,24;--semi-grey-0:249,249,249;--semi-grey-1:230,232,234;--semi-grey-2:198,202,205;--semi-grey-3:167,171,176;--semi-grey-4:136,141,146;--semi-grey-5:107,112,117;--semi-grey-6:85,91,97;--semi-grey-7:65,70,76;--semi-grey-8:46,50,56;--semi-grey-9:28,31,35;--semi-indigo-0:236,239,248;--semi-indigo-1:209,216,240;--semi-indigo-2:167,179,225;--semi-indigo-3:128,144,211;--semi-indigo-4:94,111,196;--semi-indigo-5:63,81,181;--semi-indigo-6:51,66,161;--semi-indigo-7:40,52,140;--semi-indigo-8:31,40,120;--semi-indigo-9:23,29,99;--semi-light-blue-0:233,247,253;--semi-light-blue-1:201,236,252;--semi-light-blue-2:149,216,248;--semi-light-blue-3:98,195,245;--semi-light-blue-4:48,172,241;--semi-light-blue-5:0,149,238;--semi-light-blue-6:0,123,202;--semi-light-blue-7:0,99,167;--semi-light-blue-8:0,75,131;--semi-light-blue-9:0,53,95;--semi-light-green-0:243,248,236;--semi-light-green-1:227,240,208;--semi-light-green-2:200,226,165;--semi-light-green-3:173,211,126;--semi-light-green-4:147,197,91;--semi-light-green-5:123,182,60;--semi-light-green-6:100,152,48;--semi-light-green-7:78,121,38;--semi-light-green-8:57,91,27;--semi-light-green-9:37,61,18;--semi-lime-0:242,250,230;--semi-lime-1:227,246,197;--semi-lime-2:203,237,142;--semi-lime-3:183,227,91;--semi-lime-4:167,218,44;--semi-lime-5:155,209,0;--semi-lime-6:126,174,0;--semi-lime-7:99,139,0;--semi-lime-8:72,104,0;--semi-lime-9:47,70,0;--semi-orange-0:255,248,234;--semi-orange-1:254,238,204;--semi-orange-2:254,217,152;--semi-orange-3:253,193,101;--semi-orange-4:253,166,51;--semi-orange-5:252,136,0;--semi-orange-6:210,103,0;--semi-orange-7:168,74,0;--semi-orange-8:126,49,0;--semi-orange-9:84,29,0;--semi-pink-0:253,236,239;--semi-pink-1:251,207,216;--semi-pink-2:246,160,181;--semi-pink-3:242,115,150;--semi-pink-4:237,72,123;--semi-pink-5:233,30,99;--semi-pink-6:197,19,86;--semi-pink-7:162,11,72;--semi-pink-8:126,5,58;--semi-pink-9:90,1,43;--semi-purple-0:247,233,247;--semi-purple-1:239,202,240;--semi-purple-2:221,155,224;--semi-purple-3:201,111,209;--semi-purple-4:180,73,194;--semi-purple-5:158,40,179;--semi-purple-6:135,30,158;--semi-purple-7:113,22,138;--semi-purple-8:92,15,117;--semi-purple-9:73,10,97;--semi-red-0:254,242,237;--semi-red-1:254,221,210;--semi-red-2:253,183,165;--semi-red-3:251,144,120;--semi-red-4:250,102,76;--semi-red-5:249,57,32;--semi-red-6:213,37,21;--semi-red-7:178,20,12;--semi-red-8:142,8,5;--semi-red-9:106,1,3;--semi-teal-0:228,247,244;--semi-teal-1:192,240,232;--semi-teal-2:135,224,211;--semi-teal-3:84,209,193;--semi-teal-4:39,194,176;--semi-teal-5:0,179,161;--semi-teal-6:0,149,137;--semi-teal-7:0,119,111;--semi-teal-8:0,89,85;--semi-teal-9:0,60,58;--semi-violet-0:243,237,249;--semi-violet-1:226,209,244;--semi-violet-2:196,167,233;--semi-violet-3:166,127,221;--semi-violet-4:136,91,210;--semi-violet-5:106,58,199;--semi-violet-6:87,47,179;--semi-violet-7:70,37,158;--semi-violet-8:54,28,138;--semi-violet-9:40,20,117;--semi-white:255,255,255;--semi-yellow-0:255,253,234;--semi-yellow-1:254,251,203;--semi-yellow-2:253,243,152;--semi-yellow-3:252,232,101;--semi-yellow-4:251,218,50;--semi-yellow-5:250,200,0;--semi-yellow-6:208,170,0;--semi-yellow-7:167,139,0;--semi-yellow-8:125,106,0;--semi-yellow-9:83,72,0}#root.semi-always-dark,#root[theme-mode=dark]{--semi-red-0:108,9,11;--semi-red-1:144,17,16;--semi-red-2:180,32,25;--semi-red-3:215,51,36;--semi-red-4:251,73,50;--semi-red-5:252,114,90;--semi-red-6:253,153,131;--semi-red-7:253,190,172;--semi-red-8:254,224,213;--semi-red-9:255,243,239;--semi-pink-0:92,7,48;--semi-pink-1:128,14,65;--semi-pink-2:164,23,81;--semi-pink-3:199,34,97;--semi-pink-4:235,47,113;--semi-pink-5:239,86,134;--semi-pink-6:243,126,159;--semi-pink-7:247,168,188;--semi-pink-8:251,211,220;--semi-pink-9:253,238,241;--semi-purple-0:74,16,97;--semi-purple-1:94,23,118;--semi-purple-2:115,31,138;--semi-purple-3:137,40,159;--semi-purple-4:160,51,179;--semi-purple-5:181,83,194;--semi-purple-6:202,120,209;--semi-purple-7:221,160,225;--semi-purple-8:239,206,240;--semi-purple-9:247,235,247;--semi-violet-0:64,27,119;--semi-violet-1:76,36,140;--semi-violet-2:88,46,160;--semi-violet-3:100,57,181;--semi-violet-4:114,70,201;--semi-violet-5:136,101,212;--semi-violet-6:162,136,223;--semi-violet-7:190,173,233;--semi-violet-8:221,212,244;--semi-violet-9:241,238,250;--semi-indigo-0:23,30,101;--semi-indigo-1:32,41,122;--semi-indigo-2:41,54,142;--semi-indigo-3:52,68,163;--semi-indigo-4:64,83,183;--semi-indigo-5:95,113,197;--semi-indigo-6:129,145,212;--semi-indigo-7:167,180,226;--semi-indigo-8:209,216,241;--semi-indigo-9:237,239,248;--semi-blue-0:5,49,112;--semi-blue-1:10,70,148;--semi-blue-2:19,92,184;--semi-blue-3:29,117,219;--semi-blue-4:41,144,255;--semi-blue-5:84,169,255;--semi-blue-6:127,193,255;--semi-blue-7:169,215,255;--semi-blue-8:212,236,255;--semi-blue-9:239,248,255;--semi-light-blue-0:0,55,97;--semi-light-blue-1:0,77,133;--semi-light-blue-2:3,102,169;--semi-light-blue-3:10,129,204;--semi-light-blue-4:19,159,240;--semi-light-blue-5:64,180,243;--semi-light-blue-6:110,200,246;--semi-light-blue-7:157,220,249;--semi-light-blue-8:206,238,252;--semi-light-blue-9:235,248,254;--semi-cyan-0:4,52,61;--semi-cyan-1:7,79,92;--semi-cyan-2:10,108,123;--semi-cyan-3:14,137,153;--semi-cyan-4:19,168,184;--semi-cyan-5:56,187,198;--semi-cyan-6:98,205,212;--semi-cyan-7:145,223,227;--semi-cyan-8:198,239,241;--semi-cyan-9:231,247,248;--semi-teal-0:2,60,57;--semi-teal-1:4,90,85;--semi-teal-2:7,119,111;--semi-teal-3:10,149,136;--semi-teal-4:14,179,161;--semi-teal-5:51,194,176;--semi-teal-6:94,209,193;--semi-teal-7:142,225,211;--semi-teal-8:196,240,232;--semi-teal-9:230,247,244;--semi-green-0:18,60,25;--semi-green-1:28,90,37;--semi-green-2:39,119,49;--semi-green-3:50,149,61;--semi-green-4:62,179,73;--semi-green-5:93,194,100;--semi-green-6:127,209,132;--semi-green-7:166,225,168;--semi-green-8:208,240,209;--semi-green-9:236,247,236;--semi-light-green-0:38,61,19;--semi-light-green-1:59,92,29;--semi-light-green-2:81,123,40;--semi-light-green-3:103,153,52;--semi-light-green-4:127,184,64;--semi-light-green-5:151,198,95;--semi-light-green-6:176,212,129;--semi-light-green-7:201,227,167;--semi-light-green-8:228,241,209;--semi-light-green-9:243,248,237;--semi-lime-0:49,70,3;--semi-lime-1:75,105,5;--semi-lime-2:103,141,9;--semi-lime-3:132,176,12;--semi-lime-4:162,211,17;--semi-lime-5:174,220,58;--semi-lime-6:189,229,102;--semi-lime-7:207,237,150;--semi-lime-8:229,246,201;--semi-lime-9:243,251,233;--semi-yellow-0:84,73,3;--semi-yellow-1:126,108,6;--semi-yellow-2:168,142,10;--semi-yellow-3:210,175,15;--semi-yellow-4:252,206,20;--semi-yellow-5:253,222,67;--semi-yellow-6:253,235,113;--semi-yellow-7:254,245,160;--semi-yellow-8:254,251,208;--semi-yellow-9:255,254,236;--semi-amber-0:81,46,9;--semi-amber-1:121,75,15;--semi-amber-2:161,107,22;--semi-amber-3:202,143,30;--semi-amber-4:242,183,38;--semi-amber-5:245,202,80;--semi-amber-6:247,219,122;--semi-amber-7:250,234,166;--semi-amber-8:252,246,210;--semi-amber-9:254,251,237;--semi-orange-0:85,31,3;--semi-orange-1:128,53,6;--semi-orange-2:170,80,10;--semi-orange-3:213,111,15;--semi-orange-4:255,146,20;--semi-orange-5:255,174,67;--semi-orange-6:255,199,114;--semi-orange-7:255,221,161;--semi-orange-8:255,239,208;--semi-orange-9:255,249,237;--semi-grey-0:28,31,35;--semi-grey-1:46,50,56;--semi-grey-2:65,70,76;--semi-grey-3:85,91,97;--semi-grey-4:107,112,117;--semi-grey-5:136,141,146;--semi-grey-6:167,171,176;--semi-grey-7:198,202,205;--semi-grey-8:230,232,234;--semi-grey-9:249,249,249;--semi-white:255,255,255;--semi-black:0,0,0}#root,#root[theme-mode=dark].semi-always-light{--semi-color-white:rgba(var(--semi-white),1);--semi-color-black:rgba(var(--semi-black),1);--semi-color-primary:rgba(var(--semi-blue-5),1);--semi-color-primary-hover:rgba(var(--semi-blue-6),1);--semi-color-primary-active:rgba(var(--semi-blue-7),1);--semi-color-primary-disabled:rgba(var(--semi-blue-2),1);--semi-color-primary-light-default:rgba(var(--semi-blue-0),1);--semi-color-primary-light-hover:rgba(var(--semi-blue-1),1);--semi-color-primary-light-active:rgba(var(--semi-blue-2),1);--semi-color-secondary:rgba(var(--semi-light-blue-5),1);--semi-color-secondary-hover:rgba(var(--semi-light-blue-6),1);--semi-color-secondary-active:rgba(var(--semi-light-blue-7),1);--semi-color-secondary-disabled:rgba(var(--semi-light-blue-2),1);--semi-color-secondary-light-default:rgba(var(--semi-light-blue-0),1);--semi-color-secondary-light-hover:rgba(var(--semi-light-blue-1),1);--semi-color-secondary-light-active:rgba(var(--semi-light-blue-2),1);--semi-color-tertiary:rgba(var(--semi-grey-5),1);--semi-color-tertiary-hover:rgba(var(--semi-grey-6),1);--semi-color-tertiary-active:rgba(var(--semi-grey-7),1);--semi-color-tertiary-light-default:rgba(var(--semi-grey-0),1);--semi-color-tertiary-light-hover:rgba(var(--semi-grey-1),1);--semi-color-tertiary-light-active:rgba(var(--semi-grey-2),1);--semi-color-default:rgba(var(--semi-grey-0),1);--semi-color-default-hover:rgba(var(--semi-grey-1),1);--semi-color-default-active:rgba(var(--semi-grey-2),1);--semi-color-info:rgba(var(--semi-blue-5),1);--semi-color-info-hover:rgba(var(--semi-blue-6),1);--semi-color-info-active:rgba(var(--semi-blue-7),1);--semi-color-info-disabled:rgba(var(--semi-blue-2),1);--semi-color-info-light-default:rgba(var(--semi-blue-0),1);--semi-color-info-light-hover:rgba(var(--semi-blue-1),1);--semi-color-info-light-active:rgba(var(--semi-blue-2),1);--semi-color-success:rgba(var(--semi-green-5),1);--semi-color-success-hover:rgba(var(--semi-green-6),1);--semi-color-success-active:rgba(var(--semi-green-7),1);--semi-color-success-disabled:rgba(var(--semi-green-2),1);--semi-color-success-light-default:rgba(var(--semi-green-0),1);--semi-color-success-light-hover:rgba(var(--semi-green-1),1);--semi-color-success-light-active:rgba(var(--semi-green-2),1);--semi-color-danger:rgba(var(--semi-red-5),1);--semi-color-danger-hover:rgba(var(--semi-red-6),1);--semi-color-danger-active:rgba(var(--semi-red-7),1);--semi-color-danger-light-default:rgba(var(--semi-red-0),1);--semi-color-danger-light-hover:rgba(var(--semi-red-1),1);--semi-color-danger-light-active:rgba(var(--semi-red-2),1);--semi-color-warning:rgba(var(--semi-orange-5),1);--semi-color-warning-hover:rgba(var(--semi-orange-6),1);--semi-color-warning-active:rgba(var(--semi-orange-7),1);--semi-color-warning-light-default:rgba(var(--semi-orange-0),1);--semi-color-warning-light-hover:rgba(var(--semi-orange-1),1);--semi-color-warning-light-active:rgba(var(--semi-orange-2),1);--semi-color-focus-border:rgba(var(--semi-blue-5),1);--semi-color-disabled-text:rgba(var(--semi-grey-9),.35);--semi-color-disabled-border:rgba(var(--semi-grey-1),1);--semi-color-disabled-bg:rgba(var(--semi-grey-1),1);--semi-color-disabled-fill:rgba(var(--semi-grey-8),.04);--semi-color-shadow:rgba(var(--semi-black),.04);--semi-color-link:rgba(var(--semi-blue-5),1);--semi-color-link-hover:rgba(var(--semi-blue-6),1);--semi-color-link-active:rgba(var(--semi-blue-7),1);--semi-color-link-visited:rgba(var(--semi-blue-5),1);--semi-color-border:rgba(var(--semi-grey-9),.08);--semi-color-nav-bg:rgba(var(--semi-white),1);--semi-color-overlay-bg:rgba(22,22,26,.6);--semi-color-fill-0:rgba(var(--semi-grey-8),.05);--semi-color-fill-1:rgba(var(--semi-grey-8),.09);--semi-color-fill-2:rgba(var(--semi-grey-8),.13);--semi-color-bg-0:rgba(var(--semi-white),1);--semi-color-bg-1:rgba(var(--semi-white),1);--semi-color-bg-2:rgba(var(--semi-white),1);--semi-color-bg-3:rgba(var(--semi-white),1);--semi-color-bg-4:rgba(var(--semi-white),1);--semi-color-text-0:rgba(var(--semi-grey-9),1);--semi-color-text-1:rgba(var(--semi-grey-9),.8);--semi-color-text-2:rgba(var(--semi-grey-9),.62);--semi-color-text-3:rgba(var(--semi-grey-9),.35);--semi-shadow-elevated:001pxrgba(0,0,0,.3),04px14pxrgba(0,0,0,.1);--semi-border-radius-extra-small:3px;--semi-border-radius-small:3px;--semi-border-radius-medium:6px;--semi-border-radius-large:12px;--semi-border-radius-circle:50%;--semi-border-radius-full:9999px;--semi-color-highlight-bg:rgba(var(--semi-yellow-4),1);--semi-color-highlight:rgba(var(--semi-black),1)}#root,#root.semi-always-dark,#root[theme-mode=dark],#root[theme-mode=dark].semi-always-light{-webkit-font-smoothing:antialiased;font-family:Inter,-apple-system,BlinkMacSystemFont,SegoeUI,PingFangSC,HiraginoSansGB,MicrosoftYaHei,HelveticaNeue,Helvetica,Arial,sans-serif}#root.semi-always-dark,#root[theme-mode=dark]{--semi-color-white:#e4e7f5;--semi-color-black:rgba(var(--semi-black),1);--semi-color-primary:rgba(var(--semi-blue-5),1);--semi-color-primary-hover:rgba(var(--semi-blue-6),1);--semi-color-primary-active:rgba(var(--semi-blue-7),1);--semi-color-primary-disabled:rgba(var(--semi-blue-2),1);--semi-color-primary-light-default:rgba(var(--semi-blue-5),.2);--semi-color-primary-light-hover:rgba(var(--semi-blue-5),.3);--semi-color-primary-light-active:rgba(var(--semi-blue-5),.4);--semi-color-secondary:rgba(var(--semi-light-blue-5),1);--semi-color-secondary-hover:rgba(var(--semi-light-blue-6),1);--semi-color-secondary-active:rgba(var(--semi-light-blue-7),1);--semi-color-secondary-disabled:rgba(var(--semi-light-blue-2),1);--semi-color-secondary-light-default:rgba(var(--semi-light-blue-5),.2);--semi-color-secondary-light-hover:rgba(var(--semi-light-blue-5),.3);--semi-color-secondary-light-active:rgba(var(--semi-light-blue-5),.4);--semi-color-tertiary:rgba(var(--semi-grey-5),1);--semi-color-tertiary-hover:rgba(var(--semi-grey-6),1);--semi-color-tertiary-active:rgba(var(--semi-grey-7),1);--semi-color-tertiary-light-default:rgba(var(--semi-grey-5),.2);--semi-color-tertiary-light-hover:rgba(var(--semi-grey-5),.3);--semi-color-tertiary-light-active:rgba(var(--semi-grey-5),.4);--semi-color-default:rgba(var(--semi-grey-0),1);--semi-color-default-hover:rgba(var(--semi-grey-1),1);--semi-color-default-active:rgba(var(--semi-grey-2),1);--semi-color-info:rgba(var(--semi-blue-5),1);--semi-color-info-hover:rgba(var(--semi-blue-6),1);--semi-color-info-active:rgba(var(--semi-blue-7),1);--semi-color-info-disabled:rgba(var(--semi-blue-2),1);--semi-color-info-light-default:rgba(var(--semi-blue-5),.2);--semi-color-info-light-hover:rgba(var(--semi-blue-5),.3);--semi-color-info-light-active:rgba(var(--semi-blue-5),.4);--semi-color-success:rgba(var(--semi-green-5),1);--semi-color-success-hover:rgba(var(--semi-green-6),1);--semi-color-success-active:rgba(var(--semi-green-7),1);--semi-color-success-disabled:rgba(var(--semi-green-2),1);--semi-color-success-light-default:rgba(var(--semi-green-5),.2);--semi-color-success-light-hover:rgba(var(--semi-green-5),.3);--semi-color-success-light-active:rgba(var(--semi-green-5),.4);--semi-color-danger:rgba(var(--semi-red-5),1);--semi-color-danger-hover:rgba(var(--semi-red-6),1);--semi-color-danger-active:rgba(var(--semi-red-7),1);--semi-color-danger-light-default:rgba(var(--semi-red-5),.2);--semi-color-danger-light-hover:rgba(var(--semi-red-5),.3);--semi-color-danger-light-active:rgba(var(--semi-red-5),.4);--semi-color-warning:rgba(var(--semi-orange-5),1);--semi-color-warning-hover:rgba(var(--semi-orange-6),1);--semi-color-warning-active:rgba(var(--semi-orange-7),1);--semi-color-warning-light-default:rgba(var(--semi-orange-5),.2);--semi-color-warning-light-hover:rgba(var(--semi-orange-5),.3);--semi-color-warning-light-active:rgba(var(--semi-orange-5),.4);--semi-color-focus-border:rgba(var(--semi-blue-5),1);--semi-color-disabled-text:rgba(var(--semi-grey-9),.35);--semi-color-disabled-border:rgba(var(--semi-grey-1),1);--semi-color-disabled-bg:rgba(var(--semi-grey-1),1);--semi-color-disabled-fill:rgba(var(--semi-grey-8),.04);--semi-color-link:rgba(var(--semi-blue-5),1);--semi-color-link-hover:rgba(var(--semi-blue-6),1);--semi-color-link-active:rgba(var(--semi-blue-7),1);--semi-color-link-visited:rgba(var(--semi-blue-5),1);--semi-color-nav-bg:#232429;--semi-shadow-elevated:inset0001pxhsla(0,0%,100%,.1),04px14pxrgba(0,0,0,.25);--semi-color-overlay-bg:rgba(22,22,26,.6);--semi-color-fill-0:rgba(var(--semi-white),.12);--semi-color-fill-1:rgba(var(--semi-white),.16);--semi-color-fill-2:rgba(var(--semi-white),.20);--semi-color-border:rgba(var(--semi-white),.08);--semi-color-shadow:rgba(var(--semi-black),.04);--semi-color-bg-0:#16161a;--semi-color-bg-1:#232429;--semi-color-bg-2:#35363c;--semi-color-bg-3:#43444a;--semi-color-bg-4:#4f5159;--semi-color-text-0:rgba(var(--semi-grey-9),1);--semi-color-text-1:rgba(var(--semi-grey-9),.8);--semi-color-text-2:rgba(var(--semi-grey-9),.6);--semi-color-text-3:rgba(var(--semi-grey-9),.35);--semi-border-radius-extra-small:3px;--semi-border-radius-small:3px;--semi-border-radius-medium:6px;--semi-border-radius-large:12px;--semi-border-radius-circle:50%;--semi-border-radius-full:9999px;--semi-color-highlight-bg:rgba(var(--semi-yellow-2),1);--semi-color-highlight:rgba(var(--semi-white),1)}.semi-light-scrollbar::-webkit-scrollbar,.semi-light-scrollbar::-webkit-scrollbar{height:8px;width:8px}.semi-light-scrollbar::-webkit-scrollbar-track,.semi-light-scrollbar::-webkit-scrollbar-track{background:transparent}.semi-light-scrollbar::-webkit-scrollbar-corner,.semi-light-scrollbar::-webkit-scrollbar-corner{background-color:transparent}.semi-light-scrollbar::-webkit-scrollbar-thumb,.semi-light-scrollbar::-webkit-scrollbar-thumb{background:transparent;border-radius:6px;-webkit-transition:all1s;transition:all1s}.semi-light-scrollbar:hover::-webkit-scrollbar-thumb,.semi-light-scrollbar:hover::-webkit-scrollbar-thumb{background:var(--semi-color-fill-2)}.semi-light-scrollbar::-webkit-scrollbar-thumb:hover,.semi-light-scrollbar::-webkit-scrollbar-thumb:hover{background:var(--semi-color-fill-1)}.semi-avatar{align-items:center;display:inline-flex;justify-content:center;overflow:hidden;position:relative;text-align:center;vertical-align:middle;white-space:nowrap}.semi-avatar:focus-visible{outline:2pxsolidvar(--semi-color-primary-light-active)}.semi-avatar-focus{outline:2pxsolidvar(--semi-color-primary-light-active)}.semi-avatar-no-focus-visible:focus-visible{outline:none}.semi-avatar.semi-avatar-label{align-items:center;display:flex;font-family:Inter,-apple-system,BlinkMacSystemFont,SegoeUI,PingFangSC,HiraginoSansGB,MicrosoftYaHei,HelveticaNeue,Helvetica,Arial,sans-serif;font-size:14px;font-weight:600;line-height:20px}.semi-avatar-content{-webkit-user-select:none;user-select:none}.semi-avatar-extra-extra-small{border-radius:3px;height:20px;width:20px}.semi-avatar-extra-extra-small.semi-avatar-content{-webkit-transform:scale(.8);transform:scale(.8);-webkit-transform-origin:center;transform-origin:center}.semi-avatar-extra-extra-small.semi-avatar-label{font-size:10px;line-height:15px}.semi-avatar-extra-small{border-radius:3px;height:24px;width:24px}.semi-avatar-extra-small.semi-avatar-content{-webkit-transform:scale(.8);transform:scale(.8);-webkit-transform-origin:center;transform-origin:center}.semi-avatar-extra-small.semi-avatar-label{font-size:10px;line-height:15px}.semi-avatar-small{border-radius:3px;height:32px;width:32px}.semi-avatar-small.semi-avatar-label{font-family:Inter,-apple-system,BlinkMacSystemFont,SegoeUI,PingFangSC,HiraginoSansGB,MicrosoftYaHei,HelveticaNeue,Helvetica,Arial,sans-serif;font-size:12px;line-height:16px}.semi-avatar-default{border-radius:3px;height:40px;width:40px}.semi-avatar-default.semi-avatar-label{font-family:Inter,-apple-system,BlinkMacSystemFont,SegoeUI,PingFangSC,HiraginoSansGB,MicrosoftYaHei,HelveticaNeue,Helvetica,Arial,sans-serif;font-size:18px;line-height:24px}.semi-avatar-medium{border-radius:3px;height:48px;width:48px}.semi-avatar-medium.semi-avatar-label{font-family:Inter,-apple-system,BlinkMacSystemFont,SegoeUI,PingFangSC,HiraginoSansGB,MicrosoftYaHei,HelveticaNeue,Helvetica,Arial,sans-serif;font-size:20px;line-height:28px}.semi-avatar-large{border-radius:6px;height:72px;width:72px}.semi-avatar-large.semi-avatar-label{font-family:Inter,-apple-system,BlinkMacSystemFont,SegoeUI,PingFangSC,HiraginoSansGB,MicrosoftYaHei,HelveticaNeue,Helvetica,Arial,sans-serif;font-size:32px;line-height:44px}.semi-avatar-extra-large{border-radius:12px;height:128px;width:128px}.semi-avatar-extra-large.semi-avatar-label{font-size:64px;line-height:77px}.semi-avatar-circle{border-radius:var(--semi-border-radius-circle)}.semi-avatar-image{background-color:initial}.semi-avatar>img{display:block;height:100%;object-fit:cover;width:100%}.semi-avatar-hover{height:100%;left:0;position:absolute;top:0;width:100%}.semi-avatar:hover{cursor:pointer}.semi-avatar-group{display:inline-block}.semi-avatar-group.semi-avatar{box-sizing:border-box}.semi-avatar-group.semi-avatar:first-child{margin-left:0}.semi-avatar-group.semi-avatar-extra-large{border:3pxsolidvar(--semi-color-bg-1);margin-left:-32px}.semi-avatar-group.semi-avatar-large{border:3pxsolidvar(--semi-color-bg-1);margin-left:-18px}.semi-avatar-group.semi-avatar-default,.semi-avatar-group.semi-avatar-medium,.semi-avatar-group.semi-avatar-small{border:2pxsolidvar(--semi-color-bg-1);margin-left:-12px}.semi-avatar-group.semi-avatar-extra-small{border:1pxsolidvar(--semi-color-bg-1);margin-left:-10px}.semi-avatar-group.semi-avatar-extra-extra-small{border:1pxsolidvar(--semi-color-bg-1);margin-left:-4px}.semi-avatar-group.semi-avatar-item-start-0{z-index:100}.semi-avatar-group.semi-avatar-item-end-0{z-index:80}.semi-avatar-group.semi-avatar-item-start-1{z-index:99}.semi-avatar-group.semi-avatar-item-end-1{z-index:81}.semi-avatar-group.semi-avatar-item-start-2{z-index:98}.semi-avatar-group.semi-avatar-item-end-2{z-index:82}.semi-avatar-group.semi-avatar-item-start-3{z-index:97}.semi-avatar-group.semi-avatar-item-end-3{z-index:83}.semi-avatar-group.semi-avatar-item-start-4{z-index:96}.semi-avatar-group.semi-avatar-item-end-4{z-index:84}.semi-avatar-group.semi-avatar-item-start-5{z-index:95}.semi-avatar-group.semi-avatar-item-end-5{z-index:85}.semi-avatar-group.semi-avatar-item-start-6{z-index:94}.semi-avatar-group.semi-avatar-item-end-6{z-index:86}.semi-avatar-group.semi-avatar-item-start-7{z-index:93}.semi-avatar-group.semi-avatar-item-end-7{z-index:87}.semi-avatar-group.semi-avatar-item-start-8{z-index:92}.semi-avatar-group.semi-avatar-item-end-8{z-index:88}.semi-avatar-group.semi-avatar-item-start-9{z-index:91}.semi-avatar-group.semi-avatar-item-end-9{z-index:89}.semi-avatar-group.semi-avatar-item-end-10,.semi-avatar-group.semi-avatar-item-start-10{z-index:90}.semi-avatar-group.semi-avatar-item-start-11{z-index:89}.semi-avatar-group.semi-avatar-item-end-11{z-index:91}.semi-avatar-group.semi-avatar-item-start-12{z-index:88}.semi-avatar-group.semi-avatar-item-end-12{z-index:92}.semi-avatar-group.semi-avatar-item-start-13{z-index:87}.semi-avatar-group.semi-avatar-item-end-13{z-index:93}.semi-avatar-group.semi-avatar-item-start-14{z-index:86}.semi-avatar-group.semi-avatar-item-end-14{z-index:94}.semi-avatar-group.semi-avatar-item-start-15{z-index:85}.semi-avatar-group.semi-avatar-item-end-15{z-index:95}.semi-avatar-group.semi-avatar-item-start-16{z-index:84}.semi-avatar-group.semi-avatar-item-end-16{z-index:96}.semi-avatar-group.semi-avatar-item-start-17{z-index:83}.semi-avatar-group.semi-avatar-item-end-17{z-index:97}.semi-avatar-group.semi-avatar-item-start-18{z-index:82}.semi-avatar-group.semi-avatar-item-end-18{z-index:98}.semi-avatar-group.semi-avatar-item-start-19{z-index:81}.semi-avatar-group.semi-avatar-item-end-19{z-index:99}.semi-avatar-group.semi-avatar-item-start-20{z-index:80}.semi-avatar-group.semi-avatar-item-end-20{z-index:100}.semi-avatar-group.semi-avatar-item-more{background-color:rgba(var(--semi-grey-5),1)}.semi-avatar-amber{background-color:rgba(var(--semi-amber-3),1);color:rgba(var(--semi-white),1)}.semi-avatar-blue{background-color:rgba(var(--semi-blue-3),1);color:rgba(var(--semi-white),1)}.semi-avatar-cyan{background-color:rgba(var(--semi-cyan-3),1);color:rgba(var(--semi-white),1)}.semi-avatar-green{background-color:rgba(var(--semi-green-3),1);color:rgba(var(--semi-white),1)}.semi-avatar-grey{background-color:rgba(var(--semi-grey-3),1);color:rgba(var(--semi-white),1)}.semi-avatar-indigo{background-color:rgba(var(--semi-indigo-3),1);color:rgba(var(--semi-white),1)}.semi-avatar-light-blue{background-color:rgba(var(--semi-light-blue-3),1);color:rgba(var(--semi-white),1)}.semi-avatar-light-green{background-color:rgba(var(--semi-light-green-3),1);color:rgba(var(--semi-white),1)}.semi-avatar-lime{background-color:rgba(var(--semi-lime-3),1);color:rgba(var(--semi-white),1)}.semi-avatar-orange{background-color:rgba(var(--semi-orange-3),1);color:rgba(var(--semi-white),1)}.semi-avatar-pink{background-color:rgba(var(--semi-pink-3),1);color:rgba(var(--semi-white),1)}.semi-avatar-purple{background-color:rgba(var(--semi-purple-3),1);color:rgba(var(--semi-white),1)}.semi-avatar-red{background-color:rgba(var(--semi-red-3),1);color:rgba(var(--semi-white),1)}.semi-avatar-teal{background-color:rgba(var(--semi-teal-3),1);color:rgba(var(--semi-white),1)}.semi-avatar-violet{background-color:rgba(var(--semi-violet-3),1);color:rgba(var(--semi-white),1)}.semi-avatar-yellow{background-color:rgba(var(--semi-yellow-3),1);color:rgba(var(--semi-white),1)}.semi-portal-rtl.semi-avatar,.semi-rtl.semi-avatar{direction:rtl}.semi-portal-rtl.semi-avatar-extra-extra-small.semi-avatar-content,.semi-portal-rtl.semi-avatar-extra-small.semi-avatar-content,.semi-rtl.semi-avatar-extra-extra-small.semi-avatar-content,.semi-rtl.semi-avatar-extra-small.semi-avatar-content{-webkit-transform:scale(.8);transform:scale(.8)}.semi-portal-rtl.semi-avatar-hover,.semi-rtl.semi-avatar-hover{left:auto;right:0}.semi-portal-rtl.semi-avatar-group,.semi-rtl.semi-avatar-group{direction:rtl}.semi-portal-rtl.semi-avatar-group.semi-avatar:first-child,.semi-rtl.semi-avatar-group.semi-avatar:first-child{margin-left:auto;margin-right:0}.semi-portal-rtl.semi-avatar-group.semi-avatar-extra-large,.semi-rtl.semi-avatar-group.semi-avatar-extra-large{margin-left:auto;margin-right:-32px}.semi-portal-rtl.semi-avatar-group.semi-avatar-large,.semi-rtl.semi-avatar-group.semi-avatar-large{margin-left:auto;margin-right:-18px}.semi-portal-rtl.semi-avatar-group.semi-avatar-medium,.semi-portal-rtl.semi-avatar-group.semi-avatar-small,.semi-rtl.semi-avatar-group.semi-avatar-medium,.semi-rtl.semi-avatar-group.semi-avatar-small{margin-left:auto;margin-right:-12px}.semi-portal-rtl.semi-avatar-group.semi-avatar-extra-small,.semi-rtl.semi-avatar-group.semi-avatar-extra-small{margin-left:auto;margin-right:-10px}.semi-portal-rtl.semi-avatar-group.semi-avatar-extra-extra-small,.semi-rtl.semi-avatar-group.semi-avatar-extra-extra-small{margin-left:auto;margin-right:-4px}@-webkit-keyframessemi-popover-zoomIn{0%{opacity:0;-webkit-transform:scale(.8);transform:scale(.8)}50%{opacity:1}}@keyframessemi-popover-zoomIn{0%{opacity:0;-webkit-transform:scale(.8);transform:scale(.8)}50%{opacity:1}}@-webkit-keyframessemi-popover-zoomOut{0%{opacity:1}60%{opacity:0;-webkit-transform:scale(.8);transform:scale(.8)}to{opacity:0}}@keyframessemi-popover-zoomOut{0%{opacity:1}60%{opacity:0;-webkit-transform:scale(.8);transform:scale(.8)}to{opacity:0}}.semi-popover-wrapper{background-color:var(--semi-color-bg-3);border-radius:var(--semi-border-radius-medium);box-shadow:var(--semi-shadow-elevated);font-family:Inter,-apple-system,BlinkMacSystemFont,SegoeUI,PingFangSC,HiraginoSansGB,MicrosoftYaHei,HelveticaNeue,Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;opacity:0;position:relative;z-index:1030}.semi-popover-wrapper-show{opacity:1}.semi-popover-trigger{display:inline-block;height:auto;width:auto}.semi-popover-title{border-bottom:1pxsolidvar(--semi-color-border);padding:8px}.semi-popover-confirm{position:absolute}.semi-popover-with-arrow{box-sizing:border-box;padding:12px}.semi-popover-animation-show{-webkit-animation:semi-popover-zoomIn.1scubic-bezier(.215,.61,.355,1);animation:semi-popover-zoomIn.1scubic-bezier(.215,.61,.355,1)}.semi-popover-animation-hide{-webkit-animation:semi-popover-zoomOut.1scubic-bezier(.215,.61,.355,1);animation:semi-popover-zoomOut.1scubic-bezier(.215,.61,.355,1)}.semi-popover-wrapper.semi-popover-icon-arrow{color:inherit;height:8px;position:absolute;width:24px}.semi-popover-wrapper[x-placement=top].semi-popover-icon-arrow{bottom:-7px;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.semi-popover-wrapper[x-placement=top].semi-popover-with-arrow,.semi-popover-wrapper[x-placement=top].semi-popover-with-arrow{min-width:36px}.semi-popover-wrapper[x-placement=topLeft].semi-popover-icon-arrow{bottom:-7px;left:6px}.semi-popover-wrapper[x-placement=topLeft].semi-popover-with-arrow,.semi-popover-wrapper[x-placement=topLeft].semi-popover-with-arrow{min-width:36px}.semi-popover-wrapper[x-placement=topRight].semi-popover-icon-arrow{bottom:-7px;right:6px}.semi-popover-wrapper[x-placement=topRight].semi-popover-with-arrow,.semi-popover-wrapper[x-placement=topRight].semi-popover-with-arrow{min-width:36px}.semi-popover-wrapper[x-placement=leftTop].semi-popover-icon-arrow{height:24px;right:-7px;top:6px;width:8px}.semi-popover-wrapper[x-placement=leftTop].semi-popover-with-arrow,.semi-popover-wrapper[x-placement=leftTop].semi-popover-with-arrow{min-height:36px}.semi-popover-wrapper[x-placement=left].semi-popover-icon-arrow{height:24px;right:-7px;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);width:8px}.semi-popover-wrapper[x-placement=left].semi-popover-with-arrow,.semi-popover-wrapper[x-placement=left].semi-popover-with-arrow{min-height:36px}.semi-popover-wrapper[x-placement=leftBottom].semi-popover-icon-arrow{bottom:6px;height:24px;right:-7px;width:8px}.semi-popover-wrapper[x-placement=leftBottom].semi-popover-with-arrow,.semi-popover-wrapper[x-placement=leftBottom].semi-popover-with-arrow{min-height:36px}.semi-popover-wrapper[x-placement=rightTop].semi-popover-icon-arrow{height:24px;left:-7px;top:6px;-webkit-transform:rotate(180deg);transform:rotate(180deg);width:8px}.semi-popover-wrapper[x-placement=rightTop].semi-popover-with-arrow,.semi-popover-wrapper[x-placement=rightTop].semi-popover-with-arrow{min-height:36px}.semi-popover-wrapper[x-placement=right].semi-popover-icon-arrow{height:24px;left:-7px;top:50%;-webkit-transform:translateY(-50%)rotate(180deg);transform:translateY(-50%)rotate(180deg);width:8px}.semi-popover-wrapper[x-placement=right].semi-popover-with-arrow,.semi-popover-wrapper[x-placement=right].semi-popover-with-arrow{min-height:36px}.semi-popover-wrapper[x-placement=rightBottom].semi-popover-icon-arrow{bottom:6px;height:24px;left:-7px;-webkit-transform:rotate(180deg);transform:rotate(180deg);width:8px}.semi-popover-wrapper[x-placement=rightBottom].semi-popover-with-arrow,.semi-popover-wrapper[x-placement=rightBottom].semi-popover-with-arrow{min-height:36px}.semi-popover-wrapper[x-placement=bottomLeft].semi-popover-icon-arrow{left:6px;top:-7px;-webkit-transform:rotate(180deg);transform:rotate(180deg)}.semi-popover-wrapper[x-placement=bottomLeft].semi-popover-with-arrow,.semi-popover-wrapper[x-placement=bottomLeft].semi-popover-with-arrow{min-width:36px}.semi-popover-wrapper[x-placement=bottom].semi-popover-icon-arrow{left:50%;top:-7px;-webkit-transform:translateX(-50%)rotate(180deg);transform:translateX(-50%)rotate(180deg)}.semi-popover-wrapper[x-placement=bottom].semi-popover-with-arrow,.semi-popover-wrapper[x-placement=bottom].semi-popover-with-arrow{min-width:36px}.semi-popover-wrapper[x-placement=bottomRight].semi-popover-icon-arrow{right:6px;top:-7px;-webkit-transform:rotate(180deg);transform:rotate(180deg)}.semi-popover-wrapper[x-placement=bottomRight].semi-popover-with-arrow,.semi-popover-wrapper[x-placement=bottomRight].semi-popover-with-arrow{min-width:36px}.semi-popover.semi-popover-rtl{direction:rtl}@-webkit-keyframessemi-tooltip-zoomIn{0%{opacity:0;-webkit-transform:scale(.8);transform:scale(.8)}50%{opacity:1}}@keyframessemi-tooltip-zoomIn{0%{opacity:0;-webkit-transform:scale(.8);transform:scale(.8)}50%{opacity:1}}@-webkit-keyframessemi-tooltip-bounceIn{0%{opacity:0;-webkit-transform:scale(.6);transform:scale(.6)}70%{opacity:1;-webkit-transform:scale(1.01);transform:scale(1.01)}to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1);opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframessemi-tooltip-bounceIn{0%{opacity:0;-webkit-transform:scale(.6);transform:scale(.6)}70%{opacity:1;-webkit-transform:scale(1.01);transform:scale(1.01)}to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1);opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@-webkit-keyframessemi-tooltip-zoomOut{0%{opacity:1}60%{opacity:0;-webkit-transform:scale(.8);transform:scale(.8)}to{opacity:0}}@keyframessemi-tooltip-zoomOut{0%{opacity:1}60%{opacity:0;-webkit-transform:scale(.8);transform:scale(.8)}to{opacity:0}}.semi-tooltip-wrapper{background-color:rgba(var(--semi-grey-7),1);border-radius:var(--semi-border-radius-medium);color:var(--semi-color-bg-0);font-family:Inter,-apple-system,BlinkMacSystemFont,SegoeUI,PingFangSC,HiraginoSansGB,MicrosoftYaHei,HelveticaNeue,Helvetica,Arial,sans-serif;font-size:14px;left:0;line-height:20px;max-width:240px;opacity:0;padding:8px12px;position:relative;top:0}.semi-tooltip-wrapper-show{opacity:1}.semi-tooltip-trigger{display:inline-block;height:auto;width:auto}.semi-tooltip-with-arrow{align-items:center;box-sizing:border-box;display:flex;justify-content:center}.semi-tooltip-animation-show{-webkit-animation:semi-tooltip-zoomIn.1scubic-bezier(.215,.61,.355,1);animation:semi-tooltip-zoomIn.1scubic-bezier(.215,.61,.355,1)}.semi-tooltip-animation-hide{-webkit-animation:semi-tooltip-zoomOut.1scubic-bezier(.215,.61,.355,1);animation:semi-tooltip-zoomOut.1scubic-bezier(.215,.61,.355,1)}.semi-tooltip-wrapper.semi-tooltip-icon-arrow{color:rgba(var(--semi-grey-7),1);height:7px;position:absolute;width:24px}.semi-tooltip-wrapper[x-placement=top].semi-tooltip-icon-arrow{bottom:-6px;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.semi-tooltip-wrapper[x-placement=top].semi-tooltip-with-arrow,.semi-tooltip-wrapper[x-placement=top].semi-tooltip-with-arrow{min-width:36px}.semi-tooltip-wrapper[x-placement=topLeft].semi-tooltip-icon-arrow{bottom:-6px;left:6px}.semi-tooltip-wrapper[x-placement=topLeft].semi-tooltip-with-arrow,.semi-tooltip-wrapper[x-placement=topLeft].semi-tooltip-with-arrow{min-width:36px}.semi-tooltip-wrapper[x-placement=topRight].semi-tooltip-icon-arrow{bottom:-6px;right:6px}.semi-tooltip-wrapper[x-placement=topRight].semi-tooltip-with-arrow,.semi-tooltip-wrapper[x-placement=topRight].semi-tooltip-with-arrow{min-width:36px}.semi-tooltip-wrapper[x-placement=leftTop].semi-tooltip-icon-arrow{height:24px;right:-6px;top:5px;width:7px}.semi-tooltip-wrapper[x-placement=leftTop].semi-tooltip-with-arrow,.semi-tooltip-wrapper[x-placement=leftTop].semi-tooltip-with-arrow{min-height:34px}.semi-tooltip-wrapper[x-placement=left].semi-tooltip-icon-arrow{height:24px;right:-6px;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);width:7px}.semi-tooltip-wrapper[x-placement=left].semi-tooltip-with-arrow,.semi-tooltip-wrapper[x-placement=left].semi-tooltip-with-arrow{min-height:34px}.semi-tooltip-wrapper[x-placement=leftBottom].semi-tooltip-icon-arrow{bottom:5px;height:24px;right:-6px;width:7px}.semi-tooltip-wrapper[x-placement=leftBottom].semi-tooltip-with-arrow,.semi-tooltip-wrapper[x-placement=leftBottom].semi-tooltip-with-arrow{min-height:34px}.semi-tooltip-wrapper[x-placement=rightTop].semi-tooltip-icon-arrow{height:24px;left:-6px;top:5px;-webkit-transform:rotate(180deg);transform:rotate(180deg);width:7px}.semi-tooltip-wrapper[x-placement=rightTop].semi-tooltip-with-arrow,.semi-tooltip-wrapper[x-placement=rightTop].semi-tooltip-with-arrow{min-height:34px}.semi-tooltip-wrapper[x-placement=right].semi-tooltip-icon-arrow{height:24px;left:-6px;top:50%;-webkit-transform:translateY(-50%)rotate(180deg);transform:translateY(-50%)rotate(180deg);width:7px}.semi-tooltip-wrapper[x-placement=right].semi-tooltip-with-arrow,.semi-tooltip-wrapper[x-placement=right].semi-tooltip-with-arrow{min-height:34px}.semi-tooltip-wrapper[x-placement=rightBottom].semi-tooltip-icon-arrow{bottom:5px;height:24px;left:-6px;-webkit-transform:rotate(180deg);transform:rotate(180deg);width:7px}.semi-tooltip-wrapper[x-placement=rightBottom].semi-tooltip-with-arrow,.semi-tooltip-wrapper[x-placement=rightBottom].semi-tooltip-with-arrow{min-height:34px}.semi-tooltip-wrapper[x-placement=bottomLeft].semi-tooltip-icon-arrow{left:6px;top:-6px;-webkit-transform:rotate(180deg);transform:rotate(180deg)}.semi-tooltip-wrapper[x-placement=bottomLeft].semi-tooltip-with-arrow,.semi-tooltip-wrapper[x-placement=bottomLeft].semi-tooltip-with-arrow{min-width:36px}.semi-tooltip-wrapper[x-placement=bottom].semi-tooltip-icon-arrow{left:50%;top:-6px;-webkit-transform:translateX(-50%)rotate(180deg);transform:translateX(-50%)rotate(180deg)}.semi-tooltip-wrapper[x-placement=bottom].semi-tooltip-with-arrow,.semi-tooltip-wrapper[x-placement=bottom].semi-tooltip-with-arrow{min-width:36px}.semi-tooltip-wrapper[x-placement=bottomRight].semi-tooltip-icon-arrow{right:6px;top:-6px;-webkit-transform:rotate(180deg);transform:rotate(180deg)}.semi-tooltip-wrapper[x-placement=bottomRight].semi-tooltip-with-arrow,.semi-tooltip-wrapper[x-placement=bottomRight].semi-tooltip-with-arrow{min-width:36px}.semi-portal-rtl.semi-tooltip-wrapper,.semi-rtl.semi-tooltip-wrapper{direction:rtl;left:auto;padding-left:12px;padding-right:12px;right:0}.semi-portal{left:0;position:absolute;top:0;width:100%;z-index:1}.semi-portal-inner{background-color:initial;min-width:-webkit-max-content;min-width:max-content;position:absolute}.user-info-container{position:relative}.user-info-content{background-color:#fff;border-radius:10px;padding-bottom:20px;width:320px}.user-info{display:inline-block;min-width:50px}.img-box{height:140px}.img-box,.img-box>img{border-top-left-radius:8px;border-top-right-radius:8px;width:100%}.img-box>img{height:100%}.info-box{line-height:26px;padding:016px}.username{font-size:20px;font-weight:600;line-height:28px;margin-bottom:10px}.signature{color:#646a73;margin-bottom:16px}.avatar{border-radius:50%;height:100px;margin-top:-50px;width:100px}.info{display:flex;margin-bottom:16px;width:100%}.desc{color:#646a73;flex:0072px;width:72px}.info-text{color:#1f2329;flex:11}a{color:#3370ff;text-decoration:none}.App-header{align-items:center;background-color:#282c34;color:#fff;display:flex;flex-direction:column;font-size:calc(10px+2vmin);justify-content:center;min-height:100vh}`;插件侧的代码示例:constants.ts//存放插件的自定义标签exportconstwebComponentName="plugin-template";Index.tsximportReactfrom"react";importReactDOMfrom"react-dom/client";import"./index.css";importAppfrom"./App";import{webComponentName}from"./constants";constrenderDocument=document.querySelector(webComponentName);constroot=ReactDOM.createRoot(renderDocument.shadowRoot.getElementById("root")document.getElementById("root"));root.render();App.tsximport{useEffect,useMemo,useState}from"react";import{Popover,Avatar}from"@douyinfe/semi-ui";import{webComponentName}from"./constants";import"./App.css";functionApp(props){const{userId}=props;const[userInfo,setUserInfo]=useState({});useEffect(()=>{//根据userId获得相关信息constgetUserData=async()=>{awaitsetTimeout(()=>{},1000);setUserInfo({userId,username:"木子水吉",bgSrc:"https://github.com/muzishuiji/demo_resource/blob/main/web_component/bg.pngraw=true",avatar:"https://github.com/muzishuiji/demo_resource/blob/main/web_component/avatar.jpegraw=true",signature:"想清楚,表达清楚~",department:"LarkProductivityEngineering-Frontend",okr:"https://xxx.xxx.cn/okr/user/xxx",email:"xxx.xxx@xxx.com",city:"杭州",people:"https://xxx.xxx.net/person"});};getUserData();},[userId]);constcontent=useMemo(()=>({userInfo.username}{userInfo.signature}部门{userInfo.department}OKR查看详情Email{userInfo.email}城市{userInfo.city}People查看详情),[userInfo]);return( document.querySelector(webComponentName).shadowRoot.getElementById("root")document.querySelector(".user-info-container")}> {userInfo.username});}exportdefaultApp;运行效果 已关注 关注 重播 分享 赞 关闭观看更多更多字节前端 ByteFE已关注分享点赞在看已同步到看一看写下你的评论分享视频,时长00:110/000:00/00:11 切换到横屏模式 继续播放进度条,百分之0播放00:00/00:1100:11 倍速播放中 0.5倍 0.75倍 1.0倍 1.5倍 2.0倍 超清 流畅 您的浏览器不支持 video 标签 继续观看 Web Component 探索之旅 观看更多转载,Web Component 探索之旅字节前端 ByteFE已关注分享点赞在看已同步到看一看写下你的评论 视频详情 在学习过程中的一些发现,结合Web Component的Custom Elements和Shadow DOM 确实可以提供一个比较良好的脚本和 CSS 的隔离环境,但使用Web Component做隔离的一些缺点:不能帮助你实现沙箱,如果你需要沙箱化模块逻辑代码,需要手动实现或额外引入一些库。Web Component的 DOM 隔离是把双刃剑,如果是很多模块共用的样式,你需要分别处理加载到对应的子模块使其生效,一个比较明显的是插件内部如果使用除 semi 外的库,在 body 上设置相关 style 会不生效,需要提取出来设置到 shadow dom 的最上层容器上或者手动插入到外层的 body 上。子模块和子模块之间是不同的 dom 树,因此每个模块querySelectorAll等方法上下文也是单独的,模块之间不能互相select到对方的dom,需要先获取其他模块的Web Component节点,再在其上使用querySelectorAll。微前端的简单实践Web Component 在微前端中的简单实践完整代码:https://codesandbox.io/s/ren-yuan-qia-pian-zu-jian-forked-z9yyb7file=/src/App.tsxApp.jsimport{useState}from"react";import{Tabs,TabPane}from"@douyinfe/semi-ui";import"./reactTemplate.js";import"./vueTemplate.js";import"./App.css";functionApp(){const[userId]=useState("123456789");return( 实际使用过程,会根据appid动态获取插件资源的地址,从而请求对应资源的地址暂不支持,敬请期待~);}exportdefaultApp;ReactTemplate.js//偷个懒,cssText就不请求了import{cssContent}from"./constants";classReactPluginTemplateextendsHTMLElement{constructor(){super();this.shadow=this.attachShadow({mode:"open"});//给插件搞一个容器constcontainer=document.createElement("div");container.id="root";//如果有modal需要作为getPopupcontainercontainer.style.position="relative";//container.innerHTML=divText;this.shadow.appendChild(container);//插件的style设置不会影响到外部conststyle=document.createElement("style");style.innerText="body{backgrount-color:red;}#root{--semi-shadow-elevated:001pxrgba(0,0,0,.3),04px14pxrgba(0,0,0,.1);}";conststyleNode=document.createTextNode(cssContent.toString());style.appendChild(styleNode);this.shadow.appendChild(style);}connectedCallback(){//动态插入插件的打包资源constscript=document.createElement("script");script.type="text/javascript";fetch("https://raw.githubusercontent.com/muzishuiji/demo_resource/main/web_component/bundle.js").then((response)=>{response.text().then((data)=>{script.innerText=`(function(){let__temp_exports__={};with({exports:__temp_exports__}){console.log(document);${data.replace(/[\r\n]*/[/*](=#sourceMap"\r\n]*/[/*").*[\r\n]*/g,"")};}return__temp_exports__;})();`;this.shadow.appendChild(script);});}).catch((err)=>{console.log(err,"err-----");});}//observedAttributes,定义特定属性变化时,触发attributeChangedCallback函数staticgetobservedAttributes(){return["userId"];}}if(!window.customElements.get("react-app")){window.customElements.define("react-app",ReactPluginTemplate);}VueTemplate.jsimport{vueCssContent}from"./constants";classVuePluginTemplateextendsHTMLElement{constructor(){super();this.shadow=this.attachShadow({mode:"open"});//给插件搞一个容器constcontainer=document.createElement("div");container.id="root";container.style.position="relative";//container.innerHTML=divText;this.shadow.appendChild(container);//插件的style设置不会影响到外部conststyle=document.createElement("style");conststyleNode=document.createTextNode(vueCssContent.toString());style.appendChild(styleNode);this.shadow.appendChild(style);}connectedCallback(){//动态插入插件的打包资源constscript=document.createElement("script");script.type="text/javascript";fetch("https://raw.githubusercontent.com/muzishuiji/demo_resource/main/web_component/app.js").then((response)=>{response.text().then((data)=>{script.innerText=`(function(){${data.replace(/[\r\n]*/[/*](=#sourceMap"\r\n]*/[/*").*[\r\n]*/g,"")};})();`;this.shadow.appendChild(script);});}).catch((err)=>{console.log(err,"fetcherr-----");});constchunkScript=document.createElement("script");chunkScript.type="text/javascript";fetch("https://raw.githubusercontent.com/muzishuiji/demo_resource/main/web_component/chunk.js").then((response)=>{response.text().then((data)=>{chunkScript.innerText=`(function(){${data.replace(/[\r\n]*/[/*](=#sourceMap"\r\n]*/[/*").*[\r\n]*/g,"")};})();`;this.shadow.appendChild(chunkScript);});}).catch((err)=>{console.log(err,"fetcherr-----");});}//observedAttributes,定义特定属性变化时,触发attributeChangedCallback函数staticgetobservedAttributes(){return["userId"];}//与observedAttributes结合使用attributeChangedCallback(name,oldValue,newValue){if(name==="userId"&oldValue!==newValue){}}}if(!window.customElements.get("vue-app")){window.customElements.define("vue-app",VuePluginTemplate);}Constans.js//内容过多省略,前往demo查看完整的css设置exportconstcssContent=``;exportconstvueCssContent=``;运行效果 已关注 关注 重播 分享 赞 关闭观看更多更多字节前端 ByteFE已关注分享点赞在看已同步到看一看写下你的评论分享视频,时长00:070/000:00/00:07 切换到横屏模式 继续播放进度条,百分之0播放00:00/00:0700:07 倍速播放中 0.5倍 0.75倍 1.0倍 1.5倍 2.0倍 超清 流畅 您的浏览器不支持 video 标签 继续观看 Web Component 探索之旅 观看更多转载,Web Component 探索之旅字节前端 ByteFE已关注分享点赞在看已同步到看一看写下你的评论 视频详情 飞书的人员卡片在飞书,飞书 doc 等很多网站上都能看到的人员卡片,理论上可以采取类似的实现,使用 Web Component 开发,然后打包成一个独立的 js 文件(类似于 JS SDK),供给各业务方接入。这里就不做详细的探索了,感兴趣的可以试试。跨框架的 UI 组件库一个简单的想法,感觉可以基于 Web Component 来实现跨框架的 UI 组件库,但还实践过,感觉可以实现一个 semi design 练练手~Web Component 与框架之间的对比?Web Component 与框架相比的优势在哪里?不用加载外部模块,轻量Web Component 作为 Web W3C 规范的一部分,被浏览器原生支持,使用时,不需要像 React.js、Vue.js 等引入大体积的 Runtime 文件,所有组件声明、实例化均由浏览器负责,使得最终编译的体积足够小。可以很方便的与其他框架组合使用Web Component 已经被各大框架支持,React 和 Vue 都在自己的官方文档中着重介绍了与 Web Comonent 的相互调用。使用时,可像普通组件一样引入 Web Component 模块,轻松和已有框架整合。易维护,上手成本低Web Component 是 web 规范的一部分,未来应当被大家所熟知,不像框架那样有那么多的概念,当模块更换维护人员,不会有很高的学习成本。Web Component 的缺点有哪些?兼容性要求Custom Element 定义时使用 ES6 的 Class,无法传递 ES5 的构造函数,需要额外引入 Polyfill 解决问题;一旦定义无法撤回无法使用不同的 ES6 Class 定义同名 CustomElement,仅首次定义生效;需要解决多团队协作的 CustomElement 命名冲突的问题;状态管理因为没有 state 的概念,Web Component 组件的内部属性的变化需要监听属性变化的回调事件,手动处理重渲染,心智负担可能会略重一些。感觉 Web Component 想要出线,单纯依赖原生的 API 是远远不够的,能颠覆框架的必然是框架,拥有一个框架需要具备的基础生态(路由解决方案、状态管理、dom 性能优化等)。一些延伸LitLit[3] 是 Google 出品的一个用于构建快速、轻量级的 Web Components 库。Lit 的核心是一个消除样板代码的组件基类,它提供reactive state、 scoped styles和一个小巧、快速、且富有表现力的declarative template system。我们可以基于这个库来实现semi design那样的组件库。简单使用示例:https://codesandbox.io/s/sad-sinoussi-cvlnobfile=/index.html:0-228//src/mainContainer.tsimport{LitElement,html,css}from'lit';import{customElement}from'lit/decorators.js';@customElement('main-container')exportclassLitAppextendsLitElement{staticstyles=css`:host{width:100px;height:100px;color:red;}`;render(){returnhtml`HellofromMuzishuiji!`;}}OmiOmi[4] 是一款基于 Web Component 的相关特性开发的前端跨框架跨平台框架,任何框架可以使用 Omi 自定义元素。感兴趣的朋友可以自行去了解~点击上方关注追更不迷路参考资料[1]Web Components | MDN: https://developer.mozilla.org/zh-CN/docs/Web/Web_Components[2]Using shadow DOM - Web Components | MDN:https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM[3] Lit:https://lit.dev/docs/[4]Omi:https://github.com/Tencent/omi/blob/master/README.CN.md
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-15 18:01 , Processed in 0.695364 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表