前端快速開發模版

Jeff.Zhong發表於2017-11-28

  原文地址:前端快速開發模版
  之前一直在開發移動端的單頁面應用,而移動web對效能和效果要求是比較苛刻的,所以用的都是輕量級的框架體系。基本上是Zepto加自己開發的單頁面框架,再加上若干簡單的庫。這樣前端要載入的檔案很小,修改起來也非常方便,同時這樣的輕量級的體系使用gulp進行自動化管理也是非常合適的。

  自從開發react專案後,對它的工程化和開發模式也是開了眼界,標準的框架體系就是重量級的react+redux+webpack。開發大型專案和後臺管理系統用react,vue確實是不錯的。但是開發一些小專案,比如前端h5之類的又有殺雞用牛刀的感覺。

  於是借鑑webpack工程化思想動手寫了個前端快速開發模版,用於開發快速簡潔的專案,比如之前的單頁面應用。而這個專案模版就是類似前端開發的腳手架了,基本的思路就是自動化和前端編譯,主要達到以下目的:

  1. 使用less預編譯css,使用autoprefixer進行樣式相容
  2. js程式碼用babel編譯,因此可基於es6,es7編寫程式碼
  3. 類庫檔案使用npm包,js檔案使用browserify組織和打包,不再使用sea,require之類的載入器
  4. html程式碼使用swig模版引擎進行組織和預編譯
  5. 靜態資源版本使用rev管理,當css或js檔案內容有變化,打包時自動更新版本
  6. 前端開發基礎樣式,框架的整合。

工具鏈

  • 構建工具gulp
  • 編譯打包工具browserify
  • 程式碼編譯babel
  • css相容autoprefixer
  • css預編譯less
  • html程式碼預編譯模版swig
  • 前端程式碼版本更新rev

公共類庫

  • jQuery
  • Zepto
  • 自己開發的簡潔單頁面框架app.js
  • 圖示fontello
  • 字型reboto
  • 基礎樣式base.css

檔案目錄結構

  • css: css檔案
  • less: less檔案
  • js: js檔案
  • dist: 打包生成的專案檔案
  • layout: html母板
  • lib: 公共類庫
  • template: html頁面

專案自動化

  沒有使用重量級的webpack,而是使用輕量級的gulp和browserify

公共類庫

  公共類庫的檔案,主要有基礎樣式base.css,圖示樣式reboto,字型樣式fontello,單頁面框架app,直接拷貝到dist檔案就ok。其他npm包跟nodejs專案一樣在node_modules中,不需要處理。

    //複製公共庫目錄下的所有內容
    gulp.task('copy',function(){
        return gulp.src('./lib/**')
            .pipe(gulp.dest('./dist/lib/'));
    });

css檔案

  首先將less檔案轉為css,用autoprefixer新增相容性字首,合併壓縮,放到dist目錄,最後用rev外掛生成版本資訊,這個後面用於替換更新連結用。

    //編譯css,新增相容字尾,壓縮
    gulp.task('css', function() {
        return gulp.src('./less/*.less')
            .pipe(less())
            // .pipe(concatCss("index.css"))
            .pipe(postcss([ autoprefixer({
                    "browsers": ["last 2 version", "> 0.5%", "ie 6-8","Firefox < 20"]
                    // "browsers": ["last 2 version", "> 0.1%"]
                })
            ]))
            .pipe(cleanCSS())
            // .pipe(rename({suffix: '.min'}))
            // .pipe(gulp.dest('./dist/css'));
            .pipe(rev())
            .pipe(gulp.dest('./dist/css'))
            .pipe(rev.manifest())
            .pipe(gulp.dest('./rev/css'));

    });

