主頁 > 前端設計 > [React 基礎系列] React 中的 元素 vs 組件

[React 基礎系列] React 中的 元素 vs 組件

2021-07-23 08:17:05 前端設計

[React 基礎系列] React 中的 元素 vs 組件

    • 元素
      • 元素的渲染
      • 元素的更新
      • Virtual DOM
    • 組件
      • 組件的渲染
      • 組合組件
        • 父子組件
        • 兄弟組件
      • 組件的提取
    • 總結

在這次復習官方教程之前,我還沒意識到 元素(element) 和 組件(component) 在 React 中的定義還是有些不太一樣的,通俗理解就是,組件是由元素所構成的,

第一個復習的系列是 JSX 的內容:什么是 JSX,以及如何使用 JSX

元素

官網上是這么描述元素的:

Elements are the smallest building blocks of React apps.

元素是構成 React 應用的最小磚塊,

這個翻譯我真的覺得像是機翻

直白的說法就是,元素是 React 應用中可被創建的最基本的組成部分,沒有比元素更小的組成部分了,依舊是用 Hello World 為例,如果想要在頁面上渲染 Hello World,最簡單的寫法如下:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

const element = <h1>Hello World</h1>;

ReactDOM.render(element, document.getElementById('root'));

效果如圖下:

這么看來,元素的寫法其實還頗有一種,所思即所見 的感覺,也就是說,你想在頁面上呈現的內容,就是寫出來的內容,

在這個案例中,如果想要渲染一個 h1 的標簽,其內容就是 Hello World,那么需要定義一個元素,標簽為 <h1>,定義的元素內容為 Hello World,最后將其掛載到頁面上即可,

元素的渲染

在上一篇 什么是 JSX 部分中曾經講過,其實 JSX 最侄訓被編譯成一個 JavaScript 物件,如:

const element = <h1 className="greeting">Hello, world!</h1>;

// 實際上經過編譯之后等同于
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world!',
  },
};

單純的 JavaScript 物件是沒有辦法直接被渲染到頁面上的,因此它需要借助另一個函式—— ReactDOM.render() ——將 JavaScript 物件渲染成實際的 DOM 元素,ReactDOM.render() 的使用語法為:

ReactDOM.render(element, document.getElementById('root'));

其中,element 是創建的 JavaScript 物件;

root 是 HTML 頁面上的真實存在的元素,

完整的頁面結構為:

|- public
|  |- index.html => 作為index.js的入口檔案
|- src
|  |- index.js => 作為整個專案的入口檔案

index.html 中一般只會存在一個 root 元素作為整個 DOM 樹的根節點,ReactDOM.render() 會獲取這個根節點,并負責將創建的元素由 JavaScript 物件轉化為頁面上展示的 DOM 節點,

元素的更新

React 的很多核心設計都是基于不可變性,React 中的元素也是如此,這也就意味著一旦一個 React 的元素被創建成功之后,元素的屬性、子元素便無法被更新,而唯一更新 UI 的方式就是創建一個新的元素,并且將新的元素傳入到 ReactDOM.render() 之中,

如果 React 只是在每一次有 UI 變動時,就更新一次完整的 DOM 樹,那么這就和傳統非常耗時的 DOM 操作沒什么區別,

不過,React 內部有自己的更新機制,能夠很好的對操作 DOM 進行優化,減少無意義的重復渲染造成的性能損耗,

Virtual DOM

Virtual DOM 就是 React 實作的 DOM 更新優化機制,它將 DOM 樹進行虛擬化的操作后存入記憶體之中,虛擬的 DOM 樹與真實的 DOM 樹會通過如 ReactDOM 之類的庫進行連接,

使用虛擬 DOM 樹的優點非常的明顯,在對 DOM 進行操作時,直接更新原有的 DOM 樹是非常耗時的操作,但是 ReactDOM 會對兩棵樹——虛擬 DOM 樹和真實 DOM 樹進行對比,只重新渲染更新過的 DOM 節點,這樣就能夠省去大量無意義的重新渲染,從而提升性能,

對于 React 團隊來說,他們的理念是專注于當下的頁面看起來應當是什么模樣,而非如何在未來更新 UI,

組件

組件擁有以下幾個特性:

  • 是一部分 UI
  • 這一部分 UI 是 獨立(independent)可復用 的片段
  • 這一部分 UI 可 孤立(isolate) 于其他的片段單獨存在
  • 可以通過 props 去獲得從外部傳來的資料

和 元素 對比一下:

// 組件
// 函陣列件
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
// 類組件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

