0%

我在最近的前端项目中用到了 vite 与 swc 作为构建工具和 JavaScript 编译器,二者都比较年轻,但提供更优秀的前端开发体验。当下前端生态的新选手层出不穷,本文就简单介绍一下最近几年前端生态涌现的诸多新兴的技术与工具。

Javascript 运行时

Deno

Deno是一个使用 rust 开发,基于 V8 引擎的简单、现代、安全的 Javascript 和 TypeScript 运行时。
我在 2021 年的时候写过一篇文章介绍deno。最近两年 Deno 的发展比较迅速,引入了一些新的 API 与能力,提高了稳定性以及与 Node.js 的兼容性。
顺便提一下Deno Deploy平台,可以方便地部署 Deno 项目为 severless 应用。

Bun

Bun是一个使用 zig 语言开发的基于 JavaScriptCore 引擎的全功能 JavaScript 和 TypeScript 工具集。

JavaScript 编译器与打包器

SWC

SWC是一个可扩展的基于 Rust 的下一代快速 Web 开发工具,同时支持 JavaScript 的编译与打包。Deno 和 Next.js 都使用了 SWC 作为编译器。

esbuild

esbuild是一个极快的 web 打包工具,使用 go 语言编写。

bun bundler

bun 对 JavaScript 模块的打包有原生的支持(官方的 benchmark 中比 esbuild 要快很多)。

Node.js 版本管理工具

volta

volta是一个使用 Rust 开发的新兴的 Node.js 版本管理工具。我目前正在使用 volta 管理 Node.js 版本,可以简单地根据项目切换 Node.js(包括 npm 与 yarn)的版本,安装不同版本的 Node.js 也很快。

fnm

fnm是一个快速、简单的 Node.js 版本管理工具,使用 Rust 开发。

Node.js 包管理工具

yarn v2+

新版本yarn做了很多改进,比如使用 Plug'n'Play 模式进行依赖项解析,提高性能与可靠性;支持零安装模式;对 monorepo 的支持更好等。

pnpm

pnpm是一个快速、节省空间的包管理器。

构建工具

parcel

parcel是一个快速、零配置的 Web 应用程序打包工具,支持 JavaScript、TypeScript、CSS、HTML、JSON 等文件的打包。parcel 2.0 版本使用了 swc 作为编译器,速度快了很多。

vite

vite是一个新兴的前端构建工具,在开发时使用 HMR 技术实现秒级的热更新,编译时使用 esbuild 和 Rollup 进行编译与打包,速度非常快。vite 也是 vue3 的官方构建工具。

tuborpack

tuborpack是一个使用 rust 开发的针对 JavaScript 和 TypeScript 优化的渐进式打包工具,由 Webpack 的原作者开发。

前端框架(React.js 生态)

next.js

next.js是一个 React 框架,提供了 SSR、SSG、CSR 等多种渲染模式,同时提供了很多优秀的功能,比如图片优化、代码分割、预取、预加载、路由、静态资源优化等。

Remix

Remix是一个全栈 web 框架,支持 SSR 与 CSR。

最近因工作需要,需要开发一个后台管理系统。考虑到开发效率和技术成熟度,决定使用 React 和 Ant Design 的技术栈。

之前我一直使用 create-react-app 来创建 React 项目,但是 create-react-app 已经很久没有更新了,新版的react 文档也不再推荐使用 create-react-app,而是推荐使用更成熟的框架,比如 next.js 与 remix。next.js 与 remix 都是比较大的 SSR 框架,对于简单的后台管理系统来说,可能有点大材小用。

作为一个新兴的前端构建工具,vite 的成熟度日益提升,对于 React.js 的支持也已经很完善,所以我决定使用 vite 来开发这个后台管理系统。

Read more »

最近获取到了腾讯云的的 Serveeless Stable Difussion 的内测资格,部署一个无服务器版本的 SD Web UI,总体体验比较良好。

部署流程

获取内测资格

首先需要获取内测资格,可以通过这个链接申请。

创建 Serverless 应用

访问Serverless 应用控制台,点击“新建应用”。

create serverless app

选择应用模板

在应用市场分类下选择“AI 绘画(Stable Diffusion)应用”模板,填写应用名称,点击“下一步。”。

choose app template

应用配置

填写应用名称,选择地域,点击“完成”,等待应用创建完成。

cofig app

deploy log

体验 SD Web UI

serverless 应用创建完成后,可以在应用详情页查看应用的访问地址。

app url

sd web ui

图片生成速度还是很快的,不弱于 Google Colab 的体验。以下是使用默认提示词和配置生成的赛博朋克主题的城堡图片。

sample image

总结

在公有云 GPU 机器价格高昂的环境下,使用 serverless 无服务器应用部署 AI 应用或许是一个不错的选择。

