mytac / blogs Goto Github PK
View Code? Open in Web Editor NEWjust my blogs,只有watch才能追踪!!start只是收藏!!:boom: 更多点击=》
Home Page: https://www.jianshu.com/u/5ed1bc7b3716
just my blogs,只有watch才能追踪!!start只是收藏!!:boom: 更多点击=》
Home Page: https://www.jianshu.com/u/5ed1bc7b3716
react-native init
命令行创建的rn项目,并且你的rn版本在0.38以上,则无需安装了。不太清楚的话就看一下package.json
文件中是否包含以下代码: // package.json
"scripts": {
"test": "jest"
},
"jest": {
"preset": "react-native"
}
如果没有就安装一下npm i jest --save-dev
,并把上述代码添加到package.json
文件的对应位置。
3. 以上步骤完成后,简单运行npm run test
测试一下jest是否配置成功。但我们没有写测试用例,终端会打印no tests found
。这时就配置完成了。
写一个组件
import React from 'react';
import {
Text, View,
} from 'react-native';
import PropTypes from 'prop-types';
const PostArea = ({ title, text, color }) => (
<View style={{ backgroundColor: '#ddd', height: 100 }}>
<Text style={{ fontSize: 30 }}>{title}</Text>
<Text style={{ fontSize: 15, color }}>{text}</Text>
</View>
);
export default PostArea;
在项目根目录下找到__test__
文件夹,现在,让我们使用React的测试渲染器和Jest的快照功能来与组件进行交互,并捕获呈现的输出并创建一个快照文件。
// PostArea_test.js
import 'react-native';
import React from 'react';
import PostArea from '../js/Twitter/PostArea';
import renderer from 'react-test-renderer';
test('renders correctly', () => {
const tree = renderer.create(<PostArea title="title" text="text" color="red" />).toJSON();
expect(tree).toMatchSnapshot();
});
然后在终端运行npm run test
或jest
。将会输出:
PASS __tests__\PostArea_test.js (6.657s)
√ renders correctly (5553ms)
› 1 snapshot written.
Snapshot Summary
› 1 snapshot written in 1 test suite.
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 1 added, 1 total
Time: 8.198s
Ran all test suites.
同时,在__test__文件夹下会输出一个文件,即为生成的快照。
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<View
style={
Object {
"backgroundColor": "#ddd",
"height": 100,
}
}
>
<Text
accessible={true}
allowFontScaling={true}
disabled={false}
ellipsizeMode="tail"
style={
Object {
"fontSize": 30,
}
}
>
title
</Text>
<Text
accessible={true}
allowFontScaling={true}
disabled={false}
ellipsizeMode="tail"
style={
Object {
"color": "red",
"fontSize": 15,
}
}
>
text
</Text>
</View>
`;
在下一次运行测试的时候,呈现的输出将与之前创建的快照进行比较。快照应该和代码一起提交。当快照测试失败的时候,就需要检查是否有意或无意的更改。如果是和预期中的变化一样,调用jest -u
来覆盖当前的快照。
我们来更改一下原来的代码:把第二行<Text>
的字号改为14.
<Text style={{ fontSize: 14, color }}>{text}</Text>
这时,我们再运行jest
。这时终端将会抛出错误,并指出了错误位置
。
因为这段代码是我们有意改的,这时运行jest -u
,快照被覆盖。再执行jest
则不会报错了~
【详细介绍】
3. material-ui -- google material design的react component的解决方案
4. ant-design-pro -- 开箱即用的中台前端/设计解决方案。
官网
react-native-simple-radio-button -- React Native简单和方便的动画单选按钮组件
driver.js -- 一个轻量级的,无需依赖的JavaScript引擎,可以在整个页面中推动用户的注意力
这个库可以用来给某个元素高亮,高度可定制化;
官方示例
react-native-menu -- 适用于Android和iOS的灵活下拉菜单组件,与Android的Spinner类似。
marktext -- 新一代Markdown编辑器,运行在MacOS Windows和Linux平台上
一款高性能的 Markdown 编辑器,运行于 Mac、Windows 和 Linux 平台。其简洁的外观,流程的运行,给您带来舒适的写作体验。还可以斗图LOL
lipstick -- 口红颜色可视化 - 为什么你的女神总缺一支口红这个挺有意思的,如果你不知道怎么给女朋友选口红挑色号,可以看看这个项目;本来以为是爬虫,其实是写好的json;不过这样看比较直观。
cows -- 用ASCII生成的一组不同形态的牛超级有意思,贴一段代码:
. /\ . .
. / \ . .
/ \ . . *
/ \ *
| (__) | . . **
. /| (oo) |\ **
/ | /\/\ | \ . . *
. / |=|==|=| \ . *
. / | | | | \ .
/ USA | ^||^ |NASA \ .
|______| ^^ |______| .
. (__||__) . .
. /_\ /_\ . . .
!!! !!!
The cow that jumped over the moon.
oni -- 现代的模态编辑器Oni是一种新型的编辑器,专注于最大限度地提高生产力 - 将模态编辑与现代编辑期望的功能相结合。
is-thirteen -- 检查一个数字是否等于13。本来以为是个很没有用的库(其实还是没啥用),可以看看代码,非常有意思
推荐亲测好用的梯子(随着GFW的不断升级,很多梯子也不能用了)
https://github.com/meimeihi/tizi.github.io
wasm-particles -- 这是一个视觉基准,每个粒子从WebAssembly更新,然后用WebGL绘制到屏幕上。
lottie-web -- 分析Adobe After Effects动画与Bodymovin一起导出为json,并在移动设备上原生呈现
animateplus -- 是一个专注于性能和创作灵活性的JavaScript动画库。它的目标是提供稳定的60 FPS,重量小于2 KB(缩小和压缩),使其特别适合移动设备。
react-pwa -- 具有通用路由(服务器端渲染)与React&Redux的渐进式Web应用程序,内置SEO,实现最大页面速度
react-chartkick -- 用一行React创建漂亮的图表再也不用配各种插件啦!
ReactPrimer -- React组件原型工具,生成关联的类组件代码。
我觉得这是个神器,react党一定要下一个玩玩
react-aux -- 一个用于渲染多个元素的自我消除组件
react v16之前,从一个组件中返回多个元素以将它们包装在一个辅助元素中
const Root = () => {
return <div>
<p>Hello, World!</p>
<p>I am a demo for react-aux.</p>
</div>;
};
产生如下的dom
<div>
<p>Hello, World!</p>
<p>I am a demo for react-aux.</p>
</div>
使用react-aux
const Aux = (props) => {
return props.children;
};
const Root = () => {
return <Aux>
<p>Hello, World!</p>
<p>I am a demo for react-aux.</p>
</Aux>;
};
后者生成没有包装节点的段落元素:
<p>Hello, World!</p>
<p>I am a demo for react-aux.</p>
Front-End-Checklist -- 现代网站和一丝不苟的开发人员的完美的前端清单
用这个清单来检查你的项目吧~
purgecss -- 删除未使用的CSS在gulp、webpack中都可配置!
js-code-to-svg-flowchart -- 一个将任何JavaScript代码转换成漂亮的SVG流程图的可视化库。学习其他的代码。设计你的代码。重构代码。文档代码。解释代码。
chalktalk -- 使用类似黑板的界面,演示者可以创建动画数字草图并与其进行交互,以便在现场演示或对话中展示想法和概念。
nba-go -- 最好的NBA CLI。这个究极酷,虽然看不了实况但可以随时追踪比分,很酷!!
Sudachi -- 日常个人计划的时间表编辑器。 这个目前只支持macOS
下载地址
cs-playground-react -- 用JavaScript实现常用排序算法和数据结构的交互式概述这个超赞的,作者把原理和算法的代码都放到了项目中,点击演示地址进行操作
不知道大家有没有这样的体验,当你专心浏览网站内容的时候,会从右下角弹出小广告,当你点击关闭的时候,又会跳转到另外的链接,你以为是网站搞的鬼,但你尝试去联系管理员时,人家表示不背这个锅。其实这背后是你的网络运营商在借刀杀人~
市面上常见的运营商劫持主要是http劫持,https劫持和dns劫持。我们经常看到的右下角弹小广告的一般是http劫持,如果将http转为https可以减少被运营商劫持的机率,但也可能会被伪造证书,仍然会被劫持。所以我们需要在自己的程序中设置一个监听器来监控dom中劫持插入的脚本。
需要在运营商插入非法代码之前监听dom变动,以下的例子只检查<script>
标签。
主要使用html5的一个特性MutationObserver
来观察dom变动,注意兼容性,如果你不打算兼容ie浏览器,MutationObserver
是最好的选择。
const observer = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
const isSupportObserver = !!observer
if (isSupportObserver) {
console.info('The preventor is running...')
new observer((records) => {
// ....这里处理dom变动
}).observe(watchNode, { childList: true, attributes: true })
} else {
console.error('Your platform is not supported with "window.MutationObserver",please update.')
}
经过上面的步骤,我们得到了网页中所有的<script>
,之后筛选得到运营商注入的脚本信息,进行下一步操作。(你可以设置白名单,根据白名单进行过滤,设置白名单的过程这里不具体展开了,详见文章后面的完整代码。)
records.forEach(record => {
const addedNodes = Array.from(record.addedNodes)
addedNodes.forEach((node) => {
if (node.tagName && ~filterTagList.indexOf(node.tagName.toLowerCase())) {
const isInWhiteList = whiteRegList.some((reg) => reg.test(node.src))
if (!isInWhiteList) {
badInjections.push({ badNode: node, badSource: node.src })
}
}
})
})
我们在上一步得到的运营商注入的非法脚本,需要进行拦截和处理,最后发给运营商或举报到工信部。(注意这里,在head头部同步插入的脚本无法被监听到)。
badInjections.forEach(({ badNode, badSource }) => {
badNode.remove(); // 移除非法注入节点
console.warn(`The source "${badSource}" is invalid,removed it already.`)
})
class HijackPreventor {
constructor(watchNode = Array.from(document.getElementsByTagName('body'))[0],report=()=>{}) {
this.whiteList = []
this.whiteRegList = []
this.filterTagList = ['script']
this.report = report
this.setObserver(watchNode)
}
/**
* 设置域名白名单
* @param {Array|String} list
*/
setWhilteList(item) {
if (item instanceof Array) {
this.whiteList = item
} else if (typeof item === 'string') {
this.whiteList.push(item)
} else {
console.error('[HijackPreventor]: Please set an Array or String type parameter to "setWhilteList" ')
return;
}
this.whiteRegList = this.whiteList.map(wl =>
new RegExp('/.+?\/\/' + wl + '|\/\/' + wl + '|.+?\.' + wl + '|^' + wl)
)
}
/**
* 挂载dom监听器
* @param {Node} node
*/
setObserver(watchNode) {
const observer = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
const isSupportObserver = !!observer
if (isSupportObserver) {
console.info('[HijackPreventor]: The preventor is running...')
new observer((records) => {
this.filterSafeScript(records)
}).observe(watchNode, { childList: true, attributes: true })
} else {
console.error('[HijackPreventor]: Your platform is not supported with "window.MutationObserver",please update.')
}
}
/**
* 获取非法注入
* @param {Node} records
*/
filterSafeScript(records) {
const { filterTagList, whiteRegList } = this
let badInjections = []
records.forEach(record => {
const addedNodes = Array.from(record.addedNodes)
addedNodes.forEach((node) => {
if (node.tagName && ~filterTagList.indexOf(node.tagName.toLowerCase())) {
const isInWhiteList = whiteRegList.some((reg) => reg.test(node.src))
if (!isInWhiteList) {
badInjections.push({ badNode: node, badSource: node.src })
}
}
})
})
this.handleBadInjections(badInjections)
}
/**
* 处理非法注入
* @param {Array} badInjections
*/
handleBadInjections(badInjections) {
badInjections.forEach(({ badNode, badSource }) => {
badNode.remove(); // 移除非法注入节点
console.warn(`[HijackPreventor]: The source "${badSource}" is invalid,removed it already.`)
})
this.report(badInjections)
}
/**
* 模拟插入script,用来测试
* @param {Node} appendNode
*/
mockHijack(appendNode = document.getElementsByTagName('body')[0]) {
const node = document.createElement("script");
node.src = 'https://cdn.bootcss.com/zepto/1.0rc1/zepto.min.js'
appendNode.appendChild(node)
}
}
按下图方法投诉到工信部,一步到位!
我在逛github时,经常会看到像下面这样的提交记录,是不是很可爱(也很cool~~)
使用gitmoji来编写commit,使每次的commit能够更加直观,维护起来也更加方便:
我总结了一下经常会用到的gitmoji,以及他用来代表的提交类型,如下表所示:
表情 | 代码 | 代表内容 |
---|---|---|
🎨 | :art: |
优化代码结构或格式 |
⚡ | :zap: |
提升性能 |
🔥 | :fire: |
移除代码或文件 |
🔧 | :wrench: |
修改配置文件 |
🐛 | :bug: |
修复bug |
🚑 | :ambulance: |
修复关键程序 |
✨ | :sparkles: |
介绍新功能 |
📝 | :memo: |
写文档 |
🚀 | :rocket: |
部署项目 |
💄 | :lipstick: |
更新UI和样式文件 |
🎉 | :tada: |
初始提交 |
✅ | :white_check_mark: |
添加测试 |
🔒 | :lock: |
解决安全问题 |
🍎 | :apple: |
修复macOS上的东西 |
🐧 | :penguin: |
在Linux上修复一些东西 |
🏁 | :checkered_flag: |
修复Windows上的东西 |
🤖 | :robot: |
解决Android上的东西 |
🍏 | :green_apple: |
解决iOS上的东西 |
🔖 | :bookmark: |
发布/版本标签 |
🚨 | :rotating_light: |
删除linter警告 |
🚧 | :construction: |
未完工程 |
💚 | :green_heart: |
修复CI构建 |
⬇️ | :arrow_down: |
降级依赖 |
⬆️ | :arrow_up: |
升级依赖 |
👷 | :construction_worker: |
添加CI构建系统 |
📈 | :chart_with_upwards_trend: |
添加分析或跟踪代码 |
🔨 | :hammer: |
重构代码 |
➕ | :heavy_plus_sign: |
添加依赖 |
➖ | :heavy_minus_sign: |
删除依赖 |
✏️ | :pencil2: |
修正错别字 |
💩 | :hankey: |
编写需要改进的不好的代码 |
⏪ | :rewind: |
恢复更改 |
🔀 | :twisted_rightwards_arrows: |
合并分支 |
📦 | :package: |
更新编译的文件或包 |
👽 | :alien: |
由于外部API更改而更新代码 |
🚚 | :truck: |
移动或重命名文件 |
📄 | :page_facing_up: |
添加或更新许可证 |
💥 | :boom: |
介绍突破性变化 |
🍱 | :bento: |
添加或更新静态资源 |
👌 | :ok_hand: |
由于代码审阅更改而更新代码 |
♿ | :wheelchair: |
改善无障碍 |
💡 | :bulb: |
记录源代码 |
💬 | :speech_balloon: |
更新文本或字面量 |
🗃️ | :card_file_box: |
执行与数据库相关的更改 |
🔊 | :loud_sound: |
添加日志 |
🔇 | :mute: |
删除日志 |
👥 | :busts_in_silhouette: |
添加贡献者 |
🚸 | :children_crossing: |
改善用户体验/可用性 |
之前我写过一篇有关于git提交的文档《用gitmoji来提交你的git commit吧》,然而在实际上应用并不是很方便,大多情况得翻阅gitmoji对照表来写commit,且并不规范,仅仅适用于自己开发的项目,放到团队上commit可读性不高。最近翻阅了一篇文章《你可能会忽略的 Git 提交规范》,才知道自己之前写的commit非常随意,在项目初期,写的还蛮正规的:
然而之后懒了,前面的tag也没加。(所以说,好的习惯要坚持,只要有一次没做,后面就容易堕怠)
去审查一下你自己的commit~如果你不习惯使用git GUI,在bash中运行以下命令:
$ git log [tag name/branch name] HEAD --pretty=format:%s
建议的格式如下:
<type>(<scope>): <subject>
用于声明此次commit的主要目的类别:
feat:feature、发布新功能
fix:修复bug
docs:更新文档
style: 代码格式
refactor:代码重构
test:增加测试
chore:构建过程或辅助工具的变动
用于说明commit影响的范围;如数据层(model),视图层(view),控制层(controller)等。
commit的主题描述,少于50个字。
其实在《Commit message 和 Change log 编写指南》这篇文章中都有很详细的描述,文中也提到了commit message有body和footer,用于详细描述和关闭issue的补充。不过个人觉得在subject中写这些内容已经足够了。
提到“hooks”
这个词我们应该并不陌生,比如vue
和react
都有自己的lifecycle hooks
,在git中分为客户端hooks
和服务端hooks
。在commit阶段中涉及到的是客户端hooks,其中客户端hooks包含:
pre-commit 钩子在键入提交信息前运行。 它用于检查即将提交的快照,例如,检查是否有所遗漏,确保测试运行,以及核查代码。 如果该钩子以非零值退出,Git 将放弃此次提交,不过你可以用 git commit --no-verify 来绕过这个环节。 你可以利用该钩子,来检查代码风格是否一致(运行类似 lint 的程序)、尾随空白字符是否存在(自带的钩子就是这么做的),或新方法的文档是否适当。
prepare-commit-msg 钩子在启动提交信息编辑器之前,默认信息被创建之后运行。 它允许你编辑提交者所看到的默认信息。 该钩子接收一些选项:存有当前提交信息的文件的路径、提交类型和修补提交的提交的 SHA-1 校验。 它对一般的提交来说并没有什么用;然而对那些会自动产生默认信息的提交,如提交信息模板、合并提交、压缩提交和修订提交等非常实用。 你可以结合提交模板来使用它,动态地插入信息。
commit-msg 钩子接收一个参数,此参数即上文提到的,存有当前提交信息的临时文件的路径。 如果该钩子脚本以非零值退出,Git 将放弃提交,因此,可以用来在提交通过前验证项目状态或提交信息。
post-commit 钩子在整个提交过程完成后运行。 它不接收任何参数,但你可以很容易地通过运行 git log -1 HEAD 来获得最后一次的提交信息。 该钩子一般用于通知之类的事情。
这里我们主要是在pre-commit
阶段来检查commit是否符合规范。
在提交代码前需要格式代码,这里用git hooks:pre-commit
。
针对暂存的git文件运行linters并且不要让垃圾代码滑入你的代码库!lint-staged的最新版本需要Node.js v6或更新版本。(在v7之前的lint-staged版本仍可与Node.js v4一起使用。)
在git hooks每个阶段执行脚本来避免垃圾代码的提交和push。
安装lint-staged
和husky
yarn add lint-staged husky
在package.json
写入:
"scripts": {
...
"precommit":"lint-staged"
},
"lint-staged": {
"*.js": ["eslint --fix","git add"]
},
这时当你执行git commit
操作时,一旦在暂存区存在eslint格式错误的代码,将会自动修复并加入缓存区(eslint --fix 无法自动修复的将会报错)。
如图:
这里我们使用另一个git hooks:commitmsg
,我们来安装validate-commit-msg
检查 Commit message 是否符合格式。
yarn add validate-commit-msg
在package.json
中配置:
"scripts": {
...
"commitmsg": "validate-commit-msg"
},
如果要进行自定义配置,我们可以自建一个文件.vcmrc
:
{
"types": ["feat", "fix", "docs", "style", "refactor", "perf", "test", "build", "ci", "chore", "revert",":art"],
"scope": {
"required": false,
"allowed": ["*"],
"validate": false,
"multiple": false
},
"warnOnFail": false,
"maxSubjectLength": 100,
"helpMessage": "",
"autoFix": false
}
随便写一个commit,会提示不符合规范。
让我们来写一个示例~
git commit -m "style: eslint"
测试一下,成功提交~~关于commit-msg这部分可以参考vue的commit.
写好commit也可以自动生成每个版本的更改,可以参考vue的changelog。
yarn add standard-version
在package.json
中配置:
"scripts": {
...
"release": "standard-version"
},
在发布新版本的时候,运行以下命令:
yarn release
如果按照commitizen规范书写你的commit-msg,即可在你项目中自动生成changelog,如图:
2018 react conf在今年十月底于las vegas举行,其中主讲人Ryan Florence,演示了使用最新版本的react其中的几个hooks api可以大幅减少react functional 组件的代码量,本篇文章将围绕这个内容进行补充和示范。(在文章的结尾有大会的油管连接,没有梯子的小伙伴可以在b站上自行搜索)。
比如现在有个需求,需要监听键盘输入内容,显示在屏幕的某块可见范围中,效果如下:
以下代码基于create-react-app
创建。
class App extends Component {
constructor(props) {
super(props)
this.state = {
words: ''
}
this.keyFunc = this.keyFunc.bind(this)
}
componentDidMount() {
window.addEventListener('keypress', this.keyFunc)
}
componentWillUnmount() {
window.removeEventListener('keypress', this.keyFunc)
}
keyFunc(e) {
this.setState(prev => ({
words: prev.words + e.key
}))
}
render() {
const { words } = this.state
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>{words}</p>
</header>
</div>
);
}
}
我们会发现仅仅为了绑定监听函数和使用state,就要调用两个钩子函数和声明为类组件,是不是有点小题大做了??那么我们看一下v16的react给我们带来的简洁高效!
首先安装
yarn add react@next,react-dom@next
import React, { useState, useEffect } from 'react';
function setIt(initWords) {
const [words, setWords] = useState(initWords);
const listener = (e) => { setWords(prev => (prev + e.key)) }
useEffect(() => {
window.addEventListener('keypress', listener);
console.log('aaa')
return () => window.removeEventListener('keypress', listener);
}, [])
return words
}
function App() {
const words = setIt('')
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>{words}</p>
</header>
</div>
);
}
看啊!虽然是functional component,却可以产生和class组件一样的效果,而且减少了代码量。那么useState
和useEffect
到底是干啥的呢?往下翻OVO
类似于setState
,但不支持state object扩展,如下
setState(prevState => {
// Object.assign would also work
return {...prevState, ...updatedValues};
});
useState
用法如下:
const [state, setState] = useState(initialState);
如果initState
是需要昂贵计算的结果,它也可以被延时提供,如下。
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
useEffect(didUpdate);
使用useEffect
,传递给它的函数将会在组件渲染到屏幕后运行。
在从屏幕上卸载组件之前,要清除监听器或者定时器以避免内存泄漏。useEffect
可以返回一个清除函数,来完成这项操作,如:
useEffect(() => {
const subscription = props.source.subscribe();
return () => {
// Clean up the subscription
subscription.unsubscribe();
};
});
在组件每次渲染完毕之后触发effect,在此期间如果有输入变化,总是会重新建立effect。这里就引出了useEffect
的第二个参数,他是一个数组类型,指的是产生effect所依赖的值。
比如我们将上述代码改为
useEffect(() => {
window.addEventListener('keypress', listener);
console.log('aaa')
return () => window.removeEventListener('keypress', listener);
}, [state.words])
指该effect依赖于state.effect
这个字段,如果它变化的话,将会触发effect函数,在此例中只要输入,就会触发effect中的函数(如果你尝试此例的话,当你的键盘在输入时,会看到控制台在打印aaa)。那么,如果我们传入空数组,useEffect
只会在componentDidMount
和componentWillUnmount
阶段触发,也就是仅仅触发一次(这时控制台只打印了一次aaa)。
虽然现在异步解决方案都用async/await
了,但co是一个小巧而精致的库,我们也可以学习一下他的实现原理。
co是一个函数库,用于generator的自动执行。
我们先定义一个generator,用来异步读下面几个文件
const gen=function *(){
const b=yield read('b.js')
console.log(b.length)
const c=yield read('c.js')
console.log(c.length)
}
其中,我们定义的read异步读取文件的函数为:
function read(file){
fs.readFile(file,'utf8',(err,res)=>{
g.next(res)
})
}
const g=gen()
g.next() //将分别输出两个文件的长度
虽然上面的代码也可以顺利异步执行generator函数,但是需要把next()
写到异步函数中,
增加了耦合度,而且还要创建generator的实例,代码很繁琐。
我们使用co重构上面的代码:
首先要改造一下异步函数的结构,co规定所有的异步函数必须是偏函数,称之为thunk函数:
function read(file){
return (fn)=>{
fs.readFile(file,'utf8',fn)
}
}
之后,只要把generator传到co中就大功告成了
const co=require('co')
co(gen)() //分别输出两个文件的长度
function co(fn) {
return function(done) {
const ctx = this;
const gen = fn.call(ctx);
let it = null;
function _next(err, res) {
if(err) res = err;
it = gen.next(res);
//{value:function(){},done:false}
if(!it.done){
it.value(_next);
}
}
_next();
}
}
这部分的核心代码为:
function _next(err, res) {
if(err) res = err;
it = gen.next(res);
//{value:function(){},done:false}
if(!it.done){
it.value(_next);
}
}
_next();
他的目的是递归调用generator的next(),直到done为true时,停止递归。
在调用第一次next()
的之后,it
为:
it={
value:(fn)=>{fs.readFile(file,'utf8',fn)},
done:false
}
在it.done
为false
的情况下,将_next()
这个函数作为实参传到(fn)=>{fs.readFile(file,'utf8',fn)}
中。
这时才正式执行读取文件的操作,res为读取的文件内容,之后再次执行_next()
。
第二次调用_next()
后,将res
值塞回gen
中,之后执行:
const b=res
console.log(b.length)
之后往后执行,遇到yield
又中断了,再次读取c.js
中的内容,之后又进入_next()
,再把res
值塞回gen
中,和上次一样执行下列代码:
const c=res
console.log(c.length)
之后再触发_next()
知道done
状态为true
。
这里的执行顺序比较乱,强烈建议大家去加断点多跑几遍!
function co(fn) {
return function(done) {
const ctx = this;
const gen = fn.call(ctx);
let it = null;
function _next(err, res) {
if(err) res = err;
it = gen.next(res);
//{value:function(){},done:false}
if(!it.done){
it.value(_next);
}
}
_next();
}
}
function read(file){
return (fn)=>{
fs.readFile(file,'utf8',fn)
}
}
const gen=function *(){
const b=yield read('error.js')
console.log(b.length)
const c=yield read('package.json')
console.log(c.length)
}
co(gen)()
import NanoEvents from 'nanoevents'
const emitter = new NanoEvents()
const unbind = emitter.on('tick', volume => {
summary += volume
})
function disable () {
unbind()
}
Nano ID -- 一个小巧,安全的URL友好的JavaScript字符串ID生成器。
[example]
var nanoid = require('nanoid')
model.id = nanoid() //=> "Uakgb_J5m9g~0JDMbcJqLJ"
viron -- 基于自动化设计的管理控制台你所做的只是创建一个API服务器和一个OAS2.0 json文件。然后viron管理工具已准备好使用。你不需要编写前端代码!
react-native-sideswipe简单的走马灯???
2.first-contributions -- 一个让初学者贡献开源的项目可以说是一个详细的贡献开源的教程
3.front-end-interview-handbook -- 几乎完整的答案,您可以用来面试潜在的候选人,测试自己或完全忽略“前端工作面试问题”
carbon -- 创建和分享源代码的美丽图像就是将自己的代码,生成图像,拥有很多高亮,还挺有意思的。
tetris -- 流行的游戏俄罗斯方块的克隆。其实就是个小游戏,感兴趣的可以看看源代码
OpenSC2K -- 由Maxis公司开发的SimCity 2000开源软件使用HTML5 Canvas API和SQLite编写的使用JavaScript编写的模拟城市 2000的开放源代码重建,并且构建在Electron上。
rekit -- 使用React,Redux和React-router构建可伸缩Web应用程序的工具包可以说是react的ide了
由于微信小程序在开发上不能安装npm依赖,和开发流程上也饱受诟病;Taro 是由京东·凹凸实验室(aotu.io)倾力打造的 多端开发解决方案,在本篇文章中主要介绍了使用taro搭建微信小程序的一些步骤和一个简单demo的实现。
先全局安装@tarojs/cli
$ npm install -g @tarojs/cli
$ yarn global add @tarojs/cli
之后我们初始化一个名为myApp
的项目:
$ taro init myApp
之后等待所有依赖安装完毕。
在命令行执行
$ npm run dev:weapp
taro将会进入微信小程序编译预览模式。我们打开微信开发者工具,将项目导入,会在预览窗口中看到hello world。这时我们就可以进行开发啦~~
生命周期方法 | 作用 | 说明 |
---|---|---|
componentWillMount | 程序被载入 | 对应微信小程序onLaunch |
componentDidMount | 程序被载入 | 对应微信小程序onLaunch ,在componentWillMount 之后执行 |
componentDidShow | 程序展示出来 | 对应微信小程序onShow |
componentDidHide | 程序被隐藏 | 对应微信小程序onHide |
不过当然也包含componentWillUnmout
和componentWillReceiveProps
等react原始生命周期函数,用来编写自定义组件。
在 Taro 中,路由功能是默认自带的,不需要开发者进行额外的路由配置。
// 跳转到目的页面,打开新页面
Taro.navigateTo({
url: '/pages/page/path/name'
})
// 跳转到目的页面,在当前页面打开
Taro.redirectTo({
url: '/pages/page/path/name'
})
// 传入参数 id=2&type=test
Taro.navigateTo({
url: '/pages/page/path/name?id=2&type=test'
})
我们可以使用this.$router.params
来获取路由上的参数。
Taro 以 微信小程序组件库 为标准,结合 jsx 语法规范,定制了一套自己的组件库规范。这部分可以自行去看文档。值得注意的是,小程序中的写法bind*
这种事件都要改成以on
开头。
现在使用taro构建一个很简单的demo;需要实现简单的组件调用,路由跳转传参等功能。
一个Swiper,一个列表组件:
// index.js
import Taro, { Component } from '@tarojs/taro'
import { View, Swiper,SwiperItem, Image } from '@tarojs/components'
import ListItem from '../../components/ListItem'
import './index.less'
import img0 from './img/img0.jpg'
import img1 from './img/img1.jpg'
import img2 from './img/img2.jpg'
export default class Index extends Component {
config = {
navigationBarTitleText: '首页'
}
skipToDetail(){
/* */
}
render() {
return (
<View className='index'>
<Swiper indicatorDots autoplay>
{[img0,img1,img2].map(img=>(<SwiperItem key={img}><Image src={img} /></SwiperItem>))}
</Swiper>
{listSet.map(item=>(<ListItem onClick={this.skipToDetail.bind(this)} description={item.description} title={item.title} key={item.title} />))}
</View>
)
}
}
const listSet=[
{title:'标题一',description:'描述一'},
{title:'标题二',description:'描述二'},
{title:'标题三',description:'描述三'},
]
列表组件,注意这里有个坑,就是不能直接调用函数props,会报一个警告,说是没有找到onClick handler
。查阅官方文档后,在issues 215中找到了答案,官方说是会在以后的版本中修复这个bug,目前先按下面代码写。
import Taro, { Component } from '@tarojs/taro'
import { View, Text } from '@tarojs/components'
import './index.less'
export default class ListItem extends Component {
skipToDetail(){
/* 必须得这样写=。= */
this.props.onClick()
}
render() {
const { title, description } = this.props
return (
<View className='list-item' onClick={this.skipToDetail}>
<View><Text>{title}</Text></View>
<View><Text>{description}</Text></View>
</View>
)
}
}
我们在入口文件添加新的路由,指向详情页detail:
这里需要注意先配置好pages,然后再写这个组件,要不再编译的时候会找不到这个页
// app.js
config = {
pages: [
'pages/index/index',
'pages/detail/index'
],
...
}
创建详情页:
import Taro, { Component } from '@tarojs/taro'
import { View } from '@tarojs/components'
import './index.less'
export default class Index extends Component {
componentWillMount () {
}
config = {
navigationBarTitleText: '详情页'
}
render () {
const {title,description}=this.$router.params
return (
<View>
...
</View>
)
}
}
要求点击每个列表项,需要进行跳转,并把当前的title和description传到详情页。需要在首页中的skipToDetail
中补充以下内容:
skipToDetail({title,description}){
Taro.navigateTo({
url: `/pages/detail/index?title=${title}&description=${description}`
})
}
并在render方法中将参数传入这个函数中:
render() {
return (
<View className='index'>
...
{listSet.map(item=>(<ListItem onClick={this.skipToDetail.bind(this,item)} description={item.description} title={item.title} key={item.title} />))}
</View>
)
}
在github上找了很多图表类的插件,大多都是直方图之类的显示完好,到了饼图都差强人意。因为react-native版本更新的快,用不同的版本必须适配不同的第三方库,所以很多repo维护的都比较慢(或者类似于react-native-chart就直接不更新了),如果你非得用某个第三方库,在你当前版本不适合的情况下,要么换库,要么就降级react-native版本。这导致在开发上非常棘手,因为是团队项目,不可能去降级react-native版本的啊,在找适合的第三方库的过程中也是一度陷入了绝望之中。直到在react-native-chart的readme中发现了victory-native。这里,我不得不吐槽一下他的repo名,victory什么鬼啊,没用过你家react-victory的人真心不知道你是干啥的。
说句题外话,如果自己有repo的小伙伴一定要重视你的项目命名,最好把它的功能用命名的方式描述出来,然后详细写下description,这样才能提高star数,(来自一个刚刚参透这些道理并且写了不少repo,star数扔为0的老阿姨的忠告)。如果你经常从github上找三方库你就会发现,star数很高的项目基本上都是文档详尽,description写的很清楚的一些项目。拿react-native-chart举例,虽然也是个不错的repo,但已经不能满足当前的react-native版本了,比起victory-native还是有很多不足的地方,但是他的star数要比victory-native高一倍。起个名字还是关系着命运啊,就像王胜利
和王.我会做图表
一样,你会找谁做图表呢。。
首先,先要安装react-native-svg,对应你的react-native版本号进行安装。
react-native-svg >= 3.2.0 only supports react-native >= 0.29.0
react-native-svg >= 4.2.0 only supports react-native >= 0.32.0
react-native-svg >= 4.3.0 only supports react-native >= 0.33.0
react-native-svg >= 4.4.0 only supports react-native >= 0.38.0 and react >= 15.4.0
react-native-svg >= 4.5.0 only supports react-native >= 0.40.0 and react >= 15.4.0
react-native-svg >= 5.1.8 only supports react-native >= 0.44.0 and react == 16.0.0-alpha.6
react-native-svg >= 5.2.0 only supports react-native >= 0.45.0 and react == 16.0.0-alpha.12
react-native-svg >= 5.3.0 only supports react-native >= 0.46.0 and react == 16.0.0-alpha.12
react-native-svg >= 5.4.1 only supports react-native >= 0.47.0 and react == 16.0.0-alpha.12
react-native-svg >= 5.5.1 only supports react-native >= 0.50.0 and react == 16.0.0
之后link一下
react-native link react-native-svg
重新打包安装
react-native run-ios
// or
react-native run-android
然后安装victory-native
yarn add victory-native
先引入,这里只讨论饼图,不过很多参数都是共同的,其他的见官方文档
import {
VictoryPie,
VictoryLegend,
} from 'victory-native';
使用组件
<VictoryPie
padding={{ top: 0, left: 0 }}
colorScale={colorScale}
data={[
{ x: '净值', y: 35 },
{ x: '已用', y: 40 },
{ x: '可用', y: 55 },
{ x: '总盈亏', y: 55 },
]}
innerRadius={px2dp(75)}
height={256}
width={256}
labels={() => ''}
/>
这里写的参数只是基础的几个,想要进行扩展去看下官方文档,写的非常详尽。
类似于json形式,x代表数据的类型,y是数据的值;也可以是纯数组的形式。
这个参数指每个类型的颜色,是一组数组,顺序按照data中给出的顺序。如:
const colorScale = ['tomato', 'orange', 'gold', 'navy'];
指的是圆形中间的圆半径大小。如果设置为0就是一个纯饼图。
设置整个svg的大小,不设置默认为填充父组件。
标签名,将data进行处理后渲染到图中。因为需求中不显示标签名,所以设置为()=>{}
<VictoryPie
data={sampleData}
labels={(d) => `y: ${d.y}`}
/>
内边距大小。这个属性默认并不为0,所以会在分辨率不同的设备上显示不一,建议设置为0,然后在整个组件外部包个View进行显示。
<VictoryLegend
padding={{ top: 0, left: 0 }}
title=""
orientation="vertical"
style={{ labels: { fontSize: sizes.f2 } }}
gutter={px2dp(25)}
data={[
{
name: '净值',
symbol: { fill: 'tomato', type: 'square' },
},
{
name: '已用',
symbol: { fill: 'orange', type: 'square' },
},
{
name: '可用',
symbol: { fill: 'gold', type: 'square' },
},
{ name: '总盈亏', symbol: { fill: 'navy', type: 'square' } },
]}
width={px2dp(150)}
/>
整个图例的名字。如title='图例'
类似于json的数据结构,包含图例名(name),和图例样式(type)以及图例颜色(fill)。
指的是每条图例之间的间距。
图例的排列方式。横排或纵排。
我们在开发中,可能会遇见这样的需求:
在一个列表中点击每个列表项,将每个列表项中的内容传入某个函数中进行处理。
通常结构为
<ul id="list">
<li>msg</li>
<li>msg1</li>
<li>msg2</li>
<li>msg3</li>
</ul>
这里需要点击某个列表项,弹出他的内容文本,不难写出下面的方法:
window.onload = () => {
const ulNode = document.getElementById("list")
const liNodes = ulNode.childNode || ulNode.children
for (let i = 0; i < liNodes.length; i++) {
liNodes[i].addEventListener('click', (e) => {
alert(e.target.innerHTML)
}, {
once: true
}) // once:在调用一次后被移除
}
}
然而,当我们对这个大列表进行dom操作时,比如添加一个节点,但上面的事件并没有绑定到这个新节点上,需要我们再次调用这个函数,重新遍历这些子节点,绑定事件。
事件捕获:当某个元素触发某个事件(如onclick),顶层对象document就会发出一个事件流,随着DOM树的节点向目标元素节点流去,直到到达事件真正发生的目标元素。在这个过程中,事件相应的监听函数是不会被触发的。
事件目标:当到达目标元素之后,执行目标元素该事件相应的处理函数。如果没有绑定监听函数,那就不执行。
事件冒泡:从目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被一次触发。如果想阻止事件起泡,可以使用e.stopPropagation()(Firefox)或者e.cancelBubble=true(IE)来组织事件的冒泡传播。
当子节点被点击时,click事件向上冒泡,父节点捕获到事件后,我们判断是否为所需的节点,然后进行处理。代码如下:
const ulNode = document.getElementById("list")
ulNode.addEventListener('click',(e)=>{
// e.target为当前元素的子节点,这里只对li元素进行处理
if(e.target&&e.target.nodeName.toLowerCase()==='li'){
alert(e.target.innerHTML)
}
})
const btn = document.getElementById('btn')
btn.onclick = () => {
const newLi=document.createElement('li')
const text=document.createTextNode("new msg")
newLi.appendChild(text)
ulNode.appendChild(newLi)
}
不需要再次为新节点绑定事件,照样会响应。
当我们把新建的子节点改成p元素时,
const newLi=document.createElement('p')
因为做了判断,所以点击p元素是没有响应的。
1.greenlet -- 移动一个异步函数到自己的线程。他的原理是接受一个异步函数,生成一个在Web Worker中运行的副本。
示例:
import greenlet from 'greenlet'
let get = greenlet(async url => {
let res = await fetch(url)
return await res.json()
})
console.log(await get('/foo'))
outline -- 为成长中的团队开放源代码wiki和知识库比较适合创业团队来看的
demo地址
3. ilsap -- Intellij许可证服务器活动代理这个我自己还没有测试,使用jet brains家的产品就fork一下把~~
beidou -- 服务器呈现的React应用程序的同构框架阿里出品----必属呃呃呃。。。
vue-content-placeholders -- 用于渲染虚假(渐进)内容的可组合组件,例如vue中的Facebook
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.