偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

深度探討 useEffect 使用規(guī)范

開(kāi)發(fā) 后端
在 Vue 和 Mobx 里都有計(jì)算屬性這樣的概念。因此有的人就想,在 React Hook 中,是否可以借助 useEffect 來(lái)達(dá)到計(jì)算屬性的目的。

我們?cè)诔俗罔F或者公交車的時(shí)候,在地鐵門或者公交車門的旁邊往往會(huì)有「禁止依靠」這樣的標(biāo)識(shí)。目的是為了警告乘客不要依靠車門,否則開(kāi)門的時(shí)候容易出現(xiàn)不可預(yù)測(cè)的危險(xiǎn)。

但是如果我們仔細(xì)去分析這個(gè)危險(xiǎn)的話,就會(huì)知道,他的真實(shí)情況是,在車輛運(yùn)行過(guò)程中,車門緊閉,你依靠在車門上也并不會(huì)出現(xiàn)危險(xiǎn),我們?cè)谏习喔叻迤跀D地鐵的時(shí)候,大量的人也不得不緊靠車門,甚至有的人被擠扁壓在車門上。

在制定團(tuán)隊(duì)項(xiàng)目規(guī)范時(shí)也會(huì)這樣,例如,我在帶領(lǐng)團(tuán)隊(duì)時(shí),一定會(huì)制定一條規(guī)范,要求每次代碼提交之前,個(gè)人必須檢查你的代碼里是否存在意外的修改,可能有的人在提交之前手抖往代碼里輸入了一個(gè)空格或者逗號(hào),從而導(dǎo)致重大事故。這是一個(gè)低概率發(fā)生的事情,但是我仍然會(huì)要求每次提交都要檢查。

那么,是不是也就意味著,如果不遵守我這個(gè)規(guī)范,就一定會(huì)發(fā)現(xiàn)不可預(yù)測(cè)的重大事故呢?其實(shí)并非如此,我們制定規(guī)范的目的是為了讓程序變得可控,比如團(tuán)隊(duì)里面有10個(gè)人的習(xí)慣都比較好,從來(lái)不會(huì)出現(xiàn)意外內(nèi)容提交的情況,但是只要有一個(gè)人出現(xiàn)兩次因?yàn)槭侄栋岩馔獾男薷奶峤坏搅舜a倉(cāng)庫(kù),那么這條規(guī)范就會(huì)出現(xiàn)。

既然這條規(guī)范的出現(xiàn)是為了避免意外的發(fā)生,于是有一個(gè)項(xiàng)目成員就對(duì)我的規(guī)范提出了質(zhì)疑,他認(rèn)為可以在配置上增加 pre-commit 的代碼規(guī)則檢測(cè),如果有意外的發(fā)生,那么代碼規(guī)則檢測(cè)不會(huì)通過(guò),我們就不用每次在提交之前花費(fèi)心力去檢查每一條 diff 里的修改了。

雖然我最終沒(méi)有同意他的提議,但這是一個(gè)非常好的思路

所以作為一個(gè)優(yōu)秀的開(kāi)發(fā)者,我們到底是應(yīng)該只要遵循規(guī)范就是完事了,還是應(yīng)該去看懂規(guī)范出現(xiàn)的背后邏輯,從而靈活的運(yùn)用他,或者探尋更好的解決方案呢?

我的答案是后者。

如果在這個(gè)觀念的基礎(chǔ)之上我們能達(dá)成共識(shí),我們?cè)賮?lái)一起結(jié)合 React 官方文檔,對(duì) useEffect 的使用規(guī)范進(jìn)行深入探討。

在這之前,我們要首先明確一下 useEffect 的語(yǔ)法規(guī)則,useEffect 的依賴項(xiàng)必須是 state 與 props,否則依賴項(xiàng)發(fā)生了變化,effect 也不會(huì)執(zhí)行。所以有的人說(shuō):我不愿意把 state 放到依賴項(xiàng)里,甚至反感這樣的行為,我認(rèn)為是沒(méi)有任何理論依據(jù)的。

一、計(jì)算屬性

在 vue 和 mobx 里都有計(jì)算屬性這樣的概念。因此有的人就想,在 react hook 中,是否可以借助 useEffect 來(lái)達(dá)到計(jì)算屬性的目的。

