Giter Club home page Giter Club logo

blogs's Introduction

blogs

记录和整理自己日常使用的技术wiki

项目

blogs's People

Stargazers

 avatar

Watchers

 avatar

blogs's Issues

mac安装软件[记录]

通过nvm来切换node的版本时,需要注意先把全局安装的模块给安装到新的node版本里。

查看全局安装模块

npm ls -g --depth=0

卸载全局模块

npm uninstall -g [module xx]

管理node

  • nvm: 管理node版本
  • nrm: 管理node 模块的镜像地址
  • yrm 管理yarn模块的镜像地址

使用nvm切换node版本

永久切换

需要重新打开命令行,已打开的不生效

nvm alias default <版本号>

临时切换版本

临时切换版本,只在当前终端内有效,新开终端无效。

nvm use <版本号>

本地全局包

@vue/[email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
└── [email protected]

使用issue来管理开源项目

提交PR的格式

feat: 添加了分享功能
​
给每篇博文添加了分享功能
​
- 添加分享到微博功能
- 添加分享到微信功能
- 添加分享到朋友圈功能
​
Issue #1, #2
Closes #1

type用于说明 commit 的类别,可以使用如下类别:

  • feat:新功能(feature)
  • fix:修补bug
  • doc:文档(documentation)
  • style: 格式(不影响代码运行的变动)
  • refactor:重构(即不是新增功能,也不是修改bug的代码变动)
  • test:增加测试
  • chore:构建过程或辅助工具的变动

参考

如何优雅地pull request

npm包开发[健壮/可迭代]

在开发npm包时,约定目录结构,有助于理解代码结构。

lib包用途

功能 说明 场景
纯node环境 符合commonjs规范,适合node环境使用 node server端,比如工具包,日志等
浏览器使用 使用script方式引入 组件库,client端使用
cli命令行工具 命令行使用,例如工具包/脚手架之类 命令行工具

lib 包技术架构

统一使用ts编码,好处:类型提示

  • ts: 源码编写
  • rollup: 打包工具
  • doc: 文档工具

目录约定

按目录约定:

功能 说明
src 源码目录
lib 打包输出的代码,也有dist目录,可以统一约定lib
es 符合es module规范的代码
umd 符合umd规范的代码,给浏览器的script 直接饮用
typings 类型定义

假设开发一个lib包: lib-a, 按照约定:

lib-a
  ├── umd
  |    └── index.js
  ├── es
  |    └── index.js
  ├── lib
  |    └── index.js
  ├── src -- 源码目录
  |    └── index.js
  ├── typings
  |    └── index.d.ts
  ├── .gitignore
  └── package.json
// package.json
{
  "main": "lib/index.js",
  "module": "es/index.js",
  "types": "typings/index.d.ts"
}

文档

  • READMD.md - 这个是作为你的库在 npm 网站上的主页。
  • CHANGELOG.md
  • LICENSE

单测

单元测试,

benchmark (基准测试)

基准,用于基准测试,就是一个性能的衡量基准。

发布管理

使用semantic-release 去自动管理发布
可参考https://blog.dteam.top/posts/2020-05/semantic-release.html

参考

node服务-babel7 最佳实践[项目搭建]

亮点

  • 纯node服务层,代码层使用es6+语法
  • 不需要webpack, rollup这些打包工具
  • 使用express搭建web服务
  • 使用nedb作为数据缓存
  • 使用bulebird构建Promise指令
  • 使用axios访问外部服务
  • 使用module-alias提供alias机制
  • 状态码分类,提供友好提示
  • 接口参数合法校验使用async-validator
  • 一些工具库:lodash, moment
    // todo

参考

【面向对象】设计模式:7大基本原则(5大核心原则+2补充原则)

SOLID 原则

  • S: 单一职责原则(Single Responsibility Principle, SRP)
  • O: 开闭原则(Open Closed Principle,OCP)
  • L:里氏代换原则(Liskov Substitution Principle,LSP)
  • I: 接口隔离原则(Interface Segregation Principle,ISP)
  • D: 依赖倒转原则(Dependency Inversion Principle,DIP)

面向对象的设计模式有七大基本原则

  • 单一职责原则(Single Responsibility Principle, SRP)
  • 开闭原则(Open Closed Principle,OCP)
  • 里氏代换原则(Liskov Substitution Principle,LSP)
  • 接口隔离原则(Interface Segregation Principle,ISP)
  • 依赖倒转原则(Dependency Inversion Principle,DIP)
  • 合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP)
  • 最少知识原则(Least Knowledge Principle,LKP)或者迪米特法则(Law of Demeter,LOD)
