Skip to content
This repository has been archived by the owner on Aug 4, 2019. It is now read-only.

Commit

Permalink
chore: update README
Browse files Browse the repository at this point in the history
  • Loading branch information
Saviio authored and Saviio committed Sep 19, 2018
1 parent 4e1aeb1 commit 0eda50c
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 2 deletions.
125 changes: 124 additions & 1 deletion README.md
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)
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
],
"files": [
"index.d.ts",
"lib/"
"lib/",
"index.js"
],
"author": "Saviio",
"license": "MIT",
Expand Down

3 comments on commit 0eda50c

@chuan6
Copy link

@chuan6 chuan6 commented on 0eda50c Sep 19, 2018

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 功能支持。这边是不是也需要做特殊处理?

@Saviio
Copy link
Owner

@Saviio Saviio commented on 0eda50c Sep 19, 2018

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 ref={ this.saveRef } />

{ Avatar({ ref: this.saveRef }) }

不过这里会导致: 转换的结果依赖 Avatar 是否被“正确”的实现,可能会出现非预期的场景,如此推理的话 key 也一样,我觉得可以考虑把 ref / key 作为 protected attr,检查到这2个的时候就放弃优化。

// 一个正确的 Avatar

export const Avatar = (props) => {
  return (
    <div key={ props.key } ref={ props.ref }>
      <img />
      <span>{ props.name }</span>
    </div>
  )
}

@Saviio
Copy link
Owner

@Saviio Saviio commented on 0eda50c Sep 22, 2018

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

Please sign in to comment.