Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VueJs: Error with server side rendering and Typescript

I am trying to build a stack using Server Side Rendering (SSR) and Typescript. Everything seems fine but I got the error: TypeError: Cannot read property 'render' of undefined. Here is the full stack trace:

TypeError: Cannot read property 'render' of undefined
    at normalizeRender (/Users/shoun/Documents/repositories/vuejs-ssr-typescript/node_modules/vue-server-renderer/build.js:6621:19)
    at render (/Users/shoun/Documents/repositories/vuejs-ssr-typescript/node_modules/vue-server-renderer/build.js:6840:5)
    at Object.renderToString (/Users/shoun/Documents/repositories/vuejs-ssr-typescript/node_modules/vue-server-renderer/build.js:6871:9)
    at /Users/shoun/Documents/repositories/vuejs-ssr-typescript/dist/server.js:16:14
    at Layer.handle [as handle_request] (/Users/shoun/Documents/repositories/vuejs-ssr-typescript/node_modules/express/lib/router/layer.js:95:5)
    at next (/Users/shoun/Documents/repositories/vuejs-ssr-typescript/node_modules/express/lib/router/route.js:137:13)
    at Route.dispatch (/Users/shoun/Documents/repositories/vuejs-ssr-typescript/node_modules/express/lib/router/route.js:112:3)
    at Layer.handle [as handle_request] (/Users/shoun/Documents/repositories/vuejs-ssr-typescript/node_modules/express/lib/router/layer.js:95:5)
    at /Users/shoun/Documents/repositories/vuejs-ssr-typescript/node_modules/express/lib/router/index.js:281:22
    at param (/Users/shoun/Documents/repositories/vuejs-ssr-typescript/node_modules/express/lib/router/index.js:354:14)
TypeError: Cannot read property 'render' of undefined
    at normalizeRender (/Users/shoun/Documents/repositories/vuejs-ssr-typescript/node_modules/vue-server-renderer/build.js:6621:19)
    at render (/Users/shoun/Documents/repositories/vuejs-ssr-typescript/node_modules/vue-server-renderer/build.js:6840:5)
    at Object.renderToString (/Users/shoun/Documents/repositories/vuejs-ssr-typescript/node_modules/vue-server-renderer/build.js:6871:9)
    at /Users/shoun/Documents/repositories/vuejs-ssr-typescript/dist/server.js:16:14
    at Layer.handle [as handle_request] (/Users/shoun/Documents/repositories/vuejs-ssr-typescript/node_modules/express/lib/router/layer.js:95:5)
    at next (/Users/shoun/Documents/repositories/vuejs-ssr-typescript/node_modules/express/lib/router/route.js:137:13)
    at Route.dispatch (/Users/shoun/Documents/repositories/vuejs-ssr-typescript/node_modules/express/lib/router/route.js:112:3)
    at Layer.handle [as handle_request] (/Users/shoun/Documents/repositories/vuejs-ssr-typescript/node_modules/express/lib/router/layer.js:95:5)
    at /Users/shoun/Documents/repositories/vuejs-ssr-typescript/node_modules/express/lib/router/index.js:281:22
    at param (/Users/shoun/Documents/repositories/vuejs-ssr-typescript/node_modules/express/lib/router/index.js:354:14)

Here is my server configuration:

import * as express from 'express';
import * as path from 'path';
import * as VueRender from 'vue-server-renderer';
import * as fs from 'fs-extra';
import app from './assets/app';
declare var __dirname;

// Get the HTML layout
const layout = fs.readFileSync(path.join(__dirname, 'index.html'), 'utf8');

// Create a renderer
const renderer = VueRender.createRenderer();

let server = express();

server.get('/hello', function (req, res) {
    res.send('Hello World!');
});

server.use('/assets', express.static(path.join(__dirname, 'assets')));

// Handle all GET requests
server.get('*', function (request, response) {
    // Render our Vue app to a string
    renderer.renderToString(
        // Create an app instance
        app(),
        // Handle the rendered result
        function (error, html) {
            // If an error occurred while rendering...
            if (error) {
                // Log the error in the console
                console.error(error);
                // Tell the client something went wrong
                return response
                    .status(500)
                    .send('Server Error')
            }
            // Send the layout with the rendered app's HTML
            response.send(layout.replace('<div id="app"></div>', html))
        }
    )
});

let port = 4500;
server.listen(port, () => {
    console.log(`App listening on ${port}`);
});

You can find the source code on my github repository: https://github.com/sichida/vuejs-ssr-typescript. I could really use some help because I am stuck...

Thank you very much!

like image 375
Shoun Avatar asked Apr 08 '17 11:04

Shoun


People also ask

Does Vue support server-side rendering?

By default, Vue components produce and manipulate DOM in the browser as output. However, it is also possible to render the same components into HTML strings on the server, send them directly to the browser, and finally "hydrate" the static markup into a fully interactive app on the client. A server-rendered Vue.

Does Vue work with TypeScript?

Now, you should be able to get your Vue app up and running in TypeScript with features like defineComponent , data, props, computed properties, methods, and watchers. Vue 3.0 includes better support for TypeScript out of the box, and the entire Vue code was rewritten in TypeScript to improve maintainability.

Does Vue 3 support TypeScript?

Vue is written in TypeScript itself and provides first-class TypeScript support.

How do I know if SSR is working?

Testing MethodConnect a load and power supply, and check the voltage of the load terminals with the input ON and OFF. The output voltage will be close to the load power supply voltage with the SSR turned OFF. The voltage will drop to approximately 1 V with the SSR turned ON.


2 Answers

I checked your repo and the problem is actually in the file src/assets/app.ts, in the createApp function you're returning an object of type ComponentOptions but renderToString takes an object of type Vue.

It can actually be much simpler than what you have right now:

import * as Vue from 'vue';

let createApp = function () {
  return new Vue({
    props: ['message'],
    template: '<span>{{ message }}</span>',
  });
};
export default createApp;

And that's it, you only need to return a new Vue instance.

like image 155
Luis Orduz Avatar answered Oct 23 '22 14:10

Luis Orduz


What I have found that in order for Vue to render something, it needs one of these:

  • render property
  • template property
  • Being $mount()ed

Now if you're like me, and you had something like this in your HTML:

<div id="app">
    <navbar></navbar>
    ...
</div>

and used to mount it like app.$mount('#app'), what you need to do is:

  • Move <div id="app"> entirely with its contents to a component (probably call it App.vue?)
  • Add a render property to the Vue instance like render: h => h(App) where App is the component you just created
  • Use that component directly for SSR, i.e. return it from your entry-server.js or whatever
  • Do $mount that instance in entry-client.js for hydration
like image 41
OverCoder Avatar answered Oct 23 '22 14:10

OverCoder