大纲
Decorators 装饰器
- @Component() 生命一个新的web组件
- @Prop() 声明一个公开的属性
- @State() 声明组件的内部状态
- @Watch() 声明一个在属性或状态改变时运行的钩子
- @Element() 声明对宿主元素的引用
- @Method() 声明一个公开的公共方法
- @Event() 声明组件可能发出的 DOM 事件
- @Listen() 监听 DOM 事件
生命周期钩子
- connectedCallback()
- disconnectedCallback()
- componentWillLoad()
- componentDidLoad()
- componentShouldUpdate(newValue, oldValue, propName): boolean
- componentWillRender()
- componentDidRender()
- componentWillUpdate()
- componentDidUpdate()
- render()
应用加载事件
window.addEventListener('appload', (event) => {
console.log(event.detail.namespace);
});
其他(渲染方法)
- Host: Host 是一个功能性组件,可以在渲染函数的根部使用,为宿主元素本身设置属性和事件监听器。
- h(): 它用于render()将 JSX 转换为虚拟 DOM 元素。
- readTask(): 安排一个 DOM 读取任务。提供的回调将在执行 DOM 读取的最佳时刻执行,而不会导致布局抖动。
- writeTask(): 安排一个 DOM 写入任务。提供的回调将在执行 DOM 更改的最佳时刻执行,而不会导致布局抖动。
- forceUpdate(): forceUpdate()不是同步的,可能会在下一帧中执行 DOM 渲染。
- getAssetPath(): 获取Assets的路径
- setMode()
- getMode()
- getElement()
装饰器
Component
示例
import { Component } from '@stencil/core';
@Component({
tag: 'todo-list',
styleUrl: 'todo-list.css'
})
export class TodoList {
}
可选参数ß
@Component(opts: ComponentOptions)需要一个包含所有组件级功能的必需对象。
export interface ComponentOptions {
/**
* 标记名:全局唯一,必须包含"-"
*
*/
tag: string;
/**
* 样式作用域
*/
scoped?: boolean;
/**
* 是否使用本机阴影dom封装
*/
shadow?: boolean | { delegatesFocus: boolean };
/**
* 样式文件相对路径
*/
styleUrl?: string;
/**
* 为不同模块指定不同的样式表
*/
styleUrls?: string[] | d.ModeStyles;
/**
* 内联CSS,只支持css语法,不支持less和sass
*/
styles?: string;
/**
* 资源文件夹相对路径
*/
assetsDirs?: string[];
/**
* @deprecated Use `assetsDirs` instead
*/
assetsDir?: string;
}
嵌套组件
通过将 HTML 标记添加到 JSX 代码,可以轻松组合组件。
示例:
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>
);
}
}
Prop
Props 将数据从父组件向下传递到子组件。组件需要使用@Prop()装饰器显式声明它们期望接收的 Props 。可以是number, string, boolean,甚至是Object或Array
示例
// TodoList.tsx
import { Prop } from '@stencil/core';
import { MyHttpService } from '../some/local/directory/MyHttpService';
...
export class TodoList {
@Prop() color: string;
@Prop() favoriteNumber: number;
@Prop() isSelected: boolean;
@Prop() myHttpService: MyHttpService;
}
在TodoList类中,Props 是通过this操作符访问的。
logColor() {
console.log(this.color)
}
在外部,Props设置在元素上。
在 HTML 中,您必须使用破折号-来设置属性:
<todo-list color="blue" favorite-number="24" is-selected="true"></todo-list>
在 JSX 中,你使用驼峰命名法设置一个属性:
<todo-list color="blue" favoriteNumber={24} isSelected="true"></todo-list>
它们也可以通过 JS 从元素访问。
const todoListElement = document.querySelector('todo-list');
console.log(todoListElement.myHttpService); // MyHttpService
console.log(todoListElement.color); // blue
可选参数
export interface PropOptions {
attribute?: string;
mutable?: boolean;
reflect?: boolean;
}
props 可变性
_默认情况下_Prop在组件逻辑内部是不可变的。一旦用户设置了一个值,组件就不能在内部更新它。但是,可以声明为mutable来显式允许从组件内部对 Prop 进行变异
import { Prop } from '@stencil/core';
...
export class NameElement {
@Prop({ mutable: true }) name: string = 'Stencil';
componentDidLoad() {
this.name = 'Stencil 0.7.0';
}
}
Attribute 名称
可以使用装饰器的attribute选项更改此“默认”行为@Prop()。通过使用此选项,我们可以明确哪些属性具有关联的 DOM 属性及其名称。
import { Component, Prop } from '@stencil/core';
@Component({ tag: 'my-cmp' })
class Component {
@Prop() value: string;
@Prop({ attribute: 'valid' }) isValid: boolean;
@Prop({ attribute: 'controller' }) controller: MyController;
}
Reflect Properties Values to Attributes
当“prop”设置为“reflect”时,这意味着它们的值将在 DOM 中作为 HTML 属性呈现:
@Prop({
reflect: true
})
@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>
props默认值和验证
设置默认值
import { Prop } from '@stencil/core';
...
export class NameElement {
@Prop() name: string = 'Stencil';
}
验证prop可以使用@Watch() 装饰器
import { Prop, Watch } from '@stencil/core';
...
export class TodoList {
@Prop() name: string = 'Stencil';
@Watch('name')
validateName(newValue: string, oldValue: string) {
const isBlank = typeof newValue !== 'string' || newValue === '';
const has2chars = typeof newValue === 'string' && newValue.length >= 2;
if (isBlank) { throw new Error('name: required') };
if (!has2chars) { throw new Error('name: has2chars') };
}
}
State
State装饰器用来管理内部组件,不能从外部修改。对@State()属性的任何更改都会导致组件render函数再次被调用。
示例
用@State定义一个属性open, @Listen响应点击事件切换的值。
import { Component, State, Listen, h } from '@stencil/core';
@Component({
tag: 'my-toggle-button'
})
export class MyToggleButton {
@State() open: boolean;
@Listen('click', { capture: true })
handleClick() {
this.open = !this.open;
}
render() {
return <button>
{this.open ? "On" : "Off"}
</button>;
}
}
维护一个Todo类型值列表。
import { State } from '@stencil/core';
type Todo = {
done: boolean,
description: string,
}
export class TodoList {
@State() completedTodos: Todo[];
completeTodo(todo: Todo) {
// This will cause our render function to be called again
this.completedTodos = [...this.completedTodos, todo];
}
}
并非所有内部状态都可能需要用 装饰@State(),事实上,如果您确定该值不会更改或不需要触发重新渲染,则避免使用它是一个很好的做法
class Component {
// If `cacheData` changes we don't want to rerender the component,
// so we DON'T decorate it with @State
cacheData = SOME_BIG_DATA;
// If this state change we want to run render() again
@State() value;
}
Event
生命周期
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.
**