MoreRSS

site iconJadeYang | 杨琼璞

晚晴幽草轩。90后,生于陕西,居于深圳。热爱文学、武术、科技,曾就职于博雅互动和大疆创新。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

JadeYang | 杨琼璞的 RSS 预览

生财有迹 | 您专属的资产跟踪与分析工具

2024-08-28 20:20:00

生财有迹(Wealth Tracker)是一款专注于个人资产分析的应用程序。其核心功能是:全面记录并展示用户的资产状况,帮助用户轻松了解财务现状;运用 AI 能力,结合每种资产的特性和当前环境,提供适宜的财务建议。

生财有迹 - 效果预览

项目愿景

在当今多元化的经济环境中,个人资产管理变得日益重要。它往往分散于各类金融账户和服务中,例如银行存款与理财产品、移动支付平台(如微信支付、支付宝)、公积金、医保账户、货币基金(例如余额宝)、债券、各种股票及基金产品、房地产、贵金属、外部借款(尽量不做此配置🤫)以及其他投资等等(对于部分朋友,或许还有贷款、欠款等负债)。这些账户中的数额在不断变动,使得快速准确地了解个人总资产状况成为一项挑战。

本项目旨在提供一个高效、直观的解决方案,以应对个人资产管理中的分散性和复杂性。生财有迹专注于账户余额及整体数额,避免深陷于单笔收支的琐碎细节。其目标是通过简化操作流程,帮助用户揭示个人资产的整体变化趋势,并通过友好的用户界面,使用户能够轻松记录和洞悉自己的财务信息。如欲了解更多,可移步至博文:生财有迹 | 您专属的资产跟踪与分析工具

核心特性

  • 简洁易用的操作界面:用户可以通过几个简单的步骤快速上手,无需任何复杂的财务知识。
  • 丰富的数据可视化:通过图表和图形,直观展示资产变化,帮助用户轻松掌握财务状况。
  • 本地部署的灵活性:部署在哪里由用户决定,确保数据的私密性、安全性,以及可扩展性。
  • 开源的代码架构:项目的源代码完全开放,欢迎参与贡献,以不断改进和完善工具的功能。

通过这些特性,希望建立一个用户友好、透明且可信赖的个人财分析工具,帮助用户更全面地掌握自己的财务状况。

在线体验

为了让您更直观地了解 生财有迹 的功能和特性,已在服务器上使用 pm2 部署了一个演示版本。该演示环境填充了模拟数据,方便您全面体验各项功能。无论您是想评估工具的实用性,还是出于好奇想一探究竟,欢迎访问以下链接进行体验:

https://fund.niceshare.site/

请注意,这是一个公共演示环境,因此请勿在其中输入任何真实的个人财务信息。建议您在体验后,考虑按照本文档的指导,在自己的环境中部署和使用”生财有迹”,以确保您的财务数据的隐私和安全。

如果您在使用过程中遇到任何问题或有反馈意见,欢迎通过项目的 GitHub 页面 与我们联系。您的宝贵意见将帮助我们不断改进这一工具,以更好地满足用户的需求。

先决条件

说明用户在安装和使用前,需要准备的一些先决条件,譬如:您需要安装或升级 Node.js(>= 16.*),推荐使用 PnpmYarn 作为首选包管理工具。

如何使用?

使用 Docker

使用 docker compose

1
2
3
4
5
6
7
8
9
10
11
12
13
14
version: '3.8'
services:
wealth-tracker:
image: nicejade/wealth-tracker
container_name: wealth-tracker
ports:
- '8888:8888'
volumes:
- ./data:/app/data
restart: unless-stopped
volumes:
data:

这将在后台启动服务,并且效果与下面的 docker run 命令相同。使用 Docker Compose 可以更方便地管理多个容器,并且配置更易读和维护。

或者 docker run

1
docker run -d -p 8888:8888 -v "$(pwd)/data:/app/data" nicejade/wealth-tracker

如果您在本地部署,只需打开网址——http://localhost:8888 即可访问。如果在服务器运行,可通过 http://[Server-IP]:8888 来访问,您也可以指定其他端口。

使用 Node.js

1
2
3
4
5
6
7
8
# clone project
git clone https://github.com/nicejade/wealth-tracker.git
# install & run for client
cd client && pnpm i && pnpm start
# install & run for srever
cd server && pnpm i && pnpm start

本项目客户端采用 Svelte 框架,基于 Vite 所构建,默认 5173 端口,只需打开网址—— http://localhost:5173 即可访问。

命名由来

在项目开发初期,有为应用取中文名为:”财富追踪器”(英文名:Wealth Tracker)。这个名称初看简洁明了,似乎完美契合产品理念。然而,随着开发深入,逐渐显现出其局限性:

  • 概念范畴过广:”财富”一词涵盖范围极广,不仅包括金钱资产,还可能指代时间、知识、经历,甚至是绝美容颜等无形价值。
  • 功能定位不够精准:该应用实际上专注于能被精确计算和记录、且已经形成的有形资产,而非泛泛追踪所有形式的”财富”。

这种认知更新促使重新思考产品定位和命名。偶然想到了两个颇具启发性的成语:”生财有道”和”生财有术”;而应用的核心功能恰恰是记录资产增长的轨迹。基于这一灵感,最终确定将应用命名为”生财有迹“。这个名字巧妙地结合了几个要素:

  1. “迹”与”记”同音,暗示了记录的功能。
  2. “生”与”升”同音,象征着资产的增长。
  3. 整体保留了”生财”的概念,与原有的资产主题保持一致。

“生财有迹”这个新名称不仅准确反映出产品功能定位,传达了产品理念,还蕴含了对用户资产增长的美好祝愿,希望您也会喜欢。

给予支持

生财有迹项目开源且免费,然而设计与编码需耗费时间和精力。如若您对其使用感到满意,请考虑通过以下方式进行小额捐赠:


生财有迹 - 微信赞赏码

微信扫码赞助




Buy Me A Coffee

路线图

“生财有迹”项目正处于积极的设计和研发阶段,致力于在功能丰富与易用性之间寻求平衡,以满足用户日益增长的个人财务管理需求。未来开发重点包括增强数据可视化能力、加强数据安全性、以及提升 AI 辅助分析功能等。

  • 更新资产数据记录;
  • 资产数据可视化;
  • 查阅数据记录详情;
  • 设置阶段性目标;
  • 资产数据安全性;
  • 借助 AI 辅助分析;

特别鸣谢

