react

846 查看

react

Why React?

React is a JavaScript library for creating user interfaces by Facebook and Instagram. Many people choose to think of React as the V in MVC.We built React to solve one problem: building large applications with data that changes over time.

  • Simple

  • Declarative

  • Build Composable Components

  • In fact, with React the only thing you do is build components.

react的概念

组件

React 应用都是构建在组件之上,各个组件维护自己的状态和 UI,当状态变更,自动重新渲染整个组件

import React, { Component } from 'react';
import { render } from 'react-dom';

class HelloMessage extends Component {
  render() {
    return <div>Hello {this.props.name}</div>;
  }
}

propsstate

props 就是组件的属性,由外部通过 JSX 属性传入设置

state 是组件的当前状态,可以把组件简单看成一个“状态机”,根据状态 state 呈现不同的 UI 展示。一旦状态(数据)更改,组件就会自动调用 render 重新渲染 UI,这个更改的动作会通过this.setState 方法来触发。原则:让组件尽可能地少状态。

组件就是通过这两个属性的值在 render 方法里面生成这个组件对应的 HTML 结构

相关函数

1、装载组件触发

componentWillMount

只会在装载之前调用一次,在 render 之前调用,你可以在这个方法里面调用 setState 改变状态,并且不会导致额外调用一次 render

componentDidMount

只会在装载完成之后调用一次,在 render 之后调用,从这里开始可以通过ReactDOM.findDOMNode(this)获取到组件的 DOM 节点

2、更新组件触发

componentWillReceiveProps

shouldComponentUpdate

componentWillUpdate

componentDidUpdate

3、卸载组件触发

componentWillUnmount

组合组件

使用组件的目的就是通过构建模块化的组件,相互组合组件最后组装成一个复杂的应用。在 React 组件中要包含其他组件作为子组件,只需要把组件当作一个 DOM 元素引入就可以,通过 props 传递值。

JSX

将 HTML 直接嵌入了 JS 代码里面

<a href="http://facebook.github.io/react/">Hello!</a>

React.createElement('a', {href: 'http://facebook.github.io/react/'}, 'Hello!')

通过 React.createElement 来构造组件的 DOM 树。第一个参数是标签名,第二个参数是属性对象,第三个参数是子元素。

利用 JSX 编写 DOM 结构,可以用原生的 HTML 标签,也可以直接像普通标签一样引用 React 组件。这两者约定通过大小写来区分,小写的字符串是 HTML 标签,大写开头的变量是 React 组件。HTML 里的 class 在 JSX 里要写成 className,因为 class 在 JS 里是保留关键字。同理某些属性比如 for 要写成 htmlFor。

Virtual DOM

当组件状态 state 有更改的时候,React 会自动调用组件的 render 方法重新渲染整个组件的 UI。组件 DOM 结构就是映射到这个 Virtual DOM 上,React 在这个 Virtual DOM 上实现了一个 diff 算法,当要重新渲染组件的时候,会通过 diff 寻找到要变更的 DOM 节点,再把这个修改更新到浏览器实际的 DOM 节点上,所以实际上不是真的渲染整个 DOM 树。这个 Virtual DOM 是一个纯粹的 JS 数据结构,所以性能会比原生 DOM 快很多。

Data Flow

单向数据绑定,Data Flow 只是一种应用架构的方式,比如数据如何存放,如何更改数据,如何通知数据更改等等

两种实践:官方的 Flux 和 优雅的 Redux

Redux 的基础概念

1、整个应用只有唯一一个可信数据源,也就是只有一个 Store

2、State 只能通过触发 Action 来更改

3、State 的更改必须写成纯函数,也就是每次更改总是返回一个新的 State,在 Redux 里这种函数称为 Reducer

Action 很简单,就是一个单纯的包含 { type, payload } 的对象,type 是一个常量用来标示动作类型,payload 是这个动作携带的数据。

action = {
  type: 'ADD_TODO',
  text: 'Build my first Redux app'
}

Action Creators 是一个pure function,它最后会返回一个 action 对象

