全员2.5大改版个人复盘

全员2.5大改版个人复盘

八月 12, 2021

前言

开发始于7月14,于8月12上线,这期是个非常大的修改,主要有、重构了登陆逻辑、全局替换主题色、重构导航栏、用hooks重构大改页面等。

正文

功绩

1. 大胆的使用上css变量。统一主题色

需求是把整个应用的主题色从橙色换成蓝色。不仅是一个颜色,而是一套颜色。当时脑子里第一个想到的就是css变量。所以当即尝试了一下。发现确实有效。安卓、ios、pc端以及企业微信端,都能使用。

后来收集信息了解到,caniuse上,css变量从chrome49开始支持,即使最老旧的pc版浏览器也是chrome 53内核。所以目前兼容性应该是没问题了的。

接下来考虑如何实施。

主要是根据UI顶的色调来命名:–primary-color、–primary-bg-color、–text-color-1、–text-color-2等。

定义好所有变量后,全局搜索,所有就色值,替换。

实际上遇到,部分地方用的rgba写法,而css变量写的十六进制颜色。后来了解到css变量不用一定要写一个有意义的值,更多的是一个字符串。所以可以如下解决:

1
2
3
4
5
// 定义
--primary-rgb-color: 65, 107, 246;
// 使用
background: rgba(var(--primary-rgb-color), 0.04);

作用域

css变量有作用域的概念。所以想要在整个应用使用css变量,则应该在跟元素上定义。小程序固定的根元素是page,所以在项目入口处定义即可

1
2
3
4
page {
--primary-rgb-color: 65, 107, 246;
//...
}

在小程序里有个问题:js无法读取到dom元素。这样的话,定义的css变量则没有办法修改。如果以后要动态改变主色调,则这套方案直接失效。但是短期内不会有这样的需求,目前就按最佳的方式实现即可。

如果是web项目,则不存在此问题。web项目可以把css变量定义在不同的class中,然后js动态的修改跟元素的类名即可。 主题色切换的最佳实践其实就是这么实现的。

css变量使用方法

最佳入门还是阮一峰的CSS 变量教程

此处摘要一些主要的内容来讲

变量声明

css变量声明必须是--开头,值可以随意设置。值应该是一段有意义的值。如,#aaa1,1,1,不能是#aa。

1
2
3
--color: #aaa

background: var(--color);

使用时用var()函数来读取,如上例子

作用域
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

<style>

.a { --test: red;}
.b {color: var(--test)}

</style>
<div class="a">
<div class="b">something</div>
</div>

<div class="b">
<div class="a">something</div>
</div>


类名b应该在a之内才能生效。反之不生效。

2. 重构登陆接口,大幅提升页面访问速度

全员小程序是个面向工作人员的工具,每次访问都需要获取openId、uniondId来区分用户身份。所以每次访问必调登陆接口。

以前的逻辑是,用小程序的第一个页面作为登陆页。登陆页没有UI,只是处理登陆逻辑然后再根据情况跳转下一页面。另一方面,此小程序不配置分享,只有固定几个直接访问的页面。所以,由此分析,这个页面其实并没有必要存在。

经考虑,可以把登陆逻辑放到一个高阶函数里,此高阶函数接收页面组件作为参数,根据登陆情况条件渲染页面组件。只在需要直接访问的页面加上此高阶组件即可实现。

这样可以减少一个页面的代码量。且减少了页面切换的动画和性能消耗。最终测试,登陆过程从原先的1.5s~1.8s减少到0.8s左右(微信开发者工具测试,从第一个页面didMount到首页didMount)。最高可以减少近1秒的加载时间

这样的大改造也带来了问题。

useDidShow无效

用上登陆hoc的页面可能会导致第一次useDidShow无效。因为第一次访问页面,可能是未登录状态,此时并不会渲染页面组件,就触发了useDidShow。带页面组件渲染时,useDidShow早就触发了。

解决办法是。用useEffect代替第一次useDidShow。而useDidShow用useDidShowWithoutFisrt自定义hook代替,useDidShowWithoutFirst功能如字面意思,就是排除第一次的didShow钩子

这么做的好处是:其实useEffect能更好的反应第一次show这个动作。而且useEffect是个比较通用的hook、是与平台无关的hooks。再者,第一次useDidShow与didMount重合。往往业务上不希望触发第一次didShow。所以最终决定如此设计。

从这个改变,我也开始理解React设计的一个特点。就是去平台化

就如Vue2,有Create与Mount钩子,但是确实也与浏览器有一定的相关性,一个是Dom渲染之前,一个是Dom渲染之后。那假如没有Dom渲染过程呢?就不得不根据平台更改源码,业务代码也不得不根据平台去做兼容。

但是React的Hooks就没有这个问题,是真正完全的平台无关,一样的代码,在哪个平台都能运行。这就是React的高明之处。

体验版二维码需指定访问路径,需兼容

