react fiber也就是指react16之后的版本,相比之间内部底层算法做了调整
react是基于组件开发的,类似于vue,组件就是网页上的一部分,当组件足够小就可以看做是标签,或者说标签就是最小的组件
使用react脚手架工具创建一个react App项目,执行三行代码:
npx create-react-app my-app
cd my-app
npm start
react的入口文件是index.js
app.test.js的自动化测试用到的文件
index.js中引入的react库是为了更好的解读react的组件,react的组件均以大写字母开发,类比vue也是一样的
reactDOM可以帮助react将组件挂载到页面的dom节点上
定义一个react的组件,必须使用class声明,然后继承自react.Component就会成为一个组件,格式如下
class App extends React.Component{}
组件中必须要有一个render()函数,这个函数负责这个组件要显示的内容,不过要能在页面显示还需要return出去,render()里必须只能有一个大的jsx表达式
当然最新的react组件 可以直接以函数形式 ,return一个jsx语法 就可以了。
注意点:这点和vue不一样,vue更简洁,只返回数据,基本的结构还是在HTML里写
react 里return 可以直接返回标签不需要加引号,这是属于jsx语法,jsx语法支持直接写标签,同时支持标签里写js表达式,比如{1+3},但是不允许写js语句,比如if语句之类的
react 组件里有个constructor方法,里面的内容在组件创建时执行
react在循环的时候也是要用一个key值绑定的,类似于vue
react里不建议直接操作组件里声明的数据,而是借用一个别的数据间接地改变,这样方便以后调试
react里面父子组件之间传递参数,父组件传递参数给子组件通过属性的方式,子组件接受通过this.props方式接受,类似于vue的语法,子组件的想要给父组件传递参数,需要调用绑定在自身的父组件上的一个方法,在这个方法里传递一个参数,这个和vue有点不同
react写样式有两种方式,第一种行内样式,不过需要使用{{}},外层{}说明这是一个是一个js表达式,内层说明样式是一个对象,第二种使用类名来写样式,不过注意因为class在react中起到声明一个类的作用,所以这里不能用class来声明类名,需要用className来声明
如果不想要最外层有个div包括,可以使用Fragment这个标签来实现,它只起到一个包括作用,不生成具体的标签
<noscript>标签里的内容是当 浏览器禁用了js时的提醒语句
serviceWorker 主要用来帮助从服务器请求的页面缓存到浏览器中,这样在联网情况下访问过的页面,在断网之后依旧可以访问
在react中写在js中的标签统称为jsx语法,比如render里引入的<App/> 组件,和return 返回的标签都是JSx语法,jsx中支持HTML里的一些标签比如div ul li等,同时也支持自己定义的标签,也就是自己创建的组件,但是这个组件必须是大写字母开头,同时所有的这些标签都不需要引号,不然就会报错
react中绑定了value给this.state的数据,也需要同时绑定一个onChange事件,不然value值不会改变,一直是初始给的
react中不能直接改变this.state里的数据,要想改变,必须通过this.setState来改变,这个类似于微信小程序,只能通过setData来改变,直接赋值也是改变了,但是不通过setState或者setData就不能更新视图,所以不建议那么做。
react里的注释都必须写在{}内部,如果是多行注释,使用 /**/的方式来写,{}可以和注释写在同一行,如果是单行注释//,则必须{}单独一行
标签内如果不希望被转义成字符串,希望写的标签能起到标签作用,可以使用dangerouslySetInnerHTML={{__HTML: 具体的内容的方式}},第一对花括号表示jsx语法,表示里面是一个js表达式,第二是js对象,写了这个之后就不需要在单独写一个{具体内容},类似于 vue的v-html
label标签上的for在react里不建议使用,因为和for循环重复,建议用htmlFor来代替for的功能
react中的return必须返回一个根元素或者组件,其他的内容需要包括到里面
react和vue都是只控制它挂载的元素下的内容,不控制其他的,所以可以实现vue、react、jq等共存的可能性;
react和传统方式的不同:
1、react是声明式编程,传统的jQuery是命令式编程,直接操作dom元素,而react和vue是数据驱动型的,直接操作数据
2、react可以与其他框架并存,与vue类似,都有一个接管的区域
3、组件化与vue类似
4、单项数据流,父组件可以向子组件传递数据,子组件可以使用这些数据,但是不能够去直接改变,因为传递过来的数据是只读的,但是子组件可以通过调用父组件的方法来改变父组件的数据,本质上还是父组件自己改变了自己的数据,单向数据流主要是为了方便以后的调试,因为能 改变父组件数据的方法在父组件中,父组件通过属性绑定的方式把改变数据的方法传递给子组件,子组件接受并调用,但是注意this指向,传递 给子组件的时候必须传递this,不然 子组件调用时候会因为this指向错误导致无法成功
setState两种方式,一种直接改变 数据,一种是通过函数返回值的形式,也就是 异步改变
5、视图层框架,因为单向数据流,所以如果组件树非常大,非父子组件之间传值很不方便,需要借助一些其他的工具,所以单单react无法开发大型项目,只是一个视图层框架
6、函数式编程 方便前端进行自动化测试
react propTypes对父组件传递过来的prop里的属性进行类型校验,同时可以设置哪些是必须传递的参数,或者是给参数一个默认值,defaultProps可以设置一些参数的默认值,即便propTypes里要求了是必须有的(isRequired),只要有了默认参数,不传递也不会报错
当组件的state或props发生改变时,render函数会重新执行
虚拟dom:
传统渲染dom方式:
1、state 数据
2、 模板
3、数据 + 模板 生成一个真实的dom 来显示
4、state 数据改变
5、数据 + 模板 生成一个新的真实dom 替换原来的dom
缺点:第一次生成一个完整的dom
改变后又生成一个新的完整dom
新的dom替换到原理完整的dom,非常耗性能
改良方法:
1、state 数据
2、 模板
3、数据 + 模板 生成一个真实的dom 来显示
4、state 数据改变
5、数据+ 模板 生成新的真实dom(documentFragment)
6、和原来的dom对比,找出差异的部分
7、用新的dom中改变的元素,替换掉老的dom中对应的元素
缺点:性能提升不明显,虽然第6步能节约一些性能,但是新的dom和老的dom对比时候会耗费一些性能,所以和第一种方法比性能提升不明显
react虚拟dom方法:
1、state 数据
2、 模板
3、数据 + 模板 生成一个真实的dom 来显示
<div id='abc'><span>hello world </span></div>
4、生成一个虚拟dom(虚拟dom实际上是一个js对象,用来描述真实dom)(实际上这一步在第3步之前,为了便于比较写到了这里)(耗费了一点性能,但是js生成js对象耗费的性能极低)
[ 'div' ,{ id:'abc'},[ 'span',{},'hello world '] ]
5、state 数据改变
6、数据 + 模板 生成一个新的虚拟dom(极大地提升了性能)
[ 'div' ,{ id:'abc'},[ 'span',{},'bye bye '] ]
7、新的虚拟dom和老的虚拟dom进行比较,找出其中改变的部分span中的内容 (极大地提升了性能)
8、直接操作dom,改变span中的内容(‘hello world ’ =》 ‘bye bye’)
(js生成一个js对象,耗费的性能很小,但是js生成一个dom耗费的性能很大,或者说和dom有关的操作都很耗费性能)
不过最后一个是为了和前面对比这么写的,实际上的应该如下:
1、state 数据
2、jsx 模板
3、数据+ 模板生成一个虚拟dom
4、用虚拟dom的结构生成一个真实的dom
5、state 数据改变
6、数据+ 模板生成一个新的虚拟dom
7、新的虚拟dom和老的虚拟dom对比(diff算法)
8、直接操作真实dom,改变span中的内容
虚拟dom优点:
1、性能提升很大
2、跨端应用得以实现,利用react native
虚拟dom在浏览器端会生成真实的dom,但是移动端没有真实的dom元素,只有一些原生的应用组件,虚拟dom同样可以生成一些原生的应用组件,这样就可以实现跨端开发
react 数据改变新旧虚拟dom比对,是同层比对,发现有差异的地方不再向下继续比对,直接全部替换,这样比对的速度会非常快,虽然全部替换会浪费一些性能,当时对比速度的提升提高性能作用更加明显
jsx -->React.createElement方法,生成了一个js对象 (虚拟dom)-->真实的dom
return <div>item</div> 等价于React.createElement('div',{},' item')
setState 底层可以把多个setState合并成一个setState尤其是时间很短的情况下,这样就可以大大节省性能,短时间内多次调用setState会合并成一个setState,为了 提高性能,所以要使用异步setState
key值作用
key值可以使新的虚拟dom和旧的虚拟dom迅速的匹配,这样就大大提升了对比的速度,但是如果用index做key值,比如
数据 key值 改变后(删除a) 数据 key值
a 0
b 1 b 0
c 2 c 1
这样就会发现,index做key值,实际上不能保证新旧dom的key值一致,也就无法快速的匹配
ref是react里帮助我们获取dom元素来使用的,但是不太建议使用,因为react是数据驱动类型的
react生命周期函数
ref来获取dom节点,ref绑定一个函数 ,函数里是ref的dom元素 ref= {(ul)=>{this.ul = ul}}
setState函数方式,有 两个 函数,一个包含prevState,可以改变state的值,一个是改变之后的回调 函数
定义:某一时刻组件会自动调用执行的函数
constructor属于es6里自带的,所以不归类为react的生命周期函数,但是特性是一样的,组件创建就被执行,在constructor阶段进行state和props的声明赋值。shouldComponentUpdate,必须有个返回值,布尔类型的,根据返回类型 ,决定 是否继续 执行下面的生命周期函数
componentWillReceiveProps这个生命周期 函数,在父组件传递给子组件参数,同时子组件已经 挂载在 页面上之后,父组件的 render执行就会 触发,条件比较多。认真看
componentWillUnmount页面即将被卸载时候调用,下面是新版react的生命周期 ,改成了一些内容
https://blog.csdn.net/Jack_lzx/article/details/115328782
react发送ajax请求的方法写在componentDidMount里最合适,当然也可以放在constructor里,不过更建议放到componentDidMount里,因为renter会被频繁的执行,所以放到render里不合适,放到componentWillMount也可以,但是当使用react native中比较高级的功能时可能产生冲突,所以建议放到componentDidMount中
每个组件中都必须有一个生命周期函数就是render,因为其他的生命周期函数在继承React.Component的时候都内置了
父组件的render 函数执行时,子组件的render函数也会重新执行,但是有时并不需要频繁的执行子组件的render函数,就可以使用shouldComponent的生命周期函数,在内部做对比,只有props或者state发生改变才执行,也就是子组件接受的props变化 时,再更新,
react性能优化的方法:
1、将bind(this)统一放到constructor里进行,
2、setState将短时间多个 更改的setState合并 成一个执行
3、使用虚拟dom,diff算法
4、shouldComponentUpdate中进行判断,减少子组件 render函数渲染
可以借助Charles来实现模拟接口的事件,具体在tools里的map local里设置模拟的接口,以及要重定向的具体文件(注意一定要关闭代理!)
transition:all 3s ease;设置css动画,更具体的可以使用@keyframe设置各个阶段的动画 ,使用时用animation来使用,如果要保留最后的动画状态,就要加上forwards
可以通过react-transition-group 来实现一些动画,详细用法查看github上的说明CSSTransition,当CSSTransition无法实现时,可以查看transition的说明,更底层的动画
当需要多个元素使用动画时,可以使用transitionGroup配合CSSTransition来使用,最外层使用transitiongroup,具体要使用动画的元素上使用CSSTransition来包裹
react的一个后台ui框架,antd,使用方法查看antd官网
使用store需要先创建一个store的文件夹,里面需要两个js,一个是生成store 一个是生成reducer
调试redux需要安装一个插件,就是redux developtool 谷歌商店找,根据下面提示配置就可以使用
action 里的type尽量抽取出来,不直接写一个字符串,因为如果有多个action ,那就有多个type ,如果写错了type,浏览器不会报错,但是功能就实现不了,所以建议把type抽离出来,放到一个单独的js文件里,比如actionType.js,在里面声明一个常量来存储type的字符串,这个时候所有页面都是用常量或者变量,那么一旦手误拼写错误会有提示
对于复杂的项目同样需要把action的创建放到一个统一的文件里,比如actionCreator.js里进行,一来是代码的可读性增强,另外也是为了自动化测试的方便。
why 有时候导入需要{},有时候不需要,详情:
https://blog.csdn.net/Chris__wang/article/details/82977626
redux基础特点总结:
1、一个项目只有一个store
2、只有store可以改变store里的数据,reducer里不可以,只是深克隆了一份,实际并没有去改变store的数据
3、redux都是纯函数(纯函数就是给了固定的输入,就一定有固定的 输出,不会有任何副作用,通俗点就是输入的值固定了,return的结果也是固定的,不会存在比如时间之类的不确定的内容,而且函数内不会改变参数的内容,也就是没有副作用)
redux核心api:
1.createStore 创建一个store方法
2、store.dispatch 将action传递给reducer
3、store.getState获取store中的数据
4、store.subscribe订阅store,其中的函数可以监听store中的数据变化
ui组件(傻瓜组件)负责页面的渲染,容器组件(聪明组件)负责逻辑
当一个组件只有一个render函数的时候就可以使用无状态组件来定义,比普通组件写法更简单一些,更重要的是性能更优越,因为普通组件需要先定义一个类,类里面再返回一个jsx表达式,但是无状态组件直接返回一个函数就可以了,性能更好
当子组件需要从父组件接受一个函数,并且带有参数时,不能直接写onClick={this.props.handleClick(index)} 这种形式,可以写onClick={()=>{this.props.handleclick(index)}}
redux-thunk安装:npm install redux-thrunk --save yard add redux-thunk
redux 中间件可以把react中复杂的数据请求代码放到redux的action中执行,第一步引入redux-thunk,然后在生成store的文件中,同时引入import thunk from 'redux-thunk';,然后在createStore中使用applyMiddleWare(thunk),redux-devtools,其实也是redux的中间件,redux中间件指的是action和store的中间,其实本质是对dispatch方法进行了升级。dispatch会根据action的不同进行不同的操作,如果是对象直接传递给store,如果是函数,先执行函数,之后把返回的结果给store,如果react声明周期函数中有太多太复杂的异步函数,比如请求之类的就会是react的生命周期变得太过复杂,不利于以后的维护和检查bug,所以建议抽取出来,也就是利用react-thunk来管理
action本来只可以是一个对象,因为 使用了 redux-thunk中间件,所以action可以是一个函数,当使用 dispatch方法发给store时,store进行判断 ,如果是函数,就自动执行一次,并将dispatch作为 参数传入
function* 为js函数生成器(generator函数),里面有next()和yield ,两个
redux-saga中间件作用和redux-thunk类似,使用先安装:npm install --save redux-saga
redux-saga需要多创建一个action,比如获取初始数据的action,就是普通的写法,在dispatch的过程中,sagas里的函数,会监听到 action的类型,然后用一个函数来处理请求,将请求的结果在创建一个action,通过put方法触发 dispatch,就可以来修改store中的数据了。
而redux-thunk就比较简单 ,只是对dispatch进行了 封装 ,多了一个函数的形式
react-redux 安装 npm install --save react-redux
第一个核心api就是provider,它可以通过store这个属性将store绑定到它所有内部的组件上,所以它里面所有的组件都可以使用store
简书项目
1、react默认的css样式,一旦一个文件引入就是全局都生效,其他文件也可以使用,所以不建议使用import的方式引入样式,而是借用第三方样式管理工具来管理,比如styled-components,安装还是使用npm install --save styled-components方式,需要将样式写在js里,注意项目使用的styled-components是低版本的,需要安装低版本的配合使用,全局都需要使用的样式放到injectGlobal内进行统一管理,比如重置样式的reset.css
2、需要引入reset.css文件,将样式进行统一化管理
3、多行文本输入需要使用` `里面写内容,如果需要使用到变量的话,需要使用${变量}的方式使用
4、styled-components里的标签上可以写一些属性,特殊的属性需要写在attrs上,比如a.attrs({ href:‘路径’}),当然也可以直接写在组件上
5、动画效果可以通过react-transition-group这个插件来实现,react中不建议直接操作dom元素,所以尽量通过变量来控制
6、通过combineReducer,可以方便将reducer进行分类管理,这个某个页面下的内容就可以放到某个页面单独管理,再通过combineReducer 统一管理,更明确也防止数据太多,一个页面内容太多,另外外部引入的变量再次导出的话需要加上一个{}
7、通过使用immutable来防止错误的操作state造成代码 错误,不容 排查,immutable生成一个不可更改的immutable的对象,来管理数据,通过get方法或者具体的数据,通过set方法来更改数据,其实是创建了另外一个state,
8、使用pureComponent比直接使用component性能更好一些,因为它内置使用了判断,判断该组件内数据是否变化了,如果没有变化这个组件就不会被重新渲染,不过最好结合immutable来管理数据,不然容易有坑,如果没有使用immutable,那么建议使用component,然后配sholdComponentUndata来做判断
9、
所有要使用router的页面,都要放到router里面,也就是BrowserRouter内部,才能行,不然就会报错
10、使用react-loadable实现组件的异步加载,避免首页加载过慢