AngularJS+Gulp开发极速静态博客系统(二)

编写 gulpfile

在这个项目中,我们使用 CoffeeScript 来进行开发,因此需要新建一个 gulpfile.coffee

为了方便起见,再创建一个 gulpfile.js,在其中注册 CoffeeScript 和 gulpfile.coffee,就可以使用 CoffeeScript 编写 gulpfile 了。

1
2
require('coffee-script/register')
require('./gulpfile.coffee')

构思 Gulp 任务

我们首先构思几个博客作者可能需要的任务:

  1. new 创建新文章
  2. publish 将已有文章编译与索引
  3. theme 编译主题包
  4. server 提供一个本地的服务器以便及时编译修改的文章并预览效果

为了开发方便,我们还需要一些任务辅助我们的源代码和主题包的开发:

  1. theme-dev 提供一个本地的服务器以便及时编译修改的源代码和主题包

一些变量

gulpfile.coffee 中,需要引入我们在 package.json 中的 gulp 插件依赖。

并且定义一些变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pkg = require './package.json'
# 配置文件
config = require './config'
# 目录结构
dir =
tmp: './.tmp'
src: './src'
pub: './public'
asset: './public/assets'
postPub: './public/posts'
postSrc: './posts'
theme: "./themes/#{config.theme}"

创建新文章

首先,我们需要一个文章的格式。我目前的博客是使用 HEXO 搭建的,不妨就直接采用它的格式好了。

HEXO 的文章是在 markdown 的开头使用 --- 来对元数据进行分隔,元数据使用 yaml 格式。

如下:

1
2
3
4
5
6
7
8
9
10
11
12
title: AngularJS+Gulp开发急速静态博客系统(二)
tags:
- AngularJS
- Gulp
- Markdown
- Node.JS
- Tini
categories:
- 学习笔记
date: 2014-11-28 16:23:16
---
正文

因此,我们要做的就是使用一个模板文件,通过替换其中的内容,达到创建新文章的目的。

建立一个 empty.md

1
2
3
4
5
6
7
8
9
10
11
title: 新日志-{date}
tags:
- 未添加标签
categories:
- 未分类
date: {date}
---
> 这是一个 Tini 的示例文档,你可以撰写你自己的内容来取代它。
<!--more-->

我们要做的就是替换其中内容,并复制它,以当前时间命名。建立 gulp 任务如下:

1
2
3
4
5
6
gulp.task 'new', ->
now = (new Date).toISOString().replace /(.+)T(.+)\..+/, '$1 $2'
gulp.src "#{dir.src}/empty.md"
.pipe replace /\{date\}/g, now
.pipe rename "#{now.replace /[: ]/g, '-'}.md"
.pipe gulp.dest "#{dir.postSrc}/"

这样,很简单就可以新建一篇文章了。

将已有文章编译与索引

接下来,我们就可以对博客文章进行编译和索引。

思路如下:

  1. 收集所有的文章的元数据
  2. 进行一些替换操作,完成一些 markdown 不包含的操作
  3. 使用 gulp-marked 编译成 HTML
  4. 继续完成一些我们需要的替换操作
  5. 压缩
  6. 输出
  7. 记录路径
  8. 保存索引文件

