主流源代码管理工具—GitHub

Posted ttthhh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了主流源代码管理工具—GitHub相关的知识,希望对你有一定的参考价值。

  • l Github是什么?

  首先,GitHub是一个面向开源及私有软件项目的托管平台,该平台于2008年4月10日正式上线,除了Git代码仓库托管及基本的Web管理界面以外,还提供了订阅、讨论组、文本渲染、在线文件编辑器、协作图谱(报表)、代码片段分享(Gist)等功能。目前,其注册用户已经超过350万,托管版本数量也是非常之多,其中不乏知名开源项目Ruby on Rails、jQuerypython等。
  随着越来越多的应用程序转移到了云上,在GitHub,用户可以十分轻易地找到海量的开源代码,Github已经成为了管理软件开发以及发现已有代码的首选方法。

  2008年4月10日,GitHub正式上线。

  2014年1月23日,联合创始人汤姆·普雷斯顿-维尔纳(Tom Preston-Werner)从另一位联合创始人克里斯·万斯特拉斯(Chris Wanstrath)手中接过总裁职位,后者也将接过普雷斯顿-维尔纳留下的CEO位置。

  2018年6月4日晚,微软宣布,通过75亿美元的股票交易收购GitHub。10月26日,微软以75亿美元收购GitHub交易已完成。10月29日,微软开发者服务副总裁奈特·弗里德曼(Nat Friedman)将成为GitHub的新一任CEO。

  2020年3月17日,Github宣布收购npm,GitHub现在已经保证npm将永远免费使用。

  2021年11月4日消息,微软宣布GitHub CEO奈特·弗里德曼将于11月15日卸任,其职位由GitHub产品主管托马斯·多梅克接替。

  今天,GitHub已是:
  • 一个拥有143万开发者的社区。其中不乏Linux发明者Torvalds这样的顶级黑客,以及Rails创始人DHH这样的年轻极客。
  • 这个星球上最流行的开源托管服务。目前已托管431万git项目,不仅越来越多知名开源项目迁入GitHub,比如Ruby on Rails、jQuery、Ruby、Erlang/OTP;近三年流行的开源库往往在GitHub首发,例如:BootStrap、Node.js、CoffeScript等。
  • alexa全球排名414的网站。
  • l Github怎么使用?

  Github可以用来上传自己的开源代码以及下载别人的开源代码,并且下载开源代码并不需要登录账户,那么接下来,我会简单介绍怎么使用github。

  1. 查看、下载开源代码

  我初次接触Github时就是查看别人的源代码,这也是大部分人使用的功能,即我们可以使用Github查看别人的开源代码并下载,首先找到一个Github的开源项目连接,打开后看见的第一个区域就是项目源代码,点击源代码中的某个文件就可以查看其中的内容。(这里随机选择了首页上的一个项目)
  在源码区域下,README.md文件会被自动显示,这是该项目的说明文件,用来说明软件的功能用法以及注意事项等。

  在源码区域右上角,绿色带有“Code”的按键边是下载源码包的选项,点击后选择“Download ZIP”,便可以下载源码包。

  在源码区域左上角,点击作者头像可以进入其“个人空间”,点击“Repositories”,便可以查看该作者的所有开源项目,每个项目第一行(图中的蓝色字体)即是项目名称,第二行中第一个即是该项目使用的编程语言。点击某一个项目就可以进入项目页面了。

  这些就是基本的使用Github查看、下载源代码的方法。

   2.上传代码

  使用Github上传代码的第一步就是创建账户和仓库,进入Github官网:https://github.com/。输入电子邮箱后点击sign uo for GitHub,根据步骤注册成功后,就可以创建自己的代码仓库了。页面右上角“+”中,点击New Repository,填好项目名称(Repository name)、项目简介(Description<optional>)后,勾选Add a README file,其他跟随默认,点击“Createre pository”创建。

  创建好后进入了自己的项目界面,此时里面并没有项目代码,在源码区右上角点击“Add file”——>“Upload files”,选择自己的源码文件夹上传,上传完成后点击“Commit changes”,自己的项目代码就发布成功了!

 

  此外,Github还支持在线编辑代码,点开一个代码文件,在选择右上角的编辑按钮(笔图标),就可以进行在线编辑了,编辑完成后点击页面最下面的“Commit changes”保存即可。

 

  以上就是简单的Github上传代码过程。

 

  • l 结语

  GitHub有170万名软件开发人员的忠实用户,他们平均每天更新8万个并新建7千个软件库。对GitHub网站上托管的总计超过300万个软件库,其联合创始人Chris Wanstrath曾经形象地称其为“程序员的维基百科全书”。通过整合网上资料和自己的经验,攥写关于Github的博客,有利于我在今后的学习以及小组任务中使用Github来编写自己的程序。

