前端工程化、腳手架工具、自動化構建

工程化概述

  • 面臨的問題

    • ES6+ 兼容問題
    • Less、Sass等 不支持
    • 模塊化、組件化不支持
    • 手動壓縮
    • 手動上傳代碼
    • 多人開發,難以統一風格
    • 等待後端接口完成
  • 主要解決的問題

    • 傳統語言或語法的弊端
    • 無法使用模塊化、組件化
    • 重複的機械式工作
    • 代碼風格統一、質量保證
    • 依賴後端服務接口支持
    • 整體依賴後端項目
  • 工程化的表現

    • 一切重複的工作都應該被自動化
      [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-QplZ11iN-1593528619196)(./img/1.png)]
  • 工程化不等於某個工具
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-NNz4A3Mg-1593528619198)(./img/2.png)]

    • vue-cli
    • create-react-app
    • angular-cli
  • 工程化與node

    • 前端工程化由node驅動

腳手架工具開發

  • 腳手架的本質

    • 創建項目基礎結構、提供項目規範和約定
      • 相同的組織結構
      • 相同的開發範式
      • 相同的模塊依賴
      • 相同的工具配置
      • 相同的基礎代碼
    • 前端腳手架
  • 常用的腳手架工具

    • create-react-app
    • vue-cli
    • angular-cli
    • yeoman
    • plop(用於創建特定類型文件)
  • yeoman

    • 基本使用
      • 在全局安裝yo yarn global add yo
      • 安裝對應的generator 如:生成node模塊 yarn global add generator-node
      • 通過yo運行generator yo node
    • generator
      • sub generator yo node:cli
  • yeoman 常規使用步驟

    • 明確需求
    • 找到合適的generator
    • 全局安裝找到generator
    • 通過yo運行對應的generator
    • 通過命令式交互行填寫選項
    • 生成所需的項目結構
    • 例子: generator-webapp
  • 自定義generator (基於yeoman)的實現

    • 創建generator本質上就是創建一個npm模塊
    • generator基本結構
      [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-auZ335ee-1593528619200)(./img/3.png)]
      [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-9YUN6c2P-1593528619202)(./img/4.png)]
    • 名稱約定 generator-<name>
    • 操作
      • 新建目錄generator-sample
      • yarn init
      • yarn add yeoman-generator
      • 按目錄結構創建文件代碼
      • npm link
      • yo sample
    • 根據模塊創建文件代碼
    • 接收用戶輸入代碼-prompting
    • vue Generator案例
      代碼
      • 注意 使用yo <name>之前
        • 使用npm link
        • 安裝yeoman-generator
    • 發佈generator(實際就是發佈npm模塊)(淘寶鏡像是隻讀的)
      • npm public
      • yarn public --registry=https://registry.yarnpkg.com
  • plop (小型腳手架工具)

    • 重複創建相同類型的文件
    • 基本使用
  • 腳手架的工作原理

    • 實操
      • 創建目錄cli-test
      • npm init
      • 修改package.json 增加"bin": "cli.js",
      • 創建cli.js文件 參考代碼
      • npm link
      • 命令行運行 cli-test
    • 原理
      • 通過命令行交互詢問用戶問題
      • 根據用戶回答生成文件

自動化構建系統

源代碼—(自動化構建)–>生產代碼
1、初體驗

  • 創建auto-build目錄
  • 創建index.html \ main.scss文件
  • 安裝sass
  • .\node_modules.bin\sass 查看sass命令
  • .\node_modules.bin\sass .\part02\module01\auto-build\main.scss .\part02\module01\auto-build\style.css 編譯scss文件爲css文件
  • 安裝browser-sync
  • 配置npm script
  • 安裝npm-run-all(run-p 命令)
  "build":"sass .\\part02\\module01\\auto-build\\main.scss .\\part02\\module01\\auto-build\\style.css",
    "serve":"browser-sync ./part02/module01/auto-build",
    "start":"run-p build serve"