配置任务如下:

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
# 文章缓存
posts = []
gulp.task 'posts', ->
posts = []
i = 0
gulp.src "#{dir.postSrc}/**/*.{md,markdown}"
# 读取元数据
.pipe replace /^([\S\s]+?)[\r\n]+?---[\r\n]/m, ($0, $1) ->
post = yaml $1
post.date = (post.date || new Date).getTime()
post.title = post.title || post.date
posts.push post
''
# 更多分隔位置
.pipe replace /<!--[ \t]*?more[ \t]*?-->/, '<a id=more></a>'
# 编译 markdown
.pipe marked
highlight: (code) ->
highlight(code).value
# 标记图片行
.pipe replace /<p>(.*?)<img src(.*?)>([\S\s]*?)<\/p>/gm, '<p class=img>$1<img src$2>$3</p>'
# 站外链接使用新窗口打开
.pipe replace /<a href="http(.*?)>/g, '<a href="http$1 target=_blank>'
# 压缩
.pipe minifyHtml()
# 输出
.pipe gulp.dest "#{dir.postPub}"
# 记录路径
.pipe tap (file, t) ->
post = posts[i++]
post.path = path.relative dir.postPub, file.path
console.log "[#{post.path}] #{post.title} (#{new Date(post.date)})"
gulp.task 'publish', ['posts'], ->
fs.mkdir dir.asset, ->
fs.writeFile "#{dir.asset}/index.json", JSON.stringify
config: config
posts: posts

至此,我们在 public/posts 目录下生成了我们编译好的文章,和文章的索引文件 public/assets/index.json

索引中包含了博客的配置和文章的元数据:

  • config.coffee 中所包含的配置
  • 文章的标题、时间、标签、分类、路径

编译主题包

主题包使用 LESS、CoffeeScript、AngularJS 进行开发,因此,我们需要对应使用 gulp-lessgulp-coffeegulp-autoprefixergulp-ng-annotategulp-html2jsgulp-revgulp-usemingulp-concatgulp-minify-cssgulp-minify-htmlgulp-uglify 进行配合。

首先,使用 gulp-less 把 LESS 编译为 CSS。

使用 gulp-autoprefixer 对 CSS 添加浏览器私有前缀。

使用 gulp-coffee 把 CoffeeScript 编译为 JS。

使用 gulp-ng-annotate 添加 AngularJS 的注入声明。

使用 gulp-html2js 把主题包的模板转换为 JS。

使用 gulp-usemin 创建出 gulp-minify-cssgulp-minify-htmlgulp-uglify 的任务,用来压缩 CSS、HTML、JS。

代码如下:

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
gulp.task 'js', ->
gulp.src "{#{dir.src},#{dir.theme}/scripts}/{,*/}*.coffee"
.pipe coffee
bare: true
.pipe ngAnnotate()
.pipe gulp.dest "#{dir.tmp}/scripts/"
gulp.task 'template', ->
gulp.src "#{dir.theme}/views/{,*/}*.html"
.pipe minifyHtml
empty: true
conditionals: true
quotes: true
.pipe html2js
outputModuleName: pkg.name
base: "#{dir.theme}/views"
singleModule: true
.pipe concat 'templates.js'
.pipe gulp.dest "#{dir.tmp}/scripts/"
gulp.task 'css', ->
gulp.src "#{dir.theme}/styles/*.less"
.pipe less
report: 'min'
optimization: 1
relativeUrls: false
ieCompat: true
strictImports: true
strictMath: false
strictUnits: false
.pipe apf
browsers: [
'last 2 versions'
'> 1%'
]
.pipe gulp.dest "#{dir.tmp}/styles/"
useminTheme = (useminConfig) ->
gulp.src "#{dir.theme}/views/*.html"
.pipe replace /<title([\S\s]*?)>[\S\s]*?<\/title>/, "<title$1>#{config.title}</title>"
.pipe replace /<meta name="description"[\S\s]*?>/, "<meta name=\"description\" content=\"#{config.description}\">"
.pipe usemin useminConfig
.pipe gulp.dest "#{dir.pub}/"
gulp.task 'usemin', ['clean', 'template', 'js', 'css'], ->
useminTheme
css: [
minifyCss()
rev()
]
js: [
uglify()
rev()
]
html: [
minifyHtml
empty: true
conditionals: true
quotes: true
]

创建服务器

通过配置 gulp-watch 任务与 gulp-connect 完成。

完整代码可以参考 gulpfile.coffee

现在,我们已经拥有了编译好的文章以及所需要的索引,也完成了主题包的编译。

下次,我们开始建立 AngularJS 的 ngApp。