官方文檔明確的建議是,不需要這樣做

// 案例來(lái)自官方文檔
// https://zh-hans.react.dev/learn/you-might-not-need-an-effect
function Form() {
  const [firstName, setFirstName] = useState('Taylor');
  const [lastName, setLastName] = useState('Swift');

  // ?? 避免:多余的 state 和不必要的 Effect
  const [fullName, setFullName] = useState('');
  useEffect(() => {
    setFullName(firstName + ' ' + lastName);
  }, [firstName, lastName]);
  // ...
}

實(shí)際上 react 的 re-render 機(jī)制表示 react hook 本身已經(jīng)具備了計(jì)算屬性的特性。當(dāng) state 發(fā)生變化,函數(shù)會(huì)重新執(zhí)行,內(nèi)部的代碼也會(huì)重新執(zhí)行,因此案例中的 fullName 就有一次重新計(jì)算結(jié)果的機(jī)會(huì)

function Form() {
  const [firstName, setFirstName] = useState('Taylor');
  const [lastName, setLastName] = useState('Swift');
  // ? 非常好:在渲染期間進(jìn)行計(jì)算
  const fullName = firstName + ' ' + lastName;
  // ...
}

因此我們不必借助 useEffect 來(lái)實(shí)現(xiàn)計(jì)算屬性,這是非常好的建議。

二、緩存計(jì)算結(jié)果

但是如果情況發(fā)生一些變化呢?fullName 這個(gè)案例的計(jì)算過(guò)程非常簡(jiǎn)單,如果這個(gè)計(jì)算過(guò)程非常復(fù)雜需要花費(fèi)大量的時(shí)間呢?此時(shí)我們就不得不考慮要減少這個(gè)計(jì)算過(guò)程,只在他需要重新計(jì)算的時(shí)候計(jì)算一次

這個(gè)時(shí)候,這個(gè)案例的解決方案就不再適用了,他只適合簡(jiǎn)單的運(yùn)算過(guò)程。復(fù)雜運(yùn)算過(guò)程我們還需要借助別的手段來(lái)緩存計(jì)算結(jié)果。那么使用 useEffect 是否合適?

不合適。官方文檔中,提供了一個(gè)更適合的 hook:useMemo 來(lái)緩存運(yùn)算結(jié)果。

但是為什么呢?

因?yàn)閳?zhí)行時(shí)機(jī)的問(wèn)題。事實(shí)上,useEffect 和 useMemo 都有記憶能力,他們的底層實(shí)現(xiàn)有部分相似之處,但是有一個(gè)不同之處導(dǎo)致了他們的差別非常大,那就是傳入的第一個(gè)參數(shù)的執(zhí)行時(shí)機(jī)。useMemo 在發(fā)現(xiàn)依賴項(xiàng)有改變之后,會(huì)立即重新運(yùn)算緩存的函數(shù)并返回運(yùn)算結(jié)果。但是 useEffect 則需要等待組件渲染完整之后才會(huì)開(kāi)始執(zhí)行緩存的函數(shù)。類似于

setTimeout(effect, 0)

也就意味著,當(dāng)前一輪執(zhí)行的 JSX 中無(wú)法得到 useEffect 的運(yùn)算結(jié)果。除非我們將運(yùn)算結(jié)果存儲(chǔ)在一個(gè) state 中,讓 state 發(fā)生改變而得到一輪新的 render。

因此在這種場(chǎng)景之下,useMemo 會(huì)比 useEffect 更快更合適。

官方文檔給我們提供了一個(gè)案例。

現(xiàn)在有一個(gè)復(fù)雜列表 todos,然后還有一個(gè)過(guò)濾條件 filter,todos 和 filter 運(yùn)算之后可以得到一個(gè)列表 visibleTodos。

const visibleTodos = getFilteredTodos(todos, filter);
function TodoList({ todos, filter }) {
  const [newTodo, setNewTodo] = useState('');

  // ?? 避免:多余的 state 和不必要的 Effect
  // 假設(shè) JSX中使用了 visibleTodos
  const [visibleTodos, setVisibleTodos] = useState([]);
  useEffect(() => {
    setVisibleTodos(getFilteredTodos(todos, filter));
  }, [todos, filter]);

  // ...
}

