本文由 简悦 SimpRead 转码, 原文地址 mp.weixin.qq.com
大厂技术 高级前端 Node进阶
点击上方 程序员成长指北,关注公众号
回复1,加入高级Node交流群作者:猿猴望月
每个初入大厂的前端同学,在真正投入开发之前的第一件事可能就是熟悉公司的脚手架,从那一刻起,你就抛弃了熟悉的 cra、vue-cli、vite 等,成为了合格的大厂人(误)
大厂是不是为了冲绩效所以才会开发脚手架呢?每当新技术出现,作为热爱技术的前端人我们难道就不想折腾折腾让自己的项目 “现代” 起来?
我们可以从预开发环节 ⇒ 开发环节 ⇒ 构建环节 ⇒ 预提交环节 ⇒ 发布环节,看看脚手架到底做了些什么。
本篇会做一些代码的示意,但不会真正的写一个脚手架出来。
预开发环节
简化命令与配置
在没有脚手架的时候,我们是怎么配置一个项目的呢?我们需要在项目中创建 wekpack.config.js / rollup.config.js 文件,基于开发和生产环境做各自的配置。
如果用到了一些别的工具,如 esbuild 等,又得增加一个别的配置文件,而像 eslint、prettier、jest 这些也都有各自的配置文件。而具体使用的时候,各个工具又各自需要各自的命令。
每次都记这一堆命令和配置对开发者来说是一件非常麻烦的事情,这也是脚手架对开发者来说最大的意义,它能够很大程度上简化我们需要的命令和配置。
而我们是怎么使用脚手架的呢?拿 vite 为例,我们可以通过简单的命令就创建出一个 vite 的项目。
image.png
输入命令行,进行一些选择就可以创建一个项目,不需要你去写一大堆的配置文件,直接npm run dev就能跑起来;同时,直接npm run build就可以打包成生产环境的代码。如果是大厂内部的框架,甚至还可以集成大厂内部的部署环境,跑一个npm run preview或者npm run deploy就可以预览 / 部署。
如果我们要实现这样的效果,首先我们需要在脚手架项目中预定义一些模板(类似 react、react-ts、vue、vue-ts 等),其次,我们要能在命令行中运行 bin 来进行项目创建,可以看看 vite 的源码,在 create-vite 的 package.json[1] 中
"bin": {
"create-vite": "index.js",
"cva": "index.js"
}bin 指定 create-vite 路径,而在 index.js 中则是主要做了命令行参数读取、拉取最新模板的操作,有兴趣的同学可以自己阅读一下。
同时,脚手架往往提供了自己的 config 文件,比如 vite 就提供了 vite.config 来统一配置内部所使用到的工具。这要怎么实现呢?简单来说就是在 vite 的 bin 中读取了 vite.config 文件内容,从而构建出 vite config 对象,再根据这个 vite config 对象生成对应的 rollup config 对象然后调用 rollup。
相应 vite 可以参考 github.com/vitejs/vite…[2] 中 resolveConfig 这一段
export async function resolveConfig(
inlineConfig: InlineConfig,
command: 'build' | 'serve',
defaultMode = 'development'
): Promise<ResolvedConfig> {
let config = inlineConfig
let configFileDependencies: string[] = []
let mode = inlineConfig.mode || defaultMode
// some dependencies e.g. @vue/compiler-* relies on NODE_ENV for getting
// production-specific behavior, so set it here even though we haven't
// resolve the final mode yet
if (mode === 'production') {
process.env.NODE_ENV = 'production'
}
...限定版本,规避风险
image.png
想象一下如果你是个毛子这个时候应该有多绝望哈哈,公司有自己的脚手架可以限定死版本,减少某个包的作者一时兴起或者被攻击者入侵发布了有问题的新版本,而我们又在这时新起项目或者升级包导致业务挂掉的情况。
这里说的是减少,因为总有库不是被包括在脚手架里的,这也是 lock 文件的存在意义。
开发环节
提供模板
脚手架可提供多套模板供用户选择,类似纯 js、纯 ts、vue3+ts。同时,对于一些网页需要的通用配置元素,如 favicon,title,preconnect link 可以通过 config 的方式提供给用户配置。
也可以先提供 monorepo 壳子的模板,在其中再进行项目的初始化。
约定式路由
在项目中,可以约定一个名为 pages 的文件夹,pages 下的文件夹中的 index 默认为一个个 page。在 pages 中如果给出 404、500 这样的 index,也可以方便地做一些做页面错误 catch 的处理;并且可以在脚手架中默认集成动态引入组件。
在需要做单页面维度的权限校验的场景下,这时又分两种提示方式:第一种是不该进的路由用户无法进入,第二种是可以进入,但是给一个申请权限的弱提示。如果是想要第一种提示方式的话,约定式路由需要进入页面后校验没有权限再跳出,体验上会稍差一些。
集合微前端
现在的 single-spa、qiankun 等微前端框架都非常流行,脚手架可以提供一套有微前端框架的 B 端模板,直接用于给开发者生成。如果是在大公司内部,往往对这些微前端框架本身也有一定的封装,形成公司自己的微前端框架,但对于脚手架来说,做的事情还是一样的,就是把微前端框架的部分封装起来。在开发者使用的时候,开发者甚至不需要知道页面是通过微前端的方式来加载的,就用普通写组件的方式来写就好。
提供可插拔的功能插件(权限、埋点、sentry 等
脚手架可提供一些插件,供开发者选择是否接入(插件和上 part 提到的 feature flag 各自的侧重点是:通过 feature flag 提供的功能更多与构建相关,而插件更多为开发以及一些附加平台功能)。像是埋点、sentry 都比较简单好理解,我们这里来聊一下权限。
大厂尤其是内部系统很多都是使用单点登录进行登录的,而如果访问接口没有权限一般会是 401 的错误码,所以其实可以在权限插件中做统一的无权限的登录跳转。
权限插件还可以做再细致的权限校验,我们可以结合 menu 封装,对每一个页面进行业务逻辑上的可见的设置。
网络请求层面封装
脚手架一般会根据当前的运行环境进行网络请求方面的配置,比如 dev、test、pre-release、production 等,因为不同环境的请求 baseURL、超时时间会不同。
同时,脚手架还可以提供基于接口定义生成接口定义代码的功能,通过后端提供的 swagger/thrift 文件直接生成接口请求代码。
prettier、eslint
每个大厂都会有自己对 prettier 和 eslint 的要求,将 prettier 和 eslint 的配置可以统一收口在脚手架中,形成一套代码风格。
构建环节
构建打包支持
脚手架往往提供开箱即用的构建打包工具支持,对于前端常见的文件类型提供了默认配置,一般包括:
ts、js
css / less / sass / stylus / postcss / css module / tailwind 支持,有的脚手架甚至提供了默认的换肤配置
图片、svg(包括导入为 src 或组件直接渲染)
wasm 等等
避免重复造轮子
这里的重复造轮子更多指的是性能优化层面,首先脚手架一般都有持续迭代的同学,当新技术出来时(比如 vite、esbuild、swc 等),可以先进行一波升级并推出 beta 版本或者提供出一个 feature flag,减少真正产出业务的同学重复踩坑的可能。
这里提供一个 swc 的 feature flag 供大家参考:
/**
* config sample
* swc: { jsc: { parser: { syntax: "typescript", tsx: true } } }
*/
if (options?.swc) {
return {
test: /\.(t|j)sx?$/,
use: {
loader: "swc-loader",
options:
typeof options.swc === "object"
? options.swc
: transformTsConfigToSwcConfig(),
},
exclude: /node_modules/,
};
} else {
return {
test: /\.(t|j)sx?$/,
use: {
loader: "babel-loader",
},
exclude: /node_modules/,
};
}预提交环节
commit hook
脚手架中可以集成 git commit 之前触发的钩子,钩子中主要可以做:
commit message 的 check,例如项目是 monorepo 结构,那 commit message 应该是 feat/fix/chore……(project name): xxx 改动,可以采用 commitlint - Lint commit messages[3]
代码质量的初步校验,eslint、prettier 的强校验
这里不建议在 commit hook 中做过于重的校验,因为 commit 时希望 hook 能够较快执行完,让用户还是可以尽快提交。更全的校验还是放在 CI 阶段执行,如 unit test 等。
发布环节
push 结合 CI
在预提交环节,我们提到了用 commit hook 做初步校验,但是 hook 总是有方法绕过的,所以在 CI 时还是要做 double-check。
一般脚手架会提供一个 ci yml 文件来灵活的控制构建,在 push 代码可以进行 CI,一旦 CI 有问题后续的合入或者 CD 都不可再继续。
发正式版本可以在本地用脚手架 publish,也可以在画面 CI 时直接提供打正式包的选项。
最后
Node 社群
我组建了一个氛围特别好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你对Node.js学习感兴趣的话(后续有计划也可以),我们可以一起进行Node.js相关的交流、学习、共建。下方加 考拉 好友回复「Node」即可。
“分享、点赞、在看” 支持一下参考资料
[1]
https://github.com/vitejs/vite/tree/main/packages/create-vite/package.json: _https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fvitejs%2Fvite%2Ftree%2Fmain%2Fpackages%2Fcreate-vite%2Fpackage.json_
[2]
https://github.com/vitejs/vite/blob/main/packages/vite/src/node/config.ts: _https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fvitejs%2Fvite%2Fblob%2Fmain%2Fpackages%2Fvite%2Fsrc%2Fnode%2Fconfig.ts_
[3]
https://commitlint.js.org/: _https://link.juejin.cn?target=https%3A%2F%2Fcommitlint.js.org%2F_