React 的 一路记忆

React

整体刷新
组件+单项数据流
React 的 一路记忆_第1张图片

React组件

React 的 一路记忆_第2张图片
1.React组件一般k提供方法f而是某种状态机
2.React组件可以理解q一o纯函数
3.单向数据绑定

受控组件 vs 非受控组件

  • 受控组件:表单元素状态由使用者维护
  • 非受控组件:表单元素状态DOM自身维护
    React 的 一路记忆_第3张图片

理解 JSX:是模板语言只是一种语法糖

jsx:在js代码中直接写HTML标记,其本质:动态创建组建的语法糖React.createElement

  • 在jsx中使用表达式
    1. jsx 本身也是表达式
const e=<h1>eee</h1>;
3. 在属性中使用表达式
<View  foo={1+2+3}  />;
5. 扩展属性
  const obj={a:'222',c:1111}
  const e=<View  {...obj}  />;
6. 表达式作为子元素
const e=<h1>{props.msg}</h1>;

约定:自定义组件以大写字母开头

1.React 认q小写的 tag 是原生 DOM 节点,如 div
2.大写字母开头为自定义组件
3.JSX标记可以直接使用属性语法,例如,

React 组件的生命周期及其使用场景

来源
React 的 一路记忆_第4张图片

constructor

  1. 用于初始化 内部状态,很少使用
  2. 唯一可以直接修改state的地方

getDerivedStateFromProps()

  1. 当state需要从props初始化时使用
  2. 尽量不要使用:维护两者状态一致性会增加复杂度
  3. 每次render都会调用
  4. 典型场景:表单控件获取默认值

componentDidMount()

  1. UI渲染完成后调用
  2. 只执行一次
  3. 典型场景:获取外部资源

componentWillUnmount()

  1. 组件移除时被调用
  2. 典型场景:资源释放

getSnapshotBeforeUpdate()

  1. 在页面render之前调用,state已更新
  2. 典型场景:获取render之前的DOM状态

componentDidUpdate()

  1. 每次UI更新时被调用
  2. 典型场景:页面需要根据props变化重新获取数据

shouldComponentUpdate

  1. 决定Virtual DOM是否重绘
  2. 一般可以由PureComponent自动实现
  3. 典型场景:性能优化

Virtual DOM原理和Key的属性作用

如何工作的,没有进行优化前的复杂度是:O(n^3)

  1. 广度优先的分层比较
  2. 节点类型变化,直接对比节点,如果多出 直接append,或者删除节点。
  3. 节点跨层移动:直接对比,如果和以前进行对比 ,不存在就直接删除,如果多出 就直接创建一个新的节点,不会跨层移动节点。
    线上演示

虚拟DOM的两个假设

  1. 组件的DOM结构是相对稳定的(很少出现跨层移动的场景)
  2. 类型相同的兄弟节点可以被唯一标识(节点的位置发生变化的时候,这里就是那个key)

  1. 组件的DOM结构是相对稳定的
  2. 类型相同的兄弟节点可以被唯一标识
  3. 算法复杂度为 O(n)

组件复用的另外2种形式:高阶组件和函数作为子组件

高阶组件(HOC)

高阶组件接受组件作为参数,返回新的组件。

函数作为子组件

React 的 一路记忆_第5张图片

// value 是 上面传过来的name;children是 返回的整个函数
{this.props.value && this.props.children(this.props.value)}

理解 Context API 的使用场景

函数作为子组件的设计模式

  1. 使用React.createContext创建一个新的context
    const LocaleContext = React.createContext(enStrings);
  2. 提供Provider组件
 <LocaleContext.Provider value={this.state.locale}>
        <button onClick={this.toggleLocale}>
          切换语言
        </button>
        {this.props.children}
      </LocaleContext.Provider>
  1. Provider组件下,使用Consumer组件;否则的话Consumer组件 就使用的默认初始值
<LocaleContext.Consumer>
        {locale => (
          <div>
            <button>{locale.cancel}</button>
            &nbsp;<button>{locale.submit}</button>
          </div>
        )}
      </LocaleContext.Consumer>

以下是全部代码

import React from "react";

const enStrings = {
  submit: "Submit",
  cancel: "Cancel"
};

const cnStrings = {
  submit: "提交",
  cancel: "取消"
};
const LocaleContext = React.createContext(enStrings);

