是将数据渲染为HTML视图的开源JavaScript库。
是由Facebook开发,且开源。
起初由Facebook的软件工程师Jordan Walke创建。
于2011年部署于Facebook的newsfeed。
随后在2012年部署于Instagram。
2013年5月宣布开源。
1. 原生的JavaScript操作DOM繁琐,效率低(`DOM-API操作UI`)
2. 使用JavaScript直接操作DOM,浏览器会进行大量的`重绘重排`。
3. 原生JavaScript没有`组件化`的编码方案,代码复用率低。
1. 采用`组件化`模式,`声明式编码`,提高开发效率及组件复用率。
2. 在React Native(用熟悉的js编写安卓应用)中可以使用React语法进行`移动端开发`。
3. 使用`虚拟DOM` + 优秀的`Diffing算法`,尽量减少于真实DOM的交互。(原生js和jq都是操作的真实的DOM)
1. 判断this的指向
2. class(类)
3. ES6语法规范
4. npm包管理器
5. 原型、原型链
6. 数组及常用方法
7. 模块化
react
的核心库react
扩展库es6
语句解析为es5
语句jsx
语句解析为js
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<div id="test">div>
<script src="https://unpkg.com/react@16/umd/react.development.js">script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js">script>
<script src="https://unpkg.com/[email protected]/babel.min.js">script>
<script type="text/babel">
// 1. 创建虚拟dom
// 此处一定不要写引号,因为不是字符串
const VDOM = <h1>hello react</h1>;
// 2. 渲染虚拟dom到页面
// ReactDOM.render(虚拟DOM, 渲染dom的位置)
ReactDOM.render(VDOM, document.getElementById('test'));
script>
body>
html>
关于虚拟DOM:
表达式: 一个表达式会产生一个值,可以放在任何一个需要值的地方
1). a (变量名)
2). a+b
3). demo(1) (函数调用表达式)
4). arr.map()
5). function test() {} (匿名函数)
语句(代码):控制代码走向的,没有值,不属于表达式
1). if() {}
2). for() {}
3). switch() {case: xxx}
一个js文件
当应用的js都以模块来编写,这个应用就是模块化的应用。
当应用都是以多组件的方式来实现的,这个应用就是一个组件化的应用。
类式组件(用类定义的组件)
总:
1. 类中的构造器不是必须写的,要对实例进行一些初始化操作,才写(如添加指定属性)。
2. 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super必须要调用
3. 类中定义的方法,都是放在类的原型对象上,供实例去使用
展开运算符温故:
传递多参数(展开运算符的使用)
参数的传递限制(prop-type.js)
props传递参数的简写形式
将标签属性的限制和标签属性默认值的设置写在所用类里面,通过state
关键字来定义
构造器
构造器是否接受props,是否传递super,取决于:是否需要在构造器中通过this访问props
构造器的使用情况:
this.state
赋值对象来初始化内部的state
(在方法中使用构造器里面的数据值)函数式组件使用props
注意:
函数式组件只能使用实例属性中的props
属性,函数式组件没有state
关键字,只能通过函数名.propTypes/defaultTypes来控制传递传输的类型和参数默认值。
组件内的标签可以定义ref
来标识自己
class的绑定函数
的方式可以避免上述问题,但是大多数情况下是无关紧要的。1).通过onXxx属性处理函数(注意大小写)
a. React使用的是自定义事件,而不是原生DOM事件————为了更好的兼容性
b.React中的事件是通过事件委托的方式实现的(委托给组件最外层的元素)————进行事件冒泡,就相当于在ul上面加事件,而不是在li上面加事件(为了高效)
2).通过event.target得到发生事件的DOM元素对象————发生事件的元素就是要操作的元素的时候使用,通过event.target.value
来操作DOM对象(避免过度使用ref)
表单组件的分类:
1. 受控组件(将数据通过onchange事件存储在state里面,用的时候就取,相当于vue里面的v-modal,双向数据绑定)【推荐使用这个】
3. 非受控组件(页面中的表单控件现用现取)
向对象里面插入数据:
** 高阶函数: **
如果一个函数符合下面2个规范中的任意一个,就是高阶函数
高阶函数
高阶函数
函数的柯里化: 通过函数调用继续返回函数的方式,实现多次接受参数最后统一处理的函数的编码形式。
如图:setState(更新,受阀门的控制),forceUpdate(强制更新,不受阀门的控制)
组件生命周期(组件生周期的执行顺序和代码顺序无关):
true
)生命周期的三个阶段(旧):
常用的生命钩子:
render(组件的渲染),componentDidMount(页面加载完毕,一般在这个钩子中做初始化,例如开启定时器、发送网络请求、订阅消息),componentWillUnmount(组件将要更新的钩子,一般在这个狗子中做收尾的事,例如关闭定时器、取消订阅)
npm i g create-react-app
create-react-app demo(项目名称)
npm start
下载安装nanoid,在项目文件中引用nanoid,然后通过nanoid()
来调用
npm i nanoid
注意:脚手架生成的项目依赖包中没有props-types类型限制包,要使用类型限制包,需要自己手动下载。
下载步骤:
// 创建外壳组件
import React, { Component } from "react";
import Header from './component/Header';
import List from './component/List';
import Footer from './component/Footer';
import './App.css';
// 创建并暴露App组件
export default class App extends Component {
// 初始化状态
state = {
todos: [
{ id: '001', name: '肖申克的救赎', done: true },
{ id: '002', name: '神探夏洛克', done: true },
{ id: '003', name: '小时代', done: false }
]
}
// 添加列表
addTodo = (todoObj) => {
// 获取原来的todos
const {todos} = this.state;
// 追加一个todo
const newTodos = [todoObj, ...todos];
// 更新状态
this.setState({todos:newTodos});
}
// 更新列表
changeTodo = (id, done) => {
// 获取状态中的todos
const {todos} = this.state;
const newTodos = todos.map((todoObj) => {
if(todoObj.id === id) return {...todoObj, done}
else return todoObj
})
this.setState({todos: newTodos})
}
// 删除列表
deletTodo = (id) => {
// 获取原来的todos
const {todos} = this.state;
// 删除指定id的对象
const newTodos = todos.filter((todoObj) => {
return todoObj.id !== id;
});
// 更新状态
this.setState({todos: newTodos});
}
// 全选
checkedAllTodo = (done) => {
// 获取原来的todos
const {todos} = this.state;
// 加工数据
const newTodos = todos.map((todoObj) => {
return {...todoObj, done};
});
// 更新状态
this.setState({todos: newTodos});
}
// 清除全选
clearAllDone = () => {
// 获取原来的todos
const {todos} = this.state;
// 过滤数据
const newTodos = todos.filter(todoObj => !todoObj.done);
// 更新状态
this.setState({todos: newTodos});
}
render() {
const { todos } = this.state;
return (
<div>
<div className="todo-container">
<div className="todo-wrap">
<Header addTodo={this.addTodo} />
<List todos={todos} changeTodo={this.changeTodo} deletTodo={this.deletTodo}/>
<Footer todos={todos} checkedAllTodo={this.checkedAllTodo} clearAllDone={this.clearAllDone}/>
div>
div>
div>
)
}
}
/*base*/
body {
background: #fff;
}
.btn {
display: inline-block;
padding: 4px 12px;
margin-bottom: 0;
font-size: 14px;
line-height: 20px;
text-align: center;
vertical-align: middle;
cursor: pointer;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
border-radius: 4px;
}
.btn-danger {
color: #fff;
background-color: #da4f49;
border: 1px solid #bd362f;
}
.btn-danger:hover {
color: #fff;
background-color: #bd362f;
}
.btn:focus {
outline: none;
}
.todo-container {
width: 600px;
margin: 0 auto;
}
.todo-container .todo-wrap {
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
import React, { Component } from 'react';
import PropsTypes from 'prop-types';
import {nanoid} from 'nanoid';
import './index.css'
export default class Header extends Component {
// 对接收的props进行类型、必要性的限制
static propType = {
addTodo: PropsTypes.func.isRequired
}
handleKeyUp = (event) => {
// 解构赋值获取keyCode, target
const {keyCode, target} = event;
// 判断是否是回车
if(keyCode !== 13) return;
// 处理输入的数据
if(target.value.trim() === '') {
alert('输入的信息不能为空');
return;
}
// 准备好一个todo对象
const todoObj = {id: nanoid(),name: target.value,done: false}
// 将todoObj传递给App
this.props.addTodo(todoObj);
// 清空输入
target.value = '';
}
render() {
return (
)
}
}
/*header*/
.todo-header input {
width: 560px;
height: 28px;
font-size: 14px;
border: 1px solid #ccc;
border-radius: 4px;
padding: 4px 7px;
}
.todo-header input:focus {
outline: none;
border-color: rgba(82, 168, 236, 0.8);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
}
import React, { Component } from 'react';
import PropsTypes from 'prop-types';
import Item from "../Item";
import './index.css'
export default class List extends Component {
// 对接收的props进行类型、必要性的限制
static propType = {
todos: PropsTypes.array.isRequired,
changeTodo: PropsTypes.func.isRequired,
deletTodo: PropsTypes.func.isRequired
}
render() {
const {todos, changeTodo, deletTodo} = this.props;
return (
{
todos.map((todo) => {
return -
})
}
)
}
}
/*main*/
.todo-main {
margin-left: 0px;
border: 1px solid #ddd;
border-radius: 2px;
padding: 0px;
}
.todo-empty {
height: 40px;
line-height: 40px;
border: 1px solid #ddd;
border-radius: 2px;
padding-left: 5px;
margin-top: 10px;
}
import React, { Component } from 'react';
import './index.css';
export default class Item extends Component {
state = {mouse: false}
// 鼠标移入移出的回调
handleMouse = (target) => {
return () => {
this.setState({mouse: target});
}
}
// 删除列表的回调
handleDelete = (id) => {
if(window.confirm('确定删除吗?')){
this.props.deletTodo(id);
}
}
// 勾选和取消勾选的回调
handleChange = (id) => {
return (event) => {
this.props.changeTodo(id, event.target.checked);
}
}
render() {
const {id, name, done} = this.props;
const {mouse} = this.state;
return (
)
}
}
/*item*/
li {
list-style: none;
height: 36px;
line-height: 36px;
padding: 0 5px;
border-bottom: 1px solid #ddd;
}
li label {
float: left;
cursor: pointer;
}
li label li input {
vertical-align: middle;
margin-right: 6px;
position: relative;
top: -1px;
}
li button {
float: right;
display: none;
margin-top: 3px;
}
li:before {
content: initial;
}
li:last-child {
border-bottom: none;
}
import React, { Component } from 'react';
import './index.css';
export default class Footer extends Component {
// 全选checkbox的回调
handleCheckedAll = (event) => {
this.props.checkedAllTodo(event.target.checked);
}
// 清除所有已完成的回调
handleClearAll = () => {
this.props.clearAllDone()
}
render() {
const {todos} = this.props;
// 已完成的个数(对数组进行条件统计)
const doneCount = todos.reduce((pre, todos) => pre + (todos.done ? 1 : 0), 0);
// 总数
const total = todos.length;
return (
已完成{doneCount} / 全部{total}
)
}
}
/*footer*/
.todo-footer {
height: 40px;
line-height: 40px;
padding-left: 6px;
margin-top: 5px;
}
.todo-footer label {
display: inline-block;
margin-right: 20px;
cursor: pointer;
}
.todo-footer label input {
position: relative;
top: -1px;
vertical-align: middle;
margin-right: 5px;
}
.todo-footer button {
float: right;
margin-top: 5px;
}
axios
axios.get
请求服务器地址http:\\localhost:5000
,这样写相当于3000没有的找5000要,这样只能配置一个服务器接口,局限性太小了。二、在Ract项目中配置多台代理
setupPoxy.js
配置具体规则:特点:
try {
const response = await fetch(`/api1/search/user?q=${
keyword}`);
const data = await response.json();
} catch(error ){
console.log(error);
}
一个完整的页面
不会刷新
页面,只会做页面的局部刷新
react-router-dom
分类:一般组件和路由组件,一般组件放在components文件夹下班,路由组件放在pages文件夹下。
区别:
1. 一般组件通过标签来引用,参数传什么,就收到什么,例如< Header >
2. 路由组件靠路由匹配,供路由跳转的时候使用,不用传参,就可以收到内置参数(history,location,match)
Link没有active
效果,NavLink点谁就给谁加上了active
效果.
标签体内容是一种特殊的标签属性。
封装:
import React, {
Component } from 'react';
import {
NavLink} from "react-router-dom";
export default class extends Component {
render() {
return (
<NavLink activeClassName="active" className="list-group-item" {
...this.props}/>
)
}
}
使用:
import MyNavLink from './component/MyNavLink';
<MyNavLink to="/about">About</MyNavLink>
<Route path="/about" component={
About} />
通常情况下,path和component是一一对应的关系
Switch可以提高路由匹配效率(单一匹配)
import {
Route, Switch } from "react-router-dom";
<Switch>
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
Switch>
模糊匹配:
to(可以多) —— path(从左到右匹配,顺序不能变)
默认使用模糊匹配,【输入的路径】必须包含要【匹配的路径】,且顺序要一致。
严格匹配:
**注意:**严格匹配不要随便开启,需要再开,有时候开启会导致无法匹配二级路由。
一、引入
一、使用
注意:Redirect一定要写在Route的最后面。
this.props.history.replace(`/home/message/detail/${msgObj.id}/${msgObj.title}`);
this.props.history.replace(`/home/message/detail/id=${msgObj.id}&title=${msgObj.title}`);
this.props.history.replace(`/home/message/detail`,{id: msgObj.id,title: msgObj.title});
注:
借助this.props.history对象上的API对操作路由进行跳转、前进、后退
this.props.history.push()
this.props.history.replace()
this.props.history.goBack()
this.props.history.goForward()
this.props.history.go()——传的number值为正数,就前进几步,为负数就后退几步
widthRouter可以加工一般组件,让一般组件具备路由组件所持有的API;widthRouter的返回值是一个新组件。
import {
widthRouter} from 'react-router-dom';
class Header extends Component {
back = () => {
this.props.history.goBack();
}
}
export default widthRouter(Header);
使用步骤:
npm add antd
1.安装依赖:yarn add react-app-rewired customize-cra babel-plugin-import less less-loader
2.修改package.json
…
“scripts”: {
“start”: “react-app-rewired start”,
“build”: “react-app-rewired build”,
“test”: “react-app-rewired test”,
“eject”: “react-scripts eject”
},
…
3.根目录下创建config-overrides.js
//配置具体的修改规则
const { override, fixBabelImports,addLessLoader} = require(‘customize-cra’);
module.exports = override(
fixBabelImports(‘import’, {
libraryName: ‘antd’,
libraryDirectory: ‘es’,
style: true,
}),
addLessLoader({
lessOptions:{
javascriptEnabled: true,
modifyVars: { ‘@primary-color’: ‘green’ },
}
}),
);
4.备注:不用在组件里亲自引入样式了,即:import 'antd/dist/antd.css’应该删掉
状态管理
的js库(不是react插件库)共享
的状态纯函数