HTML
CSS
JavaScript基础
JavaScript(高级)DOM
React
简介
React将数据渲染为HTML视图的开源js库
虚拟DOM本质上是普通的Object类型对象
并且虚拟DOM属性很少,比较“轻”
文件
babel.min.js : ES6转ES5 、 jsx 转 js
react.development.js : react核心库
react-dom.development.js : react扩展库
写react代码的代码块
<script type="text/babel"></script>
创建项目
首先创建react项目可以先下载脚手架create-react-app(类似于vue的脚手架vue-cli)。
①打开cmd,执行:npm install -g create-react-app ; 全局安装。
如果执行失败,可能是node版本问题, react文档中要求Node >= 8.10 和 npm >= 5.6,查看版本:node -v;npm -v;如果node版本低,可以去node官网下载Download | Node.js;(下载完成后,记得编辑环境变量和配置全局路径和缓存路径)
具体安装步骤参考博文:node安装详解_樊小樊的博客-CSDN博客_node安装
安装完node,如果cache报错,可以试一下清除缓存
以管理员身份打开cmd, 执行:npm cache verify;或者npm cache clean
②第一步成功后,cd进到你想要创建项目的文件夹,执行:create-react-app my-app 新建并对react项目进行命名(注:项目名称不能有大写,my-app是自定义的项目名)
③第二步成功后,cd进入my-app项目,执行:npm start
运行成功后浏览器会自动打开,默认端口为3000;如果没有自动打开,手动:http://localhost:3000;
④想在编辑器上开发,以vscode为例,打开vscode,点击文件–>打开文件夹位置–>打开项目(刚刚用cmd创建的my-app项目),点击查看–>终端–>执行:npm start。
虚拟Dom
1、创建虚拟Dom(Virtual-Dom)
const VDOM = <h1>Hello_React!</h1>
2、渲染Dom到页面
ReactDom.render('虚拟Dom','容器')
//这里容器,react并没有提供选择器,需要用原生的返回一个元素
//例如
ReactDOM.render(虚拟Dom,document.getElementById('test'))
使用原生ReactAPI创建虚拟DOM
这种方式对嵌套元素不方便
需要用到React内的一个API
React.createElement(标签名,标签属性,标签内容)
// 创建虚拟DOM
const VDOM = React.createElement('h1',{id:'title',class:'ddd'},'Hello React!')
// 将虚拟DOM添加到页面
ReactDOM.render(VDOM,document.getElementById('test'))
JSX
简介
JSX全称JavaScript XML
语法
1.定义虚拟DOM时,不要写引号。
2.标签中混入JS表达式时要用 “ { } ” 。
let a = 'React'
let b = 'Vue'
const VDOM = <h1>hello {a} and {b}</h1>
ReactDOM.render(VDOM,document.getElementById('test'))
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p5XLafji-1665128870140)(C:\Users\shu\AppData\Roaming\Typora\typora-user-images\image-20220818185548272.png)]
3.样式的类名指定不要用class,要用className。
const VDOM = <h1 className="h1">我是H1标签</h1>
4.React内联样式的类名需要用驼峰写法,并且内容要用大括号括起来。
const VDOM = <h1 classNam="h1" style={{color:'white',fontSize:'29px'}}>我是H1标签</h1>
5.JSX语法可以用 “ ( ) " 括起来
const VDOM = (
<h1>我是H1标签
<span>我是span标签</span>
</h1>
)
4.不允许多个根标签
这样不允许!
5.标签必须闭合
6.标签首字母
若小写字母开头,则将该标签转为html中同元素,若html中无同名元素,则报错
若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错
这里的组件定义,等后面会讲到
6.花括号内只能写表达式不能写代码
注释
{/*<h1></h1>*/}
//大括号内表示要些js的语句,然后再注释
React面 向组件编程
函数式组件
执行过程
React解析组件标签,找到了MyComponent组件。
发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面当中。
类式组件
创建的类必须继承React内置的一个类
class MyComponent extends React.Component {
}
复杂组件与简单组件
没有状态的叫做简单组件,有状态的是复杂组件(state)
构造器内的super
不写的话会报错
因为子类继承时,需要拿到父类的构造器(实例化的时候会报错,当然你不实例化就不会出错)
1、constructor()
这是ES6类的默认方法,通过 new 命令生成对象实例时自动调用该方法。并且,该方法是类中必须有的,如果没有显示定义,则会默认添加空的constructor( )方法。
2、super()
在 class 方法中,子类时会报错。报错的原因是:子类是没有自己的 this 对象的,它只能继承自父类的 this 对象,然后对其进行加工,而 super( ) 就是将父类中的 this 对象继承给子类。没有 super,子类就得不到 this 对象。
3、出现上面情况的原因是,ES5 的继承机制与 ES6 完全不同。简单解释,就是在 ES5 的继承中,先创建子类的实例对象 this,然后再将父类的方法添加到 this 上( Parent.apply(this) )。而ES6采用的是先创建父类的实例 this(故要先调用 super( ) 方法),完后再用子类的构造函数修改 this。
4、当一个构造函数前加上 new 的时候,背地里来做了四件事:
(1)生成一个空的对象并将其作为 this;
(2)将空对象的 proto 指向构造函数的 prototype;
(3)让构造函数里面的 this 指向这个空对象,并且调用该构造函数,给这个空对象添加属性和方法;
(4)返回这个新对象,所以构造函数里面不需要return(如果构造函数没有 return 或者 return 一个返回 this 值是基本类型,则返回 this;如果 return 一个引用类型,则返回这个引用类型)。
5、super(props)
(1)如果你用到了 constructor 就必须写 super(),是用来初始化 this,可以绑定事件到 this 上;
(2)如果你在 constructor 中要使用 this.props,就必须给 super 加参数:super(props);(无论有没有 constructor,在 render 中 this.props 都是可以使用的,这是React自动附带的)
6、为什么不能在 constructor 中直接使用 this.props
其实很简单,因为 this.props 必须要是一个对象,才能在它下面定义属性,而 constructor(props) 方法中 传入的参数 props 为对象,所以 super(props) 的作用就是在父类的构造函数中给 props 赋值一个对象 this.props = props 这样就能在它的下面定义你要用到的属性了,然而其他的由于没有传参就直接赋值为 undefind。
组件三大核心
state
基础
<script type="text/babel">
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = { isHot: false }
}
render() {
const isHot = this.state.isHot
return <h1>今天天气很{isHot ? '热' : '凉'}</h1>
}
}
ReactDOM.render(<Weather />, document.getElementById('test'))
</script>
事件绑定
<script type="text/babel">
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = { isHot: true }
}
render() {
const { isHot } = this.state
return <h1 onClick={demo}>今天天气很{isHot ? '热' : '冷'}</h1>
//这里onClick要大写,并且右侧大括号,代表将函数传递给onClick进行事件处理(不要直接写小括号,那样会被直接调用)
}
}
ReactDOM.render(<Weather />, document.getElementById('test'))
function demo() {
alert('啦啦');
}
</script>
类中方法的指向
类中定义的方法自动添加了严格模式
所以x()执行后的结果为undefined
解决他
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {
isHot: true
}
this.Weather = this.Weather.bind(this) //将原型对象的Weather的this指向设置为Weather对象,再将这个函数赋给Weather实例对象上的Weather函数
}
render() {
const { isHot } = this.state
return <h1 onClick={this.Weather}>今天天气很{isHot ? '热' : '冷'}</h1>
}
Weather() {
console.log(this)
if (this.state.isHot == true) {
this.state.isHot = false
}
else {
this.state.isHot = true
}
console.log(this.state.isHot)
}
}
ReactDOM.render(<Weather />, document.getElementById('test'))
但是状态不可直接更改,需要借助一个内部的api来更改
setState
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {
isHot: true
}
this.Weather = this.Weather.bind(this)
}
render() {
const { isHot } = this.state
return <h1 onClick={this.Weather}>今天天气很{isHot ? '热' : '冷'}</h1>
}
Weather() {
const isHot = this.state.isHot
this.setState({ isHot: !isHot })
}
}
ReactDOM.render(<Weather />, document.getElementById('test'))
总结
构造器:初始化数据,更改函数指向
render:状态里面读取数据,根据状态值做展示
简写
自定义方法要用赋值语句和箭头函数的方法,因为箭头函数没有this
props
基础
这里是之前版本的写法,现在最新的写法如下图(2022年10月更新)
用之前的方法不影响食用,但会出错(希望学会这种方法,以后遇到用来替之)
class Person extends React.Component {
render() {
return (
<ul>
<li>姓名:{this.props.name}</li>
<li>年龄:{this.props.age}</li>
<li>性别:this.props.sex</li>
</ul>
)
}
}
const PersonText = {
name: '张三',
age: 19,
sex: '男'
}
ReactDOM.render(<Person name={PersonText.name} age={Person.age} sex={Person.sex} />, document.getElementById('test'))
当然也可以简写为
三点运算符(展开运算符)
ReactDOM.render(<Person {...PersonText} />, document.getElementById('test'))
// 这种方式是批量传递props
//props 标签 很大量的标签
图片内为原生写法
原生里面的花括号和react里面的花括号含义不同
所以对…运算符实现也不同,所以react可以将对象展开(只能用在标签属性的传递),原生里面是不能…对象的
对props限制
引入这个依赖包
简写
static会变量放到对象身上,如果不加则放到原型对象上
当然,经测试可知,这里不用static而是直接使用
propTypes = {
name: PropTypes.string
}
也是可以的
构造器是否接收props,是否传递props
就看你是否需要在构造器中通过this访问props
但是一般直接写就完事了,这种场景用不到
类中构造器能省略就省略
函数式组件使用props
函数不能使用react三大属性的state和refs,但是可以使用props
,因为函数有个特点——能够接收参数
function Fun(props) {
const { name, age, sex } = props
return (
<h1>
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
</h1>
)
}
ReactDOM.render(<Fun name="张三" age="19" sex="男"/>, document.getElementById('test'))
总结
理解
每个组件对象都会有props(properties的简写 “属性”)
组件标签的所有属性都保存在props中
作用
通过标签属性从组件外向组件内传递变化的数据
注意
组件内部不要更改props的值,否则报错
refs
与事件的搭配使用(快被淘汰了)
class Demo extends React.Component {
Left = () => {
const { input1, btn } = this.refs
alert(input1.value)
}
Right = () => {
const { input2 } = this.refs
alert(input2.value)
}
render() {
return (
<h1>
<input ref="input1" />
<button ref="btn" onClick={this.Left}>按下获取信息</button>
<input ref="input2" onBlur={this.Right} />
</h1>
)
}
}
ReactDOM.render(<Demo />, document.getElementById('test'))
这种字符串形式的ref已经不推荐使用了
回调ref(内联和独立写法的区别)
class Demo extends React.Component {
Left = () => {
const { input1 } = this
alert(input1.value)
}
Right = () => {
const { input1 } = this
alert(input2.value)
}
render() {
return (
<h1>
<input type="text" ref={(c) => { this.input1 = c }} />
//这里简写为 <input type="text" ref={ c => this.input1 = c } />
<button onClick={this.Left}>按下获取信息</button>
<input onBlur={this.Right} ref={(c) => { this.input2 = c }} type="text" />
</h1>
)
}
}
ReactDOM.render(<Demo />, document.getElementById('test'))
这里建议用 对象上的函数来做回调函数,优点在于可以避免react刷新state导致的多次调用回调函数的问题
//案例展示
class Demo extends React.Component {
state = {
isHot: true
}
Left = () => {
const { input1 } = this
alert(input1.value)
}
change = () => {
const { isHot } = this.state
this.setState({
isHot: !isHot
})
}
render() {
const { isHot } = this.state
return (
<h1>
<p>今天天气很{isHot ? '热' : '冷'}</p>
<input ref={(c) => { this.input1 = c;console.log(c) }} type="text" />
<button onClick={this.Left}>按下获取信息</button>
<button onClick={this.change}>按下我更改天气</button>
</h1>
)
}
}
ReactDOM.render(<Demo />, document.getElementById('test'))
//案例解决
class Demo extends React.Component {
state = {
isHot: true
}
Left = () => {
alert(this.input1.value)
}
change = () => {
const { isHot } = this.state
this.setState({
isHot: !isHot
})
}
inputfun = (c) => {
this.input1 = c
console.log(c)
}
render() {
const { isHot } = this.state
return (
<h1>
<p>今天天气很{isHot ? '热' : '冷'}</p>
//这里直接将回调函数独立出来,而不是用内联
<input ref={this.inputfun} type="text" />
<button onClick={this.Left}>按下获取信息</button>
<button onClick={this.change}>按下我更改天气</button>
</h1>
)
}
}
ReactDOM.render(<Demo />, document.getElementById('test'))
淦!!!
createRef API的使用(最新的)
React.createRef调用后可以返回一个容器,该容器而已存储被ref所标识的节点,该容器是“专人专用的”,只能存一个
所以需要创建多个ref容器
class Demo extends React.Component {
creatRef1 = React.createRef()
creatRef2 = React.createRef()
fun1 = () => {
alert(this.creatRef1.current.value)
}
fun2 = () => {
alert(this.creatRef2.current.value)
}
render() {
return (
<h1>
<input type="text" ref={this.creatRef1} />
<button onClick={this.fun1}>点击提示信息</button>
<input type="text" ref={this.creatRef2} onBlur={this.fun2} placeholder="失去焦点显示内容" />
</h1>
)
}
}
ReactDOM.render(<Demo />, document.getElementById('test'))
总结
尽量避免字符串形式的ref
回调形式的ref,稍微麻烦
事件注意
通过onXxxx属性指定事件处理函数注意大小写,React使用的是自定义(合成)事件,而不是原生DOM事件(为了兼容性),React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)(事件委托更高效)
通过event.target得到发生事件的DOM元素对象(所以这里不要过渡使用Ref,而是直接使用event事件对象)
class Demo extends React.Component {
fun = (event) => { //直接传入event,使用target获取发生事件的事件源
alert(event.target.value)
}
render() {
return (
<h1>
<input type="text" onBlur={this.fun} /> {/*这里不需要ref*/}
</h1>
)
}
}
ReactDOM.render(<Demo />, document.getElementById('test'))
受控组件与非受控组件
非受控组件
阻止默认事件的触发
event.preventDefault()
btn = (e) => {
// 阻止默认行为
e.preventDefault();
alert(`您的用户名:${this.username.value} 密码:${this.password.value}`)
}
页面中所有现用现取(输入类的)组件,就叫非受控组件
class Demo extends React.Component {
username = (c) => {
this.username = c
}
password = (c) => {
this.password = c
}
btn = (e) => {
e.preventDefault();
alert(`您的用户名:${this.username.value} 密码:${this.password.value}`)
}
render() {
return (
<form>
<input type="text" ref={this.username} />
<br />
<input type="password" ref={this.password} />
<br />
<button onClick={this.btn}>登录</button>
</form>
)
}
}
ReactDOM.render(<Demo />, document.getElementById('test'))
受控组件
页面中输入类组件的信息随时都保存到状态state中,需要用时直接调用state
这种方式可以减少使用ref
class Demo extends React.Component {
state = {
password: '',
username: ''
}
usernameFun = (event) => {
this.setState({ username: event.target.value })
}
passwordFun = (event) => {
this.setState({ password: event.target.value })
}
btn = (event) => {
// 阻止默认行为
alert('您的账号:' + this.state.username + '您的密码:' + this.state.password)
console.log(this.state)
event.preventDefault();
}
render() {
return (
<form>
<input type="text" onChange={this.usernameFun} />
<input type="password" onChange={this.passwordFun} />
<button onClick={this.btn}>登录</button>
</form>
)
}
}
ReactDOM.render(<Demo />, document.getElementById('test'))
高阶函数/函数柯里化
必须要哪一个函数作为事件的回调,所以这里的函数其返回值是一个函数即可
这里的saveFormData即为高阶函数
高阶函数/函数柯里化
必须要哪一个函数作为事件的回调,所以这里的函数其返回值是一个函数即可
这里的saveFormData即为高阶函数
高阶函数定义
如果一个函数符合下面2个规范中的任何一个,那么该函数就是高阶函数
1、若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数
2、若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数
函数的柯里化
通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式
个人理解的作用是:
不用柯里化的方式
class Demo extends React.Component {
btn = () => {
alert('您的账号:' + this.state.username + '您的密码:' + this.state.password)
}
saveData = (name, value) => {
this.setState({
[name]: value
})
}
render() {
return (
<form>
用户名:<input type="text" onChange={(event) => { this.saveData('username', event.target.value) }} />
密码:<input type="password" onChange={(event) => { this.saveData('password', event.target.value) }} />
<button onClick={this.btn}>登录</button>
</form>
)
}
}
ReactDOM.render(<Demo />, document.getElementById('test'))
生命周期
卸载组件
ReactDOM.unmountComponentAtNode(document.getElementById(‘test’))
render的兄弟
componentDidMount
组件挂载完毕后调用
componentWillUnmount
组件将要卸载是调用
生命周期回调函数 <=> 生命周期钩子函数 <=> 生命周期函数 <=> 生命周期钩子
理解
组件从创建到死亡会经历一些特定的阶段
React组件中 包含一系列钩子函数(生命周期回调函数),会在特定的时刻调用
我们在定义组件时,会在特定的生命周期回调函数,中做特定的工作
生命周期图(旧)
挂载的执行流程
1、constructor构造器
2、componentWillMount组件将要挂载
3、render挂载组件
4、componentDidMount组件挂载完成
————————————挂载初始化完毕————————————
5、componentWillUnmount组件将要卸载
更新的执行流程
①
render父组件更新
componentWillRecceiveProps组件将要接收props
第一次往子组件传的参数不算(不会执行),以后传的才算(才会执行)
并且这个函数会有参数,值为父组件传递过来的值
shouldComponentUpdate组件是否应该更新?(开关/阀门)
componentWillUpdate组件将要更新
render更新组件
componentDidUpdate组件更新完成
componentWillUnmount组件将要卸载
②
setState状态更新
shouldComponentUpdate组件是否应该更新?(开关/阀门)
如果这里返回false,则不更新
默认不写,那么默认也就返回true,则更新(如果写了这个函数,那必须要写return并且返回true或者false)
componentWillUpdate组件将要更新
render更新组件
componentDidUpdate组件更新完成,他会传递两个参数,一个props(之前的props ,并不是实时的),一个状态state(之前的state,并不是实时的)
componentWillUnmount组件将要卸载
③强制更新,这种更新一般是不想通过状态更新所使用
forceUpdate强制更新,不受②中的阀门控制
render更新组件
componentDidUpdate组件更新完成
componentWillUnmount组件将要卸载
组件与组件相互通信
总结
常用
对比新旧生命周期
新版本中 这三个需要加UNSAFE
componentWillMount
componentWillReceiveProps
componentWillUpdate
这些需要加UNSAFE的生命周期,会带来不安全的编码(红色框)
新版生命周期
getDerivedStateFromProps和getSnapshotBeforeUpdate不常用
getDerivedStateFromProps
getSnapshotBeforeUpdate
快照
这个getSnapshotBeforeUpdate返回的值传给了componentDidUpdate,如下
getSnapshotBeforeUpdate案例
作用
diffing算法
虚拟DOM中key的作用
key是虚拟DOM对象的标识,在更新显示时key起着极其重要的作用
React脚手架
安装
第一步 安装库
npm i -g create-react-app
第二步 切换到要创建项目的目录 创建脚手架
create-react-app react_staging
或者这种方式
第三步 启动 yarn start
工程介绍
渲染组件时,包裹这个标签,react可以帮我们检查组件代码的合理性
记录性能
样式模块化
类名冲突了,这个样式会被覆盖,因为最后要汇聚到一个网页上面,所以其他组件如果类名或者id相同,就会导致冲突
rcc
rfc
还在更新,不妨点个收藏