java前端框架使用教程:新手完成第一张作品的方法

java前端框架使用教程:新手完成第一张作品的方法

java前端框架使用教程:新手完成第一张作品的方法

提到 Java 前端,很多人会想到用 Java 写接口,再用 Vue 或 React 写页面。但还有另一条路线:使用 Vaadin 这类 Java Web UI 框架,在 Java 代码中描述页面组件和交互,由框架负责将界面呈现在浏览器中。本文会带你从空项目做出一张可以添加、完成和删除任务的清单页面。

这里的“第一张作品”是一款适合练手的任务清单,不是完整的生产级任务管理系统。示例使用 Vaadin、Spring Boot 和 Java 17,数据暂存在内存中。照着创建文件后,可以在本机运行并通过浏览器查看效果。

一、先弄清楚:什么是 Java 前端框架

Vaadin 是一个用 Java 构建 Web 用户界面的框架。开发者可以在 Java 中创建输入框、按钮、表格等组件,设置它们的布局,并为交互注册事件。用户访问的是浏览器页面,应用运行在服务器端;框架负责组件状态与浏览器之间的通信。

这与“Java 后端加 Vue 前端”的项目结构不同。采用 Vaadin 时,常见页面逻辑和组件代码集中在 Java 项目中,不必为这张练习页面另外编写 Vue 单文件组件,也不必手动设计前后端 JSON 接口。但这不代表应用不使用浏览器技术:页面最终仍然在浏览器中显示,Vaadin 也会通过前端资源和通信机制完成界面交互。

需要注意,“Java 前端框架”不是一个边界严格、含义唯一的名称。本文选择 Vaadin,是因为它适合用 Java 快速搭建服务端驱动的 Web 界面,并不是说所有 Java 项目都应该采用这种方式。

二、适用场景与关键边界

比较适合的场景

  • 内部业务系统:例如管理后台、数据录入工具、审批流程页面。页面以表单、表格和业务操作为主时,用 Java 组件组织界面通常比较直接。
  • 团队熟悉 Java,但不想单独维护一套前端工程:原型或规模适中的应用可以减少前后端项目拆分带来的配置成本。
  • 需要快速验证业务交互:可以先用 Java 组件完成一条可运行的页面流程,再根据项目发展情况决定是否调整架构。

需要谨慎评估的场景

  • 高度定制的前端体验:如果页面依赖复杂动画、特殊图形交互、丰富的浏览器端状态管理,采用专门的前端框架可能更合适。
  • 公开网站的搜索引擎优化要求很高:应结合页面渲染方式、内容可发现性和实际搜索需求评估,不能只因为代码写在 Java 中,就默认页面一定适合 SEO。
  • 前后端必须独立演进或提供多端 API:例如同一套服务还要支持移动端、第三方接入,通常需要认真设计独立的 API 边界。UI 框架本身不能替代 API 设计。

还要区分“页面开发”和“业务安全”。Vaadin 能帮助开发者构建界面与组件交互,但不会自动替你完成登录授权、数据权限、输入校验、数据库事务或审计。隐藏一个按钮也不等于限制了接口访问;真正的权限检查必须在服务器端完成。

三、准备环境并创建项目

示例使用 Java 17、Maven、Vaadin 24.5.13 和 Spring Boot 3.4.4。安装 JDK 17 后,可在终端分别运行 java -version 和 mvn -version 检查环境。请确认 Maven 实际使用的 Java 版本也是 17 或更高。

创建如下目录结构:

java-first-work/
├── pom.xml
└── src/
    └── main/
        └── java/
            └── com/
                └── example/
                    └── demo/
                        ├── DemoApplication.java
                        └── TaskView.java

先创建 pom.xml。Vaadin 依赖通过 BOM 管理版本,示例固定了版本,方便复现;实际项目应根据团队的升级策略选择并维护依赖版本。

