React02
前端三大框架中, 来自 Facebook 公司提供的 React.
基本理念: 简化原生DOM操作. 函数封装简化 => 自制 JSX 语法简化
- angular, vue: 在html 书写 JS 代码
- react : 在 JS 中书写 HTML
组件制作方式
组成页面的零件 – 复用
- 函数
- 函数名必须大驼峰
- 类
- 必须继承父类: React.Component
- 固定的 render() 方法, 返回页面
脚手架
npm i -g create-react-app
create-react-app 包名
启动: npm start
localhost:3000
事件
onClick={this.方法名}
注释方法中的this指向
普通函数: 使用bind 绑定this
this.函数.bind(this, 其它参数)
采用箭头函数保障this
()=> this.方法名(参数)
状态值
state 和 setState
数据变化 会同步影响UI 的数据 都应该存放在state中
setState() 来更新时数据, 同步就会刷新UI
setState() 具有更新UI的特征: 有时也用来调用 实现UI的刷新
样式
style={ {属性名: 值, 属性名:值} }
外部样式css的引入: 必须带 路径标识 ./ / ../
import './App.css'
className="样式类"
双向数据绑定
- value: 负责 数据绑定到UI
- onChange: UI变化 绑定变化值给 数据项
<input value={this.state.xxx} onChange={event => this.setState({xxx: event.target.value})
// rcc
import React, { Component } from "react";
import "./App.css";
export default class App extends Component {
// 名称 价格 数量
// 分析: 数量变化 会导致 UI变化的值 -- 放state中
state = { count: 5, uname: "", upwd: "", msg: "", msgColor: "" };
//不会变化的值普通属性即可
name = "iPhone";
price = 8999;
// 特征: 普通函数 && 事件触发 && 其中用this
// bind 或 箭头函数调用: 保障this指向
_doMinus() {
this.setState({ count: this.state.count - 1 });
}
// 推荐: 方法最好是 箭头函数 -- 直接规避this指向问题
_countChanged = (event) => {
let count = event.target.value;
// 只有数字才更新给数量
// ^开头 $结尾 \d数字 * 0个及以上
if (/^\d*$/.test(count)) {
this.setState({ count });
}
};
// 事件函数:
// * 普通函数: 有this指向问题的风险要处理
// * 箭头函数: 没有this指向问题
_doLogin = () => {
//doudou 123456
if (this.state.uname == "doudou" && this.state.upwd == "123456") {
this.setState({ msg: "登录成功", msgColor: "green" });
} else {
this.setState({ msg: "登录失败", msgColor: "red" });
}
};
render() {
return (
<div>
{/* 作业2 */}
<div>
<div>
<input
value={this.state.uname}
onChange={(event) => this.setState({ uname: event.target.value })}
/>
</div>
<div>
<input
type="password"
value={this.state.upwd}
onChange={(event) => this.setState({ upwd: event.target.value })}
/>
</div>
<button onClick={this._doLogin}>登录</button>
<h4 style={{ color: this.state.msgColor }}>{this.state.msg}</h4>
</div>
{/* 作业1 */}
<div className="goods-cell">
<span>{this.name}</span>
<span>¥{this.price}</span>
<div>
<button
onClick={() => this._doMinus()}
disabled={this.state.count == 1}
>
-
</button>
<button
onClick={this._doMinus.bind(this)}
disabled={this.state.count == 1}
>
-
</button>
<input
value={this.state.count}
style={{ width: "25px", textAlign: "center" }}
onChange={this._countChanged}
/>
<button
onClick={() => this.setState({ count: this.state.count + 1 })}
>
+
</button>
</div>
<span>¥{this.state.count * this.price}</span>
</div>
</div>
);
}
}
.goods-cell {
width: 400px;
display: flex;
justify-content: space-evenly;
border-radius: 3px;
border: 1px solid purple;
margin: 10px auto;
padding: 5px;
}
条件渲染
vue中: v-if
angular: *ngIf
// 条件渲染: if
// rcc
import React, { Component } from "react";
export default class App extends Component {
state = { score: 60 };
// 条件渲染: 依赖原生的if判断 -- 制作函数 根据不同条件返回不同的 JSX
// 非事件触发: 不涉及this指向问题
showRes() {
// setState(): 会刷新所有 数据变更时 相关 的UI
let score = this.state.score;
if (score < 60) {
return <p>很遗憾, 您的成绩不及格!</p>;
} else if (score >= 60 && score < 90) {
return <b>您的成绩良好!</b>;
} else {
return <h2>恭喜您, 成绩优秀!</h2>;
}
}
showRank() {
let score = this.state.score;
if (score < 20) {
return <p>黑铁</p>;
} else if (score < 40) {
return <p>青铜</p>;
} else if (score < 60) {
return <p>白银</p>;
} else if (score < 80) {
return <p>黄金</p>;
} else {
return <p>铂金</p>;
}
}
render() {
return (
<div>
{/* {}中的代码 在页面渲染时 会自动触发! */}
{this.showRes()}
<h3>您的分数是:{this.state.score} </h3>
<button onClick={() => this.setState({ score: this.state.score - 10 })}>
减分
</button>
<button onClick={() => this.setState({ score: this.state.score + 10 })}>
加分
</button>
{this.showRank()}
</div>
);
}
}
练习
// 条件渲染: 根据某个值 来决定显示什么UI
import React, { Component } from "react";
export default class App extends Component {
state = { isLogin: false, uname: "" }; // isLogin 自制属性, 存储登录状态
// 条件渲染 依赖函数
show() {
if (this.state.isLogin) {
return (
<div>
<span>
欢迎, <b>{this.state.uname}</b>
</span>
<button onClick={() => this.setState({ isLogin: false })}>
退出
</button>
</div>
);
} else {
return (
<div>
<input
type="text"
value={this.state.uname}
onChange={(event) => this.setState({ uname: event.target.value })}
/>
<button onClick={() => this.setState({ isLogin: true })}>登录</button>
</div>
);
}
}
render() {
return <div>{this.show()}</div>;
}
}
列表渲染
vue中: v-for
angular: *ngFor
// 列表渲染 for
import React, { Component } from "react";
export default class App extends Component {
emps = [
{ name: "凯凯", age: 29, gender: 1 },
{ name: "蒙蒙", age: 19, gender: 0 },
{ name: "涛涛", age: 25, gender: 1 },
{ name: "小月", age: 16, gender: 0 },
{ name: "欣哥", age: 42, gender: 1 },
];
names = ["亮亮", "东东", "然然", "小新", "华哥"];
names_btns = [
<button>亮亮</button>,
<button>东东</button>,
<button>然然</button>,
<button>小新</button>,
<button>华哥</button>,
];
// 自动化处理: 使用for循环遍历数组, 每一项放到 jsx 语法中, 形成新的数组
showBtns() {
let arr = [];
this.names.forEach((item, index) => {
// react要求: 列表中的每一项必须有唯一标识key
let btn = <button key={index}>{item}</button>;
arr.push(btn);
});
return arr;
}
showLis() {
//1.空数组
let arr = [];
//2.遍历每一项, 改造成JSX 然后添加到数组中
this.names.forEach((item, index) => {
arr.push(<li key={index}>{item}</li>);
});
//3.返回含有JSX的数组
return arr;
}
showTrs() {
let arr = [];
this.emps.forEach((item, index) => {
// react中不存在 过滤器 和 管道的说法 {{ xxx | pipe }}
// 为什么要有管道/过滤器: vue/angular 在html中写js不方便
// react就是JS 不存在写JS不方便的情况
let sex = ["女", "男"][item.gender]; // 数组[序号]
arr.push(
<tr key={index}>
<td>{index + 1}</td>
<td>{item.name}</td>
<td>{item.age}</td>
<td>{sex}</td>
</tr>
);
});
return arr;
}
render() {
return (
<div>
{/* 数组的默认处理: 自动把数组每一项形式到页面上 */}
<div>{this.names}</div>
{/* <div>{this.names_btns}</div> */}
<div>{this.showBtns()}</div>
<ul>{this.showLis()}</ul>
<table border="1">
{/* DOM对 结构要求严格, tr应该出现在 tbody thead 或 tfoot 里 */}
<thead>
<tr>
<td>序号</td>
<td>姓名</td>
<td>年龄</td>
<td>性别</td>
</tr>
</thead>
<tbody>{this.showTrs()}</tbody>
</table>
</div>
);
}
}
map函数简化 列表渲染
// map方法 简化 列表渲染
import React, { Component } from "react";
export default class App extends Component {
names = ["lucy", "lily", "john", "mike"];
// 语法糖: ()=> { return xxxx; } 简化为 () => xxx
showBtns = () =>
this.names.map((item, index) => <button key={index}>{item}</button>);
showLis = () => this.names.map((item, index) => <li key={index}>{item}</li>);
// showBtns() {
// return this.names.map((item, index) => {
// return <button key={index}>{item}</button>;
// });
// // return arr;
// }
render() {
return (
<div>
{this.showBtns()}
<ul>{this.showLis()}</ul>
</div>
);
}
}
生命周期
不同的组件, 从出生 到 销毁 之间经历的过程: 生命周期
过程中的关键节点 都会触发对应的函数 — 钩子函数
// 生命周期
//rcc
import React, { Component } from "react";
export default class App extends Component {
state = { show: false, age: 10 };
render() {
return (
<div>
<button onClick={() => this.setState({ show: !this.state.show })}>
切换Son组件的显示
</button>
{this.state.show ? <Son age={this.state.age} /> : ""}
<button onClick={() => this.setState({ age: this.state.age * 2 })}>
增加age: {this.state.age}
</button>
</div>
);
}
}
//子组件
class Son extends Component {
state = { num: 1 };
componentDidMount() {
// 挂载时: 同vue的 mounted
console.log("componentDidMount: 组件显示时, 挂载");
}
// 面试问题: 如何提高 React 渲染效率
// 此生命周期的返回值 将会决定页面要不要刷新
shouldComponentUpdate(props, state) {
// 传入的值: 即将变成什么样, 问: 要不要刷新UI
console.log("shouldComponentUpdate:", state);
// num为偶数不更新
if (state.num % 2 == 0) {
console.log("num是偶数, 不刷新UI");
return false;
} //false代表不更新UI
console.log("num是奇数, 刷新UI");
return true;
}
// props: 父子传参的值, 类似于vue 的 props 属性
// <Son name="东东" /> 则 props = {name:'东东'}
componentDidUpdate(old_props, old_state) {
// 数据发生更新
// 参数都是发生更新之前的值
console.log("componentDidUpdate: 更新前", old_props, old_state);
console.log("componentDidUpdate: 更新后", this.props, this.state);
}
componentWillUnmount() {
//将要卸载时: 同vue的 destroy
console.log("componentWillUnmount: 组件将要消失时, 卸载");
}
render() {
return (
<div>
<h3>我是Son组件</h3>
<button onClick={() => this.setState({ num: this.state.num + 1 })}>
{this.state.num}
</button>
</div>
);
}
}
网络请求
然哥: ajax
东哥: axios
华哥: 小程序 wx.request
小新: angular的网络服务 this.http.get().subscribe()
React 本身不具备网络请求模块, 此处与vue相同, 使用 axios
到项目下, 执行 aixos 安装命令
npm i axios
日期处理库 moment
http://momentjs.cn/
安装
npm i moment
// 练习
import React, { Component } from "react";
import axios from "axios";
// 引入 JS 日期处理模块: 需要安装 npm i moment
import moment from "moment";
import "./App.css";
export default class App extends Component {
state = { data: null };
componentDidMount() {
this.getNews(1);
}
getNews(pno) {
let url =
"http://101.96.128.94:9999/mfresh/data/news_select.php?pageNum=" + pno;
axios.get(url).then((res) => {
console.log(res);
this.setState({ data: res.data });
});
}
showNews = () =>
this.state.data.data.map((item, index) => {
// 箭头函数的语法糖不支持复杂方法体. 只能改为非语法糖写法
// 时间戳 -> 年-月-日
// moment(日期类型).format(格式化的样子)
// pubTime:是字符串类型, 需要转数字类型才能给 Date() 使用
// http://momentjs.cn/docs/#/displaying/format/
let date = moment(new Date(item.pubTime * 1)).format("YYYY-MM-DD");
return (
<div key={index} className="news-cell">
<span>{item.title}</span>
<span>{date}</span>
</div>
);
});
showPages() {
// 语法糖, 快速解包
let { pageCount, pageNum } = this.state.data;
// let pageCount = this.state.data.pageCount
// let pageNum = this.state.data.pageNum
let arr = [];
// 极限值
if (pageNum == 1) {
arr.push(
<span key="prev" className="disabled">
上一页
</span>
);
} else {
arr.push(
<span key="prev" onClick={this.getNews.bind(this, pageNum - 1)}>
上一页
</span>
);
}
for (let i = 1; i <= pageCount; i++) {
arr.push(
<span
key={i}
className={pageNum == i ? "cur" : ""}
onClick={() => this.getNews(i)}
>
{i}
</span>
);
}
// 极限值
if (pageNum < pageCount) {
arr.push(
<span key="next" onClick={this.getNews.bind(this, pageNum + 1)}>
下一页
</span>
);
} else {
arr.push(
<span key="next" className="disabled">
下一页
</span>
);
}
return arr;
}
render() {
if (!this.state.data) return <div></div>;
return (
<div className="news">
<div className="content">{this.showNews()}</div>
<div className="pages">{this.showPages()}</div>
</div>
);
}
}
.goods-cell {
width: 400px;
display: flex;
justify-content: space-evenly;
border-radius: 3px;
border: 1px solid purple;
margin: 10px auto;
padding: 5px;
}
.news {
width: 700px;
margin: 0 auto;
}
.news-cell {
display: flex;
justify-content: space-between;
border-bottom: 1px dashed gray;
padding: 8px;
}
.pages {
text-align: center;
margin-top: 10px;
user-select: none;
}
.pages > span {
padding: 2px 8px;
display: inline-block;
border-radius: 4px;
border: 1px solid gray;
color: gray;
margin: 0 2px;
}
.pages > span:not(.disabled):hover,
.pages > .cur {
background-color: orange;
color: white;
border-color: orange;
}
.pages > .disabled {
color: lightgray;
border-color: lightgray;
}
ReactNative
提前说:
- 有Android手机同学, 最好带数据线 — 要真机调试
- 没有Android 手机 , 有模拟器 比较卡.
- 有 iPhone同学, 必须搭配 苹果Mac 电脑使用!
- 要想发布软件 给别人用 还要交700元 给Apple
- windows电脑
- 明天带着搭建编译环境, 保证可用!
- mac电脑
- 靠自强! https://reactnative.cn/docs/getting-started.html
.pages > span:not(.disabled):hover,
.pages > .cur {
background-color: orange;
color: white;
border-color: orange;
}
.pages > .disabled {
color: lightgray;
border-color: lightgray;
}