為何工程化
在我們的實際開發(fā)中刀荒,我們想用最新的es語法代嗤,想用less,sass等樣式預處理。我們想要使用模塊化的方式提高項目的可維護性缠借,但是運行環(huán)境卻不支持干毅。多人協(xié)作,代碼風格不統(tǒng)一泼返。發(fā)布上線需要手動壓縮與上傳硝逢,這些問題的出現(xiàn)需要我們去有一種方式去解決這些問題。工程化就是解決這些問題的一個體現(xiàn)绅喉。
工程化的體現(xiàn)
一切以提高效率渠鸽,降低成本,質量保證為目的的手段都屬于工程化柴罐。
一些成熟的工程化集成 如vue-cli , angular-cli , create-react-app
前端工程化的實現(xiàn) nodejs有巨大貢獻徽缚。
腳手架工具yeoman的使用
1.全局安裝yo
cnpm i yo -g //或者 yarn global add yo
2.安裝對應的generator
npm i generator-node -g //或者 yarn global add generator-node
3.通過yo運行generator
yo node
yeoman的sub generator使用
1.明確需求。
2.找到合適的Generator革屠。
3.全局范圍安裝找到的Generator凿试。
4.通過yo安裝找到的Generator。
5.通過命令交互填寫選項似芝。
6.生成所需要的項目結構那婉。
自定義Generator
基于yeoman搭建自己的腳手架
1.創(chuàng)建Generator模塊(generator本質上就是一個NPM模塊)
yeoman的generator模塊名稱必須是 generator-<name>
mkdir generator-sample //創(chuàng)建文件
cd generator-sample
yarn init //初始化
yarn add yeoman-generator //安裝基類 提供了一些方法
按照格式創(chuàng)建
//作為generator核心入口
//需要導出一個繼承自 yeoman generator的類型
//yeoman generator 在工作時會自動調用我們在此類型中定義的一些聲明周期方法
//在這些方法中可以通過調用父類提供的一些工具方法實現(xiàn)一些功能 比如文件寫入
const Generator = require('yeoman-generator')
module.exports = class extends Generator {
writing() {
//yoman 自動在生成文件階段調用此方法
//我們在這里嘗試往項目目錄中寫入文件
// this.fs.write(
// this.destinationPath('test.txt'),
// Math.random().toString()
// )
//通過模板方式寫入文件到目標目錄
//三個參數(shù) 1.模板文件路徑 2.輸出文件路徑 3.模板數(shù)據(jù)上下文
const tmpl = this.templatePath('foo.txt')//模板文件路徑
const output = this.destinationPath('foo.txt')//模板文件路徑
const context = { title: 'hello tem', success: true }//模板數(shù)據(jù)上下文
this.fs.copyTpl(tmpl, output, context)
}
}
通過yarn link鏈接到全局范圍 使其成為一個全局模塊包
yarn link
在其它地方就可以運行這個生成器
yo sample //剛才起的生成器名稱
創(chuàng)建一個自己的vue Generator
mkdir generator-my-vue
cd generator-my-vue
yarn init
yarn add yeoman-generator
code . //vscode 打開文件
1.還是先創(chuàng)建模板文件
2.寫模板代碼
const Generator = require('yeoman-generator')
module.exports = class extends Generator{
prompting(){
return this.prompt([
{
type:'input',
name:'name',
message:'your project name',
default:this.appname
}
])
.then(answers => {
this.answers = answers
})
}
writing(){
}
}
3.將項目結構放入template文件夾中
4.將項目結構可能發(fā)生變化的地方用模板語法替換 如:
- yarn link 到全局
6.使用 yo my-vue 安裝到對應目錄
發(fā)布generator
//先創(chuàng)建一個本地的git倉庫
//先創(chuàng)建一個gitignore
echo node_modules > .gitignore
//初始化一個本地空倉庫
git init
//查看本地倉庫狀態(tài)
git status
git add .
//創(chuàng)建一次提交
git commit -m "initial commit"
//提交到遠端倉庫
//創(chuàng)建一個遠端新倉庫 gitee或者github
git remote add origin https://gitee.com/wkpkko/generator-myvue.git
//這樣為本地倉庫添加了一個遠端倉庫的別名 push的時候可以使用這個別名
git push -u origin master //推送到遠端倉庫
//通過npm publish 發(fā)布這個模塊 yarn publish
yarn publish
//在使用淘寶鏡像發(fā)布時會出現(xiàn)問題
//發(fā)布的時候設置鏡像
yarn publish --registry=https://registry.yarnpkg.com
//自動推送到y(tǒng)arn的官方鏡像 yarn的鏡像與npm鏡像時同步的 此時模塊發(fā)布成功了
//在npm官網(wǎng) npmjs.com/package/generator-myvue
//此時模塊已經(jīng)被推送上來了
Plop一個小而美的腳手架工具
主要用于創(chuàng)建項目中特定類型文件的一個小工具,類似與yeoman中的subgeneraor,一般不會單獨使用国觉。一般會把plop集成在項目中吧恃,用來自動化創(chuàng)建同類型的項目文件
yarn add plop --dev
在項目根目錄下創(chuàng)建plopfile.js
// Plop 入口文件,需要導出一個函數(shù)
// 此函數(shù)接收一個 plop 對象麻诀,用于創(chuàng)建生成器任務
module.exports = plop => {
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}}.js',
templateFile: 'plop-templates/component.hbs'
},
{
type: 'add', // 代表添加文件
path: 'src/components/{{name}}/{{name}}.css',
templateFile: 'plop-templates/component.css.hbs'
},
{
type: 'add', // 代表添加文件
path: 'src/components/{{name}}/{{name}}.test.js',
templateFile: 'plop-templates/component.test.hbs'
}
]
})
}
創(chuàng)建模板文件
plop-templates/component.hbs
import React from 'react';
export default () => (
<div className="{{name}}">
<h1>{{name}} Component</h1>
</div>
)
component.css.hbs
.{{name}} {
}
component.test.hbs
import React from 'react';
import ReactDOM from 'react-dom';
import {{name}} from './{{name}}';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<{{name}} />, div);
ReactDOM.unmountComponentAtNode(div);
});
yarn plop component (剛才定義的生成器的名稱)
腳手架工作原理
大部分腳手架都是設置一些問題痕寓,通過這些問題為你創(chuàng)建一些模板的項目結構。
腳手架工具就是一個nodecli應用蝇闭。
通過nodejs開發(fā)一個小型的腳手架工具
mkdir sample-cli
cd sample-cli
yarn init//初始化一個package.json文件
//package.json中添加bin字段作為cli的入口文件
{
"name": "sample-cli",
"version": "1.0.0",
"description": "sample cli",
"bin":"cli.js",
"main": "index.js",
"license": "MIT"
}
創(chuàng)建cli.js
cli文件必須要有一個特定的文件頭
#!/usr/bin/env node
// Node CLI 應用入口文件必須要有這樣的文件頭
// 如果是 Linux 或者 macOS 系統(tǒng)下還需要修改此文件的讀寫權限為 755
// 具體就是通過 chmod 755 cli.js 實現(xiàn)修改
console.log('sample cli')
通過yarn link 鏈接到全局
此時就可以使用sample-cli命令執(zhí)行
// 腳手架的工作過程:
// 1. 通過命令行交互詢問用戶問題
// 2. 根據(jù)用戶回答的結果生成文件
node通過用戶詢問 我們使用inquirer模塊
yarn add inquirer
此時我們可以在cli.js載入它
const inquirer = require('inquirer')
inquirer.prompt([
{
type: 'input',
name: 'name',
message: 'Project name?'
}
]).then(answer => {
console.log(answer)
})
創(chuàng)建模板文件
#!/usr/bin/env node
// Node CLI 應用入口文件必須要有這樣的文件頭
// 如果是 Linux 或者 macOS 系統(tǒng)下還需要修改此文件的讀寫權限為 755
// 具體就是通過 chmod 755 cli.js 實現(xiàn)修改
//腳手架的工作過程
//1.通過命令行交互詢問用戶問題
//2.根據(jù)用戶回答的結果生成文件
const path = require('path')
const fs = require('fs')
const inquirer = require('inquirer')
inquirer.prompt([
{
type: 'input',
name: 'name',
message: 'Project name?'
}
]).then(answer => {
//根據(jù)用戶回答結果生成文件
//模板目錄
const templDir = path.join(__dirname,'templates')
//輸出目錄 命令行執(zhí)行路徑
const destDir = process.cwd()
//將模板下的文件全部轉換到目錄 fs的readdir方法會自動掃描目錄下所有文件
fs.readdir(templDir,(err,files) => { //files拿到所有文件列表
if(err) throw err
files.forEach(file => {
// console.log(file) //每個file就是相對于templates下的相對路徑
//可以通過模板引擎去渲染這個路徑所對應的文件
})
})
})
先安裝對應的模板引擎 (安裝一個ejs模板引擎)
yarn add ejs
之后引入模板引擎
#!/usr/bin/env node
// Node CLI 應用入口文件必須要有這樣的文件頭
// 如果是 Linux 或者 macOS 系統(tǒng)下還需要修改此文件的讀寫權限為 755
// 具體就是通過 chmod 755 cli.js 實現(xiàn)修改
//腳手架的工作過程
//1.通過命令行交互詢問用戶問題
//2.根據(jù)用戶回答的結果生成文件
const path = require('path')
const fs = require('fs')
const inquirer = require('inquirer')
const ejs = require('ejs')
inquirer.prompt([
{
type: 'input',
name: 'name',
message: 'Project name?'
}
]).then(answer => {
//根據(jù)用戶回答結果生成文件
//模板目錄
const templDir = path.join(__dirname, 'templates')
//輸出目錄 命令行執(zhí)行路徑
const destDir = process.cwd()
//將模板下的文件全部轉換到目錄 fs的readdir方法會自動掃描目錄下所有文件
fs.readdir(templDir, (err, files) => { //files拿到所有文件列表
if (err) throw err
files.forEach(file => {
// console.log(file) //每個file就是相對于templates下的相對路徑
//可以通過模板引擎去渲染這個路徑所對應的文件
//renderFile三個參數(shù) 1.文件的絕對路徑 2.工作時的數(shù)據(jù)上下文answer 3.回調函數(shù)
ejs.renderFile(path.join(templDir, file), answer, (err, result) => {
if (err) throw err
//通過文件寫入寫入到目標目錄
fs.writeFileSync(path.join(destDir, file), result)
})
})
})
})
資料來源:拉勾教育-前端訓練營