学习 React

一、基础知识

以组件方式考虑 UI 的构建

理解 React 组件
  • props + state = view
  • 属性和参数决定试图
  • React 组件一般不提供方法,而是某种状态机
受控组件 vs 非受控组件
  • 受控组件,需要传入 onChange 的方法,由外部组件控制其数据
  • 非受控组件,由自身维护数据
原则
  • 创建组件的原则 – 单一职责原则:拆分出的每个组件,职责单一
  • 数据状态管理原则 – DRY 原则:
    • 能计算得到的状态不单独存储
    • 组件尽量无状态,由 props 读取

JSX(本质不是模板引擎,而是语法糖)

在 JSX 中使用表达式
  • JSX 本身就是表达式
  • 在属性中使用表达式:{1+2}
  • 延展属性:{...props}
  • 表达式作为子元素(必须是可 render 的节点,子组件、字符串等)
优点
  • 用声明式的方式创建节点
  • 代码动态创建节点,灵活
  • 无需学习新的模板语言
约定
  • 大写字母开头的是自定义组件
  • 小写字母开头的是原生 DOM 节点
  • 可以直接使用属性语法(此时不需要大写):<menu.item />

React 的生命周期

创建时
  • Render 阶段:constructor -> getDerivedStateFormProps -> render
  • Commit 阶段: 更新 DOM 和 refs -> componentDidMount
更新时
  • Render 阶段:getDerivedStateFormProps -> shouldComponentUpdate -> render
  • Pre-commit 阶段:getSnapshotBeforeUpdate
  • Commit 阶段: 更新 DOM 和 refs -> componentDidUpdate
卸载时
  • Commit 阶段:componentWillUnmount
方法
  • constructor:用于初始化内部状态,唯一可以直接修改 state 的地方
  • getDerivedStateFormProps
    • 当 state 需要从 props 初始化时使用
    • 不推荐使用:维护两者状态一致性会增加复杂度
    • 每次 render 都会调用
    • 典型场景:表单控件获取默认值
  • componentDidMount
    • UI 渲染后调用
    • 只执行一次
  • getSnapshotBeforeUpdate
    • 在页面 render 之前调用,state 已更新
    • 典型场景:获取 render 之前的 DOM 状态
  • componentDidUpdate
    • 每次 UI 更新时调用
  • shouldComponentUpdate
    • 决定 Visual DOM 是否需要重绘
    • 一般由 PureComponent 自动实现

Visual DOM

diff 算法
  • 原理:广度优先遍历
  • 复杂度:O(n)
  • 变化类型:
    • 交换两个子节点
    • 删除节点,新增节点
两个假设
  • DOM 结构是相对稳定的
  • 类型相同的兄弟节点可以被唯一标识(key)

组件复用

高阶组件(HOC)
  • 定义一个函数接收组件作为参数,返回新的组件作为高阶组件:const EnhancedCompenent = higherOrderComponent(WrappedComponent)
  • 高阶组件自身不包含UI的展现,而是给他封装的组件提供一些功能和数据
函数作为子组件

Context API

- 用于使用全局的属性值,根组件是 Provide,子组件 Consumer
```js
const ThemeContext = React.createContext('light') // 初始值为 light
class App extends React.Component {
    render() {
        return {
            <ThemeContext.Provide value="dark">
                <ThemedButton />
            </ThemeContext.Provide>
        }
    }
}

function ThemedButton(props) {
    return {
        <ThemeContext.Consumer>
            // theme 即父组件传入的 value="dark"
            {theme => <Button {..props} theme={theme}} />}
        </ThemeContext.Consumer>
    }
}
```
- Context API 产生的变化会被 React 监听到,然后自动刷新相关的组件,不需要通过 forceUpdate
- 使用场景:主要用于全局性的某个数据

脚手架工具

  • create-react-app
    • 整合了 babel、webpack、test: jest、eslint
    • 特点:入门级,最简策略
  • Rekit
    • 增加了 Redux、React Router、Less/Scss、Feature Oriented Architecture、Dedicated IDE
  • Codesandbox.io
    • 线上项目,在线编辑

二、Redux

JS的状态管理框架

store
```js
const store = createStore(reducer)
store.getState()
store.dispatch(action)
store.subscribe(listener)
```
action
```js
// 描述操作的行为
function addTodo() {
    return {type: ADD_TODO, text: 'Build my app'}
}
store.dispatch(addTodo())
store.subscribe(() => console.log(store.getState()))
```
reducer
```js
// 更新 store 实际上是返回了一个新的 store
function todoApp(state, action) {
    switch (action.type) {
        case ADD_TODO:
            return Object.assign({}, state, {
                todos: state.todos.concat(...)
            })
        default:
            return state
    }
}
```
工具函数
  • combineReducers: 多个 reducer 组合
  • bindActionCreators: 封装 action 函数

