前言

我写过一篇关于 Lit 的文章,Material Design, Angular Material, MDC, MWC, Lit 的关系

如今 material-web MWC 已经发布 1.0 了,估计 Angular 也会在不远的将来从 material-components-web MDC 迁移到 MWC。

以后,我们要想深入理解 Angular Material 就必须对 MWC 有一定了解,而 MWC 又是基于 Lit 开发的,所以我们也需要了解 Lit。

这篇就让我们来看看 Lit 吧。

参考

Lit 官网文档

Lit 介绍

Lit 的前生是 Google 的 Polymer。它是一个帮助我们写标准 Web Components 的库。

它有 2 个特点:

  1. 标准 W3C Web Components
    Lit 开发出来的组件是 W3C 规范的 Web Components,不像 Angular、React、Vue 那些都是仿冒的。 
    符合标准的好处是可以 Plug and Play,组件本身不依赖框架技术。
    我用 Lit 写出来的组件,可以拿到 Angular、React、Vue 任何项目里跑。
  2. 提升开发体验
    在不借助任何库的情况下,手写 W3C Web Components 开发体验是很差的,代码可读性也差。
    Lit 主要就是为了解决这些问题而诞生的。
    它借助 DecoratorTemplate literals 特性,实现了声明式定义组件和 MVVM。

Lit Getting Started

Lit 的目的是开发出 W3C Web Components,所以要掌握 Lit 就必须先掌握 W3C Web Components。

不熟悉的朋友们,请先看我以前写的这篇 DOM – Web Components

安装

Lit 是可以用在纯 HTML、CSS、JS 上的,但是会降低开发体验。所以我还是鼓励大家用 TypeScript。

我这里搭配 Vite 做演示,你想改用 Webpack 或 Rollup 也都可以。

yarn create vite

它默认会有一些 sample code,我们洗掉它,从一个干净的开始。

index.css

* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

index.ts 清空

index.html

<!doctype html>
<html lang="en"> <head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Lit + TS</title>
<link rel="stylesheet" href="./src/index.css" />
<script type="module" src="/src/index.ts"></script>
</head> <body>
</body> </html>

创建组件

Web Components 是由 HTML TemplatesShadow DOMCustom Elements 三种独立的技术搭配而成的。

而 Lit 把它们组合在一起了。

hello-world.ts

import { LitElement, css, html } from 'lit';
import { customElement } from 'lit/decorators.js'; // 定义组件
// 取代了 window.customElements.define('hello-world', HelloWorldElement);
@customElement('hello-world')
export class HelloWorldElement extends LitElement {
// 取代了 <template>
render() {
return html`<h1>Hello World</h1>`;
} // 取代了 <style>
static styles = css`
h1 {
color: red;
}
`;
} // declare type for TypeScript
declare global {
interface HTMLElementTagNameMap {
'hello-world': HelloWorldElement;
}
}

HTML 和 CSS 用了 Template literals 技术。它不像 Angular 搞 compiler 黑魔法,这个只是单纯的 JS runtime render。

Tips: template literals 对 IDE 不友好,需要插件 lit-html 和 vscode-styled-components

使用组件

index.ts

import './hello-world';

setTimeout(() => {
// 动态使用
const helloWorld = document.createElement('hello-world');
document.body.appendChild(helloWorld);
}, 3000);

记得要 import 先。

index.html

<body>
<!-- 静态使用 -->
<hello-world></hello-world>
</body>

效果

Lit の Shadow DOM

所有原生 Shadow DOM 的特性,在 Lit 都可以用。

比如::host<slot>::slotted()::part()

除了 :host-context(),相关提问:Stack Overflow – :host-context not working as expected in Lit-Element web component

主要是因为 Firefox 和 Safari 本来就不支持 :host-context 所以 Lit 干脆就完全不支持了。可以使用 CSS Variables 作为替代方案。

从这里也能看出,Lit 实现 Web Components 的手法我们直觉认为的不太一样,它里面动了一些手脚。

Lit の Custom Elements

所有原生 Custom Elements 的特性,在 Lit 都可以用。

比如 lifecycle:connectedCallback、disconnectedCallback、attributeChangedCallback、static get observedAttributes。

Lit の MVVM

到目前为止,我们看到的 @customElement、extends LitElement、html``、css`` 只是 Lit 的小角色。

真正让 Lit 发亮起来的是它的 MVVM,这也是 W3C Web Components 最缺失的功能。