js檔案

  我這裡有兩個入口檔案,可以隨時將新入口檔案新增到scripts陣列中,這裡做的就是使用babel轉換程式碼,然後將外部檔案,npm包打包進來,生成sourcemap,輸出到dist資料夾,最後一樣用rev外掛生成版本資訊。

    var scripts=['app','index'];
    scripts.map(name=>{
        gulp.task(name,function(){
                return  browserify({
                    entries:'./js/'+name+'.js',  //entries file name
                    debug:true // set true so the bundle file can generate sourcemap 
                })
                .transform(babelify,{ 
                    plugins: ["transform-runtime"],
                    presets: [
                        'es2015',  //convert to es5
                        'stage-0'  //es7
                    ]
                })
                .bundle()  //merge
                .pipe(source(name+'.js'))
                .pipe(buffer())
                // .pipe(uglify())
                .pipe(sourcemaps.init({loadMaps: true})) //External sourcemap file
                .pipe(sourcemaps.write('./'))
                .pipe(rev())
                .pipe(gulp.dest('./dist/js/'))
                .pipe(rev.manifest(name+'.json'))
                .pipe(gulp.dest('./rev/js/'));
        });
    });

html檔案

  html編譯我使用的是模版引擎是swig,這裡用的是gulp-swig外掛,當然也可以用ejs或jade的引擎。但我個人比較習慣用swig,因為它更靈活,支援各種檔案格式,可以直接使用html檔案,也支援ejs不支援的layout等。gulp-swig外掛的使用也非常簡單。

    //swig編譯輸出html
    gulp.task('html', function() {
      return gulp.src('./template/*.html')
        .pipe(swig({
            defaults: {cache: false }
        }))
        .pipe(gulp.dest('./'))
    });

檔案版本控制

  之前編譯css和js的時候已經呼叫rev生成了記錄了版本資訊的json檔案,如果內容有變化它會生成新的md5檔名。這裡就是用rev-collector替換html檔案中除md5部分外相同檔名的連結,這樣就就實現了版本更新功能,用這個外掛可以更新css,js,圖片等的版本。

    //更新css和js版本,同時替換html中的連結標籤
    gulp.task('rev', scripts.concat(["css","html"]),function () {
        return gulp.src(['./rev/**/*.json', './*.html'])//add md5 suffix to js and css file, replace the link of html as well 
            .pipe( revCollector({
                replaceReved: true,
                dirReplacements: {
                    '/css': '/css',
                    '/js': '/js'
                }
            }))
            .pipe( gulp.dest('./dist') );
    });

html裡面替換後的連結格式如下

    <link rel="stylesheet" href="./css/app-d333f590b0.css">
    <script src="./js/app-62bad8e549.js"></script>

專案模版

  專案自動化配置完後,接著就是配置專案模版了,這裡分兩種型別的模版:1.移動端優先的單頁面;2.pc端優先的普通頁面。
swig標籤語法

  • extends 繼承父模板,必須在檔案最前
  • block 定義一個塊,使之可以被繼承的模板重寫,或者重寫父模板的同名塊
  • parent 將父模板中同名塊注入當前塊中
  • include 包含一個模板到當前位置

移動端單頁面模版

  在layout資料夾新建移動端的單頁面模版app-layout.html,然後再建立子目錄partial,用於存放公共程式碼塊。

  我們知道載入頁面時再去載入外部檔案是需要耗費載入時間,而頁面直出可以大大提高載入速度,同時預先載入部分樣式也可以避免載入時的白屏問題。這也是我們這裡將公共部分的js和css以程式碼塊的形式嵌入到html檔案中,達到頁面直出的效果。

  swig支援將html檔案嵌入,同時也可以將css檔案嵌入,我這裡就是把單頁面樣式檔案app.css和基礎樣式檔案app-base.css的內容直接輸出到style標籤中。主要的程式碼塊有:

  • meta.html: 頭部meta標籤
  • header-script.html: 主要是動態設定fontsize大小的指令碼
  • footer-script.html: 主要是頁面載入過程的動畫指令碼

  當然了圖示樣式,好看的字型檔案,單頁面的css,基礎的樣式css都有, 同時母版還挖出了幾個block給繼承的頁面進行重寫,比如block title,block css,block js,內容block content。css塊和js塊既可以寫外部連結也可以直接寫程式碼。 我們可以根據自己的需求進行修改和配置檔案,具體內容看如下程式碼。

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <title>{% block title %}{% endblock %}</title>
    {% include './partial/meta.html' %}
    <!-- 頁面內載入的頭部js -->
    {% include './partial/header-script.html' %}
    <!-- 頁面內載入的css -->
    <style>{% include '../lib/app/app.css' %}{% include '../lib/app-base.css' %}</style>
    <!-- 圖示 -->
    <link rel="stylesheet" href="./lib/fontello/fontello.css">
    <!-- 字型 -->
    <link rel="stylesheet" href="./lib/reboto/roboto.css">
    <!-- 專案css -->
    <link rel="stylesheet" href="./css/app.css">
    <!-- 外部css塊 -->
    {% block css %}{% endblock %}
    </head>
    <body>
    <div id="app" class="view-wrap"></div>
    <div id="loading" class="loading-wrap">
        <div class="loading">
            <div class="round"></div>
            <div class="txt">0%</div>
        </div>
    </div>
    <div id="freshing" class="fresh-wrap">
        <div class="loading">
            <div class="round"></div>
            <p class="txt">loading...</p>
        </div>
    </div>
    <!-- 內容填充塊 -->
    {% autoescape false %}
    {% block content %}{% endblock %}
    {% endautoescape %}
    <!-- 頁面內載入的尾部js -->
    {% include './partial/footer-script.html' %}
    <!-- 外部js塊 -->
    {% block js %}{% endblock %}
    </body>
    </html>

