stencil 组件

stencil 组件

    • 装饰器
      • 生命周期
      • 应用加载事件
    • 组件定义
      • 组件如何响应数据变化
    • 组件使用
      • 如何传递 slot
      • 如何暴露组件内部的方法供外部使用?
      • `@Element` 装饰器
    • Host 组件
    • 样式
    • 函数组件

stencil 提供一些装饰器、生命周期钩子和渲染函数去编写一个组件。

装饰器

装饰器是一组用于声明组件元数据的函数,会在构建产物中移除,所以不会有运行时开销。

  • @Component() 声明一个类是组件
  • @Prop() 声明一个组件的特性或者属性
  • @State() 声明组件内部状态
  • @Watch() 监听 prop 或者 state 的改变,然后执行副作用
  • @Listen() 监听组件内部的 DOM 事件
  • @Event() 声明自定义事件
  • @Method() 暴露组件方法
  • @Element() 声明组件变化的自定义标签

生命周期

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第一次挂载

connectedCallback
⬇️
componentWillLoad
⬇️
componentWillRender
⬇️
render
⬇️
componentDidRender
⬇️
componentDidLoad
⬇️
disconnectedCallback # 组件被移除

prop 或者 state 更新:

@Watch
⬇️
componentShouldUpdate
⬇️
componentWillUpdate
⬇️
componentWillRender
⬇️
render
⬇️
componentDidRender
⬇️
componentDidUpdate
⬇️
disconnectedCallback # 组件被移除

常用的:

connectedCallback 会调用多次:首次和移除后再添加到 DOM 都会调用,可设置定时器、监听原生事件等。

const el = document.createElement('my-cmp')
document.body.appendChild(el)
// connectedCallback() called
// componentWillLoad() called (first time)

el.remove()
// disconnectedCallback()

document.body.appendChild(el)
// connectedCallback() called again, but `componentWillLoad()` is not.

disconnectedCallback 组件从 DOM 中移除时调用,可在此做一些收尾工作。

componentWillLoad 在 render 之前调用, 调用一次
可再次发送 ajax 请求获取数据。

componentShouldUpdate(newValue,oldValue,property)

返回布尔值,决定组件是否重新渲染。

可以更新状态的钩子有:

componentWillLoad
@Watch
componentWillUpdate
componentWillRender

componentDidLoad(), componentDidUpdate() and componentDidRender() 更新状态,会导致再次渲染。

componentDidUpdate()、componentDidRender() 可能导致无限渲染。

父子组件的生命周期:

<cmp-a>
  <cmp-b>
    <cmp-c></cmp-c>
  </cmp-b>
</cmp-a>
cmp-a - componentWillLoad()
cmp-b - componentWillLoad()
cmp-c - componentWillLoad()

cmp-c - componentDidLoad()
cmp-b - componentDidLoad()
cmp-a - componentDidLoad()

Component Lifecycle Methods

应用加载事件

一个特殊的生命周期钩子,在整个应用加载完成后触发。

window.addEventListener('appload', event => {
  console.log(event.detail.namespace)
})

组件定义

import { Component, Prop, h, EventEmitter, Event } from '@stencil/core'

// 组件装饰器
@Component({
  tag: 'app-input', // 名字全局唯一
  styleUrl: 'index.scss', // 组件的样式
  shadow: true, // 开启 shadow root 封装组件样式
})
export class MyInput {
  @Prop() value: string | number = ''
  @Event({ eventName: 'input' }) input: EventEmitter
  // 直接使用属性名称作为事件名称
  @Event() inputChanged: EventEmitter

  onInput(e: Event) {
    // e.preventDefault() // 默认行为不行
    e.stopPropagation()
    const inputEle = e.target as HTMLInputElement
    this.input.emit(inputEle.value)
  }

  onChange(e: Event) {
    const inputEle = e.target as HTMLInputElement
    this.inputChanged.emit(inputEle.value)
  }

  render() {
    return <input value={this.value} onInput={e => this.onInput(e)} onChange={e => this.onChange(e)} />
  }
}

