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就可以將前端的路由資源交給路由來處理了。至此整個完整前后端分離開發合并方案就完成了。這種方式在后期有條件情況下也可以很容易做到前后端的完全分離開發部署。
總結
以上所述是小編給大家介紹的spring boot+vue 的前后端分離與合并方案,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對億速云網站的支持!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。