关于React的入门知识

  1. 受控组件 V.S. 非受控组件

     <FInput value={x} onChange={fn}/> 受控组件:渲染表单的React组件 还控制着 用户输入表单时变化的操作,每次状态变化都调用onChange,这种组件叫受控组件。表单数据是由 React 组件来管理的。
     <FInput defaultValue={x} ref={input}/> 非受控组件:React赋予组件一个初始值,但是不去控制后续的更新。表单数据将交由 DOM 节点来处理。
    
    
  1. React 有哪些生命周期函数?分别有什么用?(Ajax请求放在哪个阶段?)
    初始化阶段:
    componentWillMount()组件即将被渲染到页面之前触发,此时可以进行开启定时器、向服务器发送请求等操作
    render()组件渲染
    componentDidMount()组件已经被渲染到页面中后触发:此时页面中有了真正的DOM的元素,可以进行DOM相关的操作
    运行中阶段:
    componentWillReceiveProps()组件接收到属性时触发
    shouldComponentUpdate()当组件接收到新属性,或者组件的状态发生改变时触发。组件首次渲染时并不会触发
    componentWillUpdate()组件即将被更新时触发
    render()
    componentDidUpdate()组件被更新完成后触发。页面中产生了新的DOM的元素,可以进行DOM操作
    销毁阶段
    componentWillUnmount() 组件被销毁时触发。
    1. componentDidMount()中请求数据。
  2. React 如何实现组件间通信?
    1. 父子靠props 传函数
    2. 爷孙可以穿两次props
    3. 任意组件用 Redux(也可以自己写一个 eventBus
  3. shouldComponentUpdate 有什么用?
    1. 用于在没有必要更新 UI 的时候返回 false,以提高渲染性能。
    2. 但是不要滥用,这是React提供的一个紧急出口,必要的时候再使用,因为它的维护成本比较高,比如你新加了一个props,但是又忘记在shouldComponentUpdate 写,就会引起bug。
    3. 如何优化?
  4. 虚拟 DOM 是什么?
    1. 要点:虚拟 DOM 就是用来模拟 DOM 的一个对象,这个对象拥有一些重要属性,并且更新 UI 主要就是通过对比(DIFF)旧的虚拟 DOM 树 和新的虚拟 DOM 树的区别完成的。
    2. 参考:http://foio.github.io/virtual-dom/
  5. 什么是高阶组件?
    1. 文档原话——高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。
    2. React-Redux 里 connect 就是一个高阶组件,比如 connect(mapState)(MyComponent) 接受组件 MyComponent,返回一个具有状态的新 MyComponent 组件。
  6. React diff 的原理是什么?
    首先,React diff遵循三个策略:1. Web UI 中,DOM节点跨层级的移动操作特别少,可以忽略不计。2.拥有相同类的两个组件将生成相似的树形结构,拥有不同类的两个组件将生成不同的树形结构。3. 对于同一层级的一组子节点,可以通过唯一id来区分它们。基于以上三个策略,React分别对tree diff、component diff、element diff进行了算法优化。
  • tree diff

    React对树的算法进行了优化,对树进行分层比较,两棵树只比较同一层级的节点。DOM节点跨层级的操作少到忽略不计,针对这一点,React通过updateDepth对Virtual DOM树进行层级控制,只会对相同颜色方块内的DOM节点进行比较,当发现节点不存在,则会删除该节点以及所有子节点,不会再进行进一步比较,所以只要对DOM树进行一次遍历,就能完成整个DOM树的比较。
    tree diff.jpg
  • component diff
    如果是同一类型的组件,则按照原策略进行Virtual DOM Tree的比较。如果不是呢,则讲组件判断为dirty component,从而替换整个组件下的所有子节点。
    对于同一类型的组件,React允许用户使用 shouldComponentUpdate()来判断该组件是否需要diff。
    如下图,当component D 改变为 component G时,即使两个组件结构相似,一旦React判断两者为不同类型的组件,则不会进行比较,而是直接删除component D,重新创建component G以及所有子节点。

    component diff.jpg

  • element diff
    当节点处于同一层级时,React diff提供了三种节点操作
    INSERT_MARKUP(插入):新的component类型不在老集合里,即是全新的节点,则需要对新节点执行插入操作。
    MOVE_EXISTING(移动):新的component类型在老集合里,且element是可更新的类型,generateComponentChildren 已调用receiveComponent,这种情况下prevChild=nextChild,就需要执行移动操作,可以复用以前的DOM节点。
    REMOVE_NODE(删除):老component类型在新集合里面也有,但对应的element不同则不能直接进行复用和更新,需要执行删除操作。老component类型在新集合里没有,也要执行删除操作。
    开发者对同一层级的子节点,可以添加唯一索引进行区分,这样在diff时,涉及到只是位置变化的,可以只移动元素,避免删除创建等重复的操作。

  1. Redux 是什么?
    1. ReduxJavaScript 状态管理工具,提供可预测化的状态管理。
    2. Action:是把数据从应用传到 Store 的有效载荷。它是 Store 数据的唯一来源。改变 State 的唯一办法,就是使用 Action。
import { ADD_TODO } from "../actionTypes";

export const addTodo = (payload:any) => {
    return {
        type: ADD_TODO,
        payload
    }
}

Reducers: 指定了应用状态的变化如何响应 actions并发送到store 的,记住 actions只是描述了有事情发生了这一事实,并没有描述应用如何更新 state
Reducer 是一个函数,它接受 Action 和当前State 作为参数,返回一个新的 State
我们还可以将拆分后的Reducer 放到不同的文件中, 以保持其独立性并用于专门处理不同的数据域。然后使用combineReducers将多个Reducer合并成一个,输出成一个大的对象。

import { ADD_TODO } from "../actionTypes";

export default function (state = [],action:any) {
    switch (action.type){
        case ADD_TODO:
            return [...state,action.payload]
        default:
            return state
    }
}

import { ADD_TOMATO } from "../actionTypes";

export default function (state = [], action: any) {
    switch(action.type) {
        case ADD_TOMATO:
            return [...state, action.payload]
        default:
            return state
    }
}
//使用combineReducers
import { combineReducers } from "redux";
import todos from './todos'
import tomatoes from './tomatoes'

export default combineReducers({ todos, tomatoes });

Store: 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 StoreRedux提供createStore这个函数,接受reducers,用来生成 Store

import { createStore } from "redux";
import rootReducer from "./reducers"; 

const store = createStore(rootReducer)

export default store

Store有以下职责:

  • 维持应用的 state
  • 提供 getState() 方法返回应用当前的 state 树。它与 store 的最后一个 reducer 返回值相同。
  • 提供 dispatch(action) 方法分发action。这是触发 state 变化的惟一途径。
  • 通过 subscribe(listener) 注册监听器;
  • 通过 subscribe(listener) 返回的函数注销监听器。

connect:该API连接React组件与 Redux store,连接操作不会改变原来的组件类。反而返回一个新的已与 Redux store 连接的组件类。connect可以接受参数,将参数注入到组件当中

const mapStateToProps = (state: { todos: any; }, ownProps: any) => ({//注入props
    ...ownProps
})

const mapDispatchToProps = {//注入属性
    editTodo, 
    updateTodo
}

export default connect(mapStateToProps, mapDispatchToProps)(todoItem)

<Provider store> 使组件层级中的 connect()方法都能够获得 Redux store。正常情况下,你的根组件应该嵌套在 <Provider> 中才能使用 connect() 方法。

ReactDOM.render(
  <Provider store={store}>
    <MyRootComponent />
  </Provider>,
  rootEl
)
  1. connect 的原理是什么?
    react-redux 库提供的一个 API,connect的作用是让你把组件和store连接起来,产生一个新的组件(connect 是高阶组件)。

1.首先connect之所以会成功,是因为Provider组件:
- 在原应用组件上包裹一层,使原来整个应用成为Provider的子组件
- 接收Reduxstore作为props,通过context对象传递给子孙组件上的connect

2.connect做了什么:它真正连接ReduxReact,它包在我们的容器组件的外一层,它接收上面Provider 提供的store里面的statedispatch,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。

3.主逻辑源码

export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {
  return function wrapWithConnect(WrappedComponent) {
    class Connect extends Component {
      constructor(props, context) {
        // 从祖先Component处获得store
        this.store = props.store || context.store
        this.stateProps = computeStateProps(this.store, props)
        this.dispatchProps = computeDispatchProps(this.store, props)
        this.state = { storeState: null }
        // 对stateProps、dispatchProps、parentProps进行合并
        this.updateState()
      }
      shouldComponentUpdate(nextProps, nextState) {
        // 进行判断,当数据发生改变时,Component重新渲染
        if (propsChanged || mapStateProducedChange || dispatchPropsChanged) {
          this.updateState(nextProps)
            return true
          }
        }
        componentDidMount() {
          // 改变Component的state
          this.store.subscribe(() = {
            this.setState({
              storeState: this.store.getState()
            })
          })
        }
        render() {
          // 生成包裹组件Connect
          return (
            <WrappedComponent {...this.nextState} />
          )
        }
      }
      Connect.contextTypes = {
        store: storeShape
      }
      return Connect;
    }
  }

connect是一个高阶函数,首先传入mapStateToPropsmapDispatchToProps等参数,然后返回一个生产Component的函数(wrapWithConnect),然后再将真正的Component作为参数传入wrapWithConnect,这样就生产出一个经过包裹的Connect组件,该组件具有如下特点:

  • 通过props.store获取祖先Componentstore
  • props包括statePropsdispatchPropsparentProps,合并在一起得到nextState,作为props传给真正的Component
  • componentDidMount时,添加事件this.store.subscribe(this.handleChange),实现页面交互
  • shouldComponentUpdate时判断是否有避免进行渲染,提升页面性能,并得到nextState
  • componentWillUnmount时移除注册的事件this.handleChange
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容

  • [toc] REACT react :1.用来构建用户界面的 JAVASCRIPT 库2.react 专注于视图层...
    拨开云雾0521阅读 1,429评论 0 1
  • 深入JSX date:20170412笔记原文其实JSX是React.createElement(componen...
    gaoer1938阅读 8,048评论 2 35
  • React 中 keys 的作用是什么? Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除...
    e2ee31170666阅读 1,292评论 1 3
  • 今天的React题没有太多的故事…… 半个月前出了248个Vue的知识点,受到很多朋友的关注,都强烈要求再出多些R...
    浪子神剑阅读 10,067评论 6 106
  • 项目地址: 项目地地址参考地址: bilibili 1.火热的0配置的打包工具parcel 地址: parcel官...
    第十人i阅读 292评论 0 0