釋出時間:2019年1月9日 - 5分鐘閱讀
如果你曾經做過一個web應用程式,你可能使用過一個構建系統。Webpack、Gulp或Grunt都是構建系統。
Dart 1使用了一個叫Barback的系統,但是Dart 2使用了一個新的系統,叫pack:build
。那麼有什麼變化,為什麼?
什麼是構建系統?
構建系統的一個快速定義是一個程式,它接受一組輸入檔案並生成輸出檔案。這些輸出檔案通常是可執行檔案或網路的 "捆綁"。在Dart中,提供了一個開發伺服器,因此,任何對你的程式碼的改變都會立即被構建並在HTTP埠上提供。
Dart的新構建包增加了一個額外的要求:輸出必須能夠靜態地確定。例如,如果一個名為post.md
的檔案被輸入到Markdown構建器中,構建系統應該知道輸出將是post.html
檔案。它必須在前期就知道這一點。
Goodbye Transformers
考慮這個Markdown變換器:
import 'dart:async';
import 'package:barback/barback.dart';
import 'package:markdown/markdown.dart';
class Markdown extends Transformer {
Markdown();
String get allowedExtensions => ".md .markdown .mdown";
Future apply(Transform transform) async {
var content = await transform.primaryInput.readAsString();
var id = transform.primaryInput.id.changeExtension(".html");
var newContent = markdownToHtml(content, extensionSet: ExtensionSet.gitHub);
transform.consumePrimary();
transform.addOutput(new Asset.fromString(id, newContent));
}
}
複製程式碼
這個變換器接收以.md
、.markdown
或.mdown
結尾的檔案,並將原始檔案替換為使用package:markdown
生成的.html
標籤。請注意,原始檔案被替換了,而構建系統在前期無法知道這個變換器將.md
檔案轉換為.html
檔案。它只是簡單地執行這個變換器。執行這個變換器呼叫告訴構建系統寫入新的.html檔案(Barback稱之為Asset),並消耗原始檔案。
由於輸出檔案對構建系統來說是不透明的,Markdown變換器可以自由地生成額外的檔案而不告訴構建系統。另一個構建步驟可能會選擇將多個.md
檔案合併成一個.html
檔案。
你也可能已經注意到,沒有建立或銷燬任何顯式檔案。Barback在記憶體中執行變換器。它不會將檔案寫入磁碟。
我們已經看到,Barback:
- 在構建執行之前,它不知道輸入和輸出的情況。
- 通過一個抽象的介面執行構建步驟。Barback可以執行這些構建步驟,而不需要將檔案寫入磁碟。
- 在執行下一個構建步驟之前,可以刪除輸入。
Hello,Builders
讓我們看看使用package:build實現的同樣的Markdown構建步驟:
import 'package:build/build.dart';
import 'package:markdown/markdown.dart';
Builder markdownBuilder(_) => MarkdownBuilder();
class MarkdownBuilder implements Builder {
Future build(BuildStep buildStep) async {
var inputId = buildStep.inputId;
var outputId = inputId.changeExtension(".html");
var contents = await buildStep.readAsString(inputId);
var markdownContents = markdownToHtml(contents);
await buildStep.writeAsString(outputId, markdownContents);
}
Map<String, List<String>> get buildExtensions => {
'.md': [".html"]
};
}
複製程式碼
請注意,allowExtensions
已經被buildExtensions
取代。Builder
必須明確地定義它的輸入和輸出。另外一個步驟是更新 build.yaml
檔案。
metadata_stripped
markdown:
import: "package:my_builders/markdown_builder.dart"
builder_factories:
- markdownBuilder
build_extensions:
.md:
- .html
auto_apply: root_package
required_inputs:
- .md
複製程式碼
如果Builder向AssetId
寫入一個不在buildExtensions
中的副檔名,就會丟擲一個UnexpectedOutputException
。
注意,這個時候:
- 構建系統已經知道了輸入和輸出的資訊
- 檔案的輸入和輸出仍然是通過一個抽象的介面來完成的。
- 不能通過構建步驟刪除輸入
一個例子。升級Tavern,一個靜態網站生成器。
Tavern是一個Dart的靜態網站生成器。它現在已經基本支援使用Dart 2和包構建。你可以在 github.com/johnpryan/t… 找到新版本。
Tavern的目標是成為一個類似於Jekyll的工具。它還可以通過結合它的Builders和官方的Dart builders,自動構建Dart指令碼在你的靜態網站上執行。
它支援寫部落格文章,格式如下。
---
title: "Markdown Basics"
tags: ['code', 'dart']
---
# this is markdown
Here is some code about {{title}}
Tags:
{{#tags}}
<li>{{.}}
{{/tags}}
`` `
hello world!
`` `
複製程式碼
為此,它依次應用多個變換器。例如,上面的檔案post.md
可能會經歷以下轉換:
- Metadata變換器提取檔案頂部的 "後設資料 "YAML,並寫入兩個檔案
post.md
(去掉後設資料)和post.metadata.json
。 - Markdown變換器消耗
post.md
檔案並輸出post.html
檔案。 - Template變換器執行並接收兩個輸入,
post.html
和post.metadata.json
,並將post.html
替換為一個新的資產,並應用post.metadata.json
中的後設資料進行Mustache模板化。
Tavern 2需要採取不同的方法。構建步驟不能再替換具有相同id的資產。相反,必須為下一個構建步驟編寫新的檔案。
但是Metadata構建步驟如何從post的頂部提取YAML呢?post.md
不能再簡單地替換為新版本。
構建步驟需要更加明確:
- Metadata構建步驟提取YAML,並寫出兩個檔案post.contents(包含去掉頂部部分的markdown)和post.metadata(包含模板步驟的JSON檔案)。
- Markdown生成器現在接收一個.contents檔案並輸出一個.html_template檔案。
- 模板生成器接收一個.html_template檔案和一個.metadata檔案,並輸出最終的.html檔案。
- 執行一個名為CleanupBuilder的PostProcessBuilder來刪除所有.md、.contents、.metadata和.html_template_files,只留下最後的html檔案。
構建步驟可以這樣佈局:
等等,但為什麼?
強制構建步驟遵守更嚴格的規則,可以讓構建系統更好地與Bazel合作,並在某些情況下智慧地跳過構建步驟。例如,如果我們的應用使用了Sass,那麼一個Sass構建器可以和我們上面定義的管道一起執行。當Sass檔案發生變化時,構建系統將只執行構建該CSS檔案所需的步驟。
總結
一個具有自定義構建器的高效構建系統,一直是Dart SDK的核心功能。現在,它通過新的語義進行了更多的優化,但仍然對自定義構建規則有很大的支援。
參考文獻
通過www.DeepL.com/Translator(免費版)翻譯