引言

我们最近发布了一个全新的优化版用户引导流程,旨在帮助开发者快速连接并查询PlanetScale数据库。
连接数据库的方式因应用使用的语言和框架而异。每种框架都有自己的细微差别,因此我们希望无论你的应用使用何种语言或框架,都能提供一条统一的路径让你轻松完成连接操作。
在用户引导页面中,你可以创建新的数据库以及选择框架。
本次引导功能的实现核心在于 **Markdoc**。本文将详细介绍,如何使用Markdoc构建我们的产品引导流程。


借助 Markdoc 实现更灵活的功能

Markdoc 是由 Stripe 创建的一种基于 Markdown 的语法,用于构建自定义文档站点。
我们之所以能够在 PlanetScale 快速迭代功能,是因为我们优先采用易用的工具,这些工具可以让公司内部的任何人都能够贡献内容。因此,使用 Markdown 和 GitHub 来设计产品引导流程对我们来说是非常合适的选择。
然而在开发过程中,我们迅速意识到需要更多互动性和个性化内容。纯静态的 Markdown 无法满足需求,这时我们开始尝试使用 **Markdoc**。
Markdoc 的语法是 Markdown 的超集,具体来说是基于 CommonMark 规范。这意味着你不仅可以使用你熟悉的 Markdown 来写内容,还可以扩展它以添加自定义属性、自定义标签,并使用函数和/或变量。


构建用户引导流程

示例代码解析

以下代码片段是用于用户引导流程中的部分 Markdown,用于展示连接教程。

rails credentials:edit --environment production

添加以下内容:

planetscale:
  username: {% $user %}
  host: {% $host %}
  database: {% $database %}
  password: {% $password %}

你会注意到我们在 Markdown 中使用了变量(例如 $user$host$database$password)。每个用户引导路径都经过定制,旨在让用户按步骤完成操作更加简单。为选定的框架提供可直接复制粘贴的代码片段非常重要,这就是变量的重要性所在。


Markdoc中的变量

Markdoc 允许在运行时定制你的文档。我们通过使用变量将用户的凭证直接嵌入到内容中,而不是使用静态占位符值。
这类似于 Laravel 的 Blade 模板和 Ruby on Rails 的 ERB 模板。以下代码片段展示了如何设置变量以填充这些 Markdown 字段:

import React from 'react'
import { parse, renderers, transform } from '@markdoc/markdoc'

export default function Page() {
  const config = {
    variables: {
      host: 'us-east.connect.psdb.cloud',
      user: 'mpl0y3jv3a92h4qc4ufn',
      database: 'beam',
      password: 'pscale_pw_V8db13jnq5mrOWcGFn6GTs6AerDI7A0womsmnJ1qxOc',
      ssl_ca: '/etc/ssl/certs/ca-certificates.crt'
    }
  }

  const doc = `# Configure your application\n…`
  const ast = parse(doc)
  const content = transform(ast, config)
  const children = renderers.react(content, React, {})

  return <div>{children}</div>
}

Markdoc的节点功能

为了明确用户引导代码片段所在的文件,我们想扩展代码块,以支持额外的 file 属性。Markdoc 的 **Nodes**(节点)功能让你无需使用自定义语法即可定制文档的渲染方式。以下示例扩展了上一部分的代码片段,在代码块上方显示文件名。

import React from 'react'
import { parse, renderers, transform } from '@markdoc/markdoc'

export default function Page() {
  const config = {
    nodes: {
      fence: Fence.scheme
    },
    variables: {
      host: 'us-east.connect.psdb.cloud',
      user: 'mpl0y3jv3a92h4qc4ufn',
      database: 'beam',
      password: 'pscale_pw_V8db13jnq5mrOWcGFn6GTs6AerDI7A0womsmnJ1qxOc',
      ssl_ca: '/etc/ssl/certs/ca-certificates.crt'
    }
  }

  const doc = `# Configure your application\n…`
  const ast = parse(doc)
  const content = transform(ast, config)
  const children = renderers.react(content, React, { components: { Fence } })

  return <div>{children}</div>
}

function Fence({ children, file, language }) {
  return (
    <div>
      <div>{file}</div>

      <pre>
        <code className={`language-${language}`}>{children}</code>
      </pre>
    </div>
  )
}

Fence.scheme = {
  render: Fence.name,
  children: ['pre', 'code'],
  attributes: {
    file: {
      type: String
    },
    language: {
      type: String
    }
  }
}

进一步的定制

一个常见问题是用户在安全连接到 PlanetScale 时难以选择合适的 SSL 证书。为了解决这个问题,我们构建了一个通用组件,根据用户的操作系统自动切换证书。
以下代码扩展了前一节的代码片段,通过检测用户的操作系统更改 ssl_ca 变量的值:

function connectPlatform(userAgent) {
  userAgent = userAgent.toLowerCase()

  switch (true) {
    case /linux/.test(userAgent):
      return 'linux'
    case /mac/.test(userAgent):
      return 'mac'
    case /windows/.test(userAgent):
      return 'windows'
    default:
      return 'ubuntu'
  }
}

function connectSslCertificate(platform) {
  switch (platform) {
    case 'linux':
      return '/etc/ssl/certs/ca-certificates.crt'
    case 'mac':
      return '/etc/ssl/cert.pem'
    default:
      return '/etc/ssl/certs/ca-certificates.crt'
  }
}

除此之外,用户的开发环境常常与生产环境不同,因此我们还添加了一个选择器,允许用户自己选择证书。以下是最终代码:

import React, { createContext, useState } from 'react'
import { parse, renderers, transform } from '@markdoc/markdoc'

const Platform = createContext({ platform: null, setPlatform: () => {} })

export default function Page({ userAgent }) {
  const initialPlatform = connectPlatform(userAgent)
  const [platform, setPlatform] = useState(initialPlatform)

  const sslCertificate = connectSslCertificate(platform)

  const config = {
    nodes: {
      fence: Fence.scheme
    },
    variables: {
      host: 'us-east.connect.psdb.cloud',
      user: 'mpl0y3jv3a92h4qc4ufn',
      password: 'pscale_pw_V8db13jnq5mrOWcGFn6GTs6AerDI7A0womsmnJ1qxOc',
      ssl_ca: sslCertificate
    }
  }

  const doc = `# Configure your application\n…`
  const ast = parse(doc)
  const content = transform(ast, config)
  const children = renderers.react(content, React, { components: { Fence } })

  return (
    <Platform.Provider value={{ platform, setPlatform }}>
      <div>{children}</div>
    </Platform.Provider>
  )
}

结果

我们对这次用户引导的效果感到非常满意,并根据早期数据,发现它对新用户起到了积极的作用。使用 Markdoc 不仅使开发过程既简单又直接,同时发现维护(如添加新框架)也非常方便。



使用 Markdoc 实现个性化用户引导插图

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文链接:https://choupangxia.com/2025/09/13/markdoc/