在 React 中使用 Redux

```js
import { connect } from 'react-redux'

class TodoList extends Compenent {
    render() {
        const { todos, addTodo } = this.props;
        return {
            ...
        }
    }
}

function mapStateToProps(state) {
    // 需要啥绑定啥,不要返回整个 state,会影响性能
    return {
        todos: state.todos
    };
}

function mapDispatchToProps(dispatch) {
    return bindActionCreators({addTodo}, dispatch);
}

const ConnectedTodoList = connect(mapStateToProps, mapDispatchToProps)(TodoList);

export default class App extend React.Compenent {
    render() {
        return {
            <Provider store={store}>
                <ConnectedTodoList />
            </Provider>
        }
    }
}
```
- connect 的工作原理:高阶组件

异步 action

  • view 事件触发 action,dispatcher 中的 middlewares 截获这个 action,发送异步请求,等待结果后 dispatch
  • 本质上 action 都是同步的,异步 action 不是一种特殊的 action,而是由几个同步 action 和 middleware 组成达到异步的目的
  • middleware 负责截获 action、发出 action

三、React-router

定义

<Router>
    <Link to="/home"></Link>
    <Route path="/home" component={Home} />
</Router>

特性

  • 声明式定义(tag)
  • 动态路由:render 的时候才会被解析

实现方式

  • URL 路径
  • hash 路由(#)
  • 内存路由

API

  • : 普通链接,不会触发浏览器刷新,跟 的效果一样
  • : 类型 Link,但是会添加当前选中状态
  • : 满足条件时提示用户是否离开当前页面
  • : 重定向当前页面
  • : 路径匹配时显示对应组件,默认情况下多个 Route 都匹配时每个组件都会显示
  • : 只显示第一个匹配的路由

传参

  • 通过 URL 传递参数:
  • 获取参数:this.props.match.params

嵌套路由

// App
<Router>
    <Route path="/:id" component={SubRoute} />
</Router>
// SubRoute
<div>
    <Route path="/:id/:subId" component={..} />
</div>

四、工程化

UI 组件库

  • Ant Design
  • Meterial UI
  • Semantic UI

使用 Next.js 创建 React 同构应用

  • 同构应用,由服务端渲染 APP 页面
  • 打包优化
  • 使用 next/link (prefetch 预加载)
  • 使用 next/dynamic 加载组件(lazyload)

Jest 单元测试

  • 不需要浏览器环境,visual DOM 可以在 node.js 环境中运行
  • Redux 状态管理,纯数据层测试
工具
  • Jest 零配置,很好的支持 React
  • JS DOM:浏览器环境的 NodeJS 模拟
  • Enzyme:React 组件渲染和测试
  • nock:模拟 HTTP 请求
  • sinon:函数模拟和调用跟踪
  • Istanbul:单元测试覆盖率
jsdom
const JSDOM = require('jsdom').JSDOM
global.window = new JSDOM('<!DOCTYPE html><div id="react-root"></div>').window
global.document = window.document
global.navigator = window.navigator
global.HTMLElement = window.HTMLElement
Enzyme
  • shallow rendering:只 render 当前组件
  • full rendering:会 render 它的子组件
  • static rendering:会 render 所有内容
Nock
nock('url').get('json').reply(200, responseData)
Sinon
  • 确保 UI 能 call 正确的方法
const props = {
    examples: {},
    actions: {
        add: jest.fn(),
    },
};
const renderedComponent = shallow(
    <Component {...props} />
);
renderedComponent.find('.btn').simulate('click'); // 模拟点击事件
expect(props.actions.add.mock.calls.length).to(1); // 希望 function add() 被 call 1次
Istanbul
npm i -g istanbul
cd /path
istanbul cover test.js
  • 原理:对代码进行埋点,确保每行代码都被执行到

调试工具

ESLint
  • 使用 .eslintrc 配置
  • 使用 airbnb 的 JS 代码风格(预定义)
Prettier
  • 自动格式化,确保代码风格统一
  • VScode 插件
  • 使用 .prettierrc
React Dev Tools
  • 定位组件,查看 props 和 state
  • highlightUpdates,标明哪些组件发生了 update render
Redux Dev Tools
  • Diff 查看每个 action 引起的变化
  • TimeLine 回到每个 store 的状态点
  • Test 自动生成一些测试点

前端项目的理解架构

  • 易于开发
    • 开发工具是否完善
    • 生态圈是否繁荣
    • 社区是否活跃
  • 易于扩展
    • 增加新功能是否容易
    • 新功能是否会显著增加系统复杂度
  • 易于维护
    • 代码是否易于理解
    • 文档是否健全
  • 易于测试
    • 功能的分层是否清晰
    • 副作用少
    • 尽量使用纯函数
  • 易于构建
    • 使用通用技术和架构
    • 构建工具的选择
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值