2
头图

React

参考

React 源码

react 为什么实用 JSX?
萝卜青菜各有所爱。
但是 react 团队认为模板方案不好:

  • 模板分离了技术栈,增加了技术点。
  • 看起来像 html 的 jsx。
  • react 规矩性太强,需要 jsx 来辅助
例如使用 vue 上面是 html 模板,下面是js逻辑从而形成一个组件功能。但是 react 将 html 和运行逻辑都运行在一个 class。

JSX 映射虚拟 DOM 的原理?
JSX 是 creatElement 的语法糖,使用的是 babel 的转义器,实际是通过两个方法完成,一个是 creatElement,一个 reactElement

// 定义一个 react 对象
reactElement(type, key, ref, self, source, ReactCurrentOwner.current, props)

Rreact 数据流管理
数据驱动视图?
UI = render(data)

data 与 state 的区别是什么?
状态(state)与数据(data),组件中的通信就是 data 的通信,react 核心就是对数据的管理

数据流动:

  • 父 -> 子
  • 子 -> 父
  • 兄弟
  • 无关系

单向数据流

MVC的劣势双向数据流。引起混乱。

Action -> Dispatcher -> Store -> View

              |-------<------Action<-|

ACTION 视图层发送的消息

redux 是 js 的状态容器

Action -> Reducer -> Store[state, state]

              |-------<------View--<-|

redux 优缺点
缺点:

  • 使用流程复杂。
  • state 不会随着组件销毁,状态残留。
  • 频繁更新 store 时,会更加卡顿。
  • 不支持 ts。

mobx

原理是利用 es6 proxy 数据劫持,不会想 redux 使用复杂。

React Fiber

超过 16 毫秒后会掉帧。

原来的 React 是通过利用js的执行栈,一直执行到栈空位置。新版的React 维护了自己的执行栈,通过链表的形式,遍历树形结构,优化了执行过程。

参考

虚拟 DOM 和 diff算法

虚拟 DOM
React 渲染 dom 的一种优化手段,其原理是利用 createFragment ,即“创造碎片”。所有的 DOM 操作都将在“碎片”中操作,直到操作完成,再一起渲染。

diff 算法
是 React 更新 dom 元素的一套算法,其核心思想是,比较“节点树”的各个节点,根据比较的结果来决定是否更新该节点。通过其优化手段,将更新树的时间复杂度从O(n^3)变为O(n)。

diff 策略

  • 忽略跨组件的移动操作
  • 同类组件创建相同树,不同类则创建不同类的数(React Component 和 function Component)
  • 兄弟,key

分为3个更新策略 tree diff, component diff, element diff:

tree diff: 当遇到树形结构更新时,仅比较同层节点,起子节点仅有创建和删除。这就意味这,如果存在移动某个树形结构的中间节点,那么原树将直接删除该节点及其子节点,在新树中创建起节点及其子节点。另外,官方不建议存在跨节点的移动操作。

component diff: 当节点更新为不同类型的节点时,成为 dirty component,react 认为更新成不同类型的节点,其结构一定是不一样的(树形结构),因此直接删除该节点后,直接创建。

element diff: 计算兄弟节点直接的移动操作,由原来的将目标位节点删除再创建的更新策略,更改为通过标识key,判断是为原来的节点,另外也获得了是否产生移动操作的判断依据。

由此也得出了3条优化建议:

  • 设置 key
  • 不要将组件更新为不同类型
  • 减少将兄弟组件从末尾移至头部的操作

参考

Redux

待补充

React 优化

常见的优化细节:

  • function 组件代替 class 组件 -> 为什么?参考1 参考2
  • HOC
  • 使用 redux 这类状态管理工具时,部分不公用的state 不用挂载在 model 中 --> 为什么?
  • 首屏优化方案
  • lazy 懒加载组件
  • react-router 的 loading 改善体验
  • ssr (service side rendering)和 csr (client side rendering)
  • pureComponent 和 shouldUpdate 优化更新频率
  • componentDidCatch 探测错误边界
  • 通过 Fragment 减少标签深度
  • render 函数中的变量声明提升到外面,减少 GC

React-Hooks

useState 相当于 class component 中的 setState。


function render() {
  ReactDom.render(<App />, document.getElementById('root'))
}
function myState(initState) {
  let state = initState
  const update = (newState) => {
    state = newState
    render()
    
  }
  return [state, update]
}
function App () {}

useEffect 相当于 class component 中的若干声明周期函数

为什么16.8加入Hook?或者为什么增加 function component?

  • class component 复用状态会很难。HOC 虽然解决了,但是有嵌套地狱的问题。
  • class component 声明周期如果存在更改,其他生命周期可能均需要更改。
  • es6 的 class 不如 function 对初学者更友好
  • this 的指向

官方回答

站在设计者的角度来思考。

SSR

next 主要是用在 React。

npx create-next-app

react-router

根据 history 开发的无刷新路由器。
主要有3个类型:hashHistory、borwerHistory、memoryHistory,常用的是 hashHistory 和 browerHistory。
新版本是 react-router-dom,

import React from "react";
import { Router } from "react-router";
import { createBrowserHistory as createHistory } from "history";
import PropTypes from "prop-types";
import warning from "tiny-warning";

/**
 * The public API for a <Router> that uses HTML5 history.
 */
class BrowserRouter extends React.Component {
  history = createHistory(this.props);

  render() {
    return <Router history={this.history} children={this.props.children} />;
  }
}

export default BrowserRouter;

区别

browerHistory 与 hashHistory 区别

hash 路由是是根据改变 # 后面的锚点来刷新,通过 window.onhashChange 监听变化,从而根据路由处找到相应的文件
history 是通过 html5 的 window.history 这个 web API 来实现

hashHistory的特点:

  • 使用的是 history.location.hash 即 history.location.hash = "abc" (相当于是 localhost/#abc)
  • 只能通过修改 # 后面的地址实现刷新
  • 通过 window.onhashchange 监听 hash 变化,window.addEventListener('onhashchange', hashchangeHandler)
  • 不能想 window.history.go(-1) 这种方式,只能通过字符串改变 url
  • 对搜索引擎不友好,且不好追踪

browerHistory的特点:

  • 使用的是 History 对象,提供 go, back, forward 的方法
  • 相同的 url 会发生更新,并且压入历史记录中
  • 通过 push 和 replace 方法,实际是通过 pushState,replaceState 实现无刷新跳转。其中 pushState 会压入浏览器历史栈,即 History.length + 1,replaceState 则不会。

问题

使用 history 更新路由且正常渲染后,再刷新,会引起404。(待验证)
是由于 history 更新了 url,也就是访问地址,此时向后端服务器请求时,如果没有匹配地址的资源则会 404。
例如 nginx 配置 /test 地址访问。通过 history router 从 /test 跳转至 /say ,刷新时,服务器将找不到资源,可以通过配置父级路径规避掉。

window.location 也会更改 url 但是会引起刷新


donggg
203 声望584 粉丝

> github