2016 小回顾

github-2016.png

时间很快, 已经走到了 2016 的末尾, 惯例的做个小回顾。(注:这篇起笔的时间是圣诞节TAT)

年初定的目标除了没有能合理安排追番时间, 其他的都基本完成了!(话说今年追番的时间简直少的可怜QAQ)

2016 年发生了太多的事情,要回顾的事情很多,索性就不写那么多了, 只按时间序稍微列几件有趣的事情。

  • 单表亿级数据量的 MongoDB 做在线实时的数据拆分
  • 在之前做的一些应用性能分析的方案上做了一些额外的设计和开发(明年修改下开源出来)
  • PyCon China 2016
  • 一些预期的计划顺利推进、落地,产出了一些系统
  • 看了很多源码,折腾了很多东西,如果以后有空就写点东西出来(我又在给自己挖坑了)
  • 认识了很多有趣的小伙伴~

全年的状态基本和上面的截图是一致的, 全年都在 coding (截图仅限 GitHub上的记录)倒也比较开心, 另外就是现在看到自己项目的 star/fork 数,文章的阅读/收藏/转发数之类的,也已经不像以前看到 star 数刚上百时候会有那种喜悦了,大概这也是另一种成熟? 哈哈哈

另外写一下今年对我比较重要的几个数字:

  • 1354
  • 376
  • 105

对这些数字的解释, 放在以后吧 :-)

2017 年,希望想做的事情都能基本完成,挖的坑慢慢填。 感谢一路上陪我走过的各位!


可以通过下面二维码订阅我的文章公众号【MoeLove】

MoeLove

关于 webpack 你可能忽略的细节(附源码分析)

注:本篇不是入门教程,入门请直接查看官方文档。本篇的主要目标是通过实际问题来介绍 webpack 中容易被人忽略的细节, 以及源码分析(以最新发布的 release 版本1.14.0的源码为例), 并且提供几种解决方案。

webpack from the official website

随着前端技术的火热发展,工程化,模块化和组件化的思想已逐步成为主流,与之相应的,就需要有一整套工具流可以支撑起它。

现在比较热门的前端资源模块化管理和打包工具应该非 Webpack 莫属了。

Webpack 是什么

它可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分隔,等到实际需要的时候再异步加载。通过 loader 的转换,任何形式的资源都可以视作模块,比如 CommonJs 模块、 AMD 模块、 ES6 模块、CSS、图片、 JSON、Coffeescript、 LESS 等。
–引自 Webpack 中文指南

使用举例

我们来看一下官方文档中的最小用例,新建并写入以下内容到这两个文件:

cats.js

1
2
var cats = ['dave', 'henry', 'martha'];
module.exports = cats;

app.js (Entry Point)

1
2
cats = require('./cats.js');
console.log(cats);

这个时候,就可以使用 webpack 进行打包了:

1
webpack ./app.js app.bundle.js

我们来看一下发生了什么, 目录下生成了一个打包后的文件 app.bundle.js ,这就是最基础的打包过程。

提出问题

如何判断打包是否成功?

通用方案

下面是我们常用的两种判断任务是否执行成功的方案

通过 return code

通过命令执行后的 return code 来判断(在 shell 中使用 $? 获得)。 并且通常情况下 0 是执行成功, 非 0 是未成功。 我们以上面的例子来测试一下:

webpack-demo.png

可以看到 $? 的值为 0 , 且打包后的文件运行正常。

那么我们来修改一下 app.js 文件的内容, 将 require 引入的模块路径故意写错,来测试一下:

webpack-error.png

注意:箭头处 $? 的值仍然为 0, 且生成的打包后的文件运行出错。

这就说明,根据 return code 的值判断任务是否执行成功, 不可行!

通过标准错误输出

我们也会通过标准错误输出stderr)来判断一个任务执行过程中是否有错误输出。还是使用上面的例子做示范:

webpack-stderr.png

根据这个例子,可以看到 webpack 并没有标准错误输出!所以这个方法也不可行。

探究原因及源码分析

这里以最新发布的 release 版本 1.14.0 的源码作为分析。 在 lib/Compilation.js 中我们可以看到这样一段代码:

1
2
3
4
5
6
7
8
9
10
11
var errorAndCallback = function errorAndCallback(err) {
err.dependencies = dependencies;
err.origin = module;
module.dependenciesErrors.push(err);
_this.errors.push(err);
if(bail) {
callback(err);
} else {
callback();
}
};

在源码中可以看到这个函数其实被调用的还比较多, 例如:在模块为可选的时候, 会判断只是抛出警告还是处理错误, 而上面这段代码自然也不必多数, 关键点在于 bail 的值, 而我们继续找, 可以看到在 bin/config-optimist.js 中有对 bail 参数的解析, 这是一个布尔值。而因为没有太多描述, 所以这个参数就经常容易被忽略。

解决方案

1. 加 bail 参数

基于上面简要的分析, 我们来尝试下 bail 参数的作用。 仍然使用上面的例子:

我们使用 webpack --bail true app.js app.bundle.js 进行测试

webpack-bail.png

可以看到, 使用 bail 参数并传递 true 进去, 在遇到错误的时候,打包过程将会退出, return code1 且把错误信息打印到 stderr .

2. 使用 webpack-fail-plugin

webpack-fail-plugin 是专为解决这个问题而生的,它会在错误发生的时候 return 1. 使用方法也很简单:

安装:

1
npm install webpack-fail-plugin

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var failPlugin = require('webpack-fail-plugin');
 
module.exports = {
    //config 
    plugins: [
        failPlugin
    ]
}
```


### 3.使用 done plugin

具体用法如下:

```javascript

// ...
plugins: [
// ...
function() {
this.plugin("done", function(stats) {
if (stats.compilation.errors && stats.compilation.errors.length) {
console.log(stats.compilation.errors);
process.exit(1);
}
// ...
});
}
// ...
],
// ...

