React篇1--JSX语法规则、组件、组件实例的3大特性

一、React简介

React是用于构建用户界面的JavaScript库。

我们一般绘制一个页面有3步:

前两步react不管,react只管第3步。React能让你不去操作DOM,但前两步还是要自己操作。

React解决的问题:

(1)原生JavaScript操作DOM频繁、效率低(DOM-API操作UI)。

(2)使用JavaScript直接操作DOM,浏览器会进行大量的重绘重排。

(3)原生JavaScript没有组件化 编码方案,代码复用率低。

React的特点

(1)采用组件化模式声明式编码 (以前是命令式,比如修改页面上一个盒子的样式,需要js/jquery拿到,.style来改变它的样式;声明式通过语法直接定义盒子的样式,然后React直接帮你去操作DOM改变盒子样式),提高开发效率及组件复用率

(2)在React Native中可以使用React语法进行移动端开发(安卓和iOS)。

(3)使用虚拟DOM+优秀的Diffing算法 ,尽量减少与真实DOM的交互。(虚拟DOM是React操作用的,不是页面上的真实DOM,是放在内存中操作的)

一个例子说明:

原生JavaScript实现

如果在2个数据的基础上新加一个数据,则需要重新执行代码,把三个数据全部重新绘制。

React的实现,

多了一个数据则React虚拟DOM会多一个,React会进行虚拟DOM的比较,没变的虚拟DOM直接拿页面中的真实DOM来用,发生变化的虚拟DOM重新绘制。

二、React的基本使用

2.1 新建虚拟DOM

1.需要加入的js依赖

babel.min.js(可以将ES6转ES5;jsx转js,让浏览器执行)

react.development.js(react的核心库)

react-dom.development.js(react扩展库,帮忙操作DOM)

2.新建文件夹+html文件、加入js依赖

注意:react.development.js需要在react-dom.development.js前面。

说明:

(1)引入react.development.js后全局有React,引入react-dom.development.js后全局有ReactDOM。

(2)VDOM对象不要加引号。

(3)ReactDOM.render()方法是把参数1的组件渲染到参数2的容器上。获取容器也操作了DOM,React就是这个地方需要操作DOM。

看看效果:

2.2 虚拟DOM的两种创建方式

1.使用jsx创建虚拟DOM

与刚才例子的区别:

2.使用js创建虚拟DOM

可以不使用babel.min.js。

说明:

(1)React.createElement()的参数分别为(标签名、标签属性、标签内容)。

这里看不出jsx的优势,可以看一个例子:

需要在里加。

jsx写法:

js写法:

可以看到,jsx对于创建多级的标签更简便。也可以用看起来更直观的写法。

其实jsx最后也会转变为js的写法,但是开发人员不用写了。

2.3 虚拟DOM的数据类型

可以看到虚拟DOM是一般的Object对象。

真实DOM VS 虚拟DOM

加一个debugger查询属性:

总结虚拟DOM:

(1)本质是Object类型的对象(一般对象)。

(2)虚拟DOM比较"轻",真实DOM比较"重",因为虚拟DOM是React内部在用。无需真实DOM上那么多的属性。

(3)虚拟DOM最终会被React转为真实DOM,呈现在页面上。

2.4 JSX语法规则

全称为JavaScript XML,react定义的一种类似于XML的JS扩展语法。

1.定义虚拟DOM,不要写引号。

2.标签里混入javascript表达式时需要加花括号{}。

3.样式的类名指定不要用class,要用className.

效果如下:

报错原因是在JSX里面,如果想应用样式作为类名,用className而不是class。

4.内联样式,要用style={{key:value}}的形式去写

再给span标签加一个样式,使用js一般写法。

style不能用字符串,需要用双花括号。

5.只有一个根标签

6.标签必须闭合

7.标签首字母规则

(1)若小写字母开头,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错。

用一个html没有标签,可以看到内容正常显示:

(2)若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。

定义组件的内容后面讲述。

区分JS语句(代码)和JS表达式

注意:jsx里混入的javascript内容只能是表达式,不能是语句(代码)。

1.表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方。

下面都是表达式:

(1)a

(2)a+b

(3)demo(1)

(4)arr.map()

(5)function rest(){}

(6)console.log()

注意:

(1)arr.map()返回一个数组,例子如下,

(2)function rest(){}也有一个返回值,一个例子如下

2.语句(代码)。下main这些都是

(1)if(){}

(2)for(){}

(3)switch(){case:xxx}

一个例子:动态显示数组数据

目标效果:

根据之前的内容,不能在jsx里写for循环。

但是还有一个问题,浏览器报错了:

key属性是虚拟DOM的唯一标识。

修改如下:

这个写法可能还是有点问题,后面讲述。

2.5 一些概念

1. 模块与模块化

模块:向外提供特定功能的js程序,一般就是一个js文件。

模块化:当应用的js都以模块来编写的,这个应用就是一个模块化的应用。

2.组件和组件化

组件:用来实现局部功能效果的代码和资源的集合(html/css/js/image等)。

组件化:当应用是以多组件的方式实现,这个应用就是一个组件化的应用。

三、面向组件编程

