react_react生命周期 - CSDN
react 订阅
《react》是VOCALOID家族的一首歌曲,由黒うさP作词、作曲、编曲;初音ミク、镜音双子演唱,音乐时长4:52。 展开全文
《react》是VOCALOID家族的一首歌曲,由黒うさP作词、作曲、编曲;初音ミク、镜音双子演唱,音乐时长4:52。
信息
外文名称
React
歌曲语言
日语
填    词
黒うさP
编    曲
黒うさP
歌曲时长
4:52
歌曲原唱
初音未来,镜音双子
谱    曲
黒うさ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"));
    

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

    展开全文
  • 介绍react的一些基本的知识

    1.react的思想:

    react可以用下面的公式表示: UI = render(data);UI表示用户看到的界面,UI是通过纯函数的render将数据data渲染出来。

    react一个比较重要的思想是虚拟DOM(virtual DOM),传统的前端思想:如HTML,每个元素对应一个DOM节点,HTML元素逐级包含,构成了DOM树,浏览器为了渲染HTML格式的网页,就会将HTML文本解析成DOM树,根据DOM树渲染界面给用户,当需要更改界面的时候,直接改变DOM树上的节点。

    传统的DOM方法更新界面,虽然仅修改了一条JS语句,但是往往引起浏览器重新对网页进行布局,重新绘制,这样的执行过程比执行一体JS语句慢很多。

    所谓虚拟DOM是对DOM树的一种抽象,它不会直接和浏览器打交道,只是仅存在于JS空间的树形结构,每次自上而下渲染react组件的时候,会将本次产生的虚拟DOM与上一次渲染虚拟DOM进行对比(这种对比的diff算法后续会介绍),然后仅渲染不同的地方即可。

    2.react组件的数据:

    react组件分为两种:prop和state,两者的变化都会引起组件的重新渲染,一般情况:prop组件是对外的接口,state组件是内部状态,对外用prop,对内用state。

    其中prop看起来很像HTML的元素的属性,两个的区别:HTML的属性值都是字符串类型,而prop属性的值可以是任意的一种JS数据类型。在JSX中,prop属性值必须用{ }括起来。

    prop有propTypes检查,就是让组件声明自己的接口规范:

    Xxx.propTypes = {

    name : PropTypes.string.isRequired,

    value: PropTypes.number

    }

    上述表明name必须是string类型且必不可好,缺少了会报warning,value的值必须是数值。

    react组件不能修改传入的prop的值。


    state必须是一个javascript对象,改变内部state,可以通过this.setSate函数修改。


    prop和state区别:

    1.prop定义外部接口,state用于记录内部的状态;

    2. 组件不应该改变prop的值,而state存在的目的就是让组件来改变。



    关于组件的生命周期和后续知识,见下篇(正在努力整理。。。)

    展开全文
  • React hook && 函数组件相关 Hook 是 React 16.8 的新增特性,函数组件每次调用其生产的hook类型、顺序、数量应该都是一致的。不然会报错uncaught Invariant Violation: Rendered more/less hooks than ...

    React hook && 函数组件相关

    Hook 是 React 16.8 的新增特性,函数组件每次调用其生产的hook类型、顺序、数量应该都是一致的。不然会报错uncaught Invariant Violation: Rendered more/less hooks than during the previous render.

    1.useState

    import { useState } from 'react';
    const [columns, setColumns] = useState([]);
    

    2.useEffect

    如果熟悉 React 类声明周期方法,可以把 useEffect Hook 视作 componentDidMount、componentDidUpdate 和 componentWillUnmount 的组合体

    与 componentDidMount 和 componentDidUpdate 不同,使用 useEffect 调度的副作用不会阻塞浏览器更新屏幕。这使得 application 感觉上具有响应式。大多数副作用不需要同步发生。而如果需要同步进行,(比如测量布局),有一个单独的 useLayoutEffect Hook, API 和 useEffect 相同。

    从 effect 中返回一个 function?
    这是 effect 可选的清理机制。每个 effect 都可以返回一个在它之后清理的 function。这使得我们能够保持添加订阅和删除订阅彼此接近的订阅的逻辑。这同样是 effect 的一部分。

    在某些情况下,每次 render 后清理或者使用 effect 可能会产生性能问题。在类组件中,可以通过 componentDidUpdate 中编写 prevProps 或 prevState 的额外比较来解决这个问题:

    componentDidUpdate(prevProps, prevState) {
      if (prevState.count !== this.state.count) {
        document.title = `You clicked ${this.state.count} times`;
      }
    }
    

    这个要求很常见,而这种方式已经被内置到 useEffect Hook 的 API中,如果在重新渲染之间没有更新某些值,则可以告诉 React 跳过 effect,为了实现这种方式,需要将数组作为可选的第二个参数传递给 useEffect:

    import { useEffect } from 'react';
    useEffect(() => {
      document.title = `You clicked ${count} times`;
    }, [count]);// 只有在 count 发生变化的时候才会执行这个 effect
    

    3.useRequest

    const { data: latestPipelineInstanceAndQualityReportData } = useRequest(
        async () => {
          const instance = await pipelineInstanceApi.getLatestByBranch(branchType, Number(branchId));
          let imageQuality = null;
          if (instance) {
            imageQuality = await pipelineInstanceApi.getImageQuality(instance.id);
          }
          return { instance, imageQuality };
        },
        {
          refreshDeps: [branchId],
        },
      );
    

    以及

    const { data: monitorPageConfList, loading } = useRequest(
                async () => {
                    return monitorApi.getMonitorPageConf();
                },
                {
                    refreshDeps: [],
                    onError: (error) => {
                        message.error(`获取监控配置失败:${error.message}`);
                    }
                },
            );
            if (loading) {
                
    

    4.useContext

    新建一个中间文件context.tx,内容为:

    export const codeReviewContext = React.createContext({});
    

    父组件这么写:

    import { codeReviewContext } from './context';
    尖括号codeReviewContext.Provider
            value={{ branchId, branchType, demandId, tab, gitUrl, uri, query, isReviewer, thumbsUped }}
          >
          尖括号子组件/>
    尖括号/codeReviewContext.Provider>
    

    5.useCallback

    使用场景是:有一个父组件,其中包含子组件,子组件接收一个函数作为props;通常而言,如果父组件更新了,子组件也会执行更新;但是大多数场景下,更新是没有必要的,我们可以借助useCallback来返回函数,然后把这个函数作为props传递给子组件;这样,子组件就能避免不必要的更新。

    import React, { useCallback } from 'react';
    const getReleaseLog = useCallback(async (service_name) => {
            if (!service_name) {
                return Promise.resolve([]);
            }
            try {
                const diff_list = await serviceApi.diffRequirement(service_name);
                return Promise.resolve(diff_list || []);
            } catch (err) {
                message.error('getChangeLog Error: ' + err.message, 3);
                console.error('---fetch ChangelogContent error: ', err.message);
                return Promise.resolve([]);
            }
        }, []);
    

    6.useMemo

    import { useMemo } from 'react';
    function Button({ name, children }) {
      function changeName(name) {
        console.log('11')
        return name + '改变name的方法'
      }
    const otherName =  useMemo(()=>changeName(name),[name])
      return (
          <>
            <div>{otherName}</div>
            <div>{children}</div>
          </>
    
      )
    }
    export default Button
    

    这个时候我们点击 改变content值的按钮,发现changeName 并没有被调用。
    但是点击改变name值按钮的时候,changeName被调用了。
    所以我们可以使用useMemo方法 避免无用方法的调用,当然一般我们changName里面可能会使用useState来改变state的值,那是不是就避免了组件的二次渲染。
    达到了优化性能的目的

    7.useRef

    一个常见的用例是强制访问子组件:

    import { useRef } from 'react';
    function TextInputWithFocusButton() {
      const inputEl = useRef(null);
      const onButtonClick = () => {
        // `current` points to the mounted text input element
        inputEl.current.focus();
      };
      return (
        <>
          <input ref={inputEl} type="text" />
          <button onClick={onButtonClick}>Focus the input</button>
        </>
      );
    }
    

    另一个例子是异步回调函数里面要获取某个变量的最新值而不是当时的快照值

    import React, { useState } from 'react';
    function Counter() {
      const [count, setCount] = useState(0);
    
      function handleAlertClick() {
        setTimeout(() => {
          alert("You clicked on: " + count);
        }, 3000);
      }
    
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>Click me</button>
          <button onClick={handleAlertClick}>Show alert</button>
        </div>
      );
    }
    

    改写为:

    function Example() {
      const [count, setCount] = useState(0);
      const latestCount = useRef(count);
    
      useEffect(() => {
        // Set the mutable latest value
        latestCount.current = count;
        setTimeout(() => {
          // Read the mutable latest value
          console.log(`You clicked ${latestCount.current} times`);
        }, 3000);
      });
      // ...
    }
    
    
    import React, { useState, useRef } from 'react';
    function Counter() {
      const [count, setCount] = useState(0);
      const latestCount = useRef(count);
      
      useEffect(() => {
        latestCount.current = count;
      });
    
      function handleAlertClick() {
        setTimeout(() => {
          alert("You clicked on: " + latestCount.current);
        }, 3000);
      }
    
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>Click me</button>
          <button onClick={handleAlertClick}>Show alert</button>
        </div>
      );
    }
    

    7.useLayoutEffect

    用在处理DOM的时候,当你的useEffect里面的操作需要处理DOM,并且会改变页面的样式,就需要用这个,否则可能会出现出现闪屏问题, useLayoutEffect里面的callback函数会在DOM更新完成后立即执行,但是会在浏览器进行任何绘制之前运行完成,阻塞了浏览器的绘制.

    8.react.memo

    引入于16.6.0
    react.memo的实现很简单,就几行代码:

    export default function memo<Props>(
      type: React$ElementType,
      compare?: (oldProps: Props, newProps: Props) => boolean,
    ) {
      if (__DEV__) {
        if (!isValidElementType(type)) {
          warningWithoutStack(
            false,
            'memo: The first argument must be a component. Instead ' +
              'received: %s',
            type === null ? 'null' : typeof type,
          );
        }
      }
      return {
        $$typeof: REACT_MEMO_TYPE,
        type,
        compare: compare === undefined ? null : compare,
      };
    }
    
    展开全文
  •  react组件的生命周期分为三个过程:装载过程(Mount)、更新过程(Update)、卸载过程(UnMount)。 装载过程: 1.constructor  构造函数中初始化state,同时绑定成员函数的this环境,  例如:this....

    Reat的生命周期:

     react组件的生命周期分为三个过程:装载过程(Mount)、更新过程(Update)、卸载过程(UnMount)

    装载过程:

    1.constructor

     构造函数中初始化state,同时绑定成员函数的this环境,
     例如:this.onclickButton = this.onclickButton.bind(this)这样当函数被调用的时候,this始终指向该组件实例。
    2.getInitialState3.getDefaultProps这两个方法是老版本中的方法,新版本中已经弃用了。

    4.componentWillMount

     表示“将要装载”,该方法执行的时候,还没有渲染出任何的效果,即是在这里调用this.setState()函数也是没有效果,一切都晚了,换句话说componentWillMount所做的事情均可以在constructor中编写,该函数的存在的意义可能就是和他的兄弟componentDidMout对应吧偷笑

    5.render
     render是生命组件很重要的一个函数,react其他的函数可以不实现,但是render函数一定会实现的,render函数并不做实际的渲染动作,他只是返回一个JSX描述的结构,最终由react来操作渲染的过程。
    render函数是一个纯函数,由输入决定输出,render函数不会引起状态的改变,因此在render函数中调用this.setState函数是错误的。

    6.componentDidMount
     componentDidMount作用比较大,render函数执行完时,componentDidMount并不会立刻调用,componentDidMount被调用的时候,render函数返回的东西已经引发了渲染,组件已经被“装载”到DOM树上。
    也就是在该函数中可以使用AJAX来操作DOM
    还有需要注意的是componentDidMount只能在浏览器中被调用。

    更新过程:

     当组件装载之后,用户看到的是组件的第一印象,但是要提供比较好的交互效果,需要随着用户的操作来更改内容,当props或者state修改的时候,会引发更新的过程。


    1.componentWillReceiveProps(nextProps)

     网络上的误区,该函数仅在props改变的时候更新,事实上,只要是父组件的render函数调用的时候,在render函数中经历的子组件就会经历更新的过程,不管父组件传给子组件的props有没有改变,都会触发componenWillReceiveProps函数。

    注意,通过this.setState方法触发的函数不会调用该函数。


    2.shouldComponentUpdate(nextProps,nextState)

     这个函数是生命周期函数中第二重要的函数了,第一当然是render了~

    render函数返回的结果用于构造DOM对象,而shouldComponentUpdate函数返回的一个布尔值,该返回值告诉react更新是否需要继续。

     默认情况下,该函数的返回值是true,表示每一更新都要重新渲染。这难免会造成资源的浪费,当然在一些小的系统中,还是可以接受的,如果在大项目中势必会影响性能。最简单的就是比较两个参数中的当前值和前一个值是否相同,若相同,则返回false

     值得一提的是,在调用this.setState函数的时候,并不是立刻去更新该组件,在执行函数shouldComponentUpdate的时候,this.state依旧是this.setState之前的值,所以需要比较nextProps、nextState,this.props,this.state相互比较。

    3.componentWillUpdatecomponentDidUpdate

     如果组件shouldComponentUpdate函数执行结果为true的时候,React会一次执行componentWillUpdate,render,componentDidUpdate函数,作用同装载过程一样。


    卸载过程:

     卸载的过程只涉及到一个函数componentWillUnMount,当react组件要从DOM树上删掉之前,会调用 componentWillUnMount,该函数会做一些清理的工作。

    展开全文
  • 先看一段代码: function Mailbox(props) { const unreadMessages = props.unreadMessages; return ( <div> <h1>Hello!</h1> {unreadMessages.length > 0 &... <...
  • react条件渲染 &&

    2018-08-23 19:52:35
    function Mailbox(props) { const unreadMessages = props.unreadMessages; return ( &lt;div&gt; &lt;h1&gt;Hello!&lt;/h1&gt; {unreadMessages.length &......
  • 学习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
收藏数 216,726
精华内容 86,690
关键字:

react