React 的 一路记忆

React

整体刷新
组件+单项数据流
在这里插入图片描述

React组件

在这里插入图片描述
1.React组件一般k提供方法f而是某种状态机
2.React组件可以理解q一o纯函数
3.单向数据绑定

受控组件 vs 非受控组件

  • 受控组件:表单元素状态由使用者维护
  • 非受控组件:表单元素状态DOM自身维护
    在这里插入图片描述

理解 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标记可以直接使用属性语法,例如<menu.Item />,

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

来源
在这里插入图片描述

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)

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

函数作为子组件

在这里插入图片描述

// 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

在这里插入图片描述

Redux异步请求

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

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

React Router 路由

特性
  1. 声明式路由定义<Link to="/home">Home</Link>
  2. 动态路由<Route path="/home" component={Home} />
实现方式
  1. URL路径 BrowserRouter
  2. hash路由 HashRouter
  3. 内存路由 MemoryRouter (浏览器URL不发生变化,路由存放到内存中)
API
1.<Link>: 普通链接,不会触发浏览器刷新
2.<NavLink>:类似 Link 但是会添加当前选中状态
3.<Prompt> :满足条件时提示用户是否离开当前页面
4.<Redirect>: 重定向当前页面,例如登录判断
5.<Route> : 路由配置的核心标记f路径匹配时显示对应组件
6. <Switch>:只显示第一个匹配的路由
URL传递参数
  1. <Route path="/topic/:id" .../>
  2. 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. 构建工具的选择

在这里插入图片描述

文件夹结构
  • 按feature组织源文件
  • 组件和样式文件同一级
  • Redux单独文件夹
  • 单元测试保持同样目录结构放在 tests 文件夹
    在这里插入图片描述
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200427174227889.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zODAyMzU1MQ==,size_1,color_FFFFFF,t_70
    index.js内的数据;
    在这里插入图片描述
  • 每个feature 都有自己的专属路由配置
  • 顶层路由使用JSON配置 更易维护和理解
  • 如何解析JSON配置到React Router 语法

页面简单逻辑

列表页面 请求

在这里插入图片描述

处理多个数据请求的时候

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 实现数据缓存 如:搜索框

异步渲染

在这里插入图片描述

时间分片
  1. 虚拟DOM的diff操作 可以分片进行
  2. React新API:unstable_deferredUpdates
  3. Chrome 新API:requestIdleCallback
渲染挂起
  • 使用的是 React中的 Timeout 组件;
  • React新API:unstable_deferUpdate
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值