react初學(xué)

一、創(chuàng)建項(xiàng)目
第一種、使用npx命令創(chuàng)建項(xiàng)目(CRA)

// 臨時(shí)下載并運(yùn)行 create-react-app,創(chuàng)建名為 my-react-app 的項(xiàng)目
npx create-react-app my-react-app
//啟動(dòng)   進(jìn)入項(xiàng)目根目錄
npm run  start
  • npx 是 npm 5.2.0(對應(yīng)node8.1.0) 及以上版本自帶的工具,無需單獨(dú)安裝

第二種、使用vite命令創(chuàng)建項(xiàng)目,推薦

//全局安裝 Vite 腳手架(僅需一次)
npm install -g create-vite
//node.js版本 需要 ≥ 16.0.0(推薦 LTS 版本,如 v20.x)
//my-react-app:你的項(xiàng)目名稱(可自定義,如 react-demo)
// --template react:指定模板為 React(純 JS 版本);如果需要 TS 版本,替換為 react-ts
npm create vite@latest my-react-app -- --template react
//啟動(dòng)  進(jìn)入項(xiàng)目根目錄
npm run dev 

第三種、用 npx 執(zhí)行 Vite 命令創(chuàng)建項(xiàng)目(相結(jié)合,推薦)

npx create-vite@latest my-react-app -- --template react-ts
//啟動(dòng)  進(jìn)入項(xiàng)目根目錄
npm run dev 
維度 CRA(create-react-app) Vite
底層構(gòu)建工具 webpack Vite(基于 esbuild/rollup)
啟動(dòng)速度 慢(冷啟動(dòng)需幾十秒) 極快(冷啟動(dòng)僅幾百毫秒)
熱更新速度 慢(改代碼等幾秒刷新) 即時(shí)(改代碼瞬間刷新)
配置靈活性 封閉(需 eject 才能改) 開放(vite.config.js 直接配)
體積 打包后體積較大 打包體積更小、優(yōu)化更好

二、vscode插件

  • Chinese Language
  • CSS Formatter
  • ES7+ React/Redux/React-Native snippets
  • Prettier
  • Auto Rename Tag
  • Auto Close Tag
  • Path IntelliSense
  • npm Intellisense
  • Import Cost

三、語法
1.循環(huán)

const scoreList = [
  {
    id: 1,         // 唯一標(biāo)識(shí)ID
    name: "張三",  // 姓名
    score: 95      // 分?jǐn)?shù)
  },
  {
    id: 2,
    name: "李四",
    score: 88
  },
];
<ul>
     {scoreList.map(item=><li key={item.id}>{item.name}:{item.score}</li>)}
</ul>

2.條件判斷

const isLogin=false;
<div>{isLogin && <span>已登錄</span>}</div>
<div>{isLogin?<span>已登錄</span>:<span>未登錄</span>}</div>

3.綁定事件

function bindEvent(name,e){
    console.log(name+'綁定點(diǎn)擊事件',e)
}
<button onClick={(e)=>bindEvent('jack',e)}>按鈕</button>

4.引用組件

const CustomComponent=()=>{
    return <ul>
       {scoreList.map(item=><li key={item.id}>{item.name}:{item.score}</li>)}
    </ul>
}
 <CustomComponent></CustomComponent>

5.useState
類似vue3的ref和reactive,定義普通變量修改后組件種不會(huì)更新,只有通過useState定義的變量才可以更新頁面數(shù)據(jù)

// 解構(gòu)賦值:[狀態(tài)變量, 更新狀態(tài)的方法] = useState(初始值)
const [state, setState] = useState(initialValue);
  • state:當(dāng)前的狀態(tài)值(可讀);
  • setState:更新狀態(tài)的函數(shù)(可寫),調(diào)用后會(huì)觸發(fā)組件重渲染;
  • initialValue:狀態(tài)的初始值(可以是任意類型:數(shù)字、字符串、對象、數(shù)組等)。
