JavaScript 不包含传统的类继承模型,而是使用 prototypal 原型模型。虽然这经常被当作是 JavaScript 的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大。实现传统的类继承模型是很简单,但是实现 JavaScript 中的原型继承则要困难的多。

由于 JavaScript 是唯一一个被广泛使用的基于原型继承的语言,所以理解两种继承模式的差异是需要一定时间的,今天我们就来了解一下原型和原型链。

原型

我刚学习JavaScript的时候,一般都是用如下方式来写代码:

1
2
3
4
5
6
7
8
9
10
11
12
var decimalDigits = 2,
tax = 5;

function add(x, y) {
return x + y;
}

function subtract(x, y) {
return x - y;
}

//alert(add(1, 3));

通过执行各个function来得到结果,学习了原型之后,我们可以使用如下方式来美化一下代码。

原型使用方式1

在使用原型之前,我们需要先将代码做一下小修改:

1
2
3
4
var Calculator = function (decimalDigits, tax) {
this.decimalDigits = decimalDigits;
this.tax = tax;
};

然后,通过给Calculator对象的prototype属性赋值对象字面量来设定Calculator对象的原型。

1
2
3
4
5
6
7
8
9
10
Calculator.prototype = {
add: function (x, y) {
return x + y;
},

subtract: function (x, y) {
return x - y;
}
};
//alert((new Calculator()).add(1, 3));

这样,我们就可以new Calculator对象以后,就可以调用add方法来计算结果了。

原型使用方式2

第二种方式是,在赋值原型prototype的时候使用function立即执行的表达式来赋值,即如下格式:

1
Calculator.prototype = function () { } ();

它的好处在前面的帖子里已经知道了,就是可以封装私有的function,通过return的形式暴露出简单的使用名称,以达到public/private的效果,修改后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Calculator.prototype = function () {
add = function (x, y) {
return x + y;
},

subtract = function (x, y) {
return x - y;
}
return {
add: add,
subtract: subtract
}
} ();

//alert((new Calculator()).add(11, 3));

同样的方式,我们可以new Calculator对象以后调用add方法来计算结果了。

分步声明

上述使用原型的时候,有一个限制就是一次性设置了原型对象,我们再来说一下如何分来设置原型的每个属性吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
var BaseCalculator = function () {
//为每个实例都声明一个小数位数
this.decimalDigits = 2;
};

//使用原型给BaseCalculator扩展2个对象方法
BaseCalculator.prototype.add = function (x, y) {
return x + y;
};

BaseCalculator.prototype.subtract = function (x, y) {
return x - y;
};

首先,声明了一个BaseCalculator对象,构造函数里会初始化一个小数位数的属性decimalDigits,然后通过原型属性设置2个function,分别是add(x,y)和subtract(x,y),当然你也可以使用前面提到的2种方式的任何一种,我们的主要目的是看如何将BaseCalculator对象设置到真正的Calculator的原型上。

1
2
3
4
5
6
7
8
9
10
11
12
var BaseCalculator = function() {
this.decimalDigits = 2;
};

BaseCalculator.prototype = {
add: function(x, y) {
return x + y;
},
subtract: function(x, y) {
return x - y;
}
};

创建完上述代码以后,我们来开始:

1
2
3
4
5
6
var Calculator = function () {
//为每个实例都声明一个税收数字
this.tax = 5;
};

Calculator.prototype = new BaseCalculator();

我们可以看到Calculator的原型是指向到BaseCalculator的一个实例上,目的是让Calculator集成它的add(x,y)和subtract(x,y)这2个function,还有一点要说的是,由于它的原型是BaseCalculator的一个实例,所以不管你创建多少个Calculator对象实例,他们的原型指向的都是同一个实例。

1
2
3
4
var calc = new Calculator();
alert(calc.add(1, 1));
//BaseCalculator 里声明的decimalDigits属性,在 Calculator里是可以访问到的
alert(calc.decimalDigits);

上面的代码,运行以后,我们可以看到因为Calculator的原型是指向BaseCalculator的实例上的,所以可以访问他的decimalDigits属性值,那如果我不想让Calculator访问BaseCalculator的构造函数里声明的属性值,那怎么办呢?这么办:

1
2
3
4
5
var Calculator = function () {
this.tax= 5;
};

Calculator.prototype = BaseCalculator.prototype;

通过将BaseCalculator的原型赋给Calculator的原型,这样你在Calculator的实例上就访问不到那个decimalDigits值了,如果你访问如下代码,那将会提升出错。