本项目的开发过程中,依赖并受益于以下优秀的开源技术和工具(未包含全部)。它们不仅提供了强大的功能,还促进了项目的高效开发和稳定运行。

  • Svelte: 作为一种新兴的前端框架,Svelte 通过其创新的编译时技术,让我们的 Web 应用更加轻量和高效。它减少了我们需要编写的样板代码,同时提高了运行时的性能。
  • TailwindCSS: 通过提供实用主义的 CSS 类,TailwindCSS 极大地简化了样式设计的过程。它使得我们能够快速构建美观且响应式的用户界面,同时保持代码的清晰和维护性。
  • Vite: 作为一个现代化的前端构建工具,Vite 通过其快速的冷启动和即时的模块热更新,极大地提高了我们的开发效率。它利用了原生 ES 模块特性,使得项目构建更加高效。
  • Flowbite: 提供了一系列预构建的组件和模板,Flowbite 极大地加快了我们的开发流程。它帮助我们快速实现了复杂的用户界面元素,同时保持了代码的可定制性和可维护性。
  • Axios: 作为一个基于 Promise 的 HTTP 客户端,Axios 简化了我们的 Web 应用中与 API 的交互。它的易用性和广泛的功能集使得我们能够轻松处理 HTTP 请求和响应。
  • Day.js: 作为一个轻量级的 JavaScript 日期库,Day.js 提供了直观的 API 来处理日期和时间。它使得我们在应用中处理复杂的日期计算变得简单且可靠。
  • Node.js: 作为一个高效的 JavaScript 运行环境,Node.js 使得服务器端开发变得前所未有的简单和快速。它的事件驱动和非阻塞 I/O 模型极大地提高了我们的应用性能和响应速度。
  • Fastify: 这个高性能的 Node.js Web 框架为我们提供了一个简单且强大的接口来构建 RESTful API。它的低开销和高度可扩展性使得我们的后端服务既快速又稳定。
  • SQLite3: 作为一个轻量级的数据库引擎,SQLite3 为我们提供了一个无需配置的本地存储解决方案。它的简单性和高效性使得开发者在本地部署应用时能够轻松管理数据。
  • Sequelize: 作为一个强大的 ORM 框架,Sequelize 为我们提供了一种简单且直观的方式来管理数据库关系。它的灵活性和功能丰富性使得我们能够轻松实现复杂的数据操作和查询。

在此,对上述技术和工具的开发者和社区,致以崇高的敬意和衷心的感谢❤️。正是得益于他们的卓越贡献,生财有迹才能得以成功构建并持续优化。同时,也要感谢如 ChatGPT、Github Copilot、PoeKimi 等 AI 工具在开发过程中提供的宝贵支持,它们显著提高了工作效率和体验。

如何将 Tailwind CSS 引入快应用开发提升编码效率?

2023-07-19 20:20:00

Tailwind CSS,在 Web 开发领域,已经成为各种框架、开发人员标配;已经有大量实例证明,基于 Tailwind CSS 来编写样式,可以为开发者节省诸多时间;而且,能够优化减小代码体积、提升运行性能,是非常棒的技术套件。 快应用 开发,同样基于 Web 技术栈,理论上也完全可以使用 Tailwind CSS;在实际开发过程中,该如何使用 Tailwind CSS 呢?本文旨在对此做些许分享。

Tailwind CSS 的工作原理是扫描所有 HTML 文件、JavaScript 组件和任何其他模板的类名,生成相应的样式,然后将它们写入静态 CSS 文件。它快速、灵活、可靠,且运行时间为零。官方提供 Tailwind CLI 、PostCSS、Play CDN 等使用方式;下面主要讲下如何基于 Tailwind CLI 方式,在应用开发中使用 Tailwind CSS:

Tailwind CSS 可为快应用开发带来哪些好处?

Tailwind CSS 是一种现代的 CSS 框架,它提供了一组预定义的 CSS 类,用于快速开发现代 Web 应用程序。Tailwind 的核心理念是为开发者提供一种灵活的方法来构建自定义的 UI 组件,而不需要编写大量的 CSS 代码。

Tailwind CSS 的工作原理是扫描所有 HTML 文件、JavaScript 组件和任何其他模板的类名,生成相应的样式,然后将它们写入静态 CSS 文件。它快速、灵活、可靠,且运行时间为零。

在快应用开发中引入,可以为项目带来四方面的好处:更快的编写效率更小的代码总体积更高的运行效率更易于项目维护。更详细说明如下:

  • 预定义的 CSS 类可以减少样式的冗余,从而减小 CSS 文件的体积。
  • 大量配套设施,可以提高开发效率,不必手动编写大量的 CSS 代码。
  • 提供了自动优化工具,可以删除未使用的 CSS 代码。
  • 代码体积减小,可以减少下载、加载、解析等步骤耗时。
  • 使 CSS 样式更规范化,避免冗余的样式和复杂的选择器,优化性能。
  • 拥有活跃的社区和文档,可以帮助开发者快速上手和解决问题。

Tailwind CSS:当今最流行 CSS 框架;相信借助其巧妙设计、结合强大生态,您也会偏爱用它来塑造 UI;如果必须要手动编码:那无需频繁滚动编辑区,无需于 template 与 style 之间横跳,无需绞尽脑汁为类命名,无需再编写重复的 CSS……是多么美妙。

Tailwind CSS

如何在项目中使用 Tailwind CSS?

安装 Tailwind CSS 依赖

通过 npm 安装 tailwindcss 并创建 tailwind.config.js 文件,具体命令如下:

1
2
3
4
pnpm install -D tailwindcss
# OR
# npm install -D tailwindcss
npx tailwindcss init

配置您的模板路径

