中国金指尖奖”隆重揭晓
I’ve done it. I have done the thing that everyone tells you not to do as a developer directly following “don’t build your own cms (which I have also done)”. I built my own framework, Joist.
我做完了 我所做的事情是,每个人都告诉您不要紧随“不要建立自己的cms(我也这样做)”之后直接作为开发人员。 我建立了自己的框架Joist 。
Over the past 2 or so years I have been been thinking about how I personally like to write applications and build components and couldn’t find anything that did EXACTLY what I wanted EXACTLY the way I wanted. So i built Joist, a framework that I want to use that I don’t mind if you want to use too :).
在过去的两年左右的时间里,我一直在思考自己个人喜欢如何编写应用程序和构建组件,却找不到能够完全按照我想要的方式做的事情。 所以我建立了Joist,一个我想使用的框架,如果您也想使用它,我不介意:)。
Some of the things I wanted:
我想要的一些东西:
- dependency injection 依赖注入
- SMALL 小
- opinionated state management 固执的国家管理
- framework agnostic components (WebComponents) 框架不可知组件(WebComponents)
- view layer agnostic (You should be able to swap between no view library, lit-html and lighterhtml whenever you like.) 与视图层无关(您应该可以在任何视图库,lit-html和lighterhtml之间随意交换)。
In my opinion Joist meets all of my criteria. It is opinionated in some aspects and flexible in others. On it’s own @joist/component and @joist/di together weigh in at ~2kb gzipped and ~5kb with lit-html.
我认为Joist符合我的所有条件。 它在某些方面是有根据的,而在另一些方面则是灵活的。 在它自己的@龙骨/组件和@龙骨/ DI一起在〜2KB gzip压缩的权衡,并用〜点燃-HTML 5KB。
入门 (Getting Started)
The easiest way to get started with Joist is by going to webcomponents.dev and just the Joist starter. Webcomponents.dev is a an EXCELLENT site that lets you build and publish components with a variety of libraries. (Seriously even if you don’t care about Joist you should check it out.)
开始使用Joist的最简单方法是转到webcomponents.dev和Joist入门程序。 Webcomponents.dev是一个绝佳的网站,您可以使用它来构建和发布带有各种库的组件。 (严重的是,即使您不关心Joist,也应将其签出。)
If you want to build an application you can use Create Snowpack App (CSP).
如果要构建应用程序,则可以使用创建Snowpack应用程序(CSP)。
npx create-snowpack-app my-app --template @joist/starter-snowpack
This will set you up with a dev server, production builds via rollup and unit testing via web-test-runner.
这将为您设置开发服务器,通过汇总构建生产,并通过web-test-runner进行单元测试。
元素 (Elements)
元素 (Elements)
Joist is view library agnostic but comes with built in support for lit-html and is what we will use for all of our examples. Now let’s see what a Joist element looks like.
Joist与视图库无关,但是内置了对lit-html的支持,这是我们将在所有示例中使用的。 现在,让我们看一下Joist元素的外观。
import { component, JoistElement } from '@joist/component';
import { template, html } from '@joist/component/lit-html';@component({
tagName: 'my-element',
state: {
title: 'Hello World'
},
render: template(({ state }) => {
return html`<h1>${state.title}</h1>`
})
})
class MyElement extends JoistElement {}
A Joist component is defined by extending the JoistElement base custom element and adding some component metadata. Metadata includes the tag name of the new element, the default state of the element and the render function. A joist render function is passed an object called RenderCtx.
通过扩展JoistElement基本自定义元素并添加一些组件元数据来定义Joist组件。 元数据包括新元素的标签名称,元素的默认状态和渲染功能。 托梁渲染函数传递了一个称为RenderCtx的对象。
组件样式 (Component Styling)
When you are using shadow dom you can apply styles with the component `styles` property.
当您使用影子dom时,您可以使用组件的styles属性来应用样式。
import { component, JoistElement } from ‘@joist/component’;
import { template, html } from ‘@joist/component/lit-html’;@component({
tagName: ‘app-root’,
shadowDom: ‘open’,
state: {
title: ‘Hello World’
},
styles: [`
:host {
display: block;
}
h1 {
color: red;
}
`],
render: template(({ state }) => {
return html`
<h1>${state.title}</h1>
`
})
})
class AppElement extends JoistElement {}
依赖注入(DI) (Dependency Injection (DI))
At the heart of Joist is the dependency injector. The dependency injector itself is completely separate from components and is in its own package. Each Joist component has its own injector that inherits from a single global injector. This allows Joist components to construct their own locally scoped services as well as share global singletons. Services decorated with the “service” decorator will be treated as singletons.
Joist的核心是依赖项注入器。 依赖项注入器本身与组件完全分开,并位于其自己的包装中。 每个Joist组件都有自己的注射器,该注射器继承自单个全局注射器。 这允许Joist组件构建自己的本地范围服务以及共享全局单例。 用“服务”修饰符修饰的服务将被视为单例。
Services can be injected into the constructor of other services via the “inject” decorator.
服务可以通过“注入”装饰器注入到其他服务的构造函数中。
Custom elements can inject services with the get decorator. This maps a service to a property on any class that implements the InjectorBase interface. You can even use it with other web component libraries like Microsoft’s FASTElement.
自定义元素可以使用get装饰器注入服务。 这会将服务映射到实现InjectorBase接口的任何类的属性。 您甚至可以将其与其他Web组件库(例如Microsoft的FASTElement)一起使用。
import { component, JoistElement, get } from '@joist/component';
import { service, inject } from '@joist/di';@service()
class FooService {
sayHello() {
return 'Hello World';
}
}@service()
class BarService {
constructor(@inject(FooService) private foo: FooService) {} sayHello() {
return this.foo.sayHello();
}
}@component({
tagName: 'app-root',
})
class AppElement extends JoistElement {
@get(BarService)
private myService!: BarService; connectedCallback() {
super.connectedCallback(); console.log(this.myservice.sayHello());
}
}
Property based DI with the get decorator is “lazy”, meaning that the service won’t be instantiated until the first time it is requested.
使用get装饰器的基于属性的DI是“惰性”的,这意味着直到第一次请求该服务时,该服务才会被实例化。
州 (State)
Joist components differentiate between element properties and internal state. Updating internal state will cause the component view to update. This is on purpose in order to make state updates explicit. Any change in state will result in a change in the view. Joist’s component state is accessible via the State service. You can update state with the setValue and patchValue methods and watch for state changes with onChange.
托梁组件区分元素属性和内部状态。 更新内部状态将导致组件视图更新。 这是为了使状态更新明确。 状态的任何改变都将导致视图的改变。 通过状态服务可以访问托梁的组件状态。 您可以使用setValue和patchValue方法更新状态,并使用onChange监视状态更改。
import {
component,
State,
JoistElement,
get
} from '@joist/component';
import { template, html } from '@joist/component/lit-html';@component<number>({
tagName: 'my-counter',
state: 0,
render: template(({ state }) => html`${state}`)
})
class MyCounterElement extends JoistElement {
@get(State)
private state!: State<number>; connectedCallback() {
super.connectedCallback(); setInterval(() => this.update(), 1000);
} private update() {
const { value } = this.state; this.state.setValue(value + 1);
}
}
Component state is updated asynchronously which means you can pass setValue and patchValue a promise that resolves to your new state.
组件状态是异步更新的,这意味着您可以向setValue和patchValue传递一个可解析为新状态的承诺。
import {
component,
State,
JoistElement,
get
} from '@joist/component';
import { template, html } from '@joist/component/lit-html';@component<number>({
tagName: 'my-counter',
state: 'Hello',
render: template(({ state }) => html`${state}`)
})
class MyCounterElement extends JoistElement {
@get(State)
private state!: State<number>; connectedCallback() {
super.connectedCallback(); const res = Promise.resolve('World'); this.state.setValue(res);
}
}
物产 (Properties)
Since Joist elements are Custom Elements, properties behave as you would expect for an HTMLElement. Decorating your properties with the “property” decorator which will cause your elements onPropChanges method to be called with a list of PropChangs whenever that property is updated.
由于Joist元素是“自定义元素”,因此属性的行为与您对HTMLElement的期望一样。 用“属性”装饰器装饰属性,每当该属性更新时,都会使用PropChangs列表调用元素onPropChanges方法。
import {
component,
State,
JoistElement,
property,
get,
PropChange
} from '@joist/component';@component({
tagName: 'app-root',
state: ''
})
class AppElement extends JoistElement {
@get(State)
private state!: State<string>; @property()
public greeting = ''; onPropChanges(_changes: PropChange[]) {
this.state.setValue(this.greeting);
}
}
Properties also have a hook for runtime validation. The property decorator can accept 1 or many validation functions that will be run when that property is set. This is particularly helpful if you are distributing components. A validator function either return null, meaning there is no error, or an error message.
属性还具有用于运行时验证的钩子。 属性装饰器可以接受1个或多个在设置该属性时将运行的验证功能。 如果要分发组件,这特别有用。 验证器函数返回null(表示没有错误)或错误消息。
import {
component,
property,
JoistElement
} from '@joist/component';function isString(val: unknown) {
if (typeof val === 'string') {
return null;
} return { message: 'error' };
}function isLongerThan(length: number) {
return function (val: string) {
if (val.length > length) {
return null;
} return { message: 'Incorrect length' };
}
}@component()
class MyElement extends JoistElement {
@property(isString, isLongerThan(2))
public hello = 'Hello World';
}
处理程序 (Handlers)
Handlers are one of the more unique features of Joist. Handlers are way to map an “action” to corresponding methods. Multiple methods can be mapped to a single action. Multiple actions can be mapped to a single method. Handlers can can also match action based on a regular expression. The general flow is event -> handler -> state change.
处理程序是Joist较独特的功能之一。 处理程序是将“动作”映射到相应方法的方法。 可以将多个方法映射到单个操作。 可以将多个动作映射到一个方法。 处理程序还可以基于正则表达式匹配动作。 一般流程是事件->处理程序->状态更改 。
import {
component,
State,
handle,
JoistElement,
get
} from '@joist/component';
import { template, html } from '@joist/component/lit-html';@component<number>({
tagName: 'app-root',
state: 0,
render: template(({ state, run }) => {
return html`
<button @click=${run('dec')}>-</button>
<span>${state}</span>
<button @click=${run('inc')}>+</button>
`
})
})
class AppElement extends JoistElement {
@get(State)
private state!: State<number>; @handle('inc') increment() {
this.state.setValue(this.state.value + 1);
} @handle('dec') decrement() {
this.state.setValue(this.state.value - 1);
} @handle('inc')
@handle('dec')
either() {
console.log('CALLED WHEN EITHER IS RUN')
} @handle(/.*/) all(e: Event, payload: any, name: string) {
console.log('CALLED WHEN REGEX MATCHES');
console.log('TRIGGERING EVENT', e);
console.log('payload', payload);
console.log('matched name', name);
}
}
总结思想 (Closing Thoughts)
That is a quick and dirty overview of Joist. Joist is built to be opinionated but can be used à la carte. The package that I didn’t cover here is @joist/router which is stable but still a work in progress. Joist is a project I have been playing around and thinking about for quite a while and I think I am pretty happy with the result! Give it a shot, let me know what you think.
那是Joist的简要概述。 托梁的建材固执己见,但可以点菜使用 。 我在这里没有介绍的软件包是@ joist / router ,它很稳定,但仍在开发中。 Joist是一个我一直在考虑并思考了很长时间的项目,我认为我对结果非常满意! 试一试,让我知道您的想法。
中国金指尖奖”隆重揭晓