Stencil 组件使用 JSX 渲染,这是一种流行的声明式模板语法。每个组件都有一个渲染函数,它返回在运行时渲染到 DOM 的组件树。

基础用法

render 函数用于输出将绘制到屏幕上的组件树。

class MyComponent {
render() {
return (
<div>
<h1>Hello World</h1>
<p>This is JSX!</p>
</div>
);
}
}

在这个例子中,我们返回了一个 div ,它有两个子元素 h1p

Host Element

如果你想修改宿主元素本身,比如向组件本身添加一个类或一个属性,可以使用 Host 组件。

数据绑定

组件经常需要渲染动态数据。要在 JSX 中执行此操作,请使用 {}

render() {
return (
<div>Hello {this.name}</div>
)
}

如果你熟悉 ES6 模板变量,JSX 变量和 ES6 非常相似,只是没有 $ 符号。

//ES6
`Hello ${this.name}` //JSX
Hello {this.name}

条件

如果你想根据不同的条件渲染不同的内容,可以使用 if/else 来实现

render() {
if (this.name) {
return ( <div>Hello {this.name}</div> )
} else {
return ( <div>Hello, World</div> )
}
}

此外,可以使用 JavaScript 的三元运算符来创建内联条件

render() {
return (
<div>
{this.name ? <p>Hello {this.name}</p> : <p>Hello World</p> }
</div>
);
}

请注意:Stencil 重用 DOM 元素以获得更好的性能。请看下面的代码:

{
someCondition ? (
<my-counter initialValue={2} />
) : (
<my-counter initialValue={5} />
);
}

上面的代码与下面的代码完全相同:

<my-counter initialValue={someCondition ? 2 : 5} />

因此,如果某些条件发生了变化,my-counter的内部状态不会被重置,它的生命周期方法(如 componentWillLoad())也不会被触发。相反,条件语句只会触发同一个组件的更新。

如果你想在一个条件语句中销毁并重新创建一个组件,你可以指定 key 属性。这告诉 Stencil,这些组件实际上是不同的兄弟组件:

{
someCondition ? (
<my-counter key="a" initialValue={2} />
) : (
<my-counter key="b" initialValue={5} />
);
}

这样,如果某些条件发生变化,你会得到一个新的 my-counter 组件实例,它具有新的内部状态,同时也将会同步运行生命周期 componentWillLoad()componentDidLoad()

Slots

组件通常需要在其组件树的特定位置动态渲染子组件,允许开发人员在使用我们的组件时提供子内容,我们的组件将子组件放置在适当的位置。

要做到这一点,您可以在 my-component 中使用 Slot 标签。

// my-component.tsx
render() {
return (
<div>
<h2>A Component</h2>
<div><slot /></div>
</div>
);
}

然后,如果用户在创建组件 my-component 时传递子组件,那么 my-component 将把该组件放在上面第二层的 div 中:

render(){
return(
<my-component>
<p>Child Element</p>
</my-component>
)
}

slot 可以增加 name 属性,来决定内容的输出位置:

// my-component.tsx

render(){
return [
<slot name="item-start" />,
<h1>Here is my main content</h1>,
<slot name="item-end" />
]
} render(){
return(
<my-component>
<p slot="item-start">I'll be placed before the h1</p>
<p slot="item-end">I'll be placed after the h1</p>
</my-component>
)
}

Dealing with Children

JSX 中节点的子节点在运行时对应于一个节点数组,无论它们是通过 array.prototype.map 跨数组创建的,还是直接在 JSX 中声明为兄弟节点。这意味着在运行时,下面两个顶级

div 的子元素(.Todo-one 和.todo-two)的表示方式相同:

