一、react简介
1.起源
-
React是2013年,Facebook开源的JavaScript框架,那么当时为什么Facebook要推出这样一款框架呢?
- 这个源于一个需求,所产生的bug:
- 该功能上线之后,总是出现bug;
- 三个消息的数字在发生变化时,过多的操作很容易产生bug;
-
bug是否可以修复呢?当然可以修复,但是Facebook的工程师并不满足于此;
- 他们开始思考为什么会产生这样的问题;
- 在传统的开发模式中,我们过多的去操作界面的细节;(前端、iOS、Android)
- 并且需要掌握和使用大量DOM的API,当然我们可以通过jQuery来简化和适配一些API的使用;
- 另外关于数据(状态),往往会分散到各个地方,不方便管理和维护;
-
他们就去思考,是否有一种新的模式来解决上面的问题:
- 1.以组件的方式去划分一个个功能模块
- 2.组件内以jsx来描述UI的样子,以state来存储组件内的状态
- 3.当应用的状态发生改变时,通过setState来修改状态,状态发生变化时,UI会自动发生更新
2.特点:
1.采用组件化模式,声明式编程,提高开发效率及组件复用率(多平台适配)
- 命令式编程:每一个操作都是给浏览器或者计算机一步步命令
- 声明式编程:
2.在react native中可以使用react语法进行移动端开发
3.使用虚拟dom和diff算法进行
3.react开发依赖
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W4qxn2lS-1649341100264)(img/
.png)]
4.引入react依赖
方式一:直接CDN引入
方式二:下载后,添加本地依赖
方式三:通过npm管理(后续脚手架再使用)
体验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="app">app</div>
<!-- 添加react依赖 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- 开始开发 -->
<!-- 注意事项:使用jsx,并且希望javascript中的·jsx代码得到正确编译,需要给script标签添加type属性 -->
<script type="text/babel">
// ReactDOM.render(渲染的内容, 挂载的对象)
// 渲染内容中应该写jsx代码
let message = 'hello world'
function btnClick() {
message = 'hello react'
console.log(message);
render()
}
// 与vue一样,最外层只能有一个标签
function render() {
ReactDOM.render(
<div>
<h2>{message}</h2>
<button onClick={btnClick}>更改</button>
</div>,
document.getElementById('app'))
}
render()
</script>
</body>
</html>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-78GoJTyh-1649341100266)(img/hello-world.png)]
二、补充
1.es6中的class关键字
- 在ES6之前,我们通过function来定义类,但是这种模式一直被很多从其他编程语言(比如Java、C++、OC等等)转到
JavaScript的人所不适应。
-
原因是,大多数面向对象的语言,都是使用class关键字来定义类的。
-
而JavaScript也从ES6开始引入了class关键字,用于定义一个类。
-
转换成ES6中的类如何定义呢?
- 类中有一个constructor构造方法,当我们通过new关键字调用时,就会默认执行这个构造方法
- 构造方法中可以给当前对象添加属性
- 类中也可以定义其他方法,这些方法会被放到Person类的prototype上
-
另外,属性也可以直接定义在类中:
- height和address是直接定义在类中
-
继承是面向对象的一大特性,可以减少我们重复代码的编写,方便公共内容的抽取(也是很多面向对象语言中,多态的前提)。
-
ES6中增加了extends关键字来作为类的继承。
-
注意:在constructor中,子类必须通过super来调用父类的构造方法,对父类进行初始化,否则会报错。
<!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>
<script>
// es5中定义类
// function Parent(name, age) {
// this.name = name
// this.age = age
// }
class Parent {
constructor(name, age) {
this.name = name
this.age = age
}
running() {
console.log(this);
console.log(this.name, this.age);
}
name1 = 'nihao'
}
class Child extends Parent {
constructor(name, age, rate) {
super(name, age)
this.rate = rate
}
}
// es5中的function和es6中的类两者等价
let p = new Parent('sasha', 18)
let c = new Child('66', 12, 1)
console.log(p);
console.log(c);
// p.running()
</script>
</body>
</html>
2.在vscode中添加用户片段,提高代码效率
使用https://snippet-generator.app/
来生成具有json格式的代码片段
"react base code": {
"prefix": "reactapp",
"body": [
"<!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\">app</div>",
" <!-- 添加react依赖 -->",
" <script crossorigin src=\"https://unpkg.com/react@16/umd/react.development.js\"></script>",
" <script crossorigin src=\"https://unpkg.com/react-dom@16/umd/react-dom.development.js\"></script>",
" <script src=\"https://unpkg.com/babel-standalone@6/babel.min.js\"></script>",
" <script type=\"text/babel\">",
" class App extends React.Component {",
" constructor(props) {",
" super(props)",
" }",
" render() {",
" return (",
" <div>",
" hello world",
" </div>",
" )",
" }",
" }",
" ReactDOM.render(<App />, document.getElementById('app'))",
" </script>",
"</body>",
"",
"</html>"
],
"description": "react base code"
}
三、认识jsx的语法
1.认识jsx
jsx语法,将一个标签赋值给一个变量,这就是jsx语法,必须给script标签加上type属性才不会报错
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MkB4a3Rd-1649341100267)(img/认识jsx语法.png)]
2.书写规范
JSX的顶层只能有一个根元素,所以我们很多时候会在外层包裹一个div原生(或者使用后面我们学习的Fragment);
为了方便阅读,我们通常在jsx的外层包裹一个小括号(),这样可以方便阅读,并且jsx可以进行换行书写;如果不用小括号的话,必须在一行
jSX中的标签可以是单标签,也可以是双标签;
注意:如果是单标签,必须以/>结尾;
3.jsx使用
3.1注释(花括号里面+注释符号)
render() {
return (
<div>
{/*这是一段注释*/}
hello world
</div>
)
}
3.2嵌入数据和表达式,都需要花括号包裹
<!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">app</div>
<!-- 添加react依赖 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
// 在大括号中能够正常显示的内容
counter: 0, // number
name: 'sasha', // string
friends: ['kobe', '66s', 'coderwhy'], // array
// 不能正常显示的内容
test1: null,
test2: undefined,
test3: true, // Boolean
friend: {
name: 'shasha'
}
}
}
getFriend() {
console.log(this);
return this.state.friends[0]
}
// 这是一段注释
render() {
return (
<div>
{/*这是一段注释*/}
<h2>{this.state.counter}</h2>
{/*1.三元表达式*/}
<h2>{this.state.test3 ?'成功':'失败'}</h2>
{/*2.运算符表达式*/}
<h2>{this.state.test3 + ''}</h2>
{/*3.函数调用*/}
<h2>{this.getFriend()}</h2>
{/*以下不可以正常显示*/}
<h2>{this.state.test1}</h2>
<h2>{this.state.test2}</h2>
<h2>{this.state.test3}</h2>
{/*会报错,因为jsx不支持对象成为子类*/}
{/*<h2>{this.state.friend}</h2>*/}
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
</script>
</body>
</html>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JGAiKdP3-1649341100268)(img/jsx使用.png)]
3.3绑定属性
<script type="text/babel">
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
title: '我是标题',
imgUrl: '../../../南充学而思/冰激凌.jpg'
}
}
render() {
const {active} = this.state
return (
<div>
{/*1.绑定普通属性*/}
<h2 title={this.state.title}>我是标题</h2>
<img src={this.state.imgUrl} alt="" width="140px" />
{/*2.绑定class属性*/}
<div className="box">绑定class属性</div>
<div className={"box " + (active ? 'active' : '')}>绑定class属性</div>
{/*3.绑定style*/}
<div style={{color: 'red'}}>绑定style属性</div>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
</script>
3.4绑定事件
在构造函数和render函数中,this可以正常使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H1AQkcXk-1649341100269)(img/react事件绑定时的this指向问题.png)]
<script type="text/babel">
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
counter: 0
}
this.btnClick = this.btnClick.bind(this)
}
render() {
return (
<div>
<button onClick={this.btnClick}>事件绑定</button>
<button onClick={this.add}>+1</button>
<button onClick={() => {
this.sub() // 这里的this就是当前类
}}>-1</button>
</div>
)
}
// 方案一: 显式绑定this
btnClick() {
console.log('click');
console.log(this); // undefined
// 如果没用类里面的属性,那么不用显式绑定this
// 但是如果用了就需要在render函数里面显式绑定this
console.log(this.state.counter);
}
// 方式二: 使用箭头函数
add = () => {
// 箭头函数里面没有绑定this
console.log(this); // 当前类
this.state.counter++
}
// 方案三 在调用的时候使用箭头函数调用
sub() {
console.log(this);
this.state.counter--
}
}
ReactDOM.render(<App />, document.getElementById('app'))
</script>
3.5事件传递参数
箭头函数传参
<!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">app</div>
<button class="btn">原生传递参数</button>
<script>
document.getElementsByClassName('btn')[0].addEventListener('click', (e) => {
console.log(e);
})
</script>
<!-- 添加react依赖 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
movies: ['星际穿越', '大话西游', '盗梦空间', '少年派']
}
}
render() {
return (
<div>
<button onClick={this.btnClick}>react事件绑定--传递参数</button>
<ul>
{
this.state.movies.map((item, i) => {
return <li onClick={(e) => {this.liClick(item, i, e)}}>{item}</li>
})
}
</ul>
</div>
)
}
liClick(movie, index, e) {
console.log(movie, index);
}
btnClick(e) {
console.log(e);
}
}
ReactDOM.render(<App />, document.getElementById('app'))
</script>
</body>
</html>
3.6 react条件渲染
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JoCQDbHz-1649341100270)(img/react条件渲染.png)]
<script type="text/babel">
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
isLogin: true
}
}
render() {
let welcome = null
let btnText = null
// 条件渲染方法一:条件判断
if (this.state.isLogin) {
welcome = <h2>欢迎回来</h2>
} else {
welcome = <h2>请先登录</h2>
}
return (
<div>
{welcome}
{/*方法二: 三元运算符*/}
<button onClick={() => { this.changeLogin() }}>{this.state.isLogin ? '退出' : "登录"}</button>
{/*方法三: 逻辑与*/}
{/*逻辑与:一个条件不成立,那么后面的条件都不会进行判断了*/}
<h2>{this.state.isLogin && '你好呀, saSha'}</h2>
</div>
)
}
changeLogin() {
this.setState({
isLogin: !this.state.isLogin
})
}
}
ReactDOM.render(<App />, document.getElementById('app'))
</script>
v-show效果(改变style属性控制display)
<h2 style={{display: isLogin ? 'block' : 'none'}}>你好呀, saSha</h2>
3.7列表渲染
使用高阶函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bxp9tWJo-1649341100270)(img/列表渲染.png)]
4.jsx的本质
React.creatElement()与vue的h函数类似
<script type="text/babel">
const message1 = <h2>hello world</h2>
// jsx语法其实是React.createElement()的语法糖
// 所以render中message1和message2的效果一样
const message2 = React.createElement('h2', null, 'hello world')
ReactDOM.render(message2, document.getElementById('app'))
</script>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fBb9v9BC-1649341100271)(img/jsx的本质.png)]
5.虚拟DOM
<script type="text/babel">
class App extends React.Component {
constructor(props) {
super(props)
}
// render() {
// return (
// <div>
// <h2>我是标题</h2>
// <button>按钮</button>
// <div class="content">
// <p>我是页面内容</p>
// </div>
// <div class="footer">
// <p>我是尾部内容</p>
// </div>
// </div>
// )
// }
// 上面的代码实质上是下面的代码,经过babel转换的
render() {
let eleObject = /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h2", null, "\u6211\u662F\u6807\u9898"), /*#__PURE__*/React.createElement("button", null, "\u6309\u94AE"), /*#__PURE__*/React.createElement("div", {
className: "content"
}, /*#__PURE__*/React.createElement("p", null, "\u6211\u662F\u9875\u9762\u5185\u5BB9")), /*#__PURE__*/React.createElement("div", {
className: "footer"
}, /*#__PURE__*/React.createElement("p", null, "\u6211\u662F\u5C3E\u90E8\u5185\u5BB9")));
// 本质是一个对象树,这个对象树就是虚拟dom
console.log(eleObject);
return eleObject
}
}
// ReactDOM.render()这个函数将虚拟dom树变成真实dom
ReactDOM.render(<App />, document.getElementById('app'))
</script>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-73c0Miwi-1649341100271)(img/虚拟Dom.png)]
5.1为什么使用虚拟dom
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gfyX9Cuv-1649341100272)(img/为什么使用虚拟dom.png)]
5.2频繁操作dom的问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uBZNjBD4-1649341100273)(img/频繁操作dom的问题.png)]
5.3声明式编程
-
虚拟DOM帮助我们从命令式编程转到了声明式编程的模式
-
**React官方的说法:**Virtual DOM 是一种编程理念。
- 在这个理念中,UI以一种理想化或者说虚拟化的方式保存在内存中,并且它是一个相对简单的JavaScript对象
- 我们可以通过ReactDOM.render让 虚拟DOM 和 真实DOM同步起来,这个过程中叫做协调(Reconciliation);
-
这种编程的方式赋予了React声明式的API:
-
你只需要告诉React希望让UI是什么状态;
-
React来确保DOM和这些状态是匹配的;
-
你不需要直接进行DOM操作,只可以从手动更改DOM、属性操作、事件处理中解放出来;
四、以上所学可以做的案例
-
reduce的使用
-
filter的使用
-
条件渲染
-
列表渲染(map的使用)
-
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9YuIlUSq-1649341100273)(img/gouwucheanli.png)]
<!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>
<style>
table {
border: 1px solid #ddd;
border-collapse: collapse;
text-align: center;
}
td,
th {
border: 1px solid #ddd;
padding: 10px 16px;
}
th {
background-color: #ccc;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<div id="app">app</div>
<!-- 添加react依赖 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="./fomat-price.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
books: [
{
id: 1,
name: '《算法导论》',
date: '2006-9',
price: 85.00,
count: 1
},
{
id: 2,
name: '《UNIX编程艺术》',
date: '2006-2',
price: 59.00,
count: 1
},
{
id: 3,
name: '《编程珠玑》',
date: '2008-10',
price: 39.00,
count: 1
},
{
id: 4,
name: '《代码大全》',
date: '2006-3',
price: 128.00,
count: 1
},
]
}
}
renderBooks() {
return (
<div>
<table>
<thead>
<tr>
<th>#</th>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{
this.state.books.map((item, i) => {
return (
<tr>
<td>{i + 1}</td>
<td>{item.name}</td>
<td>{item.date}</td>
<td>{item.price}</td>
<td>
<button disabled={item.count <= 1} onClick={() => { this.changeBookCount(i, -1) }}>-</button>
<span>{item.count}</span>
<button onClick={() => { this.changeBookCount(i, +1) }}>+</button>
</td>
<td><button onClick={() => {
this.subBook(i)
}}>移除</button></td>
</tr>
)
})
}
</tbody>
</table>
<h2>总价格: {this.getTotalPrice()}</h2>
</div>
)
}
render() {
return this.state.books.length !== 0 ? this.renderBooks() : <h2>购物车为空~</h2>
}
getTotalPrice() {
// let sum = 0;
// for(let item of this.state.books) {
// sum += item.count * item.price
// }
// return formatPrice(sum)
// 回调函数的参数
// 参数一: 上一次回调函数的结果(没有回调就使用初始值)
const totalPrice = this.state.books.reduce((preValue, item) => {
return preValue + item.count * item.price
}, 0)
return formatPrice(totalPrice)
}
subBook(i) {
let newBooks = this.state.books.filter((item, index) => {
return index != i
})
this.setState({
books: newBooks
})
}
changeBookCount(i, count) {
let newBooks = [...this.state.books]
newBooks[i].count += count
this.setState({
books: newBooks
})
}
}
ReactDOM.render(<App />, document.getElementById('app'))
</script>
</body>
</html>