Next轻量级框架与主流工具的整合

前言

老大说以后会用 next 来做一下 SSR 的项目,让我们有空先学学。又从 0 开始学习新的东西了,想着还是记录一下学习历程,有输入就要有输出吧,免得以后给忘记学了些什么~


Next轻量级框架与主流工具的整合

github地址:https://github.com/code-coder/next-mobile-complete-app

首先,clone了next.js项目,学习里面的templates。
打开一看,我都惊呆了,差不多有150个搭配工具个template,有点眼花缭乱。
这时候就需要明确一下我们要用哪些主流的工具了:
  • [x] 数据层:redux + saga
  • [x] 视图层:sass + postcss
  • [x] 服务端:koa

做一个项目就像造一所房子,最开始就是“打地基”:

1. 新建了一个项目,用的是这里面的一个with-redux-saga的template 戳这里

2. 添加sass和postcss,参考的是 这里

  • 新建next.config.js,复制以下代码:
const withSass = require(‘@zeit/next-sass‘);
module.exports = withSass({
  postcssLoaderOptions: {
    parser: true,
    config: {
      ctx: {
        theme: JSON.stringify(process.env.REACT_APP_THEME)
      }
    }
  }
});
  • 新建postcss.config.js,复制以下代码:
module.exports = {
  plugins: {
    autoprefixer: {}
  }
};
  • package.js添加自定义browserList,这个就根据需求来设置了,这里主要是移动端的。
// package.json
"browserslist": [
    "IOS >= 8",
    "Android > 4.4"
  ],
  • 顺便说一下browserlist某些配置会报错,比如直接填上默认配置
"browserslist": [
    "last 1 version",
    "> 1%",
    "maintained node versions",
    "not dead"
  ]
// 会报以下错误
Unknown error from PostCSS plugin. Your current PostCSS version is 6.0.23, but autoprefixer uses 5.2.18. Perhaps this is the source of the error below.

3. 配置koa,参照custom-server-koa

  • 新建server.js文件,复制以下代码:
const Koa = require(‘koa‘);
const next = require(‘next‘);
const Router = require(‘koa-router‘);

const port = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== ‘production‘;
const app = next({ dev });
const handle = app.getRequestHandler();

app.prepare().then(() => {
  const server = new Koa();
  const router = new Router();

  router.get(‘*‘, async ctx => {
    await handle(ctx.req, ctx.res);
    ctx.respond = false;
  });

  server.use(async (ctx, next) => {
    ctx.res.statusCode = 200;
    await next();
  });

  server.use(router.routes());
  server.listen(port, () => {
    console.log(`> Ready on http://localhost:${port}`);
  });
});
  • 然后在配置一下package.json的scripts
"scripts": {
    "dev": "node server.js",
    "build": "next build",
    "start": "NODE_ENV=production node server.js"
  }

现在只是把地基打好了,接着需要完成排水管道、钢筋架构等铺设:

  • [x] 调整项目结构
  • [x] layout布局设计
  • [x] 请求拦截、loading状态及错误处理

1. 调整后的项目结构

-- components
-- pages
++ server
|| -- server.js
-- static
++ store
|| ++ actions
||    -- index.js
|| ++ reducers
||    -- index.js
|| ++ sagas
||    -- index.js
-- styles
-- next.config.js
-- package.json
-- postcss.config.js
-- README.md

2. layout布局设计。

ant design 一直是使用过而且比较有好感的UI框架。既然这是移动端的项目,ant design mobile 成了首选的框架。我也看了其他的主流UI框架,现在流行的UI框架有Amaze UIMint UIFrozen UI等等,个人还是比较喜欢ant design出品的。

恰好templates中有ant design mobile的demo:with-ant-design-mobile

  • 基于上面的项目结构整合with-ant-design-mobile这个demo。
  • 新增babel的配置文件:.babelrc 添加以下代码:
{
  "presets": ["next/babel"],
  "plugins": [
    [
      "import",
      {
        "libraryName": "antd-mobile"
      }
    ]
  ]
}
  • 修改next.config.js为:
const withSass = require(‘@zeit/next-sass‘);
const path = require(‘path‘);
const fs = require(‘fs‘);
const requireHacker = require(‘require-hacker‘);

function setupRequireHacker() {
  const webjs = ‘.web.js‘;
  const webModules = [‘antd-mobile‘, ‘rmc-picker‘].map(m => path.join(‘node_modules‘, m));

  requireHacker.hook(‘js‘, filename => {
    if (filename.endsWith(webjs) || webModules.every(p => !filename.includes(p))) return;
    const webFilename = filename.replace(/.js$/, webjs);
    if (!fs.existsSync(webFilename)) return;
    return fs.readFileSync(webFilename, { encoding: ‘utf8‘ });
  });

  requireHacker.hook(‘svg‘, filename => {
    return requireHacker.to_javascript_module_source(`#${path.parse(filename).name}`);
  });
}

