Future-Proof Your Design System with StencilJS

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

内容简介: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!


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

查看所有标签

猜你喜欢:

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

Java Web开发实战经典(基础篇)

Java Web开发实战经典(基础篇)

李兴华、王月清 / 清华大学出版社 / 2010-8 / 69.80元

本书用通俗易懂的语言和丰富多彩的实例,通过对Ajax、JavaScript、HTML等Web系统开发技术基础知识的讲解,并结合MVC设计模式的理念,详细讲述了使用JSP及Struts框架进行Web系统开发的相关技术。 全书分4部分共17章,内容包括Java Web开发简介,HTML、JavaScript简介,XML简介,Tomcat服务器的安装及配置,JSP基础语法,JSP内置对象,Java......一起来看看 《Java Web开发实战经典(基础篇)》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具