官网
- https://stenciljs.com/docs/api
初始项目
pnpm init stencil
- 选择component
- 安装完毕后启动
npm run start
- 页面出现helloworld即可。
新增组件
- 使用
npm run generate
命令生成组件。 - 注意!组件名要带着
-
- 往里面加个btn
/*
* @Author: yehuozhili
* @Date: 2022-04-11 22:53:51
* @LastEditors: yehuozhili
* @LastEditTime: 2022-04-11 22:55:30
* @FilePath: \ss\src\components\yehuozhili-a\yehuozhili-a.tsx
*/
import { Component, Host, h } from '@stencil/core';
@Component({
tag: 'yehuozhili-a',
styleUrl: 'yehuozhili-a.css',
shadow: true,
})
export class YehuozhiliA {
render() {
return (
<Host>
<button class="mybtn">
<slot></slot>
</button>
</Host>
);
}
}
- index.html
<!--
* @Author: yehuozhili
* @Date: 2022-04-11 22:40:28
* @LastEditors: yehuozhili
* @LastEditTime: 2022-04-11 22:56:48
* @FilePath: \ss\src\index.html
-->
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0" />
<title>Stencil Component Starter</title>
<script type="module" src="/build/ssss.esm.js"></script>
<script nomodule src="/build/ssss.js"></script>
</head>
<body>
<my-component first="Stencil" last="'Don't call me a framework' JS"></my-component>
<yehuozhili-a>你好</yehuozhili-a>
</body>
</html>
- 即可看见按钮已被更改。
装饰器使用
@Component
-
每个模板组件都必须使用包中的@Component()装饰器进行装饰@stencil/core。在最简单的情况下,开发人员必须为组件提供一个 HTMLtag名称。很多时候,styleUrl也被使用,甚至styleUrls,其中可以为不同的应用程序模式/主题提供多个不同的样式表。
-
组件导入,由于组件只是标签所以不需要引入:
import { Component, Prop, h } from '@stencil/core';
@Component({
tag: 'my-embedded-component'
})
export class MyEmbeddedComponent {
@Prop() color: string = 'blue';
render() {
return (
<div>My favorite color is {this.color}</div>
);
}
}
import { Component, h } from '@stencil/core';
@Component({
tag: 'my-parent-component'
})
export class MyParentComponent {
render() {
return (
<div>
<my-embedded-component color="red"></my-embedded-component>
</div>
);
}
}
@Prop
- @Prop()是在 HTML 元素上公开公开的自定义属性/属性。它们允许开发人员将数据传递给组件以进行渲染或以其他方式使用。
- prop跟react一样是不可变的。但是可以显示添加mutable: true为可变
- reflect: true属性比较特别点,为true时会在标签中把该属性映射上去。
@State
- 与常规的 TypeScript 类一样,Stencil 组件可能具有一个或多个内部类成员,用于保存构成组件状态的值。Stencil 允许开发人员选择使用装饰器标记持有类状态的某些部分的类成员,@State()以便在状态更改时触发重新渲染。
import { Component, h, State } from '@stencil/core';
@Component({
tag: 'yehuozhili-a',
styleUrl: 'yehuozhili-a.css',
shadow: true,
})
export class CurrentTime {
// Second, we decorate a class member with @State()
// When `currentTime` changes, a rerender will be
// triggered
@State() currentTime: number = Date.now();
render() {
// Within the component's class, its members are
// accessed via `this`. This allows us to render
// the value stored in `currentTime`
const time = new Date(this.currentTime).toLocaleTimeString();
return <span>{time}</span>;
}
}
- 与listen结合使用:
@Component({
tag: 'my-toggle-button'
})
export class MyToggleButton {
// `isOpen` is decorated with `@State()`,
// changes to it will trigger a rerender
@State() isOpen
: boolean = true;
@Listen('click', { capture: true })
handleClick() {
// whenever a click event occurs on
// the component, update `isOpen`,
// triggering the rerender
this.isOpen = !this.isOpen;
}
render() {
return <button>
{this.isOpen ? "Open" : "Closed"}
</button>;
}
}
@Watch
- 当组件上的prop或state发生变化时,模板组件会更新。为了性能和简单性,Stencil 仅比较更改的引用,并且不会在数组或对象内的数据发生更改时重新呈现。
import { Prop, State, Watch } from '@stencil/core';
export class LoadingIndicator {
@Prop() activated: boolean;
@State() busy: boolean;
@Watch('activated')
watchPropHandler(newValue: boolean, oldValue: boolean) {
console.log('The new value of activated is: ', newValue);
}
@Watch('busy')
watchStateHandler(newValue: boolean, oldValue: boolean) {
console.log('The new value of busy is: ', newValue);
}
}
@Event
- 没有模板事件之类的东西,相反,模板鼓励使用DOM 事件。但是,Stencil 确实提供了一个 API 来指定组件可以发出的事件以及组件侦听的事件。它使用Event()和Listen()装饰器这样做。
export interface EventOptions {
/**
* A string custom event name to override the default.
*/
eventName?: string;
/**
* A Boolean indicating whether the event bubbles up through the DOM or not.
*/
bubbles?: boolean;
/**
* A Boolean indicating whether the event is cancelable.
*/
cancelable?: boolean;
/**
* A Boolean value indicating whether or not the event can bubble across the boundary between the shadow DOM and the regular DOM.
*/
composed?: boolean;
}
- 例子:
import { Event, EventEmitter } from '@stencil/core';
...
export class TodoList {
// Event called 'todoCompleted' that is "composed", "cancellable" and it will bubble up!
@Event({
eventName: 'todoCompleted',
composed: true,
cancelable: true,
bubbles: true,
}) todoCompleted: EventEmitter<Todo>;
todoCompletedHandler(todo: Todo) {
const event = this.todoCompleted.emit(todo);
if(!event.defaultPrevented) {
// if not prevented, do some default handling code
}
}
}
@Listen
- 装饰器Listen()用于监听 DOM 事件,包括从@Events.
export interface ListenOptions {
target?: 'body' | 'document' | 'window';
capture?: boolean;
passive?: boolean;
}
- 比如监听window的scroll事件:
@Listen('scroll', { target: 'window' })
handleScroll(ev) {
console.log('the body was scrolled', ev);
}
@Method
- Stencil 提供了一个 API 来指定组件可以发出的事件以及组件侦听的事件。
- 开发人员应尽量少依赖公开暴露的方法,而尽可能默认使用属性和事件。随着应用程序的扩展,我们发现通过 @Prop 而不是公共方法来管理和传递数据更容易。
import { Method } from '@stencil/core';
export class TodoList {
@Method()
async showPrompt() {
// show a prompt
}
}
- 调用:
(async () => {
await customElements.whenDefined('todo-list');
const todoListElement = document.querySelector('todo-list');
await todoListElement.showPrompt();
})();
- 被methods装饰的方法需要异步:
// VALID: using async
@Method()
async myMethod() {
return 42;
}
// VALID: using Promise.resolve()
@Method()
myMethod2() {
return Promise.resolve(42);
}
// VALID: even if it returns nothing, it needs to be async
@Method()
async myMethod3() {
console.log(42);
}
// INVALID
@Method()
notOk() {
return 42;
}
@Element
- element可以返回htmlelement实例
import { Element } from '@stencil/core';
...
export class TodoList {
@Element() el: HTMLElement;
getListHeight(): number {
return this.el.getBoundingClientRect().height;
}
}
生命周期
- 生命周期官网写的比较详细了。
connectedCallback()
disconnectedCallback()
componentWillLoad()
componentDidLoad()
componentShouldUpdate(newValue, oldValue, propName): boolean
componentWillRender()
componentDidRender()
componentWillUpdate()
componentDidUpdate()
render()
- 大概看一下图就明白了
import { Component, h, State } from '@stencil/core';
@Component({
tag: 'yehuozhili-a',
styleUrl: 'yehuozhili-a.css',
shadow: true,
})
export class CustomClock {
timer: number;
@State() time: number = Date.now();
connectedCallback() {
this.timer = window.setInterval(() => {
this.time = Date.now();
}, 1000);
}
disconnectedCallback() {
window.clearInterval(this.timer);
}
render() {
const time = new Date(this.time).toLocaleTimeString();
return <span>{time}</span>;
}
}
- render函数中如果外层是host,这个host会映射到最外层的标签上。
样式
- 这里值得说的是
::part
选择器 - 在shodowdom下可以使用。
- 参见mdn :https://developer.mozilla.org/en-US/docs/Web/CSS/::part