HYPERS 前端团队博客


  • 首页

  • 分类

  • 归档

  • 标签
loop

如何使用 Electron 和 React 构建一个 APP

发表于 2018-03-28
| 字数统计 2,744

这篇文章主要记录了,使用 Electron 构建的一个 APP 过程的关键步骤和遇到的问题及解决方法。

最终成品效果图

查看源码

阅读全文 »

React + Webpack 构建打包优化

发表于 2018-01-22 | 分类于 技术
| 字数统计 1,887

React 相关的优化

  • 使用 babel-react-optimize 对 React 代码进行优化
  • 检查没有使用的库,去除 import 引用
  • 按需打包所用的类库,比如 lodash 、echarts 等

lodash 可以采用 babel-plugin-lodash 进行优化。
需要注意的是

在 babel-react-optimize 中使用了 babel-plugin-transform-react-remove-prop-types 这个插件。正常情况下,如果你在代码中没有引用到组件的 PropTypes,则完全没问题。如果你的组件用到了,那么使用该插件可能会导致问题。
具体见:

https://github.com/oliviertassinari/babel-plugin-transform-react-remove-prop-types#is-it-safe

Webpack 构建打包优化

Webpack 构建打包存在的问题主要集中于下面两个方面:

  • Webpack 构建速度慢
  • Webpack 打包后的文件体积过大

Webpack 构建速度慢

可以使用 Webpack.DDLPlugin,HappyPack 来提高构建速度。具体参见小铭在 DMP DDLPlugin 的文档。原文如下:

Webpack.DLLPlugin

  • 添加一个 webpack.dll.config.js

主要是用到一个 DllPlugin 插件,把一些第三方的资源独立打包,同时放到一个 manifest.json 配置文件中,
这样在组件中更新后,就不会重新 build 这些第三方的资源,

  • 同时独立配置 dll/vendors.js 文件,提供给 webpack.dll.config.js
  • 修改 package.json

在 scripts 中添加: "dll": "webpack --config webpack.dll.config.js --progress --colors ",。
执行 npm run dll 以后,会在 dll 目录下生产 两个文件 vendor-manifest.json ,vendor.dll.js

  • 配置 webpack.dev.config.js 文件,加入一个 DllReferencePlugin 插件,并指定 vendor-manifest.json 文件
1
2
3
4
new webpack.DllReferencePlugin({
context: join(__dirname, 'src'),
manifest: require('./dll/vendor-manifest.json')
})
  • 修改 html
1
2
3
<% if(htmlWebpackPlugin.options.NODE_ENV ==='development'){   %>
<script src="dll/vendor.dll.js"></script>
<% } %>

注意,需要在 htmlWebpackPlugin 插件中配置 NODE_ENV 参数

Happypack

通过多线程,缓存等方式提升 rebuild 效率 https://github.com/amireh/happypack

  • 在 webpack.dev.config.js 中针对不同的资源创建多个 HappyPack, 比如 js 1 个,less 1 个,并设置好 id
1
2
3
4
5
6
7
8
9
10
11
12
13
14
new HappyPack({
id: 'js',
threadPool: happyThreadPool,
cache: true,
verbose: true,
loaders: ['babel-loader?babelrc&cacheDirectory=true'],
}),
new HappyPack({
id: 'less',
threadPool: happyThreadPool,
cache: true,
verbose: true,
loaders: ['css-loader', 'less-loader'],
})
  • 在 module.rules 中配置 use 为 happypack/loader, 设置 id
1
2
3
4
5
6
7
8
9
10
11
12
13
{
test: /\.js$/,
use: [
'happypack/loader?id=js'
],
exclude: /node_modules/
}, {
test: /\.less$/,
loader: extractLess.extract({
use: ['happypack/loader?id=less'],
fallback: 'style-loader'
})
}

减少 Webpack 打包后的文件体积大小

首先需要对我们整个 bundle 进行分析,由哪些东西组成及各组成部分所占大小。
这里推荐 webpack-bundle-analyzer。安装后在 webpack.dev.config.js 中添加插件即可,就能在每次启动后自动在网站打开分析结果,如下图

