国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢 在線咨詢
15158846557 在線咨詢
所在位置: 首頁 > 營銷資訊 > 網(wǎng)站運營 > 如何建設一個開源圖形引擎的文檔網(wǎng)站

如何建設一個開源圖形引擎的文檔網(wǎng)站

時間:2023-08-14 11:57:02 | 來源:網(wǎng)站運營

時間:2023-08-14 11:57:02 來源:網(wǎng)站運營

如何建設一個開源圖形引擎的文檔網(wǎng)站:如果你看過 Vue.js 的紀錄片,就會發(fā)現(xiàn)一個開源產品的成功不僅僅是優(yōu)質的代碼,而且還需要:清晰的文檔、不土的審美、持續(xù)的迭代、定期的布道、大佬的站臺……今天來聊聊“文檔”這件看似簡單卻需要精心雕琢的小事。







眾所周知,寫完代碼和單測只是保證了功能的實現(xiàn),而面向用戶最重要的東西就是文檔。文檔在我們生活中是最司空見慣的東西,比如你買了一臺吸塵器,一定會有一本說明書告訴你怎么操作,而不是跟你說:“想知道怎么操作嗎?把吸塵器拆開自己看看工作原理吧!”。然而在軟件領域,讓用戶看源碼這種荒唐的事天天在發(fā)生。懂得文檔重要性的程序員才是好的推銷員。

言歸正傳,當開始運營團隊里的開源圖形引擎 Oasis Engine 的時候,我才發(fā)現(xiàn)做出一個簡潔好用的文檔比想象得難很多。

Oasis Engine:https://oasisengine.cn/

選型

在開源之前,我們把文檔放在語雀上。語雀作為知識管理平臺確實很不錯,如果維護得當,積硅步可以至千里。但它也有兩方面的缺點:

無法承載 API 文檔、代碼示例等復雜形態(tài)的需求;
無法滿足個性化的設計??偠灾?,一個開源產品如果連個獨立的網(wǎng)站都沒有,有點說不過去。





通過了解一些知名的開源作品的網(wǎng)站后,我發(fā)現(xiàn)他們大部分都選擇把網(wǎng)站部署在 Github Pages 上。這很符合我們團隊的想法,既然引擎都開源了,文檔當然也應該開源,讓全世界的開發(fā)者一起來維護。經(jīng)過調研,我找到了一些能夠把搭建 GIthub Pages 的靜態(tài)網(wǎng)站框架:







除了三個工程強扭在一起之后整個網(wǎng)站風格不統(tǒng)一之外,更要命的是我們還把 API(在引擎?zhèn)}庫)、教程文檔、示例放在三個 Github 倉庫(美其名曰“潔癖”),每次更新都需要從三個倉庫拷貝內容到網(wǎng)站倉庫,維護成本非常高。開源之后第一個里程碑迭代完,團隊已經(jīng)被文檔折騰得筋疲力盡,完善文檔的積極性也降低了。

初心

2021年四月,距離 Oasis 引擎開源已經(jīng)過去兩個月了,熱潮消退,褒貶的聲音已經(jīng)淡去。期間有不少人反饋我們部署在 Github Pages 上的站點打開很慢,尤其是示例頁面不翻墻根本打不開,我們沒有認識到網(wǎng)站工程的臃腫導致了訪問慢,還傻傻地以為 Github 就是慢,于是在 Gitee 上又部署了一個國內鏡像來緩解這個問題。

看了行業(yè)里成功的圖形/游戲引擎的網(wǎng)站:Unity、Unreal、Cocos、LayaAir、ThreeJS、BabylonJS,他們根據(jù)自己的定位、主打產品、發(fā)展階段、商業(yè)策略展現(xiàn)出不同的信息架構和風格。而我們應該做成什么樣呢?

回歸初心吧,少年!Oasis 引擎想成為前端友好、高性能的移動端圖形引擎,那么我們的網(wǎng)站必須給人簡潔、可靠、極速的印象。四月快結束的時候,我猛然意識到:既然我們定位是面向前端的圖形引擎,為啥不朝著前端框架的模式做呢?我重新梳理一下網(wǎng)站的需求:

