文档章节

springboot+vue的前后端分离与合并方案

上官胡闹
 上官胡闹
发布于 2017/11/23 00:05
字数 1541
阅读 7346
收藏 357

springboot和vue结合的方案网络上的主要有以下两种:

1. 【不推荐】在html中直接使用script标签引入vue和一些常用的组件,这种方式和以前传统的开发是一样的,只是可以很爽的使用vue的双向数据绑定,这种方式只适合于普通的全栈开发。

2.【推荐】使用vue官方的脚手架创建单独的前端工程项目,做到和后端完全独立开发和部署,后端单独部署一个纯restful的服务,而前端直接采用nginx来部署,这种称为完全的前后端分离架构开发模式,但是在分离中有很多api权限的问题需要解决,包括部署后的vue router路由需要在nginx中配置rewrite规则。这种前后端完全分离的架构也是目前互联网公司所采用的,后端服务器不再需要处理静态资源,也能减少后端服务器一些压力。

一、为什么做前后端分离开发合并

    在传统行业中很多是以项目思想来主导的,而不是产品,一个项目会卖给很多的客户,并且部署到客户本地的机房里。在一些传统行业里面,部署实施人员的技术无法和互联网公司的运维团队相比,由于各种不定的环境也无法做到自动构建,容器化部署等。因此在这种情况下尽量减少部署时的服务软件需求,打出的包数量也尽量少。针对这种情况这里采用的在开发中做到前后端独立开发,整个开发方式和上面提到的第二种方式是相同的,但是在后端springboot打包发布时将前端的构建输出一起打入,最后只需部署springboot的项目即可,无需再安装nginx服务器。

二、springboot和vue整合的关键操作

    实际上本文中这种前后端分离的开发,前端开发好后将build构建好的dist下static中的文件拷贝到springboot的resource的static下,index.html则直接拷贝到springboot的resource的static下。下面是示例图:

vue前端项目

springboot项目:

重点:上面这是最简单的合并方式,但是如果作为工程级的项目开发,并不推荐使用手工合并,也不推荐将前端代码构建后提交到springboot的resouce下,好的方式应该是保持前后端完全独立开发代码,项目代码互不影响,借助jenkins这样的构建工具在构建springboot时触发前端构建并编写自动化脚本将前端webpack构建好的资源拷贝到springboot下再进行jar的打包,最后就得到了一个完全包含前后端的springboot项目了。

三、整合的核心问题处理

    通过上面的整合后会出现两个比较大的问题:

    1. 无法正常访问静态资源 。

    2. vue router路由的路径无法正常解析 。

   解决第一个问题,我们必须重新指定springboot的静态资源处理前缀,代码:

@Configuration
public class SpringWebMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
        super.addResourceHandlers(registry);
    }
}

  解决第二个问题的方式是对vue的路由的路径做rewrite,交给router来处理,而不是springboot自己处理,rewrite时可以考虑路由的路径统一增加后缀,然后在springboot中编写过滤拦截特定后缀来做请求转发交给vue的路由处理。如:

const router = new VueRouter({
  mode: 'history',
  base: __dirname,
  routes: [
    {
      path: '/ui/first.vhtml',
      component: First
    },
    {
      path: '/ui/second.vhtml',
      component: secondcomponent
    }
  ]
})

后端拦截到带有vhtml的都交给router来处理,这种方式在后端写过滤器拦截后打包是完全可行的,但是前端开发的直接访问带后缀的路径会有问题。

另外一种方式是给前端的路由path统一加个前缀比如/ui,当然就可以把之前的后缀删除了,这时后端写过滤器匹配该前缀,也不会影响前端单独开发是的路由解析问题。过滤器参考如下:

/**
 * be used to rewrite vue router
 *
 * @author yu on 2017-11-22 19:47:23.
 */
public class RewriteFilter implements Filter {

    /**
     * 需要rewrite到的目的地址
     */
    public static final String REWRITE_TO = "rewriteUrl";