1
plugins.push( new BundleAnalyzerPlugin());

除此之外,还可以将打包过程输出成json文件

1
webpack --profile --json -> stats.json

然后到下面这两个网站进行分析

  • webpack/analyse
  • Webpack Chart

通过上面的图表分析可以清楚得看到,整个 bundle.js 的组成部分及对应的大小。
解决 bundle.js 体积过大的解决思路如下:

  • 生产环境启用压缩等插件,去除不必要插件
  • 拆分业务代码与第三方库及公共模块
  • webpack 开启 gzip 压缩
  • 按需加载

生产环境启用压缩等插件,去除不必要插件

确保在生产环境启动 webpack.DefinePlugin 和 webpack.optimize.UglifyJsPlugin。

1
2
3
4
5
6
7
8
9
10
11
const plugins = [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production')
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
drop_console: false //eslint-disable-line
}
})
]

拆分业务代码与第三方库及公共模块

由于项目的业务代码变更频率很高,而第三方库的代码变化则相对没有那么频率。如果将业务代码和第三库打包到同一个 chunk 的话,在每次构建的时候,哪怕业务代码只改了一行,即使第三方库的代码没有发生变化,会导致整个 chunk 的 hash 跟上一次不同。这不是我们想要的结果。我们想要的是,如果第三方库的代码没有变化,那在构建的时候也要保证对应的 hash 没有发生变化,从而能利用浏览器缓存,更好的提高页面加载性能和缩短页面加载时间。
因此可以将第三库的代码单独拆分成 vendor chunk,与业务代码分离。这样就算业务代码再怎么发生变化,只要第三方库代码没有发生变化,对应的 hash 就不变。
首先 entry 配置两个 app 和 vendor 两个chunk

1
2
3
4
5
6
7
8
entry: {
vendor: [path.join(__dirname, 'dll', 'vendors.js')],
app: [path.join(__dirname, 'src/index')]
},
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].[chunkhash:8].js'
},

其中 vendros.js 是自己定义的哪些第三方库需要纳入 vendor 中,如下:

1
2
3
4
5
6
7
8
require('babel-polyfill');
require('classnames');
require('intl');
require('isomorphic-fetch');
require('react');
require('react-dom');
require('immutable');
require('redux');

然后通过 CommonsChunkPlugin 拆分第三库

1
2
3
4
5
6
7
8
9
plugins.push(
// 拆分第三方库
new webpack.optimize.CommonsChunkPlugin({ name: 'vendor' }),
// 拆分 webpack 自身代码
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime',
minChunks: Infinity
})
);

上面的配置有两个细节需要注意

  • 使用 chunkhash 而不用 hash
  • 单独拆分 webpack 自身代码

使用 chunkhash 而不用 hash

先来看看这二者有何区别:

  • hash 是 build-specific,任何一个文件的改动都会导致编译的结果不同,适用于开发阶段
  • chunkhash 是 chunk-specific,是根据每个 chunk 的内容计算出的 hash,适用于生产

因此为了保证第三方库不变的情况下,对应的 vendor.js 的 hash 也要保持不变,我们再 output.filename 中采用了 chunkhash

单独拆分 webpack 自身代码

Webpack 有个已知问题:

webpack 自身的 boilerplate 和 manifest 代码可能在每次编译时都会变化。

这导致我们只是在 入口文件 改了一行代码,但编译出的 vendor 和 entry chunk 都变了,因为它们自身都包含这部分代码。

这是不合理的,因为实际上我们的第三方库的代码没变,vendor 不应该在我们业务代码变化时发生变化。
因此我们需要将 webpack 这部分代码分离抽离

1
2
3
4
new webpack.optimize.CommonsChunkPlugin({
name: "runtime",
minChunks: Infinity
}),

