前端组件设计
2020-04-16 11:08:42 1 举报
AI智能生成
前端组件设计
作者其他创作
大纲/内容
设计原则
细粒度考量
原则上一个组件只专注一件事情,职责单一就可以最大可能性地复用组件。
但是过度单一职责的组件也可能会导致过度抽象,造成组件库的碎片化。
单一职责组件要建立在可复用的基础上,对于不可复用的单一职责组件我们仅仅作为独立组件的内部组件即可。
但是过度单一职责的组件也可能会导致过度抽象,造成组件库的碎片化。
单一职责组件要建立在可复用的基础上,对于不可复用的单一职责组件我们仅仅作为独立组件的内部组件即可。
通用性考量
通用性设计其实是一定意义上放弃对 DOM 的掌控,而将 DOM 结构的决定权转移给开发者。
通用性设计在将 DOM 结构决定权交给开发者的同时也保留了默认结构,在开发者没有显示自定义的时候默认使用默认渲染结构。
组件的形态(DOM结构)永远是千变万化的,但是其行为(逻辑)是固定的。
因此通用组件的秘诀之一就是将 DOM 结构的控制权交给开发者,组件只负责行为和最基本的 DOM 结构。
通用性设计在将 DOM 结构决定权交给开发者的同时也保留了默认结构,在开发者没有显示自定义的时候默认使用默认渲染结构。
组件的形态(DOM结构)永远是千变万化的,但是其行为(逻辑)是固定的。
因此通用组件的秘诀之一就是将 DOM 结构的控制权交给开发者,组件只负责行为和最基本的 DOM 结构。
例子
Antd整个 Select 的组件设计非常复杂,暴露了大量自定义渲染的接口,基本将所有的 DOM 结构控制权全部暴露给了开发者,其本身只负责底层逻辑和最基本的 DOM 结构。
如组件预留了 dropdownRender 来进行自定义渲染,其依赖的 rc-select组件中的代码如下:
if(dropdownRender){
return dropdownRender(menuNode, props)
}
如组件预留了 dropdownRender 来进行自定义渲染,其依赖的 rc-select组件中的代码如下:
if(dropdownRender){
return dropdownRender(menuNode, props)
}
Vue 中的插槽与插槽的后备内容就能做到将 DOM 结构决定权交给开发者同时保留默认结构
我的想法
组件要封装不变的部分,将变化部分的控制权转交给用户
不变的部分
基本功能/行为(逻辑)
最基本的 DOM 结构
变化的部分
表现层/形态
细节 DOM 结构
样式细节
交互细节
调用方式
性能考量
技术选型
css 方案
前端技术架构与工程脑图
中的 CSS 部分
中的 CSS 部分
此文章中选择的是 CSS-in-JS 方案
jsx 已经将 js 和 html 框定到一个组件中,但是css 依然处于分离状态,
这就导致了每次引用组件却还需要显示引入 css。
css-in-js 是正式彻底组件化的解决方案。
这就导致了每次引用组件却还需要显示引入 css。
css-in-js 是正式彻底组件化的解决方案。
js 方案
Typescript
快速启动一个组件库项目
打包
webpack
rollup
我没有实际用过,以后需要了再好好调查选择与实践
Scope Hoisting将模块构建在一个函数内的做法更适合类库的打包。
代码检测
ESLint
commit 规范
写好 commit message 不仅有助于他人 review, 还可以有效的输出 CHANGELOG, 对项目的管理实际至关重要
流行方案
Angular 团队的规范
结构
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
[optional body]
[optional footer(s)]
type
- build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
- ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
- docs: Documentation only changes
- feat: A new feature
- fix: A bug fix
- perf: A code change that improves performance
- refactor: A code change that neither fixes a bug nor adds a feature
- style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
- test: Adding missing tests or correcting existing tests
将规范集成到工具流中
规范人们不一定会遵守,因为人总会偷懒。
用 commitizen 将此规范集成到工具流中,每个 commit 就不得不遵循规范了。
用 commitizen 将此规范集成到工具流中,每个 commit 就不得不遵循规范了。
commitizen
具体使用以后再看
测试工具
业务开发中由于前端需求变动频繁的特性,导致前端对测试的要求并没有后端那么高,后端业务逻辑一旦定型变动很少,比较适合测试。
但是基础类库作为被反复依赖的模块和较为稳定的需求是必须做测试的。
但是基础类库作为被反复依赖的模块和较为稳定的需求是必须做测试的。
测试库选择
Jest
我没有实际用过,以后需要了再好好调查选择与实践
- 开箱即用,内置断言、测试覆盖率工具,如果你用 MoCha 那可得自己手动配置 n 多了
- 快照功能,Jest 可以利用其特有的快照测试功能,通过比对 UI 代码生成的快照文件
- 速度优势,Jest 的测试用例是并行执行的,而且只执行发生改变的文件所对应的测试,提升了测试速度
代码美化
prettier
解放人肉美化,同时利于不同人协作的风格一致
持续集成
travis-ci
好像实际用过,但是研究不多,以后需要研究了再好好调查与实践
解放人肉测试 lint,利于保证每次 push 的可靠程度
持续集成
Travis CI 提供的是持续集成服务(Continuous Integration,简称 CI)。
它绑定 Github 上面的项目,只要有新的代码,就会自动抓取。然后,提供一个运行环境,执行测试,完成构建,还能部署到服务器。
持续集成指的是只要代码有变更,就自动运行构建和测试,反馈运行结果。确保符合预期以后,再将新代码"集成"到主干。
持续集成的好处在于,每次代码的小幅变更,就能看到运行结果,从而不断累积小的变更,而不是在开发周期结束时,一下子合并一大块代码。
它绑定 Github 上面的项目,只要有新的代码,就会自动抓取。然后,提供一个运行环境,执行测试,完成构建,还能部署到服务器。
持续集成指的是只要代码有变更,就自动运行构建和测试,反馈运行结果。确保符合预期以后,再将新代码"集成"到主干。
持续集成的好处在于,每次代码的小幅变更,就能看到运行结果,从而不断累积小的变更,而不是在开发周期结束时,一下子合并一大块代码。
快速启动脚手架
作者最近开发了一款快速启动组件库开发的命令行工具 create-component
利用 create-component init MyComponent 来快速启动项目。
我们提供了丰富的可选配置,只要你做好技术选型后,根据提示去选择配置即可。
create-component 会自动根据配置生成脚手架,其灵感就来源于 vue-cli和 Angular-cli。
利用 create-component init MyComponent 来快速启动项目。
我们提供了丰富的可选配置,只要你做好技术选型后,根据提示去选择配置即可。
create-component 会自动根据配置生成脚手架,其灵感就来源于 vue-cli和 Angular-cli。
实战设计轮播图组件
研究组件原理
研究组件效果
研究组件在代码中如何使用
基础实现
基础 HTML 结构与 CSS
基础事件效果
无缝切换
目前业界的普遍做法是将图片首尾相连,例如图片1前面连接一个图片4,图片4后跟着一个图片1。
这就是为什么之前计算长度时要+2。
当我们移动图片4时就不会出现上述向左滑图片却向右滑的情况,因为真实情况是:
图片4 -- 滑动为 -> 伪图片1,也就是位置 5 变成了位置 6。
当动画结束之后,我们迅速把伪图片1的位置设置为真图片1。
这其实是个障眼法,也就是说动画执行过程中实际上是图片4到伪图片1的过程,当结束后我们偷偷把伪图片1换成真图片1。
因为两个图一模一样,所以这个转换的过程用户根本看不出来…
这就是为什么之前计算长度时要+2。
当我们移动图片4时就不会出现上述向左滑图片却向右滑的情况,因为真实情况是:
图片4 -- 滑动为 -> 伪图片1,也就是位置 5 变成了位置 6。
当动画结束之后,我们迅速把伪图片1的位置设置为真图片1。
这其实是个障眼法,也就是说动画执行过程中实际上是图片4到伪图片1的过程,当结束后我们偷偷把伪图片1换成真图片1。
因为两个图一模一样,所以这个转换的过程用户根本看不出来…
动画效果
CSS3
简单易用上手快,兼容性好
requestAnimationFrame API
灵活性更高,能实现 css3实现不了的动画,比如众多缓动动画
缓动函数我没有实际用过,以后需要了再好好调查与实践
想用 requestAnimationFrame 实现缓动效果就需要特定的缓动函数,下面就是典型的缓动函数
1type tweenFunction = (t: number, b: number, _c: number, d: number) => number
2const easeInOutQuad: tweenFunction = (t, b, _c, d) => {
3 const c = _c - b;
4 if ((t /= d / 2) < 1) {
5 return c / 2 * t * t + b;
6 } else {
7 return -c / 2 * ((--t) * (t - 2) - 1) + b;
8 }
9}
缓动函数接收四个参数,分别是:
t: 时间
b:初始位置
_c:结束的位置
d:速度
通过这个函数我们能算出每一帧轮播图所在的位置。
在获取每一帧对应的位置后,我们需要用requestAnimationFrame不断递归调用依次移动位置。我们不断调用animation函数是其触发函数体内的 this.setState({ translateX: tweenQueue[0], }) 来达到移动轮播图位置的目的,此时将这数组内的30个位置依次快速执行就是一个缓动动画效果。
1type tweenFunction = (t: number, b: number, _c: number, d: number) => number
2const easeInOutQuad: tweenFunction = (t, b, _c, d) => {
3 const c = _c - b;
4 if ((t /= d / 2) < 1) {
5 return c / 2 * t * t + b;
6 } else {
7 return -c / 2 * ((--t) * (t - 2) - 1) + b;
8 }
9}
缓动函数接收四个参数,分别是:
t: 时间
b:初始位置
_c:结束的位置
d:速度
通过这个函数我们能算出每一帧轮播图所在的位置。
在获取每一帧对应的位置后,我们需要用requestAnimationFrame不断递归调用依次移动位置。我们不断调用animation函数是其触发函数体内的 this.setState({ translateX: tweenQueue[0], }) 来达到移动轮播图位置的目的,此时将这数组内的30个位置依次快速执行就是一个缓动动画效果。
改进
通用性优化
提示点的自定义: 我的实现是一个小点,而 antd 是用的条,这个地方完全可以将 dom 结构的决定权交给开发者。
方向的自定义: 本轮播图只有水平方向的实现,其实也可以有纵向轮播
多张轮播: 除了单张轮播也可以多张轮播
方向的自定义: 本轮播图只有水平方向的实现,其实也可以有纵向轮播
多张轮播: 除了单张轮播也可以多张轮播
性能优化
我们的轮播图其实是有自动轮播功能的,但是很多时候页面并不在用户的可视页面中,
我们可以根据是否页面被隐藏来取消定时器终止自动播放:
监听 visibilitychange 事件
(浏览器标签页被隐藏或显示的时候会触发 visibilitychange 事件)
我们可以根据是否页面被隐藏来取消定时器终止自动播放:
监听 visibilitychange 事件
(浏览器标签页被隐藏或显示的时候会触发 visibilitychange 事件)
实际项目开发中最好还是使用成熟的开源组件,要有造轮子的能力和不造轮子的觉悟
0 条评论
下一页
为你推荐
查看更多