本文档为视频学习笔记、需结合哔哩哔哩尚硅谷react教程视频阅读使用
react简介
react是干什么的
react是用于构建界面的js库——只关注视图
以往展示信息的步骤
发送请求获取数据
处理数据
操作dom进行渲染
react只关注第三步。前两部还需要之前的知识去做,react只帮忙操作dom。
——react是一个将数据渲染为html视图开源js库
react的好处
原生js的痛点
原生js操作domfan所、效率低(通过dom-api操作ui)
使用js直接操作dom,浏览器会进行大量的重绘重排
原生js没有组件化的编码方案,代码复用率低
react的特点
-
采用组件化模式、声明式编码,提高开发效率以及组件复用率。
以前用的是命令式编程,也就是每一步都要自己做(获取dom——改变颜色),但是声明式编码只需要说明这个dom应该是什么颜色。
在react native中可以使用react语法进行移动端开发。
-
使用虚拟DOM+优秀的Diffing算法,尽量减少与真实DOM的交互。
虚拟dom存放在内存中。
react使得数据变化时,浏览器只需要更新变化的部分,不需要整个重绘
diffing算法是用来进行虚拟DOM比较的
学习react之前要掌握的js基础知识
1.判断this指向
2.Class类
3.Es6语法规范
4.Npm包管理器
5.原型、原型链
6.数组常用方法
7.模块化
hello_react案例
react中js文件作用
babel.js用于es6转es5,在react时用于jsx转js
react.development.js是react核心库
react-dom.development.js是react的扩展库。用于操作dom
通过脚手架搭建一个react项目
通过引入js文件搭建一个新的react项目
新建一个文件夹hello_react。用vscode打开
在hello_react新建一个js文件夹,将babel.js、react.development.js、react-dom.development.js拖拽到这个js文件夹下面。
注意点
1. <script type="text/babel">
Type是text/babel
2. <script type="text/javascript" src="../js/react.js"></script>
<script type="text/javascript" src="../js/react-dom.js"></script>
先引入react,再引入react-dom
3. //1.创建虚拟dom
const VDOM=<h1>Hello,React</h1>//不写引号,因为不是字符串
虚拟dom不写引号,相当于直接放进去
4. ReactDOM.render(VDOM,document.getElementById('test'))
Reder语法
虚拟DOM的两种创建方式
-
用jsx创建虚拟dom
const VDOM=<h1 id="title">Hello,React</h1>//不写引号,因为不是字符串
原生js创建虚拟dom
-
原生的js不需要引入babel
<script type="text/javascript" src="../js/react.js"></script>
<script type="text/javascript" src="../js/react-dom.js"></script>
所以script type也回归text/javascript
<script type="text/javascript">
-
用React.creatElement()函数来创建虚拟DOM
const VDOM=React.createElement(标签名,标签属性,标签内容)
示例如下:
const
VDOM=React.createElement('h1',{id:'title',className:'tclass'},'hello,React')//不写引号,因为不是字符串
示例2:
const
VDOM=React.createElement('h1',{id:'title',className:'tclass'},React.createElement('span',{},'he11llo,React'))//不写引号,因为不是字符串
4.虚拟DOM与真实DOM
1.虚拟DOM就是一个普通的JS对象
2.虚拟DOM比较轻,真实DOM上面的属性比较多。因为虚拟DOM是react内部使用,无需那么多属性。
3.虚拟DOM会被react转化为真实DOM,渲染到浏览器中。
5.JSX的语法规则
Js+xml js的一种扩展语法 标签名任意:html标签或其他标签
作用
用来简化虚拟DOM的创建
Jsx语法
定义虚拟DOM的时候不能写引号
标签中混入js表达式时要用花括号,例如:
const myId='TiTLEd'
const myContent='hEllO,ReaCT11111'
//1.创建虚拟dom
const VDOM=(
<h1 id={myId.toLowerCase()}>
<span>{myContent.toLowerCase()}</span>
</h1>
)
-
js里面的样式类名不要用class(因为es6里面class是类,重复了)
要用className
-
内嵌的css样式,要用双括号括起来{{}}
外层{}表示接下来要加入的是js语句
内层括号是因为style的值一般是键值对,是一个对象
background-color 要用backgroundColor
font-size要用fontSize ——小驼峰
const VDOM=(
<h1 id={myId} style={{backgroundColor:'black'}}>
<span
style={{color:'yellow',fontSize:'15px'}}>{myContent.toLowerCase()}</span>
</h1>
)
虚拟dom只能有一个根标签
标签必须闭合
-
标签首字母
小写字母开头,会将标签转换成html中的同名标签。如果html里面没有同名标签,则报错
如果是大写字母开头,React就会渲染对应的组件,如果没有定义,就会报错
6.JSX小练习
1.VDOM中放入数组会直接遍历
2.可以通过{{}}放入表达式,但是不能放入语句或者代码。
什么是表达式:
表达式是会产生值。可以放在任何一个需要的地方
a a+b a==b demo()return undefined arr.map() function tese(){}
但是语句往往是一个操作。或者说控制代码走向。
If(){} for(){} switch(){case: xxx}
3.每个child要有一个唯一的key
7.组件和模块
模块:即为js模块,拆分js
组件:全都拆—— 一拆到底
模块化、组件化工程化
安装插件
下载安装包——解压——加载已解压程序
当打开用react写的网页的时候,reactDeveloperTools的图标才会亮起,红色表示该网站尚未打包,蓝色表示已经打包了。
F12开发者模式中,会多出两个选项components和profiler。前者用于查看组件、数据等。后者用于查看网站性能,入组件加载速度等。
本文档为视频学习笔记、需结合哔哩哔哩尚硅谷react教程视频阅读使用
9.组件之函数式组件
一个组件中应当包括,结构、样式、行为、资源(html,css,js,资源)
函数式组件
首字母必须大写
渲染时要写成标签渲染进去的形式
<div id="content">79789 </div>
<script type="text/javascript" src="../js/react.js"></script>
<script type="text/javascript" src="../js/react-dom.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
function Demo(){
return <h2>我是用函数定义的组件</h2>
}
//渲染组件到页面
ReactDOM.render(<Demo />,document.getElementById("content"))
</script>
babel经过翻译之后,默认开启严格模式,严格模式下,Demo里面的this不指向window。是个undifined
执行ReactDOM.render(<demo /> , …….)之后发生了什么
- React解析组件标签,找到Demo组件
② 发现组件是使用函数定义的,随后调用该函数,把返回的虚拟dom转换成真实DOM渲染到页面上
复习类的相关知识
1.基本创建
1.用class创建关键字
<script type="text/babel">
//用关键字class创建一个类
class Person{
}
//创建一个类的实例对象
const p1=new Person();
console.log(p1);
</script>
用构造函数实例化类
输出结果为:
前面的Person表示的是这个类由谁实例化出来的。
后面的{ }就是我们的p1,打印出的对象p1
2.构造器方法constructor()。该函数中的this指向实例对象
<script type="text/babel">
//用关键字class创建一个类
class Person{
//构造器方法
constructor(name,age){
this.name=name;
this.age=age;
}
}
//创建一个类的实例对象
const p1=new Person("make",18);
const p2=new Person("jane",19);
console.log(p1);
console.log(p2);
</script>
输出结果:
3.类的一般方法(除了构造器方法以外的方法)
-
类里面的一般方法的前面不写function关键字
-
方法中的this指向的是对象
类的一般方法,放在原型对象上。供实例使用
通过person实例调用speak时,speak中的this指向就是person的实例对象
-
<script type="text/babel">
let name = "pppp";
//用关键字class创建一个类
class Person{
//构造器方法
constructor(name,age){
this.name=name;
this.age=age;
}
//一般方法
speak(){
console.log(我的名字是${name},我的年龄是${this.age}
)
}
};
//创建一个类的实例对象
const p1=new Person("make",18);
const p2=new Person("jane",19);
console.log(p1);
console.log(p2);
p1.speak();
p2.speak();
</script>
注意这里的名字输出出来是ppp不是make
4.原型链
当调用了属性不存在的属性或者方法,会沿着原型链去找,看他的原型链上有没有谁有这个属性或者方法。
<script type="text/babel">
let name = "pppp";
//用关键字class创建一个类
class Person{
//构造器方法
constructor(name,age){
this.name=name;
this.age=age;
}
//一般方法
speak(){
console.log(我的名字是${name},我的年龄是${this.age}
)
}
};
//创建一个类的实例对象
const p1=new Person("make",18);
const p2=new Person("jane",19);
console.log(p1);
console.log(p2);
p1.speak();
p2.speak();
</script>
比如这里,p1和p2的speak都是原型上的方法。
所以在proto里面去找
类的继承
-
什么时候不用写构造器?
当构造方法与父类一致的时候
-
什么时候要写构造器
与父类不一致,或者有新的东西要添加的时候
-
必须调用super
定义一个类child继承parent的时候,并且要在child里面写构造函数的时候,必须调用super
例如:
class Person{
//构造器方法
constructor(name,age){
this.name=name;
this.age=age;
}
//一般方法
speak(){
console.log(我的名字是${name},我的年龄是${this.age}
)
}
};
//创建一个Student类集成Person
class Student extends Person{
constructor(name,age,grade){
this.name=name
this.age=age
this.grade=grade
}
}
会报错说你没使用super。、
正确写法:
class Student extends Person{
constructor(name,age,grade){
super(name,age)
this.grade=grade
}
}
也就是说supre相当于调用父类的构造函数。
注意:super要放在最前面
-
原型链的查找
在上面的例子中,实例化一个sd并调用sd.speak()方法,会发现调用成功。
const sd=new Student("张三","15","高一");
sd.speak();
其查找逻辑为:
首先sd这个对象它自己没有speak方法,所以要找它的原型也就是Student类。Student类也没有speak方法,所以只能继续沿着原型链向上找,直到找到Person
- 子类重写从父类继承的方法
//创建一个Student类集成Person
class Student extends Person{
constructor(name,age,grade){
super(name,age)
this.grade=grade
}
speak(){
console.log(我的名字是${this.name},我的年龄是${this.age},我正在读${this.grade}
)
}
}
const sd=new Student("张三","15","高一");
sd.speak();
这里会直接调用Student里面的speak()函数
总结
类中的构造器不是必须写的。要对实例进行一些初始化的操作,如添加指定属性时才写。
如果Child类继承了Parent类。而且Child类中写了构造器,那么Child类构造器中必须要调用super
类中所定义的方法,都是放在类的原型对象上。供实例使用。
11类式组件(适用于复杂组件的定义)
创建类式组件
函数式组件中函数名就是组件名
类式组件中类名就是组件
1.必须继承React.Component
2.内部必须有render()
//创建类式组件
class MyComponent extends React.Component{
render(){
return <h2>我是类定义的组件</h2>
}
}
//渲染类式组件
ReactDOM.render(<MyComponent />,document.getElementById("content"))
3. 执行ReactDOM.render(<demo /> , …….)之后发生了什么
①React解析组件标签,找到MyComponent组件
②发现组件是类定义的,随后new出该类的实例对象,并通过该实例调用原型上的render()方法。
③将render()返回的虚拟DOM转化成真实DOM渲染到页面上。
④render中的this指向的是React帮忙创建的这个实例对象——MyComponent组件实例对象
12.对state的理解——组件实例的三大核心属性之一
什么是简单组件。什么是复杂组件:
有state(状态)的是复杂组件,没有state的是简单组件。
组件的状态state驱动什么:
组件的状态里面存着数据,数据的变化驱动着页面展示的改变。
State在组件的实例上
所以只有class可以有。函数式组件都没有实例。另外两大核心属性也是一样
13.初始化state
想对类的实例对象进行衣蛾写初始化的操作,比如增加一个属性或者修改一个属性的值,就需要借助类里面的构造器了。
14.react中的事件绑定
1.原生事件绑定
1.addEventListener
onclick
内嵌事件函数
<button id=”btn1” click=”onclick()”></button>
2.react中的事件绑定
建议使用内嵌事件函数
①首先onclick要写成onClick
②首先不能用引号
render(){
const {isHot}=this.state;
return <h2 onClick="btn()">今天天气很{isHot?"炎热":"凉爽"}</h2>
}
这是错的
然后不能加小括号,因为时把btn()返回值给onclick了,为undefined
render(){
const {isHot}=this.state;
return <h2 onClick={btn()}>今天天气很{isHot?"炎热":"凉爽"}</h2>
}
这也是错的
以下这样才正确:
render(){
const {isHot}=this.state;
return <h2 onClick={btn}>今天天气很{isHot?"炎热":"凉爽"}</h2>
}
15.类中的方法中的this
class MyComponent extends React.Component{
constructor(props){
super(props)
this.state={
isHot:false
}
}
render(){
const {isHot}=this.state;
return <h2 onClick={this.btn}>今天天气很{isHot?"炎热":"凉爽"}</h2>
}
btn(){
console.log(this)
}
}
打印:undefined
①btn()定义在类中,也即是放在MyComponent的原型对象上。供实例使用
②由于btn时作为onClick的回调,也就是说只是见到那的赋值给了onClick。所以这时再使用onclick已经不是通过实例对象调用,而是直接对存放在堆里面的方法btn进行调用。所以this应该指向window
③但是,但是,由于类里面的方法默认开启严格模式,严格模式的代码块中,this禁止指向window。所以this为undefined
16.解决类中的this的指向问题、
Render()中的this指向是组件的实例对象。
在构造函数中:
constructor(props){
super(props)
this.state={isHot:false}
this.btn=this.btn.bind(this)
}
①先看右边。构造器中的this指向的是实例对象,找实例对象的btn()方法;
②但是函数挂载在原型对象上,所以实例对象中此时此刻还没btn()函数,顺着原型链找到原型对象的btn()函数。通过bind函数传递进去this。
③这个this指向谁呢?还是那句话,构造器中的this指向的是实例对象。所以这个bind会返回一个和btn()函数一样的函数,区别只在于函数内部的this指向了实例对象
④看左边,赋值给了挂载在实例对象上的btn()函数。如此一来,每次通过实例对象调用的函数btn()是直接调用的函数左边的这个。找到了,自然也不会去原型链里面找了。
本文档为视频学习笔记、需结合哔哩哔哩尚硅谷react教程视频阅读使用
17.setState的使用
状态state不能直接更改,而是要使用一个api明教setState进行更改,React才会认可,才会帮我们刷新页面
18.简写方法
1.构造方法的简写
类里面可以直接写赋值语句,例如:
class MyComponent extends React.Component{
a=1
}
前面不用写let之类的,直接写赋值语句
含义为:在该类的实例对象上追加一个属性,属性名为a,值为1
注意:是实例对象,而不是原型对象,这个变量实际挂载在实例对象身上,原型对象身上没有
但是传递进来的数据需要构造器来接
类里面可以写的东西有:构造器、render()、一般方法、赋值语句
2.bind函数的省略
//创建类式组件
class MyComponent extends React.Component{
constructor(props){
super(props)
this.state={isHot:false}
}
render(){
const {isHot}=this.state;
return <h2 onClick={this.btn}>今天天气很{isHot?"炎热":"凉爽"}</h2>
}
btn=()=>{
const isHot=this.state.isHot;
this.setState({isHot:!isHot});
}
}
//渲染类式组件
ReactDOM.render(<MyComponent />,document.getElementById("content"))
注意这一部分:
btn=()=>{
const isHot=this.state.isHot;
this.setState({isHot:!isHot});
}
①首先这个btn不再是一个声明函数,而是一个赋值语句。根据上面一小节我们知道,类里面的赋值语句的含义是,给实例对象追加一个属性btn,它的内容是一个函数
②也就是说这个btn是挂载在实例对象身上的
③btn是一个箭头函数,箭头函数内部没哟自己的this,从上下文捕获this。
④btn追加在实例对象上,所以这个this指向的是实例对象。
⑤所以之前的bind函数就可以省略掉了,btn()函数此时不会丢失this了。效果不变
3.总结:
类的经典格式:
class MyComponent extends React.Component{
state={isHot:false}
render(){
const {isHot}=this.state;
return <h2 onClick={this.btn}>今天天气很{isHot?"炎热":"凉爽"}</h2>
}
btn=()=>{
const isHot=this.state.isHot;
this.setState({isHot:!isHot});
}
}
初始化状态state={}
不用放在构造器中,直接直接写state={isHot:false}作为代替
类中的一般属性都要写成
赋值语句+箭头函数的形式
19.state总结:
①state的值是对象,包含多个key:value
②组件被称为状态机,通过更新组件的state来更新与渲染对应组件
③组件中的render的方法中的this指向实例对象
④组件中自定义的一般方法中的this为undefined
解决方法:
1.强制绑定bind
2.赋值语句+箭头函数
⑤状态数据必须用setState
20.props的基本使用
如何传递
ReactDOM.render(<Child name="123" />,document.getElementById("content"))
以这种形式书写,react会直接把name和123作为key:value对 传给组件的实例对象,(注意类比原生html的属性书写方法),存放在它的props里面。
在render中打印出this,如图可以看到props里面已经有了
如何拿到并使用
class Child extends React.Component{
render(){
console.log(this);
return(
<ul>
<li>姓名{this.props.name}</li>
<li>性别{this.props.sex}</li>
<li>年龄{this.props.age}</li>
</ul>
)
}
}
ReactDOM.render(<Child name="123" age="456" sex="789"/>,document.getElementById("content"))
直接用this.props.name
简化一下:
render(){
console.log(this);
const {name,sex,age}=this.props
return(
<ul>
<li>姓名{name}</li>
<li>性别{sex}</li>
<li>年龄{age}</li>
</ul>
)
}
21.批量传递props(批量传递标签属性)
不定参数的方法:
const p={name:'1773',sex:'***',age:'+++'}
ReactDOM.render(<Child {...p}/>,document.getElementById("content"))
要求同名
展开运算符…的知识复习:
1.对数组使用——展开数组
将数组按展开
let arr1=[1,3,5,7,9]
console.log(...arr1)
console.log(arr1)
2.最数组使用——链接两个数组
let arr1=[1,3,5,7,9]
let arr2=[2,4,6,8,10]
let arr3=[...arr1,...arr2]
console.log(arr3)
相当于把arr1和arr2展开放在一起,然后赋值给arr3
3.函数传参——不定参数
4.不能展开对象——babel+react可以
原生情况下展开运算符不允许展开对象
React+babel中,可以展开对象———但是只能用于标签属性的传参
5.知识复习——如何复制一个对象
let person={name:'123',age:'456'}
let person2=person
这种形式只是一个引用的传递,而不是复制对象的本身。
当person内部的值变化的时候,通过person2获取到的值也变化了。
应该这样:
let person={name:'123',age:'456'}
let person2={...person}
如此一来,person2只是初始值和person一样。之后就各是各的了
注意一定外面要加{},因为原生状态下展开运算符不能展开对象,这里是在构建对象字面量
6.构建对象字面量的同时修改其属性值
let person={name:'123',age:'456'}
let person2={...person}
let person3={...person2,name:"老王",address:"隔壁"}
console.log(person2)
console.log(person3)
22.为React添加props的规则限制
使用propsTypes
class Child extends React.Component{
render(){
console.log(this);
const {name,sex,age}=this.props
return(
<ul>
<li>姓名{name}</li>
<li>性别{sex}</li>
<li>年龄{age}</li>
</ul>
)
}
}
Child.propTypes={
name:PropTypes.string
}
const p={name:789798798,sex:'***',age:'+++'}
ReactDOM.render(<Child {...p}/>,document.getElementById("content"))
①首先,porpTypes要写在类的外面,否则报错(这个在下一节会处理)
②严格按照这个格式来写:
Child.propTypes={
name:PropTypes.string
}
外面Child.propTypes的p小写,里面PropTypes.string的p大写,string这里的s也要小写。
③前面要引入prop-type.js
④react的版不能太旧。
isRequired
限制名字为String且为必填项:
Child.propTypes={
name:PropTypes.string.isRequired
}
defaultProps
限制性别为String型,且默认值为“不男不女”
没填性别的话,性别就默认为 不男不女
Child.propTypes={
sex:PropTypes.string
}
Child.defaultProps={
sex:"不男不女"
}
传一个函数:
Child.propTypes={
sex:PropTypes.string,
speak:PropTypes.func
}
注意这里要写func,不能写function
23.props简写
Props是只读的
不能修改
把propTypes放到class内部里面去
class Child extends React.Component{
static propTypes={
sex:PropTypes.string,
speak:PropTypes.func
}
static defaultProps={
sex:"不男不女"
}
render(){
console.log(this);
const {name,sex,age}=this.props
return(
<ul>
<li>姓名{name}</li>
<li>性别{sex}</li>
<li>年龄{age}</li>
</ul>
)
}
}
注意要在前面加一个static
因为下面这种写法是给类的实例对象添加属性
class Child extends React.Component{
propTypes={
sex:PropTypes.string,
speak:PropTypes.func
}
defaultProps={
sex:"不男不女"
}
}
必须要加上static才能把属性直接加载原型对象上。
24.类组件中的构造器与props
①构造函数完全可以不写
②如果要写构造器函数,要接props,并且调用super(props)
形如:
constructor(props){
super(props)
}
否则构造器内部的this.props为undifined。无法通过this.的形式找到props
一般来说用不到。构造器能省就省略
25.函数式组件使用props
function Child(props){
const {name,sex,age}=props
return(
<ul>
<li>姓名{name}</li>
<li>性别{sex}</li>
<li>年龄{age}</li>
</ul>
)
}
const p={name:"8",age:'+++',speak:p}
ReactDOM.render(<Child {...p}/>,document.getElementById("content"))
<Child {...p}/>传进来的值会默认被收集成一个对象,并将这个对象作为函数组件的参数传进去 func Child(props){
给函数式组件设置propType
Child.propTypes={
sex:PropTypes.string
}
Child.defaultProps={
sex:"不男不女"
}
直接写在函数的外面。