我看别人做系统就直接网站下载软件,上海影城改造升级,如何在网站投放广告,长春网站外包2019 农历新年即将到来#xff0c;是时候总结一下团队过去一年的技术沉淀。过去一年我们支撑的数据相关业务突飞猛进#xff0c;其中两个核心平台级产品代码量分别达到30万行和80万行#xff0c;TS 模块数均超过1000个#xff0c;协同开发人员增加到20人。由于历史原因是时候总结一下团队过去一年的技术沉淀。过去一年我们支撑的数据相关业务突飞猛进其中两个核心平台级产品代码量分别达到30万行和80万行TS 模块数均超过1000个协同开发人员增加到20人。由于历史原因开发框架同时基于 React 和 Angular考虑到产品的复杂性、人员的短缺和技术背景各异我们尝试了各种方法打磨工具体系来提升开发效率以下是节选的5项主要方法。一、基于 Redux 的状态管理从2013年React发布至今已近6个年头前端框架逐渐形成 React/Vue/Angular 三足鼎立之势。几年前还在争论单向绑定和双向绑定孰优孰劣现在三大框架已经不约而同选择单向绑定双向绑定沦为单纯的语法糖。框架间的差异越来越小加上 Ant-Design/Fusion-Design/NG-ZORRO/ElementUI 组件库的成熟选择任一你熟悉的框架都能高效完成业务。那接下来核心问题是什么我们认为是 状态管理。简单应用使用组件内 State 方便快捷但随着应用复杂度上升会发现数据散落在不同的组件组件通信会变得异常复杂。我们先后尝试过原生 Redux、分形 Fractal 的思路、自研类 Mobx 框架、Angular Service最终认为 Redux 依旧是复杂应用数据流处理最佳选项之一。庆幸的是除了 React 社区Vue 社区有类似的 VuexAngular 社区有 NgRx 也提供了几乎同样的能力甚至 NgRx 还可以无缝使用 redux-devtools 来调试状态变化。无论如何优化始终要遵循 Redux 三原则 原则 方法 引发的问题 Single source of truth 组件 Stateless数据来源于 Store 如何组织 Store State is read-only 只能通过触发 action 来改变 State action 数量膨胀大量样板代码 Changes are made with pure functions Reducer 是纯函数 副作用如何处理大量样板代码 这三个问题我们是通过自研 iron-redux 库来解决以下是背后的思考如何组织 Actionaction type 需要全局惟一因此我们给 action type 添加了 prefix其实就是 namespace 的概念为了追求体验请求(Fetch)场景需要处理 3 种状态对应 LOADING/SUCCESS/ERROR 这 3 个action我们通过 FetchTypes 类型来自动生成对应到 3 个 action如何组织 Store/Reducerreducer 和 view 不必一一对应应用中同时存在组件树和状态树按照各自需要去组织通过 connect 来绑定状态树的一个或多个分支到组件树通过构造一些预设数据类型来减少样板代码。对于 Fetch 返回的数据我们定义了 AsyncTuple 这种类型减少了样板代码明确的组织结构第1层是 ROOT第2层是各个页面第3层是页面内的卡片第4层是卡片的数据这样划分最深处基本不会超过5层最终我们得到如下扁平的状态树。虽庞大但有序你可以快速而明确的访问任何数据。[Redux 状态树]如何减少样板代码 使用原生 Redux一个常见的请求处理如下。非常冗余这是 Redux 被很多人诟病的原因。const initialState { loading true, error false, data []};function todoApp(state initialState, action) { switch (action.type) { case DATA_LOADING: return { ...state, loading: true, error: false } case DATA_SUCCESS: return { ...state, loading: false, data: action.payload } case DATA_ERROR: return { ...state, loading: false, error: true } default: return state }}使用 iron-redux 后class InitialState { data new AsyncTuple(true);}function reducer(state new InitialState(), action) { switch (action.type) { /** 省略其它 action 处理 */ default: return AsyncTuple.handleAll(prefix, state, action); }}代码量减少三分之二主要做了这2点引入了预设的 AsyncTuple 类型就是 {data: [], loading: boolean, error: boolean} 这样的数据结构使用 AsyncTuple.handleAll 处理 LOADING/SUCCESS/ERROR 这 3 种 actionhandleAll 的代码很简单使用 if 判断 action.type 的后缀即可源码在这里。曾经 React 和 Angular 是两个很难调和的框架开发中浪费了我们大量的人力。通过使用轻量级的 iron-redux完全遵循 Redux 核心原则下我们内部 实现了除组件层以外几乎所有代码的复用。开发规范、工具库达成一致开发人员能够无缝切换框架差异带来的额外成本降到很低。二、全面拥抱 TypeScriptTypeScript 目前可谓大红大紫根据 2018 stateofjs超过 50% 的使用率以及 90% 的满意度甚至连 Jest 也正在从 Flow 切换到 TS。如果你还没有使用可以考虑切换绝对能给项目带来很大提升。过去一年我们从部分使用 TS 变为全面切换到 TS包括我们自己开发的工具库等。TS 最大的优势是它提供了强大的静态分析能力结合 TSLint 能对代码做到更加严格的检查约束。传统的 EcmaScript 由于没有静态类型即使有了 ESLint 也只能做到很基本的检查一些 typo 问题可能线上出了 Bug 后才被发现。下图是一个前端应用常见的4层架构。 代码和工具全面拥抱 TS 后实现了从后端 API 接口到 View 组件的全链路静态分析具有了完善的代码提示和校验能力。[前后端协作简图]除了上面讲的 iron-redux我们还引入 Pont 实现前端取数它可以自动把后端 API 映射到前端可调用的请求方法。Pont 实现原理(法语桥) 是我们研发的前端取数层框架。对接的后端 API 使用 Java SwaggerSwagger 能提供所有 API 的元信息包括请求和响应的类型格式。Pont 解析 API 元信息生成 TS 的取数函数这些取数函数类型完美并挂载到 API 模块下。最终代码中取数效果是这样的Pont 实现的效果有根据方法名自动匹配 url、method并且对应到 prams、response 类型完美并能自动提示后端 API 接口变更后前端相关联的请求会自动报错再也不担心后端悄悄改接口前端不知晓再也不需要前后端接口约定文档使用代码保证前端取数和后端接口定义完全一致另外 iron-redux 能接收到 Pont 接口响应数据格式并推导出整个 Redux 状态树的静态类型定义Store 中的数据完美的类型提示。效果如下最终 TS 让代码更加健壮尤其是对于大型项目编译通过几乎就代表运行正常也给重构增加了很多信心。三、回归 Sass/Less2015 年我们就开始实践 CSS Modules包括后来的 styled-components 等到 2019 年 css-in-js 方案依旧争论不休虽然它确实解决了一些 CSS 语言天生的问题但同时增加了不少成本新手不够友好、全局样式覆盖成本高涨、伪类处理复杂、与AntD等组件库结合有坑。与此同时 Sass/Less 社区也在飞速发展尤其是 Stylelint 的成熟可以通过技术约束的手段来避免 CSS 的 Bad Parts。全局污染约定每个样式文件只能有一个顶级类如 .home-page{ .top-nav {/**/}, .main-content{ /**/ } }。如果有多个顶级类可以使用 Stylelint rule 检测并给出警告。依赖管理不彻底。借助 webpack 的 css-loader已够用。JS 和 CSS 变量共享。关于 JS 和 Sass/Less 变量共享我们摸索出了自己的解法// src/styles/variables.jsmodule.exports { // 主颜色 primary-color: #0C4CFF, // 出错颜色 error-color: #F15533, // 成功颜色 success-color: #35B34A,};// webpack.config.jsconst styleVariables require(src/styles/variables);// ... { test: /.scss$/, use: [ style-loader, css-loader?sourceMapminimize, { loader: sass-loader, options: { data: Object.keys(styleVariables) .map(key $${key}: ${styleVariables[key]};) .join(), sourceMap: true, sourceMapContents: true } } ] }//...在 scss 文件中可以直接引用变量// page.scss.button { background: $primary-color;}四、开发工具覆盖全链路2019 年你几乎不可能再开发出 React/Angular/Vue 级别的框架也没必要再造 Ant-Design/Fusion-Design/Ng-Zorro 这样的轮子。难道就没有机会了吗当然有结合你自身的产品开发流程依旧有很多机会。下面是常规项目的开发流程图任何一个环节只要深挖都有提升空间。如果你能通过工具减少一个或多个环节带来的价值更大。单拿其中的【开发】环节展开就有很多可扩展的场景一个有代表性的例子是我们开发了国际化工具 kiwi。它同样具有 TS 的类型完美非常强大的文案提示另外还有VS Code 插件 kiwi linter自动对中文文案标红如果已有翻译文案能自动完成替换Shell 命令全量检查出没有翻译的文案批量提交给翻译人员Codemod 脚本自动实现旧的国际化方案向 Kiwi 迁移成本极低除了以上三点未来还计划开发浏览器插件来检查漏翻文案利用 Husky 在 git 提交前对漏翻文案自动做机器翻译等等。未来如果你只提供一个代码库那它的价值会非常局限。你可以参照上面的图表开发相应的扩展来丰富生态。如果你是新手推荐学习下编译原理和对应的扩展开发规范。五、严格彻底的 Code Review过去的一年我们一共进行了 1200 多次 Code Review(CR)很多同事从刚开始不好意思提 MR 到后来追着别人 ReviewCR 成为每个人的习惯。通过 CR 让项目中任何一行代码都至少被两人触达过减少了绝大多数的低级错误提升了代码质量这也是帮助新人成长最快的方式之一。【其中一个项目MR截图】Code Review 的几个技巧No magicExplicit not implicit覆盖度比深度重要覆盖度追求100%频率比仪式感重要坐公交蹲厕所打开手机都可以 Review 别人代码不需要专门组织会议粒度要尽可能小一个组件一个方法均可可以结合 Git Flow24h 小时内处理无问题直接 merge有问题一定要留 comment并且提供 action对于亟待上线来不及 Review 的代码可以先合并上线上线后再补充 Review需要自上而下的推动具有完善的规范同时定期总结 Review 经验来丰富开发规范CR 并不只是为了找错看到好的代码不要吝啬你的赞美本质是鼓励开发者间更多的沟通互相学习营造技术文化氛围总结以上5点当然不是我们技术的全部。除此之外我们还实践了移动端开发、可视化图表/WebGL、Web Worker、GraphQL、性能优化等等但这些还停留在术的层面未来到一定程度会拿出来分享。如果你也准备或正在开发复杂的前端应用同时团队人员多样技术背景各异可以参考以上5点使用 Redux 实现规范清晰可预测的状态管理深耕 TypeScript 来提升代码健壮性和可维护性借助各种 Lint 工具回归简单方便的 CSS不断打磨自己的开发工具来保证开发规范高效并严格彻底实行 Code Review 促进人的交流和提升。作者前端新能源链接https://juejin.im/post/5c617c576fb9a049e93d33a4