    /**
     * 拦截的url,url通配符之前用英文分号隔开
     */
    public static final String REWRITE_PATTERNS = "urlPatterns";

    private Set<String> urlPatterns = null;//配置url通配符

    private String rewriteTo = null;
    @Override
    public void init(FilterConfig cfg) throws ServletException {
        //初始化拦截配置
        rewriteTo = cfg.getInitParameter(REWRITE_TO);
        String exceptUrlString = cfg.getInitParameter(REWRITE_PATTERNS);
        if (StringUtil.isNotEmpty(exceptUrlString)) {
            urlPatterns = Collections.unmodifiableSet(
                    new HashSet<>(Arrays.asList(exceptUrlString.split(";", 0))));
        } else {
            urlPatterns = Collections.emptySet();
        }
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        String servletPath = request.getServletPath();
        String context = request.getContextPath();
        //匹配的路径重写
        if (isMatches(urlPatterns, servletPath)) {
            req.getRequestDispatcher(context+"/"+rewriteTo).forward(req, resp);
        }else{
            chain.doFilter(req, resp);
        }
    }

    @Override
    public void destroy() {

    }

    /**
     * 匹配返回true,不匹配返回false
     * @param patterns 正则表达式或通配符
     * @param url 请求的url
     * @return
     */
    private boolean isMatches(Set<String> patterns, String url) {
        if(null == patterns){
            return false;
        }
        for (String str : patterns) {
            if (str.endsWith("/*")) {
                String name = str.substring(0, str.length() - 2);
                if (url.contains(name)) {
                    return true;
                }
            } else {
                Pattern pattern = Pattern.compile(str);
                if (pattern.matcher(url).matches()) {
                    return true;
                }
            }
        }
        return false;
    }
}

过滤器的注册:

@SpringBootApplication
public class SpringBootMainApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootMainApplication.class, args);
    }

    @Bean
    public EmbeddedServletContainerCustomizer containerCustomizer() {
        return (container -> {
                ErrorPage error401Page = new ErrorPage(HttpStatus.UNAUTHORIZED, "/errors/401.html");
                ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/errors/404.html");
                ErrorPage error500Page = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/errors/500.html");
                container.addErrorPages(error401Page, error404Page, error500Page);
        });
    }
    @Bean
    public FilterRegistrationBean testFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new RewriteFilter());//注册rewrite过滤器
        registration.addUrlPatterns("/*");
        registration.addInitParameter(RewriteFilter.REWRITE_TO,"/index.html");
        registration.addInitParameter(RewriteFilter.REWRITE_PATTERNS, "/ui/*");
        registration.setName("rewriteFilter");
        registration.setOrder(1);
        return registration;
    }
}

这时springboot就可以将前端的路由资源交给路由来处理了。至此整个完整前后端分离开发合并方案就完成了。这种方式在后期有条件情况下也可以很容易做到前后端的完全分离开发部署。

ps:本方案只是在特定场景下的选择,如果一切条件允许,强力推荐做完全的前后端分离

版权申明:转载请注明出处,否则后果自负

© 著作权归作者所有

共有 人打赏支持
上官胡闹
粉丝 49
博文 81
码字总数 57419
作品 1
成都
程序员
加载中

评论(30)

foreach
foreach
这个我理解就是在 前后端不分离,和前后端完全分离 之间做了一个折衷。
无心robbot
无心robbot
完全的前后端分离,还是有必要的
麦小猪
麦小猪
说了这么多,至少给个demo吧
呼呼南风
呼呼南风

引用来自“呼呼南风”的评论

表示没有看懂。

引用来自“上官胡闹”的评论

