讓咱們用gulp寫個前端腳手架

項目地址

簡介

  • 公司有個新項目要作官網,須要支持國際化,UI設計了不少頁面,老闆着急要因而咱們就直接用 html + css + jquery分工開發了, ,作出來的項目結構是這樣的(直接部署到服務器上):

  • 等到項目維護迭代的時候就很麻煩,遇到了不少問題:
  1. 每一個html頁面都有導航、footer、head等公共頁面,修改須要設計全部文件
  2. 沒有使用css預處理器,用慣了sass,css嵌套寫起來很彆扭
  3. 用慣了ES6,老是想寫let
  4. 資源文件沒有加hash值
  5. 新加語言種類須要把全部html頁面複製一份從新編寫
  • 正好最近看招聘信息,好多要求要會用webpack和gulp,就想着學學gulp,用gulp搭個腳手架升級一下官網項目。
  • 下面來介紹一下這個腳手架的搭建過程

項目介紹

項目簡介

  1. 項目基於gulp、babel7構建
  2. 使用ejs開發靜態頁面,支持國際化開發
  3. js支持commenjs規範以及esm規範
  4. 使用sass預處理css,使用postcss處理瀏覽器後綴
  5. 使用browser-sync構建開發環境,使用http-proxy-middleware處理請求代理

項目結構

├── README.md
├── dev     //開發環境打包代碼
├── dist    //生產環境打包代碼
├── favicon.ico
├── gulp    //gulp配置
├── gulpfile.babel.js   //babel配置
├── package.json
├── src
│   ├── html    //index入口文件
│   │   ├── ejs
│   │   │   └── footer.ejs
│   │   └── index.html
│   ├── imgs    //圖片
│   │   └── 123.jpeg
│   ├── js  //js
│   │   ├── index.js
│   │   └── moduleA.js
│   ├── lang    //國際化語言文件
│   │   ├── en.json
│   │   └── zh-cn.json
│   ├── scss    //sass文件
│   │   ├── common
│   │   │   └── _reset.scss
│   │   └── index.scss
│   └── static  //靜態文件
│       └── jquery-3.2.1.min.js
└── user.config.js.config   //開發配置文件
複製代碼

使用gulp打包

gulp簡介

  • gulp相關內容能夠查看官網以及其它文章
  • gulp配置文件中使用相關庫能夠查詢github瞭解功能以及使用方法

gulp配置

  • gulp主要任務配置都放在了gulp文件夾下
  • 根目錄新建了gulpfile.babel.js文件,並使用 require-dir 導入gulp文件夾下的任務
//gulpfile.babel.js文件
import requireDir from 'require-dir'
requireDir('./gulp/task')
requireDir('./gulp')
複製代碼
  • 項目分爲開發環境和生產環境兩個主要任務,/gulp/dev.js是開發環境任務,/gulp/prod.js是生產環境任務,/gulp/task/文件夾下是其餘任務css

  • npm 命令html

"scripts": {
    "start": "npm run dev",//開發環境
    "dev": "gulp dev",
    "build": "gulp prod"//生產環境
},
複製代碼

開發與生產

區別

  • 首先看一下生產環境與開發環境區別:
  1. 生產環境須要資源加hash值,防止用戶緩存問題
  2. 生產環境須要壓縮代碼
  3. 開發環境須要創建本地服務器,處理轉發請求
  4. 開發環境文件更改須要同步更新,刷新瀏覽器
  5. 開發環境文件須要添加sourcemap配置,方便檢查錯誤

開發環境

  • gulp dev
gulp.task('dev',
    gulp.series(
        'clean:dev',
        'html:dev',
        gulp.parallel('scss:dev', 'js:dev', 'static:dev', 'favicon:dev'),
        'img:dev',
        'server'
    ))
