前端基於gulp後端基於freemarker的工做流程總結

前言

最近在作一個PC端的項目,因爲項目須要兼容到IE8,因此從技術選型上採起了公司以前一直沿用的前端基於gulp後端基於freemarker的模式來進行開發。javascript

那麼gulp+freemarker這種開發模式的流程究竟是怎樣的呢?我這邊就來簡單的分析一下。css

1、前端基於gulp

前端技術棧:html

  • gulp
  • jquery
  • ajax
  • less...

前端項目結構:前端

├── README.md                       項目介紹
├── src                             源碼目錄
│   ├── common 
        ├── less                    公共樣式
        ├── js                      公共js
        ├── plugins                 插件
項目公共文件   
│   ├── img                         圖片
│   ├── js                          js
│   ├── less                        樣式
├── .eslintrc.js                     eslint規則配置
├── package.json                     工程文件
├── gulpfile.js                      配置文件
├── server.js                        本地服務
複製代碼

從目錄來看,很是簡單,我這邊就主要來分析一下gulpfile.jsserver.jsvue

gulpfile.js

熟悉gulp的同窗都知道,通常咱們會將整個項目兩種環境來調用,即開發環境生產環境java

開發環境的配置:node

var gulp = require("gulp"),
    less = require("gulp-less"),
    clean = require("gulp-clean"),
    header = require("gulp-header");

/**
 * less 編譯
 * @return {[type]} [description]
 * 開發環境調用
 */
gulp.task("less", ["cleanCss"], function() {

    gulp.src(['src/less/*.less','src/common/less/*.less'])
        .pipe(plumber({
            errorHandler: errorHandler
        }))
        .pipe(less())
        .pipe(addHeader())
        .pipe(gulp.dest('dist/css'));

});
    
/**
 * js 編譯
 * @return {[type]} [description]
 * 開發環境調用
 */
gulp.task('js', ['cleanJs'], function() {
    gulp.src(['src/js/*.js', 'src/common/js/*.js'])
        .pipe(plumber({
            errorHandler: errorHandler
        }))
        .pipe(addHeader())
        .pipe(gulp.dest('dist/js'));

    gulp.src('src/common/plugins/*.js')
        .pipe(gulp.dest("dist/js/plugins"))
})

/**
 * img 輸出
 * @return {[type]} [description]
 * 開發環境調用
 */
gulp.task("imgOutput", ["cleanImg"], function(){

    gulp.src('src/img/**/*.*')
        .pipe(gulp.dest("dist/img"))

})
複製代碼

簡析上述代碼:react

在開發環境中咱們須要對咱們項目的src下的業務less、js、img和common下的公共less、js、img進行編譯打包,那麼咱們就須要藉助gulp.task()這個方法來創建一個編譯任務。建立完任務之後,咱們就須要經過gulp.src()來指向咱們須要編譯的文件jquery

最後咱們再經過gulp.pipe()來建立一個又一個咱們須要的管道,如webpack

gulp.pipe(plumber({errorHandler: errorHandler}))

function errorHandler(e) {
    // 控制檯發聲,錯誤時beep一下
    gutil.beep();
    gutil.log(e);
}
複製代碼

編譯的時候控制檯打印錯誤信息。

gulp.pipe(addHeader())

/**
 * 在文件頭部添加時間戳等信息
 */
var addHeader = function() {
    return header(banner, {
        pkg: config,
        moment: moment
    });
};

複製代碼

編譯之後在文件的頭部加上編譯時間

gulp.pipe(gulp.dest('dist/js'))

將編譯後的文件輸出到dist目錄下

生產環境的配置:

var gulp = require("gulp"),
    less = require("gulp-cssmin"),
    clean = require("gulp-uglify");
    header = require("gulp-header")
    
/**
 * css build
 * @return {[type]} [description]
 * 正式環境調用
 */
gulp.task("cssmin", ["cleanCss"], function() {

    gulp.src('src/common/less/all.base.less')
        .pipe(less())
        .pipe(cssmin())
        .pipe(rename({
            suffix: '.min'
        }))
        .pipe(addHeader())
        .pipe(gulp.dest("dist/css"));

    gulp.src('src/less/*.less')
        .pipe(less())
        .pipe(cssmin())
        .pipe(addHeader())
        .pipe(gulp.dest("dist/css"));

});

/**
 * js 編譯
 * @return {[type]} [description]
 * 正式環境調用
 */
gulp.task('jsmin', ['cleanJs'], function() {
    gulp.src(['src/js/**/*.js', 'src/common/js/**/*.js'])
        .pipe(plumber({
            errorHandler: errorHandler
        }))
        .pipe(uglify())
        .pipe(addHeader())
        .pipe(gulp.dest('dist/js'));

    gulp.src('src/common/plugins/**/*.js')
        .pipe(uglify({
            mangle: true
        }))
        .pipe(addHeader())
        .pipe(gulp.dest("dist/js/plugins"))
})
複製代碼