1
2
3
var calc = new Calculator();
alert(calc.add(1, 1));
alert(calc.decimalDigits);

重写原型

在使用第三方JS类库的时候,往往有时候他们定义的原型方法是不能满足我们的需要,但是又离不开这个类库,所以这时候我们就需要重写他们的原型中的一个或者多个属性或function,我们可以通过继续声明的同样的add代码的形式来达到覆盖重写前面的add功能,代码如下:

1
2
3
4
5
6
7
//覆盖前面Calculator的add() function 
Calculator.prototype.add = function (x, y) {
return x + y + this.tax;
};

var calc = new Calculator();
alert(calc.add(1, 1));

这样,我们计算得出的结果就比原来多出了一个tax的值,但是有一点需要注意:那就是重写的代码需要放在最后,这样才能覆盖前面的代码。

原型链

在将原型链之前,我们先上一段代码:

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
function Foo() {
this.value = 42;
}
Foo.prototype = {
method: function() {}
};

function Bar() {}

// 设置Bar的prototype属性为Foo的实例对象
Bar.prototype = new Foo();
Bar.prototype.foo = 'Hello World';

// 修正Bar.prototype.constructor为Bar本身
Bar.prototype.constructor = Bar;

var test = new Bar() // 创建Bar的一个新实例

// 原型链
test [Bar的实例]
Bar.prototype [Foo的实例]
{ foo: 'Hello World' }
Foo.prototype
{method: ...};
Object.prototype
{toString: ... /* etc. */};

上面的例子中,test 对象从 Bar.prototype 和 Foo.prototype 继承下来;因此,它能访问 Foo 的原型方法 method。同时,它也能够访问那个定义在原型上的 Foo 实例属性 value。需要注意的是 new Bar() 不会创造出一个新的 Foo 实例,而是重复使用它原型上的那个实例;因此,所有的 Bar 实例都会共享相同的 value 属性。

属性查找

当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止,到查找到达原型链的顶部 - 也就是 Object.prototype - 但是仍然没有找到指定的属性,就会返回 undefined,我们来看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function foo() {
this.add = function (x, y) {
return x + y;
}
}

foo.prototype.add = function (x, y) {
return x + y + 10;
}

Object.prototype.subtract = function (x, y) {
return x - y;
}

var f = new foo();
alert(f.add(1, 2)); //结果是3,而不是13
alert(f.subtract(1, 2)); //结果是-1

通过代码运行,我们发现subtract是安装我们所说的向上查找来得到结果的,但是add方式有点小不同,这也是我想强调的,就是属性在查找的时候是先查找自身的属性,如果没有再查找原型,再没有,再往上走,一直插到Object的原型上,所以在某种层面上说,用 for in语句遍历属性的时候,效率也是个问题。

还有一点我们需要注意的是,我们可以赋值任何类型的对象到原型上,但是不能赋值原子类型的值,比如如下代码是无效的:

1
2
function Foo() {}
Foo.prototype = 1; // 无效

hasOwnProperty函数

hasOwnProperty是Object.prototype的一个方法,它可是个好东西,他能判断一个对象是否包含自定义属性而不是原型链上的属性,因为hasOwnProperty 是 JavaScript 中唯一一个处理属性但是不查找原型链的函数。

1
2
3
4
5
6
7
8
9
// 修改Object.prototype
Object.prototype.bar = 1;
var foo = {goo: undefined};

foo.bar; // 1
'bar' in foo; // true

foo.hasOwnProperty('bar'); // false
foo.hasOwnProperty('goo'); // true

只有 hasOwnProperty 可以给出正确和期望的结果,这在遍历对象的属性时会很有用。 没有其它方法可以用来排除原型链上的属性,而不是定义在对象自身上的属性。

但有个恶心的地方是:JavaScript 不会保护 hasOwnProperty 被非法占用,因此如果一个对象碰巧存在这个属性,就需要使用外部的 hasOwnProperty 函数来获取正确的结果。

1
2
3
4
5
6
7
8
9
10
11
var foo = {
hasOwnProperty: function() {
return false;
},
bar: 'Here be dragons'
};

foo.hasOwnProperty('bar'); // 总是返回 false

// 使用{}对象的 hasOwnProperty,并将其上下为设置为foo
{}.hasOwnProperty.call(foo, 'bar'); // true

