phobal`s blogs


  • 首页

  • 归档

  • 标签

  • 关于

  • 日程

npm link 踩坑记

发表于 2018-03-01   |  

框架说明

项目采用 react 库进行开发

坑的出现

目前项目 A 开发中过程中有一些比较通用的大组件,比较合适单独提取出来作为一个项目 B,然后在

  • 开发阶段
    使用 npm link 的方式将 B 组件软链接到 A 项目中

  • 生产阶段
    使用 npm i B -S 的方式将 B 组件作为 A 项目的 dependencies

A、B 都依赖于 react , 在开发阶段通过 npm link B 的方式 B 映入到 A 中时问题出现了

Uncaught Invariant Violation: addComponentAsRefTo(…): Only a ReactOwner can have refs. You might be adding a ref to a component that was not created inside a component’s render method, or you have multiple copies of React loaded (details: fb.me/react-refs-must-have-owner).

查找问题

然后我就顺藤摸瓜到了 https://reactjs.org/warnings/refs-must-have-owner.html, 官方给出了三种可能

  • You are trying to add a ref to a functional component
  • You are trying to add a ref to an element that is being created outside of a component’s render() function
  • You have multiple (conflicting) copies of React loaded (eg. due to a misconfigured npm dependency)

    第一种情况说可能是我在函数式组件里面使用到了 ref 属性,检查代码后发现并没有,所以这种原因可以排除;

    第二种情况可能是我在 render 方法外定义的组件中使用到了 ref, 经过检查也没有,所以也排除了这个原因;

    只剩下最后一种情况了,npm 中同时存在 2 个 react ,问题终于找到了

    尝试解决

  1. 首先想到的是:会不会是因为用的其他 React 第三方库作者在开发组件库的时候将 react 写进了 dependenices 中,导致 npm 在安装的时候安装了 2 次,然后排查了下项目依赖的第三方库中比较可疑的,还真发现了一个,然后找作者解决了这个问题,然后再试了下,遗憾的是并没有解决到问题;

  2. 然后在想可能还有些没有排查干净,于是新建了一个无任何其他第三方的 demo 项目进行测试,发现还是有这个问题,说明问题应该不是因为将 react 放在了 dependencies,而没有放在 peerDependencies 中造成的.

  3. 然后尝试将 B 组件编译打包发布到 npm 仓库中去,然后像上面提到的生产环境使用的方式去使用,神奇了,居然不报上麦的错误了,程序能正常 run 起来了,这不正好说明是 是使用 npm link 的锅么?

    解决方法

既然确定了问题所在,那就带着问题去 Google 上搜索 npm link react , 第一条结果就有描述, 原来是因为通过 npm link 的方式添加进来的组件,这些组件中使用到的 react 会在源地址中去查找 react, 然后本身 A 项目已经包含了一个版本的 react,所以就会提示包含了 2 个版本的 react, 解决方案可以在 webpack config 文件中配置 alias, 将 react 手动指向 A 项目中的 node_moudules/react, 如下:

1
2
3
4
5
6
7
8
9
module.exports = {
...
resolve: {
alias: {
react: path.resolve('./node_modules/react'),
}
}
...
}

这样就能确保只会在一个地方去找 React 了。

【完】

参考:

  • https://github.com/facebook/create-react-app/issues/393

  • https://stackoverflow.com/questions/31169760/how-to-avoid-react-loading-twice-with-webpack-when-developing/38818358#38818358

tmux使用体验

发表于 2018-02-26   |  

最近使用了一段时间传说中很牛逼终端工具 tmux,先看下官方介绍

tmux 是一个优秀的终端复用器类自由软件,功能类似 GNU Screen,但使用 BSD 许可发布。用户可以通过 tmux 在一个终端内管理多个分离的会话,窗口及面板,对于同时使用多个命令行,或多个任务时非常方便。

为啥开始使用它

经常有这样一个场景,我需要 SSH 远程到一台服务器上去做一些操作,然后在上面做的工作又不是连续的,可能会断断续续的做一些操作,但是一般服务器都有设置连接超时时间,所以经常用的时候发现 SSH 已被强行退出,所以急需要一个能保持服务器会话的工具, tmux 刚好满足这样的需求

特性

  • 一个虚拟终端可以管理多个会话,窗口和面板
  • 支持分屏,同时处理多个操作
  • 窗口、面板可以在会话间自由移动,切换
  • 丰富灵活的状态行展示
  • 支持自定义快捷键,依照个人习惯配置令操作更高效
  • 不受断网影响,避免丢失重要工作进度
  • 结对编程,方便演示与协作
  • 自带复制粘贴缓冲区管理
  • 脚本化配置,可配置多种操作环境

基本操作

在启用 tmux 命令的时候通常需要先按 tmux 前缀命令, 默认的前缀是 ctrl + b

快捷键参考
https://gist.github.com/ryerh/14b7c24dfd623ef8edc7

地瓜游戏加速使用体验

发表于 2018-02-17   |  

前沿

中国共产党第十九次全国代表大会(简称中共十九大)于2017年10月18日至24日在北京召开,在这期间国内大部分游(ke)戏(xue)加(shang)速(wang)服务溃不成军,当然也包括笔者之前使用的 云梯 (300元/年,30G/月),2017.12.02 该项目服务被关闭,当然倒下的还有千千万。

听说国外一些云服务器提供商还可以正常使用,然后笔者辗转去了买了国外比较出名的 搬瓦工 VPS,然后在它上面安装了 SS,速度还可以,价格在 $29.99 / 年,用着也还挺舒心的,但是笔者是一个爱分享的,看到身边有同事朋友不能愉快的上网,就慷慨地将我的账号分享给他们用,1、2个还好,可以4、5个人同时在使用的时候就不行了,正常网页打开都很慢,然后又开始了我的探索之路。

偶然间发现了一个免费提供 SS 账号的 网站,里面有大量的免费账号,大多数都能用,只是速度不是很稳定,失效性也很难保证,很多账号今天能用,明天就不能用了,所以得频繁切换账号,使用体验很不好,于是笔者就写了一段程序,支持自动抓取网站上的账号并生成 SS 客户端支持的导入配置文件,你可以很方便的导入配置文件,项目链接 。

经过上面一番折腾后,笔者手上以握众多账号,对于上网已经不愁的,但是免费账号最大的弊端就是不稳定、速度慢、安全性不确定,更重要的原因是自从开源这段程序后很多小白用户寻求我帮忙,要么是不知道怎么安装环境,要么是代码不知道怎么运行,占据了很多业余时间和精力,尽管我很乐意帮助大家,但是人多了以后确实忙不过来,精力也有限。

回归正题

经历上面那么多事情以后我明白了一个道理:免费的才是最贵的!

我们花费很多时间在寻找一款免费的好用的上网工具,我们将这些时间利用起来做一些其他有意义的事情上受益可能会更大。我也一直在寻找一款好的上网工具来帮助我少操心,少走冤枉路,幸运的是我终于找到了,经过我使用了2个月的体验,今天我可以负责任的推荐给大家!

门户首页 - 地瓜游戏加速

优势

稳定,同一个账号不出意外可以连续使用1个星期;
速度快,香港线路延迟基本在 60 - 80 ms, 油管视频 720P 毫无压力;
服务好,官方提供了 QQ 用户群,在遇到部分线路不能使用、线路增加或者优惠活动都会在 QQ 群里通知大家;
价格便宜,最便宜的 58元/半年,30G/月;
套餐种类多,根据你的上网需求来购买包月、包年、买固定流量还是不限流量;
支持订阅,购买成功以后就会生成一个订阅链接,你可以在 SS 客户端添加订阅,并设置成自动更新,这样每次有新的服务变动,你的电脑就能同步更新了,很方便;
另外现在正处于活动期间,新用户下单可以享受半价优惠,后期续费也是按照这个价格来的,比方说笔者买的 99元包年套餐,新用户半价就是 50元/年,以后续费也是按照这个价格来,简直不要太实惠了!

工欲善其事必先利其器,利其器这种事能借助外部搞定的最好让外部去做,即便花点小钱,花小钱办大事,何乐而不为呢?

【完】

webpack踩坑记

发表于 2018-02-08   |  

TypeError: __webpack_require__(...) is not a function

错误出现场景

项目使用到了 webpack-dev-server , 启用了热更新和自动刷新的功能,当改变 js 文件后,webpack 能自动重新编译,并且浏览器也会自动刷新,但是当改变 css 文件后就会出现 TypeError: __webpack_require__(...) is not a function 这个问题

问题产生的原因

webpack 升级到 3.0 以后,插件 extract-text-webpack-plugin 的配置发生了变化

解决方法

对 extract-text-webpack-plugin 添加 allChunks 属性为 true

1
2
3
4
5
6
7
8
9
10
11
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
...
plugins: [
new ExtractTextPlugin({
filename: '[name].css',
allChunks: true // 添加属性
}),
],
...
}

npm 使用小技巧

发表于 2017-12-26   |  
  • 查看某个包的历史版本信息和详细 package.json 信息

    1
    npm view xx // 例如查看 react, npm view react
  • 看到很多包都带有 tag 的,使用下面的方式给包打 tag

    1
    2
    3
    npm publish --tag xxtagname // 发布带 tag 版本的包
    npm dist-tag ls xx // 查看 xx 包所以 tag 版本
    npm dist-tag add xx@1.0.0 lastest // 用 xx@1.0.0 替换 lastest 版本

关于 npm tag 参考 这个

浅谈cssinjs

发表于 2017-11-16   |  

浅谈 css in js

黑历史

在过去很长一段时间里大家都对网页开发的认识是这样的:

HTML : 负责网站的结构骨架
CSS : 负责美化网页
JavaScript : 负责数据和业务逻辑以及交互处理

谁要是把代码写成下面这样肯定会被认为是 freshman

1
2
3
4
5
6
<button
style="width: 60px; height: 48px"
onclick="console.log('hello world')"
>
确认
<button>

因为这样违背了 “行内样式” 和 “行内脚本” 的基本原则。

逆转

上面的这种思想统治了前端届很长一段时间(19xx ~ 2014), 直到 React 出现以后打破了这条“准则”, 因为 React 是组件结构,强制要求把 HTML、CSS、JavaScript 写在一起,对上面的例子改写成 React 如下形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React from 'react';
import ReactDom from 'react-dom';
export default class Button extends React {
render() {
return (
<button
style={{ width: 60px, height: 48px }}
onClick={()=> console.log('hello world')}
>
<button>
)
}
}
ReactDom.render(<Button />, document.getElementById('app'));
1
2
3
4
5
6
7
8
9
10
diff --git a/learning/src/actions/taskActions.js b/learning/src/actions/taskActions.js
index e0531d3..301cba0 100644
--- a/learning/src/actions/taskActions.js
+++ b/learning/src/actions/taskActions.js
@@ -108,7 +108,8 @@ export function saveTrainingJob(data, callback) {
body: JSON.stringify(data)
};
const successCallback = function () {
- callback && callback();
+ // callback && callback();

浏览器跨进程通信方法初探

发表于 2017-08-23   |  

背景

方法

  • WebSocket
  • Comet
    轮询的方式是比较传统的方式,它是在客户端设置一个定时器,指定一定时间后执行每个操作,或者递归ajax请求,伪代码如下:
    1、定时器(也称轮询)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    $(function () {
    window.setInterval(function () {
    $.get("/api/getmessage",
    { "timed": new Date().getTime() },
    function (data) {
    $("#root").append("[data: " + data + " ]<br/>");
    });
    }, 3000);
    });
    ```
    这个方式的优点是后端写起来比较容易,不用考虑过多的因素,但是缺点也很明显,请求中大部分是无用的,浪费带宽和服务器资源。
    2、递归ajax(也称长轮询)
    ``` js
    $(function () {
    (function longPolling() {
    $.ajax({
    url: "/api/getmessage",
    data: { "timed": new Date().getTime() },
    dataType: "text",
    timeout: 5000,
    error: function (XMLHttpRequest, textStatus, errorThrown) {
    $("#state").append("[state: " + textStatus + ", error: " + errorThrown + " ]<br/>");
    if (textStatus == "timeout") { // 请求超时
    longPolling(); // 递归调用
    // 其他错误,如网络错误等
    } else {
    longPolling();
    }
    },
    success: function (data, textStatus) {
    $("#state").append("[state: " + textStatus + ", data: { " + data + "} ]<br/>");
    if (textStatus == "success") { // 请求成功
    longPolling();
    }
    }
    });
    });
    })();

    这种方式的优点是始终和服务器保持一个连接,如果当前连接请求成功更新完数据以后,马上又会建立一个新的连接,如果连接超时或者请求错误,同样会继续创建一个新的连接,节省了服务器和网络资源,提高了程序的性能,缺点就是服务器要维护一个长连接增加开销。

  • LocalStorage
    主要是利用 Web Storage API 中的 localStorage 或者 sessionStorage 的发生变化时触发 storage 事件的特性.

1
2
  • sharedWorker
  • BoardcastChannel

总结

资源

跨浏览器通信库

  • 封装localstorage

从webpack1.x 到 webpack2.x

发表于 2017-07-26   |  

从webpack1.x 到 webpack2.x

这周对去年年末做的一个产品准备发布出去,那时候写的脚手架工具是基于webpack1.13.2,还不支持动态导入import,之前都是将所有的代码打在一个bundle.js 中,首次加载的时间差不多要4-5s, 升级到2.0以后使用import方式,配合react-router的动态路由,将系统按照路由进行分割,这时候首页的加载速度差不多在2s左右,下面是我在升级过程中整理的一些笔记。

resolve

resolve.root, resolve.fallback, resolve.modulesDirectories

上面这些写法被一个单独的写法 resolve.modules 取代

before

1
2
3
4
5
6
module.exports = {
resolveLoader: {
fallback: 'path.join(__dirname, 'node_modules')',
modulesDirectories: [ path.join(__dirname, 'node_modules') ],
}
}

after

1
2
3
4
5
module.exports = {
resolve: {
modules: [ "node_modules"]
}
}

resolve.extensions

此选项不再需要传一个空字符串。此行为被迁移到 resolve.enforceExtension

before

1
2
3
4
5
module.exports = {
resolve: {
extensions: ['', '.js', '.jsx', '.ts', '.tsx', '.json'],
}
}

after

1
2
3
4
5
module.exports = {
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
}
}

module

module.loaders 改为 module.rules

旧的 loader 配置被更强大的 rules 系统取代,后者允许配置 loader 以及其他更多选项。 为了兼容旧版,module.loaders 语法仍然有效,旧的属性名依然可以被解析。 新的命名约定更易于理解,并且是升级配置使用 module.rules 的好理由

before

1
2
3
4
5
6
7
8
9
module.exports = {
module: {
loaders: [
{
test: /\.tsx?$/,
loader: 'ts-loader'
}
}
}

after

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
use: {
loader: 'ts-loader'
}
}
}
}

链式loader

就像在 webpack 1 中,loader 可以链式调用,上一个 loader 的输出被作为输入传给下一个 loader。 使用 rule.use 配置选项,use 可以设置为一个 loader 数组。 在 webpack 1 中,loader 通常被用 ! 连写。这一写法在 webpack 2 中只在使用旧的选项 module.loaders 时才有效

before

1
2
3
4
5
6
7
8
9
10
module.exports = {
module: {
loaders: [
{
test: /\.css$/,
loader: 'style-loader!css-loader!postcss-loader'
}
]
}
}

after

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}
]
}
}

取消「在模块名中自动添加 -loader 后缀」

before

1
2
3
4
5
6
7
8
9
10
module.exports = {
module: {
loaders: [
{
test: /\.css$/,
loader: 'style!css!postcss'
}
]
}
}

after

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}
]
}
}

这么改的原因

json-loader 不再需要手动添加

如果没有为 JSON 文件配置 loader,webpack 将自动尝试通过 json-loader 加载 JSON 文件

这么做的目的主要是消除 webpack、 nodejs和 browserify之间的差异

配置中的 loader 默认相对于 context 进行解析

在 webpack 1 中,默认配置下 loader 解析相对于被匹配的文件。然而,在 webpack 2 中,默认配置下 loader 解析相对于 context 选项。

这解决了「在使用 npm link 或引用 context 上下文目录之外的模块时,loader 所导致的模块重复载入」的问题。

Plugin 插件

UglifyJsPlugin sourceMap

UglifyJsPlugin 的 sourceMap 选项现在默认为 false 而不是 true。这意味着如果你在压缩代码时启用了 source map,或者想要让 uglifyjs 的警告能够对应到正确的代码行,你需要将 UglifyJsPlugin 的 sourceMap 设为 true

before

1
2
3
4
5
6
module.exports = {
devtool: "source-map",
plugins: [
new UglifyJsPlugin()
]
}

after

1
2
3
4
5
6
7
8
module.exports = {
devtool: "source-map",
plugins: [
new UglifyJsPlugin({
sourceMap: true
})
]
}

UglifyJsPlugin warnings

UglifyJsPlugin 的 compress.warnings 选项现在默认为 false 而不是 true。 这意味着如果你想要看到 uglifyjs 的警告信息,你需要将 compress.warnings 设为 true

before

1
2
3
4
5
6
module.exports = {
devtool: "source-map",
plugins: [
new UglifyJsPlugin()
]
}

after

1
2
3
4
5
6
7
8
9
10
module.exports = {
devtool: "source-map",
plugins: [
new UglifyJsPlugin({
compress: {
warnings: true
}
})
]
}

UglifyJsPlugin 不再压缩 loaders

UglifyJsPlugin 不再压缩 loaders。在未来很长一段时间里,需要通过设置 minimize:true 来压缩 loaders

为了兼容旧的 loaders,loaders 可以通过插件来切换到压缩模式:

before

1
2
3
4
5
6
7
8
9
module.exports = {
ts: {
compiler: 'ntypescript',
configFileName: '.tsconfig.json',
},
postcss: function () {
return [autoprefixer, precss, px2rem(PX2REM_OPTIONS)];
},
}

after

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module.exports = {
minimize: true,
plugins: [
new webpack.LoaderOptionsPlugin({
options: {
postcss: function() {
return [autoprefixer, precss, px2rem(PX2REM_OPTIONS)];
},
ts: {
compiler: 'ntypescript',
configFileName: '.tsconfig.json',
}
}
})
]
}

移除 DedupePlugin

BannerPlugin - 破坏性改动

BannerPlugin 不再接受两个参数,而是只接受单独的 options 对象
before

1
2
3
4
5
module.exports = {
plugins: [
new webpack.BannerPlugin('Banner', {raw: true, entryOnly: true});
]
}

after

1
2
3
4
5
module.exports = {
plugins: [
new webpack.BannerPlugin({banner: 'Banner', raw: true, entryOnly: true});
]
}

默认加载 OccurrenceOrderPlugin

before

1
2
3
4
5
6
7
module.exports = {
plugins: [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
]
}

after

1
2
3
4
5
6
7
module.exports = {
plugins: [
//new webpack.optimize.DedupePlugin(),
//new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
]
}

ExtractTextWebpackPlugin - 破坏性改动

ExtractTextPlugin 需要使用版本 2,才能在 webpack 2 下正常运行
这一插件的配置变化主要体现在语法上
before

1
2
3
4
5
6
7
8
9
10
module.exports = {
module: {
rules: [
{
test: /\.less$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader!postcss-loader!less-loader")
}
]
}
}

after

1
2
3
4
5
6
7
8
9
10
11
12
13
module.exports = {
module: {
rules: [
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
}
]
}
}

在插件中
before

1
2
3
4
5
module.exports = {
plugins: [
new ExtractTextPlugin('[name].[contenthash].css'),
]
}

after

1
2
3
4
5
6
7
module.exports = {
plugins: [
new ExtractTextPlugin({
filename: '[name].[contenthash].css'
}),
]
}

通过 options 配置 loader

你不能再通过 webpack.config.js 的自定义属性来配置 loader。只能通过 options 来配置

before

1
2
3
4
5
6
7
8
9
module.exports = {
ts: {
compiler: 'ntypescript',
configFileName: '.tsconfig.json',
},
postcss: function () {
return [autoprefixer, precss, px2rem(PX2REM_OPTIONS)];
},
}

after

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports = {
plugins: [
new webpack.LoaderOptionsPlugin({
options: {
postcss: function() {
return [autoprefixer, precss, px2rem(PX2REM_OPTIONS)];
},
ts: {
compiler: 'ntypescript',
configFileName: '.tsconfig.json',
}
}
})
]
}

ES2015 的代码分割

在 webpack 1 中,可以使用 require.ensure作为实现应用程序的懒加载 chunks 的一种方法

1
2
3
4
5
6
7
8
9
10
const routes = [
{
path: '/',
getIndexRoute (nextState, cb) {
require.ensure([], require => {
cb(null, { component: require('CONTAINERS/ProjectList) })
}, 'projectlist')
},
}
]

ES2015 模块加载规范定义了 import() 方法,可以在运行时(runtime)动态地加载 ES2015 模块。webpack 将 import() 作为分割点(split-point)并将所要请求的模块(requested module)放置到一个单独的 chunk 中。import() 接收模块名作为参数,并返回一个 Promise。

1
2
3
4
5
6
7
8
9
10
11
12
const routes = [{
component: ProjectHeader,
childRoutes: [
{
path: '/',
getComponent(location, cb) {
System.import('CONTAINERS/ProjectList')
.then(loadRoute(cb)) //为每个可能的路由创建独立的 chunk
.catch(errorLoading)
}
}
}]

好消息是:如果加载 chunk 失败,我们现在可以进行处理,因为现在它基于 Promise

可以传递部分表达式给 import()。这与 CommonJS 对表达式的处理方式一致(webpack 为所有可能匹配的文件创建 context)。

import() 为每一个可能的模块创建独立的 chunk。

react-storybook 简单入门

发表于 2017-07-20   |  

React Storybook

官方说法

Storybook is a development environment for UI components. It allows you to browse a component library, view the different states of each component, and interactively develop and test components.

通俗的说

Storybook 为UI组件开发提供了一个开发环境,你可以改变组件不同状态、不同交互,通过可视化的界面来查看组件的形态。说白了就是一个通过可视化的界面来写测试。


Demo

顾虑

在引入一个新库之前你可能最担心的是这玩意儿会不会对你现有的代码产生影响,storybook也考虑到你的顾虑,你可以放心地使用,因为它完全独立与你的组件库

├── .storybook
│ ├── config.js
│ └── webpack.config.js
├── stories
│ ├── your_first_story.js
│ └── index.js
├── src
│ ├── component
│ ├──—- component.js
│ └──—- index.js

使用方法

1、全局安装storybook cli命令行工具

npm i -g @storybook/cli

2、进入到你需要创建storybook的项目中去,执行

getstorybook

3、执行完上面命令后会在你的项目中生成 .storybook 和 stories 2个文件夹,并在 package.json 的scripts中生成

1
2
3
"test:unit:watch": "cross-env NODE_ENV=test babel-node test/watch.js unit",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"

3个命令,然后执行

npm run storybook

就会在启动一个端口为6006的服务,然后在浏览器中打开就可以看到默认的界面了。

写故事

接下来的工作就是编写你的story!

先来看stories目录下官方给的示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/** stories/index.js **/
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';
import { Button, Welcome } from '@storybook/react/demo';
storiesOf('Welcome', module).add('to Storybook', () => <Welcome showApp={linkTo('Button')} />);
storiesOf('Button', module)
.add('with text', () => <Button onClick={action('clicked')}>Hello Button<\/Button>)
.add('with some emoji', () => <Button onClick={action('clicked')}>😀 😎 👍 💯</Button>);

配置

在编写好了 Story 之后需要告诉 Storybook 应该如何加载,需要进行一些简单的配置,只要添加如下类似的内容到·.storybook/config.js 中

1
2
3
4
5
6
7
8
import { configure } from '@storybook/react';
import React from 'react';
function loadStories() {
require('../stories');
}
configure(loadStories, module);

addDecorator

在使用material-ui的时候所有的组件都必须给定 theme context ,我们不可能在每一个测试组件中都不给它外面套一层定义 context 的父组件,所以 stroybook 提供了一个 addDecorator 的方法,可以将 MuiThemeProvider作用于测试组件之上,配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { configure, addDecorator } from '@storybook/react';
import React from 'react';
// 使用 material-ui 需配置
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
addDecorator((getStory) => (
<MuiThemeProvider muiTheme={getMuiTheme()}>
{getStory()}
</MuiThemeProvider>
))
import injectTapEventPlugin from 'react-tap-event-plugin';
injectTapEventPlugin();
function loadStories() {
require('../stories');
require('../stories/datepicker_single');
}
configure(loadStories, module);

你还可以在其中配置redux等来管理状态

参考

官方github地址
React Conf 2017 Thuret 分享视频

一些小技巧(持续更新中...)

发表于 2017-06-04   |  

1、 让chrome浏览器支持es7新语法

确保浏览器升级到最新版本(笔者目前版本 58.0.3029.110 (64-bit))

在地址栏输入:chrome://flags/#enable-javascript-harmony, 然后重启浏览器,此时浏览器支持es7实验性语法了。

打开控制台试试:

1
const cubed = 3 ** 3;

打印出cubed,看到27说明成功了!

2、node原生支持inspect

对需要调试的js文件执行:

1
node --inspect XX.js

然后在下面会出现一串以“chrome-devtools://” 开头的地址,将这串地址拷贝在浏览器中就可以将调试前端代码那样调试服务器端代码了

3、统计代码行数

1
2
# 包含注释,跳过空行
find . -name "*.js" | xargs grep -v "^$" | wc -l

4、解析base64
v2ex 论坛上经常看到一些关于邮箱或者 QQ 信息的,为了保密所以一般都会将其转换为 base64,那么他们是怎么做到的呢? 其实就在浏览器的控制台就可以做到。

  • 字符串转 base64
1
2
btoa('hello world')
//输出 aGVsbG8gd29ybGQ=
  • 将 base64 转字符串
1
2
atob('aGVsbG8gd29ybGQ=')
// 输出 hello world
  • 在 markdown 中使用 patch 作为语言可以达到 git diff 效果
1
2
3
4
5
6
7
8
9
10
diff --git a/learning/src/actions/taskActions.js b/learning/src/actions/taskActions.js
index e0531d3..301cba0 100644
--- a/learning/src/actions/taskActions.js
+++ b/learning/src/actions/taskActions.js
@@ -108,7 +108,8 @@ export function saveTrainingJob(data, callback) {
body: JSON.stringify(data)
};
const successCallback = function () {
- callback && callback();
+ // callback && callback();
123
phobal

phobal

web developer

21 日志
1 分类
8 标签
GitHub 微博 知乎
© 2018 phobal
由 Hexo 强力驱动
主题 - NexT.Pisces