Screeps新手游戏指南

一款玩着玩着就变成JavaScript大神的游戏。

一款可以增加你赚钱能力的游戏。

在写下这段文字的时候,我已经删除了这个游戏里所有的数据,因为这个游戏消耗了我太多时间,本着纪念这段游戏经历的目地,我写了这篇文章。

什么是Screeps?

The world's first MMO strategy open world game for programmers.

http://screeps.com

官方这样定义这款游戏:第一款针对开发者的大型多人在线开放策略游戏。而对于这款游戏,很多报道里会提到:”最难“,”战术沙盒“等概念。

是的没错,screeps就是这样一款游戏,一款需要编写代码才能玩的在线策略游戏。玩家需要在游戏中编写javascript代码来控制所有的游戏行为。比如:

查找并攻击敌人

1
2
3
4
enemy = creep.pos.findClosestByRange(FIND_HOSTILE_CREEPS); // 找到距离最近的敌人
if (creep.attack(enemy) == ERR_NOT_IN_RANGE) { // 攻击敌人
creep.moveTo(enemy); // 如果距离不够则向敌人移动
}

找到能量矿并采集

1
2
3
4
sources = creep.room.find(FIND_SOURCES); // 找到房间里所有的能量矿
if (creep.harvest(sources[0]) == ERR_NOT_IN_RANGE) { // 采集能量
creep.moveTo(sources[0]); // 如果距离不够则向矿移动
}

你的代码会保存在游戏中,并且一直运行下去。游戏甚至给你提供了缓存,有代码提示的在线代码编辑器,控制台和完善的API文档。

在游戏里都能做什么?

  • 发展基地
  • 生产工人和士兵
  • 采集资源
  • 攻击其他玩家
  • 建造防御设施
  • 和盟友互相支援

目前有哪些游戏元素

  • 两种资源:energy和power
  • 建筑:spawn,power spawn, extension,storage,road,wall,rampart,tower,observer,link
  • 四矿地图和Source Keeper
  • control rank 和 power rank
  • 远程兵种,近战兵种,治疗兵种,混合兵种
  • controller 房间等级,越高能造的建筑越多,上限8
  • GCL 游戏等级,每一个等级可以多控制一个房间

更多内容可以查看官方文档 http://support.screeps.com/

Screeps收费嘛?

游戏中程序执行会消耗游戏中的CPU,每个月你可以免费获得600个CPU,如果优化的好,600个CPU可以很轻松的控制20个游戏单位,但是如果你想发展多基地或者进入排行榜,还是建议你购买CPU进行游戏,CPU的使用上限是每天300个,也就是说,这款游戏每天可能花掉你人民币0-6块钱。

新人必须知道的

  • 你的基地叫做Spawn,你的工人和士兵叫做Creep
  • 你的代码每执行一次是一个tick,一个tick是2秒
  • 绿色的地方是泥地,如果不修路走过去比乌龟还慢,不是泥地也需要修路
  • Creep有1500ticks的生命,可以使用spawn给Creep延长生命,但是并不建议这么做
  • 路每隔一段时间就会坏,需要经常修
  • energy矿每300ticks刷新3000个,计算好你的WORK数量,5-6个就够了
  • 一定要有防御机制,任何人都可能攻击你,并且可以在几分钟内摧毁你的所有建筑,杀死你的所有Creep,而且并不需要有理由

游戏策略

如何提高工人效率?

和现实中一样,明确的分工可以有效的提高工作效率,矿工专门采矿,运输工种专门运输,升级工人只负责升级,但由于CPU有限,你需要在效率和工人数量上进行权衡。

如何节省CPU?

你可以像调试其他项目一样调试你的程序效率,找到那些CPU消耗很高的代码并且优化它们,官方文档上对每个方法的CPU消耗情况有详细的说明,对于新手,CPU最大的消耗在于查找目标和跨房间移动,解决这两个问题可以节省超过一半的CPU。