2.1 安装React Developer Tools调试工具

React Developer Tools。

安装:

(1)chrome浏览器->更多工具->扩展程序

(2)点击左上角,打开Chrome网上商店。

搜索react(提供方为facebook)

一般浏览器不能访问,百度网盘链接:https://pan.baidu.com/s/15XwT-CdN9PBwZrkQBbU1Kg

提取码:1hxn

把这个程序加入浏览器:

2.2 组件类型

一个组件包含html、css、js等内容。

2.2.1 函数式组件

上述代码定义了一个函数式组件并使用ReacDOM进行渲染。

效果:

第二个报错是找不到图标:

可以给项目配一个图标,图标名称固定为favicon.ico,放在根目录下,

第一个报错意思是函数内容不能作为组件节点,要改写成标签的形式:

还是报错,因为demo首字母没有大写。

注意:函数组件 是一个函数,但不是开发者的代码调用的,而是react调用的。

补充:jsx的this

在js中的函数的this函数是指window对象。jsx不是,打印结果为未定义:

jsx的代码要经过babel的翻译,babel翻译完后开启了严格模式,禁止自定义的函数指向window。

补充:

定义函数式组件然后选然后,后面会默认执行的内容为,

(1)React解析组件标签,找到自定义组件。

(2)发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转化为真实DOM,随后呈现在页面中。

2.2.2 类组件

1.回忆JS类相关的知识

定义类和创建实例对象:

加上方法(类中的方法不加function关键字):

方法放在了类的原型对象 中,没有放在对象中。

方法中的this是指对象 ,可以获取对象的内容。但也不全对,因为调用函数的方法有很多种,比如call,bind,apply等,这些都会改变方法的this指向。

上述代码虽然是p1实例对象调用的speak(),但是使用call(),this指向call()的参数,所以speak()方法打印不到this的信息。

类可以继承。子类直接继承父类的构造器和方法。

子类也可以有自己的构造器,需要调用super(),且写在最前面。

子类可以直接调用父类的方法:

可以看到,对象没有speak()方法,需要通过原型链查找,先找到类的原型对象,再找到父类的原型对象。

也可以重写父类方法:

可以看到,子类的原型对象和父类的原型都想都有了speak(),子类找到了就不会去找父类。

类组件

类组件的三大特性:类组件一定要继承React.Component类、必须有render()方法,render()方法必须有返回值。

这里有一个问题,类中的方法是实例使用,可是jsx中没有new实例,那render()方法是在哪里调用的?

其实写了一个类组件的组件标签,React解析组件标签,找到组件类,随后new一个实例对象,并通过该实例调用原型上的render方法。将render返回的虚拟DOM转为真实的DOM,随后呈现在页面中。

所以,render中的this是实例对象。可以打印认证。

简单组件VS复杂组件

有state的组件是复杂组件,否则是简单组件。

把数据方法组件的state中,当数据改变(即状态改变),就会重新将虚拟DOM转为真实DOM。

state默认为null。

2.3 组件实例3大核心属性

注意:函数式组件没有实例,没有this,也没有属性。但新版React具有hooks,也可以使用核心属性。

对组件实例的属性赋值写在构造器中。

1.state

一个小例子:

目标效果:

点击页面变成:

实现:

接下来要加上点击事件。

先来回顾一下JS中绑定事件的三种方式。

JS中绑定事件的三种方式

JSX绑定事件

使用按钮3的方式(避免使用document),先加一个有弹窗的点击事件:

报错是因为React把JS中原生属性名都变成了驼峰形式。

浏览器还是会报错,因为onClick的需要是一个函数,而不是字符串。

可以看到,一打开页面就触发了点击事件。

因为在创建Weather组件实例时需要调用render()方法,而需要把demo()的返回值赋给onClick,即调用了demo()。

接下来对类组件中state中的数据isHot进行修改。那用this.state可以获取吗?答案是不能。

注意,在JS中的方法中的this指的是window。

JS中使用严格模式后,方法中的this是未定义的:

JSX中,构造器和render()中使用this.state能获取到是因为这里的this指向的是实例对象。

那如何取呢?可以使用一个变量暂存。

但是这种写法很麻烦。

方法是把类组件外部的事件函数放到类组件的内部。

但是点击还是会报错:

原因是changeWeather()中的this未定义:

因为只有通过实例对象调用的方法的this才生效。在render()中直接写this.changWeather()不属于实例对象调用,为什么呢?

补充:JS类中方法this指向

先来看实例对象调用方法时方法中的this的指向。

再来看看用变量获取对象的方法后再调用方法:

可以看到this的指向未定义。原因是因为"const x = p1.study"根本没用调用study(),是把实例对象的原型对象赋值给变量再调用,再执行方法本质是从原型对象对象。类中的方法局部默认开启严格模式。

再回到刚才的问题。

在render()中将changeWeather()绑定到render()中的h1的onClick上时,其实还未调用changeWeather(),只是绑定了原型对象上的changeWeather(),点击时触发方法调用是直接调用,不是实例对象调用,类中方法默认开启严格模式,不是实例对象调用this未定义。

解决方法如下:

