目录
一、邂逅react开发和初体验
1、简介
* react是什么?
- 用于构建用户界面的javascript库
* 常见的javascript库
- jquery
- vue
- react
- angular
* react的起源
- 2013年,facebook开源的javascript库
* react的特点-声明式编程
- ui = f(state)
* react的特点-组件化开发
- 复杂的界面拆分成一个个小的组件
* react的特点-多平台适配
- 2013:web
- 2015:reactnative用于移动端开发
- 2017:reactvr用于开发虚拟现实web应用程序
* 掌握最先进的思想和技术
- react hooks的思想
- flutter(widget-element-renderobject)对应react(jsx-虚拟dom-真实dom)
* hackerrank调查显示
* react在国内外被广泛使用
- 网易云音乐
- 斗鱼
- 知乎
- 阿里云
- 优酷
2、hello react案例
- 原生实现
<h2 id="text"></h2>
<button id="btn">改变文本</button>
<script>
// 命令式编程:每做一个操作,都是给计算机(浏览器)一步步命令
// 声明式编程:
// 1、定义数据
let message = 'hello world'
let text = document.getElementById("text")
let btn = document.getElementById("btn")
// 2、将数据显示在h2元素中
text.innerHTML = message
// 3、点击按钮,界面的数据发生变化
btn.onclick = function () {
message = 'hello react'
text.innerHTML = message
}
</script>
- react实现
<div id="app"></div>
<!-- crossorigin:这个属性的目的是为了拿到跨域脚本的错误信息 -->
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- 注意事项:使用jsx,并且希望script中的jsx的代码被解析,必须在script标签中添加一个属性 -->
<!-- jsx特点:多个标签最外层(根)只能有一个标签 -->
<script type="text/babel">
/**
* 一、react开发依赖
* - react:核心代码
* - react-dom:跨平台
* - babel:代码转换工具
* 二、react-dom
* - web端:将jsx渲染成真实dom
* - native端:将jsx渲染成原生的控件(android的button,ios的uibutton)
* 三、babel
* - es6 -> babel -> es5
* - jsx(语法糖) -> babel -> React.renderElement()
* 四、引入react依赖
* - 直接cdn引入
* - 下载后,添加本地依赖
* - 通过npm管理(后续脚手架再使用)
*/
let message = "hello world"
function btnClick() {
message = 'hello react'
render()
}
function render() {
// <div></div>:jsx代码
ReactDOM.render(<div>
<h2>{message}</h2>
<button onClick={btnClick}>改变文本</button>
</div>, document.getElementById('app'))
}
render()
</script>
- 组件化实现
<div id="app"></div>
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script type="text/babel">
// 封装app组件
class App extends React.Component {
constructor() {
super();
this.state = {
message: "hello world"
}
}
render() {
return (
<div>
<h2>{this.state.message}</h2>
<button onClick={this.btnClick.bind(this)}>改变文本</button>
</div>
)
}
btnClick() {
this.setState({
message: 'hello react'
})
}
}
// 渲染组件
ReactDOM.render(<App/>, document.getElementById("app"))
</script>
二、js核心语法
1、es6的class
/*
// es5
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.running = function () {
console.log(`姓名:${this.name};年龄:${this.age}`)
}*/
// es6
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
running() {
console.log(`姓名:${this.name};年龄:${this.age}`)
}
}
/**
* 一、this的四种绑定方式
* - 独立函数调用:fun()
* - 隐式绑定:p.fun()
* - 显示绑定:fun.apply(p)
* - new绑定:new P()
* 二、箭头函数不绑定this,this从上层作用域获取
*/
const person = new Person('孟美岐', 18);
person.running()
2、类的继承
/**
* 一、面向对象的三大特性
* - 封装
* - 继承:1、减少重复的代码;2、多态的前提(鸭子类型)
* - 多态
*/
class Person {
constructor(name) {
this.name = name
}
running() {
console.log('父类方法')
}
}
class Student extends Person {
constructor(name, age) {
super(name);
this.age = age
}
eating() {
console.log(this.name, this.age)
}
}
const student = new Student('黄婷婷', 18);
student.running()
student.eating()
三、jsx核心语法
1、案例一:电影列表
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
title: "女神列表",
girls: ['黄婷婷', '孟美岐', '鞠婧祎']
}
}
render() {
const girls = this.state.girls.reduce((src, item) => {
src.push(<li>{item}</li>)
return src
}, [])
return (<div>
<div>
<h2>{this.state.title}一</h2>
<ul>{girls}</ul>
</div>
<div>
<h2>{this.state.title}二</h2>
<ul>{
this.state.girls.map(item => <li>{item}</li>)
}</ul>
</div>
</div>)
}
}
ReactDOM.render(<App/>, document.getElementById("app"));
</script>
2、案例二:计数器案例
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
counter: 0
}
}
render() {
return (<div>
<div>{this.state.counter}</div>
<button onClick={this.increment.bind(this)}>+1</button>
<button onClick={this.decrement.bind(this)}>-1</button>
</div>)
}
increment() {
this.setState({
counter: this.state.counter + 1
})
}
decrement() {
this.setState({
counter: this.state.counter - 1
})
}
}
ReactDOM.render(<App/>, document.getElementById("app"));
</script>
3、认识jsx
<!-- 引入babel(standlone:单机) -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- 属性必须设置 -->
<script type="text/babel">
// jsx语法
const element = <div>hello world</div>
ReactDOM.render(element, document.getElementById("app"));
</script>
4、为什么react选择了jsx
* react认为渲染逻辑本质上与其他ui逻辑存在内在耦合(all in js)
* 他们之间是密不可分,所以react没有将标记分离到不同的文件中,而是将它们组合到了一起,这个地方就是组件
* jsx书写规范
- 顶层只能有一个根标签,通常是原生div(或者使用后面我们学习的Fragment)
- 通常会在jsx的外层包裹一个小括号(),方便阅读并且jsx可以进行换行书写
- 可以是双标签或单标签(单标签必须以/>结尾)
5、jsx中的注释
<!-- html注释 -->
<script type="text/babel">
/**
* 文档注释
*/
class App extends React.Component {
constructor() {
super();
/*
// 多行注释
this.state = {
}*/
}
// 单行注释
render() {
return (<div>
{/* jsx的注释 */}
</div>)
}
}
ReactDOM.render(<App/>, document.getElementById("app"));
</script>
6、jsx嵌入数据
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
// 1、在{}中可以正常显示的内容
name: '黄婷婷',// String
age: 18,// Number
hobbies: ['篮球', '足球', '乒乓球'],// Array
// 2、在{}中不能显示(忽略)
test1: null,// null
test2: undefined,// undefined
test3: true,// Boolean
flag: true,
// 3、对象不能作为jsx的子类
friend: {
name: '姜贞羽',
age: 19
}
}
}
render() {
return (<div>
<div>{this.state.name}</div>
<div>{this.state.age}</div>
<div>{this.state.hobbies}</div>
<div>{this.state.test1}</div>
<div>{String(this.state.test1)}</div>
<div>{this.state.test2}</div>
<div>{this.state.test2 + ''}</div>
<div>{this.state.test3}</div>
{/* null/undefined不可以使用toString()转字符串 */}
<div>{this.state.test3.toString()}</div>
<div>{this.state.flag ? '孟美岐' : null}</div>
<div>{this.state.flag && '佟丽娅'}</div>
{/* <div>{this.state.friend}</div> */}
</div>)
}
}
ReactDOM.render(<App/>, document.getElementById("app"));
</script>
7、jsx嵌入表达式
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
firstName: '黄婷婷',
lastName: '孟美岐',
isLogin: true
}
}
render() {
const {firstName, lastName, isLogin} = this.state
return (<div>
{/* 1、运算符表达式 */}
<div>{firstName + ' ' + lastName}</div>
<div>{20 * 50}</div>
{/* 2、三元表达式 */}
<div>{isLogin ? '欢迎回来~' : '请先登录~'}</div>
{/* 3、进行函数调用 */}
<div>{this.getFullName()}</div>
</div>)
}
getFullName() {
return this.state.firstName + ' ' + this.state.lastName
}
}
ReactDOM.render(<App/>, document.getElementById("app"));
</script>
8、jsx绑定属性
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
function getSizeImage(imgUrl, size) {
return imgUrl + `?param=${size}y${size}`
}
class App extends React.Component {
constructor() {
super();
this.state = {
title: "标题",
imgUrl: "http://p1.music.126.net/KcUB_qLaNKB1maFWtQIXtQ==/109951163559331646.jpg",
link: "https://www.baidu.com",
active: true
}
}
render() {
const {title, imgUrl, link, active} = this.state
return (<div>
{/* 1、绑定普通属性 */}
<h2 title={title}>我是标题</h2>
<img src={getSizeImage(imgUrl, 140)} alt=""/>
<a href={link} target="_blank">百度一下</a>
{/* 2、绑定class */}
<div className="box title">我是div元素</div>
<div className={"box title " + (active ? 'active' : "")}>我也是div元素</div>
<label htmlFor=""></label>
{/* 3、绑定style */}
<div style={{color: 'red', fontSize: '50px'}}>我是div,绑定style属性</div>
</div>)
}
}
ReactDOM.render(<App/>, document.getElementById("app"));
</script>
9、jsx绑定事件和this处理
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
message: "你好啊",
counter: 100
}
this.btnClick = this.btnClick.bind(this)
}
render() {
return (<div>
{/* 1、方案一:bind绑定this(显示绑定) */}
<button onClick={this.btnClick}>按钮1</button>
{/* 2、方案二:定义函数时,使用箭头函数 */}
<button onClick={this.increment}>+1</button>
{/* 3、方案三(推荐):直接传入一个箭头函数,在箭头函数中调用需要执行的函数 */}
<button onClick={() => {
this.decrement('黄婷婷')
}}>-1
</button>
</div>)
}
btnClick() {
console.log(this.state.message)
}
// 箭头函数中永远不绑定this
// es6中给对象增加属性:class fields
increment = () => {
console.log(this.state.counter)
}
decrement(name) {
console.log(this.state.counter, name)
}
}
ReactDOM.render(<App/>, document.getElementById("app"));
</script>
10、jsx绑定事件-参数传递
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
persons: ['黄婷婷', '孟美岐', '鞠婧祎']
}
this.btnClick = this.btnClick.bind(this)
}
render() {
return (<div>
<button onClick={this.btnClick}>按钮1</button>
<ul>{
this.state.persons.map((item, index) => {
return (<li
className="item"
title="li"
onClick={e => {
this.liClick(item, index, e)
}}>
{item}
</li>)
})
}</ul>
</div>)
}
btnClick(event) {
console.log('按钮发生了点击', event)
}
liClick(item, index, event) {
console.log('li发生了点击', item, index, event)
}
}
ReactDOM.render(<App/>, document.getElementById("app"));
</script>
11、条件渲染
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
isLogin: true
}
}
render() {
const {isLogin} = this.state
// 1、通过if判断:逻辑代码非常多的情况
let welcome = null
if (isLogin) {
welcome = <h2>欢迎回来~</h2>
} else {
welcome = <h2>请先登录~</h2>
}
return (<div>
{welcome}
{/* 2、方案二:三元运算符 */}
<button onClick={e => this.loginClick()}>{isLogin ? '退出' : '登录'}</button>
<hr/>
<h2>{isLogin ? '你好啊,黄婷婷' : null}</h2>
{/* 3、方案三:逻辑与&& */}
{/* 逻辑与:一个条件不成立,后面的条件都不会进行判断了 */}
<h2>{isLogin && '你好啊,张婧仪'}</h2>
{isLogin && <h2>你好啊,孟美岐</h2>}
</div>)
}
loginClick() {
this.setState({
isLogin: !this.state.isLogin
})
}
}
ReactDOM.render(<App/>, document.getElementById("app"));
</script>
12、条件渲染-v-show效果
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
isLogin: true
}
}
render() {
const {isLogin} = this.state
const titleDisplayValue = isLogin ? 'block' : 'none'
return (<div>
<button onClick={e => this.loginClick()}>{isLogin ? '退出' : '登录'}</button>
<h2 style={{display: titleDisplayValue}}>你好啊,黄婷婷</h2>
</div>)
}
loginClick() {
this.setState({
isLogin: !this.state.isLogin
})
}
}
ReactDOM.render(<App/>, document.getElementById("app"));
</script>
13、列表渲染
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
persons: ['黄婷婷', '孟美岐', '张婧仪'],
numbers: [5, 8, 3, 4, 9, 2, 7, 6, 1]
}
}
render() {
return (<div>
<h2>名字列表(映射)</h2>
<ul>{this.state.persons.map(item => <li>{item}</li>)}</ul>
<h2>数字列表(过滤)</h2>
<ul>{this.state.numbers.filter(item => item >= 5).map(item => <li>{item}</li>)}</ul>
<h2>数字列表(截取)</h2>
<ul>{this.state.numbers.slice(0, 5).map(item => <li>{item}</li>)}</ul>
</div>)
}
}
ReactDOM.render(<App/>, document.getElementById("app"));
</script>
四、jsx的本质
1、jsx的本质
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
/**
* 1、jsx是React.createElement(component,props,...children)函数的语法糖
* - jsx -> babel -> React.createElement()
* 2、babel官网中快速查看转换的过程
* - https://babeljs.io/repl/#?presets=react
*/
const message1 = <h2>hello world</h2>
const message2 = React.createElement("h2", null, "hello world")
ReactDOM.render(message2, document.getElementById("app"));
</script>
2、虚拟dom的创建过程
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
}
/**
* 1、我们通过React.createElement最终创建出来一个ReactElement对象
* 2、这个ReactElement对象是什么作用呢?React为什么要创建它呢?
* - 原因是React利用ReactElement对象组成了一个javascript的对象树
* - javascript的对象树就是大名鼎鼎的虚拟DOM(virtual dom)
* 3、如何查看ReactElement的树结构呢?
* - 我们可以将之前的jsx返回结果进行打印
* - 注意下面代码中我打jsx的打印
* 4、而ReactElement最终形成的树结构就是virtual dom
* 5、jsx-虚拟dom-真实dom
* - jsx代码-ReactElement对象-真实dom
* 6、为什么使用虚拟dom,而不是直接修改真实的dom呢?
* - 很难跟踪状态发生的改变:原有的开发模式,我们很难跟踪到状态发生的改变,不方便针对我们应用程序进行调试
* - 操作真实dom性能较低:传统的开发模式会进行频繁的dom操作,而这一的做法性能非常的低
* ~ document.createElement本身创建出来的就是一个非常复杂的对象
* ~ dom操作会引起浏览器的回流和重绘,所以在开发中应该避免频繁的dom操作
* 7、声明式编程
* - virtual dom是一种编程理念
* ~ 在这个理念中,ui以一种理想化或者说虚拟化的方式保存在内存中,并且它是一个相对简单的javascript对象
* ~ 我们可以通过ReactDOM.render让虚拟dom和真实dom同步起来,这个过程中叫做协调(Reconciliation)
* - 这种编程的方式赋予了react声明式的api
* ~ 你只需要告诉react希望让ui是什么状态
* ~ react来确保dom和这些状态是匹配的
* ~ 你不需要直接进行dom操作,就可以从手动更改dom、属性操作、事件处理中解放出来
*/
render() {
const objTree = (<div>
<h2>苏州</h2>
<h2>无锡</h2>
<h2>常州</h2>
</div>)
console.log(objTree)
return objTree
}
}
ReactDOM.render(<App/>, document.getElementById("app"));
</script>
3、案例练习
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>react</title>
<style>
table {
border-collapse: collapse;
}
td, th {
border: 1px solid #cccccc;
padding: 10px 16px;
}
th {
background: #eeeeee;
}
.price {
margin: 0 6px;
}
</style>
</head>
<body>
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
books: [
{
id: 1,
name: '《算法导论》',
date: '2022-1',
price: 85.5,
count: 1
}, {
id: 2,
name: '《unix编程艺术》',
date: '2022-2',
price: 59.6,
count: 1
}, {
id: 3,
name: '《编程珠玑》',
date: '2022-3',
price: 39.8,
count: 1
}, {
id: 4,
name: '《代码大全》',
date: '2022-4',
price: 128,
count: 1
}]
}
}
render() {
return this.state.books.length ? (<div>
<table>
<thead>
<tr>
<th></th>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{this.state.books.map((item, index) => {
return (<tr key={item.id}>
<td>{item.id}</td>
<td>{item.name}</td>
<td>{item.date}</td>
<td>¥{item.price.toFixed(2)}</td>
<td>
<button disabled={item.count <= 1} onClick={() => {
this.changeCount(index, -1)
}}>-
</button>
<span className="price">{item.count}</span>
<button onClick={() => {
this.changeCount(index, 1)
}}>+
</button>
</td>
<td>
<button onClick={e => {
this.removeItem(e, index)
}}>移除
</button>
</td>
</tr>)
})}
</tbody>
</table>
<h3>总价格:¥{this.countPrice()}</h3>
</div>) : (<h2>购物车为空~</h2>)
}
changeCount(index, calculate) {
const c_books = [...this.state.books]
c_books[index].count += calculate
this.setState({
books: c_books
})
}
removeItem(e, index) {
// 保留原始合成事件,解决控制台查看合成事件对象属性报错(出于性能原因e.persist()一般不调用)
e.persist()
// 1、react中设计原则:state中的数据的不可变性
this.setState({
books: this.state.books.filter((item, indey) => indey !== index)
})
}
countPrice() {
const totalPrice = this.state.books.reduce((total, item) => {
return total += item.price * item.count
}, 0)
return totalPrice.toFixed(2)
}
}
ReactDOM.render(<App/>, document.getElementById("app"));
</script>
</body>
</html>