// 這里的 Welcome 回傳的是自定義元素
// 呼叫的方法都是一樣的
const elementOfComponent = <Welcome />;

能夠很明顯的發現,元素的概念是一個 JavaScript 的物件;而組件的概念類似于一個 JavaScript 的函式,

注:

自定義的組件必須以大寫字母開頭

小寫字母開頭的開頭的組件將會被試做是原生 DOM 的標簽

組件的渲染

以案例中的 Welcome 為例,這個時候的回傳值是空白的:

原因是因為外部沒有傳進 name 這個屬性給 Welcome組件,在獲取不到資料的情況下,props.name 的值就是 undefined,因此頁面上只會顯示 Hello,

現在,就將 name 傳給 Welcome組件,以此展示一下 props 的呼叫,

// 組件的實作方式不變
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

// 僅修改呼叫方式
const element = <Welcome name="John" />;

// 具體頁面的渲染還是需要依靠 ReactDOM 去實作
ReactDOM.render(element, document.getElementById('root'));

這時候,頁面顯示的就是更有意義的內容:

組合組件

傳統意義上來說,組合組件有這么會有 父子兄弟 這兩種關系,

父子組件

父子組件也就是在組件之中繼續嵌套一個組件,例如說一個主頁面中可以嵌套一個導航欄和正文內容:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

const Nav = () => {
  return (
    <nav>
      <ol>
        <li className="nav-list">
          <span>homepage</span>
        </li>
        <li className="nav-list">
          <span>articles</span>
        </li>
        <li className="nav-list">some article</li>
      </ol>
    </nav>
  );
};

const MainContent = () => {
  return (
    <div className="main-content">
      <h1>Header</h1>
      <p>some content</p>
    </div>
  );
};

const MainPage = (props) => {
  return (
    <>
      <Nav />
      <MainContent />
    </>
  );
};

const component = <MainPage name="John" />;

ReactDOM.render(component, document.getElementById('root'));

效果圖如下:

這個頁面的結構如下:

|- MainPage
|  |- Nav
|  |- MainContent

這其中,MainPageNav/MainContent 就是父子關系,又因為 React 的資料是單向傳輸的,所以要傳資料的話,資料只能從 MainPage 傳向 Nav/MainContent

兄弟組件

同樣是上面的案例,NavMainContent 是并列存在的,因此他們之間的關系就是兄弟關系,

在 React 之中,資料是單向傳輸的,只能通過 props 從 父 傳到 子,兄弟之間也無法交換資訊,所以 NavMainContent 之間是沒有辦法進行資料的交換的,

另外,要渲染兄弟組件有以下幾種方式:

  1. 將他們包裹在一個根元素之中,根元素作為他們的父元素

    這種寫法適合兄弟之間有一定的關聯的情況下,這種常見的案例有導航欄下的 li,它們可以隸屬于 nav 下作為子組件,

  2. 如果兄弟組件之間沒有特別明顯的層級關系,又不想額外嵌套一個 <div> </div> 在頁面上去增加復雜度,可以使用 <React.Fragment> <Component /> </React.Fragment>

    React.Fragment 是 React 提供的語法糖,這時候 React 會生成一個空的父節點,語法上是正確的,但是不會增加額外的節點,也就不會增加額外的復雜度,

  3. 上文使用的 <><Component /></>React.Fragment 的語法糖,本質上來說和 React.Fragment 的效果是一樣的,只是更加省事一些,

    這個寫法應該是 v17 之后推出的新寫法,v16 無法兼容,

組件的提取

如何合理地對組件進行提取以達到更好的復用性,一直都是一個比較復雜的問題,根據 React 的官方建議來說,當組件滿足以下兩個特性,就代表這個部分可以被提取成單獨的組件:

  • 被多次重復使用的 UI 部分

    以 蘋果 為例,頁面上的一些 UI 部分都有一定程度的相似性,如:

    這幾個產品介紹的設計結構其實都是一樣的:

    |- 組件(有不同的背景色)
    |  |- header,商品名
    |  |- 商品賣點
    |  |- 讓客戶選擇查看更多,和選擇購買的超鏈接
    |  |- 商品圖片
    

    這種情況下,就可以選擇將這幾個部分組成一個組件,隨后只要通過 props 將對應的屬性傳進去,就可以有效的降低開發時間,提升開發效率,

  • 當組件有一定程度的復雜性

    以淘寶為例:

    如果直接進行開發的話,這個頁面實作起來絕對會有相當程度的復雜性,全都寫在一個組件之中對于頁面的更新(如出現不同的活動)、后期的維護也會形成一個極大的挑戰,

    但是如果將頁面根據功能進行簡單的拆分,那么按組件實作的難度就會降低不少:

    根據劃分的區域可以大致分為:

    |- page,整個頁面
    |  |- 導航欄,導航欄組件在其他頁面也可以復用,之后直接參考即可
    |  |- 搜索,搜索組件在其他頁面也可以復用,之后直接參考即可
    |  |- 首頁的主內容
    |  |  |- 這中間還可以繼續拆分,這里不再細講
    |  |- 側邊欄,側邊欄在其他頁面也可以復用,之后直接參考即可
    

    可以看到,這里粗粗劃分了四個組件,三個組件是可以在其他頁面直接復用,也就是說,在實作其他頁面時,就不需要再去重復實作已經實作過的功能,

