Shellway的博客

【译】结合“模式实验室”和Gulp来改进工作流

你和你的团队是否在使用风格指南?你的模块是否有文档?如果否,别慌张。在本教程中,我将告诉你如何才能改进你和你团队的协作方式。我们将一起探索Brad Frost和Dave Olsen所提倡的概念:使用“模式实验室”(Pattern Lab)来创建风格指南,用Gulp来处理资源。我们开始吧。

Gulp是什么?

Gulp.js是一个流式构建系统及任务运行器。重要的概念是:你可以为你的各种文件定义各种配置。它比手动管理要快得多,节省你的脑细胞,及大把时间。
例如:在此教程中,我们将所有Sass文件放到一个管道(进程)中处理:

  • 将Sass编译成CSS,
  • 把CSS文件拼接起来,
  • 压缩输出的文件,
  • 并将所有文件移到其他位置

要想了解更多Gulp基础,你可以看看Kezz Bracey的初学者指南《Web设计命令行:用Gulp实现自动化》(The Command Line for Web Design: Automation with Gulp)。

Pattern Lab又是什么?

模式实验室是一个概念,用以创建原子式设计系统;以构建模块直接取代构建页面。它定义了诸多组成部分:

  • 原子 atoms
  • 分子 molecules
  • 有机体 organisms
  • 模板 templates
  • 页面 pages

你从最小的元素开始,逐渐构建出更大部件,直到整个页面。此方法助你节省时间,利于团队协同,及保证坚固的架构。

原子 Atoms

Atoms can’t be broken down into smaller pieces.

These are the simplest blocks, including fundamental tags like lists, colors, fonts, animations etc.

分子 Molecules

Molecules are groups of different elements (atoms) which function together like a unit.

For example, a teaser with a headline, an image, paragraph and a link like “Read more”. Each of these is a single element, but together they become a molecule; part of a larger, more complex system.

有机体 Organisms

Organisms are groups of elements (atoms and molecules) and work like a section on your website.

Think, for example, of a website header. It’s a larger system, built from molecules such as a search form and navigation, both of which are in turn built of smaller atoms.

patternlab header organism
Pattern Lab header organism, seen on small screen
Check out the online UI demos and get a feeling for the whole system.

见证奇迹的时刻

Now it’s time to combine both these systems and create a workflow for your team. Pattern Lab will give us our HTML and deliver the simple UI, Gulp will handle all the assets we need.

Our Key-Features:
  • Sass-Compiling (Libsass)
  • Server (Browser-Sync)
  • Livereload
  • Minify (Javascript, CSS and images)
  • Releasing / Deployment
    • Bump up the version
    • Tagging
    • Push files and tags to endpoint
    • Push files via rsync to a server
Introduction

To use Gulp you’ll first need to have node.js on your system. If you don’t, take a look at Kezz Bracey’s The Command Line for Web Design: Taming 3rd Party Packages for setup instructions.

Let’s start by installing Gulp.js globally. In the terminal, type:

npm install gulp -g

Now we need to clone the repository of Patternlab to give us a base to work from.

git clone git@github.com:pattern-lab/patternlab-php.git

Next we need a gulp file to setup our tasks. Create a gulpfile.js in the root of your project folder. After that we need a config-file, where we define all the paths, so create a build.config.json in your folder.

The following files are needed too:

1
2
3
.bowerrc
package.json
bower.json

After all these basic steps, we have the basic project structure. Now let’s start to build the tasks for our workflow.

Start With the Gulpfile

At the top of our gulpfile.js file, we require each dependency. If you install a plugin, you must “require” it and give it a name.

Start with gulp and our configfile for all paths and configuration.

1
2
var gulp = require('gulp'),
config = require('./build.config.json');

During our development process we won’t need to be minifying our code (that’s a waste of time unless we’re ready to deploy). With the following production variable we can toggle some tasks on and off. You will see this in action later on.