解读:

  • @Component装饰器声明该类是一个组件,传递一些元数据,比如组件的标签,标签必须全局唯一,且含有-,样式,是否开启 shadow 等。

tag 属性必需,更多参数

  • @Prop声明组件的属性

关于命名

在组件内部使用小驼峰命名,在 html 使用 dash-case 传递数据。

如何处理原生属性?

添加到自定义标签上。

如何传递对象、数组等复杂数据?

在 stencil 组件中,和 jsx 一样。

在 html 中,所有属性都是字符串,只能传递字符串

comInstance.setAttribute('prop', value) // 无效
comInstance.prop = value // work well 且对对象和数组无效

如何在 html 中修改 prop?

暴露方法和设置 prop 可变,在外部调用方法修改。

prop 的选项

export interface PropOptions {
  attribute?: string = false
  mutable?: boolean = false
  reflect?: boolean = false
}

prop 默认是组件内部不可变更的,否则触发警告通过 mutable 修改这个默认行为。

reflect:声明 DOM prop 是否对应到标签特性上,设置为 true,在 html 标签上,会显示该属性。

@Component({ tag: 'my-cmp' })
class Cmp {
  @Prop({ reflect: true }) message = 'Hello'
  @Prop({ reflect: false }) value = 'The meaning of life...'
  @Prop({ reflect: true }) number = 42
}

渲染结果:

<my-cmp message="Hello" number="42"></my-cmp>

不设置为 true,依然可以通过 DOM 对象拿到 prop。

修改特性名字 attribute

如何验证 prop?

在 Watch 中验证 prop 合法性,不合法抛出错误。

如何设置必需?

都默认可选,可在 Watch 验证是否必须。

  • @Event声明组件触发的事件,后面是事件名称

this.eventName.emit(data) 触发自定义事件,data 是发送到父组件的数据,监听 eventName 事件时通过 event.detail 获取到 data

在自定义标签上仍然能监听到原生事件,如何避免监听到原生事件呢?

阻止事件冒泡,必要时取消默认行为。

如何监听?

  • @Listen(eventName)监听事件,绑定到组件上,可通过第二个参数配置绑定的元素。
    @Listen 监听全局事件很有用。

  • 在 jsx 中,通过onXxx监听

  • html 中通过addEventListener

更多事件信息

组件如何响应数据变化

当 props 和 state 改变,stencil 重新渲染,比较变化时,比较的时引用,所以数组和对象,引用不变,不会更新。

Reactive Data

组件使用

通过自定义标签 app-input 在 stencil 组件中使用:

import { Component, h, Host, State, Watch } from '@stencil/core'

@Component({
  tag: 'app-home',
  styleUrl: 'app-home.css',
  shadow: true,
})
export class AppHome {
  @State() input = 'hello world'

  onInput(e: CustomEvent<HTMLAppInputElement>) {
    this.input = e.detail as unknown as string
  }
  // NOTE 监听原生事件
  // 被组件内阻止事件冒泡后,监听不到
  onNativeChange(e: Event) {
    const inputEle = e.target as HTMLInputElement
    console.log('原生事件', inputEle.value)
  }

  onChange(e: CustomEvent<HTMLAppInputElement>) {
    console.log(e.detail)
  }

  @Watch('input')
  inputChanged(newValue: string, oldValue: string) {
    console.log(newValue, oldValue)
  }
  render() {
    return (
      <app-input
        value={this.input}
        onInput={e => this.onInput(e)}
        onInputChanged={this.onChange}
        onChange={this.onNativeChange}
      />
    )
  }
}

如何传递 slot

<Host>
  <slot name='prepend'></slot>
  <input value={this.value} onInput={e => this.onInput(e)} onChange={e => this.onChange(e)} />
  <slot name='append'>hello</slot>
</Host>

从父组件传递:

<app-input>
  {/* 不指定slot名字,无法处理 */}
  {/* <h1>header one</h1> */}
  <h2 slot='prepend'> append slot</h2>
  {/* <div slot='append'> */}
  <span slot='append'>append</span>
  <span slot='append'>append one</span>
  <span slot='append'>append another</span>
  {/* </div> */}
