React(一)
- 一、初识React
- 1.简单介绍
- 2.React的三个依赖
- 3.Hello React案例
- 二、类组件
- 1.定义类组件并渲染
- 2.绑定事件函数(奇怪的this问题)
- 3.数组形式数据的展示(电影案例)
- 4.计数器案例
- 三、jsx语法详解
- 1.jsx的书写规范
- 2.jsx的注释
- 3.jsx嵌入变量
- 3.jsx嵌入表达式
- 4.jsx绑定属性
一、初识React
1.简单介绍
React是什么?用于构建用户界面的 JavaScript 库
React的官网文档:https://react.docschina.org/
React的特点:
1、声明式编程:声明式编程是目前整个大前端开发的模式:Vue、React、Flutter、SwiftUI;
2、组件化开发
3、多平台适配
2.React的三个依赖
1、react:包含react所必须的核心代码
2、react-dom:react渲染在不同平台所需要的核心代码
3、babel:将jsx转换成React代码的工具
依赖的引入有很多方式:
1、直接引入CDN连接
2、下载后添加本地依赖
3、脚手架npm引入
前期的学习中先在html中使用CDN引入(引入React18)
<!-- 引入三个依赖的库 -->
<!-- 1.引入react核心代码库 -->
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<!-- 2.引入渲染核心代码库 -->
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<!-- 3.引入将jsx转换成react代码的工具库 -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
3.Hello React案例
同样作为MVVM结构的框架,React在渲染DOM的时候也有自己的方式
这里要注意,在React18版本前后,我们的写法是不一样的:
- React18之前使用
ReactDOM.render(html结构或组件,容器)
- React18之后使用
ReactDOM.createRoot(容器).render(html结构或组件)
<!-- 准备好一个容器 -->
<div id="root"></div>
<script type="text/babel">
//渲染Hello World
//React18之前使用ReactDOM.render
// ReactDOM.render(<h2>Hello World</h2>, document.querySelector('#root'));
//React18之后
const root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(<h2>Hello World</h2>);
</script>
接下来是我们的点击按钮切换信息的Hello React案例完整版
<!-- 准备好一个容器 -->
<div id="root"></div>
<script type="text/babel">
//渲染Hello World
//1.定义一个变量来存储信息
let message = 'Hello Word!';
//React18之前使用ReactDOM.render
//ReactDOM.render(<h2>Hello World</h2>, document.querySelector('#root'));
//React18之后
//2.定义容器
const root = ReactDOM.createRoot(document.querySelector('#root'));
//3.先调用一次渲染函数,渲染页面
rootRender();
//下面是预先定义好的函数
//函数一:渲染函数
function rootRender() {
root.render(
<div>
<h2>{message}</h2>
<button onClick={changeMsg}>点击修改信息</button>
</div>
);
}
//函数二:按钮的点击事件触发函数
function changeMsg() {
message = 'Hello React!';
console.log(message); //函数调用了,但是没有重新渲染
rootRender(); //修改完后重新渲染页面
}
</script>
总结,这里和Vue对比有几个不一样的地方:
1、定义容器并渲染的过程,重新渲染要自己调用渲染函数,不像Vue会自动更新页面
2、读取变量使用单括号{ name }
,不像Vue用双括号{{}}
3、html的结构要作为参数传到render
函数中,不像Vue直接编辑html结构
4、点击事件使用onClick={函数名}
,而Vue是v-on:click="函数名"
二、类组件
在React中有两种定义组件的防止,一种是类组件,另一种是函数组件,这里我们使用ES6的类来声明组件,把刚才的Hello React
案例用组件的方式呈现出来。
1.定义类组件并渲染
1、定义类组件,继承React.Component,并继承父类的属性
<!-- 准备好一个容器 -->
<div id="root"></div>
<script type="text/babel">
//1.定义类组件
class App extends React.Component {
constructor() {
super();
}
}
</script>
2、添加一个state属性,用来存储数据,名字必须叫state
<!-- 准备好一个容器 -->
<div id="root"></div>
<script type="text/babel">
//1.定义类组件
class App extends React.Component {
constructor() {
super();
this.state = {
message: 'hello world',
}
}
}
</script>
3、定义一个render渲染函数,名字必须叫render,当我们拿到容器渲染组件时,就会自动调用这个函数,返回相应的html节点
<!-- 准备好一个容器 -->
<div id="root"></div>
<script type="text/babel">
//1.定义类组件
class App extends React.Component {
constructor() {
super();
this.state = {
message: 'hello world',
}
}
render() {
return (
<div>
<h2>{this.state.message}</h2>
<button>点击切换信息</button>
</div>
)
}
}
</script>
4、拿到容器,渲染组件
<!-- 准备好一个容器 -->
<div id="root"></div>
<script type="text/babel">
//1.定义类组件
class App extends React.Component {
constructor() {
super();
//1.1添加一个state属性存储数据,名字不能改,必须叫state
this.state = {
name: 'zzy',
message: 'Hello World!',
}
}
//1.3事件函数
//1.2渲染函数,名字不能改,必须叫render
render() {
return (
<div>
<h2>{this.state.message}</h2>
<button onClick={this.changeMsg}>点击修改信息</button>
</div>
)
}
}
//2.拿到容器,渲染组件
let root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(<App/>);
</script>
2.绑定事件函数(奇怪的this问题)
这里有一个this 的问题,先复习一下严格模式的东西:严格模式
这里要知道:ES6类中的函数都会默认开启严格模式(this指向undefined)
class App {
constructor(name) {
this.name = name;
}
changeMsg() {
//这里其实默认开启了严格模式
console.log(this);
}
}
//搞清楚this的问题
let app = new App();
let out = app.changeMsg;
out(); //undefined
也就是说,只有类的实例调用类原型上的方法时,this才有指向(指向实例)
这里我们可以看到,直接给按钮绑定了一个事件函数,那么在调用的时候,调用者不是实例,this肯定不指向实例。
调用这个函数就是调用类中的函数,那么this就是undefined
,可是这样我们怎么顺着原型链访问setState
进而对数据进行更改呢?其实很简单,只需要在constructor
中改变事件函数的this指向:
//1.4保存事件函数this指向
this.changeMsg = this.changeMsg.bind(this);
<!-- 准备好一个容器 -->
<div id="root"></div>
<script type="text/babel">
//1.定义类组件
class App extends React.Component {
constructor() {
super();
//1.1添加一个state属性存储数据,名字不能改,必须叫state
this.state = {
name: 'zzy',
message: 'Hello World!',
}
//1.4保存事件函数this指向
this.changeMsg = this.changeMsg.bind(this);
}
//1.3事件函数
changeMsg() {
console.log(this);
this.setState({
message: 'Hello React!',
})
}
//1.2渲染函数,名字不能改,必须叫render
render() {
return (
<div>
<h2>{this.state.message}</h2>
<button onClick={this.changeMsg}>点击修改信息</button>
</div>
)
}
}
//2.拿到容器,渲染组件
let root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(<App/>);
</script>
setState这个方法做了两件事:1、更改数据 => 2、重新渲染
3.数组形式数据的展示(电影案例)
方法1:遍历生成li,然后统一放到一个数组里,然后把数组直接展示
<!-- 准备好一个容器 -->
<div id="root"></div>
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
movies: ['星际争霸', '魔兽世界', '流浪地球', '奥里给']
}
}
render() {
//1.遍历展示数组的方法1
let lis = [];
for(let i = 0; i < this.state.movies.length; i++) {
let movie = this.state.movies[i];
let li = <li>{movie}</li>;
lis.push(li);
}
return (
<div>
<h2>电影名字</h2>
<ul>
{lis}
</ul>
</div>
)
}
}
let root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(<App/>);
</script>
方法2:使用map遍历数组,返回一个修改后的新数组
render() {
//2.方法2,直接映射
let lis = this.state.movies.map(el => <li>{el}</li>)
return (
<div>
<h2>电影名字</h2>
<ul>
{lis}
</ul>
</div>
)
}
4.计数器案例
点击加一或减一
<div id="root"></div>
<script type="text/babel">
const root = ReactDOM.createRoot(document.querySelector('#root'));
class App extends React.Component {
constructor() {
super();
this.state = {
count: 0,
}
//改变this的指向
this.add = this.add.bind(this);
this.subtract = this.subtract.bind(this);
}
render() {
let { count } = this.state;
return (
<div>
<h2>{count}</h2>
<button onClick={this.add}>点击+1</button>
<button onClick={this.subtract}>点击-1</button>
</div>
)
}
//事件函数
add() {
this.setState({
// count: this.state.count + 1
count: ++this.state.count
})
}
subtract() {
this.setState({
count: --this.state.count
})
}
}
root.render(<App/>);
</script>
三、jsx语法详解
我们直接把标签赋值给变量,这种写法在原来的js环境会报错,但是在jsx中(开启了babel)就不会报错,这就是一段jsx语法。
- JSX是一种JavaScript的语法扩展(eXtension)。
- 它用于描述我们的UI界面,并且其完成可以和JavaScript融合在一起使用;
- 它不同于Vue中的模块语法,你不需要专门学习模块语法中的一些指令(比如v-for、v-if、v-else、v-bind);
1.jsx的书写规范
React认为渲染逻辑本质上与其他UI逻辑存在内在耦合。
比如UI需要绑定事件,UI需要展示数据,当数据变化时UI又要更新;所以React没有把它们单独分开,而是封装成组件。那么jsx有什么书写规范呢?
1、JSX的顶层只能有一个根元素,所以我们很多时候会在外层包裹一个div原生(或者使用后面我们学习的Fragment);
render() {
let { count } = this.state;
return (
<div>
<h2>{count}</h2>
<br/>
</div>
)
}
2、为了方便阅读,我们通常在jsx的外层包裹一个小括号(),这样可以方便阅读,并且jsx可以进行换行书写;
3、JSX中的标签可以是单标签,也可以是双标签; 注意:如果是单标签,必须以/>
结尾,在html中不用/也行,但在这里会报错;
2.jsx的注释
在大括号中使用/**/
包裹注释
render() {
let { count } = this.state;
return (
<div>
{/*这是一段注释*/}
<h2>{count}</h2>
</div>
)
}
3.jsx嵌入变量
1、String/Number/Array这三种类型都可以正常显示(其中数组会自己拆开)
constructor() {
super();
this.state = {
name: 'zzy', //String
age: 18, //Number
habits:['吃饭','睡觉'] //Array
}
}
render() {
let { name,age,habits } = this.state;
return (
<div>
{/*这是一段注释*/}
<h2>{name}</h2>
<h2>{age}</h2>
<h2>{habits}</h2>
</div>
)
}
2、undefined/null/Boolean这三种类型默认不会显示,因为有时候我们请求数据如果没请求到,那么肯定不能把undefined显示出来。如果非要显示,请连接字符串或toString()
constructor() {
super();
this.state = {
a: undefined, //undefined
b: null, //null
c: true, //Boolean
}
}
render() {
let { a, b, c } = this.state;
return (
<div>
<h2>{a}</h2>
<h2>{b}</h2>
<h2>{c}</h2>
</div>
)
}
3、Object对象类型作为嵌入变量会报错。
constructor() {
super();
this.state = {
friend: {
name: 'ht',
age: 18,
}
}
}
render() {
let { friend } = this.state;
return (
<div>
<h2>{friend}</h2> {/*报错*/}
</div>
)
}
3.jsx嵌入表达式
1、运算符表达式
constructor() {
super();
this.state = {
firstName: 'zzy',
lastName: 'ht',
}
}
render() {
let { firstName, lastName } = this.state;
return (
<div>
<h2>{firstName + ' ' + lastName}</h2>
<h2>{30 + 20}</h2>
</div>
)
}
2、三元表达式
constructor() {
super();
this.state = {
num: 99,
}
}
render() {
let { num } = this.state;
return (
<div>
<h2>{num > 10 ? 'big' : 'small'}</h2>
</div>
)
}
3、调用函数获取返回值
constructor() {
super();
this.state = {
num: 99,
}
}
render() {
return (
<div>
<h2>{this.getNum()}</h2>
</div>
)
}
getNum() {
return this.state.num;
}
4.jsx绑定属性
比如img元素会有src属性、a元素会有href属性、元素可能需要绑定class、原生使用内联样式style等等,都可以动态去绑定。
1、绑定普通属性
constructor() {
super();
this.state = {
imgUrl: "http://p2.music.126.net/L8IDEWMk_6vyT0asSkPgXw==/109951163990535633.jpg",
link: 'http://www.baidu.com'
}
}
render() {
let { imgUrl, link } = this.state;
return (
<div>
<img src={imgUrl}/>
<a href={link}>百度</a>
</div>
)
}
2、绑定class样式。在React中,一般用className而不用calss(因为class是类的关键字)
.zzy {
color: blue;
font-size: 30px;
}
这里动态绑定类名有两种方式,一种是三元表达式+字符串拼接(或模板字符串),一种是定义一个数组,然后push进去再读取数组。
这里要注意,jsx在属性中引用数组时和在页面上展示数据时不一样:
1、属性中引用是用逗号隔开。
2、页面展示则是直接字符串相连。
constructor() {
super();
this.state = {
active: true,
msg: '动态绑定类'
}
}
render() {
let { msg, active } = this.state;
//动态绑定类名方式一
let className = `zzy ${active ? 'active' : ''}`;
//动态绑定类名方式二
let classArr = ['zzy','ht'];
if(active) classArr.push('active');
return (
<div>
<h2 className='zzy'>{msg}</h2>
<h2 className={className}>{msg}</h2>
{/*2.绑定class*/}
{/*属性动态绑定数组,转为字符串自动逗号隔开*/}
{/*展示部分动态绑定数组,转为字符串自动去掉逗号*/}
<h2 className={classArr.join(' ')}>{msg}</h2>
</div>
)
}
3、绑定style样式,这里要使用对象的形式,属性名采用驼峰命名,这里和我们刚才提到的html内容展示是不一样的,内容展示用对象会报错,而属性绑定这里不会报错,即双括号{{}}
constructor() {
super();
this.state = {
msg: '动态绑定style'
}
}
render() {
return (
<div>
{/*3.绑定style*/}
<h2 style={{color:'blue',fontSize:'36px'}}>{this.state.msg}</h2>
</div>
)
}