[JAVA] Attempt to SSR Vue.js with Spring Boot and GraalJS

Overview

After touching GraalJS, I will try SSR. I hope it will be helpful for people who want to SSR even in Java.

About GraalJS

GraalVM is a VM that can run JVM, JavaScript, Python, Ruby, etc. GraalJS is said to optimize performance by running in GraalVM environment (from official), but since it is written in Java, it is a normal JVM But it is possible to operate. This time I would like to try SSR using GraalJS in the JVM.

environment

JS file

I will not explain the settings on the JS side in detail, but prepare the JS files on the Server side and Client side with webpack according to the VueSSR Guide.

javascript:webpack.config.js


    node: {
        child_process: 'empty',
        fs: 'empty',
        net: 'empty',
        tls: 'empty',
    },

Add the above to work on the server side.

In the Java app, load the vue-server-renderer and Bundle file and render the HTML.

entry-server.js


import { createApp } from './app'

const { app, router, store } = createApp()

router.push(route)

router.onReady(() => {
    renderVueComponentToString(app, (err, res) => {
        rendered = String(res)
    })
})

I am writing a process to put the HTML acquired by renderVueComponentToString into the global variable rendered.

ScriptEngine Add a library to use GraalJS with Java's ScriptEngine.

pom.xml


		<!-- https://mvnrepository.com/artifact/org.graalvm.js/js -->
		<dependency>
			<groupId>org.graalvm.js</groupId>
			<artifactId>js</artifactId>
			<version>19.2.0</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.graalvm.js/js-scriptengine -->
		<dependency>
			<groupId>org.graalvm.js</groupId>
			<artifactId>js-scriptengine</artifactId>
			<version>19.2.0</version>
		</dependency>

Renderer.java


    public String render(String route) throws ScriptException, IOException {

        ScriptEngine engine = getEngine();
        ScriptContext context = getContext();
        Bindings engineScope = engineSetting(engine, context);
        engineScope.put("rendered", null);    //Global variable declaration
		engineScope.put("route", route);      //Global variable declaration
        engine.eval(read("static/js/server.js"), context);
        
		return context.getAttribute("rendered").toString(); //Get rendered variable to String type
	}

    private Bindings engineSetting(ScriptEngine engine, ScriptContext context) throws ScriptException, IOException {
        context.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
        Bindings engineScope = context.getBindings(ScriptContext.ENGINE_SCOPE);

        engine.setContext(context);

        engine.eval(
                "var process = { env: { VUE_ENV: 'server', NODE_ENV: 'production' }}; this.global = { process: process };",
                context);
        loadFiles(engine, context);  // vue-server-Loading renderer

        return engineScope;
    }

You can declare a global variable with the put method of the Bindings class and get the value with the getAttribute method of the ScriptContext class.

Renderer.java


    private ScriptEngine getEngine() {
        return new ScriptEngineManager().getEngineByName("graal.js");
    }

    private ScriptContext getContext() {
        return new SimpleScriptContext();
    }

To avoid the JS side becoming a singleton as described in the VueSSR guide, ScriptEngine processes it to create a new instance every time there is a request.

drawing

Draw the acquired HTML with Thymeleaf.

index.html


<body th:utext="${rendered}">
</body>

On the Vue side, please draw including the element ID used for drawing.

Initial data acquisition

The data at the time of initial drawing is acquired by writing JSON to the HTML file. This is achieved with thymeleaf.

index.html


    <script th:inline="javascript">
      /*<![CDATA[*/	    
      window.__INITIAL_STATE__ = /*[[${word}]]*/ {};
      /*]]>*/
    </script>

I have posted a Sample Project on github, so I hope you find it helpful.

Impressions

It's easier to do SSR using Nuxt or something.

reference

Vue.js Server-Side Rendering Guide terwer/spring-vue-ssr

Recommended Posts

Attempt to SSR Vue.js with Spring Boot and GraalJS
Until INSERT and SELECT to Postgres with Spring boot and thymeleaf
Connect to database with spring boot + spring jpa and CRUD operation
HTTPS with Spring Boot and Let's Encrypt
Add spring boot and gradle to eclipse
How to use built-in h2db with spring boot
Try to implement login function with Spring Boot
Try to automate migration with Spring Boot Flyway
[Java] Article to add validation with Spring Boot 2.3.1.
I wanted to gradle spring boot with multi-project
[Introduction to Spring Boot] Authentication function with Spring Security
Download with Spring Boot
Plans to support JDK 11 for Eclipse and Spring Boot
Try using DI container with Laravel and Spring Boot
Switch environment with Spring Boot application.properties and @Profile annotation
Automatically map DTOs to entities with Spring Boot API
Spring Security usage memo: Cooperation with Spring MVC and Boot
How to boot by environment with Spring Boot of Maven
Connect Spring Boot and Angular type-safely with OpenAPI Generator
Try Spring Boot from 0 to 100.
Generate barcode with Spring Boot
Hello World with Spring Boot
Get started with Spring boot
Hello World with Spring Boot!
Run LIFF with Spring Boot
SNS login with Spring Boot
Introduction to Spring Boot ① ~ DI ~
Introduction to Spring Boot ② ~ AOP ~
Spring Boot starting with Docker
Hello World with Spring Boot
Set cookies with Spring Boot
Use Spring JDBC with Spring Boot
Add module with Spring Boot
Getting Started with Spring Boot
Link API with Spring + Vue.js
Introduction to Spring Boot Part 1
Create microservices with Spring Boot
Send email with spring boot
Output embedded Tomcat access log to standard output with Spring Boot
Handle Java 8 date and time API with Thymeleaf with Spring Boot
Implement REST API with Spring Boot and JPA (Infrastructure layer)
Extract SQL to property file with jdbcTemplate of spring boot
How to call and use API in Java (Spring Boot)
Flow until output table data to view with Spring Boot
Implement REST API with Spring Boot and JPA (domain layer)
Domain Driven Development with Java and Spring Boot ~ Layers and Modules ~
I tried to get started with Swagger using Spring Boot
8 things to insert into DB using Spring Boot and JPA
With Spring boot, password is hashed and member registration & Spring security is used to implement login function.
Easily develop web applications with STS and Spring Boot. In 10 minutes.
Use Basic Authentication with Spring Boot
Introduction to Spring Boot x OpenAPI ~ OpenAPI made with Generation gap pattern ~
gRPC on Spring Boot with grpc-spring-boot-starter
Hot deploy with Spring Boot development
How to set Spring Boot + PostgreSQL
Spring Boot programming with VS Code
Until "Hello World" with Spring Boot
Inquiry application creation with Spring Boot
How to create your own Controller corresponding to / error with Spring Boot
Get validation results with Spring Boot
Compare Hello, world! In Spring Boot with Java, Kotlin and Groovy