给2019前端的5个建议

原发于知乎专栏,欢迎关注 zhuanlan.zhihu.com/p/55357377javascript

2019 农历新年即将到来,是时候总结一下团队过去一年的技术沉淀。过去一年咱们支撑的数据相关业务日新月异,其中两个核心平台级产品代码量分别达到30+万行和80+万行,TS 模块数均超过1000个,协同开发人员增长到20+人。因为历史缘由,开发框架同时基于 React 和 Angular,考虑到产品的复杂性、人员的短缺和技术背景各异,咱们尝试了各类方法打磨工具体系来提高开发效率,如下是节选的5项主要方法。css

1、基于 Redux 的状态管理

从2013年React发布至今已近6个年头,前端框架逐渐造成 React/Vue/Angular 三足鼎立之势。几年前还在争论单向绑定和双向绑定孰优孰劣,如今三大框架已经不约而同选择单向绑定,双向绑定沦为单纯的语法糖。框架间的差别愈来愈小,加上 Ant-Design/Fusion-Design/NG-ZORRO/ElementUI 组件库的成熟,选择任一你熟悉的框架都能高效完成业务。前端

那接下来核心问题是什么?咱们认为是 状态管理。简单应用使用组件内 State 方便快捷,但随着应用复杂度上升,会发现数据散落在不一样的组件,组件通讯会变得异常复杂。咱们前后尝试过原生 Redux、分形 Fractal 的思路、自研类 Mobx 框架、Angular Service,最终认为 Redux 依旧是复杂应用数据流处理最佳选项之一。java

庆幸的是除了 React 社区,Vue 社区有相似的 Vuex,Angular 社区有 NgRx 也提供了几乎一样的能力,甚至 NgRx 还能够无缝使用 redux-devtools 来调试状态变化。webpack

image.png

不管如何优化,始终要遵循 Redux 三原则:git

原则 方法 引起的问题
Single source of truth 组件 Stateless,数据来源于 Store 如何组织 Store?
State is read-only 只能经过触发 action 来改变 State action 数量膨胀,大量样板代码
Changes are made with pure functions Reducer 是纯函数 反作用如何处理,大量样板代码

这三个问题咱们是经过自研 iron-redux 库来解决,如下是背后的思考:github

如何组织 Action?web

  1. action type 须要全局唯一,所以咱们给 action type 添加了 prefix,其实就是 namespace 的概念
  2. 为了追求体验,请求(Fetch)场景须要处理 3 种状态,对应 LOADING/SUCCESS/ERROR 这 3 个action,咱们经过 FetchTypes 类型来自动生成对应到 3 个 action

如何组织 Store/Reducer?typescript

  1. reducer 和 view 没必要一一对应,应用中同时存在组件树和状态树,按照各自须要去组织,经过 connect 来绑定状态树的一个或多个分支到组件树
  2. 经过构造一些预设数据类型来减小样板代码。对于 Fetch 返回的数据咱们定义了 AsyncTuple 这种类型,减小了样板代码
  3. 明确的组织结构,第1层是 ROOT,第2层是各个页面,第3层是页面内的卡片,第4层是卡片的数据,这样划分最深处基本不会超过5层

最终咱们获得以下扁平的状态树。虽庞大但有序,你能够快速而明确的访问任何数据。redux

Redux 状态树
[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点:

  1. 引入了预设的 AsyncTuple 类型,就是 {data: [], loading: boolean, error: boolean}  这样的数据结构;
  2. 使用 AsyncTuple.handleAll  处理 LOADING/SUCCESS/ERROR 这 3 种 action,handleAll 的代码很简单,使用 if 判断 action.type 的后缀便可,源码在这里

曾经 React 和 Angular 是两个很难调和的框架,开发中浪费了咱们大量的人力。经过使用轻量级的 iron-redux,彻底遵循 Redux 核心原则下,咱们内部 实现了除组件层之外几乎全部代码的复用。开发规范、工具库达成一致,开发人员可以无缝切换,框架差别带来的额外成本降到很低

2、全面拥抱 TypeScript

TypeScript 目前可谓大红大紫,根据 2018 stateofjs,超过 50% 的使用率以及 90% 的满意度,甚至连 Jest 也正在从 Flow 切换到 TS。若是你尚未使用,能够考虑切换,绝对能给项目带来很大提高。过去一年,咱们从部分使用 TS 变为全面切换到 TS,包括咱们本身开发的工具库等。

TS 最大的优点是它提供了强大的静态分析能力,结合 TSLint 能对代码作到更加严格的检查约束。传统的 EcmaScript 因为没有静态类型,即便有了 ESLint 也只能作到很基本的检查,一些 typo 问题可能线上出了 Bug 后才被发现。

下图是一个前端应用常见的4层架构。 代码和工具全面拥抱 TS 后,实现了从后端 API 接口到 View 组件的全链路静态分析,具备了完善的代码提示和校验能力。

image.png
[先后端协做简图]

除了上面讲的 iron-redux,咱们还引入 Pont 实现前端取数,它能够自动把后端 API 映射到前端可调用的请求方法。

Pont 实现原理:(法语:桥) 是咱们研发的前端取数层框架。对接的后端 API 使用 Java Swagger,Swagger 能提供全部 API 的元信息,包括请求和响应的类型格式。Pont 解析 API 元信息生成 TS 的取数函数,这些取数函数类型完美,并挂载到 API 模块下。最终代码中取数效果是这样的:

image.png

Pont 实现的效果有:

  1. 根据方法名自动匹配 url、method,而且对应到 prams、response 类型完美,并能自动提示
  2. 后端 API 接口变动后,前端相关联的请求会自动报错,不再担忧后端悄悄改接口前端不知晓
  3. 不再须要先后端接口约定文档,使用代码保证前端取数和后端接口定义彻底一致

另外 iron-redux 能接收到 Pont 接口响应数据格式,并推导出整个 Redux 状态树的静态类型定义,Store 中的数据完美的类型提示。效果以下:

image.png

最终 TS 让代码更加健壮,尤为是对于大型项目,编译经过几乎就表明运行正常,也给重构增长了不少信心

3、回归 Sass/Less

2015 年咱们就开始实践 CSS Modules,包括后来的 styled-components 等,到 2019 年 css-in-js 方案依旧争论不休,虽然它确实解决了一些 CSS 语言天生的问题,但同时增长了很多成本,新手不够友好、全局样式覆盖成本高涨、伪类处理复杂、与AntD等组件库结合有坑。与此同时 Sass/Less 社区也在飞速发展,尤为是 Stylelint 的成熟,能够经过技术约束的手段来避免 CSS 的 Bad Parts。

  1. 全局污染:约定每一个样式文件只能有一个顶级类,如 .home-page{ .top-nav {/**/}, .main-content{ /**/ } }。若是有多个顶级类,可使用 Stylelint rule 检测并给出警告。
  2. 依赖管理不完全。借助 webpack 的 css-loader,已够用。
  3. JS 和 CSS 变量共享。关于 JS 和 Sass/Less 变量共享,咱们摸索出了本身的解法:
// src/styles/variables.js
module.exports = {
  // 主颜色
  'primary-color': '#0C4CFF',
  // 出错颜色
  'error-color': '#F15533',
  // 成功颜色
  'success-color': '#35B34A',
};
复制代码
// webpack.config.js
const styleVariables = require('src/styles/variables');

// ...
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          'css-loader?sourceMap&minimize',
          {
            loader: 'sass-loader',
            options: {
              data: Object.keys(styleVariables)
                .map(key => `\$${key}: ${styleVariables[key]};`)
                .join('\n'),
              sourceMap: true,
              sourceMapContents: true
            }
          }
        ]
      }