function addTodo(text) {
  return {
    type: 'ADD_TODO',
    text
  }
}

Reducer 用来处理 Action 触发的对状态树的更改,接受 oldState 和 action 两个参数,返回一个新的 newState

state:(oldState, action) => newState

const initialState = {
  a: 'a',
  b: 'b'
};

function someApp(state = initialState, action) {
  switch (action.type) {
    case 'CHANGE_A':
      return { ...state, a: 'Modified a' };
    case 'CHANGE_B':
      return { ...state, b: action.payload };
    default:
      return state
  }
}

Redux 里面只有一个 Store,对应一个 State 状态,所以整个 State 对象就是由一个 reducer 函数管理,但是如果所有的状态更改逻辑都放在这一个 reducer 里面,显然会变得越来越巨大,越来越难以维护。得益于纯函数的实现,我们只需要稍微变通一下,让状态树上的每个字段都有一个 reducer 函数来管理就可以拆分成很小的 reducer 了

Redux 提供了一个工具函数 combineReducers 来简化这种 reducer 合并

import { combineReducers } from 'redux';

const someApp = combineReducers({
  a: reducerA,
  b: reducerB
});

将 root reducer 函数传递给 createStore 方法即可

import { createStore } from 'redux';
import someApp from './reducers';
let store = createStore(someApp);

其实就构成了一个“单向数据流”

store.dispatch(action) -> reducer(state, action) -> store.getState()

Provider作为一个容器组件,用来接受 Store,并且让 Store 对子组件可用

import { render } from 'react-dom';
import { Provider } from 'react-redux';
import App from './app';

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

Connect 这个方法调用会返回另一个函数,这个返回的函数来接受一个组件类作为参数,最后才返回一个和 Redux store 关联起来的新组件

connect([mapStateToProps], mapDispatchToProps], [mergeProps], [options])

第一个可选参数是一个函数,只有指定了这个参数,这个关联(connected)组件才会监听 Redux Store 的更新,每次更新都会调用mapStateToProps 这个函数,返回一个字面量对象将会合并到组件的 props 属性。

第二个可选参数是一个函数,用来指定如何传递dispatch 给组件,在这个函数里面直接 dispatch action creator,返回一个字面量对象将会合并到组件的 props 属性,这样关联组件可以直接通过 props 调用到 action

import React, { Component } from 'react';
import someActionCreator from './actions/someAction';
import * as actionCreators from './actions/otherAction';

function mapStateToProps(state) {
  return {
    propName: state.propName
  };
}

function mapDispatchProps(dispatch) {
  return {
    someAction: (arg) => dispatch(someActionCreator(arg)),
    otherActions: bindActionCreators(actionCreators, dispatch)
  };
}

class App extends Component {
  render() {
    // `mapStateToProps` 和 `mapDispatchProps` 返回的字段都是 `props`
    const { propName, someAction, otherActions } = this.props;
    return (
      <div onClick={someAction.bind(this, 'arg')}>
        {propName}
      </div>
    );
  }
}

export default connect(mapStateToProps, mapDispatchProps)(App);

react的核心

转换Transformation

UIs 只是把数据通过映射关系转换成另一种形式的数据

The same input gives the same output

抽象Abstraction

You can't fit a complex UI in a single function though. It is important that UIs can be abstracted into reusable pieces that don't leak their implementation details. Such as calling one function from another.

不可以用单个函数就实现复杂的UI,把 UI 抽象成多个隐藏内部细节,又可复用的函数。通过在一个函数中调用另一个函数来实现。

组合Composition

build abstractions from the containers that compose other abstractions

combine two or more different abstractions into a new one

将两个或者多个不同的抽象组合到新的抽象

react项目构建

react技术栈

  • react

  • webpack

  • react-router

  • redux

  • redux-thunk (this API requires redux@>=3.1.0)

  • es6

react-redux-boilerplate

参考

react入门教程

官网get-started

think-in-react

react设计基础

redux 中文文档

redux-tutorial

react-router

redux-thunk中间件

react-redux-universal-hot-example