關於生產環境的配置其實跟上述的開發環境配置原理差很少,區別將在於生產環境中咱們須要藉助gulp-cssmin和gulp-uglify將css和js都進行壓縮,縮小文件的體積。

這裏提一下cleancss和cleanjs的意思,其實就是在咱們每一次編譯打包的時候將原來已經打包生成css和js都清理調,這樣保證咱們每次編譯打包的代碼都是最新的。

gulp.task("cleanCss", function() {
    return gulp.src('dist/css', {
        read: false
    }).pipe(clean());
});

gulp.task("cleanJs", function() {
    return gulp.src('dist/js', {
        read: false
    }).pipe(clean());
});

gulp.task("cleanImg", function() {
    return gulp.src('dist/img', {
        read: false
    }).pipe(clean());
});
複製代碼

開發環境監聽

gulp.task("watch", function() {

    livereload.listen();

    // 調用gulp-watch插件實現編譯有改動的LESS文件
    gulp.watch(['src/less/*.less','src/common/less/*.less'], function(file) {
        gulp.src(file.path)
            .pipe(plumber({
                errorHandler: errorHandler
            }))
            .pipe(less())
            .pipe(addHeader())
            .pipe(gulp.dest('dist/css'));
    });
    
    gulp.watch(['src/js/**/*.js', 'src/common/js/**/*.js'], function(file) {
        gulp.src(file.path)
            .pipe(gulp.dest("dist/js"))
    });

      // 監聽圖片改動
    gulp.watch('src/img/**/*.*', function(file){
        gulp.src(file.path)
            .pipe(gulp.dest("dist/img"))
    })

    // 監聽有變化的css,js,ftl文件,自動刷新頁面
    gulp.watch(['dist/**/*.css', 'dist/**/*.js',  ftlPath]).on('change', livereload.changed);

});
複製代碼

在開發項目的時候咱們須要藉助gulp.watch()來實時的監聽項目中代碼的改動,而且經過gulp-livereload這個插件來實時的刷新咱們的頁面,以提升咱們的開發效率。

說完gulpfile.js後咱們再來分析一下server.js

server.js

const path = require('path'),
	express = require('express'),
	proxy = require("express-http-proxy"),
	compress = require('compression'),
	app = express(),
	fs = require('fs'),
	config = require('./package.json'),
	projectName = config.name,
	port = process.env.PORT || '9091'

// GZIP壓縮
app.use(compress());

// 設置響應頭
app.use(function(req, res, next) {
	res.header('X-Powered-By', 'Express');
	res.header('Access-Control-Allow-Origin', '*');
	next();
})

// 當前靜態項目的資源訪問
app.use('/' + projectName, express.static('dist'));
app.use('/html',  express.static('src/pages'));

// 靜態服務器監聽
app.listen(port, '0.0.0.0', function() {
	console.log('static server running at ' + port)
})
複製代碼

這裏咱們經過node中express框架來爲咱們搭建本地服務,這裏重點提一下靜態資源項目的訪問

經過app.use()方法傳入兩個參數,其中projectName表明的是咱們在package.json中定義項目名稱,以下phip_ehr

{
  "name": "phip_ehr",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "gulp && gulp watch",
    "build": "NODE_ENV=production gulp build",
    "server": "node server.js"
  }
複製代碼

第二個參數express.static('dist')意思是將咱們的服務代理到編譯打包後的dist文件下如:http://192.168.128.68:9091/phip_ehr/js/**.js

這樣以來咱們將能夠輕鬆的獲取到整個項目下的全部靜態了。

2、後端基於freemarker

後端端技術棧:

  • java
  • freemarker ...

這裏後端的項目結構我這邊只截取跟咱們前端相關的目錄來講明

後端端項目結構:

├── templates                       項目模版
│   ├── home 
        ├── layouts                 頁面佈局
        ├── views                   業務代碼(ftl)
        ├── widgets                 項目依賴

複製代碼

layouts

default.ftl

<!DOCTYPE HTML>
<html>
	<head>
		<link rel="dns-prefetch" href="${staticServer}">
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		${widget("headInner",page.bodyAttributes)}
   		<#list page.styles as style>
	        <#if (style?index_of('http') > -1) >
	            <link href="${style}?v=${version}" rel="stylesheet" type="text/css" />
	        <#else>
	            <link href="${staticServer}/phip_ehr/css/${style}?v=${version}" rel="stylesheet" type="text/css" />
	        </#if>
	    </#list>
	</head>
	<body>
		${widget("header",page.bodyAttributes)}
		<div id='gc'>
			${placeholder}
		</div>
		${widget("footerJs")}
	</body>
</html>
複製代碼

上述代碼是整個項目頁面的佈局結構

${widget("headInner",page.bodyAttributes)}

這個方法意思是引入一些咱們前端靜態的公共樣式和一些公共的meta標籤。