这么改另一个重大的影响是改变了小程序首页的路径。会导致生产体验版二维码的接口需要修改路径。比较奇葩的是,体验版二维码需要指定路径。生产小程序码又不需要。很奇怪。但是也得记忆一下。

一个hooks的绝佳使用场景

此次需求改版了大量的页面,我也趁此机会也用hooks重构了页面。而且前面也说过,此应用有大量的模块需要判断权限去控制显示隐藏。所以业务代码中有很多需要if else去判断权限的地方。

另一方面,之前的逻辑是切换tab页时,都要同步请求两个与权限相关的接口,然后再跳转。本次优化成切换tab时,不调接口,权限由业务接口返回。如果业务接口无权限,再去查询权限接口,更新权限。也就是异步更新权限。

所以就会有异步更新权限,异步更新权限的问题。

此时,利用hooks将是一个非常完美的解决方案。

1
2
3
const permission = usePermission();

return permission ? <div></div> : null;

不管代码在任何地方,只要是React函数组件,都能按需引用usePermission。插拔式的插入项目的任何地方。

webpack loader的exclude、include

这次遇到的问题是,有个弹窗需要弹在Input组件上,所以需要Cover-View,Cover-view上有个打岔按钮,然后IOS上这个按钮无法用字体图标,只能用图片。而图片太小了,放在项目里会被转为base64,又会导致IOS上渲染不出来。

另一方面。登录HOC的三角形用的也是图片,因为这是页面加载立即呈现,所以不能用在线图片,必须转成base64,不管其大小。

所以需要一部分图片必须转换为base64,一部分必须上传为在线图片。所以需要改造Taro的url-loader实现该功能

经网上查询,所有loader都有exclude与include方法,用于过滤文件。其实描述有误。应该是,webpack提供test、include、exclude去匹配文件,交给loader处理。筛选是webpack处理的,loader并不需要做文件匹配。

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
{
module:
rules: [
{
test: /\.(png|jpe?g|gif|bpm|svg|webp)(\?.*)?$/,
include: [
path.resolve(__dirname, '..', 'src/assets/images/inline'),
],
use: [
{
loader: 'url-loader',
options: { ...urlLoaderOptions, limit: 99999 },
},
],
},
{
test: /\.(png|jpe?g|gif|bpm|svg|webp)(\?.*)?$/,
include: [
path.resolve(__dirname, '..', 'src/assets/images/online'),
],
use: [
{
loader: 'url-loader',
options: { ...urlLoaderOptions, limit: 0 },
},
],
}
]
}

Taro只能用webpack-chain去修改。有几个要注意的点:

  1. 用chain.toString()方法可以打印最终的webpack配置。会以类JSON的格式呈现。但是不是JSON。因为JSON的正则会展示为{},chain.toString()可以把正则给完整打印出来。如果直接JSON.stringify(chain.toSttring())会报错
  2. chain的每一项配置都会有个名称,只能通过名称去找到已有的配置并且覆盖。chain.toString()方法会注释出所有配置的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* config.module.rule('image') */                                   
{
test: /\.(png|jpe?g|gif|bpm|svg|webp)(\?.*)?$/,
use: [
/* config.module.rule('image').use('urlLoader') */
{
loader: '\\node_modules\\url-loader\\dist\\cjs.js',
options: {
limit: 1000,
esModule: false,
name: '[name]_[contenthash].[ext]',
useRelativePath: true,
context: 'static\\src',
publicPath: '/assets/images',
outputPath: '/assets/images'
}
}
]
},

那么就可以如此覆盖(添加exclude)

1
2
3
4
5
6
7
8
9
10
11
12
chain.merge({
module: {
rule: {
image: {
exclude: [
path.resolve(__dirname, '..', 'src/assets/images/inline'),
path.resolve(__dirname, '..', 'src/assets/images/online'),
],
},
},
},
});

css animation

设计师希望给登陆loading做个比较炫酷的效果,发来了一个gif一百多K。这当然不能用。较真的我后来硬是参考网上案例,用css的animation重写了一遍,不到10k。

之前是用过css animation。但这次有几个属性确实也让我眼前一亮

animation-iteration-count

代表运行多少次动画后停止

可以接收的值是数字,或者infinite。数字可以是小数。可以让动画运行到中间一个过程就停止。

以前往往只知道infinite,但数字、尤其是小数可以细致化控制,却不知道。

animation-direction

字面意思是运行方向。以前只知道revert,反向。但这次还遇到了alternate,代表往返运动。对往返的弹跳的皮球效果会比较有用。

组合animation

可以给animation属性指定多个keyframe,用逗号分隔。比如此次上下的动画是ease的缓动动画,旋转是linear缓动动画。这样能实现很自然的滚动的弹跳效果。

彩色icon

iconfont支持彩色字体图标了,真是牛逼。简单看了下原理,有点深,就不细说了。。。

彩色字体图标,体积小、无限扩大,几乎涵盖了所有有点。

缺点是,不能通过color来动态改颜色,iconfont一个库只能放40个彩色图标。。

iconfont 支持全新的彩色字体图标