前言

我写过一篇关于 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. 全网最适合入门的面向对象编程教程:06 类和对象的Python实现-自定义类的数据封装

    全网最适合入门的面向对象编程教程:06 类和对象的 Python 实现-自定义类的数据封装 摘要: 本文我们主要介绍了数据封装的基本概念和特性,如何设置自定义类的私有属性和私有方法,protect 属 ...

  2. 利用opencv库使用Python将视频逐帧转为图片

    做成型的语义分割软件需要,写了一个,在博客记录一下 import cv2 def video2pic(videoFile, outputFile): vc = cv2.VideoCapture(vid ...

  3. webgl(threejs)生成房间楼层

    楔子 在很多数字孪生项目中,都会涉及到楼层的建模.楼层的建模由于结构繁多,如果都是建模师进行手动建模,工作量会比较大.而楼层本身的结构,可以抽象成可以通过路径构造的对象(这和之前的文章提及的的管路以及 ...

  4. 2024-07-17:用go语言,给定一个整数数组nums, 我们可以重复执行以下操作: 选择数组中的前两个元素并删除它们, 每次操作得到的分数是被删除元素的和。 在保持所有操作的分数相同的前提下,

    2024-07-17:用go语言,给定一个整数数组nums, 我们可以重复执行以下操作: 选择数组中的前两个元素并删除它们, 每次操作得到的分数是被删除元素的和. 在保持所有操作的分数相同的前提下, ...

  5. 题解:P10677 『STA - R6』inkar-usi

    背景 把人家鸽了,感觉废了. 分析 这道题刚看到题目的时候很多人会想爆搜,但是因为 \(10^3\) 的数据范围,所以应该去想一想是不是有什么性质. 我们稍微想一想就会发现,题目上提到了可以重复走,那 ...

  6. CF1363A 题解

    洛谷链接&CF 链接 题目简述 共有 \(T\) 组数据. 对于每组数据,给定 \(n,x\) 和 \(n\) 个数,问是否可以从 \(n\) 个数中选 \(x\) 个使其和为奇数,可以输出 ...

  7. Linux 基于flock命令实现多进程并发读写文件控制

    基于flock命令实现多进程并发读写文件控制 需求描述 实际项目中,需要在Linux下通过shell脚本并发读写同一个文件,但是希望同一时刻,只有一个进程可以在读.写目标文件. 解决方案 使用floc ...

  8. SEO初学指南之关键词研究(2) - 搜索意图分析

    Hi,我是听风.这篇文章我们深入探讨下关键词研究中最重要的一点:搜索意图. 虽然这在本系列教程的第一篇中提到过搜索意图,但是我还是想再写一篇文章来帮助你们了解什么是搜索意图以及如何再关键词研究中使用它 ...

  9. Jmeter函数助手28-urldecode

    urldecode函数用于解码application/x-www-form-urlencoded字符串. String to encode in URL encoded chars:填入applica ...

  10. 【RabbitMQ】04 路由模式

    在订阅模式的基础上制定一些特定发送规则 创建路由模式的生产者: 注意这些变化,跟之前的订阅模式并不一样 package cn.dzz.routineQueueInProducer; import co ...