当检查对象上某个属性是否存在时,hasOwnProperty 是唯一可用的方法。同时在使用 for in loop 遍历对象时,推荐总是使用 hasOwnProperty 方法,这将会避免原型对象扩展带来的干扰,我们来看一下例子:

1
2
3
4
5
6
7
// 修改 Object.prototype
Object.prototype.bar = 1;

var foo = {moo: 2};
for(var i in foo) {
console.log(i); // 输出两个属性:bar 和 moo
}

我们没办法改变for in语句的行为,所以想过滤结果就只能使用hasOwnProperty 方法,代码如下:

1
2
3
4
5
6
// foo 变量是上例中的
for(var i in foo) {
if (foo.hasOwnProperty(i)) {
console.log(i);
}
}

这个版本的代码是唯一正确的写法。由于我们使用了 hasOwnProperty,所以这次只输出 moo。如果不使用 hasOwnProperty,则这段代码在原生对象原型(比如 Object.prototype)被扩展时可能会出错。

总结:推荐使用 hasOwnProperty,不要对代码运行的环境做任何假设,不要假设原生对象是否已经被扩展了。

总结

原型极大地丰富了我们的开发代码,但是在平时使用的过程中一定要注意上述提到的一些注意事项。

原文链接:JavaScript探秘:强大的原型和原型链

作用域(scope)是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理。

作用域

所有编程语言都有作用域的概念。简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在程序设计语言中一般分几种作用域:全局作用域、局部作用域和块作用域。在JavaScript中没有块作用域。

全局作用域(Global Scope)

在程序中的任何位置都能够访问到的对象具有全局作用域。

  1. 一般在最外层函数外定义的变量具有全局作用域:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var a="a";
    function doSomething(){
    var b="b";
    function innerSay(){
    alert(b);
    }
    innerSay();
    }
    alert(a); // a
    alert(b); // undefinde
    doSomething(); //b
    innerSay() //脚本错误
  2. 在JavaScript中未声明直接赋值的变量具有全局作用域:

    1
    2
    3
    4
    5
    6
    7
    8
    function doSomething(){
    var a="a";
    b= ="b";
    alert(a);
    }
    doSomething(); // a
    alert(b); // b
    alert(a); // 脚本错误
  3. 在JavaScript中,window对象的属性也拥有全局作用域。

局部作用域

具有局部作用域的对象一般只能在一段代码内访问到,在JavaScript中通常是函数内部。如上面的例子,在函数内部定义的变量不能在函数外面访问到。

作用域链(Scope Chain)

在JavaScript中,函数也是对象,实际上,JavaScript里一切都是对象。函数对象和其它对象一样,拥有可以通过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性。其中一个内部属性是[[Scope]],由ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。

JavaScript的作用域和C语言的作用域有些不同。在JavaScript中,函数的作用域在函数定义的时候就已经确定了,和函数调用的位置没有关系。

JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里

任何执行上下文时刻的作用域, 都是由作用域链(scope chain)来实现.在一个函数被定义的时候, 会将它定义时刻的scope chain链接到这个函数对象的[[scope]]属性.在一个函数对象被调用的时候,会创建一个活动对象(也就是一个对象), 然后对于每一个函数的形参,都命名为该活动对象的命名属性, 然后将这个活动对象做为此时的作用域链(scope chain)最前端, 并将这个函数对象的[[scope]]加入到scope chain中.

在函数执行过程中,没遇到一个变量,都会经历一次标识符解析过程以决定从哪里获取和存储数据。该过程从作用域链头部,也就是从活动对象开始搜索,查找同名的标识符,如果找到了就使用这个标识符对应的变量,如果没找到继续搜索作用域链中的下一个对象,如果搜索完所有对象都未找到,则认为该标识符未定义。函数执行过程中,每个标识符都要经历这样的搜索过程。

作用域链和代码优化

从作用域链的结构可以看出,在运行期上下文的作用域链中,标识符所在的位置越深,读写速度就会越慢。如上图所示,因为全局变量总是存在于运行期上下文作用域链的最末端,因此在标识符解析的时候,查找全局变量是最慢的。所以,在编写代码的时候应尽量少使用全局变量,尽可能使用局部变量。一个好的经验法则是:如果一个跨作用域的对象被引用了一次以上,则先把它存储到局部变量里再使用。例如下面的代码:

1
2
3
4
5
function changeColor(){
document.getElementById("btnChange").onclick=function(){
document.getElementById("targetCanvas").style.backgroundColor="red";
};
}

