您好,登錄后才能下訂單哦!
這篇文章主要介紹“vue3如何實現(xiàn)chatgpt的打字機效果”,在日常操作中,相信很多人在vue3如何實現(xiàn)chatgpt的打字機效果問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”vue3如何實現(xiàn)chatgpt的打字機效果”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!
首先要明確一點,chatgpt 返回的文本格式是 markdown 的,最基本的渲染方式就是把 markdown 文本轉(zhuǎn)換為 HTML 文本,然后 v-html
渲染即可。這里的轉(zhuǎn)換和代碼高亮以及防 XSS 攻擊用到了下面三個依賴庫:
marked 將markdwon 轉(zhuǎn)為 html
highlight 處理代碼高亮
dompurify 防止 XSS 攻擊
同時我們是可以在 markdown 中寫 html 元素的,這意味著我們可以直接把光標(biāo)元素放到最后!
先貼代碼
MarkdownRender.vue
<script setup> import {computed} from 'vue'; import DOMPurify from 'dompurify'; import {marked} from 'marked'; import hljs from '//cdn.staticfile.org/highlight.js/11.7.0/es/highlight.min.js'; import mdInCode from "@/utils/mdInCode"; // 用于判斷是否顯示光標(biāo) const props = defineProps({ // 輸入的 markdown 文本 text: { type: String, default: "" }, // 是否需要顯示光標(biāo)?比如在消息流結(jié)束后是不需要顯示光標(biāo)的 showCursor: { type: Boolean, default: false } }) // 配置高亮 marked.setOptions({ highlight: function (code, lang) { try { if (lang) { return hljs.highlight(code, {language: lang}).value } else { return hljs.highlightAuto(code).value } } catch (error) { return code } }, gfmtrue: true, breaks: true }) // 計算最終要顯示的 html 文本 const html = computed(() => { // 將 markdown 轉(zhuǎn)為 html function trans(text) { return DOMPurify.sanitize(marked.parse(text)); } // 光標(biāo)元素,可以用 css 美化成你想要的樣子 const cursor = '<span></span>'; if (props.showCursor) { // 判斷 AI 正在回的消息是否有未閉合的代碼塊。 const inCode = mdInCode(props.text) if (inCode) { // 有未閉合的代碼塊,不顯示光標(biāo) return trans(props.text); } else { // 沒有未閉合的代碼塊,將光標(biāo)元素追加到最后。 return trans(props.text + cursor); } } else { // 父組件明確不顯示光標(biāo) return trans(props.text); } }) </script> <template> <!-- tailwindcss:leading-7 控制行高為1.75rem --> <div v-html="html" class="markdown leading-7"> </div> </template> <style> /** 設(shè)置代碼塊樣式 **/ .markdown pre { @apply bg-[#282c34] p-4 mt-4 rounded-md text-white w-full overflow-x-auto; } .markdown code { width: 100%; } /** 控制段落間的上下邊距 **/ .markdown p { margin: 1.25rem 0; } .markdown p:first-child { margin-top: 0; } /** 小代碼塊樣式,對應(yīng) markdown 的 `code` **/ .markdown :not(pre) > code { @apply bg-[#282c34] px-1 py-[2px] text-[#e06c75] rounded-md; } /** 列表樣式 **/ .markdown ol { list-style-type: decimal; padding-left: 40px; } .markdown ul { list-style-type: disc; padding-left: 40px; } /** 光標(biāo)樣式 **/ .markdown .cursor { display: inline-block; width: 2px; height: 20px; @apply bg-gray-800 dark:bg-gray-100; animation: blink 1.2s step-end infinite; margin-left: 2px; vertical-align: sub; } @keyframes blink { 0% { opacity: 1; } 50% { opacity: 0; } 100% { opacity: 1; } } </style>
可以發(fā)現(xiàn)最基本的 markdown 顯示還是挺簡單的,話就不多說了,都在注釋里。
我想你也許對判斷消息中的代碼塊是否未閉合更感興趣,那么就繼續(xù)看下去吧!
markdown 有兩種代碼塊,一種是 `code` ,另一種是 " code ",我叫他小代碼塊和大代碼塊。
一開始我是想用正則去判斷的,但是奈何有點復(fù)雜,我實在想不出應(yīng)該如何去編寫正則,讓 chatgpt 寫的正則也會判斷失敗,而且還要考慮到轉(zhuǎn)義符,就算寫出了正則,估計也會很復(fù)雜和難以維護。
這里有請 chatgpt 簡單介紹一下有限元狀態(tài)機:
有限元狀態(tài)機(finite element state machine)是一種基于狀態(tài)機的建模技術(shù),它可以將系統(tǒng)的行為描述為一組狀態(tài)和狀態(tài)之間的轉(zhuǎn)換。每個狀態(tài)可以對應(yīng)系統(tǒng)內(nèi)的一種狀態(tài),而狀態(tài)之間的轉(zhuǎn)換則表示系統(tǒng)內(nèi)部的轉(zhuǎn)換過程。有限元狀態(tài)機還可以用于系統(tǒng)的可靠性分析和測試,同時也可以作為軟件工程中的一種建模工具,幫助代碼實現(xiàn)、測試和調(diào)試等方面。
在實際應(yīng)用中,有限元狀態(tài)機通常是通過自動化工具生成,并且可以用于控制系統(tǒng)、網(wǎng)絡(luò)通信協(xié)議、軟件接口等方面的設(shè)計和測試。由于有限元狀態(tài)機具有自動化生成和完備性檢查等優(yōu)點,因此它已經(jīng)成為了一種廣泛應(yīng)用的建模與測試工具。
請站在web前端的視角下進行介紹
從前端開發(fā)的角度來看,有限元狀態(tài)機可以用于構(gòu)建復(fù)雜的交互式Web應(yīng)用程序。在Web應(yīng)用程序中,有限元狀態(tài)機可以描述用戶界面的不同狀態(tài)(例如,登錄、注冊、未登錄等),以及狀態(tài)之間的轉(zhuǎn)換(例如,從登錄狀態(tài)轉(zhuǎn)換到注冊狀態(tài))。
通常情況下,前端工程師使用JavaScript實現(xiàn)有限元狀態(tài)機,包括定義狀態(tài)、定義狀態(tài)之間的轉(zhuǎn)換規(guī)則、處理狀態(tài)轉(zhuǎn)換時的行為等。使用有限元狀態(tài)機可以幫助前端工程師更好地掌控應(yīng)用程序的狀態(tài),從而減少代碼中的復(fù)雜條件分支和無法預(yù)測的行為。同時,有限元狀態(tài)機也可以幫助前端團隊共同理解應(yīng)用程序的狀態(tài)和轉(zhuǎn)換規(guī)則,從而更好地協(xié)作開發(fā)和維護Web應(yīng)用程序。
總之,有限元狀態(tài)機是一種非常有用的前端開發(fā)技術(shù),可以幫助前端工程師更好地構(gòu)建和管理Web應(yīng)用程序的狀態(tài)和行為,提高應(yīng)用程序的可靠性和用戶體驗。
回到正題,我可以一點一點的從頭開始去解析 markdown 文本。想象這么一個簡單的狀態(tài)轉(zhuǎn)換流程:
初始狀態(tài)為文本狀態(tài)。
遇到代碼塊標(biāo)記,文本狀態(tài)轉(zhuǎn)換到代碼塊開始狀態(tài)。
再次遇到代碼塊標(biāo)記,從代碼塊開始狀態(tài)轉(zhuǎn)換到文本狀態(tài)。
不過現(xiàn)實要更復(fù)雜一點,我們有小代碼塊和大代碼塊。有限元狀態(tài)機的妙處就在這里,當(dāng)處在小代碼塊狀態(tài)的時候,我們不需要操心大代碼塊和正常文本的事,他的下一個狀態(tài)只能是遇到小代碼塊的閉合標(biāo)簽,進入文本狀態(tài)。
理解了這些,再來看我的源碼,才會發(fā)現(xiàn)他的精妙。
const States = { text: 0, // 文本狀態(tài) codeStartSm: 1, // 小代碼塊狀態(tài) codeStartBig: 2, // 大代碼塊狀態(tài) } /** * 判斷 markdown 文本中是否有未閉合的代碼塊 * @param text * @returns {boolean} */ function isInCode(text) { let state = States.text let source = text let inStart = true // 是否處于文本開始狀態(tài),即還沒有消費過文本 while (source) { // 當(dāng)文本被解析消費完后,就是個空字符串了,就能跳出循環(huán) let char = source.charAt(0) // 取第 0 個字 switch (state) { case States.text: if (/^\n?```/.test(source)) { // 以 ``` 或者 \n``` 開頭。表示大代碼塊開始。 // 一般情況下,代碼塊前面都需要換行。但是如果是在文本的開頭,就不需要換行。 if (inStart || source.startsWith('\n')) { state = States.codeStartBig } source = source.replace(/^\n?```/, '') } else if (char === '\\') { // 遇到轉(zhuǎn)義符,跳過下一個字符 source = source.slice(2) } else if (char === '`') { // 以 ` 開頭。表示小代碼塊開始。 state = States.codeStartSm source = source.slice(1) } else { // 其他情況,直接消費當(dāng)前字符 source = source.slice(1) } inStart = false break case States.codeStartSm: if (char === '`') { // 遇到第二個 `,表示代碼塊結(jié)束 state = States.text source = source.slice(1) } else if (char === '\\') { // 遇到轉(zhuǎn)義符,跳過下一個字符 source = source.slice(2) } else { // 其他情況,直接消費當(dāng)前字符 source = source.slice(1) } break case States.codeStartBig: if (/^\n```/.test(source)) { // 遇到第二個 ```,表示代碼塊結(jié)束 state = States.text source = source.replace(/^\n```/, '') } else { // 其他情況,直接消費當(dāng)前字符 source = source.slice(1) } break } } return state !== States.text } export default isInCode
到此,關(guān)于“vue3如何實現(xiàn)chatgpt的打字機效果”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。