第二章: React初探
本章主要讲解React项目的开发环境搭建,工程代码结构及React中最基础的语法内容,同时对前端组件化思想进行介绍。
2-1 React简介
Facebook推出
2013年开源
函数式编程
使用人数最多的前端框架
健全的文档与完善的社区
React官网: reactjs.org/
把16以后的reacr叫做Reacter
2-2 React开发环境准备
- 引入.js文件来使用React
- 通过脚手架工具来编码
- react脚手架工具 Create-react-app
打开网址: reactjs.org/docs/create…
下面有两种方式搭建react脚手架
使用npm搭建React脚手架
npm install -g create-react-app
create-react-app my-app
yarn start
cd my-app
npm start
使用npx搭建React脚手架
npx create-react-app my-app
cd my-app
npm start
然后就可以看见你项目的启动界面了
2-4 React中的组件
import React, {component} from 'react'
// 其中的import {component} from 'react'
//等价于
//import React import 'react'
//const Component = React.Component
class App extends Component {
render() {
return (
<div>
hello,dell lee
</div>
);
}
}
export default App;
//或者使用下面的方式实现组件化开发
//现在新版的React都使用下述的方法实现函数
function App() {
return (
<div>
hello,andy
</div>
);
}
export default App;
在src文件夹下的index.js文件中
// src/index.js
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
意思就是说将我们的App内容挂载到public/index.html文件的id="root"的div节点上
// public/index.html
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
2-5 最基础的JSX语法
我们在index.js中引用的App就是自己定义的标签,而在App.js中使用的div标签也是JSX语法
第三章:Reacr基础精讲
3-1 使用React编写Todolist功能
react在使用函数的时候,一个函数的所有的功能最外层必须是一个元素包裹,就是说一个函数的最外层只能有一个层级标签
function TodoList() {
return (
<div >
<div><input type="text"/>
<button>提交</button>
</div>
<ul>
<li>学英语</li>
<li>learn English</li>
</ul>
</div>
);
}
export default TodoList;
但是有的时候我们不想让这个最外层的标签显示,这样的话就是用react中的Fragment代替最外层的div标签
3-2:React中的响应式设计思想和事件绑定
// 两种数据存储
// 一种存储iinput框中的值
// 一种存储显示的列表中的值
现在实现一个输入框,键盘输入什么内容,输入框显示什么内容
首先上面新建了一个TodoList.js的文件,在index.html中已经将TodoList.js挂载到了toor节点上,所以下述的内容皆是TodoList中的内容
1 我们在TodoList类中写一个输入框和提交按钮
import React, {Component, Fragment} from 'react';
class TodoList extends Componebt {
reder() {
return (
<Fragment>
<div>
<input />
<button>提交</button>
</div>
<ul>
<li>学英语</li>
<li>learn English</li>
</ul>
</Fragment>
)
}
}
2 我们使用constructor实现构造函数来继承类的属性
将输入框中显示的值使用一个变量代替,将输入框的值绑定到变量上,在里面实现一个事件,只要输入框中有值,立马就改变输入框的值,我们就可以看见只要是自己输入有数值,就会在输入框中显示出来
import React, {Component, Fragment} from 'react';
class TodoList extends Componebt {
constructor(props) {
super(props);
//组件的状态
this.state = {
inputValue: "hello ",
list: []
}
}
render() {
return (
<Fragment>
<div>
{/*为了让输入框的值只想组件状态的inputValue*/}
<input
type="text"
value={this.state.inputValue}
// 使用bind改变方法的this指向
onChange={this.handleInputChange.bind(this)}
/>
<button>提交</button>
</div>
<ul>
<li>学英语</li>
<li>learn English</li>
</ul>
</Fragment>
)
}
// 输入框中的事件
handleInputChange(e) {
// target对应着input的Dom节点
// console.log(e.target.value);
// console.log(this);
// 不改变this指向的时候,undefined,
// 在调用handleInputChange方法的时候加上.bind(this),就改变了this指向,
// 在本方法中就可以调用其他方法的属性了
// 我们不能直接使用下面的方法来直接改变input框里面的值
// this.state.inputValue=e.target.value;
// 想要改变state的值,必须使用this.setstate({})
this.setState(
{
inputValue: e.target.value
}
)
}
}
3-3: 实现TodoList新增删除功能
import React, {Component, Fragment} from 'react';
class TodoList extends Component {
// 构造函数,super是继承父类的构造函数
constructor(props) {
super(props);
//组件的状态
this.state = {
inputValue: "",
list: ["玩游戏", "吃雪糕"]
}
}
render() {
return (
<Fragment>
<div>
{/*为了让输入框的值只想组件状态的inputValue*/}
<input
type="text"
value={this.state.inputValue}
// 使用bind改变方法的this指向
onChange={this.handleInputChange.bind(this)}
/>
{/*为提交按钮绑定一个点击事件*/}
<button onClick={this.handleBtnClick.bind(this)}>提交</button>
</div>
<ul>
{
// 这一段代码的意思就是使用map循环数组的每一项,获取每一项的值item和索引index
// 然后进行回调,返回li标签,并包含item,也就是每一项的值
this.state.list.map((item, index) => {
return (
<li key={index}
onClick={this.HandleItemDelete.bind(this,index)}>{item}</li>
)
})
}
</ul>
</Fragment>
)
}
// 改变输入框的值得事件
handleInputChange(e) {
// target对应着input的Dom节点
// console.log(e.target.value);
// console.log(this);
// 不改变this指向的时候,undefined,
// 在调用handleInputChange方法的时候加上.bind(this),就改变了this指向,
// 在本方法中就可以调用其他方法的属性了
// 我们不能直接使用下面的方法来直接改变input框里面的值
// this.state.inputValue=e.target.value;
// 想要改变state的值,必须使用this.setstate({})
this.setState(
{
inputValue: e.target.value
}
)
}
// 点击事件
handleBtnClick() {
this.setState({
// [...,this.state.list]就是一个展开运算符,就是将之前list中的数据全部展示,
// 然后后面跟上要添加的数据,然后又将结果辅助给list,即实现列表添加新元素
list: [...this.state.list, this.state.inputValue],
inputValue: "" // 实现input框清空
})
}
// li标签的删除操作
HandleItemDelete(index) {
// immutable
// state 不允许我们做任何的改变
// 拷贝list
const list=[...this.state.list];
list.splice(index,1);
this.setState({
list:list
});
}
}
export default TodoList;
自己实现一个TodoList实例
import React,{Component,Fragment} from 'react'
class TodoList extends Component {
constructor(props) {
super(props);
// 定义一个组件
this.state={
inputValue:"",
list:["张三","李四","王五"]
};
}
render(){
return (
<Fragment>
<div>
<input type="text" value={this.state.inputValue} onChange={this.HandleInputChange.bind(this)}/>
<button onClick={this.HandleBtnClick.bind(this)}>提交</button>
</div>
<ul>
{
this.state.list.map((item,index) => {
return <li key={index} onClick={this.HandleLiClick.bind(this,index)}>{item}</li>
})
}
</ul>
</Fragment>
)
}
// 实现输入框输入的时候显示文本
HandleInputChange(e){
this.setState({inputValue:e.target.value})
}
// 实现提交
HandleBtnClick(){
this.setState(
{
list:[...this.state.list,this.state.inputValue],
inputValue:""
}
)
}
// 点击li实现删除
HandleLiClick(index){
const list=[...this.state.list];
list.splice(index,1);
this.setState({
list:list
});
}
}
export default TodoList;
3-4:JSX语法细节补充
1 以大写字母开头的标签是组件,以小写字母开头的元素就是普通元
2 React的注释{/**/},//,{//}
3 在我们使用class来定位标签添加css样式的时候,会有警告,应为react里面class会被识别成类,所以使用className代替class
实现点击输入框钱文本,光标定位到输入框
在input中添加id="insertArea",然后在label中添加htmlFor="insertArea"
3-5 拆分组件和组件之间的传值
父组件调用子组件
// 在父组件中
import TodoItem from "./TodoItem";
<div>
{/*组件间的传值,直接在子组件后面写content={item},就可以在子组件里面调用content,获取item的值*/}
{/*传参的时候,直接将参数当做标签的属性传过去,也可以将父组件的方法传递过去,但是要绑定父组件的this*/}
<TodoItem content={item} index={index} deleteItem={this.handleItemDelete.bind(this)}/>
</div>
// 在子组件中,调用父组件传过来的参数
class TodoItem extends Component {
constructor (props){
super(props);
this.handleClick = this.handleClick.bind(this)
}
render() {
// 父组件传一个属性一个值content,使用this.props.content来获取参数
return <div onClick={this.handleClick}>{this.props.content}</div>
}
// 子组件如何调用父组件的内容
handleClick(){
this.props.deleteItem(this.props.index)
}
}
export default TodoItem;
3-6 TodoList代码优化
import React, {Component, Fragment} from 'react'
import "./style.css"
import TodoItem from "./TodoItem";
class TodoList extends Component {
constructor(props) {
super(props);
// 定义一个组件
this.state = {
inputValue: "",
list: ["张三", "李四", "王五"]
};
// 为了优化性能,我们把this的绑定写在构造函数中
this.HandleInputChange = this.HandleInputChange.bind(this);
this.HandleBtnClick = this.HandleBtnClick.bind(this);
this.handleItemDelete = this.handleItemDelete.bind(this);
}
render() {
return (
<Fragment>
<div>
<label htmlFor="insertArea">输入内容</label>
<input id="insertArea" className="input" type="text" value={this.state.inputValue}
onChange={this.HandleInputChange}/>
<button onClick={this.HandleBtnClick}>提交</button>
</div>
<ul>
{ this.getTodoItem() }
</ul>
</Fragment>
)
}
// 为了代码更加精简,我们将ul里的代码放到一个方法中,只需要在上render中执行一个这个方法即可
getTodoItem(){
return this.state.list.map((item, index) => {
return (
<div>
{/*组件间的传值,直接在子组件后面写content={item},就可以在子组件里面调用content,获取item的值*/}
<TodoItem
key={index}
content={item}
index={index}
deleteItem={this.handleItemDelete}
/>
</div>
)
})
}
// 实现输入框输入的时候显示文本
HandleInputChange(e) {
// 异步的设置数据
const value = e.target.value;
this.setState( () => ({
inputValue:value
}));
}
// 实现提交
HandleBtnClick() {
// 在修改数据的时候prevState就是修改前的数据,这里的prevState=this.State
this.setState( (prevState)=> ({
list: [...prevState.list, prevState.inputValue],
inputValue: ""
}));
}
// 点击li实现删除
handleItemDelete(index) {
this.setState(() => {
const list = [...this.state.list];
list.splice(index, 1);
return {
list
}
} );
}
}
export default TodoList;
3-7:思考
第四章:React高级内容
4-1:Reactdeveloptools的安装和使用
在chrome的扩展程序中输入react进行安装
4-2:PropTypes与DefaultProps
import React, {Component} from 'react'
// 这里导入propTypes为了实现参数的类型指定
import propTypes from 'prop-types';
class TodoItem extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this)
}
render() {
const {content, text} = this.props;
// 父组件传一个属性一个值content,使用this.props.content来获取参数
return <div onClick={this.handleClick}>{text}-{content}</div>
}
// 子组件如何调用父组件的内容
handleClick() {
const {deleteItem, index} = this.props;
deleteItem(index)
}
}
// React中的类型指定
TodoItem.propTypes = {
text: propTypes.string.isRequired, // 指定一个参数是必须传的
content: propTypes.string, // 指定content参数是一个字符串
deleteItem: propTypes.func, // 指定deleteItem参数是一个函数
index: propTypes.number, // 指定index参数是一个数字
};
// 子组件的参数指定默认值,父组件没有传值也可以指定默认值
TodoItem.defaultProps = {
text: "hello"
};
// 更多的propTypes和DefaultProps的更多语法
// https://reactjs.org/docs/typechecking-with-proptypes.html
export default TodoItem;
4-3 :props,State与render函数
// 当我们在输入框中输入文本的时候看到的现象,是因为input的value被操作
// 当组件中的state或者props发生改变的时候,render函数就会重新执行
// 当父组件的render函数被运行的时候,子组件的render都将会被重新运行
import React, {Component} from 'react'
class Test extends Component {
// 当父组件的render函数被运行的时候,子组件的render都将会被重新运行
render() {
console.log("Test render");
return <div>{this.props.content}</div>
}
}
export default Test;
4-4:React中的虚拟DOM
在程序运行之前,就是在数据渲染之前将标签赋值到虚拟DOm中,在下次数据变更之前将现在的DOM和虚拟Dom做对比,进行局部刷新
4-7 React中ref的使用
使用ref操作DOM
<input
ref = {(input) => {this.input=input}}
/>
// 使用ref就是说让this.input指定input标签的真实的DOM节点
// 比如在我们的点击事件中,把之前传进来的e换成this.input
// 实现输入框输入的时候显示文本
HandleInputChange() {
// 异步的设置数据
const value = this.input.value;
this.setState( () => ({
inputValue:value
}));
}
// 在ul标签内实现ref
<ul ref={(ul) => {this.ul=ul}}>
{ this.getTodoItem() }
</ul>
// 在ul中实现打印div标签的长度
// 实现提交
HandleBtnClick() {
// 在修改数据的时候prevState就是修改前的数据,这里的prevState=this.State
this.setState( (prevState)=> ({
list: [...prevState.list, prevState.inputValue],
inputValue: ""
}));
// 获取ul中的div标签的长度
console.log(this.ul.querySelectorAll('div'.length))
}
4-8:React的生命周期函数
// 生命周期函数指的是在某一时刻组件会自动调用执行的函数
render就是一个生命周期函数,因为随着State和props的变化,render会被执行
初始化-->挂载-->
声明周期说明
initialization: // 是一个初始化函数
Mountiong: // 组件第一次挂载到页面的流程
componentWillMount: // 是在组件即将被挂载到页面的时刻自动执行
render: // 负责页面重新渲染的
componentDidMount: // 组件被挂载到页面之后,自动被执行
Updation: // 组件的更新,数据发生变化的时候,页面的更新会被执行
shouldComponentUpdate: // 组件被更新之前,他会自动被执行
// 如果shouldComponentUpdate返回的是True,他才会被执行
// 如果返回False,componentWillUpdate就不会被执行
componentWillUpdate: // 组件被更新之前,他会自动执行,但是他在shoudComponentUpdate之后
render: // 数据发生变化,虚拟DOm发生改变,真实的Dom发生改变,我们的页面的数据就会发生变化
ComponentDidUpdate: // 组件更新完之后,该组件会被执行
// 当一个组件ongoing父组件接受了参数
// 只要父组件的render函数被重新执行了,子组件的这个生命周期函数就会被执行
// 如果这个组件第一次存在于父组件中,不会执行
// 如果这个组件之前已经存在于父组件中,才会执行
componentWillReceivePros:
componentWillUnmount: // 当这个组件即将从页面中剔除就会被执行
先说一些shoudComponentUpdate,返回值就是True或者False,他是一个"门"的角色,"中间件"的角色,来过滤来去的数据请求
4-9: React生命周期函数的使用场景
所有的生命周期起函数都可以不写,但是render不能不写
// 父組件已更新,子組件就更新了,損耗了性能,为了避免性能的损耗,就要在子组件中使用shouldComponentUpdate
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.content !== this.props.content) {
return true;
} else {
return false;
}
}
ajax位置
我们在reder中每一次有数据变化,render函数就会被执行,加入render中有ajax请求,那么我们没输入一个值,ajax就会被执行,这是不合理的
// 我们可以将AjAx请求放到ComponentDidMount中,因为componentDidMount只会在组件挂载到页面上的时候执行一次
// 同时也可以将ajax请求放到constructor方法中,也是没有问题的,因为constructor也只是组件初始化的时候被执行一次
但是推荐将ajax放到componentDidMount中
由于React没有juqury那样内置有ajax模块,只能通过第三方的模块实现功能-----axios
// 安装yarn
npm install -g yarn
// 在项目目录下
yarn add axios
React模拟ajax进行数据请求
// TodoItem.js文件中
import axios from 'axios'
// 在componentDidMount中实现数据请求
componentDidMount() {
axios.get('/api/todolist')
.then(() => {alert("successful")})
.catch (() => {alert("error")})
}
4-10 使用charles工具进行接口数据模拟
使用charles工具模拟接口请求的时候,首先创建一个json格式的文件,比如todolist.json
["欧阳","晓晖","Andy","Kate","张三","李四","王五"]
然后在按照上面图片的步骤实现相关设置
1 点击tools,点击Map local Settings
2 勾选Enable Map Local,点击Add
3 勾选协议,输入ip地址,端口,和请求的路径,选择刚刚的json文件,点击ok
ajax接收接口数据
componentDidMount() {
axios.get('/api/todolist')
.then((res) => {
console.log(res);
this.setState(() => (
{list: [...res.data]}
))
})
.catch(() => {
alert("error")
})
}
4.11 React中实现css过渡动画
src中只有App.js和index.js代码
实现
// index.js代码
import React from 'react';
import ReactDOM from 'react-dom';
import App from "./App";
ReactDOM.render(
<React.StrictMode>
<App/>
</React.StrictMode>,
document.getElementById('root')
);
// App.js代码
import React, {Component, Fragment} from 'react'
import './style.css';
class App extends Component{
constructor(props) {
super(props);
this.state={
show:true
};
this.handleToggole = this.handleToggole.bind(this);
}
render() {
return (
<Fragment>
<div className={this.state.show ? 'show':'hide'}>hello</div>
<button onClick={this.handleToggole}>toggle</button>
</Fragment>
)
}
handleToggole(){
this.setState({
show:this.state.show ? false : true
})
}
}
export default App;
// style.css
.show{
opacity: 1; /* 显示 */
transition: all 1s ease-in; /* 过渡时间 */
}
.hide{
opacity: 0; /* 不显示 */
transition: all 1s ease-in; /* 过渡时间 */
}
4-12 React中使用CSS动画效果
// style.css
.show {
animation: show-item 2s ease-in forwards;
// 参数说明: show-item:执行动画的模块 2s:过渡时间 ease-in:曲线效果 forwards:保留最后一次动画的效果
}
.hide {
animation: hide-item 2s ease-in forwards;
}
// 参数说明:keyframes:关键帧 opacity:透明度 color:颜色
@keyframes show-item {
0% {
opacity: 0;
color: red;
}
50% {
opacity: 0.5;
color: green;
}
100% {
opacity: 1;
color: yellow;
}
}
@keyframes hide-item {
0% {
opacity: 1;
color: yellow;
}
50% {
opacity: 0.5;
color: green;
}
100% {
opacity: 0;
color: red;
}
}
4-13 : 使用 react-transition-group实现动画
Installation
# npm
npm install react-transition-group --save
# yarn
yarn add react-transition-group
Components
我们着重看CSSTransition
接下来是CSSTransition的一些用法
// App.js
import React,{ Fragment, Component } from 'react'
import { CSSTransition} from 'react-transition-group' // 导入CSSTransition模块
import './style.css'
class App extends Component{
constructor(props){
super(props);
this.state={
show:true
};
this.handleToggle=this.handleToggle.bind(this);
}
render() {
return(
<Fragment>
<CSSTransition // 使用CSSTransition来包含目标标签
in={this.state.show} // 传入的参数,布尔值
timeout={1000} // 动画执行时间
classNames="my-node" // style.css中使用用的类名的前缀
unmountOnExit // 出厂的时候(show=false)是操作目标小时,属性和位置都消失
// el就是包含的标签,就是下面的hello,onEnter就是一个钩子函数,出厂的时候执行的操作
onEnter={(el)=>{el.style.color='blue'}}
// 第一次进入的时候就执行入场效果,在style.css中.my-node-enter 后加上, .my-node-appear 以及.my-node-enter-active后加上 , .my-node-appear-active
appear={true}
>
<div>hello</div>
</CSSTransition>
{/*<div className={this.state.show ? 'show':'hide'}>hello</div>*/}
<button onClick={this.handleToggle}>Toggle</button>
</Fragment>
)
}
handleToggle() {
this.setState({
show:this.state.show ? false:true
})
}
}
export default App;
// style.css
/*
.my-node:就是CSSTransition中设置的classNames
enter:后缀是enter就说明是入场属性
exit:后缀是exit就说明是出厂属性
appear:后缀是appear就说明是第一次进入的时候要执行的操作
*/
.my-node-enter , .my-node-appear{
opacity: 0;
}
.my-node-enter-active , .my-node-appear-active{
opacity: 1;
/*color: red;*/
transition: opacity 1s ease-in;
}
.my-node-enter-done{
opacity: 1;
}
.my-node-exit {
opacity: 1;
}
.my-node-exit-active {
opacity: 0;
transition: opacity 1000ms ease-in;
}
.my-node-exit-done {
opacity: 0;
}
4-14:使用react-transition-group实现多个元素之间的动画效果
将CSSTransition和transitionGroup都导入,使用transitionGroup内部嵌套CSSTransiton
import React, {Fragment, Component} from 'react'
import {CSSTransition, TransitionGroup} from 'react-transition-group' // 导入CSSTransition模块
import './style.css'
class App extends Component {
constructor(props) {
super(props);
this.state = {
list: []
};
this.handleAddItem = this.handleAddItem.bind(this);
}
render() {
return (
<Fragment>
{/*这里套一个TransitionGroup*/}
<TransitionGroup>
{
// 循环显示list里面的元素
this.state.list.map((item, index) => {
return (
<CSSTransition
timeout={1000}
classNames="my-node"
unmountOnExit
onEnter={(el) => {
el.style.color = 'blue'
}}
appear={true}
// 添加一个index
key={index}
>
<div key={index}> {item}</div>
</CSSTransition>
)
})
}
</TransitionGroup>
{/*点击向list添加元素*/}
<button onClick={this.handleAddItem}>Toggle</button>
</Fragment>
)
}
// 向列表中添加一个item
handleAddItem() {
this.setState((prevState => {
return {
list: [...prevState.list, 'item']
}
}))
}
handleToggle() {
this.setState({
show: this.state.show ? false : true
})
}
}
export default App;