function App() {
    const [score, setScore] = useState(10);//這句話要放在函數(shù)的最頂層,否則報(bào)錯(cuò),也不能放外面,也報(bào)錯(cuò)
    const scorePlus=()=>{
        setScore(score + 10);
    }
  return (
    <div className="App">
        <div>{score}</div>
        <button onClick={(e)=>{scorePlus()}}>分?jǐn)?shù)自增</button>
    </div>
  );
}

state里面的值發(fā)生變化,組件會(huì)重新渲染

function Son (){
    //因?yàn)閟tate里面的num發(fā)生變化,所以Son組件重新渲染,所以會(huì)開多個(gè)定時(shí)器
    console.log('重新渲染了')
    const [num,setNum] = useState(0)
    let timer =  setInterval(()=>{
        setNum(num+1);
    },1000);
    return <i>this is son page:{num}</i>
}

6.使用lodash和classNames插件

import _orderBy from 'lodash/orderBy';
import _remove from 'lodash/remove';
import classNames from "classnames";
 setCommentList(_orderBy(commentList,'like','desc'))
 className={classNames('aaa',{'active':item.key==curTab})}

7.input雙向綁定

 const [value,setValue] = useState('11');
<input value={value} onChange={(e)=>setValue(e.target.value)}/>

8.獲取dom

import {useRef } from "react";
const inputRef = useRef(null);
<input ref={inputRef} />
console.log(inputRef.current) //通過inputRef.current可以拿到dom對象,inputRef.current.focus();

9.組件傳參

  • 父傳子
//子組件
function Child(props){
    console.log(props) // {name:'',age:12......}
   return (
        <div>
         {props.name}
         {props.age}
         {props.child}
         </div>
       
    )
}
const name = 'his name is jack';
const age = 12;
<Child 
      name={'jack'} 
      age={12}
      isGo={false}
      list={['1','2']}
      obj={{name:'xiaoming'}}
      child={<div>child node</div>}
></Child>
  • 子傳父
function Son({onGetMsg}){
    const msg = 'hello world';
    //注意這里寫法,用箭頭函數(shù),否則會(huì)直接執(zhí)行
    return <button onClick={()=>onGetMsg(msg)}>傳參給父組件</button>
}

function App() {
    const [msg,setMsg] = useState('默認(rèn)值');
    function getMsg(msg){
        setMsg(msg)
    }
  return (
    <div className="App">
        <h4>{msg}</h4>
      <Son onGetMsg={getMsg}></Son>
    </div>
  );
}

10.插槽

  • 默認(rèn)插槽
function Son(props){
    console.log(props)//props.children 
    return  <div> {props.children}</div>
}

function App() {
  return (
    <div className="App">
      <Son>
        <i>我是插槽</i>
      </Son>
    </div>
  );
}

11.給下級(jí)組件注入數(shù)據(jù),類似vue的provide和inject

import {createContext,useContext} from 'react'
const ctx = createContext();

function Son2(){
  const msg= useContext(ctx)
  console.log(msg) // hello world
  return <div>son2</div>
}
function Son1(){
  return <Son2></Son2>
}

function App() {
  return (
    <div className="App">
      <ctx.Provider value={'hello world'}>
          <Son1></Son1>
      </ctx.Provider>
    </div>
  );
}

12.useEffect 類似vue的watch
useEffect(()=>{},參數(shù)2)

  • 參數(shù)2為空時(shí),頁面初始化和更新都會(huì)執(zhí)行
  • 為空數(shù)組[]時(shí),只有頁面初始化會(huì)執(zhí)行
  • 為空數(shù)組[msg]時(shí),只有頁面初始化和msg變化時(shí)會(huì)執(zhí)行
    const [msg,setMsg] = useState('');
    const [count,setCount] = useState(0);
    useEffect(()=>{
        console.log('副作用被執(zhí)行了')
       return ()=>{
               /*useEffect 的 return 函數(shù)(也叫 “清理函數(shù) / 清除函數(shù)”)是 React 提供的副作用清理機(jī)制,核心作用是:在組件卸載時(shí)、或者當(dāng)前 useEffect 下次執(zhí)行前,清理掉該 useEffect 中創(chuàng)建的副作用(比如定時(shí)器、事件監(jiān)聽、網(wǎng)絡(luò)請求等),避免內(nèi)存泄漏、重復(fù)執(zhí)行等問題。*/
        }
    },[msg])
    const inputChange = (e)=>{
        setMsg(e.target.value)
    }
     const countPlus = (e)=>{
        setCount(count+1)
    }
  return (
    <div className="App">
        <input value={msg} onChange={(e)=>{inputChange(e)}}/>
        <button onClick={()=>countPlus()}>+{count}</button>
        <h2>{msg}</h2>
    </div>
  );
