react 订阅
《react》是VOCALOID家族的一首歌曲,由黒うさP作词、作曲、编曲;初音ミク、镜音双子演唱,音乐时长4:52。 展开全文
《react》是VOCALOID家族的一首歌曲,由黒うさP作词、作曲、编曲;初音ミク、镜音双子演唱,音乐时长4:52。
信息
编    曲
黒うさP
歌曲时长
4:52
歌曲原唱
初音未来,镜音双子
谱    曲
黒うさP
外文名称
React
歌曲语言
日语
填    词
黒うさP
react主要剧情
这首曲子主要是说在ACUTE中,miku、luka和kaito曾是朋友。kaito和巡音是互相相爱,但PV中没有明确表示他们是情侣关系,但是初音爱上了kaito并向他表白,后来miku出国,luka和kaito发生过暧昧关系后感觉有一股罪恶感,不再找miku,miku很嫉妒luka,于是用刀刺伤了kaito,又拿起刀子自杀。之后好友Rin带len来看好朋友miku,len【镜音レン】喜欢上了初音,但是rin【镜音リン】很久就喜欢len,知道了len喜欢上miku然后产生憎恨,不希望len再一次离开自己,最后用刀刺向len,想把len带走。让miku想起了acute里的情节(她想把kaito带走这一个情节),被miku拦住了,刀刺中了miku。在最后的片段kaito在luka的照顾下康复,rin在miku身旁痛苦地哭泣。(本为官网剧情)。
收起全文
精华内容
参与话题
问答
  • React是目前最火的前端框架之一,就业薪资很高,本课程教您如何快速学会React并应用到实战,教你如何解决内存泄漏,常用UI库的使用,自己封装组件,正式上线白屏问题,性能优化等。对正在工作当中或打算学习React...
  • React.js框架技术

    2020-03-24 14:56:07
    react.js零基础入门到项目实战,全面介绍react框架开发全家桶技术,基于create-react-app脚手架,结合实战案例,由浅入深,通俗易懂并全面的介绍react.js技术点,引入redux数据管理框架,全面详细介绍redux基础与...
  • 半小时深刻理解React

    千次阅读 多人点赞 2016-10-19 11:37:15
    声明:本文来自腾讯增值产品部官方公众号小时光茶社,为CSDN原创投稿,未经许可,禁止任何形式的转载。 作者:左明,企鹅电竞前端团队leader,腾讯高级工程师。从事web开发超过8年,主导过微云web版、腾讯云助手...

    声明:本文来自腾讯增值产品部官方公众号小时光茶社,为CSDN原创投稿,未经许可,禁止任何形式的转载。
    作者:左明,企鹅电竞前端团队leader,腾讯高级工程师。从事web开发超过8年,主导过微云web版、腾讯云助手、手Q游戏公会、企鹅电竞App等项目,有丰富的前端架构经验。
    责编:钱曙光,关注架构和算法领域,寻求报道或者投稿请发邮件qianshg@csdn.net,另有「CSDN 高级架构师群」,内有诸多知名互联网公司的大牛架构师,欢迎架构师加微信qshuguang2008申请入群,备注姓名+公司+职位。

    首先,我们来看看React在世界范围的热度趋势,下图是关键词“房价”和“React”在Google Trends上的搜索量对比,蓝色的是React红色的是房价,很明显,人类对React的关注程度已经远远超过了对房价的关注。

    从这些数据中,大家能看出什么?

    可以很明显的看出,我在一本正经的扯淡。

    图片描述

    图片描述

    从2014年到现在,React、jQuery和Angular的热度趋势对比,可以很明显的看到(上图),React在全球的热度趋势增长非常快。

    图片描述

    上图是React在国内的百度搜索指数,是拿React和Node.js做了个对比,可以看出React的关注度也已经超过了Node.js。

    虽然在关注总量上React还远不及jQuery和Angular等,但其增长速度超乎想象,你知道这意味着什么吗?

    这意味着关注React,你就已经走在了业界的前沿!

    那么React到底是什么鬼?

    图片描述

    官网说“一个用来构建用户界面的JavaScript库”。

    React起源于Facebook的内部项目,由于React的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来Web开发的主流工具。

    和Backbone、Angular等MV*框架不一样,它只处理View逻辑。所以如果你喜欢它,你可以很容易的将它接入到现有工程中,然后用React重写HTML部分即可,不用修改逻辑。

    React这么火,那么它到底有什么牛逼的地方?

    图片描述

    上图是2015年年初的数据。

    图片描述

    这是Facebook的好友动态页面,也是Facebook访问量最大的页面没有之一,通过Chrome React插件可以看到这个页面确实是用React实现的。

    图片描述

    这个图截的比较早,现在应该更多了。

    图片描述

    前面给大家来了一波前戏,相信大家已经有点迫不及待了,那么,进入正题:

    首先,先跟大家描述下React最特别的部分,听完这部分大家基本就能够在脑海里建立起一个React的大致印象。

    然后是React的核心内容,听完这部分大家待会回去就可以开始写代码了。

    最后是React能够给我们实际带来的价值,我们不做无意义的重构。

    首先,我们来看JSX——

    图片描述

    我们先说说模板。

    HTML模板有很多种编写方式,这是一种:

    图片描述

    还有这种:

    图片描述

    还有多年前流行的Script标签模板。

    这种运行时编译的模板调试起来比较困难,尤其是出现错误时,很难定位,如果要定位,需要做很多额外的工作,麻烦。

    图片描述

    近两年构建工具的流行,很多团队已经在使用Grunt等构建工具来预编译模板,从而简化模板开发,提高运行效率,减少维护成本。

    图片描述

    JSX使用的也是预编译模板,和前面看到的没什么差别,细心的同学会发现好像只是没有字符串引号了而已。

    React提供了一个预编译工具,叫react-tools,可以通过NPM命令安装,一般是在开发时期运行,运行后它会启动一个监听程序,实时监听JSX源码的修改,然后自动编译为JS代码。

    大家留意下,里面的UL LI被编译成了React.createElement(),它这么做,目的就是为了实现虚拟DOM。

    JSX还支持运行时编译,但是为了推荐大家用预编译,所以我不打算告诉你们运行时编译怎么做。

    祝贺大家已经精通了JSX。

    接下来我们来了解React最大的亮点——虚拟DOM。

    图片描述

    传统Web App和DOM直接交互,由App来控制DOM的构建和渲染、元素属性的读写、事件的注册和销毁等。

    当新产品刚上线时,这种做法看起来也挺好。但随着产品功能越来越丰富、代码量越来越多、开发团队人员越来越多——

    一年后,你的代码可能会变成这样:

    图片描述

    当然,合理的代码规划能够避免这类问题,但团队里难免会有擅长屠宰式编程的同学,分分钟把你代码改得面目全非。

    这时,React的虚拟DOM和单项数据流就能很好的解决这个问题。

    图片描述

    虚拟DOM则是在DOM的基础上建立了一个抽象层,我们对数据和状态所做的任何改动,都会被自动且高效的同步到虚拟DOM,最后再批量同步到DOM中。

    图片描述

    虚拟DOM会使得App只关心数据和组件的执行结果,中间产生的DOM操作不需要App干预,而且通过虚拟DOM来生成DOM,会有一项非常可观的收益——性能。

    图片描述

    所有人都知道DOM操作慢,渲染一个空的DIV,浏览器需要为这个DIV生成几百个属性,而虚拟DOM只有6个。

    所以说减少不必要的重排重绘以及DOM读写能够对页面渲染性能有大幅提升。

    那么我们来看看虚拟DOM是怎么做的。React会在内存中维护一个虚拟DOM树,当我们对这个树进行读或写的时候,实际上是对虚拟DOM进行的。当数据变化时,然后React会自动更新虚拟DOM,然后拿新的虚拟DOM和旧的虚拟DOM进行对比,找到有变更的部分,得出一个Patch,然后将这个Patch放到一个队列里,最终批量更新这些Patch到DOM中。

    这样的机制可以保证即便是根节点数据的变化,最终表现在DOM上的修改也只是受这个数据影响的部分,这样可以保证非常高效的渲染。

    但也是有一定的缺陷的——首次渲染大量DOM时因为多了一层虚拟DOM的计算,会比innerHTML插入方式慢。

    图片描述

    一个最基本的React组件由数据和JSX两个主要部分构成,我们先来看看数据。

    图片描述

    这是一个简单单完整的React组件【类】,细节大家先不用太在意细节,了解机制就可以。

    props主要作用是提供数据来源,可以简单地理解为props就是构造函数的参数。

    state唯一的作用是控制组件的表现,用来存放会随着交互变化的状态,比如开关状态等。

    JSX做的事情就是根据state和props中的值,结合一些视图层面的逻辑,输出对应的DOM结构。

    组件复合:

    前面我们知道了一个简单组件的构成,但单个组件肯定不能满足实际需求,我们需要做的是将这些独立的组件进行组装,同时找出共性的部分进行复用。

    图片描述

    我们拿这么一个网站来进行示例,大的模块有中心主体区域,左侧有K吧列表、应用列表等,可以看出,里面的

    都是最细粒度的组件,可以复用。

    首先,我们来看下Article的代码——

    图片描述

    这就是我们分解出来的Article组件,它需要2个属性,Article对象和ShowImage。Article对象包含图片、地址、标题、描述信息,ShowImage是一个布尔类型,用来判断是否需要显示成一个图片。

    这个组件本身的实现可以很简单也可以很复杂,但使用者可不关心你的内部实现,使用者关心组件需要什么参数就可以了。

    外国人的组件化思想比我们国内的普及程度高很多,不只局限于软件开发,包括实体行业的咖啡机、加油站、儿童摇摇车都有这种设计思想在里面。

    将复杂的UI逻辑保留在组件内部。

    希望大家在设计模块的时候,也尽可能将组件逻辑对外透明,来减少维护成本。

    图片描述

    我们继续看热点区域,大家留意一下标虚线的部分,这里复用了Article组件。这时的Article组件看起来就是一个普通的标签而已,简单吧。

    图片描述

    这个是热问组件,也复用了Article组件。

    这就是React如丝般顺滑的组件复合。

    图片描述

    这个,叫做竹笕,是中日传统禅文化中常见的庭院装饰品,它的构造可简单可复杂,但原理很简单,比如这个竹笕,水从竹笕顶部入口流入内部,并按照固定的顺序从上向下依次流入各个小竹筒,然后驱动水轮转动。对于强迫症患者来说,观赏竹笕绝对是一个很享受的过程,你会发现这些小玩意竟然能这么流畅的协调起来,好神奇。

    如果竹笕是一个组件的话,那么水就是组件的数据流。

    在React中,数据流是自上而下单向地从父节点传递到子节点,所以组件是简单且容易把握的,他们只需要从父节点提供的props中获取数据并渲染即可。如果顶层组件的某个prop改变了,React会递归地向下遍历整棵组件数,重新渲染所有使用这个属性的组件。

    图片描述

    图片描述

    这个是前面看到的KM热点问题组件,拥有一个叫做Articles的属性。

    在组件内部,可以通过this.props来访问props,props是组件唯一的数据来源,对于组件来说:

    props永远是只读的。

    不要尝试在组件内部调用setProps的方法来修改props,如果你不小心这么做了,React会报错并给出非常详细的错误提示。

    组件的属性类型如果不进行声明和验证,那么很可能使用者传给你的属性值或者类型是无效的,那会导致一些意料之外的故障。好在React已经为我们提供了一套非常简单好用的属性校验机制——

    图片描述

    React有一个PropTypes属性校验工具,经过简单的配置即可。当使用者传入的参数不满足校验规则时,React会给出非常详细的警告,定位问题不要太容易哦。

    图片描述

    PropTypes包含的校验类型包括基本类型、数组、对象、实例、枚举——

    图片描述

    以及对象类型的深入验证等。如果内置的验证类型不满足需求,还可以通过自定义规则来验证。

    如果某个属性是必须的,在类型后面加上isRequired就可以了。

    state:

    React的一大优点,就是把每一个组件都看成是一个状态机,组件内部通过state来维护组件状态的变化,这也是state唯一的作用。

    图片描述

    state一般和事件一起使用,我们先看state,然后看看state和事件怎样结合。

    这是一个简单的开关组件,开关状态会以文字的形式表现在按钮的文本上。

    首先看render方法,返回了一个button元素,给button注册了一个事件用来处理点击事件,在点击事件中对state的on字段取反,并执行this.setState()方法设置on字段的新值。一个开关组件就完成了。

    组件渲染完成后,必须有UI事件的支持才能正常工作。

    图片描述

    React通过将事件处理器绑定到组件上来处理事件。

    React事件本质上和原生JS一样,鼠标事件用来处理点击操作,表单事件用于表单元素变化等,React事件的命名、行为和原生JS差不多,不一样的地方是React事件名区分大小写。

    比如这段代码中,Article组件的section节点注册了一个onClick事件,点击后弹出alert。

    有时候,事件的处理器需要由组件的使用者来提供,这时可以通过props将事件处理器传进来。

    图片描述

    这个是刚才那个Article组件的使用者,它提供给Article组件的props中包含了一个onClick属性,这个onClick指向这个组件自身的一个事件处理器,这样就实现了在组件外部处理事件回调。

    图片描述

    这是一个React组件实现组件可交互所需的流程,render()输出虚拟DOM,虚拟DOM转为DOM,再在DOM上注册事件,事件触发setState()修改数据,在每次调用setState方法时,React会自动执行render方法来更新虚拟DOM,如果组件已经被渲染,那么还会更新到DOM中去。

    图片描述

    这些是React目前支持的事件列表。

    React的组件拥有一套清晰完整而且非常容易理解的生命周期机制,大体可以分为三个过程:初始化、更新和销毁,在组件生命周期中,随着组件的props或者state发生改变,它的虚拟DOM和DOM表现也将有相应的变化。

    图片描述

    首先是初始化过程,这里不太好理解,字比较多。

    组件类在声明时,会先调用getDefaultProps()方法来获取默认props值,这个方法会且只会在声明组件类时调用一次,这一点需要注意,它返回的默认props由所有实例共享。

    在组件被实例化之前,会先调用一次实例方法getInitialState()方法,用于获取这个组件的初始state。

    实例化之后就是渲染,componentWillMount方法会在生成虚拟DOM之前被调用,你可以在这里对组件的渲染做一些准备工作,比如计算目标容器尺寸然后修改组件自身的尺寸以适应目标容器等。

    接下来就是渲染工作,在这里你会创建一个虚拟DOM用来表示组件的结构。对于一个组件来说,render是唯一一个必须的方法。render方法需要满足这几点:

    1. 只能通过this.props或this.state访问数据
    2. 只能出现一个顶级组件
    3. 可以返回null、false或任何React组件
    4. 不能对props、state或DOM进行修改

    需要注意的是,render方法返回的是虚拟DOM。

    渲染完成以后,我们可能需要对DOM做一些操作,比如截屏、上报日志,或者初始化iScroll等第三方非React插件,可以在componentDidMount()方法中做这些事情。当然,你也可以在这个方法里通过this.getDOMNode()方法最终生成DOM节点,然后对DOM节点做爱做的事情,但需要注意做好安全措施,不要缓存已经生成的DOM节点,因为这些DOM节点随时可能被替换掉,所以应该在每次用的时候去读取。

    组件被初始化完成后,它的状态会随着用户的操作、时间的推移、数据更新而产生变化,变化的过程是组件声明周期的另一部分——

    图片描述

    上图为更新过程。

    当组件已经被初始化后,组件调用者修改组件的属性时,组件的componentWillReceiveProps()方法会被调用,在这里,你可以对外部传入的数据进行一些预处理,比如从props中读取数据写入state。

    默认情况下,组件调用者修改组件属性时,React会遍历这个组件的所有子组件,进行“灌水”,将props从上到下一层一层传下去,并逐个执行更新操作,虽然React内部已经进行过很多的优化,这个过程是很快的,如果你追求极致性能或者你发现这个过程花费了太久时间,使用shouldComponentUpdate()——

    有时候,props发生了变化,但组件和子组件并不会因为这个props的变化而发生变化,打个比方,你有一个表单组件,你想要修改表单的name,同时你能够确信这个name不会对组件的渲染产生任何影响,那么你可以直接在这个方法里return false来终止后续行为。这样就能够避免无效的虚拟DOM对比了,对性能会有明显提升。

    如果这个时候有同学仍然饥渴难耐,那么你可以尝试 不可变数据结构(用过mongodb的同学应该懂)。

    组件在更新前,React会执行componentWillUpdate()方法,这个方法类似于前面看到的componentWillMount()方法,唯一不同的地方只是这个方法在执行的时候组件是已经渲染过的。需要注意的是,不可以在这个方法中修改props或state,如果要修改,应当在componentWillReceiveProps()中修改。

    然后是渲染,React会拿这次返回的虚拟DOM和缓存中的虚拟DOM进行对比,找出【最小修改点】,然后替换。

    更新完成后,React会调用组件的componentDidUpdate方法,这个方法类似于前面的componentDidMount方法,你仍然可以在这里可以通过this.getDOMNode()方法取得最终的DOM节点。

    香港电影结尾经常看到一个剧情,就是英雄打败了坏人,然后警察出来擦屁股——

    销毁:

    图片描述

    警察偶尔还能立功,而componentWillUnmount最可怜,他除了擦屁股什么也做不了。

    你可以在这个方法中销毁非React组件注册的事件、插入的节点,或者一些定时器之类。这个过程要小心处理,如果处理不当可能导致内存泄漏,比如实例化了某个第三方插件却没销毁,这个React可帮不了你,你自己约的炮,含着泪也要打完。

    看demo点我(记得打开控制台)

    下面我们来看看React怎样结合Node.js实现服务端渲染。

    服务端渲染有多快我就不多说了。

    因为有虚拟DOM的存在,React可以很容易地将虚拟DOM转换为字符串,这便使我们可以只写一份UI代码,同时运行在node里和浏览器里。

    在node里将组件渲染为一段HTML只需要一句代码。

    图片描述

    不过围绕这个renderToString我们还要做一些准备工作。代码有点多:

    图片描述

    这是一个express的路由方法,这段代码做了这几件事:

    1. 获取数据(这里是on=true)
    2. 引入要渲染的React组件(Switching)
    3. 调用React.renderToString()方法来生成HTML
    4. 最后同时发送HTML和JSON数据给浏览器

    需要注意的是这里的JSON字符串中可能出现结尾标签或HTML注释,可能会导致语法错误,这里需要进行转义。

    图片描述

    页面的示例代码本来打算用大家更熟悉的HTML,但是为了便于阅读,所以换成了jade代码。

    这个页面做了这几件事:

    1. 将前面在action里生成的HTML写到#container元素里(服务端运行)
    2. 将服务端的JSON数据输出到全局变量gDataForClient中(服务端运行)
    3. 引入必须的JS文件(服务端运行)
    4. 获取全局变量 gDataForClient(客户端运行)
    5. 尝试重新渲染组件(客户端运行)

    这就是React的服务端渲染,组件的代码前后端都可以复用。

    当然React适用的场景不止这些

    图片描述

    (实际数据以GitHub为准)

    React-Native能够用一套代码同时运行在浏览器和node里,而且能够以原生App的姿势运行在iOS和Android系统中,既拥有Web迭代迅速的特性,又拥有原生App的体验。

    这是React和React-Native在GitHub上的数据,可以看出React-Native也是相当热门——因为React-Native能够使React的价值最大化——对老板来说,意味着有前端工程师也可以终端开发,节省了人力成本,同时对前端开发工程师来说又多了一个走向全栈的捷径。

    了解iOS开发的同学都知道,苹果爸爸对应用上架的流程和审核效率实在让人无力吐槽,很多团队上一个版本还没审核结束,下一个版本就已经做好了。而React-Native支持从网络拉取JS,这样iOS应用也能够像Web一样实现快速迭代,然后绕过苹果爸爸的审核。

    图片描述

    这个是react-native的调试过程。

    最后简单说说单元测试,单元测试的姿势是对各个模块进行最小范围的测试。

    以往对前端的UI进行单元测试,都需要依靠phantomjs、casperjs等沙盒浏览器内核,模拟用户操作,并检查操作后的DOM状态,比较繁琐,维护成本高得吓人。但有了React的虚拟DOM以后就简单多了。

    我们来演示一个checkbox的单元测试过程。

    图片描述

    图片描述

    因为虚拟DOM的存在,使得react的代码很容易做好单元测试,这是上面那段代码的测试用例,通过karma执行后即可看到结果。

    图片描述

    当然,React的缺点也比较明显:

    图片描述

    长路漫漫,唯剑作伴,一统江湖,匹夫有责。最后,祝大家早日占领终端和后台,走自己的路,让他们无路可走。


    编辑推荐:架构技术实践系列文章(部分):


    2016年11月18日-20日,由CSDN重磅打造的年终技术盛会SDCC 2016中国软件开发者大会将在北京举行,大会秉承干货实料(案例)的内容原则,本次大会共设置了12大专题、近百位的演讲嘉宾,并邀请业内顶尖的CTO、架构师和技术专家,与参会嘉宾共同探讨电商架构、高可用架构、编程语言、架构师进阶、微信开发、前端、平台架构演进、基于Spark的大数据系统设计、自动化运维与容器实践、高吞吐数据库系统设计要领、移动视频直播技术等。10月14日仍是五折抢票,最低1400元,注册参会

    展开全文
  • 十分详细的React入门实例

    万次阅读 多人点赞 2016-09-26 10:06:23
    学习React不是一蹴而就的事情,入门似乎也没那么简单。但一切都是值得的。今天给大家带来一个详细的React的实例,实例并不难,但对于初学者而言,足够认清React的思考和编写过程。认真完成这个实例的每一个细节会让...

    学习React不是一蹴而就的事情,入门似乎也没那么简单。但一切都是值得的。

    今天给大家带来一个详细的React的实例,实例并不难,但对于初学者而言,足够认清React的思考和编写过程。认真完成这个实例的每一个细节会让你受益匪浅。接下来我们开始吧!

    代码下载

    预览

    首先说明一下,本例究竟做了什么。本文实现了一个单页面人员管理系统的前台应用。包括以下功能:

    • 人员基本信息列表;
    • 人员的录入及删除;
    • 人员详细信息的查看;
    • 人员信息的编辑;
    • 根据人员身份进行筛选;
    • 根据人员某些属性进行排序;
    • 根据人姓名、年龄、身份、性别等关键字进行人员搜索。

    页面预览如下:

    图1

    图2

    为了更好地学习,请先到这里去感受一下:

    人员管理系统

    代码下载

    本文构建React组件的时候,使用了es6的语法,最终用webpack打包。最好有相关基础,我会在相关的地方进行言简意赅的说明。

    第一步:划分UI Component

    React is all about modular, composable components.

    React是模块化、组件化的。我们这里第一步要做的就是将应用划分成各个组件。我在图一、图二的基础上圈出了我们即将实现的各个组件。结果如图三、图四所示:

    图3

    图4

    每个圈出的组件功能如下,这是本应用的框架,请大家务必看清楚,其中斜体字是各个组件的名称:

    • ManageSystem 图三最外层的红色方框,这是管理模块的最外层容器,容纳整个应用;
    • StaffHeader 图三最上层蓝色方框,该模块接收用户操作的输入,包括关键字搜索输入、筛选条件以及排序方式;
    • StaffItemPanel 图三中间蓝色方框,该模块用于展示所有基于用户操作(关键字搜索、筛选、排序)结果的条目;
    • StaffFooter 图三最下层蓝色方框,该模块用于新人员的添加;
    • StaffItem 图三内层的红色方框,该模块用于展示一条人员的基本信息,包括删除和详情操作的按钮;
    • StaffDetail 图四的红色方框,每当点击StaffItem的’详情’后会显示该条目的详细信息。该模块用于展示人员的详细信息,兼有人员信息编辑的功能。

    为了更清楚地展示框架结构:

    ManageSystem
    
        StaffHeader
    
        StaffItemPanel
    
            StaffItem
            StaffItem
            StaffItem...
    
        StaffFooter
    
        StaffDetail(只在点击某条目的详情后展示)
    

    第二步:构建静态版的React应用

    在第一步中我们已经划分了各个组件,也说明了各个组件的职责。接下来我们分步完成我们的应用,首先我们做一个静态版的React,只用于render UI组件,但并不包含任何交互。

    这个步骤我们只需要参照图一、图二去做就好了,绝大部分工作基本上就是使用JSX按部就班地写html代码。这个过程不需要太多思考。每个组件中都仅仅只包含一个render()方法。

    需要注意的是,静态版的应用,数据由父组件通过props属性向下传递,state属性是用不到的,记住,state仅仅为动态交互而生。

    本应用的组件相对较多,我们不妨采用bottom-up的方式,从子组件开始。

    好了,我们开始吧。

    StaffHeader

    首先以StaffHeader为例,创建一个StaffHeader.js文件。如下:

    
    import React from 'react';
    export default class StaffHeader extends React.Component{
    
        render(){
            return (
              <div>
                  <h3 style={{'text-align':'center'}}>人员管理系统</h3>
                  <table className="optHeader">
                    <tbody>
                      <tr>
                        <td className="headerTd"><input type='text' placeholder='Search...' /></td>
                        <td className="headerTd">
                            <label for='idSelect'>人员筛选</label>
                            <select id='idSelect'>
                                <option value='0'>全部</option>
                                <option value='1'>主任</option>
                                <option value='2'>老师</option>
                                <option value='3'>学生</option>
                                <option value='4'>实习</option>
                            </select>
                        </td>
                        <td>
                            <label for='orderSelect'>排列方式</label>
                            <select id='orderSelect'>
                                <option value='0'>身份</option>
                                <option value='1'>年龄升</option>
                                <option value='2'>年龄降</option>
                            </select>
                        </td>
                      </tr>
                    </tbody>
                  </table>
              </div>
            );
        }
    }

    该组件主要用于提供搜索框,人员筛选下拉框以及排列方式下拉框。没错,我们首先就是要搭建一个静态版的React。呈现的样子参考图三最上方的蓝色框。当然,为了实现最终的样式,需要css的配合,css不是本文的关注点,本应用的css也十分简单,自行查看源代码。

    StaffItem

    StaffItem是每个具体人员的基本信息组件,用于展示人员的基本信息并接收用户的删除和点击详情的操作。新建一个StaffItem.js(该组件在StaffItemPanel中被引用):

    
    import React from 'react';
    export default class StaffItem extends React.Component{
    
        render(){
            return (
                  <tr
                    style={{'cursor': 'pointer'}}
                  >
                    <td className='itemTd'>{this.props.item.info.name}</td>
                    <td className='itemTd'>{this.props.item.info.age}</td>
                    <td className='itemTd'>{this.props.item.info.id}</td>
                    <td className='itemTd'>{this.props.item.info.sex}</td>
                    <td className='itemTd'>
                        <a className="itemBtn">删除</a>
                        <a className="itemBtn">详情</a>
                    </td>
                  </tr>
            );
        }
    }

    StaffItemPanel

    接下来是StaffItemPanel,该组件仅用于展示由父组件传入的各个人员条目,新建一个StaffItemPanel.js文件:

    
    import React from 'react';
    import StaffItem from './StaffItem.js';
    export default class StaffItemPanel extends React.Component{
    
        render(){
            let items = [];
    
            if(this.props.items.length == 0) {
                items.push(<tr><th colSpan="5" className="tempEmpty">暂无用户</th></tr>);
            }else {
                this.props.items.forEach(item => {
                    items.push(<StaffItem key={item.key} item={item}/>);
                });
            }
    
            return (
              <table className='itemPanel'>
                <thead>
                    <th className='itemTd'>姓名</th>
                    <th className='itemTd'>年龄</th>
                    <th className='itemTd'>身份</th>
                    <th className='itemTd'>性别</th>
                    <th className='itemTd'>操作</th>
                </thead>
                <tbody>{items}</tbody>
              </table>
            );
        }
    }

    该组件的功能相对简单,其中

    
            if(this.props.items.length == 0) {
                items.push(<tr><th colSpan="5" className="tempEmpty">暂无用户</th></tr>);
            }else {
                this.props.items.forEach(item => {
                    items.push(<StaffItem key={item.key} item={item} />);
                });
            }

    是为了在暂无条目的时候给出相应的提示,如下图:

    图5

    StaffFooter

    StaffFooter组件的功能是添加新人员,新建StaffFooter.js文件:

    
    import React from 'react';
    export default class StaffFooter extends React.Component{
    
        render(){
            return (
              <div>
                <h4 style={{'text-align':'center'}}>人员新增</h4>
                <hr/>
                <form ref='addForm' className="addForm">
                    <div>
                      <label for='staffAddName' style={{'display': 'block'}}>姓名</label>
                      <input ref='addName' id='staffAddName' type='text' placeholder='Your Name'/>
                    </div>
                    <div>
                      <label for='staffAddAge' style={{'display': 'block'}}>年龄</label>
                      <input ref='addAge' id='staffAddAge' type='text' placeholder='Your Age(0-150)'/>
                    </div>
                    <div>
                      <label for='staffAddSex' style={{'display': 'block'}}>性别</label>
                      <select ref='addSex' id='staffAddSex'>
                        <option value='男'></option>
                        <option value='女'></option>
                      </select>
                    </div>
                    <div>
                      <label for='staffAddId' style={{'display': 'block'}}>身份</label>
                      <select ref='addId' id='staffAddId'>
                        <option value='主任'>主任</option>
                        <option value='老师'>老师</option>
                        <option value='学生'>学生</option>
                        <option value='实习'>实习</option>
                      </select>
                    </div>
                    <div>
                      <label for='staffAddDescrip' style={{'display': 'block'}}>个人描述</label>
                      <textarea ref='addDescrip' id='staffAddDescrip' type='text'></textarea>
                    </div>
                    <p ref="tips" className='tips' >提交成功</p>
                    <p ref='tipsUnDone' className='tips'>请录入完整的人员信息</p>
                    <p ref='tipsUnAge' className='tips'>请录入正确的年龄</p>
                    <div>
                      <button>提交</button>
                    </div>
                </form>
              </div>
            )
        }
    }

    代码看起来比较长,其实就是一个html表单,这个步骤基本都是不需要太多思考的操作,代码也没有任何理解上的难度,记住,我们现在就是要把整个框架搭起来,做一个静态版的应用!同样的,呈现出最终的样式,需要一些css,自行参考源代码。呈现的样子见图三最下面的蓝色方框。

    StaffDetail

    通常情况下,该组件是不显示的,只有当用户点击某条目的详情的时候,我用了一种动画效果将该组件’浮现出来’。方法就是在css中将该组件的z-index设置为一个很大的值,比如100,然后通过逐渐改变背景透明度的动画实现浮现的效果。目前我们只需要做一个静态版的React,尚未实现用户点击操作的交互,所以这里只需要创建以下js文件,并在css中将.overLay的display设置为none就可以了,源码中的css文件已经做好了。

    
    import React from 'react';
    export default class StaffDetail extends React.Component{
    
        render(){
          let staffDetail = this.props.staffDetail;  
          if(!staffDetail)
            return null;
    
          return (
              <div className="overLay">
                <h4 style={{'text-align':'center'}}>点击'完成'保存修改,点击'关闭'放弃未保存修改并退出.</h4>
                <hr/>
                <table ref="editTabel">
                  <tbody>
                    <tr>
                      <th>姓名</th>
                      <td><input id='staffEditName' type="text" defaultValue={staffDetail.info.name}></input></td>
                    </tr>
                    <tr>
                      <th>年龄</th>
                      <td><input id='staffEditAge' type="text" defaultValue={staffDetail.info.age}></input></td>
                    </tr>
                    <tr>
                      <th>性别</th>
                      <td>
                        <select ref='selSex' id='staffEditSex'>
                          <option value="男"></option>
                          <option value="女"></option>
                        </select>
                      </td>
                    </tr>
                    <tr>
                      <th>身份</th>
                      <td>
                        <select ref="selId" id='staffEditId'>
                          <option value="主任">主任</option>
                          <option value="老师">老师</option>
                          <option value="学生">学生</option>
                          <option value="实习">实习</option>
                        </select>
                      </td>
                    </tr>
                    <tr>
                      <th>个人描述</th>
                      <td><textarea id='staffEditDescrip' type="text" defaultValue={staffDetail.info.descrip}></textarea></td>
                    </tr>
                  </tbody>
                </table>
                <p ref='Dtips' className='tips'>修改成功</p>
                <p ref='DtipsUnDone' className='tips'>请录入完整的人员信息</p>
                <p ref='DtipsUnAge' className='tips'>请录入正确的年龄</p>
                <button>完成</button>
                <button>关闭</button>
              </div>
          );
        }
    }

    和staffFooter类似,这里主要就是一个表单。

    ManageSystem

    子组件都已经做好了,接下来就是最外层的容器了。按部就班,新建一个ManageSystem.js:

    
    import React from 'react';
    import StaffHeader from './StaffHeader.js';
    import StaffItemPanel from './StaffItemPanel.js';
    import StaffFooter from './StaffFooter.js';
    import StaffDetail from './StaffDetail.js';
    
    var rawData = [{ info: {descrip:'我是一匹来自远方的狼。', sex: '男', age: 20, name: '张三', id: '主任'}},
                   { info: {descrip:'我是一匹来自远方的狼。', sex: '女', age: 21, name: '赵静', id: '学生'}},
                   { info: {descrip:'我是一匹来自远方的狼。', sex: '女', age: 22, name: '王二麻', id: '学生'}},
                   { info: {descrip:'我是一匹来自远方的狼。', sex: '女', age: 24, name: '李晓婷', id: '实习'}},
                   { info: {descrip:'我是一匹来自远方的狼。', sex: '男', age: 23, name: '张春田', id: '实习'}},
                   { info: {descrip:'我是一匹来自远方的狼。', sex: '男', age: 22, name: '刘建国', id: '学生'}},
                   { info: {descrip:'我是一匹来自远方的狼。', sex: '男', age: 24, name: '张八', id: '主任'}},
                   { info: {descrip:'我是一匹来自远方的狗。', sex: '男', age: 35, name: '李四', id: '老师'}},
                   { info: {descrip:'我是一匹来自远方的猪。', sex: '男', age: 42, name: '王五', id: '学生'}},
                   { info: {descrip:'我是一匹来自远方的牛。', sex: '男', age: 50, name: '赵六', id: '实习'}},
                   { info: {descrip:'我是一匹来自远方的马。', sex: '男', age: 60, name: '孙七', id: '实习'}}];
    
    class App extends React.Component {
    
        render(){
          return (
            <div>
              <StaffHeader/>
              <StaffItemPanel items={rawData} />
              <StaffFooter/>
              <StaffDetail/>
            </div>
          );
        }
    }
    
    React.render(<App />, document.getElementById('app'));

    以上代码中rawData是演示数据,生产中的数据应该从数据库获得,这里为了简便,直接生成了11条演示用的数据。

    第三步:编译并打包

    在第二步中,我们已经生成了各个component以及subcomponent。主要的任务已经完成了,这一步是做什么的呢?

    简单地说,上文中我们编写React Component的过程中,使用了es6和JSX的语法。(特别值得一提的是es6的Module,终于从语言规格上让Javascript拥有了模块功能。如今js渐入佳境,学习es6是十分重要且值得的!)但这些目前是不能被浏览器直接支持的。所以在使用之前,要先经过’编译’,这个过程我们是使用Babel完成的。

    关于Babel,正如其官网所言–Babel is a Javascript compiler.本应用中,它帮我们完成了es6以及JSX的编译。只不过在本例中babel是以webpack的loader的方式出现的。

    关于webpack这里也不多言了–webpack is a module bundler.请大家自己查阅相关资料。

    安装依赖项

    在这里,首先执行以下命令,安装开发依赖:

    npm install
    

    该命令会自动读取当前目录下的package.json文件,并自行安装其中的依赖项。文件内容如下:

    {
      "name": "StaffManage",
      "version": "1.0.0",
      "description": "",
      "main": "",
      "scripts": {
        "start": "webpack"
      },
      "author": "WYH",
      "license": "ISC",
      "devDependencies": {
        "babel-core": "^6.14.0",
        "babel-loader": "^6.2.5",
        "babel-preset-es2015": "^6.14.0",
        "babel-preset-react": "^6.11.1",
        "webpack": "^1.13.2"
      }
    }
    

    更具体地说,其中的开发依赖项就是

      "devDependencies": {
        "babel-core": "^6.14.0",
        "babel-loader": "^6.2.5",
        "babel-preset-es2015": "^6.14.0",
        "babel-preset-react": "^6.11.1",
        "webpack": "^1.13.2"
      }
    

    编译打包

    安装开发依赖项后,接下来就是使用webpack打包了,webpack的loader在解析文件的时候会自动使用babel对文件进行编译。配置文件如下:

    module.exports = {
        entry: __dirname + '/src/ManageSystem.js',
        output: {
            path: __dirname + '/build',
            filename: "bundle.js"
        },
        externals: {
            'react': 'React'
        },
        devtool: 'eval-source-map',  //生成source file
        module: {
            loaders: [
              {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel',
                query: {
                  presets: ['es2015', 'react']
                }
              }
            ]
        }
    };
    

    将第二步中的所有组件都放到当前目录下的src目录中,目录结构可以参考源代码,然后执行以下命令:

    npm start
    

    该命令也是在package.json中指定的。

    "scripts": {
      "start": "webpack"
    }
    

    好了,在build目录下应该已经生成bundle.js文件,这就是我们打包好的文件,我们只需要在html中引用它就行了。

    在当前目录下生成html文件如下:

    <!DOCTYPE html>
    <html lang="zh-CN">
      <head>
        <meta charset="utf-8">
        <title>人员管理</title>
        <link href="build/style.css" rel="stylesheet" />
    
      </head>
      <body>
          <div id="app">
          </div>
    
          <script src="http://cdn.bootcss.com/react/0.13.3/react.min.js"></script>
          <script src="build/bundle.js"></script>
      </body>
    </html>

    接下来在浏览器中打开index.html看看吧,静态版的React已经生成了,只是还没有动态交互而已。至此,已经完成了构建静态版的React的工作,大框架已经建立,接下来我们让它动起来!

    第四步:添加STAFF类

    本文应用涉及的功能有排序,筛选、新增、删除、修改以及关键字搜索等。功能较多,业务逻辑有些复杂。为了让React集中精力完成view层的事情,我们这里新建一个STAFF类来完成业务逻辑。

    Javascript中,类的实现是基于其原型继承机制的。但在es6中,提供了更接近传统面向对象语言的写法,引入了类(class)的概念。我们可以通过class关键字来定义类。实际上,es6的class只是一个语法糖(syntax sugar),它的绝大部分功能,es5均可以做到。而引入的class写法,是为了让对象的写法更加清晰、更加具有面向对象的感觉。

    接下来我们新建一个STAFF.js文件:

    
    class staffItem {
        constructor(item){
            this.info = {};
            this.info.name = item.name;
            this.info.age = item.age || 0;
            this.info.sex = item.sex;
            this.info.id = item.id;
            this.info.descrip = item.descrip || '';
            this.key = ++staffItem.key;
        }
    }
    staffItem.key = 0;
    
    export default class STAFF {
    
        constructor(){
            this.allStaff = [
                new staffItem(STAFF.rawData[0]),
                new staffItem(STAFF.rawData[1]),
                new staffItem(STAFF.rawData[2]),
                new staffItem(STAFF.rawData[3]),
                new staffItem(STAFF.rawData[4]),
                new staffItem(STAFF.rawData[5]),
                new staffItem(STAFF.rawData[6]),
                new staffItem(STAFF.rawData[7]),
                new staffItem(STAFF.rawData[8]),
                new staffItem(STAFF.rawData[9]),
                new staffItem(STAFF.rawData[10])
            ];
            this.staff = this.allStaff;
        }
    }
    
    STAFF.rawData = [{ descrip:'我是一匹来自远方的狼。', sex: '男', age: 20, name: '张三', id: '主任'},
                     { descrip:'我是一匹来自远方的狼。', sex: '女', age: 21, name: '赵静', id: '学生'},
                     { descrip:'我是一匹来自远方的狼。', sex: '女', age: 22, name: '王二麻', id: '学生'},
                     { descrip:'我是一匹来自远方的狼。', sex: '女', age: 24, name: '李晓婷', id: '实习'},
                     { descrip:'我是一匹来自远方的狼。', sex: '男', age: 23, name: '张春田', id: '实习'},
                     { descrip:'我是一匹来自远方的狼。', sex: '男', age: 22, name: '刘建国', id: '学生'},
                     { descrip:'我是一匹来自远方的狼。', sex: '男', age: 24, name: '张八', id: '主任'},
                     { descrip:'我是一匹来自远方的狗。', sex: '男', age: 35, name: '李四', id: '老师'},
                     { descrip:'我是一匹来自远方的猪。', sex: '男', age: 42, name: '王五', id: '学生'},
                     { descrip:'我是一匹来自远方的牛。', sex: '男', age: 50, name: '赵六', id: '实习'},
                     { descrip:'我是一匹来自远方的马。', sex: '男', age: 60, name: '孙七', id: '实习'}];

    在STAFF.js中我们实际上创建了2个类,为了实现更好的’封装性’,我们将每一个人员条目单独作为一个staffItem类,该对象中包含了该人员的所有信息,在本应用中包含他的姓名、年龄、性别、身份、个人描述等,实践中我们可以加入类似入职时间,福利薪酬,个人经历等信息。另外还有一个key值,它是一个类变量,这个值是唯一标识该staffItem用的。

    在第二步,我们在ManageSystem.js中伪造了一些数据,现在我们也把它搬到STAFF中。毕竟React不是存数据用的。

    在STAFF类的构造函数中,创建了2个实例变量,一个是allStaff,其中存储所有staffItem;一个是staff,它是最终需要给React展示的数据,是经过用户筛选操作、关键字搜索操作之后得到的人员数组。之所以这么设计变量也是为了后面的筛选、搜索等功能。在这里我们尚无这些操作的逻辑,直接将allStaff赋给staff即可。

    好了,接下来在ManageSystem中引入Staff.js,并初始化state:

    
    import React from 'react';
    import StaffHeader from './StaffHeader.js';
    import StaffItemPanel from './StaffItemPanel.js';
    import StaffFooter from './StaffFooter.js';
    import StaffDetail from './StaffDetail.js';
    
    import STAFF from './STAFF.js';
    
    class App extends React.Component {
    
        constructor(){
            super();
            this.state = {
                staff : new Staff
            };
        }
    
        render(){
          return (
            <div>
              <StaffHeader/>
              <StaffItemPanel items={this.state.staff.staff} />
              <StaffFooter/>
              <StaffDetail/>
            </div>
          );
        }
    }
    
    React.render(<App />, document.getElementById('app'));    

    在构造函数中,new了一个STAFF类,然后将this.state.staff.staff传入<StaffItemPanel/>的items属性。

    然后重新编译打包:

    npm start
    

    再次在浏览器打开index.html文件,虽然还是一个静态版的React,不过它已经变得更加模块化和’专一’了,结构也更加漂亮。

    第五步:完成新增人员功能

    关于state

    上文说过,state是为交互而生的。React是自上而下的单向数据流,state通常由上层组件拥有并控制,state的变化将触发建立在该state上的一系列自上而下的组件更新。注意,组件只能update它自己的state,如果下层组件的操作希望改变应用的状态,形成一个inverse data flow–反向数据流,我们需要从上层组件传入一个回调函数。关于state如何确定,可以参考官网一篇文章Thinking in React。接下来我们看人员功能添加是如何完成的。

    实现人员新增功能

    真正让React动起来,不妨从新增人员逻辑开始吧,这个功能比较纯粹,和其他业务耦合度不高。重新打开StaffFooter.js,加入部分代码:

    
    import React from 'react';
    export default class StaffFooter extends React.Component{
    
        handlerAddClick(evt){
            evt.preventDefault();
            let item = {};
            let addForm = React.findDOMNode(this.refs.addForm);
            let sex = addForm.querySelector('#staffAddSex');
            let id = addForm.querySelector('#staffAddId');
    
            item.name = addForm.querySelector('#staffAddName').value.trim();
            item.age = addForm.querySelector('#staffAddAge').value.trim();
            item.descrip = addForm.querySelector('#staffAddDescrip').value.trim();
            item.sex = sex.options[sex.selectedIndex].value;
            item.id = id.options[id.selectedIndex].value;
    
            /*
             *表单验证
             */
            if(item.name=='' || item.age=='' || item.descrip=='') {
                let tips = React.findDOMNode(this.refs.tipsUnDone);
                tips.style.display = 'block';
                setTimeout(function(){
                    tips.style.display = 'none';
                }, 1000);
                return;
            }
            //非负整数
            let numReg = /^\d+$/;
            if(!numReg.test(item.age) || parseInt(item.age)>150) {
                let tips = React.findDOMNode(this.refs.tipsUnAge);
                tips.style.display = 'block';
                setTimeout(function(){
                    tips.style.display = 'none';
                }, 1000);
                return;
            }
    
            this.props.addStaffItem(item);
            addForm.reset();
    
            //此处应在返回添加成功信息后确认
            let tips = React.findDOMNode(this.refs.tips);
            tips.style.display = 'block';
            setTimeout(function(){
                tips.style.display = 'none';
            }, 1000);
        }
    
        render(){
            return (
              <div>
                <h4 style={{'text-align':'center'}}>人员新增</h4>
                <hr/>
                <form ref='addForm' className="addForm">
                    <div>
                      <label for='staffAddName' style={{'display': 'block'}}>姓名</label>
                      <input ref='addName' id='staffAddName' type='text' placeholder='Your Name'/>
                    </div>
                    <div>
                      <label for='staffAddAge' style={{'display': 'block'}}>年龄</label>
                      <input ref='addAge' id='staffAddAge' type='text' placeholder='Your Age(0-150)'/>
                    </div>
                    <div>
                      <label for='staffAddSex' style={{'display': 'block'}}>性别</label>
                      <select ref='addSex' id='staffAddSex'>
                        <option value='男'></option>
                        <option value='女'></option>
                      </select>
                    </div>
                    <div>
                      <label for='staffAddId' style={{'display': 'block'}}>身份</label>
                      <select ref='addId' id='staffAddId'>
                        <option value='主任'>主任</option>
                        <option value='老师'>老师</option>
                        <option value='学生'>学生</option>
                        <option value='实习'>实习</option>
                      </select>
                    </div>
                    <div>
                      <label for='staffAddDescrip' style={{'display': 'block'}}>个人描述</label>
                      <textarea ref='addDescrip' id='staffAddDescrip' type='text'></textarea>
                    </div>
                    <p ref="tips" className='tips' >提交成功</p>
                    <p ref='tipsUnDone' className='tips'>请录入完整的人员信息</p>
                    <p ref='tipsUnAge' className='tips'>请录入正确的年龄</p>
                    <div>
                      <button onClick={this.handlerAddClick.bind(this)}>提交</button>
                    </div>
                </form>
              </div>
            )
        }
    }

    我们在提交的按钮上绑定了点击事件。点击提交按钮后,执行以下函数:

    handlerAddClick(evt){
        evt.preventDefault();
        let item = {};
        let addForm = React.findDOMNode(this.refs.addForm);
        let sex = addForm.querySelector('#staffAddSex');
        let id = addForm.querySelector('#staffAddId');
    
        item.name = addForm.querySelector('#staffAddName').value.trim();
        item.age = addForm.querySelector('#staffAddAge').value.trim();
        item.descrip = addForm.querySelector('#staffAddDescrip').value.trim();
        item.sex = sex.options[sex.selectedIndex].value;
        item.id = id.options[id.selectedIndex].value;
    
        /*
         *表单验证
         */
        if(item.name=='' || item.age=='' || item.descrip=='') {
            let tips = React.findDOMNode(this.refs.tipsUnDone);
            tips.style.display = 'block';
            setTimeout(function(){
                tips.style.display = 'none';
            }, 1000);
            return;
        }
        //非负整数
        let numReg = /^\d+$/;
        if(!numReg.test(item.age) || parseInt(item.age)>150) {
            let tips = React.findDOMNode(this.refs.tipsUnAge);
            tips.style.display = 'block';
            setTimeout(function(){
                tips.style.display = 'none';
            }, 1000);
            return;
        }
    
        this.props.addStaffItem(item);
        addForm.reset();
    
        //此处应在返回添加成功信息后确认
        let tips = React.findDOMNode(this.refs.tips);
        tips.style.display = 'block';
        setTimeout(function(){
            tips.style.display = 'none';
        }, 1000);
    }
    

    这里我们获取并简单处理了表单,特别注意

    this.props.addStaffItem(item);
    

    这一行代码,就是调用了ManageSystem通过prop属性传入的回调函数。在ManageSystem中加入相关代码:

    
    import React from 'react';
    import StaffHeader from './StaffHeader.js';
    import StaffItemPanel from './StaffItemPanel.js';
    import StaffFooter from './StaffFooter.js';
    import StaffDetail from './StaffDetail.js';
    
    import Staff from './STAFF.js';
    
    
    class App extends React.Component {
        constructor(){
            super();
            this.state = {
                staff : new Staff,
                staffDetail: null
            };
        }
    
        //增
        addStaffItem(item){
            this.setState({
                staff: this.state.staff.addStaffItem(item)
            });
        }
    
        render(){
          return (
            <div>
              <StaffHeader/>
              <StaffItemPanel items={this.state.staff.staff}}/>
              <StaffFooter addStaffItem={this.addStaffItem.bind(this)}/>
              <StaffDetail/>
            </div>
          );
        }
    }
    
    React.render(<App />, document.getElementById('app'));

    <StaffFooter addStaffItem={this.addStaffItem.bind(this)}/>中传入了addStaffItem方法。

    //增
    addStaffItem(item){
        this.setState({
            staff: this.state.staff.addStaffItem(item)
        });
    }
    

    中更新了自己的state。只不过具体的逻辑是在STAFF类的方法中完成的。

    STAFF.js:

    
    export default class STAFF {
    
        constructor(){
            this.allStaff = [
                new staffItem(STAFF.rawData[0]),
                new staffItem(STAFF.rawData[1]),
                new staffItem(STAFF.rawData[2]),
                new staffItem(STAFF.rawData[3]),
                new staffItem(STAFF.rawData[4]),
                new staffItem(STAFF.rawData[5]),
                new staffItem(STAFF.rawData[6]),
                new staffItem(STAFF.rawData[7]),
                new staffItem(STAFF.rawData[8]),
                new staffItem(STAFF.rawData[9]),
                new staffItem(STAFF.rawData[10])
            ];
            this.staff = this.allStaff;
        }
    
        //增
        addStaffItem(item) {
            let newItem = new staffItem(item);
            this.allStaff.push(newItem);
            this.staff = this.allStaff;
            return this;
        }
    }

    重新编译打包生成bundle.js文件:

    npm start
    

    再次在浏览器中打开index.html文件,试试我们新添加的人员添加功能吧!

    第六步:完成关键字搜索功能

    类似第五步新人员的添加,我们首先给StaffHeader中的搜索输入框绑定一个onChange事件,每当搜索内容改变时,触发该函数:

    StaffHeader.js

    
    import React from 'react';
    export default class StaffHeader extends React.Component{
    
        //search
        handlerSearch(){
            let bar = React.findDOMNode(this.refs.searchBar);
            let value = bar.value;
            this.props.searchStaff(value);
        }
    
        render(){
            return (
              <div>
                  <h3 style={{'text-align':'center'}}>人员管理系统</h3>
                  <table className="optHeader">
                    <tbody>
                      <tr>
                        <td className="headerTd"><input ref='searchBar' onChange={this.handlerSearch.bind(this)} type='text' placeholder='Search...' /></td>
                        <td className="headerTd">
                            <label for='idSelect'>人员筛选</label>
                            <select id='idSelect'>
                                <option value='0'>全部</option>
                                <option value='1'>主任</option>
                                <option value='2'>老师</option>
                                <option value='3'>学生</option>
                                <option value='4'>实习</option>
                            </select>
                        </td>
                        <td>
                            <label for='orderSelect'>排列方式</label>
                            <select id='orderSelect'>
                                <option value='0'>身份</option>
                                <option value='1'>年龄升</option>
                                <option value='2'>年龄降</option>
                            </select>
                        </td>
                      </tr>
                    </tbody>
                  </table>
              </div>
            );
        }
    }

    同样在事件处理函数中,调用了通过props属性传入的回调函数searchStaff:

    //search
    handlerSearch(){
        let bar = React.findDOMNode(this.refs.searchBar);
        let value = bar.value;
        this.props.searchStaff(value);
    }
    

    逐步完善ManageSystem以及STAFF类:

    ManageSystem.js:

    
    class App extends React.Component {
        constructor(){
            super();
            this.state = {
                staff : new Staff,
                staffDetail: null
            };
        }
    
        //增
        addStaffItem(item){
            this.setState({
                staff: this.state.staff.addStaffItem(item)
            });
        }
    
        /*
         * 搜索
         */
        searchStaff(word) {
            this.setState({
                staff: this.state.staff.searchStaff(word)
            });
        }
    
        render(){
          return (
            <div>
              <StaffHeader searchStaff={this.searchStaff.bind(this)} />
              <StaffItemPanel items={this.state.staff.staff}}/>
              <StaffFooter addStaffItem={this.addStaffItem.bind(this)}/>
              <StaffDetail/>
            </div>
          );
        }
    }

    STAFF.js

    
    export default class STAFF {
    
        constructor(){
            this.allStaff = [
                new staffItem(STAFF.rawData[0]),
                new staffItem(STAFF.rawData[1]),
                new staffItem(STAFF.rawData[2]),
                new staffItem(STAFF.rawData[3]),
                new staffItem(STAFF.rawData[4]),
                new staffItem(STAFF.rawData[5]),
                new staffItem(STAFF.rawData[6]),
                new staffItem(STAFF.rawData[7]),
                new staffItem(STAFF.rawData[8]),
                new staffItem(STAFF.rawData[9]),
                new staffItem(STAFF.rawData[10])
            ];
            this.staff = this.allStaff;
            this.word = '';  //搜索关键字
        }
    
        //增
        addStaffItem(item) {
            let newItem = new staffItem(item);
            this.allStaff.push(newItem);
            this.staff = this.allStaff;
            return this;
        }
    
        //搜索
        searchStaff(word){
            this.word = word;
            this.staff = this.allStaff;
            //在staff中搜索
            this.staff = this.staff.filter(item => {
                return item.info.name.indexOf(word)!=-1 || 
                       (item.info.age+'').indexOf(word)!=-1 || 
                       item.info.id.indexOf(word)!=-1 ||
                       item.info.sex.indexOf(word)!=-1;
            });
            return this;
        }
    }

    依据关键字的搜索功能至此也完成了。

    完成接下来的功能

    作为示例,第五步以及第六步完成了添加人员以及关键字搜索功能。随后随着功能的不断添加,最终代码的实现会有微小的调整。实现的方法大同小异,请大家对照源码,依照上面的方法逐步完整整个应用。我相信你实现了每一个细节之后,对面入门React一定会有十分大的帮助。

    展开全文
  • react基础教程

    千次阅读 2019-06-13 09:41:28
    react基础教程

    • 学习资源推荐:https://blog.csdn.net/qq_42813491/article/details/90213353

    官网

    • https://react.docschina.org/
      在这里插入图片描述

    • 官方教程:https://react.docschina.org/tutorial/tutorial.html

    • 介绍什么的就不说了,从代码开始

    安装

    • 在此之前,请确保已经安装了node.js.
    • cmd输入node -v 显示版本号表示已成功安装
    • node官网:https://nodejs.org/en/
      在这里插入图片描述
    • 快速开始
    npx create-react-app my-app
    cd my-app
    npm start
    
    • 注意,安装是外网安装,建议使用镜像,不然会很慢

    • 详情可参考:https://blog.csdn.net/qq_42813491/article/details/90311075

    • 以上三个命令执行完,显示以下界面表示脚手架安装成功
      在这里插入图片描述

    • 这是主界面,跑在3000端口
      在这里插入图片描述

    目录结构

    在这里插入图片描述

    • 都很常见,不多介绍了

    hello world

    • src下一些没用的可以删掉,如下:看起来对新手更友好

    在这里插入图片描述

    • index.js入口文件和下边的服务器相关配置文件不要动了,css什么的可以删掉
    • APP.js
    import React from 'react'
    
    class App extends React.Component{
    
      render(){
        return <h1>hello world</h1>
      }
    
    }
    
    export default App;
    
    • 也可以这样
    import React, {Component}from 'react'
    
    class App extends Component{
    
      render(){
        return <h1>hello world</h1>
      }
    
    }
    
    export default App;
    
    • 效果图
      在这里插入图片描述

    • 注意点

    • render里边是html+js混写的jsx语法,HTML标签不需要加引号

    • 最外层一定要有个根标签包裹,最好再加上一个圆括号

    • 单标签必须封闭
      *

    组件化

    虽然render函数里边就可以直接写模板,但是通常不会把一切东西都放进去.不利于维护,也不美观,常用的自然是组件化开发。

    • 修改目录结构,src下新建components文件夹,用来放各个组件
      注意,规范中组件名大写,且以js或jsx为后缀名,就像.vue 文件一样

    • 可能你的编辑器写完了是这样的
      在这里插入图片描述

    • 颜色怪是语法识别问题,把编译环境换成jsx就好了

    • 组件定义完记得暴露出去,APP界面用import导入,然后挂载使用

    在这里插入图片描述

    • 效果图
      在这里插入图片描述

    • 数据的定义和使用,类似vue的data

    在这里插入图片描述

    • 直接在render里边写会有警告,应该写在构造函数里边
      在这里插入图片描述

    图片和样式表的引入

    • 图片资源分为本地和远程,加载远程资源直接src引入图片地址就可以
    • 记得加alt属性,不然会有警告
     <img alt="logo"src="https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=2920084749,2018893236&fm=58&s=39C718720E8EBE011B398BAC0300F024&bpow=121&bpoh=75"/>
    

    在这里插入图片描述

    • 本地图片加载和本地样式表加载是路径引入的方式

    *import logo from './logo.png'
    <img alt="logo" src={logo}/>

    • {}是react的jsx语法环境
    • import style from './style.css'
      在这里插入图片描述
    展开全文
  • 关于React全家桶的介绍

    千次阅读 2019-01-09 11:52:41
    首先需要安装react: npm install react react-dom --save-dev JSX 语法 JSX 的基本语法规则:遇到 HTML 标签(以 &amp;amp;amp;lt; 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript ...

    起步

    首先需要安装react: npm install react react-dom --save-dev

    JSX 语法

    JSX 的基本语法规则:遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript 规则解析。JSX 允许直接在模板插入 JavaScript 变量。如果这个变量是一个数组,则会展开这个数组的所有成员

    var arr = [
        <h1>...</h1>,
        <h2>...</h2>,
    ];
    ReactDOM.render(
        <div>{arr}</div>,
        document.getElementById('example')
    );
    

    组件

    ReactDOM.render(
        <HelloMessage name="John" />,
        document.getElementById('example')
    );
    

    变量 HelloMessage 就是一个组件类。模板插入<HelloMessage />时,会自动生成 HelloMessage 的一个实例(下文的"组件"都指组件类的实例)。所有组件类都必须有自己的 render 方法,用于输出组件。

    注意,组件类的第一个字母必须大写,否则会报错,比如HelloMessage不能写成helloMessage。另外,组件类只能包含一个顶层标签,否则也会报错。
    <div></div><div></div>上面代码会报错,因为HelloMessage组件包含了两个顶层标签

    组件的用法与原生的 HTML 标签完全一致,可以任意加入属性,比如<HelloMessage name="John">,就是 HelloMessage 组件加入一个 name 属性,值为 John。组件的属性可以在组件类的this.props对象上获取,比如 name 属性就可以通过this.props.name读取。

    有一个地方需要注意,就是 class 属性需要写成 className ,for 属性需要写成 htmlFor ,这是因为 class 和 for 是 JavaScript 的保留字。

    this.props.children

    this.props 对象的属性与组件的属性一一对应,但是有一个例外,就是 this.props.children 属性。它表示组件的所有子节点

    render: function() {
        return (
            <ol>
            {
                React.Children.map(this.props.children, function (child) {
                    return <li>{child}</li>;
                })
            }
            </ol>
        );
    }
    上面代码的 NoteList 组件有两个 span 子节点,它们都可以通过 this.props.children 读取
    

    这里需要注意, this.props.children 的值有三种可能:如果当前组件没有子节点,它就是 undefined ;如果有一个子节点,数据类型是 object ;如果有多个子节点,数据类型就是 array 。所以,处理 this.props.children 的时候要小心。

    React 提供一个工具方法 React.Children 来处理 this.props.children 。我们可以用 React.Children.map 来遍历子节点,而不用担心 this.props.children 的数据类型是 undefined 还是 object。

    获取真实的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]属性。

    React 组件支持很多事件,除了 Click 事件以外,还有 KeyDown 、Copy、Scroll 等…

    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 读取用户输入的值。textarea 元素、select元素、radio元素都属于这种情况,更多介绍请参考官方文档。

    组件的生命周期

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

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

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

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

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

    componentWillReceiveProps(object nextProps):已加载组件收到新的参数时调用
    shouldComponentUpdate(object nextProps, object nextState):组件判断是否重新渲染时调用
    这些方法的详细说明,可以参考官方文档。下面是一个例子(查看 demo10 )。

    var Hello = React.createClass({
    getInitialState: function () {
        return {
            opacity: 1.0
        };
    },
    
    componentDidMount: function () {
        this.timer = setInterval(function () {
            var opacity = this.state.opacity;
            opacity -= .05;
            if (opacity < 0.1) {
                opacity = 1.0;
            }
            this.setState({
                opacity: opacity
            });
        }.bind(this), 100);
    },
    
    render: function () {
        return (
        <div style={{opacity: this.state.opacity}}>
            Hello {this.props.name}
        </div>
        );
    }
    });
    
    ReactDOM.render(
        <Hello name="world"/>,
        document.body
    );
    

    上面代码在hello组件加载以后,通过 componentDidMount 方法设置一个定时器,每隔100毫秒,就重新设置组件的透明度,从而引发重新渲染。

    另外,组件的style属性的设置方式也值得注意,不能写成style="opacity:{this.state.opacity};"而要写成style={{opacity: this.state.opacity}}这是因为 React 组件样式是一个对象,所以第一重大括号表示这是 JavaScript 语法,第二重大括号表示样式对象。

    Ajax

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

    var UserGist = React.createClass({
    getInitialState: function() {
        return {
        username: '',
        lastGistUrl: ''
        };
    },
    
    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));
    },
    
    render: function() {
        return (
        <div>
            {this.state.username}'s last gist is
            <a href={this.state.lastGistUrl}>here</a>.
        </div>
        );
    }
    });
    
    ReactDOM.render(
    <UserGist source="https://api.github.com/users/octocat/gists" />,
    document.body
    );
    

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

    我们甚至可以把一个Promise对象传入组件,请看Demo12。

    ReactDOM.render(
    <RepoList
    promise={$.getJSON(‘https://api.github.com/search/repositories?q=javascript&sort=stars’)}
    />,
    document.body
    );
    上面代码从Github的API抓取数据,然后将Promise对象作为属性,传给RepoList组件。

    路由库React-Router

    它通过管理 URL,实现组件的切换和状态的变化,开发复杂的应用几乎肯定会用到。
    使用时,路由器Router就是React的一个组件。

    import { Router } from 'react-router';
    render(<Router/>, document.getElementById('app'));
    

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

    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’)。
    你可能还注意到,Router组件有一个参数history,它的值hashHistory表示,路由的切换由URL的hash变化决定,即URL的#部分发生变化。举例来说,用户访问http://www.example.com/,实际会看到的是http://www.example.com/#/

    Route组件定义了URL路径与组件的对应关系。你可以同时使用多个Route组件。
    <Router history={hashHistory}>
    <Route path="/" component={App}/>
    <Route path="/repos" component={Repos}/>
    <Route path="/about" component={About}/>
    </Router>
    

    上面代码中,用户访问/repos(比如http://localhost:8080/#/repos)时,加载Repos组件;访问/about(http://localhost:8080/#/about)时,加载About组件。

    Route组件还可以嵌套。

    <Router history={hashHistory}>
    <Route path="/" component={App}>
        <Route path="/repos" component={Repos}/>
        <Route path="/about" component={About}/>
    </Route>
    </Router>
    

    上面代码中,用户访问/repos时,会先加载App组件,然后在它的内部再加载Repos组件。

    path属性可以使用通配符。

    <Route path="/hello/:name">
    // 匹配 /hello/michael
    // 匹配 /hello/ryan
    <Route path="/hello(/:name)">
    // 匹配 /hello
    // 匹配 /hello/michael
    // 匹配 /hello/ryan
    <Route path="/files/*.*">
    // 匹配 /files/hello.jpg
    // 匹配 /files/hello.html
    <Route path="/files/*">
    // 匹配 /files/ 
    // 匹配 /files/a
    // 匹配 /files/a/b
    <Route path="/**/*.jpg">
    // 匹配 /files/hello.jpg
    // 匹配 /files/path/to/file.jpg
    

    通配符的规则如下。

    (1):paramName
    :paramName匹配URL的一个部分,直到遇到下一个/、?、#为止。这个路径参数可以通过this.props.params.paramName取出。

    (2)()
    ()表示URL的这个部分是可选的。

    (3)*
    *匹配任意字符,直到模式里面的下一个字符为止。匹配方式是非贪婪模式。

    (4) **
    ** 匹配任意字符,直到下一个/、?、#为止。匹配方式是贪婪模式。

    Redirect 组件

    组件用于路由的跳转,即用户访问一个路由,会自动跳转到另一个路由。

    <Route path="inbox" component={Inbox}>
    {/* 从 /inbox/messages/:id 跳转到 /messages/:id */}
    <Redirect from="messages/:id" to="/messages/:id" />
    </Route>
    

    现在访问/inbox/messages/5,会自动跳转到/messages/5。

    Link

    Link组件用于取代元素,生成一个链接,允许用户点击后跳转到另一个路由。它基本上就是元素的React 版本,可以接收Router的状态。

    render() {
    return <div>
        <ul role="nav">
        <li><Link to="/about">About</Link></li>
        <li><Link to="/repos">Repos</Link></li>
        </ul>
    </div>
    }
    

    如果希望当前的路由与其他路由有不同样式,这时可以使用Link组件的activeStyle属性。

    <Link to="/about" activeStyle={{color: 'red'}}>About</Link>
    <Link to="/repos" activeStyle={{color: 'red'}}>Repos</Link>
    

    上面代码中,当前页面的链接会红色显示。

    另一种做法是,使用activeClassName指定当前路由的Class。

    <Link to="/about" activeClassName="active">About</Link>
    <Link to="/repos" activeClassName="active">Repos</Link>
    

    上面代码中,当前页面的链接的class会包含active。

    在Router组件之外,导航到路由页面,可以使用浏览器的History API,像下面这样写。

    import { browserHistory } from 'react-router';
    browserHistory.push('/some/path');
    

    如果链接到根路由/,不要使用Link组件,而要使用IndexLink组件。

    histroy 属性

    Router组件的history属性,用来监听浏览器地址栏的变化,并将URL解析成一个地址对象,供 React Router 匹配。

    history属性,一共可以设置三种值。

    browserHistory
    hashHistory
    createMemoryHistory
    

    如果设为hashHistory,路由将通过URL的hash部分(#)切换,URL的形式类似example.com/#/some/path。

    如果设为hashHistory,路由将通过URL的hash部分(#)切换,URL的形式类似example.com/#/some/path。

    如果设为browserHistory,浏览器的路由就不再通过Hash完成了,而显示正常的路径example.com/some/path,背后调用的是浏览器的History API。但是,这种情况需要对服务器改造。否则用户直接向服务器请求某个子路由,会显示网页找不到的404错误。

    createMemoryHistory主要用于服务器渲染。它创建一个内存中的history对象,不与浏览器URL互动。

    表单处理

    Link组件用于正常的用户点击跳转,但是有时还需要表单跳转、点击按钮跳转等操作。这些情况怎么跟React Router对接呢?

    下面是一个表单。

        <form onSubmit={this.handleSubmit}>
        <input type="text" placeholder="userName"/>
        <input type="text" placeholder="repo"/>
        <button type="submit">Go</button>
        </form>
    

    使用context对象。

    export default React.createClass({
        // ask for `router` from context
        contextTypes: {
            router: React.PropTypes.object
        },
        handleSubmit(event) {
            const path = `/repos/${userName}/${repo}`
            this.context.router.push(path)
        },
    })
    

    路由的钩子

    个路由都有Enter和Leave钩子,用户进入或离开该路由时触发。

    <Route path="about" component={About} />
    <Route path="inbox" component={Inbox}>
    <Redirect from="messages/:id" to="/messages/:id" />
    </Route>
    

    上面的代码中,如果用户离开/messages/:id,进入/about时,会依次触发以下的钩子。

    /messages/:id的onLeave
    /inbox的onLeave
    /about的onEnter
    下面是一个例子,使用onEnter钩子替代组件。

    <Route path="inbox" component={Inbox}>
    <Route
        path="messages/:id"
        onEnter={
        ({params}, replace) => replace(`/messages/${params.id}`)
        } 
    />
    </Route>
    

    onEnter钩子还可以用来做认证。

    const requireAuth = (nextState, replace) => {
        if (!auth.isAdmin()) {
            // Redirect to Home page if not an Admin
            replace({ pathname: '/' })
        }
    }
    export const AdminRoutes = () => {
    return (
        <Route path="/admin" component={Admin} onEnter={requireAuth} />
    )
    }
    

    下面是一个高级应用,当用户离开一个路径的时候,跳出一个提示框,要求用户确认是否离开。

    const Home = withRouter(
    React.createClass({
        componentDidMount() {
        this.props.router.setRouteLeaveHook(
            this.props.route, 
            this.routerWillLeave
        )
        },
    
        routerWillLeave(nextLocation) {
        // 返回 false 会继续停留当前页面,
        // 否则,返回一个字符串,会显示给用户,让其自己决定
        if (!this.state.isSaved)
            return '确认要离开?';
        },
    })
    )
    

    上面代码中,setRouteLeaveHook方法为Leave钩子指定routerWillLeave函数。该方法如果返回false,将阻止路由的切换,否则就返回一个字符串,提示用户决定是否要切换。

    ES6-React

    加载模块

    //不需要提前引入任何文件
    import React from "react";
    import ReactDOM from "react-dom"
    

    创建组件:使用类来创建组件代替React.createClass

    import React,{Component} from "react";
    class MyComponent extends Component{
        //组件内部代码
    }
    

    State/Props/PropTypes:es6允许将props和propTypes当作静态属性在类外初始化

    关于 PropTypes:

    组件的属性可以接受任意值,字符串、对象、函数等等都可以。有时,我们需要一种机制,验证别人使用组件时,提供的参数是否符合要求。

    组件类的PropTypes属性,就是用来验证组件实例的属性是否符合要求,PropTypes 告诉 React,这个 title 属性是必须的,而且它的值必须是字符串。

    此外,defaultProps 方法可以用来设置组件属性的默认值。

    class MyComponent extends React.Component{}
    MyComponent.defaultProps={
        name:"SunnyChuan",
        age:22
    };
    MyComponent.propTypes={
        name:React.PropTypes.string.isRequired,
        age:React.PropTypes.number.isRequired
    };
    

    es7支持直接在类中使用变量表达式,这也是我推荐的写法

    class MyComponent extends React.Component{
        static defaultProps={
            name:"SunnyChuan",
            age:22
        }
        static propTypes={
            name:React.PropTypes.string.isRequired,
            age:React.PropTypes.number.isRequired
        }
    }
    

    state和前两个不同,它不是静态的

    class MyComponent extends React.Component{
        static defaultProps={
            name:"SunnyChuan",
            age:22
        }
        state={
            isMarried:false
        }
        static propTypes={
            name:React.PropTypes.string.isRequired,
            age:React.PropTypes.number.isRequired
        }
    }
    

    数据

    通过这种user={user}的方式传递数据,如果子组件想要获得name这个数据,就需要通过this.props.user.name的方式获取数据,看下面这种方式

    // Parent Component's render method
    render: function() {
      const user = {
        name: 'Brad',
        occupation: 'Web Development',
        state: 'Arizona'
      };
      return (<ChildComponent name={user.name} occupation={user.occupation} state={user.state} />);
    }
    

    这种方式,子组件想要获得name或全部数据,需要this.props.name,前提是要像这样name={user.name} occupation={user.occupation} state={user.state}哦,也是挺麻烦的,所以使用es6的特性之一,解构赋值方便了许多哦!看下面的代码块:

    // Parent Component's render method
    render: function() {
      const user = {
        name: 'Brad',
        occupation: 'Web Development',
        state: 'Arizona'
      };
      return (<ChildComponent {...user} />);
    }
    

    这样,子组件只需要this.props.name,this.props.occupation,this.props.state方式就能获得传递过来的数据了

    函数

    React.createClass本身接收的是一个对象,对于对象中的方法,es6允许使用key(){}的形式取代key:function(){}

    class MyComponent extends React.Component{
        state={
            count:0
        }
        handleChange(){
            this.setState({count:this.state.count+1});
        }
    }
    

    需要注意的是,由于使用class创建组件,react不会再自动帮我们绑定作用域了,我们需要自己手动解决

    class MyComponent extends React.Component{
        state={
            count:0
        }
        handleChange(){
            this.setState({count:this.state.count+1});
        }
        render(){
            return (
                <div>
                <h2>当前计数是:{this.state.count}</h2>
                <button onClick={this.handleChange.bind(this)}>点击</button>
                </div>
            )
        }
    }
    

    如果你觉得这种每次都需要绑定的方法太麻烦,也可以在构造函数中去绑定

    class MyComponent extends React.Component{
        constructor(props){
            super(props);
            this.handleChange=this.handleChange.bind(this);
        }
        state={
            count:0
        }
        handleChange(){
            this.setState({count:this.state.count+1});
        }
        render(){
            return (
                <div>
                <h2>当前计数是:{this.state.count}</h2>
                <button onClick={this.handleChange}>点击</button>
                </div>
            )
        }
    }
    

    如果你觉得这种方式也麻烦,可以使用es6的箭头函数(自动绑定作用域),但是前提是你的环境要支持es7,因为箭头函数相当于表达式声明函数的简写,只有es7支持在类中这么使用(类中使用表达式state/props/propTypes也只有es7支持)

    class MyComponent extends React.Component{
      state={
        count:0
      }
      handleChange=()=>{
        this.setState({count:this.state.count+1});
      }
      render(){
        return (
          <div>
            <h2>当前计数是:{this.state.count}</h2>
            <button onClick={this.handleChange}>点击</button>
          </div>
        )
      }
    }
    

    组件生命周期

    所有的组件生命周期都可以当作普通函数使用上述三种方式编写,componentWillMount比较特殊,它还可以在构造函数中编写

    class MyComponent extends React.Component{
      componentWillMount(){
        console.log("Hello SunnyChuan");
      }
    }
    //二者等价
    class MyComponent extends React.Component{
      constructor(props){
        console.log("Hello SunnyChuan")
      }
    }
    

    扩展操作符

    使用react开发最常见的问题就是父组件要传给子组件的属性较多时比较麻烦

    class MyComponent extends React.Component{
    //假设MyComponent已经有了name和age属性
      render(){
        return (
          <SubComponent name={this.props.name} age={this.props.age}/>
         )
      }
    }
    

    使用扩展操作符可以变得很简单

    class MyComponent extends React.Component{
    //假设MyComponent已经有了name和age属性
      render(){
        return (
          <SubComponent {...this.props}/>
         )
      }
    }
    

    上述方式是将父组件的所有属性都传递下去,如果这其中有些属性我不需要传递呢?也很简单

    class MyComponent extends React.Component{
    //假设MyComponent有很多属性,而name属性不需要传递给子组件
      var {name,...MyProps}=this.props;  // var {name,...MyProps} 表示提取指定对像的属性
      render(){
        return (
          <SubComponent {...Myprops}/>
         )
      }
    }
    

    上述方法最常用的场景就是父组件的class属性需要被单独提取出来作为某个元素的class,而其他属性需要传递给子组件。

    模块化开发组件

    说了这么多,个人认为es6+react最吸引人的地方就是模块化开发,将每个小(大)组件当作一个模块

    //father.js
    import React from "react";
    import ReactDOM from "react-dom";
    import {SonComponent} from "son.js";
    class FatherComponent extends React.Component{ 
        //省去中间的业务逻辑
        render(){
            return (<SonComponent/>);
        }
    }
    ReactDOM.render(<FatherComponent/>,document.getElementById("ss"));
    
    //son.js
    import React from "react";
    class SonComponent extends React.Component{
        //省去中间的业务逻辑
        render(){
            return (<h2>"SunnyChuan"</h2>);
        }
    }
    export {SonComponent};
    

    如果你把子组件的导出设置为default export,那么在导入时就不必再加{}
    //father.js
    import SonComponent from “son.js”;

    //son.js
    export default class SonComponent extends React.Component{
        //省去中间的业务逻辑
    }
    

    花一点时间学习es6(7)+react的开发方式,你会更加喜欢使用react。

    什么是CSS Modules?

    这到底是什么呢?我们为什么要这么做呢?我们很快就进行介绍。首先,不要忘记HTML和CSS的工作原理。在HTML中一个类添加:

        <h1 class="title">An example heading</h1>
    

    在CSS中这个class的定义如下:

        .title {
            background-color: red;
        }
    

    只要CSS被添加到HTML文档上,那个<h1>的背景色就是红色。我们不需要人为处理CSS和HTML文件。浏览器本身自己就理解这些文件的格式。
    CSS Modules 和上面的方法不一样。我们不写纯HTML,我们需要在一个类似index.js这样的Javascript 文件中取写我们所有的标签。这里有一个例子来说明这是怎么回事(我们之后将会去看更多真实的实例):

        import styles from "./styles.css";
    
        element.innerHTML = 
            `<h1 class="${styles.title}">
                An example heading
            </h1>`;
    

    在我们构建的步骤中,编译器将会搜索我们导入的styles.css文件,然后到我们刚刚写的js文件中,通过styles.title使得.title class可用。我们的构建步骤将会同时处理这些东西成为新的,分离的HTML和CSS文件,并且用一个新的字符串去替换HTML和CSS选择器的class。
    通过构建工具生成的HTML也许像下面这样:

        <h1 class="_styles__title_309571057">
            An example heading
        </h1>
    

    通过构建工具生成的CSS也许像下面这样:

        ._styles__title_309571057{
            background-color: red;
        }
    

    全局作用域

    CSS Modules 允许使用:global(.className)的语法,声明一个全局规则。凡是这样声明的class,都不会被编译成哈希字符串。
    App.css加入一个全局class。

    .title {
      color: red;
    }
    
    :global(.title) {
      color: green;
    }
    

    App.js使用普通的class的写法,就会引用全局class。

    import React from 'react';
    import styles from './App.css';
    
    export default () => {
      return (
        <h1 className="title">
          Hello World
        </h1>
      );
    };
    

    运行这个示例。打开 http://localhost:8080,应该会看到h1标题显示为绿色。

    Fetch

    Fetch API是基于Promise设计,Promise 对象用于表示一个异步操作的最终状态(完成或失败),以及其返回的值。

    Promise 创建

    下面是创建 promise 的步骤:

    var promise = new Promise(function(resolve, reject) {
    	// 异步处理
    	// 处理结束后、调用resolve 或 reject
    });
    

    Promise 构造函数包含一个参数和一个带有 resolve(解析)和 reject(拒绝)两个参数的回调。在回调中执行一些操作(例如异步),如果一切都正常,则调用 resolve,否则调用 reject。
    想要某个函数?拥有promise功能,只需让其返回一个promise即可。

    function myAsyncFunction(url) {
    	return new Promise((resolve, reject) => {
    		const xhr = new XMLHttpRequest();
    		xhr.open("GET", url);
    		xhr.onload = () => resolve(xhr.responseText);
    		xhr.onerror = () => reject(xhr.statusText);
    		xhr.send();
    	});
    };
    

    Promise 使用

    对于已经实例化过的 promise 对象可以调用 promise.then() 方法,传递 resolve 和 reject 方法作为回调。
    promise.then() 是 promise 的方法: promise.then(onFulfilled, onRejected)
    promise简化了对error的处理,最为常用可以这样写:promise.then(onFulfilled).catch(onRejected)

    Fetch使用说明

    fetch(url, options).then(function(response) { 
    	// handle HTTP response
    }, function(error) {
    	 // handle network error
    })
    
    说明:
    1. fetch api返回的是一个promise对象
    2. Options:
      method(String): HTTP请求方法,默认为GET
      body(String): HTTP的请求参数
      headers(Object): HTTP的请求头,默认为{}
      credentials(String): 默认为omit,忽略的意思,也就是不带cookie;还有两个参数,same-origin,意思就是同源请求带cookie;include,表示无论跨域还是同源请求都会带cookie
    3. 第一个then函数里面处理的是response的格式
      status(number): HTTP返回的状态码,范围在100-599之间
      statusText(String): 服务器返回的状态文字描述,例如Unauthorized,上图中返回的是Ok
      ok(Boolean): 如果状态码是以2开头的,则为true
      headers: HTTP请求返回头
      body: 返回体,这里有处理返回体的一些方法
      text(): 将返回体处理成字符串类型
      json(): 返回结果和 JSON.parse(responseText)一样
      blob(): 返回一个Blob,Blob对象是一个不可更改的类文件的二进制数据
      arrayBuffer()
      formData()
    4. 如caniuse所示,IE浏览器完全不支持fetch,移动端的很多浏览器也不支持,所以,如果要在这些浏览器上使用Fetch,就必须使用fetch polyfill
    5. cookie传递
      必须在header参数里面加上credentials: ‘include’,才会如xhr一样将当前cookies带到请求中去
    6. fetch和xhr的不同
      fetch虽然底层,但是还是缺少一些常用xhr有的方法,比如能够取消请求(abort)方法
      fetch在服务器返回4xx、5xx时是不会抛出错误的,这里需要手动通过,通过response中的ok字段和status字段来判断
    展开全文
  • react

    2020-11-23 08:33:02
    一、react 的三种使用方法 1、在html中使用react (1)、创建并使用 react 组件 <!DOCTYPE html> <html> // html 中 <head> <meta charset="UTF-8" /> <title>第一次使用 ...
  • React 常见的面试题

    万次阅读 2017-04-09 22:47:32
    根据记录,问这些问题可能不是深入了解他们在使用 React 方面的经验的最佳方式。http://www.tuicool.com/articles/fqaqqmF 之所以标题是《 React 常见的面试题》,其实只是想起一个比《在 React 里面,你可以知道也...
  • React基本语法格式

    2020-11-17 18:20:08
    这里写目录标题一、搭建React工作环境:1、React 特点2、React 安装3、环境安装中引入了三个库4、页面的启动问题二、第一个React程序:Hello,React!1、React的核心方法2、JSX代码必须具备一个根节点。3、JSX是HTML...
  • reactJS 干货(reactjs 史上最详细的解析干货)

    万次阅读 多人点赞 2017-04-06 10:44:58
    一. State和 Props state是状态机。 ... 不应该包括:计算所得数据、React组件(在render()里使用props和state来创建它)、基于props的重复数据(尽可能保持用props来做作为唯一的数据来源,把pro
  • 现在最热门的前端框架有AngularJS、React、Bootstrap等。自从接触了ReactJS,ReactJs的虚拟DOM(Virtual DOM)和组件化的开发深深的吸引了我,下面来跟我一起领略 ReactJS的风采吧~~ 章有点长,耐心读完,你会有很大...
  • React安装 环境搭建

    万次阅读 2017-11-15 12:17:17
    1.安装nodejs  首先要安装nodejs,可以直接到node官网下载安装。 安装完成后,打开cmd ,输入node -v 查看是否安装 。 npm工具也会随node一起自动安装,使用npm -v 查看。 ...npm instal
  • import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; import {HashRouter,BrowserRouter} from '...
  • import React , {useState} from 'react' const addCon = ()=>{ console.log(useState(0)); const [count,setCount] = useState(0) const handelAdd = () =>{ let newCount = count; setCount...
  • import React from 'react' const {Provider,Consumer} = React.createContext('default') export default class ContextDemo extends React.Component { state={ newContext:'createContext' } render() { ...
  • import React from 'react'; import logo from './logo.svg'; import './App.css'; class App extends React.Component{ constructor(props){ super(props) this.state = { msg:'第一次的消息' } } ...
  • 环境搭建准备 ...React Naitve的简介:Facebook在React.js Conf2015大会上推出的一个用于开发Android和iOS App的一个框架,主要编程语言是JavaScript。它的出现使用即拥有Native的用户体验,又保留R
  • useState 使用也和react一样,其他也一样就不多举例 import React, { Component, useState } from 'react'; import { View, Text, StyleSheet, Button } from 'react-native'; export default function Flex ()...
  • (精华)2020年7月26日 React 组件的使用

    万次阅读 2020-07-26 10:22:17
    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <...div id="app
  • 在相应的路径下执行命令行:react-native init 项目名 (名称不可使用连接符等特殊字符,命名可以参考APP应用名称 比如 FaceBook) react-native --v //查看版本 react-native init demo --version 0.48.0//安装指定...

空空如也

1 2 3 4 5 ... 20
收藏数 291,721
精华内容 116,688
关键字:

react