正是由于使用了 useEffect,因?yàn)閳?zhí)行時(shí)機(jī)的問(wèn)題,如果不將運(yùn)算結(jié)果存儲(chǔ)在 state 中,當(dāng)前一輪的 render,在 JSX 中無(wú)法得到新的運(yùn)算結(jié)果,因此只有通過(guò) state 的重新出發(fā)一次 render 的機(jī)會(huì)讓渲染結(jié)果保持最新。

所以不推薦使用 useEffect,直接去掉就行了

function TodoList({ todos, filter }) {
  const [newTodo, setNewTodo] = useState('');
  // ? 如果 getFilteredTodos() 的耗時(shí)不長(zhǎng),這樣寫就可以了。
  const visibleTodos = getFilteredTodos(todos, filter);
  // ...
}

但是由于此案例中設(shè)定的是 getFilteredTodos 是一個(gè)耗時(shí)操作,因此我們需要使用 useMemo 來(lái)緩存他的運(yùn)算結(jié)果。這樣就可以做到當(dāng)其他 state 發(fā)生變化時(shí),getFilteredTodos 不會(huì)重新執(zhí)行。

import { useMemo, useState } from 'react';

function TodoList({ todos, filter }) {
  const [newTodo, setNewTodo] = useState('');
  const visibleTodos = useMemo(() => {
    // ? 除非 todos 或 filter 發(fā)生變化,否則不會(huì)重新執(zhí)行
    return getFilteredTodos(todos, filter);
  }, [todos, filter]);
  // ...
}

這個(gè)案例充分說(shuō)明了 useMemo 的作用。

但是案例有一些不太合理的地方。例如,todos 和 fitler 都是外部傳入的 props,也就是說(shuō),下面這一行代碼更合理的方案是在組件外部計(jì)算好,因?yàn)樗\(yùn)算所需的條件都是外部條件。

const visibleTodos = getFilteredTodos(todos, filter);

這樣我們就完全不需要考慮因?yàn)?re-render 而處理他的冗余運(yùn)算成本問(wèn)題了。因?yàn)樗倪\(yùn)算次數(shù)將會(huì)嚴(yán)格和 todos、filter 的變化保持一致。

三、與事件相關(guān)的爭(zhēng)議

現(xiàn)在我們來(lái)思考一個(gè)類似的交互方案,依然是一個(gè)任務(wù)列表

給他們?cè)O(shè)定一個(gè)過(guò)濾條件,類別,例如有兩個(gè)類別是工作類與旅游類,當(dāng)類別發(fā)生變化的時(shí)候,部分任務(wù)會(huì)隱藏

此時(shí)你就會(huì)發(fā)現(xiàn)一個(gè)問(wèn)題,如果類別也需要在 UI 中進(jìn)行顯示,那么我們就不得不把類別這個(gè)過(guò)濾條件存放在 state 中去觸發(fā) UI 的變化,與此同時(shí),類別的變化還會(huì)導(dǎo)致 todos 也發(fā)生變化

這個(gè)時(shí)候就存在兩種比較有爭(zhēng)議的寫法

第一種寫法完全更符合語(yǔ)義和解耦的思考。從語(yǔ)義上來(lái)說(shuō),當(dāng)我們點(diǎn)擊了單選按鈕切換了類別,此時(shí)只需要修改 fitler 即可,因?yàn)槲覀冎蛔隽诉@一個(gè)操作。但是 filter 的修改,還會(huì)造成別的改動(dòng):列表也會(huì)發(fā)生變化,這是一種額外的副作用。因此我們使用 useEffect 來(lái)處理這部分副作用邏輯。

從解耦的角度來(lái)說(shuō),當(dāng)點(diǎn)擊切換按鈕時(shí),我們不需要關(guān)注額外的邏輯,這對(duì)于開(kāi)發(fā)而言是一種理解上的簡(jiǎn)化,因?yàn)槲覀冊(cè)邳c(diǎn)擊時(shí)只需要關(guān)注按鈕本身,而不需要關(guān)注按鈕切換之后的后續(xù)變化。這種解耦思路更有利于后續(xù)的封裝