应该选择什么样的房间进行游戏?

遵循一个原则:附近资源丰富并且容易防守

移动速度是如何计算的?

请参考 http://codepen.io/findoff/full/RPmqOd/

如何高效的修路

  • 合理修路可以节约1/4的creep花费,你只要有一半的MOVE就可以达到最大移动速度。
  • 只要有creep经过的地方都应该修路,有一个creep走的地方修一条,有多个creep走的地方修两条,如果非常多,就修很多条,但是不要到处修。

建筑的摆放有什么要注意的

  • storage:让从所有搬运工的走动距离最短,通常选择房间的正中央,让其他使用能量的建筑环绕在周围。
  • extension:离storage要近,游戏中有各种个样的高效摆放方式。
  • spawn:让所有的单位出去工作的距离最短,通常在中心附近。
  • tower:分散在房间的各个角落,并且保证出口附近所有的位置都在10个距离以内,这样能做到最高效的升级围墙,防御敌人,修复建筑,但是要记住tower的效率没有工人高。
  • wall和rampart:建议建在最外围,这样可以让最少的敌人进入房间,易于防守和维修,一个外围的围墙最多能被3个近距离单位+4个远距离单位攻击,而一个内部的围墙可以被至少3个近距离单位+十几个远距离单位围攻。

Link的使用

  • 用来代替搬运工运输资源
  • 用来当升级工人的扩展存储
  • 用来当小型的storage
  • 将link建造在外围rampart上可以实现在有围墙的情况下和其他玩家交换资源

你可能不知道的

  • 在有observer之前,你只能看到有你的单位的房间,我指的是代码里
  • 你可以帮助其他玩家建造和维修
  • 你可以帮助其他玩家升级Controller
  • 你可以把资源丢进其他玩家的storage
  • rampart可以叠加在任何建筑上,千万别忘了给spawn、storage上rampart,wall上也可以放一个rampart
  • renew一个creep并不划算,但是你可以在creep路过的时候顺便renew一下
  • 你可以用Grunt提交你的本地代码 http://support.screeps.com/hc/en-us/articles/203022512-Committing-local-scripts-using-Grunt

我的全部游戏代码已经发布在Github上 https://github.com/m4ker/screeps

我在Screeps里的最后一分钟

死磕JavaScript面向对象 - 定义对象

前言

近来发现JavaScript能做的事情越来越多,用的人也变多了,有太多的框架和模式都搞不清楚原理(早些年的prototype, yui, jQuery, 到后来的MVC,MVVM),所以想深入的学习一下JavaScript。

经过一段时间的摸索,终于知道了ECMAScript的来龙去脉,而我对JavaScript的认知还停留在ES3上面。但对于ES3,也只知道一些简单的用法,看高手写代码竟然完全看不懂。

最近几天卡在了闭包和对象两个章节上,因为和其他语言差的太多,所以看了很多文章仍然还只是一直半解,对于一个函数作为一等公民的面向对象语言,搞不清楚对象的基本知识实在有些说不过去,所以决定对相关内容进行一次深入的整理。

原始方式 1

1
2
3
4
5
6
7
8
9
// 1.js
var robot = new Object;
// var robot = {};
robot.height = '120cm';
robot.weight = '50kg';
robot.color = 'red';
robot.say = function (string) {
console.log(string);
}

用这种方式是不是感觉有点怪?感觉不像一个整体,所以我们要用下面的方法把对象封装起来:

原始方式 2

1
2
3
4
5
6
7
8
9
// 2.js
var robot = {
height : '120cm',
weight : '50kg',
color : 'blue',
say : function (string) {
console.log(string);
}
}

在JavaScript中,我们通常以以上两种方式创建对象,在实际开发过程中我们会遇到一些问题:

  • 问题1: 没有办法识别对象的类型,因为我们创建的所有对象都是Object
  • 问题2: 创建多个对象很麻烦

工厂模式

