大纲

Decorators 装饰器

  • @Component() 生命一个新的web组件
  • @Prop() 声明一个公开的属性
  • @State() 声明组件的内部状态
  • @Watch() 声明一个在属性或状态改变时运行的钩子
  • @Element() 声明对宿主元素的引用
  • @Method() 声明一个公开的公共方法
  • @Event() 声明组件可能发出的 DOM 事件
  • @Listen() 监听 DOM 事件

生命周期钩子

image.png

  • connectedCallback()
  • disconnectedCallback()
  • componentWillLoad()
  • componentDidLoad()
  • componentShouldUpdate(newValue, oldValue, propName): boolean
  • componentWillRender()
  • componentDidRender()
  • componentWillUpdate()
  • componentDidUpdate()
  • render()

应用加载事件

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

其他(渲染方法)

  • Host: Host 是一个功能性组件,可以在渲染函数的根部使用,为宿主元素本身设置属性和事件监听器。
  • h(): 它用于render()将 JSX 转换为虚拟 DOM 元素。
  • readTask(): 安排一个 DOM 读取任务。提供的回调将在执行 DOM 读取的最佳时刻执行,而不会导致布局抖动。
  • writeTask(): 安排一个 DOM 写入任务。提供的回调将在执行 DOM 更改的最佳时刻执行,而不会导致布局抖动。
  • forceUpdate(): forceUpdate()不是同步的,可能会在下一帧中执行 DOM 渲染。
  • getAssetPath(): 获取Assets的路径
  • setMode()
  • getMode()
  • getElement()

装饰器

Component

示例

  1. import { Component } from '@stencil/core';
  2. @Component({
  3. tag: 'todo-list',
  4. styleUrl: 'todo-list.css'
  5. })
  6. export class TodoList {
  7. }

可选参数ß

@Component(opts: ComponentOptions)需要一个包含所有组件级功能的必需对象。

  1. export interface ComponentOptions {
  2. /**
  3. * 标记名:全局唯一,必须包含"-"
  4. *
  5. */
  6. tag: string;
  7. /**
  8. * 样式作用域
  9. */
  10. scoped?: boolean;
  11. /**
  12. * 是否使用本机阴影dom封装
  13. */
  14. shadow?: boolean | { delegatesFocus: boolean };
  15. /**
  16. * 样式文件相对路径
  17. */
  18. styleUrl?: string;
  19. /**
  20. * 为不同模块指定不同的样式表
  21. */
  22. styleUrls?: string[] | d.ModeStyles;
  23. /**
  24. * 内联CSS,只支持css语法,不支持less和sass
  25. */
  26. styles?: string;
  27. /**
  28. * 资源文件夹相对路径
  29. */
  30. assetsDirs?: string[];
  31. /**
  32. * @deprecated Use `assetsDirs` instead
  33. */
  34. assetsDir?: string;
  35. }

嵌套组件

通过将 HTML 标记添加到 JSX 代码,可以轻松组合组件。
示例:

  1. import { Component, Prop, h } from '@stencil/core';
  2. @Component({
  3. tag: 'my-embedded-component'
  4. })
  5. export class MyEmbeddedComponent {
  6. @Prop() color: string = 'blue';
  7. render() {
  8. return (
  9. <div>My favorite color is {this.color}</div>
  10. );
  11. }
  12. }

Prop

Props 将数据从父组件向下传递到子组件。组件需要使用@Prop()装饰器显式声明它们期望接收的 Props 。可以是number, string, boolean,甚至是Object或Array

示例

  1. // TodoList.tsx
  2. import { Prop } from '@stencil/core';
  3. import { MyHttpService } from '../some/local/directory/MyHttpService';
  4. ...
  5. export class TodoList {
  6. @Prop() color: string;
  7. @Prop() favoriteNumber: number;
  8. @Prop() isSelected: boolean;
  9. @Prop() myHttpService: MyHttpService;
  10. }

在TodoList类中,Props 是通过this操作符访问的。

  1. logColor() {
  2. console.log(this.color)
  3. }

在外部,Props设置在元素上。

在 HTML 中,您必须使用破折号-来设置属性:

  1. <todo-list color="blue" favorite-number="24" is-selected="true"></todo-list>

在 JSX 中,你使用驼峰命名法设置一个属性:

  1. <todo-list color="blue" favoriteNumber={24} isSelected="true"></todo-list>

