天天看点

React

React是用于构建用户的JS库(Facebook开源)

使用JS进行操作DOM会进行大量的重绘重排列

React采用组件化模式、声明式编码提高开发效率及组件复用率

React使用虚拟DOM和Diffing算法,减少与真实DOM的交互

Diffing算法:当浏览器进行更新时会和旧虚拟Dom进行匹配,当匹配到相同的虚拟Dom时不进行更新,只更新匹配不到(新的)的虚拟Dom

Recact使用jsx语法声明元素

基本的React使用(后期可用React脚手架进行项目搭建,无需引入这些样式和声明)

创建一个容器,并引入react相关库文件<div id="test"></div><!-- 引入react核心库 --><script src="./js/react.development.js"></script><!-- 引入react-dom操作dom --><script src="./js/react-dom.development.js"></script><!-- 引入babel,将jsx转换为js --><script src="./js/babel.min.js"></script>

声明一个script标签并使用jsx语法创建虚拟DOM

虚拟dom本质是一个object对象(一般对象)

虚拟DOM比较“轻”,真实dom比较“重”,因为虚拟dom是react内部在使用,无需真实DOM那么多属性,但虚拟DOM会被React使用Diffing算法转换为真实DOM

全称JavaScript XML,本质上React的语法糖React.createElement(component, props, ...children),主要是用来简化创建虚拟DOM

可以使用大括号引入js表达式(js语句和js表达式不同,一个表达式会产生一个值,可以使用变量接收到,比如a,a+b,function,一个语句则是控制代码走向的,如if、for、swich)

如果需要指定样式要使用className

如果要使用内联样式要使用style={{key:value}}

只要用了JSX,都要加上type="text/babel", 声明需要babel来处理

1.使用函数声明一个组件并使用标签渲染到页面上

function Demo() {

}

ReactDOM.render(<Demo/>,document.getElementById('test'))

类中的构造器不是必须写的,要对实例进行一些初始化的操作,比如添加需要指定属性时

如果A类继承了B类,且A类中写了构造器,那么A类中的super必须要调用

类中的所有方法都是放在原型对象上的,供实例使用

// 创建类式组件

简单组件和复杂组件

有状态的组件为复杂组件,用类定义,没有状态的为简单组件,用函数定义

通过更新组件的状态来更新页面

在类式组件中,可以在constructor函数中声明使用键值对声明state

constructor(props) {

注意:state的状态不能直接更改,需要借助API-setState修改

组件中的自定义方法 this指向undefined,如何解决?

a) 强制绑定this: 通过函数对象的bind()

b) 箭头函数,即赋值语句,不能直接用箭头函数

props主要通过标签属性从组件外向组件内传递数据

props是只读的,不能直接修改

使用扩展运算符...

多用于数组,在react中也可以用于展开对象,要放在标签内

使用propTypes可以对属性进行规则限制

函数式组件也能使用props,通过参数传递,如果需要参数限制将上方写在类中的static写到外面,使用Person定义

props(“properties” 的缩写)和 state 都是普通的 JavaScript 对象。它们都是用来保存信息的,这些信息可以控制组件的渲染输出,而它们的一个重要的不同点就是:props 是传递给组件的(类似于函数的形参),而 state 是在组件内被组件自己管理的(类似于在一个函数内声明的变量)。

字符串形式的ref

组件内的标签可以通过ref进行标识,相当于原生js的id

<input ref="input1"/>

回调函数式的ref

<input ref={(currentNode)=>{this.input1=currentNode}}/>

回调函数式的ref可以简写

<input ref={c=>this.input1=c}/>

此ref会被直接挂载this上,直接在this中解构赋值即可

createRef API 创建ref容器

myRef= React.createRef()

<input ref={this.myRef}/>

不要过度使用ref,如果发生事件的元素是触发事件的元素,即可以使用event.target操作元素

比如是失去焦点获取输入框的值,通过event.target可以得到发生事件的DOM元素对象

高阶函数拥有以下两个特性之一

1.A函数接收的参数是一个函数

2.A函数的返回值还是一个函数

常见的高阶函数有:Promise、setTimeout、arr.map()等

