05.组件化
组件化开发
组件化开发的优势
- 利于团队协作开发
- 利于组件复用
- 利于SPA单页面应用开发
- ……
主流组件的对比
Vue中的组件化开发:
http://fivedodo.com/upload/html/vuejs3/guide/migration/functional-components.html
- 全局组件和局部组件
- 函数组件(functional)和类组件(Vue3不具备functional函数组件」)
React中的组件化开发:
没有明确全局和局部的概念(可以理解为都是局部组件,不过可以把组件注册到React上,这样每个组件中只要导入React即可使用)
- 函数组件
- 类组件
- Hooks组件:在函数组件中使用React Hooks函数
React函数组件
创建
两个情况都是创建了一个函数组件:
- 在SRC目录中,创建一个 xxx.jsx 的文件,就创建一个组件了;
- 我们在此文件中,创建一个函数,让函数返回JSX视图(或者JSX元素、virtualDOM虚拟DOM对象)
例子:
1 | // views/FunctionComponent.jsx |
调用
在文件中,可以基于ES6Module规范导入创建的组件(导入时可以忽略.jsx后缀名),然后像写标签一样调用。
1 | //两种调用方式 |
传递属性的规则:
调用组件时,可以给被调用的组件设置(传递)各种各样的属性,例如:
<DemoOne title="我是标题" x={10} data={[100, 200]} className="box" style={{ fontSize: '20px' }} />
若设置的属性值不是字符串格式,则需要基于
{}语法
进行嵌套。
命名
组件名,一般都采用
PascalCase
(大驼峰命名法)
渲染过程
由上两章可知组件的渲染过程是
- 基于
babel-preset-react-app
把调用的组件转换为createElement
格式
1 | React.createElement(DemoOne, { |
- 执行
createElement
方法创建一个virtualDOM
对象
1 | { |
- 基于
root.render
把virtualDOM
变为真实的DOM
type值不再是一个字符串,而是一个函数了,此时:
- 函数执行→
DemoOne()
- 把
virtualDOM
中的props
,作为实参传递给函数DemoOne()
→DemoOne(props)
- 接收函数执行的返回结果(也就是当前组件的virtualDOM对象)
- 最后由render函数把组件返回的虚拟DOM变为真实DOM,插入到id=root的容器中!!
Props的处理
- 调用组件,传递进来的属性(Props)是“只读”的,原理是:props对象被冻结了
Object.freeze()
(见下文解释)
1 | //测试 |
回顾js知识点:关于对象的规则设置(了解可跳过)
阮老师单推人😘
标准库 - 属性描述对象 - 《阮一峰 JavaScript 教程》 - 书栈网 · BookStack
控制对象状态的一节,讲述了以下内容:
扩展(弱):
Object.*preventExtensions*()
:使得一个对象无法再添加新的属性Object.isExtensible(obj)
:检测是否可扩展(返回bool值,下面都是)密封(较强)
Object.seal(obj)
:密封对象,使得一个对象既无法添加新属性,也无法删除旧属性。Object.isSealed(obj)
:检测是否被密封
- 被密封的对象:可以修改成员的值,但也不能删、不能新增、不能劫持
Object.defineProperty
冻结(强)
Object.freeze(obj)
:冻结对象,对象实际上变成了常量。Object.isFrozen(obj)
检测是否被冻结
- 被冻结的对象:不能修改成员值、不能新增成员、不能删除现有成员、不能给成员做劫持
Object.defineProperty
局限性:可以通过改变原型对象,来为对象增加属性。(详见阮一峰教程)
劫持:(也在”属性描述对象’’同一章中)
Object.defineProperty()
方法允许通过属性描述对象,定义或修改一个属性,然后返回修改后的对象()👏tips:
- 被设置不可扩展的对象:除了不能新增成员、其余的操作都可以处理!!
- 被密封的对象,也是不可扩展的!!
- 同理,被冻结的对象,即是不可扩展的,也是密封的!!
- 通过三个方法的互相调用可以部分解封,但如果要加入新的属性那就只能创建一个新对象了
💬Ps:
- 越底层越要看js基础,看来八股并不是没用。
- 是不是这部分我应该单独开一章🤦♂️🤦♀️
然后让我们举一个传Props的例子:
1 | // src\index.jsx |
1 | // src\views\DemoOne.jsx |
属性的使用逻辑
- 父组件
index.jsx
调用子组件DemoOne.jsx
的时候,可以基于属性,把不同的信息传递给子组件; - 子组件接收相应的属性值,呈现出不同的效果,让组件的复用性更强!!
对于传递进来的属性规则校验
- 原理:通过把函数当做对象,设置静态的私有属性方法,来给其设置属性的校验规则
- 可以设置默认值(见上面代码):
1 | /* 函数组件.defaultProps = { |
设置其它规则,例如:数据值格式、是否必传… (依赖于官方的一个插件:
prop-types
)https://github.com/facebook/prop-types
这里就是用这个插件来进行类型检验,让原本动态的js能像c++、java这样的静态语言可以不用担心类型转换出现的问题。
1
2
3
4
5
6
7
8
9import PropTypes from 'prop-types';
函数组件.propTypes = {
title: PropTypes.string.isRequired,// 类型是字符串、必传
x: PropTypes.number,// 类型是数字
y: PropTypes.oneOfType([
PropTypes.number,
PropTypes.bool,
])// 多种校验规则中的一个
};传递进来的属性,首先会经历规则的校验,不管校验成功还是失败,最后都会把属性给形参props,只不过如果不符合设定的规则,控制台会抛出警告错误{不影响属性值的获取}!!
🤔Tips:
使用 TypeScript(TS)的话,通常不需要使用 prop-types 库。
之前写python也有类似的库,看样子动态语言都得被静态束缚。
(那python是不是强类型呢😅,与前端无关,可以忽略)
修改props的规则
- 把props中的某个属性赋值给其他内容(例如:变量、状态…)
- 我们不直接操作props.xxx=xxx,但是我们可以修改变量/状态值!!
待规划文字(可以先不读,我复习时会把内容规整进入正文)
- 在JSX元素渲染的时候,如果type是函数,则把函数执行!
- 会把解析出来的props「含children」,传递给函数
单闭合和双闭合调用的区别「插槽」
React.Children
props是只读的「被冻结」
- props的规则设置
设置默认值
设置属性规则「基于prop-types插件」
- 属性和插槽都可以让组件具备更强的复用性
- 会把解析出来的props「含children」,传递给函数
1 | // index.jsx |
1 | // views/FunctionComponent.jsx |