它们也可以通过 JS 从元素访问。

  1. const todoListElement = document.querySelector('todo-list');
  2. console.log(todoListElement.myHttpService); // MyHttpService
  3. console.log(todoListElement.color); // blue

可选参数

  1. export interface PropOptions {
  2. attribute?: string;
  3. mutable?: boolean;
  4. reflect?: boolean;
  5. }

props 可变性

_默认情况下_Prop在组件逻辑内部是不可变的。一旦用户设置了一个值,组件就不能在内部更新它。但是,可以声明为mutable来显式允许从组件内部对 Prop 进行变异

  1. import { Prop } from '@stencil/core';
  2. ...
  3. export class NameElement {
  4. @Prop({ mutable: true }) name: string = 'Stencil';
  5. componentDidLoad() {
  6. this.name = 'Stencil 0.7.0';
  7. }
  8. }

Attribute 名称

可以使用装饰器的attribute选项更改此“默认”行为@Prop()。通过使用此选项,我们可以明确哪些属性具有关联的 DOM 属性及其名称。

  1. import { Component, Prop } from '@stencil/core';
  2. @Component({ tag: 'my-cmp' })
  3. class Component {
  4. @Prop() value: string;
  5. @Prop({ attribute: 'valid' }) isValid: boolean;
  6. @Prop({ attribute: 'controller' }) controller: MyController;
  7. }

Reflect Properties Values to Attributes

当“prop”设置为“reflect”时,这意味着它们的值将在 DOM 中作为 HTML 属性呈现:

  1. @Prop({
  2. reflect: true
  3. })
  1. @Component({ tag: 'my-cmp' })
  2. class Cmp {
  3. @Prop({ reflect: true }) message = 'Hello';
  4. @Prop({ reflect: false }) value = 'The meaning of life...';
  5. @Prop({ reflect: true }) number = 42;
  6. }
  1. <my-cmp message="Hello" number="42"></my-cmp>

props默认值和验证

设置默认值

  1. import { Prop } from '@stencil/core';
  2. ...
  3. export class NameElement {
  4. @Prop() name: string = 'Stencil';
  5. }

验证prop可以使用@Watch() 装饰器

  1. import { Prop, Watch } from '@stencil/core';
  2. ...
  3. export class TodoList {
  4. @Prop() name: string = 'Stencil';
  5. @Watch('name')
  6. validateName(newValue: string, oldValue: string) {
  7. const isBlank = typeof newValue !== 'string' || newValue === '';
  8. const has2chars = typeof newValue === 'string' && newValue.length >= 2;
  9. if (isBlank) { throw new Error('name: required') };
  10. if (!has2chars) { throw new Error('name: has2chars') };
  11. }
  12. }

State

State装饰器用来管理内部组件,不能从外部修改。对@State()属性的任何更改都会导致组件render函数再次被调用。

示例

用@State定义一个属性open, @Listen响应点击事件切换的值。

  1. import { Component, State, Listen, h } from '@stencil/core';
  2. @Component({
  3. tag: 'my-toggle-button'
  4. })
  5. export class MyToggleButton {
  6. @State() open: boolean;
  7. @Listen('click', { capture: true })
  8. handleClick() {
  9. this.open = !this.open;
  10. }
  11. render() {
  12. return <button>
  13. {this.open ? "On" : "Off"}
  14. </button>;
  15. }
  16. }

维护一个Todo类型值列表。

  1. import { State } from '@stencil/core';
  2. type Todo = {
  3. done: boolean,
  4. description: string,
  5. }
  6. export class TodoList {
  7. @State() completedTodos: Todo[];
  8. completeTodo(todo: Todo) {
  9. // This will cause our render function to be called again
  10. this.completedTodos = [...this.completedTodos, todo];
  11. }
  12. }

并非所有内部状态都可能需要用 装饰@State(),事实上,如果您确定该值不会更改或不需要触发重新渲染,则避免使用它是一个很好的做法

  1. class Component {
  2. // If `cacheData` changes we don't want to rerender the component,
  3. // so we DON'T decorate it with @State
  4. cacheData = SOME_BIG_DATA;
  5. // If this state change we want to run render() again
  6. @State() value;
  7. }

Event

生命周期

  1. const el = document.createElement('my-cmp');
  2. document.body.appendChild(el);
  3. // connectedCallback() called
  4. // componentWillLoad() called (first time)
  5. el.remove();
  6. // disconnectedCallback()
  7. document.body.appendChild(el);
  8. // connectedCallback() called again, but `componentWillLoad()` is not.

**