函数的柯里化:通过函数继续调用返回函数,实现多次传参并统一处理

组件被挂载上去会自动调用函数componentDidMount,通常用来开启定时器、发送ajax请求,订阅消息等

组件将卸载之前会自动调用函数componentWillUnmount,通常用来来关闭定时器、取消订阅

组件初始化会自动调用函数render,每次更新都会调用

初始化阶段由ReactDOM.render()触发---初次渲染

constructor()

componentWillMount()

render()

componentDidMount()

更新阶段由组件内部this.setSate()或父组件重新render触发

shouldComponentUpdate()

componentWillUpdate()

componentDidUpdate()

卸载组件 由ReactDOM.unmountComponentAtNode()触发

componentWillUnmount()

初始化阶段: 由ReactDOM.render()触发---初次渲染

1.constructor()

2.getDerivedStateFromProps

3.render()

4.componentDidMount()

更新阶段: 由组件内部this.setSate()或父组件重新render触发

1.getDerivedStateFromProps

2.shouldComponentUpdate()

4.getSnapshotBeforeUpdate

5.componentDidUpdate()

卸载组件: 由ReactDOM.unmountComponentAtNode()触发

1.componentWillUnmount()

即将废弃的钩子

1.componentWillMount

2.componentWillReceiveProps

3.componentWillUpdate

虚拟DOM中key的作用

1) .简单的说:key是虚拟DOM对象的标识,在更新显示时key起着极其重要的作用。

2) .详细的说:当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,

随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下

a. 旧虚拟DOM中找到与新虚拟DOM相同的key:

(1) .若虚拟DOM中内容没变,直接使用之前的真实DOM

(2) .若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM

b. 旧虚拟DOM中未找到与新虚拟DOM相同的key

根据数据创建新的真实DOM,随后渲染到到页面

2.用ndex作为key可能会引发的问题:

1.若对数据进行:逆力添加、逆序删除等破坏顺序操作:

会产生没有必要的真实DOM更新==> 界而效果没问题,但效率低。

2 .如果结构中还包含输入类的DOM:

会产生错误DOM更新==> 界面有问题。

3 .注意:如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,

仅用于演染列表用f展示,使用index作为key是没有问题的。

3.开发中如何选择key?

1 .最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。

2 .如果确定只是简单的展示数据,用index也是可以的。

脚手架是用来帮助程序员快速创建一个基于xxx库的模板项目

react提供了一个用于创建react项目的脚手架库: create-react-app

使用脚手架开发的项目的特点: 模块化, 组件化, 工程化

第一步,全局安装:npm i -g create-react-app

第二步,切换到想创项目的目录,使用命令:create-react-app hello-react

第三步,进入项目文件夹:cd hello-react

第四步,启动项目:npm start/yarn start

父组件<Father/>

子组件<Son />

在父组件中使用自定义方法,通过子组件使用props的方法的形参传递

组件拥有共同的状态可以将状态放在组件共有的父组件上

状态在哪里,操作状态的方法就在哪里

使用消息订阅

插件库:PubSubJS

下载: npm install pubsub-js --save

使用:

import PubSub from 'pubsub-js' //引入

PubSub.subscribe('delete', function(mag,data){ }); //订阅,必须传两个参数,在组件挂载完毕即componentDidMount

PubSub.publish('delete', data) //发布消息

如果不需要消息需要取消订阅和取消定时器一样,在组件卸载时调用

PubSub.unsubscribe(this.消息名)

ajax跨域代理

在package.json中追加如下配置

说明:

优点:配置简单,前端请求资源时可以不加任何前缀。

缺点:不能配置多个代理。

工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)

第一步:创建代理配置文件

编写setupProxy.js配置具体代理规则:

优点:可以配置多个代理,可以灵活的控制请求是否走代理。

缺点:配置繁琐,前端请求资源时必须加前缀。

‘jquery和axios都是基于xhr(XMLHttpRequest)发送ajax请求

fetch: 原生函数,不再使用XmlHttpRequest对象提交ajax请求

注意:老版本浏览器可能不支持

Get请求:

Post请求