在文件中添加所有模板文件的路径 tailwind.config.js,参考性配置如下:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
const colors = require('tailwindcss/colors')
const selfCustomColors = {
brand: {
DEFAULT: '#1e293b',
},
warn: {
DEFAULT: '#f59e0b',
},
link: {
DEFAULT: '#0ea5e9',
},
mark: {
DEFAULT: '#ff4582',
},
}
module.exports = {
mode: 'jit',
content: [
'./src/**/*.{ux,html,js,svelte,vue,ts}',
'./node_modules/flowbite/**/*.js',
],
purge: {
enabled: true,
content: [
'./src/**/*.{ux,html,js,svelte,vue,ts}',
'./node_modules/flowbite/**/*.js',
],
},
// https://tailwindcss.com/docs/configuration#core-plugins
corePlugins: {
preflight: false, // disable base/reset styles
container: false, // disable container component
content: false, // disable `content` utility
accentColor: false, // disable `accent-color` utility
accessibility: false, // disable `appearance` utility
appearance: false, // disable `appearance` utility
aspectRatio: false, // disable `aspect-ratio` utility
backgroundOpacity: false, // disable `background-opacity` utility
backdropBlur: false, // disable `backdrop-blur` utility
backdropBrightness: false, // disable `backdrop-brightness` utility
backdropContrast: false, // disable `backdrop-contrast` utility
backdropGrayscale: false, // disable `backdrop-grayscale` utility
backdropHueRotate: false, // disable `backdrop-hue-rotate` utility
backdropInvert: false, // disable `backdrop-invert` utility
backdropOpacity: false, // disable `backdrop-opacity` utility
backdropSaturate: false, // disable `backdrop-saturate` utility
backdropSepia: false, // disable `backdrop-sepia` utility
blur: false, // disable `blur` utility
borderCollapse: false, // disable `border-collapse` utility
borderOpacity: false, // disable `border-opacity` utility
borderSpacing: false, // disable `border-spacing` utility
boxShadow: false, // disable `box-shadow` utility
boxShadowColor: false, // disable `box-shadow-color` utility
boxDecorationBreak: false, // disable `box-decoration-break` utility
boxSizing: false, // disable `box-sizing` utility
breakAfter: false, // disable `break-after` utility
breakBefore: false, // disable `break-before` utility
breakInside: false, // disable `break-inside` utility
brightness: false, // disable `brightness` utility
captionSide: false, // disable `caption-side` utility
caretColor: false, // disable `caret-color` utility
clear: false, // disable `clear` utility
contrast: false, // disable `contrast` utility
divideColor: false, // disable `divide-color` utility
divideOpacity: false, // disable `divide-opacity` utility
divideStyle: false, // disable `divide-style` utility
divideWidth: false, // disable `divide-width` utility
float: false, // disable `float` utility
fontVariantNumeric: false, // disable `font-variant-numeric` utility
hyphens: false, // disable `hyphens` utility
isolation: false, // disable `isolation` utility
lineClamp: false, // disable `line-clamp` utility
mixBlendMode: false, // diable `mix-blend-mode` utility
listStyleImage: false, // disable `list-style-image` utility
listStylePosition: false, // disable `list-style-position` utility
listStyleType: false, // disable `list-style-type` utility
objectPosition: false, // disable `object-position` utility
opacity: false, // disable `opacity` utility
outlineColor: false, // disable `outline-color` utility
outlineOffset: false, // disable `outline-offset` utility
outlineStyle: false, // disable `outline-style` utility
outlineWidth: false, // disable `outline-width` utility
overscrollBehavior: false, // disable `overscroll-behavior` utility
placeContent: false, // disable `place-content` utility
placeItems: false, // disable `place-items` utility
placeSelf: false, // disable `place-self` utility
placeholderOpacity: false, // disable `placeholder-opacity` utility
resize: false, // disable `resize` utility
ringColor: false, // disable `ring-color` utility
ringOffsetColor: false, // disable `ring-offset-color` utility
ringOffsetWidth: false, // disable `ring-offset-width` utility
ringOpacity: false, // disable `ring-opacity` utility
space: false, // disable `space-between` utility
textDecorationThickness: false, // disable `text-decoration-thickness` utility
textOpacity: false, // disable `text-opacity` utility
textTransform: false, // disable `text-transform` utility
textUnderlineOffset: false, // disable `text-underline-offset` utility
touchAction: false, // disable `touch-action` utility
userSelect: false, // disable `user-select` utility
verticalAlign: false, // disable `vertical-align` utility
whitespace: false, // disable `whitespace` utility
wordBreak: false, // disable `word-break` utility
willChange: false, // disable `will-change` utility
},
darkMode: false,
theme: {
colors: { ...colors, ...selfCustomColors },
extend: {
width: ({ theme }) => ({
auto: 'auto',
...theme('spacing'),
}),
height: ({ theme }) => ({
auto: 'auto',
...theme('spacing'),
}),
spacing: {
'1/2': '2px',
'1': '4px',
'3/2': '6px',
'2': '8px',
'5/2': '10px',
'3': '12px',
'7/2': '14px',
'4': '16px',
'5': '20px',
'6': '24px',
'7': '28px',
'8': '32px',
'9': '36px',
'10': '40px',
'11': '44px',
'12': '48px',
'14': '56px',
'16': '64px',
'20': '80px',
'24': '96px',
'28': '112px',
'32': '128px',
'36': '144px',
'40': '160px',
'44': '176px',
'48': '192px',
'52': '208px',
'56': '224px',
'60': '240px',
'72': '288px',
'80': '320px',
'96': '384px',
},
borderWidth: {
'DEFAULT': '1px',
'0': '0px',
'2': '2px',
'4': '4px',
'8': '8px',
},
borderRadius: {
'none': '0',
'': '1px',
'sm': '2px',
'DEFAULT': '4px',
'md': '6px',
'lg': '8px',
'xl': '12px',
'2xl': '16px',
'3xl': '24px',
},
fontSize: {
'xm': ['12px', { lineHeight: '16px' }],
'sm': ['14px', { lineHeight: '20px' }],
'base': ['16px', { lineHeight: '24px' }],
'lg': ['18px', { lineHeight: '28px' }],
'xl': ['20px', { lineHeight: '28px' }],
'2xl': ['24px', { lineHeight: '32px' }],
'3xl': ['30px', { lineHeight: '36px' }],
'4xl': ['36px', { lineHeight: '40px' }],
'5xl': ['48px', { lineHeight: '60px' }],
'6xl': ['60px', { lineHeight: '60px' }],
'7xl': ['72px', { lineHeight: '60px' }],
'8xl': ['96px', { lineHeight: '60px' }],
'9xl': ['128px', { lineHeight: '60px' }],
}
},
},
variants: {
extend: {},
},
plugins: [],
}

将 Tailwind 指令添加到您的 CSS 中

@tailwind 将 Tailwind 每个层的指令添加到您的主 CSS 文件中。

1
2
3
4
5
/* input.css */
/* @tailwind base; */
@tailwind components;
@tailwind utilities;

启动 Tailwind CLI 构建过程

运行 CLI 工具来扫描模板文件中的类并构建 CSS,参考性命令如下:

1
npx tailwindcss -i ./src/input.css -o ./src/output.css --watch

这是看起来路径简短的方式,实际上这个 input.cssoutput.css,无论是名字,或是它存在的路径,皆可以自行指定,您可以按照自己喜欢的方式、或是团队约定俗成的形式;譬如统一存放在:src/assets/styles 目录下。

需要补充说明的是:在团队开发中,输入上述命令,略显繁琐,可以在 package.json 注入命令(如下示例);如此以下,您只需在「终端」运行 pnpm tailwindcssnpm run tailwindcss 命令即可。

1
2
3
"scripts": {
"tailwindcss": "npx tailwindcss -i ./src/input.css -o ./src/output.css --watch",
}

开始在 UX 中使用 Tailwind CSS

将已编译的 CSS 文件,在 .ux (非 app.ux)文件中,通过 @import 方式引入;

1
2
3
<style>
@import './../../output.css';
</style>

现在,即可在 .uxtemplate 中,开始使用 Tailwind 的实用程序类来设置内容的样式,如下面这段代码示例:

1
2
3
4
5
6
<template>
<div class="bg-blue-300 w-full flex flex-col justify-center items-center">
<text class="text-red-600">TailwindCSS Area</text>
<text class="text-red-600">Awesome TailwindCSS</text>
</div>
</template>

当您写完保存之后,打开 output.css 即可如下的 CSS 代码:

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
/* @tailwind base; */
.flex {
display: flex
}
.w-full {
width: 100%
}
.flex-col {
flex-direction: column
}
.items-center {
align-items: center
}
.justify-center {
justify-content: center
}
.bg-blue-300 {
background-color: #93c5fd
}
.text-red-600 {
color: #dc2626
}

至此,在 快应用 开发中引入 Tailwind CSS 的准备工作,已经初步完成,您可以与 Tailwind CSS 开启愉快合作之旅,从而促使高效编码、提前完工。假如,您对 Tailwind CSS 尚不熟悉,可通过查阅 Tailwind CSS Doc ,亦可在线体验—— Tailwind Playground

