React中的Hooks

在React 16.8版本之前,組件主要分為兩種:類組件(Class Components)函數組件(Function Components)。類組件可以使用 state 來管理內部狀態,也能使用生命周期方法(如 componentDidMount)來處理副作用。而函數組件是“無狀態的”,它們只能接收 props 并返回JSX,無法擁有自己的狀態和生命周期。
React Hooks 是一系列特殊的函數,它們允許你在函數組件中“鉤入”React 的 state 及生命周期等特性。 簡單來說,Hooks 讓函數組件也能擁有和類組件幾乎同等的能力,從此你可以在不編寫 class 的情況下使用 state 和其他 React 功能。這使得函數組件成為現代React開發的首選。

使用hooks的原則:

  • 只在頂層調用 Hooks:不要在循環、條件判斷或嵌套函數中調用 Hooks。必須保證 Hooks 在每次組件渲染時的調用順序都是完全一致的。這是因為 React 依賴于 Hooks 的調用順序來正確地將 state 與對應的 useStateuseEffect 關聯起來。
  • 只在 React 函數中調用 Hooks:你只能在 React 函數組件自定義 Hooks 中調用 Hooks。不能在普通的 JavaScript 函數中調用。

數據驅動更新型:

數據更新useState:

適用于管理組件的局部狀態,如開關的開/關狀態、表單輸入的值、一個計數器的值等。當狀態邏輯簡單,且不依賴于其他復雜狀態時,useState 是最佳選擇。

表單:

import React, { useState } from 'react';function NameInput() {// 聲明一個名為 name 的狀態,初始值為空字符串const [name, setName] = useState('');const handleChange = (event) => {// 調用 setName 更新狀態,觸發重新渲染setName(event.target.value);};return (<div><input type="text" value={name} onChange={handleChange} /><p>Hello, {name}!</p></div>);
}
訂閱更新useReducer:

當狀態邏輯變得復雜,或者下一個狀態依賴于前一個狀態時,useReduceruseState 的一個更強大的替代方案。它借鑒了 Redux 的思想,通過 dispatch 一個 action 來集中管理狀態的更新邏輯。
在事件處理中,調用 dispatch({ type: 'ACTION_TYPE', payload: ... }) 來觸發狀態更新。

import React, { useReducer } from 'react';// 1. Reducer 函數:定義所有可能的狀態轉換
const counterReducer = (state, action) => {switch (action.type) {case 'increment':return { count: state.count + 1 };case 'decrement':return { count: state.count - 1 };case 'reset':return { count: 0 };default:throw new Error();}
};function Counter() {// 2. 使用 useReducer 初始化狀態const [state, dispatch] = useReducer(counterReducer, { count: 0 });return (<div><p>Count: {state.count}</p>{/* 3. Dispatch actions 來觸發更新 */}<button onClick={() => dispatch({ type: 'increment' })}>+</button><button onClick={() => dispatch({ type: 'decrement' })}>-</button><button onClick={() => dispatch({ type: 'reset' })}>Reset</button></div>);
}

狀態的獲取和傳遞值:

訂閱獲取上下文useContext:

在 React 應用中,數據通常是通過 props 從父組件單向地流向子組件。但如果一個狀態需要被深層次的子組件使用,或者被多個不同層級的組件共享,通過 props 層層傳遞(這個過程被稱為 “prop drilling” 或“屬性鉆探”)會變得非常繁瑣和難以維護。

  • 創建 Context: 使用 React.createContext() 創建一個 Context 對象。這個對象就像一個信息頻道。
const MyContext = React.createContext(defaultValue);
  • 提供 Context: 在組件樹的上層,使用 <MyContext.Provider> 組件,通過 value 屬性來“廣播”你想要共享的數據。所有被這個 Provider 包裹的子組件(無論層級多深)都能訪問到這個 value
<MyContext.Provider value={/* 你想共享的任何值 */}><App />
</MyContext.Provider>
  • 消費 Context: 在任何一個子組件中,調用 useContext(MyContext) Hook 來“訂閱”并讀取這個 value
const value = useContext(MyContext);

全局狀態管理:如應用的主題(白天/黑夜模式)、當前的登錄用戶信息、語言偏好設置等。

