pringboot和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的静态资源处理前缀,代码:

@Configurationpublic 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; }}

过滤器的注册:

@SpringBootApplicationpublic 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:本方案只是在特定场景下的选择,如果一切条件允许,强力推荐做完全的前后端分离

springboot+vue的前后端分离与合并方案的更多相关文章

  1. 一套基于SpringBoot+Vue+Shiro 前后端分离 开发的代码生成器

    一.前言 最近花了一个月时间完成了一套基于Spring Boot+Vue+Shiro前后端分离的代码生成器,目前项目代码已基本完成 止步传统CRUD,进阶代码优化: 该项目可根据数据库字段动态生成 c ...

  2. SpringBoot+vue+Iview前后端分离权限内容管理CMS系统

    hrcms基于springBoot框架的内容管理系统,采用最新最主流的技术,后端采用spring boot,mybatis-plus,freemaker,shiro,redis,mysql,等,主要功 ...

  3. SpringBoot+Jpa+SpringSecurity+Redis+Vue的前后端分离开源系统

    项目简介: eladmin基于 Spring Boot 2.1.0 . Jpa. Spring Security.redis.Vue的前后端分离的后台管理系统,项目采用分模块开发方式, 权限控制采用 ...

  4. Flask & Vue 构建前后端分离的应用

    Flask & Vue 构建前后端分离的应用 最近在使用 Flask 制作基于 HTML5 的桌面应用,前面写过<用 Python 构建 web 应用>,借助于完善的 Flask ...

  5. gin+vue的前后端分离开源项目

    该项目是gin+vue的前后端分离项目,使用gorm访问MySQL,其中vue前端是使用vue-element-admin框架简单实现的: go后台使用jwt,对API接口进行权限控制.此外,Web页 ...

  6. dotnetcore vue+elementUI 前后端分离架二(后端篇)

    前言 最近几年前后端分离架构大行其道,而且各种框架也是层出不穷.本文通过dotnetcore +vue 来介绍 前后端分离架构实战. 涉及的技术栈 服务端技术 mysql 本项目使用mysql 作为持 ...

  7. Vue .Net 前后端分离框架搭建

    [参考]IntellIJ IDEA 配置 Vue 支持 打开Vue项目 一.前端开发环境搭建 1.零基础 Vue 开发环境搭建 打开运行Vue项目 2.nodejs http-proxy-middle ...

  8. 《论vue在前后端分离项目中的实践之年终总结》

    我是2014年的时候开始了解知道的vue,当时vue还不太成熟,想用但是又怕自己hold不住,况且那时候vue还没有成熟的(路由.验证.ui组件)插件,社区也是不温不火的,再说也没有合适的机遇让我去项 ...

  9. 【转】python+django+vue搭建前后端分离项目

    https://www.cnblogs.com/zhixi/p/9996832.html 以前一直是做基于PHP或JAVA的前后端分离开发,最近跟着python风搭建了一个基于django的前后端分享 ...

随机推荐

  1. Java习题10.25

    Java习题10.25 1. 实际上这道题考查的是两同两小一大原则: 方法名相同,参数类型相同 子类返回类型小于等于父类方法返回类型, 子类抛出异常小于等于父类方法抛出异常, 子类访问权限大于等于父类 ...

  2. 在fedora下面安装ftp服务器

    Fedora版本:Fedora 12 1. 安装vsftp #yum install vsftpd 一路yes,最后提示安装成功. 2. 配置vsftpd.conf. #vi /etc/vsftpd/ ...

  3. CPUID 指令的使用

    使用 CPUID 指令可以从 processor 厂商里获得关于 processor 的详细信息,CPUID 指令是从 Intel 486 处理器以后开始加入支持. 1. 检测处理器是否支持 cpui ...

  4. 拾遗:Unix 守护进程编写规范

    //标准库自带函数,通常以 daemon(0, 0) 方式调用 int daemon(int nochdir, int noclose) Linux: #include <unistd.h> ...

  5. seo具备的条件

    对于SEO这行业,许多想学习这一行,但是并非每一个人都有这样才能.因为SEO份这行靠是真材实料并非虚拟人才.现在找到高工资的SEO人才需要 有三年以上经验,熟悉PHP.html.asp.java等等这 ...

  6. Ubuntu安裝nginx-1.6.2

    1.在執行完./configure 和make install 後出現 test -d '/usr/local/nginx/logs'         || mkdir -p '/usr/local/ ...

  7. C++数据类型之字符型&转义字符

    字符型 **作用:** 字符型变量用于显示单个字符 **语法:**  char ch = 'a'; > 注意1:在显示字符型变量时,用单引号将字符括起来,不要用双引号 > 注意2:单引号内 ...

  8. USACO2007 捕牛记 /// queue+桶 oj1503

    题目大意: John和牛都站在一条直线上,开始时John位于坐标点N上( 0 ≤ N ≤ 100,000 ),牛位于坐标点K上( 0 ≤ K ≤ 100,000 ). John有两种行动方式:步行和瞬 ...

  9. drools原生drl规则文件的使用

    在初识drools中对drl文件进行了简单的介绍.这里举个例子来具体说明下.主要是写了规则之后我们如何用java代码来run起来. drl文件内容如下: rule "ageUp12" ...

  10. css---2D变形

    1.transfrom:rotate(360deg); 用前要加transition: 2s;                      deg重点 transform:rotate(angle); ...