React全家桶简介

2019-09-02 15:41:52 浏览数 (1)

当前前端开发已经进入以vue、react、webpack为代表的编程2.0时代。在1.0时代,代码是写给机器的;在2.0时代,代码是写给工具的,然后由工具处理后再转给机器。

一、React全家桶简介

JSX语法糖

使用React,不一定非要使用JSX语法,可以使用原生的JS进行开发。但是React作者强烈建议我们使用JSX,因为JSX在定义类似HTML这种树形结构时,十分的简单明了。这里简单的讲下JSX的由来。

比如,下面一个div元素,我们用HTML语法描述为:

代码语言:javascript复制
<div class="test">
 <span>Test</span>
</div>

如果换做使用javascript描述这个元素呢:

代码语言:javascript复制
{
 type:"div",
 props:{
   className:"test",
   children:{
     type:"span",
     props:{
       children:"Test"
     }
   }
 }
}

这样我们就可以在javascript中创建一个Virtual DOM(虚拟DOM)了。当然,这样是没法复用的,我们再把它封装一下:

const Div=>({text}){ return { type:"div", props:{ className:"test", children:{ type:"span", props:{ children: text, }, }, }, } }

接下来再实现这个div就可以直接调用Div('Test')来创建。但上述结构看起来实在让人不爽,写起来也很容易写混,一旦结构复杂了,很容易让人找不着北,于是JSX语法应运而生。我们用写HTML的方式写这段代码,再经过翻译器转换成javascript后交给浏览器执行。

上述代码用JSX重写:

代码语言:javascript复制
const Div =()=>(
 <div className="test">
 <span>Test</span>
 </div>
);

所谓语法糖,就是借助工具将复杂工作由人工转为机器完成。小程序中的WXS,也可以看作是一种语法糖,但是一种尚未成熟的语法糖。ES6的箭头函数在JS中也是语法糖,Bable可以将它自动转化为同等的ES5语法。

Virtual DOM

虚拟DOM的概念并不是FB首创却在FB的手上大火了起来。

在Html中对DOM进行更新操作十分昂贵,为减少对于真实DOM的操作,诞生了Virtual DOM的概念,也就是用javascript把真实的DOM树描述了一遍,使用的也就是我们刚刚说过的JSX语法。对比如下:

每次数据更新之后,重新计算Virtual DOM,并和上一次的Virtual DOM对比,对发生的变化进行批量更新。(注:React也提供了shouldComponentUpdate生命周期回调,来减少数据变化后不必要的Virtual DOM对比过程。)

Virtual DOM虽然渲染比传统的DOM操作要好一些,但对比DOM节点也是需要计算的,最大的好处在于可以很方便的和其它平台集成,比如react-native就是基于Virtual DOM渲染出原生控件。具体渲染出的是Web DOM还是Android控件或是iOS控件就由平台决定了。

React-router

应用程序的路由。它通过管理 URL,实现组件的切换和状态的变化。对应于vue-router。

安装

代码语言:javascript复制
npm install -S react-router

使用,可以将路由器Router看作React的一个组件

代码语言:javascript复制
import { Router } from 'react-router';
render(<Router/>, document.getElementById('app'));

Router组件本身只是一个容器,真正的路由要通过Route组件定义。使用JXS语法。

代码语言:javascript复制
import { Router, Route, hashHistory } from 'react-router';
render((
 <Router history={hashHistory}>
   <Route path="/" component={App}/>
 </Router>), document.getElementById('app')
);

上面代码中,用户访问根路由/(比如http://www.example.com/),组件APP就会加载到document.getElementById('app')。

Redux

从角色功能上讲,相当于vuex。主要包括三个主要概念:State、action、reducer。

Redux 和 React 之间没有关系。Redux 支持 React、Angular、Ember、jQuery 甚至纯 JavaScript。

Redux 默认并不包含 React 绑定库,可能需要单独安装。

代码语言:javascript复制
npm install --save react-redux

随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态)。 这些 state 可能包括服务器响应、缓存数据、本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化。Redux就是为了解决这个复杂场景而设计的。

可以把React的model看作是一个个的子民,每一个子民都有自己的一个状态,所有model统一由Redux统一管理。

组件分两种,容器组件和展示组件。

Store

Store 是Redux中的主要概念,是保存数据的地方,它实际上是一个Object tree。整个应用只能有一个 Store。

Store主要职责:

  • 维持应用的 state;
  • 提供 getState() 方法获取 state;
  • 提供 dispatch(action) 方法更新 state;
  • 通过 subscribe(listener) 注册监听器;
  • 通过 subscribe(listener) 返回的函数注销监听器。
action

State 的变化,会导致 View 的变化。Action 就是 View 发出的通知,表示 State 应该要发生变化了。即store的数据变化来自于用户操作。一般来说,View会通过 store.dispatch() 将 action 传到 store。

Action 是一个对象。其中的type属性是必须的,表示 Action 的名称

代码语言:javascript复制
const action = {  
 type: 'ADD_TODO',  
 payload: 'Learn Redux'
};
reducer

Action 只是描述了有事情发生了这一事实,并没有指明应用如何更新 state。而这正是 reducer 要做的事情。

Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。

代码语言:javascript复制
const reducer = function (state, action) {  
  // ...
 return new_state;
};

单向数据流

与vue一样,react的数据流动也是单向的。

Redux 应用中数据的生命周期遵循下面 4 个步骤:

  • 调用 store.dispatch(action)。
  • Redux store 调用传入的 reducer 函数。
  • 根 reducer 应该把多个子 reducer 输出合并成一个单一的 state 树。
  • Redux store 保存了根 reducer 返回的完整 state 树。
