新建一个springboot和vue前后端分离项目,并集成软件包构建。
简单的说就是利用maven的ant插件调用node的npm对web项目构建后,拷贝到spring boot项目下,再打包。
这样通过maven命令可以一次性完成前后端分离项目的构建。
预置条件
请自行安装idea、Webstorm 、jdk、nodejs以及maven
-
jdk11
-
maven
配置阿里云镜像,本人使用的settings.xml如下
<?xml version="1.0" encoding="UTF-8"?> <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> <localRepository>D:\myMvnRepo</localRepository> <pluginGroups> </pluginGroups> <proxies> </proxies> <servers> </servers> <mirrors> <mirror> <id>alimaven</id> <name>aliyun maven</name> <url>https://maven.aliyun.com/repository/central</url> <mirrorOf>central</mirrorOf> </mirror> </mirrors> <profiles> <profile> <id>spring</id> <activation> <jdk>11</jdk> </activation> <repositories> <repository> <id>spring</id> <url>https://maven.aliyun.com/repository/spring</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories> </profile> </profiles> </settings>
-
node 16
配置cnpm,则后文的npm命令都是cnmp代替了。
npm install -g cnpm --registry=https://registry.npm.taobao.org
springboot hello world
-
创建项目
手动配置maven项目成spring-boot项目当然也是可以的。或者从官网初始化
这里我们利用idea封装的新建spring-boot项目选项。
选择java、maven 、jdk11
点击next ,本人选择的spring boot版本为 2.6.7,添加了一个依赖 Spring web
此后单击Create便可新建一个项目。
请注意如果您使用的settings.xml如果不在当前用户目录下,则不是当前用户全局生效的。可以单独为该项目设置,打开idea 的setting ,File -> Setting (Ctrl+Alt + S),进入 Build,Execution,Deployment > Build Tools > Maven 选择 User settings file后的override,便可选择文件,指定settings.xml。
-
编写服务端代码
找到自行设置包名唯一的java文件,这里省去新建Controller,直接复用这个文件,
在类声明上为其加上
@RestController
注解。
然后使用@GetMapping
注解hello方法,表示对/helloworld
这个地址的get请求 映射当前方法。方便前端调试,可以先允许跨域,故对Controller使用
@CrossOrigin
注解。
总体如下(注意package
,以自己的为准),服务端编写完成。package com.example.springboot.hello; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @RestController @CrossOrigin public class HelloSpringBootApplication { public static void main(String[] args) { SpringApplication.run(HelloSpringBootApplication.class, args); } @GetMapping("/helloworld") public String hello() { return "Hello World!"; } }
-
运行
有main方法的java类的idea会生成执行按钮图标,或者在打开那个唯一java文件的界面ctrl+shift+F10便可构建并运行服务端。
注意
后文将使用npm 运行web服务 ,默认端口也是
8080
。虽然遇到8080被占用的情况,会自动端口数值增加来绑定。但springboot作为服务端默认端口8080,服务端自动绑定端口是不安全的,若需要指定springboot的web端口。
在
src/main/resources/application.properties
中 指定,例如指定为8000server.port=8000
访问
http://127.0.0.1:8000/helloworld
可以看到 服务端的响应
vue hello world
vue4 elementUI搭建
以下过程可能因为各种模块版本变化而失败。
-
cnpm安装vue
cnpm install vue
-
创建项目
这里直接使用webstorm创建一个项目名为
hello-vue
。这里看起来是vue5创建时是4. -
webstorm设置cnpm
webstorm 上File -> Setting 打开Setting
直接搜索npm或者 进入Languages & Frameworks >Node.js and NPM
选中Package manager
选择文件。
此时建议显示隐藏文件(不出意外cnpm在隐藏文件里)。
本人路径如下
C:\Users\immor\AppData\Roaming\npm\node_modules\cnpm
可输入以下内容代替上述地址,则不会因为用户名不一样而导致路径找不到。
~\AppData\Roaming\npm\node_modules\cnpm
-
添加element ui
vue可以通过ui添加 或者命令添加见element ui的readme
这里项目已经创建好,跳过新建,直接添加即可,在刚才创建的项目下执行以下命令。
vue add element
此时提示部分引入还是全部引入
可以使用上下键选择,按
enter
确定这里我选择Fully import.
后续的确认选项本人选择如下:
? Do you wish to overwrite Element's SCSS variables? Yes ? Choose the locale you want to load zh-CN
然后报错了
npm ERR! code 1 npm ERR! path D:\Users\immor\idea\hello-vue\node_modules\node-sass
看到node-sass和sass-loader以及element-ui没有装上
这里我们修改一下package.json中依赖的版本号,高版本解析elements添加后的src/element-variables.scss会出错,以下版本号目前可行。"node-sass": "^6.0.1", "sass-loader": "^10.2.0",
再使用cnpm安装相关依赖
cnpm install
此时运行配置里面有npm serve项,可以点击运行,也可以使用
cnpm run serve
运行 -
Vue里使用elementUI
在main.js确保含有ElementUI 必要的导包
然后全部引用只需
Vue.use(ElementUI);
(此外,为了方便后文不重复贴代码,还导入了axios,并赋值到Vue的prototype中 )
main.js示例如下:
import Vue from 'vue' import App from './App.vue' import './plugins/element.js' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' import axios from 'axios' Vue.use(ElementUI) Vue.config.productionTip = false Vue.prototype.$http = axios new Vue({ render: h => h(App), }).$mount('#app')
-
Vue中使用ElementUI按钮和axios发送请求到springboot服务端
此时需要往package.json添加axios的依赖
往
devDependencies
节点下添加一条记录
"axios": "^0.27.2"
注意逗号将新建项目生成的src/App.vue 进行删减,只剩一个图标和大体框架。目前内容就导入了一个HelloWorld.vue
<template> <div id="app"> <img src="./assets/logo.png"> <HelloWorld msg="Welcome to Your Vue.js App"/> </div> </template> <script> import HelloWorld from './components/HelloWorld.vue' export default { name: 'app', components: { HelloWorld } } </script> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
修改components/HelloWorld.vue添加一个ElementsUI的按钮,并将点击事件设置为请求前述服务端的
/helloworld
地址.<template> <div> <el-button v-on:click="sendReq" type="primary">hello world</el-button> </div> </template> <script> export default { name: 'HelloWorld', props: { msg: String }, methods: { sendReq: function () { let url = '/helloworld'; // 兼容开发模式跨域调用,建议构建前删掉该if语块 if (process.env.NODE_ENV === 'development') { url = 'http://' + location.hostname + ':8000' + url; } this.$http.get(url, { }).then((rsp) => { this.$message({ message: 'server say:' + rsp['data'], type: 'success' }) }).catch((err) => { this.$message.error('发生异常:' + err); }) } } } </script>
正常情况,点击hello world按钮会收到服务端的响应
配置总体构建
Springboot对服务端项目构建已经在springboot插件里面集成好了,根据开始的选择,执行mvn package
就能打包。
但前后端分离项目,需要前端编译为静态页面后丢进 src/main/resources/static,再执行mvn package
人肉执行npm 构建再复制再执行mvn命令,也是可以的。
这里我们使用maven的ant插件代劳
-
引入ant插件
在springboot 项目的pom.xml build>plugins中新增plugin,
<artifactId>maven-antrun-plugin</artifactId> <version>1.8</version>
但需要运行配置,运行配置里面可以写ant的target但idea并不能很好识别maven的ant配置。复杂的ant可以链接到ant文件。
大体上如下(后面会进一步修改,这里不用复制)
<plugin> <artifactId>maven-antrun-plugin</artifactId> <version>1.8</version> <executions> <execution> <id>build web</id> <phase>generate-resources</phase> <goals> <goal>run</goal> </goals> <configuration> <target name="npm build web"> <echo>use npm:${npm.name}</echo> <echo>web ui source in : ${web.ui.dir}</echo> <echo>java static source dir is : ${java.web.res}</echo> <property name="npm" value="${npm.name}"/> <property name="web.ui.dir" value="${web.ui.dir}"/> <property name="java.web.res" value="${java.web.res}"/> <ant antfile="build.xml"/> </target> </configuration> </execution> <execution> <id>clean</id> <phase>clean</phase> <goals> <goal>run</goal> </goals> <configuration> <target name="clean web static"> <echo>clean web static</echo> <delete verbose="true" includeEmptyDirs="true"> <fileset dir="${java.web.res}"> <include name="**/*"/> <exclude name=".gitkeep"/> </fileset> </delete> </target> </configuration> </execution> </executions> </plugin>
配置了两个
在 generate-resources阶段,执行npm 去构建前端项目。并将生成的文件复制到在static目录。
clean阶段则删除复制过来的文件。
其中generate-resources 内容主要是传递三个变量(这三个变量,在maven的properties里面声明,后文贴出完整pom会有)给ant再调用build.xml。
build.xml内容如下,主要是在不同操作系统下用不同的命令行调用
npm run build
<?xml version="1.0" encoding="UTF-8" ?> <project name="npm" default="build"> <condition property="isMac"> <os family="mac"/> </condition> <condition property="isWindows"> <os family="windows"/> </condition> <condition property="isUnix"> <os family="unix"/> </condition> <target name="copyBuild"> <echo>clean ${java.web.res}</echo> <delete verbose="true" includeEmptyDirs="true"> <fileset dir="${java.web.res}"> <include name="**/*"/> <exclude name=".gitignore"/> </fileset> </delete> <echo>copy build result to ${java.web.res}</echo> <copy todir="${java.web.res}"> <fileset dir="${web.ui.dir}/dist"> <include name="**/*.*"/> </fileset> </copy> </target> <target name="doMac" if="isMac"> <echo message="build at mac target "/> </target> <target name="doWindows" if="isWindows"> <echo message="build at windows target ;web src in:${web.ui.dir}"/> <exec executable="cmd.exe"> <arg line="/c cd /d ${web.ui.dir} && ${npm} run build "/> </exec> <antcall target="copyBuild"/> </target> <target name="doUnix" if="isUnix"> <echo message="build at Unix target;web src in:${web.ui.dir}"/> <echo>cmd is:cd ${web.ui.dir} && ${npm} run build</echo> <exec executable="/bin/sh"> <arg line="-c " cd ${web.ui.dir} && ${npm} run build ""/> </exec> <antcall target="copyBuild"/> </target> <target name="build" depends="doMac, doWindows, doUnix" description="Public target"><!-- set the operating system test properties --> <echo message="build webui success!"/> </target> </project>
-
使用profile控制构建web的开关
按照上述方法设置的pom文件每次构建java都需要构建web。
npm构建项目本身会判断是否增量,第二次没有首次花费时间,但依然没有必要每次构建web。ant里面调用npm和复制做成动态也可以通过一些判断实现,但这里我们可以通过maven的profile来控制web的构建。
将pom中的ant插件配置放置在一个名为
buildWeb
的profile中,总体的pom如下:关于properties配置注意事项,前文创建web项目名为hello-vue,和springboot文件是平级。故在
properties
标签里设置了web.ui.dir
的值为${basedir}/../hello-vue
通过当前根目录退到上一级再找到 hello-vue文件夹。这里请根据自己放置web代码实际位置配置。npm.name 这里也是配做 cnpm
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.7</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>hello-spring-boot</artifactId> <version>0.0.1-SNAPSHOT</version> <name>hello-spring-boot</name> <description>hello-spring-boot</description> <properties> <java.version>11</java.version> <npm.name>cnpm</npm.name> <web.ui.dir>${basedir}/../hello-vue</web.ui.dir> <java.web.res>${basedir}/src/main/resources/static</java.web.res> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <profiles> <profile> <id>buildWeb</id> <build> <plugins> <plugin> <artifactId>maven-antrun-plugin</artifactId> <version>1.8</version> <executions> <execution> <id>build web</id> <phase>generate-resources</phase> <goals> <goal>run</goal> </goals> <configuration> <target name="npm build web"> <echo>use npm:${npm.name}</echo> <echo>web ui source in : ${web.ui.dir}</echo> <echo>java static source dir is : ${java.web.res}</echo> <property name="npm" value="${npm.name}"/> <property name="web.ui.dir" value="${web.ui.dir}"/> <property name="java.web.res" value="${java.web.res}"/> <ant antfile="build.xml"/> </target> </configuration> </execution> <execution> <id>clean</id> <phase>clean</phase> <goals> <goal>run</goal> </goals> <configuration> <target name="clean web static"> <echo>clean web static</echo> <delete verbose="true" includeEmptyDirs="true"> <fileset dir="${java.web.res}"> <include name="**/*"/> <exclude name=".gitkeep"/> </fileset> </delete> </target> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile> </profiles> </project>
此时没有配置为默认激活buildWeb
-
idea 激活profile
可以看到idea的maven插件界面展开Profiles可以看到刚才配置的buildWeb
我们勾选该项,再点击package便可触发,构建web。
构建一次之后,static目录下有了内容,后续仅仅编译java时,则不需要勾选此项。
-
命令构建激活profile
使用mvn命令时 使用
-P
指定激活的profilemvn -P buildWeb clean mvn -P buildWeb install
-
-
降低耦合的建议
将构建web任务单独作为maven模块,然后web服务的模块编译依赖该模块,便能触发先构建web ui再编译和打包web服务。
或者使用gradle同时替代maven与ant。
运行
构建出的jar包脱离ide运行,只需jre即可。在target目录生成了一个jar包,使用java -jar执行。
jar -jar hello-spring-boot-0.0.1-SNAPSHOT.jar