import React, { useState, useContext, createContext } from 'react';// 1. 創建一個主題 Context,可以給一個默認值
const ThemeContext = createContext('light');// App 組件作為頂層組件
function App() {const [theme, setTheme] = useState('light');const toggleTheme = () => {setTheme(current => (current === 'light' ? 'dark' : 'light'));};// 2. 使用 Provider 將 theme 和 toggleTheme 函數提供給所有子組件return (<ThemeContext.Provider value={{ theme, toggleTheme }}><Toolbar /></ThemeContext.Provider>);
}// Toolbar 組件,它本身不需要 theme,只是一個中間組件
function Toolbar() {return (<div><ThemedButton /></div>);
}// ThemedButton 是真正需要使用 theme 的深層子組件
function ThemedButton() {// 3. 使用 useContext 直接獲取共享的主題和方法const { theme, toggleTheme } = useContext(ThemeContext);const style = {background: theme === 'dark' ? '#333' : '#eee',color: theme === 'dark' ? '#fff' : '#333',padding: '10px',border: 'none',borderRadius: '5px'};return (<button style={style} onClick={toggleTheme}>當前是 {theme} 主題,點我切換</button>);
}export default App;
元素組件獲取useRef:

useRef 是一個非常獨特的 Hook。雖然它也用于在組件中存儲數據,但它與 useState 有一個本質區別:更新 useRef 的值不會觸發組件的重新渲染

用途:
1.訪問 DOM 元素
這是 useRef 的首要用途。你可以創建一個 ref,并將它附加到 JSX 元素的 ref 屬性上,之后就可以通過這個 ref 直接訪問該 DOM 節點。

import React, { useRef } from 'react';function FocusInput() {// 1. 創建一個 ref 對象const inputRef = useRef(null);const handleFocus = () => {// 3. 通過 .current 屬性訪問 DOM 節點并調用其方法if (inputRef.current) {inputRef.current.focus();}};return (<div>{/* 2. 將 ref 附加到 input 元素上 */}<input ref={inputRef} type="text" /><button onClick={handleFocus}>Focus the input</button></div>);
}

2.存儲一個可變的引用值:

因為 useRef 的值在每次渲染時都保持不變,且更新它不會觸發重渲染,所以它也可以作為一個“實例變量”,用來存儲那些你需要在多次渲染之間共享、但又不想觸發視圖更新的數據。更新.current屬性不會觸發任何重渲染

import react, { useState, useEffect, useRef } from 'react';function RenderCounter() {const [count, setCount] = useState(0);// 使用 ref 來存儲渲染次數const renderCount = useRef(0);useEffect(() => {// 每次渲染后,renderCount 的值加一// 注意:更新 ref 不會觸發另一次渲染,避免了無限循環renderCount.current = renderCount.current + 1;});return (<div><p>State Count: {count}</p><p>This component has rendered {renderCount.current} times.</p>  <button onClick={() => setCount(c => c + 1)}>Trigger Re-render</button></div>);
}

狀態派生和保存型:

  • 當你需要緩存一個計算結果(如一個經過過濾的數組,一個復雜的計算值)時,用 useMemo
  • 當你需要緩存一個函數本身(通常是為了作為 prop 傳遞)時,用 useCallback
派生新狀態useMemo:

useMemo 的核心作用是 “記憶”一個計算結果。在 React 中,當一個組件的 stateprops 改變時,整個組件函數會重新執行,這意味著函數內部的所有代碼(包括一些復雜的計算)都會被重新運行。如果某個計算非常耗時,這就會導致界面卡頓,影響用戶體驗。只有當其依賴項發生變化時,它才會重新執行計算,否則它會直接返回上一次緩存的結果。

適用場景:

  • 對一個巨大的列表進行排序或過濾。
  • 在組件中進行復雜的數學運算或數據處理。
  • 當一個子組件的 props 需要通過復雜計算得出時,用 useMemo 來穩定這個 prop,防止子組件不必要的重新渲染。