需要补充说明的是,使用 Tailwind CSS 并不破坏您原来的 CSS 书写✍🏻方式;您完全可以结合喜欢的预处理器(如 Sass、Less、Stylus),来共同工作,而无需做更多设置(如下代码示例);但我以为当您熟悉 Tailwind CSS 之后,大有可能也会“移情而别恋”。

1
2
3
4
5
6
7
8
9
10
11
12
<style lang="scss">
@import './../../assets/styles/style.scss';
@import './../../assets/styles/output.css';
.primary-btn {
width: 90 * $size-factor;
height: 16 * $size-factor;
background-color: $brand;
border-radius: 8 * $size-factor;
color: $white;
}
</style>

如何基于 Tailwind CSS 提升开发效率?

安装 Tailwind CSS IntelliSense 扩展

您可以在应用开发工具的「扩展市场」,通过关键字 Tailwind 检索”Tailwind CSS IntelliSense“扩展——适用于 Visual Studio Code 的智能 Tailwind CSS 工具,用户提供自动完成、语法突出显示和 linting 等高级功能来增强 Tailwind 开发体验;略作适配即可支持 *.ux;详细设置,可参见: Tailwind CSS IntelliSense

Tailwind CSS IntelliSense 提升 ux 开发效率

默认情况下,编辑“字符串”内容时(例如在 JSX 属性值中),开发工具中不会触发完成。更新设置 editor.quickSuggestions 可能会改善您的体验:

1
2
3
"editor.quickSuggestions": {
"strings": "on"
}

参考基于 Tailwind CSS 组件的开源库

Tailwind CSS 生态发展繁荣超乎想象,就连其衍生产品也不可胜数;如 Flowbite ——包含 600 多个 UI 组件、部分和页面的开源库进行开发,该库使用 Tailwind CSS 的实用程序类构建并在 Figma 中设计。它不仅能充当 Tailwind CSS 插件使用,也可以拷贝到项目直接使用,还可以在线查阅效果(支持多种屏幕设备类型),挑选匹配实现方案;在仍是基于组件树构建页面的时代,极大提升页面搭建效率。如: pagination 组件:

Tailwind CSS Flowbite Pagination


常见问题及说明

如果您熟悉 Web 开发,针对上述教程,您可能会有诸多疑惑;实际上,笔者在实践之时,也有发现,只不过因为各种机制限制,目前只能如此。下面就与诸君分享,期可解惑:

不能在 script 标签中,引入输出 CSS 文件么?

按 Web 开发习惯,在 app.ux 文件中可以直接 import 输出 CSS 文件(如下代码所示),那为何未建议如此呢?

1
2
3
<script>
import './output.css'
</script>

因为这样做会报如下错误;具体是缺乏与之匹配的 loader;当然,可以使用 css-loaderstyle-loader 来处理 CSS 文件,详见 ChatGPT | You may need an appropriate loader….

1
2
3
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| /* @tailwind base; */

但在快应用开发过程中,却不能生效;因为 style-loader 的作用是将 CSS 注入 DOM,其中用到了诸多浏览器 API(document),而快应用的开发虽然是基于前端技术栈,但其运行环境止于 v8,实际的渲染是由 Android 底层完成,并未走浏览器这条路,因此无法工作。在构建工具优化、兼容之前,通过 style 标签中引入 CSS,成了主要选择。

为何在 tailwind.config.js 禁用如此多样式?

Tailwind corePlugins 部分允许您完全禁用 Tailwind 通常默认生成的类(如果您的项目不需要它们);这个设计对于快应用实在必要的紧,在上述示例中,对 text-opacity 等样式进行禁用,也是无奈之举。具体原因在于以下两点:

其一:快应用标准虽然基于 Web 技术栈,但只是 Web 标准子集,如蛮多 CSS 属性并不能很好的支持;其二:默认情况下,TailwindCSS 会将样式属性的值存储为 CSS 变量(例如 --tw-text-opacity),以便进行动态计算和响应式设计。

如此一来,在 template 中使用类 text-red-300border-spacing-1,输出的便是如下 CSS;这样的写法,在快应用中并不能得到正确解析,也就无法起到期待效果,于是禁用便成了必须要做之事。当然,这样禁用也好处:利于输出更小提及的 CSS 文件。其他属性亦复如是。

1
2
3
4
5
6
7
8
9
.text-red-300 {
--tw-text-opacity: 1;
color: rgb(252 165 165 / var(--tw-text-opacity))
}
.border-spacing-1 {
  --tw-border-spacing-x: 4px;
  --tw-border-spacing-y: 4px;
  border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y)
}

为何要在 input.css 中移除 @tailwind base;

这么做主要未解决问题: How disable default styles for : before and : after

1
2
3
4
5
/* input.css */
/* @tailwind base; */
@tailwind components;
@tailwind utilities;

在一般 Web 开发过程中,会将 base、components、utilities 等指令,都添加到 CSS 文件项目中;但,在快应用开发过程中,这无疑是累赘;因为它的存在,会使得 output.css 输出较多对项目没有裨益额外内容(如下代码所示),故而移除;这与通过 corePlugins 禁用基础样式(preflight)、禁用容器组件(container)作用类似。

1
2
3
4
5
6
7
8
*, ::before, ::after {
--tw-border-spacing-x: 0;
......
}
::backdrop {
--tw-border-spacing-x: 0;
......
}

为什么要在配置中通过 theme 自定义长度单位?

修改长度单位从 rempx,这也是不得已而为之。 快应用 标准对 长度单位 的支持,在 1070 及以下版本仅支持长度单位 px%。从 1080 版本开始,新增了长度单位 dp。它无法支持 rem,就只能自定义设置为 px;在通过 theme 自定义长度单位,是 Tailwind CSS 官方提供的功能,以此能够实现更精细的样式控制;虽然操作不难,但属性较多、约定繁杂。后续时间如果允许,将探索 rem to px 批量转换插件来实现,以达到快速修改。如果您感兴趣,可以查阅 Tailwind CSS 完整版本配置—— config.full.js

其他暂未解决问题

  • 少部分类名写法,快应用构建不支持

在 Tailwind CSS 机制中,支持使用类名诸如 h-[100px] w-1/3 (对应生成 CSS 如下),然而,在快应用构建工具暂不支持这类名,于是乎这样写便不会生效,当然也不会造成什么问题;后续亟待打包工具优化。

1
2
3
4
5
6
.h-\[100px\] {
height: 100px
}
.w-1\/3 {
width: 33.333333%
}

在打包(Toolkit)未解决该问题之前,您可以手动写一个 class,或者使用内联样式(style)来兼容;在这种情况下,更推荐后者;因为引入 Tailwind CSS 的开发模式中,基本不用手写 class,即无需滚动到 style 标签或跳转 less、(s)css、文件,那么直接在 template 中使用内联更为便捷。

1
2
3
4
5
6
7
<div class="training-list-width" style="width: 326px;"></div>
<style>
.training-list-width {
width: 326px;
}
</style>
  • 快应用构建,资源存在重复引用问题

