前言

我写过一篇关于 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_电传打字机_打印头_print_head_carriage_词源

    换行回车 回忆上次内容 上次我们 diy了 自己的小动物 还可以 让小动物 变色.报时 还可以 说些话 这很亚文化 很酷炫的亚文化 不是吗? 回忆一下 最开始 研究报时 的 时候 回到 本行行头 的 ...

  2. [oeasy]教您玩转python - 0005- 勇闯地下城

     ​ 继续运行 回忆上次内容 上次从1行代码进化到了2行代码 yy p粘贴剪贴板中的内容 将剪贴板中的代码粘贴9999次 9999p 真的实现了万行代码梦 是真·圆梦 没有撒谎的那种 不过圆梦之后多少 ...

  3. 项目中的坑记录~v-if和v-show的坑

    有个功能是这样的,点击获取验证码,获取验证码之后将输入框禁用,进行倒计时11秒. 问题:第一次的倒计时是从6开始的, 之后的倒计时都是从9开始倒计,没有从11开始 解决:主要是用了v-show.倒计时 ...

  4. exceptionx:灵活便捷的Python异常处理库,让异常处理更高效!

    exceptionx English | 中文 exceptionx 是一个灵活且便捷的Python异常处理库,允许你动态创建异常类,并提供多种异常处理机制. exceptionx 的前身是 gqyl ...

  5. web3 产品介绍:硬件钱包Ledger 离线管理私钥更安全

    Ledger是一款硬件钱包,可以安全地存储用户的加密资产,并在需要时进行交易.作为一种离线存储设备,Ledger钱包比在线钱包更加安全,因为它能够保护用户的私钥和交易信息,使其免受黑客攻击和网络病毒的 ...

  6. 06 定时器和PWM(1)

    前言 前面介绍了一下外部中断,这一节主要介绍一下内部定时器和PWM,这两个知识还是比较重要的. 一.定时器 1.什么是定时器 定时器其实和计数器一样,我们通过设置一个值,当计数器运行一个计数寄存器向上 ...

  7. 第九讲: MySQL为什么有时候会选错索引?

    第九讲: MySQL为什么有时候会选错索引? ​ 前面我们介绍过索引,你已经知道了在 MySQL 中一张表其实是可以支持多个索引的. ​ 但是,你写 SQL 语句的时候,并没有主动指定使用哪个索引.也 ...

  8. 【Java / JavaScript】AES加密解密

    Java封装的AES加密解密工具类: 几个重要的参数信息 - 需要指定一个密钥串sKey 密钥内容自定义 数字 + 字母 + 特殊符号 - 加密方式为 AES - AES下面的模式ECB - ECB下 ...

  9. 【Spring】06 Aop切面功能

    什么是Aop? Aspect Oriented Programming 面向切面编程 通过预编译的方式和运行期动态代理实现程序功能统一维护的一种技术 是OOP的延续,也是Spring第二个核心内容 可 ...

  10. Python的GDAL库绘制多波段、长时序遥感影像时间曲线图

      本文介绍基于Python中的gdal模块,对大量长时间序列的栅格遥感影像文件,绘制其每一个波段中.若干随机指定的像元的时间序列曲线图的方法.   在之前的文章中,我们就已经介绍过基于gdal模块, ...