美文网首页
Get Strated of React

Get Strated of React

作者: lndyzwdxhs | 来源:发表于2018-06-01 10:14 被阅读21次

痛点:前端html的开发越来越复杂,没有可重用和模块化编程

  • React不是一个完整的MVCMVVM框架,它只负责View
  • 基于Virtual DOM思想

0x01 JSX & Components

  • 类似于DOM结构的对象,html标签默认小写,自定义的组件(Components)首字母大写
  • jsx中需要执行js表达式时,需要使用中括号({})包起来
  • jsx的样式不能使用class表示,因为在jsclass是关键字,需要使用clasName表示
  • jsx内的style需要使用对象来表示,属性使用驼峰式命名规则
// 默认jsx
<div> Hello Word! </div>
// 自定义组件
<Hello className='xxxx' style={{color:'red',fontSize:'20px'}} name='xxx' />
state & props
  • props是组件调用方在调用组件时传入的,props一旦指定一般不会改变,在结构上,props也是属于调用方
  • state是私属于组件方,state是可变的,即可以理解为函数内部状态参数。通过ComponentsetState方法来修改state的值
  • state是组件内部的状态,state变化的时候会重新render
componentDisMount : function () {
  var _self = this;
  // 为什么要对this重新赋值?
  // 因为在window.setTimeout()函数参数之外this指的就是当前的component,
  // 回调函数中的this指的就是最外边的全局对象:global或window
  // this的4中用法:
  // 1:构造函数内部this,指的是创建的对象
  function cst(){
    this.x=1;
  };
  var t1 = new cst();
  // 2:call apply bind中使用,传入的this替换函数内部的this
  // 3:this指的是调用函数的那个对象
  // 4:this出现在setTimeout函数的函数内部时,this指的是全局对象
  window.setTimeout(function () {
    _self.setState({
      fontSize:'40px',
    });
  }, 1000);
}
context
  • 什么场景下使用context:当你不想每层组件都手动传递需要的props的时候
  • 官方建议不要使用context,用props & state代替控制数据流
  • 官方详细参考资料

使用props传递color属性

class Button extends React.Component {
  render() {
    return (
      <button style={{background: this.props.color}}>
        {this.props.children}
      </button>
    );
  }
}

class Message extends React.Component {
  render() {
    return (
      <div>
        {this.props.text} <Button color={this.props.color}>Delete</Button>
      </div>
    );
  }
}

class MessageList extends React.Component {
  render() {
    const color = "purple";
    const children = this.props.messages.map((message) =>
      <Message text={message.text} color={color} />
    );
    return <div>{children}</div>;
  }
}

使用context,我们可以自动地在组件树中传递参数。

const PropTypes = require('prop-types');

class Button extends React.Component {
  render() {
    return (
      <button style={{background: this.context.color}}>
        {this.props.children}
      </button>
    );
  }
}

Button.contextTypes = {
  color: PropTypes.string
};

class Message extends React.Component {
  render() {
    return (
      <div>
        {this.props.text} <Button>Delete</Button>
      </div>
    );
  }
}

class MessageList extends React.Component {
  getChildContext() {
    return {color: "purple"};
  }

  render() {
    const children = this.props.messages.map((message) =>
      <Message text={message.text} />
    );
    return <div>{children}</div>;
  }
}

MessageList.childContextTypes = {
  color: PropTypes.string
};

通过在MessageListcontext提供者)中添加childContextTypesgetChildContextReact会向下自动传递参数,任何组件只要在它的子组件中(这个例子中是Button),就能通过定义contextTypes来获取参数。
如果contextTypes没有定义,那么context将会是个空对象。

0x02 React Components Lifecycle

Lifecycle
mounted
  • React ComponentsRender解析生成对应的DOM节点并被插入到浏览器DOM结构的过程,即当我们在浏览器上可以看到组件的时候,Mounted已经结束了
update
  • 一个MountedReact Components被重新Render的过程,差量变更
  • 4hook函数一般不进行修改
unmounted
  • 一个MountedReact Components对应的DOM节点被从浏览器DOM结构中移除的过程

0x03 React Event Listener

  • React Component上绑定事件,采用驼峰式来绑定事件

0x04 When the project starts to use

yeoman
  • 用来生成项目代码结构,根据自己选择架构,自动生成项目结构
webpack
  • 相当于盖好了房子给用户来使用,grunt等工具则是需要用户自己来盖房子
  • Webpack本身只能处理JavaScript模块,如果要处理其他类型的文件,就需要使用loader进行转换。将css文件转换成style属性绑定到DOM上,将图片等url也加到对应的位置