前文中提到,在 script 标签中 import CSS 资源,暂时未得到支持;但,在 style 标签中 @import 的 CSS 文件,被 A、B 两个页面(*.ux)引入,那么对应的 css 内容,就会被打入对应页面(即便有的内容没有被使用),从而导致代码体积略有增加(即便参考 如何优化「快应用」rpk 包体积? 一文中的方法,也不可避免)。这是快应用构建本身存在的问题,使用 Tailwind CSS 也不例外。后续需要打包工具优化。

以上,就是在 快应用 开发过程中,引入 Tailwind CSS 所需的操作、以及常见问题说明。肯定还有异常更多情况没有考虑到,后续会逐步补充;如果您在实际运用过程中,欢请留言交流、反馈、分享。

2023 年 07 月 05 日写于〔深圳福田〕

猜您可能感兴趣的文章

关于「晚晴幽草轩」博客缓更说明

2023-05-01 20:20:00

晚晴幽草轩 于 2014 年,采用 Hexo 所搭建;专注互联网开发,以及分享优质网站、AI 应用、效率工具、心得感悟等内容;至今已近 9 年。只不过它是本地 Markdown 静态博客,不能随时在线更新,已然不适如今之快节奏;每有新的见闻、感悟,有在以下几个在线平台进行记载:

  • 倾城之链:基于 Vue2Node.js 等技术栈,所搭建的在线导航平台,用以记录所遇见的有价值网站,每周定期更新。
  • 静轩之别苑,由 Ghost 所驱动,2019 年 元月基于 Docker 部署于阿里云服务器,用于记录技术相关心得。
  • 悠然宜想亭, 2021 年 3 月 基于 Flarum(一款优雅简洁论坛软件)所搭建的“社区型个人博客”,至今有记录文章近 300 篇,不定期更新。
  • 半缘修道观 ,2022 年 11 月,基于 MemosDocker Compose 所搭建的“个人微博/推特/备忘录📕”,用于记录学习、感悟、电影、投资、娱乐、科普、代码、 ChatGPT 等相关内容,不到半年已记录近 200 条,是目前更新最为频繁的平台。

除此之外,闲暇时间,有构建些 Web 应用,诸如:玉桃文飨轩 (Markdown 在线转图片)、素问智聊斋(ChatGPT 在线客户端),感兴趣朋友可移步以了解更多。后续如果有写值得分享的长篇,依旧会同步至「晚晴幽草轩」。

2025 年 5 月 02 日写于〔深圳福田〕

素问智聊斋,非官方 ChatGPT 在线客户端,免费可用

2023-04-10 20:20:00

素问智聊斋,非官方 ChatGPT 在线客户端,旨在提供更便捷的 ChatGPT 访问体验;它基于非官方 ChatGPT APISvelteTailwindCSSVite 和 NodeJS 所搭建,无需账号,零配置,即可与 ChatGPT 畅聊;支持自定义 OPENAI API KEY。

素问智聊斋,非官方 ChatGPT 在线客户端

目标与哲学

OpenAI 于 2022 年 11 月推出的超级对话模型 ChatGPT, 受到来自世界各地的认可和赞誉,令人印象深刻。然而,由于一些原因,如果没有正确搭建相应的环境(🪜),ChatGPT 在部分地区就无法正常使用。鉴于此,搭建了这个服务,以便用户能够方便地使用 ChatGPT。当然,条件允许您可前往 OpenAI 官网上注册、登录、申请专属 API KEY。此外,为了保障用户的数据安全,本服务的操作过程中不会存储任何使用者的数据,因此可以放心使用(备注:这段介绍有使用本服务加以润色)。

适用场景

  • 没有注册 Open AI 账号,导致无法使用 ChatGPT 时;
  • 因为网络环境限制,无法正常使用 ChatGPT 时;
  • 具备账号,想让远方亲人、朋友快速体验 ChatGPT;

补充说明:承诺后台不会保存任何数据;基于 API KEY 发起的对话内容,会同步到对应账号;如果您习惯使用桌面客户端,类似项目有: lencx/ChatGPT

诞生初衷

最原始的动机,是因为:以上两点适用场景所对应的诉求,我觉得都需要。但,这并不足以缘起这个项目,更详细的阐述如下:

作为颇爱折腾的技术人员,ChatGPT 自然是要体验;有请时在美国的朋友,帮着注册®️了账号;然而,蛮多时候即便采用科学上网,设备却无法成功登陆 ChatGPT 官网地址 。恰逢一个契机——帮朋友写几行关于 ChatGPT 的示例;当痛点(Motivation:动机)碰上可执行性(Ability:能力),外加适当的 触发条件 (Trigger),福格行为模型(BJ Foog’s Behaviour Model)已全部满足,于是乎, 素问智聊斋 就有了诞生的充足理由。

命名来源

早在 关于“悠然宜想亭”的由来,以及未来 一文提及,对于网站取名多是五个字;或是兴趣的原因,其后缀带有 亭台楼阁、轩榭廊舫,殿庭苑斋 其中一个;其中大部分已被使用;因为「Chat」的应为对应的是「对话、聊天」;于是,「斋」字就这么定下;AI 对应智能,所以「智聊」也就应景而生。

原本命名为「智聊问异斋」,后觉不妥,略阅资料,遂修改为 素问智聊斋;素问:最早或于《黄帝内经》;釋名:“「素者,本也;問者,黃帝問於岐伯也」”。另有解释为:“ 即问本,探索人与自然的根本性问题 ”。当想 ChatGPT 说:“解释下「素问」”,它给出以下解释:

「素问」是一种中国古代哲学思想,它强调对本质的探究。它的核心思想是:任何事物都有其本质,而且这个本质不受时间和空间的束缚,所以要理解它,就必须通过探究本质来探索事物的真相。

技术选型

前端采用了 SvelteTypeScriptTailwindCSSFlowbiteMDSvexVite 、 等流行技术栈,而后台则采用 Fastify 框架;在服务器上,则基于 Nginxpm2 进行部署;其中 Svelte、TailwindCSS、MDSvex 等工具,极大提升了编写代码的舒适度,令人青睐有加。

思考感悟

关于 ChatGPT(或其他 AI 产品),相信接触过它的人们,或多或少都会有自己的感受;目前为止,吾有以下几点看法:

  • 类似 ChatGPT 这样的 AI 产品,必将像如今的手机一般,成为绝大部分人类生活必需品,这很难以人类意志为转移;
  • 毕竟是工具,使用乃至产生依赖,倒无不可;但也不能荒废自身,我辈仍须保持深度学习习惯,及提高学习能力;
  • AI 会进而加剧人与人之间的差异;因为不喜欢折腾或探究新鲜事物的人,很难尽早去接触它,而是等时代浪潮拍打过来。

未来规划