</app-input>

如何通过 slot 传递数据到父组件?

slot 的高级用法

如何暴露组件内部的方法供外部使用?

通过 @Method 暴露方法, ref 获取组件实例,调用组件方法。

  @Method() // @Method 装饰器要求方法返回Promise
  async getValue() {
    return this.value
  }

在父组件通过 ref 调用组件方法

@Component({
  tag: 'app-home',
  styleUrl: 'app-home.css',
  shadow: true,
})
export class AppHome {
  person: Person = { name: 'John', age: 23 }
  appInput!: HTMLAppInputElement
  componentWillLoad() {
    console.log('Component is about to be rendered', this.appInput)
  }
  componentDidLoad() {
    console.log(this.appInput) // 组件实例
    this.appInput.getValue().then(console.log)
    console.log(this.appInput.person) // 拿到自定义属性
    console.log(this.appInput.title) // 拿到原生属性
    // console.log(this.appInput?.onInput)// 拿不到没有暴露的方法
  }
  render() {
    return <app-input ref={refInput => (this.appInput = refInput)} person={this.person} title='input' />
  }
}

通过函数的方式绑定 ref 到组件上,组件挂载后,ref 是组件实例。

prop、method、原生属性是共有的,其他都是私有的。

@Element 装饰器

在组件内部获取自组件实例。

和在组件外部通过 ref 获取组件实例,值是同一个。

Host 组件

Host 组件一个内置组件,不会渲染到页面上。

常用的场景:

  • 在组件内部设置自定义标签的属性
import { Component, Host, h } from '@stencil/core'

@Component({ tag: 'todo-list' })
export class TodoList {
  @Prop() open = false
  render() {
    return (
      <Host
        aria-hidden={this.open ? 'false' : 'true'}
        class={{
          'todo-list': true,
          'is-open': this.open,
        }}
      />
    )
  }
}
  • 作为Fragment

样式

stencil 使用 Shadow DOM 要封装 DOM 和样式,内部样式不泄露到外部,外部样式不影响组件内部的 DOM。

@Component({
  shadow: true,// 启用 shadow DOM
})

不启用,和平时的 style 一样书写,就是全局样式。

启用后:

  • 样式隔离:内外部样式不相互影响。

  • 设置样式的选择改变

启用前,可通过自定义标签获取到 DOM

app-input {
}

启用后,自定义标签失效,使用 :host

:host {
}

启用后,对 :host 选择器有影响,目前还不清楚是什么影响。

都使用 :host

  • 获取 DOM 的方式改变
this.el.querySelector('div') // 启用前
this.el.shadowRoot.querySelector('div') // 启用后

如何从外部改变内部的样式?

  • CSS custom 变量

CSS 变量往往设置成全局的,但是全局样式文件只能导入一个。

  • ::part and ::theme, an ::explainer
  • 全局样式

哪些情况该考虑使用全局样式:

  1. Theming: defining CSS variables used across the app

  2. 字体

  3. body background

  4. CSS resets

更多教程

函数组件

import { h } from '@stencil/core'
import './index.css'
export const Hello = props => <h1>Hello, {props.name}!</h1>

函数组件无法使用样式,且只能在 JSX 中使用,以大写字母开头,不能在 html 中使用,这个很坑爹。

函数组件还有以下限制:

  • 不会被编译成 web component,故无法在 html 中使用
  • 不能使用生命周期函数
  • 不会创建 DOM 节点
  • 无法使用 shadow DOM 和 scoped style,其实无法应用样式

这些特点,限制了函数组件的使用场景,除了 renderProp ,几乎无用。

renderProp 只能在 jsx 中使用。

How To Build Web Components Using Stencil JS

Creating Web Components with Stencil

你的前端框架要被 Web 组件取代了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/749237.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Web网页端IM产品RainbowChat-Web的v7.0版已发布