4. 使用 webpack 2

不过 webpack 2 现在还在 beta 阶段,可以期待下。 (webpack 2 也仍然是使用 bail 参数)

Composer 使用技巧简述

最近使用了世界最好的语言 PHP 用来管理依赖关系的工具 Composer. 稍微做点记录, 以做备忘. 如有错误还望指出.

安装

1
2
3
4
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('SHA384', 'composer-setup.php') === 'aa96f26c2b67226a324c27919f1eb05f21c248b987e6195cad9690d5c1ff713d53020a02ac8c217dbf90a7eacc9d141d') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

上述代码来自官网.

局部安装

上述代码执行完成后, 只是下载到了 composer.phar 文件, 可以通过 php composer.phar 在任意位置执行.

全局安装

全局安装只是把 composer.phar 安装到 PATH 下即可. 可以像下面这样:

1
sudo mv composer.phar /usr/local/bin/composer

国内镜像加速

使用Composer中文网提供的中国全量镜像进行加速.

单项目加速

进入项目目录(即 composer.json 文件所在目录) 执行:

1
composer config repo.packagist composer https://packagist.phpcomposer.com

这条命令将在 composer.json 文件的末尾添加镜像加速配置:

1
2
3
4
5
6
7

"repositories": {
"packagist": {
"type": "composer",
"url": "https://packagist.phpcomposer.com"
}
}

全局加速

可以直接执行:

1
composer config --global repo.packagist composer https://packagist.phpcomposer.com

配置

参考 官方配置文档.

因为 composer 默认情况下, 会在使用的用户的家目录下生成 .composer 文件夹, 存放配置相关, 所以我们可以对这些部分进行配置.

1
2
3
composer config --global data-dir /www/.composer
composer config --global cache-dir /www/.composer
# cache-files-maxsize 也可以稍微大一点, 还有缓存时间

Auth 相关

Gitlab token 可以查看 https://docs.gitlab.com/ee/api/README.html#oauth-2-tokens

注意事项

type 配置为 gitlab, github 等时, 会优先通过 .composer/auth.json 文件中的配置来选择认证信息进行验证. 如果没有解决 Auth 认证相关问题, 可以使用普通的 vcs 例如 git 来使用.

Git workflow 详谈

作为一名工程师, Git 在日常开发中是不可或缺的工具。
这里详细介绍几种比较常用的基于 Git 的工作流模型, 以便于团队协作的规范化和效率提升。

中心化工作流

使用过SVN的应该都知道, SVN使用的是集中式管理流程, 如果你刚从SVN 切换到 Git , 你可以尝试使用中心化工作流的方式。这样,你几乎不需要变更之前的工作方式, 就可以完成平滑的过渡了。 而且在使用过程中还可以看到 Git 优于 SVN 的地方:
第一,每个成员都可以在本地拥有一份完整的项目代码仓库,而不只是一个工作区的副本,任何人都可以在本地执行 addcommit ,而不需要考虑远端仓库是否有变更,直到需要的时候再去提交即可。
第二,Git 的工作区、暂存区、引用更新等设计,可以给开发者更多自由来切换当前工作,且不会造成代码丢失。

工作细节

中心化工作流的方式是:在远端(远端可以是服务器端,也可以是本地的任意目录)新建一个仓库,默认是 master 分支,作为唯一的中心仓库。 所有人都 clone 这个仓库作为本地仓库,并在本地仓库进行开发。本地的提交是和远端仓库无关的,等需要的时候再 push 进主仓库的 master 分支即可。

在这种方式下, 远端是唯一确定的中心仓库, 所有人都要以这个仓库为准。 所以,在提交之前要先 fetch 最新提交,在这些提交之上作出自己的更改(一般我们使用 rebase来完成)。

如果本地的修改和远端仓库中的变更发生了冲突,那么 Git 会暂停 rebase ,并让你来解决这些冲突。我们可以很简单的使用 git statusgit add 等命令完成冲突的合并。 另外, 如果我们解决不了冲突, 我们也可以使用 git rebase --abort 很容易的退出 rebase 的过程。

这样每天的工作方式就变成了,从中心仓库拉取最新代码, 然后开始一天的工作, 开发完成后,拉取中心仓库的更新, 合并代码后, 再提交至中心仓库, 结束一天的工作。 这样的好处就是不需要变更原先(使用SVN)的工作方式。当然弊端也很明显,你并不知道中心仓库的代码是否是稳定的,或者说并不能确定当你的代码和中心仓库代码合并后,是否是稳定的,带来的问题就是开发进度和回滚不那么方便控制。

示例

我们有两位程序员, A 和 B, 两人同时在对一个项目做开发, 并且使用 Git 的中心化工作流方式。

1.创建远端中心仓库

这里我们有两种方式:

  • 借助于已经搭建好的平台 GitHub/GitLab 之类的,点击 create repo 即可。

  • 在远端(这里只是为了区别本地仓库,事实上,使用任何一个其他人可以连通的机器都可以,包括自己本地其他目录) 创建一个 裸仓库 ,创建裸仓库和我们平时创建本地仓库的区别,可以参考我另一篇文章 Git 本地仓库和裸仓库

这里以第二种方式为例:

1
2
# --bare 参数必须有
git init --bare /the/repo/path.git

2.所有人都 clone 中心仓库到本地作为本地仓库

1
git clone /the/repo/path.git

注意仓库地址必须是正确的, 且有权限访问才能 clone 成功。

3.程序员 A 在他的本地仓库进行功能开发并进行发布

一般情况下,我们通过 git status 看看当前状态,并通过 git addgit commit 等命令完成本地仓库的提交。 当然这个提交影响的也只是本地仓库而已,并没有对中心仓库产生任何影响,所以我们既不需要关心别人有什么提交,也不用担心我们当前的提交是否对别人造成了影响。当 A 认为自己所开发的功能已经完成, 那他将执行 git push origin master 这样的操作,将自己本地仓库所有不存在于中心仓库的提交都 push 到远端的中心仓库上。