这个函数引用了两次全局变量document,查找该变量必须遍历整个作用域链,直到最后在全局对象中才能找到。这段代码可以重写如下:

1
2
3
4
5
6
function changeColor(){
var doc=document;
doc.getElementById("btnChange").onclick=function(){
doc.getElementById("targetCanvas").style.backgroundColor="red";
};
}

这段代码比较简单,重写后不会显示出巨大的性能提升,但是如果程序中有大量的全局变量被从反复访问,那么重写后的代码性能会有显著改善

改变作用域链

函数每次执行时对应的运行期上下文都是独一无二的,所以多次调用同一个函数就会导致创建多个运行期上下文,当函数执行完毕,执行上下文会被销毁。每一个运行期上下文都和一个作用域链关联。一般情况下,在运行期上下文运行的过程中,其作用域链只会被 with 语句和 catch 语句影响。

with会降低代码执行效率。

另外一个会改变作用域链的是try-catch语句中的catch语句。当try代码块中发生错误时,执行过程会跳转到catch语句,然后把异常对象推入一个可变对象并置于作用域的头部。在catch代码块内部,函数的所有局部变量将会被放在第二个作用域链对象中。

请注意,一旦catch语句执行完毕,作用域链机会返回到之前的状态。try-catch语句在代码调试和异常处理中非常有用,因此不建议完全避免。你可以通过优化代码来减少catch语句对性能的影响。一个很好的模式是将错误委托给一个函数处理。

Javascript的预编译

JavaScript是一种脚本语言,JavaScript执行过程是一种翻译执行的过程,那么JavaScript的执行中, 有没有类似编译的过程呢?其实JavaScript是有预编译过程的。看下面的例子:

1
2
3
4
alert(typeof eve); //function
function eve() {
alert('I am Laruence');
};

在eve声明前调用它是有意义的。JavaScript在执行每一段JavaScript代码之前, 都会首先处理var关键字和function定义式(函数定义式和函数表达式)。在调用函数执行之前, 会首先创建一个活动对象, 然后搜寻这个函数中的局部变量定义,和函数定义, 将变量名和函数名都做为这个活动对象的同名属性; 对于局部变量定义,变量的值会在真正执行的时候才计算, 此时只是简单的赋为undefined。

对于函数的定义需要注意

1
2
3
4
5
6
7
8
alert(typeof eve); //结果:function
alert(typeof walle); //结果:undefined
function eve() { //函数定义式
alert('I am Laruence');
};
var walle = function() { //函数表达式
}
alert(typeof walle); //结果:function

函数定义式和函数表达式是不同, 对于函数定义式, 会将函数定义提前,而函数表达式, 会在执行过程中才计算

JavaScript在执行每一段代码JavaScript时会进行预编译,看下面的例子:

1
2
3
4
5
6
7
8
<script>
alert(typeof eve); //结果:undefined
</script>
<script>
function eve() {
alert('I am Laruence');
}
</script>

一个问题:

1
2
3
4
5
6
7
8
9
var name = 'laruence';
function echo() {
alert(name);
var name = 'eve';
alert(name);
alert(age);
}

echo();

三次alert()输出的内容分别是什么?

很多人可能马上给出的答案是这样的:

1
2
3
laruence
eve
[脚本出错]

但其实, 运行结果应该是:

1
2
3
undefined
eve
[脚本出错]

因为会以为在echo中, 第一次alert的时候, 会取到全局变量name的值, 而第二次值被局部变量name覆盖, 所以第二次alert是’eve’. 而age属性没有定义, 所以脚本会出错。

这篇博客记录了我搭建我的个人博客的全过程。网络上有很多关于搭建个人博客的文章,但很多文章都不全,这里完整的记录了我搭建这儿静态博客的过程,希望可以帮助有相同需求的人。

前言

为什么要写博客,因为我想就下自己在学习和生活中的点点滴滴。以前学习很多东西,看一两遍就记住了。但是最近发现自己的记性没有以前好了,所以就想到了建个博客。一来可以把自己学习经验和生活感触写下来,二来是可以和大家分享自己的那么一点点有用的东西。

为什么要开博客?可以看看cnFeat的这篇《为什么你要写博客?》

或者也可看看这篇文章《我的博客时代》

有人会问为什么要搭建一个属于自己的独立博客,网上有很多博客网站可以选择。答案很简单,一个独立的博客是真正属于自己的。

为什么选择hexo和Github Pages

