最近接触到两个小项目,是用传统的开发方式,由服务端渲染页面,前端用到thymeleaf和jsp
虽然笔者也可以按照项目的架构搭一个阿帕奇服务器把java项目跑起来,但无疑是费时费力的,可能做完这两个项目换别的技术架构时,这个环境就没用了,又得装别的环境才能跑页面。而且在电脑装太多环境,电脑负担也很重。
有没有别的办法?我首先想到的是nodejs。
所谓传统开发,一般就是把静态页面先写好,等后端把页面路由弄好了,再将这些静态页面改成jsp等形式放到项目中。这些“套”好的页面,脱离了环境单独打开是不能查看的。这给分离开发带来了不便。
但我们可以用nodejs模拟一个服务,提供接口和测试数据,页面模板则使用nodejs第三方插件包转换一下,返回给前端。与搭建其它服务器相比,nodejs环境配置和建服务器更快速,切换模板引擎也更方便。虽然也涉及一些后端知识,但nodejs使用javascript,前端付出的学习成本更低。
下面说说如何搭建一个项目。
1搭建express服务器。
这里为了方便,我使用了应用生成器。
npm install express-generator -g
express server
cd server
npm install
为了更改项目后能自动重启服务,建议安装supervisor插件
npm install -g supervisor
打开package.json,更改start命令:
{
"name": "server",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "supervisor ./bin/www"
},
"dependencies": {
"cookie-parser": "~1.4.3",
"debug": "~2.6.9",
"express": "~4.16.0",
"http-errors": "~1.6.2",
"jade": "~1.11.0",
"morgan": "~1.9.0"
}
}
运行项目
npm start
打开浏览器http://localhost:3000
相关信息参考express官方网站
2 静态文件路径修改
通常传统的开发中,项目的页面也会放在一个固定的文件夹里,为了避免迁移时的不便,文件夹的结构应当保留,这需要在express服务器中做相关修改。
例如项目的文件放在project文件夹中,static存放静态文件,templates存放页面
首先配置静态文件路径
在app.js中修改
app.use(express.static(path.join(__dirname, 'project/static')));
3 模板插件
你能从上图看到home文件夹中有两个文件:
index.html使用的是thymeleaf模板,index.jsp使用的是jsp模板。 事实上你可以在这个项目中同时解析n种模板。
在express的官方文档中提到。
和 Express 兼容的模板引擎,比如 Jade,通过 res.render() 调用其导出方法 __express(filePath, options, callback) 渲染模板。
有一些模板引擎不遵循这种约定,Consolidate.js 能将 Node 中所有流行的模板引擎映射为这种约定,这样就可以和 Express 无缝衔接。
然而在 consolidate.js 插件文档中可以看到,并不支持thymeleaf和jsp模板
https://www.npmjs.com/package/consolidate
所以我们不能用express的render函数,改用直接读取文件,通过第三方插件转换的方法。
我们能很容易找到jsp和thymeleaf的npm 模板插件
jsp-js https://www.npmjs.com/package/jsp-js?activeTab=readme
thymeleaf https://www.npmjs.com/package/thymeleaf
安装模板引擎
npm i jsp-js --save
npm i thymeleaf --save
4 修改路由
路由非常关键,因为解析模板的代码都在这里,但你也可以另起文件处理,这里只是为了描述方便放在同一文件中。
在routes/index.js中加入:
var express = require('express');
var router = express.Router();
var fs = require('fs');
var path = require('path');
var relativePath = '../project/templates/'
// thymeleaf
var thymeleaf = require('thymeleaf');
var thymeleafTemplate = thymeleaf.TemplateEngine;
var StandardDialect = thymeleaf.StandardDialect;
var templateEngine = new thymeleafTemplate({
dialects: [
new StandardDialect('th')
]
});
function getThymeleaf(filePath, params, handle) {
var html = fs.readFileSync(path.join(__dirname, relativePath,filePath), {
encoding: 'utf-8'
});
html = handle(html);
return new Promise((resolve,reject) => {
templateEngine.process(html, params)
.then(result => {
resolve(result)
});
})
}
//jsp
const JSPJs = require('jsp-js').Renderer;
const jsp = new JSPJs({
root: [path.join(__dirname, relativePath)]
})
function getJsp(filePath, params, handle) {
return new Promise((resolve,reject) => {
var html = jsp.render(filePath, params);
html = handle(html);
resolve(html);
})
}
router.get('/thymeleaf', function(req, res, next) {
getThymeleaf('home/index.html', {
name: 'thymeleaf',
img: '/img/default.jpg'
}, function(html){
// 对读取到的页面进行预处理,以解决特殊问题,如 html = html.replace("${type}!='manage'", "true")
return html
})
.then(result => {
res.send(result)
});
});
router.get('/jsp', function(req, res, next) {
getJsp('home/index.jsp', {
name: 'jsp',
img: '/img/default.jpg'
}, function(html){
// 对读取到的页面进行预处理,以解决特殊问题,如 html = html.replace("<%=type!='manage'%>", "true")
return html
})
.then(result => {
res.send(result)
});
});
module.exports = router;
在实际使用中,笔者发现thymeleaf还是有些语法不能解析,因此在getThymeleaf函数和getJsp函数中加了一个handle参数,参数可以传入一个方法,对拿到的文件字符串进行处理,将需要解析的语法整串替换掉再返回,虽然粗暴,却也节省时间。
index.html 页面内容
Document
index.jsp页面内容
Document
<%=name%>
你能看到两种模板都成功解析了。
5.修改及迁移
如果后端已经“套”好页面,但样式需要修改,或者页面新增一些内容,你拿到项目文件之后,只需在express中做一些简单的配置就能把页面跑起来,而且你无须更改页面已经写好的逻辑。做完修改后,后端把页面放在项目中就能运行。
当然,严格来说,路由需要后端去写,这就不算是完全的前后端分离了。只是前端使用Nodejs模拟出一个服务,后端就无需关注前端页面,前端也不用等后端套页面时才发现问题。
然而将“套”页面的工作交给前端做,前端还需要熟悉该模板的语法,并且在建造模拟数据时要与后端沟通。
相关代码可从github上获取:
github地址