stencil 组件

news2024/12/23 19:22:25

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.coloradmin.cn/o/1868850.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第 3 章使用图像和标签

现在您已经对 SwiftUI 有了基本的了解,并了解了如何显示文本内容,现在是时候学习如何在应用中显示图像了。在本章中,我们将探讨Label最常见的用户界面组件之一的用法,以及Image用于在屏幕上渲染图像的视图。与上一章中所做的类似,我将通过构建一个简单的演示向您展示如何使…

Navicat 外网连接 mysql (1、通过SSH方式内网访问 2、对外开放3306端口)

1、通过SSH方式内网访问 直接常规方式使用IP、账号密码连接&#xff0c;失败 SSH方式&#xff1a; 常规 选项卡中&#xff1a;localhost录入数据库账号密码 SSH 选项卡中&#xff1a;勾选使用SSH&#xff0c;输入服务器IP、账号、密码 如果出现该错误&#xff0c;可能是服务器…

会计报表分析

目录 一. 会计报表的种类 \quad 一. 会计报表的种类 \quad 反应财务状况的是资产负债表 反应经营成果的是利润表 有时间点的就是静态表 动态表就是有一个区间的, 比如一年, 一个季度等

PLC网关如何选择?plc网关作用-天拓四方

一、PLC网关在工业自动化领域的重要性和作用 PLC网关在工业自动化领域的重要性和作用不言而喻。作为工业自动化系统的重要组成部分&#xff0c;PLC网关起到了关键的桥梁作用&#xff0c;实现了PLC与其他设备、系统之间的数据传输和通信。 首先&#xff0c;PLC网关的重要性体现…

5G赋能安防视频监控:EasyCVR视频汇聚融合创新技术,共筑多场景安全防线

随着科技的快速发展&#xff0c;第五代移动通信技术&#xff08;5G&#xff09;已逐渐成为我们生活中的重要组成部分。其中&#xff0c;5G技术以其超高速、低延迟、大连接数的特点&#xff0c;正在深刻改变着我们的生活方式和社会运行模式。安防监控领域作为社会安全的重要组成…

如何实现智慧农田的精准灌溉

如何实现智慧农田的精准灌溉 智慧农田的精准灌溉是现代农业技术发展的重要组成部分&#xff0c;它集成了物联网、大数据分析、人工智能以及现代水利技术&#xff0c;旨在通过实时监测土壤湿度、气象条件及作物生长状况&#xff0c;实现水资源的高效利用和作物产量、品质的双重…

智慧海洋灾害监测预警系统解决方案

一、概述 近年来&#xff0c;我国海洋经济持续增长&#xff0c;为我国综合国力带来了新机遇。但是&#xff0c;每年由于海洋灾害带来的损失也不可忽略&#xff0c;这些灾害包括&#xff1a;风暴潮、海浪、海冰、海啸动力环境灾害、赤潮、绿潮等等。针对目前海洋监管力量薄弱&am…

Ubuntu20.04离线安装Docker

链接&#xff1a;https://pan.baidu.com/s/10OLClTHSIJY-_nbldcoFFQ 提取码&#xff1a;x4nt --来自百度网盘超级会员V4的分享 1.下载3个docker离线安装包&#xff0c;下载网址&#xff1a; https://download.docker.com/linux/ubuntu/dists/xenial/pool/stable/amd64/ 2.把…

怎么隐藏宝塔面板左上角绑定的手机号码?

宝塔面板后台的左上角会显示我们绑定的宝塔账号&#xff08;手机号码&#xff09;&#xff0c;每次截图的时候都要去抹掉这个号码&#xff0c;那么能不能直接将这个手机号码隐藏掉呢&#xff1f; 如上图红色箭头所示的手机号码&#xff0c;其实就是我们绑定的宝塔账号&#xff…

Android Studio无法正确引入包内存在的类

Android Studio 无法识别同一个 package 里的类&#xff0c;显示为红色&#xff0c;但是 compile 没有问题。 重启&#xff0c;rebuild,clean都没有用。 多半是因为 Android Studio 之前发生了错误&#xff0c;某些 setting 出了问题。 解决方法如下&#xff1a; 点击菜单中的…

生命在于折腾——Macbook虚拟机开启360核晶

