07月01, 2017

记前后分离实践

最近在跟进一个项目,需要扩展一些功能。

不过要做的第一步,先将前端分离出来,不然后端不是太方便集群。

之前的代码也差不多算前后分离,界面数据都是API接口返回,只是路由是定义在后端的而已。

java-project

上面是java的目录,ftl是FreeMarker模板。

说起来,还得感谢后台的哥们,这个前端代码写的还算比较可以,用到了requirejs。

其实移植到一个独立的前端项目,只需要改这样几个部分就行了:

  • 将ftl改成html后缀,将里面$baseUrl改掉,一般改成相对路径即可
  • 想办法将头部、左侧菜单抽离成模板

第一点,想必很多经历过java项目的前端,都可以看到如下类似的代码:

<script src="${base}/resources/uilib/common/jquery-2.1.0.min.js"></script>
<script src="${base}/resources/uilib/bootstrap-3/js/bootstrap.min.js"></script>
<script src="${base}/resources/uilib/requirejs/require.js"></script>
<script src="${base}/resources/vault/modules/security/tripledes.js"></script>

这个只需要将${base}批量改相对URL即可。

另外我们还需要设置一个全局的baseUrl,有两个目的:

  • 处理全局的location.href,都带上baseUrl
  • 劫持所有的a链接的link,带上baseUrl
  • requireJs,查找文件目录时,通过baseUrl来找

之所以考虑这个,是因为生成的项目不一定是放在根目录下的,一旦放在其他目录下,如果开始将URL路径写死,那后期就坑大发了。

var $baseUrl = "/";

// 处理超链接
function handleLinkUrl(elem) {
    var $this = $(elem);
    var link = $this.data("href");
    $this.attr("href", $baseUrl + link);
}

第二点,对于后端来说,还是蛮方便,像ftl模板里面,只需要这样写即可:

[#include "/admin/common/left.ftl"]

放到前端来的话,似乎不那么方便了。

目前前端主流的构建工具大概这样几种:gulp、fis3、webpack。

我一开始是想选择webpack的,但看了目前的结构及代码,发现并不是那么合适。

最终还是选择了fis3,它配置多页的代码还是相当容易的,嵌套HTML更是方便:

<!--inline[../../commonTpl/left.html]-->

以上的语法fis3能够支持,但似乎不推荐,不过我之前为了兼容相对路径的插件,才这样处理的,不知道现在这个bug有没有fix。

另外需要考虑几点:

  • 相对路径,fis3默认是生成绝对路径的URL,虽说有时候绝对比相对查找速度要快,但有时候难免会有相对路径的需求
  • 代理,我觉得能配nginx尽量配nginx吧,基本的代理FIS能满足,但像上传文件这种特殊的,就处理不了

所以下面就是我的fis-conf.js及nginx的配置:

fis.set('project.files', [

]);

/**
 * 设置忽略的文件
 * 参考:http://fis.baidu.com/fis3/docs/api/config-props.html#project.ignore
 */
fis.set('project.ignore', [
    'fis-conf.js',
    'package.json',
    '.git/**',
    '.svn/**',
    'node_modules/**',
    'psd/**',
    'bower.json',
    'README.md'
]);

//方案一
//fis.match('src/*/*.{css,js,html}',  {
//    standard: false
//});

// 方案二:启用插件
fis.hook('relative');

// 让所有文件,都使用相对路径。
fis.match('**', {
    relative: true
})


fis.match('::packager', {
    postpackager: fis.plugin('loader', {
        // 输出资源表类型
        // 根据你所使用的模块加载器进行配置
        //allInOne: true,
        resourceType: 'amd', // 取值[amd | cmd | mod | system]
        useInlineMap: true // config 配置文件内联输出
    })
});

var query = '?v=123456798';

// 应用占位符
fis.media('prod').match('*', {
    query: query
});

// 基本用法
fis.media('prod').match('::package', {
    // 默认query为md5
    postpackager: [
        fis.plugin('loader', {
            // 输出资源表类型
            // 根据你所使用的模块加载器进行配置
            //allInOne: true,
            resourceType: 'amd', // 取值[amd | cmd | mod | system]
            useInlineMap: true // config 配置文件内联输出
        }),
        fis.plugin('query-x', {
            placeholder: query // 这里传入占位符
        })
    ]
});

// 清除其他配置,只保留如下配置
fis.media('prod').match('*.js', {
    // fis-optimizer-uglify-js 插件进行压缩,已内置
    optimizer: fis.plugin('uglify-js')
});

fis.media('prod').match('*.css', {
    // fis-optimizer-clean-
    // css 插件进行压缩,已内置
    optimizer: fis.plugin('clean-css')
});

fis.media('prod').match('*.png', {
    // fis-optimizer-png-compressor 插件进行压缩,已内置
    optimizer: fis.plugin('png-compressor')
});
server {
        listen       8889;
        server_name  localhost;
        location / {
            proxy_pass   http://127.0.0.1:8600;       # fis3的端口号
            proxy_redirect              off;
            proxy_set_header            Host $host;
            proxy_set_header            X-Real-IP $remote_addr;
            proxy_set_header            X-Forwarded-For $proxy_add_x_forwarded_for;
        }  

        location ~ /vault/ {
            proxy_pass       http://127.0.0.1:8080;
            proxy_set_header            Host $host;
            proxy_set_header            X-Real-IP $remote_addr;
            proxy_set_header            X-Forwarded-For $proxy_add_x_forwarded_for;
        }

    }

好了,说一些坑吧。

我个人觉得多页有些地方不是太方便处理,比如说权限。

比如说某个菜单用户看不了,那么在render page的时候,隐藏即可。除了隐藏还得屏蔽URL,这个在多页里面处理会略麻烦。

需要每个页面挂载一个公共js,且要写在head中,不然的话,等用户看到页面了,才跳“您没有权限看到当前的页面”的page,不免显得有些太Low。

而单页则有一个路由的概念,在react中,可以通过onEnter来拦截每个路由,还是蛮方便的。

另外可能就是不管是多页还是单页都会存在的一个问题,比如说我们跳index.html。

这个放到前端来,可能跳index.html,然后ajax请求回来,说未登录,再跳回login.html。这样体验并不是太好,当然处理手段还是有的,比如说判断Cookie,或者是onEnter里面做请求拦截。

相反,如果是后端来render page的话,则可以在后端判断有没有登录,有的话则跳index.html,没有则跳login.html。

当然这个事,也可以交由Node来处理了。

本文链接:www.my-fe.pub/post/frontEnd-backEnd-split.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。