一、关于RainbowChat-Web RainbowChat-Web是一套Web网页端IM系统&#xff0c;是RainbowChat的姊妹系统&#xff08;RainbowChat是一套基于开源IM聊天框架 MobileIMSDK (Github地址) 的产品级移动端IM系统&#xff09;。 ► 详细介绍&#xff1a;http://www.52im.net/thread-2…

【Sklearn驯化-回归指标】一文搞懂机器学习中回归算法评估指标:mae、rmse等

【Sklearn驯化-回归指标】一文搞懂机器学习中回归算法评估指标&#xff1a;mae、rmse等 本次修炼方法请往下查看 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 免…

大学计算机

项目一 了解计算机 1.1 了解计算机的诞生及发展阶段 1.2 认识计算机的特点、应用和分类 1&#xff0e;计算机的特点 1. 计算机的特点 2.计算机的应用 3.计算机的分类 4.数量单位 1.3 了解计算机操作系统的概念、功能与种类 1.操作系统概念 2.操作系统的作用 1&#xff0e…

Linux 中变量的取用与设定

优质博文&#xff1a;IT-BLOG-CN Linux是一个多人多任务的环境&#xff0c;每个人登录系统都能取得一个bash shell&#xff0c;每个人都能够使用bash下达mail这个指令来接收自己的邮箱等等。问题是&#xff0c;bash如何得知你的邮箱是那个文件&#xff1f;这就需要『变量』的帮…

jmeter性能测试

一.jmeter基本使用 1.元件执行顺序 配置元件&#xff1b; 前置处理器&#xff1b; 定时器&#xff1b; sampler&#xff1b; 后置处理器&#xff1b;&#xff08;关联&#xff0c;正则表达式提取器&#xff09; 断言&#xff1b; 监听&#xff1b;&#xff08;不涉及顺序&…

Windows 电脑类别怎么区分?不同类别区分总结

电脑类别 Windows 电脑的类别有哪些&#xff1f;我们可以大致分为这三类&#xff1a;CopilotPC、AI PC、普通 PC。下面就来看看这些电脑类别的区别。 普通 PC 普通 PC 就是指那些标准的台式电脑或者笔记本电脑&#xff0c;它们是由中央处理器&#xff08;CPU&#xff09;以及…

【面试题】信息安全风险评估要做些什么

信息安全风险评估是识别、评估和管理信息系统中潜在风险的重要过程。它具有以下几个关键步骤&#xff1a; 1.资产识别&#xff1a; 确定需要保护的信息资产&#xff0c;如硬件、软件、数据、人员等。例如&#xff0c;企业的客户数据库、重要的业务文档等。 2.威胁评估&#…

手把手教你打造高精度STM32数字时钟,超详细步骤解析

STM32数字时钟项目详解 1. 项目概述 STM32数字时钟是一个集成了时间显示、闹钟功能、温湿度检测等多功能于一体的小型电子设备。它利用STM32的实时时钟(RTC)功能作为核心,配合LCD显示屏、按键输入、温湿度传感器等外设,实现了一个功能丰富的数字时钟系统。 2. 硬件组成 STM…

文献解读-基因编辑-第十二期|《CRISPR-detector:快速、准确地检测、可视化和注释基因组编辑事件引起的全基因组范围突变》

关键词&#xff1a;基因组变异检测&#xff1b;全基因组测序&#xff1b;基因编辑&#xff1b; 文献简介 标题&#xff08;英文&#xff09;&#xff1a;CRISPR-detector: fast and accurate detection, visualization, and annotation of genome-wide mutations induced by g…

做外贸有些事说早了,未必是好事

如果说能说话&#xff0c;其实谁也会&#xff0c;但是能把话说好却并不是一个简单的事&#xff0c;而且说话的时机往往也影响着事情的结局和走向&#xff0c; 所以才有了老人常提起的那句话&#xff1a;三岁学说话&#xff0c;一生学闭嘴。 最近我又因为图省事而犯了一个错误&…

云通SIPX,您的码号资源智能调度专家!

在数字化转型的浪潮中&#xff0c;号码资源作为企业与客户沟通的重要桥梁&#xff0c;其管理效率直接关系到企业运营的成败。随着运营商对号码资源管理的规范化和精细化&#xff0c;企业对高效、智能的号码资源管理需求日益增长&#xff0c;以实现对外呼叫的降本增效。 一、什么…