import React, { useState, useMemo } from 'react';// 假設 allUsers 是一個包含 1000 個對象的巨大數組
const allUsers = [...]; function UserList() {const [searchTerm, setSearchTerm] = useState('');const [anotherState, setAnotherState] = useState(false);//  沒有優化的寫法:// 無論 searchTerm 變不變,只要組件重渲染(比如點擊 Toggle 按鈕),// filter 這個昂貴操作就會被重新執行一次。// const filteredUsers = allUsers.filter(user => user.name.includes(searchTerm));//  使用 useMemo 優化:// 這個 filter 操作現在被“記憶”了。const filteredUsers = useMemo(() => {console.log('Filtering logic is running...'); // 你會發現只有在 searchTerm 改變時才會打印return allUsers.filter(user => user.name.includes(searchTerm));}, [searchTerm]); // 依賴項是 searchTermreturn (<div><input type="text" placeholder="Search users..." onChange={e => setSearchTerm(e.target.value)} />{/* 這個按鈕的點擊只會更新 anotherState,不會觸發上面的 filter 計算 */}<button onClick={() => setAnotherState(!anotherState)}>Toggle</button><ul>{filteredUsers.map(user => <li key={user.id}>{user.name}</li>)}</ul></div>);
}
保存狀態useCallback:

useCallback是什么?

  • useCallback 的核心作用是 “記憶”一個函數。在 JavaScript 中,函數是對象。在 React 組件每次重新渲染時,在函數組件內部定義的所有函數都會被重新創建。這意味著,即使函數體內的代碼完全一樣,前后兩次渲染生成的函數在內存中也是兩個不同的引用。
  • 當一個函數作為 prop 傳遞給一個被 React.memo 優化的子組件時,這個問題就變得很關鍵。因為父組件的每次重渲染都會創建一個新的函數實例,導致子組件接收到的 prop (那個函數) 每次都“不相等”,從而使得 React.memo 的優化失效,子組件依然會不必要地重新渲染。
  • useCallback 就是用來解決這個問題的。它會緩存你提供的函數實例,只有當其依賴項改變時,才會重新創建一個新的函數實例
import React, { useState, useCallback } from 'react';// 使用 React.memo 優化子組件,只有 props 變化時才重渲染
const MemoizedButton = React.memo(({ onClick, children }) => {console.log(`Button "${children}" is rendering...`);return <button onClick={onClick}>{children}</button>;
});function ParentComponent() {const [count, setCount] = useState(0);const [anotherState, setAnotherState] = useState(0);// 沒有優化的寫法:// 每次 ParentComponent 重渲染,都會創建一個新的 handleIncrement 函數。// const handleIncrement = () => setCount(count + 1);// 使用 useCallback 優化:// handleIncrement 函數被緩存了,它的引用只在依賴項變化時才更新。// 因為依賴項是空數組 [],所以它在組件的整個生命周期內都保持不變。const handleIncrement = useCallback(() => {setCount(prevCount => prevCount + 1); // 使用函數式更新,避免依賴 count}, []); // 這個函數依賴 anotherState,所以只有 anotherState 變化時才會重新創建const handleAnotherAction = useCallback(() => {// ... do something with anotherState}, [anotherState]);return (<div><p>Count: {count}</p><button onClick={() => setAnotherState(anotherState + 1)}>Update Another State (will not re-render Increment button)</button><MemoizedButton onClick={handleIncrement}>Increment Count</MemoizedButton><MemoizedButton onClick={handleAnotherAction}>Another Action</MemoizedButton></div>);
}

工具類:

服務端渲染: useId:

useId 是 React 18 中引入的一個新 Hook,它的主要目的是在客戶端和服務端生成穩定且唯一的 ID,以解決服務端渲染(Server-Side Rendering, SSR)和客戶端激活(Hydration)過程中的 ID 不匹配問題。
useId 出現之前,如果我們需要為組件生成一個唯一的 ID,生成一個在服務端和客戶端之間穩定、唯一且無沖突的 ID 字符串。

import React, { useId } from 'react';function FormField() {// 調用 useId 生成一個在 SSR 和 CSR 中都穩定的唯一 IDconst id = useId();console.log('Generated ID:', id); // 在服務端和客戶端會打印出相同的 ID,例如 ":r1:"return (<div>{/* 使用生成的 id 來關聯 label 和 input */}<label htmlFor={id}>Your Name:</label><input id={id} type="text" name="name" /></div>);
}// 如果一個組件需要多個 ID
function ComplexFormField() {const id = useId();return (<div><label htmlFor={`${id}-firstName`}>First Name</label><input id={`${id}-firstName`} type="text" /><label htmlFor={`${id}-lastName`}>Last Name</label><input id={`${id}-lastName`} type="text" /></div>);
}

