玩转 React(四)- 创造一个新的 HTML 标签
在第二篇文章 《新型前端开发方式》 中有说到 React 有很爽的一点就是给我们一种创造 HTML 标签的能力,那么今天这篇文章就详细讲解下 React 是如何提供这种能力的,作为前端开发者如何来运用这种能力。 在第三篇文章 《JavaScript代码里写HTML一样可以很优雅》 中介绍了 JavaScript 的扩展语法 JSX,相信大家已经知道了,所谓的创造新的 HTML 的能力,其实就是以极其类似 HTML 的 JSX 语法来使用基于 React 编写的视图层组件。所以说,要完成今天的任务,我们只需要搞清楚一个问题即可:如何基于 React 编写视图层组件。 内容摘要
通过内容摘要可以让你快速了解本文内容是否对你有用,从而决定是否继续阅读,节省你的时间也是一件很有意义的事情。 定义组件的几种姿势下面介绍一下在 React 中定义组件的几种方式。 1. 类继承有过 Java 等面向对象开发经验的同学一定很容易接受这种方式。ES6 为 JavaScript 增加了类和类继承的特性。子类会继承父类的“基因”(成员方法、属性),如果父类是一个组件,那子类自然而然也是一个组件。 React 提供了 通过继承自 React 提供的组件基类,我们可以这样来创建一个组件: import React,{Component} from 'react'; class HelloMessage extends Component { render() { return <div>Hello world.</div>; } } 通过类继承的方式创建一个组件,就是这么简单,只要继承 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>; 实际上就是只实现了类继承方式中的 示例代码:https://codepen.io/Sarike/pen... 类继承 vs 函数式组件这两种定义组件的方式,在实际的开发中都经常会被用到,对大部分人来说类继承的方式用得频率会更高一些。 类继承的方式,相较于函数式组件,虽然写起来略繁琐,但是它拥有更多的特性:
函数式组件虽然没有 state 和生命周期函数等特性,但是它有更简洁的书写方式,另外还有更好的性能,不用处理一些复杂的特性,执行效率当然高了。 现在你可以无需关心 另外,还有一点需要注意,不管那种方式,组件的名称首字母必须为大写。严格来说,是 JSX 要求用户自定义的组件名首字母必须为大写,如果是小写字母开头,那么 React 会将其当成内置的组件直接将其渲染成一个 html 标签,从而不会正确渲染用户自定义的组件。 如果你非要将组件名称以小写字母开头,那你在以 JSX 语法使用之前也必须将其赋值为一个大写字母开头的变量,如下所示: function helloMessage() { return <div>Hello world.</div> } const HelloMessage = helloMessage; ReactDOM.render(<HelloMessage />,mountNode); 但这有事何必呢,纯粹是没事儿找事儿,大家在实际项目开发时,直接将组件名以大写字母开头即可。 属性上面说完了在 React 中两种定义组件的方式。在上面的例子中,我们定义的组件都是静态的,然而在实际的开发中,视图层组件往往会进行频繁更新,或者需要从后端 API 获取动态数据在组件中展示。这就需要组件拥有接收外部输入的能力。 属性是组件的输入在第二篇文章 《新型前端开发方式》 中有说到 “视图是数据的映射”,那么其中说的数据指的就是属性。 如果把组件理解为一个函数,那么属性就是这个函数的参数,函数的返回值就是呈现到页面上的视图。而且通过上面部分的学习,在 React 中组件确实可以以函数的形式来定义,而且函数的参数就是一个包含当前组件接收到的所有属性的对象。 如下所示带有属性 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 一样。 还是上面 一种很容易想到的做法: <div>Hello {this.props.name || 'World'}.</div> 这样确实可以解决当前这个需求,但是属性可能还会是个 Object,也可能是个函数,你当然可以先判断下这个属性是否为 因此,React 提供了相应的机制可以设置组件属性的默认值,如下所示,你需要通过组件的静态字段 import React,{Component} from 'react'; class HelloMessage extends Component { render() { return <div>Hello {this.props.name}.</div>; } } HelloMessage.defaultProps = { name: 'World' } 这样就可以了, 示例代码:https://codepen.io/Sarike/pen... 属性的类型及校验在开发较复杂的前端应用时,我们经常会遇到许多因为类型检查导致的问题,例如上面的 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 提供的可用的数据类型检查器。
如果你想指定某些属性为 另外你还可以通过一个函数自定义属性验证器,如果验证不通过你需要返回一个 Error 实例,如下所示: function(props,propName,componentName) { if (!/matchme/.test(props[propName])) { return new Error( 'Invalid prop `' + propName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } } 设置组件的属性值上面咱们了解到组件的属性有很多种类型,下面说一下各种类型的属性是如何传递给组件的。其实很简单,属性的值可以用一对大括号 return ( <User name="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} /> 值为
|