玩轉 React(四)- 創造一個新的 HTML 標簽

在第二篇文章 《新型前端開發方式》 中有說到 React 有很爽的一點就是給我們一種創造 HTML 標簽的能力,那么今天這篇文章就詳細講解下 React 是如何提供這種能力的,作為前端開發者如何來運用這種能力。

在第三篇文章 《JavaScript代碼里寫HTML一樣可以很優雅》 中介紹了 JavaScript 的擴展語法 JSX,相信大家已經知道了,所謂的創造新的 HTML 的能力,其實就是以極其類似 HTML 的 JSX 語法來使用基于 React 編寫的視圖層組件。所以說,要完成今天的任務,我們只需要搞清楚一個問題即可:如何基于 React 編寫視圖層組件。

內容摘要

  • 定義組件兩種方式:類繼承組件、函數式組件。
  • 類繼承組件有更豐富的特性,函數式組件書寫更簡潔,執行效率更高。
  • 組件名稱首字母要大寫。
  • 屬性是一個組件的外部輸入。
  • 屬性值可以通過 {} 設置任意的 JS 表達式。
  • 屬性是只讀的。
  • 屬性可以設置默認值。
  • 屬性可以設置類型,開發階段 React 會對屬性進行類型檢查。
  • 為組件所有屬性設置類型檢查是個好習慣,有助于協作開發。

通過內容摘要可以讓你快速了解本文內容是否對你有用,從而決定是否繼續閱讀,節省你的時間也是一件很有意義的事情。

定義組件的幾種姿勢

下面介紹一下在 React 中定義組件的幾種方式。

1. 類繼承

有過 Java 等面向對象開發經驗的同學一定很容易接受這種方式。ES6 為 JavaScript 增加了類和類繼承的特性。子類會繼承父類的“基因”(成員方法、屬性),如果父類是一個組件,那子類自然而然也是一個組件。

React 提供了 ComponentPureComponent 兩個父類,他們之間有一點點區別,我們在之后的文章中會詳細介紹,現在你可以將他們同等看待,暫且無須理會。

通過繼承自 React 提供的組件基類,我們可以這樣來創建一個組件:

import React, {Component} from 'react';class HelloMessage extends Component {render() {return <div>Hello world.</div>;}
}

通過類繼承的方式創建一個組件,就是這么簡單,只要繼承 Component 基類并實現 render 方法即可。然后就可以把 HelloMessage 當成一個新的“HTML 標簽”來用了,如下你可以把它渲染到頁面上:

ReactDOM.render(<HelloMessage />, document.querySelector('#root'));

你也可以用它來裝配其它組件,如:

import React, {Component} from 'react';class HelloMessageList extends React.Component {render() {return (<div><HelloMessage /><HelloMessage /><HelloMessage /></div>)}
}

當然,例子沒有任何實際意義,只是為了演示組件的定義及其用法。

演示代碼:https://codepen.io/Sarike/pen...

2. 函數式組件

顧名思義,函數式組件,就是以函數的形式來定義一個組件,如下所示:

import React from 'react';function HelloMessage() {return <div>Hello world.</div>;
}// 或者:const HelloMessage = () => <div>Hello world.</div>;

實際上就是只實現了類繼承方式中的 render 方法。

示例代碼:https://codepen.io/Sarike/pen...

類繼承 vs 函數式組件

這兩種定義組件的方式,在實際的開發中都經常會被用到,對大部分人來說類繼承的方式用得頻率會更高一些。

類繼承的方式,相較于函數式組件,雖然寫起來略繁瑣,但是它擁有更多的特性:

  • 內部狀態:state
  • 生命周期函數

函數式組件雖然沒有 state 和生命周期函數等特性,但是它有更簡潔的書寫方式,另外還有更好的性能,不用處理一些復雜的特性,執行效率當然高了。

現在你可以無需關心 state 和生命周期函數的具體作用,下一篇文章我會詳細講解,等你看完下一篇文章之后,至于選擇哪種方式的問題就很好解決了。在開發一個組件的時候,我是這樣來做的:當我一開始就知道這個組件會用到 state 或者生命周期函數時,毫無疑問直接使用類繼承的方式;如果一開始用不到這些特性也不確定未來會不會用到,那我就先用函數式組件,如果隨著業務的演進,組件需要應用這些特性的時候,我會再把它重構成類繼承的方式。這個重構非常簡單,只需要將原來的函數變成組件類的 render 方法即可。

