# 指導
> 這個指導實際上不是必須的,但是我們強烈建議每一個人來遵守。因為沒有人會喜歡用一個不好的插件。這個指導能在某種意義上確保你的插件能很好的適應 gulp,以此來讓你的生活變得更將輕松。
[編寫插件](../) > 指導
1. 你的插件不應該去做一些現有 node 模塊已經能很容易做到的事情
* 比如:刪除一個文件夾并不需要做成一個 gulp 插件,在 task 里使用一個類似 [del](https://github.com/sindresorhus/del) 這樣的插件即可。
* 只是為了封裝而封裝一些的東西進去,這只會增加很多低質量的插件到生態中,這不符合 gulp 的期望。
* gulp 插件都是以文件為基礎操作的,如果你發現你正在把一些很復雜的操作塞進 stream 中去,那么,請直接寫一個 node 模塊就好。
* 一個好的 gulp 插件例子像是 gulp-coffee,coffee-script 模塊并不能直接和 vinyl 做很好的適配,因此,才去封裝它來使用相應的功能,并且將一些比較痛苦的操作抽象出來,做成更簡單的 gulp 插件來使用。
1. 你的插件應該只做**一件事**,并且做好。
* 避免使用配置選項,使得你的插件能勝任不同場合的任務。
* 比如:一個 JS 壓縮插件不應該有一個加頭部的選項
1. 你的插件不能去做一些其他插件做的事:
* 不應該去拼接,用 [gulp-concat](https://github.com/wearefractal/gulp-concat) 去做
* 不應該去增加頭部,用 [gulp-header](https://github.com/godaddy/gulp-header) 去做
* 不應該去增加尾部,用 [gulp-footer](https://github.com/godaddy/gulp-footer) 去做
* 如果是一個常用的可選的操作,那么,請在文檔中注明你的插件通常和其他某個插件一起使用
* 在你的插件中使用其他的插件,這能大大減少你的代碼量,并保證生態系統的穩定。
1. 你的插件必須被**測試過**
* 測試一個插件很簡單,你甚至不需要 gulp 就能測試
* 參考其他的插件是怎么做的
1. 在 `package.json` 中增加一個名為 `gulpplugin` 的關鍵字,這可以讓它能在我們的搜索中出現
2. 不要再 stream 里面拋出錯誤
* 你應該以觸發 **error** 事件來代替
* 如果你在 stream 外面遇到錯誤,比如在創建 stream 時候發現錯誤的配置選項等,那么你應該拋出它。
1. 錯誤需要加上以你插件名字作為前綴
* 比如: `gulp-replace: Cannot do regexp replace on a stream`
* 使用 gulp-util 的 [PluginError](https://github.com/gulpjs/gulp-util#new-pluginerrorpluginname-message-options) 類來完成它
1. `file.contents` 的類型需要總是在輸入輸出中保持一致
* 如果 file.contents 為空 (不可讀) 請將他忽略,并傳過去
* 如果 file.contents 是一個 stream,但是你不支持,那么請觸發一個錯誤
* 不要把 stream 硬轉成 buffer 來使你的插件支持 stream,這會引發很嚴重的問題。
1. 在你處理完成之前,不要將 `file` 傳到下游去
2. 使用 [`file.clone()`](https://github.com/wearefractal/vinyl#clone) 來復制一個文件或者創建另一個以此為基礎的文件
3. 使用我們 [模塊推薦頁](recommended-modules/) 上列舉的模塊來讓你的開發更加輕松
4. 不要把 `gulp` 作為一個依賴
* 使用 gulp 來測試你的插件的工作流這的確很酷,但請務必確保你將它放到 devDependency 中
* 在你的插件中依賴 gulp,這意味著安裝你的插件的用戶將會重新安裝一遍 gulp 以及所有它所依賴的東西。
* 沒有任何理由說明你需要將 gulp 寫到你的插件代碼中去,如果你發現你必須這么做,那么請開一個 issue,我們會幫你解決。
## 為什么這些指導這么嚴格?
gulp 的目標是為了讓用戶覺得簡單,通過提供一些嚴格的指導,我們就能提供一致并且高質量的生態系統給大家。不過,這確實給插件作者增加了一些需要考慮的東西,但是也確保了后面的問題會更少。
### 如果我不遵守這些,會發生什么?
npm 對每個人來說是免費的,你可以開發任何你想要開發的東西出來,并且不需要遵守這個規定。我們承諾測試機制將會很快建立起來,并且加入我們的插件搜索中。如果你堅持不遵守插件導覽,那么這會反應在我們的打分/排名系統上,人們都會更加喜歡去使用一個 "更加 gulp" 的插件。
### 一個插件大概會是怎么樣的?
```
// through2 是一個對 node 的 transform streams 簡單封裝
var through = require('through2');
var gutil = require('gulp-util');
var PluginError = gutil.PluginError;
// 常量
const PLUGIN_NAME = 'gulp-prefixer';
function prefixStream(prefixText) {
var stream = through();
stream.write(prefixText);
return stream;
}
// 插件級別函數 (處理文件)
function gulpPrefixer(prefixText) {
if (!prefixText) {
throw new PluginError(PLUGIN_NAME, 'Missing prefix text!');
}
prefixText = new Buffer(prefixText); // 預先分配
// 創建一個讓每個文件通過的 stream 通道
return through.obj(function(file, enc, cb) {
if (file.isNull()) {
// 返回空文件
cb(null, file);
}
if (file.isBuffer()) {
file.contents = Buffer.concat([prefixText, file.contents]);
}
if (file.isStream()) {
file.contents = file.contents.pipe(prefixStream(prefixText));
}
cb(null, file);
});
};
// 暴露(export)插件主函數
module.exports = gulpPrefixer;
```
- gulp 中文文檔
- 入門指南
- gulp API 文檔
- 編寫插件
- 指導
- 使用 buffer
- 使用 Stream 處理
- 測試
- FAQ
- gulp 技巧集
- 整合 streams 來處理錯誤
- 刪除文件和文件夾
- 使用 watchify 加速 browserify 編譯
- 增量編譯打包,包括處理整所涉及的所有文件
- 將 buffer 變為 stream (內存中的內容)
- 在 gulp 中運行 Mocha 測試
- 僅僅傳遞更改過的文件
- 從命令行傳遞參數
- 只重新編譯被更改過的文件
- 每個文件夾生成單獨一個文件
- 串行方式運行任務,亦即,任務依賴
- 擁有實時重載(live-reloading)和 CSS 注入的服務器
- 通過 stream 工廠來共享 stream
- 指定一個新的 cwd (當前工作目錄)
- 分離任務到多個文件中
- 使用外部配置文件
- 在一個任務中使用多個文件來源
- Browserify + Uglify2 和 sourcemaps
- Browserify + Globs
- 同時輸出一個壓縮過和一個未壓縮版本的文件
- 改變版本號以及創建一個 git tag
- Swig 以及 YAML front-matter 模板