複製代碼
  • 開發環境使用 browser-sync 搭建服務器,使用 http-proxy-middleware 代理請求jquery

  • 同時建立user.config.js.config文件,供開發配置服務端口和請求代理配置使用,須要複製一份改成user.config.js,防止多人開發衝突。webpack

  • 開發環境不進行代碼壓縮等處理,打包後代碼放在dev文件夾下git

  • /gulp/task/server.js文件中建立server任務es6

gulp.task('server', function () {
    browserSync.init({
        server: "./dev",
        port: userConfig.port,
        middleware: proxyMiddleware
    });
});
複製代碼

生產環境

  • gulp prod
gulp.task('prod',
    gulp.series(
        'clean',
        'html:prod',
        gulp.parallel('scss:prod', 'js:prod', 'static:prod', 'favicon:dist'),
        'img:prod'
    ))
複製代碼
  • 生產環境任務主要是在開發環境任務基礎上,添加壓縮、hash編碼等任務,打包文件放在dist文件夾下

文件處理

清理文件夾

  • 打包前使用 gulp-clean 清空dev|dist文件夾,/gulp/task/clean.js文件中的clean:prod、clean:dev、clean任務
gulp.task('clean:prod',function () {
    return gulp.src('./dist', {read: false,allowEmpty: true})
        .pipe(gulpClean());
})
gulp.task('clean:dev',function () {
    return gulp.src('./dev', {read: false,allowEmpty: true})
        .pipe(gulpClean());
})
gulp.task('clean',gulp.parallel('clean:prod','clean:dev'))
複製代碼

處理js

  • 因爲官網項目不須要太多js操做,所以引入jquery足夠了,jquery做爲靜態文件引入,以後會講
  • /gulp/task/js.js文件處理js任務,項目js入口代碼在/src/js/文件夾下
  • 首先分析一下需求,由於有多個html頁面,每一個頁面須要處理不一樣的表單和頁面邏輯,所以js也須要有多個入口。
  • html中能夠使用相對於服務器路徑引用js文件
<script src="/js/index.js"></script>
複製代碼
  • 使用 browserify 打包js文件,能夠支持commonjs模塊化,同時也使用了 gulp-babel ,使項目支持SE6語法以及esm模塊化開發。
  • 入口文件配置
//若是須要多個入口文件,則繼續配置
let entries = [
    {
        name: 'index',
        entry: ['src/js/index.js']
    }
]
複製代碼
  • 開發環境處理js,處理流程:任務js:dev->devArrFun循環入口文件->makeBundle打包js->從新命名->生成文件到dev文件夾->同時監聽變化->從新打包刷新瀏覽器
  • 生產環境處理js,處理流程:任務js:prod->prodArrFun循環入口文件->bundle打包js存儲在dev文件夾中->任務js:dev2dist壓縮js、添加hash、替換html文件中的路徑
  • 任務代碼
//使用browserify和babel打包js文件
function makeBundle(name,entry){
    if(!bundleArr[name]){
        let b = browserify({
            entries: entry,
            debug: devServer,
            extensions: ['es6'],
        })
            .transform(html2js)
            .transform(babelify)
            .on('error', function (err) { console.error(err); })
        bundleArr[name] = b
    }
    return bundleArr[name]
}

//直接打包不檢測更新
function bundle(name,entry){
    let b = makeBundle(name,entry)
    return b
        .bundle()
        .pipe(source(`${name}.js`))
        .pipe(buffer())
        .pipe(replace('@img', 'img'))
        .pipe(gulp.dest('dev/js'))
        .pipe(gulpif(devServer,global.browserSync.reload({stream: true})))//文件變化刷新瀏覽器
}

//開發環境打包
let devArrFun = entries.map(i=>{//循環入口,每一個文件都打包
    return devFun.bind(null,i.name,i.entry)
})

//打包js並檢測更新
function devFun(name,entry) {

    devServer = true

    let b = makeBundle(name,entry)

    b.plugin(watchify);

    //文件變化從新打包js
    b.on('update',bundle.bind(null,name,entry))

    return bundle(name,entry)
}

//生產環境打包
let prodArrFun = entries.map(i=>{
    return bundle.bind(null,i.name,i.entry)
})