可能是我描述的不够清晰
:smile:大家都是码农,前后端分离的目的是方便各自的开发,这种场景着重体现在移动开发或者微信开发。后端开发人员看到这种模式,具有绝对的优势,因为这几年的前端框架逐渐成熟,比如:路由、自动化等等。但是对于后端人员想开发一个系统,那么就面临着如果把这种成熟的框架利用起来。前后分离最核心的问题就是前后代码解耦,可是后端开发人员如何实现方便的联调呢?这时候webpack出现了,解决了这个问题,它集成了代理,并且maven可以操作node,控制前端打包、实时的生成编译之后的文件,同时拷贝到指定的目录。说了这么多,重点是什么?那么maven怎么调用webpack的打包、编译、拷贝等等。
j
jamen-chen
http://gitee.com/jamen/slife
Spring Boot搭建的脚手架
米老头
很low
上官胡闹
上官胡闹

引用来自“呼呼南风”的评论

表示没有看懂。
可能是我描述的不够清晰
呼呼南风
呼呼南风
表示没有看懂。
rockingMan
rockingMan
jhipster
tkggusraqk
tkggusraqk
这个搞的有点复杂了,直接webpack配置好,build,然后jek合并过去就可以了
J2EE Velocity 前端集成方案--jello

jello 是针对服务端为 JAVA + Velocity 的前端集成解决方案。jello 为优化前端开发而生,提供前后端开发分离、自动性能优化、模块化开发机制等功能。 前后端分离 基于 velocity 模板引擎实现...

叶秀兰
2014/07/28
3.3K
0
前后端分离到前后端整合进行开发

首先我是很赞同业界的前后端分离的开发模式,虽然现在都讲究全栈工程师,但是毕竟术业有专攻,前端同学专注多终端,后端同学关注高性能高可用。大家各自再自己的专注点上发光发热。 想想我们...

王小明123
2016/08/01
1K
4
前后端分离

前后端分离实践有感 前后端分离并不是什么新鲜事,到处都是前后端分离的实践。然而一些历史项目在从一体化 Web 设计转向前后端分离的架构时,仍然不可避免的会遇到各种各样的问题。由于层出不...

晨曦艾伯特
01/11
0
0
前后端分离,最佳实践

文章目录 1.前后端分离是什么 2.为什么需要前后端分离 3.前后端分离,最佳实践 3.1.简单分离模式 3.2.服务端渲染模式 3.2.1.方式一,JSP渲染 3.2.2.方式二,静态页渲染 3.3.Node.js渲染模式 ...

郭恩洲_OSC博客
2016/11/21
30
0
Spring Boot + Vue前后端分离开发实战

前后端分离是一种开发方式,已经变得越来越流行了,越来越多的企业都开始往这个方向靠拢。那么,为什么要选择前后端分离呢?前后端分离对实际开发有什么好处呢? 前后端分离可以让专人做专事,...

尹吉欢
06/10
0
0

没有更多内容

加载失败,请刷新页面

加载更多

你为什么在Redis里读到了本应过期的数据

一个事故的故事 晚上睡的正香突然被电话吵醒,对面是开发焦急的声音:我们的程序在访问redis的时候读到了本应过期的key导致整个业务逻辑出了问题,需要马上解决。 看到这里你可能会想:这是不...

IT--小哥
今天
2
0
祝大家节日快乐,阖家幸福! centos GnuTLS 漏洞

yum update -y gnutls 修复了GnuTLS 漏洞。更新到最新 gnutls.x86_64 0:2.12.23-22.el6 版本

yizhichao
昨天
3
0
Scrapy 1.5.0之选择器

构造选择器 Scrapy选择器是通过文本(Text)或 TextResponse 对象构造的 Selector 类的实例。 它根据输入类型自动选择最佳的解析规则(XML vs HTML): >>> from scrapy.selector import Sele...

Eappo_Geng
昨天
2
0
Windows下Git多账号配置,同一电脑多个ssh-key的管理

Windows下Git多账号配置,同一电脑多个ssh-key的管理   这一篇文章是对上一篇文章《Git-TortoiseGit完整配置流程》的拓展,所以需要对上一篇文章有所了解,当然直接往下看也可以,其中也有...

morpheusWB
昨天
5
0
中秋快乐!!!

HiBlock
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部