MVVM 的中心思想

MVVM 的宗旨就是不要直接操作 DOM,不要调用 DOM API,凡事都通过 MVVM 库去控制。

Lit 提供了很多种 binding、listening、query 的方式去取代 DOM 操作。

binding & listening

@customElement('hello-world')
export class HelloWorldElement extends LitElement {
@state()
private value = 'default value'; private updateValue() {
this.value = 'new value';
} render() {
return html`
<h1>${this.value}</h1>
<button @click="${this.updateValue}">update value</button>
`;
} static styles = css``;
}

效果

如果你熟悉 Angular,那应该对 Lit 的语法不会感到太陌生。它俩其实挺像的。毕竟都是 Google 出品,师出同门嘛。

我们逐个来看

有一个 value 属性,@state() 表示这个属性会被用于模板。这时 Lit 就知道每当这个属性值发生变化,那就需要 re-render(它具体如何实现重渲染我不清楚,估计不会是大面积的替换,应该会做性能优化)

有一个 updateValue 方法,调用它就更新 value 属性。

把 value 属性插入模板。

@click 是一个特殊字符串,表示监听 click 事件,接着把 updateValue 方法插入模板。

至此,Lit 就掌握足够信息,可以做监听和 update DOM 了。

Attribute & Property

@state 和 @property 的区别是,一个是 private 一个是 public。

@customElement('hello-world')
export class HelloWorldElement extends LitElement {
@property({ type: Number, attribute: 'number-value' })
numberValue!: number; @property({ type: Boolean, attribute: 'bool-value' })
boolValue = false; render() {
return html` <h1>${this.numberValue.toFixed(4)}</h1>
<h1>${this.boolValue}</h1>`;
} static styles = css``;
}

外部 HTML 控制

<hello-world number-value="50" bool-value></hello-world>

外部 JS 操控

document.querySelector('hello-world')!.numberValue = 50;
document.querySelector('hello-world')!.boolValue = false;

Dispatch Event

@customElement('hello-world')
export class HelloWorldElement extends LitElement {
private handleClick() {
this.dispatchEvent(new CustomEvent('clickhelloworld', { bubbles: true }));
} render() {
return html`<h1 @click="${this.handleClick}">Hello World</h1>`;
} static styles = css``;
}

外部监听

document.body.addEventListener('clickhelloworld', e => {
console.log('clicked', e.target); // <hello-world>
});

注:组件 this.dispatchEvent 的这个 this 只的是 <hello-world> 这个 element。所以 event 不需要设置 composed

用 Lit 重写 Counter Component

在 DOM – Web Components 文章的结尾,我写了一个 Counter Component,我们现在用 Lit 重写一遍。

最终效果是这样

counter.ts

import { LitElement, css, html } from 'lit';
import { customElement, property, state } from 'lit/decorators.js'; @customElement('counter-component')
export class HTMLCounterElement extends LitElement {
@property({ type: Number })
step = 1; @state()
private number = 0; private minus() {
this.number -= this.step;
} private plus() {
this.number += this.step;
} render() {
return html`
<div class="counter">
<button class="minus" @click="${this.minus}">-</button>
<span class="number">${this.number}</span>
<button class="plus" @click="${this.plus}">+</button>
</div>
`;
} static styles = css`
.counter {
display: flex;
gap: 16px;
}
.counter :is(.minus, .plus) {
width: 64px;
height: 64px;
}
.counter .number {
width: 128px;
height: 64px;
border: 1px solid gray;
font-size: 36px;
display: grid;
place-items: center;
}
`;
} declare global {
interface HTMLElementTagNameMap {
'counter-component': HTMLCounterElement;
}
}

index.html

<counter-component step="10"></counter-component>

Lit 的局限

我们拿 material-web MWC 和 material-components-web MDC 做对比。

MDC 是传统手法,先有 HTML、CSS,然后 JS 做 binding。

MWC 是 Web Components 手法,没有 HTML、CSS,一切都是 JS 生成的。

结论就是 Lit 的渲染依赖于 JS。

如果我们用 Lit 开发企业网站,需要 SEO,那就需要搞服务端渲染 Server-side rendering。但这个目前还在 experimental 阶段。

