前端基于gulp后端基于freemarker的工作流程总结

栏目: 编程工具 · 发布时间: 6年前

内容简介:最近在做一个PC端的项目,由于项目需要兼容到IE8,所以从技术选型上采取了公司之前一直沿用的前端基于gulp后端基于freemarker的模式来进行开发。那么gulp+freemarker这种开发模式的流程到底是怎样的呢?我这边就来简单的分析一下。前端技术栈:

前言

最近在做一个PC端的项目,由于项目需要兼容到IE8,所以从技术选型上采取了公司之前一直沿用的前端基于gulp后端基于freemarker的模式来进行开发。

那么gulp+freemarker这种开发模式的流程到底是怎样的呢?我这边就来简单的分析一下。

一、前端基于gulp

前端技术栈:

  • 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.js

gulpfile.js

熟悉gulp的同学都知道,一般我们会将整个项目两种环境来调用,即 开发环境生产环境

开发环境的配置:

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"))

})

简析上述代码:

在开发环境中我们需要对我们项目的src下的业务less、js、img和common下的公共less、js、img进行编译打包,那么我们就需要借助gulp.task()这个方法来建立一个编译任务。创建完任务以后,我们就需要通过gulp.src()来指向我们需要编译的文件

最后我们再通过gulp.pipe()来创建一个又一个我们需要的管道,如

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

这样以来我们将可以轻松的获取到整个项目下的所有静态了。

二、后端基于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),但是针对一些对于兼容性要求很高的多页项目,这种开发模式也是可取的。

缺点:对后端服务依赖太强,往往后端服务一旦出现报错或者挂掉后,前端的工作就没有办法开展下去了,从而加大了前后端的开发成本。


以上所述就是小编给大家介绍的《前端基于gulp后端基于freemarker的工作流程总结》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Writing Windows VxDs and Device Drivers, Second Edition

Writing Windows VxDs and Device Drivers, Second Edition

Karen Hazzah / CMP / 1996-01-12 / USD 54.95

Software developer and author Karen Hazzah expands her original treatise on device drivers in the second edition of "Writing Windows VxDs and Device Drivers." The book and companion disk include the a......一起来看看 《Writing Windows VxDs and Device Drivers, Second Edition》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具