另外,還有一點需要注意,不管那種方式,組件的名稱首字母必須為大寫。嚴格來說,是 JSX 要求用戶自定義的組件名首字母必須為大寫,如果是小寫字母開頭,那么 React 會將其當成內置的組件直接將其渲染成一個 html 標簽,從而不會正確渲染用戶自定義的組件。

如果你非要將組件名稱以小寫字母開頭,那你在以 JSX 語法使用之前也必須將其賦值為一個大寫字母開頭的變量,如下所示:

function helloMessage() {return <div>Hello world.</div>
}const HelloMessage = helloMessage;ReactDOM.render(<HelloMessage />, mountNode);

但這有事何必呢,純粹是沒事兒找事兒,大家在實際項目開發時,直接將組件名以大寫字母開頭即可。

屬性

上面說完了在 React 中兩種定義組件的方式。在上面的例子中,我們定義的組件都是靜態的,然而在實際的開發中,視圖層組件往往會進行頻繁更新,或者需要從后端 API 獲取動態數據在組件中展示。這就需要組件擁有接收外部輸入的能力。

屬性是組件的輸入

在第二篇文章 《新型前端開發方式》 中有說到 “視圖是數據的映射”,那么其中說的數據指的就是屬性。

如果把組件理解為一個函數,那么屬性就是這個函數的參數,函數的返回值就是呈現到頁面上的視圖。而且通過上面部分的學習,在 React 中組件確實可以以函數的形式來定義,而且函數的參數就是一個包含當前組件接收到的所有屬性的對象。

如下所示帶有屬性 name 的組件定義:

import React, {Component} from 'react';class HelloMessage extends Component {render() {return <div>Hello {this.props.name}.</div>;}
}

函數式:

import React from 'react';function HelloMessage(props) {return <div>Hello {props.name}.</div>;
}// 或者:const HelloMessage = props => <div>Hello {props.name}.</div>;

屬性的傳遞也跟 HTML 一樣(在本文的最后一部分會有各種類型屬性的詳細介紹),如下所示:

import React, {Component} from 'react';
import ReactDOM from 'react-dom';class HelloMessageList extends Component {render() {return (<div><HelloMessage name="Lucy" /><HelloMessage name="Tom" /><HelloMessage name="Jack" /></div>)}
}ReactDOM.render(<HelloMessageList />, document.querySelector('#root'));

這樣頁面上會展示出:

Hello Lucy.
Hello Tom.
Hello Jack.

示例代碼:https://codepen.io/Sarike/pen...

屬性必須為只讀的

屬性必須為只讀的,這一點非常重要,請嚴格遵守。對應到上面說到的,如果把組件理解為一個函數,那么這個函數必須為一個純函數(Pure function),在純函數中不能修改其參數,確定的輸入必須有確定的輸出。

雖然有些時候,你修改了組件的屬性,貌似也能正常工作。沒錯,因為 JavaScript 語言特性的原因,沒人能阻止你這么做。但是請先相信我,嚴格遵守這條規則不僅能讓你少踩很多坑,而且能讓你的應用穩定性更強、維護性更強。如果你直接修改組件的屬性,React 并不會感知到此修改,從而不會重新渲染組件,就導致了當前組件的視圖展示與數據不一致,但這個被修改的屬性會隨著下一次組件的渲染被生效到視圖上,而且這次渲染的時機是不確定的,不難想象,如果一個規模較大的項目里充滿了這種不確定性是多么痛苦的一件事情。總之,如果你隨意修改組件的屬性,會很容易讓你的應用充滿許多難以排查的 BUG。

默認屬性

通常情況下,我們需要為組件的屬性設為默認值。就像 HTML 標簽的屬性也有默認值一樣,例如 form 標簽的 method 屬性默認值是 GET,input 標簽的 type 屬性默認值是 text 一樣。

還是上面 HelloMessage 組件,如果需求是當不傳入 name 屬性時,默認展示 Hello World.,也就是說 name 屬性的默認值是 World。

一種很容易想到的做法:

<div>Hello {this.props.name || 'World'}.</div>

這樣確實可以解決當前這個需求,但是屬性可能還會是個 Object,也可能是個函數,你當然可以先判斷下這個屬性是否為 undefined 然后決定是否使用默認值,但是這樣會讓代碼顯得很不優雅,而且也會增加很多繁瑣的判斷邏輯。