副作用:我的修改,導(dǎo)致了除我之外的 UI 發(fā)生變化

function TodoList({ todos }) {
  const [newTodos, setNewTodos] = state(todos)
  const [fitler, setFilter] = state(1)

  useEffect(() => { }, [
    setNewTodos(getFilteredTodos(todos, filter));
 ], [filter])

  function onFilterChange(value) {
    setFilter(value)
  }
}

但是這種更符合語(yǔ)義和解耦的方案,違背了剛才的規(guī)范。因?yàn)槲覀兪褂?useEffect 去監(jiān)聽(tīng)一個(gè) state,修改另外一個(gè) state。不過(guò)剛才的規(guī)范的目的之一,是為了避免出現(xiàn)冗余的 state,本案例里面并沒(méi)有冗余的 state,filter 也是必須存在的。那么看上去前提條件跟規(guī)范有一些出入

于是,React 官方文檔還存在另外一條規(guī)范

react 官方文檔把 useEffect 稱為一種逃離方案「逃生艙」,我們應(yīng)該在最后使用它。因此在這個(gè)情況下,官方文檔建議把邏輯放到事件中處理,而不是 useEffect。

在這個(gè)案例中,下面的寫法是官方文檔更推薦的寫法

function TodoList({todos}) {
  const [newTodos, setNewTodos] = state(todos)
  const [fitler, setFilter] = state(1)

  function onFilterChange(value) {
    setFilter(value)
    setNewTodos(getFilteredTodos(todos, value))
  }
  // ...
}

使用 useEffect 雖然更符合語(yǔ)義和解耦,但是他會(huì)額外執(zhí)行一次 render,因此在執(zhí)行速度上,這種寫法是更快的。

useEffect 有更復(fù)雜的執(zhí)行邏輯,如果你對(duì)其掌握得不夠準(zhǔn)確時(shí),他很容易導(dǎo)致你的程序出現(xiàn)一些你無(wú)法理解的迷惑現(xiàn)象,因此在這兩個(gè)基礎(chǔ)之上,react 官方文檔的意思就是,useEffect 能不用就不用。

但是如果我們已經(jīng)對(duì) useEffect 的運(yùn)行機(jī)制非常清楚,并且他使用他付出的代價(jià)只是一次 re-render,我會(huì)更傾向于選擇前者:更符合語(yǔ)義、解耦好更利于封裝,而不是嚴(yán)格遵守規(guī)范。

事實(shí)上,只要你不亂來(lái),一次 re-render 的成本很低,除非是在一些特殊場(chǎng)景,例如渲染大量的 input 或者高頻渲染

如果在性能上還有爭(zhēng)議的話,那么接下來(lái)我們把本次案例進(jìn)行一個(gè)修改,新修改的交互將會(huì)更容易出現(xiàn)在我們的實(shí)踐中。

當(dāng)過(guò)濾條件發(fā)生變化,新的列表并不是從本地?cái)?shù)據(jù)中運(yùn)算得來(lái),而是接口從服務(wù)端獲取。

那么兩種寫法的代碼就會(huì)變成

// 使用 useEffect
function TodoList({ todos }) {
  const [newTodos, setNewTodos] = state(todos)
  const [fitler, setFilter] = state(1)

  useEffect(() => { }, [
    api(filter).then(res => {
      setNewTodos(res.data)
    })
 ], [filter])

  function onFilterChange(value) {
    setFilter(value)
  }
  // ...
}
// 不使用 useEffect
function TodoList({ todos }) {
  const [newTodos, setNewTodos] = state(todos)
  const [fitler, setFilter] = state(1)

  function onFilterChange(value) {
    setFilter(value)
    api(filter).then(res => {
      setNewTodos(res.data)
    })
  }
  // ...
}

此時(shí)就會(huì)發(fā)現(xiàn),使用 useEffect 的性能劣勢(shì)消失不見(jiàn)了。因?yàn)榧词刮覀冊(cè)谑录姓?qǐng)求了接口,但是由于異步事件的存在,導(dǎo)致 setFilter 與 setNewTodos 無(wú)法合并優(yōu)化,他們只能在不同的時(shí)間里觸發(fā) re-render。

