文档章节

【天赢金创】我是如何看待React 组件开发

第三方支付接口
 第三方支付接口
发布于 2015/09/24 09:18
字数 2166
阅读 77
收藏 2

从 auto-ellipsis 看 React 组件开发

auto-ellipsis 是一个用于解决文本超长溢出截断并加 ... 的 React 组件。

关于 React

随着 React 的火热,随之而来的负面消息也变得更多。之前网上就有人批评说 React 的鼓吹者很多,甚至被定性为『无脑』,这就如同当年批评 jQuery 一样。

React 对我而言,不仅仅是一个前端 View 库,它对我的影响主要有以下几方面:

  1. 拥抱前沿技术 - babel 让我在项目中可以提前使用 ES2015+;webpack-dev-server 和 react-hot-loader 让我的开发过程无比顺畅;webpack 让我的打包上线变得极其方便;redux 让我能更好的管理应用状态。也许你会说这些和 React 没有绝对关系,但事实上,正是 React 的巨大的生态圈活力使得我能够接触并拥抱这些前沿技术;

  2. 享受开发 SPA - 我之前尝试过 Angular,但 React 才是适合我的,我可以自己实践开发 SPA,并且有兴趣去探索相关的技术(比如:构建 universal apps);

  3. 组件化思想 - React 将组件化能够真正用于开发中,实践中才能对组件化思想体会更深;

  4. 前端开发的思考:Flux 的单向数据流的思想,以 FRP 为指导思想的 Redux。这些都让我尝试去思考索前端开发。

下面开始介绍 auto-ellipsis 的开发过程。

CSS 中的 ellipsis

