Lazy-Loaded vue-router components don't work with SSR
Asked Answered
P

2

6

I have two different entry points for server.js and client.js.(I'm using vue-server-renderer and laravel-mix) - (my server.js and client.js looks exactly like described here - spatie/laravel-server-side-rendering and if I make static export import Test from '../views/Test' it works..

If I try importing the route without lazy loading, SSR works:

import Test from "../views/Test";

export const routes = [{
    path: '/my-route',
    name: "Test",  
    component: Test,
}]

But if I try lazy-loading, it fails on the SSR:

export const routes = [{
    path: '/my-route',
    name: "Test"
    component: () => import('../views/Test.vue'),
}]

Cannot find module './js/chunks/server/0.js?id=c3384f174123f0848451'

For the () => import('../views/Home.vue), client.js works, only server.js doesn't work.


My server.js:

import renderVueComponentToString from 'vue-server-renderer/basic';
import app from './app';
import {router} from './router/index';

new Promise((resolve, reject) => {
    router.push(context.url);
    router.onReady(() => {
        resolve(app);
    }, reject);
})
    .then(app => {
        renderVueComponentToString(app, (err, res) => {
            if (err) throw new Error(err);

            dispatch(res);
        });
    });

The full error is:

The command "/usr/bin/node /home/vagrant/Code/project/storage/app/ssr/1228cfee3f79dc5949bd898950384e53.js" failed Exit Code: 1(General error)

Working directory: /home/vagrant/Code/project/public Output:

================ Error Output: ================ internal/modules/cjs/loader.js:628 throw err; ^

Error: Cannot find module './js/chunks/server/0.js?id=c3384f174123f0848451'


Update

I think I may have an idea why this is happening (I may be wrong):

export const routes = [{
     path: '/', 
     name: "Home", 
     component: () => import('../views/Home')
}]

with this code, I get an error:

Error: Cannot find module './js/chunks/server/0.js?id=c3384f174123f0848451'

The command "/usr/bin/node /home/vagrant/Code/project/storage/app/ssr/717358e60bfd52035a1e58256cdfbba0.js" failed. Exit Code: 1(General error) Working directory: /home/vagrant/Code/project/public Output: ================ Error Output: ================ internal/modules/cjs/loader.js:628 throw err; ^ Error: Cannot find module './js/chunks/server/0.js?id=c3384f174123f0848451'

Look at the paths: In my compiled file (which is at public/js) I have this line:

var chunk = require("./js/chunks/server/" + ({}[chunkId]||chunkId) + ".js?id=" + {"0":"c3384f174123f0848451"}[chunkId] + "");

That seems like a relative path. However the file is actually running in what I specify in the config/ssr.php - 'temp_path' => storage_path('app/ssr') - so it cannot find the path.

However, even I change the temp_path to public_path() so that it can find the chunk from ./js/chunks/server/ (which is public/js/chunks/server/0.js), it still throws the same error. Even though the SSR's temp_path is different.

The command "/usr/bin/node /home/vagrant/Code/project/public/3560d8d101faa4bdef316054b14873cc.js" failed. Exit Code: 1(General error) Working directory: /home/vagrant/Code/project/public Output: ================ Error Output: ================ internal/modules/cjs/loader.js:628 throw err; ^ Error: Cannot find module './js/chunks/server/0.js?id=c3384f174123f0848451'

Also if I console.log(_dirname) in renderVueComponentToString() it returns me '/'

Pegu answered 3/10, 2019 at 10:15 Comment(0)
P
4

I solved it, now it works with SSR & code-splitting only in client-side - if you have any better idea I'm still all ears.

I used spatie/laravel-server-side-rendering and it's pretty easy to setup.

Here is my solution (and my changes on spatie/laravel-server-side-rendering):

I learn about separating bundles from charlesBochet's comment, however instead of 2x webpack.mix.js files I used one.

  • package.json
"scripts": {
    // concurrently is just for building them asynchronously 
    "dev-all": "concurrently \"npm --section=server run dev\" \"npm --section=client run dev\"  --kill-others-on-fail",

    // can also build them separately if you wish
    "dev-client": "npm --section=client run dev",
    "dev-server": "npm --section=server run dev"
     ...
}
  • webpack.mix.js
if (process.env.npm_config_section === 'server') {
    mix.js('resources/js/app-server.js', 'public/js')
        .webpackConfig({
            target: 'node',

            // Prevent code-splitting for server-build
            plugins: [
                new webpack.optimize.LimitChunkCountPlugin({
                    maxChunks: 1,
                })
            ],
        })
        // merge manifest is a package for merging manifests,
        // otherwise they'll get overwritten by each other
        // https://github.com/kabbouchi/laravel-mix-merge-manifest
        .mergeManifest()
        .version();

} else if (process.env.npm_config_section === 'client') {
    mix.js('resources/js/app-client.js', 'public/js')
        .webpackConfig({
            target: 'web',
            output: {
                chunkFilename: 'js/chunks/[name].js?id=[chunkhash]',
                publicPath: '/',
            },
        })
        .mergeManifest()
        .version();

    // Only build css with the client build, server build only needs
    // the html and not the css
    mix.sass('resources/sass/app.scss', 'public/css')
} else {
    console.log(
        '\x1b[41m%s\x1b[0m',
        'Provide correct --section argument to build command: server, client'
    );
    throw new Error('Provide correct --section argument to build command!')
}
  • app-server.js - should wait for the router to be ready
new Promise((resolve, reject) => {
    router.push(context.url);
    router.onReady(() => {
        resolve(app);
    }, reject);
})
    .then(app => {
        renderVueComponentToString(app, (err, res) => {
            if (err) throw new Error(err);
            dispatch(res);
        });
    });
  • app-client.js
router.onReady(function() {
    app.$mount('#app');
})
  • and finally router file code-splitting works for app-client.js
export const routes = [
{
    path: '/',
    name: "Home",
    component: () => import('../views/Home.vue')
},
Pegu answered 9/10, 2019 at 5:2 Comment(0)
A
1

Not sure where the problem comes from, but you should read this :

Note that it is still necessary to use router.onReady on both server and client before returning / mounting the app, because the router must resolve async route components ahead of time in order to properly invoke in-component hooks ... Vue SSR, Routing and Code-Splitting

So instead of

app.$mount('#app');

Try

router.onReady(() => {
  app.$mount('#app')
})

Hope this helps.

Angele answered 7/10, 2019 at 13:50 Comment(3)
I see your point, I added my server.js code to the OP. But it returns me Cannot find module './js/chunks/server/0.js?id=c3384f174123f0848451' - (btw, your code is for client-entry not server-entry and my problem is at server-entry; client entry works fine)Pegu
Hey man! Actually you directed me to the right way. Thanks a lot for that! I have posted to full solution if you want to take a lookPegu
Glad it helped ... Just starting a VUE SSR project, learned a lot from your experienceAngele

© 2022 - 2024 — McMap. All rights reserved.