Future-Proof Your Design System with StencilJS

栏目: IT技术 · 发布时间: 5年前

内容简介:As you may know, when Ionic was initially released, it was built specifically for Angular. Then, other frameworks started to emerge and take a large chunk of the frontend community — and Ionic wanted its tools to be used by anyone regardless of the framewo

An introduction to StencilJS: A compiler that generates web components.

Future-Proof Your Design System with StencilJS
Image by Angel-Kun from Pixabay

What is StencilJS?

StencilJS is a library for generating web components, built by the team behind the Ionic Framework .

As you may know, when Ionic was initially released, it was built specifically for Angular. Then, other frameworks started to emerge and take a large chunk of the frontend community — and Ionic wanted its tools to be used by anyone regardless of the framework of choice.

Rewriting the same components for every major framework would have been an impossible (and probably wrong) task. Web Components allow us to solve this issue — but not without limitations, that are all well-documented .

Stencil attempts to provide an abstraction on top of Web Components in order to simplify how we write and ship them.

One of the most interesting things about Stencil is that the code compiled is shipped without Stencil (just like Svelte , another “disappearing” framework): this allows the components to be incredibly lightweight and makes them ideal for being used with other frameworks such as React, Vue or Angular.

It is for their lightweightness and native browser support that Web Components are increasingly used for design systems. They can even be incorporated into existing modular React/Vue/Angular component libraries built using tools like Bit , as each component in that library is completely independent.

So, for example, you can push newly built Web Components to your Bit collection (library) that up until now has only contained React components (and let your team use them in your React projects).

Future-Proof Your Design System with StencilJS
Modular and dynamic design systems on bit.dev

Why I chose Stencil for my Project

I’ve recently embarked on a personal project that has taken much of my time lately.

When I had to make a choice about what tools to use, I started researching a library for building a collection of components with the following conditions:

  • It needed to be very fast
  • It needed to be very lightweight and simple
  • It needed to be extremely future-proof
  • It needed to be supported by an active and vibrant community

My use case is to build 3rd party widgets to be loaded on the user’s websites. As you can imagine, performance and weight are essential. No one wants a 3rd party to slow down their website!

Possible Candidates

Other than Stencil, I started researching various other tools:

While the above are extremely valid options, and probably would have worked also very well for my use-case, I decided to go with Stencil for the following reasons:

  • JSX: love it or hate it, it is very well-known and it is used in many other frameworks. Anyone could pick it up in a matter of hours.
  • Small and smart bundles: all components are lazy-loaded and will use the correct bundle for the browser being used thanks to differential loading
  • First-Class Typescript Support — this was pretty important to me
  • Despite using JSX as a templating language, as a primarily an Angular user, I found it extremely easy to get started with and pretty much everything made sense from the beginning. There are some things to watch out as we will see in the next sections.

After having used Stencil with great delight, I decided to write this article to introduce you to this tool and share my experience.

The building blocks of StencilJS

In this section, we’ll see how to build one with Stencil, starting from the basics. In order to understand how Stencil works, we’ll explore its most important topics:

  • Defining a component
  • Passing properties
  • Handling internal state
  • Emitting Events
  • Exposing Methods
  • Templating with JSX and slots

Once you get a grasp of these concepts, you can be up and running with writing Stencil components in very little time! Yes, it’s that easy.

The Anatomy of a Stencil Component

A stencil component gets declared with the Component decorator; yes, this may be familiar, it does look like Angular.

We define:

  • its tag name with the property tag
  • the component’s styles using the property styleUrls
  • a function named render that is responsible for defining the template using JSX . And yes again, that’s familiar, because it works similarly to React’s class components.
// single-choice.tsximport { Component, h } from '@stencil/core';@Component({
tag: 'single-choice',
styleUrls: ["./single-choice.css"]
})
export class SingleChoiceComponent {
render() {
return 'I will be a single choice field!';
}
}

Notice: import h is needed if we use JSX within the render function

Once we build the component using the Stencil compiler and import the scripts on our web page, we can simply call the component as we would with a normal HTML element:

<single-choice></single-choice>

Scoped and Native Shadow DOM

Web Components can be scoped using Shadow DOM (using the property shadow ), but this is still not supported across all browsers ( such as IE11, and Safari only partially supports it ).

Just like Angular’s emulated view encapsulation, Stencil provides a property called scoped which will emulate the same behavior and encapsulate the style of our components.

By default, I always set scoped instead of shadow .

@Component({
tag: 'single-choice',
scoped: true
})

Passing Properties to Components

Passing properties to components also remind a lot of how Angular works. In order to pass properties, we can define class properties and decorate them with the decorator Prop .

import { ..., Prop } from "@stencil/core";@Component({...})
export class SingleChoiceComponent {
@Prop() id: string;
render() {
return (
<label for={this.id}></label>
);
}
}