4.程序员 B 在他本地仓库进行功能开发

B 在 clone 中心仓库后所做的操作和 A 一样,在本地仓库进行项目开发,并在本地仓库进行提交,他不需要知道中心仓库发生了什么样的变化。

5.程序员 B 将自己开发的功能并进行发布

B 在确认自己开发的功能已经完成后,想要将自己的代码通过 git push origin master 这样的操作发布至中心仓库,但是却被中心仓库提示他的修改已经和中心仓库有了分叉, 需要他先执行 git pull 之类的操作, 将中心仓库上 A 的提交与 B 本地的提交进行合并才允许他并入中心仓库。所以,他执行了 git pull --rebase origin master 来将中心仓库的修改并入他的本地仓库。使用 --rebase 参数的意义在于 fetch 执行完成后,将把 B 的所有提交移至 master 顶端。

当然这里不使用 --rebase 参数也会成功,只不过是会生成一个合并提交,有些情况下使用 --ff 参数也可以避免产生合并提交。在这里使用 --rebase 只是一个建议操作。

如果 A 和 B 修改的文件没有关联,一般情况下会直接完成合并,如果发生冲突,Git 将会暂停 rebase 的过程,并列出当前冲突的文件,你可以简单的使用 git statusgit add 等命令进行合并,合并后使用 git rebase --continue 继续 rebase 的过程。或者使用 git rebase --abort 退出 rebase 过程。

在 B 合并完成后,可以执行 git push origin master 将自己开发的功能发布至中心仓库。

至此,基础的中心化工作流方式就介绍完了,但是这里也很容易看出来其中的问题,除了前面说到过的以外,还有就是效率低下,如果很多人都在持续进行提交,那很影响新功能的提交(多人持续性进行提交)。 一个比较容易提升效率的方式就是切换到特性分支工作流的方式。

特性分支工作流

基于特性的分支工作流,可以为每个特性做隔离,避免对中心仓库主干代码造成影响。

工作细节

顾名思义, 就是根据每个特性都会开一个新的分支,每个分支都应该包含着描述性的名称,无论是一个人开发,还是多人协同,该特性的全部开发工作都在这个分支上进行。待该特性开发完成后, 并入主分支,然后删除分支,代码上线。

这种情况下, 最大的优势在于, 所有的特性开发都可以并行处理。 不必要像中心化工作流方式, 每个人的变动都可能引起其他的人的代码合并, 并且所有功能都杂糅在一起, 从测试和回滚都会变得很繁琐。 另外的一个好处就是特性分支可以推送到中心仓库,这样也便于单独测试。

这里需要注意的是,特性分支往主分支合并的时机,应该是该特性开发完成,并测试通过,避免对主干代码造成污染。

在进行分支隔离后,我们发现,我们当前只处理了开发模式,但并没有涵盖一个很完备的产品生命周期, 开发、发布、维护等过程,所以,我们有了 Gitflow 工作流。

Gitflow 工作流

基于Gitflow 的工作流方式, 这种工作流方式, 主要是管理着新功能开发,发布及维护等模式,根据不同类型的工作对分支进行定义, 分为 特性分支修复分支release 分支开发分支主分支

主分支:中心仓库建立后的默认 master 分支(当然使用其他分支也可以,但要保证该分支是受保护的)。主分支随时保持代码是稳定的,并且有明确的版本标签,后续代码回滚等操作都将从主分支进行。

开发分支:中心仓库建立后,从 master 分支切出来,此时与 master 分支保持一致。后续演进中,开发分支随时保持代码最新,但却不一定是线上实际运行的代码。

1
git checkout -b develop

特性分支:应该从开发分支切出,开发完成后, 再合并进入开发分支, 如果达到了发布标准, 则从开发分支切出 release 分支, 切出来的这个分支,只做该版本内的代码修复, 不再加入新功能, 这时此分支处于锁定的状态。

修复分支, 用于对线上主分支代码的及时修复, 待修复完成后, 合并进入主分支, 再并入开发分支。 修复分支只能从主分支切出。

发版分支, 一般命名为 release-xxx 这个分支只能从开发分支切出, 最后并入主分支,打上版本号的标签,它也应该并入开发分支,如果中间有其他修复的话。

fork 工作流

fork 分支流和上面介绍的所有工作流都不太一样。它的上游有一个唯一仓库, 所有人都是 fork 这个仓库, 在自己的远端和自己的本地各维护一个仓库,待开发完成后推入自己的远端仓库,并结合 GitHub/GitLab等提交 Pull Request,进入了 review 阶段,待通过后,将会被合并入上游唯一的仓库。这种方式比较适合 GitHub 中的大型开源项目, 对于小团队的内部项目, 这种方式可能未必合适。
而且 fork 工作流, 会占用更多的资源(毕竟每个人都维护一份远端仓库)。 而且每个人都看不到其他人的动态,只有当提交 Pull Request 的时候, 才知道每个人发生了什么。

总结

我个人比较推荐的是 Gitflow 的开发工作流, 这种方式下,一切都是可控的, 每个分支都有各自独立的功能,目的性很明确, 同时,在做代码回滚之类的操作也是可以直接剔除。 另外, 在这种工作流方式下, 团队中的每个人都能很轻易的知道其他人在做什么, 做出了什么样的改变, 对于团队协作, 或许更加合适。

当然所有的工作流并不一定能完全套用, 可以吸取一些规范, 合并入自己的日常工作, 将代码仓库的合作流程标准化和规范化, 这也是一切自动化的基础。

<全文完>

Git 本地仓库和裸仓库

通常我们会用 git init 命令来将我们所在的目录转换为一个 Git 本地仓库或者初始化一个新的空仓库。

