React

简介

当2014年Facebook推出React时,给整个业界带来全新的看待网页应用开发的方式,和React一同问世的Flux,也克服传统MVC框架的很多弊病。技术在不断发展,在2015年,Flux的一个变体Redux出现,进一步优化了Flux的功能。


优秀之处:

专注视图层:(不处理路由,数据,以及逻辑的处理

VirtualDom只对虚拟dom更新,不对真实dom更新;只对dom进行必要的更新,实现的重绘的最小化;

jsx语法:

• 高内聚,低耦合

• onClick 不是原生dom中的onclick事件,这里是onClict是通过react的事件委托方式,触发挂在顶层dom节点上的一个事件处理函数(提高了性能);

• 可以将样式一起写入组件文件中;

• 关于jsx语法的说法:

• 在jsx语法中可以直接写入组件,注意组件为了和普通标签区别,必须第一个字母大写;

• jsx语法是历史的进步还是倒退;(样式,结构,逻辑于一体)

活跃的生态圈:(开源项目)

组件式开发

手动搭建项目:

• npm init

• 安装项目依赖

cnpm install --save-dev babel-core babel-loader babel-preset-stage-2 babel-plugin-transform-react-jsx babel-plugin-transform-runtime babel-preset-es2015 babel-preset-react babel-preset-stage-0 css-loader html-loader html-webpack-plugin react react-dom style-loader url-loader webpack webpack-dev-server

• 配置babelrc文件:

 {

    "presets": [

        "es2015",

        "stage-0",

        "react"

    ],

    "plugins": ["transform-runtime", "transform-react-jsx"]

}

• webpack.config.js的配置文件:

除了如下配置有更改,其他可以完全遵照vue的配置

{

                test: /\.js|.jsx$/,

                use: [

                    "babel-loader",

                ],

                exclude: /node_modules/

            },

• 在vsCode中开发react的配置:

安装jsx插件配置如下:


安装React/Redux/react-router Snippets插件,可以帮助我们快速生成一定的语句;

所有的react组件使用jsx后缀来标识;

自动化构建项目:

• 初始化:

全局安装:

npm install –-global create- react- app

初始化项目:

create- react- app myReactApp

启动服务:

cd myReactApp

npm start

• 组件写法:


import React,{Component} from "react"

import ClickCount from "./ClickCount.jsx"

class Main extends Component {

  constructor(props){

    super(props)

  }

  render(){

    return (

      <ul>

        <li><ClickCount name="first" num={1}></ClickCount></li>

        <li><ClickCount name="second" num={10}></ClickCount></li>

        <li><ClickCount name="third" num={7}></ClickCount></li>

      </ul>

    )

  }

}

export default Main

注:对于jsx语法的解析必须引入React,虽然这里不用,但是如果不引入会因无法解析jsx而报错;组件的定义必须遵循首字母大写

• jsx语法使用:

属性:

1、属性值直接为字符串

2、动态解析属性值

const element = (<img src={user.avatarUrl}></img>);

react的jsx:

const element = (

  <h1 className="greeting">

    Hello, world!

  </h1>

);


用React.createElement()同样可以达到同样的效果;

const element = React.createElement(

  'h1',

  {className: 'greeting'},

  'Hello, world!'

);

• 项目的分解:

npm run eject 进行项目配置文件的弹射;

• react的工作方式:

• 从繁琐的dom处理中解脱出来,响应式,函数式编程思维;输入相同的数据,将产出相同的效果;数据更新,dom同时做出更新;

• Virtual Dom

Dom:结构化文本的抽象表达式;

虚拟dom:对dom树的抽象,不会触及浏览器,只存在于js内存的空间树形结构,每次的数据刷新,render函数的执行都会触发一个新旧虚拟dom 的对比,如果无改变,则不更新,有改变,只更新相应dom结构;

元素的渲染

jsx语法将元素渲染到dom

更新元素渲染:

React 元素都是immutable 不可变的。当元素被创建之后,你是无法改变其内容或属性的;更新界面的唯一方法就是重新挂载节点

function tick() {

  const element = (

      <h2>{new Date().toLocaleTimeString()}.</h2>

  );

  ReactDOM.render(

    element,

    document.getElementById('root')

  );

}

setInterval(tick, 1000);


只会更新必要的部分(可以在谷歌开发者工具中进行dom更新的查看)

reactjsx语法中,如果标签没有子元素,可以直接使用闭合标签;

react的组件必须首字母进行大写,否则React.createEelement则无法查找到组件,进行编译;

jsx支持标签名为表达式;,如果使用动态标签名,则需先将表达式解析赋值给一个变量;



React组件以及基本语法:

• 组件的设计要素:

高内聚:把逻辑紧密相关的内容放在一个组件中;

低耦合:不同组件之间的依赖关系要尽量弱化;尽量保持每个组件的独立性;

• 组件的生命周期:

• 装载过程:

• Constructor:

初始化state;

绑定成员函数的this环境(bind、:: )防止以解构的方式进行全局作用域进行调用;

例:this.fn=this.fn.bind(this)或this.fn=::this.fn

• GetInitialState:初始化state值,必须结合React.createClass()使用,使用es6的方式,此函数无效果;


const Test=React.createClass({

  getInitialState:function(){

    return {

      name:"zhangsan"

    }

  },

  getDefaultProps:function(){

    return {

      name:"zhangsan" //这里的zhangsan相当于默认值

    }

  }

})


• GetDefaultProps:初始化props值,必须结合React.createClass()使用,使用es6的方式,此函数无效果,用属性defaultProps代替;



class Test extends Component {

  constructor(props){

super(props)

this.state={data:new Date()}

  }

  render(){

    return (

      <ul>

        <li><ClickCount name="third" num={7}></ClickCount></li>

      </ul>

    )

  }

}

Test.defaultProps={

    name:"moren"

}



• ComponentWillMount:在render函数调用之前进行调用;

• Render:并不做实际的渲染动作,只返回一个jsx描述结构,最后渲染与否由react来决定;必须是个纯函数,不能设计到数据的更变(this.setState);


render(){

    return (

      <ul>

        <li><ClickCount name="third" num={7}></ClickCount></li>

      </ul>

    )

  }


• ComponentDidMount :在render函数调用之后进行调用;但不是在render函数调用之后立即调用,而是当所有的dom树挂载并渲染完成后才会调用,这是因为render函数并不进行渲染,而只是返回一个jsx对象,渲染的工作由react库进行,只有当所有的组件的jsx对象组合完毕之后,react通过对比后才会渲染,所有此钩子函数是发生的所有组件的render函数都执行后才会执行;(只能在浏览器端触发)

注:与angular中的link或者post函数有点相似,但是这里不仅指子组件,而是当前组件中的所有组件,包括兄弟组件;提供了dom操作的接口

• 更新过程:

• ComponentWillReceiveProps:传入的props的改变或者组件进行刷新(forceUpdate函数触发组件的刷新)都会触发此函数,但是通过this.setState改变的数据则不会触发此函数,

• ShouldComponentUpdate:react组件的钩子函数两个需要有返回值的钩子函数之一,另一个为render,此钩子函数的返回值为一个bolen值,如果为true时,则prop的改变以及state的改变都会引起组件的刷新,如果为false时,则不再进行渲染;此钩子函数接受两个参数,一个是nextProps,一个是nextState,可以将将要更新的值和此时的做对比,然后返回true和false来进行性能的校优;

• ComponentWillUpdate:跟componentWillMount相似

• Render

• ComponentDidUpdate:跟componentDidMount相似

• 卸载过程:

ComponentWillUnmount:此钩子函数可以在组件卸载前执行,可以进行手动添加dom元素的删除,以及计时器和事件监听的移除;

React组件的数据

组件内部数据类型:

两种数据结构,prop和state,这两种数据的改变都会引起组件的重新渲染;

• Prop:组件的外部接口,接受外部数据;跟html的属性的书写类似,但是除了接受字符串之外,还可以接受js对象,数字等的;如果组件进行数据反馈给外界,可以通过prop传入组件一个函数;

• Prop的读取:

由class类的constructor函数接受,以及super()方法调用时的传入;

最后的props为一个对象,键值分别为传入时的属性名;这里比较方便的是使用es6的解构赋值;

• PropTypes的检查:

可以通过增加类的propTypes属性来定义prop规格,在constructor函数中进行如下定义:

Index.propTypes={     //组件全局进行配置

caption:PropTypes.string.isRequired,

initValue:PropTypes.number

}

注意:此属性不会影响组件的渲染,只是做到在开发过程中辅助开发;

• State:由于组件不能改变出入的prop,所以当组件要进行自己的状态纪录时就需要用到state;

• 初始化:state的初始化可以在constructor中,通过this.state进行设置,值必须为一个js对象的格式,通常将prop传入的外部值赋给state,以便后续操作;设置默认值时,可以用 | | 操作符进行;

• 读取和更新state

更新使用this.setState({count:this.state.count++})方法;

读取为:this.state.count这种方式

• Prop和state的对比:

Prop:定义外部接口;赋值在外部环境使用组件时;组件内部不能更改

State:纪录内部状态;赋值在组件内部;在组件内部可以进行更改;


reactcss的样式书写

全局引入css文件

此种方式会将css文件在全局引入,也就是说当前样式文件中书写的所有样式,在任何一个组件都可以引用,此种引用方式有可能会造成全局的污染,变量名冲突,又或者说不符合组件式开发的思想;此种方式一般是引入全局的css文件;

import “app.css”


局部作用域(css modules的方式)

此种方式采用的是css modules的做法,通过此做法,最后会将css文件中类名和dom中类名形成唯一的hash值,这样就不会造成类名的冲突了,使用场景,一般为组件内部的样式防止全局书写造成的类名污染;

比如如下写法:

app.css:

.title {

  color: red;

}

jsx:

import style from './App.css';

export default () => {

  return (

    <h1 className={style.title}>

      Hello World

    </h1>

  );

};

最后会编译成如下代码:

<h1 class="_3zyde4l1yATCOkgn-DBWEL">

  Hello World

</h1>

._3zyde4l1yATCOkgn-DBWEL {

  color: red;

}


注:在css Modules中还提供了一种css全局作用域的书写方式,也就是说如果通过此种方式注册的css类名不会被替换为hash值,书写方式如下:

css

:global(.title){

color:red;

}


行内样式:

jsxdom上直接使用style属性进行样式的设置,样式的值为一个对象,这种方式的书写,可以支持css样式以js对象的方式进行书写,如果不想将组件的内部样式进行作用域隔离使用css modules的方式进行书写,则可以此种方式进行书写,可以直接在render函数中定义样式对象,也可以通过专门外部样式js文件配置样式对象,然后引入到dom进行使用,书写方式如下:

var style={

color:red;

}

<div style={style}>天气真好</div>

注:在书写css属性时,一定要采用驼峰式写法;

事件处理

react中的事件绑定属性采用的是驼峰式写法:

<button onClick={this.clickfn}>点击</button>

处理react使用class类定义方法时,无法自动绑定this的弊端:

constructor中使用bind为事件函数绑定this

this.clickfn=this.clickfn.bind(this)

dom中调用函数时,先使用箭头函数绑定this,箭头函数的this是在定义时形成的

<button onClick={()=>this.clickfn()}>点击</button>


jsxonClickhtmlonclick不同之处


htmlonclick弊端:

注册的事件处理函数都是全局环境,污染了全局环境;

使用onclick的dom元素,如果在dom树中删除时,必须手动的注销事件处理器,否则会造成内容泄露;


jsxonClick:

挂载的每一个函数都是在组件内部,而不是全局;

无论多少个onClick都是采用的事件委托的方式,在dom树顶层添加一个事件监听函数,此函数会根据具体组件分配具体的函数


条件判断:

reactjsx语法中,如果遇到js语句使用{}进行包裹;

if语句的使用:


render() {

        var element=null

            if(this.state.stateT){

                   element= <div > stateT为true时 < /div>

                }else{

                     element= <div > stateT为false时 < /div>

                }

        return (

            <div>

                <button onClick={this.changeFn}>切换</button>

                {element}

            </div>

        )

    }


jsx与运算符&&的结合:


render(){

        return (

             <div>

                <button onClick={this.changeFn}>切换</button>

                {this.state.stateT&& <div > stateT为false时 < /div>}

            </div>

        )

    }

三目运算符


render(){

        return (

             <div>

                <button onClick={this.changeFn}>切换</button>

                {this.state.stateT?<div>stateT为true时</div>:<div> stateT为false时</div>}

            </div>

        )

}

jsx语法中,render可以返回null,表示不进行任何dom的渲染,但是钩子函数会执行;

列表和keys

基础用法:(key一般采用数据id,或者数据在数组中的索引)


render() {

        var element=this.state.list.map((i,index)=><li key={index}>{i}</li>)

        return (

            <div>

                <button onClick={this.changeFn}>切换</button>

                {element}

            </div>

        )

}

注:如果后续进行数据的重新排序,则最好不要使用索引当作key值,否则,由于key值和dom的内容同时改变,则会引起全部dom节点的重绘;如果使用的是id,则只会调整dom节点的顺序,不会引起重绘dom树;(可以用上面例子演示,推荐使用id作为标识,而不是index)

key值只需要在兄弟元素之间唯一就可以,不需要是在全局唯一的


render() {

        var element=this.state.list.map((i,index)=><li key={i}>{i}</li>)

        var element2=this.state.list.map((i,index)=><li key={i}>{i+1}</li>)

        return (

            <div>

                <button onClick={this.changeFn}>切换</button>

                {element}

                {element2}

            </div>

        )

     }

jsx中使用map进行遍历


render() {

        return (

            <ul>

                <button onClick={this.changeFn}>切换</button>

               {this.state.list.map((i)=><li key={i}>{i}</li>)}

            </ul>

        )

    }

表单

vue中,我们知道由于vuemvvm架构,vm之间是响应式的,可以实时同步,但是在react中不存在数据的双向数据绑定,只是负责view层的渲染,所以我们在react中取表单中的值时需要一定的方式

受控组件(采用react的单向数据流方式实现vue一样的双向数据绑定)

changeFn(event){

        this.setState({

            name:event.target.value

        })

    }

    render() {

        return (

            <div>

                <p>表单</p>

                <label htmlFor="name">name:</label>

                <input type="text" id="name" value={this.state.name} onChange={this.changeFn}/>

                <p>{this.state.name}</p>

            </div>

        )

    }

如果想对从input中获取到的值进行格式化时,则如下操作就可以实现:

this.setState({

            name:event.target.value.toUpperCase()

        })

textarea标签在react中设置和读取值都采用value的方式

react中的使用select,并实时获取其选择的值:

render() {

        return (

            <div>

                <p>表单</p>

                <label htmlFor="name">name:</label>

                <select value={this.state.name} onChange={this.changeFn}>

                    <option value="one">one</option>

                    <option value="two">two</option>

                    <option value="three">three</option>

                </select>

                <p>{this.state.name}</p>

            </div>

        )

}

react中实现多表单元素值得获取

changeFnAll(event){

        let target=event.target

        let name=target.name

        let value=target.type==="checkbox"?target.checked:target.value

        this.setState({

            [name]:value

        })

    }

    // 实现多表单元素值的获取

    render() {

        return (

            <div>

                <p>表单</p>    

                <input type="checkbox" value={this.state.checkbox} onChange={this.changeFnAll} name="checkbox"/>

                <input type="text" value={this.state.text} onChange={this.changeFnAll}  name="text"/>

                <p>{this.state.checkbox.toString()}</p>

                <p>{this.state.text}</p>

            </div>

        )

    }

组件间通讯

父组件向子组件传递数据:

字符串常量的传递:

<MyComponent message="hello world" />

<MyComponent message={'hello world'} />

没有给属性传值,它默认为 true

<MyTextBox autocomplete />

不建议如下写法,容易与es6的对象简洁表示法冲突

<MyTextBox autocomplete={true} />

使用扩展运算符进行属性的设置

  const props = {firstName: 'Ben', lastName: 'Hector'};

<Greeting {...props} />;

子组件向父组件传递数据

只能通过将父组件的一个方法用prop的方式传入子组件,在子组件触发更变时,调用这个传入的方法,然后把子组件更变后的值以参数的形式传入父组件;从而达到父组件和子组件的通信的效果;


兄弟组件间的通讯

状态提升

对于没有直接关联关系的两个节点,就如 Child_1 与 Child_2 之间的关系,他们唯一的关联点,就是拥有相同的父组件。参考之前介绍的两种关系的通讯方式,如果我们向由 Child_1 向 Child_2 进行通讯,我们可以先通过 Child_1 向 Parent 组件进行通讯,再由 Parent 向 Child_2 组件进行通讯

观察者模式

也就是像angular和vue的事件派发机制,在这里react没有内置的事件派发模式,所以需要手动封装一个事件派发机制;

如下代码:

var watchObj = {

    arrFn: {},

    on(name, fn) {

        if (this.arrFn[name] === undefined) {

            this.arrFn[name] = []

        }

        this.arrFn[name].push(fn)

    },

    emit() {

        let name, args;

        if (arguments.length == 0) {

            return false;

        }

        name = arguments[0]

        args = [].concat(Array.prototype.slice.call(arguments, 1));

        if (this.arrFn[name] !== undefined && this.arrFn[name].length > 0) {

            this.arrFn[name].forEach(function(i) {

                i(...args)

            }, this);

        }

    }

}

export default watchObj


组合与继承

vue中的有一种组件特性叫内容分发,可以在父组件中自定义子组件的dom结构,在react中也提供了这么一种机制

包含关系:(在子组件通过this.props.children获取父组件中写在子组件开闭标签间的dom节点)

父组件:

render() {

        return (

            <div>

               <Children>

                <p>相当于vue的内容分发</p>

               </Children>

            </div>

        )

    }

子组件:

render() {

        return (

            <div>

                <p>这里是子组件</p>

               {this.props.children}

            </div>

        )

    }


子组件中动态渲染其他组件:(在父组件中将其他组件动态绑定到prop属性上,在子组件中通过相应的属性来获取)


父组件:

render() {

        return (

         var one= <span>这里为true</span>

        var two=<span>这里为false</span>

        return (

            <div>

                <Slot one={one} two={two}></Slot>

            </div>

        )

        )

    }

子组件:

render() {

       return <div>   

      <p>  这里是子组件</p>

        {this.state.state?this.props.one:this.props.two}

    </div>

}


react-router

 2.0 完全向后兼容 1.0,所有在 1.0 被弃用的 API 都会在控制台以 warn 的形式打印出来,在 3.0 中将会完全移除 1.0 所弃用的东西,现在最新版本是4.2.0版本;但是4.0版本后,有很多方法不再向前兼容(这里讲解的为2.8.1版本)


基本用法:

首先我们需要导入一些组件...

import { Router, Route, Link } from 'react-router'

配置路由信息:

使用jsx语法

React.render((

  <Router>

    <Route path="/" component={App}>

      <Route path="about" component={About} />

      <Route path="inbox" component={Inbox} />

    </Route>

  </Router>

), document.body)

使用js对象进行配置

const routes = {

  path: '/',

  component: App,

  childRoutes: [

    { path: 'about', component: About },

    { path: 'inbox', component: Inbox },

  ]

}

React.render(<Router routes={routes} />, document.body)

获取参数

通过动态路由片段

<Route path="messages/:id" component={Message} />

 可以通过如下方式进行获取id

this.props.params.id

通过query字符串

/foo?bar=baz;

this.props.location.query.bar

路由的配置规则(Router

路径语法:

:paramName – 匹配一段位于 / # 之后的 URL 命中的部分将被作为一个参数

() – 在它内部的内容被认为是可选的

* – 匹配任意字符(非贪婪的)直到命中下一个字符或者整个 URL 的末尾,并创建一个 splat 参数

如下解释:

// 匹配 /hello/michael 和 /hello/ryan

<Route path="/hello/:name">         

// 匹配 /hello, /hello/michael 和 /hello/ryan

<Route path="/hello(/:name)">   

// 匹配 /files/hello.jpg 和 /files/path/to/hello.jpg  

<Route path="/files/*.*">          

常用路由类型:


browserHistory:react推荐使用(采用的是html5的浏览器路由纪录)

hashHistory:默认的(采用的是hash的浏览器路由纪录)

createMemoryHistory


使用browserHistory如下:

import { browserHistory } from 'react-router'

<Router history={browserHistory}>

路由的跳转配置(Link):

需要动态的引入组件:

import { Link} from “react-router“

<Link></Link>

属性:

to: 值为字符串,例:”/list” (路由跳转的路径);


query:值为对象,例:{name:1} (这里相当于/list?name=1,这种路由的书写方式);


state:值为对象,可以通过this.location.state获取到当前的值;


activeClassName:当某个 route 是激活状态时,<Link> 可以接收传入的 className。失活状态下是默认的 class。写法如下:activeClassName="current"


activeStyle当某个 route 是激活状态时,可以将样式添加到链接元素上;写法如下:activeStyle={{color: 'red'}}


onClick(e):自定义点击事件的处理方法。如处理 <a> 标签一样 - 调用 e.preventDefault() 来防止过度的点击,同时 e.stopPropagation() 可以阻止冒泡的事件


默认路由的设置:

jsx:

<IndexRoute component={ Index }/>

js对象:

const routes = {

  path: '/',

  component: App,

  indexRoute:{

    component:Index

  },

  childRoutes: [

    { path: 'list', component: List },

  ]

}


在嵌套路由中使用{this.props.children}来渲染动态路由,这里相当于vue-router中的router-view

路由对应组件的渲染

单一组件的渲染:(this.props.children

路由:

            <Route path='center' component={Center}>

                <Route path="left" component={Left}></Route>

            </Route>

组件:(Center

render(){

        return (

          <div>

            <p>这里是中心也</p>

            {this.props.children}

          </div>

        )

   }

多组件的渲染


路由:

            <Route path='center' component={Center}>

               <Route path="left" components={{left:Left,right:Right}}></Route>

            </Route>

组件:

render(){

const { left, right } = this.props

        return (

          <div>

            <p>这里是中心也</p>

            {left}-{right}

          </div>

        )

   }

异步组件(组件的懒加载)

单组件路由的定义:(getComponent()方法使用)

注:这里采用的是webpackrequire.ensure()进行的组件懒加载,接受三个参数,第一个参数为加载当前模块时的依赖,第二个参数为加载的当前的模块的回掉函数,第三个参数为加载完毕后模块的名称;

这里的getComponent函数第一个参数为当前路由的信息,第二个参数为一个回调函数,这里的回掉函数遵循node.js的回掉函数风格(回调函数的第一个参数为错误的捕捉,一般无错误,所以一般默认填入null,第二个参数为异步装载成功的组件;)


<Route path="list" getComponent={(location, cb) => {

            require.ensure([],(require)=>{

               cb(null, require('./components/routerPage/list.jsx').default)

            },'List')

          }} />



多组件路由的定义:(getComponents方法)

<Route path="center" getComponents={(location, cb) => {

            require.ensure([],function(require){

               cb(null, {center:require('./components/routerPage/center.jsx').default,list:require('./components/routerPage/center.jsx').default})

            },'Center')

js对象的方式配置路由组件的懒加载

const routes = {

  path: '/',

  getComponent(nextState, cb) {

    require.ensure([], (require) => {

      cb(null, require('./components/app.jsx').default)

    }, 'App')

  },

  indexRoute:{

    getComponent(nextState, cb) {

      require.ensure([], (require) => {

        cb(null, require('./components/index.jsx').default)

      }, 'Index')

    },

  },

  childRoutes: [

    // require("./routes/listR.js").default

     {

        path: 'list',

        getComponent(nextState, cb) {

            require.ensure([], (require) => {

                cb(null, require('./components/routerPage/list.jsx').default)

            }, 'List')

        }

    }

  ]

}

Route的钩子函数:(Route组件上书写)

onEnter(nextState, replaceState, callback?)

当 route 即将进入时调用。它提供了下一个路由的 state,一个函数重定向到另一个路径。this 会触发钩子去创建 route 实例。

当 callback 作为函数的第三个参数传入时,这个钩子将是异步执行的,并且跳转会阻塞直到 callback 被调用。

onLeave()

当 route 即将离开时调用

路由注入到组件的props上的属性和参数:

history:路由的前端历史纪录和路由跳转方法

go():

goBack():

goForward():

push():

replace()

location:当前路由的参数信息

state:通过在link上的state属性设置的对象值

action:当前路由的纪录形式

pathname:当前hash路由

query:通过路由的传参,路径?后的路径,或者是query属性传递的

params:通过动态路由传递的值

routeParams:(小心有坑)

this.props.params 是直接在组件中指定 route 的一个子集。例如,如果 route 的路径是 users/:userId 而 URL 是 /users/123/portfolios/345,那么 this.props.routeParams 会是 {userId: '123'},并且 this.props.params 会是 {userId: '123', portfolioId: 345}。

route:当前路由的配置信息

routes:当前路由所包含的所有路由片段

在路由跳转前阻止路由的跳转:

在组件内部如下引入:

import { withRouter } from 'react-router'

组件抛出时:

export default withRouter(List)

组件回调函数的注册:

componentDidMount(){

         this.props.router.setRouteLeaveHook(

             this.props.route,

             this.routerWillLeave)

    }

    routerWillLeave(nextLocation) {

      // 返回 false 会继续停留当前页面,

      // 否则,返回一个字符串,会显示给用户,让其自己决定

        return '确认要离开?';

    }

数据的请求

constructorcomponentDidUpdate中(在进入路由或者进入路由的相应组件内部进行数据请求);

withRouter进入目标路由或组件前,进行数据的请求


PropTypes类型检查

react15.5版本后,React.PropTypes已经废弃了,需要单独引入prop-types库进行验证

import PropTypes from 'prop-types'

class Children extends Component {

    constructor(props) {

        super(props)

    }

   render() {

        return (

            <div>

                <p>这里是子组件</p>

               {this.props.name}

            </div>

        )

    }

}

Children.propTypes={

    name:PropTypes.string

}


prop-types库可以校验的类型如下:


import PropTypes from 'prop-types';

MyComponent.propTypes = {

  // 你可以将属性声明为以下 JS 原生类型

  optionalArray: PropTypes.array,

  optionalBool: PropTypes.bool,

  optionalFunc: PropTypes.func,

  optionalNumber: PropTypes.number,

  optionalObject: PropTypes.object,

  optionalString: PropTypes.string,

  optionalSymbol: PropTypes.symbol,


  // 任何可被渲染的元素(包括数字、字符串、子元素或数组)。

  optionalNode: PropTypes.node,


  // 一个 React 元素

  optionalElement: PropTypes.element,


  // 你也可以声明属性为某个类的实例,这里使用 JS 的

  // instanceof 操作符实现。

  optionalMessage: PropTypes.instanceOf(Message),


  // 你也可以限制你的属性值是某个特定值之一

  optionalEnum: PropTypes.oneOf(['News', 'Photos']),


  // 限制它为列举类型之一的对象

  optionalUnion: PropTypes.oneOfType([

    PropTypes.string,

    PropTypes.number,

    PropTypes.instanceOf(Message)

  ]),


  // 一个指定元素类型的数组

  optionalArrayOf: PropTypes.arrayOf(PropTypes.number),


  // 一个指定类型的对象

  optionalObjectOf: PropTypes.objectOf(PropTypes.number),


  // 一个指定属性及其类型的对象

  optionalObjectWithShape: PropTypes.shape({

    color: PropTypes.string,

    fontSize: PropTypes.number

  }),


  // 你也可以在任何 PropTypes 属性后面加上 `isRequired`

  // 后缀,这样如果这个属性父组件没有提供时,会打印警告信息

  requiredFunc: PropTypes.func.isRequired,


  // 任意类型的数据

  requiredAny: PropTypes.any.isRequired,


  // 你也可以指定一个自定义验证器。它应该在验证失败时返回

  // 一个 Error 对象而不是 `console.warn` 或抛出异常。

  // 不过在 `oneOfType` 中它不起作用。

  customProp: function(props, propName, componentName) {

    if (!/matchme/.test(props[propName])) {

      return new Error(

        'Invalid prop `' + propName + '` supplied to' +

        ' `' + componentName + '`. Validation failed.'

      );

    }

  },


  // 不过你可以提供一个自定义的 `arrayOf` 或 `objectOf`

  // 验证器,它应该在验证失败时返回一个 Error 对象。 它被用

  // 于验证数组或对象的每个值。验证器前两个参数的第一个是数组

  // 或对象本身,第二个是它们对应的键。

  customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {

    if (!/matchme/.test(propValue[key])) {

      return new Error(

        'Invalid prop `' + propFullName + '` supplied to' +

        ' `' + componentName + '`. Validation failed.'

      );

    }

  })

};

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • React框架学习 React的起源和发展 起初facebook在建设instagram(图片分享)的时候嘞,因为...
    hcySam阅读 663评论 0 1
  • 函数是面向过程的,函数的调用不需要主体,而方法是属于对象的,调用方法需要一个主体-即对象。 npm install...
    Gukson666阅读 458评论 0 3
  • 1. React简介 React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScrip...
    王蕾_fd49阅读 404评论 0 0
  • #React框架的学习# React的起源和发展 起初facebook在建设instagram(图片分享)的时候因...
    你好爱人i阅读 695评论 0 1
  • 夜莺2517阅读 127,706评论 1 9