因爲如今開發中,前端能夠負責的範圍愈來愈大,早已不是僅限制於網頁。像 App、小程序、甚至桌面應用,均可以使用前端技術來開發。css
因此原來經過前端寫 Demo,後端套數據的模式早就沒法支持現代多元化前端開發。html
前端工程化就是在這個背景下成爲了受人重視的技術,而且是目前前端開發必備的技術之一。前端
技術是爲了解決問題而存在的,前端工程化解決了不少問題。好比 ES6 新語法、Less/Sass/PostCss 等 CSS 工具和模塊化。它們都是沒法被運行環境直接支持的。可是使用前端工程化之後,就能夠將它們轉換爲運行環境支持的代碼。vue
還有一些部署項目時所須要作的操做,好比壓縮代碼及資源文件,將代碼上傳至服務器等。若是讓人來手動處理這些任務,會很容易出現操做失誤。這種狀況下,讓機器自動來完成這些任務更爲合理。node
多人協做時,代碼風格、代碼質量都須要獲得保證。react
開發過程當中須要等待後端接口提早完成。webpack
這些都是前端工程化負責的範疇。git
總結起來,前端工程化解決的問題主要有 6 個部分。web
傳統語言或語法的弊端。(ES6+語法、TypeScript、Sass)vue-cli
沒法使用模塊化/組件化(ES Modules、Components)
重複的機械式工做(Build、Publish)
代碼風格統1、質量保證(git、ESLint)
依賴後端服務接口支持(Mock)
總體依賴後端項目(DevServer)
一切以提升效率、下降成本、質量保證爲目的的手段都屬於工程化。
一切重複的工做都應該被自動化。
一個常規前端項目的總體環節大約有 5 步:
建立項目-編碼-預覽/測試-提交-部署
1.建立項目階段:
藉助腳手架工具自動建立項目目錄結構和特定類型的文件。
2.編碼階段:
藉助工具實現代碼格式化、校驗代碼風格,還可使用新的特性來編寫源代碼,經過編譯/構建/打包生成運行環境支持的運行時代碼。
3.預覽/測試階段
開發過程,須要 Mock 服務,須要使用基礎的 Web 服務器來託管項目,好比 Nginx、Apatch 等。但這些服務都不提供熱更新的體驗,因此須要藉助現代化的工具來提升開發體驗。
在編譯轉換後,若是發生異常,須要定位源代碼的位置。這是就須要使用 Source Map 技術來映射運行代碼和源代碼。
4.代碼提交階段
使用 git hooks 對項目總體進行檢查,包括代碼風格的檢查和項目質量的檢查,確保不會將有問題的代碼提交到 git 倉庫中,這樣 git 倉庫的代碼永遠都是可用的。除此之外,還能夠對 git log 的格式進行限制,這樣在代碼分支合併和回滾時有很大的價值。
主要藉助的工具備 Lint-staged 和持續集成等。
5.部署階段
CI/CD、自動發佈。
能夠經過一行命令自動的將項目打包上傳至 ftp 服務器。
如今有些工具很是強大,像 Webpack,以致於不少人認爲前端工程化就是指 webpack。
實際上,工具不是工程化的核心,工程化的核心是對項目的總體規劃和架構。工具只是實現這種規劃和架構的一種手段。
一個項目的工程化,首先要規劃出一個項目總體的工做流架構。
如文件組織結構(按做用分層、按業務分層)、代碼開發範式(語法、規範、標準、語言,如 ES6+、TypeScript 等)、先後端分離方式(Ajax 和中間層)。
規劃完工做流總體架構之後,再來考慮使用哪些工具來實現這套規劃。
能夠借鑑目前業界成熟的工程化方案中的思路,如 create-react-app、vue-cli、angluar-cli、gatsby-cli 等。
它們並非一個簡單的項目生成腳手架,而是工程化工具的集成。
目前前端的工程化很大程度上都歸功於 Node.js。
Node.js 是繼 Ajax 後有一次顛覆式的前端工業革命,沒有 Node.js 就沒有今天的前端。
目前絕大多數工程化工具都是由 Node.js 開發的,因此前端工程化是由 Node.js 強力驅動的。
總之,工程化是爲了解決問題而存在的。
腳手架工具是前端工程化的發起者,它是自動幫助咱們建立項目基礎結構、提供項目規範和約定的工具。
在開發相同類型的項目時,會有不少相同的東西,包括:
組織結構
開發範式
模塊依賴
工具配置
基礎代碼
...
這些相同的東西不須要人爲建立,可使用腳手架工具快速建立項目骨架。而後基於這個骨架來開發。
好比使用大型的 IDE,如 IDEA 和 Visual Studio 等來建立項目的過程就是一個腳手架的工做流程。
前端的腳手架和服務端項目、移動端項目不太同樣。由於技術選型過於多樣化,因此不會集中在某一個特定的 IDE 中,目前流行的作法是在命令行中來完成項目的建立。
React 項目: create-react-app
Vue 項目: vue-cli
Angular 項目: angular-cli
它們根據用戶選擇的信息建立對應的項目基礎結構,不過它們只能服務於自己的框架,並不通用。
另外一類是通用型腳手架工具,表明像 Yeoman。優勢是靈活易擴展。
還有一類是在項目開發過程當中會使用到的,好比 Plop。它能夠建立某種特定類型的文件。好比某個模塊或是某個組件的基礎代碼。
yeoman 是最老牌、最強大、最通用的腳手架工具之一,基於 node.js 開發。
不一樣於常規的腳手架工具,yeoman 更像是一個腳手架的運行平臺。
咱們能夠本身定義 Generator 來建立屬於本身的項目腳手架。
Yeoman 的缺點就是它的優勢,由於不夠專一,因此在專一於某項框架的腳手架建立過程當中不如 vue-cli 這類工具。
npm i -g yo
複製代碼
安裝完 yo 以後,能夠查看版本號來確認安裝是否成功。
yo --version
複製代碼
只安裝 yo 是不夠的,它只是一個 Generator 運行模塊,還須要安裝特定的 Generator。
好比一個 node 項目,就須要安裝generator-node
。
npm i -g generator-node
複製代碼
安裝完成以後,就能夠建立 node 項目了。
命令是 yo 加上 generator 的名字。(node 包名去掉 generator-)
yo node
複製代碼
在項目建立過程當中,yeoman 會提問一些問題,經過用戶提交的答案來建立不一樣的項目結構。
每一個 generator 均可以提供 sub generator,用於生成特定的文件或者基礎代碼。但也不是每個 generator 都提供了 sub generator,具體能夠去 generator 的官方文檔查看。
運行 sub generator 也比較簡單,就是 yo 加上 generator 的名字,再加上冒號(:),最後加上 sub generator 的名字。
好比在 node.js 項目中建立一個命令。
yo node:cli
複製代碼
大體上分爲 6 步。
由於官方的 generator 在某些場景下做用會很是侷限,因此咱們有必要自定義本身的 generator。
generator 本質上就是一個 npm 模塊,只須要符合特定的結構便可。
generator 的名字必須是 generator-<name>
的格式。
generator 須要有一個生成器的基類。
總體的的操做步驟以下。
建立項目根目錄。
mkdir generator-sample
複製代碼
初始化項目。
npm init -y
複製代碼
安裝 yeoman-generator。
npm i yeoman-generator
複製代碼
按照項目結構要求,建立特定的文件夾。
入口文件是 generators/app/index.js
。
這個文件須要導出一個繼承自 Yeoman Generator 的 class。Yeoman Generator 在工做時會自動調用這個 class 中的生命週期函數。
在這個導出的類中,能夠調用父類提供的工具方法實現一些具體的功能。
好比在 writing 生命週期函數中能夠建立一些文件。
const Generator = require("yeoman-generator");
module.exports = class extends Generator { writing() { // 模版文件路徑 const tmpl = this.templatePath("foo.txt"); // 輸出目標路徑 const output = this.destinationPath("foo.txt"); // 模板數據上下文 const context = { title: "Hello", success: false }; this.fs.copyTpl(tmpl, output, context); } }; 複製代碼
操做文件可使用父類中提供的 this.fs 模塊下的方法。這個 fs 和 nodejs 原生的 fs 不同,是高度封裝過的,因此功能更增強大。
若是一個項目所須要的文件過多,挨個文件建立會很麻煩,這時候使用模板的方式來建立文件更加方便。
在 generators/app 目錄下建立 templates 文件夾,該文件夾下的文件都是模板文件。能夠被 this.templatePath 方法獲取到。
模板文件徹底遵循 EJS 語法,即尖括號百分號的寫法。
<%= name %>
複製代碼
EJS 模板官網:https://ejs.co/
最終使用 this.fs.copyTpl 方法將模板拷貝到目標目錄下。
相對於手動建立每一個文件,模板的方式大大提升了效率。
模版中的動態數據須要用戶來輸入,通常會在命令行中發起詢問。
promting 生命週期函數中能夠調用父類的 prompt 方法來發起詢問,這個詢問返回的是一個異步函數。
const Generator = require("yeoman-generator");
module.exports = class extends Generator { prompting() { // prompt 的參數是一個數組,能夠發起多個問題 // 每一個問題都是以一個對象的形式體現 // type 是問題的類型,input 表明須要用戶來輸入,除此之外還有單選、多選等。 // name 是問題的 key // message 是問題的提示 // default 是用戶不作輸入時的默認值 return this.prompt([ { type: "input", name: "name", message: "Your project name", default: this.appname, }, ]).then((answers) => { // answers 會以一個對象的形式返回 // { name: 'user input value' } // 將這個對象掛載到 this.answers this.answers = answers; }); } writing() { // 模版文件路徑 const tmpl = this.templatePath("foo.txt"); // 輸出目標路徑 const output = this.destinationPath("foo.txt"); // 將上下文對象替換爲用戶輸入對象 const context = this.answers; this.fs.copyTpl(tmpl, output, context); } }; 複製代碼
首先能夠經過 vue-cli 生成一個基礎的 vue 項目骨架,而後將這個骨架做爲基礎模板來使用。須要被變量替換的地方都使用 EJS 的語法替換掉。
在 writing 生命週期函數中經過遍歷的方式將模板中全部文件都調用一遍 this.fs.copyTpl。
具體步驟就不演示了。
generator 的發佈流程和 npm 普通的包是同樣的,經過 npm publish
命令去發佈。
若是想要被 yeoman 官方收錄,那麼就須要在關鍵詞中添加 yeoman-generator。
除了 yeoman 這類大型的腳手架工具外,還有不少小而美的腳手架工具,好比 Plop。
Plop 經常使用於在項目開發過程當中建立某類類型的文件,優勢相似於 Yeoman 中 sub generator 的概念。
好比在 React 項目的開發過程當中,開發一個組件須要建立 1 個目錄,該目錄下須要 3 個文件。
. |____Footer | |____Footer.tsx | |____Footer.scss | |____Footer.test.ts 複製代碼
若是咱們手動的建立這些文件,並編寫通用的代碼,過程會很是繁瑣,因此可使用 Plop 來實現這個功能。
首先將 plop 添加到項目中。
npm install --save-dev plop
複製代碼
在項目根目錄下建立 plopfile.js 文件,它是 plop 工做所須要的入口文件。
一個簡單的 plopfile.js 配置以下:
// 導出一個函數
// 該函數須要接收一個 plop 對象參數 module.exports = (plop) => { // 調用 setGenerator 方法 // 第一個參數是命令名,第二個參數是配置對象 plop.setGenerator("component", { description: "create a component", // 說明 // 詢問 prompts: [ { type: "input", name: "name", message: "component name", default: "MyComponent", }, ], // 動做 actions: [ { type: "add", // 添加文件 // 使用 {{ }} 語法拿到上面詢問的值 path: "src/components/{{name}}/{{name}}.tsx", // 模板的文件路徑 templateFile: "plop-templates/component.hbs", }, // 添加 scss 文件和 test 文件 { type: "add", // 添加文件 // 使用 {{ }} 語法拿到上面詢問的值 path: "src/components/{{name}}/{{name}}.scss", // 模板的文件路徑 templateFile: "plop-templates/component.scss.hbs", }, { type: "add", // 添加文件 // 使用 {{ }} 語法拿到上面詢問的值 path: "src/components/{{name}}/{{name}}.test.ts", // 模板的文件路徑 templateFile: "plop-templates/component.test.ts.hbs", }, ], }); }; 複製代碼
上面這種 hbs 文件遵循 handlebars 的語法,具體參考:https://handlebarsjs.com/
模板通常存放在根目錄下的 plop-templates 文件夾下。
最後就能夠經過命令運行 plop 腳本。
npx plop component
複製代碼
總結一下,plop 的使用步驟共有 5 步。
腳手架的工做原理相對簡單,大體上都是在啓動時詢問用戶一些問題,經過獲得的答案配合模板生成對應的文件。
開發腳手架的思路:
經過命令行交互詢問客戶問題。
根據用戶回答的結果生成文件。
建立腳手架項目文件夾。
mkdir sample-scaffolding
複製代碼
初始化項目。
npm init -y
複製代碼
在 package.json 中添加 bin 字段,定義 cli 的入口文件。
{
"bin": "cli.js" } 複製代碼
建立 cli.js。
touch cli.js
複製代碼
由於須要在控制檯對用戶發起詢問,因此要藉助一個 nodejs 的庫,inquirer。
npm i --save-dev inquirer
複製代碼
除此以外,還須要藉助模板引擎來給模板文件注入變量,因此須要藉助 ejs。
npm i ejs
複製代碼
如今能夠在 cli.js 中編寫邏輯。
#!/usr/bin/env node
const fs = require("fs"); const path = require("path"); const inquirer = require("inquirer"); const ejs = require("ejs"); // 執行命令後,直接發起詢問。 inquirer .prompt([ { type: "input", name: "name", message: "Project name?", }, ]) .then( // 返回值 anwsers 是一個對象 (anwsers) => { // 模板目錄 const tmplDir = path.join(__dirname, "templates"); // 目標目錄 const destDir = process.cwd(); // 將模板目錄下的全部模板文件經過模板引擎轉換後複製到目標目錄中 fs.readdir(tmplDir, (err, files) => { if (err) throw err; // 遍歷全部模板文件 files.forEach((file) => { // 經過 ejs 渲染文件 ejs.renderFile(path.join(tmplDir, file), anwsers, (err, result) => { if (err) throw err; // 將 ejs 渲染後的文件寫入目標文件中 fs.writeFileSync(path.join(destDir, file), result); }); }); }); } ); 複製代碼
第一行的 #!/usr/bin/env node
是告訴系統,該文件經過 node 來執行。
接下來建立模板文件夾。
mkdir templates
複製代碼
建立模板文件 index.html 和 style.css。
index.html 內容:
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title><%= name %></title> <link rel="stylesheet" href="./style.css" /> </head> <body> hello, world! </body> </html> 複製代碼
style.css 內容:
body {
background-color: #ddffdd; color: #060606; } 複製代碼
將命令連接到全局。
npm link
複製代碼
到這裏,簡單的腳手架 demo 就開發完成了。
如今開始測試,到其餘目錄建立一個項目目錄。
mkdir test-project
複製代碼
進入該目錄,執行腳手架命令。
sample-scaffolding
複製代碼
此時命令行會出現詢問,輸入項目名,就能夠看到生成後的文件了。
? Project name? hello
複製代碼
腳手架工具是前端工程化的第一塊內容,到此講解結束,接下來會發布另外一篇文章,關於前端工程化第二塊的內容,也是前端工程化的核心部分,自動化構建。敬請期待。
- END -