用法

  • 将当前目录转换为一个本地仓库
1
git init

这个命令执行后会在本地生成一个 .git 的文件夹,用来追踪仓库的所有变更。效果如下:

git init

  • 指定某个目录成为本地仓库
1
git init <repo>

这个命令执行后, 将创建一个名为repo且只包含 .git 子文件夹的空目录。效果如下:

git init repo

  • 指定某个目录成为中心仓库(裸仓库)
1
git init --bare <repo>

这个命令执行后,将在本地创建一个名为 repo 的文件夹, 里面包含着 Git 的基本目录, 我们一般会将这个文件夹命名为后面加 .git 的形式,如 repo.git (这也是为什么我们从 GitHub clone 仓库的时候,地址都是 xxx.git 这样的形式的原因)。效果如下:

git init --bare repo.git

详细说一下使用 --bare 参数的含义,使用 --bare 参数初始化的仓库,我们一般称之为裸仓库, 因为这样创建的仓库并不包含 工作区 , 也就是说,我们并不能在这个目录下执行我们一般使用的 Git 命令。

对比

我们来对比一下直接使用 git init 创建的仓库和加了 --bare 参数的两个仓库。 我们直接看两个仓库的的 config 文件中的内容:

  • 直接 git init 创建的仓库:
1
2
3
4
5
6
7
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
  • 加了 --bare 创建的裸仓库:
1
2
3
4
5
6
[core]
repositoryformatversion = 0
filemode = true
bare = true
ignorecase = true
precomposeunicode = true

可以看到最直观的差异在于 bare 配置项是否为 true , 此外不加 --bare 创建的本地仓库配置中有一项 logallrefupdates = true , 作用根据名字就可以看出来, 记录所有的 ref (引用) 更新, 关于 ref 的部分之后有时间可以再写,这个配置可以理解为是 Git 的一道防线。

功能差异

我们可以使用最简单的例子演示一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

# 直接创建本地仓库
(Tao) ➜ git init repo

# 创建裸仓库
(Tao) ➜ git init --bare repo.git

# 分别 clone 两个仓库
(Tao) ➜ git clone repo c1
Cloning into 'c1'...
warning: You appear to have cloned an empty repository.
done.
(Tao) ➜ git clone repo.git c2
Cloning into 'c2'...
warning: You appear to have cloned an empty repository.
done.

# 进入 c1 仓库

(Tao) ➜ cd c1
(Tao) ➜ c1 git:(master) touch test
(Tao) ➜ c1 git:(master) ✗ g add -A
(Tao) ➜ c1 git:(master) ✗ g commit -m "test commit"
[master (root-commit) b1e32ad] test commit
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 test
(Tao) ➜ c1 git:(master) git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 200 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: error: refusing to update checked out branch: refs/heads/master
remote: error: By default, updating the current branch in a non-bare repository
remote: error: is denied, because it will make the index and work tree inconsistent
remote: error: with what you pushed, and will require 'git reset --hard' to match
remote: error: the work tree to HEAD.
remote: error:
remote: error: You can set 'receive.denyCurrentBranch' configuration variable to
remote: error: 'ignore' or 'warn' in the remote repository to allow pushing into
remote: error: its current branch; however, this is not recommended unless you
remote: error: arranged to update its work tree to match what you pushed in some
remote: error: other way.
remote: error:
remote: error: To squelch this message and still keep the default behaviour, set
remote: error: 'receive.denyCurrentBranch' configuration variable to 'refuse'.
To /Users/tao/repo
! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to '/Users/tao/repo'

# 进入 c2 仓库重复执行

(Tao) ➜ c1 git:(master) cd ../c2
(Tao) ➜ c2 git:(master) touch test
(Tao) ➜ c2 git:(master) ✗ git add -A
(Tao) ➜ c2 git:(master) ✗ git commit -m "test commit"
[master (root-commit) 7aacc58] test commit
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 test
(Tao) ➜ c2 git:(master) git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 201 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To /Users/tao/repo.git
* [new branch] master -> master

从裸仓库 clone 下来的本地仓库可以进行正常的 push 操作, 但是从一般仓库 clone 下来的本地仓库却不行。 这也正是裸仓库存在的意义。 裸仓库一般情况下是作为远端的中心仓库而存在的。

总结

使用 git init --bare <repo> 可以创建一个裸仓库,并且这个仓库是可以被正常 clonepush 更新的, 裸仓库不包含工作区,所以并不会存在在裸仓库上直接提交变更的情况。

源码编译Vim 8

Vim 8.0 在2016.09.12发布了, 在各 Linux 发行版还没更新包的时候就源码编译来使用吧! 以 Debian 编译 Vim 为例.

简单编译截图

下载Vim 源码

可以在Vim 官网下载打包好的源代码.
或者使用 Git:

1
git clone https://github.com/vim/vim.git

编译

1
2
3
cd vim/src

./configure --with-compiledby="TaoBeier" --with-features=huge --enable-pythoninterp=yes --with-python-config-dir=/usr/lib/python2.7/config-x86_64-linux-gnu --enable-cscope --enable-perlinterp=yes --enable-rubyinterp=yes --with-luajit --enable-luainterp=yes --with-lua-prefix=/usr/include/lua5.1 --enable-multibyte --with-x --enable-fail-if-missing

上面的参数使用 ./configure --help 都可以看到对应用途. 上面使用的参数是增加了对 Python, Ruby, Perl, 以及X window的支持.

建议加上 --enable-fail-if-missing 参数, 以方便定位到哪里依赖缺失.

依赖

这里检查可能通不过, 首先是 lualuajit. 需要执行

sudo apt-get install lua5.1 liblua5.1-0 luajit libluajit-5.1-dev

其次注意 Python 的 config 位置.

最后就是 Perl 需要 sudo apt-get install libperl-dev.

最终

1
make && sudo make install