class LocaleProvider extends React.Component {
  state = { locale: cnStrings };
  toggleLocale = () => {
    const locale =
      this.state.locale === enStrings
        ? cnStrings
        : enStrings;
    this.setState({ locale });
  };
  render() {
    return (
      <LocaleContext.Provider value={this.state.locale}>
        <button onClick={this.toggleLocale}>
          切换语言
        </button>
        {this.props.children}
      </LocaleContext.Provider>
    );
  }
}

class LocaledButtons extends React.Component {
  render() {
    return (
      <LocaleContext.Consumer>
        {locale => (
          <div>
            <button>{locale.cancel}</button>
            &nbsp;<button>{locale.submit}</button>
          </div>
        )}
      </LocaleContext.Consumer>
    );
  }
}

export default () => (
  <div>
    <LocaleProvider>
      <div>
        <br />
        <LocaledButtons />
      </div>
    </LocaleProvider>
    <LocaledButtons />
  </div>
);

Redux状态管理框架

  • 单项数据流
  • 唯一状态来源
  • 纯函数更新store,通过action触发
Store,action,reducer

React 的 一路记忆_第6张图片

Redux异步请求

这里多了一个Middlewares中间件,这里举个例子,使用redux-thunk;它接收到actions的时候,不是直接访问dispatch,而是去访问api,如果请求的api返回的是一个成功,他就会返回一个成功的action,失败的话 就会返回一个失败的action。也就是Middlewares进行先截获,预处理,再返回新的action。异步action是几个同步action的使用,通过中间件进行实现的。
React 的 一路记忆_第7张图片

Redux运行基础,不可变数据

  1. 性能优化(产生一个新的数据对象,只比较数据引用的地址是否相等,更换新的数据对象)
  2. 易于调试和跟踪(记录了前一个状态和后一个状态)
  3. 易于推测(action 推测 前后的状态)
操作不可变数据
  1. {…},Object.assign
  2. immutability-helper //深层次的节点
  3. immer // 建立一个代理,但是数据比较多的时候 性能会不太好

React Router 路由

特性
  1. 声明式路由定义Home
  2. 动态路由
实现方式
  1. URL路径 BrowserRouter
  2. hash路由 HashRouter
  3. 内存路由 MemoryRouter (浏览器URL不发生变化,路由存放到内存中)
API
1.: 普通链接,不会触发浏览器刷新
2.:类似 Link 但是会添加当前选中状态
3. :满足条件时提示用户是否离开当前页面
4.: 重定向当前页面,例如登录判断
5. : 路由配置的核心标记f路径匹配时显示对应组件
6. :只显示第一个匹配的路由
URL传递参数
  1. this.props.match.params
嵌套路由
  1. 每个React组件都可以是路由容器
  2. React Router 的声明式语法可以方便的定义嵌套路由

UI组件库

  1. Antd Design (比较齐全)
  2. Material UI (没有tree)
  3. Semantic UI (没有tree)

同构应用

简单介绍
使用 next.js开发。
https://www.nextjs.cn/

  1. 页面就是 pages 目录下的一个组件
  2. static 目录映射静态文件
  3. page具有特殊静态方法 getInitialProps
  4. 动态加载资源 dynamic

单元测试

一些工具

1.Jest:Facebook开源的 js单元测试框架
2.JS DOM 浏览器环境的 NodeJS 模拟
3.Enzyme:React 组件渲染和测试
4.nock模拟 http 请求
8. sinon: 函数模拟和调用跟踪
6.istanbul:单元测试覆盖率

常用开发调试工具

  1. ESLint (告诉你发生的错误)
  2. Prettier (保存自动格式化)
  3. React DevTools
  4. Redux DevTools

架构

前端项目的理想架构:
可维护,可扩展,可测试,易开发,易构建
易于开发

  1. 开发工具是否完善
  2. 生态圈是否繁荣
  3. 社区是否活跃

易于扩展

  1. 增加新功能是否容易
  2. 新功能是否显著增加系统复杂度

易于维护
1.代码是否容易理解
3. 文档是否健全

易于测试
4. 功能的分层是否清晰
5. 副作用少
6. 尽量使用纯函数

易于构建
7. 使用通用技术和架构
8. 构建工具的选择

React 的 一路记忆_第8张图片

文件夹结构

  • 按feature组织源文件
  • 组件和样式文件同一级
  • Redux单独文件夹
  • 单元测试保持同样目录结构放在 tests 文件夹
    React 的 一路记忆_第9张图片
    React 的 一路记忆_第10张图片
    index.js内的数据;
    React 的 一路记忆_第11张图片
  • 每个feature 都有自己的专属路由配置
  • 顶层路由使用JSON配置 更易维护和理解
  • 如何解析JSON配置到React Router 语法