1
2
3
4
// Production Handling
// Description: Use 'production' variable with 'gulpif'
// Toggle minifing and optimization for assets
var production;

With things setup, we can now start adding various tasks to help us in our development!

任务1:清除所有静态资源
1
2
3
4
5
// 安装所需的依赖
npm install gulp-clean
// 引入依赖
var clean = require('gulp-clean');

If you delete an image from the folder “source/”, you’ll find there’s a copy of the image in “public/” too. Because of this duplication we’ll perform a simple step to clean the image folder in “public/”.

1
2
3
4
5
6
7
8
9
10
// 任务: Clean:before
// 描述: 运行其他任务前,清空资源目录
gulp.task('clean:before', function () {
return gulp.src(
config.assets.dest
)
.pipe(clean({
force: true
}))
});

任务2:处理脚本
1
2
3
4
5
6
7
// 安装所需的依赖
npm install gulp-concat gulp-uglify gulp-rename
// 引入依赖包
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');

For deployment purposes it’s important to have just one file with all scripts. To achieve this we’ll use the plugin gulp-concat and combine all our scripts to produce application.js. If the variable production is true, then application.js will be uglified and get a new name: application.min.js.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
gulp.task('scripts', function () {
return gulp.src(config.scripts.files)
.pipe(concat(
'application.js'
))
.pipe(gulpif(production, uglify()))
.pipe(gulpif(production, rename({
suffix: '.min'
})))
.pipe(gulp.dest(
config.scripts.dest
))
.pipe(browserSync.reload({stream:true}));
});
任务3:字体

此任务仅将所有字体放到公共目录,无他。

1
2
3
4
5
6
7
8
// 任务: 处理字体
gulp.task('fonts', function () {
return gulp.src(config.fonts.files)
.pipe(gulp.dest(
config.fonts.dest
))
.pipe(browserSync.reload({stream:true}));
});
任务4:图片

此步骤,我们将安装并引入gulp-imagemin插件,之后就可以用它来压缩图片了。这将节省内存和提高性能。

1
2
3
4
5
// 安装imagemin
npm install gulp-imagemin
// 引入依赖包
var imagemin = require('gulp-imagemin');

如果变量“production”为true,那所有图片将被压缩。压缩完后,我们就把图片放进目的文件夹。

1
2
3
4
5
6
7
8
9
// 任务: 处理图片
gulp.task('images', function () {
return gulp.src(config.images.files)
.pipe(gulpif(production, imagemin()))
.pipe(gulp.dest(
config.images.dest
))
.pipe(browserSync.reload({stream:true}));
});
任务5:处理Sass

安装并引入依赖包:gulp-sass和gulp-cssmin。

1
2
3
4
5
6
// 安装所需依赖
npm install gulp-sass gulp-cssmin
// 引入依赖包
var sass = require('gulp-sass');
var cssmin = require('gulp-cssmin');

现在我们将会把所有Sass文件,通过gulp-sass插件编译成CSS,当生产环境(production)参数为true时,cssmin将把CSS再压缩。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 任务: 处理Sass及CSS
gulp.task('sass', function () {
return gulp.src(config.scss.files)
.pipe(sass())
.pipe(gulpif(production, cssmin()))
.pipe(gulpif(production, rename({
suffix: '.min'
})))
.pipe(gulp.dest(
config.scss.dest
))
.pipe(browserSync.reload({stream:true}));
});
任务6:模式实验室服务器

模式实验室自带服务器,你可以通过终端命令来开启它。使用前需要安装gulp-shell插件。

1
2
3
4
5
// 安装所需依赖
npm install gulp-shell
// 引入依赖包
var shell = require('gulp-shell');

