async render () {
    // compile!
    const stats = await compile([this.clientConfig, this.serverConfig])
    const serverBundle = require(path.resolve(this.outDir, 'manifest/server.json'))
    const clientManifest = require(path.resolve(this.outDir, 'manifest/client.json'))

    // remove manifests after loading them.
    await fs.remove(path.resolve(this.outDir, 'manifest'))

    // ref: https://github.com/vuejs/vuepress/issues/1367
    if (!this.clientConfig.devtool && (!this.clientConfig.plugins
      || !this.clientConfig.plugins.some(p =>
        p instanceof webpack.SourceMapDevToolPlugin
        || p instanceof webpack.EvalSourceMapDevToolPlugin
      ))) {
      await workaroundEmptyStyleChunk(stats, this.outDir)
    }

    // create server renderer using built manifests
    this.renderer = createBundleRenderer(serverBundle, {
      clientManifest,
      runInNewContext: false,
      inject: false,
      shouldPrefetch: this.context.siteConfig.shouldPrefetch || (() => true),
      template: await fs.readFile(this.context.ssrTemplate, 'utf-8')
    })

    // pre-render head tags from user config
    // filter out meta tags for they will be injected in updateMeta.js
    this.userHeadTags = (this.context.siteConfig.head || [])
      .filter(([headTagType]) => headTagType !== 'meta')
      .map(renderHeadTag)
      .join('\n    ')

    // if the user does not have a custom 404.md, generate the theme's default
    if (!this.context.pages.some(p => p.path === '/404.html')) {
      await this.context.addPage({ path: '/404.html' })
    }

    // render pages
    logger.wait('Rendering static HTML...')

    // If maxConcurrency is set, instead of rendering all pages concurrently,
    // build task would render pages by smaller group to prevent OOM.
    if (this.maxConcurrency) logger.info(`max concurrency set: ${this.maxConcurrency}`)
    const pagePaths = []
    const maxConcurrency = this.maxConcurrency || 100000
    for (let i = 0; i < this.context.pages.length; i += maxConcurrency) {
      const segmentPaths = await Promise.all(
        this.context.pages.slice(i, i + maxConcurrency)
        .map(page => this.renderPage(page))
      )
      pagePaths.push(...segmentPaths)
    }

    readline.clearLine(process.stdout, 0)
    readline.cursorTo(process.stdout, 0)

    await this.context.pluginAPI.applyAsyncOption('generated', pagePaths)

    // DONE.
    const relativeDir = path.relative(this.context.cwd, this.outDir)
    logger.success(`Generated static files in ${chalk.cyan(relativeDir)}.`)
    const { duration } = performance.stop()
    logger.developer(`It took a total of ${chalk.cyan(`${duration}ms`)} to run the ${chalk.cyan('vuepress build')}.`)
    console.log()
  }
Compile and render pages.

@returns {Promise<void>}
@api public