当前位置: 首页 > 工具软件 > Stencil > 使用案例 >

【stencil】stencil快速上手

宇文弘懿
2023-12-01

官网

  • 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
 类似资料: