结束了期末考!来整理一下前端 react 笔记!!!
一、React 介绍
1.1 React
- React
- 相信每个做开发的人对它都或多或少有一些印象
- 解释:用于构建用户界面的 JavaScript 库
- 目前对于前端开发来说,几乎很少直接使用原生的JavaScript来开发应用程序,而是选择一个 JavaScript库(框架)
- 在过去的很长时间内,jQuery 是被使用最多的 JavaScript 库
- 在过去的一份调查中显示,全球前 10,000 个访问最高的网站中,有65%使用了 jQuery,是当时最受欢迎的 JavaScript 库
- 但是,目前甚至已经处于淘汰的边缘了
- 而无论是国内外,最流行的其实是三大框架:Vue、React、Angular
1.2 React的技术特点
- React 由 Facebook 来更新和维护,它是大量优秀程序员的思想结晶
- React 的流行不仅仅局限于普通开发工程师对它的认可
- 大量流行的其他框架借鉴 React 的思想
- Vue.js 框架设计之初,有很多的灵感来自 Angular 和 React
- 包括 Vue3 很多新的特性,也是借鉴和学习了 React
- 比如 React Hooks 是开创性的新功能
- Vue Composition API 学习了 React Hooks 的思想
- Flutter的很多灵感都来自React,来自官网的一段话
- 事实上 Flutter 中的 Widget – Element – RenderObject
- 对应 React 的就是 JSX – 虚拟 DOM – 真实DOM
- 所以React可以说是前端的先驱者,它总是会引领整个前端的潮流
1.3 重要性
- React 和 Vue 都是前端工程师必须掌握的两个框架:
- 在前端整个职业发展的过程中,不能仅仅将自己局限在某一个框架或者技术中
- 并且 React 是作为前端进阶来说都是必须要掌握的一个框架
二、React 开发
2.1 React的介绍
- React:用于构建用户界面的 JavaScript 库
- React的官网文档:https://zh-hans.reactjs.org/
- ReactNative:移动端应用程序(IOS/Android)
2.2 React的特点
2.2.1 声明式编程
- 是目前整个大前端开发的模式:Vue、React、Flutter、SwiftU
- 允许只需要维护好自己的状态,当状态改变时候,react 可以根据最新的状态( render 函数)去渲染 UI 界面
- UI 界面 = f (state)
2.2.2 组件化开发
- 将复杂的界面划分成一个个小的组件,最终成为一棵组件树
2.2.3 多平台适配
- 最初开发 web 页面
- ReactNative 用于开发移动端跨平台
- ReactVR 用于开发虚拟显示 Web 应用程序
2.3 开发依赖
- 开发React必须依赖三个库(添加依赖才能运行)
- react:包含 react 所必须的核心代码
- react-dom:react 渲染在不同平台所需要的核心代码
- babel:将 jsx 转换成 React 代码的工具
- 第一次接触 React 会被它繁琐的依赖搞蒙,居然依赖这么多东西
- 对于 Vue 来说,只是依赖一个 vue.js 文件即可,但是 react 居然要依赖三个包
- 这三个库是各司其职的,目的就是让每一个库只单纯做自己的事情
- 在 React 的 0.14 版本之前是没有 react-dom 这个概念的,所有功能都包含在 react 里
- 为什么要进行拆分呢?原因就是 react-native
- react 包中包含了 react web 和 react-native 所共同拥有的核心代码
- react-dom 针对 web 和 native 所完成的事情不同
- web 端:react-dom会将jsx最终渲染成真实的DOM,显示在浏览器中
- native 端:react-dom会将jsx最终渲染成原生的控件(比如 Android 中的 Button,iOS 中的 UIButton)
2.4 Babel 和 React 的关系
- babel
- Babel ,又名 Babel.js
- 是目前前端使用非常广泛的编译器、转移器
- 当下很多浏览器并不支持 ES6 的语法,但是确实 ES6 的语法非常的简洁和方便,开发时希望使用它
- 那么编写源码时就可以使用 ES6 来编写,之后通过 Babel 工具,将 ES6 转成大多数浏览器都支持的 ES5 的语法
- React 和 Babel 的关系
- 默认情况下开发 React 其实可以不使用 babel
- 但是前提是使用 React.createElement 来编写源代码,它编写的代码非常的繁琐和可读性差
- 直接编写 jsx(JavaScript XML)的语法,并且让 babel 转换成 React.createElement
2.5 Hello World
- 编写 React 的script代码中,必须添加 type=“text/babel” ,作用是可以让 babel 解析 jsx 的语法
- ReactDOM. createRoot 函数:用于创建一个 React 根,之后渲染的内容会包含在这个根中
- 参数:将渲染的内容,挂载到哪一个 HTML 元素上,定义一个 id 为 app 的 div
- root.render函数:
- 参数:要渲染的根组件
- 通过 {} 语法来引入外部的变量或者表达式
<body>
<div id="root"></div>
<!-- 添加依赖 -->
<!-- 依赖三个包 -->
<!--1. CDN引入 -->
<!--2. 下载引入 -->
<!--3. npm下载引入 -->
<script crossorigin src="./lib/react.js"></script>
<script crossorigin src="./lib/react_dom.js"></script>
<script crossorigin src="./lib/babel.js"></script>
<!-- 告诉是文本类型/babel -->
<script type="text/babel">
// 编写react代码(jsx语法)
// jsx语法-->普通的js代码 通过babel
// 渲染hello world
// 渲染到哪
// 可以创建多个根
// React 18 之后
const root = ReactDOM.createRoot(document.querySelector
('#root'))
root.render(<h1>Hello world</h1>)
</script>
</body>
2.6 hello react
- 原始代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="root"></div>
<!-- 引入依赖 -->
<script src="./lib/react.js"></script>
<script src="./lib/react-dom.js"></script>
<script src="./lib/babel.js"></script>
<script type="text/babel">
const root = ReactDOM.createRoot(document.querySelector("#root"))
// 1. 将文本定义成变量
let message = "hello react"
// 2. 监听按钮的点击:不要获取元素,而是定义一个函数进行绑定:onClick={}
function btnClick() {
console.log("btnClick!");
// 2.1修改数据
message = "hello world"
// 2.2重新渲染界面(修改完数据不会重新渲染)
RooterRender()
}
// 第一次直接调用进行渲染
RooterRender()
// 3. 封装一个渲染函数
function RooterRender() {
// 第一个小括号:render 方法调用
// 第二个小括号:表示接下来写的东西是一个整体
// 动态的绑定就是使用大括号
root.render((
<div>
<h2>{message}</h2>
<button onClick={btnClick()}>修改文本</button>
</div>
))
}
</script>
</body>
</html>
2.7 Hello React – 组件化开发
- 整个逻辑其实可以看做一个整体,可以将其封装成一个组件:
- root.render 参数是一个 HTML 元素或者一个组件
- 可以先将之前的业务逻辑封装到一个组件中,然后传入到 ReactDOM.render 函数中的第一个参数
- 暂时使用类的方式封装组件
- 定义一个类(类名大写,组件的名称是必须大写的,小写会被认为是HTML元素),继承自 React.Component
- 实现当前组件的 render 函数
- render 当中返回的 jsx 内容:渲染的内容
2.8 组件化 - 数据依赖
- 在组件中的数据可以分成两类:
- 参与界面更新的数据:当数据变量时,需要更新组件渲染的内容
- 不参与界面更新的数据:当数据变量时,不需要更新将组建渲染的内容
- 参与界面更新的数据也可以称之为是参与数据流,这个数据是定义在当前对象的 state 中
- 可以通过在构造函数中 this.state = {定义的数据}
- 当数据发生变化时,可以调用 this.setState 来更新数据,并且通知 React 进行 update 操作
- 在进行 update 操作时,就会重新调用 render 函数,并且使用最新的数据,来渲染界面
2.9 组件化 – 事件绑定
- 组件化问题二:事件绑定中的this
- 在类中直接定义一个函数,并且将这个函数绑定到元素的 onClick 事件上,当前这个函数的 this 指向的是谁呢?
- 默认情况下是 undefined
- 因为在正常的 DOM 操作中,监听点击,监听函数中的 this 其实是节点对象(比如说是button对象)
- 这次因为 React 并不是直接渲染成真实的 DOM ,所编写的 button只是一个语法糖,它的本质 React 的 Element 对象
- 那么在这里发生监听的时候,react 在执行函数时并没有绑定 this ,默认情况下就是一个undefined
- 在绑定的函数中,可能想要使用当前对象,比如执行 this.setState 函数,就必须拿到当前对象的 this
- 方式一:
onClick = {this.btnClick.bind(this)}
- 方式二:constructor 中对需要绑定的方法提前绑定好 this:
this.btnClick = this.btnClick.bind(this)
- 方式一:
2.10 Hello-react 代码重构
- 组件化的方式(内聚性更强)
- 数据 data
- 函数方法
- 渲染内容: render 函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script src="../lib/babel.js"></script>
<script src="../lib/react-dom.js"></script>
<script src="../lib/babel.js"></script>
<script type="text/babel">
// 继承了React.Component就是一个类
class App extends React.Component {
// 组件数据:参与界面刷新的数据(参与数据流)/不参与界面更新的数据
// 数据定义在组件内部
constructor() {
super()
// 在这里防放置数据
// 参与页面更新的数据必须放在这里
this.state = {
message: "hrllo youlishu"
}
// 对需要绑定的方法提前绑定好 this
// this.btnClick = this.btnClick.bind(this)
}
// 组件方法(实例方法)、类的实例方法
btnClick() {
// 内部完成了两件事情:
// 1. 将state中message值修改掉
// 2. 自动重新执行render函数
console.log("btnClick")
console.log("this:", this)
this.setState({
message: "hello hahh"
})
}
// 渲染内容 render 方法:固定的方法,必须是这个名字
render() {
// 解构
const { message } = this.state
return (
<div>
<h2>{message}</h2>
<button onClick={() => this.btnClick()}>修改文本</button>
</div>
)
}
}
// this 绑定问题
// 创建一个类
// const app = new App()
// const foo = app.btnClick
// foo()//默认绑定 => window => 在严格模式下指向 undefined
// // 在 ES6 class 下就是严格模式
// // 这里是指向 window × 实际是 undefined ,因为用到了 babel ,会对代码进行转换
// function bar() {
// console.log(this);
// }
// bar()
// 创建根
const root = ReactDOM.createRoot(document.querySelector("#app"))
// 渲染一个组件
root.render(<App />)
</script>
</body>
</html>
2.11 电影列表
- for 循环
- map 展示列表:对数组中的每一个元素进行改变,再返回另一个新的数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script src="./lib/react.js"></script>
<script src="./lib/react-dom.js"></script>
<script src="./lib/babel.js"></script>
<script type="text/babel">
// 1. 创建 root
const root = ReactDOM.createRoot(document.querySelector("#root"))
// 封装 App 组件
class App extends React.Component {
constructor() {
super()
this.state = {
movies: ['星际穿越', '老人与海', '我喜欢你', '大话西游']
}
}
render() {
// 1. 对movies进行for循环
// const liEls = []
// for (let i = 0; i < this.state.movies.length; i++) {
// const movie = this.state.movies[i]
// const liEl = <li>{movie}</li>
// liEls.push(liEl)
// }
// return (
// <div>
// <h1>电影列表</h1>
// <ul>
// 能够直接显示数组中的元素
// {liEls}
// </ul>
// </div>
// )
// 2. movies数组=>liEls
// const liEls = this.state.movies.map(movie=> <li>{movie}</li>)
// ()小括号目的:把里面的东西当成一个整体
// 3. 既然有2,不如直接上,如果里面特别复杂,另外的做法
return (
<div>
<h1>电影列表</h1>
<ul>
{this.state.movies.map(movie => <li>{movie}</li>)}
</ul>
</div>
)
}
}
// 2. 渲染组件
// 必须要这样写 <App /> => jsx => React.createElement
root.render(<App />)
</script>
</body>
</html>
2.12 计数器
<body>
<div id="root"></div>
<script src="./lib/react.js"></script>
<script src="./lib/react-dom.js"></script>
<script src="./lib/babel.js"></script>
<script type="text/babel">
// 1. 定义App根组件
class App extends React.Component {
constructor() {
super()
this.state = {
message: "hello youlishu",
counter: 1000
}
}
increment() {
this.setState({
counter: this.state.counter + 1
})
console.log("increment");
}
decrement() {
this.setState({
counter: this.state.counter - 1
})
console.log("decrement");
}
render() {
const { counter } = this.state
return (
< div >
<h2>当前计数:{counter}</h2>
<button onClick={() => this.increment()}>+1</button>
<button onClick={() => this.decrement()}>-1</button>
</ div>
)
}
}
// 2. 创建root并且渲染App函数
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App />)
</script>
</body>
2.13 VSCode代码片段
- VSCode 中的代码片段有固定的格式,所以一般会借助于一个在线工具来完成
- 具体的步骤如下
- 第一步,复制自己需要生成代码片段的代码
- 第二步,https://snippet-generator.app/在该网站中生成代码片段
- 第三步,在 VSCode 中配置代码片段
三、React基础-JSX语法
3.1 JSX
-
let element2 = <div>hhhh</div>
- 它不是一段字符串(因为没有使用引号包裹)
- 它看起来是一段 HTML 元素,但是能在 js 中直接给一个变量赋值html吗
- 其实是不可以的,如果将 type=“text/babel” 去除掉,那么就会出现语法错误
- 其实它是一段 jsx 的语法;
-
是 js 的语法扩展,很多地方称之为 JavaScript XML
- js + extention
- js 基础上所拓展出来的代码
-
可以看成 html in js
- html 写在 js 里面
-
用于描述 UI 界面,并且可以和 js 融合在一起
-
不同于 Vue 中的模板语法,不需要专门学习模块语法中的一些指令
-
all in js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script src="../lib/react.js"></script>
<script src="../lib/react-dom.js"></script>
<script src="../lib/babel.js"></script>
<script type="text/babel">
// 1. 定义App根组件
class App extends React.Component {
constructor() {
super()
this.state = {
message: "Hello World"
}
}
render() {
const { message } = this.state
// 把一个元素赋值给了 element ,js 中没有这种语法
const element = <h2>{message}</h2>
return element
}
}
// 2. 创建root并且渲染App组件
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App />)
</script>
</body>
</html>
3.2 react选择 JSX 原因
- react认为渲染逻辑本质上与其他 UI 逻辑存在内在耦合
- UI 需要绑定事件
- UI 需要展示数据状态
- 在某些状态发生改变时,需要改变 UI
- 所以呢就认为他们之间是密不可分的,没有将标记分离到不同的文件中,而是将他们组合到了一起形成组件
- JSX 是嵌入到 js 中的一种结构语法
3.3 编写规范
- jsx 结构中只能有一个根元素
- jsx 结构通常会包裹一个 () ,将整个 jsx当做一个整体,实现换行
- 可以写单标签或双标签,但是单标签必须以 / 结尾
<body>
<div id="root"></div>
<script src="../lib//react.js"></script>
<script src="../lib//react-dom.js"></script>
<script src="../lib/babel.js"></script>
<script type="text/babel">
// 1. 定义App根组件
class App extends React.Component {
constructor() {
super()
this.state = {
message:"Hello World"
}
}
render() {
const { message } = this.state
// 书写规范
// 1. jsx结构中只能有一个根元素
// 2. jsx结构通常会包裹一个(),将整个jsx当做一个整体,实现换行
// 3. 可以写单标签或双标签,但是单标签必须以 / 结尾
return (
<div>
<h2>{message}</h2>
</div>
)
}
}
// 2. 创建root并且渲染App组件
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App/>)
</script>
</body>
3.4 JSX 的使用
-
注释:
{/*注释写法*/}
,jsx 文件 -
JSX嵌入变量作为子元素
- 直接显示:Number、String、Array
- 显示为空:null、undefined、boolean
- 想要看到它们
- 转成字符串:toString()、与空字符串拼接 +‘’、String(变量)
- Object 类型不能显示,不能直接插入(以后开发遇到最多的)
- 取出里面的东西
- 明确地告诉 react 你需要什么
-
JSX 嵌入表达式
- 插入对应的表达式
- 插入三元运算符
- 调用方法获取结果
<body>
<div id="root"></div>
<script src="../lib//react.js"></script>
<script src="../lib//react-dom.js"></script>
<script src="../lib/babel.js"></script>
<script type="text/babel">
// 1. 定义App根组件
class App extends React.Component {
constructor() {
super()
this.state = {
// Number、String、Array
message: "Hello World",
// 数组
names: ["美洋洋", "嗨森", "懒图图"],
// 特殊
aaa: undefined,
counter: 9,
friend: {
name: "jecy"
},
lastName: "lisasss",
age: 19,
movies: ["哈哈", "拉拉", "乖乖"]
}
}
render() {
const { message, names, aaa, counter, friend, lastName, age } = this.state
const fullName = lastName + names
const liEls = this.state.movies.map(movie => <li key={movie}>{movie}</li>)
return (
<div>
<h2>{message}</h2>
<h1>数组:</h1>
<h2>{names}</h2>
<h1>{aaa}</h1>
<h1>{counter}</h1>
<h1>{friend.name}</h1>
<h2>{10 + 20}</h2>
<h1>{fullName}</h1>
<h2>{age >= 18 ? '成年人' : '未成年人'}</h2>
<ul>{liEls}</ul>
<ul>{this.getMoviesEls()}</ul>
</div>
)
}
getMoviesEls() {
const liEls = this.state.movies.map(movie => <li>{movie}</li>)
return liEls
}
}
// 2. 创建root并且渲染App组件
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App />)
</script>
</body>
3.5 JSX 绑定属性
-
都是使用 {},没有那么多指令
-
jsx 绑定属性(统一使用大括号进行绑定)
-
基本属性
- 所有元素的 title 属性
- 图片的 src 属性
- a 的 href 属性
-
绑定 class 属性:最好使用 className,而不是使用 class
-
className =“{aaa bbb {aaaa}}” 这样做不行
-
字符串拼接
const className = `abc aaa ${isActive ? 'active' : ''}` <h2 className={className}>哈哈哈哈</h2>
-
将所有的 class 放在数组中
const classList = ['aaa', 'bbb', 'ccc'] if (isActive) classList.push("active") <h2 className={classList.join(" ")}>哈哈哈哈</h2>
- 使用第三方库
-
-
绑定 style 样式属性:绑定对象类型(动态)
- 对象里面插入 js 内容:{{}}
<h2 style={{ color: "red", fontSize: "50px" }}>hehhehhh</h2>
-
<body>
<div id="root"></div>
<script src="../lib//react.js"></script>
<script src="../lib//react-dom.js"></script>
<script src="../lib/babel.js"></script>
<script type="text/babel">
// 1. 定义App根组件
class App extends React.Component {
constructor() {
super()
this.state = {
message: "Hello World",
title: "哈哈哈",
imgURL: "https://bkimg.cdn.bcebos.com/pic/a6efce1b9d16fdfa4e685e5cb58f8c5494ee7b78?x-bce-process=image/watermark,image_d2F0ZXIvYmFpa2U5Mg==,g_7,xp_5,yp_5",
href: "https://www.baidu.com",
isActive: true,
}
}
render() {
const { message, title, imgURL, href, isActive } = this.state
// 写法一:字符串拼接
const className = `abc aaa ${isActive ? 'active' : ''}`
// 写法二:将所有的class放在数组中
const classList = ['aaa', 'bbb', 'ccc']
if (isActive) classList.push("active")
// 写法三:第三方库classnames
return (
<div>
{/*1. 基本属性绑定*/}
<h2 title={title}>我是h2元素</h2>
<img src={imgURL} alt="" />
<a href={href}>百度一下</a>
{/*2. 绑定class属性:最好使用className*/}
<h2 className={className}>哈哈哈哈</h2>
<h2 className={classList.join(" ")}>哈哈哈哈</h2>
{/*3. 绑定style属性:绑定对象类型 */}
<h2 style={{ color: "red", fontSize: "50px" }}>hehhehhh</h2>
</div>
)
}
}
// 2. 创建root并且渲染App组件
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App />)
</script>
</body>
3.6 React 事件绑定
- 如果原生 DOM 原生有一个监听事件,可以如何操
- 方式一:获取 DOM 原生,添加监听事件
- 方式二:在 HTML 原生中,直接绑定 onclick
- 方式三:vue @click
- 方式四:小程序 bindtap
- 在 React 中是操作
- React 事件的命名采用小驼峰式(camelCase),而不是纯小写
- 需要通过 {} 传入一个事件处理函数,这个函数会在事件发生时被执行
3.7 this 绑定规则
- 普通绑定 -
onClick={this.btnClick}
- 在内部是独立函数调用,所以 this 为 undefined - this绑定方式一: bind绑定 -
onClick={this.btnClick.bind(this)}
- this绑定方式二: ES6 class fields -
onClick={this.btnClick}
-btnClick = () => {}
- 类有作用域,this 指向这个实例
- this绑定方式三: 直接传入一个箭头函数 -
onClick={() => this.btnClick()}
- 调用了箭头函数
<body>
<div id="root"></div>
<script src="../lib//react.js"></script>
<script src="../lib//react-dom.js"></script>
<script src="../lib/babel.js"></script>
<script type="text/babel">
// this的四种绑定规则
// 1. 默认绑定:独立执行
// 2. 隐式绑定:被一个对象执行 obj.foo()
// 3. 显式绑定:call、apply、bind
// 4. new绑定:创建一个新对象并且赋值给this
// 1. 定义App根组件
class App extends React.Component {
constructor() {
super()
this.state = {
message: "Hello World",
counter: 100
}
}
btn1Click() {
const { counter } = this.state
console.log("btnClick");
this.setState({
counter: counter + 1
})
}
render() {
const { message, counter } = this.state
return (
<div>
<h2>{message}</h2>
{/*只是表示一个引用,传给了button*/}
{/*严格模式下函数独立调用:undefined*/}
{/*<button onClick={this.btn1Click}>btnClick</button>*/}
<button onClick={() => this.btn1Click()}>btnClick</button>
<h2>当前计数:{counter}</h2>
</div>
)
}
}
// 2. 创建root并且渲染App组件
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App />)
</script>
</body>
3.8 事件参数传递
3.8.1 知识点
- event参数的传递 -
onClick={(event) => this.btn1Click(event)}
- 额外参数的传递 -
onClick={(event) => this.btn2Click(event, "http", 18)}
- 不推荐使用 bind
<div id="root"></div>
<script src="../lib//react.js"></script>
<script src="../lib//react-dom.js"></script>
<script src="../lib/babel.js"></script>
<script type="text/babel">
// 1. 定义App根组件
class App extends React.Component {
constructor() {
super()
this.state = {
message: "Hello World"
}
}
btnClick(event, name, age) {
console.log(event, name, age);
}
render() {
const { message } = this.state
return (
<div>
{/*event参数的传递*/}
<button onClick={(event) => this.btnClick(event, "有理数", 12)}>btnClick</button>
</div>
)
}
}
// 2. 创建root并且渲染App组件
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App />)
</script>
3.8.2 电影案例
class App extends React.Component {
constructor() {
super()
this.state = {
message: "Hello World",
movies: ["有理数", "shige", "dameinv"],
currentIndex: 0
}
}
movieClick(index) {
this.setState({
currentIndex: index
})
}
render() {
const { message, currentIndex } = this.state
return (
<div>
<h2>{message}</h2>
<ul>
{this.state.movies.map((movie, index) => {
return <li onClick={() => this.movieClick(index)} key={movie} className={currentIndex === index ? 'active' : ''}>{movie}</li>
})}
</ul>
</div>
)
}
}
<body>
<div id="root"></div>
<script src="../lib//react.js"></script>
<script src="../lib//react-dom.js"></script>
<script src="../lib/babel.js"></script>
<script type="text/babel">
// 1. 定义App根组件
class App extends React.Component {
constructor() {
super()
this.state = {
message: "Hello World",
movies: ["有理数", "shige", "dameinv"],
currentIndex: 0
}
}
movieClick(index) {
this.setState({
currentIndex: index
})
}
render() {
const { message, currentIndex, movies } = this.state
const liEls =
movies.map((movie, index) => {
return (
<li onClick={() => this.movieClick(index)} key={movie} className={currentIndex === index ? 'active' : ''}>{movie}</li>
)
})
return (
<div>
<h2>{message}</h2>
<ul>
{liEls}
</ul>
</div>
)
}
}
// 2. 创建root并且渲染App组件
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App />)
</script>
</body>
3.9 React条件渲染
- 某些情况下,界面的内容会根据不同的情况显示不同的内容,或者决定是否渲染某部分内容
- 在 vue 中,会通过指令来控制:比如 v-if、v-show
- 在 React 中,所有的条件判断都和普通的 JavaScript 代码一致
- 常见的条件渲染的方式?
- 方式一:条件判断语句
- 适合逻辑较多的情况
- 方式二:三元运算符
- 适合逻辑比较简单
- 方式三:与运算符&&
- 适合如果条件成立,渲染某一个组件;如果条件不成立,什么内容也不渲染
- v-show的效果
- 主要是控制 display 属性是否为 none
- 方式一:条件判断语句
- 示例
- 条件判断方式一:使用 if 进行条件判断并对变量进行赋值,使用最多的方式
- 条件判断方式二:三元运算符适用于表达式不长的情况
- 条件判断方式三:&& 逻辑与运算
<body>
<div id="root"></div>
<script src="../lib//react.js"></script>
<script src="../lib//react-dom.js"></script>
<script src="../lib/babel.js"></script>
<script type="text/babel">
// 1. 定义App根组件
class App extends React.Component {
constructor() {
super()
this.state = {
message: "Hello World",
isReady: false,
friend: {
name: "有理数",
desc: "性别难爱好少"
}
}
}
render() {
const { message, isReady, friend } = this.state
// 1. 条件判断方式一:使用if进行条件判断并对变量进行赋值
let showElement = null
if (isReady) {
showElement = <h2>准备开始比赛吧~</h2>
} else {
showElement = <h1>抓紧时间准备吧~</h1>
}
return (
<div>
<h2>{message}</h2>
<p>这个人的比赛状态:</p>
{/*1. 方式一:根据条件给变量赋值不同的内容*/}
<h1>{showElement}</h1>
{/*2. 方式二:三元运算符*/}
<div>{isReady ? <button>开始战斗!</button> : <h3>赶紧准备!!</h3>}</div>
{/*场景:当某一个值有可能为undefined时,使用&&进行条件判断*/}
{/*3. 方式三:&&逻辑与运算*/}
<div>{friend && <div>{friend.name + " " + friend.desc}</div>}</div>
</div>
)
}
}
// 2. 创建root并且渲染App组件
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App />)
</script>
</body>
- v-if 和 v-show 的效果
changeShow() {
this.setState({
isShow: !this.state.isShow
})
}
render() {
const { message, isShow } = this.state
return (
<div>
<button onClick={() => this.changeShow()}>切换</button>
{isShow && <h2>{message}</h2>}
{/* v-show效果 */}
<h1 style={{ display: isShow ? 'block' : 'none' }}>哈哈哈哈或或</h1>
</div>
)
}
3.10 React 列表渲染
- 真实开发中会从服务器请求到大量的数据,数据会以列表的形式存储
- 比如歌曲、歌手、排行榜列表的数据
- 比如商品、购物车、评论列表的数据
- 比如好友消息、动态、联系人列表的数据
- 在 React 中并没有像Vue模块语法中的 v-for指令,而且需要通过 JavaScript 代码的方式组织数据,转成 JSX
- 但是 React 中的JSX正是因为和 JavaScript无缝的衔接,让它可以更加的灵活
- React 是真正可以提高我们编写代码能力的一种方式
- 展示列表
- 在 React 中,展示列表最多的方式就是使用数组的 map高阶函数
- 很多时候在展示一个数组中的数据之前,需要先对它进行一些处理
- 比如过滤掉一些内容:filter 函数
- 比如截取数组中的一部分内容:slice 函数
<body>
<div id="root"></div>
<script src="../lib//react.js"></script>
<script src="../lib//react-dom.js"></script>
<script src="../lib/babel.js"></script>
<script type="text/babel">
// 1. 定义App根组件
class App extends React.Component {
constructor() {
super()
this.state = {
students: [
{ id: 111, name: "iosa", score: 99 },
{ id: 112, name: "liye", score: 49 },
{ id: 113, name: "hhye", score: 199 },
{ id: 114, name: "yesa", score: 149 },
]
}
}
render() {
const { students } = this.state
// 分数大于100的人进行展示
const filterStudents = students.filter(item => {
return item.score > 100
})
// 分数大于一百的前两个人进行展示[start,end)
const sliceStudents = filterStudents.slice(0, 2)
return (
<div>
<h2>学生列表数据</h2>
<div className="list">
{
students.filter(item => item.score > 100).slice(0, 2).map(item => {
return (
<div class="item" key={item.id}>
<h2>学号:{item.id}</h2>
<h2>姓名:{item.name}</h2>
<h2>分数:{item.score}</h2>
</div>
)
})
}
</div>
</div>
)
}
}
// 2. 创建root并且渲染App组件
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App />)
</script>
</body>
- 链式调用
student.filter(item=>item.score > 100).slice(0,2).map(item=>{
return (
<div>
// key作用:提高diff算法效率
<h2 key={item.id}>{item.id}</h2>
</div>
)
}))
3.11 列表中的 key
- key主要的作用是为了提高 diff 算法时的效率
3.12 JSX的本质
-
实际上,jsx 仅仅只是 React.createElement(component, props, …children) 函数的语法糖
- 所有的 jsx 最终都会被转换成 React.createElement 的函数调用
-
createElement 需要传递三个参数:
-
参数一:type
- 当前 ReactElement 的类型
- 如果是标签元素,那么就使用字符串表示 “div”
- 如果是组件元素,那么就直接使用组件的名称
-
参数二:config
- 所有 jsx 中的属性都在 config 中以对象的属性和值的形式存储
- 比如传入 className 作为元素的 class
-
参数三:children
- 存放在标签中的内容,以 children 数组的方式进行存储
- 当然,如果是多个元素呢?React 内部有对它们进行处理,处理的源码在下方
-
默认 jsx 是通过 babel 进行语法转换的,所以我们之前写的jsx代码都需要依赖 babel
-
可以在 babel 的官网中快速查看转换的过程:https://babeljs.io/repl/#?presets=react
-
使用 jsx 更加方便阅读
3.13 虚拟DOM的创建过程
- 通过 react.createElement 最终创建出来一个 ReactElement 对象
- react 利用 reactElement 对象组成了一个 JavaScript 的对象树-——虚拟 DOM
- Virtual DOM 是一种编程概念,UI 以一种理想化或者说虚拟化的方式保存在内存中
- Virtual DOM 本质上是 JavaScript 对象,是真实 DOM 的描述,⽤⼀个 JS 对象来描述⼀个 DOM 节点
- jsx 借助 babel 转成 React 代码的本质是 - 转换成 React.createElement 的函数调用
- 通过 React.createElement 的函数创建出来的
ReactElement
对象 - React利用
ReactElement
对象组成了一个 JavaScript 的对象树 —— JavaScript 的对象树就是虚拟 DOM
-
虚拟 dom 作用
-
通过 diff 算法以最⼩的代价更新变化的视图,没有更新所有的数据
-
跨平台渲染
- web
- ios/android 控件
-
3.14 声明式编程
- 虚拟 DOM 从命令式编程转到了声明式编程的模式
- div——>ReactElement对象——>div元素(协调:通过 root.render 虚拟 dom 和真实 dom 同步起来)
- 只需要告诉 React 希望让 UI 是什么状态
- React 来确保 DOM 和这些状态是匹配的
- 不需要直接进行 DOM 操作,就可以从手动更改 DOM 、属性操作、事件处理中解放出来
3.15 购物车案例
- 数据准备,直接引入
//data.js
const books = [
{
id: 1,
name: "你不知道的JavaScript",
date: 2001,
price: 33,
count: 2
},
{
id: 2,
name: "算法入门大导轮",
date: 2003,
price: 99,
count: 2
},
{
id: 3,
name: "数据结构与算法",
date: 2002,
price: 53,
count: 9
},
{
id: 4,
name: "如何一夜成名",
date: 2006,
price: 999,
count: 233
},
{
id: 5,
name: "一只特立独行的猪",
date: 2009,
price: 23,
count: 1
}
]
- 格式化数字
function formatPrice(price) {
// 保留两位小数
return "¥" + Number(price).toFixed(2);
}
- 代码
<body>
<div id="root"></div>
<script src="../lib//react.js"></script>
<script src="../lib//react-dom.js"></script>
<script src="../lib/babel.js"></script>
<script src="./data.js"></script>
<script src="./format.js"></script>
<script type="text/babel">
// 1. 定义App根组件
class App extends React.Component {
constructor() {
super()
this.state = {
message: "Hello Books",
books: books
}
}
changeCount(index, count) {
// react中不要直接修改原来的数据
// 对原来的数据进行浅拷贝
const newBooks = [...this.state.books]
// 通过newBooks进行修改
newBooks[index].count += count
// 执行 setState:render函数重新渲染
this.setState({ books: newBooks })
}
deleteBook(index) {
// 也是要对原来的数组进行浅拷贝
const newBooks = [...this.state.books]
// 处理数组的利器
newBooks.splice(index, 1)
this.setState({ books: newBooks })
}
// 购物车页面
renderBookList() {
const { books, message } = this.state
const totalPrice = books.reduce((preValue, item) => {
return preValue + item.count + item.price
}, 0)
return (
<div class="book">
<h2>{message}</h2>
<table>
<thead>
<tr>
<th>序号</th>
<th>书籍</th>
<th>日期</th>
<th>价格</th>
<th>数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{
books.map((item, index) => {
return (
<tr key={item.id}>
<td>{index + 1}</td>
<td>{item.name}</td>
<td>{item.date}</td>
<td>{item.price}</td>
<td>
{/*在按钮属性中进行限制:disabled = true*/}
<button onClick={() => this.changeCount(index, -1)} disabled={item.count <= 1}>-</button>
{item.count}
<button onClick={() => this.changeCount(index, 1)}>+</button>
</td>
<td>
<button onClick={() => this.deleteBook(index)}>删除</button>
</td>
</tr>
)
})
}
</tbody>
</table>
<h1>总价格:{formatPrice(totalPrice)}</h1>
</div>
)
}
renderEmpty() {
return <div><h1>hahhhh购物车为空哦</h1></div>
}
render() {
const { message, books } = this.state
// 根据条件调用函数
return books.length ? this.renderBookList() : this.renderEmpty()
}
}
// 2. 创建root并且渲染App组件
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App />)
</script>
</body>
四、React脚手架解析
react 基本用的都是 webpack,不用考虑 vite
4.1 脚手架
- 脚手架(Scaffold)
- 其实是一种工具,快速生成项目的工程化结构
- 每个项目作出完成的效果不同,但是它们的基本工程化结构是相似的
- 既然相似,就没有必要每次都从零开始搭建,完全可以使用一些工具,帮助生产基本的工程化模板
- 不同的项目,在这个模板的基础之上进行项目开发或者进行一些配置的简单修改即可
- 这样也可以间接保证项目的基本机构一致性,方便后期的维护
- 总结:脚手架让项目从搭建到开发,再到部署,整个流程变得快速和便捷
4.2 前端脚手架
- Vue:@vue/cli
- Angular:@angular/cli
- react:create-react-app
- cra
- 帮助生成一个通用的目录结构,并且将所需的工程环境配置好
- 都是基于电脑上的Node环境——>借助webpack
4.3 创建 react 项目
- 首先需要进行安装:
npm i create-react-app -g
- 检查版本:
create-react-app --version
- 创建项目:
create-react-app 项目名称(不能有大写字母)
- 创建完成后,进入对应的目录,就可以将项目跑起来
cd 01-test-react
- 之前是
yarn
管理,后面又改成了npm
npm run start
4.4 目录结构分析
- node_modules:存放我们依赖的第三方包
- public
- favicon.ico – 应用程序顶部icon图标
- index.html – 应用的index.html入口文件
- 使用了 new HtmlWebpackPlugin()
- logo192.png – 在manifest.json中被使用
- logo512.png – 在manifest.json中被使用
- manifest.json – 与web app配置相关–为了做pwa
- robots.text – 指定搜索引擎可以或者不可以爬取那些信息
- src:编写源代码
- App.css – App组件相关样式
- App.js – App组件代码文件
- App.test.js – App组件的测试代码文件
- index.css – 全局样式文件
- index.js – 整个应用程序的入口文件
- logo.svg – 启动项目时,所看到的React图标
- reportWebVitals.js – 默认帮我们写好的 注册pwa相关的代码
- setupTests.js – 测试初始文件
- .gitignore
- package-lock.json:真实依赖的包
- package.json
4.5 pwa
- 全称 Progressive Web App,即渐进式 WEB 应用
- 一个 PWA 应用首先是一个网页, 可以通过 Web 技术编写出一个网页应用
- 随后添加上 App Manifest 和 Service Worker 来实现 PWA 的安装和离线等功能
- 这种 Web 存在的形式,也称之为是 Web App
- 作用
- pwa 可以将网页添加至主屏幕,点击主屏幕图标可以实现启动动画以及隐藏地址栏
- pwa 实现离线缓存功能,即使用户手机没有网络,依然可以使用一些离线功能
- pwa 实现了消息推送
4.6 从零编写代码
- 先将不需要的文件统统删掉
- 将 src 下的所有文件都删除
- 将 public文件下除 favicon.ico 和 index.html 之外的文件都删除掉
- 在src目录下,创建一个 index.js 文件,这是 webpack 打包的入口
import ReactDOM from "react-dom/client";
// 编写react代码,并且通过react渲染出来对应的内容
import App from "./App";
const root = ReactDOM.createRoot(document.querySelector("#root"));
root.render(<App/>);
- Components:
HelloWorld.jsx
import React from "react";
class HelloWorld extends React.Component{
render() {
return (
<div>
<h2>hello world</h2>
<p>hello,你好世界!!!</p>
</div>
)
}
}
export default HelloWorld
- 不希望直接在 root.render 中编写过多的代码,就可以单独抽取一个组件 App.jsx
import React from "react";
import HelloWorld from "./Components/HelloWorld";
// 编写一个组件
class App extends React.Component {
constructor() {
super();
this.state = {
message: "hello~~~",
};
}
render() {
const { message } = this.state;
return (
<div>
<h1>{message}</h1>
<HelloWorld></HelloWorld>
</div>
);
}
}
export default App
- npm run eject:弹出所有的配置,回不去了,不可逆操作,不推荐
- 脚手架会将webpack配置隐藏起来