上图红框中的代码的作用是在初始化实例对象(调用构造器)时给实例对象加一个changeWeather方法,该方法中的this指向实例对象(bind方法可以返回一个方法和改变方法中this的指向,注意bind方法没有调用函数 )。所以"this.changeWeather=this.changeWeather.bind(this)"等号左边的this.changeWeather是实例对象的,由render()方法调用(点击事件触发调用),而等号右边的this.changeWeather是原型上的。

可以看到,成功获取到实例对象,该实例对象有一个changeWeather方法(原型上也有)。在点击后触发事件调用的是实例对象上的changeWeather方法。

获取到了就可以修改state中isHot的值:

实际上这样写是没效果的。原因是state中的数据不能直接更改,直接更改React不认可,需要使用内置API--setState(React.Component中定义的方法)去更改。

这样就达到点击切换天气文字的效果。

再来说一些setState的细节问题。若是state中对象有多个字段。

使用setState()参数为一个新的对象,但是不是用新对象完全覆盖旧对象(this.setState({isHot:!isHot})执行后原来state中的wind还是存在,只是更新了isHot属性的值。),即setState()的更新是一种合并操作。

另外,每一次调用setState()后React都会帮忙调用render()重新渲染,达到改变state中的数据后页面内容自动重新渲染。

state的简写方式

功能已经实现了,但是代码 还是可以精简一下。

补充:JS中类的方法

类中除了构造器和方法,还可以写赋值语句,作用是新增或修改所以类的实例对象的属性值。

回到刚才说精简代码的问题。

这样所有实例对象的state都有相同的默认值并且都有changeWeather方法。

但是方法还需要修改,因为这样写的话方法中的this没法指向实例对象。需要改成箭头函数。

原因是箭头函数的this若是未定义,则会去找外侧的this,这里是实例对象。

精简后的代码为:

另外注意:state中的数据只能是对象。

2.props

之前的数据都是在构造器或者类中写死的,若是要从组件外部往组件内部传数据 写到state中,则需要使用组件的属性props。

我们知道html中的标签可以给属性赋值,类组件对象也可以。

使用props的例子:

props批量数据传递

使用React中props批量传递数据的简写方式:

注意:这种方式要求传递的字段名与组件中使用的字段名相同。

给props传递数值信息时需要去掉引号加花括号。

对props进行限制

我们可以对props中的对象的字段进行不同的限制,比如类型、默认值、非空等。

需要先引入一个依赖prop-types.js(下载到项目目录中):

说明:

(1)ProTypes的string表示是字符串,number表示是数字,isRequired表示非空,func表示函数。

另外注意:props是只读的,只允许读,不允许改

props的简写方式

类组件构造器中的props

之前说过,构造器有一个默认参数props。重写类组件的构造器时需要在最前面加上super(props)。

函数组件使用props

函数组件没有实例,this未定义,不能使用state和ref属性,但是可以使用props属性。因为函数可以接受参数。

3.ref

用一个例子来展示。

效果:

可以看出这种写法很麻烦,需要使用document。

React的写法是使用ref属性。

字符串形式的ref(不推荐)

给组件中render()的html标签加一个ref属性后,会保存到实例对象的refs属性(key为ref的值,value为标签名)中:

react中使用this.refs可以直接拿到节点:

注意:这里refs拿到的节点不是虚拟DOM,是真实DOM。

拿到节点后可以获取组件的属性:

接着实现右边的效果:

回调形式的ref

可以看到,箭头函数的参数是节点本身。

另外,如果ref回调函数是以内联函数的方式(箭头函数是内联函数)定义的,则在更新操作会被调用两次。是因为在每次渲染时都会创建一个新的函数实例,所以react清空旧的ref并且设置新的。

也可以通过将ref的回调函数定义成class的绑定函数 的方式可以避免上述问题:

但是两种方式采用哪种无关紧要,常用内联函数的形式。

createRef

React.createRef()返回一个容器,该容器可以存储被ref所标识的节点。

上面的写法直接将带ref属性的input节点存储到this.myRef中。

先看看myRef的结构:

可以看到,通过current获取属性值。

注意:一个容器只能放一个节点。

相关推荐
copyer_xyf1 小时前
Python venv 虚拟环境
前端·后端·python
无聊的老谢1 小时前
Vue 3 + TypeScript 构建大型电信运维平台的前端架构设计
前端·vue.js·typescript
xiaofeichaichai2 小时前
Map / Set / WeakMap / WeakSet
前端·javascript
李可以量化2 小时前
成交量的终极量化策略:价量共振指标完整实现(下篇)
前端·数据库·人工智能
copyer_xyf3 小时前
Python 如何同时做很多事:进程、线程、协程
前端·后端·python
gqk013 小时前
Delegate.Target/ Method
前端·ui·xhtml
有梦想的程序星空4 小时前
【环境配置】Vue3项目离线化本地部署echarts全攻略
前端·javascript·vue·echarts
IT_陈寒4 小时前
被Vite的动态导入坑了一整天,原来问题出在这
前端·人工智能·后端
薛先生_0994 小时前
vue-路由重定向
前端·javascript·vue.js