1、背景及特性
UI问题 ----组件处理
传统是局部刷新处理,react整体进行刷新,不管数据具体是那个发生变化,只关心发生变化与否
1个概念4api 单项数据流 完善的错误提示
数据模型问题
针对传统mvc 提出flux架构:单项数据流
基于衍生出 redux 和 Mobx 状态管理框架
2、组件方式考虑UI的构建
传统模式的是html+js 数据 +form
react 方式 commentBox + CommentList + CommentForm
c02 CommentBox
https://codesandbox.io/s/6n20nrzlxz
创建一个简单的组件步骤
- 创建静态UI ->原生Html标签
- 考虑组件的状态组成 ->状态来自外部还是需要内部维护
- 考虑组件的交互方式-> 组件内部进行的一些操作如何暴露给外部使用
创建组件的原则:
-
单一职责原则
- 每个组件只做一件事
- 如果组件变得复杂,那么应该拆分成小组件
-
数据状态管理原则 :DRY原则
- 能计算得到的状态就不要单独存储
- 组件尽量无状态,所需数据通过props获取
3、JSX的本质
jsx本质是动态创建组件的语法糖
优点:声明式创建界面的直观 代码动态创建界面的灵活 无需学习新的模板语言
4、react组件的生命周期及其使用场景
-
constructor
- 用于初始化内部状态,很少使用
- 唯一可以直接修改state的地方
-
getDerivedStateFromProps
- 典型场景:表单控件获取默认值
- 每次render都会调用,尽量不要使用
-
componentDidMount
- ui渲染完成后调用
- 只执行一次
- 典型场景:获取外部资源,页面初始化
-
componentWillUnmount
- 组件移除时调用
- 典型场景:资源释放
-
getSnapshotBeforeUpdate
- 在页面render之前调用,state已更新
- 典型场景:获取render之前的DOM状态
-
componentDidUpdate
- 每次UI更新时被调用
- 典型场景:页面需要根据props变化重新获取数据,获取分页页面切换后的数据
-
shouldComponentUpdate
- 决定virtual DOM 是否要重绘
- 一般可以由PureComponent自动实现,自动判断 当前props和state与之前的props和state是否发生变化,如果没有变化自动阻止更新
- 典型场景:性能优化
5、virtual DOM 及key 属性作用
-
diff
广度优先分层比较
-
虚拟DOM的两个假设
- 组件的DOM节点是相对稳定的
- 类型相同的兄弟节点可以被唯一标识
https://supnate.github.io/react-dom-diff/index.html 可一看到dom节点被更新卸载
整体删除节点,在添加创建节点
6、高阶组件和函数作为子组件
-
组件复用两种形式:高阶组件和函数作为子组件
https://codesandbox.io/s/react-geek-time-cjyp8 c06
函数作为子组件关键是 props.children()的使用 显示按钮下对应图片、颜色块
https://www.bilibili.com/video/av37159071/?p=12
7、context API及其使用场景
context api 解决组件通信问题,redux 重度依赖它
8、脚手架工具创建项目
- react React App,rekit
2.Condesandbox 在线前端开发工具ide
9、打包注意事项
- 设置nodejs环境为 production
- 禁用开发时的专用代码,比如 logger
- 设置应用根路径
10、状态管理
状态管理框架redux
redux 让组件的通信更加容易,
redux特性:
- Single Source of Truth
- 可预测性:state + action = new state
- 纯函数更新store —reducer
11、深入理解store,Action,reducer
- store
- getState()
- dispatch(action)
- subscribe(listener)
react中redux使用组件
combineReducer
bindActionCreators({type:’’,palyload:’’},store.dispatch)
connect组件
store绑定到组件的state应该更加精细,需要哪些绑定那些,否则其他参数的变化会导致组件的无效更新渲染
理解异步action 、redux中间件
异步action不是真正的action而是几个同步action的组合的使用
中间件在dispatch中截获action做特殊处理
如何组织action和reducer
- 所有action放在一个文件,会无线扩展
- action / reducer 分开,实现业务逻辑时需要来回切换
- 系统中有哪些Action不够直观
新的方式:
单个action和reducer放在同一个文件内
优点:
- 易于开发:不用在action和reducer之间来回切换
- 易于维护:每个action文件都很小,容易理解
- 易于测试:每个业务逻辑只需对应一个测试文件
- 易于理解:文件名就是action名字,文件列表就是action的列表
不可变数据
原因:性能优化 易于调试和追踪 易于推测
判断引用是否相等来判断store有没有发生变化,通过引用比较
如何操作不可编数据
- 原生写法:{…},Object.assign 性能最高的写法,es6的原生写法,大部分情况都可以使用这种
- immutability-helper 主要用于层次比较深的节点更新,复杂场景更新的节点比较深
- immer 性能不如前两种,写法上就像是修改原来的state一样。为state的属性创建代理实现,在应用程序比较小的,节点不多时,性能还行
12、react router路由不只是页面切换,更是代码组织【代码分割的一种】
- 为什么需要路由
- 单页应用需要进行页面切换
- 通过URL可以定位到页面
- 更有语义的组织资源
路由是一个后端的概念
特性:声明式路由定义 动态路由
三种路由实现方式:
-
url路径 标准的URL路径
-
hash路由 不是通过斜杠/方式,而是通过#方式
与url的区别:大部分情况都可以使用url方式进行路由,如果支持一些低版本浏览器时,用斜杠方式浏览器会页面不刷新,这时就需要使用hash路由【HashRouter】
-
内存路由【MemoryRouter】,路由不会反应到url上,服务器段渲染时会用但是前端也可以用
import {MemoryRouter} from 'react-router'
基于路由配置进行资源组织
- 实现业务逻辑的松耦合
- 易于扩展重构和维护
- 路由层面实现 lazy load
react router 核心api
1.<Link> :普通链接,不会触发浏览器刷新
<Link to="/about"></Link>
2.<NavLink>:类似link但是会添加一个当前选中状态
<Link to="/about" activeClassName="selected"></Link>
3.<Prompt>:满足条件时提示用户是否离开当前页面
<Prompt when={formIsHalfFilledOut}
message="Are you sure you want to leave"/>
场景:用户切换页面的确认操作,当用户正在填写一个表单,这时用户点了一个链接,其实用户还没有填完,这时就要提醒用户是否真实要离开当前页面,使用此标签进行提示。
4.<Redirect>:重定向当前页面,例如登录时
<Route exact path='/' render={()=>(
loggedIn ? (
<Redirect to='/dashboard'>
):(
<PublicHomePage>
)
)>
5.<Route>:路由配置的核心标记,路径匹配时显示对应组件
关键字 exact 代表是否精确匹配路径
匹配多个路由,多个组件都会显示,
场景:列表界面,点击删除按钮,在原界面上层显示弹出删除提示框
6.<Switch>:只显示第一个匹配的路由
只会匹配第一个符合的路径。
参数定义,嵌套路由的使用场景
通过url 传递参数
1.如何通过url 传递参数:<Route path="/topic/:id">
何时需要URL传参:页面状态尽量通过Url参数定义
2.如何获取参数: this.props.match.params
3.https://github.com/pillarjs/path-to-regexp
嵌套路由
-
每个react组件都可以是路由容器
-
React Router的声明式语法可以方便的定义嵌套路由
相对路径必须一致
13、UI组件库对比和介绍
ant Design,Material UI, Semantic UI
-
ant Design 服务于企业级产品的设计体系 https://ant.design/docs/react/introduce-cn
-
Material UI 主要在于UI设计风格更为花哨,时尚,面向最终消费者使用,
提供更好看的支持 https://material-ui.com/zh/
-
Semantic UI 把UI作为language 描述,定义了很多样式class来描述uI https://semantic-ui.com/introduction/getting-started.html
选择UI库的考虑因素
- 组件库是否齐全
- 样式风格是否符合业务需求
- API 设计是否便携和灵活
- 技术支持是否完善
- 开发是否活跃
14、Jest /Enzyme等工具进行单元测试
react让前端测试变得容易
- react应用很少需要访问浏览器api
- 虚拟DOM可以在Nodejs环境运行和测试
- Redux隔离了状态管理,纯数据层单元测试
单元测试涉及的工具
- Jest:Facebook开源的js单元测试框架
- JS DOM:浏览器环境的NodeJS模拟环境
- Enzyme: React 组件渲染和测试
- nock:模拟Http请求
- sinon:函数模拟和调试追踪
- Istanbul :单元测试覆盖率
15、开发调试工具
ESLint/prettier/React DevTool/Redux DevTool
16、前端项目的理想架构
可维护 可扩展 可测试 易开发 易构建
五个方面相互制约协调
-
易开发
- 开发工具是否完善
- 生态圈是否繁荣
- 社区是否活跃
-
易于扩展
- 增加新功能是否容易
- 新功能是否显著增加系统复杂度
-
易于维护
- 代码是否容易理解
- 文档是否健全
-
易于测试
- 功能的分层是否清晰
- 副作用少
- 尽量使用纯函数
-
易于构建
-
使用通用的技术和架构,打包通常使用webpack
-
构建工具的选择
-
17、拆分复杂度
按领域模型(feature)组织代码,降低耦合度
功能技术上进行拆分,将业务逻辑拆分为高内聚低耦合的模块
如何组织Component、action和reducer
文件夹结构
- 按feature组织源文件
- 组件和样式文件同一级
- redux单独文件夹
- 单元测试保持同样的结构目录放在tests文件夹
样式文件进行单独的打包流程
常量放在自己的每个feature中
使用 root loader加载feature下的各个资源
如何组织react router的路由配置
使用json定义顶层路由。
- 每个feature都有自己的专属配置
- 顶层路由使用json配置更容易理解和维护
- 如何解析json配置到React Router语法
路由取值 this.props.match.params.id
页面跳转是在组件加载完成后进行,
handleClose = () => {
this.props.history.goBack();
};
通过 this.props.form.getFieldValue(“job”) 获取form 的元素值
18、开发列表要考虑的技术要点
-
如何翻页
-
如何进行内容搜索
当前页面前端搜索,服务器端的后端搜索
-
如何缓存数据
当从详细页面返回列表页面时可能不需要刷新
-
何时进行页面刷新
对缓存的刷新,
列表页的某些数据,进行了修改,那么当我们再次返回到这个列表页面时这时候我们就要进行页面的刷新,就是对缓存的刷新
前端store的模型设计
const byId = {};// 存储id:object map映射
const items = []; // 存储id
action.data.items.forEach(item => {
items.push(item.id);
byId[item.id] = item;
});
这种扁平的数据结构,也是redux比较推荐的方式,有利于后续计算和性能的优化
为列表的store设计存储模型,组件页面设计路由及将页面所需的参数放在url中,这样url可以直接访问
数据缓存在store中,在数据发生变化是进行更新Item及byID
缓存更新,加载状态,错误处理
这里页面列表使用缓存思路为:
在页面加载时更新store,当点击列表中某一条查看详情,在返回时,这时候可以使用store缓存,将刚才列表页的数据从store中取出来,不需要重新冲后台再次请求获取数据,需要做的事就是判断什么情况下才允许进行后台获取数据更新store。
列表中的数据对象可以包含详情页面的所有数据,这时可以使用缓存,但同时在详情页面也必须提供能够自己获取数据的能力,页面刷新时读取数据或直接通过链接进入详情页时
页面数据来源多个请求的处理
按照是否存在数据添加loading
在一个action 中同时派发连个dispatch和同时执行两个后台调用
19、常见页面布局
- 从0开始用css实现
- 使用css grid系统
- 使用组件库例如antd
20、集成第三方js库
集成第三方js库技术要点
- 使用ref获取原生DOM节点引用
- 手动将组件状态更新到DOM节点
- 组件销毁时移除原生节点DOM事件
21、使用react实现拖放的技术要点
- 如何使用react的鼠标事件系统
- 如何判断拖放开始和结束 利用MounseDown和MounseUp
- 如何实现拖放元素的位置移动
- 拖放状态在组件中如何维护
react 有一个库react-beautiful-dnd
22、性能是第一需求:时刻考虑性能问题
如何避免应用出现性能问题
- 了解常见的性能问题常见
- 键盘输入时觉得明显卡顿 :输入框输入字符是每次更新时卡顿
- 鼠标移动或者滚动页面时卡顿:鼠标移动时侧边栏跟着动,这时处理使用一个debounds,d当鼠标不在移动这时调整侧边栏位置。
- 时刻注意代码的潜在性能问题
- 注意什么时候拆分组件及代码的优化空间,拆分粒度是否够细,粒度越细组件优化空间越大,组件细了之后,组件可以优化为纯组件,那它下面的DOM节点可以作为一个整体 看待,当这些DOM节点对应的组件的状态没有变的时候,这些细的组件就不需要参与到虚拟DOM的diff操作中,因此性能更高
- 注重重构的代码
- 代码的耦合性必须非常低,模块之前相对独立
- 了解如何使用工具定位性能问题
- 定位性能问题产生的原因利用工具 react-devTool Chrome提供的DevTool调试性能问题及内存问题
网络性能优化:自动化按需加载
动态加载模块
利用webpack打包工具可以很容易实现自动化按需加载这个功能,利用webpack提供的api
-
如何在react中实现按需加载
-
什么是按需加载
当切换到某个页面时,这个页面的内容所需的东西才会加载出来,而不是在应用一开始就加载了。
-
使用Webpack的import ApI
-
使用react-loadable 库实现react异步加载
使用react-loadable这个高阶组件
使用Chrome-DevTool进行性能调优
-
使用ReactDevTool 找到多余渲染
- 使用hightlight-update 可以看到页面中哪一部分进行了更新,
- 在组件绑定store的属性时,页面用到哪些就绑定哪些,否则引起不必要的渲染更新
-
使用Chrome DevTool 定位性能瓶颈
-
- 了解如何使用工具定位性能问题
- 定位性能问题产生的原因利用工具 react-devTool Chrome提供的DevTool调试性能问题及内存问题
网络性能优化:自动化按需加载
动态加载模块
利用webpack打包工具可以很容易实现自动化按需加载这个功能,利用webpack提供的api
-
如何在react中实现按需加载
-
什么是按需加载
当切换到某个页面时,这个页面的内容所需的东西才会加载出来,而不是在应用一开始就加载了。
-
使用Webpack的import ApI
-
使用react-loadable 库实现react异步加载
使用react-loadable这个高阶组件
使用Chrome-DevTool进行性能调优
-
使用ReactDevTool 找到多余渲染
- 使用hightlight-update 可以看到页面中哪一部分进行了更新,
- 在组件绑定store的属性时,页面用到哪些就绑定哪些,否则引起不必要的渲染更新
-
使用Chrome DevTool 定位性能瓶颈
F12 下的 performance 这个功能进行分析
-