首先启动PD虚拟机&#xff0c;打开360&#xff0c;发现提示如下&#xff1a; 此时将虚拟机关机。 打开该虚拟机设置&#xff1a; 将虚拟机监控程序改为Parallels&#xff0c;并启动nested虚拟化。 改好后截图如下&#xff1a; 保存设置&#xff0c;开机 此时就可以开启了…

执行yum命令报错Could not resolve host: mirrors.cloud.aliyuncs.com; Unknown error

执行yum命令报错 [Errno 14] curl#6 - "Could not resolve host: mirrors.cloud.aliyuncs.com; Unknown error 修改图中所示两个文件&#xff1a; vim epel.repo vim CentOS-Base.repo 将所有的http://mirrors.cloud.aliyuncs.com 修改为http://mirrors.aliyun.com。 修改…

【漏洞复现】FastAdmin——任意文件读取漏洞

声明&#xff1a;本文档或演示材料仅供教育和教学目的使用&#xff0c;任何个人或组织使用本文档中的信息进行非法活动&#xff0c;均与本文档的作者或发布者无关。 文章目录 漏洞描述漏洞复现测试工具 漏洞描述 FastAdmin是一个免费开源的后台管理框架&#xff0c;其lang存在…

多功能气象传感器的工作原理

TH-WQX9多功能气象传感器是一种集成了多种传感器技术的气象观测装置&#xff0c;旨在同时测量和监测大气中的多个气象要素&#xff0c;以提供全面、准确的气象信息。以下是关于多功能气象传感器的详细介绍&#xff1a; 技术原理 多功能气象传感器采用多种传感器技术相结合&…

6月27日-四象限法则

四象限法则&#xff0c;又称为艾森豪威尔矩阵&#xff08;Eisenhower Matrix&#xff09;&#xff0c;是一种时间管理和任务优先级排序的方法。它将任务分为四个象限&#xff0c;帮助个人识别哪些任务最重要&#xff0c;哪些可以推迟或委托&#xff0c;以及哪些可以完全忽略。以…

测试:MyBatisDemo

MyBatis Demo 先创建一个 Maven 项目&#xff0c;名称为 MyBatis01。 之后创建并连接 MySQL 数据库&#xff0c;然后执行 sql 脚本&#xff1a; CREATE DATABASE mybatis;USE mybatis;DROP TABLE IF EXISTS user;CREATE TABLE user ( id int(20) NOT NULL, name varchar(30)…

怎么在vscode里运行一个cpp文件

文章目录 1.需要下载g编译器&#xff0c;或clang&#xff08;快&#xff0c;但是优化效果没有g好&#xff09;2.新建文件夹和cpp文件&#xff08;tasks.json&#xff09;3.怎么在vscode里调试(launch.json)4.怎么设置让中断输出的字符是中文&#xff01;5.飞机大战 1.需要下载g…

功能全,性能强,桌面型拓展坞,奥睿科11合1硬盘拓展坞分享:

现在很多人追求轻便办公&#xff0c;MINI PC 和笔记本电脑成为办公首选。 虽然PC越做越小巧&#xff0c;确实带来了便利&#xff0c;但随之而来的问题是&#xff0c;许多常用接口都被舍弃了&#xff0c;甚至很多的MINI PC 和笔记本都没有任何的拓展功能。 近些年&#xff0c;…

STM32之IIC(软件)

介绍 IIC &#xff08; 又称为 I2C 或 IC &#xff09;是一种串行通信协议&#xff0c; IIC使用两根线路来进行通信&#xff1a; 串行数据线&#xff08;SDA&#xff09; 和 串行时钟线&#xff08;SCL&#xff09; 。 SDA 线上的数据在 SCL 线的时钟信号下进行 同步传输。 主…

微信公众号写作时必备的AI提示词(也称为指令或Prompt)

猫头虎 &#x1f42f; 微信公众号写作时必备的AI提示词&#xff08;也称为指令或Prompt&#xff09; &#x1f389; 大家好&#xff0c;我是猫头虎&#xff0c;科技自媒体博主。今天&#xff0c;我们来聊聊如何利用AI提示词&#xff0c;打造出爆款的微信公众号文章。&#x1…