工厂模式用来解决代码重用的问题(问题2),可以用一个函数多次创建同一个结构的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 3.js
function createRobot(height, weight, color) {
return {
height : height + 'cm',
weight : weight + 'kg',
color : color,
say : function (string) {
console.log(string);
}
}
}

var robot1 = createRobot(100,50,'red');
var robot2 = createRobot(120,60,'blue');
console.log(robot1.height,robot1.weight,robot1.color); // 100cm 50kg red
console.log(robot2.height,robot2.weight,robot2.color); // 120cm 60kg blue

这段代码看上去已经很好的解决了代码重用的问题,但是又产生了新问题:

  • 问题3:多个同类型对象的方法被重复创建

这里指的就是robot1.say和robot2.say方法,本是相同的函数却被声明了两次,体现在代码里就是:

1
console.log(robot1.say === robot2.say); // false

为了解决这个问题,我们要把say方法拿到工厂外面单独进行定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 4.js
function say(string) {
// var say = function(string) {
console.log(string);
}
function createRobot(height, weight, color) {
return {
height : height + 'cm',
weight : weight + 'kg',
color : color,
say : say
}
}

var robot1 = createRobot(100,50,'red');
var robot2 = createRobot(120,60,'blue');
console.log(robot1.say === robot2.say); // true
console.log(robot1 instanceof Object); // true
console.log(robot1 instanceof createRobot); // false

这样我们就解决了问题3,say方法只被声明了一次,所有robot对象的say方法都指向同一个函数。

构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 5.js
function say(string) {
// var say = function(string) {
console.log(string);
}
function Robot(height, weight, color) {
this.height = height + 'cm';
this.weight = weight + 'kg';
this.color = color;
this.say = say;
}
var robot1 = new Robot(100,50,'red');
var robot2 = new Robot(120,60,'blue');
console.log(robot1.height,robot1.weight,robot1.color); // 100cm 50kg red
console.log(robot2.height,robot2.weight,robot2.color); // 120cm 60kg blue
console.log(robot1.say === robot2.say); // true
console.log(robot1 instanceof Object); // true
console.log(robot1 instanceof Robot); // true

构造函数看上去更像其他语言中的面向对象,并且解决了问题1,可以识别对象的类型。

原型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 6.js
function Robot() {
}
Robot.prototype.height = '120cm';
Robot.prototype.weight = '60kg';
Robot.prototype.color = 'blue';
Robot.prototype.parts = ['body'];
Robot.prototype.say = function(string) {
console.log(string);
}

var robot1 = new Robot();
var robot2 = new Robot();
console.log(robot1.height); // 120cm
console.log(robot2.height); // 120cm
console.log(robot1 instanceof Object); // true
console.log(robot1 instanceof Robot); // true
console.log(robot1.say === robot2.say); // true

这里可以看到,使用原型模式使得robot1和robot2公用了同一套属性值和方法。并且可以正确识别对象类型。

接下来我又尝试了对实例的属性进行修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
console.log(robot1.hasOwnProperty('height')); // false
console.log(robot2.hasOwnProperty('height')); // false
robot1.height = '100cm';
console.log(robot1.height); // 100cm
console.log(robot2.height); // 120cm
console.log(robot1.hasOwnProperty('height')); // true
console.log(robot2.hasOwnProperty('height')); // false
robot1.say = function(string) {
console.log('Ah...' + string);
};
robot1.say('hello world'); // Ah...hello world
robot2.say('hello world'); // hello world
delete robot1.say;
robot1.say('hello world'); // hello world
robot2.say('hello world'); // hello world

这段代码说明了,实例上定义的属性/方法会屏蔽掉原型上的同名属性/方法,而在delete掉实例上的方法后,原型上的方法在实例上又恢复可用了。但是我遇到了一个例外:

1
2
3
console.log(robot1.parts, robot2.parts); // [ 'body' ] [ 'body' ]
robot1.parts.push('arm');
console.log(robot1.parts, robot2.parts); // [ 'body', 'arm' ] [ 'body', 'arm' ]

