国外很火的一本书, 居然有大佬翻译成中文了,果断买了,发现并没有太多react设计模式的内容,大都是一些常用的用法,就当温故而知新了
对应书书的每一章,写个笔记,书的目录
1 React 基础
2 整理代码
3 开发真正可复用的组件
4 组合一切
5 恰当地获取数据
6 为浏览器编写代码
7 美化组件
8 服务端渲染的乐趣与益处
9 提升应用性能
10 测试与调试
11 需要避免的反模式
12 未来的行动
1. React 基础
React 是最流行的 UI 开发库之一
声明式编程 > 命令式编程 忽略中间执行步骤,只关心结果1
2
3
4
5
6
7
8
9
10
11const toLowerCase = input => input.map(
value => value.toLowerCase()
)
const toLowerCase = input => {
const output = []
for (let i = 0; i < input.length; i++) {
output.push(input[i].toLowerCase())
}
return output
}
元素最重要的属性是 type,另一个比较特殊的属性是 children,它是可选的,用于表示
元素的直接后代。
type 属性很重要,因为它告诉 React 如何处理元素本身。实际上,如果 type 属性是字符串,
那么元素就表示 DOM 节点;如果 type 属性是函数,那么元素就是组件。1
2
3
4
5
6
7 {
type: Title,
props: {
color: 'red',
children: 'Hello, Title!'
}
}
当元素的 type 属性是函数时,React 会调用它,传入 props 来取回底层元素。React 会一直 对返回结果递归地执行相同的操作,直到取回完整的 DOM 节点树,然后就可以将它渲染到屏幕。 这个过程称作一致性比较,React DOM 和 React Native 都利用它在各自的平台上创建 UI。
2. 整理代码
jsx : JSX 仅仅是语法糖,是一种函数,在浏览器中运行前需要转译成 JavaScript, 实际上,运行 Babel 时会将
class 和 for 都是 JavaScript 的保留字, 使用时:1
<label className="awesome-label" htmlFor="name" />
JSX 可以利用行内条件来判断:1
2
3
4
5
6
7
8
9
10
11
12存在判断
<div>
{isLoggedIn && <LoginButton />}
</div>
三元判断
<div>
{isLoggedIn ? <LogoutButton /> : <LoginButton />}
</div>
函数条件判断
<div>
{this.canShowSecretData() && <SecretData />}
</div>
函数式编程基础
JavaScript 的函数是一等对象
高阶函数接受一个函数作为参数,也可以传入其他参数, 最后返回另一个函数。返回的函数通常会添加一些增强的特殊行为
纯粹函数是指它不产生副作用,也就是说它不会改变自身作用域以外的任何东西
柯里化是函数式编程的常用技巧。柯里化过程就是将多参数函数转换成单参数函数,这些单 参数函数的返回值也是函数
3. 开发真正可复用的组件
createClass工厂方法 和 继承React.Component 的区别:
第一个区别在于如何定义组件期望接收的 prop 及其默认值
(propTypes , getDefaultProps / || Button.propTypes , Button.defaultProps)
第二个区别是组件 初始状态 的定义方式不同
(getInitialState || 构造器里this.state)
第三个区别是函数的自动绑定
(this指向当前的组件 || 在当前的constracter里 this.handleClick = this.handleClick.bind(this))
无状态函数:1
() => <button />
没有生命周期和state,没有this指向,但是能接受父组件的props, 用于渲染
用个div标签包裹起来 才能获取到 组件的引用1
const component = ReactTestUtils.renderIntoDocument(<div><Button/></div>)
setState({}) 异步1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16function shouleKeepSomethingInReactState() {
if (canICalculateItFormProps()) {
// 不要把props里的数据复制到状态里
// 直接在render()方法里计算即可
return false
}
if (!amIUsingItRenderMethod()) {
// 不要把没有参与渲染的数据放进状态里
// 比如API订阅的部分就适合放在外部
// 模块的自定义私有字段 或 变量
return false
}
return true
}
props类型检查 (类型和是否必须传入)1
2
3
4
5
6Profile.propTypes = {
user: React.PropTypes.shape({
name: React.PropTypes.string.isRequired,
surname: React.PropTypes.string,
}).isRequired,
}
可复用组件 : 通过定义通用的集合属性,对该列表组件做一些抽象并与显示的数据解耦1
2
3
4
5
6
7
8
9
10
11const List = ({ collection, textKey, titleKey }) => (
<ul>
{collection.map(item =>
<Item
key={item.id}
text={item[textKey]}
title={item[titleKey]}
/>
)}
</ul>
)
4. 组合一切
children 是一个特殊的 prop,拥有者组件可以将它传递给渲染方法内定义的组件
容器组件与表现组件模式
UI 定义在表现组件中,并且表现组件以 prop 的形式从容器组件接收数据。 因为表现组件通常不含逻辑,所以可以将它创建为函数式无状态组件。
mixin的淘汰
- mixin 只能和 createClass 工厂方法搭配使用
- mixin 的另一个常见问题是冲突。实际上,虽然 React 确实可以聪明地合并生命周期回调, 但如果两个 mixin 定义或调用了同样的函数名,抑或在状态中使用了相同的属性,那么 React 对 此无能为力。
高阶组件
const HoC = Component => EnhancedComponent
这比用 mixin 具有很多优势:首先没有污染任何状态,其次不需要组件来实现任何方法。这意味着组件和高阶组件没有耦合,可以在整个应用中复用它们。
再次强调,用 props 代替状态能分离组件与逻辑,这样一来,我们就可以在风格指南中使用 组件,忽略任何复杂逻辑,只传入 props 即可
recompose 略过
函数子组件1
2
3
4const FunctionAsChild = ({ children }) => children()
FunctionAsChild.propTypes = {
children: React.PropTypes.func.isRequired,
}
首要优点是,可以像高阶组件那样封装组件,在运行时为它们传递变量而不是固定属性
其次,以这种方式组合组件不要求 children 函数使用预定义的 prop 名称。
最后,封装器的可复用程度很高,因为它不关心子组件要接收什么,只期望传入一个函数 (按函数子组件模式编写的组件也可以用于应用的不同部分,托管各种各样的子 组件)
5. 恰当的获取数据
React 中的数据流是单向的,即从组件树的顶部流向底部。这种方式有很多好处, 它简化了组件行为以及组件间的关系,增强了代码的可预测性和可维护性
当子组件需要向父组件推送信息或触发事件时,React 通常采用回调函数来实现
例:先创建 Buttons 组件来显示增减按钮。当点击按钮时,不再触发内部函数,而是触发 props 上传来的函数1
2
3
4
5
6
7
8
9
10const Buttons = ({ onDecrement, onIncrement }) => (
<div>
<button onClick={onDecrement}>-</button>
<button onClick={onIncrement}>+</button>
</div>
)
Buttons.propTypes = {
onDecrement: React.PropTypes.func,
onIncrement: React.PropTypes.func,
}
因此,每当子组件需要向父组件推送数据或者通知父组件发生了某个事件时,可以传递回调 函数,同时将其余逻辑放在父组件中
实际上,服务端渲染和客户端渲染都会触发 componentWillMount 函数。
第 8 章将详细介绍服务端渲染。现在你只需要知道,当在服务端渲染组件时,触发异步 API 会带来预料之外的结果。
因此,我们只能用 componentDidMount 钩子,这样就能确保只在浏览器端调用 API 请求
高阶组件其实就是函数,它接受组件和一些其他参数,然后返回添加了某些特殊行为的新组件
react-refetch
npm install react-refetch –save1
2
3
4import { connect } from 'react-refetch'
const connectWithGists = connect(({ username }) => ({
gists: `https://api.github.com/users/${username}/gists`,
}))
实际上,react-refetch 库注入的属性与我们在 connect 函数中指定的键同名。
gists prop 并不是真正的数据,而是一种名为 PromiseState 的特殊对象。
PromiseState 对象是 Promise 的同步表示,并且它具备一些很有用的属性,如 pending 和 fulfilled,可以利用这两个属性来显示加载动画或数据列表。1
2
3
4
5
6
7
8
9 const List = ({ gists }) => (
gists.fulfilled && (
<ul>
{gists.value.map(gist => (
<li key={gist.id}>{gist.description}</li>
))}
</ul>
)
)
6. 为浏览器编写代码
我们可以实现自由组件,以允许表单域保存自己的内部状态。也可以使用受控组件,它们的 表单域状态完全由我们控制。
一般的表单
1 | handleChange({ target }) { |
react-jsonschema-form
npm install –save react-jsonschema-form1
2
3
4
5
6
7
8
9
10
11
12
13
14import Form from 'react-jsonschema-form'
const schema = {
type: 'object',
properties: {
firstName: { type: 'string', default: 'Dan' },
lastName: { type: 'string', default: 'Abramov' },
},
}
render() {
return (
<Form schema={schema} onSubmit={this.handleSubmit} />
)
}
props 中的 schema 就是我们前面定义的 schema 对象。它可以像当前示例一样静态定义,也 可以从服务端接收,或者用 props 组合而来
我们只需要将处理器函数赋值给库组件 Form 的 onSubmit 回调,就能轻松地创建可用表单。
还有其他回调,比如每当表单元素的值改变时就会触发的 onChange,以及提交的表单数据 无效时就会触发的 onError
React 引入了合成事件的概念。合成事件对象封装了浏览器提供的原生 事件对象,它在任何浏览器中都具有相同属性1
2
3
4
5
6<button
onClick={this.handleClick}
onDoubleClick={this.handleDoubleClick}
>
Click me!
</button>
只要使用 on 开头的属性,我们就是在向 React 描述期望达成的行为,但库本身不会在底层
DOM 节点上添加真正的事件处理器。
React 实际做的是在根元素上添加单个事件处理器,由于事件冒泡机制,这个处理器会监听 所有事件。当浏览器触发我们想要的事件时,React 会代表相应组件调用处理器。这个技巧称作 事件代理,可以优化内存和速度
ref1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
handleClick () {
this.element.focus()
// this.element.reset()
}
render () {
return (
<form>
<input
type="text"
ref={element => (this.element = element)}
/>
<button onClick={this.handleClick}>Focus</button>
</form>
)
}
这个回调函数会在组件挂载时被调用,元素参数表示输入的 DOM 实例。值得注意的是,卸 载组件时也会调用这个回调,并传入 null 参数来释放内存。回调函数要做的就是保存元素对象 的引用,方便以后使用(如在 handleClick 方法触发时使用)
动画 react-addons-css-transition-group
npm install –save react-addons-css-transition-group1
2
3
4
5
6
7
8
9
10import CSSTransitionGroup from 'react-addons-css-transition-group'
const Transition = () => (
<CSSTransitionGroup
transitionName="fade"
transitionAppear
transitionAppearTimeout={500}
>
<h1>Hello React</h1>
</CSSTransitionGroup>
)
1 | .fade-appear { |
动画 react-motion
npm install –save react-motion1
2
3
4
5
6
7
8
9
10import { Motion, spring } from 'react-motion'
const Transition = () => (
<Motion
defaultStyle={{ opacity: 0.01 }}
style={{ opacity: spring(1) }}
>
{interpolatingStyle => (
<h1 style={interpolatingStyle}>Hello React</h1>
)} </Motion>
)
接着我们可以看到 Motion 组件有两个属性:第一个属性 defaultStyle 表示初始样式。
我们将透明度设置为 0.01 来隐藏元素并开始淡入动画。
样式属性表示最终样式,但这里没有直接设置最终值,而是利用 spring 函数,这样样式值 就能从初始状态渐变到最终状态。
在 spring 函数每次迭代计算的过程中,子函数会即时接收到改变后的样式值,只要将收到 的对象赋值给子组件的样式属性,就能看到透明度产生渐变。
可扩展矢量图形(scalable vector graphic,SVG)
我们以往经常用图标字体来创建图标,但这项技术的问题众所周知,最大的问题便是它不具备可访问性。再者,用 CSS 定位图标字体相当困难,并且它们无法在所有浏览器中同样美观显示。以上原因就是我们应该在 Web 应用中改用 SVG 的理由。
倾向于选择 SVG 的另一个理由是,可以用 CSS 和 JavaScript 很方便地在运行时修改它们,这使得它们成为了 React 函数式编程的完美候补方案1
2
3
4
5
6
7
8
9
10
11
12
13
14const Circle = ({ x, y, radius, fill }) => (
<svg>
<circle cx={x} cy={y} r={radius} fill={fill} />
</svg>
)
Circle.propTypes = {
x: React.PropTypes.number,
y: React.PropTypes.number,
radius: React.PropTypes.number,
fill: React.PropTypes.string,
}
<Circle x={20} y={20} radius={20} fill="blue" />
加上类型定义的好处在于,SVG 及其属性的使用会更加明确,接口变得更加清晰,我们可 以准确知道如何配置图标。
7. 美化组件 (css)
1 | const style = { |
在 React 中为元素添加行内样式非常简单。只要创建一个对象,它的属性名就是 CSS 规则名,属性值就是常规 CSS 文件中用到的那些值。
唯一的区别在于,为了符合 JavaScript 语法,连字符式的 CSS 规则名必须改为驼峰式,另外 属性值必须是字符串,因此需要用引号包裹起来
数字值也有例外:可以不带引号或度量单位书写它们,默认单位为像素
Radium
npm install –save radium1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import radium from 'radium'
const styles = {
backgroundColor: '#ff0000',
width: 320,
padding: 20,
borderRadius: 5,
border: 'none',
outline: 'none',
':hover': {
color: '#fff',
},
':active': {
position: 'relative',
top: 2,
},
'@media (max-width: 480px)': {
width: 160,
}
}
const Button = () => <button style={styles}>Click me!</button>
export default radium(Button)
radium 函数是一个高阶组件
能看到悬停特效却无法用 CSS 模拟出来,是因为 Radium 用 js 来应用和移除样式对 象中定义的悬停状态
为了媒体查询可以正常工作,尤其是服务端渲染的情况下,Radium 会将与媒体查询相关的 规则注入 DOM 中的一个样式元素,并且所有属性都会添加!important 关键词
这样做是为了在 Radium 计算出匹配的查询条件前,避免页面文档切换不同样式时发生闪烁。 将样式放入样式元素,让浏览器照常完成工作,可以避免样式闪烁现象
因此,需要导入 StyleRoot 组件,然后用它封装整个应用:1
2
3
4
5
6
7
8
9
10
11import { StyleRoot } from 'radium'
class App extends Component {
render() {
return (
<StyleRoot>
...
</StyleRoot>
)
}
}
css模块1
<button class="_2wpxM3yizfwbWee6k0UlD4">Click me!</button>
css-loader 允许你在 JavaScript 模块中导入 CSS 文件,并且启用 modules 标记时,所有
类名都只作用于导入它们的模块。
前面提到过,导入的字符串并不是随机的,而是根据文件散列值和其他一些参数生成的,它 在代码库中是唯一的。
最后,style-loader 接收 CSS 模块转换的结果,并将样式注入页面头部
CSS 模块的其他一些特性也很值得一提。
第一个就是 global 关键词。给任何类添加:global 前缀,意味着请求 CSS 模块不要为当 前选择器加上局部作用域。
举例来说,修改 CSS 代码,如下所示:
:global .button {
…
}
输出结果如下所示:
.button { …
}
React CSS 模块
npm install –save react-css-modules1
2
3
4import cssModules from 'react-css-modules'
const Button = () => <button styleName="button">Click me!</button>
const EnhancedButton = cssModules(Button, styles)
Styled Component
npm install –save styled-components
1 | import styled from 'styled-components' |
支持scss;另一项绝佳特性是主题。将组件封装在 ThemeProvider 组件中,可以为组件树注入 主题属性,当要和其他组件共享一部分样式,剩下部分取决于当前选中主题时,创建 UI 会变得 非常方便。
8. 服务端渲染的乐趣与益处
seo
用户体验和转化率
npm install –save-dev webpack-node-externals1
2
3
4
5
6
7
8
9
10
11const nodeExternals = require('webpack-node-externals')
const client = {
entry: './src/client.js',
output: {
path: './dist/public',
filename: 'bundle.js',
},
module: { loaders },
target: 'node',
externals: [nodeExternals()],
}
服务端配置中新增了 target 部分,我们指定其值为 node,以此告诉 Webpack 忽略 Node.js的所有内置系统包,如 fs。另外,externals 部分用之前导入的库告诉 Webpack 忽略这些依赖
1 | import express from 'express' |
首先导入 express,它可以很简单地创建带路由的 Web 服务器,也可以托管静态文件
接着导入 React、ReactDOM 以及需要渲染的 App 组件。注意 ReactDOM 导入语句中的/server 路径。最后,导入刚刚定义的模板
要想在服务端使用获取 API,需要先安装 isomorphic-fetch 库
npm install –save isomorphic-fetch1
2
3
4
5
6
7
8
9
10
11import fetch from 'isomorphic-fetch'
app.get('/', (req, res) => {
fetch('https://api.github.com/users/gaearon/gists')
.then(response => response.json())
.then(gists => {
const body = ReactDOM.renderToString(<App gists={gists} />)
const html = template(body)
res.send(html)
})
})
此处先获取 gists,然后传给 App 组件的属性,再将它渲染成字符串
Next.js (详见github)
npm init
npm install –save next
“scripts”: {
“dev”: “next”
},
Next.js 依赖于约定,其中最关键的一条约定是,创建的页面要和浏览器 URL 匹配。默认页面是 index,因此我们创建一个名为 pages 的文件夹,并在其中创建 index.js 文件
9. 提升应用性能
Web 应用的高性能是提供良好用户体验与提升用户转化率的关键
1 一致性比较的原理,以及如何使用 key 属性帮助 React 更好地工作。
2 如何使用生产版本的 React 以便更快运行。
3 shouldComponentUpdate 和 PureComponent 能为我们做什么,以及如何使用它们。
4 常用的优化手段以及与性能相关的常见错误。
5 不可变数据的含义与用法。 10 促使应用更快运行的工具与库
当显示组件时,React 会调用自己的渲染方法,还会递归调用子组件的渲染方法。组件的渲染方法会返回 React 元素树,然后 React 根据它来判断更新 UI 需要执行哪些 DOM 操作
一致性比较: 能够计算出使屏幕上产生预期变化所需要的最小操作集合
如果两个元素的类型不同,则它们渲染的树也不同
开发人员可以用 key 属性标记子组件,使它们在不同渲染过程得以保留1
2
3
4
5handleClick() {
this.setState({
items: this.state.items.concat('baz'),
})
}
对性能进行分析的插件 : react-addons-perf
npm install –save-dev react-addons-perf1
2
3
4
5
6
7
8
9
10import Perf from 'react-addons-perf'
componentWillUpdate() {
Perf.start()
}
componentDidUpdate() {
Perf.stop()
Perf.printOperations()
}
Perf 对象提供了一些有用的方法来监控 React 组件的性能。使用 start()可以开始记录数 据,使用 stop()可以告诉插件我们已经收集了足够多的数据并准备显示出来。
还有很多不同的方法可以用来显示浏览器控制台中收集到的数据。其中 printWasted 用处 最大,它可以打印出组件执行渲染方法所耗费的时间,并返回上次渲染过程中的相同元素。Perf 插件还提供了 DOM 中与 React 操作相关的一些有用信息,对应的方法名为 printOperations
key 的使用很简单,为列表中的每一项添加唯一的 key 属性即可。此处重点在于,key 属性 的值在每次渲染过程中不能改变,因为 React 会比较渲染前后的 key 属性值,从而判断元素是新 增的还是已有的
启用 Eslint,并开启 eslint-plugin-react 插件的 jsx-key 规则,那么 linter 也会在缺少 key 属性时给出警告
优化手段:1
2
3
4
5new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
shouldComponentUpdate1
2
3shouldComponentUpdate(nextProps, nextState) {
return this.state.items !== nextState.items
}
PureComponent 浅比较不会检查对象中嵌套的深层属性,并且有时会给出意外 结果
无状态组件实际上不会带来任何性能上的提升
检查组件更新的插件 : why-did-you-update 一个能自动提示信息的第三方库1
2
3
4if (process.env.NODE_ENV !== 'production') {
const { whyDidYouUpdate } = require('why-did-you-update')
whyDidYouUpdate(React)
}
onClick={() => {}} 这样每次都要重新渲染, 应该把所有的 常量 / 函数 的定义放到 return 外去定义
React 认为每次渲染都传入了新函数的原因是,即使函数实现保持不变,每次调用箭头函数 都会返回一个全新创建的函数。使用 React 常常会犯这个错,不过只要稍微重构一下组件就能轻 松解决这个问题
所有对象在创建时都会返回新的实例,但即使包含相同的值,两个 新数组也永远不会相等, [] === [] false
之前提过,如果只是一个组件本身渲染多次,这并不算什么大问题。只有测试性能后,发现 多次渲染很长的列表,进而导致应用响应性变差,问题才变得严重起来, 拆分为不同的组件, 相互的状态数据互不影响
每次修改对象时都创建新的实例 :
const obj = Object.assign({}, this.state.obj, { foo: ‘bar’ })
this.setState({ obj })
const obj = { …this.state.obj, foo: ‘bar’ }
this.setState({ obj })
安装babel插件 提升性能
React 常量元素转换器 : 它会寻找不随 props 改变的所有静态元素,并将它们从渲染方法(或者无状态函数式组件)中抽离出来,以避免多余地调用 createElement
React 行内元素转换器 : 它会将所有 JSX 声明(或 者 createElement 调用)替换成优化过的版本,以便代码可以更快执行
npm install –save-dev babel-plugin-transform-react-constant-elements
npm install –save-dev babel-plugin-transform-react-inline-elements
在 .babelrc 配置1
2
3{
"plugins": ["transform-react-constant-elements", "transform-react-inline-elements"]
}
10. 测试与调试
1 为何测试应用很重要,以及测试如何帮助开发人员更快地行动。 如何搭建 Jest 环境,并用 TestUtils 测试组件。
2 如何用 Mocha 搭建相同的环境。
3 什么是 Enzyme,以及为何它是测试 React 应用的必备工具。
4 如何测试真实的组件。
5 Jest 快照与 Istanbul 代码覆盖率工具。
6 高阶组件和包含多层嵌套子组件的复杂页面的常用测试方案。 React 开发者工具与一些错误处理技巧。
11. 需要避免的反模式
1 用 prop 初始化状态导致意外结果的场景。
2 为何直接修改状态的做法不对,还会损伤性能。
3 如何选择正确的 key 属性来协助一致性比较器更好地工作。
4 为何在 DOM 元素上展开 props 对象不可取,以及有哪些替代做法
用父组件传来的 prop 初始化状态往往是一种反模式
两个主要错误是:
我们违背了单一数据源原则;
传给组件的 count 属性发生变化时,状态不会相应地更新
首先,如果不通过 setState 修改状态,则会出现两种糟糕的情况:
1 状态改变不会触发组件重渲染;
2 以后无论何时调用 setState,之前修改的状态都会渲染到页面上, 直接修改状态引发的第二个问题是,无论组件的其他部分何时调用 setState,之前修改的状态都会渲染到页面上,这很出乎意料
最后非常重要的一点是,直接修改状态会严重影响性能
正确的做法应该是始终将新值赋给 state 属性1
2
3
4
5handleClick() {
this.setState({
items: this.state.items.concat('baz'),
})
}
数组的 concat 函数会将新元素拼接到原有数组上,然后返回新的数组。这样 PureComponent 就会发现状态中有了新数组,然后正确的重新渲染.
key 的值必须唯一且稳定,它只能标识唯一一项数据
当在 DOM 元素上展开 props 对象时,就会有添加未知 HTML 属性的风险,这是很糟糕的做法
问题不仅和展开操作符有关,逐个展开非标准属性会导致相同的问题和警告。因为展开操作 符隐藏了我们所要展开的属性,所以很难判断具体向元素传递了什么
12. 未来的行动
1 如何通过提交问题和发起 pull request 为 React 库做贡献。
2 为何回馈社区以及分享自己的代码很重要。
3 发布开源代码时需要牢记的重点。
4 如何发布 npm 包和使用语义化版本号
假设用 React 开发应用时发现了 bug,应该怎么做呢?首先,也是最重要的一点是,创建一个简单的示例来重现问题。
( 可以帮你百分之百地确定这是 React 的 bug,而不是你自己的应用代码的问题;
有助于 React 团队快速理解问题,无须深入研究你的应用代码,从而可以加快修复过程)
首先,如果想要发布 React 组件,那么你需要编写全面的测试集
测试使代码更稳健;
测试能帮助其他开发人员理解代码的功能;
测试有助于发现新增的代码缺陷;
测试让参与编写代码的其他开发人员更有信心
第二点,添加描述组件的 README 文件,其中包括使用示例、API 文档以及可用的 prop
另外,在仓库中添加 LICENSE 文件也很重要
你应该尽量减小软件包并少用依赖。在决定是否使用某一个库时,开发人员往往会仔细考虑 它的大小。记住,大软件包会给性能造成负面影响
最佳选择是尽量少提供样式,允许用户自由配置组件。这样一来,开发人员就更愿意使用它, 因为这能和他们自己的技术方案相匹配。
Airbnb 的 react-dates 组件是一个配置化程度很高的完美示例,它用 Storybook 展示了 组件的所有状态。你可以将它的代码仓库作为绝佳的范例,以学习如何在 GitHub 上发布 React 组件。
发布 npm 包
npm init
版本号由三个以点号分隔的数字组成,它们都有各自的含义。版本号中的最后一个数字表示 补丁,当库的新版包含 bug 修复补丁时,发布到 npm 上需要增加该数字
中间的数字表示次要版本,库新增特性时需要修改它,并且这些特性不能破坏已有的 API
最后,第一个数字表示主版本,公开发布的版本包括重大改动时应该增加该数字, 包的第一个版本号通常是 0.1.0
要想发布 npm 包,必须有 npm 账户,在控制台中执行以下命令即可创建账户: npm adduser $username
$username 表示你选择的用户名。
有了账户后,就可以执行以下命令:
npm publish
这时,npm 就会用 package.json 中指定的包名和版本号注册一个入口。
任何时候要想修改库并发布新版,只需要执行以下命令:
npm version $type
$type 可以是补丁版本、次要版本或者主版本。这条命令会自动修改 package.json 中的版本 号,如果当前文件夹处于版本控制之下,它还会创建commit 并打上标签。
版本号更新后, 只要再次执行 npm publish , 用户就可以下载新版了