因此,React 提供了相應的機制可以設置組件屬性的默認值,如下所示,你需要通過組件的靜態字段 defaultProps 來設置組件屬性的默認值。如下所示:

import React, {Component} from 'react';class HelloMessage extends Component {render() {return <div>Hello {this.props.name}.</div>;}
}
HelloMessage.defaultProps = {name: 'World'
}

這樣就可以了,<HelloMessage /> 這樣不為組件設置任何屬性,那么它就會在頁面上展示Hello World.

示例代碼:https://codepen.io/Sarike/pen...

屬性的類型及校驗

在開發較復雜的前端應用時,我們經常會遇到許多因為類型檢查導致的問題,例如上面的 HelloMessage 組件,我期望其 name 屬性只能是字符串類型的,你要是給我一個 Object,我是沒法正確展示的。為了在開發過程中盡快的發現這類問題,React 為組件添加了類型檢查的機制,你需要給組件設置靜態字段 propTypes 來設置組件各個屬性的類型檢查器。

import React, {Component} from 'react';
import PropTypes from 'prop-types';class HelloMessage extends Component {render() {return <div>Hello {this.props.name}.</div>;}
}
HelloMessage.defaultProps = {name: 'World'
}
HelloMessage.propTypes = {name: PropTypes.string
}

這樣在開發過程中 React 就能校驗組件接收到的屬性值是否符合指定的類型,如果校驗不通過,將會拋出警告。React 只會在開發模式下進行屬性類型檢查,當代碼進行生產發布后,為了減少額外的性能開銷,類型檢查將會被略過。

其實,為每一個組件編寫完善的屬性類型是一個非常好的習慣,這不僅能及時發現問題,更重要的是配合幾句簡單額注釋,這將成為該組件一份非常好的文檔,一個完善的組件應該具有良好的封裝性和易復用性,在一個協作開發的項目中,其他開發者需要引用你開發的組件時,只需要看一下組件的屬性列表,大致就可以了解如何來使用這個組件,省去了很多不必要的溝通。

下面是 React 提供的可用的數據類型檢查器。

  • PropTypes.array
  • PropTypes.bool
  • PropTypes.func
  • PropTypes.number
  • PropTypes.object
  • PropTypes.string
  • PropTypes.symbol
  • PropTypes.element 元素,其實就是 JSX 表達式,上一篇文章有說過 JSX 是 React.createElement 的語法糖,一個 JSX 表達式實際上會生成一個 JS 對象,在 React 中稱之為元素(Element)。
  • PropTypes.node 所有可以被渲染的數據類型,包括:數值, 字符串, 元素或者這些類型的數組。
  • PropTypes.instanceOf(Message) 某個類的實例
  • PropTypes.oneOf(['News', 'Photos']) 枚舉,屬性值必須為其中的某一個值。
  • PropTypes.oneOfType([PropTypes.string, PropTypes.number]) 類型枚舉,屬性必須為其中某一個類型。
  • PropTypes.arrayOf(PropTypes.number) 屬性為一個數組,且數組中的元素必須符合指定類型。
  • PropTypes.objectOf(PropTypes.number) 屬性為一個對象,且對象中的各個字段的值必須符合指定類型。
  • PropTypes.any 任何類型

如果你想指定某些屬性為必需屬性,你可以鏈式調動其 isRequired 來標識某個屬性對于當前組件來說是必需的。如果在使用組件時未指定則會拋出警告提醒。

另外你還可以通過一個函數自定義屬性驗證器,如果驗證不通過你需要返回一個 Error 實例,如下所示:

function(props, propName, componentName) {if (!/matchme/.test(props[propName])) {return new Error('Invalid prop `' + propName + '` supplied to' +' `' + componentName + '`. Validation failed.');}
}

設置組件的屬性值

上面咱們了解到組件的屬性有很多種類型,下面說一下各種類型的屬性是如何傳遞給組件的。其實很簡單,屬性的值可以用一對大括號 { } 來包圍,其中可以指定任意的 JavaScript 表達式。如下所示:

return (<Username="Tom"                            // 字符串age={18}                              // 數值isActivated={true}                    // 布爾值interests={['basketball', 'music']}   // 數組address={{ city: 'Beijing', road: 'BeiWuHuan' }} // 對象/>
)

