前言
这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天,前面我们讲了前端页面三剑客(HTML,CSS,Javascript)和网络相关的HTTP,这回我们来讲提升我们开发效率的前端框架React
前置知识
- HTML,JS,CSS基础
- 基础的数据结构/算法知识,如二叉树,深度遍历等
- 会使用浏览器提供的DOM API 来修改DOM,更新UI
React
将从以下几个点去介绍
-
- React 的历史与应用
-
- React 的设计思路
-
- React (hooks) 的写法
-
- React 实现
-
- React 状态管理库
-
- 应用级框架科普
React的历史应用
市面上应用的场景:
-
- 前端应用开发,如 Facebook,Instagram, Netflix 网页版
-
- 移动原生应用开发,如 Instagram,DiscordOculus.
-
- 结合 Electron,进行桌面应用开发。
- ...
React 发展历史
2010年
Facebook 在其 php 生态中,引入了 xhp 框架,首次引入了组合式组件的思想,启发了后来的 React 的设
计。
2011年
Jordan Walke 创造了 FaxJS,
也就是后来的 React 原型:
2012年
在 Facebook 收购 nstagram 后,该 FaxJS 项目在内部得到使用,Jordan Walke 基于 FaxJS 的经验,创造了 React.
2013年
React 正式开源,在 2013 JSConf 上 Jordan Walke 介绍了这款全新的框架
2014年-今天
生态大爆发,各种围绕 React 的新工具/新框架开始涌现:
总结: 一个东西的产生就是解决了某些通点,有前提才有后续的发展。
React 的设计思路
当我点击不同型号或者不同的颜色改变相应的价格
下面就是通过原生JS 去实现的思路指示
React 的设计思路-UI 编程痛点
- 状态更新,UI 不会自动更新,需要手动地调用DOM进行更新。
- 欠缺基本的代码层面的封装和隔离,代码层面没有组件化。
- UI 之间的数据依赖关系,需要手动维护如果依赖链路长,则会遇到 回调地狱"Callback Hell"。
React 的设计思路响应式与转换式
响应式系统
事件 -> 执行既定回调 -> 状态变更
前端UI
事件 -> 执行既定回调 -> 状态变更 -> UI 更新
React 的设计与实现- 响应式编程
原来:
- "状态更新,UI不会自动更新,需要手动地调用 DOM 进行更新。"
- "欠缺基本的代码层面的封装和隔离,代码层面没有组件化。"
- "UI之间的数据依赖关系,需要手动维护,如果依赖链路长,则会遇到'Callback Hell'。"
期望:
状态更新,UI 自动更新前端代码组件化,可复用,可封装状态之间的互相依赖关系,只需声明即可
React 的设计与实现 - 组件化
总结:
- 组件是 组件的组合/原子组件
- 组件内拥有状态,外部不可见
- 父组件可将状态传入组件内部
React 的设计与实现-状态归属问题
当前价格]状态属于谁 ?
正常都会想到是顶栏,型号,橱窗,甚至颜色;
但是你会发现这些状态是各个自我的共性的状态,这就需要一个人统一去管理,那就是Root节点;
但是都有一个Root节点去管理的话,那么当数据量庞大时,也会进行臃肿
所以就有分层管理,自己的父节点管理属于自己的状态,最近祖宗节点管理父节点的状态,以此类推
当前价格 如何改变?
将 onChangeValue() 向下传递(Javascript中, 函数是[一等公民])
思考:
- React 是单向数据流,还是双向数据流
- 如何解决状态不合理上升的问题 ?
- 组件的状态改变后,如何更新 DOM ?
- React 是单项数据,永远是父组件传递子组件,只是子组件通过函数传递去将状态传递给父组件;
- 通过状态管理库进行
- 通过
React实现进行更新DOM
组件设计
- 组件声明了状态和 UI 的映射。
- 组件有
Props/State两种状态 - “组件”可由其他组件拼装而成
映射关系: 我输入几个状态返回几个UI;
State: 内部私有状态 Props: 从外部传递过来的状态
组件拼装: 可通过拆分功能,合并功能去进行组件拼装
组件代码会是什么样子?
- 组件内部拥有私有状态 State。
- 组件接受外部的 Props 状态提供复用性
- 根据当前的 State/Props,返回一个 Ul。
可能长成以下的样子(单纯想象以下,不一定对)
function Component(props) {
// props 是父组件的状态
const { url } = props;
this.text = '点击我'; // 状态
// 返回一个'UI'
return (<div>
<SubComponent props={{ color: 'red' }}><SubComponent>
<img src={url}></img>
<button>text</button>
</div>
}
React 的设计思路-生命周期
并没有完全给出全部的生命周期,只是将突出的生命周期给了出来,挂载
Mounting的时候,更新Updating的时候,卸载Unmounting的时候。
React (hooks) 的写法
import React,{ useState } from 'react';
function Example() {
// Declare a new state varibale, which we'll call 'count'
const [count, setCount] = useState(0)
return (
<div>
<p>You click {count} times</p>
<button onClick={() => setCount(count + 1) }>
Click me
</button>
</div>
);
}
添加了状态管理库等插件,通过设置状态,返回元素并触发事件,更新UI
import React,{ useState } from 'react';
function Example() {
// Declare a new state varibale, which we'll call 'count'
const [count, setCount] = useState(0)
// Similar to componentDidMount and componentDidUpdate;
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
})
return (
<div>
<p>You click {count} times</p>
<button onClick={() => setCount(count + 1) }>
Click me
</button>
</div>
);
}
以上代码就比之前的代码添加了副作用的函数;
副作用: 一个组件内部单纯的只是执行一个语句,就是输入1+2,输出3,那就是一个纯函数;反之组件内部执行一个语句,影响外部;
影响外部的例子:发起一个网络请求,和外部进行一个交互;给本地的session/location storage去存储了一个字段; 上述代码中的改变了document.title,就是改变了外部系统的一个东西
Hook 使用法则
- 不要在循环,条件或嵌套函数中调用Hook.
// wrong
if (x > 1) {
const [x, setX] = useState(0)
}
// Uncaught (in promise) ReferenceError: y is not defined
React 的实现
React 的实现 - Problems
JSX不符合JS标准语法- 返回的
JSX发生改变时,如何更新DOM State/Props更新时要重新触发render函数
React 的实现 - Problem1
compile
const Test = (props) => {
const { url } = props
return (<div className="root">
<img src={url} />
</div>)
}
explore
"use strict"
const Test = props => {
const {
url
} = props;
return /*#__PURE__*/React.createElement('div',{className: 'root'}, /*#__PURE__*/React.createElement('img',{
src: url
}));
}
React 的实现-Problem2
Virtual DOM (虚拟 DOM)
Virtual DOM 是一种用于和真实 DOM 同步,而在JS 内存中维护的一个对象,它具有和DOM 类似的树状结构,并和DOM 可以建立一一对应的关系
官网上介绍:
它赋予了 React 声明式的API:您告诉 React 希望让UI是什么状态,React 就确保 DOM 匹配该状态。这使您可以从属性操作、事件处理和手动 DOM 更新这些在构建应用程序时必要的操作中解放出来
- 声明式编程 指令式编程 响应式编程
- 声明式编程:只需要告诉你需要干什么事件,不会告诉你步骤
- 指令式编程:就是直接告诉你一个个步骤去完成某件事
- 响应式编程:是声明式编程的一个类别,不仅可以声明UI,当某个状态发生改变的时候,就可以更改其他依赖的,它又可以自动更新UI(自己响应自己的过程)
为什么不直接将声明式语法植入浏览器系统里面呢?干嘛还需要引入React ReactDOM 包
浏览器作为应用平台,它自己不能提供高层的东西,如果将高层东西植入进去的,研发的自由度就会降低。
React 的实现 -How to Diff?
更新次数少 <--TradeOff--> 计算速度快
完美的最小 Diff 算法,需要 O(n3)的复杂度
牺牲理论最小 Diff,换取时间,得到了 O(n)复杂度的算法
Heuristic O(n) Algorithm
总结: React编译时和转译后的代码比较,通过改良DIff算法去优化DOM 和 Virtual DOM 的比较的效率,通过状态管理库进行管理数据状态,进行响应数据,更新UI
React 状态管理库
React 状态管理库 - 核心思想
将状态抽离到UI外部进行统一管理
React 状态管理库 - 推荐
将状态抽离到UI外部进行统一管理
React 状态管理库-状态机
当前状态,收到外部事件,迁移到下一个状态
React 状态管理库 - Modern.js/Reduck
字节内部研发的状态管理库:modernjs.dev/
总结: 我们需要将需要复用的状态数据进行放进这个状态管理库,然后通过相关暴露出来的API进行做一些事,理解状态管理库实际在做什么事?运用场景?
应用级框架科普
-
- Next.js 硅谷明星创业公司 Vercel 的 React 开发框架,稳定,开发体验好,支持 Unbundled Dev,SWC等,其同样有 Serverless 一键部署平台帮助开发者快速完成部署。口号是“Let'sMakeWebFaster
-
- Modern.js 字节跳动 Web Infra 团队研发的全栈开发框架内置了很多开箱即用的能力与最佳实践,可以减少很多调研选择工具的时间。
-
- Blitz 无 API 思想的全栈开发框架,开发过程中无需写API 调用与 CRUD 逻辑,适合前后端紧密结合的小团队项目
Next.js 的功能
Modern.js的功能
总结:前人裁树,后人乘凉;市面上已经有对我们开发应用比较好的工具已经非常的智能,快速,选项多了,合理使用这些工具,可让自己的开发效率变得比较高