最近又架构和编写了 2 个项目,在编写过程中,有一些思考
在我们 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
这样就可以在不同的仓库,使用不同的 User
和 Key
了。
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
这个采访过很多的非 nodejs
后端,它们是想做一个这样的东西来免去那种 对接之苦
,但是 GraphQL
的这种形式,它们实际上不是很欢迎。我想了想,最主要的原因,还在于 前后端之间的博弈
,这个技术,最大的受益方是 前端
,后端往往需要付出一部分开发成本,俗称 吃力不讨好
。 所以这也无怪乎,前端需要一手包揽,从前端到中台的这一部分机制了。
前端由于react/vue/angular
这几个框架,带来的一些编程思维上的改变,去倒逼后端改革,后端不愿意改,那前端就自己做了呗(笑)。
前端直接使用 SQL
/ MongoDB
查询,这也不是不可以,这到变成了后端省力,前端工作量倍增的事情,其实就是把原先 viewModel
(view) -> model
(bc) -> entity
(db) 这一步中,view
那一层可以直达 db
。
这种方式虽然不安全,但是对外包项目来说,还是足以的,毕竟 crud
那一套就能赚钱,还在乎什么安全性呢? 要安全,得加钱!
一次性的 token,我这里特指 wx.login
这种,一旦发生解密行为,就失效的 js code
持久化的 token,这里主要说的那种接口调用凭证,系统通常在登录时候获取,在半小时左右失效,可通过 api 延期。
所以一次性的,通常在解密的 finally 就要重新获取,出于不信任后端的目的,这样最保险。
而持久化的 token,就需要充分和后端协调,比如说,你是用 http only 的 set-token
,还是直接给 (不安全),过期时间是你来维护,还是我来做一套本地缓存的机制来处理,token 过期之后,你是给我一个 401 还是怎样的 code,来触发我拦截器里的某些行为(比如跳转到登录页,促使重新登录),这些本质上,就不是一个编程的行为,而是一个沟通的行为。
在 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'
, 这种一旦带上那种 iife
的windows
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)
// ...
}
这种做法,显然会让代码变得丑陋。
当然有些包解决了这类的问题,比如 axios
在 server
和 client
有不同的适配器,来保证性状一直,但是这种情况,考虑到拦截器中第三方包的影响,我还是习惯性的给 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
这种做法在进行文件夹扫描的时候,还是很容易区分出来的。
随笔大概就这些了