本文是我們 .NET教育系列的一部分,該教育系列探討了 .NET 技術(shù)的好處,以及它是如何不僅可以幫助傳統(tǒng)的 .NET 開發(fā)人員,還可以幫助所有想要為市場(chǎng)提供可靠、高效且經(jīng)濟(jì)的解決方案的技術(shù)人員的。隨著 .NET Core 3.0 的發(fā)布,微軟擁有了通用、模塊化、跨平臺(tái)和開源平臺(tái)的下一個(gè)主要版本,該版本最初是在 2016 年發(fā)布的。.NET Core 最初是為了支持下一代 ASP.NET 解決方案而創(chuàng)建的,但現(xiàn)在它驅(qū)動(dòng)了許多其他場(chǎng)景,包括物聯(lián)網(wǎng)、云和下一代移動(dòng)解決方案,并且是這些場(chǎng)景的基礎(chǔ)。3.0 版本增加了許多常用的特性,比如對(duì) WinForms、WPF 和 Entity Framework 6 的支持。由于 .NET Core 對(duì)命令行的重視,導(dǎo)致它的工具發(fā)生了巨大的變化。這非常適用于 .NET Core 跨平臺(tái)、工具無(wú)關(guān)的鏡像。dotnet CLI 是實(shí)現(xiàn)這些優(yōu)勢(shì)功能的入口點(diǎn),它包含了許多用于創(chuàng)建、編輯、構(gòu)建和打包 .NET Core 項(xiàng)目的不同命令。在本文中,我們僅關(guān)注 dotnet CLI 的一個(gè)方面:dotnet new 命令。該命令主要用于創(chuàng)建項(xiàng)目,學(xué)習(xí)過程中,我們經(jīng)常會(huì)創(chuàng)建一個(gè)簡(jiǎn)單的樣板項(xiàng)目,然后就把它忘掉了。本文中,我們將學(xué)習(xí)如何充分利用這個(gè)命令,通過傳遞參數(shù)來(lái)修改已生成的項(xiàng)目,并將學(xué)習(xí)如何使用該命令來(lái)創(chuàng)建文件和項(xiàng)目。我們還將看到這個(gè)工具是一個(gè)成熟的模板引擎,它不僅可以用來(lái)安裝自定義模板,還可以用來(lái)制作個(gè)人模板。dotnet new 實(shí)踐
要怎樣使用 dotnet new 呢?讓我們從頭開始,直到找出最有趣的部分。要?jiǎng)?chuàng)建一個(gè)簡(jiǎn)單的控制臺(tái)應(yīng)用程序,那么先啟動(dòng)命令行,將目錄切換為一個(gè)新的空文件夾(這是一個(gè)重要步驟,后面將會(huì)對(duì)此進(jìn)行說明),然后調(diào)用 dotnet new console: |
The template "Console Application" was created successfully. |
|
Processing post-creation actions... |
Running 'dotnet restore' on /Users/matt/demo/MyNewApp/MyNewApp.csproj... |
Restoring packages for /Users/matt/demo/MyNewApp/MyNewApp.csproj... |
Generating MSBuild file /Users/matt/demo/MyNewApp/obj/MyNewApp.csproj.nuget.g.props. |
Generating MSBuild file /Users/matt/demo/MyNewApp/obj/MyNewApp.csproj.nuget.g.targets. |
Restore completed in 234.92 ms for /Users/matt/demo/MyNewApp/MyNewApp.csproj. |
|
|
正如前面提到的,首先確保我們?cè)谝粋€(gè)新的空文件夾中。默認(rèn)情況下,dotnet new 將會(huì)在當(dāng)前文件夾中創(chuàng)建文件,并且不會(huì)刪除已經(jīng)存在的文件。我們可以使用 --output 選項(xiàng)創(chuàng)建一個(gè)新文件夾。例如,可以通過鍵入如下命令在名為 ConsoleApp42 的新文件夾中創(chuàng)建項(xiàng)目:> dotnet new console --output ConsoleApp42 |
The template "Console Application" was created successfully. |
|
Processing post-creation actions... |
Running 'dotnet restore' on ConsoleApp42/ConsoleApp42.csproj... |
Restoring packages for /Users/matt/demo/ConsoleApp42/ConsoleApp42.csproj... |
Generating MSBuild file /Users/matt/demo/ConsoleApp42/obj/ConsoleApp42.csproj.nuget.g.props. |
Generating MSBuild file /Users/matt/demo/ConsoleApp42/obj/ConsoleApp42.csproj.nuget.g.targets. |
Restore completed in 309.99 ms for /Users/matt/demo/ConsoleApp42/ConsoleApp42.csproj. |
|
看看創(chuàng)建了什么
此時(shí),dotnet new 已經(jīng)創(chuàng)建了一個(gè)新的控制臺(tái)項(xiàng)目,并且還原了 NuGet 包(它已經(jīng)準(zhǔn)備好運(yùn)行了)。但是,讓我們先看下我們到底創(chuàng)建了什么:> ls ConsoleApp42/ConsoleApp42.csproj Program.cs obj/
可以看到,現(xiàn)在有了一個(gè)基于輸出文件夾名稱的項(xiàng)目文件。如果我們需要,也可以使用 --name 參數(shù)來(lái)指定一個(gè)不同的名稱:dotnet new console --output ConsoleApp42 --name MyNewApp
它將在名為 ConsoleApp42 的文件夾中創(chuàng)建項(xiàng)目文件,并使用 MyNewApp 作為正在創(chuàng)建的控制臺(tái)應(yīng)用程序的名稱,我們將得到一個(gè) MyNewApp.csproj。如果我們查看 Program.cs,還將看到該 name 參數(shù)也被用來(lái)更新命名空間了: |
|
|
|
|
|
static void Main(string[] args) |
|
Console.WriteLine("Hello World!"); |
|
|
|
為另一個(gè)項(xiàng)目做準(zhǔn)備
但是,如果我們查看一下我們剛才創(chuàng)建的項(xiàng)目的文件夾目錄結(jié)構(gòu),可能會(huì)發(fā)現(xiàn)它丟失了一些內(nèi)容,它沒有解決方案文件。我們只有一個(gè)項(xiàng)目,雖然它可以在 dotnet run 時(shí)正常運(yùn)行,但當(dāng)我們添加另一個(gè)項(xiàng)目時(shí),它將會(huì)引起問題。我們可以輕松創(chuàng)建一個(gè)項(xiàng)目:
該命令將創(chuàng)建一個(gè)新的空解決方案文件。然后,向其中添加項(xiàng)目是另外的步驟。如果我們要在演示示例的根文件夾目錄中創(chuàng)建解決方案,命令如下所示:dotnet sln add ConsoleApp42/MyApp.sln |
|
也可以使用 dotnet sln 命令刪除或列出解決方案中的項(xiàng)目。如果要添加或刪除對(duì)項(xiàng)目的引用,則需要使用 dotnet add 命令。我建議大家閱讀下 Jeremy Miller 的關(guān)于可擴(kuò)展的 dotnet CLI 的文章以獲取更多詳細(xì)細(xì)節(jié),或者通過鍵入 dotnet help sln 或 dotnet help add 獲取。添加另一個(gè)項(xiàng)目也非常簡(jiǎn)單,但是我們必須以兩步的方式來(lái)完成:創(chuàng)建,然后添加。例如,可以將一個(gè)測(cè)試項(xiàng)目添加到解決方案中:dotnet new nunitdotnet sln add Tests/MyAppTests.csproj
向項(xiàng)目添加新文件
向項(xiàng)目中添加新文件甚至更加容易,這主要?dú)w功于.NET Core 對(duì) MSBuild 文件的改進(jìn)。我們不需要再在.csproj 文件中顯式地列出 C# 文件了,因?yàn)樗鼈兪峭ㄟ^通配符自動(dòng)獲取的。我們只需要在文件夾中創(chuàng)建一個(gè)文件,它將自動(dòng)成為項(xiàng)目的一部分。我們既可以手動(dòng)也可以使用 dotnet new 提供模板文件來(lái)創(chuàng)建文件。說到模板,我們?cè)趺粗烙心男┛捎玫哪0迥兀?/span>如何區(qū)分項(xiàng)目模板( project template)和項(xiàng)模板(item template)呢?這是 dotnet new --list 的工作,它可以輸出所有可用的模板列表:
模板 | 簡(jiǎn)稱 | 語(yǔ)言 | 標(biāo)簽 |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
ASP.NET Core Web App (Model-View-Controller) | | | |
| | | |
ASP.NET Core with Angular | | | |
ASP.NET Core with React.js | | | |
ASP.NET Core with React.js and Redux | | | |
| | | Web/Razor/Library/Razor Class Library |
| | | |
| |
| |
| |
| |
| |
| |
| |
| |
上述列表列出了所有的模板。使用 --type 參數(shù)可以進(jìn)行進(jìn)一步的過濾,如使用 --type project、–type item 或–type other。項(xiàng)目模板將會(huì)創(chuàng)建一個(gè)項(xiàng)目,項(xiàng)模板將會(huì)創(chuàng)建一個(gè)文件,而其他模板僅對(duì) sln 模板創(chuàng)建的解決方案文件有用。這個(gè)列表中的簡(jiǎn)稱(上表第 2 列)是調(diào)用 dotnet new 時(shí)使用的名稱(例如 dotnet new console、dotnet new classlib、dotnet new mvc 等)。有些模板支持多種語(yǔ)言,默認(rèn)語(yǔ)言為方括號(hào)中的(劇透下,它們都是 C#)。我們可以使用 --language 選項(xiàng)來(lái)選擇不同的語(yǔ)言,但是要注意#符號(hào)!一些命令行 shell 將其視為注釋字符,使用–language F# 解析可能會(huì)失敗。這時(shí)可以通過引用值 - “–language F#” 來(lái)處理。最后,每個(gè)模板都有一個(gè)或多個(gè)標(biāo)簽。這些標(biāo)簽是一種對(duì)模板進(jìn)行分類的方法,但目前它們并不是作為命令行工具的一部分被使用。但是,它們可以被其他宿主用于分組或過濾。是的,沒錯(cuò),dotnet new 模板引擎可以用于其他宿主,比如 IDE。稍后再詳細(xì)介紹。自定義模板
到目前為止,我們只研究了一個(gè)非常簡(jiǎn)單的 Hello World 控制臺(tái)應(yīng)用程序,并添加了一些測(cè)試。讓我們看些更有趣的東西吧。假設(shè)要?jiǎng)?chuàng)建一個(gè)新的 ASP.NET 項(xiàng)目。查看上面的模板列表,我們有幾個(gè)選擇??梢詣?chuàng)建一個(gè)空的 web 項(xiàng)目、一個(gè) MVC 項(xiàng)目、一個(gè)帶有 Angular 的項(xiàng)目或者一個(gè)帶有 React.js 的項(xiàng)目。但是這些都是相當(dāng)嚴(yán)格的模板。我們能對(duì)這些模版進(jìn)行定制嗎?好消息是:可以的。模板可以接受一些變更生成內(nèi)容的參數(shù)。 – help 命令將會(huì)提供模板所理解的參數(shù)的詳細(xì)信息。讓我們從一個(gè)簡(jiǎn)單的例子開始吧: |
|
|
Description: A project for creating a class library that targets .NET Standard or .NET Core |
|
|
netcoreapp2.1 - Target netcoreapp2.1 |
netstandard2.0 - Target netstandard2.0 |
|
|
|
|
Default: false / (*) true |
|
|
* Indicates the value used if the switch is provided without a value. |
在此,我們可以看到 classlib 模板有兩個(gè)參數(shù):一個(gè)是 --framework,它用于指定要將什么目標(biāo)框架寫入項(xiàng)目文件中;另一個(gè)是 --no-restore,它用于控制在創(chuàng)建項(xiàng)目時(shí)是否執(zhí)行 NuGet 還原。web 模板也有類似的參數(shù),但是它們的數(shù)量比我們這里列出的要多得多。嘗試 dotnet new mvc --help 可以了解更多詳細(xì)信息。有些參數(shù)可以指定身份驗(yàn)證類型、是否禁用 HTTPS、是否是使用 LocalDB 替換 SQLite 等等。這些參數(shù)中的每一個(gè)都會(huì)變更模板代碼的生成方式,它們有的是替換文件中的內(nèi)容,有的是根據(jù)需要包含或排除文件。當(dāng)我們討論幫助文檔時(shí),有兩個(gè)非常有用的命令:dotnet help new,它將在 dotnet new 命令本身之上打開一個(gè)網(wǎng)頁(yè);dotnet new {template} –help,它將顯示命名模板及其參數(shù)的幫助信息。添加自定義模板
dotnet new 命令的真正強(qiáng)大之處在于添加新的自定義模板。更好的是,只需要將模版打包到一個(gè) NuGet 包中并上傳到 nuget.org 上,即可實(shí)現(xiàn)模板的分發(fā)和共享。這使得開始使用一個(gè)框架或自動(dòng)化創(chuàng)建新項(xiàng)目樣板或項(xiàng)目項(xiàng)樣板非常容易。要添加一個(gè)新的自定義模板,使用 dotnet new --install {template} 命令,傳入一個(gè) NuGet 包名或一個(gè)本地模板的文件目錄。但如何找到新的模板呢?一種方法是搜索我們正在使用的框架,并查看它的模板是否可用,但這樣做容易出錯(cuò)。幸運(yùn)的是,我們可以訪問 dotnetnew.azurewebsites.net 并通過關(guān)鍵字搜索模板。網(wǎng)站上有超過 500 個(gè)模板,這使得它成為了一個(gè)很好的發(fā)現(xiàn)資源的地方。例如,可以使用 dotnet new --install Amazon.Lambda.Templates 為 AWS Lambda 項(xiàng)目安裝一組模板。通過 NuGet 包安裝模板有一個(gè)非常好的特性,那就是每個(gè)包可以包含多個(gè)模板。這個(gè) AWS Lambda 包包含了 28 個(gè)不同的模板,并且還包括了一個(gè)教程項(xiàng)目。當(dāng)然,如果我們不想再使用某個(gè)模板了,只需要使用 dotnet new --uninstall {package} 卸載它即可。這里傳遞的名稱是已安裝模板包的名稱,如果我們不確定對(duì)應(yīng)的名稱,只需要運(yùn)行 dotnet new --uninstall 即可獲得一個(gè)列表。創(chuàng)建自己的模板
我們還可以創(chuàng)建自己的自定義模板。它們不一定是針對(duì)流行框架的,但可能是針對(duì)內(nèi)部或個(gè)人項(xiàng)目的。實(shí)際上,如果我們經(jīng)常要自己創(chuàng)建一個(gè)特定的文件夾目錄結(jié)構(gòu)、引用集或樣板文件,那么可以考慮創(chuàng)建項(xiàng)目或項(xiàng)模板。項(xiàng)目模板只是簡(jiǎn)單的純文本文件,包括 .csproj files (它們不要求生成的模板是特定于 .NET Core 的,并且它們可以用于任何框架。)。創(chuàng)建一個(gè)新的模板是非常容易的,而且維護(hù)起來(lái)也很容易。傳統(tǒng)上,可以執(zhí)行文本替換的模板會(huì)使用一種特殊的語(yǔ)法,比如 VARIABLEVARIABLE 標(biāo)記,該標(biāo)記將在計(jì)算模板時(shí)被替換。不幸的是,這對(duì)于文件類型來(lái)說通常是無(wú)效的語(yǔ)法,文件類型會(huì)使得它無(wú)法通過運(yùn)行項(xiàng)目來(lái)測(cè)試模板的正確性。這會(huì)導(dǎo)致 bug 并減緩迭代時(shí)間,基本上也會(huì)帶來(lái)一些維護(hù)上的麻煩。幸運(yùn)的是,模板引擎的設(shè)計(jì)者已經(jīng)考慮到了這一點(diǎn),并且想出了一個(gè)更好的工作方式:運(yùn)行模板。想法很簡(jiǎn)單:模板就是純文本文件。沒有特殊格式,也沒有特殊標(biāo)記。所以, C# 文件始終是有效的 C#文件。如果某個(gè)模板想要替換某些文本,比如用基于項(xiàng)目名稱的命名空間替換 C# 命名空間,則可以使用簡(jiǎn)單的搜索和替換來(lái)處理。例如,假設(shè)我們有一個(gè)這樣的模板:
模板的 JSON 配置定義了一個(gè)符號(hào)來(lái)替換命名空間。符號(hào)的值是基于項(xiàng)目名稱的,它可能應(yīng)用了內(nèi)置轉(zhuǎn)換來(lái)確保它只包含有效的字符。這個(gè)符號(hào)還將定義它要替換的文本 “RootNamespace”。當(dāng)模板引擎處理每個(gè)文件時(shí),如果它看到 "RootNamespace",會(huì)用符號(hào)值將其替換。這種簡(jiǎn)單的搜索和替換通常是基于符號(hào)的,而符號(hào)又是基于參數(shù)的,例如模板名、輸出名或其他實(shí)際的自定義參數(shù)。但是,也可以基于生成器來(lái)創(chuàng)建符號(hào),如創(chuàng)建 GUID、隨機(jī)數(shù)或當(dāng)前時(shí)間戳等等。但是,如果一個(gè)模版沒有條件代碼(根據(jù)參數(shù)添加或刪除的內(nèi)容),那么它是不完整的。dotnet new 是如何處理這個(gè)問題并將“運(yùn)行模板”作為一個(gè)選項(xiàng)的呢?它實(shí)際上是在每個(gè)文件類型的基礎(chǔ)上處理的,內(nèi)置了一些默認(rèn)配置,并且能夠?yàn)槲粗奈募袷蕉x自己的樣式。本質(zhì)上,其思想是對(duì)那些支持它的文件類型使用特定于文件的預(yù)處理程序(例如,用于 C#或 C++ 的#if),對(duì)那些不支持它的文件類型使用特殊格式的注釋,比如 JSON。 |
public class HomeController : Controller |
|
public IActionResult Index() => View(); |
|
public IActionResult About() |
|
ViewData["Message"] = "Your application description page."; |
|
|
|
|
public IActionResult Contact() |
|
ViewData["Message"] = "Your contact page."; |
|
|
|
|
public IActionResult Error() => View(); |
|
模板的所有元數(shù)據(jù)都位于 template.json 文件中。它包括模板的簡(jiǎn)稱、描述、作者、標(biāo)簽和支持的語(yǔ)言。因?yàn)橐粋€(gè)模板只能針對(duì)一種語(yǔ)言,所以它還包括一個(gè)“組標(biāo)識(shí)”選項(xiàng),當(dāng)有多個(gè)模板時(shí)可以指定該選項(xiàng),為每個(gè)模版指定一種語(yǔ)言。元數(shù)據(jù)文件還可以包括有關(guān)要復(fù)制或重命名文件的源和目標(biāo)、條件文件副本、替換符號(hào)、命令行參數(shù)和創(chuàng)建后的操作(如包還原)等可選信息。但默認(rèn)情況下,模板引擎將復(fù)制并處理模板文件結(jié)構(gòu)中的所有文件。{"author": "Matt Ellis","classifications": [ "Hello world" ],"name": "Hello world template","identity": "Matt.HelloWorldTemplate.CSharp","shortName": "helloworld","guids": [ "d23e3131-49a0-4930-9870-695e3569f8e6" ],"sourceName": "MyTemplate"}
template.json 文件必須放在模板文件夾結(jié)構(gòu)的根目錄下,即位于一個(gè)名為 .template.config 的文件夾中。文件夾目錄結(jié)構(gòu)的其余部分可以完全由我們來(lái)定:模板引擎在評(píng)估模板時(shí)將會(huì)保持相同的目錄結(jié)構(gòu)。換句話說,如果我們將 README.md 文件添加到模板的根目錄中,那么當(dāng)調(diào)用 dotnet new 時(shí),模板引擎將會(huì)在輸出文件夾的根目錄中創(chuàng)建一個(gè) README.md。所以,如果我們使用–output MyApp,將得到一個(gè)名為 MyApp/README.md 的文件。> tree -a.├── .template.config│ └── template.json├── MyTemplate.csproj├── Program.cs└── Properties└── AssemblyInfo.cs
2 個(gè)目錄,4 個(gè)文件
要安裝和測(cè)試模板,只需要調(diào)用 dotnet new --install {template},就像安裝自定義模板一樣,但這一次是將路徑傳遞給模板文件夾結(jié)構(gòu)的根目錄。如果要卸載,使用 dotnet new --uninstall {template}。同樣地,如果我們不確定要傳遞什么參數(shù),可以使用 dotnet new --uninstall 來(lái)獲取完整的列表。分發(fā)模板
一旦我們的模板準(zhǔn)備就緒,可以分發(fā)了,就可以把它打包成一個(gè) NuGet 包并上傳到 nuget.org 上。我們需要像往常一樣創(chuàng)建一個(gè) .nuspec 文件,但是需要做兩個(gè)小的調(diào)整:添加一個(gè) packageType 元素并將 name 屬性設(shè)置為“Template”,然后確保模板文件夾結(jié)構(gòu)被復(fù)制到一個(gè)名為“content”的文件夾中。 |
|
|
|
<id>MattDemo.HelloWorldTemplate</id> |
|
|
|
<authors>Matt Ellis</authors> |
|
<description>Hello World template</description> |
|
|
|
<packageTypename="Template"/> |
|
|
|
|
|
|
|
<filesrc=".template.config/template.json"target="content/.template.config"/> |
|
<filesrc="MyTemplate.csproj"target="content/"/> |
|
<filesrc="Program.cs"target="content/"/> |
|
<filesrc="Properties/*"target="content/Properties/"/> |
|
|
|
|
此外,可以在一個(gè)包中包含多個(gè)模板:只需在“content”下創(chuàng)建多個(gè)文件夾,并為每個(gè)模板添加一個(gè) .tempate.config/template.json 即可。在 template.json 文件中還有更多的選項(xiàng)和功能,但是涵蓋所有選項(xiàng)和功能超出了本文的范圍。不過,基于我們這里討論的這些內(nèi)容,也可以看出模板引擎是非常強(qiáng)大、靈活的,并且它使用起來(lái)相當(dāng)簡(jiǎn)單。請(qǐng)查看微軟文檔網(wǎng)站和 dotnet/templating Github 上的 Wiki 了解更多內(nèi)容。模板引擎
dotnet new 最有趣的一點(diǎn)是,它被設(shè)計(jì)成可以在多個(gè)宿主上使用。dotnet new CLI 工具只是其中的一個(gè)宿主 :模板引擎本身也可以用作其他應(yīng)用程序的 API。對(duì)于那些喜歡使用 IDE 而不是命令行的人來(lái)說,這是非常好的,但是他們?nèi)匀幌M軌蜉p松地添加自定義項(xiàng)目模板,而這在 IDE 中并不總是那么容易。我們可以在 JetBrains Rider 中看到這一點(diǎn)。“新建項(xiàng)目”對(duì)話框由模板引擎 API 來(lái)提供支持,其中列出了所有可用的模板,甚至包括自定義模板。當(dāng)用戶想要?jiǎng)?chuàng)建一個(gè)項(xiàng)目時(shí),模板引擎用于生成文件。如果仔細(xì)觀察,我們會(huì)發(fā)現(xiàn) Rider 的模版比 .NET CLI 的要多。這是因?yàn)?Rider 提供了額外的模板來(lái)支持 .NET 框架和 Xamarin 項(xiàng)目。模板引擎 API 允許宿主將模板安裝到一個(gè)自定義的位置,并且可以從這兩個(gè)位置拿到模板列表,這意味著 Rider 將顯示由 dotnet new --install 安裝的自定義模板,以及使用“新建項(xiàng)目”對(duì)話框的“更多模板”頁(yè)中的安裝按鈕。重新加載后,新模板將與所有其他模板一樣顯示在列表中。輕松創(chuàng)建自定義的新項(xiàng)目
dotnet new 命令使得創(chuàng)建新項(xiàng)目和項(xiàng)目項(xiàng)變得很容易。默認(rèn)模板集將幫助我們快速構(gòu)建基于命令行或 ASP.NET 的 .NET Core 應(yīng)用程序,并且有助于創(chuàng)建測(cè)試項(xiàng)目和其他基于.NET 語(yǔ)言的目標(biāo)項(xiàng)目。我們可以很容易地安裝自定義模板來(lái)創(chuàng)建具有其他要求的項(xiàng)目,例如不同的文件夾結(jié)構(gòu)或框架依賴關(guān)系。自定義模板的格式使我們可以輕松地創(chuàng)建自己的模板,它雖然利用替換變量和條件代碼,但仍能保持模板項(xiàng)目的可編譯性和可維護(hù)性。與 dotnet sln 命令以及其他可擴(kuò)展的 dotnet CLI 命令一起使用,new 模板引擎使創(chuàng)建和管理項(xiàng)目、項(xiàng)目項(xiàng)和解決方案變得更加容易,并且它可以跨平臺(tái)、直接利用命令行進(jìn)行創(chuàng)建。本文于網(wǎng)絡(luò)整理,版權(quán)歸原作者所有,如來(lái)源信息有誤或侵犯權(quán)益,請(qǐng)聯(lián)系我們刪除或授權(quán)事宜。