React 是 Facebook 在2013年开源的用于构建用户界面的 JavaScript 库。
1. React 独立架构
React 是 MVC 中薄薄的一层 V,把数据变成 DOM 显示出来,它只关注表现层。
自带的 View 和 Controller 库,在实现应用时,不需要任何其他的库也可以自运行。
React 独立架构的核心是单向数据流,模型图如下:
其实 React 还有一个很重要的设计思想 => 声明式表达 + 数据驱动DOM
只需要定义数据和 DOM 的对应关系,在数据变化的时候,DOM 会自动变化 => React是个响应式框架
拓展:Flux 架构
React 中有很多地方都用到了 Flux 架构,比如 React Hooks,Redux 也是用的 Flux 架构。
Flux 是由 Facebook 在2014年开源的一款用于构建用户界面的应用程序架构。它不同于 MVC/MVVM,是基于 dispatcher 的前端应用架构模式,如果用 MVC 的命名习惯,它应该叫 ADSV(Action Dispatcher Store View)。流程图如下👇👇👇
Flux 应用是由三部分组成的:
- dispatcher(分发事件)
- store(保存数据、响应事件、更新数据 )
- view(订阅store中的数据,渲染页面 => React组件)
Flux 没有一个职责明确的 controller,但其实是存在一个 controller-view 的角色,将 store 和 view 进行绑定,controller-view 是整个应用的最顶层,不涉及任何业务逻辑。
从流程图上可以看到数据从 action 到 dispatcher,再到 store,最终到 view,是单向不可逆的,不会像MVC那样有交错的关系。这也是它的一个核心思想 => 数据和逻辑永远单向流动。这个单向数据流是在独立架构上的延伸。
🌸 Flux 思想最著名的实现就是 Redux。
2. 函数式编程
React 在设计时带有函数式编程的基因,因为 React 组件本身就是纯函数。React 的 createElement 方法保证了组件是纯净的,即传入指定 props 得到一定的 Virtual DOM ,整个过程都是可预测的。 ——《深入React技术栈》
React 也是深入贯彻函数式编程的思想,那么什么是函数式编程呢?
函数式编程有三大特性:
- 函数是一等公民,函数可以出现在任何地方;
- 函数式编程关心的是数据映射,不会改变原数据,根据原数据,生成新的数据;
- 函数式编程是抽象的,方便复用。
React 中函数式编程的体现:
- 在 React 中,强调一个组件不能去修改传入的 prop 值,也是遵循 Immutable 的原则。
- 在 React 中,组件的 render 函数应该是一个纯函数,来保证组件渲染的结果只和 state/props 有关系,遵循 这个公式。
在 Redux 中,更是强调 Immutable 的作用,每个 reducer 不能够修改 state,只能返回一个新的state,这也是函数式编程的要求。
这里补充常用编程范式的特点,根据个人理解总结,欢迎指正!
3. 组件开发模式
react 的组件,从概念上类似于 JavaScript 函数,组件接受任意入参(即 “props”),并返回用于描述页面展示内容的 React 元素。
react 的组件分为函数组件(Function Components)、类组件(Class Components)
// 函数组件
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// 类组件 有生命周期
class Welcome extends React.Component {
constructor(props){
super(props); // 将父类的this对象继承给子类 (MDN参考)
}
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
下面这里例子就可以在页面上渲染 “Hello, Sara” 在 CodePen 上尝试
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
🌸 render()
class 组件中唯一必须实现的方法,用于渲染 dom, render()方法必须返回 reactDOM
注意: 不要在 render 里面 setState,否则会触发死循环导致内存崩溃
React 生命周期
4. JSX 语法
React 为了方便 View 层组件开发模式,承载了构建 HTML 结构化页面的职责,我们可以通过 JSX 处理数据和 DOM 之间的关系,而不需要直接操作 DOM。
// 基本使用
const element = <h1>Hello, world!</h1>;
// 通过引号,给属性值赋值
const element = <div className="title" desc="标题"></div>;
// 通过大括号,在属性值中插入一个 JavaScript 表达式
const element = <img src={user.avatarUrl} />;
// JSX + 判断语句
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
// JSX + 循环表达式
function getGreeting(user) {
arr.map((item,index)=>{
return <li key={index}>{item}</li>
})
}
之所以 JSX 能支持上述写法,是因为在编译阶段 Babel 会把 JSX 转译成 React.createElement() 函数调用。
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
// 上面 JSX 表达式,会被转译成下面这样
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
从这个例子中也可以看出,虽然这些 h1 div img 等标签的名称与 HTML 标签一样,但其实在 JSX 中并没有直接使用 HTML 标签,只是形似的字符串而已(以下称“虚拟元素”)。JSX 操纵虚拟元素来模拟把 HTML 融入 JavaScript 代码中,然后在编译阶段,在转成纯 JavaScript 后由浏览器执行。
总结: JSX 本质
JSX 元素是调用 React.createElement(component, props, ...children)
React 不强制使用 JSX,也可以直接调用 React.createElement
总结: JSX 特点
- JSX 是一种 JS 扩展的表达式;
- JSX 是带有逻辑的标记语法,有别于 HTML 模版;
- JSX 支持样式、逻辑表达式和事件。
🌸 JSX 防止注入攻击
React DOM 在渲染所有输入内容之前,默认会进行转义。所有的内容在渲染之前都被转换成了字符串,它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。这样可以有效地防止 XSS(cross-site-scripting, 跨站脚本)攻击。
5. 虚拟DOM(Virtual DOM)
在传统页面的开发模式中,每次需要更新页面时,都要手动操作 DOM
然而我们都知道 DOM 操作既复杂(难以维护)又昂贵(性能消耗极大),所以之前会格外注意重排与重绘来保证性能。
React 中,在基建层会统一把我们的编写的代码先编译成 JavaScript 对象树,存在内存里,然后再进一步映射成真实DOM。每次数据更新后,重新计算 Virtual DOM,并和上一次生成的 Virtual DOM 做对比(diff),对发生变化的部分做批量更新,减少低效操作。
React 也提供了直观的 shouldComponentUpdate 生命周期回调,来减少数据变化后不必要的 Virtual DOM 对比过程,以保证性能。 shouldComponentUpdate(){return false}
使用虚拟DOM最大的好处其实还在于方便和其他平台集成,比如 react-native 是基于 Virtual DOM 渲染出原生控件,因为 React 组件可以映射为对应的原生控件。在输出的时候,是输出 Web DOM,还是 Android 控件,还是 iOS 控件,就由平台本身决定了。因此,react-native 有一个口号——Learn Once,Write Anywhere。
总结:虚拟 DOM 优势
- 操作 DOM 前对数据进行对比,只有数据变化的时候才去操作 DOM;
- 会整合 DOM 操作,可以减少 DOM 操作,提升性能;
- 便于和其他平台集成,虚拟 DOM 可以映射成 Web DOM,Android 控件,IOS 控件;