页面简单逻辑

列表页面 请求

React 的 一路记忆_第12张图片

处理多个数据请求的时候

1.请求之间无依赖 关系,可以并发进行
2.请求有依赖 需要依次进行
3.请求完成之前,页面显示 Loading 状态

向导页面需要考虑的技术要点

1.使用 URL 进行导航的好处
2.表单内容存放的位置
3.页面状态如何切换

React Portals

1.写弹窗使用的api
2.可以将虚拟 DOM 映射到任何真实 DOM 节点
3.解决了漂浮层的问题,比如Dialog,Tooltip 等

  renderDialog() {
    return (
      <div className="portal-sample">
        <div>这是一个对话框!</div>
        <br />
        <Button
          type="primary"
          onClick={() => this.setState({ visible: false })}
        >
          关闭对话框
        </Button>
      </div>
    );
  }
  render() {
    if (!this.state.visible) return this.renderButton();
    return ReactDOM.createPortal(
      this.renderDialog(),
      document.getElementById("dialog-container"),
    );
  }
}

一个拖放组件

import React, { Component } from "react";

require("./DndSample.css");

const list = [];
for (let i = 0; i < 10; i++) {
  list.push(`Item ${i + 1}`);
}

const move = (arr, startIndex, toIndex) => {
  arr = arr.slice();
  arr.splice(toIndex, 0, arr.splice(startIndex, 1)[0]);
  return arr;
};

const lineHeight = 42;
class DndSample extends Component {
  constructor(props) {
    super(props);
    this.state.list = list;
  }
  state = {
    dragging: false,
    draggingIndex: -1,
    startPageY: 0,
    offsetPageY: 0,
  };

  handleMounseDown = (evt, index) => {
    this.setState({
      dragging: true,
      startPageY: evt.pageY,
      currentPageY: evt.pageY,
      draggingIndex: index,
    });
  };
  handleMouseUp = () => {
    this.setState({ dragging: false, startPageY: 0, draggingIndex: -1 });
  };
  handleMouseMove = evt => {
    let offset = evt.pageY - this.state.startPageY;
    const draggingIndex = this.state.draggingIndex;
    if (offset > lineHeight && draggingIndex < this.state.list.length - 1) {
      // move down
      offset -= lineHeight;
      this.setState({
        list: move(this.state.list, draggingIndex, draggingIndex + 1),
        draggingIndex: draggingIndex + 1,
        startPageY: this.state.startPageY + lineHeight,
      });
    } else if (offset < -lineHeight && draggingIndex > 0) {
      // move up
      offset += lineHeight;
      this.setState({
        list: move(this.state.list, draggingIndex, draggingIndex - 1),
        draggingIndex: draggingIndex - 1,
        startPageY: this.state.startPageY - lineHeight,
      });
    }
    this.setState({ offsetPageY: offset });
  };

  getDraggingStyle(index) {
    if (index !== this.state.draggingIndex) return {};
    return {
      backgroundColor: "#eee",
      transform: `translate(10px, ${this.state.offsetPageY}px)`,
      opacity: 0.5,
    };
  }

  render() {
    return (
      <div className="dnd-sample">
        <ul>
          {this.state.list.map((text, i) => (
            <li
              key={text}
              onMouseDown={evt => this.handleMounseDown(evt, i)}
              style={this.getDraggingStyle(i)}
            >
              {text}
            </li>
          ))}
        </ul>
        {this.state.dragging && (
          <div
            className="dnd-sample-mask"
            onMouseMove={this.handleMouseMove}
            onMouseUp={this.handleMouseUp}
          />
        )}
      </div>
    );
  }
}

export default DndSample;

//css

.dnd-sample ul {
  display: inline-block;
  margin: 0;
  padding: 0;
  background-color: #eee;
}

.dnd-sample li {
  cursor: default;
  list-style: none;
  border-bottom: 1px solid #ddd;
  padding: 10px;
  margin: 0;
  width: 300px;
  background-color: #fff;
}

.dnd-sample-mask {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0);
}

性能场景

  1. react-loadable 实现React 异步加载,代码的分包。
  2. reselect 实现数据缓存 如:搜索框

异步渲染

React 的 一路记忆_第13张图片

时间分片

  1. 虚拟DOM的diff操作 可以分片进行
  2. React新API:unstable_deferredUpdates
  3. Chrome 新API:requestIdleCallback
渲染挂起
  • 使用的是 React中的 Timeout 组件;
  • React新API:unstable_deferUpdate

你可能感兴趣的:(随手记论文,react)