//...
复制代码

在 scss 文件中,能够直接引用变量

// page.scss
.button {
  background: $primary-color;
}
复制代码

4、开发工具覆盖全链路

2019 年,你几乎不可能再开发出 React/Angular/Vue 级别的框架,也不必再造 Ant-Design/Fusion-Design/Ng-Zorro 这样的轮子。难道就没有机会了吗?

固然有,结合你自身的产品开发流程,依旧有不少机会。下面是常规项目的开发流程图,任何一个环节只要深挖,都有提高空间。若是你能经过工具减小一个或多个环节,带来的价值更大。

image.png

单拿其中的【开发】环节展开,就有不少可扩展的场景:

image.png

一个有表明性的例子是,咱们开发了国际化工具 kiwi。它一样具备 TS 的类型完美,很是强大的文案提示,另外还有:

  1. VS Code 插件 kiwi linter,自动对中文文案标红,若是已有翻译文案能自动完成替换
  2. Shell 命令全量检查出没有翻译的文案,批量提交给翻译人员
  3. Codemod 脚本自动实现旧的国际化方案向 Kiwi 迁移,成本极低

除了以上三点,将来还计划开发浏览器插件来检查漏翻文案,利用 Husky 在 git 提交前对漏翻文案自动作机器翻译等等。

将来若是你只提供一个代码库,那它的价值会很是局限。你能够参照上面的图表,开发相应的扩展来丰富生态。若是你是新手,推荐学习下编译原理和对应的扩展开发规范。

5、严格完全的 Code Review

过去的一年,咱们一共进行了 1200+ 屡次 Code Review(CR),不少同事从刚开始很差意思提 MR 到后来追着别人 Review,CR 成为每一个人的习惯。经过 CR 让项目中任何一行代码都至少被两人触达过,减小了绝大多数的低级错误,提高了代码质量,这也是帮助新人成长最快的方式之一。

image.png

【其中一个项目MR截图】

Code Review 的几个技巧:

  1. No magic
  2. Explicit not implicit
  3. 覆盖度比深度重要,覆盖度追求100%
  4. 频率比仪式感重要,坐公交蹲厕所打开手机均可以 Review 别人代码,不须要专门组织会议
  5. 粒度要尽量小,一个组件一个方法都可,能够结合 Git Flow
  6. 24h 小时内处理,无问题直接 merge,有问题必定要留 comment,而且提供 action
  7. 对于亟待上线来不及 Review 的代码,能够先合并上线,上线后再补充 Review
  8. 须要自上而下的推进,具备完善的规范,同时按期总结 Review 经验来丰富开发规范
  9. CR 并不仅是为了找错,看到好的代码,不要吝啬你的赞美
  10. 本质是鼓励开发者间更多的沟通,互相学习,营造技术文化氛围

总结

以上5点固然不是咱们技术的所有。除此以外咱们还实践了移动端开发、可视化图表/WebGL、Web Worker、GraphQL、性能优化等等,但这些还停留在术的层面,将来到必定程度会拿出来分享。

若是你也准备或正在开发复杂的前端应用,同时团队人员多样技术背景各异,能够参考以上5点,使用 Redux 实现规范清晰可预测的状态管理,深耕 TypeScript 来提高代码健壮性和可维护性,借助各类 Lint 工具回归简单方便的 CSS,不断打磨本身的开发工具来保证开发规范高效,并严格完全实行 Code Review 促进人的交流和提高。

Links

  1. Pont:github.com/nefe/pont
  2. Kiwi:github.com/nefe/kiwi
  3. iron-redux: github.com/nefe/iron-r…
  4. The State of JavaScript 2018

咱们还在招聘,欢迎邮件简历,来信必回。shaoyin.ssy@alibaba-inc.com

相关文章
相关标签/搜索