在之前的学习中,我们已经掌握了 stencilJs
中的一些核心概念和基础知识,如装饰器 Prop
、State
、Event
、Listen
、Method
、Component
以及生命周期方法。这些知识是构建复杂组件和应用的基础,而抽屉组件是一个很好的示例,能够综合运用这些知识,让我们更深入地理解它们的作用和用法。
为什么选择抽屉组件作为综合练习呢?因为抽屉组件是现代 Web 应用中常见的 UI 元素,具有以下特点:
State
装饰器来管理和控制组件内部的状态。Prop
装饰器来接收外部传递的数据。Method
装饰器来定义公开方法。通过实际构建一个抽屉组件,我们能够在综合应用的背景下,更深入地理解这些概念的作用和相互关系。同时,这也为我们未来在实际项目中构建更复杂的组件和应用奠定了坚实的基础。抽屉组件的案例将帮助我们更好地运用 stencilJs
的知识,从而成为更有信心和能力的前端开发者。
使用以下的命令创建一个 Stencil 项目
#使用 npm
npm init stencil
#使用 yarn
yarn create stencil
#使用 pnpm
pnpm create stencil
创建成功,终端显示如下
Stencil 项目内置一个生成组件命令 generate
,使用下面的命令生成一个组件
#使用 npm
npm run generate
#使用 yarn
yarn generate
#使用 pnpm
pnpm run generate
执行之后会让用户输入一个组件的名字(以-作为连字符),输入之后按回车键会让用户选择要生成的文件,选择之后按回车就能生成一个组件了。你可以在 src/components
目录下看到 ce-drawer
, 如下图
首先,创建组件的 HTML 结构:
import { Host, h } from '@stencil/core';
@Component({
tag: 'ce-drawer',
styleUrl: 'ce-drawer.css',
shadow: true,
})
export class CeDrawer {
renderHeader() {
if (this.showHeader) {
return (
<div class="ivy-drawer-header">
<slot name="header">{this.header}</slot>
</div>
);
} else {
return null;
}
}
render() {
return (
<Host>
<div class="ivy-mask"></div>
<div class="ivy-drawer">
{this.renderHeader()}
<div class="ivy-drawer-body">
<slot></slot>
</div>
</div>
</Host>
);
}
}
接下来,声明 prop
:
import { Component, Event, EventEmitter, Host, Method, Prop, Watch, h } from '@stencil/core';
@Component({
tag: 'ce-drawer',
styleUrl: 'ce-drawer.css',
shadow: true,
})
export class CeDrawer {
@Prop({
attribute: 'show',
mutable: true,
reflect: true,
})
visible: Boolean = false;
@Prop() width: string = '36%';
@Prop({
attribute: 'show-header',
mutable: true,
reflect: true,
})
showHeader: boolean = false;
@Prop({
attribute: 'header',
})
header: string = '';
@Prop({
attribute: 'mask-closable',
mutable: true,
reflect: true,
})
maskClosable: boolean = true;
@Prop({
attribute: 'placement',
mutable: true,
reflect: true,
})
placement: string = 'right';
/**监听传入的 placement 是否符合要求*/
@Watch('placement')
validateName(val: string) {
const flag = ['left', 'right', 'top', 'bottom'].includes(val);
if (!flag) {
throw new Error('placement 必须是 left/right/top/bottom 其中之一');
}
}
renderHeader() {
if (this.showHeader) {
return (
<div class="ivy-drawer-header">
<slot name="header">{this.header}</slot>
</div>
);
} else {
return null;
}
}
render() {
return (
<Host show={this.visible}>
<div class="ivy-mask" onClick={this.maskClose.bind(this)}></div>
<div
class="ivy-drawer"
style={{ width: ['left', 'right'].includes(this.placement) ? this.width : '100%', height: ['top', 'bottom'].includes(this.placement) ? this.width : '100%' }}
>
{this.renderHeader()}
<div class="ivy-drawer-body">
<slot></slot>
</div>
</div>
</Host>
);
}
}
接着,声明自定义事件和遮罩层点击事件:
// ...
maskClose() {
if (this.maskClosable) {
this.visible = false;
}
}
@Event() closed: EventEmitter;
closeHandler() {
this.closed.emit();
}
最后,声明外部可用的辅助方法,例如显示/关闭 Drawer 组件:
// ...
@Method()
async open() {
this.visible = true;
}
@Method()
async close() {
this.closeHandler();
this.visible = false;
}
手机扫一扫
移动阅读更方便
你可能感兴趣的文章