使用 Markdoc 实现个性化用户引导
引言
我们最近发布了一个全新的优化版用户引导流程,旨在帮助开发者快速连接并查询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 不仅使开发过程既简单又直接,同时发现维护(如添加新框架)也非常方便。
关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台
除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接