Connect

React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来。

代码语言:javascript复制
import { connect } from 'react-redux';
const TodoList = connect()(Memos);

上面代码中Memos是个UI组件,TodoList就是由 React-Redux 通过connect方法自动生成的容器组件。完整的connect方法这样使用:

代码语言:javascript复制
import { connect } from 'react-redux'
const TodoList = connect(
 mapStateToProps
)(Memos)

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

Provider

这个Provider 其实是一个中间件,它是为了解决让容器组件拿到state对象而存在的。

代码语言:javascript复制
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中“一切皆组件”的设计思想。

react-router

安装

npm install --save react-router-dom

React UI库

知名的库有:Ant Design,Material UI

二、React使用简介

获取原始DOM节点

组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM)。只有当它插入文档以后,才会变成真实的 DOM 。根据 React 的设计,所有的 DOM 变动,都先在虚拟 DOM 上发生,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做 DOM diff ,它可以极大提高网页的性能表现。

但是,有时需要从组件获取真实 DOM 的节点,这时就要用到 ref 属性

var MyComponent = React.createClass({ handleClick: function() { this.refs.myTextInput.focus(); }, render: function() { return ( <div> <input type="text" ref="myTextInput" /> <input type="button" value="Focus the text input" onClick={this.handleClick} /> </div> ); } });

上面代码中,组件 MyComponent 的子节点有一个文本输入框,用于获取用户的输入。这时就必须获取真实的 DOM 节点,虚拟 DOM 是拿不到用户输入的。为了做到这一点,文本输入框必须有一个 ref 属性,然后this.refs.[refName]就会返回这个真实的 DOM 节点。

需要注意的是,由于this.refs.[refName]属性获取的是真实 DOM ,所以必须等到虚拟 DOM 插入文档以后,才能使用这个属性,否则会报错。上面代码中,通过为组件指定 Click 事件的回调函数,确保了只有等到真实 DOM 发生 Click 事件之后,才会读取this.refs.[refName]属性。

this.state

组件免不了要与用户互动,React 的一大创新,就是将组件看成是一个状态机,一开始有一个初始状态,然后用户互动,导致状态变化,从而触发重新渲染 UI。

var LikeButton = React.createClass({ getInitialState: function() { return {liked: false}; }, handleClick: function(event) { this.setState({liked: !this.state.liked}); }, render: function() { var text = this.state.liked ? 'like' : 'haven't liked'; return ( <p onClick={this.handleClick}> You {text} this. Click to toggle. </p> ); } }); ReactDOM.render( <LikeButton />, document.getElementById('example') );

上面代码是一个 LikeButton 组件,它的 getInitialState 方法用于定义初始状态,也就是一个对象,这个对象可以通过 this.state 属性读取。当用户点击组件,导致状态变化,this.setState 方法就修改状态值,每次修改以后,自动调用 this.render 方法,再次渲染组件。

由于 this.props 和 this.state 都用于描述组件的特性,这两个概念可能会产生混淆。一个简单的区分方法是,this.props 表示那些一旦定义,就不再改变的特性,而 this.state 是会随着用户互动而产生变化的特性。

关于表单的值

用户在表单填入的内容,属于用户跟组件的互动,所以不能用 this.props 读取。

var Input = React.createClass({ getInitialState: function() { return {value: 'Hello!'}; }, handleChange: function(event) { this.setState({value: event.target.value}); }, render: function () { var value = this.state.value; return ( <div> <input type="text" value={value} onChange={this.handleChange} /> <p>{value}</p> </div> ); } }); ReactDOM.render(<Input/>, document.body);

上面代码中,文本输入框的值,不能用 this.props.value 读取,而要定义一个 onChange 事件的回调函数,通过 event.target.value 读取用户输入的值。

关于组件的生命周期

组件的生命周期分成三个状态:

  • Mounting:已插入真实 DOM
  • Updating:正在被重新渲染
  • Unmounting:已移出真实 DOM

React 为每个状态都提供了两种处理函数,will 函数在进入状态之前调用,did 函数在进入状态之后调用,三种状态共计五种处理函数。

  • componentWillMount()
  • componentDidMount()
  • componentWillUpdate(object nextProps, object nextState)
  • componentDidUpdate(object prevProps, object prevState)
  • componentWillUnmount()

这五个函数的全名与OC类库的命名风格颇为类似。

此外,React 还提供两种特殊状态的处理函数。

  • componentWillReceiveProps(object nextProps):已加载组件收到新的参数时调用
  • shouldComponentUpdate(object nextProps, object nextState):组件判断是否重新渲染时调用

Ajax

组件的数据来源,通常是通过 Ajax 请求从服务器获取,可以使用 componentDidMount 方法设置 Ajax 请求,等到请求成功,再用 this.setState 方法重新渲染 UI。

componentDidMount: function() { $.get(this.props.source, function(result) { var lastGist = result[0]; if (this.isMounted()) { this.setState({ username: lastGist.owner.login, lastGistUrl: lastGist.html_url }); } }.bind(this)); },

上面代码使用 jQuery 完成 Ajax 请求,这是为了便于说明。React 本身没有任何依赖,完全可以不用jQuery,而使用其他库。

等等。


参考资料:

  1. https://juejin.im/post/587b52d8570c352201fd993d
  2. http://www.ruanyifeng.com/blog/2016/05/react_router.html
  3. https://blog.csdn.net/stwuyiyu/article/details/86140697

0 人点赞