gulp.task('js',gulp.parallel(prodArrFun))

//開發環境任務
gulp.task('js:dev',gulp.parallel(devArrFun))

//將dev中文件轉入dist文件夾中
gulp.task('js:dev2dist',function () {
    return gulp.src('dev/js/*.js')
        .pipe(uglify())
        .pipe(md5(6, './dist/*.html'))
        .pipe(gulp.dest('dist/js'))
})
//生產環境任務
gulp.task('js:prod',gulp.series('js','js:dev2dist'))
複製代碼

處理css

  • 使用sass預處理css,使用postcss的autoprefixer添加瀏覽器前綴,/src/scss/文件夾放置sass入口文件
  • 使用相對於服務器路徑引用css文件
<link rel="stylesheet" href="/css/index.css">
複製代碼
  • 處理流程依然是查找入口文件,打包scss文件,同時開發環境監聽文件變化刷新瀏覽器,生產環境進一步處理開發環境打包的文件。
  • 任務代碼
function scss() {
    return gulp
        .src('./src/scss/*.scss')//查找入口文件
        .pipe(gulpif(devServer,sourcemaps.init()))//開發環境添加sourcemap配置
        .pipe(sass().on('error', sass.logError))
        .pipe(postcss([autoprefixer()]))//添加瀏覽器前綴
        .pipe(replace('../imgs', '../imgs'))//處理圖片路徑
        .pipe(replace('../../imgs', '../imgs'))
        .pipe(gulpif(devServer,sourcemaps.write()))
        .pipe(gulp.dest('./dev/css'));//開發環境存放文件
}

gulp.task('scss',scss)

gulp.task('scss:dev', function () {
    devServer = true
    //開發環境監聽文件變化從新打包並刷新瀏覽器
    gulp.watch(['./src/scss/*.scss','./src/scss/*/*.*'], function (event) {
        return scss().pipe(global.browserSync.reload({stream: true}));
    });
    return scss()
});

gulp.task('scss:dev2dist',function () {
    return gulp.src('./dev/css/*.css')
        .pipe(webpcss())//處理webp文件
        .pipe(cleanCSS())//壓縮文件
        .pipe(md5(6, './dist/*.html'))//添加hash,並替換html中的文件名稱
        .pipe(gulp.dest('./dist/css'));//生產環境保存文件
})

gulp.task('scss:prod', gulp.series('scss','scss:dev2dist'));
複製代碼

處理圖片

  • css以及html中使用圖片能夠直接相對路徑引用,在scss以及html打包中會替換img文件路徑
  • 圖片存儲在/src/imgs/文件夾中,目前只支持兩級目錄
  • 開發環境只是圖片複製,生產環境會壓縮圖片、添加hash、替換css\html中的文件
  • 任務代碼
const srcArr = ['./src/imgs/*.{png,gif,jpg,jpeg}','./src/imgs/*/*.{png,gif,jpg,jpeg}']

function img(){
    return gulp
        .src(srcArr)
        .pipe(gulp.dest('./dev/imgs'))
}

gulp.task('img',img)

gulp.task('img:dev',function () {

    gulp.watch(srcArr, function (event) {
        return img(event.path)
            .pipe(global.browserSync.reload({stream: true}))
    });

    return img();
})

gulp.task('img:dev2dist',function () {
    return gulp
        .src(srcArr)
        .pipe(imagemin())//壓縮圖片
        .pipe(md5(6, ['./dist/*.html', './dist/css/*.css', './dist/js/*.js']))//添加hash,替換文件名
        .pipe(gulp.dest('./dist/imgs'))
})

gulp.task('img:prod',gulp.series('img','img:dev2dist'))
複製代碼

處理靜態文件

  • jquery等其餘庫能夠放在/src/static/文件加下,在html中使用相對於服務器路徑引用便可,/gulp/task/static.js中的任務負責,處理靜態資源的複製。
<script src="/static/jquery-3.2.1.min.js"></script>
複製代碼