很多人用wordpress,你为什么要用github pages来搭建?

  1. github pages有300M免费空间,资料自己管理,保存可靠;
  2. 学着用github,享受github的便利,上面有很多大牛,眼界会开阔很多;
  3. 顺便看看github工作原理,最好的团队协作流程;
  4. github是趋势;
  5. 你不觉得一个文科生用github很geek吗?瞬间跻身技术界;
  6. 就算github被墙了,我可以搬到国内的gitcafe中去。

hexo可以帮助生成静态页面。

GitHub Pages是什么?

GitHub Pages本用于介绍托管在GitHub上的项目, 不过,由于他的空间免费稳定,用来做搭建一个博客再好不过了。

github Pages可以被认为是用户编写的、托管在github上的静态网页。

购买域名

只推荐上godaddy购买,安全,而且可以使用支付宝。

教程(截止至2014年5月10日)如下

1、查你想要的域名;

2、查到适合的域名之后选择「continue to Cart」;

3、godaddy附加收费服务,不要管,继续「continue to Cart」;

4、确认购买。修改购买年限,默认是两年,可以修改成1/2/3/5/10年,随自己喜欢。现在godaddy上com每年的默认费用是12.99美元,附加上ICANN的管理费用就是13.17美元。

如果你不是土豪,可以上网搜godaddy的优惠码,一大堆,找一个填进这里,填完之后,一年的费用会变成8.99美元。

说明一下:一般来讲,使用网上的优惠码第一年收费8.99美元,以后每年的收费是10.99美元,不过在网上可以搜到合适的优惠码,可以每年的收费都是8.99美元,记得多测试自行鉴别。

如图,我买了五年的费用就是45.85美元,随后点击「Proceed to Checkout」

5、结算。登录或注册界面,填完必要的信息之后,选择用支付宝结算。

如果以上的教程如果不够清晰,可以参照这一份《2013年10月新版godaddy域名注册图文教程》

6、检查。结算后,重新登录,去「My Account」,域名已经显示在你的账户了。

7、补充一些注意事项:

  • 输入优惠码没有优惠或者优惠幅度较低,请清除浏览器cookies再尝试;
  • 如果没有支付宝支付选项,有可能是使用的优惠码不支持支付宝,请重新清除浏览器cookies再尝试;
  • 注册时用户填写信息时一定要输入正确的邮箱名字,否则修改十分麻烦。
  • 买完域名之后一定要记得去自己的邮箱查看激活邮件,否则域名激活不了。

安装所需软件

安装下来软件:

打开Git

可以通过下面两种方式打开Git:

  • Windows开始菜单的快捷方式it Bash
  • 鼠标右键打开Git Bash

验证Node.js是否安装成功

在Windows下win+R打开运行,输入cmd回车

打开命令行窗口

在命令行窗口输入node回车,出现下面的提示表示安装成功

注册Github

访问:www.github.com

注册你的username和邮箱,邮箱十分重要,GitHub上很多通知都是通过邮箱的。

注册过程比较简单,详细也可以看:
一步步在GitHub上创建博客主页全系列

配置SSH keys

要想让本地的给项目与在github上的项目联系,需要使用SSH keys。

检查SSH keys的设置

首先我们需要检查你电脑上现有的ssh key:

1
$ cd ~/.ssh   检查本机的ssh密钥

如果提示:No such file or directory 说明你是第一次使用git。

生成新的SSH Key:

1
2
3
$ ssh-keygen -t rsa -C "邮件地址@youremail.com"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/your_user_directory/.ssh/id_rsa):<回车就好>

注意1: 此处的邮箱地址,你可以输入自己的邮箱地址;注意2: 此处的「-C」的是大写的「C」

然后系统会要你输入密码:

1
2
Enter passphrase (empty for no passphrase):<输入加密串>
Enter same passphrase again:<再次输入加密串>

在回车中会提示你输入一个密码,这个密码会在你提交项目时使用,如果为空的话提交项目时则不用输入。这个设置是防止别人往你的项目里提交内容。

注意:输入密码的时候没有*字样的,你直接输入就可以了。

最后看到这样的界面,就成功设置ssh key了:

添加SSH Key到GitHub

在本机设置SSH Key之后,需要添加到GitHub上,以完成SSH链接的设置。

  • 打开本地C:\Documents and Settings\Administrator.ssh\id_rsa.pub文件。此文件里面内容为刚才生成人密钥。如果看不到这个文件,你需要设置显示隐藏文件。准确的复制这个文件的内容,才能保证设置的成功。

  • 登陆github系统。点击右上角的 Account Settings—->SSH Public keys —-> add another public keys

  • 把你本地生成的密钥复制到里面(key文本框中), 点击 add key 就ok了