其中的 name 只要不在 entry 即可,通常使用 "runtime" 或 "manifest"。
另外一个参数 minChunks 表示:在传入公共chunk(commons chunk) 之前所需要包含的最少数量的 chunks。数量必须大于等于2,或者少于等于 chunks的数量,传入 Infinity 会马上生成 公共chunk,但里面没有模块。

更多关于 CommonChunkPlugin 可以查看 官方文档

拆分公共资源

同 上面的拆分第三方库一样,拆分公共资源可以将公用的模块单独打出一个 chunk,你可以设置 minChunk 来选择是共用多少次模块才将它们抽离。配置如下:

1
2
3
4
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
minChunks: 2,
}),

是否需要进行这一步优化可以自行根据项目的业务复用度来判断。

开启 gzip

使用 CompressionPlugin 插件开启 gzip 即可:

1
2
3
4
5
6
7
8
// 添加 gzip
new CompressionPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: /\.(js|html)$/,
threshold: 10240,
minRatio: 0.8
})

按需加载

本篇不做具体介绍

参考资料

  • Webpack 日常使用与优化
  • 使用 Webpack 打包单页应用的正确姿势
  • webpack打包分析与性能优化
  • Webpack 持久化缓存实践
  • 深入理解 webpack 文件打包机制

本文作者:超人

小而美的 React Form 组件

发表于 2017-09-09 | 分类于 前端
| 字数统计 3,347

背景

之间在一篇介绍过 Table 组件《React 实现一个漂亮的 Table》 的文章中讲到过,在企业级后台产品中,用的最多且复杂的组件主要包括 Table、 Form、 Chart,在处理 Table 的时候我们遇到了很多问题。今天我们这篇文章主要是分享一下 Form 组件,在业务开发中, 相对 Table 来说,Form 处理起来更麻烦,不是所有表单都像注册页面那样简单,它往往需要处理非常多的逻辑,比如:

  • 需要处理数据验证逻辑。
  • 在一个项目中的很少有可以复用的表单,每个表单的逻辑都需要单独处理。
  • 在表单中往往存在需要自定义的组件,比如:Toggle、Tree、Picker 等等。

Form 作为一个功能型组件,它需要解决的问题无非就是两个:

  • 把一个数据对象扔给它,它能够在让 Form 内的交互型组件获取到数据并展示出来,同时这些组件上的数据也能反过来让 Form 组件获取到。
  • 对数据的有效性进行验证。

在 React 项目开发中没有用过其他 Form 相关的组件,但是这里我又忍不住想和 Ant Design 的 Form 对比一下,以下是我在 antd 官网上的一个截图:

大家可以通过以上图片看到,我想输入自己的中文名字都不能正常输入,这里主要存在两个问题:

  • onChange 被触发了多次,在一次中文未完整的输入前,不应该触发 onChange 事件。
  • 另外,中文在文本框内就不能正常显示。

我在想这两个问题不在组件上处理,在哪里处理的呢?访问地址: https://ant.design/components/form-cn/ 可以试一下,也希望他们可以解决掉这个问题。这个问题应该怎么解决,我之前做过记录: React 中, 在 Controlled(受控制)的文本框中输入中文 onChange 会触发多次。

我们在设计的时候自然是解决了这些问题,预览效果: https://rsuitejs.com/form-lib/ ,接下来看一下具体的设计。

阅读全文 »

React 实现一个漂亮的 Table

发表于 2017-09-03 | 分类于 前端
| 字数统计 2,793

概述

对于企业级后台产品来说,Table 应该是使用最频繁的组件了,它通常比 Form 和 Chart 的使用还频繁。对于这么一个常用的组件,我们决定要把它从 RSuite 中单独出来开发,并且要具有一定的通用性,适应很多场景。 首先看一下,Table 完成的效果。

  • 预览地址: https://rsuitejs.com/rsuite-table
  • Github: https://github.com/rsuite/rsuite-table