.truncate { width: 250px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

老实说,我所遇到的需求,CSS 中的 ellipsis 基本很少能够满足:

  1. 它只针对单行。但实际中更多的是希望在指定宽高的区域自动截断并加 ...;

  2. 它不能生成提示信息,比如 title。你不能寄希望于用户从审查元素中获得完整的文本信息。

目前,auto-ellipsis 基本无法优雅地通过 CSS 来实现。但是,仔细想想这个需求原本就不是纯样式上的问题。我们不仅仅希望自适应截断(不管尾部加 ...),还希望有提示信息(tooltip or title),这是一个功能需求,可以封装成一个组件。

如何实现

既然 CSS 无法实现,那就只有依靠 JS 来实现。最简单的想法就是:从后向前不断的裁剪文本,检查文本是否溢出,一旦不溢出,我们就终止这个过程。考虑 <div>content</div>,这个过程主要分为两部分:

  1. 裁剪文本:直接暴力的把 div 节点的 text 节点进行替换(<div>content</div> -> <div>conten</div>);

  2. 检查文本是否溢出:我最先想到的是在 div 元素外面套一层 div,设置外层 div overflow: hidden, 内层 divoverflow: visible,外层 div 定宽高,这样比较内外层元素的高度或者相对于视口的 bottom 就好。

显然上面的方法是有效的,但也极其暴力的。首先多套一个 div 就会让人很不爽,于是我们注意到 text 节点也是 dom,可以比较 div 节点和 text 节点吗?可惜 text 节点没办法获得其高度和位置信息。

这时,也许你记得《JavaScript 高级程序设计》中有介绍 Range 这个概念。老实说,我当时看的时候没多大感觉。是的,Range 派上用场了。

Range 属于 dom 对象,通过 Range 可以选择文档中的一个区域,而不必考虑节点的界限。我们可以通过 Range 实现文本的裁剪(比暴力替换文本节点要高效)。 Range 的高度和位置信息可以获取,我们可以通过 getBoundingClientRect() 来获取 div 节点和 Range 相对于视口的 bottom,进行位置比较。而且, Range 的创建对用户透明,这意味着整个裁剪检查的过程 UI 不会有变化。

我们还可以做一些优化:考虑 div 元素的 padding-buttom 和 border-bottom-width;匹配文本减去三个字符用于存放...;考虑 word-break ,最终文本截取到空格处(考虑到中文等其他语言,不好实现...)。

React 组件的封装

首先,组件的属性 props 就是组件的对外接口。对于 auto-ellipsis,我们的对外接口包括:tag(组件的标签),content(文本信息),addTitle(截断时是否加 title 属性),styles(自定义样式)。

其次,组件的状态 state 是随着时间而变化的,一般来说基础组件(dumb component)最好是状态无关的,由上层业务组件(smart component)来管理状态。通常,组件状态的改变是由用户交互造成的,所以组件只需要暴露用户交互结束后相应的处理接口(比如:handleClick)就好。

对于 auto-ellipsis,我们基本没有与用户交互(如果元素宽高不是定值,如百分比,那么视口大小变化是会造成影响的,我们这里不考虑这种情形)。实际上我们更多的是对 DOM 的直接操作,那么我们何时重新渲染组件,何时需要重新剪裁文本?

React 对组件生命周期的管理非常强大,我们只需要考虑怎么做比较合适就好。首先,我们需要在组件初始化挂载结束时(componentDidMount,可操作 DOM)尝试裁剪文本;其次,组件更新时,我们需要在组件更新完毕后(componentDidUpdate,可操作 DOM)尝试裁剪文本;最后,我们需要考虑是否要使用 shouldComponentUpdate,这主要是基于性能考虑。我觉得,对于基础组件,考虑到这三点就足够了,任何更复杂的设计只会让你的组件变得不那么通用,甚至引入一些潜藏的 bug。实际大多数情况下,基础组件连 shouldComponentUpdate 都不该使用,因为虚拟 DOM 已经很快了。但是 auto-ellipsis 比较特殊,它的每次更新需要重新操作 DOM,所以还是可以考虑进行优化的。

shouldComponentUpdate(nextProps, nextState) { return JSON.stringify(this.props) !== JSON.stringify(nextProps)
}

CSS modules

CSS 模块化一直是组件封装的难题。webpack(style-loader, css-loader) 提供了使用 JS module loader 来加载 CSS 的功能。但这只更多的只是对资源的声明依赖和加载,并不是 CSS 模块化。解决 CSS 模块化要解决:CSS 局部作用域的问题;CSS 模块的输入和输出。

css-modules 通过生成唯一的 className,从工程角度上解决了 CSS 局部作用域的问题。css-modules 的输入和输出都是 JS 对象,这个对象是一系列 local-className: global-className 的映射(注意:输入输出不包含全局样式,可以通过css-loader?modules 来开启默认局部样式,:global 开头是全局样式)。CSS 模块之间通过 composes 来组合。

React-css-modules 通过 high-order component点击预览 的方式将 css-modules 自然地应用于 React component,并且使用 styleName 和 className 来区分 local CSS 和 global CSS。我给 react-css-modules 提了一个 PR,用于解决自定义组件的样式,通过样式的声明顺序(先 import 组件,再 import 自定义 CSS 模块)来确保相同选择器下自定义样式具有更高的优先级(可以使用 css-loader?modules&localIdentName=[local]-[hash:base64:5],这样可以通过 [local] 标识 local-className,方便自定义样式)。注:PR 未通过,作者认为有些 hack,最终实现是可以给组件传递 styles 属性,不过是直接替换默认 styles。那么,如果我想在默认 styles 基础上修改一些样式,则需要在 css-modules 中处理,这部分讨论参见 讨论

import React from 'react' import ReactDOM from 'react-dom' import CSSModules from 'react-css-modules' import styles from './auto-ellipsis.css' @CSSModules(styles) export default class AutoEllipsis extends React.Component { static propTypes = { tag: React.PropTypes.string, content: React.PropTypes.string.isRequired, addTitle: React.PropTypes.bool, styles: React.PropTypes.object,
    }
    render() { const props = { styleName: 'root',
        } const {tag, content} = this.props return React.createElement(tag, props, content)
    }
}

关于 CSS 模块化 和 CSS 局域化可具体参考 hax 的 关于前端开发中“模块”和“组件”概念的思考

测试

前端组件的测试,按照宿主一般可分为浏览器环境 和 Node.js 环境。测试框架的话,我推荐 mocha。

浏览器环境可以实际生成 DOM,测试真实有效。可以使用 webpack 配合 mocha-loader,使得测试和开发统一。但是,不方便使用 travis-ci 等一些集成工具。

Node.js 环境下需要模拟 DOM(jsdom),React 组件下可以和 react-addons-test-utils 配合使用。再者,一些涉及到 dom 位置的组件,无法使用模拟测试(比如:jsdom 中的 getBoundingClientRect 返回的都是 0)。

auto-ellipsis 显然依赖于 dom 位置信息,所以采用了浏览器环境测试。

项目地址:https://github.com/ideal-react/auto-ellipsis 。

本文转载自:http://segmentfault.com/a/1190000003782410

第三方支付接口
粉丝 14
博文 50
码字总数 28661
作品 0
深圳
程序员
私信 提问
【天赢金创】Facebook:我们是如何构建第一个跨平台的React Native APP

今年早些时候,我们介绍过iOS版的React Native. React Native带来的是用web方式的React - 自声明式的UI组件和快速的开发迭代来完成手机平台的功能,然后为了保持速度、保真性、并达到原生的体...

第三方支付接口
2015/09/23
43
0
【天赢金创】Reflux学习指南

这个是 simple Todo with React and2 的第二部 - Reflux 第一部可以看 simple Todo with React and Flux1 也可以看 学习flux的一些浅显理解2 Reflux 相对 Flux 来说,真的是简单很多,好理解...

第三方支付接口
2015/09/23
90
0
聊聊 React Router v4 的设计思想

React Router v4 发布已经有几个月了,但好像并没有得到太多人的青睐,大家(包括我们团队自己)还是习惯使用v2、v3版本。这一方面是因为v4版本是一次破坏性的升级,从v2、v3 升级到v4,必需...

苍山沭河
2017/08/06
0
0
前端进阶(四)React和Redux

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014744118/article/details/82120657 React和Redux是相互独立的两个框架。 一、React React 是一个采用声明式...

CoolSummmer
2018/08/27
0
0
前端每周清单第 31 期: iOS 11 Viewport 解析,Preact PWA 性能优化案例,JS 内存泄露分析

前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点;分为新闻热点、开发教程、工程实践、深度阅读、开源项目、巅峰人生等栏目。欢迎关注【前端之巅】微信公...

王下邀月熊
2017/09/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Netty整合Protobuffer

现在我们都知道,rpc的三要素:IO模型,线程模型,然后就是数据交互模型,即我们说的序列化和反序列化,现在我们来看一下压缩比率最大的二进制序列化方式——Protobuffer,而且该方式是可以跨...

算法之名
30分钟前
16
0
如何用C++实现栈

栈的定义 栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压...

BWH_Steven
48分钟前
5
0
编程作业20190210900169

1编写一个程序,提示用户输入名和姓,然后以“名,姓”的格式打印出来。 #include <stdio.h>#include <stdlib.h> int main(){ char firstName[20]; char lastName[20]; print......

1李嘉焘1
今天
12
0
补码的优点及原理分析

只讨论整数 1.计算机内部为什么没有减法器? 减法运算本身其实就是加法,如x - y即x +(-y),所以只需要将负数成功表示出来并可以参加加法运算,那加法器就可同时实现“+”和“-”的运算。这...

清自以敬
今天
76
0
Docker 可视化管理 portainer

官网安装指南: https://portainer.readthedocs.io/en/latest/deployment.html docker-compose.yml 位置,下载地址:https://downloads.portainer.io/docker-compose.yml...

Moks角木
今天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部