JavaScript Library – Lit的更多相关文章

  1. A javascript library providing cross-browser, cross-site messaging/method invocation. http://easyxdm.net

    easyXDM - easy Cross-Domain Messaging easyXDM is a Javascript library that enables you as a develope ...

  2. Dynamices CRM JS 类库 神器 XrmServiceToolkit - A Microsoft Dynamics CRM 2011 & CRM 2013 JavaScript Library

    XrmServiceToolkit - A Microsoft Dynamics CRM 2011 & CRM 2013 JavaScript Library http://xrmservic ...

  3. Raphaël—JavaScript Library

    Raphaël-JavaScript Library What is it? Raphaël is a small JavaScript library that should simplify yo ...

  4. a Javascript library for training Deep Learning models

    w强化算法和数学,来迎接机器学习.神经网络. http://cs.stanford.edu/people/karpathy/convnetjs/ ConvNetJS is a Javascript l ...

  5. JavaScript 工具库:Cloudgamer JavaScript Library v0.1 发布

    JavaScript 工具库:Cloudgamer JavaScript Library v0.1 发布   研究了一年多的js,也差不多写一个自己的js库了.我写这个不算框架,只是一个小型的js工具 ...

  6. jQuery JavaScript Library v3.2.1

    /*! * jQuery JavaScript Library v3.2.1 * https://jquery.com/ * * Includes Sizzle.js * https://sizzle ...

  7. A JavaScript library for reading EXIF meta data from image files.

    exif-js/exif-js: JavaScript library for reading EXIF image metadata https://github.com/exif-js/exif- ...

  8. javascript library

    <!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8& ...

  9. 转:Build Your First JavaScript Library

    http://net.tutsplus.com/tutorials/javascript-ajax/build-your-first-javascript-library/ Step 1: Creat ...

  10. [React] 01 - Intro: javaScript library for building user interfaces

    教学视频: http://www.php.cn/code/8217.html React 教程: http://www.runoob.com/react/react-tutorial.html 本篇是 ...

随机推荐

  1. [oeasy]python0037_字符画艺术_asciiview_自制小动物_imagick_asciiart

    ​ 牛说(cowsay) 回忆上次内容 我们狂飙了一路 从用shell 直接执行 python程序 到用shell 循环执行 python程序 循环体中 把 python的 输出结果 用管道 交给了 ...

  2. Vue3中如何使用this

    vue3提供了getCurrentInstance ,通过这个属性,直接使用ctx是错误的,需要找到全局属性globalProperties import { getCurrentInstance } ...

  3. 假期小结4hive学习beeline

    学习Beeline是连接和管理Hive的一种有效方式.Beeline是一个用于与Hive交互的命令行工具,它支持通过JDBC连接到Hive服务器,并执行查询和管理Hive表等操作. 以下是一些关于Be ...

  4. Umov移动方块-scratch编程作品

    程序说明: <Umov移动方块>是一款基于Scratch平台制作的小游戏.在这个游戏中,玩家将面对一个3×3的圆圈棋盘,并通过鼠标控制蓝色方块在这些圆圈中灵活移动.游戏的挑战在于,舞台的四 ...

  5. Codeforces Round 953 (Div. 2)

    Codeforces Round 953 (Div. 2) 闲来无事水题解. A . B . C 显然 \(k\) 是偶数.考虑 \(k\) 的上界,\(p_{1}=n,p_{n}=1\),产生 \( ...

  6. 【Mybatis-Plus】制作逆向工具

    官方文档可参考: https://baomidou.com/pages/779a6e/#快速入门 工具需要的依赖 <?xml version="1.0"?> <p ...

  7. 【MySQL】主从配置

    一主一从(或二从)部署: 参考博客: https://www.cnblogs.com/lelehellow/p/9633315.html 正好借着3台机器,可以做一个 1主 2从的分配 192.168 ...

  8. 【Java】Main方法的命令行参数

    可以使用命令行注入参数执行

  9. 从.net开发做到云原生运维(三)——.net core的学习路线

    1. 前言 前面几篇文章主要讲了.net技术相关的基本概念和一些涉及的项目什么,本身也没讲太多底层的技术,这篇文章依旧不讲具体的技术,主要介绍.net技术的学习路线.学习路线也是我18年毕业的时候看到 ...

  10. 图床、云对象存储、CDN业务:“自定义 CDN 加速域名” 与 “自定义源站域名” 的区别

    参考: https://developer.qiniu.com/kodo/8622/dev-the-binding-source-domain-name ----------------------- ...