微站点前后端体系建设
标签:
微站点之前基于前后端完全分离的策略,采用了angular框架作前端的基本架构,利用yeoman前端工具快速搭出脚手架。在顺利完成了几个版本的开发 后,出现了一些意想不到的变化:
- 我接手了后台接口的开发,采用nodejs后端,基本部署规参考express项目部署。
- angular的学习门槛比较高,初中级的前端不懂,也无法独立完成工作; 蛋疼的是,官方的文档还被墙封了(万恶的gfw ),所以一直是以来是我在开发与扩展。
- 无法平滑升级。
- 需求变动频繁,需要接入第三方的业务接口。
- SEO不友好,后期推广顾虑。
- 微信上的自定义分享比较麻烦,后期采用了微信的api,上了中控服务器,算是解决了。
所以迫切需要采用全新的架构来解决现有的问题。 需要解决的问题有:
- 直接在服务端直接输出模板。
- 实现依赖声明,按需加载。
- 方便协作开发。
- 手机端友好。
参考百度前端工具框架,张云龙的前端体系建设。【语言能力扩展】、【基于表的静态资源管理系统】与【前端资源聚合】是fis的一大亮点,前端领域语言只要扩展了资源定位、内容嵌入和依赖声明三种能力,就可以实现绝大多数前端开发需求。
后端还是采用sails,但要剥离自带的grunt前端任务管理。
- 使用 sails new microsite.vzhen –no-frontend 生成一个新的工程。
- 干掉Gruntfile.js文件,创建.sailsrc 文件编辑
"generators": { "modules": {} }, "hooks" : { "grunt": false },status: "develop"}
这样一来,就剥离了自带的grunt前端任务管理。
基于sails 的特点来定制目录规范,根据sails官方文档说明,静态资源管理的目录是 .tmp/public;模板不做发布,还是在views目录下。
- pages目录存放页面资源。
- components 存放模块化资源。
发布后,components的所有资源,pages的除了ejs模板外,都会发布到.tmp/public/ 目录,pages的ejs模板会发布到views。
- 编辑.gitignore 加上views目录
- fis-conf.js 的配置如下:
fis.config.set('project.include', ['common/**','components/**','pages/**']);
fis.config.set('roadmap.path', [
{
reg: /^\/pages\/(.*(?:\.(js|less|png|jpg|jpeg)))$/i,
release: '/.tmp/public/$1',
isPage: true,
url: '/$1',
id: '$1'
} ,
{
reg: /^\/pages\/(.*(?:\.ejs))$/i,
isHtmlLike: true,
useMap: true,
release: '/views/$1',
id: '$1'
} ,
{
reg: /^\/components\/(.*)$/i,
release: '/.tmp/public/components/$1',
url: '/components/$1',
isComponents: true
} ,
{
reg: '**',
useStandard: false,
useOptimizer: false
}
]);
fis.config.set('project.fileType.text', 'ejs');
fis.config.set('roadmap.ext.less', 'css');
fis.config.set('modules.parser.less','less');
fis.config.set('modules.postprocessor.js', function(content, file){
//只对模块化js文件进行包装
if(file.isPage ){
content = '(function(){' + content + '})(window)';
}
if (file.isComponents && file.filename !== "mod") {
content = 'define("' + file.id +
'", function(require,exports,module){' +
content + '});';
}
return content;
});
var createFrameworkConfig = function(ret, conf, settings, opt){
var map = {
res : {},
pkg : {}
};
fis.util.map(ret.map.res, function(id, res){
var r = map.res[id] = {};
if(res.deps) r.deps = res.deps;
//有打包的话就不要加url了,以减少map.js的体积
if(res.pkg) {
r.pkg = res.pkg;
} else {
r.url = res.uri;
}
});
fis.util.map(ret.map.pkg, function(id, res){
var r = map.pkg[id] = {};
r.url = res.uri;
if(res.deps) r.deps = res.deps;
});
fis.util.map(ret.src, function(subpath, file){
if(file.filename == 'layout'){
var content = file.getContent();
content = content.replace(/\b__FRAMEWORK_CONFIG__\b/g, JSON.stringify(map));
file.setContent(content);
}
});
}
fis.config.set('modules.postpackager', [createFrameworkConfig]);
接下来后台需要实现一个根据编译生成的map.json 文件实现收集依赖声明,从而实现按需引用的组件函数。
var has = {}, uris = { "css": [], "js": [] };
var _slice = Array.prototype.slice;
var fs = require('fs');
var map = JSON.parse(fs.readFileSync("map.json", "utf-8"));
function load_widget(id) {
has = {}, uris = { "css": [], "js": [] };
function _process(id) {
if (has[id]) { return; }
var resource = map["res"][id];
var pkg = map["pkg"];
if (!!resource) {
has[id] = true;
var type = resource["type"];
var uri = resource["uri"];
var pkg_key = resource["pkg"];
if (type == "css" || type == "js") {
if (!!pkg_key) {
uris[type].push(pkg[pkg_key]["uri"]);
pkg[pkg_key]["has"].forEach(function(val,index) {
has[val] = true;
});
}else {
uris[type].push(uri);
}
}
var deps = resource["deps"];
if (deps) {
for (var i = 0; i < deps.length; i++) {
_process(deps[i]);
}
}
}
}
_process(id);
}
module.exports = {
load_widget: load_widget,
getDeps: function() {
return {
css: _slice.call(uris["css"], 0).reverse(),
js: _slice.call(uris["js"], 0).reverse()
};
}
};
来看一下父版布局模板,css,js 都是动态输出的。
<!DOCTYPE html>
<html>
<head>
<title>医生微站点</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<% if(css && css.length > 0) { %>
<% for(var i = 0; i < css.length ;i++) { %>
<% var c = css[i] %>
<link rel="stylesheet" href="<%= c %>" type="text/css" media="screen" charset="utf-8">
<% } %>
<% } %>
</head>
<body>
<%- body %>
</body>
<script src="../components/modjs/mod.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">
require.resourceMap(__FRAMEWORK_CONFIG__);
</script>
<% if(js && js.length > 0) { %>
<% for(var i = 0; i < js.length ;i++) { %>
<% var j = js[i] %>
<script src="<%= j %>" type="text/javascript" charset="utf-8"></script>
<% } %>
<% } %>
</html>
部署时,ssh到服务器,定位到相关目录。执行 sh deploy.sh 就可以了。 开发时:
- nodemon ./app.js localhost 1341 启动sails 服务器