springboot+thymeleaf工程中集成Vue3模板

springboot+thymeleaf工程中集成Vue3模板

概要

在springboot+thymeleaf的工程中,引入vue3并支持thymeleaf模板中嵌套vue模板,并且支持前端的组件式和模块式开发,适合前后端不分离的小型系统或网站,适合独立的开发者快速上手开发属于自己的项目。

技术栈

  • java 17
  • Springboot 3.0+
  • thymeleaf
  • vue3
  • element-plus

搭建介绍

1、 创建一个Springboot3工程

main
├── java --核心java代码
     ├── controller -- thymeleaf 模板试图控制层
├── resources -- 资源目录
     ├── config -- 配置文件
     ├── static -- 静态文件包含css,js 和 vue模板
             ├── components -- 组件依赖包含vue.js、element-plus、less以及vue3-sfc-loader
             ├── css -- 样式文件,可以引入less
             ├── js -- 自定义JS,支持模块化语法
             └── view -- vue 模板
     └── templates -- thymeleaf 模板

2、创建前端组件

各组件的下载地址:

  • unocss:https://unocss.jiangruyi.com
  • vue3: https://cn.vuejs.org
  • vue3-sfc-loader: https://gitcode.com/gh_mirrors/vu/vue3-sfc-loader
  • element-plus: https://element-plus.org/zh-CN
  • less: https://less.bootcss.com
    按下图所示将前端组件加入到工程目录下:
    在这里插入图片描述

3、编写一个vue模板作为系统主页面

这里使用了vue3,编写前可以先去了解一下Vue3的语法糖。vu3官网地址。取名为index.vue,放在工程目录resources/static/view下。虽然static下的文件可以被直接访问,但如果直接访问vue模板将缺少有关的加载组件,浏览器是无法成功加载,所以不用担心页面被直接访问从而泄露的问题。

<script>
import {defineAsyncComponent, onMounted, ref} from "vue"
import {ElMessage} from 'element-plus'
import {routes} from "../js/router.mjs";
import Btn1 from './btn1.vue'


export default {
  name: 'index',
  components: {
    Btn1
  },
  setup() {
    const message = ref('Hello Vue3')
    const currentComponent = ref(defineAsyncComponent(() => {
      return import(routes["test01"])
    }))

    onMounted(() => {
      console.log('index mounted')
    })


    const changeView = (name) => {
      currentComponent.value = defineAsyncComponent(() => {
        return import(routes[name])
      })
    }

    return {message, changeView, currentComponent}
  }
}
</script>

<template>
  <div class="main-page">
    <el-card>
      <template #header>
        element-plus 展示 {{ message }}
      </template>
      <div class="mb-4">
        <Btn1></Btn1>
      </div>

      <div class="p3">
        <el-button type="primary" @click="changeView('test01')">
          <el-icon class="mr1">
            <Promotion/>
          </el-icon>
          test01
        </el-button>

        <el-button type="primary" @click="changeView('test02')">
          <el-icon class="mr1">
            <Promotion/>
          </el-icon>
          test02
        </el-button>
      </div>

      <div class="p3">
        <component :is="currentComponent"></component>
      </div>

    </el-card>
  </div>
</template>

<style scoped lang="less">
.main-page {
  padding: 20px;
  height: 100%;
}
</style>

4、编写app.html

  app.html 为前端项目的主入口,需要引入上述加入到工程目录的组件(注意:这里css和js的引入需要用到thymeleaf 的相关语法)。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
    <meta content="width=device-width, initial-scale=1.0, user-scalable=no" name="viewport"/>
    <title>vue3-sfc-loader-demo</title>
    <link th:href="@{/components/element-plus/css/index.css}" rel="stylesheet">
    <link th:href="@{/css/loading.css}" rel="stylesheet">
</head>
<body>
<div id="app" un-cloak></div>
</body>

<script th:src="@{/components/unocss/runtime.js}"></script>
<script th:src="@{/components/vue3/index.js}"></script>
<script th:src="@{/components/vue3-sfc-loader/index.js}"></script>
<script th:src="@{/components/less/index-v4.js}"></script>
<script th:src="@{/components/element-plus/index.js}"></script>
<script th:src="@{/components/element-plus/icons.js}"></script>
<script th:src="@{/components/element-plus/zh_cn.js}"></script>
<script type="module">

    const {createApp, defineAsyncComponent} = Vue;
    const {loadModule} = window['vue3-sfc-loader'];

    const options = {
        moduleCache: {
            vue: Vue,
            "element-plus": ElementPlus,
            less: less
        },

        async getFile(url) {
            return await fetch(url).then(res => {
                if (!res.ok) throw Object.assign(new Error(res.statusText + " " + url), {res});
                return {
                    getContentData: asBinary => asBinary ? res.arrayBuffer() : res.text()
                };
            });
        },

        addStyle(textContent) {
            const style = Object.assign(document.createElement("style"), {textContent});
            const ref = document.head.getElementsByTagName("style")[0] || null;
            document.head.insertBefore(style, ref);
        }
    };
	
    const tmpPath = "view/index.vue"

    const app = createApp({
        components: {
            'app-component': defineAsyncComponent(() => loadModule(tmpPath, options))
        },
        template: '<app-component></app-component>'
    });

    app.use(ElementPlus, {
        locale: ElementPlusLocaleZhCn
    });

    for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
        app.component(key, component);
    }

    app.mount("#app")

</script>
</html>

const tmpPath = “view/index.vue” 这块为上面创建的vue模板路径,需要按照实际工程编写的目录而定。

5.映射app.html访问地址

package top.ycdev.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class ViewController {

    @RequestMapping("/index")
    public String index() {
        return "app";
    }
}

路径可以根据实际情况而定,这边使用了index,也可定义为“/”,这样就可以通过IP+端口直接访问。

6、编写application.yml

server:
  port: 8900

spring:
  application:
    name: demo
  thymeleaf:
    cache: false ##关闭缓存

注意:开发中可以关闭模板缓存,这样修改了页面可以实时更新,不用重启项目。

7、访问页面

   浏览器中访问 http://localhost:8900/index 即可。

效果展示

在这里插入图片描述

源代码

Gitee源代码地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值