对robot1属性的修改同时影响了robot2, 这说明对于数组的修改是发生在原型上的,这并不是我想要得到的效果。

  • 问题4: 对于原型上数组属性的修改影响到了其他实例

《JavaScript 高级程序设计》对原型链进行了详细的解读,当然上面这个问题就是这本书里讲到的,并且还讲了下面一些内容:

1. 创建构造函数的时候发生了什么?

  1. 在创建任何函数的时候,JavaScript都会创建一个原型对象,并且将函数的prototype属性指向该原型对象
  2. 原型对象会有一个constructor属性,指向该函数
  3. 而原型对象的__proto__属性,指向Object.prototype
  4. Object.prototype.constructor 指向 Object 函数
  5. Object.prototype是原型链最顶层,没有__proto__属性
1
2
3
4
5
6
7
8
9
10
11
// 7.js
function myFunc () {
}

console.log(myFunc.prototype); // {}
console.log(typeof myFunc.prototype); // object , 证明1
console.log(myFunc.prototype.constructor); // [Function: myFunc] ,证明2
console.log(myFunc.prototype.__proto__); // {}
console.log(myFunc.prototype.__proto__ === Object.prototype); // true,证明3
console.log(myFunc.prototype.__proto__.constructor); // [Function: Object],证明4
console.log(myFunc.prototype.__proto__.__proto__); // null,证明5

创建构造函数时的原型链

2. 使用new进行实例化之后发生了什么?

  1. 基于构造函数创建了一个实例对象
  2. 该实例对象的__proto__属性指向构造函数的prototype属性
  3. 该实例对象的constructor指向构造函数 // todo: 这部分要放到图片里吗?
  4. 原型链其他部分未发生变化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var myObj = new myFunc;

console.log(myObj instanceof myFunc); // true
console.log(myObj instanceof Object); // true
console.log(myObj.constructor); // [Function: myFunc]

console.log(myObj.__proto__ == myFunc.prototype); // true

console.log(myFunc.prototype); // {}
console.log(typeof myFunc.prototype); // object
console.log(myFunc.prototype.constructor); // [Function: myFunc]
console.log(myFunc.prototype.__proto__); // {}
console.log(myFunc.prototype.__proto__.constructor); // [Function: Object]
console.log(myFunc.prototype.__proto__ === Object.prototype); // true
console.log(myFunc.prototype.__proto__.__proto__); // null

实例化对象时的原型链

3. 属性/方法调用的内部流程是怎么样的?

  1. 在实例中找
  2. 如果没找到,去__proto__里找
  3. 如果没找到,去__proto__.__proto__里找
  4. 如果__proto__为null,说明已经到达了原型链的最顶级,也就是Object.prototype,属性/方法在原型链上未定义,返回undefined

4. 重新定义原型链的时候遇到的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
// 8.js
function myFunc() {
}

var myObj = new myFunc();

myFunc.prototype = {
say:function(string){
console.log(string);
}
};

myObj.say('hello world'); // TypeError: undefined is not a function

这看上去很难理解,因为重写构造方法的原型并不会删掉之前的原型,也不会改变实例和原型之间的关系,所以我们又发现了一个问题

  • 问题5:重新定义prototype会切断原型链,并且你会发现constructor也不见了。

重新定义原型前的原型链
重新定义原型后的原型链

所以如果想要完美的重写prototype需要这样:

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
// 9.js
function myFunc() {
}

var myObj = new myFunc;

myFunc.prototype = {
constructor : myFunc,
// __proto__ 会自动创建并指向 Object.prototype
say: function(string) {

console.log(string);
}
};

console.log(myObj instanceof myFunc); // false
console.log(myObj instanceof Object); // true

console.log(myFunc.prototype.__proto__ == myObj.__proto__.__proto__); //true

myObj.__proto__ = myFunc.prototype;

console.log(myObj instanceof myFunc); // true
console.log(myObj instanceof Object); // true

