在React开发中,一个典型的React组件通常会混杂着逻辑操作部分和展示部分。逻辑操作部分指的是和页面UI无关的内容,如API的调用,数据的处理,事件处理函数。 展示部分则指的是创建页面UI 的内容,就是组件中render 函数的内容。
简单地写一个组件Geo 来看一下,这个组件会展示我们的位置信息。为了简单起见,用create-react-app创建项目。项目中的src目录主要存放源代码,所以我们在其内部新建一个目录components, 用于存放我们的组件。一般我们直接写js 文件,暴露出我们的组件,供其他组件使用。在components 目录下新建Geo.js 文件,代码如下,很简单,由于展示经纬度,所以需要两个状态: lon/lat, 分别表示经度/纬度;同时,组件渲染完成后,在其生命周期函数componentDidMount 下调用html5 的navigator Api 获取经纬度。
import React, {Component} from 'react'; export default class Geo extends Component { constructor(props) { super(props); this.state = { lat: null, // 纬度 lon: null // 经度 } this.handleSucess = this.handleSucess.bind(this) } //调用navigator API 获取当前的位置 componentDidMount() { if(navigator.geolocation) { navigator.geolocation.getCurrentPosition(this.handleSucess) } } // 位置获取成功后的回调函数 handleSucess({coords}) { var lat = coords.latitude.toFixed(2); var lon = coords.longitude.toFixed(2); this.setState({ lat , lon }) } render() { return ( <div> <div>纬度: {this.state.lat}</div> <div>经度: {this.state.lon} </div> </div> ) } }
然后在App.js 中 引入Geo组件,就是在App.js 文件上部写下
import Geo from './components/Geo';
并将App render 函数中以下代码
<p className="App-intro"> To get started, edit <code>src/App.js</code> and save to reload. </p>
改为
<div className="App-intro"> <Geo /> </div>
这时页面可以看到经纬度的展示,在chrome 需要FQ,因为他调用的是google地图,你可以用手机看一下效果
很明显地可以看到逻辑和展示的混杂。Navigator API 就是逻辑操作,render函数则是纯展示部分。
这时我们看一下容器和展示模式。容器和展示模式指的是把组件的逻辑操作部分和展示部分进行分离,分别放到不同的文件中,这样,每一个组件都会分成两个部分,两者都有各自的职责,一个只负责操作逻辑,叫做容器container, 一个只负责页面展示,叫做展示Presentational。 现在把我们的Geo 组件按照容器和展示模式进行一下拆分,这时新建一下js 文件,命名为Geo-container.js,它是一个容器组件,书写页逻辑,那么原来的Geo.js文件就变成了纯渲染组件。这种命名规则是React社区的规范,加container 后缀表示容器组件,不加则表示展示组件。
Geo-container.js 内容如下,它是直接把展示组件引入,然后对其进行传参
import React, {Component} from 'react'; import Geo from './Geo' // 引进展示组件 export default class GeoContainer extends Component { constructor(props) { super(props); this.state = { lat: null, // 纬度 lon: null // 经度 } this.handleSucess = this.handleSucess.bind(this) } componentDidMount() { if(navigator.geolocation) { navigator.geolocation.getCurrentPosition(this.handleSucess) } } handleSucess({coords}) { var lat = coords.latitude.toFixed(2); var lon = coords.longitude.toFixed(2); this.setState({ lat , lon }) } // 在容器组件内部,只渲染我们引入的展示组件,并把状态当做参数进行传递 render() { return (<Geo {...this.state}/>) } }
Geo.js 由于变成了纯渲染组件,所以用无状态组件的样式进行书写,
import React from 'react'; // 由于展示组件内部没有任何的状态,只负责展示,所以用无状态组件。 const Geo = ({lat,lon}) => { return ( <div> <div>纬度: {lat}</div> <div>经度: {lon} </div> </div> ) } export default Geo
在App.js中直接引入容器组件
import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; import GeoContainer from './components/GeoContainer'; // 引入我们的空器组件 class App extends Component { render() { return ( <div className="App"> <div className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h2>Welcome to React</h2> </div> {/*渲染我们的容器组件*/} <div className="App-intro"> <GeoContainer /> </div> </div> ); } } export default App;
这样同样实现了我们的功能,但职责更为了清晰,组件更容易被复用