Python 是一门动态类型语言,没有编译器对变量类型正确性的检查与保证,这也意味着经常需要在运行时对变量的类型进行校验,尤其是在后端接口开发中,毕竟前端传入的数据往往是不可控的。

Python 3.5 引入了类型注解与 typing 模块,可以对 Python 代码进行静态类型检查,很大程度上提高了代码的可读性与可维护性,尤其是在较大的项目中。

除了静态类型检查,Python 的类型注解也可以在应用在运行时,例如 FastAPI(Pydantic) 就是利用了类型注解来进行请求参数解析、数据校验和 OpenAPI 文档生成的。

实际上 Python 标准库提供了一个简单的运行时类型检查的能力。

使用 isinstance 进行类型检查

isinstance 函数最常见的用法是判断一个对象是否是某个类型(及其子类)的实例,例如:

1
2
3
isinstance(1, int)  # True
isinstance("hello", str) # True
isinstance(None, object) # True

使用isinstance检查抽象类型

到了类型注解的时代,我们可以使用 isinstance 来检查一个对象是否实现了某个抽象接口,例如:

1
2
3
from typing import Callable, Iterable
isinstance(print, Callable) # True
isinstance([1, 2, 3], Iterable) # True

有一些遗憾的是,这里并不能为抽象类型添加范型参数(毕竟对容器的每个元素进行类型检查是一个非常耗时的事情),例如:

1
isinstance([1,2,3], list[int]) # TypeError: isinstance() argument 2 cannot be a parameterized generic

使用 isinstance 检查 typing.Protocol 自定义类型

Python 3.8 引入了 typing.Protocol 类与typing.runtime_checkable装饰器,可以用来定义类型,然后在运行时对对象进行类型检查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from typing import Protocol, runtime_checkable
from dataclasses import dataclass

@runtime_checkable
class HasName(Protocol):
name: str

def say_hello(obj: HasName) -> None:
assert isinstance(obj, HasName), "obj must have a name attribute"
print(f"Hello {obj.name}")

@dataclass
class Person:
name: str

@dataclass
class Dog:
nick: str


say_hello(Person("John")) # Hello John
say_hello(Dog("Bobby")) # AssertionError: obj must have a name attribute

总结

虽然会带来一定的性能损耗,但是运行时的类型检查在很多场景下都是必要的,isinstance 函数可以帮助我们实现这一目的。
对于更复杂的类型检查,可以借助 dataclass 或者 pydantic。
值得一提的是 pydantic 的 2.0 版本使用 rust 上线了核心的数据校验逻辑,性能上有了很大的提升。

今天在自己的 PC 上部署和体验了ChatGLM-6B的推理服务,简单记录一下流程。

web ui

ChatGLM-6B 简介

ChatGLM-6B 是一个开源的、支持中英双语的对话语言模型,基于 General Language Model (GLM) 架构,具有 62 亿参数。结合模型量化技术,用户可以在消费级的显卡上进行本地部署(INT4 量化级别下最低只需 6GB 显存)。
ChatGLM-6B 使用了和 ChatGPT 相似的技术,针对中文问答和对话进行了优化。经过约 1T 标识符的中英双语训练,辅以监督微调、反馈自助、人类反馈强化学习等技术的加持,62 亿参数的 ChatGLM-6B 已经能生成相当符合人类偏好的回答,更多信息请参考我们的博客

Read more »

晚上闲来无事,和 New Bing 就 solid.js 主题进行了一些交流,在十来轮的对话中,我对 solid.js 有了更深的了解。

编写的一个示例组件

calculator

节选的对话内容

介绍一下 solid.js

Solid.js 是一个用于创建用户界面的声明式 JavaScript 库 ¹。它受到 React 的大影响,但与 React 不同的是,它不使用虚拟 DOM,而是将模板编译为真实的 DOM 节点,并用细粒度的反应来更新它们 ⁴。你可以在你的应用中声明你的状态,并在任何地方使用它,当状态发生变化时,只有依赖于它的代码才会重新运行 ⁴。
Solid.js 的优点是性能高,语法简洁,灵活度大,开发体验好 ¹。它在许多 UI 速度和内存利用率的基准测试中都名列前茅 ¹。它还支持所有预期的现代库特性,甚至改进了一些特性以提高开发体验 ¹。

Solid.js 目前还是一个相对新的库,可能还没有 React 那么成熟和稳定。如果你想了解更多关于 Solid.js 的信息,你可以访问官方网站 ¹,或者查看教程 ² 和 GitHub 仓库 ⁴。

