spatie / laravel-server-side-rendering

Server side rendering JavaScript in your Laravel application
https://sebastiandedeyne.com/posts/2018/server-side-rendering-javascript-from-php
MIT License
662 stars 63 forks source link

Not sure how to pass context to store #2

Closed amirshawn closed 6 years ago

amirshawn commented 6 years ago

Hi,

Thank you as always for your great packages! I'm trying this out but got stuck trying to pass data to save in my Vuex store. I was wondering if you might have a more detailed example of what the app-server.js and app-client files would look like. How do I access that context in those files?

This part of the directions are what I'm not sure how to implement:

// app-server.js

store.user = context.user // { name: 'Sebastian' }

// Render the app...

Thank you!!

sebastiandedeyne commented 6 years ago

Hi @amirshawn, first of all, thanks for checking out the package already, I hope it'll be of use to you :)

So, a bit more about context...

We added the ability to send context to your server side script because otherwise it'd be hard to retrieve data from the script. That said, context is only for the server script. In the client script you're on your own because there are a few different ways to share data there, and we don't want to be opinionated about it.

I'll be posting a fully working example app later this week, but for now I'll go through context usage with a few snippets.

Say you need the current user for your application state. First, we'll add the render call to the view, and pass the current user via context. The data you're sending to context can contain anything that can be serialized to json.

{!! ssr('js/app.js')->withContext('user', Auth::user()) !!}

Now, the global context variable on the server will have a user key available.

If I'd have a Vuex store, I'd set my user to the store before rendering the app on the server.

// app-server.js

import app from './app';
import store from './store';
import renderVueComponentToString from 'vue-server-renderer/basic';

store.commit('setUser', context.user);

renderVueComponentToString(app, (err, html) => {
    if (err) {
        throw new Error(err);
    }

    dispatch(html);
});

So we've got a server rendered app with out current user now, but as you stated, we need it on the client too. The easiest way to share data with the client is by adding a script tag.

<script>
    window.user = @json(Auth::user())
</script>
{!! ssr('js/app.js')->withContext('user', Auth::user()) !!}

Now we can access the user from the client script.

// resources/assets/js/app-client.js

import app from './app';
import store from './store';

store.commit('setUser', window.user);

app.$mount('#app');

Sending a huge chunk of context in the view as a script can have a hefty performance penalty, so we leave it up to the package user to decide how they want to retrieve client state. Other options are using an external script tag or an api call for example.

Hope this helps, feel free to ask away if you have other issues or concerns!

amirshawn commented 6 years ago

Thank you so much for this response!! I've been racking my brain for days trying to understand how this works. I'm sure I'll be back with more questions but your response has helped me immensely :)

amirshawn commented 6 years ago

I just have to say thank you again. I finally got things from the database to server render! Thank you so much for the missing piece of the puzzle :)

sebastiandedeyne commented 6 years ago

Great! Let me know if you have any more thoughts or questions on the package, all feedback is valuable!

amirshawn commented 6 years ago

I do have another question you could probably help me with. In the Vue documentation for server data fetching, their example entry-server.js file allows you to call the asyncData function on the component and then bind them to the context. I've been trying to make this work with your package but haven't been able to figure it out. If I understand it correctly, this could be very helpful because all component data could be fetched by the component and passed to the server state, so I wouldn't have to fetch them seperately.

amirshawn commented 6 years ago

Another part I'm not sure how to use that could use more documentation is in the config file this section: ` /*

I haven't tried using this yet, but it would be nice to have a little more directions on how to use this.

Thank you :)

amirshawn commented 6 years ago

On further investigation I've noticed that the server redering isn't working for components in the router-view. I was wondering if the example app you are working on will include vue-router?

sebastiandedeyne commented 6 years ago

The context key in the configuration file is the same as calling withContext, but automatically happens with every ssr() call. It's a convenience to have if you're rendering apps in multiple views which all need the same context.

Regarding the router, did you share the url with the router in your server script?

app.$router.push(context.url);

url is automatically added to context if you're using the laravel-server-side-rendering package, so you don't need to worry about setting that up.

https://github.com/spatie/laravel-server-side-rendering/blob/3a6d578bd25d560b01b1609833db5f98198fb865/src/SsrServiceProvider.php#L50

sebastiandedeyne commented 6 years ago

It's not finished or documented yet, but here's the initial version of a working example with vue + veux + vue-router

https://github.com/spatie/laravel-server-side-rendering-examples

amirshawn commented 6 years ago

Hi @sebastiandedeyne , I am passing the url to the router. The components in my App.vue file are rendering but for some reason the components in the router-view aren't. I tested with an inline template in the router file and that worked. But not with imported components.

I'm curious are you using V8JS or Node? I'm using V8js.

sebastiandedeyne commented 6 years ago

The example app runs as it should on both Node and V8. Not sure how I can help, would need to see / have some code to play around with to debug.

Are you using code splitting or waiting for something in a mounted hook that could stop the server from prerendering?

amirshawn commented 6 years ago

I think I'm dealing with conflicts with some of my components. I installed vue-no-ssr and wrapped some components with it and now I am getting some rendering in the router-view. I'm going to have to figure out which components will work with ssr and which won't. Thank you for your help, I really appreciate it!!!

amirshawn commented 6 years ago

I'm curious what you think the best way to pass data to the client? I'm doing an ajax calls at the moment and what I noticed is that when the page loads, the server rendered page flashes for a second then the client page replaces it. I used the method you mentioned and passed the data to the window, but with a lot of data it seemed kind of messy. How would you go about using external script tags? Have you been able to use the asyncData function that Vue mentions in the docs?