最开始促使我们去实现这个 Table 组件是因为产品经理希望表格可以像 Excel一样固定表头和列,我们都知道 HTML table 是不支持这个功能,但是在实际应用中,对于数据行多列多的情况下,固定表头和列非常有用,方便数据关联浏览。我们的组件库都是 React 的, 开源环境中也没有找到一个适合的我们的 Table 组件。 Ant Design 中的 Table 估计有些人用过,UI 比较漂亮,但是在固定表头和列的这个功能上我还是有些不满意,特别是要同时固定表头和列的时候,在 Retina 屏幕上,Ant Table 通过触摸板滚动表格,固定的区域和非固定的区域会对不整齐,看上去会抖动,这个体验不是特别好。我知道 facebook 的 FixedDataTable 针对这块的处理做的还不错,是一个好的参考,特别是大数据量渲染也不卡顿,但是有些功能也不能满足我们的业务场景,比如在要 Table 中呈现一个树形结构就没有这个功能。所以还是决定自己造这个轮子。

阅读全文 »

React 高阶组件浅析

发表于 2017-08-24 | 分类于 前端
| 字数统计 2,348

背景

高阶组件的这种写法的诞生来自于社区的实践,目的是解决一些交叉问题(Cross-Cutting Concerns)。而最早时候 React 官方给出的解决方案是使用 mixin 。而 React 也在官网中写道:

We previously recommended mixins as a way to handle cross-cutting concerns. We’ve since realized that mixins create more trouble than they are worth.

官方明显也意识到了使用mixins技术来解决此类问题所带来的困扰远高于其本身的价值。更多资料可以查阅官方的说明。

高阶函数的定义

说到高阶组件,就不得不先简单的介绍一下高阶函数。下面展示一个最简单的高阶函数

1
const add = (x,y,f) => f(x)+f(y)

当我们调用add(-5, 6, Math.abs)时,参数 x,y 和f 分别接收 -5,6 和 Math.abs,根据函数定义,我们可以推导计算过程为:

1
2
3
4
x ==> -5
y ==> 6
f ==> abs
f(x) + f(y) ==> Math.abs(-5) + Math.abs(6) ==> 11

用代码验证一下:

1
add(-5, 6, Math.abs); //11

高阶在维基百科的定义如下

高阶函数是至少满足下列一个条件的函数:

  • 接受一个或多个函数作为输入
  • 输出一个函数

高阶组件的定义

那么,什么是高阶组件呢?类比高阶函数的定义,高阶组件就是接受一个组件作为参数并返回一个新组件的函数。这里需要注意高阶组件是一个函数,并不是组件,这一点一定要注意。
同时这里强调一点高阶组件本身并不是 React API。它只是一种模式,这种模式是由 React 自身的组合性质必然产生的。
更加通俗的讲,高阶组件通过包裹(wrapped)被传入的React组件,经过一系列处理,最终返回一个相对增强(enhanced)的 React 组件,供其他组件调用。

阅读全文 »

H5 是 HTML5 吗?

发表于 2017-08-22
| 字数统计 2,231

背景

作为程序员经常会被业务人员问一些技术相关的热门词,比如:“H5页面是什么?” , 我的回答是:“符合 HTML5 标准实现的页面,大家为了方便,简称 H5“。 真的是这样的吗? 当连续有人问相关的问题,并详细了解一些业务以后,慢慢的发现 H5 已经不单单是一个技术相关的词。

之前被我们产品人员也问到一个类似的问题:“怎么通过技术手段来判断一个网站是H5的页面,还是普通页面?”,听到这个问题我懵了,按照我前面的解释 “符合 HTML5 标准实现的页面就是 H5页面”,那当前互联网上看到的网站基本上都在采用了 HTML5 标准。详细了解业务以后才知道,他的要求是把微信里面那种好友之间,或者朋友圈分享的那种带有主题性质的宣传页面(实在是不知道怎么描述)和我们平时电脑上访问的页面通过技术区分识别出来。这个功能能不能实现,具体怎么实现这里不讨论,但是可以看出在业务人员和一些客户眼里,在微信上分享的那种炫酷的页面就是H5页面。 那这里的 H5 还是 HTML5 吗? 从这种应用上来说已经不是了。

