整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          React入門:從入口文件到組件化的奧秘

          前端開發領域,React和Vue的出現,標志著前端開發者可以“Say goodbye to DOM coding”,即不必再直接與DOM元素頻繁互動,他們引領著我們告別傳統的DOM操作時代,邁入MVVM(Model-View-ViewModel)模式的嶄新天地。

          通過上一篇文章 [初識React框架](https://juejin.cn/post/7385752495534522383),我們能知道React是一個由Facebook開發并維護的開源JavaScript庫,并以其高效、靈活的組件化思想,成為了構建用戶界面的首選工具之一。它通過采用組件化的方式來組織代碼,使得復雜的UI結構變得易于管理和維護。本文將從一個簡單的React項目入口文件`index.js`出發,逐步揭開React的神秘面紗,探索其核心概念與機制,直至深入組件化的精髓。

          一、React入門

          我們來到React項目入口文件index.js中,這是React應用的起點。

          import ReactDOM from 'react-dom/client';

          const root=ReactDOM.createRoot(

          document.getElementById('root')

          );

          root.render(<h1>征程</h1>)

          通過import ReactDOM from 'react-dom/client';引入了React的DOM渲染庫,而ReactDOM.createRoot()方法則標志著React接管DOM的第一步。這個方法接收一個DOM元素作為參數(如document.getElementById('root')),為React創建了一個掛載點。從這一刻起,React將獨立管理這部分DOM,也就是這里將成為React展示的舞臺,根節點root 內部就是react的世界了,此后的所有React組件將在這個根節點下進行渲染。root.render()方法接收一個React元素作為參數,并將其渲染到與root關聯的DOM節點上。

          二、JSX:讓JavaScript擁抱HTML

          在React的世界里,JSX是最具特色的語法糖。通過允許在JavaScript中直接編寫類似HTML的代碼,極大地提升了代碼的可讀性和編寫效率。

          import ReactDOM from 'react-dom/client';

          import React from 'react';

          const root=ReactDOM.createRoot(

          document.getElementById('root')

          );

          // react 的法寶 : JSX語法

          const element1 = (<h1>正大會</h1>)

          const element2 =React.createElement('div',{id:'name'},'正小會')

          // root.render(element1)

          root.render(element2)

          <h1>正大會</h1>這樣的JSX表達式,不僅直觀地描述了UI結構,而且與React.createElement()相比,更加簡潔明了。JSX背后,是編譯器將其轉換為React元素(如React.createElement('h1', null, '正大會'))的過程,從而確保了JavaScript引擎的正常解析執行。也就是說明,在react中我們只要運用JSX,就可以在JavaScript直接編寫HTML。

          三、職責分離

          React鼓勵我們以函數或類的形式定義組件,每個組件負責渲染一塊UI,這種設計模式大大促進了代碼的復用和維護。

          import ReactDOM from 'react-dom/client';

          import React from 'react';

          // module 模塊化的導入

          import APP from './app/app';

          const root =ReactDOM.createRoot(

          // 就做這一次DOM查找,ReactDOM來做查找

          // DOM編程 性能很差

          document.getElementById('root')

          )

          root.render(<APP/>)

          index.js就用于生成一個root節點,而其他的東西就交給其他的組件APP,但是要注意的是,root.render()接受的是一個參數,所以根組件里必須返回一個值。

          // 根組件

          // react 使用函數創建一個組件,一定要返回JSX

          // 頁面由組件組成(更利于復用)

          const APP = ()=>{

          const element = (

          <div className='container'>

          <h1> classname='title'>周老板</h1>

          </div>

          )

          return element

          }

          // module 模塊化的輸出

          export default APP

          通過定義const APP = ()=>{...}這樣的函數創建一個組件,而在組件中我們可以寫一堆的HTML、css、js,而頁面將由組件組成,更利于復用。而組件內部可以繼續嵌套其他組件(AppHeader組件),如<AppHeader />,形成了一個組件樹結構,這便是React應用的核心架構。

          import AppHeader from './components/app-header';

          const APP = ()=>{

          const element = (

          <div className='container'>

          <AppHeader/>

          <AppHeader />

          </div>

          )

          return element

          }

          export default APP

          四、組件化

          組件化是React的靈魂。它允許我們將界面拆分成多個獨立、可復用的部分,每個部分都有自己的狀態和生命周期。

          const AppHeader = (props)=>{

          const {name}= props;

          return (

          <div className="app-header">

          <h1 className="title">{name}</h1>

          </div>

          )

          }

          export default AppHeader

          AppHeader組件是可以傳入參數的,別忘了他是由函數來創建的。通過屬性(props)傳遞數據(如name),這樣就可以獲取調用AppHeader的那個組件中的name值,更加利于復用。

          import AppHeader from './components/app-header';

          const APP = ()=>{

          const element = (

          <div className='container'>

          <AppHeader name='吳彥祖'/>

          <AppHeader name='彭于晏'/>

          </div>

          )

          return element

          }

          export default APP

          組件之間可以進行通信,同時保持各自的純凈性,降低了耦合度。組件化不僅提高了代碼的組織性和可測試性,也為大規模應用的開發提供了有力支撐。

          結語

          React不僅僅是一個庫,它代表了一種全新的前端開發范式。通過ReactDOM.createRoot與JSX的巧妙結合,React讓我們能夠在JavaScript中以聲明式的方式描述UI,通過組件化思想實現界面的模塊化開發。這一系列機制,不僅提升了開發效率,也使得代碼更易于理解和維護。不愧是“前端第一框架”,這可不是虛名昂。接下來,我將持續學習并分享,讓我們一起加油!!

          者 | Lakindu Hewawasam

          譯者 | 許學文

          策劃 | 丁曉昀

          如果你在開發工作中使用的是 React 框架,那么首當其沖要學習的就是思考如何設計組件。組件設計并非簡單地將多個組件合成一個集合,而是需要思考如何設計更小、復用性更強的組件。例如,思考下面這張組件圖:



          簡化的組件圖


          圖中有三個組件,分別是:


          1. Typography 組件
          2. Footer 組件
          3. Sizeable Box 組件


          如圖所示,Typography 組件同時被 Footer 和 Sizeable Box 組件使用。通常我們以為這樣,就能構建一個簡單、易維護和易排除錯誤的應用了。但其實只是這樣思考組件的設計是遠遠不夠的。


          如果你知道如何從組件的視角思考問題,就可以通過在 React 組件中使用設計模式來提高代碼的整體模塊性、可擴展性和可維護性。


          因此,下面這五種設計模式,是你在使用 React 時必須要掌握的。


          模式一:基礎組件


          首先,在使用 React 時候,請嘗試為應用設計基礎組件。


          基礎UI組件,就是一個具備默認行為且支持定制化的組件。


          例如,每個應用都會通過基礎的樣式、按鈕設計或者基礎的排版,來實現應用在視覺和交互上的一致性。這些組件的設計特點是:


          1. 組件會應用一組默認的配置。因此,使用者無需進行任何額外的配置,就可以快速基于默認配置使用組件。
          2. 組件可以支持定制化,使用者通過定制化可以覆蓋組件的默認行為,從而為組件提供自定義的整體視覺和交互效果。


          通過一個 Button 組件就能很好地說明基礎組件模式的實現。示例如下:


          1. Button 組件可能會有諸如空心、實心等不同形態。
          2. Button 組件可能會有默認的文本。


          現在你就可以利用基礎組件模式進行設計,使組件的使用者可以改變其行為。請參考我基于基礎組件模式完成的Button組件,示例代碼如下:


          import React, { ButtonHTMLAttributes } from 'react';
          
          
          
          
          // 按鈕組件的形態:實心或者空心
          type ButtonVariant = 'filled' | 'outlined';
          
          
          
          
          export type ButtonProps = {
            /**
             * the variant of the button to use 
             * @default 'outlined'
             */
            variant?: ButtonVariant;
          } & ButtonHTMLAttributes<HTMLButtonElement>;;
          
          
          
          
          const ButtonStyles: { [key in ButtonVariant]: React.CSSProperties } = {
            filled: {
              backgroundColor: 'blue', // Change this to your filled button color
              color: 'white',
            },
            outlined: {
              border: '2px solid blue', // Change this to your outlined button color
              backgroundColor: 'transparent',
              color: 'blue',
            },
          };
          
          
          
          
          export function Button({ variant = 'outlined', children, style, ...rest }: ButtonProps) {
            return (
              <button
                type='button'
                style={{
                  ...ButtonStyles[variant],
                  padding: '10px 20px',
                  borderRadius: '5px',
                  cursor: 'pointer',
                  ...style
                }} {...rest}>
                {children}
              </button>
            );
          }

          復制代碼


          仔細觀察代碼會發現,這里 Button 組件的 props 類型合并了原生 HTML 中 button 標簽屬性的全部類型。這意味著,使用者除了可以為 Button 組件設置默認配置外,還可以設置諸如 onClick、aria-label 等自定義配置。這些自定義配置會通過擴展運算符傳遞給 Button 組件內部的 button 標簽。


          通過不同的上下文設置,可以看到不同的 Button 組件的形態,效果截圖如下圖。


          這個可以查看具體設置:

          https://bit.cloud/lakinduhewa/react-design-patterns/base/button/~compositions



          基礎組件在不同上下文中的使用效果


          通過不同的上下文,你可以設定組件的行為。這可以讓組件成為更大組件的基礎。


          模式二:組合組件


          在成功創建了基礎組件后,你可能會希望基于基礎組件創建一些新的組件。


          例如,你可以使用之前創建的 Button 組件來實現一個標準的 DeleteButton 組件。通過在應用中使用該 DeleteButton,可以讓應用中所有刪除操作在顏色、形態以及字體上保持一致。


          不過,如果出現重復組合一組組件來實現相同效果的現象,那么你可以考慮將它們封裝到一個組件中。


          下面,讓我們來看看其中一種實現方案:

          https://bit.cloud/lakinduhewa/react-design-patterns/composition/delete-button



          使用組合模式創建組件


          如上面的組件依賴圖所示,DeleteButton 組件使用基礎的 Button 組件為所有與刪除相關的操作提供標準的實現。下面是基本代碼實現:


          // 這里引入了,基礎按鈕組件和其props
          import { Button, ButtonProps } from '@lakinduhewa/react-design-patterns.base.button';
          import React from 'react';
          
          
          
          
          export type DeleteButtonProps = {} & ButtonProps;
          
          
          
          
          export function DeleteButton({ ...rest }: DeleteButtonProps) {
            return (
              <Button
                variant='filled'
                style={{
                  background: 'red',
                  color: 'white'
                }}
                {...rest}
              >
                DELETE
              </Button>
            );
          }

          復制代碼


          我們使用基于模式一創建的 Button 組件來實現的 DeleteButton 組件的效果如下:



          現在我們可以在應用中使用統一的刪除按鈕。此外,如果你使用類似 Bit 的構建系統進行組件的設計和構建,那么當 Button 組件發生改變時,可以讓CI服務自動將此改變傳遞到DeleteButton組件上,就像下面這樣(當 Button 組件從 0.0.3 升級到了 0.0.4,那么 CI 服務會自動觸發,將 DeleteButton 組件從 0.0.1 升級到 0.0.2):


          Bit 上的一個 CI 構建


          模式三:使用 Hooks


          React Hooks 是React v16就推出來的特性,它不依賴類組件實現狀態管理、負效應等概念。簡而言之,就是你可以通過利用 Hooks API 擺脫對類組件的使用需求。useSate 和 useEffect 是最廣為人知的兩個 Hooks API,但本文不打算討論它們,我想重點討論如何利用 Hooks 來提高組件的整體可維護性。


          例如,請考慮下面這個場景:


          1. 有一個 BlogList 組件。
          2. BlogList 組件會通過調用一個簡單的 API,獲取博客文章列表數據,同時將其渲染在組件上。


          基于上面的案例,你可能會像下面這樣將 API 邏輯直接寫在函數組件中:


          import React, { useState, useEffect } from 'react';
          import axios from 'axios';
          const BlogList = () => {
              const [blogs, setBlogs] = useState([]);
              const [isLoading, setIsLoading] = useState(true);
              const [error, setError] = useState(null);
              useEffect(() => {
                  axios.get('https://api.example.com/blogs')
                      .then(response => {
                          setBlogs(response.data);
                          setIsLoading(false);
                      })
                      .catch(error => {
                          setError(error);
                          setIsLoading(false);
                      });
              }, []);
              if (isLoading) return <div>Loading...</div>;
              if (error) return <div>Error: {error.message}</div>;
              return (
                  <div>
                      <h2>Blog List</h2>
                      <ul>
                          {blogs.map(blog => (
                              <li key={blog.id}>{blog.title}</li>
                          ))}
                      </ul>
                  </div>
              );
          };
          export default BlogList;

          復制代碼


          這樣寫,組件也能正常工作。它將會獲取博客文章列表并且渲染在 UI 上。但是,這里將 UI 邏輯和 API 邏輯混在一起了。


          理想情況下,React 組件應該不需要關系如何獲取數據。而只需要關心接收一個數據數組,然后將其呈現在 DOM 上。


          因此,實現這一目標的最佳方法是將 API 邏輯抽象到 React Hook 中,以便在組件內部進行調用。這樣做就可以打破 API 調用與組件之間的耦合。通過這種方式,就可以在不影響組件的情況下,修改底層的數據獲取邏輯。


          其中一種實現方式如下。


          1.useBlog hook


          import { useEffect, useState } from 'react';
          import { Blog } from './blog.type';
          import { Blogs } from './blog.mock';
          
          
          
          
          export function useBlog() {
            const [blogs, setBlogs] = useState<Blog[]>([]);
            const [loading, setLoading] = useState<boolean>(false);
          
          
          
          
            useEffect(() => {
              setLoading(true);
               // 注意:這里的setTimeout非實際需要,只是為了模擬API調用
              setTimeout(() => {
                setBlogs(Blogs);
                setLoading(false);
              }, 3000);
            }, []);
          
          
          
          
            return { blogs, loading }
          }

          復制代碼


          如上代碼所示,useBlog hook 獲取博客列表數據,然后賦值給狀態變量,最后通過導出變量給到消費者(BlogList 組件)使用:



          Hook 效果


          2.BlogList 組件


          import React from 'react';
          // 引入上面封裝的 useBlog hook
          import { useBlog } from '@lakinduhewa/react-design-patterns.hooks.use-blog';
          export function BlogList() {
            const { blogs, loading } = useBlog();
            if (loading) {
              return (
                <p>We are loading the blogs...</p>
              )
            }
            return (
              <ul>
                {blogs.map((blog) => <ol
                  key={blog.id}
                >
                  {blog.title}
                </ol>)}
              </ul>
            );
          }

          復制代碼



          BlogList 組件效果


          通過調用 useBlog 和使用其導出的狀態變量,我們在 BlogList 組件中使用了 Hooks。如此,相對于之前,我們可以減少大量代碼,并以最少的代碼和精力維護兩個組件。


          此外,當你使用類似 Bit 這樣的構建系統時(就像我一樣),只需將 useBlog 組件導入本地開發環境,然后再修改完成之后重新推送回 Bit Cloud。Bit Cloud 的構建服務器可以依托依賴樹將此修改傳遞給整個應用。因此如果只執行一些簡單修改,甚至不需要訪問整個應用。


          模式四:React Providers


          此模式的核心是解決組件狀態共享。我們都曾是 props 下鉆式傳遞的受害者。但如果你還沒有經歷過,那這里簡單解釋下:“props 下鉆式傳遞”就是當你在組件樹中進行 props 傳遞時,這些 props 只會在最底層組件中被使用,而中間層的組件都不會使用該 props。例如,看看下面這張圖:

          props 下鉆式傳遞


          從 BlogListComponent 一直向下傳遞一個 isLoading 的 props 到 Loader。但是,isLoading 只在 Loader 組件中使用。因此,在這種情況下,組件不但會引入不必要的 props,還會有性能開銷。因為當 isLoading 發生變化時,即使組件沒有使用它,React 依然會重新渲染你的組件樹。


          因此,解決方案之一就是通過利用 React Context 來使用 React Context Provider 模式。React Context 是一組組件的狀態管理器,通過它,你可以為一組組件創建特定的上下文。通過這種方式,你可以在上下文中定義和管理狀態,讓不同層級的組件都可以直接訪問上下文,并按需使用 props。這樣就可以避免 props 下鉆式傳遞了。


          主題組件就是該模式的一個常見場景。例如,你需要在應用程序中全局訪問主題。但將主題傳遞到應用中的每個組件并不現實。你可以創建一個包含主題信息的 Context,然后通過 Context 來設置主題。看一下我是如何通過React Context實現主題的,以便更好地理解這一點:

          https://bit.cloud/lakinduhewa/react-design-patterns/contexts/consumer-component


          import { useContext, createContext } from 'react';
          export type SampleContextContextType = {
            /**
             * primary color of theme.
             */
            color?: string;
          };
          export const SampleContextContext = createContext<SampleContextContextType>({
            color: 'aqua'
          });
          export const useSampleContext = () => useContext(SampleContextContext);

          復制代碼


          在 Context 中定義了一種主題顏色,它將在所有實現中使用該顏色來設置字體顏色。接下來,我還導出了一個 hook——useSampleContext,該 hook 讓消費者可以直接使用 Context。


          只是這樣還不行,我們還需要定義一個 Provider。Provider 是回答 "我應該與哪些組件共享狀態?"問題的組件。Provider的實現示例如下:


          import React, { ReactNode } from 'react';
          import { SampleContextContext } from './sample-context-context';
          export type SampleContextProviderProps = {
            /**
             * primary color of theme.
             */
            color?: string,
            /**
             * children to be rendered within this theme.
             */
            children: ReactNode
          };
          export function SampleContextProvider({ color, children }: SampleContextProviderProps) {
            return <SampleContextContext.Provider value={{ color }}>{children}</SampleContextContext.Provider>
          }

          復制代碼


          Provider 在管理初始狀態和設置 Context 可訪問狀態的組件方面起著至關重要的作用。


          接下來,你可以創建一個消費者組件來使用狀態:



          消費者組件


          模式五:條件渲染


          最后一個想和大家分享的是條件渲染模式。今天,人人都知道 React 中的條件渲染。它通過條件判斷來選擇組件進行渲染。


          但在實際使用中我們的用法常常是錯誤的:


          // ComponentA.js
          const ComponentA = () => {
              return <div>This is Component A</div>;
          };
          // ComponentB.js
          const ComponentB = () => {
              return <div>This is Component B</div>;
          };
          // ConditionalComponent.js
          import React, { useState } from 'react';
          import ComponentA from './ComponentA';
          import ComponentB from './ComponentB';
          const ConditionalComponent = () => {
              const [toggle, setToggle] = useState(true);
              return (
                  <div>
                      <button onClick={() => setToggle(!toggle)}>Toggle Component</button>
                      {toggle ? <ComponentA /> : <ComponentB />}
                  </div>
              );
          };
          export default ConditionalComponent;

          復制代碼


          你是否注意到,這里我們將基于條件的邏輯耦合到了 JSX 代碼片段中。通常,你不應該在 JSX 代碼中中添加任何與計算相關的邏輯,而只將與 UI 渲染相關的內容放在其中。


          解決這個問題的方法之一是使用條件渲染組件模式。創建一個可重用的 React 組件,該組件可以根據條件渲染兩個不同的組件。它的實現過程如下:


          import React, { ReactNode } from 'react';
          export type ConditionalProps = {
            /**
             * the condition to test against
             */
            condition: boolean
            /**
             * the component to render when condition is true
             */
            whenTrue: ReactNode
            /**
             * the component to render when condition is false
             */
            whenFalse: ReactNode
          };
          export function Conditional({ condition, whenFalse, whenTrue }: ConditionalProps) {
            return condition ? whenTrue : whenFalse;
          }

          復制代碼


          我們創建了一個可以按條件渲染兩個組件的組件。當我們將其集成到其他組件中時,會使代碼更簡潔,因為無需在 React 組件中加入復雜的渲染邏輯。你可以像下面這樣使用它:


          export const ConditionalTrue = () => {
            return (
              <Conditional
                condition
                whenFalse="You're False"
                whenTrue="You're True"
              />
            );
          }
          export const ConditionalFalse = () => {
            return (
              <Conditional
                condition={false}
                whenFalse="You're False"
                whenTrue="You're True"
              />
            );
          }

          復制代碼


          實際的輸入如下:



          總結


          掌握這五種設計模式,為 2024 年做好充分準備,構建出可擴展和可維護的應用吧。


          如果你想詳細深入本文中討論的模式,請隨時查看我在Bit Cloud的空間:

          https://bit.cloud/lakinduhewa/react-design-patterns


          感謝你的閱讀!

          原文鏈接:2024年,你應該知道的5種React設計模式_架構/框架_InfoQ精選文章



          題描述:

          • 圖片的路徑是正確的;
          • 在原生html中寫是沒有任何問題的;
          • 但是當我們通過img標簽中的src引用圖片但是不顯示;


          • 代碼是這樣子的:

          src寫的圖片路徑但是不顯示圖片


          通過在state中定義,也不顯示



          解決方法:

          • 通過import解決


          • 通過require解決


          需要注意的是,require里只能寫字符串,不能寫變量。

          • 結果




          為什么會出現這種情況?

          • create-react-app的官網,官網明確說出最好將圖片樣式等放在public文件夾中,是可以用的<img src="../images/photo.png"/>這種形式的;

          官網鏈接:https://facebook.github.io/create-react-app/docs/using-the-public-folder#when-to-use-the-public-folder






          加油,程序員。。。


          主站蜘蛛池模板: 亚洲不卡av不卡一区二区| 美女视频黄a视频全免费网站一区| 国产综合视频在线观看一区| 久久精品动漫一区二区三区| 国产精品毛片a∨一区二区三区 | 中文字幕一区二区三区精彩视频| 国产乱码精品一区二区三区香蕉| 亚洲国产成人久久综合一区77 | 国产精品高清视亚洲一区二区| 午夜影院一区二区| 国产在线精品一区二区三区直播| 精品国产福利在线观看一区| 国产在线第一区二区三区| 国产成人一区二区三区在线| 一区二区视频免费观看| 国产丝袜无码一区二区三区视频| 亚洲福利视频一区| 日韩一区二区精品观看| 久久国产一区二区| 视频一区二区在线播放| 久久精品亚洲一区二区三区浴池| 性色AV一区二区三区无码| 亚洲色婷婷一区二区三区| 亚洲综合一区二区三区四区五区| 无码日韩人妻AV一区免费l| 亚洲毛片αv无线播放一区 | 久久亚洲AV午夜福利精品一区| 精品国产免费一区二区三区香蕉| 精品成人一区二区三区四区| 亚洲爽爽一区二区三区| 一区二区三区四区免费视频| 国产精品美女一区二区| 亚洲视频一区二区三区| 黄桃AV无码免费一区二区三区| 国偷自产av一区二区三区| 亚洲高清毛片一区二区| 久夜色精品国产一区二区三区| 激情综合一区二区三区| 日本一区免费电影| 欧美日韩综合一区二区三区| 精品无码国产一区二区三区51安|