Blog | Phodal - A Growth Engineerhttp://www.phodal.com/blog/2022-01-01T03:41:17.730909+00:00Blog前端技术规划与战略:20222021-12-23T14:50:59+00:002022-01-01T03:41:17.730909+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/frontend-strategy-2021/最近几年,因为前端没啥有意思的东西好玩的,主要精力就在工作相关的后端架构咨询和设计上。只是,刚好最近在编写知识管理元框架 [Quake](https://github.com/phodal/quake) ,应用了一些算是比较新的架构思想,特别好玩。所以,这篇文章结合一些公司的前端架构需求,社区上的一些趋势,以及自己的探索编写出来的。
### 回顾 2021 规划
开始之前,先回回顾一下我在年初写的《[前端规划:我的 2021 前端技术战略](https://www.phodal.com/blog/frontend-plan-2021/)》,总体上有四个关键要素:
* **前端架构治理**。主要的点是:大型前端应用、规范之旅。
* **微前端“普及”**。主要的点是:微前端框架成熟、
* **低代码平台的返璞**。主要的点是:重塑用户体验、构建开发者体验。
* **超越前端**。主要的点是:Serverless 一体化、重回跨语言前端(如 Rust 等)。
总体上来看,基本上和今年的趋势大差不差了(明明还是差很多的)。所以,如果让我去规划 2022 的话,它可能是这样的。
### 1. 元宇宙的前端:复兴的前端 3D 世界。
6 年前,我在玩 VR 探索前端可能性的时候,写了一系列的相关文章,比如那篇《[Oculus + Node.js + Three.js 打造VR世界](https://github.com/phodal/oculus-nodejs-threejs-example)》,还有《[JavaScript 在 VR 世界的应用](https://www.phodal.com/blog/why-javascript-will-use-vr-world/)》,总体上,我并不看好普通的前端开发往 VR 世界扩展,它多数是一些图形学相关的知识,需要上手成本会略高一点。
不过,如果一定要的话,可以参考一下我上个月写的《[从 Codecity 到元宇宙:元宇宙的软件形态会怎样的?](https://www.phodal.com/blog/codecity-metaverse-thinking-in-metaverse-software/)》。只是从某各意义上来说,元宇宙和狭义上的 Web 前端没有多大的关系,除了技术,我们还需要这么一些知识:
1. 构建 3D 世界的能力,如结合 Three.js 来进行与 VR 的交互。
2. 将物体从物理世界复刻虚拟世界。即数据孪生。
3. 从平面软件到物体建模。
4. 接入虚拟化与沉浸式。
所以,如果想做计划储备那就需要:游戏编程、数字孪生、交互设计、协作模式。总体来说,关系不大,它可以构建一个快速的 MVP。
除了对于元宇宙本身来说,企业对于虚拟形象的热门化,也会带动前端在 3D 世界的发展。
### 2. 无组件架构:新的可复用模式
虽然,我玩微前端玩得很早,一直觉得微前端(MicroFrontend)这名字不好,只是也懒得去纠结。与现有的遗留系统迁移使用的微前端模式相比,我更看好诸如于 ComponentLess 这样的组件模式,有点类似于 Serverless 之于 NanoService。使用更细粒度的 Web Component 组件,可以构建出更小的应用,易于替换与解耦设计。
回到案例上, 在构建和设计 [Quake](https://github.com/phodal/quake) 应用的时候,我们就是这么玩的:
* Web Component 作为基础。使用了基于 Web Component 作为应用的基础,在这个基础上套娃(wrapper)了 React 框架的组件、Vue 框架的组件、Angular 框架的组件……。
* 组件级别的封装。我可以用 React 中的某个框架,可以用 Angular 中的某个框架,只需要封装为 Web Component 即可。
* 无限套娃模式。可以在 A 组件调用 B 组件,B 组件还可以调用 A,然后无限死循环了。
* 随意可抛弃式。
所以,就孕育出了无组件的定义:
> 无组件(Componentless)架构是一种架构模式,它是指大量依赖于**三方组件**(运行时依赖的组件而非编译时依赖的组件,即编译即服务)或暂存容器中运行的自定义代码的前端应用。应用的三方组件如同三方 API 服务一样,可各自独立发布、独立部署,应用无需重新编译、构建和部署。
### 3. 低代码及其细化策略
从模式上来说,在低代码社区里,低代码平台已经初步分为(可以是阶段,也可以是方向):
* nocode 无代码:不需要写代码就能做出一个应用。
* lowcode 低代码:仅需写少量代码就可以做出一个应用。
* procode 专业代码:应用所有的代码都需要开发人员编写。
对于三种不同的方式,成熟的低代码都有自己的方案。不同公司有不同的策略:在一个平台上支撑三种不同的方式,又或者是在三个平台上支撑三中不同的方式,只是会复用三种不同的基础设施。这些并不重要,重要的是在实现会发现:多数的低代码平台太复杂了,还需要进一步地拆分。
### 4. 开发者们的体验
今年与越来越多的小伙伴们交流起了开发者体验,于是开源了电子书《[开发者体验:探索与重塑](https://dx.phodal.com/)》(<https: dx.phodal.com=""></https:>)。从趋势来说,在开源继续热火朝天的 2022 年来说,开发者体验应该会成为人们研究该领域的一个新趋势。原先我们假定的是:开发团队应该关注于开发者体验。然而,从国内的实践情况来看,所有与这个平台、工具、SDK 等相关的利益相关者们,都应该关注于开发者体验。尤其是,相关的最高管理者,他们决定了相关项目的 KPI/OKR。
无论如何,在设计 KPI 的时候,记得:从协同价值出发、以过程改进为核心,引入适当的不可量化指标。
不过,在写 2021 年规划的时候,我也同样预测了这个趋势。所以,我并不确定他会不会如预期一样。只是呢,在未来的几年里,它是一种不可避免的趋势。
### 5. 更好的遗留系统迁移
对于大部分公司而言,人们对于前端遗留系统的预期是:不花费大量的时间和精力,只需要它能 run 起来,在未来几年后一起重写(这个事可能不会发生)。所以,在 2021 年里,我尝试去扩展遗留系统迁移的方式。
在 IE 逐渐被淘汰之后,在前端遗留系统的迁移上,我们会有更多的选择,诸如于采用下一代 Web 组件化 Web Components。因此,我们可以采用诸如于领域元组件的方式 —— 它是一个包含特定领域组件(业务场景)的集合。其内部包含了一系列的组件,依赖于输入的领域元数据或者领域特定语言,构建出适用于特定领域的组件。
当然了,原先的微前端架构、花式构建(组件式构建、拆分式构建)依旧是试用的,只是现在有了更多的方法了。
### 6. 有限的改进前端工具
从 2021 年的趋势来看,已经有越来越多的前端工具,使用 Rust 和 Golang 等 “更快” 的语言编写。所以,这个趋势依旧会在 2022 年继续。毕竟,凡是用 JavaScript 写的,都可能用 Rust 重写一遍。
只是从个人的体验来说,这种改进方式是有限的,前端应用的构建慢多数是因为前端项目代码量大导致的。所以,除了改进前端工具之外,还有一种出路是减少代码的编译。
### 7. 前端元框架
继续上面的话题,作为一个架构师,跳出具体框架和具体技术栈来考虑问题,便会发现好多事件特别好玩。先前的主要源泉来自于 2018 年构思的:《[前端下半场:构建跨框架的 UI 库](https://www.phodal.com/blog/build-cross-framework-ui-library/)》,以及去年构思的《[Yiki:元微前端框架](https://www.phodal.com/blog/yiki-meta-microfrontends-framework/)》。
过去,我一直构建一个基于 Web Component 的 UI 库,后来太懒了。所以,我就继续用 Ionic 的组件库,配合上上面提到的 NanoComponent,搭建一个 Web Component 路由,我们就可以构建一个全新的前端架构模式。基于此,还可以构建出一个适用于前端框架的元框架。框架本身是用来解决问题,提供生产力的,但是前端框架太多就是个问题。
这个有待于在 2022 年里,再想想怎么搞。
### 8. 探索前端 + AI 的方向
市面上当前流行的 Design to Code,Product to Code,个人觉得是把牛刀用到了吃鸡上:说不上是有意义的创新。说是在降低工作量,但是可能投入远比这多的工作量 —— 远不如 DSL 来得靠谱 + 可迁移。
我一直在思考有没有更创建的前端(边缘侧) AI 方向,但是没有想到。我相信聪明的社区会有更多的答案。
### 10 - 1. DSL as Core
最后一点,就是过去我一直在玩的东西,DSL as Core。回到这个思想来看的话,我觉得什么设计转代码是不合理的,KPI 思想下的产物。理想的模式,应该是 DSL 生成设计 + DSL 生成代码,类似于 [unflow](https://github.com/inherd/unflow) 的思想。这样一来,我们就可以无更顺畅的将业务需求转为设计,并实现它的自动化
所以,在 Quake 中是这么玩的,通过 DSL:
```bash
---------------------------------------------------------------
| Calendar(flow("show_calendar"), 12x) |
---------------------------------------------------------------
| Network(<graph-network>, 4x) | Timeline(flow("show_timeline"), 8x) |
---------------------------------------------------------------
```
就可以生成以下的页面:
![](https://pic1.zhimg.com/80/v2-dbbab7ef6514ff8230b3b29758d5ff29_1440w.jpg?source=3af55fa1)
当然了,上面的 `flow` 是来自于 DSL,类似于:`from('todo','blog').to(<quake-calendar>).filter('created_date > 2021.01.01 AND created_date < 2021.12.31')`会自动生成对应的 JavaScript 代码,又或者是任意的后端代码。它可以从数据库中读取 `todo` 和 `blog`,然后渲染到 `<quake-calendar>` 组件中。
随后,只需要为这些 DSL 创建一个配置页面,就可以实现低代码或者无代码。这种模式并不适合于常规的业务开发, 但是非常的好玩:我需要有 CRUD API,还有组件,我就能提供无限的可能性。
## 其它
和 2020、2021 年相比,相信 2022 年会出现更多好玩的前端技术。</quake-calendar></quake-calendar></graph-network>Angular Router Reuse 的那些坑2019-07-12T14:58:00+00:002019-07-12T14:58:37.131924+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/issue-on-angular-router-reuse-strategy/RouteReuseStrategy(路由复用策略)是 Angular 框架提供的一种路由复用机制。它可以在用户切换页面的时候,暂存路由的状态,在返回路由的时候,恢复这些快照。
**使用场景**
我们在做一个后台管理系统,它使用的是 Tab 页 + 无限滚动的方式实现。一旦我们从这个列表页进入详情页,再返回列表页时。不仅会回到列表的最上面,而且还会重置 Tab 的状态。为此,我们的业务需求是:记错这个页面的状态。
于是乎,我使用了之前的:《[适用于 Angular Lazyload 下的 RouteReuseStrategy](https://www.phodal.com/blog/angular-2-lazyload-reuse-stragery/)》,然后出了一堆的 bug。
### 1. queryParams
我遇到的第一个坑是 queryParams 失效了——对于跳转过来的 URL 来说,原先的代码是:
```typescript
this.route.queryParams.subscribe(params => {
this.param1 = params['param1'];
this.param2 = params['param2'];
});
```
后来,临时改成了通过 ``queryString`` 来解决 URL 上的参数。
### 2. 全局 cache 问题
先前配置的 ``shouldAttach`` 是通过判断缓存中,是否存在对应的路由,即:
```typescript
/** 若 path 在缓存中有的都认为允许还原路由 */
public shouldAttach(route: ActivatedRouteSnapshot): boolean {
if (!!route.data.reusePath && !!SimpleReuseStrategy.handlers[route.data.reusePath]) {
return true;
}
return false;
}
```
这样做导致了一个问题,不管从哪个页面过来,它会被 cahce。
**解决方式 1**:在组件中判断上一页是否是详情页,如果是则使用缓存,如果不是则重新加载数据。这是我在上上一个项目中采用的方式,但是这种方式太麻烦了,需要在每个组件中写上对应的逻辑。
**解决方式 2**:配置需要缓存的页面,结合 reusePath 使用。
相关的步骤如下:
1. 标记详情页。在对应的 componet 中添加一个全局可访问的静态变量,诸如于 ``componetName = 'BlogDetailComponent'``.
2. 在 ``shouldAttach`` 的时候,先判断是否是 reuse,再判断上一个页面是否是需要缓存的页面。
这样一来,我们就可以将主要的逻辑放置在 ``SimpleReuseStrategy``,而非每个 component 中。
```typesciprt
public shouldAttach(route: ActivatedRouteSnapshot): boolean {
let cachablePage = false;
...遍历 this.cachedPages,判断是否等于 lastPageName
if (!!route.data.reusePath && !!SimpleReuseStrategy.handlers[route.data.reusePath] && cachablePage) {
return true;
}
this.lastPageName = route.routeConfig.component('componetName');
return false;
}
```
于是,又遇到新的坑。
### 3. 元素不销毁
采用 RouteReuseStrategy 时,页面只是被缓存,而没有被销毁。因为 Component 对就的 ngDestory 方法不会被销毁。
我们遇到的第一个坑是:无限滚动的 ``infiniteScroll`` 不会被 desotry。于是当我们在详情页的时候,仍然会触发 ``scrolled`` 方法,而解决的办法也很简单。在我们二次封装的组件里,在 ``ngOnInit`` 时,监听路由是否变化,然后调用 ``infiniteScroll`` 的 ``destoryScroller()``。
```typescript
this.router.events.subscribe((event) => {
if (event instanceof NavigationStart && this.scrollEl) {
this.scrollEl.destoryScroller();
}
})
```
除此,我们还有其它的组件也有同样的问题,解决方式是相似的。
### 其它方式
可以在我们的 ``SimpleReuseStrategy`` 类中,提供静态方法给组件使用。
```typescript
public static deleteRouteSnapshot(name: string): void {
if (AppRouteReuseStrategy.handlers[name]) {
delete SimpleReuseStrategy.handlers[name];
} else {
SimpleReuseStrategy.waitDelete = name;
}
}
```