测试

可以输入下面的命令,看看设置是否成功,git@github.com的部分不要修改:

1
$ ssh -T git@github.com

如果是下面的反馈:

1
2
3
The authenticity of host 'github.com (207.97.227.239)' can't be established.
RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.
Are you sure you want to continue connecting (yes/no)?

不要紧张,输入yes就好,然后会看到:

1
Hi cnfeat! You've successfully authenticated, but GitHub does not provide shell access.

设置用户信息

现在你已经可以通过SSH链接到GitHub了,还有一些个人信息需要完善的。

Git会根据用户的名字和邮箱来记录提交。GitHub也是用这些信息来做权限的处理,输入下面的代码进行个人信息的设置,把名称和邮箱替换成你自己的,名字必须是你的真名,而不是GitHub的昵称。

1
2
$ git config -global user.name "cnfeat"//用户名
$ git config -global user.email "cnfeat@gmail.com"//填写自己的邮箱

SSH Key配置成功

本机已成功连接到github。

若有问题,请重新设置。常见错误请参考:

GitHub Help - Generating SSH Keys

GitHub Help - Error Permission denied (publickey)

使用GitHub Pages建立博客

与GitHub建立好链接之后,就可以方便的使用它提供的Pages服务,GitHub Pages分两种,一种是你的GitHub用户名建立的username.github.io这样的用户&组织页(站),另一种是依附项目的pages。

想建立个人博客是用的第一种,形如suen427.github.io这样的可访问的站,每个用户名下面只能建立一个。

github上建立仓库

登录后系统,在github首页,点击页面右下角「New Repository」

填写项目信息:

project name:cnfeat.github.io

description: Sun’s Blog

注:Github Pages的Repository名字是特定的,比如我Github账号是suen427,那么我Github Pages Repository名字就是suen427.github.io。
点击「Create Repository」完成创建。

详细可以看这里:一步步在GitHub上创建博客主页(2)

用Hexo克隆主题

Hexo介绍

Hexo的作者是tommy351,根据官方介绍,Hexo是一个简单、快速、强大的博客发布工具,支持Markdown格式。

安装Hexo

利用 npm 命令即可安装。(在任意位置点击鼠标右键,选择Git bash)

1
$ npm install -g hexo

部署Hexo

安装完成后,在你喜爱的文件夹下(如H:\hexo),执行以下指令(在H:\hexo内点击鼠标右键,选择Git bash),Hexo 即会自动在目标文件夹建立网站所需要的所有文件。

1
$ hexo init

安装依赖包

1
$ npm install

本地查看

现在我们已经搭建起本地的hexo博客了,执行以下命令(在H:\hexo),然后到浏览器输入localhost:4000看看。

1
2
$ hexo generate
$ hexo server

复制博客的主题

建立了Hexo文件之后复制wuchong的修改的主题,我的就是复制他的修改的。

1
$ git clone https://github.com/wuchong/jacman.git themes/jacman

或者复制yangjian的

1
$ git clone https://github.com/A-limon/pacman.git themes/pacman

启用博客的主题

修改Hexo目录下的_config.yml配置文件中的theme属性,将其设置为jacman

1
theme: jacman

注意:Hexo有两个_config.yml文件,一个在根目录,一个在theme下,此时修改的是在根目录下的。

更新主题

1
2
$ cd themes/jacman
$ git pull

注意:为避免出错,请先备份你的_config.yml文件后再升级。

使用与调试

在本地如果需要预览或者调试你的博客,可以启动本地服务器:

1
hexo serve

部署到Github前需要配置_config.yml文件。

1
2
3
4
deploy:
type: github
repository: git@github.com:suen427/suen427.github.io.git
branch: master

如果你是为一个项目制作网站,那么需要把branch设置为gh-pages。若要绑定自定义域名也可以参考Hexo或Github Page的帮助文档,制作一个cname文件。

之后执行下列指令即可完成部署,注意部署会覆盖掉你之前在版本库中存放的文件。

1
2
3
$ hexo clean
$ hexo generate
$ hexo deploy

将独立域名与GitHub Pages的空间绑定

GitHub Pages的设置

方法一:在Repository的根目录下面,新建一个名为CNAME的文本文件,里面写入你要绑定的域名,比如suen427.com。

