最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文詳解React Redux設(shè)計思想與工作原理

 更新時間:2023年09月24日 17:26:14   作者:左耳咚  
最近看項目中使用了?Redux,?便嘗試了解一波?Redux?的設(shè)計思想與工作原理,所以本文詳細的給大家介紹了Redux設(shè)計思想與工作原理,需要的朋友可以參考下

設(shè)計思想

在開始了解之前,我們需要先了解 Redux 解決了什么問題?

Redux 解決了什么問題

在沒有 Redux 之前, 如果組件之間存在大量通信,甚至有些通信跨越多個組件,或者多個組件之間共享一套數(shù)據(jù),簡單的父子組件間傳值不能滿足我們的需求,自然而然地,我們需要有一個地方存取和操作這些公共狀態(tài)。而 redux 就為我們提供了一種管理公共狀態(tài)的方案,便于管理比較復(fù)雜的通信場景。

Redux 的設(shè)計理念

Redux 的設(shè)計采用了 Facebook 提出的 Flux 數(shù)據(jù)處理理念

在 Flux 中通過建立一個公共集中數(shù)據(jù)倉庫 Store 進行管理,整體分成四個部分即: View (視圖層)、Action (動作)、Dispatcher (派發(fā)器)、Store (數(shù)據(jù)層)

如下圖所示,當(dāng)我們想要修改倉庫的數(shù)據(jù)時,需要從 View 中觸發(fā) Action,由 Dispatcher 派發(fā)到 Store 修改數(shù)據(jù),從而驅(qū)動視圖更新

這種設(shè)計的好處在于其數(shù)據(jù)流向是單一的,數(shù)據(jù)的修改一定是會經(jīng)過 Action、Dispatcher 等動作才能實現(xiàn),方便預(yù)測、維護狀態(tài)的流向。

當(dāng)我們了解了 Flux 的設(shè)計理念后,便可以照葫蘆畫瓢了。

如下圖所示,在 Redux 中同樣需要維護一個公共數(shù)據(jù)倉庫 Store, 而數(shù)據(jù)流向只能通過 View 觸發(fā) Action、 Reducer更新派發(fā), Store 改變從而驅(qū)動視圖更新

工作原理

當(dāng)我們了解了 Redux 的設(shè)計理念后,趁熱打鐵炫一波 Redux 的工作原理,我們知道使用 Redux 進行狀態(tài)管理的第一步就是需要先創(chuàng)建數(shù)據(jù)倉庫 Store, 也就會需要調(diào)用 createStore 方法。那我們就先拿 createStore 開炫。

createStore

從 Redux 源碼中我們不難看出,createStore 接收 reducer、初始化state、中間件三個參數(shù),當(dāng)執(zhí)行 createStore 時會記錄當(dāng)前的 state 狀態(tài),并返回 store 對象,包含 dispatch、subscribe、getState 等屬性。

其中

  • dispatch: 用來觸發(fā) Action
  • subscribe: 當(dāng) store 值的改變將觸發(fā) subscribe 的回調(diào)
  • getState: 用來獲取當(dāng)前的 state 狀態(tài)。

getState 比較簡單,直接返回當(dāng)前的 state 狀態(tài),接下來我們將著重了解 dispatch 與 subscribe 的實現(xiàn)。

function createStore(reducer, preloadedState, enhancer) {
   let currentReducer = reducer // 記錄當(dāng)前的 reducer
   let currentState = preloadedState // 記錄當(dāng)前的 state
   let isDispatching = false // 是否正在進行 dispatch
   function getState() {
      return currentState // 通過 getState 獲取當(dāng)前的 state
   }
   // 觸發(fā) action
   function dispatch(action: A) {}
   function subscribe(listener: () => void) {}
   // 初始化 state
   dispatch({ type: ActionTypes.INIT } as A)
   // 返回一個 sttore
   const store = {
    dispatch: dispatch as Dispatch<A>,
    subscribe,
    getState
   }
   return store
}

dispatch

在 Redux 中, 修改數(shù)據(jù)的唯一方式就是通過 dispatch,而 dispatch 接受一個 action 對象作為參數(shù),執(zhí)行 dispatch 方法,將生成新的 state,并觸發(fā)監(jiān)聽事件。