最终结果

完整编译截图

PS: 其实我一开始是想介绍 Vim 8 的, 然后碰巧在 GitHub 上有人问如何编译之类的, 就先写了这篇.

利器系列-更高效的Vim

这是利器系列第0篇, 当然要以每天我使用率最高的Vim来开始啦!

截图

screenshot.png

安装

(你需要一个有Python支持的Vim版本. 请使用 vim --version | grep +python 来检查)

  • 依赖(Debian/Ubuntu 平台)

    sudo apt-get install python vim exuberant-ctags git

    sudo pip install dbgp vim-debug pep8 flake8 pyflakes isort

  • 依赖(RedHat/CentOS 平台)

    CentOS 6.7的yum源自带的Python版本较旧,推荐自行安装Python2.7.

    sudo yum install python vim ctags git

    sudo pip install dbgp vim-debug pep8 flake8 pyflakes isort

  • 依赖(Mac OS 平台)

    brew install python vim git

    wget http://tenet.dl.sourceforge.net/project/ctags/ctags/5.8/ctags-5.8.tar.gz && tar -zxvf ctags-5.8.tar.gz && cd ctags-5.8 && ./configure && make && sudo make install

    sudo pip install dbgp vim-debug pep8 flake8 pyflakes isort

  • 下载vimrc 文件到用户主目录

    wget https://raw.githubusercontent.com/tao12345666333/vim/master/vimrc -O $HOME/.vimrc

  • 打开 Vim

    打开Vim, 它将会自动安装插件. 请耐心等待它完成. 或者你可以使用下面的命令来自行安装.

    vim -E -u $HOME/.vimrc +qall

  • 享受你的Vim并个性化它吧!

支持特性

插件管理(Vundle)

在这份配置中,使用了Vundle作为插件管理器. Vundle会自动接管 .vim 文件夹,所有配置好的插件将默认下载至~/.vim/bundle/, 在使用之前请确保.vim文件夹干净. Vundle的插件安装需要触发 git clone 操作,搜索需要 curl 支持.