useEffect 只能 return 一個(gè)【清理函數(shù)】,不能 return 任何別的東西!

// ? 錯(cuò)誤:直接 return 了異步請求
useEffect(async () => {
  const res = await axios.get(...)
}, [])
因?yàn)?async 函數(shù)會(huì) 自動(dòng) return 一個(gè) Promise,React 不允許!
? 正確寫法
useEffect(() => {
  const fetchData = async () => {
    const res = await axios.get("xxx");
  };
  fetchData();

  // 只有這個(gè) return 是允許的!
  return () => {
    console.log("組件卸載時(shí)清理");
  };
}, []);

13.自定義hook鉤子函數(shù)

function useToggle(){
        const [show,setShow] = useState(true)
        const toggle = ()=>{
            setShow(!show);
        }
        return{
            show,
            toggle,
        }
    }   
    const {show : show1,toggle : toggle1} = useToggle();
    const {show : show2,toggle : toggle2} = useToggle();
  return (
    <div className="App">
        <button onClick={()=>toggle1()}>toggle</button>
        {show1 && <div>哈哈哈哈哈哈</div>}
         <button onClick={()=>toggle2()}>toggle</button>
        {show2 && <div>嘿嘿嘿嘿嘿嘿嘿嘿</div>}
    </div>

配合接口使用

function App() {
    const [commentList,setCommentList] = useState([])
   useEffect(async ()=>{
        async function getList(){
            let {data} = await axios.get('http://localhost:10034/comments');
            console.log(data)
            return data;
        }
        let data = await getList();
        setCommentList(data)
   },[])
  return (
    <ul className="App">
        {commentList.map(item=><li>
            <h4>{item.username}</h4>
            <p>{item.content}</p>
            <p>{item.likeCount}</p>
            <p>{item.publishTime}</p>
        </li>)} 
    </ul>
  );
}

四、redux
Redux Toolkit(RTK)

  • 定義數(shù)據(jù) 使用createSlice 來定義數(shù)據(jù)
  • 使用數(shù)據(jù) 使用useSelector來使用數(shù)據(jù)
  • 修改數(shù)據(jù) 使用useDispatch來改變數(shù)據(jù)

1.安裝

npm install @reduxjs/toolkit react-redux

2.項(xiàng)目結(jié)構(gòu)
src根目錄信件store文件夾,然后將不同的模塊放入modules文件夾中,在index.js中引用


image.png

3.demo:一個(gè)login組件,一個(gè)home組件,login登錄后,home的用戶信息發(fā)生改變
分析:需要將用戶信息存入store中,方便組件同時(shí)使用數(shù)據(jù)
(1)定義:用來存放用戶信息的數(shù)據(jù)

// userinfoStore.js 
import { createSlice } from "@reduxjs/toolkit";
//createSlice 用來定義數(shù)據(jù)和action的方法
const userinfoSlice = createSlice({
    name:'userInfo',
    initialState:{
        username:'admin',
        truename:'用戶名',
        xzqdm:'340000',
        phone:'1234567890'
    },
    reducers:{
       //  參數(shù)兩個(gè),一個(gè)state,一個(gè)action,action.payload是傳過來的數(shù)據(jù)
        setUsername(state,action){
            state.username = action.payload;
        },
        setTruename(state,action){
            state.truename = action.payload;
        },
        setXzqdm(state,action){
            state.xzqdm = action.payload;
        },
        setPhone(state,action){
            state.phone = action.payload;
        },
    }
})
//將action的方法導(dǎo)出,方便其他組件使用并且改變state數(shù)據(jù)
export const {
    setUsername,
    setTruename,
    setXzqdm,
    setPhone,
} = userinfoSlice.actions;
//導(dǎo)出,主要為了給store中的index.js使用
export default userinfoSlice.reducer;
// store/index.js
import userinfoReducer from './modules/userinfoStore'
import { configureStore } from '@reduxjs/toolkit'
//用來管理所有的數(shù)據(jù),reducer不定義的數(shù)據(jù),其他組件就訪問不到
export const store = configureStore({
    reducer:{
        userInfo:userinfoReducer,//組件中使用state后面的變量名用的是這里的
    }
})

2.使用:通過 useSelector 來獲取值

import { useSelector } from "react-redux";
//state.userInfo和store/index.js中的reducer保持一致
const {username,truename,xzqdm,phone} = useSelector((state)=>state.userInfo);

3.修改: 通過 useDispatch 來修改值

import { useDispatch } from "react-redux";
import {setUsername,setTruename,setXzqdm,setPhone} from '../store/modules/userinfoStore'
function LoginPage(){
    const dispath = useDispatch();//需要放在函數(shù)體的頂部,否則報(bào)錯(cuò)
    const handleLogin = ()=>{
        let userInfo = {
            username:'xiaoming',
            truename:'小明',
            xzqdm:'341001',
            phone:'1789578961'
        }
      // 將要修改的值傳過去
       dispath(setUsername(userInfo.username));
       dispath(setTruename(userInfo.truename));
       dispath(setXzqdm(userInfo.xzqdm));
       dispath(setPhone(userInfo.phone));
    }
    return(
        <div>
           <button onClick={handleLogin}>登錄</button>
        </div>
    )
}

4.在根標(biāo)簽注入store

import { Provider } from 'react-redux';
import {store} from './store/index'
root.render(
    <Provider store={store}>
        <App />
    </Provider>
);

五.React Router 路由

npm install react-router-dom //安裝依賴

1.定義路由

import { createBrowserRouter,createHashRouter,Navigate } from 'react-router-dom'
// createBrowserRouter 這種是定義history模式路由
// createHashRouter  這種是定義hash模式路由
const router = createBrowserRouter([
    {path:'/login', element:<Login/>},
    {path:'home',element:<Home/>},
    {//多級(jí)路由
        path:'/layout',
        element: <Layout/>,
        children:[
            //index:true 類似vue的redirect
            {index:true,element:<Navigate to="/home" replace/>},
            {path:'home',element:<Home/>},
        ],
    },
])
export default router;

2.路由跳轉(zhuǎn)

import { useNavigate } from "react-router-dom";
const navigate = useNavigate();
navigate('/home');

3.路由傳參
(1) 從地址欄取參數(shù),類似form的get

import { useSearchParams } from "react-router-dom";
{path:'user',element:<Article/>},//router中正常定義即可
navigate ('/user?id=100&name=jack');//這是傳參方式
const [params] = useSearchParams();//這是取參方式
const userId = params.get('id');
const userName = params.get('name');
<div>用戶id:{userId}</div>
<div>用戶名:{userName}</div>

(2) 隱藏參數(shù),類似form的post

import { useParams } from "react-router-dom";
{path:'article/:id',element:<Article/>},//需要在router中這樣定義
const navigate = useNavigate();
navigate('/article/100');//傳參方式
const {id} = useParams();//取參方式
<h2>文章id:{id}</h2>

4.入口文件使用router

import {RouterProvider} from 'react-router-dom'
import router from './router/index'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
      <RouterProvider router={router} />
);

