React 开发环境准备
- 下载依赖速度快
npm install --registry=https://registry.npm.taobao.org
nodejs.org 下载左边稳定版本
- node -v
npm -v
正常输出版本号,就安装成功
reactjs.org->Create a New React App
- npx create-react-app my-react
- cd my-react
- npm start
public->index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<title>Odj First React</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
- src-> App.js
import React, { Component } from "react";
// import {Component} from 'react';
// <==>
// import React from "react";
// const {Component}=React;
// <==>
// const Component=React.Component;
class App extends Component {
render () {
return (
<div>
hello world
</div>
);
}
}
export default App;
- src->index.js
import React from 'react';//帮助我们去解析 JSX 语法
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
JSX 语法
- JSX 语法里面,有两类型的标签
- JSX 第一种标签:普通的 html 标签
- JSX 第二种标签:组件标签(首字母大写)
使用 React 编写 TodoList 功能
TodoList.js
import React, { Component, Fragment } from "react";
class TodoList extends Component {
render () {
return (
<Fragment>
<input />
<ul>
<li>learn React</li>
<li>learn Component</li>
</ul>
</Fragment>
);
}
}
export default TodoList;
index.js
import React from "react";
import ReactDom from "react-dom";
import TodoList from './TodoList'
ReactDom.render(<TodoList />, document.getElementById('root'));
React 中数据驱动的设计思想和事件绑定
import React, { Component, Fragment } from "react";
class TodoList extends Component {
// constructor 在组件创建的第一个时刻自动被执行
constructor(props) {
super(props);
//定义数据,固定语法,this.state
//在组件中创建了两个数据,数据一定要定义在 state 中
this.state = {
inputValue: 'hello world',
list: []
}
}
handleInputChange (e) {
// console.log(e.target.value);
// console.log(this);
//改变数据 setState
this.setState({
inputValue: e.target.value
})
}
render () {
// 如果一个属性等于 js 表达式或 js 变量的时候,最外面一定要有{}
//.bind(this) this 就会指向当前组件
return (
<Fragment>
<input
value={this.state.inputValue}
onChange={this.handleInputChange.bind(this)}
/>
<ul>
<li>learn React</li>
<li>learn Component</li>
</ul>
</Fragment>
);
}
}
export default TodoList;
实现 TodoList 新增删除功能
import React, { Component, Fragment } from "react";
class TodoList extends Component {
constructor(props) {
super(props);
this.state = {
inputValue: '',
list: []
}
}
handleInputChange (e) {
this.setState({
inputValue: e.target.value
})
}
handleKeyUp (e) {
// console.log(e.keyCode)
if (e.keyCode === 13) {
//...this.state.list ==> 'learn React', 'learn Component'
const list = [...this.state.list, this.state.inputValue];
this.setState({ list, inputValue: '' })
}
}
handleItemClick (index) {
// alert(index);
const list = [...this.state.list];
list.splice(index, 1);
this.setState({ list })
}
render () {
return (
<Fragment>
<input
value={this.state.inputValue}
onChange={this.handleInputChange.bind(this)}
onKeyUp={this.handleKeyUp.bind(this)}
/>
{/* 循环输出的每一项都要加上 key 值 */}
<ul>
{this.state.list.map((value, index) => {
return (
<li
key={index}
onClick={this.handleItemClick.bind(this, index)}>
{value}
</li>
)
})}
</ul>
</Fragment>
);
}
}
export default TodoList;
更多 JSX 语法细节
import React, { Component, Fragment } from "react";
import './style.css'
class TodoList extends Component {
constructor(props) {
super(props);
this.handleInputChange = this.handleInputChange.bind(this);
this.handleKeyUp = this.handleKeyUp.bind(this);
this.state = {
inputValue: '',
list: []
}
}
handleInputChange (e) {
this.setState({
inputValue: e.target.value
})
}
handleKeyUp (e) {
// console.log(e.keyCode)
if (e.keyCode === 13 && e.target.value !== '') {
//...this.state.list ==> 'learn React', 'learn Component'
const list = [...this.state.list, this.state.inputValue];
this.setState({ list, inputValue: '' })
}
}
handleItemClick (index) {
// alert(index);
const list = [...this.state.list];
list.splice(index, 1);
this.setState({ list })
}
getListItems () {
return this.state.list.map((value, index) => {
return (
<li
key={index}
// dangerouslySetInnerHTML={{ __html: value }}
>
{value}
<input type='button' value='删除'
onClick={this.handleItemClick.bind(this, index)} />
{/* 不转译<h1>test</h1>
{ __html: value } js 对象
=>dangerouslySetInnerHTML={{ __html: value }}*/}
</li>
)
})
}
render () {
return (
<Fragment>
{/* 输入框光标聚焦 */}
<label htmlFor='myinput'>请输入内容: </label>
<input
id="myinput"
className="input"
value={this.state.inputValue}
onChange={this.handleInputChange}
onKeyUp={this.handleKeyUp}
/>
{/* 循环输出的每一项都要加上 key 值 */}
<ul>
{this.getListItems()}
</ul>
</Fragment>
);
}
}
export default TodoList;
React 组件与生命周期
组件拆分与组件之间的传值
TodoList.js
this.handleItemClick = this.handleItemClick.bind(this);
getListItems () {
//父子组件的概念
//父组件通过属性的形式向子组件传值
return this.state.list.map((value, index) => {
return <TodoItem
content={value}
index={index}
key={index}
deleteFunction={this.handleItemClick}
/>
})
}
TodoItem.js
import React, { Component } from "react";
class TodoItem extends Component {
constructor(props) {
super(props);
this.handleItemClick = this.handleItemClick.bind(this);
}
handleItemClick () {
//改变父组件中的 list 数据
// console.log(this);
//子组件想要和父组件通信,它要调用父组件传递过来的方法
//deleteFunction={this.handleItemClick}
const { deleteFunction, index } = this.props;
deleteFunction(index);
}
render () {
//子组件通过 this.props 的属性,从父组件接收传递过来的值
const { content } = this.props;
return <li onClick={this.handleItemClick}>{content}</li>
}
}
export default TodoItem;
React 的核心特性总结
- 声明式开发
- 可以与其他框架并存
- 组件化
- 单向数据流
- 函数式编程
Props,State,与 render 函数
index.js
import Counter from './counter';
ReactDom.render(<Counter />, document.getElementById('root'));
Child.js
import React, { Component } from "react";
class Child extends Component {
render () {
console.log('child render')
return (
<div>{this.props.number}</div>
)
}
}
export default Child;
Counter.js
import React, { Component, Fragment } from "react";
import Child from './child'
class Counter extends Component {
constructor(props){
super(props); this.handleBtnClick=this.handleBtnClick.bind(this);
this.state={
counter:1
}
}
handleBtnClick(){
const newCounter=this.state.counter+1;
this.setState({
counter:newCounter
})
}
render () {
// console.log('render');
//当组件初次创建的时候,render 函数会被执行一次
//当 state 数据发生变更的时候,render 函数会被重新执行
//当 props 数据发生变更的时候,render 函数会被重新执行
return (
<Fragment>
<button onClick={this.handleBtnClick}>增加</button>
<Child number={this.state.counter}/>
</Fragment>
)
}
}
export default Counter;
React 中 ref 的使用
- ref 写咋 html 标签上,获取的是 dom 节点
- ref={(button) => { this.buttonElem = button }}
- handleBtnClick () {
console.log(this.buttonElem.clientTop); - ref 写在组件标签上,获取的是组件的 js 实例
Counter.js
import React, { Component, Fragment } from "react";
class Counter extends Component {
...
handleBtnClick () {
const newCounter = this.state.counter + 1;
console.log(this.divElem.innerHTML);
//setState 是异步的
this.setState(() => {
return {
counter: newCounter
}
}, () => {
console.log(this.divElem.innerHTML);
})
}
render () {
return (
<Fragment>
<button onClick={this.handleBtnClick}>增加</button>
<div ref={(div) => { this.divElem = div }}>{this.state.counter}</div>
</Fragment>
)
}
}
export default Counter;
React 中的生命周期函数
-
在 React 中, 生命周期函数指的是组件在某一时刻会自动执行的函数
-
当数据发生变化时,render 函数 [生命周期函数] 会被自动执行
-
counter.js
import React, { Component, Fragment } from "react";
import Number from "./number";
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');
}
//渲染时自动执行
//组件更新前 componentWillUpdate 之后 自动执行
render () {
console.log('render')
return (
<div>
<div onClick={this.handleClick}>hello world</div>
<Number count={this.state.number} />
</div>
)
}
//挂载后自动执行
componentDidMount () {
console.log('componentDidMount');
}
//更新前自动执行
shouldComponentUpdate () {
console.log('shouldComponentUpdate');
return true;//提升组件更新的性能
}
//组件更新前 shouldComponentUpdate 之后 自动执行
componentWillUpdate () {
console.log('componentWillUpdate');
}
//组件更新前 render 自动执行
componentDidUpdate () {
console.log('componentDidUpdate');
}
//组件从页面中移除前 自动执行
componentWillUnmount () {
console.log('componentWillUnmount');
}
}
export default Counter;
- number.js
import React, { Component } from "react";
class Number extends Component {
componentWillReceiveProps(){
console.log('child componentWillReceiveProps ------------')
}
shouldComponentUpdate(){
console.log('child shouldComponentUpdate')
return true
}
componentWillMount () {
console.log('child componentWillMount')
}
render () {
console.log('child render')
return <div>{this.props.count}</div>
}
componentDidMount () {
console.log('child componentDidMount')
}
}
export default Number;
生命周期函数使用实例
- (1)
import React, { Component } from "react";
class Counter extends Component {
render () {
return (
<div>Hello world</div>
)
}
//页面一加载的时候,就会给全局对象绑定事件
componentDidMount () {
window.addEventListener('click', () => {
console.log('window click');
})
}
}
export default Counter;
- (2)
import React, { Component } from "react";
class Counter extends Component {
handleClick(){
console.log('window click');
}
componentWillMount(){
window.addEventListener('click',this.handleClick)
}
render () {
return (
<div>Hello world</div>
)
}
//移除掉,不会影响其他组件
componentWillUnmount(){
window.removeEventListener('click',this.handleClick)
}
}
export default Counter;
- (3)当我们获取 ajax 数据的时候,使用生命周期函数
npm install axios --save
import React, { Component } from "react";
import axios from 'axios';
class Counter extends Component {
render () {
return (
<div>Hello world</div>
)
}
componentDidMount () {
const promise = axios.get('http://www.dell-lee.com/react/api/demo.json')
promise.then((res) => {
console.log(res);
})
}
}
export default Counter;
3-1 Ant Design 组件库的使用
npm install antd --save
"antd": "^3.10.9",
- index.js
import 'antd/dist/antd.css';
import React, { Component } from "react";
import { List } from 'antd';
const data = [
'Racing car sprays.',
'Japanese princess.',
];
class Counter extends Component {
render () {
return (
<List
style={{
marginLeft: 20,
marginRight: 20,
marginTop: 20
}}
header={<div>Header</div>}
footer={<div>Footer</div>}
bordered
dataSource={data}
renderItem={item => (<List.Item>{item}</List.Item>)}
/>
)
}
}
export default Counter;
React 中的前端路由
npm install react-router-dom --save
"react-router-dom": "^4.3.1",
- newButton.js
import React, { Component } from "react";
import { Button } from 'antd';
import { Link } from 'react-router-dom'
class NewButton extends Component {
render () {
return (
<Link to="/list/123"> <Button type="primary">按钮</Button></Link>
)
}
}
export default NewButton;
- newList.js
import React, { Component } from "react";
import { List } from 'antd';
const data = [
'Racing car sprays.',
'Japanese princess.',
];
class NewList extends Component {
render () {
console.log(this.props.match.params.id)
return (
<List
style={{
marginLeft: 20,
marginRight: 20,
marginTop: 20
}}
header={<div>Header</div>}
footer={<div>Footer</div>}
bordered
dataSource={data}
renderItem={item => (<List.Item>{item}</List.Item>)}
/>
)
}
}
export default NewList;
- index.js
import React, { Component } from "react";
import ReactDom from "react-dom";
import { BrowserRouter, Route } 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/:id' component={ NewList } />
<Route path='/button' component={ NewButton } />
</div>
</BrowserRouter>
)
}
}
ReactDom.render(<Entry />, document.getElementById('root'));
项目:React 实现新闻网站
"antd": "^3.10.9",
"axios": "^0.18.0",
"react": "^16.6.3",
"react-dom": "^16.6.3",
"react-router-dom": "^4.3.1",
"react-scripts": "2.1.1",
"web-vitals": "^2.1.4"
Header 组件拆分及样式布局
Ajax 获取 Header 组件数据
- components->Header->index.js
import React, { Component, Fragment } from "react";
import { Menu, Icon } from 'antd';
import axios from "axios";
import logo from './logo.png'
import './style.css';
class AppHeader extends Component {
constructor(props) {
super(props);
this.state = {
list: []
}
}
getMenuItems () {
return this.state.list.map(item => {
return (
<Menu.Item key={item.id}>
<Icon type={item.icon} />{item.title}
</Menu.Item>
)
})
}
componentDidMount(){
axios.get('http://www.dell-lee.com/react/api/header.json')
.then((res)=>{
this.setState({
list:res.data.data
})
})
}
render () {
return (
<Fragment>
<img src={logo} className='app-header-logo' alt="" />
<Menu mode="horizontal" className="app-header-menu">
{this.getMenuItems()}
</Menu>
</Fragment>
)
}
}
export default AppHeader;
列表页面的制作及路由配置
- src->index.js
import { BrowserRouter, Route, Switch } from "react-router-dom";
import List from './containers/List/';
import Detail from './containers/Detail/';
<Content className="content">
<BrowserRouter>
<Switch>
<Route path='/detail' component={Detail} />
<Route path='/' component={List} />
</Switch>
</BrowserRouter>
</Content>
- src->containers->Detail->index.js
import React,{ Component } from "react";
class Detail extends Component{
render(){
return <div>Detail</div>
}
}
export default Detail;
使用动态路由获取不同列表内容
-
src->index.js
//
如果要用 Link 跳转,则必须在 Router 里面
-
components->Header->index.js
import { Link } from "react-router-dom";
getMenuItems () {
return this.state.list.map(item => {
return (
<Menu.Item key={item.id}>
<Link to={`/${item.id}`}>
< Icon type={item.icon} />{item.title}
</Link>
</Menu.Item >
)
})
}
- src->containers->List->index.js
import React, { Component, Fragment } from "react";
import { List, message } from 'antd';
import axios from "axios";
class PageList extends Component {
componentWillReceiveProps (nextProps) {
const id = nextProps.match.params.id;
axios.get('http://www.dell-lee.com/react/api/list.json?id=' + id)
.then((res) => {
// console.log(res.data.data);
if (res.data.success) {
let list = res.data.data
if (list.length > 0) {
this.setState({
data: list
});
} else {
message.info("查询结果为空!")
}
} else {
message.error("请求失败!")
}
})
}
constructor(props) {
super(props);
this.state = {
data: []
}
}
render () {
console.log(this.props.match.params.id);
return (
<List
style={{ background: '#fff' }}
bordered
dataSource={this.state.data}
renderItem={(item) => (
<Fragment>
<List.Item> {item.title}
</List.Item>
</Fragment>
)}
/>
)
}
componentDidMount () {
let url='http://www.dell-lee.com/react/api/list.json';
const id = this.props.match.params.id;
if(id){
url=url+'?id='+id;
}
axios.get(url)
.then((res) => {
// console.log(res.data.data);
if (res.data.success) {
let list = res.data.data
if (list.length > 0) {
this.setState({
data: list
});
} else {
message.info("查询结果为空!")
}
} else {
message.error("请求失败!")
}
})
}
}
export default PageList;