2、常用的自動化構建工具

  • grunt
    • 構建速度慢
  • gulp
    • 同時執行多個任務
    • 生產文件存於內存
  • fis
    • 資源加載、性能優化等集成
      3、grunt
  • grunt 基本使用
    • 安裝grunt
    • 目錄下新建gruntfile.js文件參考代碼
    • yarn grunt foo執行
  • grunt 標記任務失敗參考代碼
    • return false
    • done(false)異步任務
  • grunt的配置方法參考代碼
  • grunt多目標任務
        // 多目標任務 options不會當做子任務 而是配置 子任務的option會覆蓋上層的options
    grunt.initConfig({
        build:{
            options:{
                foo:'foo'
            },
            js:1,
            css:{
                options:{
                    foo:'bar'
                }
            }
        }
    })
    grunt.registerMultiTask('build',function(){
        console.log(this.options());
        console.log(`target:${this.target},data:${this.data}`);
    })
    
  • grunt插件的使用
    • 安裝grunt-contrib-clean
    • grunt.loadNpmTasks(‘grunt-contrib-clean’) 加載
      grunt.initConfig({
        clean:{
            // temp:'temp/app.js',
            // temp:'temp/*.js',
            temp:'temp/**'
        }
    })
    grunt.loadNpmTasks('grunt-contrib-clean')
    
  • grunt常用插件
    • grunt-sass
      • 依賴sass模塊
    const sass = require('sass')

module.exports = grunt =>{

    grunt.initConfig({
        sass:{
            options:{
                implementation:sass
            },
            main:{
                file:{
                    'dist/css/main.css':'src/scss/main.scss'
                }
            }
        }
    })
    grunt.loadNpmTasks('grunt-sass')

}
    
  • grunt-babel
    • 依賴@babel/core @babel/preset-env
const loadGruntTasks = require('load-grunt-task') // 自動加載

module.exports = grunt =>{
    grunt.initConfig({
        babel:{
            options:{
                presets:['@babel/preset-env']
            },
            main:{
                files:{
                    'dist/js/app.js':'src/js/app.js'
                }
            }
        }
    })
    loadGruntTasks(grunt)
}
  • grunt-contrib-watch
 watch:{
            js:{
                files:['src/js/*.js'],
                tasks:['babel']
            },
            css:{
                files:['src/scss/*.scss'],
                tasks:['sass']
            }
        }

4、gulp

  • 基本使用

    • 安裝gulp
    • 創建入口文件 gulpfile.js文件 參考代碼
    • yarn gulp <taskName>
  • 組合任務

const {series,parallel} = require('gulp')
const task1 = done=>{
    setTimeout(()=>{
        console.log('task1');
        done();
    },1000)
}
const task2 = done=>{
    setTimeout(()=>{
        console.log('task2');
        done();
    },1000)
}
const task3 = done=>{
    setTimeout(()=>{
        console.log('task3');
        done();
    },1000)
}

exports.foo = series(task1,task2,task3); // 串行
exports.bar = parallel(task1,task2,task3);//並行同步執行
  • 異步任務
const fs = require('fs')
exports.cb = done=>{
    console.log('cb task');
    done()
}

exports.cb_error = done =>{
    console.log();
    done(new Error('task failed!'))
}

exports.promise = ()=>{
    console.log('promise task');
    return Promise.resolve()
}
exports.promise_error = ()=>{
    console.log('promise task');
    return Promise.reject(new Error('task failed'))
}

const timeout = time =>{
    return new Promise(resolve=>{
        setTimeout(resolve,time)
    })
}
exports.async = async ()=>{
    await timeout(1000)
    console.log('async task');
}

// exports.stream = ()=>{
//     const readStream  = fs.createReadStream('package.json')
//     const writeStream = fs.createWriteStream('temp.txt')
//     readStream.pipe(writeStream)
//     return readStream
// }


exports.stream = done =>{
    const readStream  = fs.createReadStream('package.json')
    const writeStream = fs.createWriteStream('temp.txt')
    readStream.pipe(writeStream)
    readStream.on('end',()=>{
        done()
    })
}
  • gulp構建過程核心原理
    • 讀出文件—轉換—寫入
