当前位置: 首页 > news >正文

南充做网站略奥网络网站优化排名公司

南充做网站略奥网络,网站优化排名公司,网站源代码怎么上传,重庆餐饮品牌策划公司[React]利用Webcomponent封装React组件 为什么这么做 我个人认为#xff0c;最重要的点是可以很方便地跨框架挂载和卸载wc元素#xff08;至少我在项目里是这么玩的#xff09;#xff0c;此外#xff0c;基于wc的css沙箱以及它的shadowRoot机制#xff0c;可以提供一套…[React]利用Webcomponent封装React组件 为什么这么做 我个人认为最重要的点是可以很方便地跨框架挂载和卸载wc元素至少我在项目里是这么玩的此外基于wc的css沙箱以及它的shadowRoot机制可以提供一套隔离机制保证每个渲染组件的边界分明。 利用AI总结罗列了一下都有啥优点… 封装性Web Components提供了一种封装UI组件的方法使得组件可以在不同的框架或无框架环境中重用。可重用性封装为Web Components的React组件可以在任何支持Web Components的环境中使用不限于React应用。封装的样式和行为Web Components允许你封装组件的HTML结构、样式和行为确保样式和行为不会泄露到父组件或全局作用域。独立性Web Components封装的组件具有独立性它们拥有自己的DOM树和作用域不会影响外部环境。易于集成Web Components提供了一种标准化的集成方式可以更容易地将React组件集成到其他Web应用中。更好的性能Web Components的自定义元素可以在不影响主线程的情况下进行升级和渲染这有助于提高应用性能。标准化Web Components基于W3C标准这意味着它们在不同的浏览器和环境中具有更好的一致性和兼容性。易于维护由于Web Components封装的组件具有清晰的接口和封装性维护和更新组件变得更加容易。样式隔离Web Components的Shadow DOM技术可以确保组件的样式不会受到外部样式的影响同时也防止组件内部样式泄露到外部。生命周期管理Web Components允许你定义组件的生命周期钩子如connectedCallback、disconnectedCallback等这与React组件的生命周期方法类似。跨框架使用封装为Web Components的React组件可以被其他前端框架或库使用例如Vue、Angular或原生JavaScript。自定义元素Web Components允许开发者定义自定义HTML元素这些元素可以像标准HTML元素一样使用。易于测试Web Components的封装性使得测试组件变得更加简单因为你可以独立于其他组件来测试它们。更好的封装和抽象Web Components提供了一种封装和抽象UI组件的方式使得组件的实现细节对使用者是透明的。 Webcomponent入门 先来简单地过一下webcomponent的基础 官方文档https://developer.mozilla.org/zh-CN/docs/Web/API/Web_components 示例 下面是一个最简单的示例自定义了一种名为”simple-component“的元素并且它没有shadowRoot意味着它并没有与外界隔离样式。 class SimpleComponent extends HTMLElement {constructor() {super();this.innerHTML pHello, World!/p;} }customElements.define(simple-component, SimpleComponent);下面是一个内容更丰富一些的示例有基础的大概过一眼也知道大概了。 // 1.自定义标签都是用class 的形式去继承 class myDiv extends HTMLElement {// 监听static get observedAttributes() {return [option]}constructor() {super()// 这样我们才能够去追加元素this.attachShadow({ mode: open })}// 重要生命周期方法 开始connectedCallback() {console.log(connectedCallback生命周期)this.render({option: this.getAttribute(option),})// 获取元素console.log(this.shadowRoot.querySelector(.content))console.log(this.shadowRoot: , this.shadowRoot)document.addEventListener(click, e {// 重要冒泡的顺序通过这个可以判断有没有在鼠标内部进行点击if (e.composedPath().includes(this)) {console.log(点击了里面)}})this.shadowRoot.querySelector(.content).addEventListener(click, e {console.log(e: , e)// window.dispatchEvent})}// 重要生命周期方法 重新渲染 .甚至还是第一次进行渲染比connect还快// 会重新渲染 connectCallbackattributeChangedCallback(attr, oldValue, newValue) {if (oldValue) {switch (attr) {case option:this.shadowRoot.querySelector(.title).textContent newValue}}console.log(attributeChangeCallback, attr, oldValue, newValue)}borderAdd() {console.log(borderadd)this.shadowRoot.querySelector(.content).style.border 3px solid green}render(data) {let { option } data// console.log()let nodeTemplate document.createElement(template)nodeTemplate.innerHTML div classcontent div classtitle${option} /div slot namecontainer/slot/divlet nodeStyles document.createElement(style)// shadow dom 的样式绝对隔离// 重要 :host选择器可以选中根也就是my-div的样式。外面的选择器样式要高于这个nodeStyles.innerHTML :host(.active) .content{margin-top:20px;background:rgba(0,0,0,30%);}:host{display:block}.content{width:100px;height:100px;background:rgba(0,0,0,20%)}::slotted([slotcontainer]){display:none}::slotted(.active){display:block}this.shadowRoot.appendChild(nodeTemplate.content)this.shadowRoot.appendChild(nodeStyles)setTimeout(() {this.borderAdd()}, 3000)} }// 名字必须小写 驼峰必须要转成横线 customElements.define(my-div, myDiv) shadowRoot 一个Web组件可以有且仅有一个shadowRoot。shadowRoot是与该组件关联的影子DOM的根节点。当使用attachShadow方法创建影子DOM时它会返回一个shadowRoot对象这个对象是唯一的并且与创建它的元素关联。 例如 class MyComponent extends HTMLElement {constructor() {super();this.shadow this.attachShadow({ mode: open });this.shadow.innerHTML pI am in the shadow DOM!/p;} }customElements.define(my-component, MyComponent);在这个例子中 MyComponent类扩展了HTMLElement定义了一个Web组件。在构造函数中通过调用this.attachShadow({ mode: open })创建了一个shadowRoot并将其存储在this.shadow变量中。这个shadowRoot是唯一的并且与MyComponent实例关联。 关键点 唯一性每个Web组件实例只能有一个shadowRoot。关联性shadowRoot与创建它的Web组件实例是紧密关联的不能被其他组件实例访问。 因此尽管可以在shadowRoot内创建多个子元素和结构但每个Web组件实例只能有一个shadowRoot。这有助于保持组件的封装性和独立性。 生命周期 connectedCallback 当自定义元素被插入到文档DOM树中时调用此方法。这类似于React中的componentDidMount。 disconnectedCallback 当自定义元素从DOM树中移除时调用此方法。类似于React中的componentWillUnmount。 attributeChangedCallback 当自定义元素的属性被更改时调用此方法。它接收三个参数属性名称、旧值和新值。这可以用于响应属性的变化类似于React中的componentDidUpdate但是它是针对属性而不是状态。 adoptedCallback 当自定义元素被移动到新的文档时调用此方法。这在Web Components中是特有的因为自定义元素可以跨文档使用。 主要属性 观察者模式Observed attributes 通过在自定义元素类中定义一个静态的observedAttributes属性数组可以指定哪些属性的更改应该触发attributeChangedCallback。 connected 和 disconnected 属性 这些属性可以用于检查自定义元素是否已经连接到文档的DOM树中。 shadowRoot 属性 每个自定义元素都有一个shadowRoot属性它是一个Shadow DOM树的根。可以在这个属性上使用生命周期回调来管理Shadow DOM的创建和更新。 constructor 虽然不是Web Components的生命周期回调但是自定义元素的构造函数是定义元素属性和方法的地方并且在元素实例化时调用。 Lit框架入门 一般知道了上面的基础就可以写wc组件了但实际开发中肯定还是需要借助一些已有的开发框架来辅助开发而Lit就是目前最成熟且使用量最高的。 原理介绍 Web组件的更新并不是每次都进行全量更新。Web组件的更新机制非常灵活能够根据组件的状态和属性的变化来决定是否需要更新。以下是一些关键点 属性变化触发更新 Web组件的更新通常是由属性的变化触发的。当组件的属性发生变化时浏览器会调用attributeChangedCallback方法来处理这些变化。 状态变化触发更新 组件的内部状态变化也可能导致更新。例如在LitElement中当使用property装饰器定义的属性发生变化时会触发更新。 生命周期方法 组件的生命周期方法如connectedCallback, disconnectedCallback, adoptedCallback, firstUpdated, updated等都可以在特定时机触发更新。 选择性更新 更新机制可以是选择性的。例如在LitElement中可以通过使用requestUpdate方法来请求更新而不必每次都进行全量更新。 虚拟DOM 一些Web组件框架如LitElement使用虚拟DOM技术来优化更新过程。虚拟DOM可以比较组件的新旧状态并只更新那些实际发生变化的部分。 优化性能 为了避免不必要的全量更新Web组件通常会使用一些优化技术例如节流throttle和防抖debounce来减少更新次数。 自定义渲染逻辑 开发者可以通过自定义渲染逻辑来控制组件的更新过程。例如可以在render方法中手动决定哪些部分需要重新渲染。 条件渲染 组件可以通过条件渲染来决定是否需要更新某些部分。例如只有当特定条件满足时才重新渲染某些元素。 示例 以下是一个使用LitElement的示例展示了如何控制组件的更新 import { LitElement, html, css, property } from lit;class MyComponent extends LitElement {property({ type: String })message ;render() {return htmldivp${this.message}/p/div;}updated(changedProperties) {super.updated(changedProperties);if (changedProperties.has(message)) {console.log(Message updated:, this.message);}} }customElements.define(my-component, MyComponent);在这个示例中 message属性使用property装饰器定义当其值发生变化时会触发组件的更新。render方法定义了组件的渲染逻辑只有当message属性发生变化时相关的部分才会重新渲染。updated方法在组件更新后被调用可以在这里处理更新后的逻辑。 通过这种方式Web组件可以有效地控制更新过程避免不必要的全量更新从而提高性能。 增加的生命周期和内置属性 Lit 相对于传统 Web 组件规范增加的一些生命周期钩子和特性 render 方法 这是 Lit 的核心特性之一。render 方法是一个返回组件模板的函数Lit 会根据这个方法的内容来渲染组件的 UI。 update 方法 这个方法在组件的属性或状态发生变化时被调用。Lit 会调用这个方法来决定是否需要重新渲染组件。 shouldUpdate 方法 这个方法允许开发者自定义更新逻辑决定是否需要进行更新。如果返回 false则跳过更新。 willUpdate 方法 在组件更新之前被调用可以用于执行更新前的准备工作。 updated 方法 在组件更新之后被调用可以用于执行更新后的逻辑处理。 firstUpdated 方法 在组件首次更新后被调用。这与 Web 组件的 connectedCallback 有些相似但专门用于处理首次渲染后的逻辑。 connectedCallback 这是 Web 组件规范中的方法Lit 也支持。当组件被插入到文档中时调用。 disconnectedCallback 这是 Web 组件规范中的方法Lit 也支持。当组件从文档中移除时调用。 attributeChangedCallback 这是 Web 组件规范中的方法Lit 也支持。当组件的属性发生变化时调用。 adoptedCallback 这是 Web 组件规范中的方法Lit 也支持。当组件被移动到新文档时调用。 requestUpdate 方法 这个方法可以被开发者调用以请求更新组件的属性。Lit 会安排在下一个微任务中处理这些更新。 updateComplete Promise 一个 Promise当组件的更新完成后会解析。这可以用于在更新完成后执行异步操作。 样式管理 Lit 提供了 CSSResult 和 unsafeCSS 等 API用于更安全和方便地管理组件的样式。 属性装饰器 使用 property 装饰器定义的属性会触发更新并且可以指定属性的类型和是否同步到 DOM 属性。 状态管理 Lit 通过 state 方法和 reactive 装饰器提供了一种声明式的方式来管理组件的状态。 核心结合Lit框架实现React组件封装 那么基于以上我们可以很容易地就实现利用Lit框架创造出一个webcomponent容器然后用来包裹React组件。 Base基础类 import { LitElement, ReactiveElement, adoptStyles, unsafeCSS, PropertyValues } from lit import { property } from lit/decorators.jstype ThrottleFn (...args: any[]) void type DelayFn (fn: ThrottleFn) voidconst throttleWith T extends ThrottleFn(fn: T,delayFn: DelayFn,leading false ): T {let lastArgs: ParametersT, lastThis: unknown, isWaiting falseconst throttledFn (...args: ParametersT) {lastArgs args// eslint-disable-next-linelastThis thisif (!isWaiting) {if (leading) {fn.apply(lastThis, lastArgs)}isWaiting truedelayFn(() {fn.apply(lastThis, lastArgs)isWaiting false})}}return throttledFn as T }export default class Base extends LitElement {private _wcStyle?: stringproperty({ attribute: wc-style })get wcStyle() {return this._wcStyle}set wcStyle(val: string | undefined) {this._wcStyle valthis.adoptStyles()}/*** 使事件不能跨越ShadowDOM边界传播*/property({ type: Boolean, attribute: prevent-compose })protected preventCompose false/*** 使事件不冒泡*/property({ type: Boolean, attribute: prevent-bubbles })protected preventBubbles false// 应用样式protected adoptStyles throttleWith(() {const apply () {if (this.renderRoot instanceof ShadowRoot) {const styles (this.constructor as typeof ReactiveElement).elementStyles.slice() // 获取原有样式this.wcStyle styles.push(unsafeCSS(this.wcStyle))adoptStyles(this.renderRoot, styles) // 重新应用样式}}this.renderRoot ? apply() : this.updateComplete.then(apply)},(fn: any) Promise.resolve().then(fn))// 派发事件emit(eventName: string, detail?: any, options?: CustomEventInit) {let event new CustomEvent(eventName, {detail,composed: !this.preventCompose,bubbles: !this.preventBubbles,cancelable: false,...options,})this.dispatchEvent(event)return event}// 判断 slot 是否传入内容hasSlot(name?: string) {if (name name ! default) {return !![...this.childNodes].find(node node.nodeType node.ELEMENT_NODE (node as Element).getAttribute(slot) name)}return [...this.childNodes].some(node {if (node.nodeType node.TEXT_NODE !!node.textContent?.trim()) {return true}if (node.nodeType node.ELEMENT_NODE) {const el node as HTMLElementif (!el.hasAttribute(slot)) {return true}}return false})}// 各个生命周期// 挂载时connectedCallback() {super.connectedCallback()console.log(Custom element added to page.)// 第一次被插入文档时执行跳过节点删除后又重新插入的情形if (!this.hasUpdated) {this.setAttribute(wc-component, )this.setAttribute(wc-pending, )}}// 卸载时disconnectedCallback() {super.disconnectedCallback()console.log(Custom element removed from page.)}// 移动到另一个文档的时候adoptedCallback() {console.log(Custom element moved.)}// 元素的属性被添加、删除或修改时调用attributeChangedCallback(name: string, oldValue: any, newValue: any) {super.attributeChangedCallback(name, oldValue, newValue)console.log(Attribute ${name} has changed.)}// 或使用静态属性代替get方法static get observedAttributes() {// 指定要监听的元素的属性数组// 对应的attr改变后会触发attributeChangedCallback// return [name, date]return []}// 是否应该更新protected shouldUpdate(_changedProperties: PropertyValues): boolean {return true}// 即将更新protected willUpdate(_changedProperties: PropertyValues): void {super.willUpdate(_changedProperties)console.log(willUpdate)}// 首次更新元素时调用。实现在更新后对元素执行一次性工作protected firstUpdated(changedProperties: PropertyValues) {super.firstUpdated(changedProperties)console.log(this.hasUpdated: , this.hasUpdated)// this.requestUpdate()// 两帧数后执行requestAnimationFrame(() {requestAnimationFrame(() {this.removeAttribute(wc-pending)})})}protected updated(_changedProperties: PropertyValues): void {super.updated(_changedProperties)this.updateComplete.then((res) {console.log(updateComplete, res)})} }withProperties封装 import type { LitElement, PropertyValues } from littype ConstructorT new (...args: any[]) Texport default T extends ConstructorLitElement(superClass: T) {class WithPropertiesElement extends superClass {props: Recordstring, any {}willUpdate(changedProperties: PropertyValues) {const obj [...changedProperties.entries()].reduceany((obj, [key]) ((obj[key] (this as any)[key]), obj),{})this.props { ...this.props, ...obj }super.willUpdate(changedProperties)}}return WithPropertiesElement as Constructor{props: Recordstring, any} T } 这段代码定义了一个高阶组件Higher-Order ComponentHOC用于增强 LitElement 组件的功能。具体来说它的作用是 创建一个带有额外属性管理功能的组件类 通过扩展传入的基类比如 LitElement 或其子类添加一个 props 属性来存储组件的属性值。 在组件更新前处理属性变化 重写 willUpdate 生命周期方法这个方法在组件的属性发生变化并且组件即将更新之前被调用。 收集并存储属性变化 使用 changedProperties 对象一个 Map 类型的对象包含属性名和属性变化的信息来收集属性的变化。将变化的属性存储到 this.props 对象中这样可以通过 props 属性访问组件的所有属性值。 保持基类的 willUpdate 方法的调用 调用 super.willUpdate(changedProperties) 以确保基类的 willUpdate 方法也能正常执行。 代码详解 定义了一个默认导出的函数它接受一个构造函数 superClass应该是 LitElement 或其子类的构造函数。创建一个新类 WithPropertiesElement继承自 superClass。在 WithPropertiesElement 类中定义了一个 props 属性用于存储属性值。重写 willUpdate 方法在组件更新前处理属性变化并将变化的属性存储到 this.props 中。返回 WithPropertiesElement 类并通过类型断言确保它具有额外的 props 属性。 使用示例 假设你有一个基础的 LitElement 组件 import { LitElement, html } from lit;class MyElement extends LitElement {count 0;render() {return htmlpCount: ${this.count}/p;} }customElements.define(my-element, MyElement);你可以使用这个高阶组件来增强它 import { WithPropertiesElement } from ./WithPropertiesElement; import { LitElement, html } from lit;const EnhancedElement WithPropertiesElement(MyElement);customElements.define(enhanced-element, EnhancedElement);const element new EnhancedElement(); document.body.appendChild(element);console.log(element.props); // { count: 0 }在这个示例中EnhancedElement 继承自 MyElement 并添加了属性管理功能。可以通过 element.props 访问组件的所有属性值。 这种模式在需要在组件中统一管理属性或在组件更新前进行额外处理时非常有用。 存放React组件的webcomponent基类 重头戏来了 import { ChildPart, html, PropertyValues } from lit import { query } from lit/decorators.js import { Fragment, createElement as h } from react import ReactDOM from react-dom import withProperties from ../mixin/withProperties import LightBase from ./Basetype H typeof hconst Root: React.FCany props {return h(Fragment, {...props,}) }const omit (obj: Recordstring, any, filter: string[] []) Object.fromEntries(Object.entries(obj).filter(([key]) !filter.includes(key)))// React组件基类 export default class extends withProperties(LightBase) {// 子类要重写这个方法来渲染自己的组件protected renderReact(h: H): React.ReactNode {return null}protected customContainer(): Element | undefined {return this.$reactRoot}protected getReactProps(props: Recordstring, any) {return omit(props, [preventCompose, preventBubbles, localeMessages])}protected extraStyle query(.react-root)$reactRoot?: HTMLElementupdated(changed: PropertyValues) {super.updated(changed)this.doRender()}connectedCallback() {super.connectedCallback()// 节点删除后重新插入的情形if (this.hasUpdated) {this.doRender()}}disconnectedCallback() {super.disconnectedCallback()this.doUnmount()}private container?: Elementprivate doRender() {const container this.customContainer()if (!container) {this.doUnmount() // 卸载前一次渲染的内容} else {this.container containerReactDOM.render(h(Root, {}, this.renderReact(h)), container, () {// hack for error: https://github.com/lit/lit/blob/f8ee010bc515e4bb319e98408d38ef3d971cc08b/packages/lit-html/src/lit-html.ts#L1122// 在React中使用此组件且非首次更新时会报错因为lit默认会在组件下创建一个注释节点更新时会对这个节点进行操作而React渲染时把这个注释节点干掉了这里要把他加回去const childPart (this as any).__childPart as ChildPart | undefinedchildPart?.startNode this.appendChild(childPart.startNode)})}}private doUnmount() {if (this.container) {ReactDOM.unmountComponentAtNode(this.container)}}render() {return html div classreact-root/div } }使用Demo import { unsafeCSS } from lit import { customElement, property } from lit/decorators.js import ReactBase from ./ReactBase// 自己的React组件 import Component from ./Componentimport style from ./index.less?inlinecustomElement(my-diy-react-wc) export default class DataReport extends ReactBase {static get styles() {return unsafeCSS([style])}/*** 自定义属性*/property()language: string zh-CN// ReactBase中用来渲染React不要删除renderReact() {return Component language{this.language} /} } 参考文章 https://juejin.cn/post/7296850940404580364?searchId2024071620331848BC966F0D2051B9C533#heading-9 lit官网https://lit.dev/docs/components/styles/ webcomponent文档https://developer.mozilla.org/en-US/docs/Web/API/Web_components
http://www.tj-hxxt.cn/news/223534.html