另外一个案例,被一个刚开始学习前端的学生问到一个问题:“React 制作的页面都是 SPA(single page web application,单页应用程序) 的,那 HTML5 可以实现 SPA 吗?”,当被问到这个问题的时候我一下子也不知道怎么回答,只是让他先去学习一下 HTML5 ,以及 SPA 到底是什么?要搞清楚。 HTML是超文本标记语言,是一种规范,一种标准,“5” 是HTML第五次重大修改,可以理解为第五个版本,包含了很多功能特性。 SPA 是通过 HTML 与 Javscript 实现的单页应用程序,就是在一个页面上展示所有数据,通过 Javascript 去动态更新程序。所以 SPA 的实现和是不是 HTML5 没有大的关系。这位同学问出这样的问题,我的猜想是这样的,我们在前面提到的那种在微信上分享的那种页面大多数都是一个单页面,然后通过手指向下滚动来浏览一张张炫酷的动画。导致他认为这样的页面就是所谓的 SPA 页面,再加上多数人都认为这种页面都 HTML5 实现的页面,所有他才会问出 HTML5 和 SPA 扯在一起。

我相信现在大多数人认为的 H5 页面,就是在手机微信分享的那种炫酷的网页。 HTML5,作为技术人员都知道是一种标准,一种规范。

阅读全文 »

代码测试覆盖率分析

发表于 2017-08-20
| 字数统计 2,433

背景

最近我们前端团队在重构大量的 UI 组件,为了保证代码质量,我要求团队中的成员必须编写单元测试,并且测试覆盖率达到 80% 以上。那么问题来了,为什么是 80% 的覆盖率? 这是一个硬性的考核指标吗?

这里所说的测试覆盖率,是指的是开发人员写的单元测试的覆盖率,不是测试人员的功能测试的覆盖率。

哪些地方需要写单元测试?

为什么需要写单元测试就不再阐述,我相信大家都知道,特别是在持续集成过程中的重要性。但是,从我的经历来看,当前的软件市场环境中,不管是用的瀑布模式,还是螺旋模式,还是敏捷模式,很多软件没有写单元测试。

我也是一个程序员,每天需要写一些的业务代码,对于写单元测试来说,确实需要我很多时间和精力,因为它也需要设计用例和一些体力活。所以在我们的一些项目中也存在很多功能没有单元测试,主要原因有以下几个点:

  • 业务逻辑更新太快,单元测试不可复用;
  • 业务时间紧急,迭代周期时间短,没有时间写单元测试;
  • UI 上很多测试,通过单元测试代码无法覆盖。

在《软件测试》一书中讲测试的原则,第一条就是:“完全测试程序是不可能的”。所以对于以上部分需不需测试,取决于你软件性质,时间和团队。但是对于满足以下几点代码我建议需要编写单元测试:

  • 和安全相关的代码逻辑;
  • 核心的功能模块,函数;
  • 短期不会发生变化的 UI 组件;
  • 提供外部调用的接口。
阅读全文 »

Sass & Compass 教程 -- 语法篇(二)

发表于 2017-08-19
| 字数统计 1,017

阅读本篇你将收获

  • 使用混合器封装重用的样式。
  • 使用选择器继承减少样式重复。
  • 从视觉效果的角度出发描述元素。
阅读全文 »

Git使用入门教程

发表于 2017-08-06
| 字数统计 5,462

大纲图

阅读全文 »

Sass & Compass 教程 —— 语法篇(一)

发表于 2017-08-06
| 字数统计 1,338

阅读本篇你将收获

  • 变量需要先声明后使用,且不存在变量提升。
  • 嵌套使得样式结构清晰。
  • 利用导入来分离代码,优化文件组织。
阅读全文 »
123
Hypers

Hypers

25 日志
2 分类
45 标签
© 2019 Hypers - 全站共58.9k字
由 Hexo 强力驱动
主题 - NexT.Muse