This repository has been archived by the owner on Aug 4, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
126 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,125 @@ | ||
# ts-sfc-plugin | ||
A plugin for optimizing stateless component in React (tsx) | ||
|
||
A plugin for optimizing stateless component of React (tsx) | ||
|
||
## Why | ||
React functional component(SFC) is easy to use and help to reduce code size significantly, but sometimes | ||
people might have been some misunderstanding about its perfomance. Usually, we think functional components would avoid some overheads like mounting / unmounting / lifecycle checking and memory allocations, but in fact, there're no special optimizations currently (but after react 16 was released, sfc is indeed faster than before). | ||
|
||
Fortunately SFC just function in react world, if we do care about performance in production there're still a way to improve and this plugin here come to optimize these situation. | ||
|
||
```javascript | ||
const code1 = ( | ||
<div> | ||
<Avatar /> | ||
</div> | ||
) | ||
|
||
const code2 = ( | ||
<div> | ||
{ Avatar() } | ||
</div> | ||
) | ||
``` | ||
As we cannot recognize if the component is functional, we have to use an anotation to tag the expression: | ||
|
||
```javascript | ||
<Avatar sfc /> | ||
// Plugin use `sfc` as identifier by default, but you can pass an option to override it. | ||
``` | ||
|
||
## How to use | ||
|
||
### webpack | ||
```javascript | ||
module: { | ||
rules: [ | ||
{ | ||
test: /\.(jsx|tsx|js|ts)$/, | ||
loader: 'ts-loader', | ||
options: { | ||
transpileOnly: true, | ||
getCustomTransformers: () => ({ | ||
before: [sfcPlugin()], | ||
}), | ||
compilerOptions: { | ||
module: 'esnext', | ||
}, | ||
}, | ||
exclude: /node_modules/, | ||
} | ||
], | ||
} | ||
``` | ||
|
||
### code | ||
|
||
```javascript | ||
import React from 'react' | ||
|
||
export const Avatar = ({ name }) => { | ||
return ( | ||
<div> | ||
<img src=... /> | ||
<span>{ name }</span> | ||
</div> | ||
) | ||
} | ||
``` | ||
|
||
```javascript | ||
import React from 'react' | ||
import { Avatar } from './avatar.component' | ||
|
||
export class App extends React.PureComponent { | ||
render() { | ||
return ( | ||
<div> | ||
<Avatar name={ 'hello world' } sfc /> | ||
</div> | ||
) | ||
} | ||
} | ||
``` | ||
|
||
|
||
## Defect | ||
The following code is recommanded | ||
|
||
```javascript | ||
<Avatar sfc> | ||
// enable rule: `jsx-boolean-value` in tslint.json | ||
``` | ||
|
||
using declaration merging in global .d.ts | ||
```javascript | ||
import React from 'react' | ||
|
||
declare module 'react' { | ||
namespace JSX { | ||
interface IntrinsicAttributes extends React.Attributes { | ||
sfc?: boolean | ||
} | ||
} | ||
} | ||
``` | ||
|
||
Also, code like the usage will not work as expect, because the plugin does not include any runtime type checking. | ||
|
||
```javascript | ||
const component = <Avatar sfc={ this.props.flag } /> | ||
``` | ||
|
||
## Benchmark | ||
|
||
React 16.4, ```<Dot />```, 50 times, MacBook Pro (Retina, 13-inch, Early 2013) | ||
|
||
| Classical | Functional | Direct-call | Auto-transform | | ||
|--|--|--|--| | ||
| 660ms | 408ms | 226ms | 229ms | | ||
|
||
## Refs | ||
|
||
[scu vs sfc](https://stackoverflow.com/questions/45795380/component-with-shouldcomponentupdate-vs-stateless-component-performance) | ||
|
||
[45% faster react functional components now](https://medium.com/missive-app/45-faster-react-functional-components-now-3509a668e69f) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,7 +20,8 @@ | |
], | ||
"files": [ | ||
"index.d.ts", | ||
"lib/" | ||
"lib/", | ||
"index.js" | ||
], | ||
"author": "Saviio", | ||
"license": "MIT", | ||
|
0eda50c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Saviio 第一次看 ts 转换器代码,学习了@~@
但我们为什么选择在 ts 这层做?js 写的 react 应该也用得上吧。主要是看到 babel 已经有插件了,是不满足我们的需求吗?
babel 插件在遇到有
ref
props 时,放弃优化,因为类实现才有完整的 ref 功能支持。这边是不是也需要做特殊处理?0eda50c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@chuan6
Q1: 因为我们不用 babel 啊,ts 编译完了就没办法用 babel 的这个 transform 了, 如果要用 babel 的 transform,就必须用 babel 来编译 ts (但实际上,从我的几次观察里,我认为 typescript compiler 的编译效果要比 babel 来的更好,质量更可靠,overhead 也小,我也不想因为一两个额外的工具修改/破坏我们原有的完整的 tool chain)
P.S. 我一早就知道这个插件了, babel 实际上有一整套 react 优化插件,我都很想要 23333
Q2: ref 的话,我觉得不尽然,我回头尝试一下,因为理论上应该被优化成如下的场景
不过这里会导致: 转换的结果依赖 Avatar 是否被“正确”的实现,可能会出现非预期的场景,如此推理的话 key 也一样,我觉得可以考虑把 ref / key 作为 protected attr,检查到这2个的时候就放弃优化。
0eda50c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@chuan6
关于 Key / Ref
我去看了一眼类型,正如你说的,statelss component 根本就不支持 ref,所以在 ts plugin 这个场景里,ref 不需要成为 deopt 的理由。
而对于 key 而言,鉴于这个 plugin 实际上会删除一层 vd, 因此 key 实际上失去了符合原有语义的那一层 vd,在 mode 2 中会直接被擦除。
详情见 https://github.com/Saviio/ts-sfc-plugin#deopt