console.log(myFunc.prototype.__proto__ == myObj.__proto__.__proto__); //true

myObj.say('hello world');

除了上面这些问题,其实原型模式还有一个问题:

  • 问题6: 原型模式的构造函数没有参数

混合构造函数&原型模式

为了解决问题4和问题6,就出现和混合构造函数&原型模式

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
// 10.js
function Robot(height, weight, color) { // 问题6解决
this.height = height + 'cm';
this.weight = weight + 'kg';
this.color = color;
this.parts = ['body'];
}

Robot.prototype.say = function(string) {
console.log(string);
}

var robot1 = new Robot(100,50,'red');
var robot2 = new Robot(120,60,'blue');
console.log(robot1);
/*
{ height: '100cm',
weight: '50kg',
color: 'red',
parts: [ 'body' ] }
*/

console.log(robot2);
/*
{ height: '120cm',
weight: '60kg',
color: 'blue',
parts: [ 'body' ] }
*/

robot1.parts.push('arm');
console.log(robot1.parts,robot2.parts); // [ 'body', 'arm' ] [ 'body' ], 问题4解决

动态原型模式

为了让代码看上去更美观一点儿,又有了动态原型模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 11.js
function Robot(height, weight, color) {
this.height = height + 'cm';
this.weight = weight + 'kg';
this.color = color;
this.parts = ['body'];

if (Robot.prototype.say === undefined) {
// if (Robot.say === undefined) { // 这样不行, 会重复执行,why?
// if (this.prototype.say === undefined) { // 这样也不行, TypeError: Cannot read property 'say' of undefined,why?
Robot.prototype.say = function(string) {
console.log(string);
}
}
}

var robot1 = new Robot(100,50,'red');
var robot2 = new Robot(120,60,'blue');

console.log(robot1.say === robot2.say);
robot1.say('hello world');
robot2.say('hello world');

混合工厂模式/寄生构造函数模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 12.js
function createRobot(height, weight, color) {
return {
height : height + 'cm',
weight : weight + 'kg',
color : color,
say : function (string) {
console.log(string);
}
}
}

var robot1 = new createRobot(100,50,'red');
var robot2 = new createRobot(120,60,'blue');
console.log(robot1.height,robot1.weight,robot1.color); // 100cm 50kg red
console.log(robot2.height,robot2.weight,robot2.color); // 120cm 60kg blue

W3School和《JavaScript 高级程序设计》中都提到了这样一种模式,而且还都说明了不推荐这样使用。这里让我很想不明白,从代码上看,和工厂模式没有区别,只是实例化的时候使用了new关键字,但是实例化的时候new并没有生效,个人理解这种方式的工作原理和工厂模式是一样的,所以也和工厂模式有着同样的问题。

(未完待续)

经典问题再解读:不用中间变量交换两个变量的值

大概八年前,写过这样一篇文章:不使用中间变量来交换变量的值,后来面试的时候常常遇到这题,最近翻出来看,发现当时对这个问题的理解不够深刻,所以今天又整理了一下。

1. 一些有限制的方法

字符串版本

1
2
3
4
5
6
<?php
$a = "a";
$b = "b";
$a .= $b; // a=ab, b=b
$b = str_replace($b, "", $a); // a=ab, b=a
$a = str_replace($b, "", $a); // a=b, b=a
1
2
3
4
5
6
<?php
$a = "a";
$b = "b";
$a .= $b; // a=ab, b=b
$b = substr($a, 0, (strlen($a) - strlen($b))); // a=ab, b=a
$a = substr($a, strlen($b)); // a=b, b=a

上面这两个方法使用了字符串替换和截取的方法,有一个限制就是只适用于字符串。

加减法

1
2
3
4
5
a = 1;
b = 2;
a = a + b; // a=3, b=2
b = a - b; // a=3, b=1
a = a - b; // a=2, b=1
1
2
3
4
5
a = 1;
b = 2;
a = b - a; // a=1,b=2
b = b - a; // a=1,b=1
a = b + a; // a=2,b=2