0x05 Redux

  • React是纯View层框架,需要搭配数据流才能进行完整的前端开发,这个数据流就是Redux
  • 目前的数据流框架:FluxreFluxRedux
  • Redux简答、单一状态树
Flux单向数据流 Redux数据流
action
  • 普通的JS对象Objects
  • 一般由方法生成
  • 必须有type属性
const addTodo = (text) => {
    return {
        type : "ADD_TODO",
        id : nextTodoId++,
        text
    }
}
reducer
  • 也由方法生成
  • 生成它的方法是一个纯函数(纯方法就是没有任何外界依赖,1+1=2这样的必然性 )
  • 传入旧的state,返回新的state
const todo = (state, action) => {
    switch (action.type) {
        case "ADD_TODO":
            return {
                id : action.id,
                text : action.text,
                completed : false
             }
        default:
            return state
    }
}
store
  • store包含所有的reducer
  • action作用于store上,而不是直接作用于reducer
  • reducer根据store做响应
  • statereducer的混合体
  • store是唯一的
  • 包括了完整的state
  • state完全可预测
react-redux
  • 项目中可以直接使用redux,也可以使用react-redux,后者提供了便捷的API
  • 组件需要分成两类:UI componentcontainer componentUI组件展示样式,container组件处理业务逻辑、数据和状态
  • 如果一个组件既有 UI 又有业务逻辑怎么办?将它拆分成下面的结构:外面是一个容器组件,里面包了一个UI 组件。前者负责与外部的通信,将数据传给后者,由后者渲染出视图。
  • connect() 用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来。

下面代码中,TodoListUI 组件,VisibleTodoList就是由 React-Redux 通过connect方法自动生成的容器组件。

import { connect } from 'react-redux'
const VisibleTodoList = connect()(TodoList);

但是,因为没有定义业务逻辑,上面这个容器组件毫无意义,只是 UI 组件的一个单纯的包装层。为了定义业务逻辑,需要给出下面两方面的信息。
(1)输入逻辑:外部的数据(即state对象)如何转换为 UI 组件的参数
(2)输出逻辑:用户发出的动作如何变为 Action 对象,从 UI 组件传出去。
因此,connect方法的完整 API 如下。

import { connect } from 'react-redux'
const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

上面代码中,connect方法接受两个参数:mapStateToPropsmapDispatchToProps。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将state映射到 UI 组件的参数(props),后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action

mapStateToProps()

  • 它的作用就是像它的名字那样,建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系
  • 函数返回一个对象,里面的每一个键值对就是一个映射

下面代码中,mapStateToProps是一个函数,它接受state作为参数,返回一个对象。这个对象有一个todos属性,代表 UI 组件的同名参数,后面的getVisibleTodos也是一个函数,可以从state算出 todos 的值。

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}

下面就是getVisibleTodos的一个例子,用来算出todos

const getVisibleTodos = (todos, filter) => {
  switch (filter) {
    case 'SHOW_ALL':
      return todos
    case 'SHOW_COMPLETED':
      return todos.filter(t => t.completed)
    case 'SHOW_ACTIVE':
      return todos.filter(t => !t.completed)
    default:
      throw new Error('Unknown filter: ' + filter)
  }
}

mapStateToProps会订阅 Store,每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。
mapStateToProps的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象。

// 容器组件的代码
//    <FilterLink filter="SHOW_ALL">
//      All
//    </FilterLink>

const mapStateToProps = (state, ownProps) => {
  return {
    active: ownProps.filter === state.visibilityFilter
  }
}

使用ownProps作为参数后,如果容器组件的参数发生变化,也会引发 UI 组件重新渲染。
connect方法可以省略mapStateToProps参数,那样的话,UI 组件就不会订阅Store,就是说 Store 的更新不会引起 UI 组件的更新。

mapDispatchToProps()

mapDispatchToPropsconnect函数的第二个参数,用来建立 UI 组件的参数到store.dispatch方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象。
如果mapDispatchToProps是一个函数,会得到dispatchownProps(容器组件的props对象)两个参数。

const mapDispatchToProps = (
  dispatch,
  ownProps
) => {
  return {
    onClick: () => {
      dispatch({
        type: 'SET_VISIBILITY_FILTER',
        filter: ownProps.filter
      });
    }
  };
}

从上面代码可以看到,mapDispatchToProps作为函数,应该返回一个对象,该对象的每个键值对都是一个映射,定义了 UI 组件的参数怎样发出Action
如果mapDispatchToProps是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出。举例来说,上面的mapDispatchToProps写成对象就是下面这样。