<?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>

    <groupId>com.example</groupId>
    <artifactId>java-first-work</artifactId>
    <version>1.0.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.4</version>
        <relativePath/>
    </parent>

    <properties>
        <java.version>17</java.version>
        <vaadin.version>24.5.13</vaadin.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.vaadin</groupId>
                <artifactId>vaadin-bom</artifactId>
                <version>${vaadin.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-spring-boot-starter</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

然后创建 Spring Boot 启动类 src/main/java/com/example/demo/DemoApplication.java:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

四、实现任务清单页面

接下来创建 TaskView.java。页面包含标题、任务输入框、添加按钮和任务表格。每条任务有完成状态,也可以删除。为了让示例独立运行,任务暂时存放在当前页面对象中的列表里。

package com.example.demo;

import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.Route;

import java.util.ArrayList;
import java.util.List;

@Route("")
public class TaskView extends VerticalLayout {

    private final List<Task> tasks = new ArrayList<>();
    private final Grid<Task> grid = new Grid<>(Task.class, false);

    public TaskView() {
        setSizeFull();
        setPadding(true);
        setSpacing(true);

        H2 title = new H2("我的任务清单");

        TextField taskInput = new TextField("新任务");
        taskInput.setPlaceholder("例如:整理本周计划");
        taskInput.setMaxLength(120);
        taskInput.setWidth("24em");

        Button addButton = new Button("添加任务", event -> {
            String text = taskInput.getValue().trim();

            if (text.isEmpty()) {
                Notification.show("请先填写任务内容");
                return;
            }

            tasks.add(new Task(text));
            refreshGrid();
            taskInput.clear();
            taskInput.focus();
        });

        taskInput.addKeyPressListener(
                com.vaadin.flow.component.Key.ENTER,
                event -> addButton.click()
        );

        grid.addColumn(Task::getDescription)
                .setHeader("任务")
                .setAutoWidth(true)
                .setFlexGrow(1);

        grid.addComponentColumn(task -> {
            Checkbox completed = new Checkbox("已完成");
            completed.setValue(task.isCompleted());
            completed.addValueChangeListener(event -> {
                task.setCompleted(Boolean.TRUE.equals(event.getValue()));
                refreshGrid();
            });
            return completed;
        }).setHeader("状态");

        grid.addComponentColumn(task -> new Button("删除", event -> {
            tasks.remove(task);
            refreshGrid();
            Notification.show("任务已删除");
        })).setHeader("操作");

        add(title, new HorizontalLayout(taskInput, addButton), grid);
        expand(grid);
        refreshGrid();
    }

    private void refreshGrid() {
        grid.setItems(tasks);
    }

    private static class Task {
        private final String description;
        private boolean completed;

        private Task(String description) {
            this.description = description;
        }

        public String getDescription() {
            return description;
        }

        public boolean isCompleted() {
            return completed;
        }

        public void setCompleted(boolean completed) {
            this.completed = completed;
        }
    }
}

代码中的 @Route("") 将页面映射到应用根路径。继承 VerticalLayout 后,可以用 add 将组件按垂直方向放入页面;输入框和按钮则放在 HorizontalLayout 中。Grid 负责展示任务列表,addComponentColumn 用来在表格单元格中放入可交互的组件。

setMaxLength(120) 为输入框设置客户端组件的最大长度,帮助用户避免输入过长内容。不过,界面上的长度限制不能替代服务端验证:如果数据还会通过其他入口写入,服务端仍应检查字符长度、空值和业务规则。这里的示例只处理当前页面中的输入,没有数据库或外部接口。

五、运行并验证

在项目根目录执行:

mvn spring-boot:run

首次启动时 Maven 需要下载依赖,等待应用启动完成后,在浏览器中打开 http://localhost:8080/。输入任务内容,点击“添加任务”或按回车;勾选“已完成”可以更新状态,点击“删除”可以移除任务。

也可以先打包,再运行生成的 JAR:

mvn clean package
java -jar target/java-first-work-1.0.0.jar

这份练习作品的数据仅保存在运行中的页面对象内存中。刷新页面、重新启动程序或切换到新的页面实例后,不能保证数据仍然存在。因此它适合学习组件、布局和事件,不适合作为需要持久化的真实任务系统。生产应用应通过服务层读写数据库,并根据实际需求处理用户身份、数据归属、并发修改和事务。

六、常见错误与排查方法

1. Java 版本和依赖版本不匹配

如果构建时报编译版本错误,先查看 java -version、mvn -version 和 pom.xml 中的 java.version。有时命令行使用了一个 JDK,IDE 却配置了另一个 JDK。不要只改代码来绕过版本问题;应确认所用 Vaadin、Spring Boot 与 JDK 版本彼此兼容。

2. 页面返回 404

检查启动类与 TaskView 的包路径。Spring Boot 默认扫描启动类所在包及其子包;如果页面放到了扫描范围之外,组件路由可能无法被发现。还要确认浏览器访问的是 http://localhost:8080/,以及控制台中没有启动失败信息。

3. 修改数据后表格没有更新

练习代码在增加、完成或删除任务后都会调用 refreshGrid()。使用组件状态时,要确认更新操作作用在当前页面实际使用的数据上,并按需要通知或刷新相应组件。若后续换成数据库服务,也要将查询结果重新交给表格,而不能只更新一个与表格无关的列表。

4. 把页面输入限制当成安全校验

setMaxLength 和非空判断改善了当前表单的使用体验,但不能代替完整的服务端验证,也不能防止所有绕过客户端的请求。涉及持久化时,应在服务层验证数据,并使用参数化数据库操作;涉及身份权限时,应在服务端对每次敏感操作授权。不要因为按钮没有显示,就认为用户无法访问对应的业务操作。

5. 误以为内存列表就是持久化存储

ArrayList 只存在于当前 JVM 的内存中。应用重启会丢失数据;如果把可变集合做成多个用户共享的全局状态,还可能造成用户数据串扰和并发问题。本例将列表放在页面实例中,只用于演示交互。正式项目应定义合适的服务层和数据模型,再选择数据库及访问方式。

6. 不检查组件版本就复制网上代码

不同版本的 Vaadin、Spring Boot 或 Java 可能存在 API 与配置差异。复制示例时,如果出现类找不到、方法签名不一致或依赖冲突,应先核对项目的依赖版本与文档版本,不要通过随意加入多套版本的依赖来消除报错。

七、下一步怎么扩展

完成第一张作品后,可以按业务复杂度逐步扩展:把 Task 移到独立文件,为任务加入创建时间和筛选状态,再增加服务类负责业务操作,最后接入数据库。每次只增加一个明确能力,并验证页面、服务和数据之间的边界,通常比一次性堆入登录、权限、数据库和复杂布局更容易排错。

若项目需要多人协作、复杂状态管理或独立前后端发布,也可以比较 Vaadin 与“Spring Boot API 加 Vue/React”的方案。选择框架时,重点不是代码写在哪种语言里,而是团队能否维护、架构是否满足产品需求,以及服务端的数据和安全边界是否清晰。

通过这个任务清单,你已经完成了一个可启动、可交互的 Java Web 页面:用组件搭布局,用事件响应操作,用表格展示数据。它是理解 Vaadin 工作方式的起点;要成为可交付的应用,还需要补上持久化、验证、权限和部署等工程环节。

© 版权声明

相关文章