新注册 Open AI 账号,为 API Key 调用费用赠送 18$,为期三个月;4 月 1 日过期之后,为使得服务能够正常运行,有继续购买它的服务。ChatGPT 的价格为:0.002 美元 1000 个 tokens(约为 750 个英文单词),GPT4 则更贵一些;当使用费用超过所能承受的能力,外加服务器使用费用,或可能考虑植入定量商业化措施。现如今,如果您认为 素问智聊斋 对您有一定价值,可以考虑为发起 赞助

其他作品

以上介绍内容,部分使用 ChatGPT 加以润色 。截止目前,它可以协助人类,已经很多,诸如学术论文、内容创作、翻译、数据分析、研究咨询、简历和求职信、广告文案、编写小说、个人陈述、 SEO 优化等等,如果想让它更好帮助到您,可以移步 ChatGPT 中文调教指南 ,学习怎么让它在不同场景,更好听理解您的指令。

您可能感兴趣的文章

为什么说 WebAssembly 让 Web 的未来更光明?

2022-12-30 21:21:00

WebAssembly 堪称一个革命性的技术,它给 Web 带来了更多的可能性,对于 CPU 密集型的应用,比如图片、音频、视频、直播、机器学习、AR、VR、游戏、在线会议、在线文档、在线 IDE、在线游戏等等,WebAssembly 既可以帮助 Web 突破性能瓶颈,也可以让 Web 得以利用其他语言的代码库。随着越来越多业务跟服务转战云端,基于 Web 本身优势,外有 WebAssembly 技术性能加持,在可预见的未来,WebAssembly 可让 Web 再次绽放。

WebAssembly 历史起源

WebAssembly 起源于 Mozilla 员工的一个业余项目。2010 年,在 Mozilla 从事 Android Firefox 开发的 Alon Zakai,为了把他以前开发的游戏引擎移植到浏览器上运行,利用业余时间开发了一款名叫 Emscripten 的编译器,可以把 C++ 代码通过 LLVM IR 编译成 JavaScript 代码。

到了 2011 年底,Emscripten 甚至能够成功编译 Python 和 Doom 等大型 C++ 项目,Mozilla 此时觉得这个项目很有前途,于是成立团队并邀请 Alon 全职开发这个项目。2013 年 Alon 和其他成员一起提出了 asm.js 规范,asm.js 是 JavaScript 语言的一个严格子集,试图通过“减少动态特性”和”添加类型提示“的方式帮助浏览器提升 JavaScript 优化空间。相较于完整的 JavaScript 语言,裁剪后的 asm.js 更靠近底层,更适合作为编译器目标语言。

asm.js 只提供两种数据类型:32 位带符号整数,64 位带符号浮点数,其他数据类型比如字符串、布尔值或者对象,asm.js 一概不提供,它们都是以数值的形式存在,保存在内存中,通过 TypedArray 调用。类型的声明也有固定写法:变量 | 0 表示整数,+变量 表示浮点数。例如下面一段代码:

1
2
3
4
5
6
7
8
9
function MyAsmModule() {
"use asm"; // 告诉浏览器这是个 asm.js 模块
function add(x, y) {
x = x | 0; // 变量 | 0 表示整数
y = y | 0;
return (x + y) | 0;
}
return { add: add };
}

支持 asm.js 的引擎提前识别出了类型,可以进行激进的 JIT(即时编译)优化,甚至是 AOT(事先编译)编译,大幅提升性能。不支持 asm.js 按普通 JavaScript 代码执行也不会影响运行结果。

但是 asm.js 的缺点也很明显,那就是“底层”得不够彻底,例如代码仍然是文本格式;代码编写仍然受 JavaScript 语法限制;浏览器仍然需要完成解析脚本、解释执行、收集性能指标、JIT 编译等一系列步骤。如果采用像 Java 类文件那样的二进制格式,不仅能缩小文件体积,减少网络传输时间和解析时间,还能选用更接近机器的字节码,这样 AOT/JIT 编译器实现起来会更轻松,效果也更好。

与此同时,Google 的 Chrome 团队也在试图解决 JavaScript 性能问题,但方向有所不同。Chrome 给出的解决方案是 NaCl(Google Native Client)和 PNaCl(Portable NaCl)。通过 NaCl/PNaC1,Chrome 浏览器可以在沙箱环境中直接执行本地代码。

asm.js 和 NaCl/PNaC1 技术各有优缺点,二者可以取长补短。Mozilla 和 Google 也看到了这一点,所以从 2013 年开始,两个团队就经常交流和合作。后来他们决定结合两个项目的长处,合作开发一种基于字节码的技术。到了 2015 年,“WebAssembly” 确定为正式名称并对外公开,W3C 成立了 WASM 社区小组(成员包括 Chrome、Edge、Firefox 和 WebKit),致力于推动 WASM 技术的发展。

  • 2016 年 Rust 1.14 发布,开始支持 WASM。
  • 2017 年 Google 决定放弃 PNaCl 技术;四大浏览器 Chrome、Edge、Safari、Firefox 更新版本开始支持 WASM。
  • 2018 年 Go 1.11 发布,开始支持 WASM。
  • 2019 年 Emscripten 更新为默认使用 LLVM 编译为 WASM 代码,停止对 asm.js 的支持;WebAssembly 成为万维网联盟(W3C)的推荐标准,与 HTML,CSS 和 JavaScript 一起成为 Web 的第四种语言。
  • 2021 年 1 月 6 日,Wasmer(🚀支持 WASI 和 Emscripten 的领先 WebAssembly 运行时) 1.0.0 版本发布。
  • 2022 年 09 月 20,Wasmtime(WebAssembly 的快速安全运行时) 的官方 1.0 版本发布。

全面认识 WebAssembly

WebAssembly 诞生背景

Web 平台的成熟催生了复杂而苛刻的网络应用,如交互式三维可视化,音频和视频软件,以及游戏。随着伴随着这一点,Web 上代码的效率和安全性变得比以往更加重要。然而,作为唯一的网络内置语言——JavaScript 并没有很好地满足这些要求,特别是作为编译目标。来自四个主要浏览器供应商的工程师们迎接挑战,合作设计了一个可移植的低级字节码,称为 WebAssembly 。它提供了紧凑的表示、高效的验证和编译,以及安全的低至无开销的执行。WebAssembly 不是致力于一个特定的编程模型,而是 WebAssembly 是对现代硬件的抽象,使其与语言、硬件和平台无关,其使用范围不只是 Web 浏览器;就目前而言,Node.js、Deno、 WebAssembly 运行时 等环境都可使用。

WebAssembly 是什么?

WebAssembly 是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C / C ++ 等语言提供一个编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。WebAssembly 是可移植体积小加载快并且兼容 Web 浏览器的全新类汇编语言格式,其具有高效安全开放标准等特性。

WebAssembly (缩写为Wasm)是基于堆栈的虚拟机的二进制指令格式。其设计目的不是为手写代码,而是为诸如 C、C++、Rust 等低级语言提供一个高效的、可移植的编译目标,支持在 Web 上部署客户端和服务器应用程序。

WebAssembly 试图取代 JavaScript 吗?