This decorator though can accept some configuration you may be unaware of:

  • attribute : the name of the attribute to pass, in case the class property’s name needs to be different
  • mutable : by default, properties are immutable. Once it’s set from outside of the component, it cannot be mutated — unless we explicitly set this property to true
  • reflect : if we want to expose the attribute to the DOM of the component, we can set this property, so that we can access the property from the outside
@Component({...})
export class SingleChoiceComponent {
@Prop({
attribute: 'id',
mutable: true,
reflect: true
})
fieldId: string;

render() {...}
}

Now that we exposed the property id , we can access it using the DOM API:

const singleChoice = document.querySelector('single-choice');
const id = singleChoice.id;

Internal State

Stencil attempts to maximize performance and efficiency by re-rendering only when necessary. If you’re used to a framework like Angular, or Svelte, you may not immediately understand why your component is not updating its view.

In order to trigger a re-render when a property changes, Stencil provides the decorator State :

@Component({...})
export class SingleChoiceComponent {
@Prop() id: string;
@State() value: string;
render() {...}
}

Notice:If we forget decorating the property value , the view will not re-render.

Events

Of course, components can also expose events to their parents to enable child-parent communication.

An event is defined in the following way:

Event
EventEmitter
@Component({...})
export class SingleChoiceComponent {
@Prop() id: string;
@State() value: string;
@Event() valueChanged: EventEmitter<Option>; onClick(option: Option) {
this.valueChanged.emit(option);
}
render() {...}
}

If you are using the event with a child rendered within the render function, you can simply pass the event down and call a method:

<parent-component>
<single-choice
valueChanged={(e) => this.choiceSelected(e))
></single-choice>;
</parent-component>

In cases where our child component is nested deep within the parent’s children, we can listen to custom events thanks to the decorator Listen .

class ParentComponent {
@Listen('valueChanged')
valueChanged(value) {
// do something with value
}
}

Methods

Component methods are not to be confused by the methods you normally define in the component’s class. Stencil allows certain methods to be exposed as public API by decorating them with the decorator Method .

@Component({...})
export class SingleChoiceComponent {
@Prop() id: string;
@State() value: string;
@Method()
async getValue() {
return this.value;
}
}

One the method is defined, we can call it from the outside:

const singleChoice = document.querySelector('single-choice');
const value = singleChoice.getValue();

Gotchas:

  • Public methods should always be async
  • It’s not recommended to use public methods to expose the source of truth of a component. The suggestion is to rely solely on events and props instead.

Templating: JSX and slots

If you have ever worked with React, Preact or any other library that uses JSX, there’s not much new for you to learn to start using Stencil. If not, there’s a little bit to learn, but fortunately, JSX is fairly simple.

Of course, you can also define functional components and use them in the render function:

const Label = (_, text: JSX.Element) => 
<label>{text}</label>
@Component({...})
export class SingleChoiceComponent {
render() {
return (
<Label>
<slot />
</Label>
);
}
}

As you can see above, slots can be helpful to render the content of a component. You can also define named slots to control where the content will be rendered.

class MyComponent() {
render() {
return (
<div>
<slot name="heading">
</div>
);
}
}
<my-component>
<h1 slot="heading">Heading</h1>
</my-component>

Final words

StencilJS has been a great library to work with. As I work mostly with Angular, I was pretty used to having a really good developer experience and a rich ecosystem, but Stencil hasn’t let me down in this regard.

It is a very valid choice if you want to complement your applications with a set of highly reusable components. As mentioned earlier, you can gradually build a design system composed of Web Components or replace an existing one (implemented with some framework) by using tools like Bit . This will future-proof your design system and make it available to every other front-end technology used within your company. It’s also a valid choice if you’re using plain Javascript, as the overhead added is extremely minimal.

Sure, I’m hitting bugs here and there, but the team is generally very responsive and they ship new releases very often. Having Ionic’s components as a reference is also a great way to figure out best practices and see how the core team approached their architecture.

Expect more articles about Stencil from me in the future!

Resources

If you need any clarifications, or if you think something is unclear or wrong, do please leave a comment!


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

互联网冲击

互联网冲击

杰伦•拉尼尔 (Jaron Lanier) / 李龙泉、祝朝伟 / 中信出版社 / 2014-5-1 / CNY 65.00

在《互联网冲击》一书中,关于网络技术对经济造成的影响,作者进行了卓有远见的预测。拉尼尔断言,数字网络的崛起会造成我们经济的衰退,并且摧毁中产阶级。如今,科技已经征服了一个又一个行业——从媒体到医药业,再到制造业。我们的就业形势和个人财富都将面临更加严峻的挑战。  但还有另外一种方法,能够让科技掌握我们的未来。在本书中,作者不仅展现了他的雄心壮志,而且也处处体现着他的人文关怀。拉尼尔指明了一条新信息......一起来看看 《互联网冲击》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具