1.一體化:把 API 文檔(TypeDoc)、教程文檔(Markdown)、示例(Typescript)等不同格式的數(shù)據(jù)源放到一個站點,并且支持全局內容搜索;
2.示例嵌入:支持在教程文檔中嵌入功能示例,并且支持跳轉到 Codepen 等流行在線開發(fā)環(huán)境中編輯;
3.多版本:不同引擎版本的文檔同時存在,支持版本切換;
4.國際化:支持中英語言。
梳理完畢,我發(fā)現(xiàn)要做的其實是個類似 Ant Design 的站點。這里有個誤解,前文中提到 Dumi 的 dumi-theme-mobile,我錯誤地以為 Ant Design Mobile 的網(wǎng)站是基于 Dumi 實現(xiàn)的(而且 Ant Design Mobile 的作者也推薦我用 Dumi),而 Dumi 已經(jīng)是調研過后的放棄的方案,又由于 Ant Design Mobile 和 Ant Design 的網(wǎng)站風格相似,我仍以為 Ant Design 也是用 Dumi 做的,直到發(fā)現(xiàn) Ant Design Pro 的網(wǎng)站源碼,我才知道是基于 Gatsby 實現(xiàn)的。

開搞

接下去的內容雖然是這篇文章的主題,但可能比較無聊,事實上我完全可以省略上述心路歷程,把文章的標題改成《如何用 Gatsby 實現(xiàn)一個文檔網(wǎng)站》。然而,我想強調的是當一個人面對一個陌生的領域,勢必會走彎路,當回頭看的時候,這些彎路都是收獲。

發(fā)現(xiàn) Gatsby 的時候,我十分興奮,以至于五一假期五天時間都在搗鼓這個工具;假期結束的時候,同事們驚訝地發(fā)現(xiàn)我已經(jīng)把網(wǎng)站的功能基本寫完了。那么,Gatsby 到底是個什么東西呢?它和上述選型中的其他方案有什么區(qū)別呢?

我認為最本質的區(qū)別是:Gatsby 有一個叫 GraphQL 的中間數(shù)據(jù)層。不管你的輸入是什么格式,只要能轉成 GraphQL 格式的數(shù)據(jù),就能在 Gatsby 中通過查詢語句獲取數(shù)據(jù),最后渲染成 React 組件。比如,Oasis 引擎的官網(wǎng)就希望把 TypeDoc、Markdown、Typescript 格式的文件數(shù)據(jù)轉成 React 組件:





相當完美的流程!這意味著數(shù)據(jù)和樣式解耦,原先各種格式都要通過不同的工具編譯成 HTML,現(xiàn)在可以通過一個工具轉成 React 組件,而 React 組件的樣式可以統(tǒng)一管控。

處理 TypeDoc 數(shù)據(jù)

TypeScript(TS) 是近幾年最流行的前端開發(fā)語言,出于代碼質量和可維護性的考慮,Oasis 引擎也采用了 TypeScript 編寫。TypeDoc 是社區(qū)中比較優(yōu)秀的生成 TS API 文檔的工具,它能夠讀取 Typscript 的聲明數(shù)據(jù)并生成 HTML 網(wǎng)頁,但似乎很少人知道它其實有 Node module——也就是說只用它的 Node API 讀取數(shù)據(jù),渲染交給其他工具。

至此,聰明的讀者想必已經(jīng)知道了:找一個 TypeDoc 轉 GraphQL 的工具。幸運的是,我在 Gatsby 的社區(qū)就找到一個 gatsby-source-typedoc 插件(Gatsby 的插件生態(tài)很茂盛),順藤摸瓜,又找了該插件作者寫的文章。有趣的是,文章作者是一個叫 Excalibur.js 的游戲引擎的開發(fā)者,所謂同是引擎開發(fā)者,相逢何必曾相識,這就是猿糞啊。但是,我高興得有點早,因為文章提供的信息非常有限。這個插件僅僅是幫你讀取 TypeDoc 的數(shù)據(jù)轉成 GraphQL,然后你自己 JSON.parse 數(shù)據(jù),再然后 Please do something with that data by yourself…