源: 与必应的对话, 2023/4/15
(1) SolidJS · Reactive Javascript Library. https://www.solidjs.com/.
(2) GitHub - solidjs/solid: A declarative, efficient, and flexible …. https://github.com/solidjs/solid.
(3) SolidJS · Reactive Javascript Library. https://www.solidjs.com/tutorial.
(4) 次世代の React? Solid.js について - Zenn. https://zenn.dev/nicky/articles/754f0ca74c887a.
(5) 5 Places SolidJS is not the Best - DEV Community. https://dev.to/this-is-learning/5-places-solidjs-is-not-the-best-5019.

Solid.js如何实现细粒度的反应?

Solid.js实现细粒度的反应的原理是基于观察者模式²。它使用一个全局的栈来在运行时追踪哪些信号(Signal)被读取或写入,以及哪些效果(Effect)或记忆(Memo)依赖于它们¹。当一个信号的值发生变化时,它会通知所有订阅了它的效果或记忆,并重新执行它们的函数¹。这样,只有真正需要更新的部分才会重新运行,而不是整个虚拟DOM³。

这种反应性的方式非常强大和灵活,它可以处理动态变化的依赖关系,比如通过执行不同的条件分支¹。它也可以通过多层间接性来工作,任何在跟踪范围内执行的函数也会被跟踪¹。你可以把Solid.js的反应性看作是一个自动化的事件系统,你只需要声明你的数据和视图,剩下的更新逻辑都由框架来处理。

源: 与必应的对话, 2023/4/15
(1) What is Reactivity - docs.solidjs.com. https://docs.solidjs.com/references/concepts/reactivity.
(2) SolidJS · Reactive Javascript Library. https://www.solidjs.com/guides/reactivity.
(3) Reactivity with SolidJS - Frontend Masters. https://frontendmasters.com/courses/reactivity-solidjs/.
(4) First look at fine-grained reactivity in Solid - SolidJS Source Code …. https://jser.dev/react/2023/02/26/reactivity-in-solidjs.html.
(5) SolidJS · Reactive Javascript Library. https://www.solidjs.com/?ref=workingsoftware-dev.

举个例子说明信号、效果和记忆的用法。

好的,我来给你一个简单的例子。假设我们要做一个计算器,可以输入两个数字,并显示它们的和、差、积和商。我们可以用信号来表示输入的数字,用效果来监听输入的变化并更新显示的结果,用记忆来缓存计算的结果。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { createSignal, createEffect, createMemo } from "solid-js";

const Calculator = () => {
// 创建两个信号,表示输入的数字
const [num1, setNum1] = createSignal(0);
const [num2, setNum2] = createSignal(0);

// 创建四个记忆,表示计算的结果
const sum = createMemo(() => num1() + num2());
const diff = createMemo(() => num1() - num2());
const prod = createMemo(() => num1() * num2());
const quot = createMemo(() => num1() / num2());

// 创建一个效果,当输入或结果发生变化时,打印到控制台
createEffect(() => {
console.log(`num1: ${num1()}, num2: ${num2()}`);
console.log(`sum: ${sum()}, diff: ${diff()}, prod: ${prod()}, quot: ${quot()}`);
});

// 返回一个JSX元素,包含两个输入框和四个显示结果的标签
return (
<div>
<input type="number" value={num1()} onInput={(e) => setNum1(e.target.value)} />
<input type="number" value={num2()} onInput={(e) => setNum2(e.target.value)} />
<p>Sum: {sum()}</p>
<p>Diff: {diff()}</p>
<p>Prod: {prod()}</p>
<p>Quot: {quot()}</p>
</div>
);
};
Read more »

随着 ChatGPT、Midjourney 的流行,越来越多的企业和个人开始使用 AIGC 来生成内容,并且尝试利用大语言模型来开发应用(相关的开源项目有 hwchase17/langchainmicrosoft/JARVIS)。

ChatGPT 等大语言模型实际上经常会生成一些准确度不是很高的内容(被有些人称作“幻觉”),这也是它们的局限性之一。

人工验证 AIGC 内容的真实性是一件很费时并且成本很高的事情,一定程度上也违背了使用 AI 工具的初衷。

我们可以使用一些其他工具来验证 AIGC 的内容,例如对于 AI 生成的代码,可以使用对应语言的静态分析工具(pylint、eslint 等);对于 AI 生成的图片,可以使用图像识别工具(Google Vision APIAzure Computer Vision等)。

当然,我们可以使用 AI 工具本身来验证 AIGC 内容。

使用 ChatGPT 生成 SQL

SQL Generator

在上图的实验中,我让 Chatgpt 扮演一个 SQL 生成器,在每次提问之后,让它生成对应的 SQL 语句。

使用 ChatGPT 验证 SQL

SQL Linter