方法二:到我的github仓库,点击右下角的「Download ZIP」,下载源文件,解压,找到CNAME文件,用记事本打开,将suen427.com修改成你的域名,放进Hexo\source目录下,用hexo命令提交上去。

DNS设置

用DNSpod,快,免费,稳定。

注册DNSpod,添加域名,如下图设置。

其中A的两条记录指向的ip地址是github Pages的提供的ip

  • 192.30.252.153
  • 192.30.252.154

如博客不能登录,有可能是github更改了空间服务的ip地址,记得及时到在GitHub Pages查看最新的ip即可

www指定的记录是你在github注册的仓库。

去Godaddy修改DNS地址

更改godaddy的Nameservers为DNSpod的NameServers。

1、点击「My Account」,管理我的域名。

2、点击域名。

3、将godaddy的Nameservers更改成f1g1ns1.dnspod.net和f1g1ns2.dnspod.net

如有不详看可以看DNSpod提供的官方帮助

详细也可以看这里:一步步在GitHub上创建博客主页(3)

搭建完成

至此,独立博客就算搭建完成,如需进步一完善请在参看以下文章或博客下留言。

Pacman主题介绍 by yangjian

使用hexo搭建博客 by yangjian

hexo系列教程:(二)搭建hexo博客 by zippera(推荐)

hexo系列教程:(三)hexo博客的配置、使用 by zippera(推荐)

进阶篇:Hexo设置

网站搭建完成后,就可以根据自己爱好来对Hexo生成的网站进行设置了,对整站的设置,只要修改项目目录的_config.yml就可以了,这是我的设置,可供参考。

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# Hexo Configuration
## Docs: http://hexo.io/docs/configuration.html
## Source: https://github.com/hexojs/hexo/

# Site #整站的基本信息
title: Sun's Blog #网站标题
subtitle: 记录学习成长的点点滴滴 #网站副标题
description: 个人博客 学习分享 笔记 随笔 #网站描述
author: Sun #网站作者
email: suen427@gmail.com #联系邮箱
language: zh-CN #网站语言
timezone: Asia/Shanghai #时区

# URL
## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/'
url: http://suen427.github.io #你的域名
root: /
permalink: :year/:month/:day/:title/
permalink_defaults:

# Directory
source_dir: source
public_dir: public
tag_dir: tags
archive_dir: archives
category_dir: categories
code_dir: downloads/code
i18n_dir: :lang
skip_render:

# Writing
new_post_name: :title.md # File name of new posts
default_layout: post
titlecase: false # Transform title into titlecase
external_link: true # Open external links in new tab
filename_case: 0
render_drafts: false
post_asset_folder: false
relative_link: false
future: true
highlight:
enable: true
line_number: true
auto_detect: true
tab_replace:

# Category & Tag
default_category: uncategorized
category_map:
tag_map:

# Archives
## 2: Enable pagination
## 1: Disable pagination
## 0: Fully Disable
archive: 2
category: 2
tag: 2

# Date / Time format
## Hexo uses Moment.js to parse and display date
## You can customize the date format as defined in
## http://momentjs.com/docs/#/displaying/format/
date_format: YYYY-MM-DD
time_format: HH:mm:ss

# Pagination
## Set per_page to 0 to disable pagination
per_page: 10
pagination_dir: page

# Extensions
## Plugins: https://github.com/tommy351/hexo/wiki/Plugins
## Themes: https://github.com/tommy351/hexo/wiki/Themes
theme: jacman
plugins:
- hexo-generator-feed
- hexo-generator-sitemap

#Feed Atom
feed:
type: atom
path: atom.xml
limit: 20

# Markdown
## https://github.com/chjj/marked
markdown:
gfm: true
pedantic: false
sanitize: false
tables: true
breaks: true
smartLists: true
smartypants: true

# Stylus
stylus:
compress: false

# Deployment
## Docs: http://hexo.io/docs/deployment.html
deploy:
type: git
repository: git@github.com:suen427/suen427.github.io.git

修改局部页面

页面展现的全部逻辑都在每个主题中控制,源代码在hexo\themes\jacman\中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.
├── languages #多语言
| ├── default.yml#默认语言
| └── zh-CN.yml #中文语言
├── layout #布局,根目录下的*.ejs文件是对主页,分页,存档等的控制
| ├── _partial #局部的布局,此目录下的*.ejs是对头尾等局部的控制
| └── _widget#小挂件的布局,页面下方小挂件的控制
├── source #源码
| ├── css#css源码
| | ├── _base #*.styl基础css
| | ├── _partial #*.styl局部css
| | ├── fonts #字体
| | ├── images #图片
| | └── style.styl #*.styl引入需要的css源码
| ├── fancybox #fancybox效果源码
| └── js #javascript源代码
├── _config.yml#主题配置文件
└── README.md #用GitHub的都知道