案例呈現(xiàn)


image.png

項(xiàng)目結(jié)構(gòu)


image.png

router/index.js 代碼

import { createBrowserRouter,Navigate} from 'react-router-dom'
import {AuthRoute} from './guard' //導(dǎo)航守衛(wèi)
import {lazy} from 'react'
// lazy 的作用:讓路由組件 按需加載,不浪費(fèi)流量,提升首屏速度,在入口文件搭配搭配 Suspense使用

const Login = lazy(()=>import('../components/login'))
const Layout = lazy(()=>import('../components/layout/index'))
const Home = lazy(()=>import('../components/layout/home'))
const About = lazy(()=>import('../components/layout/about'))
const Article = lazy(()=>import('../components/layout/article'))
const User = lazy(()=>import('../components/layout/user'))
const Notfound = lazy(()=>import('../components/404'))

const router = createBrowserRouter([
    {
        path:'/login',
        element:<Login/>,
    },
  /* {
        path:'/manage',
        element:(
           <AuthRoute>
                <Layout/> //將所有需要登錄才能訪問的后臺(tái)頁面使用AuthRoute包裹
            </AuthRoute>
      ), 
   },*/
    {
        path:'/',
        element:(
            <AuthRoute>
                <Layout/> //將所有需要登錄才能訪問的后臺(tái)頁面使用AuthRoute包裹
            </AuthRoute>
        ),
        children:[
            //index:true 類似vue的redirect
            {index:true,element:<Navigate to="/home" replace/>},
            {path:'home',element:<Home/>},
            {path:'about',element:<About/>},
            {path:'user',element:<User/>},
           {path:'article/:id/:name',element:<Article/>},
        ],
    },
    {
        path:'*',
        element:<Notfound/>
    }
])
export default router;

