Browse Source

fix(ssr): properly handle errors in async component

fix #6778
Evan You 8 years ago
parent
commit
8936b8d9c1
4 changed files with 30 additions and 10 deletions
  1. 6 2
      src/server/create-renderer.js
  2. 1 1
      src/server/render-context.js
  3. 4 7
      src/server/render.js
  4. 19 0
      test/ssr/ssr-string.spec.js

+ 6 - 2
src/server/create-renderer.js

@@ -78,11 +78,15 @@ export function createRenderer ({
         return false
       }, cb)
       try {
-        render(component, write, context, () => {
+        render(component, write, context, err => {
           if (template) {
             result = templateRenderer.renderSync(result, context)
           }
-          cb(null, result)
+          if (err) {
+            cb(err)
+          } else {
+            cb(null, result)
+          }
         })
       } catch (e) {
         cb(e)

+ 1 - 1
src/server/render-context.js

@@ -26,7 +26,7 @@ export class RenderContext {
   write: (text: string, next: Function) => void;
   renderNode: (node: VNode, isRoot: boolean, context: RenderContext) => void;
   next: () => void;
-  done: () => void;
+  done: (err: ?Error) => void;
 
   modules: Array<(node: VNode) => ?string>;
   directives: Object;

+ 4 - 7
src/server/render.js

@@ -193,21 +193,18 @@ function renderAsyncComponent (node, isRoot, context) {
     if (resolvedNode) {
       renderComponent(resolvedNode, isRoot, context)
     } else {
-      reject()
+      // invalid component, but this does not throw on the client
+      // so render empty comment node
+      context.write(`<!---->`, context.next)
     }
   }
 
-  const reject = err => {
-    console.error(`[vue-server-renderer] error when rendering async component:\n`)
-    if (err) console.error(err.stack)
-    context.write(`<!--${node.text}-->`, context.next)
-  }
-
   if (factory.resolved) {
     resolve(factory.resolved)
     return
   }
 
+  const reject = context.done
   let res
   try {
     res = factory(resolve, reject)

+ 19 - 0
test/ssr/ssr-string.spec.js

@@ -575,6 +575,25 @@ describe('SSR: renderToString', () => {
     })
   })
 
+  it('should catch async component error', done => {
+    Vue.config.silent = true
+    renderToString(new Vue({
+      template: '<test-async></test-async>',
+      components: {
+        testAsync: () => Promise.resolve({
+          render () {
+            throw new Error('foo')
+          }
+        })
+      }
+    }), (err, result) => {
+      Vue.config.silent = false
+      expect(err).toBeTruthy()
+      expect(result).toBeUndefined()
+      done()
+    })
+  })
+
   it('everything together', done => {
     renderVmWithOptions({
       template: `