function dispatch(action) {
    // 如果已經(jīng)在觸發(fā)中,則不允許再次出發(fā) dispatch (禁止套娃)
    // 例如:在 reducer 中觸發(fā) dispatch
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }
    try {
      // 上鎖
      isDispatching = true
      // 調(diào)用 reducer,獲取新的 state
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }
    // 觸發(fā)訂閱事件
    const listeners = (currentListeners = nextListeners)
    listeners.forEach(listener => {
      listener()
    })
    return action
  }

subscribe

在 Redux 中, 可以通過 subscribe 方法來訂閱 store 的變化, 一旦 store 發(fā)生了變化, 就會執(zhí)行訂閱的回調(diào)函數(shù)

可以看到 subscribe 方法接收一個回調(diào)函數(shù)作為參數(shù), 執(zhí)行 subscribe 方法將會返回一個 unsubscribe 函數(shù), 用于取消訂閱

function subscribe(listener: () => void) {
    if (isDispatching) {
      throw new Error()
    }
    let isSubscribed = true // 防止調(diào)用多次 unsubscribe
    ensureCanMutateNextListeners() // 確保 nextListeners 是 currentListeners 的快照,而不是同一個引用
    const listenerId = listenerIdCounter++
    nextListeners.set(listenerId, listener) //nextListeners 添加訂閱事件
    // 取消訂閱事件
    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }
      if (isDispatching) {
        throw new Error()
      }
      isSubscribed = false
      ensureCanMutateNextListeners(); // 如果某個訂閱事件執(zhí)行了 unsubscribe, nextListeners 創(chuàng)建了新的內(nèi)存地址,而原先的listeners 依然保持不變 (dispatch 方法中的312 行)
      nextListeners.delete(listenerId)
      currentListeners = null
    }
  }

ensureCanMutateNextListeners 與 currentListeners 的作用

承接上文,在 subscribe 中不管是注冊監(jiān)聽還是取消監(jiān)聽都會調(diào)用 ensureCanMutateNextListeners 的方法,那么這個方法是做什么的呢?

從函數(shù)的邏輯上不難得出答案:

ensureCanMutateNextListeners 確保 nextListeners 是 currentListeners 的快照,而不是同一個引用

function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) { // currentListeners 用來確保循環(huán)的穩(wěn)定性
      nextListeners = new Map()
      currentListeners.forEach((listener, key) => {
        nextListeners.set(key, listener)
      })
    }
}

在 dispatch 或者 subscribe 函數(shù)中,都是通過 nextListeners 觸發(fā)監(jiān)聽,那為何還需要使用 currentListeners?

這里就不賣關(guān)子了,這里的 currentListeners 用于確保在 dispatch 中 listener 的數(shù)量不會發(fā)生變化, 確保當(dāng)前循環(huán)的穩(wěn)定性。

請看下面的例子??

const a = store.subscribe(() => {
  /* a */
});
const b = store.subscribe(() => a());
const c = store.subscribe(() => {
  /*/ c */
});
store.dispatch(action);

上面的代碼在 Redux 中是被允許的, 通過 subscribe 注冊監(jiān)聽函數(shù) a、b、c,此時 nextListeners 指向 [a, b, c]

當(dāng)執(zhí)行 dispatch 時, listener、currentListeners、nextListeners 將指向地址 [a, b, c];

// dispatch 觸發(fā)監(jiān)聽事件的邏輯
// 觸發(fā)訂閱事件 
const listeners = (currentListeners = nextListeners)
listeners.forEach(listener => { listener() })

當(dāng)執(zhí)行到 b 監(jiān)聽函數(shù)時,將解綁 a 函數(shù)的監(jiān)聽事件,如果直接修改 nextListeners, 在循環(huán)中操作數(shù)組是非常危險的事情, 因此借助 ensureCanMutateNextListeners、currentListeners 為 nextListeners 開辟了新的內(nèi)存地址,對 nextListeners 的操作將不影響 listener。

實現(xiàn)一個 mini react-redux

上文我們說到,一個組件如果想從 store 存取公用狀態(tài),需要進行四步操作:

  • import引入store
  • getState獲取狀態(tài)
  • dispatch修改狀態(tài)
  • subscribe訂閱更新

代碼相對冗余,我們想要合并一些重復(fù)的操作,而 react-redux 就提供了一種合并操作的方案:react-redux提供 Providerconnect 兩個API, Provider 將 store 放進 this.context 里,省去了 import 這一步, connect將 getState、dispatch 合并進了this.props,并自動訂閱更新,簡化了另外三步,下面我們來看一下如何實現(xiàn)這兩個API:

Provider

Provider 組件比較簡單,接收 store 并放進全局的 context 對象,使 store 可用于任何需要訪問 Redux store 的嵌套組件