相关文章:

  • 做外文翻译的网站制作图片软件免费版
  • 国内旅行做行程网站wordpress模板是什么意思
  • 广州seo网站推广技巧app手机软件
  • 可不可以自己做网站沙井做网站的公司
  • 建设搜索引擎友好的网站对做网站有什么建议
  • 电信网站备案系统做网站都需要什么资料
  • 江苏省建设斤网站有没有专门的销售公司
  • 岚县网站建设全网关键词优化公司哪家好
  • 网站建设管理经验做法盗网站asp源码
  • 电器网站建设免费咨询上海网站架设
  • 做外销网站总部在深圳的互联网公司
  • 怎么做网站添加二维码男科医院咨询免费
  • 网站的评测系统怎么做的做招聘的h5用哪个网站
  • 网站流量盈利知己图书网站建设策划书
  • 视频网页制作教程360优化大师官网
  • 如何给网站的关键词做排名架设一个网站
  • 网站不备案不能访问吗wordpress如何添加百度地图
  • 手机静态网站开发制作中国门户网站建设重要性
  • 订阅号可以做网站么二手房装修
  • 自己做网站能赚钱么网站论坛模板下载
  • 兰州网站分类导航企业网站推广方案设计
  • 怎么弄免费的php空间做网站中国哪家做网站的公司最大
  • 淘宝网站建设目标是什么意思江油网站制作
  • 手机h5免费模板网站模板中国建筑未来走势预测
  • 网站优化软件有哪些php网站开发技术 pdf
  • 淄川网站建设yx718wordpress xampp
  • 视频工厂网站建设物流公司怎么做网站
  • 网站备案在线注销html成品网页模板下载
  • 表白网站在线制作软件软件开发文档
  • 兰州网站程序建设浙江建设网一官方网站