Hexo命令

常用命令:

1
2
3
4
5
hexo new "postName" #新建文章
hexo new page "pageName" #新建页面
hexo generate #生成静态页面至public目录
hexo server #开启预览访问端口(默认端口4000,'ctrl + c'关闭server)
hexo deploy #将.deploy目录部署到GitHub

简写:

1
2
3
4
hexo n == hexo new
hexo g == hexo generate
hexo s == hexo server
hexo d == hexo deploy

常用复合命令:

1
2
hexo d -g #生成加部署
hexo s -g #预览加部署

发表新文章

用hexo发表新文章

1
$ hexo n #写文章

其中my new post为文章标题,执行命令后,会在项目\source_posts中生成my new post.md文件,用编辑器打开编写即可。

当然,也可以直接在\source_posts中新建一个md文件。

写完后,推送到服务器上,执行

1
2
$ hexo g #生成
$ hexo d #部署 # 可与hexo g合并为 hexo d -g

用Hexo发表文章的Markdown语法

使用jacman或pacman主题,建议按此标准语法写:

1
2
3
4
5
6
7
8
title: postName #文章页面上的显示名称,可以任意修改,不会出现在URL中
date: 2013-12-02 15:30:16 #文章生成时间,一般不改,当然也可以任意修改
categories: example #分类
tags: [tag1,tag2,tag3] #文章标签,可空,多标签请用格式,注意:后面有个空格
description: 附加一段文章摘要,字数最好在140字以内。
---

以下正文

安装插件

添加sitemap和feed插件

1
2
$ npm install hexo-generator-sitemap
$ npm install hexo-generator-feed

修改_config.yml,增加以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Extensions
Plugins:
- hexo-generator-feed
- hexo-generator-sitemap

#Feed Atom
feed:
type: atom
path: atom.xml
limit: 20

#sitemap
sitemap:
path: sitemap.xml

Hexo上传README文件

Github的版本库通常建议同时附上README.md说明文件,但是hexo默认情况下会把所有md文件解析成html文件,所以即使你在线生成了README.md,它也会在你下一次部署时被删去。怎么解决呢?

在执行hexo deploy前把在本地写好的README.md文件复制到public文件夹中,再去执行hexo deploy

404页面

GitHub Pages有提供制作404页面的指引:Custom 404 Pages

直接在根目录下创建自己的404.html或者404.md就可以。但是自定义404页面仅对绑定顶级域名的项目才起作用,GitHub默认分配的二级域名是不起作用的,使用hexo server在本机调试也是不起作用的。

图床

推荐使用七牛(10G空间,免费)。

RSS插件

hexo提供了RSS的生成插件,需要手动安装和设置。使用插件 hexo-generator-feed 能生成 Atom 1.0 或者 RSS 2.0 feed. 安装很简单

1
$ npm install hexo-generator-feed -save

然后在 Hexo 根目录下的 _config.yml 里配置一下

1
2
3
4
feed:
type: atom
path: atom.xml
limit: 20

  • type 表示类型, 是 atom 还是 rss2.
  • path 表示 Feed 路径
  • limit 最多多少篇最近文章

sitemap

同样的,我们使用插件 hexo-generator-sitemap 能生成站点地图, 方法如下

1
$ npm install hexo-generator-sitemap -save

然后在 Hexo 根目录下的 _config.yml 里配置一下

1
2
sitemap:
path: sitemap.xml

  • path 表示 Sitemap 的路径. 默认为 sitemap.xml.
    对于国内用户还需要安装插件 hexo-generator-baidu-sitemap, 顾名思义是为百度量身打造的. 安装
    1
    $ npm install hexo-generator-baidu-sitemap -save

然后在 Hexo 根目录下的 _config.yml 里配置一下

1
2
baidusitemap:
path: baidusitemap.xml

参考资料:

[1]如何搭建一个独立博客——简明Github Pages与Hexo教程
[2]Hexo 优化与定制(二)
[3]hexo系列教程:(二)搭建hexo博客

感谢

cnFeat

wuchong

yangjian