import React, { createContext } from 'react';
let StoreContext;
const Provider = (props) => {
  StoreContext = createContext(props.store);
  return <StoreContext.Provider value={props.store}>{ props.children }</StoreContext.Provider>
}

connect

下面我們來思考一下如何實現(xiàn) connect ,我們先回顧一下connect的使用方法

connect(mapStateToProps, mapDispatchToProps)(App)

connect 接收 mapStateToProps、mapDispatchToProps 兩個函數(shù),然后返回一個高階函數(shù), 最終將 mapStateToProps、mapDispatchToProps 函數(shù)的返回值通過 props 形式傳遞給 App 組件

我們直接放出connect的實現(xiàn)代碼,并不復(fù)雜:

import React, { createContext, useContext, useEffect } from 'react';
export function connect(mapStateToProps, mapDispatchToProps) {
    return function (Component) {
      const connectComponent: React.FC = (props) => {
        const store = useContext(StoreContext);
        const [, updateState] = useState();
        const forceUpdate = useCallback(() => updateState({}), []);
        const handleStoreChange = () => {
            // 強制刷新
            forceUpdate();
        }
        useEffect(() => {
          store.subscribe(handleStoreChange)
        }, [])
        return (
          <Component
              // 傳入該組件的props,需要由connect這個高階組件原樣傳回原組件  
              { ...(props) }
              // 根據(jù) mapStateToProps 把 state 掛到 this.props 上 
              { ...(mapStateToProps(store.getState())) }
              // 根據(jù)mapDispatchToProps把dispatch(action)掛到this.props上
              { ...(mapDispatchToProps(store.dispatch)) }
          />
        )
      }
      return connectComponent;
    }
}

可以看出 connect 通過 useContext 實現(xiàn)和 store 的鏈接,將 state 作為第一個參數(shù)傳給 mapStateToProps、將 dispatch 作為第一個參數(shù)傳遞給 mapDispatchToProps,最終將結(jié)果通過 props 形式傳遞給子組件。

其實 connect 這種設(shè)計,是裝飾器模式的實現(xiàn),所謂裝飾器模式,簡單地說就是對類的一個包裝,動態(tài)地拓展類的功能。這里的 connect 以及 React 中的高階組件(HoC)都是這一模式的實現(xiàn)。

對類的裝飾常用于拓展類的功能,對類中函數(shù)的裝飾常用于 AOP 切面

@decorator
class A {}
// 等同于
class A {}
A = decorator(A) || A;

裝飾器只能用于類和類的方法,不能用于函數(shù),因為存在函數(shù)提升。 如果一定要裝飾函數(shù),可以使用高階函數(shù)

mini react-redux

通過上文,我們了解了 Provider 與 connect 的實現(xiàn),我們可以寫個 mini react-redux 來測試一下

1 創(chuàng)建如下目錄結(jié)構(gòu)

2 實現(xiàn) createStore 函數(shù) 創(chuàng)建一個 createStore.ts 文件,createStore 最終將返回 store 對象,包含 getState、dispatch、subscribe

export const createStore = (reducer: Function) => {
    let currentState: undefined = undefined;
    const obervers: Array<Function> = [];
    function getState() {
        return currentState;
    }
    function dispatch(action: { type: string}) {
        currentState = reducer(currentState, action);
        obervers.forEach(fn => fn());
    }
    function subscribe(fn: Function) {
        obervers.push(fn);
    }
    dispatch({ type: '@@REDUX/INIT' }); // 初始化 state
    return {
        getState,
        dispatch,
        subscribe
    }
}

3 實現(xiàn) reducer

createStore 函數(shù)接收一個 reducer 方法,reducer 常用來分發(fā) action, 并返回新的 state

// reducer.ts
const initialState = {
    count: 0
}
export function reducer(state = initialState, action: { type: string}) {
    switch (action.type) {
        case 'add': 
            return {
                ...state,
                count: state.count + 1
            }
        case 'reduce':
            return {
                ...state,
                count: state.count - 1
            }
        default:
            return initialState;
    }
}

4 實現(xiàn) Provider 與 connect