export const pageQuery = graphql` typedoc(typedocId: { eq: "default" }) { internal { content } }`export default function MyPage({ data: { typedoc } }) { const typedocContent = JSON.parse(typedoc?.internal.content); // do something with that data...}


當時的想法是,反正 TypeDoc 的默認樣式也不好看,我就重寫一個渲染器吧…萬萬沒想到,這一重寫就是五一三天假期 。主要原因是 TypeDoc 的數(shù)據(jù)類型挺復雜的,比如類型就有這么多(可能還沒列全,終于能夠理解為啥 TypeDoc 官方的渲染器每次升級都有不小的變化):

export enum Kinds { MODULE = 1, ENUM = 4, CLASS = 128, INTERFACE = 256, TYPE_ALIAS = 4194304, FUNCTION = 64, PROPERTY = 1024, CONSTRUCTOR = 512, ACCESSOR = 262144, METHOD = 2048, GET_SIGNATURE = 524288, SET_SIGNATURE = 1048576, PARAMETER = 32768, TYPE_PARAMETER = 131072,}


這里說一下具體的步驟:
1.從 Oasis 引擎?zhèn)}庫獲取數(shù)據(jù)源,就是入口級別的 index.ts 文件。由于 Oasis Engine 是一個 monorepo 倉庫,要獲取每個子倉庫的 index.ts 的路徑,最后寫入到一個臨時文件 tsfiles.js 里:

const glob = require('glob');const fs = require('fs');glob(`${EngineRepoPath}/packages/**/src/index.ts`, {realpath: true}, function(er, files) { var re = new RegExp(/([^test]+).ts/); var tsFiles = []; for (let i = 0; i < files.length; i++) { const file = files[i]; var res = re.exec(file); console.log('[Typedoc entry file]:', file); if (!res) continue; tsFiles.push(`"${file}"`); } fs.writeFile('./scripts/typedoc/tsfiles.js', `module.exports = [${tsFiles.join(',')}];`, function(err) {});});


2.在 gatsby-config.js 中配置插件:

const DTS = require('./scripts/typedoc/tsfiles');module.exports = { plugins: [ { resolve: "gatsby-source-typedoc", options: { src: DTS, typedoc: { tsconfig: `${typedocSource}/tsconfig.json` } } } ]}


3.打開 http://localhost:8000/___graphql 如果看到左側面板中有 typedoc 說明數(shù)據(jù)讀取已經(jīng)成功,勾選一下 internal> content 執(zhí)行查詢,可以到詳細的數(shù)據(jù):







4.接下去就是使用 gatsby 創(chuàng)建頁面,gatsby 提供了 createPages.js 入口編寫創(chuàng)建頁面的代碼,以下就是插件作者在文章中省略的 do something with that data… 部分的代碼:

async function createAPI(graphql, actions) { const { createPage } = actions; const apiTemplate = resolve(__dirname, '../src/templates/api.tsx'); const typedocquery = await graphql( ` { typedoc { internal { content } } } `, ); let apis = JSON.parse(typedocquery.data.typedoc.internal.content); // do something with that data... const packages = apis.children.map((p) => { return { id: P.ID | Truly Identity, kind: p.kind, name: p.name.replace('/src', '') }; }); if (apis) { apis.children.forEach((node, i) => { const name = node.name.replace('/src', ''); // 索引頁 createPage({ path: `${version}/api/${name}/index`, component: apiTemplate, context: { node, type: 'package', packages } }); // 詳情頁 if (node.children) { node.children.forEach((child) => { createPage({ path: `${version}/api/${name}/${child.name}`, component: apiTemplate, context: { node: child, type: 'module', packages, packageIndex: i } }); }) } }); }}


最終的結果,可以訪問 https://oasisengine.cn/0.3/api/core/index。樣式是不是比 TypeDoc 默認的好看一點?可能有人會問:Typdoc 也可以直接轉成 Markdown,你為什么大費周折呢?因為一個圖形引擎的復雜度相當高,API 有成千上萬個,如果用 Markdown 展示是非常難看的,所以 TypeDoc 的存在是有意義的。

在 Markdown 中嵌入 Demo

這是一個很樸素的需求,就是希望能在文檔中嵌入 Demo, 方便開發(fā)者理解文檔中描述的功能,增強文檔和示例的關聯(lián)性。這也是我們做面向前端的引擎必須具備的優(yōu)勢,市面上大部分引擎網(wǎng)站都是文檔和示例分離的,更別說一些 Native 引擎想在網(wǎng)頁里渲染都很難呢。比如材質文檔中講到 PBRMaterial,總得展示一下 PBR 材質的樣子吧。我們是搞圖形學的,又不是搞服務端的,只是文字描述多么干澀啊。







可以負責任地告訴大家,我的五一假期剩余兩天就是被這個功能消耗掉的 。接下來說一下具體的實現(xiàn)思路。

首先,我想讓維護文檔的同學輕松一點,在 Markdown 文件中嵌入一個 Demo 應該是一行代碼的事情,比如:

<playground src="pbr-helmet.ts"></playground>多么簡單優(yōu)雅!可是問題來了:怎么從 Markdown 中“提取”出這行代碼并最終渲染成想要的樣子呢?不要忘了 Markdown 本來就是 gatsby 的一項數(shù)據(jù)源,gatsby 正是通過 gatsby-transformer-remark 插件解析數(shù)據(jù)的,而數(shù)據(jù)的解析從原理上繞不過抽象語法樹,看了一下 graphiQL 果然有 AST 數(shù)據(jù):







1.第一步,在語法樹中找到 標簽替換成我想要的數(shù)據(jù)。于是,我就開始了人生的第一個 gatsby 插件 gatsby-remark-oasis 的編寫:

// `gatsby-remark-oasis` plugin: // Extract <playground> from markdown AST and replace the contentconst visit = require('unist-util-visit');const fs = require('fs');const Prism = require('prismjs');module.exports = ({ markdownAST }, { api, playground, docs }) => { visit(markdownAST, 'html', (node) => { if (node.value.includes('<playground')) { const src = /src="(.+)"/.exec(node.value); if (src && src[1]) { const name = src[1]; const path = `playground/${name}` const code = fs.readFileSync(`./${path}`, { encoding: 'utf8' }); node.value = `<playground name="${name}"><textarea>${code}</textarea>${Prism.highlight(code, Prism.languages.javascript, 'javascript')}</playground>`; } } }); return markdownAST;};


這里有人可能會覺得奇怪,既然已經(jīng)把源碼塞入到 (為了省去轉義的工作)中,為何引入一個 Prsimjs 再把代碼解析成 HTML 片段呢?

如果你分析一下上文中 Demo 的構成,會發(fā)現(xiàn)有兩部分構成:左邊是一個預覽,右邊是代碼片段,這個代碼片段就是通過 Prsimjs 美化生成的。如果我們在運行時使用 Prsimjs 也是可以的,但我們在插件里完成解析就相當于在編譯期完成這項工作,可以避免運行時引入一個 Prsimjs 的包增加網(wǎng)頁體積。

2.完成上一步之后,數(shù)據(jù)終于到了 React 中,但 React 也不認識 這個組件。于是,我們就需要另一個插件 gatsby-remark-component-parent2div 來把 聲明成 React 組件:

{ resolve: 'gatsby-transformer-remark', options: { plugins: [ // Extract <playground> from html markdwon AST and replace the content { resolve: 'gatsby-remark-oasis', options: { api: `/${version}/api/`, playground: `/${version}/playground/`, docs: `/${version}/docs/`, } }, // convert <playground> to React Componennt { resolve: "gatsby-remark-component-parent2div", options: { components: ["Playground"], verbose: true } }, ], }, },


注意這兩個插件使用的是 gatsby-transformer-remark 插件生成的數(shù)據(jù),所以插件配置要嵌套在 gatsby-transformer-remark 的 plugins 里,這是一條數(shù)據(jù)處理管線。

3.最后一步,我們在 React 代碼中把 替換成真正的 React 組件,這一步通過使用 rehype-react 來實現(xiàn):

import RehypeReact from "rehype-react";import Playground from "../Playground";const renderAst = new RehypeReact({ createElement: React.createElement, components: { "playground": Playground }}).Compiler;export default class Article extends React.PureComponent<ArticleProps> { render () { return renderAst(this.props.content.htmlAst); }}


至于 組件本身的編寫就相對簡單了。值得提一下的是這里的左側 Demo 預覽其實是一個 iframe 嵌入的 html 頁面,為此我也通過 gatsby 的 createPages API 創(chuàng)建了很多 Demo 頁面。為了把 Typescript 示例文件編譯成 React 頁面,我寫了第二個 gatsby 插件(實際更復雜,這里只展示最重要的 babel transform 部分,感興趣的可以看一下插件源碼):

// gatsby-node.jsconst babel = require("@babel/core");exports.onCreateNode = module.exports.onCreateNode = async function onCreateNode( { node, loadNodeContent, actions, createNodeId, reporter, createContentDigest }) { const { createNode } = actions const content = await loadNodeContent(node) // 省略了 babel 配置 const result = babel.transformSync(content, {...}); const playgroundNode = { internal: { content: result.code, type: `Playground`, }, } playgroundNode.internal.contentDigest = createContentDigest(playgroundNode) createNode(playgroundNode) return playgroundNode}


主體的功能完成之后,又加了一些小功能,比如在二維碼預覽、新頁面打開,以及 CodePen、CodeSandBox、Stackblitz 的跳轉編輯。這些小功能非常實用,既可以驗證功能的可靠性,又可以增強開發(fā)者的互動。







全局搜索

前面說了圖形引擎的功能和 API 是非常多,特別對于深度使用引擎的開發(fā)者來說,如果沒有搜索真的很痛苦。一開始我覺得這是個小功能,后來我發(fā)現(xiàn)確實也只是個小功能:)不過這個功能讓我苦苦等了 20 天 。







這里用到了 Algolia Docsearch。Algolia 是一家提供云搜索服務的公司,簡單來說,Docsearch 的服務器會每隔 24 小時爬取網(wǎng)站的數(shù)據(jù),然后網(wǎng)站引入 Docsearch 的前端 SDK 訪問爬取的數(shù)據(jù)。實現(xiàn)這樣的搜索需要兩步:

1.去官網(wǎng)申請后,會收到一份郵件詢問你是否是網(wǎng)站管理員,是否能夠引入 Docsearch 的 前端SDK:





我自信地回復郵件“Yes, I can…”,然而從此以后杳無音信。過了半個月,此時我已經(jīng)回復了三封郵件,依然沒有收到回復。于是我換了個郵箱申請,過了幾天終于收到了確認郵件,里面包含了分配給 http://oasisengine.cn 的 apiKey。

2.收到 apiKey 后,我第一時間去驗證功能,發(fā)現(xiàn)搜索結果并不是我期望的。和早期 SEO 優(yōu)化一樣,想讓搜索結果滿意,要么網(wǎng)站根據(jù)爬蟲的默認規(guī)則修改網(wǎng)站內容,要么修改爬蟲的爬取規(guī)則。Docsearch 為開發(fā)者提供了后者的選項,只要提供一個配置文件到這個 docsearch-configs 倉庫就可以。這里展示一下比較關鍵的字段:

{ // 要爬取的頁面 url 匹配規(guī)則 "start_urls": [ { "url": "https://oasisengine.cn/(?P<version>.*?)/docs/.+?-cn", "variables": { "version": [ "0.3" ] }, "tags": [ "cn" ] }, ], // 爬取頁面中哪些 HTML 標簽的數(shù)據(jù) "selectors": { // 一級類目,這個很關鍵,搜索的結果分類就可以根據(jù)這個實現(xiàn)的 "lvl0": { "selector": ".docsearch-lvl0", "global": true, "default_value": "Documentation" }, "lvl1": "article h1", "lvl2": "article h2", "lvl3": "article h3", "lvl4": "article h4", "lvl5": "article h5", "text": "article p, article li" }}


負責 docsearch-configs 倉庫的 PR 合并的是個法國帥哥,服務太好了,我前一分鐘發(fā)PR,他后一分鐘就回復了,堪比在線答疑。相比之下,負責郵件回復的部門效率真的太低了。

小結

以上就是建站過程中遇到主要幾個問題以及解法,走彎路的過程比真正寫代碼的過程長得多。這幾年一直在沉浸于互動圖形開發(fā)方向,趁著這次建站的機會也更新了一些前端技術棧,受益匪淺,比如第一次使用 GraphQL,感覺非常強大,預感以后還有用武之地。

Oasis 引擎的文檔發(fā)展才剛剛開始,我們深知這是一份需要逐年累月打磨的工作。希望這點小小的工作,能幫助團隊更好地迭代文檔,幫助開發(fā)者更快地找到所需的

在此如果大家有需要的話,可以領取阿里云代金券,免費領取,以防后事之需
阿里云代金券
騰訊云代金券

關鍵詞:引擎,圖形,建設

74
73
25
news

版權所有? 億企邦 1997-2025 保留一切法律許可權利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點擊下載Chrome瀏覽器
關閉