總結

本章學習內容主要學習了一下 元素 和 組件,知識點包括:

  • 元素

    • 元素是 React 應用的最小組成部分

    • 元素被編譯完成后是一個 JavaScript 物件

    • 元素的渲染是通過 ReactDOM.render() 實作的

    • 元素具有不可變性,需要更新元素只能重新創建一個元素,并傳入 ReactDOM.render() 中重新渲染

    • React 實作了 Virtual DOM,會對比更新前后的虛擬 DOM 樹,并且只重新渲染更新過的 DOM 節點

  • 組件

    • 組件是可獨立復用的 UI 代碼段

    • 組件類似于 JavaScript 中的函式

    • 組件可以組合使用

    • 為了方便實作以及維護,可以提取組件,做更細顆粒的實作

下一章的學習將會注重與組件的狀態管理以及生命周期,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/289619.html

標籤:其他

上一篇:jQuery 樣式、屬性、元素操作、影片效果、尺寸位置操作

下一篇:【熬夜猛肝萬字博文】學妹問我怎么入門 Javascript,百般盤問下我終于決定貢獻出自己的 JavaScript入門筆記(四)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • vue移動端上拉加載

    可能做得過于簡單或者比較low,請各位大佬留情,一起探討技術 ......

    uj5u.com 2020-09-10 04:38:07 more
  • 優美網站首頁,頂部多層導航

    一個個人用的瀏覽器首頁,可以把一下常用的網站放在這里,平常打開會比較方便。 第一步,HTML代碼 <script src=https://www.cnblogs.com/szharf/p/"js/jquery-3.4.1.min.js"></script> <div id="navigate"> <ul> <li class="labels labels_1"> ......

    uj5u.com 2020-09-10 04:38:47 more
  • 頁面為要加<!DOCTYPE html>

    最近因為寫一個js函式,需要用到$(window).height(); 由于手寫demo的時候,過于自信,其實對前端方面的認識也不夠體系,用文本檔案直接敲出來的html代碼,第一行沒有加上<!DOCTYPE html> 導致了$(window).height();的結果直接是整個document的高 ......

    uj5u.com 2020-09-10 04:38:52 more
  • WordPress網站程式手動升級要做好資料備份

    WordPress博客網站程式在進行升級前,必須要做好網站資料的備份,這個問題良家佐言是遇見過的;在剛開始接觸WordPress博客程式的時候,因為升級問題和博客網站的修改的一些嘗試,良家佐言是吃盡了苦頭。因為購買的是西部數碼的空間和域名,每當佐言把自己的WordPress博客網站搞到一塌糊涂的時候 ......

    uj5u.com 2020-09-10 04:39:30 more
  • WordPress程式不能升級為5.4.2版本的原因

    WordPress是一款個人博客系統,受到英文博客愛好者和中文博客愛好者的追捧,并逐步演化成一款內容管理系統軟體;它是使用PHP語言和MySQL資料庫開發的,用戶可以在支持PHP和MySQL資料庫的服務器上使用自己的博客。每一次WordPress程式的更新,就會牽動無數WordPress愛好者的心, ......

    uj5u.com 2020-09-10 04:39:49 more
  • 使用CSS3的偽元素進行首字母下沉和首行改變樣式

    網頁中常見的一種效果,首字改變樣式或者首行改變樣式,效果如下圖。 代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, ......

    uj5u.com 2020-09-10 04:40:09 more
  • 關于a標簽的講解

    什么是a標簽? <a> 標簽定義超鏈接,用于從一個頁面鏈接到另一個頁面。 <a> 元素最重要的屬性是 href 屬性,它指定鏈接的目標。 a標簽的語法格式:<a href=https://www.cnblogs.com/summerxbc/p/"指定要跳轉的目標界面的鏈接">需要展示給用戶看見的內容</a> a標簽 在所有瀏覽器中,鏈接的默認外觀如下: 未被訪問的鏈接帶 ......

    uj5u.com 2020-09-10 04:40:11 more
  • 前端輪播圖

    在需要輪播的頁面是引入swiper.min.js和swiper.min.css swiper.min.js地址: 鏈接:https://pan.baidu.com/s/15Uh516YHa4CV3X-RyjEIWw 提取碼:4aks swiper.min.css地址 鏈接:https://pan.b ......

    uj5u.com 2020-09-10 04:40:13 more
  • 如何設定html中的背景圖片(全屏顯示,且不拉伸)

    1 <style>2 body{background-image:url(https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture); 3 background-size:cover;background ......

    uj5u.com 2020-09-10 04:40:16 more
  • Java學習——HTML詳解(上)

    HTML詳解 初識HTML Hyper Text Markup Language(超文本標記語言) 1 <!--DOCTYPE:告訴瀏覽器我們要使用什么規范--> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <!--meta 描述性的標簽,描述一些 ......

    uj5u.com 2020-09-10 04:40:33 more
最新发布
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 07:59:23 more
  • 生產事故-走近科學之消失的JWT

    入職多年,面對生產環境,盡管都是小心翼翼,慎之又慎,還是難免捅出簍子。輕則滿頭大汗,面紅耳赤。重則系統停擺,損失資金。每一個生產事故的背后,都是寶貴的經驗和教訓,都是專案成員的血淚史。為了更好地防范和遏制今后的各類事故,特開此專題,長期更新和記錄大大小小的各類事故。有些是親身經歷,有些是經人耳傳口授 ......

    uj5u.com 2023-04-18 07:55:04 more
  • 記錄--Canvas實作打飛字游戲

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。螢屏上,有一個白色的矩形框,里面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與螢屏上的單詞匹配,那么我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那么我就會輸 ......

    uj5u.com 2023-04-04 08:35:30 more
  • 了解 HTTP 看這一篇就夠

    在學習網路之前,了解它的歷史能夠幫助我們明白為何它會發展為如今這個樣子,引發探究網路的興趣。下面的這張圖片就展示了“互聯網”誕生至今的發展歷程。 ......

    uj5u.com 2023-03-16 11:00:15 more
  • 藍牙-低功耗中心設備

    //11.開啟藍牙配接器 openBluetoothAdapter //21.開始搜索藍牙設備 startBluetoothDevicesDiscovery //31.開啟監聽搜索藍牙設備 onBluetoothDeviceFound //30.停止監聽搜索藍牙設備 offBluetoothDevi ......

    uj5u.com 2023-03-15 09:06:45 more
  • canvas畫板(滑鼠和觸摸)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canves</title> <style> #canvas { cursor:url(../images/pen.png),crosshair; } #canvasdiv{ bo ......

    uj5u.com 2023-02-15 08:56:31 more
  • 手機端H5 實作自定義拍照界面

    手機端 H5 實作自定義拍照界面也可以使用 MediaDevices API 和 <video> 標簽來實作,和在桌面端做法基本一致。 首先,使用 MediaDevices.getUserMedia() 方法獲取攝像頭媒體流,并將其傳遞給 <video> 標簽進行渲染。 接著,使用 HTML 的 < ......

    uj5u.com 2023-01-12 07:58:22 more
  • 記錄--短視頻滑動播放在 H5 下的實作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 短視頻已經無數不在了,但是主體還是使用 app 來承載的。本文講述 H5 如何實作 app 的視頻滑動體驗。 無聲勝有聲,一圖頂百辯,且看下圖: 網址鏈接(需在微信或者手Q中瀏覽) 從上圖可以看到,我們主要實作的功能也是本文要講解的有: ......

    uj5u.com 2023-01-04 07:29:05 more
  • 一文讀懂 HTTP/1 HTTP/2 HTTP/3

    從 1989 年萬維網(www)誕生,HTTP(HyperText Transfer Protocol)經歷了眾多版本迭代,WebSocket 也在期間萌芽。1991 年 HTTP0.9 被發明。1996 年出現了 HTTP1.0。2015 年 HTTP2 正式發布。2020 年 HTTP3 或能正... ......

    uj5u.com 2022-12-24 06:56:02 more
  • 【HTML基礎篇002】HTML之form表單超詳解

    ??一、form表單是什么

    ??二、form表單的屬性

    ??三、input中的各種Type屬性值

    ??四、標簽 ......

    uj5u.com 2022-12-18 07:17:06 more