find-github-star 开发历程:
项目地址 find-github-star
0x01. 这是一个什么玩意儿?
github上有太多太多的牛人, 这个东西可以帮助你通过给定的一个github的用户, 然后通过他关注的人, 找出他关注的人里的被关注数最高的几个. 然后不断的循环, 从而通过关系链找到github上最受欢迎的大神~ 这个东西还只是一个小东西, 如果你有兴趣, 可以fork这个小的不能再小的项目...
项目截图
0x02. 为什么要做这个东西?
一来是自己确实想做着玩一玩, 还有就是这个项目用到了react + redux. 想进一步的熟悉redux这个玩意儿。
0x03. 开工开工~ 搭建环境
用到的工具:webpack. 直接上配置
var webpack = require('webpack');
var OpenBrowserPlugin = require('open-browser-webpack-plugin');
module.exports = {
entry: [
'webpack/hot/dev-server',
'webpack-dev-server/client?http://localhost:8080',
'./src/entry.js'
],
output: {
path: './build',
filename: '[name].js'
},
module: {
loaders: [
{ test: /\.less$/, loader: 'style!css!less' },
{ test: /\.jsx?$/, loader: 'babel-loader', exclude: /node_modules/ }
]
},
plugins: [
new OpenBrowserPlugin({ url: 'http://localhost:8080' }),
]
}
0x04. 使用react-redux
当我刚开始使用react-redux的时候, 我的内心是绝望的. 什么connect, provider, 各种props映射, 各种dispatch映射.但是毕竟就是想拿这个东西顺便学习一下react-redux. 话不多说, 上代码!
entry.js
const loggerMiddleware = createLogger()
let store = createStore(reducer, compose(
applyMiddleware(thunk, loggerMiddleware),
window.devToolsExtension ? window.devToolsExtension() : f => f
))
devTools.updateStore()
render(
<Provider store={store}>
<App />
</Provider>, document.getElementsByTagName('div')[0])
if (module.hot) {
module.hot.accept()
}
这里用到了redux中间件的概念, 这里建议看官方的文档比较靠谱. 中间件thunk是允许你向dispatch传一个函数, 中间件LoggerMiddleware会记录你的每一个action, 并且利用方法console.group, 美观的输出.
好! Provider组件才是重点, Provider利用react的context机制, 将创建好的store暴露给app以及其所有后代. 这样App的后代就能通过context访问到store啦! 最后面那个if 就是webpack的webpack dev server插件的超炫功能----热替换.
因为store已经暴露到了整个App下, 所以所有组件可以调用store.dispatch来分发动作(数据流出组件), 也可以调用store.subscribe来订阅(数据流入组件). 但是redux的设计者觉得这样有点坑, 因为在react中, 有些组件没有state, 只有props, 这样的组件我们称之为纯组件. 于是乎, redux将组件分成了两个部分, 容器组件和展示组件.对于展示组件(纯组件)并不需要状态, 容器组件给出固定的props, 总会输出一致的展示组件. 于是, state都放到了容器组件这里, 为了让容器组件更好的获取(输出)store里的state. 就要使用connect. 上代码!
Main.js
class Main extends Component {
createProfiles(profiles, repos) {
return profiles.map((profile, index) =>
<Profile key={index} {...profile} repo={repos[index]} />)
}
render() {
return (
<main>
{ this.createProfiles(this.props.profiles, this.props.repos) }
</main>
)
}
}
function mapStateToProps(state) {
return {
profiles: state.profiles,
repos: state.repos
}
}
export default connect(
mapStateToProps
)(Main)
最下面的connect是一个可以将Main组件包装起来的函数, Main组件被包装后会在外层新增一个新的组件包裹Main. mapStateToProps会在store的state发生变化的时候被调用, 其返回值会传给Main组件作为props, 其实看名字就知道了.
0x05 数据的抓取.
这里用到了异步action, 直接上代码!
actions.js
export const fetchProfiles = username => (dispatch, getState) => {
dispatch(requestProfile(username))
return fetch(`https://api.github.com/users/${username}`)
.then((response) => {
// 检查用户是否存在
if (response.status !== 200) {
throw new Error('profiles fetch failed')
}
return fetch(`https://api.github.com/users/${username}/following`)
})
.then(response => response.json())
.then(following => {
return Promise.all(following.map(f => {
return fetch(`https://api.github.com/users/${f.login}`)
}))
})
.then(responses => Promise.all(responses.map(response => response.json())))
.then(followingUsers => {
const sortedUsers = followingUsers.sort((a, b) => b.followers - a.followers)
return sortedUsers.slice(0, 3)
})
.then((users) => {
dispatch(requestSuccess(users))
console.dir(users)
return Promise.all(users.map(user =>
fetch(`https://api.github.com/users/${user.login}/repos`)))
})
.then(responses => Promise.all(responses.map(res => res.json())))
.then((repos) => {
repos = repos.map(repo => repo.slice(0, 3))
dispatch(requestReposSuccess(repos))
})
.catch((err) => dispatch(requestFailed(err)))
}
这里才是最好玩(最坑)的地方, fetchProfiles函数是一个Action Creator,只要爬取数据, 这个函数就会被调用. 这里用到了各种then(旗帜鲜明的表示用好Promise/A+规范真的是爽歪歪.)fetchProfiles会被传入dispatch(用来分发之后的异步action)和getStore.倒数第n行的dispatch的调用就继续发起一个异步action, 下一个action的代码如下:
function requestReposSuccess(repos) {
repos = repos.map(repo => repo.sort((a, b) => b.stargazers_count - a.stargazers_count))
repos = repos.map(repo => repo.map(r => ({
star: r.stargazers_count,
name: r.name,
description: r.description.slice(0, 40) + ' ...'
})))
return {
type: REQUEST_REPOS_SUCCESS,
payload: repos
}
}
旗帜鲜明的表示es6的匿名函数的写法真的是让我像写诗一样写代码~
0x06. 最后
大概的就这么多, 不总结的话就不像是一篇文章了, 总结如下:
webpack确实是一个好东西, 要是能够用上热替换的话真的是太强大了, 怪不得能火成这鸟样...其代码分割也是很强大的
使用react中的context可以让react自动将信息传到子树中的任何组件,但是组件必须设置contextTypes, 否则无法访问对应的context.对于主题等这些全局信息应该使用context传给子树, 但是一些平常的state最好别传, 原因, 而且context的API不是稳定的, 今后可能会发生变化.
redux中store是唯一存储状态的容器(这也是和flux的不同之处), 还有容器组件和展示组件, redux通过provider注入store中的state到App中, 通过connect来构造容器组件, 方便组件更好的获得(输出)state, 同时限制展示组件只能从容器组件获取state. 一个App中的容器组件可以有多个.
redux的中间件的用法及原理, 很强势, 建议去看. 这是地址
thunk以及promise等中间件可以实现异步的action.
最后就是写这个项目感觉很棒, es6+react+babel+webpack+redux 写前端真的是酷比(苦逼)了!
写这篇文章真的很不容易哈, 如果你觉得我写的对你有那么一点点的帮助, 可以选择关注我的新浪微博, Twitter, Github, Facebook, 个人主页, 个人主页还在备案, 马上开通~
参考文章:
完!~ 荆轲刺秦王~ ~~~~~~其实我不是王尼玛...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。