乘除法

1
2
3
a = a * b;
b = a / b;
a = a / b;

用除法来解决这个问题,多了一个限制,b不能等于0

一句话版本

1
a = b + 0 * (b = a);
1
a = (b - a) + (b = a);
1
a = (a + b) - (b = a);
1
a = b + (b = a) * 0;

这些方法利用了表达式的返回值

所有的加减乘除的方法里有两方面限制:

  • 只适用于数字
  • 如果变量是浮点数,会有精度上的损失

看到网上有一些人提出适用+和*的时候会导致结果向上溢出,但其实这并不影响结果,因为最后逆操作会产生一次向下溢出。

eval版

1
eval("a="+b+";b="+a);

eval版本可能有两个问题

  • 安全性
  • 是如果想支持更多的数据类型比较麻烦

异或版本

1
2
3
4
5
6
<?php
$a=10; //$a=1010
$b=12; //$b=1100
$a=$a^$b; //$a=0110,$b=1100
$b=$a^$b; //$a=0110,$b=1010
$a=$a^$b; //$a=1100=12,$b=1010

下面是简化版本

1
2
3
4
<?php
$a ^= $b;
$b ^= $a;
$a ^= $b;

异或适用于整数和字符串

2. 适用于所有的数据类型,并且没有限制的方法

对象版

1
2
3
a = {a : b, b : a};
b = a.b;
a = a.a;

数组版

1
2
3
a = [a,b];
b = a[0];
a = a[1];
1
a = [b,b=a][0];

匿名函数版

1
2
3
4
5
6
7
8
a=(function(){
try {
return b;
}
finally {
b = a;
}
})();

PHP 版本

1
list($var1, $var2) = [$var2, $var1];

Python&Ruby版本

1
a,b = b,a; // python和ruby程序员都笑了:D

ES6 版本

1
[a,b] = [b,a];

3. 总结一下

解决这个问题的基本思想有以下几种:

  • 将两个变量同时放入其中一个变量,再分别取出,例如字符串版本,数组版本,对象版本

  • 将两个变量通过某种计算的结果放入其中一个变量,再用计算结果和另一个已知变量逆向取回结果,例如异或版本和一部分加减乘除的版本

  • 利用语言特性,例如eval版本,匿名函数版本,php版本和python&ruby版本

WordPress到hexo搬家小记

WordPress是一个以PHP和MySQL为平台的自由开源的博客软件和内容管理系统。WordPress具有插件架构和模板系统。Alexa排行前100万的网站中有超过16.7%的网站使用WordPress。到了2011年8月,约22%的新网站采用了WordPress。WordPress是目前因特网上最流行的博客系统。

前言

从08年开始玩WordPress,着迷于她的强大和灵活,臣服于她强大的themes机制和plugins机制。当是还有不少博客专门研究wordpress,我也曾经深入的研究过其中的一些原理,还开发了sablog-x转wordpress的工具。

我为什么要放弃WordPress?

时过境迁,不再是曾经意气风发的少年,折腾的少了,内容更新也缓慢,由于WordPress太过庞大,插件质量也参差不齐,导致WordPress出现过很多的漏洞,经常发现内容被篡改,文件被篡改,被垃圾评论骚扰,甚至有些内容被改丢了,而发现的时候可能已经过去几个月了。

我为什么选择hexo?

  • 其实最主要的原因,是最近开始关注Node,而hexo是目前最流行的Node博客系统。而为什么关注Node,说来话长,这可能要从PHP为什么是“最好的语言”开始说起,而我不得不承认JavaScript将成为下一个“最好的语言”。

  • 生成纯静态网站,可以直接发布到Github,几乎不存在性能问题和安全问题,而那些你觉得不可能静态化的内容,用服务就好了,比如:评论、搜索

  • 基于Markdown文件的博客系统,Markdown让写作更美好,曾起何时我也有过这样一个想法,想开发一个基于文本的博客系统,不过最终没有开发完那个系统,后来我用那个思想开发了 http://qltx.cn ,里面有一个从目录文件生成数据的算法。

  • 轻量,有完善的中文文档 https://hexo.io/zh-cn/ ,作者是台湾开发者@tommy351

