After touching GraalJS, I will try SSR. I hope it will be helpful for people who want to SSR even in Java.
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.
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.
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.
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.
It's easier to do SSR using Nuxt or something.
Vue.js Server-Side Rendering Guide terwer/spring-vue-ssr
Recommended Posts