EthanLin-TWer / ethanlin-twer.github.io

💥学习笔记,React 全家桶 / TDD / JavaScript / 开发者效率 / 敏捷实践 / Udacity / 学习之道 等主题🍺
https://ethanlin-twer.github.io
143 stars 17 forks source link

1. 纯 React 技术目录 #122

Open EthanLin-TWer opened 7 years ago

EthanLin-TWer commented 7 years ago

如何学习一样新东西?

我希望以前端作为职业切入点来立足于行业(而事实上我现在的工作更多跟NodeJS与后端有关系……),而React又是前端中我选择的切入点,所以这个系列希望能以系统化的思路来组织React、产出学习经验,也即是最好有一个学习路线图。

2017年学习的时候,React官方对自己的定位是一个声明式、函数式的UI库。其处理的核心是UI,优势在于其声明式、组件化的特性提高了前端代码的可维护性和可扩展性。而前端工作相关的数据仓库、副作用、路由这三个主要部分,主要是由周边的组件/库来负责。

在社区多个解决方案的迭代中,逐渐形成了以react-redux/redux-saga/react-router为主要解决方案的几个组件(当然还有其他的方案,比如mobx/redux-thunk/react-routerv3和v4的不同路由API等),也出现了以封装最佳实践、简化模板代码为初衷的二次框架,如dva/umi系列。UI方面,antd也为许多企业应用所接纳。

而到了2020年的今天,React在UI这个核心以外在这三大方面中的数据流和副作用方面又多做了一些工作(比如Context API、hooks API等),使得以“前端问题域”为核心的解决方案更加完善,当然是赋能了开发者能用React来更简单地做前端的解决方案。这一点,在阅读《深入浅出React&Redux》的时候如能对比思考,会有更多收获。

《深入浅出》建构的路线图 与 读书法

如果把“前端解决方案”作为一个整体来看待,在React(当然其他的技术栈也成立)这个套餐内它的各个部分分别是:React本身提供UI能力、数据流/副作用/路由由社区方案负责。这本书的结构也围绕这几个点展开:

React部分的章节:

社区方案的部分:

第8章“单元测试”写得没我好,三观也不正,属于和稀泥、为了讨好更多受众的写法:“测也好不测也好,对开发有用才是好”。说得好像没测试还会更好似的。但问题的关键也不在这,关键是你不练怎么会好呢?你不练TDD,怎么知道什么才是对开发有用的测试呢?第10章、第12章可选读,没事不用读。

除此以外,一个正常的前端项目还必须有以下的工具/能力支撑,好在到了2020年的今天,许多方案选型已经相对固定了:

目录

最佳的学习资料

EthanLin-TWer commented 6 years ago

常用重构手法和刻意练习

EthanLin-TWer commented 6 years ago

工程实践

组件中的条件逻辑与列表渲染 - Conditional Rendering & List Data Rendering

简单来说,组件中经常会出现 if-else 这样的渲染逻辑,比如列表长度为0时不渲染组件,比如在一个大组件中,选择性决定是否渲染某个小部分。有了 if-else,就一定要出现 JSX 的 { } 标签,原本顺畅的声明式 UI JSX 语法就难看许多。因此,如前所述,为了保证 React 声明式 UI 代码的编写和阅读能力,我们的原则是,使用尽量少的 JSX 语法来处理组件中的 if 渲染逻辑。

经过考察各种各样的方案,只有下面👇这种方式是我所能勉强接受的、差强人意的解决方案。已经很难看了。

const NewsItem = ({ text, optionalVideoId }) => (
  <Text>{ text }</Text>
  { !!optionalVideoId && (
    <VideoPlayer id={optionalVideoId} />
  ) }
)

下面👇这种很清晰,多出来的 if JSX 非但不影响效果,反而让组件「读」起来也很顺畅。从阅读上讲比上面👆的写法好不少,但上面其实是相对比较复杂的 case,无法用这种手法进行处理。

const Login = ({ isLoggedIn, loginMode }) => {
  if (isLoggedIn) {
    return <Profile loginMode={ loginMode } />
  } 
  return <Login />
}

const Login = ({ isLoggedIn, loginMode }) => {
  return isLoggedIn ? <Profile loginMode={ loginMode } /> : <Login />
}