不会。在 WebAssembly 常见问题 中,作者给出了明确说明:“WebAssembly 旨在补充而非替代 JavaScript。随着时间的推移,WebAssembly 将允许许多语言被编译到 Web,而 JavaScript 具有令人难以置信的势头,并将继续保持 Web 的单一、特权( 如上所述 )动态语言。此外,预计 JavaScript 和 WebAssembly 将在许多配置中一起使用”。

理解 WebAssembly 文件格式

编译器 VS 解释器

  • 源代码(source code):由开发者基于 JavaScript、TypeScript 等编程语言直接提供,主要用来给人类阅读、维护的文本格式。即便是压缩、混淆后,依然是文本格式。
  • AST:抽象语法树(Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。之所以说语法是“抽象”的,是因为这里的语法,并不会表示出真实语法中出现的每个细节。
  • 汇编代码:(Assembly Code),是介于高级语言和机器码之间的一种中间语言(也是人看的懂的语言)。它比机器代码高一级,比高级语言低一级。而且,它的语法类似于英语,但比高级编程语言更难。它经过转译最终也会成为机器码,所以抽象上来说,它和字节码是同一级别的。可以简单将其理解为机器语言的助记符,便于人们理解和记忆。像用地址符号(Symbol)或标号(Label)代替指令或操作数的地址,常见的 x86 汇编指令有 MOV、PUSH、POP 等。
  • 字节码:(Byte Code)是一种包含 执行程序、由一序列 OP 代码(操作码)/数据对 组成的二进制文件。 字节码是一种 中间码,它比机器码更抽象,需要 直译器转译 后才能成为机器码的中间代码(通常不是人类可读的语言)。
  • 机器码:(Machine Code),学名 机器语言指令,有时也被称为 原生码(Native Code),是电脑的 CPU 可直接解读 的数据(即计算机只认识 0 和 1)。
  • WebAssembly 文本格式 :推荐扩展名是 .wat(WebAssembly Text Format),是另外一种输出格式,是使用 “S- 表达式” 的文本格式,可以近似理解为与二进制等价的汇编代码。部分浏览器的开发者工具支持将 WASM 转换成 WAT 查看,便于在线调试。社区提供了 wasm2watwat2wasm 等成熟的工具将二者进行转换,可以在 WebAssembly/wabt (WebAssembly Binary Toolkit) 工具集中找到,所以也是可以直接编写 WAT 再转换成 WASM。
  • WebAssembly 二进制格式.wasm) :WebAssembly 的最终形式,是一种低级字节码,基于 16 进制表示;无需解析,只要解码并且检查确认代码,即可编译为机器码
  • WASI:(WebAssembly System Interface),一种与不同操作系统通信统一标准;它是为 WASM 专门设计一套引擎无关(engine-indepent)、面向非 Web 系统(non-Web system-oriented)的 API 标准;遵循可移植性、安全性两大原则。WASI 在 WASM 字节码与虚拟机之间,增加了一层“系统调用抽象层”。

WebAssembly 优缺点

从 WebAssembly 诞生背景及目标来看,它所要解决的是 JavaScript 语言本身限制,不足以应对 CPU 密集型应用的问题。因此,优缺点比较对象是 JavaScript、以及 Native Client 和 asm.js 等尝试“解决 Web 上安全快速代码”的方案。

Native Client:是一个沙箱,用于在浏览器中高效、安全地运行已编译的 C 和 C++ 代码,独立于用户的操作系统。它已于 2020 年弃用,支持将于 2021 年 6 月结束。
Asm.js:代码在很多方面类似于 C,但它仍然是可以在所有当前引擎中运行的完全有效的 JavaScript。Firefox 是目前唯一可以做到这一点的浏览器。已被弃用。

WebAssembly VS JavaScript 优点

更好性能

WebAssembly 和 JS 运行过程对比

  • 下载
    • JS 是高级语言,便于开发者阅读和编写,文件体积比汇编语言大。
    • WebAssembly 不是设计用于手动编写的,也不是供人类阅读的。代码被编译为 WebAssembly 之后,字节码会以二进制格式而不是文本格式表示,这可以减小文件大小,从而支持快速传输和下载。
  • 解析/解码
    • JS 需要先通过词法分析、语法分析生成 AST,然后再由 AST 生成字节码。
    • WebAssembly 是一种字节码,不需要进行解析,只需要解码并且检查确认代码没有错误就可以了。
  • 编译(优化、反优化):
    • JS 是弱语言类型,无法预知变量类型。JIT 监测热点代码,在代码执行若干次后,优化编译器才将其编译为机器码并优化。如果数据类型被动态修改,优化编译器会进行反优化,下次执行时会回退到解释器解释执行。
    • WebAssembly 代码是静态类型的,即可以预知变量持有的值的类型。WebAssembly 代码可以从一开始就编译为机器码,无须先监测,因此第一次运行代码就可以看到性能提升。WebAssembly 更接近机器码,在编译阶段不需要做太多的优化;WebAssembly 代码的类型是确定的,不存反优化的阶段。
    • WebAssembly 二进制文件的设计方式使得模块验证可以在一轮内完成,其结构也支持并行编译文件的不同部分。
    • 浏览器厂商引入了流编译技术,在浏览器下载和接收文件时,该技术可以将 WebAssembly 代码编译为机器码。流编译支持 WebAssembly 模块下载完毕,即进行初始化,这样会显著加速模块的启动过程。
  • 执行
    • JS 是一种编程语言,更注重开发效率和代码可读性,开发者编写的代码不一定能发挥 JIT (just-in-time) 的优化机制。
    • WebAssembly 被设计为编译目标,专注于提供更加理想的指令(执行效率更高的指令)给机器,执行效率更高。
  • 垃圾回收
    • JS 引擎会自动进行垃圾回收,垃圾回收可能会在一个不合适的时机启动并造成性能损耗。大多数浏览器已经能给垃圾回收安排一个合理的启动时间,但还是会增加性能开销。
    • WebAssembly 不支持垃圾回收,内存操作都是手动控制的(像 C、C++一样),性能由开发者控制。

便携且安全

WebAssembly 独立于平台、独立于硬件和独立于语言,它对设备或浏览器没有任何特殊要求,这增强了其便携性;其设计原则是与其他 Web 技术和谐共处,并保持向后兼容。代码在内存安全的沙盒环境中进行验证和执行,可以防止安全漏洞和数据损坏。此外,像其他 Web 代码一样,它遵循浏览器的同源策略和授权策略。

支持多线程

WebAssembly 不直接支持多线程。但是,WebAssembly 程序可以在宿主环境中执行,这个宿主环境可能支持多线程。因此,WebAssembly 程序间可以通过宿主环境来协调多线程(通过 Web Worker、共享线性内存 SharedArrayBufferWebAssembly atomics )。详情可参见文章: Using WebAssembly threads from C, C++ and Rust

集成已有库

如果应用程序使用 C/C++、Rust 或任何其他兼容语言,WebAssembly 可以轻松地将代码或桌面应用程序用于 Web。如今(2022 年 12 月)生态已经非常丰富,除了用于 Rust 的 wasm-pack 和用于 C/C++Emscripten 之外,还有 AssemblyScriptTinyGo 凡此种种,详情可参见: Awesome Wasm Compilers

