首先,我们要知道他是2011年Facebook所开发的一个用于构建用户界面的javascript库
具有以下的特点
虚拟dom:
就是当页面的数据变化的时候,react都会重新构建整个dom树(减少了页面的更新次数),然后将重新构建的dom树和上一次的dom树进行比对,得到dom结构,然后只将需要变化的部分在浏览器上进行dom更新
简而言之,就是减少更新次数,最小化渲染区域
diff算法:
diff算法,就是虚拟dom到真实dom转换过程中的最小操作过程叫做调和,调和正是diff算法的具体表现
diff算法的原理:
Javascript和XML结合的一种格式,是JavaScript 的语法扩展,在 React 中使用 JSX 来描述用户界面,提高代码的可读性,能使应用非常可靠,并能够提高其性能
在jsx中,遇到<>就会被当作HTML解析,遇到{}里的内容就会被当作js解析
但是在使用jsx需要注意的是:
多行文本必须使用一个根节点对其进行包裹,否则报错
使用的HTML标签必须严格按照W3C定义的标准书写,否则报错
jsx中的注释是{/* */},其他的注释会报错
html的value属性要写成defaultValue
html的checked属性要写成:defaultChecked
引用外部样式时, 不要使用class作为属性名,class 写className,因为class是关键字
【注意】:在浏览器是无法读取jsx的
浏览器处理的对象只有三种,html,css,javascript,所以为了是浏览器能够识别并且解析jsx,首先我们需要Babel这样的转换插件,可以将jsx语言转换为js对象,在渲染到浏览器上
jsx的优点:
1.允许使用熟悉的语法来定义 HTML 元素树;
2.JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
3.程序结构更容易被直观化;
4.它是类型安全的,在编译过程中就能发现错误。
5.使用 JSX 编写模板更加简单快速。;
组件是react应用ui的构建块,这些组件将整个ui划分为细化的模块,对于相似的模块进行重复使用,但是每一个组件之间都是相互独立的,一个是改变不会影响到其他的模块
【定义组件名必须是大写,组件的返回值必须是一个根元素】
分为两种
函数组件(又被称为无状态组件)
当组件仅是接收 props,并将组件自身渲染到页面时就叫做函数组件
不会产生组件对象,没有自身的状态和生命周期的钩子
类组件
会产生组件对象,可以有自身的状态和生命周期的钩子
【函数组件和类组件之间的区别】:
函数组件的性能比类组件的性能要高,因为类组件使用的时候要实例化,而函数组件直接执行函数取返回结果即可。为了提高性能,尽量使用函数组件。
函数组件没有state,没有this,没有生命周期
React 组件的生命周期有三个不同的阶段:
*初始渲染阶段:*这是组件即将开始其生命之旅并进入 DOM 的阶段。
constructor(): react数据的初始化方法
componentWillMount:一般用的比较少,但是还未渲染DOM时。
render:组件在这里生成虚拟的DOM节点
//这是AJAX请求和DOM或状态更新应该发生的地方
componentDidMount:组件真正在被装载之后
*更新阶段:*一旦组件被添加到 DOM,它只有在 prop 或状态发生变化时才可能更新和重新渲染。这些只发生在这个阶段(this.setState({}))。
componentWillUpdate:组件即将更新不能修改属性和状态
render:组件重新描绘
componentDidUpdate:组件已经更新
componentWillReceiveProps:组件将要接收到属性的时候调用,初始化不触发
shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回false,接收数据后不更新,阻止render调用重绘)
*卸载阶段:*这是组件生命周期的最后阶段,组件被销毁并从 DOM 中删除。
componentWillUnmount:组件即将销毁
在 React 中,事件是对鼠标悬停、鼠标单击、按键等特定操作的触发反应。处理这些事件类似于处理 DOM 元素中的事件
绑定函数的过程中不加() 否则函数会立即执行
和原生的事件的区别在于:
react中的事件绑定有四种方法:
箭头函数
funa=()=>{}
<button onClick={this.funa}>点击我修改</button>
事件调用为箭头函数【可传参】
funa=function(a,b){}
onClick={()=>{this.funa(参数1,参数2)}}
bind方法【可传参】
funa=function(){}
onClick={this.funa.bind(this,参数一,参数2)}
constructor提前绑定
class MyCom extends React.Component{
constructor(props){
super(props)
//提前对事件进行绑定
this.fund=this.fund.bind(this)
}
fund=function(){}
onClick={this.fund}
ref是用来标识组件内部的元素的(也可以说是用来操作dom元素的)
【切记:ref用在类组件上,因为函数组建没有实例】
ref三种使用方法:
字符串
ref=“xxx”;
使用:this.refs.xxx.样式
funa = () => {
this.refs.demona.style.color="red"
}
<h1 ref="demona" onClick={this.funa}>1</h1>
使用回调函数(推荐)
使用ref={(xiaoming)=>{this.xianghong=xiaoming}}绑定dom
使用 this.xianghong.XXX 修改dom元素样式
funb = () => {
this.b.style.backgroundColor="yellow"
}
<h1 ref={(a)=>{this.b=a}} onClick={this.funb}></h1>
React.createRef()
使用ref={this.自定义名字}绑定dom
在constructor里使用this.自定义名字=React.createRef()进行定义
使用his.自定义名字.current.XXX 修改dom元素样式
constructor(props) {
super(props)
this.demonref=React.createRef()
}
func = () => {
this.demonref.current.style.backgroundColor="yellow"
}
<h1 ref={this.demonref} onClick={this.func}></h1>
props:是react中不可变的属性,是整个应用中父组件给子组件传值的一种方式,一般用来展示动态生成的数据
state:是react中状态,是数据的来源,而且state是可变的
两者之间的区别
Props | State |
---|---|
组件对外的 | 组件对内的接口 |
不能在自己所在组件修改值 | 可以在所在组件修改值 |
一般用来传递参数 | 一般用来更新渲染 |
具有可读性和不变性 | 不可通过外部访问和修改 |
是组件的一个属性 | 是一种数据结构 |
Props 是 React 中属性的简写。它们是只读组件,必须保持纯,即不可变。它们总是在整个应用中从父组件传递到子组件。子组件永远不能将 prop 送回父组件。这有助于维护单向数据流,通常用于呈现动态生成的数据。
状态是 React 组件的核心,是数据的来源,在react中使用state进行表示
基本上状态是确定组件呈现和行为的对象。与props 不同,它们是可变的,并创建动态和交互式组件。可以通过 this.state()
访问它们。
【注意】:在函数组件中不能使用state,因为它没有实例
使用:
//定义:
this.state={
名值对
}
//使用:
this.state.xxx
//修改
this.setState({
名值对
})
在调用setState 之后,react会将传进来的参数对象与当前组件的状态进行合并,然后触发调和的过程,经过调和的过程以后,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面
在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染
首先,了解setState是异步操作,其次明白会自动重新触发render()
// 修改state
this.setState({
text:"你好么么哒"
},()=>{
//修改以后的数据
console.log("回调中的打印"+this.state.text)
})
//原始数据,没有被修改
console.log(this.state.text)
Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。
在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。
此外,React 还需要借助 Key 值来判断元素与本地状态的关联关系,因此我们绝不可忽视转换函数中 Key 的重要性。
react中用来负责渲染表单的组件
同时仍然控制用户后续输入时的变化,值是来自于state控制的
在HTML当中,像 ,
受控组件就是组件的状态受React控制,实时更新,类似于vue中的双向绑定
this.state = {
value: props.value
}
Change(e) {
this.setState({
value: e.target.value
})
}
<input value={this.state.value} onChange={e =>this.Change(e)}/>
this.props.children,主要作用是用来接收父组件写在自定义标签的开关标签之中的内容的
//父组件
<son>
<p>1</p>
<p>2</p>
<p>3</p>
</son>
//子组件
{
this.props.children
}
this.props.children的值有三种可能:
正传
父组件:自定义名字=值
let obj={name:"xixi",age:30}
<mycom value={...obj}>
子组件:this.props.自定义名字
this.props.value.XXXX
逆传
子组件:this.props.自定义名字.bind(this,传递的值)
<button onClick={this.props.father.bind(this,"我是逆向传来的数据")}>点击进行数据逆向传值</button>
父组件:自定义名字={this.fun}
fun=(can)=>{
console.log(can)
}
<p>我是逆向传值的爸爸</p>
<Son father={this.fun}></Son>
同胞传值
使用pubsub-js插件
下载:npm install --save pubsub-js
子组件1:使用PubSub.publish()发送数据
import Pubsub from "pubsub-js"
Pubsub.publish("pao","这里是传过来的参数")
子组件2:使用PubSub.subScribe() 接受数据
import Pubsub from "pubsub-js"
Pubsub.subscribe("pao", (a, b) => {
//参数a是表示发送的事件名
console.log("a"+a)
//参数a是表示发送的参数
console.log("b"+b)
})
跨组件传值
使用content(新建一个content文件夹)
第一步:创建content
import {createContent} from "react"
let content=createContent()
let {Provider,Consumer}=content
<Provider value={{name:"zhi",text:"123"}}>
//因为需要当前的组件包裹需要接受数据的组件,
// 所以需要使用this.props.children,将对包裹组件的渲染
this.props.children
//暴露组件和consumer
第二步:在根组件进行使用,将所有的app进行包裹
第三步:使用consumer
```js
{
(value)=>{
return({value.text})
}
}
在React组件的构建过程中,是react中代码或者ui块的复用的技术(类似于vue的混入)
它们可以接受子组件提供的任何动态,但不会修改或复制其输入组件中的任何行为
所以我们也将其成为纯组件
高阶组件的特点是参数是组件,同时返回值也是一个组件
【组件是将props转换为UI,但是高阶组件是将一个组件转换为另一个组件】
高阶组件的作用
渲染劫持(反向继承–条件渲染)
在使用高阶组件的使用传递一个参数给高阶组件,在高阶组件之内对传递过来的参数进行判断,符合条件的渲染,不符合条件的另做处理
修改继承组件为父组件
暴露组件的时候需要传递参数
代码复用
withRouter就是一个高阶组件,用来给不是有路由管理的页面注入路由的
使用:
第一步:解构
import { withRouter } from "react-router-dom"
第二步:修改暴露方法
class jslink extends Component {
}
第三步:使用
<jslink>
react的路由分类有两种
react的路由模式有两种:
react路由跳转的两个方式
声明式
<Link to="/my">我的</Link>
//或者
<NavLink to="/my">我的</NavLink>
编程式
this.props.history.push("/" + 路径)
react中路由传参的两种方式
params
//配置参数
<Route path="/home/:name" component={Home} />
//发送数据
//声明式
<Link to="/home/我是参数">去Home</Link>
//编程式
<b onClick={()=>{this.props.history.push("/home/编程式参数")}}>点击给home传递参数</b>
//接受数据(类组件)
componentWillMount(){
console.log(this.props.match.params.name)
}
//(函数组件)
useEffect(){
console.log(形参.match.params.属性名e)
}
state
//声明式
<Link to={{pathname:"/news",state:{name:"我是state的传递参数"}}}>去news页面传参</Link>
//编程式
<strong onClick={()=>{this.fun()}}>点击传递</strong>
fun(){
this.props.history.push({pathname:"/news",state:{name:"我是state的传递参数"}})
}
componentWillMount(){
console.log(this.props.location.state.name)
}
redux的三个原则
什么时候用redux
redux的常用方法
createStore():创建redux的核心对象,在引入redux的之后解构出来
import {createStore} from "redux"
getState():获取数据
store.getState().xxx
dispatch();触发action
store.dispatch({type:"一般情况为大写名字"})
subscribe():变化监听器,当有变化的时候触发
store.subScribe(()=>{
this.setState({ age:store.getState().age, })
})
redux的执行流程:
在工作中因为redux中的数据或很多,所以将redux进行封装
对派发动作任务进行封装
对派发动作名进行封装
对reducer中的数据进行封装
将reducer进行合并(combineReducers)
import {combineReducers} from "redux"
let reducer=combineReducers({
homeR
})
export default reducer
合并以后的reducer应用到redux中
使用的时候要加上模块名
是一个专门用来简化react中的redux对象的
使用步骤
先进行react-redux的下载并引入解构出Provider
import {Provider} from "react-redux"
在根组件中使用provider将根进行包裹起来,并传入数据
<Provider store={store}>
<App/>
</Provider>
在需要使用的组件中使用我高阶组件connnect进行使用
import { connect } from "react-redux"
//暴露
export default connect(state => ({ state }))(demon)
使用数据,使用的时候要加上模块名
<h5>{this.props.state.demoR.name}</h5>
ajax可以放在 componentDidMount方法中执行
当从服务端获取数据时可以将数据存储在 state 中,再用 this.setState 方法重新渲染 UI。
原生的jquery库
axios
注意post发送数据传参的问题
使用qs库 或 URLSearchParams
下载:npm install --save qs
引用:
使用:let key=qs.stringify({
key:val
})
fetch原生函数(太新,老版本的浏览器不支持)
XMLHttpRequest的最新替代技术——Fetch API, 它是W3C的正式标准 (xx.json转换成json字符串)
//post
fetch("地址", {
method: "POST",
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: "key=val&key=val"
})
.then(req=>req.json())
.then(function(ok) {console.log(ok)})
//get
fetch("http://localhost:8888/get?key=val", {
method: "get"
})
.then(req=>req.json())
.then(function(ok) {console.log(ok)})
}
fetch ,ajax ,axios
传统 Ajax 指的是 XMLHttpRequest(XHR),核心使用XMLHttpRequest对象,多个请求之间如果有先后关系的话,就会出现回调地狱。JQuery ajax 是对原生XHR的封装
axios 是一个基于Promise ,本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范,
fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。
在调用方法之前,子类构造函数无法使用 this ,引用 super()
在ES6中,在子类的 constructor
中必须先调用 super
才能引用 this
在 constructor
中才可以使用 this.props
class MyComponent extends React.Component {
constructor(props) {
super();
console.log(this.props); // undefined
// 但是 Props 参数仍然可用
console.log(props); // Prints { name: 'sudheer',age: 30 }
}
render() {
// 构造函数外部不受影响
console.log(this.props) // { name: 'sudheer',age: 30 }
}
}
hook是react16版推出的新特性,
主要的作用:是让无状态组件可以使用状态和react的其他特性。
使用范围:Hook在class中没有作用
常用的hook有:
useState()->用来定义和管理本地状态
import React, { useState } from 'react'
//useState里可以是一个对象
let [state, action] = useState("默认值")
<p>{state}</p>
onClick={()=>{action(state+1)}
useRef()->获取Dom节点
import React, { useState,useRef } from 'react'
let U=useRef(null)
<p ref={U}>我是小白鼠,我就要变色了</p>
<h2 onClick={()=>{action(U.current.style.color="red")}}>开始操作ref</h2>
useEffect()->函数组件中没有生命周期,那么可以使用 useEffect 来替代
import React, { useState,useRef,useEffect } from 'react'
useEffect(()=>{
console.log("hahahah")
})
useReducer()->防止state里面的数据操作太复杂。减少state的数据操作复杂度[类似于类组件中的redux]
import React, { useReducer } from 'react'
let [state, dispatch] = useReducer(reducer, 0)
function reducer(state, action) {
switch (action.type) {
case "ADD": return state + 1
break;
default: return state
break;
}
}
function add() {
dispatch({ type: "ADD" })
}
<p>{state}</p>
<button onClick={add}>+1</button>
React的StrictMode是一种帮助程序组件,可以帮助您编写更好的react组件,您可以使用包装一些组件,
StrictMode的作用:
在JavaScript中, this
的值取决于当前上下文。在React类的组件方法中,开发人员通常希望它引用组件的当前实例,因此有必要 将 这些方法 绑定 到该实例【通常,这是在构造函数中完成的】
这个方法用来判断是否需要调用 render 方法重新描绘 dom
因为 dom 的描绘非常消耗性能,如果我们能在 shouldComponentUpdate 方法中能够写出更优化的 dom diff 算法,可以极大的提高性能。
相似之处:
不同之处:
数据
vue->双向数据绑定和单向数据流
OM元素绑定的data值,当发生改变后,vue的响应式机制会自动监听data的变化重新渲染
react->单向数据流
DOM元素依赖于state,但改变state不会改变渲染好的DOM,通过setState()才能重新渲染
虚拟DOM的不同
vue:计算出虚拟DOM的差异,在渲染的过程中跟踪每个组件的依赖关系,不会重新渲染整个组件树
react:当应用的状态改变时,重新渲染全部子组件,可以通过shouldComponentUpdate生命周期进行优化
模板与jsx
vue:具有单文件组件,可以把html、css、js写在一个vue文件里----MVVM框架
react:依赖于jsx,在JavaScript中创建DOM----视图层框架
dangerouslySetInnerHTML = {{ __html:你要插入的字符串 }}
1.把图片放到public文件夹中 直接使用图片名使用
2.不在public文件夹中 .使用require引用
<img src={require("../assets/a.jpg")}>
3.不在public文件夹中使用 导入图片路径:
import Imag1 from "../assets/a.jpg"
<img src={Imag1}>
构建 React 应用程序时,在多层嵌套组件来使用另一个嵌套组件提供的数据
prop 从每个组件一层层的传递下去,从源组件传递到深层嵌套组件,这叫做prop drilling。
prop drilling的主要缺点是原本不需要数据的组件变得不必要地复杂,并且难以维护。
为了避免prop drilling,一种常用的方法是使用React Context。通过定义提供数据的Provider组件,并允许嵌套的组件通过Consumer组件或useContext Hook 使用上下文数据。
在react的15.5版本之后,props验证被迁移到了 prop-types库中
语法:
组件名.propTypes={
name:PropTypes.Number
}
这两种方法都依赖于对传递给组件的props的浅比较,如果 props 没有改变,那么组件将不会重新渲染。虽然这两种工具都非常有用,但是浅比较会带来额外的性能损失,因此如果使用不当,这两种方法都会对性能产生负面影响。
通过使用 React Profiler,可以在使用这些方法前后对性能进行测量,从而确保通过进行给定的更改来实际改进性能。
npm run build打包上线后,页面出现空白的原因
【解决】:package.json这个文件中添加一行"homepage":"./"
react-developer-tools---->谷歌浏览器开发者调试工具
使用:
进入谷歌浏览器扩展程序中打开开发者模式
选择加载以解压的扩展程序
添加文件
forceUpdate()就是重新调用render渲染。
有些变量不在state上,当时你又想达到这个变量更新的时候,刷新render;
配置别名的步骤
下载:npm install react-app-rewired -D
在package.json的配置文件中修改scripts
scripts:{
"start": "react-app-rewired start"
}
根目录下新建一个config-overrides.js
const path = require('path');
function resolve(dir) {
return path.join(__dirname, '.', dir)
}
module.exports = function override(config, env) {
config.resolve.alias = {
'@': resolve('src'),
"com":resolve("src/components")
}
return config;
}
重新启动
在package.json文件下找到scripts属性,看,第一个属性是start还是dev,要是start就使用,npm run start
要是dev,就使用npm run dev
【原因】:是因为eslint屏蔽了这个方法,脚手架默认安装了ESlint
console.log(confirm("aa"))
【解决】:使用// eslint-disable-next-line下面这句js 就不用eslint进行语法检查了。
console.log(confirm)
.json这个文件中添加一行"homepage":"./"
react-developer-tools---->谷歌浏览器开发者调试工具
使用:
进入谷歌浏览器扩展程序中打开开发者模式
选择加载以解压的扩展程序
添加文件
forceUpdate()就是重新调用render渲染。
有些变量不在state上,当时你又想达到这个变量更新的时候,刷新render;
配置别名的步骤
下载:npm install react-app-rewired -D
在package.json的配置文件中修改scripts
scripts:{
"start": "react-app-rewired start"
}
根目录下新建一个config-overrides.js
const path = require('path');
function resolve(dir) {
return path.join(__dirname, '.', dir)
}
module.exports = function override(config, env) {
config.resolve.alias = {
'@': resolve('src'),
"com":resolve("src/components")
}
return config;
}
重新启动
在package.json文件下找到scripts属性,看,第一个属性是start还是dev,要是start就使用,npm run start
要是dev,就使用npm run dev
【原因】:是因为eslint屏蔽了这个方法,脚手架默认安装了ESlint
console.log(confirm("aa"))
【解决】:使用// eslint-disable-next-line下面这句js 就不用eslint进行语法检查了。
console.log(confirm)