开发环境准备
1. 安装node.js
2. 脚手架工具 reactjs.org --> 点击get started --> Create a New React App
3. 创建项目
npx create-react-app my-app
cd my-app
npm start
4. 安装依赖
yarn install --> my-app问价夹会有yarn.lock文件
文件目录
1. 浏览器窗口小图标
修改文件中的 favicon.ico 即可
2. 如果要使用npm,将yarn.lock和node_modules文件删除,命令行执行npm install
使用react完成ToDoList
- 新建 src/index.js,引入React和ReactDom,然后将其挂载在root上。
import React from 'react';
import ReactDom from 'react-dom';
ReactDom.render(<div>hello world</div>, document.getElementById('root'));
- 新建组件 src/TodoList.js
import React, { Component } from 'react';
class TodoList extends Component {
render() {
return (
<div>TodoList</div>
);
}
}
// 组件导出
export default TodoList;
占位组件使用,Fragment
import React, { Component, Fragement } from 'react';
class TodoList extends Component {
render() {
return (
<Fragement>
<input >
<ul>
<li>learn react</li>
<li>learn Component</li>
</ul>
</Fragement>
);
}
}
React中数据驱动的设计思想/事件绑定
1)React是基于数据驱动的框架,组件中定义数据需要写进 this.state中,想要改变其中的值,需要用this.setState({key: val})
2)JSX语法中,如果属性=js表达式或者js变量,必用{}
<input value = { this.state.inputValue } />
3)如果要改变事件中的this指向,可以在绑定事件的时候,通过.bind(this)来改变this指向当前组件
<input value={ this.state.inputValue } onChange={this.handleInputChange.bind(this)}/>
实现TodoList新增/删除功能
1) 列表数据循环展示,注意循环输出的每一项需要加key值,否则会报错
<ul>
{this.state.list.map((value, index) => {
return <li key={index}>{value}</li>
})}
</ul>
2)添加列表项
handleKeyUp(e) {
if (e.keyCode === 13) { // 回车键
// 数组list原始值 和input框中输入值
// 展开运算符 复制新的数组
const list = [...this.state.list, this.state.inputValue];
// 回车后改变list的值,并且清空input框中的值
this.setState({
list: list,
inputValue: ''
})
}
}
3)删除列表项
// bind(this, val)除了改变this指向,还可以传递参数
<ul>
{this.state.list.map((value, index) => {
return (
<li key={index} onClick={this.handItemClick.bind(this, index)}>
{value}
</li>
)
})}
</ul>
handItemClick(index) {
// console.log(index);
const list = [...this.state.list];
// 将数组list 从下标index开始删除1项
list.splice(index, 1);
this.setState({list});
}
JSX语法细节
1)bind(this)单独领出来写
<input value={ this.state.inputValue }
onChange={this.handleInputChange.bind(this)}
onKeyUp={this.handleKeyUp.bind(this)}
/>
bind提取出来写在函数中
2)将列表循环提出来,写在函数里
getListItems() {
return this.state.list.map((value, index) => {
return (
<li key={index} onClick={this.handItemClick.bind(this, index)}>
{value}
</li>
)
})
}
3)JSX中写注释,需要将内容包裹在{}里面,使用表单label for语句,需要写成htmlFor
组件相关
组件拆分与组件传值
1. 组件拆分:新建组件src/TodoItem.js,然后在TodoList.js中引入
2. 组件传值:
父传子(属性方式传递, this.props接收)
父组件传递:<TodoItem content={value} />
自组件接收:<li> {this.props.content} </li>
3. 子组件和父组件通信:调用父组件传递过来的方法,需要注意的是,父组件在传递方法的过程中注意修改作用域
React 核心特性
1. 声明式开发(不需要操作dom,只需要定义js模板和数据,修改数据即可)
2. 可以与其他框架并存:
存在与其他框架解耦的机制,index.html页面只有id='root'的div与React有关,其他div可以用其他框架来实现,他们互不影响
3. 组件化
4. 单向数据流(父组件可以改自组件的数据,但是自组件不可改父组件)
5. 函数式编程
React生命周期
- Props, State 与 render 函数
注意render函数生么时候会被执行:
当组件初次创建的时候,render函数会被执行一次
当state数据发生变更的时候,render函数会被重新执行
当props数据发生变更的时候,render函数会被重新执行
- React中ref的使用:
例:点击btn的时候,获取btn离页面窗口顶部的距离,需要操作dom
ref 写在html标签上,获取的是dom节点
ref 写在组件上,获取的是组件的js实例
<button
onClick={this.handleBtnClick}
ref={(button) => {this.btnElem = button}}
>
增加
</button>
handleBtnClick() {
console.log(this.btnElem.clientTop); // 1
}
- setState 是异步的
handleBtnClick() {
console.log(this.divElem.innerHTML); // 1
const newCounter = this.state.counter + 1;
this.setState({
counter: newCounter
})
console.log(this.divElem.innerHTML); // 1
}
setState 异步写法: 两个函数作为参数,第二个函数会在第一个函数执行完再执行
handleBtnClick() {
console.log(this.divElem.innerHTML); // 1
const newCounter = this.state.counter + 1;
this.setState(() => {
return {
counter: newCounter
}
}, () => {
console.log(this.divElem.innerHTML); // 2
})
}
import React, { Component, Fragment } from 'react';
class Counter extends Component {
handleClick() {
const newNumber = this.state.number + 1;
this.setState({
number: newNumber
})
}
// 初始化
constructor(props) {
console.log('constructor');
super(props);
this.handleClick = this.handleClick.bind(this);
this.state = {
number : 1
}
}
// 生命周期函数:
// 页面挂载前自动执行
componentWillMount() {
console.log('componentWillMount');
}
// 页面渲染时自动执行
render() {
console.log('render');
return (
<div onClick={this.handleClick}>hello world{this.number}</div>
)
}
// 页面挂载后自动执行
componentDidMount() {
console.log('componentDidMount');
}
// 更新前自动执行(数据更新),返回布尔值
shouldComponentUpdate() {
console.log('shouldComponentUpdate');
// return true;
// 返回false的时候,后面的生命周期函数将不会再执行,
// 页面不会在render函数重新渲染,从而提高网页性能
return false;
}
// 更新时自动执行
componentWillUpdate() {
console.log('componentWillUpdate');
}
// 更新结束
componentDidUpdate() {
console.log('componentDidUpdate');
}
}
export default Counter;
执行顺序:componentWillMount --> render --> componentDidMount --> shouldComponentUpdate --> componentWillUpdate --> render --> componentDidUpdate
页面挂载前后
生命周期函数使用实例
- 页面加载之后绑定全局事件
import React, { Component } from 'react';
// 原始写法
// class Counter extends Component {
// handleClick() { // 点击hell 之后才会绑定该全局事件
// window.addEventListener('click', () => {
// console.log('window click');
// })
// }
// render() {
// return (<div onClick={this.handleClick.bind(this)}>hello ...</div>)
// }
// }
// 使用生命周期函数写法
// class Counter extends Component {
// handleClick() {
// console.log('window click');
// }
// componentWillMount() {
// window.addEventListener('click', this.handleClick)
// }
// render() {
// return (<div>hello ...</div>)
// }
// componentWillUnmount() {
// window.removeEventListener('click', this.handleClick);
// }
// }
export default Counter;
- ajax请求
安装axios: npm install axios --save
重启项目
文件中引入axios包: import axios from 'axios';
ajax请求一般写进 生命周期函数的componentDidMount
Ant Design 组件库的使用
安装: npm install antd --save
index.js中引入样式 import 'antd/dist/antd.css'
重启项目
组件文件中引入组件库中组件 import { List, Typography } from 'antd';
使用组件库中组件
React中的前端路由
根据路径的不同,给用户展示不同的组件
- BrowserRouter,创建一个路由;
- Route,路由项,配置路由项地址,显示对应的组件;
安装: cnpm install react-router-dom --save
index.js中引入: import { BrowserRouter, Route, Link } from 'react-router-dom';
import React, { Component } from 'react';
import ReactDom from 'react-dom';
import { BrowserRouter, Route, Link } from 'react-router-dom';
import newList from './newList';
import newButton from './newButton';
import 'antd/dist/antd.css';
class Entry extends Component {
render() {
return (
<BrowserRouter>
<div>
<Route path='/list' component={newList} />
<Route path='/button' component={newButton} />
</div>
</BrowserRouter>
)
}
}
ReactDom.render(<Entry />, document.getElementById('root'));
// 网页中打开地址:
http://localhost:3000/list
http://localhost:3000/button
- Link,组件之间跳转。例:点击button页面按钮,跳转list页面。
引入Link组件,然后通过<Link to="/页面地址">实现路由跳转
import React, { Component } from 'react';
import { Button } from 'antd';
import { Link } from 'react-router-dom';
class newButton extends Component {
render() {
return (
<Link to="/list">
<Button type="danger">按钮</Button>
</Link>
)
}
}
export default newButton;
link跳转的同时携带参数:
1. 传递参数: <Link to="/list?a=123"> 或者 <Link to="/list/123">
2. 在配置路由项的时候,路径后加上:id(将传递的参数放进变量id,以方便其他地方获取)
<Route path='/list/:id' component={newList} />
3. 获取参数:render中打印 console.log(this.props.location.search); 或者
console.log(this.props.match.params.id);