此时,我们将从“core”目录复制风格指南文件夹到“public”目录。至此,你也可以在浏览器中看到一个立体的“前端”了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 任务: patternlab
// 描述: 通过PHP脚本构建静态模式实验室服务器
gulp.task('patternlab', function () {
return gulp.src('', {read: false})
.pipe(shell([
'php core/builder.php -gpn'
]))
.pipe(browserSync.reload({stream:true}));
});
// 任务: styleguide
// 描述: 从“core/”目录复制“风格指南”文件夹到“public”目录
gulp.task('styleguide', function() {
return gulp.src(config.patternlab.styleguide.files)
.pipe(gulp.dest(config.patternlab.styleguide.dest));
});
任务7:开启服务器并观察文件

模式实验室具有内置的服务器,而Browser-Sync则自动处理CSS变动注入,而无需页面重新加载。

1
2
3
4
5
6
// 安装所需依赖
npm install browser-sync gulp-watch
// 引入依赖包
var browser-sync = require('browser-sync');
var watch = require('gulp-watch');

观察器会留意文件改动,并自动触发特定任务。之后,browser-sync就会更新浏览器视图。

1
2
3
4
5
6
7
8
9
10
11
// 任务: BrowserSync
// 描述: 以ghost模式运行BrowserSync服务器
gulp.task('browser-sync', function() {
browserSync({
server: {
baseDir: config.root
},
ghostMode: true,
open: "external"
});
});

We specify the files for the watcher and trigger the tasks which we need in the event of a change.

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
// Task: Watch files
gulp.task('watch', function () {
// Watch Pattern Lab files
gulp.watch(
config.patternlab.files,
['patternlab']
);
// Watch scripts
gulp.watch(
config.scripts.files,
['scripts']
);
// Watch images
gulp.watch(
config.images.files,
['images']
);
// Watch Sass
gulp.watch(
config.scss.files,
['sass']
);
// Watch fonts
gulp.watch(
config.fonts.files,
['fonts']
);
});

任务8:默认任务

Writing gulp in the shell triggers the default task. But before Gulp starts this, it triggers the clean:before task to clean all the public files.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Task: Default
// Description: Build all stuff of the project once
gulp.task('default', ['clean:before'], function () {
production = false;
gulp.start(
'patternlab',
'styleguide',
'fonts',
'sass',
'images',
'scripts'
);
});

Task 9: Start Process 任务9:开始处理

Let’s create a task to develop at the styleguide, but without minifying assets. This triggers browser-sync, builds all assets and starts the watcher.

1
2
3
4
5
6
7
8
9
10
11
// Task: Start your production-process
// Description: Typ 'gulp' in the terminal
gulp.task('serve', function () {
production = false;
gulp.start(
'browser-sync',
'default',
'watch'
);
});

Task 10: Releasing and Tagging 任务10:发布和打标签

For this step we’re going to need a few new plugins.

The plugin gulp-bump is to update the version number.
gulp-filter will give us a specific file of the stream.
gulp-git allows us to use git statements in gulp.
And gulp-tag-version is for generating the tag.

1
2
// Install needed dependencies
npm install gulp-bump gulp-filter gulp-git gulp-tag-version
1
2
3
4
5
// Require dependencies
var bump = require('gulp-bump');
var filter = require('gulp-filter');
var git = require('gulp-git');
var tagversion = require('gulp-tag-version');

Now you define the gulp-task release, set the variable production to true (now we need to minify) and open the stream. You must take all files with a version number, use the plugin bump and make it possible to define the type (patch, minor or major) via a parameter in the shell.

If you execute the release-task without a type then gulp-bump will take a patch - x.x.1. After this you push the files to the root and commit the changes. Now it’s time to generate a new tag for the project. The file package.json is needed to get the current version number for the new tag.

Finally, we push all files and tags to the origin and in the branch which we want.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Function: Releasing (Bump & Tagging)
// Description: Bump npm versions, create Git tag and push to origin
gulp.task('release', function () {
production = true;
return gulp.src(config.versioning.files)
.pipe(bump({
type: gulp.env.type || 'patch'
}))
.pipe(gulp.dest('./'))
.pipe(git.commit('Release a ' + gulp.env.type + '-update'))
// read only one file to get version number
.pipe(filter('package.json'))
// Tag it
.pipe(tagversion())
// Publish files and tags to endpoint
.pipe(shell([
'git push origin develop',
'git push origin --tags'
]));
});
Task 11: Deployment 任务11:部署

