This project was bootstrapped with Create React App.
In the project directory, you can run:
Runs the app in the development mode.
Open http://localhost:3000 to view it in the browser.
The page will reload if you make edits.
You will also see any lint errors in the console.
Launches the test runner in the interactive watch mode.
See the section about running tests for more information.
Builds the app for production to the build
folder.
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.
Your app is ready to be deployed!
See the section about deployment for more information.
Note: this is a one-way operation. Once you eject
, you can’t go back!
If you aren’t satisfied with the build tool and configuration choices, you can eject
at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except eject
will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
You don’t have to ever use eject
. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
You can learn more in the Create React App documentation.
To learn React, check out the React documentation.
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
三个步骤
- Mounting: 将组件插入到DOM中
- Updating: 将组件更新到DOM中
- Unmouting: 将组件移除DOM
生命周期钩子函数 其中渲染和更新中间会执行render
- ComponentWillMount: 组件将要渲染
- ComponentDidMount: 组件渲染完毕
- ComponentWillReceiveProps: 组件将要接收props的值
- ShouldComponentUpdate: 组件接收state或者props 判断是否更新 返回布尔值
- ComponentWillUpdate: 组件将要更新
- ComponentDidUpdate: 组件更新完毕
- ComponentWillUnmount: 组件将要卸载
props 传值 变量方法都可以
- 普通变量 只需要在子组件上增加属性 例如
age = {this.state.age}
子组件中只需要在props中取指定属性即可 - 方法 父组件直接传递方法 子组件使用用方法需要注意 如果没有传参直接在指定位置绑定方法即可 如果有传参 需要改成箭头函数 例如
onClick = { () => { add(111) } }
组件中写入内容 这些内容可以被识别和控制
react
中需要自己开发支持插槽 组件中写入的HTML 可以传入到props
中 在props
中的childern
中
对于数组中只有jsx语法的组件的的情况可以直接放在花括号中展示 不用再循环
- 安装
react-router-dom
依赖 - 引入组件使用
// BrowserRouter
import { BrowserRouter as Router, Link, Route } from 'react-router-dom'
// HashRouter
import { HashRouter as Router, Link, Route } from 'react-router-dom'
- 使用 可以写多个router 当作普通jsx对象使用
<Router basename="/">{/* 设置跟路径的路由(默认路由 自动加在前面的路由) */}
<div className="nav">
<Link to="/">Home</Link>{/* 路由跳转的a标签 */}
<Link to="/me">Me</Link>
<Link to="/product" replace>Product</Link>{/* replace 点击链接后将新地址替换为历史访问的原地址 */}
</div>
<Route path="/" exact component={Home}></Route>{/* 跳转的路由 componet的值是组件 exact精确匹配 防止会出现两个路由的问题 例如 / 和 /me 不皆缺匹配跳转的话 跳转me会两个都显示 */}
<Route path="/me" component={Me}></Route>
<Route path="/product" component={Product}></Route>
</Router>
Link
组件可以设置to
属性来进行路由跳转to
属性可以直接写路径的字符串 也可以通过一个对象进行路径的设置 例如
let meObj = {
pathname: "me", // 跳转的路由
search:"?uesrname=admin", // get传参
hash:"#abc", // 设置的哈希值(锚值)
state:{msg:"helloworld"} // 传入组件的数据 自定义
}
<Link to={meObj}>Me</Link>
- 动态路由
function News(props){
// 动态路由的传参在 props.match.params 中
return (
<div>
<h1>新闻页,新闻id:{props.match.params.id}</h1>
</div>
)
}
<Link to="/news/456789">新闻页面</Link>
<Route path="/news/:id" component={News}></Route>
重定向 当我们访问某个组件时 如果有重定向组件 那么就会修改页面路径 使页面内容显示为所定向的内容
Redirect
function LoginInfo(props) {
// 通过 控制状态值跳转不同的路由
switch (props.location.state.loginState) {
case 'success':
return <Redirect to="/"></Redirect>
default:
return <Redirect to="/login"></Redirect>
}
}
let FormCom = () => {
let pathObj = {
pathname: "/logininfo",
state:{
loginState: 'fail'
}
}
return (
<div>
<h1>表单验证</h1>
<Link to={pathObj}>登录验证页面</Link>
</div>
)
}
<Route exact path="/logininfo" component={LoginInfo}></Route>
Switch 组件内部的Route只匹配一次 只要匹配到了 剩余的路由规则将不再匹配 例如
<Route exact path="/abc" component={()=>(<h1>abc1</h1>)}></Route>
<Route exact path="/abc" component={()=>(<h1>abc2</h1>)}></Route>
{/* 这里只匹配第一个abc路由 */}
使用js跳转
// 指定路由跳转
this.props.history.push('/',{ state: 111 })
this.props.history.replace('/',{ state: 111 })
// 前进
this.props.history.go(1);
this.props.history.goForWard();
// 后退
this.props.history.go(-1);
this.props.history.goBack();
-
store
数据仓库 保存数据的地方 -
state
包含了整个应用所需要的数据state
中的数据不能直接修改 数据仓库的所有数据都放在一个state
中 -
action
一个动作 触发数据改变的方法 -
dispatch
将动作触发成方法 -
reducer
一个函数 通过获取动作 改变数据 生成一个新的状态state
从而改变页面
redux应用步骤 首先在redux中引入createStore
createStore
创建仓库
// 创建reducer函数 初始化数据 修改状态值
const reducer = function(state={num},action) {
switch (action.type) {
// 判断动作的类型
case "add":
state.num++;
break;
default:
state.num--;
break;
}
// 注意解构 防止旧版本不会触发监听
return {...state};
}
// 创建store仓库
const store = createStore(reducer);
-
调用
dispatch
动作触发store.dispatch({type: "add"})
-
subscribe
监听状态变化重新渲染页面
store.subscribe(()=>{
ReactDOM.render(<Counter/>,document.querySelector("#root"))
})
import { createStore } from "redux"
import { Provider, connect } from 'react-redux'
Provider
组件 自动将store
里的state
和组件进行关联connect
将组件和数据和方法进行连接
- 第一步同
redux
创建仓库createStore
以及reducer
function reducer(state={ num:0 },action) {
switch (action.type) {
case 'add':
state.num++
break;
default:
break;
}
return { ...state };
}
const store = createStore(reducer);
- 映射状态(
state
)和方法(函数)
const addAction = {
type: "add"
}
// 将store的state映射到组件里的props
function mapStateToProps(state){
return {
value: state.num
}
}
// 将store中的dispatch映射到组件的props里 实现方法的共享
function mapDispatchToProps(dispatch) {
return {
onAddClick: ()=>{ dispatch(addAction) }
}
}
- 将两个方法 将数据仓库的
state
和修改state
的方法映射到组件上 形成新的组件
class Counter extends React.Component{
render() {
const { value, onAddClick } = this.props;
return (
<div>
<h1>计数的数量:{ value }</h1>
<button onClick={ onAddClick }>数字+1</button>
</div>
)
}
}
const App = connect (
mapStateToProps,
mapDispatchToProps
)(Counter)
- 最后修改
ReactDOM
的render
ReactDOM.render(
<Provider store={store}>
<App></App>
</Provider>,
document.querySelector("#root")
)
另一种方式 对
reducer
中的action
和state
进行封装
// 对action进行一次封装
const ActionFnObj = {
add:function(state,action){
state.num++;
return {...state}
}
}
// 对state的值进行初始化
const StateObj = {
num: 0
}
function reducer(state = StateObj, action) {
// 判断是否为第一次进入 reducer 第一次action的type是redux版本号等内容
if(ActionFnObj[action.type]){
state = ActionFnObj[action.type](state, action)
return { ...state };
}else {
return { ...state };
}
}
- 安装
antd
和babel-plugin-import
- 暴露配置
eject
npm run eject -y
注意如果配置了git但是有内容没有提交到git 无法进行该操作 要先提交 - 配置
babel
在package.json
中找到babel
然后在里面presets
同级增加 此时就不需要加载样式文件
"plugins": [
["import", { "libraryName": "antd", "style": "css" }]
]
- 引入需要使用的组件 例如
Button
等 直接使用
npm init
生成package.json
文件- 安装
express
依赖 建议使用cnpm
或者yarn
yarn add express --save
安装axios
依赖 - 新建
index.js
内容如下
const express = require("express");
const axios = require("axios")
const app = express();
app.get('/api/newsdata', async (req,res)=>{
// 解决ajax跨域
res.append('Access-Control-Allow-Origin', "*");
res.append('Access-Control-Allow-content-type', "*")
// 请求头条数据
let result = await axios.get("https://c.m.163.com/ug/api/wuhan/app/data/list-total?t=318929278640")
let data = result.data;
res.send(data);
res.json({ name: "老马" })
})
app.listen(8080, ()=>{
console.log("http://localhost:8080/api/newsdata");
})
node
切换到这个目录 执行node index.js
增强函数组件的功能 在没有hook的时候: 没有状态 没有生命周期 没有引用 在hook里的提供的方法都是use开头的
// 初始化状态和修改状态的方法 注意命名 一定要是set跟上变量名 首字母小写驼峰命名
const [a,setA] = useState(0); // 给a赋值0
// 使用和修改
{a} // 直接使用
setA(a+1) // 修改
// setA仍然是异步的 如果想取到改变后的数据
setA(()=>{
console.log(a+1);
return a+1;
})
// 初始化ref
const p1 = useRef();
// 使用ref
p1.current.style.background="red"
- 相当于componentDidMount、componentDidUpdate 和 componentWillUnmount 三个状态
- 副作用 (DOM操作 数据请求 组件更新)
- useEffect在组件函数内部执行 可以获取state和props 采用闭包的形式
- 无阻塞更新
- useEffect(回调函数, 数组(可不写)) 两个参数 第二个参数控制在状态改变时监听那个状态 例子
// 只监听obj这个状态
useEffect(()=>{
console.log(obj)
}, [obj])
- 可以写多个useEffect()
- 父组件
import { createContext } from 'react';
export default createContext();
- 子组件
import React, {useContext} from 'react'
import MyContent from './MyContent' // 父组件
export default ()=>{
let count = useContext(MyContent);
return (
<h3>
{count}
</h3>
)
}
- 使用
import MyContent from './component/MyContent' // 父组件
import ChildContext from './component/ChildContext' // 子组件
<MyContent.Provider value={a}>
<ChildContext/>
</MyContent.Provider>
- 相当于
shouldComponentUpdate
类似作用 在渲染过程中避免重复渲染的问题 当状态或者父组件传来的属性发生改变时 更新组件 useMemo
用的是Memoization
来提高性能的Memoization
是javascript
的缓存技术- 如果我们有CPU密集型操作 我们可以通过将初始操作的结果缓存在缓存中来优化使用 如果操作必然会再次执行 我们不再麻烦两次使用我们的CPU 因为相同的结果存储在某个地方 我们只是简单的返回结果 这个是以空间换速度 所以最好确定你是否值得这么做 有些场景很有必要使用
useMemo()
是一个函数 两个参数 第一个是函数 第二个是数组useMemo(()=>{}, [可以不写])
参数类似useEffect
如果第二个参数和useEffect
一样的话 会在useEffect
之前执行
使用方式
let res = useMemo(()=>{
return {count, num}; // 监控改变的状态
},[count]) // 通过第二个参数控制哪个状态改变时页面组件更新 如果是空数组就不会改变 如果没有默认是所有状态
// 例如
<h3>count{res.count}----num{res.num}</h3>
<button onClick={()=>{
setCount(count+1)
}}>改变count</button> {/* 点击这个按钮 组件会更新 */}
<button onClick={()=>{
setNum(num+1)
}}>改变num</button> {/* 点击这个按钮 状态会改变但是组件不会更新 */}
- 控制组件什么时候更新
- 同样用到缓存计数 和
useMemo
不同的是useCallback
是一个函数 是个函数就可以执行 useCallback()
有两个参数 第一个参数是函数 第二个是数组const callback = useCallback(()=>{},[])
;- 可以直接
callBack()
执行
使用方式
const [count, setCount] = useState(0)
let callback = useCallback(()=>{
console.log(count);
return count;
},[count]) // 最好写第二个参数 不然调用的时候会一直不改变
<h3>{callback()}</h3>{/* 使用时直接执行方法 */}
- 可以让你在使用
ref
时自定义暴露给父组件的实例值 在大多数情况下 应当避免使用ref
这样的命令代码useImperativeHandle
应当与forwardRef
一起
import React, { forwardRef, useRef } from 'react';
const Forward = forwardRef((props, ref)=>{
return (
<>
{/* ref 传递到子组件上 */}
<h3 ref={ref}>123</h3>
<h4>456</h4>
</>
)
})
export default (()=>{
const h3El = useRef(null);
return (
<>
<Forward ref={h3El}/>
<button onClick={()=>{
// 父组件直接使用heEl即可 就是上面哪个h3
// 在这里还有一层current
console.log(h3El)
}}>获取子组件dom</button>
</>
)
});
// 第一个参数 ref
// 第二个参数 函数的return 暴露给父组件的属性还有方法
// 第三个参数 监控哪个状态改变时重新传入ref 如果不写 任何状态改变都会传入 最好第二个参数return有的状态在第三个参数中都有
不同点
1. 执行时间不同 useEffect 在componentDidMount后执行 useLayoutEffect 在浏览器执行绘制之前执行(会阻塞组件挂载 慎用)useLayoutEffect 会在 useEffect 之前执行
- 和redux中的reducer一样 reducer就是一个函数
useReducer()
是一个函数 第一个参数reducer 第二个参数初始值 第三个参数inituerReducer()
返回值是个数组 第一个是state 第二个是dispatchconst [state, dispatch] = useReducer(reducer,初始值)
useReducer
和useContext
结合 实现和redux类似的作用
// 定义reducer和context
import React, { createContext, useReducer } from 'react';
// reducer和context实现redux类似功能
export const MyCountext = createContext();
// reducer 修改状态的方法
const reducer = (state, action) => {
{/* 通过action的type不同执行不同操作 */}
...
}
// state 状态值
const data = {name:"zhangsan", age:19}
export const Reducer = (props) => {
let [state, dispatch] = useReducer(reducer, data)
return (
<MyCountext.Provider value={{state, dispatch}}>
{props.children}
</MyCountext.Provider>
)
}
// 使用reducer
import { MyCountext } from './Reducer'
let {state, dispatch} = useContext(MyCountext)
// 属性 {state.name}
// 修改state的方法 dispatch({type:"***", 其他属性})
自定义hook 和普通函数没有区别 都是一些函数的封装 方便使用 注意
- 自定义hook 必须以use开头
- 自定义hook 可以使用普通hook(useState, useEffect......)来封装
import React,{useState} from 'react';
// 自定义hook
const useCus = (val, num) => {
let [count,setCount] = useState(val);
const add = () => {
setCount(count+num)
}
return {count,add}
}
export default ()=>{
let {count,add} = useCus(10,2)
return (
<>
<h1>{count}</h1>
<button onClick={()=>{
add();
}} >加2</button>
</>
)
}
globalThis
全局变量 相当于window
三种全局变量
window 只能在浏览器中生效
self 在浏览器和webworker中生效
globalThis 浏览器webworker以及node中可以使用
Promise.allSettled
多个promise并发执行
let p1 = new Promise((resove, reject)=>setTimeout(resolve(1),200));
let p2 = new Promise((resove, reject)=>setTimeout(reject('error'),300));
let p3 = new Promise((resove, reject)=>setTimeout(resolve(2),400));
// 以前并发执行 参数中任何一个promise为reject则整个promise.all会终止
Promise.all([p1,p2,p3]).then(res=>console.log(res)).catch(e=>console.log(e));
// Promise.allSettled 结果会依次打印出来
Promise.allSettled([p1,p2,p3]).then(res=>console.log(res)).catch(e=>console.log(e));
- 空位合并操作符
??
可选链操作符?
let Libra = {
info:{
name: 'Libra',
money: 100
}
}
Libra.info.money ?? 100; // 可以替代逻辑或 而且当money为0的时候不会取100 只有money为undefined时才会取100
Libra.info.money; // 常规写法这样写如果没有info属性语句会报错
Libra?.info?.money; // 使用可选链操作符不会报错 会是undefined
BigInt
最大值
在js中又一个最大值 Number.MAX_SAFE_INTEGER
当需要使用更大的值时可以在普通数值后面拼接一个 n 也可以使用BigInt(num)将普通数值转换为BigInt