一个路由就是一个映射关系(key:value)

key为路径, value可能是function(后端路由,当前端请求时返回函数调用的结果)或component(前端路由)

1) 理解: value是function, 用来处理客户端提交的请求。

2) 注册路由: router.get(path, function(req, res))

3) 工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据

1) 浏览器端路由,value是component,用于展示页面内容。

2) 注册路由: <Route path="/test" component={Test}>

3) 工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件

插件库:react-router-dom

导航区的a标签改为Link标签

<code>&lt;Link to="/home"&gt;Home&lt;/Link&gt;</code>

展示区的Route进行路径与组件匹配

<code>&lt;Route path='/home' component={Home}/&gt;</code>

在index的APP外包裹<code>&lt;BrowserRouter&gt;</code>或<code>&lt;HashRouter&gt;</code>

区别

1.底层原理不一样:

BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。

HashRouter使用的是URL的哈希值。

2.path表现形式不一样

BrowserRouter的路径中没有#,例如:localhost:3000/demo/test

HashRouter的路径包含#,例如:localhost:3000/#/demo/test

3.刷新后对路由state参数的影响

(1).BrowserRouter没有任何影响,因为state保存在history对象中。

(2).HashRouter刷新后会导致路由state参数的丢失!!!

4.备注:HashRouter可以用于解决一些路径错误相关的问题。

NavLink是Link标签的升级版,如果被点击时想添加属性可以使用activeClassName="属性名"

<code>&lt;NavLink activeClassName="backColor" to="/about"&gt;About&lt;/NavLink&gt;</code>

对NavLink进行封装,标签体的内容会通过props属性的children传递给子组件,所以可以直接结构赋值出所有的props

Swich标签可以进行单一匹配,如果两个路由的地址相同但组件不同,默认会展示两个组件

使用swich可以避免展示两个组件,匹配到第一个就不继续进行匹配

一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由

在public的index.html中可能会引入外部样式,因为路由的原因导致样式丢失

解决方法:

1.public/index.html 中 引入样式时不写 ./ 写 / (常用)

2.public/index.html 中 引入样式时不写 ./ 写 %PUBLIC_URL% (常用)

3.使用HashRouter代替BrowserRouter(少用)

路由的匹配规则默认为模糊匹配

开启精准匹配<code>&lt;Route exact={true} path="/about" component={About}/&gt;</code>

可以简写<code>&lt;Route exact path="/about" component={About}/&gt;</code>

注意:严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由

多级路由需要加上父路由的path值

1.params参数

路由链接(携带参数):&lt;Link to='/demo/test/tom/18'}&gt;详情&lt;/Link&gt;

注册路由(声明接收):&lt;Route path="/demo/test/:name/:age" component={Test}/&gt;

接收参数:this.props.match.params

2.search参数

路由链接(携带参数):&lt;Link to='/demo/test?name=tom&amp;age=18'}&gt;详情&lt;/Link&gt;

注册路由(无需声明,正常注册即可):&lt;Route path="/demo/test" component={Test}/&gt;

接收参数:this.props.location.search

备注:获取到的search是urlencoded编码字符串,需要借助querystring的parse方法解析

3.state参数

路由链接(携带参数):&lt;Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}&gt;详情&lt;/Link&gt;

接收参数:this.props.location.state

备注:刷新也可以保留住参数

可以在不借助Link和NavLink的情况下进行导航的跳转

借助this.prosp.history对象上的API对操作路由跳转、前进、后退

this.prosp.history.push(要去往的路由)

this.prosp.history.replace(要取代的路由,不留下历史记录)

this.prosp.history.goBack()后退一步

this.prosp.history.goForward()前进一步

this.prosp.history.go(n)后退或前进n步,n为负值是后退

路由组件有自带的三个props属性,而如果是自己用标签注册的组件则没有props属性

history

location

match

如果一般组件也想使用路由组件的特有API可以使用withRouter

导入withRouter,并在导出组件时使用withRouter(组件名)方法导出,返回一个新组件

安装antd:yarn add antd

按需引入即可,记得引入样式

样式可以进行按需引入参考官方文档即可

继续阅读