第一章: 概述
前端工程化: 提高效率, 降低成本, 保證品質
解決的問題:顔色代表不同問題分類
使用es6 相容, lass sass 等無法直接運作, 子產品化方式無法直接運作, 手動重複性動作(部署/壓縮), 團隊風格統一,github 拉的代碼品質不可保證。 開發/整體依賴後端接口。
涉及領域:(對照下圖)
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL1kERPNTV65UNVpHW3BjMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zROBlL1ETN2UzNxATM4EDOwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
第二章:腳手架(scaffolding) Yeoman
本質: 建立項目基礎結構,規範和約定
Yeoman + generators => 各種腳手架
Yoeman 平A 技能
yeoman 使用 node-generator 建立 node modules 流程
Guohais-MBP:~ guohaiqu$ node -v |v16.0.0 |v10.16.2
Guohais-MBP:~ guohaiqu$ npm -v |7.19.0 |6.9.0
Guohais-MBP:~ guohaiqu$ yarn -v |1.22.10 |1.17.3
Guohais-MBP:~ guohaiqu$ yarn global add yo |全局安裝 yoeman
Guohais-MBP:~ guohaiqu$ yarn global add generator-node |全局安裝 generator-node
|進入檔案|
Guohais-MBP:my_module guohaiqu$ yo node
|回答一些問題| => 安裝
Yeoman Sub Generator (子生成器)
Guohais-MBP:my_module guohaiqu$ yo node:cli
Overwrite package.json yes
報錯: premisson denied
sudo chmod -R 755 檔案目錄(my_module)
sudo chmod -R 755 檔案目錄(my_module/lib)
yarn unlink
yarn link
使用 yoeman + generator-webapp
yarn global add generator-wbapp
yo webapp
自定義Genertator = 建立npm 子產品
1. 檔案結構 固定(如下圖,是否包含子生成器)
2. 檔案名 固定 generator-filename
|| terminal
mkdir generator-sample // 建立生成器子產品的目錄
cd generator-sample // 進入檔案夾
yarn init // 建立 package.json
yarn add yeoman-generator // 安裝 子產品 yeoman-generator = 提供生成器 基類
|| vs code
generators/app/index.js // 建立 index.js(generator核心入口)
||index.js
具體功能
const Generator = require('yeoman-generator')
module.exports = class extends Generator {
writing() {
this.fs.write(
this.destinationPath('temp.txt'),
Math.random().toString()
)
}
}
Guohais-MacBook-Pro:generator-sample guohaiqu$ yarn link
cd ..
mkdir my-test
cd my-test
yo sample
添加模版檔案
app/templates/foo.txt
// 模版檔案建立
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)
}
// 指令行互動方法
prompting () {
return this.prompt([
{
type: "input",
name: "title",
message: "your project name", // 提示
default: this.appname // 項目目錄名
},
{
type: "input",
name: "gg",
message: "second", // 提示
default: "gg"
}
])
.then (answers =>{
this.answers = answers
console.log(this.answers) //{ title: 'ggg', gg: 'yyy' }
})
}
// in foo.txt : eg: <%= title %><%= gg %>
批量添加模版檔案
const Generator = require('yeoman-generator')
module.exports = class extends Generator {
prompting() {
return this.prompt([
{
type:"input",
name:"project_name",
message:"enter project name",
default: this.appname
}
])
.then(answers => {
this.answers = answers
})
}
writing() {
const templates = [
'public/first.html',
'public/first.css',
'src/second.html',
'src/second.css'
]
templates.forEach(item => {
this.fs.copyTpl(
this.templatePath(item),
this.destinationPath(item),
this.answers
)
});
}
}
.gitignore 忽略 node_modules
第三章:腳手架 Plop
是: 配合項目使用的小型腳手架
執行個體: react + plop 在react 中使用 plop 建立檔案
1. 作為項目以來安裝 plop | yarn add plop --dev |
2. 項目根下 :建立 plopfile.js , plop入口檔案, 定義腳手架
3. 編寫模版 hbs | plop-teamplates > filename.filetype.hbs ( handle bar)
4. plop cli 運作 腳手架 |yarn plop component/生成器名|
plopfile.js
plopfile.js :導出一個函數 ,| module.exports = () => {} |
其接收一個plop 對象(形式參數), 用來建立 generator | module.exports = plop => {} |
plop 對象 : > (成員) plop.setGenerator (參數1: 生成器名string , 參數2: 配置選項object)
配置選項 : description「string」 | prompts「array > object」 |actions 「array > object」
npx create-react-app my-app
yarn add plop --dev
module.exports = plop => {
plop.setGenerator ('component', {
description : "create component",
prompts: [
{
type: "input",
name: "name",
message: "component name",
default: "myComponent"
}
],
actions: [
{
type: "add",
path: "src/components/{{name}}/{{name}}.js",
templateFile: "plop-templates/component.js.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.js.hbs"
},
]
})
}
第四章:scaffolding 原理
建立項目 - 添加package.json 檔案 - 添加"bin": "cli.js" 字段(指定cli 應用入口檔案)
cli應用: 必備的兩個條件 如下
#!/usr/bin/env node |必須添加檔案頭|
Guohais-MacBook-Pro:scaffolding-logic guohaiqu$ chmod 755 cli.js
「mac linux 修改 755 權限」
調用:
yarn link
scaffolding-logic(檔案名)
step 1 : scaffolding logic - 詢問資訊
yarn add inquirer
const inquirer = require("inquirer")
inquirer.prompt([
{
type:"input",
name: "name",
message:"project name"
}
])
.then(anwsers =>{
console.log(anwsers)
})
// in console: scaffloding-logic (FILENAME)
step 2 : scaffolding logic - 添加檔案(讀取 + 寫入)
const path = require("path") // 讀取路徑
const fs = require("fs") // 讀取檔案
const ejs = require("ejs") // 渲染檔案
const tmpDir = path.join(__dirname, 'templates')
const distDir = process.cwd() // node process cwd 擷取目前工作環境
fs.readdir(tmpDir,(err, files)=>{
if(err) throw err
files.forEach(file => {
ejs.renderFile(path.join(tmpDir, file), anwsers,(err, result)=>{
if(err) throw new err
fs.writeFileSync(path.join(distDir,file),result) //絕對路徑,檔案内容
})
})
})
第五章:自動化建構
一句話概述:自動轉換代碼| 如:scss - css
npm scripts 可自動發現 node_modules 下的指令, 在 bin 下面
yarn add sass --dev
//without npm scripts
./node_modules/.bin/sass scss/main.scss css/style.css
// with npm scripts
scripts:{
// NPM SCRIPTS : 自動找到node_modules\bin
build: "sass scss/main.scss css/style.css"
}
npm run build
測試伺服器子產品
yarn add browser-sync --dev
ATTENTION!
<head>
<style media="screen" type="text/css">
@import "css/style.css";
</style>
</head>
開啟伺服器前 需要先編譯scss
方式1 : hook
"scripts": {
"build": "sass scss/main.scss css/style.css",
"preserve":"yarn build",
"serve":"browser-sync ."
},
方式2 : npm-run-all 子產品
yarn add npm-run-all --dev
"scripts": {
"build": "sass scss/main.scss css/style.css --watch",
"serve": "browser-sync . --files \"css/*.css\"",
"start": "run-p build serve".
},
yarn start
解釋:
scripts: { | 自動查找 bin 下的指令
--watch | 監聽scss的檔案變化, 改變就轉譯
--files \"css/*.css\" | 監聽檔案變化,改變就重新整理頁面
-- run-p | 同時運作多個指令
}
話題: 對比grunt gulp fis
grunt :速度慢|磁盤讀寫|生态完善|急于臨時檔案 | 微核心|靈活
gulp :速度相對塊 |記憶體處理|預設多任務同時執行|易懂 |生态完善 |微核心|靈活
fis :捆綁套餐 |适合新手 |大而全 |适合初學者
第七章: Grunt
mkdir grunt | 建立檔案目錄
cd grunt | 進入項目
yarn init | 初始化package.json
yarn add grunt | 安裝 grunt 子產品
touch gruntfile.js | 建立 grunt 入口檔案
gruntfile.js 入口檔案 說明
1. gruntfile.js :導出一個函數 ,| module.exports = () => {} |
2. 其接收一個grunt 形式參數 | module.exports = grunt => {} |
3. grunt : > (成員) grunt.registerTask (參數1: 任務名 ,參數2(可選):任務描述stringm 參數3: 函數)
4. grunt.registerTask(‘default’,【“任務1”,“任務2”】)
備注: grunt 預設同步模式
備注:grunt 任務名 |執行特定任務
備注:grunt |執行預設任務
const { registerTask } = require("grunt") |導入grunt
module.exports = grunt => {
grunt.registerTask('foo','its a foo' , ()=>{ |同步任務
console.log('gg')
return false |同步任務标記失敗
})
grunt.registerTask('bar', function(){ |異步任務
const done = this.async()
setTimeout(()=>{
console.log("2s delay")
done()
done(false) |異步任務标記失敗
}, 1000)
})
grunt.registerTask('default', ['foo','bar']) |預設任務
}
// yarn grunt <function-name> | --force
多目标任務 和 配置選項
grunt.registerMutiTask ('參數1:任務名 ', 參數2: 函數 )
grunt.initConfig ({
任務名:{
options:{
foo: 'bar'
}
目标名1 : {
options: {
foo:'baz' // 覆寫
}
}
目标名2 : ‘目标2 執行的 任務’
}
})
${this.target} | 擷取目标名
${this.data} |擷取值的名
grunt.initConfig({
build: {
options: {
foo: 'baz' |為build 添加配置選項
},
js: {
options:{
foo:'bar' | 覆寫外圈的foo
},
val: 'something'
},
css: '123'
}
})
grunt.registerMultiTask('build', function(){
console.log(this.options()) |this.options是一個函數,調用他得到配置選項
console.log(this.data)
})
yarn grunt build
Running "build:js" (build) task
{ foo: 'bar' }
{ options: { foo: 'bar' }, val: 'gg' }
Running "build:css" (build) task
{ foo: 'baz' }
123
Grunt 插件
1. 安裝插件 |2. 導入插件 |3. 配置選項
//1. 安裝
yarn add grunt-contrib-clean
2. 導入
grunt.loadNpmTasks('grunt-contrib-clean')
3. 配置選項
grunt.initConfig({
clean: {
temp:'temp'
}
})
4. 使用
yarn grunt clean
yarn add grunt-sass sass --dev |grunt-sass 需要依賴sass 來完成
const sass = require('sass') |導入sass 子產品
grunt.initConfig({
sass:{
options: {
sourceMap: true,
implementation: sass
},
main:{
files:{
'dist/css/main.css':'src/scss/main.scss'
}
}
}
})
grunt.loadNpmTasks('grunt-sass') |導入grunt-sass 任務
yarn add grunt-babel @babel/core @babel/preset-env --dev
grunt.initConfig {
main: {
options:{
sourceMap: true,
presets: ['@babel/preset-env'] |最新 ECMA 特性
},
files:{
'dist/js/app.js':'src/js/app.js'
}
}
}
grunt-contrib-watch --dev
grunt.initConfig({
watch:{
js:{
files: ['src/js/*js'], | 監視 js 檔案
tasks: ['babel'] | 執行 babel 任務
}
}
})
grunt.registerTasks('default' ['babel', 'watch']) | 開始時先編譯一下再監視
yarn add load-grunt-tasks --dev
const loadGruntTasks = require ('load-grunt-tasks')
loadGruntTasks(grunt)
第八章 - Gulp
預備: 安裝 glup --dev| 2. gulpfile.js |gulp commands (yarn gulp <任務名>)
yarn init --yes
yarn add gulp --dev
touch gulpfile.js
導出任務
exports.foo = done =>{ // 傳入異步任務
console.log('foo')
done() // 标記異步任務任務完成
}
exports.default = done =>{ // 預設任務
console.log('default')
done() // 标記異步任務任務完成
}
const gulp = require ('gulp') // 以前的方法
gulp.task('bar',done=>{
console.log('working')
done()
})
series + parallel 任務
const {series, parallel} = require('gulp') |導入順序和并行執行
注: 未被導出的任務被視為私有
const task1 = done=>{
setTimeout(() => {
console.log('task1')
done()
},1000)
}
exports.foo = series(task1, task2, task3)
exports.bar = parallel(task1, task2, task3)
異步任務
//1. 回調函數實作異步任務
exports.callback = done =>{
console.log('callback function')
done()
}
exports.callback_err = done =>{
console.log('callback function')
done(new Error('task failed!')) //錯誤優先,第一個參數時錯誤 // 終止後續任務
}
2. promise 實作異步
exports.promise_error = () =>{
console.log('promise task!')
return Promise.reject(new Error('task failed!')) // 終止後續任務
// return Promise.resolve() // gulp 會忽略值, 是以留白就可
}
// 3. async await node環境限制 version>8
const timeout = time => {
return new Promise(resolve =>{
setTimeout(resolve, time)
})
}
exports.async = async()=>{
console.log(async)
await timeout(1000) // await 異步任務(promise 對象)
}
4. stream 最常見方式
exports.stream = () => {
const readStream = fs.createReadStream('package.json')
const writeStream = fs.createWriteStream('temp.txt')
readStream.pipe(writeStream)
// done()
// return readStream
//readStream.on('end',()=>{
// done()
//})
}
核心原理
讀取 轉換 寫入 :基于 流 的操作
const fs = require('fs')
const {Transfrom} = require ('stream')
exports.default =()=> { 導出預設任務
const read = fs.createReadStream(' file_url') 建立檔案讀取流
const write = fs.createWriteStream(' file_url') 建立檔案讀取流
const transform = new Transform({ 建立檔案轉換流
transform:(chunk, encoding, callback) {
const input = chunk.toString()
const output = input.reaplace('正則')
}
})
read
.pipe(transform)
.pipe(write)
}
Gulp 插件使用:
{ src dest } , gulp-clean-css, gulp-rename
安裝 | 導入|放入pipe(something(明确參數))
const {src, dest} = require('gulp')
const cleanCss = require('gulp-clean-css')
const rename = require('gulp-rename')
exports.default = () => {
return src('src/normalize.css')
.pipe(cleanCss())
.pipe(rename({extname:'.min.css'}))
.pipe(dest('dist'))
}
Gulp 案例:scss > css (gulp-sass)
{ base: "src" }, 導出檔案的檔案架構保留
sass({outputsytle: "expanded"}),使用sass 導出css 時的檔案格式
const {src, dest} = require('gulp')
//const sass = require('gulp-sass') // gulp-sass 導入
const sass = require('gulp-sass')(require('sass')); // gulp-sass 5
const style = ()=>{
src('src/styles/scss/*.scss', {base: 'src'}) // 保留内部檔案路徑
.pipe(sass({outputsytle: "expanded"})) // css書寫格式
.pipe(dest('dist'))
}
module.exports = { // = exports.style 另一種寫法
style
}
Gulp 案例:ECMA (gulp-babel)
gulp-babel 是一個轉換 平台
需要安裝@babel/core @babel/preset-env 來轉換(babely具體實施)
yarn add gulp-babel --dev
yarn add @babel/core @babel/preset-env --dev
const babel = require('gulp-babel');
const script = () => {
return src('src/assets/scripts/*.js', {base: "src"})
.pipe(babel({presets:['@babel/preset-env']}))
.pipe(dest('dist'))
}
module.exports = {
script
}
Gulp 案例:HTML (gulp-swig)
傳入需要的 資料, 定義 data 對象, 傳入 swig()
const {src, dest} = require('gulp')
const swig = require('gulp-swig')
const data = {
menus: [{},{},{}],
pkg: require('./package.json'),
data: new Data()
}
const page = () => {
src('src/**/*html')
.pipe(swig({defaults:{cache:false}})) // 加入清除緩存
//.pipe(swig({data})) //傳入資料
.pipe(dest('dist'))
}
module.exports = {
page
}
Gulp 案例:ALL TOGETHER
const compile = parallel(style, script, page)
module.exports = {
compile
}
// yarn gulp compile
Gulp 案例:圖檔壓縮 (gulp-imagemin)
yarn add gulp-imagemin --dev
const imagemin = require ('gulp-imagemin')
const image = () =>{
return src('src/**', {base: 'src'})
.pipe(imagemin())
.pipe(dest('dist'))
}
module.exports = {
image
}
// yarn gulp image
Gulp 案例:複制 + 删除 ( del :不是gulp插件)
其他檔案如 public 下的 直接 複制源檔案, src('....'). pipe(dest('dist'))
删除: yarn add del --dev
build 前删除 dist, 確定不覆寫
const del = require('del')
cosnt clean = () => {
return del(['dist'])
}
const build = series(clean, compile)
Gulp 案例: 自動加載插件 (grunt-load-plugins)
//隻适用于 grunt 插件
const loadPlugins = require( 'grunt-load-plugins' )
const plugins = loadPlugins()
省略了: const imagemin = require( 'grunt-imagemin' )
使用插件時:pipe( plugins.imagemin() )
而不是: pipe(imagemin())
Gulp 案例: dev-server (browser-sync --dev: 不是gulp插件)
導入插件 | 建立伺服器 |建立函數 | 定義配置
配置: notify |port |files|server(baseDir,r)|
const browserSync = require('browser-sync') // 引入 browser-sync
const bs = browserSync.create() // 建立伺服器
const serve = ()=> {
bs.init({
notify: false, // 開啟顯示
port: 2080, // 設定伺服器端口
files: "dist/**", // 監視dist 下檔案的變化
server: {
baseDir: "dist", // 指定檔案路徑
routes: {
'/node_modules': 'node_modules'
}
}
})
}
監視dist 檔案
添加init 配置, 監視檔案
files: "dist/**", // 監視dist 下檔案的變化 重新整理頁面
Gulp 案例: dev-server (監視src 檔案變化)
const {watch} = require ('gulp') , 監視檔案,判斷知否執行任務
watch('參數1:路徑',參數2: 任務)
開啟server前使用
const serve = ()=> {
watch('src/assets/styles/*.scss', style)
watch('src/assets/scripts/*.js', script)
watch('src/*.html', page)
Gulp 案例: useref
yarn add gulp-useref --dev
dist 中html 檔案内的檔案引用打包成vendor
.pipe( useRef( {searchPath:['dist','.']} ) )
const useRef = require('gulp-useref');
const ref = ()=>{
return src('dist/*.html',{base:'dist'})
.pipe(useRef({searchPath:['dist','.']}))
.pipe(dest('dist'))
}
Gulp 案例: 檔案壓縮(gulp-htmlmin,gulp-clean-css,gulp-uglify)
壓縮html,css, js
yarn add gulp-if --dev
compile 完成後 會生成<start> <end> 字元, 根據此來進行壓縮, 是以需要先compile 并在寫入dist 前完成 壓縮
const isif = require('gulp-if');
const htmlMin = require('gulp-htmlmin');
const cleanCss = require('gulp-clean-css');
const uglify = require('gulp-uglify');
const ref = ()=>{
return src('dist/*.html',{base:'dist'})
.pipe(useRef({searchPath:['dist','.']}))
.pipe(isif(/\.js$/, uglify()) )
.pipe(isif(/\.css$/, cleanCss()) )
.pipe(isif(/\.html$/, htmlmin({
collapseWhitespace:true,
minifyCSS:true,
minifyJS:true,
})) )
.pipe(dest('release'))
}
第九章: 可複用_自動化建構_工作流
使用腳手架建立新項目 => github 托管
複制 pulpfile.js 至 新項目入口檔案 lib/index.js
将源中的devDependencies複制到目标的dependencies 并在目标項目中安裝這些檔案 yarn
删除 源: gulpfile.js 内容, devDependencies 字元,node_modules檔案
将模版在全局link, yarn link
在源檔案中 yarn link <name>pages
在源檔案中有 scripts 字元, 運作他們,
關于.pipe(babel({ presets:[require('@babel/preset-env')] }))
require()會在自己目錄下找,一直向上級找。
yarn add gulp-cli --dev
yarn add gulp --dev