而第一種寫法由于解耦做得比較好,因此他可以很容易在自定義 hook 的語(yǔ)法規(guī)則之下,簡(jiǎn)化組件的邏輯

function TodoList({ todos }) {
  const {newTodos, filter, setFilter} 
    = useTodoList(api)

  function onFilterChange(value) {
    setFilter(value)
  }
  // ...
}

這樣我們就可以把 useEffect 和 異步邏輯通過(guò)封裝的方式藏起來(lái)。這種情況之下的選擇上,我更傾向于選擇更好的語(yǔ)義和更好的解耦。他在性能上的犧牲非常非常小。

四、useEffectEvent

在官方文檔中

https://zh-hans.react.dev/learn/separating-events-from-effects。

介紹了一個(gè)實(shí)驗(yàn)性 api,useEffectEvent,用于從 Effect 中提取非響應(yīng)式邏輯,他能夠繞開(kāi)閉包的困擾,讀取到最新的 state 和 props

import { useEffect, useEffectEvent } from 'react';

function ChatRoom({ roomId, theme }) {
  const onConnected = useEffectEvent(() => {
    showNotification('Connected!', theme);
  });
  // ...

先介紹一下官方案例的交互:

首先我們要完成一個(gè)聊天室的切換功能。聊天室切換時(shí),我們需要斷開(kāi)之前的連接,并接上新的連接。

聊天室在切換后連接成功時(shí),需要有一個(gè)提示,表示我進(jìn)入到了新的聊天室,并已經(jīng)連接成功了。

與此同時(shí),該案例設(shè)計(jì)了一個(gè)交互點(diǎn),新增了一個(gè)配置,去修改提示組件的風(fēng)格,讓他可以切換到 dark 主題

當(dāng)我選中 Use dark theme 時(shí),那個(gè)提示組件也會(huì)彈出來(lái)露露臉。

事實(shí)上,實(shí)踐中不應(yīng)該出現(xiàn)這種交互,這里之所以出現(xiàn)是因?yàn)榘阉?dāng)成一個(gè)問(wèn)題來(lái)解決的

在代碼的設(shè)計(jì)中,isDark 被設(shè)計(jì)成為了一個(gè)響應(yīng)數(shù)據(jù)。

const [isDark, setIsDark] = useState(false);

然后我們封裝了一個(gè) CharRoom,使用時(shí)將 roomId 與 theme 作為 props 傳入

<ChatRoom
  roomId={roomId}
  theme={isDark ? 'dark' : 'light'}
/>

在封裝 ChatRoom 時(shí),由于 showNotification 的執(zhí)行需要 theme 作為參數(shù),于是,theme 就不得不作為 useEffect 的依賴項(xiàng)傳入,否則 showNotification 無(wú)法獲取最新的 theme 值

這是因?yàn)殚]包的存在

// theme = isDark ? 'dark' : 'light'
const serverUrl = 'https://localhost:1234';

function ChatRoom({ roomId, theme }) {
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.on('connected', () => {
      showNotification('Connected!', theme);
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId, theme]);

  return <h1>Welcome to the {roomId} room!</h1>
}

但是如果把 theme 作為依賴項(xiàng)之后,問(wèn)題就產(chǎn)生了,由 roomId 切換導(dǎo)致的聊天室的斷開(kāi)和重連邏輯就變得混亂了,因?yàn)楫?dāng)你修改主題時(shí),這段邏輯也會(huì)執(zhí)行。這明顯是不合理的。

因此,react 團(tuán)隊(duì)正在想辦法設(shè)計(jì)一個(gè) api,將 useEffect 的響應(yīng)式邏輯與非響應(yīng)式邏輯區(qū)分開(kāi)。

解決代碼如下:

