AI 编程的未来

很久没写博客了,这一年发生了很多事。主要是换了城市,还成立了家庭,然后工作上躺平了半年之后终于又开始工作了。

工作内容果不其然全面地转向了 AI 创作,现今与 AI 无关的程序员岗位几乎已经绝迹了,大概程序员这个职业也快终结了哈哈哈。

这周末去听了 GOSIM 的大会,开车过去半个小时(是的,我买车了),跟两年前一样见到了不少老外程序员,还是永恒不变的几个 Rust 国内布道者。一切似乎都变了,但一切似乎又都没变。主题无外乎 AI,机器人和 Rust,有种诗与远方的平和,大概互联网公司也开始迈向国企化了。

讲座非常密集,两天时间估计有接近百场,听了估计不到十场,听得印象深刻的有:

  1. Servo 嵌入式开发
  2. Ant Design 开源十周年
  3. 语音 Agent
  4. MarkdownFlow 面向 Agent 的 HTML
  5. Prune4Web
  6. Crate 供应链安全
  7. Makepad 工作坊
  8. ArceOS 组件化内核

老外讲师都比较自由奔放,几乎每个都是络腮胡或是长发飘飘,要么西装革履,要么几乎穿个大裤衩。然后 PPT 清一色全是英文长句子,有的篇幅辽阔,有的言简意赅。不管讲的是浅显还是夸张,都透露出一种听我的就对了的口气。国内的讲师基本都是 T 恤衬衫,呆着眼镜,然后拿着反复斟酌排版仍然透露出寒酸气息的 PPT,时刻关注观众反应地小心谨慎地陈述。


总得来说,无外乎以下几种方向:

  1. 如何让 AI 更好地理解人类世界(主要是人自身的输入)
  2. 如何让 AI 更好地表达自己
  3. Rust 如何赋能安全性/自身如何安全
  4. 人类手搓代码不容易,大家行行好给点 financial support

总的看来真是机械降神,科技平权啊。人类前 30 年辛辛苦苦建立的互联网世界,一夜间被 AI 几乎全部给颠覆了。不懂编程的人都能喊出要让所有的程序让 AI 重新写一遍的口号了,程序员的生存空间已然变得极其狭窄。这次几乎没听到讲得通俗易懂的机器人开发,总觉得机器人领域现在也缺少一个降维的过程,翻来覆去还是传感器和控制算法这一套,如何面向更实际用户的需求来进化,可能也会促使这个行业诞生 ChatGPT 这样的巨无霸应用。

结合到现在工作的开发内容,AI 已经在如何和计算机好好相处方面全面超越了人类,目前已有的纯软件方面的问题只剩下如何让 AI 可以互相沟通了。当然还有与人交互,但这就不是 AI 自身需要完善的能力范围了。

下一届大会,是不是就应该出现 AI 讲师了呢?完全由 AI 撰写并完成演讲的讲座,让我们拭目以待吧!


网站主题

随着 CSS Variables 的普及,切换网站主题变成了一件轻松又愉快的工作,只需简单在 @media (prefers-color-scheme: dark) 下指定新的 --* 变量即可。但如果有多个主题,或者是主题嵌套该怎么办呢?
本文将告诉你如何解决这一切。

主题
纯色主题
带图主题
自定义主题
亮色主题
深色主题
主题管理器
document.body

主题复用

假设我们有三个主题,

  • 纯色默认主题 A (即 :root)
  • 暗色主题 B 继承自 A
  • 带图主题 C 继承自 A

很明显,我们可以写成下面这样的样式表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
:root,
[data-theme='B'],
[data-theme='C'] {
--red: #f00;
--bg: #fff;
}

[data-theme='B'] {
--bg: #000;
}

[data-theme='C'] {
--background: url(c.png)
}

:root {
--bg: #fff;
}

但我们很快发现一个问题,主题间可能存在覆盖问题,A 与 B 的 CSS 优先级是一样的,如果有一个在下方就会被覆盖,那如何能加深这个深色模式呢?答案是加一层选择器。

我们发现 data-theme 已经不适合表达包含明暗和其他配色的多个主题了,于是用 data-mode 来表示明暗。

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
:root,
[data-theme='A'],
[data-theme='A'][data-mode='light'],
[data-theme='B'][data-mode='dark'],
[data-mode='dark'],
[data-theme='C'],
[data-theme='C'][data-mode='light'] {
--red: #f00;
--bg: #fff;
}

[data-mode='dark'],
[data-theme='B'][data-mode='dark'], {
--bg: #000;
}

[data-theme='C'],
[data-theme='C'][data-mode='light'] {
--background: url(c.png)
}

// 其他地方引入的样式

:root {
--bg: #fff;
}

看起来多了很多无用的代码,但我们列个表格来分析下各种组合。

明暗 A B C
Light 白底无图 N/A 白底带图
Dark N/A 黑底无图 N/A

可以看到最后一个 :root 由于优先级不如前方主题将不会覆盖前面的样式。

主题嵌套

前面我们解决了平级主题的复用,但如果是父子元素的嵌套呢?这时就变成了一个 CSS Variables 的嵌套。

HTML 模板

前端三剑客 HTML/CSS/JavaScript。JavaScript 已经飞黄腾达,甚至都不认浏览器这个祖宗了;CSS 痴迷与自己的布局样式、动画和色彩、Houdini 扩展等等,进入了修仙的通道。看来看去只剩 HTML 这个老顽固还在自己的一亩三分地里爬坑,几十年来一点长进都没有。

