当前位置: 首页 > news >正文

ES6, React, Redux, Webpack写的一个爬 GitHub 的网页

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://jiavan.com/react-async...

  • http://www.ruanyifeng.com/blo...

完!~ 荆轲刺秦王~ ~~~~~~其实我不是王尼玛...
苦逼

相关文章:

  • 第一次到这里来
  • RabbitMQ
  • 每天早晚二十分钟,让你人生从此不同
  • Json map
  • CSDN Blog更换服务器维护通知(今日21:00-22:00)
  • 初试牛刀的收获-实现Web文件的上传
  • 终于要毕业了,没有什么好说的
  • UITableViewCell 高度自适应
  • 技术及培训问题讨论专贴。[学员必看]
  • ADSL Modem之DHCP的配置及应用
  • BI_DBA_安装(1):安装OS及DB
  • 洛谷P1582 倒水
  • ISA 2004 進階管理實務
  • PHP系列目录
  • 怎样成为优秀的软件测试员
  • [nginx文档翻译系列] 控制nginx
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • 2017-09-12 前端日报
  • 30天自制操作系统-2
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • Android路由框架AnnoRouter:使用Java接口来定义路由跳转
  • AzureCon上微软宣布了哪些容器相关的重磅消息
  • CentOS7简单部署NFS
  • markdown编辑器简评
  • mysql中InnoDB引擎中页的概念
  • Tornado学习笔记(1)
  • V4L2视频输入框架概述
  • Webpack 4x 之路 ( 四 )
  • 反思总结然后整装待发
  • 关于Java中分层中遇到的一些问题
  • 极限编程 (Extreme Programming) - 发布计划 (Release Planning)
  • 学习Vue.js的五个小例子
  • MiKTeX could not find the script engine ‘perl.exe‘ which is required to execute ‘latexmk‘.
  • 继 XDL 之后,阿里妈妈开源大规模分布式图表征学习框架 Euler ...
  • 扩展资源服务器解决oauth2 性能瓶颈
  • 你学不懂C语言,是因为不懂编写C程序的7个步骤 ...
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • ​iOS安全加固方法及实现
  • # C++之functional库用法整理
  • %3cscript放入php,跟bWAPP学WEB安全(PHP代码)--XSS跨站脚本攻击
  • (39)STM32——FLASH闪存
  • (52)只出现一次的数字III
  • (ZT)北大教授朱青生给学生的一封信:大学,更是一个科学的保证
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (附源码)php投票系统 毕业设计 121500
  • (附源码)springboot码头作业管理系统 毕业设计 341654
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (算法)Game
  • (五)Python 垃圾回收机制
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • (原創) X61用戶,小心你的上蓋!! (NB) (ThinkPad) (X61)
  • .[hudsonL@cock.li].mkp勒索病毒数据怎么处理|数据解密恢复
  • .mat 文件的加载与创建 矩阵变图像? ∈ Matlab 使用笔记
  • .NET Core 实现 Redis 批量查询指定格式的Key
  • .Net Core 中间件验签