Search code examples
javascriptnode.jsvue.jsnuxt.jsvue-server-renderer

how to rendering vue-component in vue file with (vue-server-renderer)


I want to begining the rendering component with vuejs

I have a simple node server

   const Vue = require('vue');
const server = require('express')();
const template = require('fs').readFileSync('index.template.html', 'utf-8');
const renderer = require('vue-server-renderer')
    .createRenderer({
        template
    })
const context = {
    title: 'vue ssr',
    meta: `
        <meta name="keyword" content="vue,ssr">
        <meta name="description" content="vue srr demo">
    `,
};
server.get('/test2', (req, res) => {
    res.end("test");
});

server.get('/test', (req, res) => {
    const app = new Vue({
        data: {
            url: req.url
        },
        template: `<div>The visited URL is: {{ url }}</div>`,
    });
    renderer
        .renderToString(app, context,(err, html) => {
            //console.log('app', app )
            if (err) {
                res.status(500).end('Internal Server Error')
                return;
            }
            res.end(html);
        });
})

server.listen(8080);

A url: localhost:8080/test works enter image description here

My problem concerns the render in front. I use nuxt.js, and i test with a simple page path: project/pages/test.vue

//// File test.vue to display the render

<template>
  <div>
    test
  </div>
</template>
<script>
  export default {
    async asyncData({ params }) {
      try {
        let result = await fetch(`http://localhost:8080/test`)
          .then(res => res.json())
          .then(data => data);
        console.log('result', result)
        return `<span> test </span>`;
      } catch (e) {
        console.error("SOMETHING WENT WRONG :" + e);
        return `<span> error </span>`;
      }
    },
  }
</script>

The result of the call:

{
  size: 0,
  timeout: 0,
  [Symbol(Body internals)]:
   { body:
      PassThrough {
        _readableState: [ReadableState],
        readable: true,
        domain: null,
        _events: [Object],
        _eventsCount: 2,
        _maxListeners: undefined,
        _writableState: [WritableState],
        writable: false,
        allowHalfOpen: true,
        _transformState: [Object] },
     disturbed: false,
     error: null },
  [Symbol(Response internals)]:
   { url: 'http://localhost:8080/test',
     status: 200,
     statusText: 'OK',
     headers: Headers { [Symbol(map)]: [Object] },
     counter: 0 } }

My question is how to display the content of the call in my view. I didn't find an element of response. Thanks

Edit 1 : To correct the display a simple string or object is ok #BACK

server.get('/test/string', (req, res) => {
    res.json('simple chaine ')
})
server.get('/test/object', (req, res) => {
    res.json({ id: 1, name: 'un object' })
})

#FRONT

<template>
  <div class="container">
    <p v-if="data_error">{{ data_error }}</p>

    <h2>ASYNC Test sur une chaine de caractère</h2>
    <div><p>Display example string: {{ data_string }}</p></div>

    <h2>ASYNC Test sur un object</h2>
    <div><pre>{{ data_object }}</pre></div>
  </div>
</template>
<script>
  export default {
    async asyncData() {
      try {
        const responseString = await fetch('http://localhost:8080/test/string');
        const string = await responseString.json();

        const responseObject = await fetch('http://localhost:8080/test/object');
        const object = await responseObject.json();

        return {
          data_string: JSON.parse(JSON.stringify(string)),
          data_object: JSON.parse(JSON.stringify(object)),
          data_error: null,
        }
      } catch (e) {
        return { data_error: e }
      }
    },
  }
</script>

But If I want display a component, I have a prob #BACK

server.get('/test/count', (req, res) => {
    const app = new Vue({
        data: {
            count: 0
        },
        methods: {
            counter() {
                this.count++
            }
        },
        template: `<div>
            <button v-on:click="counter">click</button>
            The visited URL is: {{ count }}
        </div>`,
    });

    renderer.renderToString(app).then(html => {
        console.log(html)
        res.json(html);
    }).catch(err => {
        res.status(500).end('Internal Server Error')
        console.error(err)
    })
})