虽然 web components 让人们看到了重拾 HTML 组件的希望,但随着 Safari 等厂商的不配合,以及深入探究后的继承兼容性问题,最终也没有达成一致。HTML 还是归于一滩死水。但这样的的好处也很明显,像其他领域还在挣破头皮要开发新功能时,HTML 只要能渲染 if/else, for loop,import/include, pipe/filter 就谢天谢地足够日常使用了。

正因如此,其实各家 MVVM 与其说是为了渲染 HTML 而写了个框架,不如说是挂羊头卖狗肉专职搞数据流,只是把随手写的 HTML 模板滥竽充数地塞到了用户面前。而这也造成了各家的模板语言五花八门,根本无法有效互通。

React
Svelte
Vue
Angular

更可恶的是,小程序还有一套模板语言……

这些模板可谓是争奇斗艳,群魔乱舞。但最大的一个问题是,很多模板都在破坏 HTML 自身的图灵完备。几十年后,不要说读懂这段代码,就是运行版本都找不到。

有悲观的博主甚至直接切换到了 web components 来对抗这种不确定性,但这只适合于自娱自乐的自嗨项目,公司级项目肯定不合适。咋办呢?先调研调研用过的模板吧。

模板大赏

非 JavaScript 模板

Django 模板

这大概是我用过最早的模板了,甚至还记得当时第一次联动 MySQL 输出动态内容时的喜悦,语法接近于 ejs,大括号加 % 包裹一切。用起来平平无奇,只记得 Python 传数据用 dictionary 很麻烦,每次都要加 ['*'] 来获取对应的键值。但这种模板的好处是跟后端彻底分离,只作为模板单独存在,也容易替换。

Plim

平心而论这可以说是我见过地表最强的模板了,像瑞士军刀一样百搭。基于 Mako 的设计,使其支持了很多你想要的功能,很多你想都想不到的功能它也支持。甚至直接在模板层编译 Sass 和 Stylus,还有 markdown。但它最最令人满意的是,和 Jade(现在叫 Pug 了)还有 Python 一样,是基于缩进来区分嵌套层级的。

Plim

less is more

当你写了一整天代码,突然说要改个模板中的链接,你就会发现没有闭合尖括号的代码是多么的赏心悦目。

JavaScript 模板

静态模板

Handlebars / Mustache

这两个框架都以他们的 logo 而闻名,大胡子形象深入人心。要说还有啥特别之处,倒不如说是他们都有别的语言的实现,可以方便地迁移。哦,对了,还都定位于可以渲染不只是 HTML 的内容,比如 RTF 或者配置文件 config.ini 之类,但还有哪个别的框架不能这么做吗?微笑脸😊

ejs

一个标榜自己是纯 JS 语法的模版,广义上来说它也算是 JSX 的一种了。相比其他模板遮遮掩掩去写 loop 和 if/else 标签,它做到了原生 JS 的控制流。

1
2
3
<% if (user) { %>
<h2><%= user.name %></h2>
<% } %>
1
2
3
4
5
<ul>
<% users.forEach(function(user){ %>
<%- include('user/show', {user: user}); %>
<% }); %>
</ul>

怎么说呢,看起来虽然有些怪,但还是比较符合直觉的哈。

nunjunks
又一个舶来品,从 Python 的 Jinja2 改装而来,总体平平无奇,有个 asyncAll 的功能可以在模板里用 Promise,但我何必呢 …… 直接在外面拼好不就行了。

动态模板

JSX

JSX 可谓是大道至简。语法上除了直接渲染 Array 展开表示 for loop 和 && 表示 if 之外,再无别的特殊语法。哦,还有个 dangerouslySetInnerHTML 可以表示 raw 输出 js 内容到 HTML。

可以说是跟 React 的理念一样,消除了绝大部分需要记忆的内容。

all-in-one (Vue, Svelte)

这种模板,又可以说是框架的一部分,早已为了框架整体的性能或者架构设计而做了很多特殊适配。比如 Angular 的 Ivy Rendering Engine 就思考了这样一个问题,到底是现有组件还是现有模板?很明显从数据的流向来说,先有组件里的 data 再有 template,这样就出现了问题,在加载组件时要先加载组件的 class 部分再去 compile template,那不就存在 template 不对应但却运行下去了?所以 Ivy 就把 template 里的类型定义反转暴露给组件侧进行 AOT 检查。

详见 https://www.angularminds.com/blog/what-is-angular-ivy

The Ivy Compilation Model

In the Ivy model, Angular decorators (@Injectable, etc) are compiled to static properties on the classes (ngInjectableDef). This process takes place without a complete analysis of code, and in most cases with a decorator only. Here, the only exception is @Component, which requires knowledge of the meta-data from the @NgModule which declares the component in order to properly generate the ngComponentDef. The selectors which are applicable during the compilation of a component template are determined by the module that declares that component.

The information needed by Reference Inversion and type-checking is included in the type declaration of the ngComponentDef in the .d.ts. Here, Reference Inversion is the process of determining the list of the components, directives, and pipes on which the decorator(which is getting compiled ) depends, allowing the module to be ignored altogether.

解决方案

目标

  • 同时支持 SSR 和 SPA 动态渲染
  • 对数据和现有框架侵入低

Next.js 介绍