React项目介绍及按需加载

React项目介绍及按需加载

2023年7月4日发(作者:)

React项⽬介绍及按需加载做React需要会什么?react的功能其实很单⼀,主要负责渲染的功能,现有的框架,⽐如angular是⼀个⼤⽽全的框架,⽤了angular⼏乎就不需要⽤其他⼯具辅助配合,但是react不⼀样,他只负责ui渲染,想要做好⼀个项⽬,往往需要其他库和⼯具的配合,⽐如⽤redux来管理数据,react-router管理路由,react已经全⾯拥抱es6,所以es6也得掌握,webpack就算是不会配置也要会⽤,要想提⾼性能,需要按需加载,也得⽤上,还有单元测试。。。。React 是什么⽤脚本进⾏DOM操作的代价很昂贵。有个贴切的⽐喻,把DOM和JavaScript各⾃想象为⼀个岛屿,它们之间⽤收费桥梁连接,js每次访问DOM,都要途径这座桥,并交纳“过桥费”,访问DOM的次数越多,费⽤也就越⾼。 因此,推荐的做法是尽量减少过桥的次数,努⼒待在ECMAScript岛上。因为这个原因react的虚拟dom就显得难能可贵了,它创造了虚拟dom并且将它们储存起来,每当状态发⽣变化的时候就会创造新的虚拟节点和以前的进⾏对⽐,让变化的部分进⾏渲染。整个过程没有对dom进⾏获取和操作,只有⼀个渲染的过程,所以react说是⼀个ui框架。React的组件化react的⼀个组件很明显的由dom视图和state数据组成,两个部分泾渭分明。state是数据中⼼,它的状态决定着视图的状态。这时候发现似乎和我们⼀直推崇的MVC开发模式有点区别,没了Controller控制器,那⽤户交互怎么处理,数据变化谁来管理?然⽽这并不是react所要关⼼的事情,它只负责ui的渲染。与其他框架监听数据动态改变dom不同,react采⽤setState来控制视图的更新。setState会⾃动调⽤render函数,触发视图的重新渲染,如果仅仅只是state数据的变化⽽没有调⽤setState,并不会触发更新。 组件就是拥有独⽴功能的视图模块,许多⼩的组件组成⼀个⼤的组件,整个页⾯就是由⼀个个组件组合⽽成。它的好处是利于重复利⽤和维护。React的 Diff算法react的diff算法⽤在什么地⽅呢?当组件更新的时候,react会创建⼀个新的虚拟dom树并且会和之前储存的dom树进⾏⽐较,这个⽐较多过程就⽤到了diff算法,所以组件初始化的时候是⽤不到的。react提出了⼀种假设,相同的节点具有类似的结构,⽽不同的节点具有不同的结构。在这种假设之上进⾏逐层的⽐较,如果发现对应的节点是不同的,那就直接删除旧的节点以及它所包含的所有⼦节点然后替换成新的节点。如果是相同的节点,则只进⾏属性的更改。对于列表的diff算法稍有不同,因为列表通常具有相同的结构,在对列表节点进⾏删除,插⼊,排序的时候,单个节点的整体操作远⽐⼀个个对⽐⼀个个替换要好得多,所以在创建列表的时候需要设置key值,这样react才能分清谁是谁。当然不写key值也可以,但这样通常会报出警告,通知我们加上key值以提⾼react的性能。React组件是怎么来的组件的创造⽅法为Class() ——创造⼀个类,react系统内部设计了⼀套类系统,利⽤它来创造react组件。但这并不是必须的,我们还可以⽤es6的class类来创造组件,这也是Facebook官⽅推荐的写法。这两种写法实现的功能⼀样但是原理却是不同,es6的class类可以看作是构造函数的⼀个语法糖,可以把它当成构造函数来看,extends实现了类之间的继承 —— 定义⼀个类Main 继承ent所有的属性和⽅法,组件的⽣命周期函数就是从这来的。constructor是构造器,在实例化对象时调⽤,super调⽤了⽗类的constructor创造了⽗类的实例对象this,然后⽤⼦类的构造函数进⾏修改。这和es5的原型继承是不同的,原型继承是先创造⼀个实例化对象this,然后再继承⽗级的原型⽅法。了解了这些之后我们在看组件的时候就清楚很多。当我们使⽤组件< Main />时,其实是对Main类的实例化——new Main,只不过react对这个过程进⾏了封装,让它看起来更像是⼀个标签。有三点值得注意:1、定义类名字的⾸字母必须⼤写 2、因为class变成了关键字,类选择器需要⽤className来代替。 3、类和模块内部默认使⽤严格模式,所以不需要⽤use strict指定运⾏模式。组件的⽣命周期组件在初始化时会触发5个钩⼦函数:1、getDefaultProps()设置默认的props,也可以⽤dufaultProps设置组件的默认属性。2、getInitialState()在使⽤es6的class语法时是没有这个钩⼦函数的,可以直接在constructor中定义。此时可以访问。3、componentWillMount()组件初始化时只调⽤,以后组件更新不调⽤,整个⽣命周期只调⽤⼀次,此时可以修改state。4、 render()react最重要的步骤,创建虚拟dom,进⾏diff算法,更新dom树都在此进⾏。此时就不能更改state了。5、componentDidMount()组件渲染之后调⽤,可以通过Node()获取和操作dom节点,只调⽤⼀次。在更新时也会触发5个钩⼦函数:6、componentWillReceivePorps(nextProps)组件初始化时不调⽤,组件接受新的props时调⽤。7、shouldComponentUpdate(nextProps, nextState)react性能优化⾮常重要的⼀环。组件接受新的state或者props时调⽤,我们可以设置在此对⽐前后两个props和state是否相同,如果相同则返回false阻⽌更新,因为相同的属性状态⼀定会⽣成相同的dom树,这样就不需要创造新的dom树和旧的dom树进⾏diff算法对⽐,节省⼤量性能,尤其是在dom结构复杂的时候。不过调⽤pdate会跳过此步骤。8、componentWillUpdate(nextProps, nextState)组件初始化时不调⽤,只有在组件将要更新时才调⽤,此时可以修改state9、render()不多说10、componentDidUpdate()组件初始化时不调⽤,组件更新完成后调⽤,此时可以获取dom节点。还有⼀个卸载钩⼦函数11、componentWillUnmount()组件将要卸载时调⽤,⼀些事件监听和定时器需要在此时清除。以上可以看出来react总共有10个周期函数(render重复⼀次),这个10个函数可以满⾜我们所有对组件操作的需求,利⽤的好可以提⾼开发效率和组件性能。React-Router路由Router就是React的⼀个组件,它并不会被渲染,只是⼀个创建内部路由规则的配置对象,根据匹配的路由地址展现相应的组件。Route则对路由地址和组件进⾏绑定,Route具有嵌套功能,表⽰路由地址的包涵关系,这和组件之间的嵌套并没有直接联系。Route可以向绑定的组件传递7个属性:children,history,location,params,route,routeParams,routes,每个属性都包涵路由的相关的信息。⽐较常⽤的有children(以路由的包涵关系为区分的组件),location(包括地址,参数,地址切换⽅式,key值,hash值)。react-router提供Link标签,这只是对a标签的封装,值得注意的是,点击链接进⾏的跳转并不是默认的⽅式,react-router阻⽌了a标签的默认⾏为并⽤pushState进⾏hash值的转变。切换页⾯的过程是在点击Link标签或者后退前进按钮时,会先发⽣url地址的转变,Router监听到地址的改变根据Route的path属性匹配到对应的组件,将state值改成对应的组件并调⽤setState触发render函数重新渲染dom。当页⾯⽐较多时,项⽬就会变得越来越⼤,尤其对于单页⾯应⽤来说,初次渲染的速度就会很慢,这时候就需要按需加载,只有切换到页⾯的时候才去加载对应的js⽂件。react配合webpack进⾏按需加载的⽅法很简单,Route的component改为getComponent,组件⽤的⽅式获取,并在webpack中配置chunkFilename。const chooseProducts = (location, cb) => { ([], require => { cb(null, require('../Component/chooseProducts').default) },'chooseProducts')}const helpCenter = (location, cb) => { ([], require => { cb(null, require('../Component/helpCenter').default) },'helpCenter')}const saleRecord = (location, cb) => { ([], require => { cb(null, require('../Component/saleRecord').default) },'saleRecord')}const RouteConfig = ( //⾸页 //帮助中⼼ //销售记录 );组件之间的通信react推崇的是单向数据流,⾃上⽽下进⾏数据的传递,但是由下⽽上或者不在⼀条数据流上的组件之间的通信就会变的复杂。解决通信问题的⽅法很多,如果只是⽗⼦级关系,⽗级可以将⼀个回调函数当作属性传递给⼦级,⼦级可以直接调⽤函数从⽽和⽗级通信。组件层级嵌套到⽐较深,可以使⽤上下⽂getChildContext来传递信息,这样在不需要将函数⼀层层往下传,任何⼀层的⼦级都可以通过t直接访问。兄弟关系的组件之间⽆法直接通信,它们只能利⽤同⼀层的上级作为中转站。⽽如果兄弟组件都是最⾼层的组件,为了能够让它们进⾏通信,必须在它们外层再套⼀层组件,这个外层的组件起着保存数据,传递信息的作⽤,这其实就是redux所做的事情。组件之间的信息还可以通过全局事件来传递。不同页⾯可以通过参数传递数据,下个页⾯可以⽤来获取。其实react本⾝很简单,难的在于如何优雅⾼效的实现组件之间数据的交流。Redux⾸先,redux并不是必须的,它的作⽤相当于在顶层组件之上⼜加了⼀个组件,作⽤是进⾏逻辑运算、储存数据和实现组件尤其是顶层组件的通信。如果组件之间的交流不多,逻辑不复杂,只是单纯的进⾏视图的渲染,这时候⽤回调,context就⾏,没必要⽤redux,⽤了反⽽影响开发速度。但是如果组件交流特别频繁,逻辑很复杂,那redux的优势就特别明显了。我第⼀次做react项⽬的时候并没有⽤redux,所有的逻辑都是在组件内部实现,当时为了实现⼀个逻辑⽐较复杂的购物车,洋洋洒洒居然写了800多⾏代码,回头⼀看我⾃⼰都不知道写的是啥,画⾯太感⼈。先简单说⼀下redux和react是怎么配合的。react-redux提供了connect和Provider两个好基友,它们⼀个将组件与redux关联起来,⼀个将store传给组件。组件通过dispatch发出action,store根据action的type属性调⽤对应的reducer并传⼊state和这个action,reducer对state进⾏处理并返回⼀个新的state放⼊store,connect监听到store发⽣变化,调⽤setState更新组件,此时组件的props也就跟着变化。流程是这个样⼦的:值得注意的是connect,Provider,mapStateToProps,mapDispatchToProps是react-redux提供的,redux本⾝和react没有半⽑钱关系,它只是数据处理中⼼,没有和react产⽣任何耦合,是react-redux让它们联系在⼀起。接下来具体分析⼀下,redux以及react-redux到底是怎么实现的。先上⼀张图明显⽐第⼀张要复杂,其实两张图说的是同⼀件事。从上⽽下慢慢分析:先说说redux:redux主要由三部分组成:store,reducer,action。store是⼀个对象,它有四个主要的⽅法:1、dispatch:⽤于action的分发——在createStore中可以⽤middleware中间件对dispatch进⾏改造,⽐如当action传⼊dispatch会⽴即触发reducer,有些时候我们不希望它⽴即触发,⽽是等待异步操作完成之后再触发,这时候⽤redux-thunk对dispatch进⾏改造,以前只能传⼊⼀个对象,改造完成后可以传⼊⼀个函数,在这个函数⾥我们⼿动dispatch⼀个action对象,这个过程是可控的,就实现了异步。2、subscribe:监听state的变化——这个函数在store调⽤dispatch时会注册⼀个listener监听state变化,当我们需要知道state是否变化时可以调⽤,它返回⼀个函数,调⽤这个返回的函数可以注销监听。 let unsubscribe = ibe(() => {('state发⽣了变化')})3、getState:获取store中的state——当我们⽤action触发reducer改变了state时,需要再拿到新的state⾥的数据,毕竟数据才是我们想要的。getState主要在两个地⽅需要⽤到,⼀是在dispatch拿到action后store需要⽤它来获取state⾥的数据,并把这个数据传给reducer,这个过程是⾃动执⾏的,⼆是在我们利⽤subscribe监听到state发⽣变化后调⽤它来获取新的state数据,如果做到这⼀步,说明我们已经成功了。4、replaceReducer:替换reducer,改变state修改的逻辑。store可以通过createStore()⽅法创建,接受三个参数,经过combineReducers合并的reducer和state的初始状态以及改变dispatch的中间件,后两个参数并不是必须的。store的主要作⽤是将action和reducer联系起来并改变state。action:action是⼀个对象,其中type属性是必须的,同时可以传⼊⼀些数据。action可以⽤actionCreactor进⾏创造。dispatch就是把action对象发送出去。reducer:reducer是⼀个函数,它接受⼀个state和⼀个action,根据action的type返回⼀个新的state。根据业务逻辑可以分为很多个reducer,然后通过combineReducers将它们合并,state树中有很多对象,每个state对象对应⼀个reducer,state对象的名字可以在合并时定义。像这个样⼦:const reducer = combineReducers({ a: doSomethingWithA, b: processB, c: c})combineReducers:其实它也是⼀个reducer,它接受整个state和⼀个action,然后将整个state拆分发送给对应的reducer进⾏处理,所有的reducer会收到相同的action,不过它们会根据action的type进⾏判断,有这个type就进⾏处理然后返回新的state,没有就返回默认值,然后这些分散的state⼜会整合在⼀起返回⼀个新的state树。接下来分析⼀下整体的流程,⾸先调⽤ch将action作为参数传⼊,同时⽤getState获取当前的状态树state并注册subscribe的listener监听state变化,再调⽤combineReducers并将获取的state和action传⼊。combineReducers会将传⼊的state和action传给所有reducer,并根据action的type返回新的state,触发state树的更新,我们调⽤subscribe监听到state发⽣变化后⽤getState获取新的state数据。redux的state和react的state两者完全没有关系,除了名字⼀样。上⾯分析了redux的主要功能,那么react-redux到底做了什么?React-Redux如果只使⽤redux,那么流程是这样的:component --> dispatch(action) --> reducer --> subscribe --> getState --> component⽤了react-redux之后流程是这样的:component --> actionCreator(data) --> reducer --> componentstore的三⼤功能:dispatch,subscribe,getState都不需要⼿动来写了。react-redux帮我们做了这些,同时它提供了两个好基友Provider和connect。Provider是⼀个组件,它接受store作为props,然后通过context往下传,这样react中任何组件都可以通过context获取store。也就意味着我们可以在任何⼀个组件⾥利⽤dispatch(action)来触发reducer改变state,并⽤subscribe监听state的变化,然后⽤getState获取变化后的值。但是并不推荐这样做,它会让数据流变的混乱,过度的耦合也会影响组件的复⽤,维护起来也更⿇烦。connect --connect(mapStateToProps, mapDispatchToProps, mergeProps, options) 是⼀个函数,它接受四个参数并且再返回⼀个函数--wrapWithConnect,wrapWithConnect接受⼀个组件作为参数wrapWithConnect(component),它内部定义⼀个新组件Connect(容器组件)并将传⼊的组件(ui组件)作为Connect的⼦组件然后return出去。所以它的完整写法是这样的:connect(mapStateToProps, mapDispatchToProps, mergeProps, options)(component)mapStateToProps(state, [ownProps]):mapStateToProps 接受两个参数,store的state和⾃定义的props,并返回⼀个新的对象,这个对象会作为props的⼀部分传⼊ui组件。我们可以根据组件所需要的数据⾃定义返回⼀个对象。ownProps的变化也会触发mapStateToPropsfunction mapStateToProps(state) { return { todos: };}mapDispatchToProps(dispatch, [ownProps]):mapDispatchToProps如果是对象,那么会和store绑定作为props的⼀部分传⼊ui组件。如果是个函数,它接受两个参数,bindActionCreators会将action和dispatch绑定并返回⼀个对象,这个对象会和ownProps⼀起作为props的⼀部分传⼊ui组件。所以不论mapDispatchToProps是对象还是函数,它最终都会返回⼀个对象,如果是函数,这个对象的key值是可以⾃定义的function mapDispatchToProps(dispatch) { return { todoActions: bindActionCreators(todoActionCreators, dispatch), counterActions: bindActionCreators(counterActionCreators, dispatch) };}mapDispatchToProps返回的对象其属性其实就是⼀个个actionCreator,因为已经和dispatch绑定,所以当调⽤actionCreator时会⽴即发送action,⽽不⽤⼿动dispatch。ownProps的变化也会触发mapDispatchToProps。mergeProps(stateProps, dispatchProps, ownProps):将mapStateToProps() 与 mapDispatchToProps()返回的对象和组件⾃⾝的props合并成新的props并传⼊组件。默认返回({}, ownProps, stateProps, dispatchProps) 的结果。options:pure = true 表⽰Connect容器组件将在shouldComponentUpdate中对store的state和ownProps进⾏浅对⽐,判断是否发⽣变化,优化性能。为false则不对⽐。其实connect函数并没有做什么,⼤部分的逻辑都是在它返回的wrapWithConnect函数内实现的,确切的说是在wrapWithConnect内定义的Connect组件⾥实现的。下⾯是⼀个完整的 react --> redux --> react 流程:⼀、Provider组件接受redux的store作为props,然后通过context往下传。⼆、connect函数在初始化的时候会将mapDispatchToProps对象绑定到store,如果mapDispatchToProps是函数则在Connect组件获得store后,根据传⼊的ch和action通过bindActionCreators进⾏绑定,再将返回的对象绑定到store,connect函数会返回⼀个wrapWithConnect函数,同时wrapWithConnect会被调⽤且传⼊⼀个ui组件,wrapWithConnect内部使⽤class Connect extends Component定义了⼀个Connect组件,传⼊的ui组件就是Connect的⼦组件,然后Connect组件会通过context获得store,并通过te获得完整的state对象,将state传⼊mapStateToProps返回stateProps对象、mapDispatchToProps对象或mapDispatchToProps函数会返回⼀个dispatchProps对象,stateProps、dispatchProps以及Connect组件的props三者通过(),或者mergeProps合并为props传⼊ui组件。然后在ComponentDidMount中调⽤ibe,注册了⼀个回调函数handleChange监听state的变化。三、此时ui组件就可以在props中找到actionCreator,当我们调⽤actionCreator时会⾃动调⽤dispatch,在dispatch中会调⽤getState获取整个state,同时注册⼀个listener监听state的变化,store将获得的state和action传给combineReducers,combineReducers会将state依据state的key值分别传给⼦reducer,并将action传给全部⼦reducer,reducer会被依次执⾏进⾏的判断,如果有则返回⼀个新的state,如果没有则返回默认。combineReducers再次将⼦reducer返回的单个state进⾏合并成⼀个新的完整的state。此时state发⽣了变化。dispatch在state返回新的值之后会调⽤所有注册的listener函数其中包括handleChange函数,handleChange函数内部⾸先调⽤getState获取新的state值并对新旧两个state进⾏浅对⽐,如果相同直接return,如果不同则调⽤mapStateToProps获取stateProps并将新旧两个stateProps进⾏浅对⽐,如果相同,直接return结束,不进⾏后续操作。如果不相同则调⽤te()触发Connect组件的更新,传⼊ui组件,触发ui组件的更新,此时ui组件获得新的props,react --> redux --> react 的⼀次流程结束。上⾯的有点复杂,简化版的流程是:⼀、Provider组件接受redux的store作为props,然后通过context往下传。⼆、connect函数收到Provider传出的store,然后接受三个参数mapStateToProps,mapDispatchToProps和组件,并将state和actionCreator以props传⼊组件,这时组件就可以调⽤actionCreator函数来触发reducer函数返回新的state,connect监听到state变化调⽤setState更新组件并将新的state传⼊组件。connect可以写的⾮常简洁,mapStateToProps,mapDispatchToProps只不过是传⼊的回调函数,connect函数在必要的时候会调⽤它们,名字不是固定的,甚⾄可以不写名字。简化版本:connect(state => state, action)(Component);项⽬搭建上⾯说了react,react-router和redux的知识点。但是怎么样将它们整合起来,搭建⼀个完整的项⽬。1、先引⽤ ,redux,react-router 等基本⽂件,建议⽤npm安装,直接在⽂件中引⽤。2、从 ,redux,react-router 中引⼊所需要的对象和⽅法。import React, {Component, PropTypes} from 'react';import ReactDOM, {render} from 'react-dom';import {Provider, connect} from 'react-redux';import {createStore, combineReducers, applyMiddleware} from 'redux';import { Router, Route, Redirect, IndexRoute, browserHistory, hashHistory } from 'react-router';3、根据需求创建顶层ui组件,每个顶层ui组件对应⼀个页⾯。4、创建actionCreators和reducers,并⽤combineReducers将所有的reducer合并成⼀个⼤的reduer。利⽤createStore创建store并引⼊combineReducers和applyMiddleware。5、利⽤connect将actionCreator,reuder和顶层的ui组件进⾏关联并返回⼀个新的组件。6、利⽤connect返回的新的组件配合react-router进⾏路由的部署,返回⼀个路由组件Router。7、将Router放⼊最顶层组件Provider,引⼊store作为Provider的属性。8、调⽤render渲染Provider组件且放⼊页⾯的标签中。可以看到顶层的ui组件其实被套了四层组件,Provider,Router,Route,Connect,这四个组件并不会在视图上改变react,它们只是功能性的。通常我们在顶层的ui组件打印props时可以看到⼀堆属性:上图的顶层ui组件属性总共有18个,如果刚刚接触react,可能对这些属性怎么来的感到困惑,其实这些属性来⾃五个地⽅:组件⾃定义属性1个,actionCreator返回的对象6个,reducer返回的state4个,Connect组件属性0个,以及Router注⼊的属性7个。

发布者:admin,转转请注明出处:http://www.yc00.com/web/1688418806a135552.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信