此页面需要javascript支持,请在浏览器中启用javascript

2021/10/23日 最近项目中的一些思考

项目思考
随笔
共1742个字,阅读时间 9 分钟
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://icebreaker.top/articles/2021/10/23-recent-project-tips

Image

2021/10/23 日 最近项目中的一些思考

最近又架构和编写了 2 个项目,在编写过程中,有一些思考

多 ssh key 管理

在我们 user root (例如 C:\Users\Administrator) 下面有很多的 .xxx 文件夹

其中就有一个 .ssh 文件夹,里面存放着我们的 ssh key

平常我们维护一个 ssh key 键值对在里面就行 (id_rsa,id_rsa.pub)

要在不同的代码仓库,使用不同的 ssh key , 多 key 管理就很有必要了。

这时候我们可以在 .ssh , 再创建一个 ssh key (id_rsa_gitlab) 新建一个 config,内容如下

Host xxx.com
  HostName http://gitlab.dev.xxx.com/
  IdentityFile C:\\Users\\\Administrator\\.ssh\\id_rsa_gitlab
  PreferredAuthentications publickey
  User icebreaker

Host github.com
  HostName github.com
  IdentityFile C:\\Users\\\Administrator\\.ssh\\id_rsa
  PreferredAuthentications publickey
  User sonofmagic

这样就可以在不同的仓库,使用不同的 UserKey 了。

初创项目中的 ts

Typescript 使用非常方便,但是对于初创项目来说,倒是不见得有那么友好。

举个例子,我们和后端进行对接的时候,会知道在 AxiosResponse<T> 定义返回值类型,是非常爽的一件事情,在定义 vuex/redux 时,也能让 action 进行智能感应,这个比 js 的开发体验,要好很多。

但是在有些场景,反而不是那么合适,甚至会拖后腿。

举个例子,我们在 uni-app 中使用 uview-ui,thorui 时,就会遇到问题。一方面这类的控件库,都是使用 jsdoc 来进行类型注释的,另外一方面,出于 esaycom 的组件扫描下, Type 类型实际上是很难进行导入的。

这种情况下,要不去使用 HbuilderX 的智能提示,要不就盲人摸象,对照着文档进行代码的编写。

所以这种情况下,ts type 的维护,就变成了一种低效率的行为。

尤其是在 vue 2.x ts 版本下,我们有时还需要去维护 data,$refs 的类型,这种似乎都在和快速开发的理念相悖。

所以干脆去 allowJs 然后,在上述所说的 api/store/utils/lib 这些地方使用 ts,在频繁变化的业务代码那用 js,在业务稳定后,再把不变的部分,改造成ts,这样渐进式的改造,似乎才是比较合理的。

交互数据 restful/GraphQL/or other?

前端通常把后端作为提供原始数据的地方。

restful 所有的前端对接过,足够的简单,好处都很清楚,只要和后端沟通清除,是容易写出受欢迎的数据格式。但是人与人之间的沟通,恰恰是最不稳定的因素,因为大家都想偷懒,导致很多情况下存在一种博弈的关系。而且也受到团队之间磨合的影响,简单的同时,不稳定因素最大。

GraphQL 这个采访过很多的非 nodejs 后端,它们是想做一个这样的东西来免去那种 对接之苦,但是 GraphQL 的这种形式,它们实际上不是很欢迎。我想了想,最主要的原因,还在于 前后端之间的博弈,这个技术,最大的受益方是 前端,后端往往需要付出一部分开发成本,俗称 吃力不讨好。 所以这也无怪乎,前端需要一手包揽,从前端到中台的这一部分机制了。

前端由于react/vue/angular 这几个框架,带来的一些编程思维上的改变,去倒逼后端改革,后端不愿意改,那前端就自己做了呗(笑)。

前端直接使用 SQL / MongoDB 查询,这也不是不可以,这到变成了后端省力,前端工作量倍增的事情,其实就是把原先 viewModel(view) -> model(bc) -> entity(db) 这一步中,view 那一层可以直达 db

这种方式虽然不安全,但是对外包项目来说,还是足以的,毕竟 crud 那一套就能赚钱,还在乎什么安全性呢? 要安全,得加钱!

一次性的 token 与持久化的 token

一次性的 token,我这里特指 wx.login 这种,一旦发生解密行为,就失效的 js code

持久化的 token,这里主要说的那种接口调用凭证,系统通常在登录时候获取,在半小时左右失效,可通过 api 延期。

所以一次性的,通常在解密的 finally 就要重新获取,出于不信任后端的目的,这样最保险。

而持久化的 token,就需要充分和后端协调,比如说,你是用 http only 的 set-token,还是直接给 (不安全),过期时间是你来维护,还是我来做一套本地缓存的机制来处理,token 过期之后,你是给我一个 401 还是怎样的 code,来触发我拦截器里的某些行为(比如跳转到登录页,促使重新登录),这些本质上,就不是一个编程的行为,而是一个沟通的行为。

spa ssr 混用?难以处理的代码与配置

在 ssr 项目中,构建一块 spa 净土,实际上是很容易的,只需要写一个组件,告诉服务端, spa 那一块不需要渲染就行。

但是问题来了,整个项目本质上还是 ssr , 那么用户敲 url 初次进入某个 spa 页面,是会触发 server + client 的生命周期的。

所以很多时候,我们需要在中间件里写出这样的代码:

const mdw: Middleware = function ({ store, redirect, req }) {
  if (process.client) {
    
    if (!store.getters['user/authenticated']) {
      localStorage.removeItem('token')
      return redirect('/login')
    }
  }
  if (process.server) {
    if (typeof req.headers.cookie === 'string') {
      const cookieMap = cookie.parse(req.headers.cookie)
      const token = cookieMap[LocalKeys.AccessToken]
      if (!token) {
        return redirect('/login')
      }
    } else {
      redirect('/login')
    }
  }
}

其实这段代码是非常尴尬的:

因为,我们看似用 process.server , process.client 来进行条件编译了,实际上却会遇到更复杂的情况。

比如 esm 的 import xx from 'xxx', 这种一旦带上那种 iifewindows or process 这种全局对象,在 import 进来的时候,立刻会变为 里外不是人。 这个有 react/vue ssr 经历的同学们就很清楚了。

这导致我们经常在条件编译的同时,进行同步引用,比如说:

if(process.client){
  require('xxx')
  const pkg = await import('xxx')
  const __filename = fileURLToPath(import.meta.url)
  const __dirname = dirname(__filename)
  // ...
}

这种做法,显然会让代码变得丑陋。

当然有些包解决了这类的问题,比如 axiosserverclient 有不同的适配器,来保证性状一直,但是这种情况,考虑到拦截器中第三方包的影响,我还是习惯性的给 axios 多创建几个实例,比如 request.client.ts and request.server.ts, 再把拦截器,中间件这类的,单独拎出来维护,以此来区分三个大类: client-only , server-only , all/both

当然在命名上,除了文件夹分发,也可以用命名来区分,比如:

xxx.client.js -> client-only
xxx.server.js -> server-only
xxx.js -> all

这种做法在进行文件夹扫描的时候,还是很容易区分出来的。

总结

随笔大概就这些了