JAVA编程题期末题库【中】

8.计算邮资 程序代码: public static void main(String[] args) {// 计算邮资//if多分支语句//创建对象java.util.Scanner inputnew java.util.Scanner(System.in); //提示输入用户&#xff0c;输入邮件的重量System.out.println("邮件的重量&#xff1a;");int wei…

VMware ESXi 8.0U2c macOS Unlocker OEM BIOS Huawei (华为) FusionServer 定制版

VMware ESXi 8.0U2c macOS Unlocker & OEM BIOS Huawei (华为) FusionServer 定制版 ESXi 8.0U2 标准版&#xff0c;Dell (戴尔)、HPE (慧与)、Lenovo (联想)、Inspur (浪潮)、Cisco (思科)、Hitachi (日立)、Fujitsu (富士通)、NEC (日电)、Huawei (华为)、xFusion (超聚…

小型智能驱鸟器,建筑驱鸟专用

随着城市化进程的加快&#xff0c;鸟类与人类的居住空间逐渐交织重合&#xff0c;鸟类对建筑物的侵扰问题也愈发凸显。家庭庭院、住宅窗前、屋顶&#xff0c;甚至那些承载着历史与文化底蕴的名胜古迹和精美雕像&#xff0c;都时常受到鸟粪的污染。鸟粪具有腐蚀性且很难清理&…

智能疏散指示系统为什么是验收的必然考核标准?哪些厂家具备资质

智能疏散系统需要什么&#xff1f;现阶段&#xff0c;随着我国经济不断发展趋势的加快&#xff0c;住宅建筑具有复杂的特点。近年来&#xff0c;我国高层住宅、大中型建筑、综合性公共建筑越来越多。随着这座现代建筑的进步&#xff0c;我发现这种类型的建筑在发生火灾或事故时…

【高级篇】主从复制与高可用性:构建坚若磐石的数据库基础设施(十二)

引言 在上一章《备份与恢复》中,我们深入探讨了如何通过各种备份策略和恢复技术,确保数据的安全性和业务的连续性。然而,为了应对更大规模的业务挑战和灾难恢复需求,仅仅依靠备份是不够的。本章,我们将聚焦于MySQL的主从复制与高可用性技术,从原理到实践,从配置到优化,…

程序猿大战Python——Python与MySQL交互一

pymysql模块的安装 目标&#xff1a;了解如何安装pymysql模块&#xff1f; 当要使用Python和MySQL数据库进行交互&#xff0c;需要借助一个第三方模块&#xff1a;pymysql。 在使用pymysql模块前&#xff0c;先进行安装&#xff1a; pip install pymysql 有时使用pip instal…

iptables(11)target(SNAT、DNAT、MASQUERADE、REDIRECT)

简介 前面我们已经介绍了ACCEPT、DROP、REJECT、LOG,这篇文章我们介绍SNAT、DNAT、MASQUERADE、REDIRECT,这几个参数的定义我们在上篇文章中都有介绍,我这里再列出回顾一下 DNAT(目标地址转换)和 SNAT(源地址转换) 原理:修改数据包的源或目标 IP 地址。通常用于 NAT(…

制造业“智改数转”背景下,产品经理考取NPDP证书的重要性

在“智改数转”&#xff08;智能化改造和数字化转型&#xff09;已成为推动制造业高质量发展的关键路径背景下&#xff0c;作为连接市场需求与产品实现的关键角色&#xff0c;产品经理的角色愈发重要。而考取NPDP&#xff08;新产品开发专业人士认证&#xff09;证书&#xff0…

VBA 进度条(2)

1.前提 1-1. 在VBA编辑器找到工具-引用-勾选MicroSoft Visual Basic for Applications Extensibility Library 1-2. 信任中心 -> 宏设置 -> 开发人员宏设置 -> 选中“信任对VBA工程对象模型的访问” 2.类模块 Private objApp As Object Private u…