/* eslint-disable react-hooks/rules-of-hooks */
//@ts-nocheck 
import React, { createContext, useContext, useEffect } from 'react';
let StoreContext;
const Provider = (props) => {
  StoreContext = createContext(props.store);
  return <StoreContext.Provider value={props.store}>{ props.children }</StoreContext.Provider>
}
export default Provider;
export function connect(mapStateToProps, mapDispatchToProps) {
    return function (Component) {
      const connectComponent: React.FC = (props) => {
        const store = useContext(StoreContext);
        const [, updateState] = React.useState();
        const forceUpdate = React.useCallback(() => updateState({}), []);
        const handleStoreChange = () => {
            // 強制刷新
            forceUpdate();
        }
        useEffect(() => {
          store.subscribe(handleStoreChange)
        }, [])
        return (
          <Component
              // 傳入該組件的props,需要由connect這個高階組件原樣傳回原組件   
              { ...(props) }
              // 根據(jù) mapStateToProps 把 state 掛到 this.props 上       
              { ...(mapStateToProps(store.getState())) }
              // 根據(jù)mapDispatchToProps把dispatch(action)掛到this.props上              
              { ...(mapDispatchToProps(store.dispatch)) }
          />
        )
      }
      return connectComponent;
    }
}

5 修改 main.tsx

// main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import  Provider from './react-redux/index.tsx';
import { createStore } from './react-redux/createStore.ts';
import { reducer } from './react-redux/reducer.ts';
ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
      <Provider store={createStore(reducer)}>
        <App />
      </Provider>
  </React.StrictMode>,
)

6 修改 App.tsx

// App.tsx
import { useState } from 'react';
import { connect } from './react-redux';
const addAction = {
  type: 'add'
}
const mapStateToProps = (state: { count: number }) => {
  return {
    count: state.count
  }
}
const mapDispatchToProps = (dispatch: any) => {
  return {
    addCount: () => {
      dispatch(addAction)
    }
  }
}
interface Props {
  count: number;
  addCount: () => void;
}
function App(props: Props): JSX.Element {
  const { count, addCount } = props;
  return (
    <div className="App">        
      { count }        
      <button onClick={ () => addCount() }>增加</button>      
    </div>
  );
}
export default connect(mapStateToProps, mapDispatchToProps)(App);

運行項目,點擊增加按鈕,如能正確計數(shù),我們整個redux、react-redux的流程就走通了。

中間件

在大部分場景下, 我們需要自定義 dispatch 的行為, 在 Redux 中, 我們可以使用 中間件來拓展 dispatch 的功能

類似于 Express 或者 Koa, 在這些框架中,我們可以使用中間件來拓展 請求 和 響應(yīng) 之間的功能

而 Redux 中間件的作用是在 action 發(fā)出之后, 到達 reducer 之前, 執(zhí)行一系列的任務(wù)

在 Redux 中我們可以通過 applyMiddleware 生成一個強化器 enhancer 作為 createStore 的第二個參數(shù)傳遞。

import { createStore, applyMiddleware } from 'redux'  
import rootReducer from './reducer'  
import { print1, print2, print3 } from './exampleAddons/middleware'  
const middlewareEnhancer = applyMiddleware(print1, print2, print3)  
// Pass enhancer as the second arg, since there's no preloadedState  
const store = createStore(rootReducer, middlewareEnhancer)  
export default store

正如它們的名稱所示,每個中間件在調(diào)度操作時都會打印一個數(shù)字

import store from './store'  
store.dispatch({ type: 'todos/todoAdded', payload: 'Learn about actions' })  
// log: '1'  
// log: '2'  
// log: '3'

在這個例子中,當(dāng)觸發(fā) dispatch 的內(nèi)部執(zhí)行順序如下:

  • The print1 middleware (which we see as store.dispatch)
  • The print2 middleware
  • The print3 middleware
  • The original store.dispatch
  • The root reducer inside store

實現(xiàn)一個中間件

從上文得知, 我們了解了如何使用中間件, 接下來我們將實現(xiàn)一個中間件。

在 Redux 中,中間件其實是由三個嵌套函數(shù)組成

function exampleMiddleware(storeAPI) {  
   return function wrapDispatch(next) {  
       return function handleAction(action) {  
         // Do anything here: pass the action onwards with next(action),  
         // or restart the pipeline with storeAPI.dispatch(action)  
         // Can also use storeAPI.getState() here  
         return next(action)  
      }  
   }  
}

最外層函數(shù) exampleMiddleware 將會被 applyMiddleware 調(diào)用,并傳入 storeAPI 對象( 形如 {dispatch, getState} ),

中間層函數(shù) wrapDispatch 接收一個 next 參數(shù),next 實際上就是中間管道的下一個中間件函數(shù),如果是最后一個 next,那么他的下一個中間件函數(shù)就是 dispatch

