整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          JavaScript基礎(chǔ):fetch()的用法,返回Promise,返回值json()化

          etch API 提供了一個 JavaScript接口,用于訪問和操縱HTTP管道的部分,例如請求和響應(yīng)。它還提供了一個全局 fetch()方法,該方法提供了一種簡單,合理的方式來跨網(wǎng)絡(luò)異步獲取資源。

          請注意,fetch規(guī)范與jQuery.ajax()主要有兩種方式的不同,牢記:

          當(dāng)接收到一個代表錯誤的 HTTP 狀態(tài)碼時,從 fetch()返回的 Promise 不會被標(biāo)記為 reject, 即使該 HTTP 響應(yīng)的狀態(tài)碼是 404 或 500。相反,它會將 Promise 狀態(tài)標(biāo)記為 resolve (但是會將 resolve 的返回值的 ok 屬性設(shè)置為 false ),僅當(dāng)網(wǎng)絡(luò)故障時或請求被阻止時,才會標(biāo)記為 reject。

          默認(rèn)情況下,fetch 不會從服務(wù)端發(fā)送或接收任何 cookies, 如果站點依賴于用戶 session,則會導(dǎo)致未經(jīng)認(rèn)證的請求(要發(fā)送 cookies,必須設(shè)置 credentials 選項)。自從2017年8月25日后,默認(rèn)的credentials政策變更為same-originFirefox也在61.0b13中改變默認(rèn)值

          一個基本的 fetch請求

          fetch('http://example.com/movies.json')
           .then(function(response) {
           return response.json();
           })
           .then(function(myJson) {
           console.log(myJson);
           });
          

          這里我們通過網(wǎng)絡(luò)獲取一個JSON文件并將其打印到控制臺。最簡單的用法是只提供一個參數(shù)用來指明想fetch()到的資源路徑,然后返回一個包含響應(yīng)結(jié)果的promise(一個 Response 對象)。

          當(dāng)然它只是一個 HTTP 響應(yīng),而不是真的JSON。為了獲取JSON的內(nèi)容,我們需要使用 json()方法(在Bodymixin 中定義,被 Request 和 Response 對象實現(xiàn))。

          支持的請求參數(shù)

          fetch() 接受第二個可選參數(shù),一個可以控制不同配置的 init 對象:

          // Example POST method implementation:
          postData('http://example.com/answer', {answer: 42})
           .then(data => console.log(data)) // JSON from `response.json()` call
           .catch(error => console.error(error))
          function postData(url, data) {
           // Default options are marked with *
           return fetch(url, {
           body: JSON.stringify(data), // must match 'Content-Type' header
           cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
           credentials: 'same-origin', // include, same-origin, *omit
           headers: {
           'user-agent': 'Mozilla/4.0 MDN Example',
           'content-type': 'application/json'
           },
           method: 'POST', // *GET, POST, PUT, DELETE, etc.
           mode: 'cors', // no-cors, cors, *same-origin
           redirect: 'follow', // manual, *follow, error
           referrer: 'no-referrer', // *client, no-referrer
           })
           .then(response => response.json()) // parses response to JSON
          }
          

          更多內(nèi)容:https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch

          新手課程中,我們建立了一個非常簡單的dApp,使用HTML、CSS和一些JavaScript。然而,在現(xiàn)實世界中,這些類型的"vanilla"網(wǎng)站實現(xiàn)已經(jīng)是過去式了。

          今天,我們使用web框架來簡化web開發(fā)過程。但它更簡單嗎?取決于你從誰的角度看問題。如果你剛剛開始,以前從未做過這件事,那么你可能需要一段時間才能理解所有必要的概念。但是,從長遠(yuǎn)來看,你會感謝自己,并為自己花時間學(xué)習(xí)而感到高興。

          現(xiàn)在最大和最常用的網(wǎng)絡(luò)框架是:

          • React[1]
          • Angular[2]
          • Vue[3]

          雖然它們各有優(yōu)缺點,但到目前為止,React已經(jīng)在Web開發(fā)領(lǐng)域掀起了風(fēng)暴。在Web3領(lǐng)域也是如此,React是構(gòu)建dApps最常用的網(wǎng)絡(luò)框架。在整個新手課程中,以及所有以后的課程中,我們將使用大量的React,所以可以把這個級別看作是React的速成課程,它將教會你足夠的東西來開始。這并不是要取代在專注于Web2教學(xué)的平臺上學(xué)習(xí)React,而是作為一個指南,讓你了解有多少東西是必須開始學(xué)習(xí)的,這樣你就不會陷入教程的地獄。

          實際上,我們將使用Next.js--它是React本身的一個擴展--但后面會有更多的內(nèi)容。讓我們先學(xué)習(xí)一些React。

          什么是React?

          React是一個網(wǎng)絡(luò)框架,它使得構(gòu)建和響應(yīng)你的網(wǎng)絡(luò)應(yīng)用的 "視圖 "變得容易。視圖 "是在屏幕上顯示的內(nèi)容,它如何變化,如何更新,等等。React本質(zhì)上只是給你一個模板語言,你可以創(chuàng)建返回HTML的Javascript函數(shù)。

          正常的Javascript函數(shù)會返回Javascript相關(guān)的東西--字符串、數(shù)字、布爾值、對象等等。React基本上結(jié)合了Javascript和HTML,產(chǎn)生了一種他們稱之為JSX的語言。在JSX中,類似Javascript的函數(shù)返回HTML,而不是常規(guī)的Javascript事物。基本上就是這樣了。

          返回HTML的Javascript函數(shù)的組合被稱為組件。組件是用JSX編寫的。雖然一開始看起來很笨拙,但一旦你習(xí)慣了,它們實際上是很容易操作的。

          下面是一個簡單組件的例子。

          function ShoppingList() {
              return (
                <div className="shopping-list">
                  <h1>Shopping List</h1>
                  <ul>
                    <li>Apples</li>
                    <li>Bananas</li>
                    <li>Grapes</li>
                  </ul>
                </div>
              );
          }

          運行代碼[4]

          很好,但這與HTML沒有什么區(qū)別。但是,如果你想根據(jù)一個數(shù)組來呈現(xiàn)一個項目的列表呢?

          function ShoppingList() {
              const items = ["Apples", "Bananas", "Grapes"]
              
              return (
                <div className="shopping-list">
                  <h1>Shopping List</h1>
                  <ul>
                      {items.map((item, index) => <li key={index}>{item}</li>)}
                  </ul>
                </div>
              );
          }

          運行代碼[5]

          哇,看看這個! 我們剛剛在HTML中使用了Javascript。在JSX中,你可以通過用大括號{}包裹JS代碼在HTML中編寫Javascript。實際上,如果你多想一下,你就會明白發(fā)生了什么。.map()是一個Javascript函數(shù),它在一個數(shù)組上循環(huán),并為每個項目返回一些東西。在這種情況下,它在 items 數(shù)組上循環(huán),并返回一個 li 元素,其中有 Javascript 變量 item 的值,也就是 HTML。明白了嗎?

          在我們的組件中,我們基本上嵌入了另一個組件。map函數(shù)是一個返回HTML的JS函數(shù)。它是一個組件。盡管它沒有被明確定義為頂級函數(shù),它仍然是一個組件。

          將組件嵌入到其他組件中是 React 的強大功能。這稱為組合。我們將多個返回 HTML 的 Javascript 函數(shù)組合在一起,并從中構(gòu)建一個組合的 HTML 文檔,該文檔將顯示在 Web 應(yīng)用程序上。

          組件之間的數(shù)據(jù)傳遞

          如果組件只是靜態(tài)的,它們就不是很有用。當(dāng)然,遍歷數(shù)組和其他東西都很好,但當(dāng)今大多數(shù) Web 應(yīng)用程序都不是靜態(tài)文檔。今天的大多數(shù) Web 應(yīng)用程序都會從某種服務(wù)器、數(shù)據(jù)庫或區(qū)塊鏈中動態(tài)獲取數(shù)據(jù)。這意味著經(jīng)常需要相同的組件來顯示不同的數(shù)據(jù)。

          擁有組件的主要用例是能夠編寫可重用的代碼,并且可以在其中包含不同的信息,而無需再次重寫整個代碼。

          讓我們看一個例子。這兩個代碼哪個更易讀?

          <div class="cards">
              <div class="card">
                <img src="img_avatar.png" alt="Avatar">
                <div class="container">
                  <h4><b>Alice</b></h4>
                  <p>Frontend Developer</p>
                </div>
              </div> 
          
              <div class="card">
                <img src="img_avatar.png" alt="Avatar">
                <div class="container">
                  <h4><b>Bob</b></h4>
                  <p>Backend Developer</p>
                </div>
              </div> 
          
              <div class="card">
                <img src="img_avatar.png" alt="Avatar">
                <div class="container">
                  <h4><b>Charlie</b></h4>
                  <p>Full Stack Developer</p>
                </div>
              </div>
          </div>
          function Cards() {
              return (
                  <div className="cards">
                      <!--Data is passed to children through HTML attributes-->
                      <Card name="Alice" job="Frontend Developer" />
                      <Card name="Bob" job="Backend Developer" />
                      <Card name="Charlie" job="Full Stack Developer" />
                  </div>
              )
          }
          
          // Card receives an object as an argument
          // We can destructure the object to get specific variables
          // from inside the object - name and job
          function Card({name, job}) {
              return (
                  <div className="card">
                      <img src="img_avatar.png" />
                      <div className="container">
                          <h4><b>{name}</b></h4>
                          <p>{job}</p>
                      </div>
                  </div>
              )
          }

          運行代碼[6]

          純 HTML 示例重復(fù)使用相同的代碼 3 次,盡管真正改變的只是人名和他們的職位。

          在 JSX 中,我們可以將每個組件抽象Card為一個組件,該組件從其父組件(在本例中為 )獲取某些數(shù)據(jù)Cards。父組件通過類似 HTML 的屬性name="Alice"(然后,Card組件可以根據(jù)從父級接收到的內(nèi)容返回帶有可變數(shù)據(jù)的 HTML。

          這段代碼更容易重用和擴展。想要稍微改變所有卡片的外觀嗎?只需修改一個組件!并非所有復(fù)制粘貼的 HTML。

          交互式組件

          好的,所以我們現(xiàn)在可以在組件之間傳遞數(shù)據(jù)。這一切都很好,但我們還沒有添加交互性。諸如在單擊按鈕或在輸入框中鍵入文本時能夠運行一些代碼等。

          值得慶幸的是,在 Javascript 中,函數(shù)可以在其中包含函數(shù)。例如,

          function someFunc() {
              
              function otherFunc() {
                  console.log("Hello!")
              }
              
              otherFunc();
          }
          
          someFunc(); // will print "Hello!"
          
          otherFunc(); // will throw an error! undefined here

          otherFunc只能在someFunc自身內(nèi)部使用。這是常規(guī) Javascript 中很少使用的功能,但在使用 React 時使用非常頻繁。讓我們通過一個例子來看看為什么。

          function Button() {
              
              function handleClick() {
                  console.log("Hello")
              }
              
              return (
                  <button 
                      className="button" 
                      onClick={handleClick}>
                      Click Me!
                  </button>
              )
              
          }

          運行代碼[7]

          我們有一個名為Button. 在這個函數(shù)中,我們有另一個函數(shù)叫做handleClick。在 HTML<button>標(biāo)記中,我們指定onClick={handleClick}當(dāng)單擊按鈕時handleClick調(diào)用該函數(shù)。此功能僅在Button組件內(nèi)部可用。單擊 Web 應(yīng)用程序上的按鈕將Hello在瀏覽器控制臺中打印。這就是我們使用 React 構(gòu)建交互式網(wǎng)站的方式!

          這個例子仍然相當(dāng)簡單,因為handleClick沒有參數(shù)。如果我們想在用戶輸入輸入框時在控制臺中打印文本怎么辦?我們?nèi)绾螌⑽谋緜鬟f給函數(shù)?

          就是這樣。

          function PrintTypedText() {
              
              function handleOnChange(text) {
                  console.log(text);
              }
              
              return (
                  <input 
                      type="text" 
                      onChange={(e) => handleOnChange(e.target.value)}
                  />
              )
          }

          運行代碼[8]

          HTMLinput元素提供了一個方便的事件偵聽器 -onChange每次輸入框中的文本發(fā)生更改(鍵入新字符、刪除字符等)時都會觸發(fā)該事件偵聽器。

          但是,除了觸發(fā)一個函數(shù)之外,它還傳遞了更改的 HTML 元素(在e此處引用)。然后我們可以獲取 HTML 元素e并使用提取文本e.target.value并將其作為參數(shù)傳遞給該參數(shù),該參數(shù)handleOnChange會將文本記錄到瀏覽器控制臺。

          不同的 HTML 元素有不同的事件處理程序 -這是兩個示例onChangeonClick但還有更多!您可以在此處[9]找到所有 HTML 事件的列表。

          通過將 HTML 事件與函數(shù)處理程序相結(jié)合,我們可以做各種很酷的事情!從服務(wù)器加載數(shù)據(jù)、向服務(wù)器發(fā)送數(shù)據(jù)、更新我們的視圖等。

          React Hooks - useState 和 useEffect

          好的,我們已經(jīng)討論了組合、數(shù)據(jù)傳遞和交互性。但是,我們的應(yīng)用程序仍然很愚蠢。交互性將允許您在單擊按鈕等時運行一些代碼,但是如果您想更新一些變量怎么辦?

          不幸的是,以下不起作用

          function DoesNotWork() {
              let myNumber = 0;
              
              function increment() {
                  myNumber++;
              }
              
              return (
                  <div>
                      <p>{myNumber}</p>
                      <button onClick={increment}>Increment!</button>
                  </div>
              )
          }

          運行代碼[10]

          無論點擊多少次Increment,屏幕上顯示的數(shù)字都會卡在0。這是因為當(dāng)您像myNumber從 React 組件中更新常規(guī)變量時,即使值已更新,React 實際上也不會重新渲染 Web 應(yīng)用程序的視圖。它不會自動更新頁面的 HTML 視圖。

          React Hooks 是“掛鉤”到 React 組件的不同部分的函數(shù),允許您執(zhí)行諸如在變量值更改時更新視圖,或在每次加載頁面或更改變量時自動運行一些 JS 代碼等操作,以及許多更酷的東西!我們將主要關(guān)注 95% 的時間使用的三個 React 鉤子 - useStateuseEffectuseRef

          useState

          很多時候,您希望 HTML 視圖根據(jù)某些變量的值變化進(jìn)行更新。我們可以使用該useState鉤子來維護(hù)一個變量,該變量會在每次更改其值時自動重新渲染屏幕上顯示的 HTML。這是一個例子:

          function ThisWorks() {
              // myNumber is the variable itself
              // setMyNumber is a function that lets us update the value
              // useState(0) initializes the React Hook 
              // with the starting value of 0
              const [myNumber, setMyNumber] = useState(0);
              
              function increment() {
                  // Sets the new value to the old value + 1
                  setMyNumber(myNumber + 1);
              }
              
              return (
                  <div>
                      <p>{myNumber}</p>
                      <button onClick={increment}>Increment!</button>
                  </div>
              )
          }

          運行代碼[11]

          如果您嘗試運行上述代碼,您將看到 Web 應(yīng)用程序的視圖自動更新以反映變量的新值。

          useState在 React 中使用創(chuàng)建的變量稱為狀態(tài)變量。狀態(tài)變量可以更新并自動更新應(yīng)用程序的視圖。這是另一個在輸入框中使用狀態(tài)變量的示例。

          function StateWithInput() {
              // myName is the variable
              // setMyName is the updater function
              // Create a state variable with initial value
              // being an empty string ""
              const [myName, setMyName] = useState("");
              
              function handleOnChange(text) {
                  setMyName(text);
              }
              
              return (
                  <div>
                      <input type="text" onChange={(e) => handleOnChange(e.target.value)} />
                      <p>Hello, {myName}!</p>
                  </div>
              )
          }

          運行代碼[12]

          我們看到 HTML 上顯示的文本隨著輸入框內(nèi)容的變化而變化。偉大的!

          關(guān)于 useState,我想說的最后一件事是,您還可以將它用于字符串和數(shù)字等基本類型。您還可以使用它們來存儲數(shù)組和對象。但是,這里有一個警告。讓我們看一個例子:

          function StateArrayDoesNotWork() {
            const [fruits, setFruits] = useState([]);
            const [currentFruit, setCurrentFruit] = useState("");
          
            function updateCurrentFruit(text) {
              setCurrentFruit(text);
            }
          
            function addFruitToArray() {
              fruits.push(currentFruit);
            }
          
            return (
              <div>
                <input type="text" onChange={(e) => updateCurrentFruit(e.target.value)} />
                <button onClick={addFruitToArray}>Add Fruit</button>
          
                <ul>
                  {fruits.map((fruit, index) => (
                    <li key={index}>{fruit}</li>
                  ))}
                </ul>
              </div>
            );
          }

          運行代碼[13]

          如果您嘗試運行它,您將看到屏幕上沒有顯示任何水果。另請注意,我們沒有在setFruits任何地方使用該函數(shù),而只是嘗試.push使用fruits數(shù)組。

          當(dāng)我們嘗試直接更新數(shù)組時,React 不會注冊狀態(tài)更改,這也被認(rèn)為是無效的狀態(tài)更新,可能導(dǎo)致應(yīng)用程序出現(xiàn)意外行為。我們知道我們需要以某種方式使用setFruits,但是如何使用呢?答案是我們實際上需要創(chuàng)建水果數(shù)組的副本,將水果添加到其中,并將狀態(tài)變量完全設(shè)置為新數(shù)組。下面的例子:

          function StateArray() {
            const [fruits, setFruits] = useState([]);
            const [currentFruit, setCurrentFruit] = useState("");
          
            function updateCurrentFruit(text) {
              setCurrentFruit(text);
            }
          
            function addFruitToArray() {
                // The spread operator `...fruits` adds all elements
                // from the `fruits` array to the `newFruits` array
                // and then we add the `currentFruit` to the array as well
                const newFruits = [...fruits, currentFruit]
                setFruits(newFruits);
            }
          
            return (
              <div>
                <input type="text" onChange={(e) => updateCurrentFruit(e.target.value)} />
                <button onClick={addFruitToArray}>Add Fruit</button>
          
                <ul>
                  {fruits.map((fruit, index) => (
                    <li key={index}>{fruit}</li>
                  ))}
                </ul>
              </div>
            );
          }

          運行效果[14]

          如果您嘗試運行上述代碼,您將看到它按預(yù)期工作。每次按下按鈕時,輸入框中的當(dāng)前文本都會添加到數(shù)組中,這會導(dǎo)致 HTML 上顯示的水果列表更新。您可以繼續(xù)添加任意數(shù)量的水果!

          對象也是如此。如果您的狀態(tài)變量包含一個對象,您需要先創(chuàng)建該對象的副本,更新一個值,然后將狀態(tài)變量完全設(shè)置為新對象。

          useEffect

          所以我們現(xiàn)在可以管理狀態(tài),太好了!狀態(tài)變化也會影響我們渲染的 HTML,也很棒!

          但是,通常需要在第一次加載頁面時自動運行一些代碼——可能是從服務(wù)器或區(qū)塊鏈獲取數(shù)據(jù)——并且還需要在某個狀態(tài)變量發(fā)生變化時自動運行一些代碼。

          這些類型的功能稱為副作用。React 為我們提供了useEffect允許我們編寫這些效果的鉤子。useEffect接受兩個參數(shù) - 一個函數(shù)和一個依賴數(shù)組。函數(shù)是運行效果時運行的代碼,依賴數(shù)組指定何時觸發(fā)副作用。

          考慮一個示例,當(dāng)網(wǎng)站首次加載時,它想從服務(wù)器加載一些數(shù)據(jù)。這樣做時,它希望向用戶顯示一個加載屏幕,然后在加載數(shù)據(jù)后,移除加載屏幕并顯示實際內(nèi)容。我們?nèi)绾巫龅竭@一點?

          
          function LoadDataFromServer() {
            // Create a state variable to hold the data returned from the server
            const [data, setData] = useState("");
            // Create a state variable to maintain loading state
            const [loading, setLoading] = useState(false);
          
            async function loadData() {
              // Set `loading` to `true` until API call returns a response
              setLoading(true);
          
              // Imaginary function that performs an API call to load
              // data from a server
              const data = await apiCall();
              setData(data);
          
              // We have the data, set `loading` to `false`
              setLoading(false);
            }
          
            // loadData is the function that is run
            // An empty dependency array means this code is run
            // once when the page loads
            useEffect(() => {
              loadData();
            }, []);
          
            // Display `"Loading..."` while `loading` is `true`,
            // otherwise display `data`
            return <div>{loading ? "Loading..." : data}</div>;
          }

          運行代碼[15]

          如果您從鏈接運行上述代碼,您將看到它Loading...在屏幕上顯示 5 秒鐘,然后顯示ABCDEF. 這是因為apiCall是一個等待 5 秒然后返回字符串的函數(shù)ABCDEF

          首次加載頁面時的useEffect調(diào)用loadData(由于依賴項數(shù)組為空)和狀態(tài)變量使 HTML 呈現(xiàn)適當(dāng)?shù)膬?nèi)容。

          這對于在頁面第一次加載時運行代碼很有用,但是每次狀態(tài)變量的值發(fā)生變化時運行一些代碼呢?例如,當(dāng)您在 Facebook 上搜索某人的姓名時,F(xiàn)acebook 如何在您每次添加/刪除角色時獲取并顯示推薦?

          你也可以useEffect通過在依賴數(shù)組中提供狀態(tài)變量來做到這一點。每次該變量的值發(fā)生變化時,都會運行效果。

          function DependentEffect() {
            const names = ["Alice", "Bob", "Charlie", "David", "Emily"];
          
            const [recommendations, setRecommendations] = useState([]);
            const [searchText, setSearchText] = useState("");
          
            useEffect(() => {
              // If user is not searching for anything, don't show any recomendations
              if (searchText.length === 0) {
                setRecommendations([]);
              } 
              // Else, find recommendations
              else if (searchText.length > 0) {
                const newRecs = names.filter((name) =>
                  name.toLowerCase().includes(searchText.toLowerCase())
                );
                setRecommendations(newRecs);
              }
            }, [searchText]);
          
            return (
              <div>
                <input type="text" onChange={(e) => setSearchText(e.target.value)} />
                <h2>Recommendations:</h2>
                <ul>
                  {recommendations.map((rec, index) => (
                    <li key={index}>{rec}</li>
                  ))}
                </ul>
              </div>
            );
          }

          運行代碼[16]

          如果您運行上述代碼并嘗試輸入一些字母,您將看到推薦列表會隨著您在搜索框中添加/刪除新字符而自動更新。這是因為當(dāng)你更新輸入框時,searchText是通過onChange處理程序更新的,它觸發(fā)了useEffect,它更新了recommendations列表,它更新了 HTML 視圖。

          您也可以類似地創(chuàng)建依賴于多個狀態(tài)變量的副作用,而不僅僅是一個。如果任何因變量發(fā)生變化,就會產(chǎn)生副作用。您只需將更多狀態(tài)變量添加到依賴項數(shù)組即可。

          useEffect(() => {
              // Some code
          }, [stateVar1, stateVar2, stateVar3, andSoOn])

          useRef

          useRef是另一個比較常用的 React 鉤子。它與表面上非常相似useState,但有一些實際上非常重要的細(xì)微差別使得這個 React Hook 學(xué)習(xí)起來很重要。

          useRef變量創(chuàng)建如下:

          
          function Component() {
              const myValue = useRef();
              
              function updateMyValue(newValue) {
                  myValue.current = newValue;
              }
              
              function printMyValue() {
                  console.log(myValue.current);
              }
          }

          1 - 沒有重新渲染

          與類似useStateuseRef鉤子還允許我們將變量存儲在可以隨時間更新的組件中。但是,與狀態(tài)變量不同,更新 ref 變量的值不會導(dǎo)致 HTML 視圖重新呈現(xiàn)。

          因此,如果您有一個useRef變量并且您在 HTML 視圖中顯示它的值,則更新該變量不會更新 HTML 視圖。

          
          function CounterWithRef() {
              const myNumber = useRef();
              
              function increment() {
                  if (myNumber.current !== undefined) {
                      myNumber.current += 1;
                  } else {
                      myNumber.current = 1;
                  }
                  console.log(myNumber.current);
              }
              
              return (
                  <div>
                      <p>{myNumber}</p>
                      <button onClick={increment}>Increment!</button>
                  </div>
              )
          }

          運行代碼[17]

          如果您運行上面的代碼,您會注意到每次單擊按鈕時,值都會遞增并打印在瀏覽器控制臺中,但 HTML 視圖實際上并沒有更新。事實上,HTML 視圖不顯示任何內(nèi)容,因為 is 的初始值,myNumber.current并且undefined由于 HTML 沒有更新,因此undefined即使值實際上正在更新,它仍然保持與 HTML 相關(guān)的內(nèi)容。

          2 - 同步更新

          我們之前沒有提到useState的是,當(dāng)我們使用setXYZ函數(shù)更新狀態(tài)變量時,它實際上并沒有立即更新。

          在 React 中為狀態(tài)變量設(shè)置新值是異步發(fā)生的,這意味著如果您在將狀態(tài)變量設(shè)置為新值后立即嘗試使用它的值,您可能實際上看不到新值被反映,因為它是異步發(fā)生的。

          我們再看一下使用時的Counter例子useState

          
          function AsyncStateVariables() {
            const [number, setNumber] = useState(0);
          
            function increment() {
              setNumber(number + 1);
              console.log(number);
            }
          
            return (
              <div>
                <p>{number}</p>
                <button onClick={increment}>Increment</button>
              </div>
            );
          }

          運行代碼[18]

          當(dāng)您運行它時,請注意視圖上發(fā)生了什么以及控制臺中發(fā)生了什么。當(dāng)您第一次單擊按鈕時,狀態(tài)變量應(yīng)該更新為1- 這就是視圖上發(fā)生的情況,網(wǎng)頁顯示1. 但是,如果您查看瀏覽器控制臺,0則會打印該值而不是1. 當(dāng)您繼續(xù)單擊按鈕時,此模式將繼續(xù)。

          這是因為setNumber調(diào)用是異步運行的,當(dāng)我們到達(dá)該console.log(number)行時,該值尚未更新,因此它會打印number. 當(dāng)它確實被更新時,HTML 被重新渲染以顯示新值。

          useRef另一方面,允許同步更新。當(dāng)您使用它更新引用變量的值時,myVar.current = newValue它會立即更新,并且沒有延遲。這有時會派上用場。

          3 - 引用 DOM 元素

          讓我們做的另一件很酷的事情useRef是它允許我們直接引用 DOM 元素。這是useState.

          例如,您可以input使用直接引用元素useRef

          
          function InputFocus() {
            const inputRef = useRef();
          
            useEffect(() => {
              inputRef.current.focus();
            }, []);
          
            return <input ref={inputRef} type="text" />;
          }

          運行代碼[19]

          當(dāng)您運行上面的示例時,您會注意到頁面加載后,該input元素已經(jīng)處于焦點位置,即您可以開始輸入而無需先單擊它。這是因為我們持有一個對input元素的引用,并且useEffect由于有一個空的依賴數(shù)組,它在頁面加載時運行,它專注于input元素。

          React文件結(jié)構(gòu)

          太好了,如果你剛剛開始使用 React,我們已經(jīng)介紹了你應(yīng)該知道的主要概念。但到目前為止,我們只處理了孤立的組件示例。一個實際的 React 項目是什么樣子的?

          React 應(yīng)用程序通常使用create-react-app(CRA)之類的工具創(chuàng)建。CRA 是一個命令行工具,可幫助您設(shè)置新的 React 項目并安裝所有必需的依賴項,而無需手動創(chuàng)建所有樣板。

          當(dāng)您使用 CRA 時,您最終會得到如下所示的文件結(jié)構(gòu)。

          package.json文件應(yīng)該是可識別的。CRA 通過 Node.js 環(huán)境工作,并且package.json是創(chuàng)建所有依賴項和項目元數(shù)據(jù)的地方 - 與任何其他 Node.js 項目一樣。

          src/文件夾包含組件和 CSS 樣式,基本上是任何特定于 React 的代碼。這里的主要組件是App.js,它是您第一次設(shè)置 React 應(yīng)用程序時創(chuàng)建的自動生成的組件。index.js是 React 應(yīng)用程序的主要入口點,但通常你不需要太多(或根本不需要)修改它。它只包含一些樣板 React 代碼,這些代碼獲取您的組件并將其轉(zhuǎn)換為可以在瀏覽器中運行的實際 HTML 和 JS。

          然后該public/文件夾默認(rèn)只包含一個文件 - index.html. 你通常不會自己碰這個。這是一個超級簡單的準(zhǔn)系統(tǒng) HTML 文件。當(dāng)一個 React 應(yīng)用程序運行時,React 會在后臺執(zhí)行一些魔法,它會獲取所有組件和 Javascript 代碼,將其轉(zhuǎn)換為可以在瀏覽器中運行的實際 HTML 和 JS,并index.html用所有這些替換其中的內(nèi)容。然后,更新index.html的就是向用戶顯示的內(nèi)容。

          如果您想將圖像、字體、音樂等添加到您的網(wǎng)站,它們也會進(jìn)入該public/文件夾。該public/文件夾基本上包含您希望在您的網(wǎng)站上直接訪問的所有內(nèi)容。

          例如,如果您在文件夾中添加了一個名為的圖像avatar.pngpublic/那么您可以在 React 組件中顯示該圖像,如下所示:

          <img src="/avatar.png" />

          雖然這可能看起來很奇怪,因為您的組件位于src/文件夾中而不是public/文件夾中 - 它工作的原因是因為圖像與位于同一文件夾中index.html- 并且index.html是您的 React 代碼實際結(jié)束的地方。因此,當(dāng)使用 的相對路徑引用圖像時/avatar.png,它知道它avatar.png必須在public文件夾內(nèi)。

          后端部分

          到目前為止,我們一直在討論 React,以及它的所有前端功能。但是后端呢?

          React 不是后端框架,因此如果您想創(chuàng)建自己的 API 后端,則必須使用 Node.js 和 Express 之類的東西建立一個單獨的項目。然而這很麻煩,就好像后端和前端是同一個項目一樣,你可能有很多代碼可以在兩者之間重用和共享。此外,維護(hù)兩個項目總是比只維護(hù)一個項目更難。

          輸入,Next.js

          接下來是 React 的元框架。這是什么意思?嗯,React 本身就是一個構(gòu)建 Web 應(yīng)用程序的框架。接下來是一個 React 框架,它還引入了一些 React 沒有的附加功能。

          如果你了解 React,那么 Next 90% 是完全一樣的東西,你可以很快開始使用它,但是我想談?wù)?Next 帶來的這些額外功能。

          首先,正如標(biāo)題和介紹所暗示的那樣,Next 允許您在單個項目中編寫前端和后端代碼。您使用 React 構(gòu)建前端,并使用與使用 Express 類似的語法編寫后端 API 端點 - 但都在同一個項目中。

          其次,Next 使創(chuàng)建多頁 Web 應(yīng)用程序變得非常容易。React 最初旨在幫助創(chuàng)建單頁應(yīng)用程序(SPA),組件非常適合!但是,如果您的網(wǎng)站有多個頁面怎么辦?例如https://learnweb3.io/https://learnweb3.io/about等等https://learnweb3.io/tracks

          為此,React Router引入了諸如此類的庫,這使之成為可能,但也有點麻煩。“下一步”通過允許基于文件名的自動頁面路由大大簡化了這一點。

          最后,Next 還具有服務(wù)器端渲染 (SSR) 和靜態(tài)站點生成 (SSG)。這些不是我們將在我們的曲目中使用的功能,所以我不會在這里花太多時間,但如果您想了解更多關(guān)于它們的信息,請隨時閱讀推薦閱讀。

          Next中的路由

          在討論創(chuàng)建后端服務(wù)器之前,我們將討論路由,因為這將幫助您了解它是如何工作的。

          類似于create-react-app,Next 有一個名為的工具create-next-app,可以自動幫助您輕松設(shè)置新的 Next.js 項目。

          當(dāng)您創(chuàng)建一個新的 Next.js 項目時,您最終會得到一個如下所示的文件結(jié)構(gòu):

          這是很多文件!但別擔(dān)心,其中很多與我們已經(jīng)討論過的 React 類似。

          public/文件夾的工作方式完全相同,但不包含index.html文件。但是,如果您想添加圖像、圖標(biāo)、字體、音樂等,您可以將它們?nèi)糠旁?span style="color: #DD1144; --tt-darkmode-color: #DD1144;">public/文件夾中。

          styles/文件夾是一個很好的補充,為您的所有 CSS 文件提供了一個專用位置。

          pages/就是偉大的。_app.js是一個自動生成的文件,通常您不會自己接觸它,并設(shè)置了一些樣板代碼,允許 Next 呈現(xiàn)正確的組件。

          pages/index.js是您網(wǎng)站的主頁。基本上,文件pages夾中的每個文件都是您網(wǎng)站的路線。遵循 Javascript/HTML 樣式的命名約定,這意味著index文件是“主”文件。因此,pages/index.js當(dāng)您第一次打開您的網(wǎng)站時將加載的視圖。

          如果您在pages文件夾下添加更多文件,例如一個名為about.js- 的文件,它將在YOUR_DOMAIN/about(有趣的事實:LearnWeb3 的網(wǎng)站是使用 Next 創(chuàng)建的,這正是我們https://learnweb3.io/about頁面的工作方式)。

          這很棒,因為您不必處理諸如 React Router 之類的事情,并且構(gòu)建多頁網(wǎng)站就像在pages/文件夾下創(chuàng)建一個新文件一樣簡單,Next 會自動為您生成基于文件名的路由。

          您還可以通過在文件夾下創(chuàng)建子文件夾來進(jìn)行多級路由pages/。例如,類似pages/tracks/freshman.js的東西會有 route YOUR_DOMAIN/tracks/freshman

          在 Next 中編寫 API

          然而,下面有一個特殊的文件夾,pages/它也是自動生成的。pages/api文件夾。與呈現(xiàn) HTML 視圖的常規(guī)頁面不同,pages/api文件夾下的任何內(nèi)容都充當(dāng) API 端點。

          讓我們看一下自動生成的pages/api/hello.js文件:

          
          export default function handler(req, res) {
            res.status(200).json({ name: 'John Doe' })
          }

          這是一個非常類似于 Express 的功能。如果你要去YOUR_DOMAIN/api/hello- 而不是呈現(xiàn) HTML 視圖,這將返回一個 JSON 對象{name: 'John Doe'}- 這是一個超級簡單的 API 端點。

          與常規(guī)的 HTML 視圖類似,您可以pages/api通過創(chuàng)建新文件來創(chuàng)建 API 端點,并且端點路由基于文件名。

          結(jié)論

          我希望這篇文章對您有所幫助,并且可以作為速成課程。我故意在這里更多地關(guān)注 React 而不是 Next,因為習(xí)慣前端部分對我們來說將比后端部分更相關(guān)。此外,后端代碼基本上是常規(guī)的 Javascript,而前端是 JSX,我想讓你更熟悉它。

          讀物/視頻

          • 30 分鐘學(xué)會 React[20]
          • Next.js in 100 Seconds // 加上完整的教程[21]
          • Scrimba 的完整 React 課程[22]
          • Next.js 速成班[23]

          引用鏈接

          [1] React: https://reactjs.org/
          [2] Angular:
          https://angular.io/
          [3] Vue:
          https://vuejs.org/
          [4] 運行代碼:
          https://codesandbox.io/s/icy-water-xrm2ch?file=/src/App.js
          [5] 運行代碼:
          https://codesandbox.io/s/confident-tesla-5l3k1t?file=/src/App.js
          [6] 運行代碼:
          https://codesandbox.io/s/zealous-carson-44om2s?file=/src/App.js
          [7] 運行代碼:
          https://codesandbox.io/s/quizzical-sammet-59fi5i?file=/src/App.js


          [8] 運行代碼:
          https://codesandbox.io/s/mutable-feather-el4sbe?file=/src/App.js
          [9] 您可以在此處:
          https://www.w3schools.com/jsref/dom_obj_event.asp
          [10] 運行代碼:
          https://codesandbox.io/s/romantic-euclid-44hh8x
          [11] 運行代碼:
          https://codesandbox.io/s/intelligent-hoover-31bwmg
          [12] 運行代碼:
          https://codesandbox.io/s/young-microservice-c1e5be?file=/src/App.js
          [13] 運行代碼:
          https://codesandbox.io/s/kind-jasper-vbs0f9?file=/src/App.js
          [14] 運行效果:
          https://codesandbox.io/s/suspicious-monad-gpes3r?file=/src/App.js


          [15] 運行代碼:
          https://codesandbox.io/s/practical-cache-ib3c0f?file=/src/App.js:186-756
          [16] 運行代碼:
          https://codesandbox.io/s/cold-darkness-eub0eh?file=/src/App.js
          [17] 運行代碼:
          https://codesandbox.io/s/small-paper-9ecbfp?file=/src/App.js
          [18] 運行代碼:
          https://codesandbox.io/s/reverent-lichterman-k4n448?file=/src/App.js
          [19] 運行代碼:
          https://codesandbox.io/s/input-focus-zntci?file=/src/App.js
          [20] 30 分鐘學(xué)會 React:
          https://www.youtube.com/watch?v=hQAHSlTtcmY
          [21] Next.js in 100 Seconds // 加上完整的教程:
          https://www.youtube.com/watch?v=Sklc_fQBmcs
          [22] Scrimba 的完整 React 課程:
          https://scrimba.com/learn/learnreact
          [23] Next.js 速成班:
          https://www.youtube.com/watch?v=mTz0GXj8NN0

          言:

          一位前端界的大神讓我去思考的一個問題, 給了Big-man一段代碼,如下:

          function Seriously(options) {
           // if called without 'new', make a new object and return that
           if(window === this || !(this instanceof Seriously) || this.id !== undefined) {
           return new Seriously(options);
           }
          }
          

          return語句執(zhí)行之后還會繼續(xù)執(zhí)行嗎?這是大神上來讓我解決的問題,既然提到了return那我也就隨帶解決JS中另外的兩種結(jié)束循環(huán)的方法break, continue。

          Break語句:

          break語句會使運行的程序立刻退出包含在最內(nèi)層的循環(huán)或者退出一個switch語句。

          由于它是用來退出循環(huán)或者switch語句的, 所以只有當(dāng)它出現(xiàn)在這些語句的時候, 這種形式的break語句才是合法的。

          如果一個循環(huán)的終止條件非常復(fù)雜, 那么使用break語句來實現(xiàn)某些條件比用一個循環(huán)表達(dá)式所有的條件容易得多。

          for(var i = 519; i < 550; i++) {
           if(i == 522) {
           break;
           }
           console.log(i);
           alert(i);
           document.write(i);
          }
          

          當(dāng)i = 521的時候,直接退出for這個循環(huán)。這個循環(huán)將不再被執(zhí)行。

          對于輸出結(jié)果的話,可以自己去測試的吧。

          Continue語句:

          continue語句和break語句相似。所不同的是,它不是退出一個循環(huán),而是開始循環(huán)的一次新迭代。

          continue語句只能用在while語句、do/while語句、for語句、或者for/in語句的循環(huán)體內(nèi), 在其他地方使用都會引起錯誤?

          for(var i = 5; i >=0; i--) {
           if(i == 4 || i == 3 || i == 1) {
           continue;
           }
           console.log(i);
           alert(i);
           document.write(i);
          }
          

          當(dāng)i = 4、i = 3以及i = 1的時候,直接跳出for循環(huán)。下次繼續(xù)執(zhí)行。

          至于輸出結(jié)果,還希望大家去打印一下。

          Return語句:

          return語句就是用于指定函數(shù)返回的值。return語句只能出現(xiàn)在函數(shù)體內(nèi),出現(xiàn)在代碼中的其他任何地方造成語法錯誤!

          for(var i = 1; i < 10; i++) {
           if(i == 8) {
           return;
           }
           console.log(i);
           alert(i);
           document.write(i);
          }
          

          執(zhí)行結(jié)果Uncaught SyntaxError: illegal return statement(...)

          錯誤意思是非法捕獲的查詢返回語句。

          當(dāng)執(zhí)行return語句時, 即使函數(shù)主題中還有其他語句, 函數(shù)執(zhí)行也會停止!

          <script type="text/javascript">
           if(username == "") {
           alert("please input your username: ");
           return false;
           } else if (qq == "") {
           alert("please input your qq number: ");
           return false;
           }
          </script>
          

          上面的實例里,當(dāng)username為空時,就不會再向下執(zhí)行,在一些表單提交中,也可以通過return false來阻止默認(rèn)的提交方式,改用Ajax的提交方式,例如:

          <form id="form" onSubmit="return false">
          ...
          </form>
          

          this對應(yīng)的全局變量:

          window == this這個Boolean等式,在不同的情況下的展現(xiàn)都不一樣的。

          <!DOCTYPE html>
          <html>
          <head>
           <title></title>
          </head>
          <body>
          <script type="text/javascript">
           function a() {
           console.log(window === this)
           }
           a();
          </script>
          </body>
          </html>
          

          這個時候的window === this打印出來的是true,這也就意味著this絕對等于window。

          <!DOCTYPE html>
          <html>
          <head>
           <title></title>
          </head>
          <body>
          <script type="text/javascript">
           'use strict'
           function a() {
           console.log(window === this)
           }
           a();
          </script>
          </body>
          </html>
          

          這個時候window === this返回回來的值卻是false, 而且打印出來的this是undefined的。

          所以嚴(yán)格模式下面的代碼操作需要更加的規(guī)范和合理才可以的。

          vector illustration web development shield sign

          ---------------------

          作者:JD9

          來源:CSDN

          原文:https://blog.csdn.net/XXJ19950917/article/details/78310346

          版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請附上博文鏈接!


          主站蜘蛛池模板: 一区二区三区杨幂在线观看| 日韩一区二区在线观看视频| 亚洲日韩中文字幕无码一区| 日本一区二区在线| 亚欧在线精品免费观看一区 | 精品国产免费一区二区三区香蕉| 无码毛片一区二区三区视频免费播放| 国产精品一区二区三区99| 国产色综合一区二区三区| 亚洲色精品VR一区区三区| 国产午夜精品片一区二区三区| 亚洲制服丝袜一区二区三区| 久久免费精品一区二区| 亚洲欧美国产国产综合一区| 蜜桃视频一区二区三区| 视频在线观看一区二区三区| 久久久91精品国产一区二区| 嫩B人妻精品一区二区三区| 亚洲不卡av不卡一区二区| 无码人妻一区二区三区免费手机| 日韩一区二区三区在线| 性色AV一区二区三区| 夜夜精品视频一区二区| 亚洲爽爽一区二区三区| 精品国产一区二区三区久久久狼| 无码一区二区三区中文字幕| 无码精品久久一区二区三区 | 国产一区在线视频观看| 国产综合无码一区二区辣椒 | 国产成人一区二区三区免费视频| 精品一区二区视频在线观看| 久久精品无码一区二区三区日韩| 亚洲av无码片区一区二区三区| 国产福利电影一区二区三区久久老子无码午夜伦不 | 久久久av波多野一区二区| 色欲综合一区二区三区| 日韩精品一区二区三区中文| 久久精品无码一区二区无码| 亚洲一区二区三区高清不卡| 国产福利日本一区二区三区| 一区五十路在线中出|