It’s possible to deploy all the files to a server via rsync; doing so is super fast and comfortable. Type gulp deploy in your terminal and after a few seconds you have the local project at the server. You don’t need to drag and drop folders manually. Automate all the things. You define the host and the path of the folder, which you want to deploy in build.config.js.

1
2
3
4
5
6
7
8
// Task: Deploy static content
// Description: Deploy static content using rsync shell command
gulp.task('deploy', function () {
return gulp.src(config.deployment.local.path, {read: false})
.pipe(shell([
'rsync '+ config.deployment.rsync.options +' '+ config.deployment.local.path +'/ '+ config.deployment.remote.host
]))
});
Final Gulpfile 最终的Gulpfile

You’ve written so much code, and here is the final result! You may prefer to have a separate file for each task, in which case you’re welcome to split it up. In this case, for the sake of simplicity, we’ll display everything in a single Gulpfile:

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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
var gulp = require('gulp'),
bump = require('gulp-bump'),
clean = require('gulp-clean'),
concat = require('gulp-concat'),
browserSync = require('browser-sync'),
cssmin = require('gulp-cssmin'),
filter = require('gulp-filter'),
git = require('gulp-git'),
gulpif = require('gulp-if'),
imagemin = require('gulp-imagemin'),
rename = require('gulp-rename'),
sass = require('gulp-sass'),
shell = require('gulp-shell'),
tagversion = require('gulp-tag-version'),
uglify = require('gulp-uglify'),
config = require('./build.config.json');
// Trigger
var production;
// Task: Clean:before
// Description: Removing assets files before running other tasks
gulp.task('clean:before', function () {
return gulp.src(
config.assets.dest
)
.pipe(clean({
force: true
}))
});
// Task: Handle scripts
gulp.task('scripts', function () {
return gulp.src(config.scripts.files)
.pipe(concat(
'application.js'
))
.pipe(gulpif(production, uglify()))
.pipe(gulpif(production, rename({
suffix: '.min'
})))
.pipe(gulp.dest(
config.scripts.dest
))
.pipe(browserSync.reload({stream:true}));
});
// Task: Handle fonts
gulp.task('fonts', function () {
return gulp.src(config.fonts.files)
.pipe(gulp.dest(
config.fonts.dest
))
.pipe(browserSync.reload({stream:true}));
});
// Task: Handle images
gulp.task('images', function () {
return gulp.src(config.images.files)
.pipe(gulpif(production, imagemin()))
.pipe(gulp.dest(
config.images.dest
))
.pipe(browserSync.reload({stream:true}));
});
// Task: Handle Sass and CSS
gulp.task('sass', function () {
return gulp.src(config.scss.files)
.pipe(sass())
.pipe(gulpif(production, cssmin()))
.pipe(gulpif(production, rename({
suffix: '.min'
})))
.pipe(gulp.dest(
config.scss.dest
))
.pipe(browserSync.reload({stream:true}));
});
// Task: patternlab
// Description: Build static Pattern Lab files via PHP script
gulp.task('patternlab', function () {
return gulp.src('', {read: false})
.pipe(shell([
'php core/builder.php -gpn'
]))
.pipe(browserSync.reload({stream:true}));
});
// Task: styleguide
// Description: Copy Styleguide-Folder from core/ to public
gulp.task('styleguide', function() {
return gulp.src(config.patternlab.styleguide.files)
.pipe(gulp.dest(config.patternlab.styleguide.dest));
});
// task: BrowserSync
// Description: Run BrowserSync server with disabled ghost mode
gulp.task('browser-sync', function() {
browserSync({
server: {
baseDir: config.root
},
ghostMode: true,
open: "external"
});
});
// Task: Watch files
gulp.task('watch', function () {
// Watch Pattern Lab files
gulp.watch(
config.patternlab.files,
['patternlab']
);
// Watch scripts
gulp.watch(
config.scripts.files,
['scripts']
);
// Watch images
gulp.watch(
config.images.files,
['images']
);
// Watch Sass
gulp.watch(
config.scss.files,
['sass']
);
// Watch fonts
gulp.watch(
config.fonts.files,
['fonts']
);
});
// Task: Default
// Description: Build all stuff of the project once
gulp.task('default', ['clean:before'], function () {
production = false;
gulp.start(
'patternlab',
'styleguide',
'fonts',
'sass',
'images',
'scripts'
);
});
// Task: Start your production-process
// Description: Typ 'gulp' in the terminal
gulp.task('serve', function () {
production = false;
gulp.start(
'browser-sync',
'default',
'watch'
);
});
// Task: Deploy static content
// Description: Deploy static content using rsync shell command
gulp.task('deploy', function () {
return gulp.src(config.deployment.local.path, {read: false})
.pipe(shell([
'rsync '+ config.deployment.rsync.options +' '+ config.deployment.local.path +'/ '+ config.deployment.remote.host
]))
});
// Function: Releasing (Bump & Tagging)
// Description: Bump npm versions, create Git tag and push to origin
gulp.task('release', function () {
production = true;
return gulp.src(config.versioning.files)
.pipe(bump({
type: gulp.env.type || 'patch'
}))
.pipe(gulp.dest('./'))
.pipe(git.commit('Release a ' + gulp.env.type + '-update'))
// read only one file to get version number
.pipe(filter('package.json'))
// Tag it
.pipe(tagversion())
// Publish files and tags to endpoint
.pipe(shell([
'git push origin develop',
'git push origin --tags'
]));
});
Configfile 配置文件