render() {
return (
<>
<div class="todo-one">
{this.todos.map((todo) => (
<span>{ todo.taskName }</span>
)}
</div>
<div class="todo-two">
<span>{ todos[0].taskName }</span>
<span>{ todos[1].taskName }</span>
</div>
</>
)
}

如果这个子元素数组是动态的,即任何节点都可以被添加、删除或重新排序,那么最好为每个元素设置一个唯一的 key 属性,如下所示:

render() {
return (
<div>
{this.todos.map((todo) =>
<div key={todo.uid}>
<div>{todo.taskName}</div>
</div>
)}
</div>
)
}

当子数组中的节点被重新排列时,Stencil 会努力在渲染时保留 DOM 节点,但它不能在所有情况下都这样做。设置一个 key 属性可以让 Stencil 确保在渲染时能够匹配新旧子节点,从而避免

不必要地重新创建 DOM 节点。

不要使用数组索引或其他非唯一值作为键。尝试确保每个子节点都有一个不变的 key,并且在其所有兄弟节点中是唯一的。

处理用户输入

Stencil 使用原生的 DOM 事件。

下面是一个处理按钮点击的例子。注意箭头函数的使用。

...
export class MyComponent {
private handleClick = () => {
alert('Received the button click!');
} render() {
return (
<button onClick={this.handleClick}>Click Me!</button>
);
}
}

这是另一个监听输入变化的例子。注意箭头函数的使用。

...
export class MyComponent {
private inputChanged = (event: Event) => {
console.log('input changed: ', (event.target as HTMLInputElement).value);
} render() {
return (
<input onChange={this.inputChanged}/>
);
}
}

复杂的模板内容(Complex Template Content)

到目前为止,我们已经看到了如何只返回一个根元素的例子。我们也可以在根元素中嵌套元素

在组件有多个“顶级”元素的情况下,render 函数可以返回一个数组。注意 div 元素。

render() {
return ([
// first top level element
<div class="container">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>, // second top level element, note the , above
<div class="another-container">
... more html content ...
</div>
]);
}

或者你可以使用 Fragment 函数组件,在这种情况下你不需要添加逗号:

import { Fragment } from '@stencil/core';
...
render() {
return (<Fragment>
// first top level element
<div class="container">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div> <div class="another-container">
... more html content ...
</div>
</Fragment>);
}

也可以使用 innerHTML 直接将内容内联到元素中。例如,当动态加载一个 svg,然后想要在 div 中渲染它时,这就很有用了。这就像在普通的 HTML 中一样:

<div innerHTML={svgContent}></div>

获取 DOM 元素的引用

jsx 中使用 ref 属性来获取 dom 的引用,示例如下

@Component({
tag: "app-home",
})
export class AppHome {
textInput!: HTMLInputElement; handleSubmit = (event: Event) => {
event.preventDefault();
console.log(this.textInput.value);
}; render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input
type="text"
ref={(el) => (this.textInput = el as HTMLInputElement)}
/>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}

避免共享 JSX 节点

在 jsx 中应该避免共享 jsx 节点,每一个 jsx 节点应该都是唯一的,这是因为在再次渲染时会遇到问题。

@Component({
tag: 'my-cmp',
})
export class MyCmp { render() {
- const sharedNode = <div>Text</div>;
return (
<div>
- {sharedNode}
- {sharedNode}
+ <div>Text</div>
+ <div>Text</div>
</div>
);
}
}

或者,可以创建一个工厂函数来返回一个通用的 JSX 节点,因为返回值将是一个唯一的实例。 示例如下:

@Component({
tag: "my-cmp",
})
export class MyCmp {
getText() {
return <div>Text</div>;
} render() {
return (
<div>
{this.getText()}
{this.getText()}
</div>
);
}
}

结束语

至此,我们已经基本把 StencilJs 的相关基础知识已经学习的差不多了,在下一个章节中将会使用之前学习到的知识来开发一个常用的组件。

由于我们只是使用 StencilJs 来开发 web component 组件,其它不想关的知识(router)便不再讲解。

StencilJs 学习之 JSX的更多相关文章

  1. stenciljs 学习九 使用jsx

    可以使用jsx 方便组件的开发 基本格式 主要是render 函数 class MyComponent { render() { return ( <div> <h1>Hell ...

  2. 学习 React(jsx语法) + es2015 + babel + webpack

    视频学习地址: http://www.jtthink.com/course/play/575 官方地址 https://facebook.github.io/react/ 神坑: 1.每次this.s ...

  3. stenciljs 学习五 事件

    组件可以使用Event Emitter装饰器发送数据和事件. Event 定义 参考: import { Event, EventEmitter } from '@stencil/core'; ... ...

  4. stenciljs 学习四 组件装饰器

    stenciljs 可以方便的构建交互式组件 支持以下装饰器 component prop watch state method element component 说明 component 包含ta ...

  5. React学习笔记 - JSX简介

    React Learn Note 2 React学习笔记(二) 标签(空格分隔): React JavaScript 一.JSX简介 像const element = <h1>Hello ...

  6. stenciljs 学习十一 pwa 支持

    stenciljs 对于pwa 的支持是自动注入的,我们只需要简单的配置,stenciljs使用workbox 配置 默认配置 { skipWaiting: true, clientsClaim: t ...

  7. stenciljs 学习十 服务器端渲染

      stenciljs提供了 ssr 支持,对于express 最简单的就是使用提供的中间件 express 集成 const express = require('express'); const ...

  8. stenciljs 学习八 组件测试

    测试对于框架来说比较重要,对于web 组件的测试同样很重要,类似的jest 很方便,stenciljs也是基于jest 开发的 包含两个核心api render(), flush() 测试配置 pac ...

  9. stenciljs 学习七 路由

    stenciljs路由类似react router 安装 npm install @stencil/router --save 使用 导入包 import "@stencil/router& ...

  10. stenciljs 学习六 组件开发样式指南

    组件不是动作,最好使用名词而不是动词, 文件结构 每个文件一个组件. 每个目录一个组件.虽然将类似的组件分组到同一目录中可能是有意义的,但我们发现当每个组件都有自己的目录时,更容易记录组件. 实现(. ...

随机推荐

  1. 超全 泛微 E9 Ecology 9开发资料大全 开源资源下载 泛微E9二次开发 泛微开发实战经验 泛微开发实战例子 泛微二次开发项目例子 泛微二次开发Demo 泛微二次开发完整例子 泛微二次开发入门

    由于工作需要,E9在泛微一推出来,以前所在的企业就第一时间上线了,经过四年多的运行,功能强大再加上在上面开发非常多的业务,一般的企业员工只需要打开泛微就可以处理完平时信息化的业务.后来又去外包公司专业 ...

  2. 数据挖掘系统聚类—R实现

    系统聚类法 聚类就是按照某个特定标准把一个数据集分割成不同的类或簇,最后的结果是希望同类之间的差异性尽可能小,不同类之间的差异性尽可能大.不同的类具有能够表达异于其他类的指标,这样针对不同的类,后续就 ...

  3. 有关Spring的ioc理解之代理模式

    AOP代理模式可以实现事务控制和业务逻辑代码横切. 使用代理模式,动态代理实现横切. 什么是代理? 接口就是指定要做的事情,要实现的逻辑. 代理类似于房源租房 public interface ZuF ...

  4. Java 8新特性之 Optional 类

    前言 java.util.Optional 是java8中引进的一个新的类,我们通过Optional类的源码可以看到,该方法的作用可以对可能缺失的值进行建模,而不是直接将null赋值给变量. Opti ...

  5. 在.NET 6.0中自定义接口路由

    大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的垫脚石,让我们一起精进. 在本文中,我们将讨论ASP.NET Core中的新路由.我们将了解什么是接口(en ...

  6. 21-HMR

    /* HMR:hot module replacement 热模块替换 / 模块热替换 作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块) 极大提升构建速度 样式文件:可以使用HMR ...

  7. 一文讲透产品经理如何用好ChatGPT

    作者:京东零售 何雨航 "4.0版本的ChatGPT可以有效提升产品经理工作效率,但并无法替代产品经理的角色." 一.引言 3月15日,OpenAI发布了最新的基于GPT-4的Ch ...

  8. 【Visual Leak Detector】在 VS 高版本中使用 VLD

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记. 本篇介绍如何在 VS 高版本中使用 vld2.5.1.同系列文章目录可见 <内存泄漏检测工具>目录 目录 说明 1. 使用前 ...

  9. ai问答:使用 Vue3 组合式API 和 TS 封装 echarts 折线图

    使用这个组件时,只需要传入合适的chartData数组,就可以渲染一个折线图,并且响应数据变化. <template> <div ref="chart" styl ...

  10. 张量(Tensor)、标量(scalar)、向量(vector)、矩阵(matrix)

    张量(Tensor):Tensor = multi-dimensional array of numbers 张量是一个多维数组,它是标量,向量,矩阵的高维扩展 ,是一个数据容器,张量是矩阵向任意维度 ...