pnpm init stencil
npm run start
npm run generate
命令生成组件。-
/*
* @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>
);
}
}
<!--
* @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()装饰器进行装饰@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>
);
}
}
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>;
}
}
@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>;
}
}
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);
}
}
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
}
}
}
export interface ListenOptions {
target?: 'body' | 'document' | 'window';
capture?: boolean;
passive?: boolean;
}
@Listen('scroll', { target: 'window' })
handleScroll(ev) {
console.log('the body was scrolled', ev);
}
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();
})();
// 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;
}
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>;
}
}
::part
选择器