react_reactjs - CSDN
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

    千次阅读 多人点赞 2019-07-05 10:08:08
    声明:本文来自腾讯增值产品部官方公众号小时光茶社,为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-11-21 12:11:17
    学习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基础教程

    千次阅读 2020-01-14 10:13:56
    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'
      在这里插入图片描述
    展开全文
  • 这是2019 react入门到高级新课程 学习react,不仅能带来技术提升,同时提高开发效率和体验,更能带来好的就业机会。 本课程主要分为以下几个部分:  一,前端工程化基础?  主要学习node和npm、...
  • 本文内容参考And Design 具体API内容请参考 Ant Design 此部分内容为个人学习记录专用: 表头只支持列合并,使用 column 里的 colSpan 进行设置。 表格支持行/列合并,使用 render 里的单元格属性 colSpan 或者 ...

    本文内容参考And Design 具体API内容请参考 Ant Design
    此部分内容为个人学习记录专用:

    表头只支持列合并,使用 column 里的 colSpan 进行设置。
    表格支持行/列合并,使用 render 里的单元格属性 colSpan 或者 rowSpan 设值为 0 时,设置的表格不会渲染。

    这句话 是来自And Design Table中关于表格行/列合并 内容。

    废话先不说 先上效果图:
    在这里插入图片描述
    我们想实现的内容 是对数据的列进行合并,因为有很多数据都是相同的 比如说我效果图中的省份,直属台等信息。
    在查找Antd API与效果图中我们知道,要想实现这样的效果,我们需要对Table表头进行处理,rowSpan(对应行 主要 处理使用) **colSpan(对应列) **
    后端返回的数据是我们使用的常规数据 我们主要是在前端进行处理
    先看下我们表头

    columns = [
                {
                    title: '直属台',
                    dataIndex: 'ZHISHUTAI',
                    key: 'ZHISHUTAI',
                    render: (text, row, index) => {
                        return {
                            children: text,
                            props: {
                                rowSpan: row['ZHISHUTAI&rowSpan'],
                            }
                        }
                    },
                }, 
    

    这里 我们要知道 rowSpan的作用是告诉Table中表格数据那些行合并
    rowSpan中对应的是我们行的数据 如rowSpan:5 就代表5行进行合并 而0 表示不渲染
    (具体可看Antd 中文官网
    ZHISHUTAI&rowSpan 是我们将后台数据进行处理后的结果,比如说 我50条数据中,前10条是 直属台的同名字字段 例如:291台,这时候 我们就要自己计算出 291台有几个(因为计算机不知道 所以要通过JS去算出具体数据,然后重新拼装在JSON的数据中去,在这部分中进行调用。)
    这时候 我们在通过这个字段将对应的rowSpan给确定下俩。涉及一个知识点 关于对象的引用 对象引用两种方式 “ . ” 点 与 “[]”中括号,一个表示确定静态的(确定内容的) 一个表示动态的(变量值) 而我们在判断对象是是否存在某一属性时 可以使用 in 属性或者是 hasOwnProperty 这两个属性。 一个延伸至继承属性(也就是原型上), 一个仅在自有属性 。 此部分了解即可 因为 我也不知道 干啥可以用到 毕竟 闲鱼。

    展开全文
  • 项目场景: 前端开发往往会遇到表格处理的问题,例如合并单元格,该文章给处理自动合并单元格的方法 问题: 例如我们想把下面的表格做出合并: 班级 姓名 班主任 语文 数学 ... 郝老师
  • React&antd表格合并列

    2020-06-30 10:15:06
    合并表格中连续的相同的列 // antd表格合并列 /* key: 键值 row:当前行 index:当前行索引 listData:表格全部数据 */ export const rowSpanTable = (key, row, index, listData) => { let rowSpan = 1 ...
  • antd Table合并单元格原始数组原始数据 使用Table展示如下name是本文实例需要合并的字段首先将原始数组做处理使用方法合并结果作者:黄仕达 原始数组 const data = [ { key: '0', name: 'John Brown', ...
  • 关于React全家桶的介绍

    千次阅读 2019-01-22 11:01:15
    首先需要安装react: npm install react react-dom --save-dev JSX 语法 JSX 的基本语法规则:遇到 HTML 标签(以 &amp;amp;amp;lt; 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript ...
  • 现在最热门的前端框架有AngularJS、React、Bootstrap等。自从接触了ReactJS,ReactJs的虚拟DOM(Virtual DOM)和组件化的开发深深的吸引了我,下面来跟我一起领略 ReactJS的风采吧~~ 章有点长,耐心读完,你会有很大...
  • 说说前端框架React

    万次阅读 2018-12-07 16:54:41
    上一篇文章讲了一下他的儿子vue,作为vue的爹,react有什么牛逼的地方吗?当然有。他的诞生是facebook为了处理开发ins中出现的问题,起初调研过市场上已存的mvc框架,发现都不太满意,于是就推陈出新,开发了react...
  • 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() { ...
  • (精华)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
  • import React , {useEffect,useState} from 'react' const UseEffect = ()=>{ const [loading,setLoading] = useState(true) useEffect(()=>{ setTimeout(()=>{ setLoading(false) },2000) }) ...
  • 在相应的路径下执行命令行:react-native init 项目名 (名称不可使用连接符等特殊字符,命名可以参考APP应用名称 比如 FaceBook) react-native --v //查看版本 react-native init demo --version 0.48.0//安装指定...
  • import React , {useReducer} from 'react' // (state,action)=>newState const UseReducer = ()=>{ const reducer = (state,action) =>{ if(action.type === 'add'){ return { ...state, count:state...
  • 欢迎大家关注【跨平台开发那些事】公众号,定期推...基于最新版本React Native实现JsBundle预加载,界面秒开优化 一、开源库介绍 今年1月份,新开源的react-natvigation库备受瞩目。在短短不到3个月的时间,gith...
1 2 3 4 5 ... 20
收藏数 278,518
精华内容 111,407
关键字:

react