PC端優先模版

  接著就是建立PC端優先的模版layout.html,這個和單頁面模版相似,主要區別就是基礎樣式,同時不用嵌入移動端的程式碼,頁面模版請看如下程式碼:

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <title>{% block title %}{% endblock %}</title>
    {% include './partial/meta.html' %}
    <link rel="stylesheet" href="./lib/fontello/fontello.css">
    <link rel="stylesheet" href="./lib/reboto/roboto.css">
    <style>{% include '../lib/base.css' %}</style>
    <link rel="stylesheet" href="./css/index.css">
    {% block css %}{% endblock %}
    </head>
    <body>
    {% autoescape false %}
    {% block content %}{% endblock %}
    {% endautoescape %}
    {% include './partial/copyright.html' %}
    {% block js %}{% endblock %}
    </body>
    </html>

使用方式

  專案模板完成了,我以建立單頁面專案為例演示使用的步驟

  1. 首先從github clone模版到資料夾webapp-project,安裝相關npm包。
    ```bash
    git clone https://github.com/edwardzhong/project-template.git webapp-project

     npm install

    ```

  2. 在less資料夾內建立app.less,並編寫程式碼
    ```less
    @lightBlue:hsl(198, 73%, 53%);

     // base
     html,body{
         font-family: "Roboto", "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
         font-size: 0.25rem;
         color: #555;
         width: 100%;
         height: 100%;
     }
     body,p,h1,h2,h3,h4,h5,h6{margin:0;}
     ul,ol{list-type:none; margin:0; padding:0;}
     // todo more ...

    ```

  3. 在js資料夾建立app.js並編寫程式碼, 使用es6寫程式碼就是舒服?
    ```js
    require('../lib/zepto.js');// zepto沒有使用CommonJs規範,修改後使用require引入
    import { webApp } from '../lib/app/app.js'

     var App=webApp({
         container:'app',
         animate:{ effects:['slideInRight', 'slideOutLeft'],delay:600},
         preLoad:function(){
    
         },
         aftLoad:function(){
             loadPercent(100);
         }
     })
     .other('/index',{temId:'tempIndex',create:createIndex})
     .when('/button',{temId:'tempButton'})
     .when('/form',{temId:'tempForm'})
     .when('/dialog',{temId:'tempDialog',create:createDialog})
     .init();
    
     // todo: more ...

    ```

  4. 在template資料夾建立app.html,引用單頁面模板app-layout.html,並編寫內容,我這裡把單頁面的各個檢視程式碼也以程式碼塊的方式引入。
    html {% extends '../layout/app-layout.html' %} {% block title %}Web App{% endblock %} {% block js %} <script src="./js/app.js"></script> {% endblock %} {% block content %} {% include './partial/index.html' %} {% include './partial/button.html' %} {% include './partial/dialog.html' %} {% include './partial/form.html' %} {% endblock %}

  5. 執行自動化命令,進行編譯,合併,壓縮,打包
    bash npm run build
  6. 最後專案檔案都輸出到dist資料夾,找到app.html執行即可。

最後

  程式碼請看https://github.com/edwardzhong/project-template.git

相關文章