最內(nèi)層函數(shù) handleAction 接收一個 Action 對象

此時,我們知道了如何編寫一個中間件,接下來我們將實現(xiàn)一個 logger 中間件

const loggerMiddleware = storeAPI => next => action => {  
    console.log('dispatching', action)  
    let result = next(action)  
    console.log('next state', storeAPI.getState())  
    return result  
}

寫完 logger 中間件后,我們嘗試在 Redux 中使用,如下

import { createStore, applyMiddleware } from "redux";
const initialState = {
  count: 0
}
function reducer(state = initialState, action: { type: string}) {
  switch (action.type) {
      case 'add': 
          return {
              ...state,
              count: state.count + 1
          }
      case 'reduce':
          return {
              ...state,
              count: state.count - 1
          }
      default:
          return initialState;
  }
}
const logger1 = storeAPI => next => action => {  
  console.log('logger1 開始');
  const result = next(action)  
  console.log('logger1 結(jié)束');
  return result  
}
const logger2 = storeAPI => next => action => {  
  console.log('logger2 開始');
  const result = next(action)  
  console.log('logger2 結(jié)束');
  return result  
}
const logger3 = storeAPI => next => action => {  
  console.log('logger3 開始');
  const result = next(action)  
  console.log('logger3 結(jié)束');
  return result  
}
const middlewares = applyMiddleware(logger1, logger2, logger3);
const store = createStore(reducer, middlewares);
store.dispatch({ type: 'add' });

最終將打印

從打印的記過來看,如果之前有接觸過 Express 或者 Koa 的同學(xué),應(yīng)該可以很快發(fā)現(xiàn),這個是一個洋蔥模型

applyMiddleware 的實現(xiàn)原理

從上可知,Redux 提供了一個 applyMiddleware 方法用于將中間件拓展到 dispatch 上

具體是如何拓展的呢?

從源碼我們不難看出,最終是通過 compose 也就是利用 reduce 方法,將下一個的中間件函數(shù)作為參數(shù),在上一個中間件的函數(shù)體內(nèi)執(zhí)行。

注意這里傳入 compose 內(nèi)的每一個函數(shù)都是一個雙層嵌套函數(shù)。

// applyMiddleware 源碼
export default function applyMiddleware(
  ...middlewares
) {
  // 返回一個接收 createStore為入?yún)⒌暮瘮?shù)
  return createStore => (reducer, preloadedState) => {
    // 創(chuàng)建 store
    const store = createStore(reducer, preloadedState)
    let dispatch: Dispatch = () => {
      throw new Error(
        'Dispatching while constructing your middleware is not allowed. ' +
          'Other middleware would not be applied to this dispatch.'
      )
    }
    /**
     * middleware 形如:
     * ({dispatch, getState}) => next => action => { ... return next(action) }
     */
    const middlewareAPI: MiddlewareAPI = {
      getState: store.getState,
      dispatch: (action, ...args) => dispatch(action, ...args)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)
    return {
      ...store,
      dispatch
    }
  }
}
function compose(...funcs) {
  if (funcs.length === 0) {
    // infer the argument type so it is usable in inference down the line
    return (arg:) => arg
  }
  if (funcs.length === 1) {
    return funcs[0]
  }
  return funcs.reduce(
    (a, b) =>
      (...args) =>
        a(b(...args))
  )
}

模擬洋蔥模型

承接上文,我們大概了解了什么是洋蔥模型,接下來我們將模擬一波洋蔥模型的實現(xiàn)。

const func1 = (fn) => () => {
    console.log('進入func1', fn);
    const res = fn();
    console.log('離開func1');
    return res;
}
const func2 = (fn) => () => {
    console.log('進入func2', fn);
    const res = fn();
    console.log('離開func2');
    return res;
}
const func3 = (fn) => () => {
    console.log('進入func3', fn);
    const res = fn();
    console.log('離開func3');
    return res;
}
const composeB = (...fns) => {
    if (fns.length === 0) return arg => arg    
    if (fns.length === 1) return fns[0]  
    return fns.reduce((res, cur) => {
        return (...args) => res(cur(...args))
    });
}
// (...args) => func1((...args) => func2((...args) => func3(...args))) // 從左到右入棧
const dispatch = () => void 0;
const c = composeB(func1, func2, func3)(dispatch);
c();

總結(jié)