展開操作符

你也可以用展開操作符 ... 將一個對象的所有字段展開,依次作為屬性傳遞給組件,上面的代碼等價于:

const userInfo = {name: 'Tom',age: 18,isActivated: true,interests: ['basketball', 'music'],address: { city: 'Beijing', road: 'BeiWuHuan' }
}
return <User {...userInfo} />

值為 true 的屬性的簡寫

如果是屬性類型為布爾值,且當前屬性值為 true 可以只寫屬性名,如下所示:

<inputdisabled     // 禁用該輸入框type="text"
/>

children 屬性

用戶自定義的組件內可以通過 this.props.children 來獲取一個特殊的屬性。該屬性與其它屬性的區別就是傳遞方式不同。

children 屬性的值是指一對閉合的 JSX 標簽中間的內容,如下所示:

<UserList><User name="Tom" /><User name="Lucy" />
</UserList>

那么在 UserList 內部可以通過 this.props.children 來獲取下面這個 JSX 片段:

<User name="Tom" />
<User name="Lucy" />

該示例中,獲取到的實際上是一個包含兩個 User 元素對象的數組。

總結

本文主要介紹了在 React 中組件的定義方式,以及幾個關鍵的注意事項。另外介紹了組件屬性的作用、屬性默認值、屬性類型校驗以及如何為組件傳遞屬性。

希望內容對大家有用,如有任何問題和建議可以給我留言,謝謝。

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

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

相關文章

mac word 設置語言_如何更改Mac的語言和區域設置

mac word 設置語言If you want to use your Mac in a different language, or you’re live in a different region, then you can change it in OS X. When you do, it’ll display everything in your preferred language, currency, date format, and more. 如果您想以其他語…

【Luogu3931】SAC E#1 - 一道難題 Tree

problem solution codes //樹形DP //f[u]:割掉u和u子樹中所有的葉子節點所需要的最小代價 #include<iostream> #include<vector>using namespace std; typedef long long LL; const int N (int)1e510, inf 1e9;int n, S;struct node{LL to, v;node(LL to, LL v):…

IT史上十大收購案

本文講的是IT史上十大收購案【IT168 資訊】據英國資訊網站V3報道&#xff0c;本周&#xff0c;業界中的大事件無疑是硬件巨頭Intel公司斥資76.8億美元全盤收購著名安全軟件公司McAfee。本次收購被看做是軟硬件領域的一次親密接觸&#xff0c;下面為大家盤點近年來IT領域中影響較…

飛利浦dicom_如何按計劃打開或關閉飛利浦色相燈

飛利浦dicomThe Philips Hue app can do a handful of cool stuff with your Hue lights, including the ability to schedule your lights to turn on and off at specific times throughout the day. Here’s how to set it up so that you never have to flip a switch ever…

Mono生命周期小實驗

今天在寫代碼的時候&#xff0c;遇到一個初始化順序問題&#xff0c;于是做了一個實驗&#xff0c;下面記錄結果&#xff1a; 情景&#xff1a; 1.在 腳本A中實例化 一個預制體&#xff0c;該預制體掛有腳本B 2.在 腳本A中&#xff0c;獲取實例化物體 身上的 腳本B&#xff0c;…

[讀書筆記]大型分布式網站架構設計與實踐.分布式緩存

前言&#xff1a;本書是對分布式系統架構涉及到的相關技術的一本科普書籍。由于很難作為開發參考&#xff0c;只能但求了解。所以通篇淺讀&#xff0c;對分布式系統進行大致的了解。因為寫的非常好&#xff0c;感覺非常有意思&#xff0c;自己也做不出總結。所謂的讀書筆記也就…

寧波保哥后院_如何拋出終極后院電影之夜

寧波保哥后院Most people have the basics of throwing a movie night down: you get a movie, you get snacks, you get comfortable, and boom, you’re done. When it comes to throwing a movie party in the backyard, however, things get a little trickier. Read on as…

大廠前端高頻面試問題與答案精選

近日&#xff0c;GitHub上一位名為木易楊&#xff08;yygmind&#xff09;的開發者&#xff0c;在 GitHub 中建了一個名為Advanced-Frontend/Daily-Interview-Question項目&#xff0c;該項目每天會更新一道前端大廠面試題&#xff0c;并邀請開發者在issue區中作答&#xff0c;…

Maven打包小技巧--持續更新