function ChatRoom({ roomId, theme }) {
  const onConnected = useEffectEvent(() => {
    showNotification('Connected!', theme);
  });

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.on('connected', () => {
      onConnected();
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]); // ? 聲明所有依賴項(xiàng)
  // ...

事實(shí)上,在現(xiàn)有的方案之下,我們也有適合的解決方案。

首先我們要考慮一個(gè)交互上的特性,主題的更改,對(duì)于提示組件的影響并非是實(shí)時(shí)的。也就是說(shuō),當(dāng)我在修改主題時(shí),我們并不需要一個(gè)提示組件出來(lái)露露臉。

因此,我們此時(shí)有機(jī)會(huì)考慮設(shè)計(jì)一個(gè)非響應(yīng)式的數(shù)據(jù)來(lái)存儲(chǔ)主題的更改。另一個(gè)角度,是否選中的 UI 樣式的修改,是 input 組件內(nèi)部自己的交互邏輯,因此此時(shí)也不需要外部提供一個(gè)響應(yīng)式數(shù)據(jù)來(lái)控制 input 是否被選中。

const isDark = useRef(false);

完整的邏輯代碼如下,該代碼可在對(duì)應(yīng)的官方案例中運(yùn)行

import { useState, useEffect, useRef } from 'react';
import { createConnection, sendMessage } from './chat.js';
import { showNotification } from './notifications.js';

const serverUrl = 'https://localhost:1234';

function ChatRoom({ roomId, theme }) {
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.on('connected', () => {
      showNotification('Connected!', theme.current ? 'dark' : 'light');
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId, theme]);

  return <h1>Welcome to the {roomId} room!</h1>
}

export default function App() {
  const [roomId, setRoomId] = useState('general');
  const isDark = useRef(false);
  return (
    <>
      <label>
        Choose the chat room:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="general">general</option>
          <option value="travel">travel</option>
          <option value="music">music</option>
        </select>
      </label>
      <label>
        <input
          type="checkbox"
          onChange={e => (isDark.current = e.target.checked)}
        />
        Use dark theme
      </label>
      <hr />
      <ChatRoom
        roomId={roomId}
        theme={isDark}
      />
    </>
  );
}

這樣,在我們前面提到的數(shù)據(jù)驅(qū)動(dòng) UI 的哲學(xué)邏輯驅(qū)動(dòng)之下,精確分析數(shù)據(jù)與 UI 的關(guān)系,我們也完美的解決了這個(gè)問(wèn)題。

五、總結(jié)

react 官方文檔在建議與規(guī)范的角度上會(huì)盡可能讓大家避免使用 useEffect,我猜測(cè)大概是由于許多初學(xué)者在 useEffect 對(duì)于依賴項(xiàng)的使用會(huì)產(chǎn)生不少疑問(wèn)而導(dǎo)致的。但并不代表在 useEffect 的思考上,就沒(méi)有更合理的使用方式,他也不是一個(gè)反模式。

包括我們制定團(tuán)隊(duì)規(guī)范也是一樣,團(tuán)隊(duì)規(guī)范保障的是整個(gè)項(xiàng)目的底線,并不一定能代表項(xiàng)目上限,也不一定能代表最佳實(shí)踐。

因此,我更倡導(dǎo)大家在學(xué)習(xí)規(guī)范時(shí),去充分理解規(guī)范出現(xiàn)的背后邏輯,靈活的運(yùn)用他,并積極探尋更好的解決方案。

責(zé)任編輯:姜華 來(lái)源: 這波能反殺
相關(guān)推薦

2024-03-07 12:40:28

Python*args開(kāi)發(fā)

2009-11-24 15:44:26

Visual Stud

2025-05-12 01:33:00

異步函數(shù)Promise

2010-03-19 09:12:05

JRuby

2010-03-17 14:33:44

云計(jì)算

2010-01-27 16:10:32

C++靜態(tài)構(gòu)造函數(shù)

2010-12-22 11:19:09

Java字節(jié)代碼

2010-05-24 17:13:34

Linux SNMP

2016-08-12 22:47:17

互聯(lián)網(wǎng)計(jì)算廣告

2012-12-26 10:46:07

2010-01-08 15:06:35

JSON功能

2023-11-30 07:45:11

useEffectReact

2009-11-23 10:31:25

PHP使用JSON

2015-08-06 10:28:24

git規(guī)范流程

2018-03-14 08:10:44

深度學(xué)習(xí)

2016-10-14 13:46:26

2009-12-15 18:30:56

Ruby使用DBI包裝

2009-12-02 15:02:09

PHP simplex

2018-04-19 08:58:17

容器塊存儲(chǔ)

2010-03-04 13:37:20

Python yiel
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)