headInner.ftl

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta property="wb:webmaster" content="3b0138a4c935e0f6" />
<meta property="qc:admins" content="341606771467510176375" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />

<link rel="stylesheet" href="${staticServer}/phip_ehr/css/reset.css?v=${version}" type="text/css"/>
<link rel="stylesheet" href="${staticServer}/phip_ehr/css/common.css?v=${version}" type="text/css"/>
複製代碼

這裏提一下${staticServer}這個固然指的就是咱們靜態域名了,須要在後端項目的配置文件config中來聲明,即指向咱們前端的靜態服務

**.mursi.attributesMap.staticServer=http://192.168.128.68:9091
**.mursi.attributesMap.imgServer=http://192.168.128.68:9091
複製代碼

引入完咱們的公共樣式之後,那接下來咱們業務樣式怎麼引入呢?

<#list page.styles as style>
    <#if (style?index_of('http') > -1) >
        <link href="${style}?v=${version}" rel="stylesheet" type="text/css" />
    <#else>
        <link href="${staticServer}/phip_ehr/css/${style}?v=${version}" rel="stylesheet" type="text/css" />
    </#if>
</#list>
複製代碼

這段代碼就是用來引入咱們的業務樣式的,意思是利用後端框架封裝的page這個對象中style屬性,而後對全部頁面的style標籤進行遍歷

而後在咱們業務代碼(ftl)中將能夠經過addstyle這個方法來引入咱們的業務樣式了

${page.addStyle("audit.css")}
複製代碼

${widget("header",page.bodyAttributes)}

這個方法的意思是引入咱們頁面中公共的頭部

${placeholder}

這個意思是引入咱們頁面主體內容部分

${widget("footerJs")}

這個意思是引入咱們頁面中js文件

footerJS.ftl

<script type="text/javascript">
    
    $GC = {
        debug: ${isDev!"false"},
        isLogined : ${isLogin!"false"},
        staticServer : '${staticServer}',
        imageServer : '${imageServer}',
        kanoServer : '${kanoServer}',
        version:"${version}",
        jspath:"${staticServer}" + "/phip_ehr/js"
    };

    // $GS { Array } - the init parameters for startup
    $GS = [$GC.jspath + "/plugins/jquery-1.8.1.min.js",
            $GC.jspath + "/GH.js?_=${version}",
            $GC.jspath + '/plugins/validator.js',function(){

            // load common module
            GL.load([GH.adaptModule("common")]);

             // load the modules defined in page
             var moduleName = $("#g-cfg").data("module");
             if(moduleName){
                var module = GH.modules[moduleName];
                if(!module) {
                    module = GH.adaptModule(moduleName);
                }
                if(module) {
                    GL.load([module]);
                }
            }

    }];
</script>

<!-- 引入js模塊加載器 -->
<script type="text/javascript" src="${staticServer}/phip_ehr/js/GL.js?_=${version}" ></script>

<script src="http://127.0.0.1:35729/livereload.js"></script>


複製代碼

這段代碼中$GC就指的是初始化一些變量,而後$GS中就是引入咱們項目中依賴的公共js,如jquery、common.js等。其次是經過GL.js這個模塊加載器來加載咱們的業務js

這樣咱們就能夠在咱們的業務ftl中經過data-moduls來引入每一個頁面中的業務js了

a.ftl

<div class="g-container gp-user-info J_UserInfo" id="g-cfg" data-module="a" data-fo-appcode="1" data-header-fixed="1" data-page="infirmary"></div>
複製代碼

a.js

GH.run(function() {
    GH.dispatcher('.J_Home', function() {
        return {
            init: function() {
                this.switchPatient();
            },

            /**
             * 
             * 切換就診人檔案
             */
             switchPatient: function() {
             	console.log(1);
             }
        }
    })
}, [GH.modules['validator'],GH.modules['datepicker']]);
複製代碼

dispatcher就是至關於頁面的分發器,固然每一個頁面只能擁有一個獨立的分發器,run()方法就是咱們封裝在GH.js的公共調用方法

那麼咱們項目中引入 一些公用的插件要怎麼引入呢?

那麼咱們就在GH.js裏封裝了GH.modules()方法來引入插件,這裏就不詳細的說明了。

這裏順帶也提一下ftl,什麼是ftl?ftl就是相似於咱們的html同樣,可是它不一樣的地方就是它是基於freemarker語法來進行編寫的一種後端模版引擎。咱們項目中一些能夠同步加載的數據均可以利用freemarker的語法在ftl中直接進行操做

小結:

這種先後端不分離的模式有哪些優缺點呢?

優勢:雖然在開發效率上比不上純先後端分離的模式(vue+webpack,react+webpack),可是針對一些對於兼容性要求很高的多頁項目,這種開發模式也是可取的。

缺點:對後端服務依賴太強,每每後端服務一旦出現報錯或者掛掉後,前端的工做就沒有辦法開展下去了,從而加大了先後端的開發成本。

相關文章
相關標籤/搜索