React組件設(shè)計(jì)

4年前 (2021-08-28)閱讀1072回復(fù)0
鈕秋荷
鈕秋荷
  • 管理員
  • 發(fā)消息
  • 注冊(cè)排名2382
  • 經(jīng)驗(yàn)值65
  • 級(jí)別管理員
  • 主題13
  • 回復(fù)0
樓主
印刷廠直印加工●彩頁(yè)1000張只需要69元●名片5元每盒-更多產(chǎn)品印刷報(bào)價(jià)?聯(lián)系電話:138-1621-1622(微信同號(hào))

  組件分類

  展示組件和容器組件

  展示組件 容器組件

  關(guān)注事物的展示 關(guān)注事物如何工作

  可能包含展示和容器組件ID嵌套樣式問題,并且一般會(huì)有DOM標(biāo)簽和css樣式 可能包含展示和容器組件,并且不會(huì)有DOM標(biāo)簽和css樣式

  常常允許通過(guò)this.props.children傳遞 提供數(shù)據(jù)和行為給容器組件或者展示組件

  對(duì)第三方?jīng)]有任何依賴ID嵌套樣式問題,比如store 或者 flux action 調(diào)用flux action 并且提供他們的回調(diào)給展示組件

  不要指定數(shù)據(jù)如何加載和變化 作為數(shù)據(jù)源ID嵌套樣式問題,通常采用較高階的組件,而不是自己寫,比如React Redux的connect(), Relay的createContainer(), Flux Utils的Container.create()

  僅通過(guò)屬性獲取數(shù)據(jù)和回調(diào)

  很少有自己的狀態(tài)ID嵌套樣式問題,即使有,也是自己的UI狀態(tài)

  除非他們需要的自己的狀態(tài)ID嵌套樣式問題,生命周期,或性能優(yōu)化才會(huì)被寫為功能組件

  下面是一個(gè)可能會(huì)經(jīng)常寫的組件,評(píng)論列表組件,數(shù)據(jù)交互和展示都放到了一個(gè)組件里面ID嵌套樣式問題。

  // CommentList.js

  class CommentList extends React.Component {

  constructor() {

  super();

  this.state = { comments: [] }

  componentDidMount() {

  $.ajax({

  url: "/my-comments.json",

  dataType: 'json',

  success: function(comments) {

  this.setState({comments: comments});

  }.bind(this)

  render() {

021yin.com ments.map(renderComment)} /ul;

  renderComment({body, author}) {

  return li{body}—{author}/li;

  我們對(duì)上面的組件進(jìn)行拆分,把他拆分成容器組件 CommentListContainer.js 和展示組件 CommentListID嵌套樣式問題。

  // CommentListContainer.js

  class CommentListContainer extends React.Component {

  constructor() {

  super();

  this.state = { comments: [] }

  componentDidMount() {

  $.ajax({

  url: "/my-comments.json",

  dataType: 'json',

  success: function(comments) {

  this.setState({comments: comments});

  }.bind(this)

  render() {

021yin.com ments} /;

  // CommentList.js

  class CommentList extends React.Component {

  constructor(props) {

  super(props);

  render() {

021yin.com ments.map(renderComment)} /ul;

  renderComment({body, author}) {

  return li{body}—{author}/li;

  優(yōu)勢(shì):

  展示和容器更好的分離ID嵌套樣式問題,更好的理解應(yīng)用程序和UI

  重用性高ID嵌套樣式問題,展示組件可以用于多個(gè)不同的state數(shù)據(jù)源

  展示組件就是你的調(diào)色板ID嵌套樣式問題,可以把他們放到單獨(dú)的頁(yè)面,在不影響應(yīng)用程序的情況下,讓設(shè)計(jì)師調(diào)整UI

  迫使你分離標(biāo)簽ID嵌套樣式問題,達(dá)到更高的可用性

  有狀態(tài)組件和無(wú)狀態(tài)組件

  下面是一個(gè)最簡(jiǎn)單的無(wú)狀態(tài)組件的例子:

  function HelloComponent(props, /* context */) {

  return divHello {props.name}/div

  ReactDOM.render(HelloComponent name="Sebastian" /, mountNode)

  可以看到,原本需要寫“類”定義(React.createClass 或者 class YourComponent extends React.Component)來(lái)創(chuàng)建自己組件的定義(有狀態(tài)組件),現(xiàn)在被精簡(jiǎn)成了只寫一個(gè) render 函數(shù)ID嵌套樣式問題。更值得一提的是,由于僅僅是一個(gè)無(wú)狀態(tài)函數(shù),React 在渲染的時(shí)候也省掉了將“組件類” 實(shí)例化的過(guò)程。

  結(jié)合 ES6 的解構(gòu)賦值,可以讓代碼更精簡(jiǎn)ID嵌套樣式問題。例如下面這個(gè) Input 組件:

  function Input({ label, name, value, ...props }, { defaultTheme }) {

  const { theme, autoFocus, ...rootProps } = props

  return (

  label

  htmlFor={name}

  children={label || defaultLabel}

  {...rootProps}

  input

  name={name}

  type="text"

  value={value || ''}

  theme={theme || defaultTheme}

  {...props}

  Input.contextTypes = {defaultTheme: React.PropTypes.object};

  無(wú)狀態(tài)組件不像上述兩種方法在調(diào)用時(shí)會(huì)創(chuàng)建新實(shí)例,它創(chuàng)建時(shí)始終保持了一個(gè)實(shí)例,避免了不必要的檢查和內(nèi)存分配,做到了內(nèi)部?jī)?yōu)化ID嵌套樣式問題。

  無(wú)狀態(tài)組件不支持 "ref"

  高階組件

  高階組件通過(guò)函數(shù)和閉包,改變已有組件的行為,本質(zhì)上就是 Decorator 模式在 React 的一種實(shí)現(xiàn)ID嵌套樣式問題。

  當(dāng)寫著寫著無(wú)狀態(tài)組件的時(shí)候ID嵌套樣式問題,有一天忽然發(fā)現(xiàn)需要狀態(tài)處理了,那么無(wú)需徹底返工:)

  往往我們需要狀態(tài)的時(shí)候,這個(gè)需求是可以重用的ID嵌套樣式問題。

  高階組件加無(wú)狀態(tài)組件,則大大增強(qiáng)了整個(gè)代碼的可測(cè)試性和可維護(hù)性ID嵌套樣式問題。同時(shí)不斷“誘使”我們寫出組合性更好的代碼。

  高階函數(shù)

  function welcome() {

  let username = localStorage.getItem('username');

  console.log('welcome ' + username);

  function goodbey() {

  let username = localStorage.getItem('username');

  console.log('goodbey ' + username);

  welcome();

  goodbey();

  我們發(fā)現(xiàn)兩個(gè)函數(shù)有一句代碼是一樣的,這叫冗余唉ID嵌套樣式問題。(平時(shí)可能會(huì)有一大段代碼的冗余)。

  下面我們要寫一個(gè)中間函數(shù),讀取username,他來(lái)負(fù)責(zé)把username傳遞給兩個(gè)函數(shù)ID嵌套樣式問題。

  function welcome(username) {

  console.log('welcome ' + username);

  function goodbey(username) {

  console.log('goodbey ' + username);

  function wrapWithUsername(wrappedFunc) {

  let newFunc = () = {

  let username = localStorage.getItem('username');

  wrappedFunc(username);

  return newFunc;

  welcome = wrapWithUsername(welcome);

  goodbey = wrapWithUsername(goodbey);

  welcome();

  goodbey();

  好了,我們里面的 wrapWithUsername 函數(shù)就是一個(gè)“高階函數(shù)”ID嵌套樣式問題。

  他做了什么?他幫我們處理了 username,傳遞給目標(biāo)函數(shù)ID嵌套樣式問題。我們調(diào)用最終的函數(shù) welcome的時(shí)候,根本不用關(guān)心 username是怎么來(lái)的。

  舉一反三的高階組件

  下面是兩個(gè)冗余的組件ID嵌套樣式問題。

  import React, {Component} from 'react'

  class Welcome extends Component {

  constructor(props) {

  super(props);

  this.state = {

  username: ''

  componentWillMount() {

  let username = localStorage.getItem('username');

  this.setState({

  username: username

  render() {

  return (

  divwelcome {this.state.username}/div

  export default Welcome;

  import React, {Component} from 'react'

  class Goodbye extends Component {

  constructor(props) {

  super(props);

  this.state = {

  username: ''

  componentWillMount() {

  let username = localStorage.getItem('username');

  this.setState({

  username: username

  render() {

  return (

  divgoodbye {this.state.username}/div

  export default Goodbye;

  我們可以通過(guò)剛剛高階函數(shù)的思想來(lái)創(chuàng)建一個(gè)中間組件,也就是我們說(shuō)的高階組件ID嵌套樣式問題。

  import React, {Component} from 'react'

  export default (WrappedComponent) = {

  class NewComponent extends Component {

  constructor() {

  super();

  this.state = {

  username: ''

  componentWillMount() {

  let username = localStorage.getItem('username');

  this.setState({

  username: username

  render() {

  return WrappedComponent username={this.state.username}/

  return NewComponent

  import React, {Component} from 'react';

  import wrapWithUsername from 'wrapWithUsername';

  class Welcome extends Component {

  render() {

  return (

  divwelcome {this.props.username}/div

  Welcome = wrapWithUsername(Welcome);

  export default Welcome;

  import React, {Component} from 'react';

  import wrapWithUsername from 'wrapWithUsername';

  class Goodbye extends Component {

  render() {

  return (

  divgoodbye {this.props.username}/div

  Goodbye = wrapWithUsername(Goodbye);

  export default Goodbye;

  看到?jīng)]有,高階組件就是把 username 通過(guò) props 傳遞給目標(biāo)組件了ID嵌套樣式問題。目標(biāo)組件只管從 props里面拿來(lái)用就好了。

  為了代碼的復(fù)用性,我們應(yīng)該盡量減少代碼的冗余ID嵌套樣式問題。

  提取共享的state,如果有兩個(gè)組件都需要加載同樣的數(shù)據(jù),那么他們會(huì)有相同的 componentDidMount 函數(shù)ID嵌套樣式問題。

  找出重復(fù)的代碼,每個(gè)組件中constructor 和 componentDidMount都干著同樣的事情,另外,在數(shù)據(jù)拉取時(shí)都會(huì)顯示Loading... 文案,那么我們應(yīng)該思考如何使用高階組件來(lái)提取這些方法ID嵌套樣式問題。

  遷移重復(fù)的代碼到高階組件

  包裹組件ID嵌套樣式問題,并且使用props替換state

  盡可能地簡(jiǎn)化

  組件開發(fā)基本思想

  單功能原則

  使用react時(shí),組件或容器的代碼在根本上必須只負(fù)責(zé)一塊UI功能ID嵌套樣式問題。

  讓組件保持簡(jiǎn)單

  如果組件根本不需要狀態(tài),那么就使用函數(shù)定義的無(wú)狀態(tài)組件ID嵌套樣式問題。

  從性能上來(lái)說(shuō),函數(shù)定義的無(wú)狀態(tài)組件 ES6 class 定義的組件 通過(guò) React.createClass() 定義的組件ID嵌套樣式問題。

  僅傳遞組件所需要的屬性ID嵌套樣式問題。只有當(dāng)屬性列表太長(zhǎng)時(shí),才使用{...this.props}進(jìn)行傳遞。

  如果組件里面有太多的判斷邏輯(if-else語(yǔ)句)通常意味著這個(gè)組件需要被拆分成更細(xì)的組件或模塊ID嵌套樣式問題。

  使用明確的命名能夠讓開發(fā)者明白它的功能,有助于組件復(fù)用ID嵌套樣式問題。

  基本準(zhǔn)則

  在shouldComponentUpdate中避免不必要的檢查.

  盡量使用不可變數(shù)據(jù)類型(Immutable).

  編寫針對(duì)產(chǎn)品環(huán)境的打包配置(Production Build).

  通過(guò)Chrome Timeline來(lái)記錄組件所耗費(fèi)的資源.

  在componentWillMount或者componentDidMount里面通過(guò)setTimeOut或者requestAnimationFram來(lái)延遲執(zhí)行那些需要大量計(jì)算的任務(wù).

  組件開發(fā)技巧

  form表單里的受控組件和不受控組件

  受控組件

  在大多數(shù)情況下,我們推薦使用受控組件來(lái)實(shí)現(xiàn)表單ID嵌套樣式問題。在受控組件中,表單數(shù)據(jù)由 React 組件負(fù)責(zé)處理。下面是一個(gè)典型的受控組建。

  class NameForm extends React.Component {

  constructor(props) {

  super(props);

  this.state = {value: ''};

  this.handleChange = this.handleChange.bind(this);

  this.handleSubmit = this.handleSubmit.bind(this);

  handleChange(event) {

  this.setState({value: event.target.value});

  handleSubmit(event) {

  alert('A name was submitted: ' + this.state.value);

  event.preventDefault();

  render() {

  return (

  form onSubmit={this.handleSubmit}

  label

  input type="text" value={this.state.value} onChange={this.handleChange} /

  /label

  input type="submit" value="Submit" /

  /form

  設(shè)置表單元素的value屬性之后,其顯示值將由this.state.value決定,以滿足React狀態(tài)的同一數(shù)據(jù)理念I(lǐng)D嵌套樣式問題。每次鍵盤敲擊之后會(huì)執(zhí)行handleChange方法以更新React狀態(tài),顯示值也將隨著用戶的輸入改變。

  對(duì)于受控組件來(lái)說(shuō),每一次 state(狀態(tài))變化都會(huì)伴有相關(guān)聯(lián)的處理函數(shù)ID嵌套樣式問題。這使得可以直接修改或驗(yàn)證用戶的輸入和提交表單。

  不受控組件

  因?yàn)椴皇芸亟M件的數(shù)據(jù)來(lái)源是 DOM 元素,當(dāng)使用不受控組件時(shí)很容易實(shí)現(xiàn) React 代碼與非 React 代碼的集成ID嵌套樣式問題。如果你希望的是快速開發(fā)、不要求代碼質(zhì)量,不受控組件可以一定程度上減少代碼量。否則。你應(yīng)該使用受控組件。

  一般情況下不受控組件我們使用ref來(lái)獲取DOM元素進(jìn)行操作ID嵌套樣式問題。

  class NameForm extends React.Component {

  constructor(props) {

  super(props);

  this.handleSubmit = this.handleSubmit.bind(this);

  handleSubmit(event) {

  alert('A name was submitted: ' + this.input.value);

  event.preventDefault();

  render() {

  return (

  form onSubmit={this.handleSubmit}

  label

  Name:

  input type="text" ref={(input) = this.input = input} /

  /label

  input type="submit" value="Submit" /

  /form

  組件條件判斷

  三元函數(shù)組件判斷渲染

  const sampleComponent = () = {

  return isTrue ? True!

: falseID嵌套樣式問題!

  使用表達(dá)式替換不必要的三元函數(shù)

  const sampleComponent = () = {

  return isTrue ? True!

: none/

  const sampleComponent = () = {

  return isTrue True!

  需要注意的是如果isTrue 為 0 ,其實(shí)會(huì)轉(zhuǎn)換成 false,但是在頁(yè)面中顯示的時(shí)候,還是會(huì)返回0顯示到頁(yè)面中ID嵌套樣式問題。

  多重嵌套判斷

  // 問題代碼

  const sampleComponent = () = {

  return (

  div

  {flag flag2 !flag3

  ? flag4

  ? Blah

  : flag5

  ? Meh

  : Herp

  : Derp

  /div

  解決方案:

  最佳方案: 將邏輯移到子組件內(nèi)部

  使用IIFE(Immediately-Invoked Function Expression 立即執(zhí)行函數(shù))

  滿足條件的時(shí)候使用return強(qiáng)制跳出函數(shù)

  const sampleComponent = () = {

  const basicCondition = flag flag2 !flag3;

  if (!basicCondition) return Derp

  if (flag4) return Blah

  if (flag5) return Meh

  return Herp

  setState異步性

  在某些情況下,React框架出于性能優(yōu)化考慮,可能會(huì)將多次state更新合并成一次更新ID嵌套樣式問題。正因?yàn)槿绱?,setState實(shí)際上是一個(gè)異步的函數(shù)。 如果在調(diào)用setState()函數(shù)之后嘗試去訪問this.state,你得到的可能還是setState()函數(shù)執(zhí)行之前的結(jié)果。

  但是,有一些行為也會(huì)阻止React框架本身對(duì)于多次state更新的合并,從而讓state的更新變得同步化ID嵌套樣式問題。 比如: eventListeners, Ajax, setTimeout 等等。

  React框架之所以在選擇在調(diào)用setState函數(shù)之后立即更新state而不是采用框架默認(rèn)的方式,即合并多次state更新為一次更新,是因?yàn)檫@些函數(shù)調(diào)用(fetch,setTimeout等瀏覽器層面的API調(diào)用)并不處于React框架的上下文中,React沒有辦法對(duì)其進(jìn)行控制ID嵌套樣式問題。React在此時(shí)采用的策略就是及時(shí)更新,確保在這些函數(shù)執(zhí)行之后的其他代碼能拿到正確的數(shù)據(jù)(即更新過(guò)的state)。

  解決setState函數(shù)異步的辦法?

  根據(jù)React官方文檔,setState函數(shù)實(shí)際上接收兩個(gè)參數(shù),其中第二個(gè)參數(shù)類型是一個(gè)函數(shù),作為setState函數(shù)執(zhí)行后的回調(diào)ID嵌套樣式問題。通過(guò)傳入回調(diào)函數(shù)的方式,React可以保證傳入的回調(diào)函數(shù)一定是在setState成功更新this.state之后再執(zhí)行。

  this.setState({count: 1}, () = {

  console.log(this.state.count); // 1

  React源碼中setState的實(shí)現(xiàn)

  ReactComponent.prototype.setState = function(partialState, callback) {

  invariant(

  typeof partialState === 'object' ||

  typeof partialState === 'function' ||

  partialState == null,

  'setState(...): takes an object of state variables to update or a ' +

  'function which returns an object of state variables.'

  this.updater.enqueueSetState(this, partialState);

  if (callback) {

  this.updater.enqueueCallback(this, callback, 'setState');

  updater的這兩個(gè)方法,和React底層的Virtual Dom(虛擬DOM樹)的diff算法有緊密的關(guān)系,所以真正決定同步還是異步的其實(shí)是Virtual DOM的diff算法ID嵌套樣式問題。

  依賴注入

  在React中,想做依賴注入(Dependency Injection)其實(shí)相當(dāng)簡(jiǎn)單ID嵌套樣式問題。可以通過(guò)props來(lái)進(jìn)行傳遞。但是,?cè)绻M件數(shù)量很多,并且組件嵌套層次很深的話,這種方式就不太合適。

  高階組件

  // inject.jsx

  var title = 'React Dependency Injection';

  export default function inject(Component) {

  return class Injector extends React.Component {

  render() {

  return (

  Component

  {...this.state}

  {...this.props}

  title={ title }

  // Title.jsx

  export default function Title(props) {

  return { props.title };

  // Header.jsx

  import inject from './inject.jsx';

  import Title from './Title.jsx';

  var EnhancedTitle = inject(Title);

  export default function Header() {

  return (

  header

  EnhancedTitle /

  /header

  context

  React v16.3.0 之前的 Context:

  var context = { title: 'React in patterns' };

  class App extends React.Component {

  getChildContext() {

  return context;

  App.childContextTypes = {

  title: PropTypes.string

  class Inject extends React.Component {

  render() {

  var title = this.context.title;

  Inject.contextTypes = {

  title: PropTypes.string

  之前的 Context 作為一個(gè)實(shí)驗(yàn)性質(zhì)的 API,直到 React v16.3.0 版本前都一直不被官方所提倡去使用,其主要原因就是因?yàn)樵谧咏M件中使用 Context 會(huì)破壞 React 應(yīng)用的分型架構(gòu)ID嵌套樣式問題。

  這里的分形架構(gòu)指的是從理想的 React 應(yīng)用的根組件樹中抽取的任意一部分都仍是一個(gè)可以直接運(yùn)行的子組件樹ID嵌套樣式問題。在這個(gè)子組件樹之上再包一層,就可以將它無(wú)縫地移植到任意一個(gè)其他的根組件樹中。

  但如果根組件樹中有任意一個(gè)組件使用了支持透?jìng)鞯?Context API,那么如果把包含了這個(gè)組件的子組件樹單獨(dú)拿出來(lái),因?yàn)槿鄙倭颂峁?Context 值的根組件樹,這時(shí)的這個(gè)子組件樹是無(wú)法直接運(yùn)行的ID嵌套樣式問題。

  并且他有一個(gè)致命缺陷:任何一個(gè)中間傳遞的組件shouldComponentUpdate 函數(shù)返回false,組件都不會(huì)得到更新ID嵌套樣式問題。

  新的Context Api

  新的Context Api 采用聲明式的寫法,并且可以透過(guò)shouldComponentUpdate 函數(shù)返回false的組件繼續(xù)向下傳播,以保證目標(biāo)組件一定可以接收到頂層組件 Context 值的更新,一舉解決了現(xiàn)有 Context API 的兩大弊端,也終于成為了 React 中的第一級(jí)(first-class) APIID嵌套樣式問題。

  新的 Context API 分為三個(gè)組成部分:

  React.createContext 用于初始化一個(gè) ContextID嵌套樣式問題。

  XXXContext.Provider作為頂層組件接收一個(gè)名為 value的 prop,可以接收任意需要被放入 Context 中的字符串,數(shù)字,甚至是函數(shù)ID嵌套樣式問題。

  XXXContext.Consumer作為目標(biāo)組件可以出現(xiàn)在組件樹的任意位置(在 Provider 之后),接收 children prop,這里的 children 必須是一個(gè)函數(shù)(context = ())用來(lái)接收從頂層傳來(lái)的 ContextID嵌套樣式問題。

  const ThemeContext = React.createContext('light');

  class App extends React.Component {

  render() {

  return (

  ThemeContext.Provider value="dark"

  Toolbar /

  /ThemeContext.Provider

  function Toolbar(props) {

  return (

  div

  ThemedButton /

  /div

  function ThemedButton(props) {

  return (

  ThemeContext.Consumer

  {theme = Button {...props} theme={theme} /}

  /ThemeContext.Consumer

  事件處理中的this指向問題

  class Switcher extends React.Component {

  constructor(props) {

  super(props);

  this.state = { name: 'React in patterns' };

  render() {

  return (

  button onClick={ this._handleButtonClick }

  click me

  /button

  _handleButtonClick() {

  console.log(`Button is clicked inside ${ this.state.name }`);

  // 將導(dǎo)致

  // Uncaught TypeError: Cannot read property 'state' of null

  我們可以通過(guò)下面三種方式簡(jiǎn)單實(shí)現(xiàn)this指向的綁定:

  在constructor 中事先綁定 this._buttonClick = this._handleButtonClick.bind(this);

  調(diào)用時(shí)使用箭頭函數(shù) button onClick={ () = this._buttonClick() }

  ES7中的綁定操作符 button onClick={ ::this._buttonClick() }

  給setState傳入回調(diào)函數(shù)

  setState() 不僅能接受一個(gè)對(duì)象,還能接受一個(gè)函數(shù)作為參數(shù)呢,該函數(shù)接受該組件前一刻的 state 以及當(dāng)前的 props 作為參數(shù),計(jì)算和返回下一刻的 stateID嵌套樣式問題。

  // assuming this.state.count === 0

  this.setState({count: this.state.count + 1});

  this.setState({count: this.state.count + 1});

  this.setState({count: this.state.count + 1});

  // this.state.count === 1, not 3

  this.setState((prevState, props) = ({

  count: prevState.count + props.increment

  // Passing object

  this.setState({ expanded: !this.state.expanded });

  // Passing function

  this.setState(prevState = ({ expanded: !prevState.expanded }));

  組件切換技巧

  import HomePage from './HomePage.jsx';

  import AboutPage from './AboutPage.jsx';

  import UserPage from './UserPage.jsx';

  import FourOhFourPage from './FourOhFourPage.jsx';

  const PAGES = {

  home: HomePage,

  about: AboutPage,

  user: UserPage

  const Page = (props) = {

  const Handler = PAGES[props.page] || FourOhFourPage;

  return Handler {...props} /

  React style

  組件分類

  基礎(chǔ)組件ID嵌套樣式問題, 布局組件, 排版組件

  給無(wú)狀態(tài)的純UI組件應(yīng)用樣式

  請(qǐng)保持樣式遠(yuǎn)離那些離不開state的組件. 比如路由, 視圖, 容器, 表單, 布局等等不應(yīng)該有任何的樣式或者css class出現(xiàn)在組件上. 相反, 這些復(fù)雜的業(yè)務(wù)組件應(yīng)該有一些帶有基本功能的無(wú)狀態(tài)UI組件組成.

  class SampleComponent extends Component {

  render() {

  return (

  form onSubmit={this.handleSubmit}

  Heading children='Sign In'/

  Input

  name='username'

  value={username}

  onChange={this.handleChange}/

  Input

  type='password'

  name='password'

  value={password}

  onChange={this.handleChange}/

  Button

  type='submit'

  children='Sign In'/

  /form

  // 表達(dá)組件(帶樣式)

  const Button = ({

  ...props

  const sx = {

  fontFamily: 'inherit',

  fontSize: 'inherit',

  fontWeight: 'bold',

  textDecoration: 'none',

  display: 'inline-block',

  margin: 0,

  paddingTop: 8,

  paddingBottom: 8,

  paddingLeft: 16,

  paddingRight: 16,

  border: 0,

  color: 'white',

  backgroundColor: 'blue',

  WebkitAppearance: 'none',

  MozAppearance: 'none'

  return (

  button {...props} style={sx}/

  樣式模塊(style module)

  一般來(lái)說(shuō), 在組件內(nèi)寫死(hard code)樣式應(yīng)該是要被避免的. 這些有可能被不同的UI組件分享的樣式應(yīng)該被分開放入對(duì)應(yīng)的模塊中.

  // 樣式模塊

  export const white = '#fff';

  export const black = '#111';

  export const blue = '#07c';

  export const colors = {

  white,

  black,

  blue

  export const space = [

  0,

  8,

  16,

  32,

  64

  const styles = {

  bold: 600,

  space,

  colors

  export default styles

  // button.jsx

  import React from 'react'

  import { bold, space, colors } from './styles'

  const Button = ({

  ...props

  const sx = {

  fontFamily: 'inherit',

  fontSize: 'inherit',

  fontWeight: bold,

  textDecoration: 'none',

  display: 'inline-block',

  margin: 0,

  paddingTop: space[1],

  paddingBottom: space[1],

  paddingLeft: space[2],

  paddingRight: space[2],

  border: 0,

  color: colors.white,

  backgroundColor: colors.blue,

  WebkitAppearance: 'none',

  MozAppearance: 'none'

  return (

  button {...props} style={sx}/

  樣式函數(shù)(Style Functions)

  // Modular powers of two scale

  const scale = [

  0,

  8,

  16,

  32,

  64

  // 通過(guò)這個(gè)函數(shù)去取得一部分的樣式

  const createScaledPropertyGetter = (scale) = (prop) = (x) = {

  return (typeof x === 'number' typeof scale[x] === 'number')

  ? {[prop]: scale[x]}

  : null

  const getScaledProperty = createScaledPropertyGetter(scale);

  export const getMargin = getScaledProperty('margin');

  export const getPadding = getScaledProperty('padding');

  // 樣式函數(shù)的用法

  const Box = ({

  m,

  p,

  ...props

  const sx = {

  ...getMargin(m),

  ...getPadding(p)

  return div {...props} style={sx}/

  // 組件用法.

  const Box = () = (

  div

  Box m={2} p={3}

  A box with 16px margin and 32px padding

  /Box

  /div

  常見小坑

  state不更新ID嵌套樣式問題?

  class SampleComponent extends Component {

  // constructor function (or getInitialState)

  constructor(props) {

  super(props);

  this.state = {

  flag: false,

  inputVal: props.inputValue

  render() {

  return div{this.state.inputVal AnotherComponent/}/div

  這樣做的危險(xiǎn)在于, 有可能組件的props發(fā)生了改變但是組件卻沒有被更新. 新的props的值不會(huì)被React認(rèn)為是更新的數(shù)據(jù)因?yàn)闃?gòu)造器constructor或者getInitialState方法在組件創(chuàng)建之后不會(huì)再次被調(diào)用了,因此組件的state不再會(huì)被更新ID嵌套樣式問題。 要記住, State的初始化只會(huì)在組件第一次初始化的時(shí)候發(fā)生。

  class SampleComponent extends Component {

  // constructor function (or getInitialState)

  constructor(props) {

  super(props);

  this.state = {

  flag: false

  render() {

  return div{this.props.inputValue AnotherComponent/}/div

  更干凈的render函數(shù)?

  更干凈的render函數(shù)? 這個(gè)概念可能會(huì)有點(diǎn)讓人疑惑.

  其實(shí)在這里干凈是指我們?cè)趕houldComponentUpdate這個(gè)生命周期函數(shù)里面去做淺比較, 從而避免不必要的渲染.

  class Table extends PureComponent {

  render() {

  return (

  div

  {this.props.items.map(i =

  Cell data={i} options={this.props.options || []}/

  /div

  這種寫法的問題在于{this.props.options || []} 這種寫法會(huì)導(dǎo)致所有的Cell都被重新渲染即使只有一個(gè)cell發(fā)生了改變. 為什么會(huì)發(fā)生這種事呢?

  仔細(xì)觀察你會(huì)發(fā)現(xiàn), options這個(gè)數(shù)組被傳到了Cell這個(gè)組件上, 一般情況下, 這不會(huì)導(dǎo)致什么問題. 因?yàn)槿绻衅渌腃ell組件, 組件會(huì)在有props發(fā)生改變的時(shí)候淺對(duì)比props并且跳過(guò)渲染(因?yàn)閷?duì)于其他Cell組件, props并沒有發(fā)生改變). 但是在這個(gè)例子里面, 當(dāng)options為null時(shí), 一個(gè)默認(rèn)的空數(shù)組就會(huì)被當(dāng)成Props傳到組件里面去. 事實(shí)上每次傳入的[]都相當(dāng)于創(chuàng)建了新的Array實(shí)例. 在JavaScript里面, 不同的實(shí)例是有不同的實(shí)體的, 所以淺比較在這種情況下總是會(huì)返回false, 然后組件就會(huì)被重新渲染. 因?yàn)閮蓚€(gè)實(shí)體不是同一個(gè)實(shí)體. 這就完全破壞了React對(duì)于我們組件渲染的優(yōu)化.

  const defaultval = []; // --- 也可以使用defaultProps

  class Table extends PureComponent {

  render() {

  return (

  div

  {this.props.items.map(i =

  Cell data={i} options={this.props.options || defaultval}/

  /div

  還是多次重新渲染

  class App extends PureComponent {

  render() {

  return MyInput

  onChange={e = this.props.update(e.target.value)}/;

  class App extends PureComponent {

  update(e) {

  this.props.update(e.target.value);

  render() {

  return MyInput onChange={this.update.bind(this)}/;

  在上面的兩個(gè)壞實(shí)踐中, 每次我們都會(huì)去創(chuàng)建一個(gè)新的函數(shù)實(shí)體. 和第一個(gè)例子類似, 新的函數(shù)實(shí)體會(huì)讓我們的淺比較返回false, 導(dǎo)致組件被重新渲染. 所以我們需要在更早的時(shí)候去bind我們的函數(shù).

  class App extends PureComponent {

  constructor(props) {

  super(props);

  this.update = this.update.bind(this);

  update(e) {

  this.props.update(e.target.value);

  render() {

  return MyInput onChange={this.update}/;

  命名

  引用命名

  React模塊名使用帕斯卡命名ID嵌套樣式問題,實(shí)例使用駱駝式命名

  // bad

  import reservationCard from './ReservationCard';

  // good

  import ReservationCard from './ReservationCard';

  // bad

  const ReservationItem = ReservationCard /;

  // good

  const reservationItem = ReservationCard /;

  高階模塊命名

  // bad

  export default function withFoo(WrappedComponent) {

  return function WithFoo(props) {

  return WrappedComponent {...props} foo /;

  // good

  export default function withFoo(WrappedComponent) {

  function WithFoo(props) {

  return WrappedComponent {...props} foo /;

  const wrappedComponentName = WrappedComponent.displayName

  || WrappedComponent.name

  || 'Component';

  WithFoo.displayName = `withFoo(${wrappedComponentName})`;

  return WithFoo;

  屬性命名

  避免使用DOM相關(guān)的屬性來(lái)用作其他的用途ID嵌套樣式問題。

  // bad

  MyComponent style="fancy" /

  // good

  MyComponent variant="fancy" /

  私有函數(shù)添加 _ 前綴?

  在React模塊中,不要給所謂的私有函數(shù)添加 _ 前綴,本質(zhì)上它并不是私有的ID嵌套樣式問題。

  為什么?_ 下劃線前綴在某些語(yǔ)言中通常被用來(lái)表示私有變量或者函數(shù)ID嵌套樣式問題。但是不像其他的一些語(yǔ)言,在JS中沒有原生支持所謂的私有變量,所有的變量函數(shù)都是共有的。盡管你的意圖是使它私有化,在之前加上下劃線并不會(huì)使這些變量私有化,并且所有的屬性(包括有下劃線前綴及沒有前綴的)都應(yīng)該被視為是共有的。

  Ordering React 模塊生命周期

  class extends React.Component 的生命周期函數(shù):

  可選的 static 方法

  constructor 構(gòu)造函數(shù)

  getChildContext 獲取子元素內(nèi)容

  componentWillMount 模塊渲染前

  componentDidMount 模塊渲染后

  componentWillReceiveProps 模塊將接受新的數(shù)據(jù)

  shouldComponentUpdate 判斷模塊需不需要重新渲染

  componentWillUpdate 上面的方法返回 trueID嵌套樣式問題, 模塊將重新渲染

  componentDidUpdate 模塊渲染結(jié)束

  componentWillUnmount 模塊將從DOM中清除, 做一些清理任務(wù)

  點(diǎn)擊回調(diào)或者事件處理器 如 onClickSubmit() 或 onChangeDescription()

  render 里的 getter 方法 如 getSelectReason() 或 getFooterContent()

  可選的 render 方法 如 renderNavigation() 或 renderProfilePicture()

  render render() 方法

  如何定義 propTypes, defaultProps, contextTypes, 等等其他屬性...

  import React from 'react';

  import PropTypes from 'prop-types';

  const propTypes = {

  id: PropTypes.number.isRequired,

  url: PropTypes.string.isRequired,

  text: PropTypes.string,

  const defaultProps = {

  text: 'Hello World',

  class Link extends React.Component {

  static methodsAreOk() {

  return true;

  render() {

  return a href={this.props.url} data-id={this.props.id}{this.props.text}/a;

  Link.propTypes = propTypes;

  Link.defaultProps = defaultProps;

  export default Link;

0
0
收藏0
回帖

React組件設(shè)計(jì) 期待您的回復(fù)!

取消
載入表情清單……
載入顏色清單……
插入網(wǎng)絡(luò)圖片

取消確定

圖片上傳中
編輯器信息
提示信息