标记 设计模式原则名称 简单定义
SRP 单一职责原则 一个类只负责一个功能领域中的相应职责
OCP 开闭原则 对扩展开放,对修改关闭
LSP 里氏代换原则 所有引用基类的地方必须能透明地使用其子类的对象
ISP 接口隔离原则 类之间的依赖关系应该建立在最小的接口上
DIP 依赖倒转原则 依赖于抽象,不能依赖于具体实现
CARP 合成/聚合复用原则 尽量使用合成/聚合,而不是通过继承达到复用的目的
LOD 迪米特法则 一个软件实体应当尽可能少的与其他实体发生相互作用

参考:

astexplorer parser的实现

astexplorer 是一个集成了很多AST的编译器。可以实时看输出的AST语法。

我对其如何实现兼容多个版本的parser感兴趣,记录下我的整理思路。

问题

有的设计的parser会拆分成多个独立的模块,可能需要异步加载多个parser模块,这个怎么处理呢?

答:使用parser.loadParser去加载多个异步模块。

js正则表达式

正则表达式,主要使用场景是针对字符串进行替换,分词摘取等

正则表达式测试:https://c.runoob.com/front-end/854
正则表达式图形理解:https://regexper.com/

一些有用的表达式:

生成ast时,需要做分词处理,比如

  1. html属性名称

通常是用字母、下划线、冒号开头(vue的绑定属性用:开头,通常我们不会这么定义)的,然后包含字母数字下划线中划线冒号和点的

const attrKey = /[a-zA-Z_:][-a-zA-Z0-9_:.]*/
  1. html的属性的
    有以下几种写法:
    class="title"
    class='title'
    class=title
const attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)=("([^"]*)"|'([^']*)'|([^\s"'=<>`]+)/

editor

概述

整理遇到的编辑器

目录

  • sunmao-ui: 一个可扩展的低代码ui框架

学习一项新技能

每次带着问题去了解,并做相关笔记整理。

基本

  • 一句话概括是什么
  • 官方文档走一遍
  • 写一个demo试试
  • 多读别人的项目或源码,试着去理解。
  • 自己尝试写一个作品

进价

  • 读十个以上开源项目或作品
  • 写十个以上项目或作品
  • 理解其内部实现机制和原理。
  • 反哺开源社区,对其提供优质作品,优化流程等等

参考

webpack 4.x 最小压缩代码

codepen

(function(modules) { // webpackBootstrap
  // The module cache
  var installedModules = {};
  // The require function
  function __webpack_require__(moduleId) {
    // Check if module is in cache
    if (installedModules[moduleId]) {
      /******/
      return installedModules[moduleId].exports;
    }
    // Create a new module (and put it into the cache)
    var module = installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
    };
    // Execute the module function
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    // Flag the module as loaded
    module.l = true;
    // Return the exports of the module
    return module.exports;
  }
  // expose the modules object (__webpack_modules__)
  __webpack_require__.m = modules;
  // expose the module cache
  __webpack_require__.c = installedModules;
  // define getter function for harmony exports
  __webpack_require__.d = function(exports, name, getter) {
    if (!__webpack_require__.o(exports, name)) {
      Object.defineProperty(exports, name, {
        enumerable: true,
        get: getter
      });
    }
  };
  // define __esModule on exports
  __webpack_require__.r = function(exports) {
    if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
      Object.defineProperty(exports, Symbol.toStringTag, {
        value: 'Module'
      });
    }
    Object.defineProperty(exports, '__esModule', {
      value: true
    });
  };
  // create a fake namespace object
  /******/
  // mode & 1: value is a module id, require it
  /******/
  // mode & 2: merge all properties of value into the ns
  /******/
  // mode & 4: return value when already ns object
  /******/
  // mode & 8|1: behave like require
  __webpack_require__.t = function(value, mode) {
    if (mode & 1) value = __webpack_require__(value);
    if (mode & 8) return value;
    if ((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
    var ns = Object.create(null);
    __webpack_require__.r(ns);
    Object.defineProperty(ns, 'default', {
      enumerable: true,
      value: value
    });
    if (mode & 2 && typeof value != 'string') for (var key in value) __webpack_require__.d(ns, key,
    function(key) {
      return value[key];
    }.bind(null, key));
    return ns;
  };
  // getDefaultExport function for compatibility with non-harmony modules
  __webpack_require__.n = function(module) {
    var getter = module && module.__esModule ?
    function getDefault() {
      return module['default'];
    }:
    function getModuleExports() {
      return module;
    };
    __webpack_require__.d(getter, 'a', getter);
    return getter;
  };
  // Object.prototype.hasOwnProperty.call
  __webpack_require__.o = function(object, property) {
    return Object.prototype.hasOwnProperty.call(object, property);
  };
  // __webpack_public_path__
  __webpack_require__.p = "/";

  // Load entry module and return exports
  return __webpack_require__(__webpack_require__.s = "wNqJ");
})
({
  "wNqJ":
  (function(module, exports) {

    var add = function add() {
      console.log('add');
    };

    add();
  })
});

