文章目录
- 一、初始React
- 1. React的基本认识
- 2. Hello案例
- 2.1 三个依赖
- 2.2 渲染页面
- 2.3 hello案例完整代码
- 二、类组件
- 1. 封装类组件
- 2. 组件里的数据
- 3. 组件里的函数 (重点)
- 4. 案例练习
- (1) 展示电影列表
- 三、JSX语法
- 1. 认识JSX
- 2. JSX书写规范及注释
- 3. JSX嵌入变量作为子元素
- 4. JSX嵌入表达式
- 5. JSX绑定属性
- (1) title,src,href属性
- (2) 绑定class
- (3) 绑定style样式
一、初始React
1. React的基本认识
React是:用于构建用户界面的JavaScript库;
React官网文档:React官网
React的三个特点:
(1) 声明式编程
(2) 组件化开发
和Vue一样,将复杂的页面分解成一个个组件
(3) 多平台适配
2013 React发布之初是开发Web页面
2015 推出ReactiveNative用于开发移动端平台
2017 推出ReactVR,用于开发虚拟显示Web应用程序。
2. Hello案例
2.1 三个依赖
React需要引入三个依赖,
(1) react:包含react所必须的核心代码
(2) react-dom:react渲染在不同平台所需要的核心代码
(3) babel:将jsx转换成React代码的工具
引入的方式有三种
(1) CDN引入
(2) 下载引入
(3) npm下载引入(脚手架)
本案例中采用cdn引入
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- babel -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
拓展:
Babel是目前前端使用非常广泛的编译器;可以将Es6、React JSX语法、TypeScript等语法转化为普通的JavaScript代码,让浏览器认识代码并运行。
2.2 渲染页面
React18版本前后,渲染Dom的写法不同:
React18之前: ReactDOM.render(渲染的内容,容器)
React18之后: ReactDOM.createRoot(容器).render(渲染的内容)
渲染的内容指的是html结构或组件
容器:也就是指定在哪里渲染页面
<div id="root"></div>
<!-- 指定type="text/babel";babel才会解析这里的jsx语法代码 -->
<script type="text/babel">
// 渲染Hello World
// React18之前:
//ReactDOM.render(<h2>Hello World</h2>, document.querySelector("#root"))
// React18之后:
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<h2>Hello World</h2>)
</script>
2.3 hello案例完整代码
<!-- 定义一个容器 -->
<div id="root"></div>
<!-- 添加依赖:三个依赖 -->
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script type="text/babel">
const root = ReactDOM.createRoot(document.querySelector("#root"))
// 1. 定义内容变量
let message = 'Hello World'
// 2. 初始化渲染页面
rootRender()
// 渲染页面函数,为了在后续更新页面时方便渲染,将渲染封装成一个函数
function rootRender () {
root.render((
<div>
<h2>{message}</h2>
<button onClick={btnClick}>修改内容</button>
</div>
))
}
// 按钮监听事件
function btnClick () {
// 1.修改数据
message = "Hello React"
// 2. 重新渲染
rootRender()
}
</script>
总结:
- 读取变量使用单括号
{message}
,不像Vue用双括号{{}}
- 绑定点击事件用
onClick={函数名}
,Vue是@click="函数名"
- 需要自己调用函数进行渲染,Vue是会自动渲染。
- 注意下面这样的写法是错误的,即只渲染部分页面。这样渲染的
<h2>
会将button
覆盖掉
二、类组件
React中组件有两类:类组件和函数式组件;
1. 封装类组件
(1) 定义一个类(继承React.Component
),类名必须大写(类名就是组件名),小写会被认为是HTML元素。
(2) 实现当前组件的render函数:render返回的jsx内容,就是之后React会渲染的内容。
<script type="text/babel">
// 定义组件App
class App extends React.Component {
constructor() {
super()
}
// 组件数据
// 组件方法(实例方法)
// 渲染到界面上,render函数
render () {
return <h2>hello world</h2>
}
}
// 将组件渲染到界面上
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App />) // App根组件渲染到界面上
</script>
执行 root.render(<App />)
语句时,会调用类中的render
方法,进行界面渲染。
2. 组件里的数据
数据分为两类:参与界面更新、不参与界面更新。
参与界面更新的数据也叫参与数据流,定义在当前对象的state中,写在构造函数里。
使用该数据时:this.state.变量名称
修改数据时: this.setState(....)
class App extends React.Component {
constructor() {
super()
// 定义数据
this.state = {
message: "Hello World",
}
}
// 渲染时使用数据
render () {
return (
<div>
<h2>{this.state.message}</h2>
<button>修改内容</button>
</div>
)
}
}
3. 组件里的函数 (重点)
需要提前看一下严格模式里的this指向问题:博主DantinZhang总结的严格模式
在严格模式下,函数在独立调用时(不是通过某个对象调用),this的值为undefined;
babel在编译代码时,自动加上了use strict
,即设置为严格模式;
需要知道:ES6类中的函数都会默认开启严格模式
<script type="text/babel">
class App {
constructor(name) {
this.name = name;
}
btnClick () {
//这里其实默认开启了严格模式,
console.log('btn:', this);
}
}
//搞清楚this的问题
let app = new App();
let out = app.btnClick ;
out(); //这里打印undefined,是因为函数里默认开启严格模式
function fun () {
console.log('fun', this);
}
fun() // 这里打印undefined,是因为babel编译时加了严格模式
</script>
将 type="text/babel"
去掉之后,out()
是类里的函数,还是打印undefined
;而fun()
不是类里的函数,去掉babel后,打印Window。
本案例需要在点击事件的回调函数里修改message
的值
btnClick () {
// 通过this.setState修改message的值
this.setState({
message: 'Hello React'
})
}
问题是:btnClick
里的this
不指向实例对象,指向undefined
解决方式一:
绑定回调函数时,通过bind改变this的指向。
解决方式二:在构造函数里改变this指向;这也是官方推荐的写法。
<!-- 定义一个容器 -->
<div id="root"></div>
<script type="text/babel">
// 1. 定义类组件
class App extends React.Component {
constructor() {
super()
// 1.1 定义组件数据,添加一个state属性存储数据,名字不能改,必须叫state
this.state = {
message: "Hello World",
}
// 1.4 对需要绑定的方法,提前绑定好this
this.btnClick = this.btnClick.bind(this)
}
// 1.3 组件方法(实例方法)
btnClick () {
console.log('btn:', this);
this.setState({
message: 'Hello React'
})
}
// 1.2 渲染函数,名字不能改,就叫render
render () {
return (
<div>
<h2>{this.state.message}</h2>
<button onClick={this.btnClick}>修改内容</button>
</div>
)
}
}
// 2. 将组件渲染到界面上
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App />) // App根组件渲染到界面上
</script>
setState
方法来自于继承的React.Componetn
,其内部完成了两件事:
(1) 将state里的message值改掉;(2) 自动重新执行render函数
4. 案例练习
案例多写几遍,熟悉结构
(1) 展示电影列表
<!-- 定义一个容器 -->
<div id="root"></div>
<script type="text/babel">
// 1. 定义类组件
class App extends React.Component {
constructor() {
super()
this.state = {
movies: ['飞屋环游记', '夏日友情天', '玩具总动员']
}
}
render(){...}
}
// 2. 渲染组件
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App />)
</script>
渲染方式一:循环遍历
render () {
// 遍历展示数据
let lis = []
for (let i = 0; i < this.state.movies.length; i++) {
let ele = <li>{this.state.movies[i]}</li>
lis.push(ele)
}
return (
<div>
<h2>电影名字</h2>
<ul>
{lis}
</ul>
</div>
)
}
渲染方式二:map函数
// 渲染方式二,map
render () {
let lis = this.state.movies.map(item => <li>{item}</li>)
return (
<div>
<h2>电影名字</h2>
<ul>
{lis}
</ul>
</div>
)
}
三、JSX语法
1. 认识JSX
// 1. 定义元素内容
const element = <div>Hello World</div>
// 2. 渲染
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(element)
在js中,将一段html直接赋值给变量element会出现语法错误。而在jsx语法中(开启babel:type="text/babel"
),第2行代码不会报错。
- JSX是一种JavaScript的语法扩展(eXtension),也称为JavaScript XML
- 它用于描述我们的UI界面,并且它可以和JavaScript融合在一起使用;
- 它不同于Vue中的模块语法,不需要专门学习模块语法中的一些指令(比如v-for、v-if、v-else、v-bind);
2. JSX书写规范及注释
书写规范:
(1) JSX只能有一个根元素,一般会在外层包裹一个<div>
(或者使用后边学习的Fragment)
(2) 为了方便阅读,有多行代码时,会在jsx外层包裹一个小括号
(3) JSX中的标签可以是单标签或双标签
注释
语法:{/*注释内容...*/}
render () {
return (
<div>
{/*JSX的注释写法*/}
// JSX的注释写法---这样写仍旧会展示到页面上
<h2>当前计数为:{this.state.number}</h2>
</div>
)
}
3. JSX嵌入变量作为子元素
子元素就是标签里的内容。<h2 title='111'>aaa</h2>
aaa是子元素,title是标签属性。
(1). 当变量是Number,String,Array类型时,可以直接显示
this.state = {
// 1. 变量是Number,String,Array类型
number: 0,
name: 'tom',
movies: ['加勒比海盗', '百鸟朝凤', '飞屋环游记'],
}
render () {
const { number, name, movies } = this.state
return (
<div>
{/*1. 变量是Number,String,Array时,直接显示*/}
<h2>{number}</h2>
<h2>{name}</h2>
<h2>{movies}</h2>
</div>
)
}
(2). 当变量是null,undefined,Boolean类型时,不显示
若要显示,则需要转换成字符串(比如toString
方法、空字符串拼接
、String(变量)
)
this.state = {
// 2. 变量是null, undefined, Boolean
aaa: null,
bbb: undefined,
ccc: true,
}
render () {
const { aaa, bbb, ccc } = this.state
return (
<div>
{/*2. 变量是null, undefined, Boolean时,内容为空*/}
<h2>{aaa}</h2>
<h2>{bbb}</h2>
<h2>{ccc}</h2>
{/*2 若要显示,则需要转换成字符串*/}
<h2>{aaa + ''}</h2>
<h2>{String(bbb)}</h2>
<h2>{ccc.toString()}</h2>
</div>
)
}
(3). Object对象类型的变量不能作为子元素,会报错
this.state = {
friend: {
name: 'jerry'
}
}
render () {
const { friend } = this.state
return (
<div>
<h2>{friend}</h2>
</div>
)
}
可以写对象里具体的属性
{/*<h2>{friend}</h2>*/}
<h2>{friend.name}</h2> // jerry
<h2>{Object.keys(friend)[0]}</h2> // name
4. JSX嵌入表达式
运算表达式,三元运算符,执行函数
this.state = {
firstName: '张',
lastName: '三',
age: 20,
movies: ["流浪地球", "星际穿越", "独行月球"]
}
// 渲染函数
render () {
const { firstName, lastName } = this.state
const fullName = firstName + ' ' + lastName
const { age } = this.state
const ageText = age >= 18 ? "成年人" : "未成年人"
return (
<div>
{/*1 运算表达式*/}
<h2>{10 + 20}</h2>
<h2>{firstName + '' + lastName}</h2>
<h2>{fullName}</h2>
{/*2 三元运算符*/}
<h2>{ageText}</h2>
<h2>{age > 18 ? '成年人' : '未成年人'}</h2>
{/*3 执行一个函数*/}
<ul>{this.state.movies.map(movie => <li>{movie}</li>)}</ul>
<ul>{this.getMovieEls()}</ul>
</div >
)
}
getMovieEls () {
const liEls = this.state.movies.map(movie => <li>{movie}</li>)
return liEls
}
5. JSX绑定属性
(1) title,src,href属性
this.state = {
title: "哈哈哈",
imgURL: "https://ts1.cn.mm.bing.net/th/id/R-C.95bc299c3f1f0e69b9eb1d0772b14a98?rik=W5QLhXiERW4nLQ&riu=http%3a%2f%2f20178405.s21i.faiusr.com%2f2%2fABUIABACGAAgoeLO-wUo4I3o2gEw8Qs4uAg.jpg&ehk=N7Bxe9nqM08w4evC2kK6yyC%2bxIWTjdd6HgXsQYPbMj0%3d&risl=&pid=ImgRaw&r=0",
href: "https://www.baidu.com",
}
// 渲染函数
render () {
const { title, imgURL, href } = this.state
return (
<div>
<h2 title={title}>h2 标题</h2>
<img src={imgURL} />
<a href={href}>百度链接</a>
</div >
)
}
(2) 绑定class
需求:给h2
绑定 abc cba
, 当isActive
为true时,绑定active
,否则不绑。
注意:React绑定类名时,用className,而不是class(用class会有警告)
(1)方式一:拼接字符串
this.state = {
isActive: false
}
render () {
const { isActive } = this.state
// 1. 绑定方法一:字符串拼接
const className = `abc cba ${ isActive ? 'active' : '' }`
return (
<div>
<h2 className="abc cba">哈哈哈哈</h2>
{/*动态绑定*/}
<h2 className={className}>哈哈哈哈</h2>
</div >
)
}
缺点是,当isAcrtve
为false时,类名里会多出一个空格
(2) 方式二:将所有的class类名放到数组里
render () {
const { isActive } = this.state
// 2. 绑定方法二:将所有的class放到数组里
const className = ['abc', 'cba']
// isActive为true就添加active类名
if (isActive) className.push('active')
return (
<div>
<h2 className={className.join('')}>哈哈哈哈</h2>
</div >
)
}
}
当
className
为数组时:
类名解析出来有逗号:<h2 class="abc,cba">哈哈哈哈</h2>
所以需要.join进行处理
(3)方式三:第三方库classnames -> npm install classnames;后续再补充
(3) 绑定style样式
绑定style样式时,需要使用对象形式,属性名采用驼峰命名;
注意:JSX绑定子元素时不可以用对象,这里是绑定属性,可以用对象
this.state = {
objStyle: { color: "red", fontSize: "30px" }
}
render () {
const {objStyle } = this.state
return (
<div>
{ /* 绑定style属性: 绑定对象类型,第一个{}是语法,第二个{}表示对象 */}
<h2 style={{ color: "red", fontSize: "30px" }}>呵呵呵呵</h2>
<h2 style={objStyle}>呵呵呵呵</h2>
</div >
)
}