NO.1 跳過測試&#xff0c;打包指定環境 mvn clean install -Dmaven.test.skiptrue -P dev 其中&#xff1a;clean將target目錄中的文件移除&#xff1b; install根據配置文件&#xff0c;將本地工程打包成jar/war包&#xff1b; -Dmaven.test.skiptrue&#xff0c;打包時路過測…

OpenLayers學習筆記5——使用jQuery UI實現查詢并標注(UI篇)

近期事情非常多&#xff0c;老板給的壓力也非常大。經常出差&#xff0c;另外項目和個人研究還都要跟上&#xff0c;本月要交論文&#xff0c;還要寫專利&#xff0c;僅僅能抽時間來學習其它的東西了。 關于OpenLayers的在博客中不會寫太多詳細的實現&#xff08;網上有非常多o…

C++ 排序函數 sort(),qsort()的用法

想起來自己天天排序排序&#xff0c;冒泡啊&#xff0c;二分查找啊&#xff0c;結果在STL中就自帶了排序函數sort,qsort&#xff0c;總算把自己解脫了~ 所以自己總結了一下&#xff0c;首先看sort函數見下表&#xff1a; 函數名功能描述sort對給定區間所有元素進行排序stable_s…

.net core 實現默認圖片

web 上 如果圖片不存在 一般是打xx 這時候 一般都是會設置默認的圖片 代替 現在用中間件的方式實現統一設置 一次設置 全部作用 .net core 實現默認圖片 Startup 文件 app.UseDefaultImage(defaultImagePath: Configuration.GetSection("defaultImagePath").Va…

spring cloud config將配置存儲在數據庫中

轉載請標明出處&#xff1a; https://blog.csdn.net/forezp/...本文出自方志朋的博客 Spring Cloud Config Server最常見是將配置文件放在本地或者遠程Git倉庫&#xff0c;放在本地是將將所有的配置文件統一寫在Config Server工程目錄下&#xff0c;如果需要修改配置&#xff0…

VMware虛擬機VMware Authorization Service不能啟動問題

出現VMware Authorization Service不能啟動問題&#xff0c;注意要在安裝VMware Player時使用管理員權限轉載于:https://www.cnblogs.com/mingzhang/p/9152873.html

PHP替換回車換行的三種方法

一個小小的換行&#xff0c;其實在不同的平臺有著不同的實現&#xff0c;為什么要這樣&#xff0c;世界是多樣的&#xff01;本來在Unix世界換行用/n來代替換行&#xff0c;Windows為了體現不同&#xff0c;就用/r/n&#xff0c;更有意思的是&#xff0c;Mac中又用了/r。所以&a…

全球的weex資源都在這里

WeeX FAQ QQ: Weex大前端 516682889Weexbox&#xff1a; 943913583WeeX相關資源 weex官方資源 weex官網 Weex Market 已掛 : 一個提供 Weex 第三方組件的網站&#xff0c;您可以在這里找到你需要的 Weex 組件。 Playground : Playground在線&#xff0c;直接在線編寫代碼并預覽…

初步解決博客園代碼高亮的一個方案

今天我要推薦的是一個免費而且支持markdown語法的軟件——Typora 它有很多優點&#xff0c;支持多種類型代碼的高亮風格&#xff0c;方便的排版處理&#xff0c;支持Latex等&#xff0c;最重要的一點是真正做到了所見即所得ヽ(&#xff9f;?&#xff9f;)&#xff92;(&#x…

git工作原理

工作區&#xff1a;就是你在電腦里能看到的目錄。暫存區&#xff1a;英文叫stage, 或index。一般存放在 ".git目錄下" 下的index文件&#xff08;.git/index&#xff09;中&#xff0c;所以我們把暫存區有時也叫作索引&#xff08;index&#xff09;。版本庫&#xf…

【前端基礎進階】JS-Object 功能詳解

Object.assign(target,source1,source2,...)該方法主要用于對象的合并&#xff0c;將源對象source的所有可枚舉屬性合并到目標對象target上,此方法只拷貝源對象的自身屬性&#xff0c;不拷貝繼承的屬性。Object.assign方法實行的是淺拷貝&#xff0c;而不是深拷貝。也就是說&am…

解決“無法從套接字讀取更多數據”

重啟下Oralce服務即可。轉載于:https://www.cnblogs.com/fkeyta/p/9153297.html