迁移过程

这里并不想写安装过程,安装步骤可以去官方看文档,简单记录一下数据迁移过程中遇到的一些问题

  • npm 安装速度慢,经常卡住,遇到这种情况可以把vpn调整到全局模式重试

  • 数据转换官方提供了migration插件,通过命令行导入wordpress是非常快的,不过转换完毕后我的hexo就无法访问了,这种情况通常是因为文章中的某些字符无法解析,排除法找到那篇文章处理一下就可以了。

  • hexo中的分类和WordPress中的分类不同,一篇文章只能属于一个分类,所以要调整所有文章的分类内容

  • 官方推荐的很多theme安装后都无法使用,这是版本兼容性的问题

  • 迁移过来之后WordPress中的附件需要处理,我的做法是把整个wp-content目录原样放到了public目录下,这会有一个问题,就是每次generate都会被清理掉,所以更好的办法是放到第三方

  • 评论使用disqus,注册完毕要验证邮箱,创建app,并且要设置安全域名,这样才能成功加载,在disqus中导入WordPress的备份数据可以完成评论的迁移

  • WordPress中的gallery标签在hexo中全部失效,可以用hexo中的photos属性,因为缩略图布局的缘故,所以我改动了theme,增加了一个wpphotos属性,并且用脚本将WordPress中的gallery全部转成了url,caption的格式

  • 后期我对默认theme做了大量的改动,如果想找到更多的themes,可以去官网或者看这篇文章 http://www.zhihu.com/question/24422335

说点儿题外话

在迁移的过程中我又重新整理了我写过的所有文章,一共有500多篇,感慨自己曾经的执着、稚嫩,而那大概400篇文章,将永远沉睡在草稿中。

从我第一次搭建博客到现在已经有10个年头,维护这个网站花掉了我大量的精力,几乎伴随着我整个大学时代和职业生涯,甚至还有高中的一段时间,从一个技术小白到一名web工程师,我在整个过程中学到了非常多的技术知识,也对人生有了很多新的感悟,虽然这里并没有写出特别好的技术类文章。

如果说有什么东西见证过我的青春,那一定就是这里。

公益项目志愿者招募(PHP,UI)

我们是谁?

中国•支教联盟(CNAEF),创办于2006年4月。由自愿支持农村中小学教育的社会各界爱心人士自发组织的全国性民间公益机构。以联系和提供支教为主题,主要发布支教信息,传播贫困地区教育现状,共同关注孩子成长。自成立以来,长期致力于为发达地区爱心咨询寻找资助对象,为欠发达地区教育引入社会各界力量。

我们要做什么?

我们想继续完善一下我们的网站,如果有可能会有自己的博客和手机应用,我们都是业余的志愿者,每个人都不能在这个事情上花上很多时间,所以,我们需要你的加入。

你最好有以下一种或多种技能:

  • 能做一些简单web页面
  • 能写一些PHP代码,最好用过一些框架
  • 能写一些JavaScript脚本
  • 会用git或者想学习如何使用git
  • 能够开发简单的手机app(android/ios不限)

如果你是个大牛当然最好,如果你是个菜鸟,我们会提供全方位的技术指导。需要强调的是:我们不能提供报酬,不过欢迎各位来北京约饭。

我的联系方式

  • QQ 35650697
  • makerwang#gmail.com
  • 也可以加入我们的QQ群:249822315

相关链接

中国支教联盟官方网站:http://www.go9999.com

中国支教联盟代码仓库:https://github.com/CNAEF

关于我的支教经历请移步:https://blog.foolbird.net/4503.html