JonWatkins / vue-runtime-template-compiler

A simple Vue component to compile templates at runtime
https://jonwatkins.github.io/vue-runtime-template-compiler/
MIT License
42 stars 8 forks source link

Crashes on server-side-rendering in Nuxt #1

Open rchl opened 4 years ago

rchl commented 4 years ago

Trying to use this package in a Nuxt project, page crashes with some error related to using window. That's understandable as there is no window when server-rendering but I don't see this package trying to access window properties explicitly. There might be something in vue-compiler maybe.

Here is a repro: https://codesandbox.io/s/vue-runtime-template-compiler-crash-d65ls?file=/pages/about.vue Navigate to about link and then refresh the about page (to trigger SSR rendering). Beware, it freezes the tab!

rchl commented 4 years ago

More information: Nuxt uses the "main" export by default which is the dist/vue-runtime-template-compiler.umd.js one. That one's code references window without type checking it:

!function(e, r) {
  "object" == typeof exports&& "object" == typeof module ?
      module.exports = r() :
      "function" == typeof define && define.amd ?
      define("VueRuntimeTemplateCompiler", [], r) :
      "object" == typeof exports ? exports.VueRuntimeTemplateCompiler = r() :
                                   e.VueRuntimeTemplateCompiler = r()
}(
    window, (function() {

(see last line)

To fix the crash, I believe you need to set output.globalObject = 'this' for the umd build config per documentation here https://webpack.js.org/configuration/output/#outputglobalobject

rchl commented 4 years ago

This fixes the crash but there is still some issue with this package not working in SSR.

rchl commented 4 years ago

The remaining issue is with client-side hydration of SSR'ed response throwing an error.

I've spent quite some time trying to figure that out but weren't able to come to any conclusion.

I think it might be something to do with vue-template-compiler or vue-loader-plugin needing different settings for server and client rendering. If it's related to vue-template-compiler then it would probably be fixable quite easily as settings can be applied during runtime but if it's webpack/vlue-loader-plugin related then not sure how that would need to be solved. Two separate builds for server/client?

ma-jahn commented 4 years ago

@rchl did you found a solution or an alternative?

rchl commented 4 years ago

No, didn't investigate further.

simplenotezy commented 4 years ago

I'd like to use this in combination with Nuxt as well. Also curious how to import it, to avoid loading the vue-compiler into the whole application (the part about codesplitting). If I were to import this using nuxt plugins, I imagine it would load on all pages in the application.

rchl commented 4 years ago

You can import it in specific component. There is an example in the readme.

But still, it's useless in Nuxt.

JonWatkins commented 4 years ago

Sorry for the late reply, I have just been having a look into this issue, but not much luck as of yet. I have pushed a new version that addresses the UMD build by adding the following to the webpack config (I was not aware of the webpack 4 issue).

module.exports = merge(baseConfig, {
  output: {
    ...
    globalObject: 'this'
    ....
  }
})

As for the issue with Nuxt not rendering on the server there is a work around for now using the "client-only" component. This is not much use if you want the template to be rendered on the server. But will allow the client to correctly render the content after the re-hydration.

<template>
  <div>
    <h1>About</h1>
    <client-only placeholder="Loading...">
      <runtime-template-compiler :template="`<p>{{content}}</p>`"/>
    </client-only>
  </div>
</template>

<script>
import { RuntimeTemplateCompiler } from "vue-runtime-template-compiler";

export default {
  components: {
    RuntimeTemplateCompiler
  },
  data() {
    return {
      content: "PASS"
    };
  }
};
</script>

I'll keep working on the problem to see if I can get Nuxt to render the complete component on the server.

FreekVR commented 4 years ago

Just as a side note, I was able to get this working in my app (I decided to drop this component for my own render function in the end).

I think swapping compileToFunctions for ssrCompileToFunctions should work, all you need to add it to find a solid way to detect whether the app is running under node or javascript :)

<script>
import { compileToFunctions, ssrCompileToFunctions } from 'vue-template-compiler';
import sanitizeHtml from 'sanitize-html';

import config from './config.js';

export default {
    props: {
        input: {
            type: String,
            required: true,
            default: ''
        },
        config: {
            type: Object,
            required: false,
            default: () => { return { ...config }; }
        }
    },

    computed: {
        processed() {
            return sanitizeHtml(this.input, config);
        }
    },

    render(createElement) {
        if (this.$isServer) {
            return createElement(ssrCompileToFunctions(`<div>${this.processed}</div>`));
        } else {
            return createElement(compileToFunctions(`<div>${this.processed}</div>`));
        }
    }
};
</script>