router/guard.js代碼 -- 導(dǎo)航守衛(wèi)

//導(dǎo)航守衛(wèi)
import { Navigate } from "react-router-dom";

export const AuthRoute = ({children})=>{
    const isLoin = localStorage.getItem('token');
    if(!isLoin){
        return <Navigate to="/login" replace></Navigate>
    }
    return children;
}

index.js/main.js入口文件 代碼

import React ,{Suspense} from 'react';
import ReactDOM from 'react-dom/client';
import {RouterProvider} from 'react-router-dom'
import router from './router/index'
import './index.css'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
   <React.StrictMode>
        <Suspense fallback={<div>頁面加載中...</div>}>
            <RouterProvider router={router} />
        </Suspense>
   </React.StrictMode>
);

login.js 登錄

import { useNavigate } from "react-router-dom";

const Login = ()=>{
    const navigate = useNavigate();
    const handleLogin = ()=>{
        localStorage.setItem('token','123');
        navigate('/home');
    }
    return (
        <div>
            <h1>login.vue</h1>
            <button onClick={()=>{handleLogin()}}>登錄</button>
        </div>
    )
    
}
export default Login;

layout.js

import { useState } from "react";
import { Outlet, useLocation, useNavigate } from "react-router-dom";

const Layout = ()=>{
    const navigate = useNavigate();
    const location = useLocation();
    const [userid,setUserId] = useState('');
    const navs = [
        {path:'/home',label:'home'},
        {path:'/about',label:'about'},
        {path:'/user?id=100&name=jack',label:'user'},
    ];
    const go = (path)=>{
        navigate(path);
    }
    const logout = ()=>{
        localStorage.removeItem('token');
        go('/login')
    }
    return (
        <div className="layout">
            <div className="header">
                {navs.map(e=><div onClick={()=>{go(e.path)}} key={e.path}>{e.label}</div>)}
                <div onClick={()=>{logout()}}>退出登錄</div>
            </div>
            <div>
                <input placeholder="輸入文章id" value={userid} onChange={(e)=>{setUserId(e.target.value)}}/> 
              <button onClick={()=>{go(`/article/${userid}/一篇文章`)}}>查看文章</button>
            </div>
            <div className="content">
                <p>當(dāng)前路徑:{location.pathname}</p>
                <hr/>
                <div><Outlet/></div>//Outlet類似router-view
            </div>
        </div>
    )
}
export default Layout;

user.js 代碼 : 通過useSearchParams 取參

import { useSearchParams } from "react-router-dom";

const Component = ()=>{
    const [params] = useSearchParams();
    const userId = params.get('id');
    const userName = params.get('name');
    return (
        <div>
            <div>用戶id:{userId}</div>
            <div>用戶名:{userName}</div>
        </div>
    )
}
export default Component;

article.js : 通過 useParams的取參

import { useParams } from "react-router-dom";

const Article = ()=>{
    const {id,name} = useParams();
    return (
        <div>
            <div>文章id:{id}</div>
            <div>文章標(biāo)題:{name}</div>
        </div>
    )
}
export default Article;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容