書寫至此,突然有一絲煽情,之前在下對于 redux 充滿了未知與恐懼,剛開始特別害怕學(xué)不懂,便遲遲不敢嘗試,不斷地擺爛,破罐子破摔。可當(dāng)靜下心來,接納自己的愚蠢,慢慢地一遍又一遍地讀每一行代碼與一些很 nice 的文章時,似乎恐懼是自己事前設(shè)定好的。而生活里也并不是只有成功 or 失敗,失敗也不應(yīng)該判定一個人的價值,所以不需要懼怕失敗。

以上就是一文詳解Redux設(shè)計思想與工作原理的詳細內(nèi)容,更多關(guān)于Redux設(shè)計思想與工作原理的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • React組件的創(chuàng)建與state同步異步詳解

    React組件的創(chuàng)建與state同步異步詳解

    這篇文章主要介紹了react組件實例屬性state,有狀態(tài)state的組件稱作復(fù)雜組件,沒有狀態(tài)的組件稱為簡單組件,狀態(tài)里存儲數(shù)據(jù),數(shù)據(jù)的改變驅(qū)動頁面的展示,本文結(jié)合實例代碼給大家詳細講解,需要的朋友可以參考下
    2023-03-03
  • React生命周期的使用與實例解讀

    React生命周期的使用與實例解讀

    這篇文章詳細介紹了React組件的生命周期,包括掛載、更新、卸載和錯誤處理階段,通過代碼示例和詳細講解,幫助讀者理解每個生命周期方法的作用和使用場景
    2026-01-01
  • 快速創(chuàng)建React項目并配置webpack

    快速創(chuàng)建React項目并配置webpack

    這篇文章主要介紹了創(chuàng)建React項目并配置webpack,在這里需要注意,Create?React?App?requires?Node?14?or?higher.需要安裝高版本的node,本文給大家介紹的非常詳細,需要的朋友參考下吧
    2022-01-01
  • React Native:react-native-code-push報錯的解決

    React Native:react-native-code-push報錯的解決

    這篇文章主要介紹了React Native:react-native-code-push報錯的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • 基于visual studio code + react 開發(fā)環(huán)境搭建過程

    基于visual studio code + react 開發(fā)環(huán)境搭建過程

    今天通過本文給大家分享基于visual studio code + react 開發(fā)環(huán)境搭建過程,本文給大家介紹的非常詳細,包括react安裝問題及安裝 Debugger for Chrome的方法,需要的朋友跟隨小編一起看看吧
    2021-07-07
  • 一步步設(shè)置你的第一個Next.js項目(非常詳細!)

    一步步設(shè)置你的第一個Next.js項目(非常詳細!)

    Next.js是一個基于React的全棧框架,旨在幫助開發(fā)者構(gòu)建高性能、可擴展的Web應(yīng)用,這篇文章主要介紹了如何設(shè)置你的第一個Next.js項目的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2025-09-09
  • 解決react中l(wèi)abel標簽for報錯問題

    解決react中l(wèi)abel標簽for報錯問題

    這篇文章主要介紹了react中l(wèi)abel標簽for報錯問題,解決辦法就是react中l(wèi)abel標簽沒有for屬性,用htmlFor代替for屬性,感興趣的朋友跟隨小編一起看看吧
    2022-02-02
  • react?cropper圖片裁切實例詳解

    react?cropper圖片裁切實例詳解

    這篇文章主要為大家介紹了react?cropper圖片裁切實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-11-11
  • React中classnames庫使用示例

    React中classnames庫使用示例

    這篇文章主要為大家介紹了React中classnames庫使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10
  • react-native 封裝選擇彈出框示例(試用ios&android)

    react-native 封裝選擇彈出框示例(試用ios&android)

    本篇文章主要介紹了react-native 封裝選擇彈出框示例(試用ios&android),具有一定的參考價值,有興趣的可以了解一下
    2017-07-07

最新評論

铜山县| 含山县| 新闻| 万宁市| 禄劝| 乌拉特前旗| 昌平区| 额敏县| 宁蒗| 南皮县| 霍林郭勒市| 七台河市| 讷河市| 长治县| 南宁市| 永兴县| 民权县| 濉溪县| 宁陕县| 红原县| 永年县| 宁武县| 林周县| 大港区| 周宁县| 南丹县| 全椒县| 刚察县| 平果县| 上栗县| 南乐县| 阿鲁科尔沁旗| 溧水县| 蓬安县| 响水县| 拉萨市| 辛集市| 社会| 黎城县| 体育| 和静县|