react_react-native - 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库的使用,自己封装组件,正式上线白屏问题,性能优化等。对正在工作当中或打算...
  • 这是2019 react入门到高级新课程 学习react,不仅能带来技术提升,同时提高开发效率和体验,更能带来好的就业机会。 本课程主要分为以下几个部分:  一,前端工程化基础?  主要学习node和npm、...
  • 完全征服React Native

    2018-10-22 21:38:05
    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识...
  • 半小时深刻理解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

    2019-10-26 19:55:09
    主要介绍了react项目构成,react的使用等

    React项目

    目录

    1、简介
    2、项目根目录结构配置文件解析
    3、react使用

    1. 简介
    2. 组件状态
    3. 属性
    4. 组件生命周期
    5. 无状态组件
    6. 高阶组件

    简介

    一些工具的组合。
    一个纯粹的index.js 在网页上是可以直接运行的,webserver的本质。
    因为react多用Mixin等,我们需要搭建开发环境,将这些代码转译。只要你做代码修改,保存后会自动重新转译,转译完之后立即动态刷新。
    项目的根目录很重要,很多都是从项目的根目录开始的。

    项目根目录结构

    .(这里有个点)
    |----.babelrc babel的预设值
    |----.gitignore 忽略文件
    |----index.html 项目的根目录
    |----jsconfig.json VScode相关文件,一些项目相关的配置可以放在这里
    |----LICENSE
    |----.npmrc
    |----package.json
    |----package-lock.json
    |----README.md
    |----src/ 项目里的js文件
    | |----app.js
    | |----AppState.js
    | |----index.html
    | |----index.js
    |----node_modules/ 所依赖的模块
    | |----…
    |----webpack.config.dev.js
    |----webpack.config.prod.js

    配置文件

    首先我们先来了解一下每个配置文件
    pacjage.json
    npm init产生的文件,里面记录项目信息,所有项目依赖。

    版本管理
    “repository”:{“type”:“git”,
    “url”:“https://192.168.124.135/react-mobx/react-mobx-starter.git”}

    项目管理
    “scripts”:{
    “test”:“jest”,
    “start”:“webpack-dev-server”,
    “build”:“webpack -p --config webpack.config.prod.js”
    }
    start指定启动webpack的dev server开发用WEB Server,主要提供两个功能:静态文件支持、自动刷新和热替换HMR。HMR可以在应用程序运行中替换、添加、删除模块,无需重新加载页面,只需要把变化的部分替换掉。不使用HMR则会自动刷新。 --hot启动HMR --inline 默认模式,使用HMR的时候建议开启,热替换时会有消息显示在控制台。
    build 使用webpack构建打包,对应$ npm run build,将会按照 webpack -p --config webpack.config.prod.js 对路径里的文件进行打包,然后根目录下会多出一个分发目录,目录含有的文件如下:
    在这里插入图片描述
    我们看看index文件

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div id="root"></div> // id最好不要重复,作为唯一id,JS通过document.getElementByID('root')来找到这个,再进行操作
    <script type="text/javascript" src="./assets/app-d2e554f9.js"></script></body> // 直接在浏览器打开是没有任何显示,因为 <script type="text/javascript" src="/assets/app-d2e554f9.js">这一句中是从web上加载/assets/app-d2e554f9.js,但是我们在本地加载的时候,这个根就变成本地的逻辑驱动器的根了,如果在webserver中运行,则是webserver的根,因此找不到这个文件,文件并没有错,将app-d2e554f9.js加入到项目根下的assets下。在src前加"."就是在index同一个目录下即可。
    </html>
    

    如果想修改样式,
    有几种方式;1、html内部修改,例如:修改html样式

    项目依赖
    devDependencies 开发时依赖,不会打包到目标文件中。对应npm install xxx --save-dev。例如babel的一些依赖,只是为了转译,不需要放在生产环境中。
    dependencies 运行时依赖,会打包到项目中,对应npm install xx --save.

    开发时依赖
    “devDependencies”: {
    “babel-core”: “^6.24.1”,
    “babel-jest”: “^19.0.0”,
    “babel-loader”: “^6.4.1”,
    “babel-plugin-transform-decorators-legacy”: “^1.3.4”,
    “babel-plugin-transform-runtime”: “^6.23.0”,
    “babel-preset-env”: “^1.4.0”,
    “babel-preset-react”: “^6.24.1”,
    “babel-preset-stage-0”: “^6.24.1”,
    “css-loader”: “^0.28.0”,
    “html-webpack-plugin”: “^2.28.0”,
    “jest”: “^19.0.2”,
    “less”: “^2.7.2”,
    “less-loader”: “^4.0.3”,
    “react-hot-loader”: “^3.0.0-beta.6”,
    “source-map”: “^0.5.6”,
    “source-map-loader”: “^0.2.1”,
    “style-loader”: “^0.16.1”,
    “uglify-js”: “^2.8.22”,
    “webpack”: “^2.4.1”,
    “webpack-dev-server”: “^2.4.2”}
    ^版本号,例如^2.4.3,表示安装2.x.x中不低于2.4.3的版本;~版本号表示安装 2.4.x的最新版本,不安装2.5.x;latest表示安装最新版本。一般来说做项目时,大版本号不做改变。

    babel转译,因为开发用了很多ES6语法,从6.x开始babel拆分成很多插件,需要什么引入什么。
    “babel-core” 核心。
    “babel-loader” webpack的loader,webpack基于loader。
    “babel-preset-xxx” babel预设的转换插件
    “babel-plugin-transform-decorators-legacy” 转换装饰器用的。

    css样式相关包括
    “css-loader”、“less”、“less-loader”、“style-loader”。

    “react-hot-loader” react热加载插件。

    “source-map” 文件打包,会压缩合并JS,成一行,下断点没用,没有办法调试,可以将JS源码映射到一个source-map文件中,可以用来看JS的源文件是什么,可以定位到源文件的位置;也需要webpack的loader。

    “webpack” 打包工具

    “webpack-dev-server” 启动一个开发的server

    运行时依赖
    “dependencies”: {
    “antd”: “^2.9.1”,
    “axios”: “^0.16.1”,
    “babel-polyfill”: “^6.23.0”,
    “babel-runtime”: “^6.23.0”,
    “mobx”: “^3.1.9”,
    “mobx-react”: “^4.1.8”,
    “mobx-react-devtools”: “^4.2.11”,
    “prop-types”: “^15.5.8”,
    “react”: “^15.5.4”,
    “react-dom”: “^15.5.4”,
    “react-router”: “^4.1.1”,
    “react-router-dom”: “^4.1.1”}

    “antd” 基于react的前端的库,蚂蚁金服开源的react的UI库,做中后台管理很方便。

    “axios” 异步库,异步请求支持。

    “babel-polyfill” 提供抹去JS差异的工具,解决浏览器api不支持的问题。。

    “react” 开发框架
    “react-dom”:支持dom
    “react-router”: 支持路由
    “react-router-dom”: dom绑定路由

    “mobx” react状态相关,状态管理库,透明化。还可以用react,redx
    “mobx-react” , “mobx-react-devtools” mobx和react结合的模块
    mobx和react结合是很棒的组合,同时还可以react和redx

    前端路由,前端的url进行变化。

    .babelrc babel转译的配置文件。
    {
    “presets”: [
    “react”,
    “env”,
    “stage-0”
    ],
    “plugins”: [“transform-decorators-legacy”, “transform-runtime”]
    }

    webpack配置
    webpack.config.dev.js是一个符合Commonjs的模块。

    const path = require(‘path’);
    const webpack = require(‘webpack’);

    module.exports = { // 导出
    devtool: ‘source-map’, // 生成及如何生成source-map文件,source-map 适合生产环境使用,会生成完成Sourcemap独立文件(运行后会生成一个大的压缩过的JS文件,会告诉你如何映射到你现在的源码)。由于在build中添加了引用注释,所以开发工具知道如何找到Sourcemap。
    entry: { //入口,webpack会从入口(从src下的index.js开始)开始,找出直接或间接的模块和库,最后输出稳bundles文件,entry如果是一个字符串,定义是一个入口文件,如果是一个数组,数组中每一项都会执行,里面包含入口文件(./src/index),另一个参数可以用来配置一个服务器( ‘react-hot-loader/patch’),我们这里配置的是热加载插件,可以自动刷新。
    ‘app’: [
    ‘react-hot-loader/patch’,
    ‘./src/index’ //从src下的index.js开始,
    ]
    },
    output: { // 输出,告诉webpack输出bundles到哪里去,如何命名。filename定义输出到的bundle的名称,
    path: path.join(__dirname, ‘dist’), //输出目录是__dirname, ‘dist’
    filename: ‘bundle.js’, //输出目录的名字
    publicPath: ‘/assets/’ //可以是绝对路径,一般会以/结尾,/assets/表示网站根目录下的assets目录下
    },
    resolve: { // 解析,设置模块如何被解析
    extensions: [’.js’] // 自动解析扩展名,"js"的意思是,如果用户导入模块时不用带扩展名,它会尝试补全。
    },
    module: { //模块,如何处理不同的模块,
    rules: [// 匹配请求的规则,以应用不同的加载器,解析器等。
    {
    test: /.jsKaTeX parse error: Expected 'EOF', got '}' at position 292: … ] }̲, {…/, //遇到less文件结尾的,则用less-loader加载,然后css-loader加载css,最后用style标签把css文件加载到DOM里。
    use: [
    { loader: “style-loader” },
    { loader: “css-loader” }, // css没有模块化、复用的概念,不是语言
    { loader: “less-loader” } //是一个css的预处理语言,扩展了css,增加了变量,Mixin,函数等开发语言特性,是一套语法规则和解析器,将写好的LESS解析成css。less可以使用在浏览器端和服务器端。我们在node_modules下可以看到/.bin/lessc,需要安装npm install less试着将一个test.less文件转化,在shell中输入node__modules/.bin/lessc test.less。会输出转化后的css
    ]
    }
    ]
    },
    plugins: [ // webpack插件
    new webpack.optimize.OccurrenceOrderPlugin(true), //并发插件
    new webpack.HotModuleReplacementPlugin(),//热加载
    new webpack.DefinePlugin({‘process.env’: {NODE_ENV: //全局常量配置 JSON.stringify(‘development’)}})
    ],
    devServer: { //开发server
    compress: true, //开启压缩
    port: 3000,
    publicPath: ‘/assets/’, // 公共路径
    hot: true, //启用热加载
    inline: true,
    historyApiFallback: true,
    stats: {
    chunks: false
    },
    proxy: { //http代理,如果你访问的是http://ip/api路径就会把这个请求转发到 target:‘http://127.0.0.1:8080’,可以更改。
    ‘/api’: {
    target: ‘http://127.0.0.1:8080’,
    changeOrigin: true
    }
    }
    }
    };

    vscode配置
    jsconfig.json是vscode的配置文件,覆盖当前目录。

    以上是所有配置文件,需要修改name,version,description,需要修改repository仓库地址,需要修改author,license信息,修改好之后就可以开发了。

    一个纯粹的index.js 在网页上是可以直接运行的,webserver的本质。

    启动项目
    在项目根目录下启动
    $ npm start
    需要等一会儿如图
    npm start
    启动成功就可以访问,使用http://127.0.0.1:3000就可以看到index了

    • 文件路径为啥要用.,视频中没有。
    • less可以使用在浏览器端和服务器端。我们在node_modules下可以看到/.bin/lessc,需要安装npm install less试着将一个test.less文件转化,在shell中输入node_modules/.bin/lessc test.less。会输出转化后的css。这个没有实现,在shell输入命令没有用,应该是文件路径的问题。
    • 以上是所有配置文件,需要修改name,version,description,需要修改repository仓库地址,需要修改author,license信息,修改好之后就可以开发了。修改要做一下。

    React

    简介

    React是Facebook开发并开源的前端框架。解决的是前端MVC框架中的View视图层的问题。跑在浏览器端的。写起来就像在写面向对象的语言,很方便,所以这是必须学会的。

    Virtual DOM

    DOM(文档对象模型Document Object Model),看图:
    HTML 文档
    这是一个HTML文档,是要被浏览器加载的,浏览器的渲染引擎要解析这些标记,要知道这些标记对应的格式(之前动态的改格式是通过样式表来解决的),实际上浏览器在一开始要加载html css文件,将内容解析之后,把这个html绘制,绘制的同时要知道这是什么样式大小等做出来。dom将这些标记解析成树状结构,在内存中,节点具有类型。如图:
    DOM tree
    为什么呢?因为html本身就是树形结构。
    我们可以对dom树操作,然后修改,将网页所有内容映射到一颗树型结构的层级对象模型上,浏览器提供对DOM的支持,用户可以是用脚本调用DOM API来动态的修改DOM结点,从而达到修改网页的目的,这种修改是浏览器中完成,浏览器会根据DOM的修改重绘改变的DOM结点(带有类型)部分。
    但是修改DOM重新渲染的代价很高,前端框架为了提高效率,尽量减少DOM的重绘,提出来Virtual DOM,所有的修改都是在Virtual DOM上完成的,通过比较算法,找出浏览器DOM之间的差异,使用这个差异操作DOM,浏览器只需要渲染这部分变化就行了。
    React实现了DOM Diff 算法可以高效对比Virtual DOM和DOM的差异。

    支持JSX语法
    是一种JavaScript和XML混写的语法,是JavaScript的扩展。
    将src下的index.js文件修改,然后react会热加载保存后的index.js。代码如下:

    import React from 'react';
    import { render } from 'react-dom'; // render要修改成 ReactDOM即可成功,控制dom修改的。需要加载上。
    
    
    class Root extends React.Component{ //把组件加载到DOM中
        render(){
            return <div>good work</div>//必须return一个jsx的文件
        }
    }
    
    ReactDOM.render(<Root />,document.getElementById("root"));
    

    成功运行:
    成功
    因为是热加载,所以调试起来很方便。
    其中import React from ‘react’;导入React模块,
    import { render } from ‘react-dom’; 导入react的DOM模块
    class Root extends React.Component组件类定义,从React.Component类上继承。这个类生成JSXElement对象,即React元素,其中包含渲染函数render(),返回组件中渲染的内容,只能返回一个顶级元素。
    ReactDOM.render(,document.getElementById(“root”)),第一个参数是JSXElement对象,即React元素;第二个是DOM的Element元素,将React元素添加到DOM的Element元素中并渲染。

    还可以使用React.createElement创建react元素,第一参数是React组件或者一个HTML的标签名称(如div,span等)。如下

    import React from 'react'; // 一定要导入react
    import ReactDOM from 'react-dom';
    
    class Root extends React.Component{ //把组件加载到DOM中 Root是组件,继承自React.Component,其中的render必须是return一个JSX文件
        render(){
            // return <div>good <br /> work</div>//必须return一个jsx的文件,必须是一个元素<div>good <br /> work</div>,如div,如果这样<div> work</div><br />,就是两个元素
          return React.createElement('div',null, 'good work');
          }
    }
    
    ReactDOM.render(React.createElement(Root),document.getElementById('root'))
    
    // ReactDOM.render(<Root />,document.getElementById("root")); //对于<Root />这种单标记,必须有/结尾,不然会报错
    //例如换行符<br />这样才有效果
    

    显然JSX更好用,无论是嵌套还是易读性。

    增加一个子元素如下:

    import React from 'react'; // 一定要导入react
    import ReactDOM from 'react-dom';
    
    class Sub extends React.Component {
      render(){
        return <span> I am Sub element</span>;
      }
    }
    
    class Root extends React.Component { //把组件加载到DOM中 Root是组件,继承自React.Component,其中的render必须是return一个JSX文件
        render(){
            return <div>good work
              <hr />
              <Sub /> </div>;//必须return一个jsx的文件,必须是一个元素<div>good <br /> work</div>,如div,如果这样<div> work</div><br />,就是两个元素
          // return React.createElement('div',null, 'good work');
          }
    }
    
    // ReactDOM.render(React.createElement(Root),document.getElementById('root'))
    
    ReactDOM.render(<Root />,document.getElementById("root")); //对于<Root />这种单标记,必须有/结尾,不然会报错
    //例如换行符<br />这样才有效果
    

    再次注意!!
    React组件要有只能一个return顶级元素的render函数
    所有元素必须必和,如<br / >。

    JSX规范,可以到网上找!
    标签中首字母小写就是html标记,首字母大写就是组件。
    要求严格的HTML标记,要求所有标签都必须闭合。如<br / >。/前要有一个空格;
    单行省略小括号,多行请使用小括号;
    元素可以嵌套,要注意缩进;
    JSX表达式:使用{}括起来,如果大括号内使用了引号,会当做字符串处理,例如{2>1?‘true’:‘false’},比较2和1的大小后输出ture,true变成字符串了。

    组件状态state**

    每一个React组件都有一个状态变量state,它是一个JS对象,可以为它定义属性来保存值。
    如果状态变化了,会触发UI的重新渲染。使用setState()方法可以修改state值。
    state是组件内部使用,组件私有属性。

    每一个标签控制的范围内,只要用鼠标做动作就归该标签管。参考W3C中的onclick事件,只要你点击了,就调用回调函数,一般是用动态增加就是,如< button οnclick=‘my function()’> Click me</ button>(JSX中 button onClick)标签小写,事件响应用小驼峰来写。

    看栗子:
    修改index.js

    class Sub extends React.Component {
      state = {count:0};
      clickHandler(event){console.log(event.target);
      console.log(event.target.id);
      this.setState({count:this.state.count + 1})}
      // this.state.count++;};  // this 的坑,定义时绑定,调用时绑定,此时为undifined。
      render(){
        return <button id="sub" onClick={this.clickHandler.bind(this)}> I am Sub element . {this.state.count}. {2>1?'true':'false'}</button>;
      }
    }
    class Root extends React.Component {
          state = {domain:'I am domain',p1:' I am p1'}
          render(){
            // this.setState({p1:' spy '}) //不可以对还在更新中的state使用setstate 
            // this.state.p1 = 'spy 1'
            setTimeout(()=>this.setState({p1:' spy '}),5000) 
            return <div>good work {this.state.domain} {this.state.p1}
              <hr />
              <Sub /> </div>;
          // return React.createElement('div',null, 'good work');
          }
    }
    ReactDOM.render(<Root />,document.getElementById("root"));
    

    保存后页面会自动部分重载,如图
    button实现
    如果我们在Root中这样修改state,this.setState({p1:’ spy '}) 会报错,因为不可以对还在更新中的state使用setstate,因为render和这个修改是同步的,因此我们用一个延时的异步调用来修改,即可得到想要的结果。而Sub中完全不同,sub是事件驱动调用,因此也是异步调用,不会出现在Root中的问题。
    clickHandler把state的值变化了,引起了render函数的重绘。而传统的做法是将button的文本里的值等于了一下。id是留给JS调用的。render函数一旦调用,浏览器中已经绘制好了。组件中的state变化了,组件会调用render函数重回,这是由框架决定的。
    state的变化引起render的调用,因为render里含有state的参数,而重绘是靠虚拟dom,虚拟dom一比较两次节点的差异,如果没有差异,就不会重新渲染,即使你的state变化了;如果有就会重新变化。
    如果state的变化引起了render函数返回值的变化,在调用render后,虚拟dom比较两个节点的差异,发现有差异,然后浏览器会重新渲染,将改变的一小块重新渲染;如果state的变化没有引起render函数返回值的变化,在调用render后,虚拟dom比较两个节点的差异,发现没有差异,则浏览器不会调用重新渲染。

    看看一个状态例子。

    <html>
    <head>
        <script type='text/javascript'>
            function getEventTrigger(event){
                x = event.target; //从事件中获取元素
                alert('触发的元素id是', +x.id);
            }
            </script>
    </head>
    <body>
        <div id = 't1' onmousedown = "getEventTrigger(event)">
        点击这句话,会触发一个时间,并弹出一个警示框
        </div>
    </body>
    </html>>
    

    在这个例子中,div的id是t1,鼠标按下事件绑定了一个函数,只要鼠标按下就会触发调用getEventTrigger函数,浏览器会给它一个参数event,当事件触发时,event包含触发这个事件的对象,可以找到这个对象的元素并获取属性,然后将属性输出(在这里属性时id = ‘t1’)。这里是传统写法,如何变成前端框架来实现呢?

    HTML DOM 的JavaScript事件

    名称 此事件发生在何时
    onabort 图像的加载被中断
    onblur 元素失去焦点
    onchange 域的内容被改变
    onclick 当用户点击某个对象时调用的事件句柄
    ondblclick 当用户双击某个对象时调用的事件句柄
    onerror 在加载文档或图像时发生错误
    onfocus 元素获得焦点
    onkeydown 某个键盘按键被按下
    onkeypress 某个键盘按键被按下并松开
    onkeyup 某个键盘按键被松开
    onload 一张页面或图像完成加载
    onmousedown 鼠标按钮被按下
    onmousemove 鼠标被移动
    onmouseout 鼠标从某元素移开
    onmouseover 鼠标移动到某元素上
    onmouseup 鼠标被松开
    onreset 重置按钮被点击
    onresize 窗口或框架被重新调整大小
    onselect 文本被选中
    onsubmit 确认按钮被点击
    onunload 用户退出页面

    onload一般用在body上,dom树一点一点形成,其中AMD的异步模块加载要有一个回调,因此会用到onload。
    onchange域,注册的时候,写入用户名时会有该用户名已经使用(异步调用模式,并返回调用结果一个json)。在JSX中要注意小驼峰。on后面首字母要大写。

    阻止默认行为,event.preventDefault()调用一下即可阻止例如,点击链接弹窗,试验一下即可。
    使用react实现上面的传统的HTML

    import React from 'react';
    import ReactDOM from 'react-dom';
    class Toggle extends React.Component{
      state = {flag:true};
      handleClick(event){
        console.log(event.target.id);
        console.log(event.target === this);
        console.log(this);
        console.log(this.state);
        this.setState({flag:!this.state.flag});
      }
      render(){//注意绑定this,onClick一定要写成小驼峰
        return <div id = "t1" onClick={this.handleClick.bind(this)}>
          点我触发事件。{this.state.flag.toString()}
        </div>;
      }
    }
    class Root extends React.Component {
      state = {p1:'p1 is here',p2:'p2 is here'};
      render(){
        setTimeout(()=> this.setState({p1:'exchange p1'}),3000);
      return (
        <div>
          <div>your happy for {this.state.p1}, {this.state.p2}</div>
          <br />
          <Toggle />
        </div>
      );
      }
    }
    ReactDOM.render(<Root />,document.getElementById("root"));
    

    网页输出:
    在这里插入图片描述
    分析
    Toggle类
    有属性state,当render完成后,网页上有一个div标签,div标签对象捆绑了一个click事件的处理函数,div标签内有文本内容。如果通过点击左键,就触发了click方法关联的handleClick函数,在这个函数里将状态值改变。状态值state的改变将引发render重绘。如果组件自己的state改变了,只会触发自己的render方法重绘。

    注意{this.handleClick.bind(this)}不能外加引号,一定要绑定this,否则触发捆绑的函数时,this是函数执行的上下文决定的,this已经不是触发事件的对象了。console.log(event.target.id);取回的产生事件的对象的id,但是这不是我们封装的组件对象。 console.log(event.target === this);是false。所以这里一定要用this,而这个this是通过绑定来的。

    react中的事件
    使用小驼峰命名
    使用JSX表达式,表达式中指定事件处理函数
    不能使用return false,如果要阻止事件默认行为,使用event.preventDefault()

    属性props

    我们将React组件(例如Toggle)当做标签使用,为其增加属性,如:
    ,尝试增加属性:
    test = ‘think god’ 这个属性作为一个单一的对象传递给组件,加入到组件的props属性中。
    parent = {this} 可以传对象在其中,这个this是在Root元素中,是Root组件本身。
    在Root中使用JSX语法为Toggle增加子元素,这些子元素也会被加入到Toggle组件的props.children中。看栗子:

    import React from 'react';
    import ReactDOM from 'react-dom';
    
    
    class Toggle extends React.Component{
      state = {flag:true};
      handleClick(event){
        console.log(event.target.id);
        console.log(event.target === this);
        console.log(this);
        console.log(this.state);
        this.setState({flag:!this.state.flag});
      }
      render(){//注意绑定this,onClick一定要写成小驼峰
        return <div id = "t1" onClick={this.handleClick.bind(this)}>
          点我触发事件。{this.state.flag.toString()}
          <hr /> {this.props.test}
          <br />
          {this.props.children} // 不能修改props的属性值,类似于变成只读
          {this.props.parent.state.p1} //在组件内调用props所含有的属性
        </div>;
      }
    }
    
    class Root extends React.Component {
      state = {p1:'p1 is here',p2:'p2 is here'};
      render(){
        setTimeout(()=> this.setState({p1:'exchange p1'}),3000);
      return (
        <div>
          <div style = {{color:'red'}} >your happy for {this.state.p1}, {this.state.p2}</div>
          <br />
          <Toggle test = 'think god' parent = {this}>  // 将属性通过props添加到Toggle组件中,其中parent为类
            <a href='http://baidu.com'>baidu</a>
            <span>I am the Toggle children <br /></span>
          </Toggle>
        </div>
      );
      }
    }
    
    ReactDOM.render(<Root />,document.getElementById("root"));
    

    输出为
    props
    但是不能修改props的属性,因为是公有属性,组件外可访问,只读。和state不同,state是组件私有属性,组件外无法访问,可以修改。

    构造器constructor

    使用ES6的构造器,要提供一个参数props,并把这个参数使用super传递给父类。就是this 的props。
    例如:

    import React from 'react';
    import ReactDOM from 'react-dom';
    
    
    class Toggle extends React.Component{
      constructor(props){
        super(props)
        this.state = {flag:true};
      }
      handleClick(event){
        console.log(event.target.id);
        console.log(event.target === this);
        console.log(this);
        console.log(this.state);
        this.setState({flag:!this.state.flag});
      }
      render(){//注意绑定this,onClick一定要写成小驼峰
        return <div id = "t1" onClick={this.handleClick.bind(this)}>
          点我触发事件。{this.state.flag.toString()}
          <hr /> {this.props.test}
          <br />
          {this.props.children}
          {this.props.parent.state.p1}
        </div>;
      }
    }
    
    class Root extends React.Component {
      constructor(props){
        super(props);
        this.state = {p1:'p1 is here',p2:'p2 is here'};
      }
      // state = {p1:'p1 is here',p2:'p2 is here'};
      render(){
        setTimeout(()=> this.setState({p1:'exchange p1'}),3000);
      return (
        <div>
          <div style = {{color:'red'}} >your happy for {this.state.p1}, {this.state.p2}</div>
          <br />
          <Toggle test = 'think god' parent = {this}>
            <a href='http://baidu.com'>baidu</a>
            <span>I am the Toggle children <br /></span>
          </Toggle>
        </div>
      );
      }
    }
    
    ReactDOM.render(<Root />,document.getElementById("root"));
    

    输出同上。

    组件的生命周期

    组件的生命周期分成三个状态:
    Mounting:已插入真实DOM
    Updating:正在被重新渲染
    Unmounting:已移出真实DOM
    组件的生命周期状态,说明在不同时机访问,组件处于生命周期的不同状态上。在不同的生命周期访问组件,就有不同的方法
    如下:

    • 装载组件触发

      • componentWillMount:在渲染前调用,在客户端也在服务器。只会在装载之前调用一次。

      • 有一次render的调用

      • componentDidMount:在第一次渲染后掉用,只在客户端,之后组件已经生成了对应的DOM结构,可以通过this.getDOMNoder()来进行访问。如果想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout,setInterval或者法师AJAX请求等操作(防止异步阻塞UI)。只在装载完成后调用一次,在render之后。

    • 更新组件触发,这些方法不会再首次render组件的周期调用

      • componentWillReceiveProps(nextProps)在组件接受到一个新的prop时被调用。这个方法在初始化render时不会被调用.

      • shouldComponentUpdate(nextProps,nextState)返回一个bool值(如果返回false,则更新流程到此为止,不会到达render,每次渲染都会经过它)。在组件接受到新的props或者state时被调用,在初始化时或者使用forceUpdate时不被调用。

        • 可以在你确认不需要更新组件时使用。
        • 如果设置为false,就是不允许组件更新,那么componentWillUpdate,ComponentDidUpdate不会执行。
      • componentWillUpdate(nextProps,nextState)在组件接受到新的props或者state但还没有render时被调用,在初始化时不会被调用。

      • ComponentDidUpdate(prevProps,prevState)在组件完成更新后立即调用。在初始化时不会被调用。

    • 卸载组件触发

      • componentWillUnmount在组件从DOM中移除的时候立刻被调用。

    流程图如下:
    组件生命周期
    constructor构造器是最早执行的函数。
    触发 更新生命周期函数,需要新state或者props。
    因此重新写src/index.js
    构造两个组件,在子组件Sub中,加入所有生命周期。
    看栗子:

    import React from 'react';
    import ReactDOM from 'react-dom';
    
    class Sub extends React.Component{
      constructor(props){
        // console.log(...props);
        console.log('Sub constructor');
        super(props);// 调用父类的构造器
        this.state = {count:0};
      }
      handleClick(event){
        this.setState( {count : this.state.count +1} );
      }
      render(){
        console.log('Sub render')
        return (<div id='sub' onClick={this.handleClick.bind(this)}>
          Sub's count ={this.state.count}
        </div>);
      }
      componentWillMount(){
        console.log('Sub componentWillMount');
      }
      componentDidMount(){
        console.log('Sub  componentDidMount');
      }
      componentWillUnmount(){
        console.log('componentWillUnmount')
      }
    
    }
    
    class Root extends React.Component{
      constructor(props){
        // console.log(...props);
        console.log('Root Constructor');
        super(props);
        this.state = {};
      }
      render(){
        return (
          <div>
            <Sub />
          </div>
        )
      }
    }
    
    ReactDOM.render(<Root />,document.getElementById("root"));
    

    运行输出
    在这里插入图片描述
    从这里可见这些周期函数的调用顺序。每次点击,造成的state,或者props改变,都会调用一次render。其中props是空的{}。
    这里再增加一些元素,以更加清晰的方式看这个栗子;

    class Sub extends React.Component{
      constructor(props){
        console.log(1 ,props);
        console.log('Sub constructor');
        super(props);// 调用父类的构造器
        this.state = {count:0};
      }
      handleClick(event){
        this.setState( {count : this.state.count +1} );
      }
      render(){
        console.log('Sub render')
        return (<div style = {{height:100+'px',color:'red',backgroundColor:'#f0f0f0'}}>
          <a id='sub' onClick={this.handleClick.bind(this)}>
            Sub's count ={this.state.count}
          </a>
        </div>);
      }
      componentWillMount(){
        console.log('Sub componentWillMount');
      }
      componentDidMount(){
        console.log('Sub  componentDidMount');
      }
      componentWillUnmount(){
        console.log('componentWillUnmount')
      }
      componentWillReceiveProps(nextprops){
        console.log(this.props);
        console.log(nextprops);
        console.log('Sub componentWillReceiveProps',this.state.count);
      }
      shouldComponentUpdate(nextProps,nextState){
        console.log('Sub shouldComponentUpdate', this.state.count, nextState);
        return true;
      }
      
      componentWillUpdate(nextProps,nextState){
        console.log('Sub componentWillUpdate', this.state.count, nextState)
      }
    
      componentDidUpdate(prevProps,prevState){
        console.log('Sub componentDidUpdate', this.state.count, prevState)
      }
    }
    
    class Root extends React.Component{
      constructor(props){
        console.log(2,props);
        console.log('Root Constructor');
        super(props);
        this.state = {flag:true,name :'root'};
      }
      handleClick(event){
        this.setState({
          flag:!this.state.flag,
          name:this.state.flag ? this.state.name.toLowerCase():this.state.name.toUpperCase()
        });
      }
      render(){
        return (
          <div id ='root' onClick={this.handleClick.bind(this)}>
            name is {this.state.name}
            <hr />
            <Sub />
          </div>
        )
      }
    }
    

    运行结果如下
    在这里插入图片描述
    运行结果
    componentWillMount第一次装载,再首次render之前;
    componentDidMount 第一次装载结束,再首次render之后;

    componentWillReceiveProps 在组件内部,props是只读不可变的,但是这个函数可以接收新的props,可以对props进行处理,如果加入this.props={name:‘customer’},就可以偷偷更换props,触发后也要进入shouldComponentUpdate.

    shouldComponentUpdate判断是否需要组件更新,就是是否render,精确的控制渲染,提高性能。

    componentWillUpdate在除了首次render之外,每次render前执行,componentDidUpdate在render之后调用。
    这些函数只是为了精确控制。

    但是这样编写代码过于复杂,如果可以用上箭头函数,装饰器等就更好了,下面说一说高阶技术。

    无状态组件

    React从15.0开始支持无状态组件,定义如下:

    function Root(props){
      return <div>{props.name}</div>
    }
    
    ReactDOM.render(<Root name = 'root'/>,document.getElementById("root"));
    

    当然可以使用箭头函数
    let Root = (props) => <div>{props.name}</div>
    ReactDOM.render(,document.getElementById(“root”));

    就是将函数作为组件,不需要state状态,生命周期函数,只需要一个props然后返回React元素即可。

    高阶组件

    上面的无状态组件如果要进行增强,就要用到类似于装饰器,将Root组件的div外部再加一层div。
    先做一个加强函数,看栗子,科里化,加箭头函数的变化:

    let Wrapper = Component=>props =>
    (<div>
      {props.name}
      <hr />
      <Component />
    </div>);
    let Root = props=> <div>{props.name}</div>;
    let NewRoot = Wrapper(Root)
    ReactDOM.render(<NewRoot name = 'root'/>,document.getElementById("root")); // 注意命名。开头大写
    

    再加装饰器:

    let Wrapper = Component=>props =>
    (<div>
      {props.setName}
      <hr />
      <Component {...props}/>//在传进来的Component组件中加入属性,使得root可以使用props
    </div>);
    
    @Wrapper
    class Root extends React.Component{
      render(){
        return <div>{this.props.setName}</div>;
      } 
    }
    ReactDOM.render(<Root setName = 'root'/>,document.getElementById("root"));
    

    带参装饰器
    就是多几个箭头函数的事。例如

    let Wrapper =id => Component=>props =>
    (<div>
      {props.setName}
      <hr />
      <Component {...props} ids={id}/>
    </div>);
    
    @Wrapper('this id')
    class Root extends React.Component{
      render(){
        return <div> {this.props.setName, this.props.ids}</div>;
      } 
    }
    
    ReactDOM.render(<Root setName = 'root'/>,document.getElementById("root"));
    

    可以将参数传到各个组件中。这样更像面向对象语言。

    展开全文
  • 博客待更新…… 完整项目源码见https://github.com/smileyqp/loginReactApp

    博客待更新……

    完整项目源码见

    https://github.com/smileyqp/loginReactApp

    React&Redux+node.js+postgreSQL实现登录认证系统

    注意:client中开启的命令为:npm start
    在server中开启的命令:npm start;这个是直接在整个项目下进行npm start命令便可
    因为配置了:

    "start": "nodemon --watch server --exec babel-node -- server/index.js"</br>
    

    nodemon是为了前端刷新时候实时刷新不用重启的一个配置

    1、在整个项目目录下
    创建server和client两个包分别放置server和前端代码;
    整个项目根目录下执行下面两个创建json目录进行项目初始化以及安装express框架

    npm init -y</br>
    npm install express --save
    

    2、如何解决ES6语法转化问题?
    安装babel;babel是用于ES6语法解析,babel网址:https://babel.bootcss.com/
    npm install --save-dev babel-cli babel-preset-env
    创建 .babelrc 文件

    {
      "presets": ["env"]
    }
    

    之后在package.json中添加

    "start": "babel-node server/index.js"
    

    3、如何解决修改代码之后刷新不能立即生效问题?
    npm install nodemon --save-dev
    然后package.json中配置改为 “start”: " nodemon --watch server --exec babel-node – server/index.js"


    4、前端页面布局开始
    引入bootstrap,在index.html中

    <link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"></br>
    

    bootstrap用到的样式网址:https://getbootstrap.com/docs/4.3/examples/navbars/

    5、添加引入路由的库

    npm install react-router-dom --save
    

    6、安装axios用于post数据

    npm install axios
    

    7、在全局的安装body-parser

    npm install body-parser --save
    

    并在server中的index中引用body-parser

    import bodyParser from 'body-parser';
    app.use(bodyParser.json());
    

    8、在client的package.json中添加 “proxy”: “http://localhost:6060”

    9.注意clone下来之后在整个项目目录下面添加.babelrc文件,里面加上
    {
        "presets": ["env"]
    }
    

    10、用于做表单前段过滤验证的js库
    https://github.com/chriso/validator.js
    整个项目安装validator库

    npm install validator --save
    

    11.整个项目下安装lodash库

    npm install lodash --save
    

    12.此点结合10和11;主要是怎样进行为空错误处理
    后端接收前台传过来的请求;对应的URL定位到register的

    13.整个项目安装classnames库;来解决表单错误提示时候是否明显问题;即作用为,动态改变控制节点的className属性来改变其样式(见signupForm中实例)

    npm install classnames
    import classnames from 'classnames';
    className={classnames('form-control',{'is-invalid':errors.username})}
    

    解释:is-invalid这个className的值是errors.username;当errors.username存在时候为true这个clasName存在;当errors.username不存在,那么这个is-invalid也是为false不显示的

    14、做注册跳转:
    方法一:是直接通过其父组件拿到其history的属性;然后push到上一个页面即:

    //signupPage中
    <SignupForm history={this.props.history} userSignupRequest={this.props.userSignupRequest}/>
    //signupForm中,在form表单请求返回中,返回成功的话就push定位到上一个历史记录即login等界面
    this.props.userSignupRequest(this.state).then(
        () => {
            this.props.history.push('/');
        },
        ({response}) => {
            this.setState({errors:response.data,isLoading:false})}
    );
    

    方法二:SignupForm中直接引入withRouter;然后再在导出的时候使用withRouter这个方法

    import {withRouter} from 'react-router-dom';
    
    export default withRouter(SignupForm);
    
    //push部分的代码不变
    this.props.userSignupRequest(this.state).then(
        () => {
            this.props.history.push('/');
        },
        ({response}) => {
            this.setState({errors:response.data,isLoading:false})}
    );
    

    方法三:取出上下文

    import PropTypes from 'prop-types';
    
    static propTypes = {
        router:PropTypes.object
    }
    

    15、添加flash
    在client中安装shortid这个库,这个库是用来生成唯一的随机数,保证唯一性(详情使用见reducer中的flashMessage.js中)

    npm install shortid --save
    
    import shortid from 'shortid';
    
    case ADD_FLASH_MEASSAGE:
        return [
            ...state,//原来的state
            {
                id:shortid.generate(),//shortid是查询随机数保证它唯一的
                type:action.message.type,
                text:action.message.text
            }
        ]
    

    缕清整个项目前后台数据交互方式

    前台请求发送部分

    • 在SignupForm中。用state对象存储username、email、password、passwordConfirm等信息
    • 在其form表单中将value值设置成this.state.username等;让state中的对象实时与state保持一致(注意别忘记添加onchangeFunction;因为react中的是唯一数据源)
    • 在SignupForm表单中的form中添加onSubmit={this.onSubmit}这个function;并且这个function是触发了action为userSignupRequest(这个function是带参数为当时表单穿进去的数据的state对象)的function;这个userSignupRequest的function是在SignupPage中的时直接引用进来之后通过SignupForm组件传给SignupForm的
    • 在SignupActions中的userSignupRequest方法引入了axios用于dispatch数据;比如此处是进行post数据给后台;此时dispatch也是带了当时传进来的数据state;dispatch之后带的两个参数分别为请求路径以及请求数据axios.post(’/api/users’,userData)
    • 到了次部前台基本流程结束;不过在json中添加一个响应代理服务器;当前是加上后台响应的localhost:6060
    • 此时前台发送请求就基本结束

    后台接收请求部分

    • 后台在整个项目安装bodyParser(路径解析);以及babel(ES6语法解析);并进行引用,在index页面引用bodyParser;
    • 在server的index中进行路径解析;判断前端传过来的是请求哪一个路径的;并进行匹配;例如此处进行请求的路由为/api/users;那么index中为app.use(’/api/users’,users);对应的users进入users.js中进行继续匹配
    • 匹配到users.js中之后,查看请求类型;此案例中请求的类型为post;那么对应users.js中为(注意;users.js中的路由;虽然是’/'但是其实其根目录是相对于/api/users而言;并且users.js中还可以有其他的请求方法以及请求路径;详情见users.js中注释部分)
    router.post('/',(req,res)=>{
        //console.log(req.body);
        const {errors,isValid} = validateInput(req.body);
        if(!isValid){
            res.status(400).json(errors);
        }
    });
    
    • 此案例中接下来进行的部分是前台传过来的表单state进行是否为空验证;安装validator,validator是一个用于表单验证的第三方库;安装lodash用其中的isEmpty方法(详情见users.js中);此时整个后台就可以对传过来的表单数据进行验证,如果为空就可以将错误信息传给前台
    • 前台signupForm.js中的submit中的一个dispath的axio方法有then方法;then方法是一个request成功之后获得返回数据的一个方法:
    this.props.userSignupRequest(this.state).then(
        () => {},
        ({response}) => {this.setState({errors:response.data,isLoading:false})}
    );
    
    • 然后在获得数据中将这个返回的errors放进state中;然后在render中获得errors渲染在页面上(isLoading是放置重复提交;前一个errors.username用于判断是否显示后面的,有显示没有不显示)
    const {errors} = this.state;//提取错误信息
    {errors.username && <span className='form-text form-muted'>{errors.username}</span>} 
    
    展开全文
  • 1、创建Login类 2、创建TestLogin类 import java.util.Scanner; public class Login { String name; //账号 String pwd; //密码 /** * @param args ... System.out.println("\t欢迎进入经销商系统");... ...

    1、创建Login类

    2、创建TestLogin类

    import java.util.Scanner;
    public class Login {
    String name;        //账号
    String pwd;            //密码
        /**
         * @param args
         */
        public void Login() {
            // 输出出菜单
            System.out.println("\t欢迎进入经销商系统");
            System.out.println("\t1、登陆系统");
            System.out.println("\t2、退出");
            System.out.println("请输入要进入的菜单:");
            boolean con;
            do{
                Scanner input=new Scanner(System.in);
                con=false;
                int no=input.nextInt();
                if(no==1){
                    input();
                }else if(no==2){
                    Send();
                    con=true;
                }
                
            }while(con);

        }
        public void input(){
            Scanner input=new Scanner(System.in);
            System.out.println("请输入账号");
            String name=input.next();
            System.out.println("请输入密码");
            String pwd=input.next();
            if("lyg".equals(name)&&"abc123..".equals(pwd)){
                System.out.println("登陆成功");
                System.out.println("*****************************************");
                System.out.println("\n\t欢迎登陆经销商系统");
                System.out.println("\t 1.客户信息维护");
                System.out.println("\t 2.客户信息查询");
                System.out.println("\t 3.客户信息修改");
                System.out.println("输入0退出系统:");
                int no=input.nextInt();
                if(no==0){
                    Send();
                }else{
                    System.out.println("输入错误结束。");
                }
            }else{
                System.out.println("密码错误,请重新登陆");
                input();
            }    
        }
        public void Send(){
            System.out.println("系统已退出,欢迎下次在使用");
        }
          

    }
     

    -------------------------------------------

    import java.util.Scanner;
    public class TestLogin {

        /**
         * @param args
         */
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            Login l=new Login();
            l.Login();
            Scanner input=new Scanner(System.in);
            int choice=input.nextInt();
            switch(choice){
            case 1:
                l.Login();
            case 2:
                l.Send();
            }

        }

    }
     

    展开全文
  • 以登录存贮账号密码为例: actions部分: actions/login.js: export function updateAccount(value) { return { type: 'UPDATE_ACCOUNT', value } } export function updatePsW(value) { retur...
  • SPA的鉴权方式和传统的web应用不同:由于页面的渲染不再依赖服务端,与服务端的交互都通过接口来完成,而REASTful风格的接口提倡无状态(state less),通常不使用cookie和session来进行身份认证。...
  • 仔细想想,我们的后台系统还没有一个登录功能,太不靠谱,赶紧把防盗门安上!SPA的鉴权方式和传统的web应用不同:由于页面的渲染不再依赖服务端,与服务端的交互都通过接口来完成,而REASTful风格的接口提倡无状态...
  • 学习React不是一蹴而就的事情,入门似乎也没那么简单。但一切都是值得的。今天给大家带来一个详细的React的实例,实例并不难,但对于初学者而言,足够认清React的思考和编写过程。认真完成这个实例的每一个细节会让...
  • react基础教程

    2020-01-14 10:13:56
    react基础教程
  • React声明周期(16.3版本以前) 分为3个阶段: 1. 加载阶段 1. constructor() // 加载的时候调用一次,可以舒适化state 2. render() // 渲染页面 3. componentDidMount() // 组件挂在后触发 2. 更新阶段 1. ...
  • 很多人都听说过比特币、以太坊等区块链项目,也了解过区块链的工作原理,可能还动手写过 Solidity 智能合约代码。对于一个之前未曾了解过区块链的新手来说,接触一个区块链项目最好的途径可能就是它的区块浏览器...
  • 前端面试题(React

    2019-05-16 15:13:06
    React 1,调用 setState 之后发生了什么? 在代码中调用setState函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。 经过调和过程,React 会以相对高效的方式...
  • React事务机制解析

    2019-01-11 23:28:57
    react有一定了解的开发人员应该都听说过react的事务机制,这个机制几乎贯穿于react所有提供的方法,包括react中最常使用的setState函数 那么,react的事务机制到底是一种什么样的机制呢,为了解释事务机制的实现...
  • 大家好,我是凯文,本篇文章将介绍React前端框架的环境配置以及项目搭建方法,其中涉及到了node.js(js运行平台)、npm(依赖包管理工具)等内容。网上已经有许多类似的教程,这篇文章可以给各位做个参考,同时给我...
  • React组件之间传值

    2016-03-21 10:07:37
    今天群里面有很多都在问关于 React 组件之间是如何通信的问题,之前自己写的时候也遇到过这类问题。下面是我看到的一篇不错英文版的翻译,看过我博客的人都知道,我翻译可能不会按部就班,会尽可能用中文的意思,来...
  • 近期和一些朋友聊到了 React-Native 的官方重构状态,而刚好近期发布的 0.59.x 系列版本中,上层设计出现了比较大的调整,结合体验之后的状态,就想聊聊 React-Native 的现状、新版本的升级体验、还有新支持的 React...
  • 前言React是Facebook推出的一个前端框架,之前被用于著名的社交媒体Instagram中,后来由于取得了不错的反响,于是Facebook决定将其开源。出身名门的React也不负众望,成功成为当前最火热的三大前端框架之一。相比于...
1 2 3 4 5 ... 20
收藏数 217,704
精华内容 87,081
关键字:

react