对于列表渲染,RN 中有 FlatList SectionList 这样内置的类用以渲染列表,但 react 中一般还是需要通过手动的 map 来处理。手动 map 的缺点是太过命令式,需要写一些样板代码,并且最重要的是让 React 的声明式 UI 能力和阅读体验大打折扣。然后我觉得,下面这些常见的逻辑,其实都可以通过封装组件来达到声明式的目的。

<If condition={data.length !== 0}>
  <Data />
</If>
<List data={data} renderItem={({ id, title }) => <div>{id}: {title}</div>} />

卧槽,今天发现这些库干的事情就跟我想的一样啊,用来对付简单逻辑,又获得模板声明式的好处,又可以省去测试并依然有信心。然后复杂逻辑就得益于 Vanilla JS 来处理,简直 React 完美的解决方案:

组件的拆分粒度

项目目录结构划分

EthanLin-TWer commented 6 years ago

单元测试

200

EthanLin-TWer commented 6 years ago

性能优化

首先性能优化界有句经典的名言,叫「Measure/Profile first, optimise second」,意思就是先分析瓶颈,再进行优化。这是推崇数据先行的思路,理由是我们从理论上推导所知的性能瓶颈,不一定是真正的问题所在,因为在我们写的代码和实际在机器上运行的代码中间,还可能有编译器或框架本身会为我们做优化,这中间过程是不可见不可预测的。因此,先做 profile,从数据上看到性能瓶颈以后再进行优化,往往是比较高效的做法。

然后,程墨在 《浅出》 中引用了 Donald 的名言:「过早优化是万恶之源」并补全了原句,指出其实过早优化非架构上的、对性能影响不大的代码才是万恶之源,对核心代码的架构和性能进行优化是合理的,并且应该时时进行,越早越好,越到后面成本越高。至于什么算核心代码,大概没人能给出明确定义,只能依靠各人经验而为了。但他这里就指出了,经验(而非数据)在优化领域还是有用武之地的。

那么,以 React 为 UI 库的代码还有什么可优化的地方?不是说 React 的 diff 算法已经非常牛逼,reconciliation 已经非常高效了么?这是因为,React 只是个库(愿意的话也可叫框架),而没有任何库能完美契合 所有 使用者的需求。这就导致,为了保证绝大多数人的正常使用,它必须使用保守的设计策略,而不能假设每个开发者都具有比较丰富的背景知识。这个正确而保守的策略就为高级开发者带来了优化的空间。那么问题来了,你,是不是(是否愿意成为)这个高级的开发者呢?

React 组件的优化策略从大面上分列如下:

避免不必要的 re-render

shouldComponentUpdate

这个方法是组件渲染前会被 React 询问的方法,如果返回 false,则组件不会被重新渲染。能否精准地控制这个方法的返回,是提升渲染、更新性能的重要因素。那么同学可能会有疑问,我写了几个月的前端代码,我们项目也没几个地方出现 shouldComponentUpdate 方法啊,难道这就说明我们项目的代码性能都不行吗?嗯,说对了一半。

待补。

数组唯一的 key 属性

避免不必要的数据计算

使用 reselect

经过一段时间使用发现,reselect 简直是 redux 项目的标配。如果你的 mapStateToProps() 中都仅是非常简单的从 store 中挑值,那当我没说,你很大概率不需要 reselect。但假如有以下的场景,那么几乎可以肯定 reselect 会是一个很好的方案:

其他方案

其他问题

用上 reselect 以后你可能会发现另一个问题,就是当你一份同样的 reducer 逻辑需要给多个组件使用时,你需要一个机制来区分。很多时候这个机制就是 const mapStateToProps = (store, ownProps) => ({})。好,这个机制可能会有问题,因为 ownProps 可能会一直变,导致 selector 多次重复计算,失去其缓存的优势。

问题的本质在于,ownProps 共享的不只是 reducer 核心逻辑,还需要有各自独立的数据结构,而 reselect 不能提供各自独立的缓存层,它只有一层。所以不仅需要一套机制共享 reducer 逻辑,还需要另一套机制,提供互相隔绝的数据缓存层。解决方案:

参考资料

JimmyLv commented 6 years ago

非常期待 reselect 这一部分。

EthanLin-TWer commented 6 years ago

深入原理 - React 核心机制

React.createElement() API

Virtual DOM 实现

Fiber / Reconciliation / diff 算法

SSR - 服务器端渲染

EthanLin-TWer commented 6 years ago

高阶组件