ejs以及國際化配置

  • 最後一項是對於html的處理,再來回顧一下咱們的需求
  1. 導航、footer、head等能夠使用公共模板
  2. 添加國際化配置文件能夠生成不一樣語言頁面
  • 所以我使用了 ejs 來編寫html,同時添加語言配置的json文件,打包出不一樣的語言頁面

處理公共html模塊

  • /src/html/文件夾存放html以及ejs文件,使用ejs加載公共模塊
<%- include('ejs/footer',{type: common.type}) %>
複製代碼

html入口

  • 處理html的任務在/task/html.js中,每次新增頁面,須要配置html入口文件
//該配置會打包出index.html(中文)以及index-en.html(英文)兩個頁面
//html入口文件
let htmlConfig = [
    {
        entry: 'src/html/index.html',
        name: 'index.html',//打包後文件名
        lang: 'zh-cn'//ejs語言配置文件,對應/src/lang下的json文件
    },
    {
        entry: 'src/html/index.html',
        name: 'index-en.html',
        lang: 'en'
    },
]
複製代碼

國際化處理

  • /src/lang/文件夾中爲不語言添加不一樣配置
  • 舉個例子:
  1. 好比en.json以及zh-cn.json的配置以下
//en.json
{
    "footer": "footer"
}
//zh-cn.json
{
    "footer": "福特兒"
}
複製代碼
  1. ejs中使用模板
<div><%= footer %></div>
複製代碼
  1. 打包出html分別爲
//index.html
<div>福特兒</div>
//index-en.html
<div>footer</div>
複製代碼
  • 咱們還可以使用ejs其餘語法編寫好比:按條件渲染生成不一樣html處理頁面差別、用變量處理dom元素屬性、生成不一樣class處理語言顯示等問題,這些就須要靠你的智慧了。github

  • 任務代碼web

//處理ejs模板
function html(config){
    return gulp
        .src(config.entry)
        .pipe(data(function(file) {//加載語言配置
            return JSON.parse(fs.readFileSync(`src/lang/${config.lang}.json`))
        }))
        .pipe(replace('../imgs', './imgs'))//替換圖片路徑
        .pipe(replace('../../imgs', './imgs'))
        .pipe(ejs().on('error', handleError))//錯誤處理
        .pipe(rename(config.name))//替換文件名
        .pipe(gulp.dest('dev'))
}

let htmlDevArr = htmlConfig.map(config=>{
    return function (config) {
        //監聽變化從新打包而且刷新瀏覽器
        gulp.watch([config.entry,'src/html/*/*.*','src/lang/*'], function (event) {
            return html(config).pipe(global.browserSync.reload({stream: true}));
        });
        return html(config)
    }.bind(null,config)
})

//開發環境命令
gulp.task('html:dev',gulp.parallel(htmlDevArr))

gulp.task('html:dev2dist',function () {
    return gulp
        .src('dev/*.html')
        .pipe(htmlmin({ collapseWhitespace: true }))//壓縮html
        .pipe(gulp.dest('dist'))
})

let htmlProdArr = htmlConfig.map(config=>{
    return function (config) {
        return html(config)
    }.bind(null,config)
})
//生產環境命令
gulp.task('html:prod',gulp.series(gulp.parallel(htmlProdArr),'html:dev2dist'))
複製代碼

錯誤處理

  • 開發中還遇到了一個問題:ejs模板報錯時會中止gulp任務,須要使用 gulp-notify 處理報錯,防止任務終止
const notify = require("gulp-notify");
module.exports = function(){
    var args = Array.prototype.slice.call(arguments)

    notify.onError({
        title: 'compile error',
        message: '<%=error.message %>'
    }).apply(this, args)

    this.emit();
}
複製代碼

總結

  • 以上就是gulp官網項目腳手架搭建過程,因爲是一邊學習gulp一邊搭的,不少處理方法都是一邊搜索一邊找合適的加上的,但願你們多提意見。
相關文章
相關標籤/搜索