#FRONT ( not work, i just have html, not event, i don't know if it possible )

<template>
  <div v-html="data_compCount"></div>
</template>
<script>
  export default {
    async asyncData({ params }) {
      try {
        const responseCounter = await fetch('http://localhost:8080/test/count');
        const compCount = await responseCounter.json();
        return {
          data_compCount: JSON.parse(JSON.stringify(compCount)),
          data_error: null,
        }
      } catch (e) {
        console.error("SOMETHING WENT WRONG :" + e);
        return `<span> error </span>`;
      }
    },
  }
</script> 

Solution

  • I'll break it down in two simple steps:

    Step 1: Displaying a value (any value) in a vue component:

    <template>
      <div>
        Display example string: {{ example_string }}
        Another way to display a string: 
        <span v-text="example_string"></span>
    
        A way to display an object (for debugging purposes mostly):
        <pre>{{example_data}}</pre>
      </div>
    </template>
    <script>
    export default {
       data() {
           return {
              example_string: 'This is an example string',
              example_data: { id: 1, name: 'Example' }
           }
       }
    }
    </script>
    

    Step 2: Fetching a response from the server and using the data:

    <script>
    export default {
        data() { 
            return {
                // we will fetch data from server and store them
                // in example_data. 
                example_data: null
            }
        },
    
        // To ensure the data gets loaded on page start/component initialisation:
        mounted() {
            this.loadData();
            // or: this.loadData2();
        },
    
        methods: { 
           // async await example:
           async loadData() {
              var data = await (fetch(url).then(response => response.json());
    
              this.example_data = data;
           },
    
           // without async/await:
           loadData2() {
              fetch(url).then(response => response.json()).then(data => {
                 this.example_data = data;
              });
           }
        },
    
    }
    </script>
    

    Edit:
    the data() function is a special Vue function. It returns an object with values that will be available to inside your vue template and on the this object in all other function. The functions you created that return value should be rewritten like so. Please note that I cannot test against any backend so interpret these code samples as examples:

    #BACK:

    <template>
      <div class="container">
        <p v-if="data_error">{{ data_error }}</p>
    
        <h2>ASYNC Test sur une chaine de caractère</h2>
        <div><p>Display example string: {{ data_string }}</p></div>
    
        <h2>ASYNC Test sur un object</h2>
        <div><pre>{{ data_object }}</pre></div>
      </div>
    </template>
    <script>
      export default {
        // Vue's special data function
        data() {
           return {
               data_string: null,
               data_object: null,
               data_error: null
           }
        },
        // Vue's special 'mounted' (initialisation function)
        mounted() {
            // We want to load data when the page loads.
            this.asyncData();
        },
    
        methods: { 
          // Always write your function inside this methods object.
          async asyncData() {
            try {
              const responseString = await fetch('http://localhost:8080/test/string');
              const string = await responseString.json();
    
              const responseObject = await fetch('http://localhost:8080/test/object');
              const object = await responseObject.json();
    
              this.data_string = string;
              this.data_object = object;
              this.data_error = null;
            } catch (e) {
              console.error("SOMETHING WENT WRONG :" + e);
              this.data_error = e;
            }
          }
        }
      }
    </script>
    

    #FRONT:

    <template>
      <div v-html="data_compCount"></div>
    </template>
    <script>
      export default {
        // Vue data function.
        data() {
           return {
               data_compCount: null
           }
        },
        // Vue mounted function
        mounted() {
           this.asyncData();
        },
        methods: { 
          async asyncData({ params }) {
            try {
              const responseCounter = await fetch('http://localhost:8080/test/count');
              const compCount = await responseCounter.json();
    
              this.data_compCount = compCount;
              this.data_error = null;
            } catch (e) {
              console.error("SOMETHING WENT WRONG :" + e);
              this.data_error = e;
            }
          }
        }
      }
    </script>