執行副作用型:

異步執行副作用useEffect:

這是處理副作用最常用、也是你最應該首先考慮的 Hook。useEffect 允許你在組件渲染到屏幕之后,執行一些與渲染本身無關的操作,比如數據獲取、設置訂閱、手動操作 DOM 等。“異步執行” 這個描述非常關鍵。useEffect 的執行時機是在 React 完成 DOM 更新并將其繪制到屏幕上之后。這意味著 useEffect 內部的代碼不會阻塞瀏覽器的繪制過程,從而保證了用戶界面的流暢和響應性。

執行流程:

  • React 渲染組件。
  • 瀏覽器更新 DOM 并且**繪制(Paint)**界面。
  • 然后useEffect 內部的函數被執行。
import React, { useState, useEffect } from 'react';function UserProfile({ userId }) {const [user, setUser] = useState(null);useEffect(() => {// 這個函數會在組件渲染到屏幕上之后被調用console.log('Component has been painted to the screen.');async function fetchUserData() {console.log('Starting data fetch...');const response = await fetch(`https://api.example.com/users/${userId}`);const userData = await response.json();setUser(userData);console.log('Data fetch complete.');}fetchUserData();// 清理函數:在組件卸載或下一次 effect 執行前運行return () => {console.log('Cleaning up previous effect.');};}, [userId]); // 依賴數組,僅在 userId 變化時重新執行if (!user) {return <div>Loading...</div>;}return <h1>{user.name}</h1>;
}
同步執行副作用: useLayoutEffect:

useLayoutEffect 在 API 上與 useEffect 完全相同,但它們的執行時機截然不同。這微小的差異導致了其用途的巨大區別。 “同步執行”useLayoutEffect 的關鍵。它會在 React 計算完所有 DOM 變更之后,但在瀏覽器將這些變更繪制到屏幕上之前同步執行。

執行流程:

  • React 渲染組件,并計算出 DOM 的變更。
  • 在瀏覽器繪制前useLayoutEffect 內部的函數被同步執行。
  • useLayoutEffect 內部的代碼可能會再次觸發狀態更新,導致組件同步地重新渲染。
  • 然后,瀏覽器才將最終的 DOM 變更繪制到屏幕上。
    因為它是同步執行的,所以如果內部邏輯非常耗時,它會阻塞瀏覽器的繪制,導致頁面卡頓。因此,應該謹慎使用。

只有當你需要在瀏覽器繪制前,讀取 DOM 布局信息并同步地使用這些信息來改變 DOM 時,才應該使用 useLayoutEffect。這樣做是為了防止用戶看到“閃爍”(Flicker)現象——即組件先以一種狀態渲染,然后又立即變為另一種狀態。

import React, { useState, useLayoutEffect, useRef } from 'react';function AutoWidthInput() {const [width, setWidth] = useState(0);const divRef = useRef(null);// 使用 useLayoutEffect 來防止閃爍useLayoutEffect(() => {// 這個 effect 會在 DOM 更新后、瀏覽器繪制前執行if (divRef.current) {console.log('Reading layout before paint.');// 讀取 div 的寬度const measuredWidth = divRef.current.offsetWidth;// 同步更新 statesetWidth(measuredWidth);}}, []); // 空數組表示只在掛載后執行一次// 如果這里用的是 useEffect,你可能會短暫地看到 input 寬度為 0,然后才跳到正確寬度,造成閃爍。return (<div><div ref={divRef} style={{ width: '200px', marginBottom: '10px', background: 'lightblue' }}>Measure my width!</div><input type="text" style={{ width: `${width}px` }} placeholder="My width matches the div" /></div>);
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/94248.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/94248.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/94248.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【21】C# 窗體應用WinForm ——圖片框PictureBox屬性、方法、實例應用

文章目錄12. 圖片框PictureBox12.2 PictureBox插入、刪除圖片12.2.1 插入方式一&#xff1a;右鍵導入12.2.2 插入方式二&#xff1a;程序路徑讀入12.2.3 刪除圖片&#xff1a;右鍵清除12.3 實例&#xff1a;一鍵實現圖片交換12.4 圖片與窗口尺寸——SizeMode屬性——實例對比1 …

Vue-Router 4.0:新一代前端路由管理

&#x1f90d; 前端開發工程師、技術日更博主、已過CET6 &#x1f368; 阿珊和她的貓_CSDN博客專家、23年度博客之星前端領域TOP1 &#x1f560; 牛客高級專題作者、打造專欄《前端面試必備》 、《2024面試高頻手撕題》、《前端求職突破計劃》 &#x1f35a; 藍橋云課簽約作者、…

vuhub Corrosion2靶場攻略

靶場下載&#xff1a; 下載地址&#xff1a;https://download.vulnhub.com/corrosion/Corrosion2.ova 靶場使用&#xff1a; 我這里是使用Oracle VirtualBox虛擬機打開靶場&#xff0c;使用VMware打開攻擊機kali&#xff0c;要使這兩個機器能互相通信&#xff0c;需要將這兩…

定制開發開源AI智能名片S2B2C商城小程序的特點、應用與發展研究

摘要&#xff1a;本文聚焦定制開發開源AI智能名片S2B2C商城小程序&#xff0c;深入剖析其技術特點、功能優勢。通過分析在實體店與線上營銷、新零售閉環生態構建、智慧場景賦能以及微商品牌規范化運營等方面的應用&#xff0c;探討其發展趨勢。旨在為營銷技術專家中的營銷創客及…

ulimit參數使用詳細總結

目錄 1. 基本介紹 1.1 核心功能 1.2 作用范圍 1.3 限制類型 2. 基本語法 3. 常用選項? 3.1 常見options 3.2 查看當前限制 4. 核心概念 4.1 軟限制&#xff08;Soft Limit&#xff09; 4.2 硬限制&#xff08;Hard Limit&#xff09; 5. 修改限制 5.1 臨時修改 …

基于ASIC架構的AI芯片:人工智能時代的算力引擎

基于ASIC架構的AI芯片&#xff1a;人工智能時代的算力引擎在深度學習模型參數量呈指數級增長、訓練與推理需求爆炸式發展的今天&#xff0c;通用處理器&#xff08;CPU、GPU&#xff09;在能效比和計算密度上的局限日益凸顯。基于ASIC&#xff08;Application-Specific Integra…

Linux信號機制:從硬件中斷到用戶態處理

當你在終端按下 CtrlC 時&#xff0c;一個簡單的組合鍵觸發了操作系統最精妙的異步通信機制。這種跨越硬件與軟件的協作&#xff0c;正是Linux信號系統的精髓所在。本文將帶你深入探索信號處理的全過程&#xff0c;從CPU中斷到用戶態函數調用&#xff0c;揭示Linux最強大的進程…

C語言基礎:動態申請練習題

1. 動態申請一個具有10個float類型元素的內存空間&#xff0c;從一個已有的數組中拷貝數據&#xff0c;并找出第一次出現 12.35 的下標位置&#xff0c;并輸出。#include <stdio.h> #include <stdlib.h> #include <string.h>int main() {// 動態申請10個flo…

MATLAB 實現 SRCNN 圖像超分辨率重建

MATLAB 實現 SRCNN 圖像超分辨率重建 MATLAB代碼實現&#xff0c;用于基于三層卷積神經網絡的圖像超分辨率重建。代碼參考了多個來源&#xff0c;結合了SRCNN的典型實現步驟。 1. MATLAB代碼實現 % 超分辨率卷積神經網絡(SRCNN)的測試代碼 % 參考文獻&#xff1a;Chao Dong, Ch…

知識蒸餾 - 基于KL散度的知識蒸餾 HelloWorld 示例

知識蒸餾 - 基于KL散度的知識蒸餾 HelloWorld 示例 flyfish 知識蒸餾 - 蒸的什么 知識蒸餾 - 通過引入溫度參數T調整 Softmax 的輸出 知識蒸餾 - 對數函數的單調性 知識蒸餾 - 信息量的公式為什么是對數 知識蒸餾 - 根據真實事件的真實概率分布對其進行編碼 知識蒸餾 - …

從結構到交互:HTML5進階開發全解析——語義化標簽、Canvas繪圖與表單設計實戰

一、語義化標簽進階&#xff1a;重構頁面結構的「邏輯語言」 在 HTML5 的舞臺上&#xff0c;語義化標簽是熠熠生輝的主角&#xff0c;它們為網頁賦予了清晰的邏輯結構&#xff0c;使其更易被搜索引擎理解和被開發者維護。其中&#xff0c;<section>與<article>標簽…

標準七層網絡協議和TCP/IP四層協議的區別

分別是什么? OSI七層協議是國際標準組織制定的標準協議。其中七層分別是物理層,數據鏈路層,網絡層,傳輸層,會話層,表示層,應用層。 TCP/IP協議是美國軍方在后期網絡技術的發展中提出來的符合目前現狀的協議。其中四層分別是網絡接口層對應七層中的物理層和數據鏈路層,…

前端面試手撕題目全解析

以下是前端面試中常遭遇的“手撕”基礎題目匯總&#xff0c;涵蓋 HTML→JS→Vue→React&#xff0c;每題附經典實現&#xff0f;原理解析&#xff0c;可現場答題或后端總結。 HTML 基礎題 &#x1f4dd; 語義化卡片&#xff08;Semantic Card ARIA&#xff09; <article cl…

道格拉斯-普克算法 - 把一堆復雜的線條變得簡單,同時盡量保持原來的樣子

道格拉斯-普克算法 - 把一堆復雜的線條變得簡單&#xff0c;同時盡量保持原來的樣子 flyfish 道格拉斯-普克算法&#xff08;Douglas-Peucker Algorithm解決的問題其實很日常&#xff1a;把一堆復雜的線條&#xff08;比如地圖上的道路、河流&#xff0c;或者GPS記錄的軌跡&…

團購商城 app 系統架構分析

一、引言 團購商城 APP 作為一種融合了電子商務與團購模式的應用程序&#xff0c;近年來在市場上取得了顯著的發展。它為用戶提供了便捷的購物體驗&#xff0c;同時也為商家創造了更多的銷售機會。一個完善且高效的系統架構是保障團購商城 APP 穩定運行、提供優質服務的基礎。本…

【AI平臺】n8n入門7:本地n8n更新

?0、前言 目標&#xff1a;本地n8n部署后&#xff0c;有新版本&#xff0c;然后進行更新。官方文檔&#xff1a;Docker | n8n Docs特別說明&#xff1a; n8n鏡像更新后&#xff0c;容器重建&#xff0c;所以之前在n8n配置的東西&#xff0c;就莫有了&#xff0c;工作流提前導…

還在使用Milvus向量庫?2025-AI智能體選型架構防坑指南

前言說明&#xff1a;數據來源&#xff1a;主要基于 Milvus&#xff08;v2.3&#xff09;和 Qdrant&#xff08;v1.8&#xff09;的最新穩定版&#xff0c;參考官方文檔、GitHub Issues、CNCF報告、以及第三方評測&#xff08;如DB-Engines、TechEmpower&#xff09;。評估原則…

3-verilog的使用-1

verilog的使用-1 1.判斷上升沿 reg s_d0; reg s_d1; wire signal_up ; //判斷信號的上升沿 assign signal_up (~touch_key_d1) & touch_key_d0; always (posedge clk or negedge rst_n) beginif(rst_n 1b0) begins_d0< 1b0;s_d1< 1b0;endelse begins_d0&…

ESXI虛擬交換機 + H3C S5120交換機 + GR5200路由器組網筆記

文章目錄一、組網拓撲與核心邏輯1. 拓撲結構2. 核心邏輯二、詳細規劃方案1. VLAN 與 IP 地址規劃2. 設備連接規劃三、配置步驟1. H3C S5120 交換機配置&#xff08;VLAN 與端口&#xff09;2. H3C GR5200 路由器配置&#xff08;路由、網關、NAT&#xff09;3. ESXi 虛擬交換機…

python的駕校培訓預約管理系統

前端開發框架:vue.js 數據庫 mysql 版本不限 后端語言框架支持&#xff1a; 1 java(SSM/springboot)-idea/eclipse 2.NodejsVue.js -vscode 3.python(flask/django)–pycharm/vscode 4.php(thinkphp/laravel)-hbuilderx 數據庫工具&#xff1a;Navicat/SQLyog等都可以 該系統通…