setupRequireHacker();

function moduleDir(m) {
  return path.dirname(require.resolve(`${m}/package.json`));
}

module.exports = withSass({
  webpack: (config, { dev }) => {
    config.resolve.extensions = [‘.web.js‘, ‘.js‘, ‘.json‘];

    config.module.rules.push(
      {
        test: /.(svg)$/i,
        loader: ‘emit-file-loader‘,
        options: {
          name: ‘dist/[path][name].[ext]‘
        },
        include: [moduleDir(‘antd-mobile‘), __dirname]
      },
      {
        test: /.(svg)$/i,
        loader: ‘svg-sprite-loader‘,
        include: [moduleDir(‘antd-mobile‘), __dirname]
      }
    );
    return config;
  }
});
  • static新增hd.min.js
// 这里做了一些小改动,meta标签固定1倍,根节点的fontSize为375设计稿 / 10px
!(function(e) {
  function t(a) {
    if (i[a]) return i[a].exports;
    var n = (i[a] = { exports: {}, id: a, loaded: !1 });
    return e[a].call(n.exports, n, n.exports, t), (n.loaded = !0), n.exports;
  }
  var i = {};
  return (t.m = e), (t.c = i), (t.p = ‘‘), t(0);
})([
  function(e, t) {
    ‘use strict‘;
    Object.defineProperty(t, ‘__esModule‘, { value: !0 });
    var i = window;
    (t[‘default‘] = i.flex = function(e, t) {
      var a = e || 10,
        n = t || 1,
        r = i.document,
        o = navigator.userAgent,
        d = o.match(/Android[Ss]+AppleWebkit/(d{3})/i),
        l = o.match(/U3/((d+|.){5,})/i),
        c = l && parseInt(l[1].split(‘.‘).join(‘‘), 10) >= 80,
        p = navigator.appVersion.match(/(iphone|ipad|ipod)/gi),
        s = i.devicePixelRatio || 1;
      p || (d && d[1] > 534) || c || (s = 1);

      var u = 1,
        m = r.querySelector(‘meta[name="viewport"]‘);
      m || ((m = r.createElement(‘meta‘)), m.setAttribute(‘name‘, ‘viewport‘), r.head.appendChild(m)),
        m.setAttribute(
          ‘content‘,
          ‘width=device-width,user-scalable=no,initial-scale=‘ + u + ‘,maximum-scale=‘ + u + ‘,minimum-scale=‘ + u
        ),
        (r.documentElement.style.fontSize = (a / 2) * s * n + ‘px‘);
    }),
      (e.exports = t[‘default‘]);
  }
]);
flex(10, 1);
  • _document.js新增引用
<script src="/static/hd.min.js" />
<link rel="stylesheet" type="text/css" href="//unpkg.com/antd-mobile/dist/antd-mobile.min.css" />
  • 构造布局
  1. 在components文件夹新增layouttabs文件夹
++ components
|| ++ layout
|| || -- Layout.js
|| || -- NavBar.js
|| ++ tabs
|| || -- TabHome.js
|| || -- TabIcon.js
|| || -- TabTrick.js
|| || -- Tabs.js
  1. 应用页面大致结构是(意思一下)
  • 首页
nav
content
tabs
  • 其他页
nav
content
  • 最后,使用redux管理nav的title,使用router管理后退的箭头
// other.js
static getInitialProps({ ctx }) {
    const { store, req } = ctx;
    // 通过这个action改变导航栏的标题
    store.dispatch(setNav({ navTitle: ‘Other‘ }));
    const language = req ? req.headers[‘accept-language‘] : navigator.language;

    return {
      language
    };
  }
// NavBar.js 
componentDidMount() {
// 通过监听route事件,判断是否显示返回箭头
Router.router.events.on(‘routeChangeComplete‘, this.handleRouteChange);
}

handleRouteChange = url => {
if (window && window.history.length > 0) {
  !this.setState.canGoBack && this.setState({ canGoBack: true });
} else {
  this.setState.canGoBack && this.setState({ canGoBack: false });
}
};
// NavBar.js
let onLeftClick = () => {
  if (this.state.canGoBack) {
    // 返回上级页面
    window.history.back();
  }
};
需要留意的是,在同一个页面改变query,不会覆盖history的记录,而是增加一条,因此调用history.back()只是根据浏览器地址记录返回。这里可以再做优化。

3、请求拦截、loading状态及错误处理

  • 封装fetch请求,使用单例模式对请求增加Authrition授权、全局loading等处理。