const fs = require('fs')
const {Transform} = require('stream')
exports.default = ()=>{
    // 文件讀取
    const read = fs.createReadStream('a.css')
    // 文件寫入流
    const write = fs.createWriteStream('a.min.css')

    const transform = new Transform({
        transform:(chunk,encoding,cb)=>{
            const input = chunk.toString();
            const output = input.replace(/\s+/g,'').replace(/\/\*.+?\*\//g,'')
            cb(null,output)
        }
    })
    // 把讀取出來的文件流導入寫入文件流
 
    read.pipe(transform).pipe(write);
    return read
}
  • 文件操作API + 插件的使用
const {src,dest} = require('gulp')
const cleanCss = require('gulp-clean-css')
const rename = require('gulp-rename')

exports.default = ()=>{
    return src('a.css')
        .pipe(cleanCss())
        .pipe(rename({extname:'.min.css'}))
        .pipe(dest('dist'))
}
  • 案例:樣式編譯
const { src, dest } = require('gulp')
const sass = require('gulp-sass')
const style = () => {
    return src('src/assets/style/*.scss', { base: 'src' })
        .pipe(sass({outputStyle:'expanded'}))
        .pipe(dest('dist'))
}

module.exports = {
    style
}
  • 案例:腳本編譯
const babel = require('gulp-babel')
const script = ()=>{
    return src('src/assets/scripts/*.js', { base: 'src' })
    .pipe(babel({presets:['@babel/preset-env']}))
    .pipe(dest('dist'))
}
  • 案例:頁面模板編譯
const swig = require('gulp-swig')
const page = ()=>{
    return src('src/*.html', { base: 'src' })
    .pipe(swig())
    .pipe(dest('dist'))
}
  • 案例:圖片和字體文件轉換
const imagemin = require('gulp-imagemin')
const image = ()=>{
    return src('src/assets/images/**', { base: 'src' })
        .pipe(imagemin())
        .pipe(dest('dist'))
}
const font = ()=>{
    return src('src/assets/font/**', { base: 'src' })
        .pipe(imagemin())
        .pipe(dest('dist'))
}
  • 案例:其他文件及文件清除
// 主要是拷貝文件
const extra = ()=>{
    return src('public/**', { base: 'public' })
        .pipe(dest('dist'))
}

// del插件
const del = require('del')
const clean = ()=>{
    return del(['dist'])
}
// 構建之前清除
const build = series(clean,parallel(compile,extra))

  • 案例:自動加載插件
const gulpLoadPlugins = require('gulp-load-plugins')
const plugins = gulpLoadPlugins()
// 使用時 通過plugins.sass 的方式

const style = () => {
    return src('src/assets/style/*.scss', { base: 'src' })
        .pipe(plugins.sass({ outputStyle: 'expanded' }))
        .pipe(dest('dist'))
}
  • 案例:開發服務器 (熱更新)
const browserSync = require('browser-sync')
const bs = browserSync.create()


const server = () => {
    bs.init({
        server:{
            baseDir:'dist'
        }
    })
}
  • 案例:監視變化及構建優化
const { src, dest, parallel ,series,watch} = require('gulp')
const server = () => {
    watch('src/assets/style/*.scss',style)
    watch('src/assets/scripts/*.js',script)
    watch(['src/assets/images/**','src/assets/font/**'],bs.reload)

    bs.init({
        notify:false,//關閉右上角的tip
        port:2080,//端口 
        open:false,//關閉自動打開瀏覽器
        files:'dist/**',// 監聽的目錄
        server:{
            baseDir:'dist',
            routes:{
                '/node_modules':'node_modules'
            }
        }
    })
}
  • 案例:useref文件引用處理
<!-- 構建註釋 -->
<!-- build:css assets/styles/vendor.css -->
<link rel='stylesheet' href='node_modules/bootstrap/dist/css/bootstrap.css'>
<!-- endbuild-->

const useref = ()=>{
    return src('dist/*.html',{base:'dist'})
        .pipe(plugins.useref({searchPath:['dist','.']}))
        .pipe(dest('dist'))
}
  • 案例:文件壓縮
const useref = ()=>{
    return src('dist/*.html',{base:'dist'})
        .pipe(plugins.useref({searchPath:['dist','.']}))
        // html js css
        // gulp-html gulp-uglify gulp-clean-css
        .pipe(plugins.if(/\.js$/,plugins.uglify()))
        .pipe(plugins.if(/\.html$/,plugins.htmlmin({collapseWhitespace:true})))
        .pipe(plugins.if(/\.css$/,plugins.cleanCss()))

        .pipe(dest('release'))
}

useref打亂了之前的構建流程。

  • 案例:重新規劃構建過程

5、封裝自動化構建工作流

  • 準備

    • 新建一個項目(含遠程倉庫)
    • 安裝zce-cli
  • 提取gulpfile

    • 將gulp-demo中的gulpfile.js 文件內容複製到gxw-pages項目下的lib/index.js(入口文件)中
    • 將gulp-demo中的package.json中安裝的依賴複製到gxw-pages的package.json的dependencies
    • 刪除gulp-demo項目中的依賴、清空gulpfile.js
    • gxw-pages項目通過yarn link 鏈接到本地全局
    • 在gulp-demo項目中通過yarn link "gxw-pages" 鏈接到本項目
    • gulp-demo項目中gulpfile.js添加代碼module.exports = require('gxw-pages')
    • gulp-demo項目中安裝一下依賴(原本項目依賴)
    • 安裝一下gulp-cl、gulp
    • 運行腳本yarn gulp clean
  • 解決模塊中的問題

    • gulp-demo項目中創建page.config.js文件(目的是抽離出一些配置信息)
    • 在gxw-pages項目中lib/index.js加載配置文件
    const cwd = process.cwd();// 返回當前命令行工作目錄
    let config = {
        // default config
    }
    try {
        const loadConfig = require(`${cwd}/pages.config.js`)
        config = Object.assign({},config,loadConfig)
    } catch (error) {
        
    

}
- 將gxw-pages項目中lib/index.js用到的相關配置改成加載過來的數據
const page = () => {
return src(‘src/*.html’, { base: ‘src’ })
.pipe(swig({ data: config.data }))
.pipe(dest(‘dist’))
}
```

  • 抽象路徑配置
    • 把寫死的路徑改成可配置的
    let config = {
    // default config
    build:{
    src:'src',
    dist:'dist',
    temp:'temp',
    public:'public',
    paths:{
      styles:'assets/style/*.scss',
      scripts:'assets/scripts/*.js',
      pages:'*.html',
      images:'assets/images/**',
      fonts:'assets/font/**'
    }
    }
    

}
const style = () => {
return src(config.build.paths.styles, { base: config.build.src,cwd:config.build.src})
.pipe(sass({ outputStyle: ‘expanded’ }))
.pipe(dest(‘dist’))
}

```
  • 包裝gulp cli
    • gulp-demo項目中gulpfile.js刪除
    • yarn gulp build --gulpfile .\node_modules\gxw-pages\lib\index.js
    • yarn gulp build --gulpfile .\node_modules\gxw-pages\lib\index.js --cwd . (指定當前目錄爲工作目錄)
    • 上面的方法傳參太多
    • 解決:在gxw-pages項目中提供一個cli
      • 在gxw-pages項目新建bin/gxw-pages.js
      • 在package.json中配置"bin":“bin/gxw-pages.js”
      #!/usr/bin/env node 
      process.argv.push('--cwd')
      process.argv.push(process.cwd())
      process.argv.push('--gulpfile')
      process.argv.push(require.resolve('..'))
      
      require('gulp/bin/gulp')
      
      
      • gxw-pages clean·
  • 發佈使用 gwx-pages
    • package.json文件files增加
     "files": [
        "lib",
        "bin"
    ],
    
    • npm publish (要先登錄)
    • npm i gwx-pages 在其他項目中使用

6、Fis (高度集成、內置webserver)

  • 基本使用
    • 安裝fis3
    • yarn fis3 release (默認構建任務)
    • yarn fis3 release -d dist (指定輸出目錄)
    • 配置文件fis-conf.js
  • 編譯與壓縮
    • yarn fis3 inspect 查看編譯過程
// 安裝 fis-parser-node-sass
fis.match('**/*.scss',{
    rExt:'.css',// 修改擴展名
    parser:fis.plugin('node-sass'),
    optimizer:fis.plugin('clean-css')//壓縮
})

// 安裝 
fis.match('**/*.js',{
    parser:fis.plugin('babel-6.x'),
    optimizer:fis.plugin('uglify-js')
})


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章