配置(截取了部分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
" let Vundle manage Vundle
Bundle 'gmarik/vundle'

" ============================================================================
" Active plugins
" You can disable or add new ones here:

" Plugins from github repos:

" Python and PHP Debugger
Bundle 'fisadev/vim-debug.vim'
" Better file browser
Bundle 'scrooloose/nerdtree'
" Code commenter
Bundle 'scrooloose/nerdcommenter'
" Class/module browser
Bundle 'majutsushi/tagbar'
" Code and files fuzzy finder
Bundle 'kien/ctrlp.vim'
" Extension to ctrlp, for fuzzy command finder
Bundle 'fisadev/vim-ctrlp-cmdpalette'
" Zen coding
Bundle 'mattn/emmet-vim'
" Git integration
Bundle 'motemen/git-vim'
" Tab list panel
Bundle 'kien/tabman.vim'

支持操作

命令 解释
:PluginList 列出所有Plugin
:PluginInstall(!) 安装/更新Plugin
:PluginSearch(!) foo 搜索foo相关的Plugin
:PluginClean(!) 清理未使用的Plugin
:PluginUpdate 更新插件

工程文件浏览(NERDTree)

在这份配置中, 使用了NERDTree查看文件列表. 你可以在NERDTree中浏览和打开你文件系统中的目录或文件. 还可以进行文件隐藏和过滤, 设置添加书签等. 在NERDTree窗口输入?可获得操作指南. 这份配置中默认过滤掉了.pyc, .git, .hg, .svn等文件或文件夹的显示.

配置

1
2
3
4
5
6
7
8
9
10
11
12
" auto open or close NERDTree
autocmd vimenter * if !argc() | NERDTree | endif
autocmd bufenter * if (winnr("$") == 1 && exists("b:NERDTreeType") && b:NERDTreeType == "primary") | q | endif

" NERDTree -----------------------------

" toggle nerdtree display
map <F3> :NERDTreeToggle<CR>
" open nerdtree with the current file selected
nmap ,t :NERDTreeFind<CR>
" don;t show these file types
let NERDTreeIgnore = ['\.pyc$', '\.pyo$']

支持操作

快捷键 解释
F3 打开/关闭NERDTree
,t 打开NERDTree并选中当前文件

语法检查

在这份配置中, 使用Syntastic插件进行语法静态检查. 包括但不限于C/C++/Go/Python/Haskell/Ruby/JavaScript等. 在本配置中对JavaScript的静态检查使用eslint,可以支持ES6及JSX等, 细节可以参考JSLint, JSHint和ESLint的对比及Vim配置, 想要切换检查工具只要修改对应位置即可.

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
" Syntastic ------------------------------

" show list of errors and warnings on the current file
nmap <leader>e :Errors<CR>
" turn to next or previous errors, after open errors list
nmap <leader>n :lnext<CR>
nmap <leader>p :lprevious<CR>
" check also when just opened the file
let g:syntastic_check_on_open = 1
" syntastic checker for javascript.
" eslint is the only tool support JSX.
" If you don't need write JSX, you can use jshint.
" And eslint is slow, but not a hindrance
" let g:syntastic_javascript_checkers = ['jshint']
let g:syntastic_javascript_checkers = ['eslint']
" don't put icons on the sign column (it hides the vcs status icons of signify)
let g:syntastic_enable_signs = 0
" custom icons (enable them if you use a patched font, and enable the previous
" setting)
let g:syntastic_error_symbol = '✗'
let g:syntastic_warning_symbol = '⚠'
let g:syntastic_style_error_symbol = '✗'
let g:syntastic_style_warning_symbol = '⚠'

特性

保存时自动进行语法静态检查,方便的错误提示及灵活的可扩展性.

支持操作

快捷键 解释
\e 打开错误列表
\n 移动到下一个错误位置
\p 移动到上一个错误位置

Git支持

在这份配置中, 使用vim-fugitivevim-signify做Git方面的支持. 可以进行常用的git操作及优雅的状态提示等(目前支持githg).

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
" Signify ------------------------------

" this first setting decides in which order try to guess your current vcs
" UPDATE it to reflect your preferences, it will speed up opening files
let g:signify_vcs_list = [ 'git', 'hg' ]
" mappings to jump to changed blocks
nmap <leader>sn <plug>(signify-next-hunk)
nmap <leader>sp <plug>(signify-prev-hunk)
" nicer colors
highlight DiffAdd cterm=bold ctermbg=none ctermfg=119
highlight DiffDelete cterm=bold ctermbg=none ctermfg=167
highlight DiffChange cterm=bold ctermbg=none ctermfg=227
highlight SignifySignAdd cterm=bold ctermbg=237 ctermfg=119
highlight SignifySignDelete cterm=bold ctermbg=237 ctermfg=167
highlight SignifySignChange cterm=bold ctermbg=237 ctermfg=227

支持操作

快捷键 解释
:Git [args] 类似执行git命令一样
:Gstatus 类似git status.在列表中使用-添加/移除文件
:Gcommit [args] 类似 git commit
:Gmerge [args] 类似 git merge
:Gpull [args] 类似 git pull
:Gpush [args] 类似 git push
:Gvdiff [revision] 类似 git push 但是会切分窗口

更多详细的操作可以使用 :help fugitive

Tag支持

在这份配置中,使用了Tagbar做Tag支持,可以显示当前文件中定义的类/变量等.

配置

1
2
3
4
5
6
" Tagbar -----------------------------

" toggle tagbar display
map <F4> :TagbarToggle<CR>
" autofocus on tagbar open
let g:tagbar_autofocus = 1

支持操作

快捷键 解释
F4 打开Tag列表

超全自动补全

在这份配置中, 使用了Neocomplcache作为主要的自动补全插件.

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
" NeoComplCache ------------------------------

" most of them not documented because I'm not sure how they work
" (docs aren't good, had to do a lot of trial and error to make
" it play nice)

" Disable AutoComplPop.
let g:acp_enableAtStartup = 0
" Use neocomplcache.
let g:neocomplcache_enable_at_startup = 1
let g:neocomplcache_enable_ignore_case = 1
" Use smartcase.
let g:neocomplcache_enable_smart_case = 1
let g:neocomplcache_enable_auto_select = 1

let g:neocomplcache_enable_fuzzy_completion = 1
let g:neocomplcache_enable_camel_case_completion = 1
let g:neocomplcache_enable_underbar_completion = 1
let g:neocomplcache_fuzzy_completion_start_length = 1
let g:neocomplcache_auto_completion_start_length = 1
let g:neocomplcache_manual_completion_start_length = 1
" Set minimum syntax keyword length.
let g:neocomplcache_min_keyword_length = 1
let g:neocomplcache_min_syntax_length = 1
let g:neocomplcache_lock_buffer_name_pattern = '\*ku\*'
" complete with workds from any opened file
let g:neocomplcache_same_filetype_lists = {}
let g:neocomplcache_same_filetype_lists._ = '_'
" <TAB>: completion.
inoremap <expr><TAB> pumvisible() ? "\<C-n>" : "\<TAB>"
" Define keyword.
if !exists('g:neocomplcache_keyword_patterns')
let g:neocomplcache_keyword_patterns = {}
endif
let g:neocomplcache_keyword_patterns['default'] = '\h\w*'
" Plugin key-mappings.
inoremap <expr><C-g> neocomplcache#undo_completion()
inoremap <expr><C-l> neocomplcache#complete_common_string()
" <C-h>, <BS>: close popup and delete backword char.
inoremap <expr><C-h> neocomplcache#smart_close_popup()."\<C-h>"
inoremap <expr><BS> neocomplcache#smart_close_popup()."\<C-h>"
inoremap <expr><C-y> neocomplcache#close_popup()
inoremap <expr><C-e> neocomplcache#cancel_popup()

支持操作

快捷键 解释
<Tab> 使用Tab键进行待提示项目选择
<C-g> 取消补全
<C-l> 完成待补全项中共同的字符串
<C-h> 关闭待选项
<C-y> 关闭待选项
<C-e> 退出待选项
<BS> 关闭待选项

类Tmux的窗口选择

在这份配置中,使用了vim-choosewin进行窗口管理器. 支持类Tmux的操作.

配置

1
2
3
4
5
6
" Window Chooser ------------------------------

" mapping
nmap - <Plug>(choosewin)
" show big letters
let g:choosewin_overlay_enable = 1

支持操作

快捷键 解释
- 开启窗口选择
- [ 选择上一个tab的窗口
- ] 选择下一个tab的窗口

更多操作可以使用 :help choosewin

灵活的Tab管理

在这份配置中使用了TabMan进行Tab管理,可以进行灵活切换与管理

配置

1
2
3
4
5
" TabMan ------------------------------

" mappings to toggle display, and to focus on it
let g:tabman_toggle = 'tl'
let g:tabman_focus = 'tf'

支持操作

快捷键 解释
tl 开启/关闭tab管理
tf 将光标移动到tab管理窗口

优雅的状态栏

在这份配置中,使用了Airline提供更多状态栏支持.

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
" Airline ------------------------------

let g:airline_powerline_fonts = 1
let g:airline_theme = 'bubblegum'
"let g:airline#extensions#tabline#enabled = 1
"let g:airline#extensions#tabline#left_sep = ' '
"let g:airline#extensions#tabline#left_alt_sep = '|'
let g:airline#extensions#whitespace#enabled = 1

" to use fancy symbols for airline, uncomment the following lines and use a
" patched font (more info on the README.rst)
if !exists('g:airline_symbols')
let g:airline_symbols = {}
endif

let g:airline_left_sep = ''
let g:airline_left_alt_sep = ''
let g:airline_right_sep = ''
let g:airline_right_alt_sep = ''
let g:airline_symbols.branch = ''
let g:airline_symbols.readonly = ''
let g:airline_symbols.linenr = ''

支持特性

可以显示分支,语法静态检查结果等.

自动插入头部

在这份配置中写了个小函数根据新建的不同类型的文件,自动插入头部,支持python, ruby, bash等.

Markdown实时预览

在这份配置中, 使用了vim-instant-markdownvim-markdown做Markdown格式的支持,可以支持实时预览等特性.

此功能需要有node环境支持,可以执行 npm -g install instant-markdown-d 进行安装.

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
" Vim-markdown ------------------------------

" Disabled automatically folding
let g:vim_markdown_folding_disabled=1
" LeTeX math
let g:vim_markdown_math=1
" Highlight YAML frontmatter
let g:vim_markdown_frontmatter=1

" Vim-instant-markdown -----------------

" If it takes your system too much, you can specify
" let g:instant_markdown_slow = 1
" if you don't want to manually control it
" you can open this setting
" and when you open this, you can manually trigger preview
" via the command :InstantMarkdownPreview
let g:instant_markdown_autostart = 0

支持操作

快捷键 解释
:InstantMarkdownPreview 手动触发markdown文件的预览

多游标选择、编辑等

在这份配置中, 可以在高亮某单词时, 通过使用 Ctrl-n 或者 Ctrl-p 进行多游标选择, 然后进行编辑或修改等操作.

快速文件查找

在这份配置中, 可以通过使用,R进行全文查找或者,r进行快速查找, 或者在当前字符串上使用,wR以及,wr来进行全文查找或者快速查找.

快速注释

使用NERDCommenter插件完成快速注释, 可以通过\ci进行快速注释.

Python 支持

完备的Python支持, 可以自动识别当前是系统环境或虚拟环境, 使用:Isort可智能对导入包进行排序, 使用:PymodeLintAuto可自动格式化.

除了上述列出的功能以外, 还有很多方便的特性,可以大大提升效率,在使用中慢慢体会吧!有问题可以在tao12345666333/vim on github 提issue

Python性能优化之工具篇

之前对公司的一个后端项目做了些性能优化, 学到一些值得记录的东西, 这篇主要是介绍在做性能分析时所用到的工具, 至于如何优化基本就是智者见智吧, 如果有时间可能会写的.

Python web应用程序的常见性能指标

此处忽略外部DNS解析等方面的性能
  • 响应时间
  • 错误率
  • 吞吐率
  • 执行时间
  • 内存占用

Python 性能分析工具

##TODO

想从二次元群体手里赚钱?你真的懂二次元吗!

“节操”,“鬼畜”,“萌”, “二次元”这些词是不是逐渐的融入了你的日常? 这些词其实都是二次元产物。越来越多的人将关注点放到了“二次元群体”上,但是这里的价值和它真正的含义你懂了吗?

什么是原本的二次元

很多人认为二次元就是指“二维平面”,“平面世界”,也有人认为是ACGN,但这个观点其实是带有误区的。

为什么这样说呢?我们一起来追溯一下“二次元”这个词是什么时候出现的,有部和我年龄相仿的动画片《机动战舰》(暴露年龄系列,我下一篇就写这个),其中的反派们超级崇拜一部叫《激钢人3》的动画片,甚至这部动画片已经成为反派军中的圣典(除了很热血很好看外,其实是因为他们逃亡的时候就只带了这一部动画片吧,充分说明做资源党是有好处的!),当然剧中的主角也是很喜欢这部动画片的。动画(机动战舰)中的反派非常喜欢激钢人3中的女主菜菜子,但是毕竟现实和动画是不可逾越的(明明都是在动画里面!),处于伤感中的反派们相互安慰:“菜菜子再好,但她毕竟只是二次元的女子啊!”,这句话引起了很多ACG爱好者的共鸣,所以逐步的借用了其中的“二次元”来描述自己所喜爱的由ACG创造出来的世界了。

什么是现在的二次元

很多人说现在的二次元是“泛二次元”,可这个”泛“到底是泛到了什么程度呢?这就好像从15年开始,”IP“在圈子里吵得火热,但到底什么是IP?很多人只是知道一个名词或者一个模糊的概念罢了。
有多少真正爆款的IP产生呢?

现在一般指的二次元确实是”泛二次元“,不仅包含着ACGN,还包含着其相关的衍生产物(这不是废话吗?)。也就是这种模棱两可的概念让绝大多数人身处次元壁外,不得其门而入。

为了避免概念混淆,本篇不具体解释“核心二次元文化”和“泛二次元文化”(放到下篇来写),谈谈现在的二次元群体即“泛二次元”群体。

“宅”

大多数情况下,ACG爱好者被冠上了“宅”的标签,外界一般这样看我们“不爱出门”,“就知道看动漫”,“沉迷于虚拟世界”等等。实际上“宅”也分了很多种,“御宅族”应该是相对较早的称呼了,现在大多使用“宅男/宅女”来形容这部分人和长期待在家里不出去的人。并且基于当前环境和舆论等,“宅”一度被加上了变异的色彩。

但是当二次元群体以“宅”自称或称呼别人的时候,一般是指对于某种兴趣爱好的痴迷,有时候也指大触(某方面很厉害的人)。这种倾注了情感的痴迷,不仅支撑着我们可以长时间宅在家,还可以不断的利用现有资源,发展自己的圈子和技能,结识同好,分享兴趣,一起做很多好玩的事情。一般会被称为“动漫宅”,“偶像宅”,“技术宅”等,也会称为“游戏狗”,“单身汪”之类的。(但是我们都有着做现充的觉悟的!)

“腐”

这个群体中女性相比男性更多一些(目前表现出来的是这样),这个群体多表现为对“BL”(boy’s love),“GL”(girl’s love)等的喜爱。“腐”和“宅”并不能完全隔离开,会有重合,不过“宅”群体一般更多的关注点在于虚拟世界中,而很可能一个并不宅的妹子有天你突然发现她其实是腐的,并且长期YY你和其他男同事(orz)。

“Coser”,“lo娘”,“舞见”

作为一枚野生后期,对这部分群体和我接下来要谈的“唱见”等了解也相对多了一些。Coser(玩Cosplay 的人),lo娘(喜欢Lolita穿衣风格的人),舞见(一般指宅舞的舞者)都逐渐变多,这也和大众对这个群体的接受度逐渐变高了有关。这个群体相对的要辛苦一些,喜欢某些东西,喜欢到了不满足于看,而想自己亲自去尝试。

Coser需要经过服装,道具,化妆等工序打磨,而一般在网上直接买的衣服可能不合适还需要自行修改。这三类人都很容易被外界的人投以另类的目光,但是也比较容易通过活动或者交流找到同好。毕竟这些都是既看脸又走心的形式,既传达自己对原作的感情又能将自己美好的一面示人。

“唱见”

“唱见”(投稿翻唱歌曲的业余歌手)也越来越多,而且很多也唱的非常棒!以动漫的OP,ED投稿较多,目前有很多的应用或网站都提供了方便的音频发布,电台之类的功能,比较容易获得粉丝。

“网配”

“网配”(网络配音)大多是基于网配文或某些小说进行PIA戏,也有一部分人会选择制作广播剧之类的。

其他

其他就是一部分我不常接触或者人数相对较少的群体了。

为什么是二次元群体

在大致了解了当前二次元群体之后,我们来谈谈为什么从二次元群体入手

  1. 群体基数大,覆盖面广。在上面我已经大致说过了,看看自己的周围,随便上微博、贴吧之类的看看,大抵就知道了;
  2. 增长迅速。看看当前A、B站,腾讯动漫等的用户增长量也可窥一斑;
  3. 付费能力强。只要戳中了二次元群体的G点,付费能力非常强,以Coser和lo娘为例,一般都会花上万块在服装上;
  4. 自我传播能力较强。二次元群体大多活跃在手Q,微博,贴吧等平台上,且有自己的圈子,自我传播能力相对更强;
  5. 用户相对忠实。由于二次元文化作为一个独特的亚文化发展起来,当前占据主体的还是90后,00后,碎片化时间相对较多,且更容易在应用/网站中形成关系链;
  6. 二次元作品相对可扩展的面更广,更容易找到增长点。

可能发展的方向

资源平台

二次元群体本就对资源有非常高的渴望和期待,当然也很挑剔,有着很完整的选择要求,用资源吸引到用户,才是解决了用户的需求。

周边产业

二次元群体对于正版周边产物是有着强烈需求的,但是目前无论是市场还是价格上都不是很好。

同人

同人资源对二次元文化的发展是有着相当大影响的,若能吸引到用户,将可能迎来爆发式的增长。

以上都是我一本正经的胡说八道,小心误入歧途 2333

2015小回顾

时间过得很快2015年最后一天。 还是继续给自己做个总结吧。 去年也有做总结,不过没有发布到Blog罢了。

2015年初在公司定了两个主要目标:

  • 对工具的打磨
  • 计算机图形学

第一个目标算是基本完成,现在在用的工具都很顺手,效率能提升很多,而且用起来也很爽!现在也还会再尝试一些其他的工具,或者提升现在使用的工具的便利性。有时间的话可以写篇文章介绍一下。第二个目标目前没有什么进展,可以考虑是不是要留到今年了XD,当然还有一些其他自己定的目标基本完成,大致写写自己的收获好了。

收获

(大致按照时间来写吧)

  • 读了一部分 Nginx 的源码,算是对Nginx有了比以前更深入的认识,但是现在回过头来再重新审视的话,发现对它的理解还远远不够,还需要继续深入。
  • 学习了 Haskell, Haskell我觉得是一门非常棒的语言,非常值得学习,通过对这门语言的学习,改变了我的一些思维方式。除了用它写过一些小程序以外,还没能在工程中大量使用,今后继续加油,尽量引入到工程中~
  • 学习了 Node.js, 同样的还缺乏工程中的实践
  • 学习了 Backbone.js,AngularJSReactJS. Backbone记忆中应该是5月份左右学习的,ReactJS要晚很多。写Backbone的时候想起来了之前用过的Riot, 轻量,随意,但是要写完整的应用相比AngularJS之类的要写很多代码。
  • 开源了qyweixin和一个HTTPmultipart的库,下载量各自也都已经几千了,上次在评论中也看到确实有人在使用~ 还是蛮开心的啦!
  • 学会了使用 Docker,也准备学习一些内部机制之类的东西。
  • 还在翻译中的Tornado 4.3文档也会尽快抽时间翻译完成的.
  • 此外还有学习了Lua和正在深入学习的Shell,还有一系列的框架和库, 正在用来写一些自己的小东西。
  • 1383 Contributions.
  • 运营了公众号 TheMoeLove

还有一些目前还无法放入收获这一项中的东西,在2016年尽量都再多积累一些。

另外,下面这部分也非常的重要!

  • 认识了很多好玩的小伙伴,从他们那里也学到了很多东西
  • 工作中同事们都很好,感谢给予的帮助~

总结

  • 学会拒绝。一些事情是可以容忍的,但是要遵守原则,也要考虑时间成本和效率问题。
  • 学习和思考。这一年之间其实学到的东西也蛮多的,只不过有些东西没能在工程实践中应用,效果不是很明显。多思考,做个靠谱的人,尽量不在相同的地方摔倒两次。
  • 多读书
  • 尝试新东西
  • 多深入一些。浅尝辄止不是应该有的状态。
  • 合理安排追番时间 2333