参考:

动态获取routes(require.context)

代码

require.context(
  './', true, 
  /^\.\/(?!utils|transpilers)[^/]+\/(transformers\/([^/]+)\/)?(codeExample\.txt|[^/]+?\.js)$/
  );

require.context语法

require.context(directory, useSubdirectories = false, regExp = /^\.\//);

这段代码的意思:

  • 嵌套遍历当前目录下的所有子目录,忽略路径包含utils,transpilers的文件
  • codeExample.text只能存在一级子目录或者/xx/transformers/xxx/目录下
  • 所有符合上述条件的js文件

使用场景

  • 动态路由获取

正则表达式

/^\.\/(?!utils|transpilers)[^/]+\/(transformers\/([^/]+)\/)?(codeExample\.txt|[^/]+?\.js)$/

codepen

codepen

涉及到的正则语法

匹配'x'仅仅当'x'后面不跟着'y',这个叫做正向否定查找。

例如,/\d+(?!.)/匹配一个数字仅仅当这个数字后面没有跟小数点的时候。正则表达式/\d+(?!.)/.exec("3.141")匹配‘141’而不是‘3.141’

x(?!y)

参考

typescript 备忘录

ts周边

ts语法

ts类型库

ts playground

ts type unit test

类型挑战

vue2.x的学习

watch的使用

监听数据变化时使用,一般用于当数据变化,需要执行联动时操作。

  1. 监听属性作为函数使用,监听某个数据,数据名称作为key:
    这种使用方式,在第一次绑定时,不会立即触发。只有等到监听属性变化时,才会触发。
watch: {
    // 如果 `question` 发生改变,这个函数就会运行
    question: function (newQuestion, oldQuestion) {
      this.answer = 'Waiting for you to stop typing...'
      this.debouncedGetAnswer()
    }
  },
  1. 监听属性作为对象使用,有handler函数和2个参数immediate和deep属性可使用
  • immediate:true 表示立即执行
  1. deep属性
    当需要监听某个data的嵌套属性时,这个字段就派上作用了。
watch: {
  obj: {
    handler(newName, oldName) {
      console.log('obj.a changed');
    },
    immediate: true,
    deep: true
  }
} 

不过,这样写,可能会有性能影响,如果obj对象嵌套多,会造成一定的内存开销,可以用字符串形式监听。

优化完:

watch: {
 'obj.a': {
    handler(newName, oldName) {
      console.log('obj.a changed');
    },
    immediate: true,
  }
} 

参考

技能树

记录前端实用性的开源库

flex:1与文本过长显示省略号

直接贴最终代码: 传送门

我们知道css实现文本过长,实现省略号:

.text {
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
}
.w100{ width: 100px;}
<div class="text w100">这段css要生效,前提是长度固定(例如width:100px),使用百分比都不行</div>

这段css要生效,前提是长度固定(例如width:100px),使用百分比都不行。

当使用flex布局时,固定长度+可变长度(适配不同移动端设备),这时需要注意几个css属性就可以实现

html结构需要调整:

<div class="filed">
  <div class="filed--label">萨达多撒多</div>
   <div class="filed-text">
      <div class="filed-text--wrapper">当使用flex布局时,固定长度+可变长度(适配不同移动端设备),这时需要注意几个css属性就可以实现</div>
   </div>
</div>
.filed { display: flex;}
.filed--label {width: 100px;}
.filed-text {
  flex:1;
  overflow: hidden;
}
.filed-text--wrapper {
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
   width: 100%;
}

奇技淫巧系列

位运算

按位操作符将其操作数当作 32 位的比特序列(由 0 和 1 组成)操作,返回值依然是标准的 JavaScript 数值

位运算在某些场景下,如需要大量计算的情况下,可以提高性能,比如图片处理,虚拟dom等

  1. & (按位与)

对每对比特位执行与(AND)操作。只有 a 和 b 都是 1 时,a AND b 才是 1。

场景:适用于频繁条件判断时使用

if (flags & VNodeFlags.ELEMENT) {
  // VNode 是普通标签
  mountElement(/* ... */)
} else if (flags & VNodeFlags.COMPONENT) {
  // VNode 是组件
  mountComponent(/* ... */)
} else if (flags & VNodeFlags.TEXT) {
  // VNode 是纯文本
  mountText(/* ... */)
}

参考

h5录屏记录

web常用事件

wiki: https://developer.mozilla.org/zh-CN/docs/Web/Events

{selector: 'body > #u1 .active '}

遇到问题

采集到输入后才添加的className,导致自动化脚本找不到元素

点击输入框,触发点击事件,采集到数据: {selector: 'body > .input-wrap .active'}
在自动化跑脚本时,.active样式是点击后才加上的,所以自动化时找不到此元素。
答:className由于有可能动态变化,所以,className标识元素不安全,应该采用tag来表示。

:nth-child()在selenium和puppeteer里表现不一致问题

// puppeteer里与document.querySelectorAll表现一致
document.querySelectorAll('body:nth-child(2)')
===
document.querySelectorAll('body')

//selenium里表示找body集合里第2个元素,与jquery api 表现一致。

npm, yarn常用命令

说明

abc包为例:

安装

# 安装到dependencies
npm i --save abc
yarn add abc

# 安装到devDependencies
npm i --save-dev abc
yarn add -D abc

# 全局安装
npm i -g abc
yarn global add abc

查看

# 项目
npm list
yarn list

# 全局查看
npm list -g --depth=0
yarn global list

删除

# 项目
npm uninstall abc
yarn remove abc

# 全局
npm uninstall -g abc
yyarn global remove abc

更新

# 项目
npm update abc
yarn upgrade abc

# 全局
npm update -g abc
yarn global upgrade abc

项目创建(模板)

在使用lerna publish时,确保以下2点:

  • node源为https://registry.npmjs.org/
  • npm账号为登录状态,因为lerna在提交时会创建git钩子。如果报错了,目前没有很好的方案去回滚解决。
npm login
Username: xxxxx
Password:
Email: (this IS public) [email protected]
Logged in as xxxxx on https://registry.npmjs.org/.

登录成功后,再使用lerna publish发布版本。

typescript类型声明

约束参数的函数类型,返回参数函数的返回值

  • 提取参数类型: Parameters
  • 提取函数返回值类型: ReturnType
const fnIntercept = <F extends (...args: any[]) => any>(fn: F, intercept) => {
  return (...args: Parameters<F>): ReturnType<F> => {
    intercept(...args);
    return fn(...args);
  };
};

理解宏任务和微任务

event loop

以一个宏任务(script(整体代码))开始,内部执行task,执行完当前所有的微任务队列后,单个宏任务完毕,返回值。
可以简单理解为2层循环,外层为宏任务循环,内存为微任务循环。

event loop

  • 宏任务主要有:script(整体代码)、setTimeout、setInterval、I/O、UI 交互事件、postMessage、MessageChannel、setImmediate(Node.js 环境)。
  • 微任务主要有:Promise.then、 MutationObserver、 process.nextTick(Node.js 环境)。

特点:

  • 如果函数的return值未被console,可以忽略,因为表现不一致(如果返回值使用了,就正常输出),跟引擎有关,暂不做深入
  • micro-task 当前循环会执行完全部微任务

执行下看看,会输出啥

(function test() {
    setTimeout(function() { console.log(1) }, 0); //回调会被添加到macro-task队列
    new Promise(function(resolve) {
        console.log(2);
        for (var i = 0; i < 10000; i++) {
            i == 9999 && resolve();
        }
        console.log(3);
    }).then(function() { // then 1
        console.log(4);
        new Promise(function(resolve) {
            setTimeout(function() { //回调会被添加到macro-task队列
                resolve();
                console.log(5);
            }, 0);
            console.log(6);
        }).then(function() { // then 2
            console.log(7); //回调会在resolve后,添加到micro-task队列
        });
    }).then(function() { // then 3
        console.log(10);
        new Promise(function(resolve) {
            setTimeout(function() { //回调会被添加到macro-task队列
                resolve();
                console.log(11);
            }, 0);
            console.log(12);
        }).then(function() { // then 4
            console.log(13); //回调会在resolve后,添加到micro-task队列
        });
    })
    console.log(8);
    return 9;
})();
//控制台打印的结果如下:
//2
//3
//8
//4
//6
//10
//12
//9 (此次是chrome输出的,干扰项,请忽略,因为多个语句块,也只返回最后一个,所以没有任何意义~)
//1
//5
//7
//11
//13

分析:

从script开始(整体代码):

第一轮:

入栈:
task = [
2,
3,
8,
]

console:
2,3,8,

简化宏任务,实际是发起异步任务,异步任务执行完毕,再将cb存放到macro-task

macro-task = [
1,
]
micro-task = [
then 1
]

执行micro-task: then 1
入栈:
task = [4, 6]
console:
4,6
macro-task = [
setTimeout(1),
setTimeout(5)
]
micro-task = [
then 3
]

执行micro-task:then 3
入栈:
task = [10, 12]
macro-task = [
setTimeout(1),
setTimeout(5),
setTimeout(11)
]

setTimeout(1) 直接执行,没啥可说的
...
执行macro-task: setTimeout(5)
入栈:
task = [5]
micro-task = [then 2]

最终console:
2,3,8,4,6,10,12
1,
5,7
11,13

能理解下面的,基本没问题了

let a = (function test() {
    setTimeout(function() { console.log(1) }, 0); //回调会被添加到macro-task队列
    new Promise(function(resolve) {
        console.log(2);
        for (var i = 0; i < 10000; i++) {
            i == 9999 && resolve();
        }
        console.log(3);
    }).then(function() { // then 1
        console.log(4);
        new Promise(function(resolve) {
            setTimeout(function() { //回调会被添加到macro-task队列
                resolve();
                console.log(5);
            }, 0);
            console.log(6);
        }).then(function() { // then 2
            console.log(7); //回调会在resolve后,添加到micro-task队列
        });
    }).then(function() { // then 3
        console.log(10);
        new Promise(function(resolve) {
            setTimeout(function() { //回调会被添加到macro-task队列
                resolve();
                console.log(11);
            }, 0);
            console.log(12);
        }).then(function() { // then 4
            console.log(13); //回调会在resolve后,添加到micro-task队列
        });
    })
    console.log(8);
    return 9;
})();
console.log(a)
console.log(77);

let b = (function test2 () {
    setTimeout(function() { console.log(21) }, 0);
    new Promise(function(resolve) {
        console.log(22);
        for (var i = 0; i < 10000; i++) {
            i == 9999 && resolve();
        }
        console.log(23);
    }).then(function() { // then 1
        console.log(24);
        new Promise(function(resolve) {
            setTimeout(function() { //回调会被添加到macro-task队列
                resolve();
                console.log(25);
            }, 0);
            console.log(26);
        }).then(function() { // then 2
            console.log(27); //回调会在resolve后,添加到micro-task队列
        });
    })
    console.log(88);
    return 99;
})();

console.log(b)

//控制台打印的结果如下:
//2
//3
//8
//9
//77
//22
//23
//88
//99
//4
//6
//24
//26
//10
//12
//1
//21
//5
//7
//25
//27
//11
//13 

参考 https://zhuanlan.zhihu.com/p/30894022 (图)

JS 基础

判断类型 作用
String string
Number number
Boolean boolean
Null object
Undefined undefined
Symbol symbol
BigInt bigint
Function function
宿主对象 (由 JS 环境提供)
其他任何对象 object

基本类型 (7种)

  • string
  • number
  • boolean
  • null
  • undefined
  • symbol
  • bigint(不常用)

除了 null 和 undefined之外,所有基本类型都有其对应的包装对象

typeof

类型
String string
Number number
Boolean boolean
Null object
Undefined undefined
Symbol symbol
BigInt bigint
Function function
宿主对象 (由 JS 环境提供)
其他任何对象 object

Object.js(o1, o2)

判断两个值是否相同

参考

sketch源文件的另类打开方式

  • 复制一份,备份
  • 右键源文件,另存为zip格式
  • 双击打开,就能看到文件目录结构:
document.json
meta.json
pages
previews
user.json

layout

左右2列布局

场景: 常见于移动端表单布局

使用flex布局,核心是justify-content: space-between; 表示:

均匀排列每个元素,首个元素放置于起点,末尾元素放置于终点

.layout-2-columns{
  display: flex;
  justify-content: space-between;
}
<div class="layout-2-columns" >
  <div class="label">城市</div>
  <div class="wrapper">
    <select name="cars" id="cars">
      <option value="volvo">Volvo</option>
      <option value="saab">Saab</option>
      <option value="mercedes">Mercedes</option>
      <option value="audi">Audi</option>
    </select>
  </div>
</div>

参考

astexplorer库 源码分析(-)

背景

为什么想分析这个库呢,因为我最近有个项目,想学习如何实现一个AST解析。

而这个库呢,包含了很多AST库如何解析,是一个非常好的入手项目。

线上地址:https://astexplorer.net/
仓库地址:https://github.com/fkling/astexplorer

技术架构

  • react
  • saga
  • redux

项目结构

// todos

核心**

parser

  • parser.loadParser
  • parser.parse(realParser, code, parserSettings || parser.getDefaultOptions())
  • parser.getNodeName(value)
  • parser.nodeToRange(value)
  • parser.forEachProperty(value)
  • parser.locationProps.has(key)
  • parser.typeProps.has(key)
  • parser.settings

ast

//todos

移动端手势

什么是手势

为什么要做手势

如何构建手势库

实现手势的底层原理

原生触摸事件

  • touchstart
  • touchmove
  • touchcancel
  • touchend

原生事件的缺陷

移动端click会有2个问题:

  • 会有200~300ms的延迟
  • 延迟响应,会出现穿透,即点击会触发非当前层的点击事件。

移动浏览器为什么会有300ms的延迟呢

是因为双击缩放的功能,这个功能最早是由iphone为解决访问网站所设计(当时主流网站都是为大屏设计)

300ms延迟目前最好的解决方案

  • 使用fastclick库,大概10k左右,如果不介意引入库大小
  • 封装一个tap事件,用例代替click事件。

常见的事件和手势

  • tap
  • longtap
  • dbtap
  • move/drag
  • swipe(Right/Left/Up/Down)
  • pinch/zoom
  • rotate

应用场景

实现手势的开源类库

参考

plantuml 安装graphviz失败处理[mac]

查看 brew.git 当前源

$ cd "$(brew --repo)" && git remote -v
origin https://github.com/Homebrew/brew.git (fetch)
origin https://github.com/Homebrew/brew.git (push)

查看 homebrew-core.git 当前源

$ cd "$(brew --repo homebrew/core)" && git remote -v
origin https://github.com/Homebrew/homebrew-core.git (fetch)
origin https://github.com/Homebrew/homebrew-core.git (push)

修改 brew.git 为阿里源

$ git -C "$(brew --repo)" remote set-url origin https://mirrors.aliyun.com/homebrew/brew.git

修改 homebrew-core.git 为阿里源

$ git -C "$(brew --repo homebrew/core)" remote set-url origin https://mirrors.aliyun.com/homebrew/homebrew-core.git

zsh 替换 brew bintray 镜像

$ echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles' >> ~/.zshrc
$ source ~/.zshrc

bash 替换 brew bintray 镜像

$ echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles' >> ~/.bash_profile
$ source ~/.bash_profile

刷新源

$ brew update

切换完毕后再次执行brew install Graphviz ,成功!

参考:

如何有效阅读开源库源码

工具

codecrumbs

可视化代码结构,给代码之前增加关联,注释及按步骤播放

madge

madge
代码生成依赖关系图

带着目的去看源码

带着一个问题去看,最小原则,去掉干扰元素。

github manage project

使用github的功能来管理项目的整个迭代。

issues语言问题

issue,一般都是用英文写,那如果项目都用issue来创建,英语不好,全靠翻译,这尼玛自己给自己添堵呢!效率呢?
推荐issues-translate-action) 做双语翻译,nice!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.