了解 React
- react 是什么?
- react 是一个视图层框架,类似 MVC 中的 V
- 视图
- 将后端的数据高效的渲染到视图
- 将用户输入的信息高效的展示在界面
- react 具有组件系统
- 组件是一个独立的聚合体,复用性高,比如: 车上的零件坏了
- react 是单向数据流
- react 特点
- 虚拟 DOM
- DIFF 算法【16 版本之后更新为 Filber 算法】
- 组件系统化
- 单向数据流
- jsx
- 函数式编程
- 插件化编程
- 一切皆组件
react 脚手架使用
- create-react-app
1 2
| $ cnpm i create-react-app -g $ create-react-app
|
- dva
- umi
类组件/函数组件
- 在 src 目录下创建 index.js 入口文件和 App.js 组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
import React from "react";
class App extends React.Component { render() { return <div>这是React类组件写法</div>; } }
export default App;
|
- 在 index.js 中引入并使用
1 2 3 4 5 6 7
|
import ReactDOM from "react-dom"; import App from "./App";
ReactDOM.render(<App />, document.querySelector("#root"));
|
组件嵌套
入口文件 index.js/Farther.js/Son.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import React from "react"; import Son from "./Son";
class Farther extends Component { render() { return ( <div> Far <Son /> </div> ); } }
export default Farther;
|
1 2 3 4 5 6 7 8 9 10
| import React from "react";
class Son extends React.Component { render() { return <div>Son</div>; } }
export default Son;
|
1 2 3 4 5
| import ReactDOM from "react-dom"; import Farther from "./Farther";
ReactDOM.render(<Farther />, document.querySelector("#root"));
|
组件组合
this.props.children 可以类似插槽的功能,用于组合,接下来每个 index.js 入口文件都不写了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| import React, { Component } from 'react' class Todolist extends Component { render() { return ( <div> <h1>Todolist</h1> { this.props.children } </div> ) } }
class Tab extends React.Component{ render () { return <div> <h1> tab </h1> </div> } } class Content extends React.Component{ render () { return <div> <h1> content </h1> </div> } } class TabBar extends React.Component{ render () { return <div> <h1> TabBar </h1> </div> } }
class App extends Component { render() { return ( <div> <TodoList> <Tab/> <Content/> <TabBar> </TodoList> </div> ) } }
export default App
|
react 组件的样式
全局样式
- 写法 import ‘xxx.css/scss/less’
- 渲染到界面时类名就是自定义类名
局部样式
- xxx.module.css 局部样式
- import styles from ‘xxx.module.css’
cra 使用 sass
- 需要安装两个插件 node-sass sass-loader
1
| $ yarn add node-sass sass-loader -D
|
cra 使用 less
- 找到 sass 配置 config 并拷贝
- 安装 less 和 less-loader
1
| $ yarn add less less-loader -D
|
行内样式
在标签中加 style 属性 并用双括号
1 2 3 4 5 6 7 8 9 10 11 12
| <p style={{ width: "100px", height: "100px", background: "red", }} ></p>
|
类名的操作/classnames 插件
1
| $ yarn add classnames -D
|
1 2 3 4 5 6 7 8 9
| import cl from "classnames";
<p className={cl({ a: true, b: true, c: false, })} ></p>;
|
样式组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import React, { Component } from "react";
import styled from "styled-components";
const Container = styled.div` width: 100px; height: 100px; background: ${(props) => props.color || "red"}; `;
export default class App extends Component { color = "pink"; render() { return ( <div> <Container /> <Container color={this.color} /> </div> ); } }
|
react 事件
合成事件
案例:点击按钮弹出框
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import React, { Component } from "react";
export default class App extends Component { fn = () => { alert("111"); };
render() { return ( <div> <button onClick={this.fn}>点击</button> </div> ); } }
|
事件对象
案例:回车时弹出 input 的 value 值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import React, { Component } from "react";
export default class App extends Component { getVal = (e) => { if (e.keyCode == 13) { alert(e.target.value); } };
render() { return ( <div> <input type="text" placeholder="请输入..." onKeyDown={this.getVal} /> </div> ); } }
|
事件传参
事件传参时需要加一层箭头函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import React, { Component } from "react";
export default class App extends Component { fn = (n) => { alert(n); };
render() { return ( <div> <button onClick={() => { this.fn(10); }} > 点击 </button> </div> ); } }
|
this 绑定问题
问题: 普通函数中的 this 丢失, 是普通函数不是箭头函数
原因: 引文事件源这个 button 是虚拟 DOM
解决: bind
- 调用时绑定 bind this.fn.bind(this)
- 在构造函数中绑定 this constructor () { super(); this.fn = this.fn.bind(this) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import React, { Component } from "react";
export default class App extends Component {
fn() { console.log("普通函数this", this); }
render() { return ( <div> <button onClick={this.fn.bind(this)}>按钮</button> </div> ); } }
|
原生事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import React, { Component } from "react";
export default class App extends Component { componentDidMount() { const btn = document.querySelector("button"); btn.onclick = function () { alert("原生事件"); }; }
render() { return ( <div> <button>按钮</button> </div> ); } }
|
react 组件数据形式
props-外部传入
- 父组件通过属性绑定的形式绑定数据给子组件
- 子组件获取属性用{this.props.xxx}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import React, { Component } from "react";
class Hello extends Component { render() { return ( <div> {} <p> {this.props.name} </p> <p> {this.props.age} </p> </div> ); } }
export default class App extends Component { render() { return ( <div> {} <Hello name="yyc" age={18} /> </div> ); } }
|
props-自身定义
类组件通过一个关键字 static defaultProps = {} 来定义自身属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import React, { Component } from "react";
export default class App extends Component { static defaultProps = { color: "pink", }; render() { const { color } = this.props; return ( <div> <p> {color} </p> </div> ); } }
|
props-属性校验
- 需要第三方依赖包 prop-types
1
| $ yarn add prop-types -S
|
- 代码部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import React, { Component } from "react"; import PropTypes from "prop-types";
class Hello extends Component { render() { const { name, age } = this.props; return ( <div> <p> {name} </p> <p> {age} </p> </div> ); } }
Hello.propTypes = { name: PropTypes.string, age: PropTypes.number, addProp(props, propName, componentName) { if (props.age < 18) { alert(" 未成年 "); } }, };
export default class App extends Component { render() { return ( <div> <Hello name="yyc" age={17} /> </div> ); } }
|
state-两种定义形式
- 构造函数定义
- class 直接定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import React, { Component } from "react";
export default class App extends Component {
state = { name: "yyc", };
render() { const { name } = this.state; return ( <div> <p> {name} </p> </div> ); } }
|
setState-改变 state-案例
setState(()=>{},()=>{})
接收两个回调函数
第一个回调函数可以带有参数 pre,用于接收旧值
- 案例 1: 计数按钮
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import React, { Component } from "react";
export default class App extends Component { constructor() { super(); this.state = { count: 1, }; }
add = () => { this.setState((pre) => { return { count: pre.count + 1, }; }); };
render() { const { count } = this.state; return ( <div> <button onClick={this.add}> + </button> <p> {count} </p> </div> ); } }
|
- 案例 2: 修改网页 title-合成事件-异步
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import React, { Component } from "react";
export default class App extends Component { constructor() { super(); this.state = { name: "张三", }; }
changeName = () => { this.setState( () => { return { name: "李四", }; }, () => { document.title = this.state.name; } ); };
render() { const { name } = this.state; return ( <div> <button onClick={this.changeName}>改变名字</button> <p> {name} </p> </div> ); } }
|
- 案例 3: 原生同步
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import React, { Component } from "react";
export default class App extends Component { constructor() { super(); this.state = { name: "张三", }; }
componentDidMount() { const btn = document.querySelector("button"); const _this = this; btn.onclick = function () { _this.setState({ name: "李四", }); document.title = _this.state.name; }; }
render() { const { name } = this.state; return ( <div> <button onClick={this.changeName}>改变名字</button> <p> {name} </p> </div> ); } }
|
setState()在 render()中不能使用
render 函数中不能直接调用 setState
报错: 栈溢出(死循环)
原因: render 函数本身就是用于解析 this.state 和 this.props 的,解析时开始矛盾
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import React, { Component } from "react";
export default class App extends Component { constructor() { super(); this.state = { count: 1, }; } render() { const { count } = this.state; return ( <div> <p> {count} </p> </div> ); } }
|
react-数据渲染
条件渲染
demo: 渲染一个标签, 在 react 中 {} 单花括号可以写 js 逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import React, { Component } from "react";
export default class App extends Component { state = { flag: true, };
setFlag = () => { this.setState({ flag: !this.state.flag, }); }; render() { const { flag } = this.state; return ( <div> <button onClick={this.setFlag}>按钮</button> { flag && <p>你好</p> } </div> ); } }
|
列表渲染 + 数据请求 + 反向代理
- 列表渲染使用 map 语法, 也就是原生的数组方法, 使用该方法一定要是数组。
- cra-配置文件抽离-反向代理:
- 目录路径 config/webpackDevServer.config.js
- 找到 proxy
- 书写反向代理
1 2 3 4 5 6
| proxy: { '/ajax': { target: 'https://m.maoyao.com', changeOrigin: true } },
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| import React, { Component } from "react";
export default class App extends Component { state = { list: null, flag: false, };
getList = () => { this.setState({ flag: true, }); fetch( "/ajax/movieOnInfoList?token=&optimus_uuid=03700780983E11EBBCE74F727D62BDE1238C6477E95F4AE98875338903338692&optimus_risk_level=71&optimus_code=10" ) .then((data) => data.json()) .then((res) => { this.setState({ list: res.movieList, flag: false, }); }) .catch((error) => Promise.reject(error)); }; render() { const { list, flag } = this.state; return ( <div> <button onClick={this.getList}>按钮</button> { flag && <div>loading...</div> } <ul> { list && list.map((item) => <li key={item.id}> {item.nm} </li>) } </ul> </div> ); } }
|
路径别名
- 路径别名配置
- 目录路径 config/webpack.config.js
- 找到 alias c
- 书写路径别名配置
1 2 3 4
| alias: { 'c': path.join(__dirname,'./src/components') }
|