一壶茶,从Vue入手React

167 阅读6分钟

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 哲学 - React

💡 最小的组件与组件构成组件,随后组件间的嵌套与组合逐步构成了React应用。

Hello World

  1. 创建组件(类组件)
  2. 使用组件
//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;

函数式组件的特点:

  1. 没有this,组件中打印this,会得到undefined
  2. 没有state,数据的控制不受state管理,需要使用hooks
  3. 没有生命周期

使用变量

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 可以使你获得了必要的性能收益,那么这些成本都是值得承担的,但最好使用之前先测量一下

宝啊~来聊聊 9 种 React 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')
)

Part4 - 路由 | 前端aka老师-你单排吧

样式穿透

//文件名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/*"]
      }
    }
  }