WebAssembly VS JavaScript 缺陷

尽管 WebAssembly 具有很多优点,但目前仍存在些缺陷,譬如:

  • 旧浏览器不支持:它只能在支持 WebAssembly 的浏览器(或 runtime)中运行;倘若环境不支持,需要使用其他方式来实现(假如要考虑兼容);
  • 不能直接访问 DOM:WebAssembly 目前还不能直接访问浏览器的 DOM 元素;如果代码要访问浏览器 DOM,需要调用 JavaScript 方法来实现( 已有计划实现 );
  • 存在局限:比起 JavaScript,编写相关代码不够灵活,需要经过编译;需要手动分配内存,且缺少用于自动内存管理的垃圾收集 (GC)(尽管有计划添加 GC);

显而易见,没有技术会是银弹,只有把技术放在适用的场景下才能达到事半功倍的效果;在开发效率,与运行效率两者之间,无法同时做到最优;根据不同环境、需求,做平衡才是更优的选择。因此,除了计划解决的部分,WebAssembly 携带部分缺陷在所难免;毕竟它是对 JavaScript 的补充。

WebAssembly 应用场景

WASM 并不是为了优化您的网站,而是为了在执行以下任务时将浏览器(和服务器端运行时,如 Node.js、Deno)提升到一个新的水平:

  • 机器学习:TensorFlow.js;
  • 视频编辑:Clipchamp、Mastershot;
  • 实时合作编辑:Figma;
  • 游戏开发:Unity Web;
  • AR/VR 直播应用:(非常低的延迟);
  • 音乐编辑和流媒体: FFMPEG.WASM
  • 平台仿真:AutoCAD、Google Earth;
  • 加密解密:JSVMP(编译及二进制特性);
  • 开发工具:vim web、SQLlite web;
  • 图像识别及处理: Squoosh.app ,多线程客户端图像压缩;
  • 开发人员工具:(编辑器、编译器、调试器……);
  • 代替 JavaScript 框架:Yew、Blazor、Tokamak、Prism;
  • …….

更多使用场景可参见: WebAssembly Use Cases ;任何需要大量编码和大量性能调整的事物,都是 WASM 的完美用例。如需使用 WASM 的开源项目列表,您可以访问 Made with WebAssembly 这个社区。下面是些声名远扬的项目:

  • Tensorflow :是将 AI 和 ML 带给 JS 开发人员的主要库之一;在它 添加了 wasm 后端支持后 ,与普通 JS 版本相比,模型的性能平均提高了 10 倍。
  • Unity :作为主要的游戏开发引擎之一,它能够将您的项目导出为与 Web 兼容。从 2018 年开始,这个过程是通过 编译成 WebAssembly 来完成的。这是正确使用 WASM 的强大功能的完美示例。
  • Google Earth :该产品已经存在超过 15 年,曾经是一个桌面应用程序。 现在感谢 WASM,他们通过将旧代码编译为 WASM 将其 移植到网络中(就像 Autodesk 为其 CAD 应用程序所做的一样)。
  • Yew :有没有想过将 Rust 用于您的网络应用程序?现在你可以了,感谢 Yew 和 Web Assembly 给我们带来的力量。你不仅可以将 Rust 用于你的 UI,而且这个框架还提供了一个多线程环境来工作。Github 拥有 26kb Star(备注:目前还不是 1.0。准备好因破坏 API 更改而进行重大重构)。
  • Blazor :构建美观的 Web 应用,使用 .NET 和 C# 的强大功能构建全栈 Web 应用程序,而无需编写一行 JavaScript。生态可参见 awesome-blazor
  • Pyodide :是基于 WebAssembly 的浏览器和 Node.js 的 Python 发行版。

WebAssembly 主要的几个概念

为了理解 WebAssembly 是如何在 Web 运行的,需要了解几个关键概念:

  1. Instance :一个包含它在运行时用到的所有状态,包含 Memory、Table、以及一系列导入值的 Module,一个 Instance 类似一个 ES2015 的模块,它被加载到具有特定导入集的特定全局变量中
  2. Module :通过浏览器编译成为可执行机器码的 WebAssembly 二进制文件,Module 是无状态的,类似 Blob,能够在 Window 和 Worker 之间通过 postMessage 共享,一个 Module 声明了类似 ES2015 模块类似的 import 和 export。
  3. Memory :一个可调整大小的 ArrayBuffer,其中包含由 WebAssembly 的低层次内存访问指令读取和写入的线性字节数组。
  4. Table :一个可调整大小的类型化引用数组(如函数),然而处于安全和可移植性的原因,不能作为原始字节存储在内存中。

WebAssembly 的 JavaScript API 提供给开发者创建 Module、Memory、Table 和 Instance 的能力,给定一个 WebAssembly 的 Instance,JS 代码可以同步的调用它的 exports – 被作为普通的 JavaScript 函数导出。任意 JavaScript 函数可以被 WebAssembly 代码同步的调用,通过将 JavaScript 函数作为 imports 传给 WebAssembly Instance。

因为 JavaScript 能够完全控制 WebAssembly 代码的下载、编译和运行,所以 JavaScript 开发者可以认为 WebAssembly 只是 JavaScript 的一个新特性:可以高效的生成高性能的函数。

1
2
import {foo} from "./myModule.wasm";
foo();

在未来, WebAssembly 模块可以以 ES2015 的模块加载形式加载,如通过 import<script type="module">,意味着 JS 可以获取、编译、和导入一个 WebAssembly 模块,就像导入 ES2015 模块一样简单。详见: WebAssembly 的 ES 模块集成提案

如何在应用里使用 WebAssembly?

WebAssembly 给 Web 平台添加了两块内容:一种二进制格式代码,以及一系列可用于加载和执行二进制代码的 API。时值 2022 年,生态已颇具规模,详情可参见: Awesome Wasm ;主要流行的入口有:

  • 使用 EMScripten 来移植 C / C++ 应用;
  • 编写 Rust 应用,然后将 WebAssembly 作为它的输出;
  • 使用 AssemblyScript ,它是一门类似 TypeScript 的语言,能够编译成 WebAssembly 二进制;

对于 Web 开发者来说,可是使用类 TypeScript 的形式来尝试 WebAssembly 的编写,而不需要学习 C 或 Rust 的细节,那么 AssemblyScript 或将会是最好的选择。AssemblyScript 将 TypeScript 的变体编译为 WebAssembly,使得 Web 开发者可以使用 TypeScript 兼容的工具链,例如 Prettier 、VSCode Intellisense,你可以查看 AssemblyScript 文档 来了解如何使用。

补充说明,如今已有大量的工具库被编译为 WebAssembly,并做了封装;譬如 Photon (一个高性能的 Rust 图像处理库,它编译为 WebAssembly,允许在本地和 Web 上进行安全、快速的图像处理),其使用方式与其他 npm 包没有区别;在基于 Vue3 开发的作品: 玉桃文飨轩 中就有使用,如您感兴趣,可移步源码实现: markdown2png

参考资料及资源网站

资源网站

参考资料

您可能感兴趣的文章