React
🔥 在拥有Vue基础之上,入门React是很快速的,但其中也有不少区别。对我个人而言,最大的感觉在于Vue更加开箱即用,而React则能够容纳风格不一的最佳实践(这也让搭建脚手架时React似乎更加繁琐)前导知识:JSX语法
JSX = Javascript+ XML
这十分类似于插槽的用法:
Message.alert({
// 此处使用了JSX
messge: <div>{message}</div>,
type: 'success'
})
//被()所包含的是XML
return (
<div>
<img src="hi.png"/>
</div>
)
在React中,我们应该将我们的组件名称的首字母大写,这样子可以更好区分html标签与组件。
🔑JSX中的不同:
//1. class --> className
return (
<div className="ThisDiv">
Hi
</div>
)
//2. style --> {{key:value}}
<input type="text" id="ipt" style={{background: 'pink', color: 'green'}} />
//3. for --> htmlFor
<label htmlFor="ipt">label</label>
STEP1:快览
#构建
npx create-react-app demo
#npx侧重于执行命令。
#npx会临时下载依赖包,用完就删除,这意味着每次都会得到最新的版本。
#运行
cd demo && npm start
React 哲学
💡 最小的组件与组件构成组件,随后组件间的嵌套与组合逐步构成了React应用。Hello World
- 创建组件(类组件)
- 使用组件
//app1.js
//创建组件类
import React from "react";
const name = "key"
export default class App1 extends React.Component {
render() {
return (
<div>
<h1>Hello,{name}</h1>{/* JSX中引用变量只需要加单花括号 */}
</div>
);
}
}
//index.js
//使用该组件
import App1 from './App1';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<App1 />
);
📌事件、state与setState
在vue中,我们在data,methods里定义变量、函数,直接使用 this.xxxx = xxx 对数据进行修改,在React中也差不多,但是对于对变量的修改,则类似于小程序(实际上是小程序借鉴了React)
- state:变量存放的地方
- setState:当需要修改变量时,使用该方法。(否则页面将不会刷新新的数据)
import React, { Component } from 'react'
export default class App extends Component {
//声明变量
state = {
username: "key"
}
render() {
return (
<div>
{/* 使用变量 */}
<h1>{this.state.username}</h1>
{/* onClick、事件的使用 */}
<button onClick={this.handleClick.bind(this)}>按钮</button>
</div>
)
}
//事件
handleClick(){
//修改变量
this.setState({
username: "李四"
})
}
}
❗️函数的调用
📢注意本句用法tundefined leClick.bind(this) 务必给函数绑定this的指向,否则将无法确定上下文,函数内this将指向 undefined
❗️变量的修改
📢对于数组,需进行深拷贝后赋值,并通过setState修改。
let newArr = JSON.parse(JSON.stringify(this.state.arr));
this.setState({
arr: newArr
})
同样的,对于数字类型的变量不能直接进行++ - — 操作,需要使用+=1
-
个人感觉在这点Vue通过数据劫持来监听数据的变动确实做得更好,接下来的Hook,router也是...
STEP2: 函数式组件
在上面的例子中我们都使用了类组件,接下来来看最常用的函数式组件,函数式组件是最重要的内容
// App.jsx
import React from 'react'
function App(){
return <h2>Hi</h2>
}
export default App;
函数式组件的特点:
- 没有this,组件中打印this,会得到undefined
- 没有state,数据的控制不受state管理,需要使用hooks
- 没有生命周期
使用变量
import React, { useState } from 'react';//useState Hook
function Example() {
// 声明一个叫 "count" 的 state 变量
const [count, setCount] = useState(0);//初始值为0
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>{/* 箭头函数的用法,相当于.bind(this) */}
Click me
</button>
</div>
);
}
//等价与类组件的写法:
//this.state = {
// count: 0
// };
组件间传参
- 父传子
// 父组件
import Sub from './Sub'
let msg = "你好世界"
export default function App(){
return <Sub msg={msg} />
}
// 子组件
export default function Sub(props) {
return <h2>{props.msg}</h2>
}
父组件传入参数,子组件通过props使用
- 子传父
// 父组件
import Sub from './Sub'
let msg = "你好世界"
export default function App(){
const fn = function(arg){
console.log(arg)
}
return <Sub msg={msg} fn={fn} />
}
// 子组件
export default function Sub(props) {
return (
<>
<h2>{props.msg}</h2>
<button onClick={()=>props.fn(123)}>将123传递给父组件</button>
</>
)
}
请注意,无论是子→父还是父→子,其实都是父组件在工作。你可以看到父组件传入了一个方法,当子组件调用了该方法后,父组件中的方法运行并输出控制台信息
❗️Hooks
随着React推出Hooks,函数式组件已是未来的React开发趋势。
在上面的例子中我们已经穿插了useState:
useEffect 被称为副作用钩子,这个 Hook 和 useState 一样是一个基础钩子。Effect Hook 可以让你在函数组件中执行副作用操作。
useEffect Hook 支持两个参数,第一个参数为一个函数表示副作用效应函数,默认情况下它在第一次渲染之后和每次更新之后都会执行。
第二个参数是一个数组,指定了第一个参数(副效应函数)的依赖项。只有该数组中的变量发生变化时,副效应函数才会执行。
import React, {useEffect, useState} from 'react'
export default function App(){
const [num1, setNum1] = useState(1)
const [num2, setNum2] = useState(1)
useEffect(()=>{
console.log('num1更新了')
}, [num1])
return (
<>
<h2>数字1:{num1}</h2>
<button onClick={()=>setNum1(num1+1)}>累加num1</button>
<hr />
<h2>数字2:{num2}</h2>
<button onClick={()=>setNum2(num2+1)}>累加num1</button>
</>
)
}
useRef
使用useRef可以获取某个组件或元素:
import React, {useRef} from 'react'
export default function App(){
const element = useRef(null);
return (
<>
<input type="text" ref={element} />
<button onClick={()=>console.log(element.current.value)}>获取input的值</button>
</>
)
}
这里涉及两个概念:
不受控组件
不受控组件代表表单元素的值不受state控制,那么获取表单元素的值便只能通过ref获取(函数式组件中使用useRef获取,用法如以上代码)。
受控组件
受控组件代表表单元素的值受state控制(函数式组件中可以理解为受useState控制),获取其值时直接通过state获取即可
关于性能优化想说的事
关于 useCallback 以及 useMemo 这两个 Hook 都是 React 提供给开发者作为性能优化手段的方法。
但是大多数时候,你不需要考虑去优化不必要的重新渲染。 React 是非常快的,我能想到你可以利用时间去做很多事情,比起做这些类似的优化要好得多。
对于 useCallback 和 useMemo 来说,我个人认为不合理的利用这两个 Hook 不仅仅会使代码更加复杂,同时有可能会通过调用内置的 Hook 防止依赖项和 memoized 的值被垃圾回收从而导致性能变差。
如果说,有些情况下比如交互特别复杂的图表、动画之类,使用这两个 Hook 可以使你获得了必要的性能收益,那么这些成本都是值得承担的,但最好使用之前先测量一下。
React Redux
类似于vuex
npm i redux react-redux --save
// /store/reducer.js
// 定义默认数据
const defaultState = {
msg: "你好世界"
}
// 导出一个函数
export default (state=defaultState, action) => {
return state;
}
// /store/index.js
import reducer from './reducer'
import {createStore} from 'redux'
export const store = createStore(reducer);
// index.js
import ReactDOM from 'react-dom'
import App from './App'
import {store} from './store'//引入
import {Provider} from 'react-redux'//引入react redux
ReactDOM.render(
<Provider store={store}> {/* 将需要使用的组件包裹 */}
<App />
</Provider>
,
document.getElementById('root')
)
// App.js
import React from 'react'
import {connect} from 'react-redux'//引入connect
function App(props) {
return (
<>
<h2>{props.msg}</h2> {/* 通过props使用 */}
</>
)
}
// state映射
const mapStateToProps = (state) => {
return {
msg: state.msg
}
}
export default connect(mapStateToProps)(App)//柯里化
//connect(state映射, dispatch映射)(当前组件类)
修改值
// 定义默认数据
const defaultState = {
msg: "你好世界"
}
// 导出一个函数
export default (state=defaultState, action) => {
let newState = JSON.parse(JSON.stringify(state))
switch(action.type){
case "changeMsg":
newState.msg = action.value;
break;
default:
break;
}
return newState;
}
import React from 'react'
import {connect} from 'react-redux'
function App(props) {
return (
<>
<h2>{props.msg}</h2>
<button onClick={props.changeMsgFn}>修改msg</button>
</>
)
}
const mapStateToProps = (state) => {
return {
msg: state.msg
}
}
//第二个参数
const mapDispatchToProps = (dispatch) => {
return {
changeMsgFn(){
let action = {type: 'changeMsg', value: 'hello world'}
dispatch(action)
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
React Router
npm install react-router-dom
// /router/index.js
mport { BrowserRouter, Route, Routes } from 'react-router-dom'
import App from '../App'
import Home from '../pages/Home'
import List from '../pages/List'
import Detail from '../pages/Detail'
const BaseRouter = () => (
<BrowserRouter>
<Routes>
<Route path='/' element={<App />}>
<Route path='/home' element={<Home />}></Route>
<Route path='/list' element={<List />}></Route>
<Route path='/detail' element={<Detail />}></Route>
</Route>
</Routes>
</BrowserRouter>
)
export default BaseRouter
// index.js
mport ReactDOM from 'react-dom'
import Router from './router/index.jsx'
ReactDOM.render(
<Router />,
document.getElementById('root')
)
样式穿透
//文件名index.scss -> index.modules.scss
import styles from './index.modules.scss'
function Home(props) {
return (
{/* 使用 */}
<div className={styles.h1}>
<h1>Home</h1>
</div>
);
}
export default Home;
craco(alias)
yarn add -D @craco/craco
//craco.config.js
const path = require("path");
module.exports = {
// webpack 配置
webpack: {
// 配置别名
alias: {
// 约定:使用 @ 表示 src 文件所在路径
"@": path.resolve(__dirname, "src"),
},
},
};
//jsconfig.js
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
}
}