在另外一个 playgroud 中,我让 chatgpt 扮演一个 SQL 验证器,让它验证第一个实验中的 SQL 语句是否正确。

总结

上面的实验总体上来说还比较初步,不过我们完全可以使用 AI 工具来验证 AIGC 的内容。某种程度上,这也算一种GAN的应用。

我最近配置了一台新的 PC,整体配置还是不错的。

  • 处理器 13th Gen Intel(R) Core(TM) i7-13790F 2.10 GHz
  • 机带 RAM 32.0 GB (31.8 GB 可用)
  • 硬盘 HS-SSD-CC700 2048GB
  • 显卡 NVIDIA GeForce 2060 Super

新 PC 的使用体验十分良好,以至于我想随时通过其他设备远程访问到机器,毕竟在云平台上使用相同配置的虚拟机每个月大概是需要几千元人名币的。

访问方式

Teamviewer 或向日葵

出于个人隐私和成本的考虑,我没有使用 Teamviewer 和向日葵等商业应用。

RDP

远程登录 Windows 设备最主流的方式是通过微软提供的 RDP 协议,在 Windows 设备上,可以通过内置的“远程桌面连接”应用;在安卓和 iOS 等移动设备上,可以从应用商店下载“Microsoft 远程桌面应用”。

Windows RDP
RDP on Android

值得注意的是,如果想要其他设备远程访问到你的 Windows PC,系统版本需要是专业版,普通的家庭版是不提供这个能力的。

SSH

Windows 10/11 系统是可以开启 openssh server 的。

openssh server

开启后就可以通过 ssh 客户端远程连接 Windows PC 了。

ssh to a windows pc login

ssh to a windows pc result

配置公网访问

设置好 RDP 和 openssh server 后,就可以在局域网内远程登录到 Windows 设备了。但如果想要在公网机器上自由远程访问,则需要一点额外的配置,毕竟国内的家庭宽带通常是不提供固定的公网 IP 的。这种将局域网设备的端口暴露到公网上就是典型的内网穿透的应用场景了。

可以使用内网穿透服务将 3389 端口(RDP)和 22 端口(sshd)暴露到公网上。

使用 ngrok 等服务

内网穿透最简单的方式是使用ngrok等现成服务,操作流程比较简单。使用这些服务需要注意保护好个人隐私。

在云服务器配置内网穿透服务

如果有一台在公网上的云服务器的话,完全可以自己搭建内网穿透服务。有很多工具可以用,我比较喜欢的是使用 rust 编写的 rathole。

rapiz1/rathole: A lightweight and high-performance reverse proxy for NAT traversal, written in Rust. An alternative to frp and ngrok. (github.com)

云服务器上暴露的端口最好不要继续使用 3389 和 22 端口(当然 22 端口可能已经在使用中了),以免被一些恶意访问骚扰。

配置开机启动 rathole 服务

可以使用 nssm 将 rathole 应用封装为一个 Windows 服务,从而可以开机自动启动(以及一些意外情况下的自动重启)。

NSSM - the Non-Sucking Service Manager

config windows services

总结

通过简单的配置,我们可以从任何地方远程访问自己的 Windows 设备。

最近用 Python 实现了一个BrainFuck 解释器,简单介绍一下过程。

BrainFuck 介绍

BrainFuck是一门非常简单的图灵完备的编程语言,只有 8 个指令:
Brainfuck 包含一个有 30,000 个单元为 0 的数组,和一个数据指针指向当前的单元。

  • + : 指针指向的单元的值加 1
  • - : 指针指向的单元的值减 1
  • > : 将指针移动到下一个单元(右边的元素)
  • < : 将指针移动到上一个单元(左边的元素)
  • . : 打印当前单元的内容的 ASCII 值 (比如 65 = ‘A’).
  • , : 读取一个字符到当前的单元
  • [ : 如果当前单元的值是 0,则向后调转到对应的]处
  • ] : 如果当前单元的值不是 0,则向前跳转到对应的[处

使用 BrainFuck 编写程序十分繁琐,以下是一个输出字符“A”的程序:

1
++++++ [ > ++++++++++ < - ] > +++++ .

以下是来自New Bing对这段程序的解读:

  • ++++++:将数据指针指向的单元的值增加 6,变为 6。
  • [ > ++++++++++ < - ]:这是一个循环,每次循环都会将数据指针右移一位,将新单元的值增加 10,然后左移一位,将原单元的值减 1,直到原单元的值变为 0。这样,循环结束后,数据指针右边的单元的值变为 60(6 乘以 10)。
  • >:将数据指针右移一位,指向刚才修改过的单元。
  • +++++:将该单元的值增加 5,变为 65。
  • .:输出该单元的值对应的 ASCII 字符,即大写字母 A。
Read more »