Now we need our configfile to set the various paths. This file is necessary to maintain the paths and configuration of the project:

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
{
"root": "./public",
"deployment": {
"local": {
"path": "public"
},
"remote": {
"host": "YOUR HOST"
},
"rsync": {
"options": "-avzh --delete -e ssh"
}
},
"assets": {
"base": "source/assets/",
"dest": "public/assets/"
},
"versioning": {
"files": [
"./package.json",
"./bower.json"
]
},
"scripts": {
"base" : "source/assets/javascripts/",
"files" : [
"source/assets/javascripts/**/*.js"
],
"dest" : "public/assets/javascripts/"
},
"components": {
"base": "source/assets/components/"
},
"scss": {
"base" : "source/assets/scss/",
"files": [
"source/assets/scss/**/*.scss"
],
"dest" : "public/assets/stylesheets/"
},
"fonts": {
"base" : "source/assets/fonts/",
"files": [
"source/assets/fonts/**/*"
],
"dest" : "public/assets/fonts/"
},
"images": {
"base" : "source/assets/images/",
"files": [
"source/assets/images/**/*"
],
"dest" : "public/assets/images/"
},
"patternlab": {
"styleguide": {
"files": [
"core/styleguide/**"
],
"dest": "public/styleguide/"
},
"files": [
"source/_patterns/**/*.mustache",
"source/_patterns/**/*.json",
"source/_data/*.json"
]
}
}

总结

I love working with a combination of Gulp and Pattern Lab. I’ve worked in different teams and set this foundation for each project. Feedback by each team member has always been positive because of its easy-to-follow process. Everybody have a solid surface, can pick the module and use it with ease. For questions or feedback please see the comments section below.

原文:https://webdesign.tutsplus.com/tutorials/combining-pattern-lab-with-gulp-for-improved-workflow--cms-22187

译者:Shellway Ho

转载请保留出处:http://shellway.cn/2017/04/14/Combining-Pattern-Lab-with-Gulp-for-Improved-Workflow/