const mapDispatchToProps = {
  onClick: (filter) => {
    type: 'SET_VISIBILITY_FILTER',
    filter: filter
  };
}

<Provider> 组件

connect方法生成容器组件以后,需要让容器组件拿到state对象,才能生成 UI 组件的参数。

一种解决方法是将state对象作为参数,传入容器组件。但是,这样做比较麻烦,尤其是容器组件可能在很深的层级,一级级将state传下去就很麻烦。

React-Redux 提供Provider组件,可以让容器组件拿到state

import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'

let store = createStore(todoApp);

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

上面代码中,Provider在根组件外面包了一层,这样一来,App的所有子组件就默认都可以拿到state了。

它的原理是React组件的context属性,请看源码。

class Provider extends Component {
  getChildContext() {
    return {
      store: this.props.store
    };
  }
  render() {
    return this.props.children;
  }
}

Provider.childContextTypes = {
  store: React.PropTypes.object
}

上面代码中,store放在了上下文对象context上面。然后,子组件就可以从context拿到store,代码大致如下。

class VisibleTodoList extends Component {
  componentDidMount() {
    const { store } = this.context;
    this.unsubscribe = store.subscribe(() =>
      this.forceUpdate()
    );
  }

  render() {
    const props = this.props;
    const { store } = this.context;
    const state = store.getState();
    // ...
  }
}

VisibleTodoList.contextTypes = {
  store: React.PropTypes.object
}

React-Redux自动生成的容器组件的代码,就类似上面这样,从而拿到store

redux-saga
  • sagaMiddleware中,可以使用takeEvery或者takeLatestAPI来监听某个action
  • 当某个action触发后,saga可以使用callfetchapi发起异步操作,操作完成后使用put函数触发action,同步更新state,从而完成整个State的更新

0x06 React-router

  • react原生的路由可以满足简单的应用,复杂场景使用起来非常复杂
  • 官方参考文档

不使用react-router的情况

import React from 'react'
import { render } from 'react-dom'

const About = React.createClass({/*...*/})
const Inbox = React.createClass({/*...*/})
const Home = React.createClass({/*...*/})

const App = React.createClass({
  getInitialState() {
    return {
      route: window.location.hash.substr(1)
    }
  },

  componentDidMount() {
    window.addEventListener('hashchange', () => {
      this.setState({
        route: window.location.hash.substr(1)
      })
    })
  },

  render() {
    let Child
    switch (this.state.route) {
      case '/about': Child = About; break;
      case '/inbox': Child = Inbox; break;
      default:      Child = Home;
    }

    return (
      <div>
        <h1>App</h1>
        <ul>
          <li><a href="#/about">About</a></li>
          <li><a href="#/inbox">Inbox</a></li>
        </ul>
        <Child/>
      </div>
    )
  }
})

React.render(<App />, document.body)

上述代码使用this.state.route来动态监控URL HASH部分的变化,从而渲染不同的<Child>组件

使用react-router的情况

import React from 'react'
import { render } from 'react-dom'

// 首先我们需要导入一些组件...
import { Router, Route, Link } from 'react-router'

// 然后我们从应用中删除一堆代码和
// 增加一些 <Link> 元素...
const App = React.createClass({
  render() {
    return (
      <div>
        <h1>App</h1>
        {/* 把 <a> 变成 <Link> */}
        <ul>
          <li><Link to="/about">About</Link></li>
          <li><Link to="/inbox">Inbox</Link></li>
        </ul>

        {/*
          接着用 `this.props.children` 替换 `<Child>`
          router 会帮我们找到这个 children
        */}
        {this.props.children}
      </div>
    )
  }
})

// 最后,我们用一些 <Route> 来渲染 <Router>。
// 这些就是路由提供的我们想要的东西。
React.render((
  <Router>
    <Route path="/" component={App}>
      <Route path="about" component={About} />
      <Route path="inbox" component={Inbox} />
    </Route>
  </Router>
), document.body)

0xFF 问题总结:

  • 拿不到组件的refs引用?

组件显示了以后才能拿到组件的refs引用,因为refs拿的是真是DOM虚拟DOM真正插入到HTML DOM的时候才能拿到refs
显示以后,也不一定可以对内部的参数进行操作,需要delay()或者setTimeout()进行延迟操作


欢迎关注微信公众号(coder0x00)或扫描下方二维码关注,我们将持续搜寻程序员必备基础技能包提供给大家。


相关文章

网友评论

      本文标题:Get Strated of React

      本文链接:https://www.haomeiwen.com/subject/duyhsftx.html