要点:1、单例模式。2、延迟loading。3、server端渲染时不能加载loading,因为loading是通过document对象操作的
// /api/proxyFetch.js
import { Toast } from ‘antd-mobile‘;
import ‘isomorphic-unfetch‘;

// 请求超时时间设置
const REQUEST_TIEM_OUT = 10 * 1000;
// loading延迟时间设置
const LOADING_TIME_OUT = 1000;

class ProxyFetch {
  constructor() {
    this.fetchInstance = null;
    this.urlPrefix = ‘‘;
    this.headers = { ‘Content-Type‘: ‘application/json‘, DeviceId: ‘android/ios‘ };
    this.init = { credentials: ‘omit‘, headers: this.headers };
    // 处理loading
    this.requestCount = 0;
    this.isLoading = false;
    this.loadingTimer = null;
  }

  /**
   * 请求1s内没有响应显示loading
   */
  showLoading() {
    if (this.requestCount === 0) {
      this.loadingTimer = setTimeout(() => {
        Toast.loading(‘加载中...‘, 0);
        this.isLoading = true;
        this.loadingTimer = null;
      }, LOADING_TIME_OUT);
    }
    this.requestCount++;
  }

  hideLoading() {
    this.requestCount--;
    if (this.requestCount === 0) {
      if (this.loadingTimer) {
        clearTimeout(this.loadingTimer);
        this.loadingTimer = null;
      }
      if (this.isLoading) {
        this.isLoading = false;
        Toast.hide();
      }
    }
  }

  /**
   * 获取proxyFetch单例对象
   */
  static getInstance() {
    if (!this.fetchInstance) {
      this.fetchInstance = new ProxyFetch();
    }
    return this.fetchInstance;
  }

  /**
   * get请求
   * @param {*} url
   * @param {*} params
   */
  async get(url, params = {}, isServer = false) {
    const options = { method: ‘GET‘ };
    if (params) {
      let paramsArray = [];
      // encodeURIComponent
      Object.keys(params).forEach(key => paramsArray.push(key + ‘=‘ + params[key]));
      if (url.search(/?/) === -1) {
        url += ‘?‘ + paramsArray.join(‘&‘);
      } else {
        url += ‘&‘ + paramsArray.join(‘&‘);
      }
    }
    return await this.dofetch(url, options, isServer);
  }

  /**
   * post请求
   * @param {*} url
   * @param {*} params
   */
  async post(url, params = {}, isServer = false) {
    const options = { method: ‘POST‘ };
    let data = ‘‘;
    switch (params.bodyType) {
      case ‘form‘:
        options.headers[‘Content-Type‘] = ‘application/x-www-form-urlencoded;charset=UTF-8‘;
        Object.keys(params.bodys).map(index => {
          let param = encodeURI(params.bodys[index]);
          data += `${index}=${param}&`;
        });
        options.body = data;
        break;
      case ‘file‘:
        data = new FormData();
        Object.keys(params.bodys).map(index => {
          data.append(index, params.bodys[index]);
        });
        options.body = data;
        break;
      default:
        options.body = JSON.stringify(params);
        break;
    }
    return await this.dofetch(url, options, isServer);
  }

  /**
   * fetch主函数
   * @param {*} url
   * @param {*} options
   * @param {boolean} isServer 是否是服务端渲染的请求
   * @param {boolean} setAuthorization 登录接口设置Authorization
   */
  dofetch(url, options, isServer, setAuthorization) {
    !isServer && this.showLoading();
    return Promise.race([
      fetch(this.urlPrefix + url, { ...this.init, ...options }),
      new Promise((resolve, reject) => {
        setTimeout(() => reject(new Error(‘request timeout‘)), REQUEST_TIEM_OUT);
      })
    ])
      .then(response => {
        !isServer && this.hideLoading();
        if (setAuthorization) {
          this.headers.Authorization = response.json().token;
          return {};
        } else {
          return response;
        }
      })
      .catch(e => {
        if (!server) {
          this.hideLoading();
          Toast.fail(e.message);
        }
        return { ok: false, status: ‘501‘, statusText: e.message };
      });
  }
}

export default ProxyFetch.getInstance();

写在最后

一个完整项目的雏形大致出来了,但是还是需要在实践中不断打磨和优化。

如有错误和问题欢迎各位大佬不吝赐教 :)

以上是关于主流源代码管理工具—GitHub的主要内容,如果未能解决你的问题,请参考以下文章

主流源代码工具Github的介绍

几种源代码管理工具

Next轻量级框架与主流工具的整合

两行代码搞定UI主流框架

Git在AndroidStudio中的使用

耗费2月整理了大数据所有主流框架的demo代码大全