Detailed example of vue3 realizing the typewriter effect of chatgpt
Apr 18, 2023 pm 03:40 PM在做 chatgpt 鏡像站的時(shí)候,發(fā)現(xiàn)有些鏡像站是沒做打字機(jī)的光標(biāo)效果的,就只是文字輸出,是他們不想做嗎?反正我想做。于是我仔細(xì)研究了一下,實(shí)現(xiàn)了打字機(jī)效果加光標(biāo)的效果,現(xiàn)在分享一下我的解決方案以及效果圖
共識(shí)
首先要明確一點(diǎn),chatgpt 返回的文本格式是 markdown 的,最基本的渲染方式就是把 markdown 文本轉(zhuǎn)換為 HTML 文本,然后 v-html
渲染即可。這里的轉(zhuǎn)換和代碼高亮以及防 XSS 攻擊用到了下面三個(gè)依賴庫(kù):
- marked 將markdwon 轉(zhuǎn)為 html
- highlight 處理代碼高亮
- dompurify 防止 XSS 攻擊
同時(shí)我們是可以在 markdown 中寫 html 元素的,這意味著我們可以直接把光標(biāo)元素放到最后!
將 markdown 轉(zhuǎn)為 html 并處理代碼高亮
先貼代碼
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 }) // 計(jì)算最終要顯示的 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; } /** 小代碼塊樣式,對(duì)應(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 顯示還是挺簡(jiǎn)單的,話就不多說了,都在注釋里。
我想你也許對(duì)判斷消息中的代碼塊是否未閉合更感興趣,那么就繼續(xù)看下去吧!
代碼塊是否未閉合
markdown 有兩種代碼塊,一種是 `code` ,另一種是 " code ",我叫他小代碼塊和大代碼塊。
一開始我是想用正則去判斷的,但是奈何有點(diǎn)復(fù)雜,我實(shí)在想不出應(yīng)該如何去編寫正則,讓 chatgpt 寫的正則也會(huì)判斷失敗,而且還要考慮到轉(zhuǎn)義符,就算寫出了正則,估計(jì)也會(huì)很復(fù)雜和難以維護(hù)。
經(jīng)過短暫的苦思冥想后,我想到了之前在 《Vue.js設(shè)計(jì)與實(shí)現(xiàn)》 中看到的用有限元狀態(tài)機(jī)解析 html 文本的方案。
這里有請(qǐng) chatgpt 簡(jiǎn)單介紹一下有限元狀態(tài)機(jī):
有限元狀態(tài)機(jī)(finite element state machine)是一種基于狀態(tài)機(jī)的建模技術(shù),它可以將系統(tǒng)的行為描述為一組狀態(tài)和狀態(tài)之間的轉(zhuǎn)換。每個(gè)狀態(tài)可以對(duì)應(yīng)系統(tǒng)內(nèi)的一種狀態(tài),而狀態(tài)之間的轉(zhuǎn)換則表示系統(tǒng)內(nèi)部的轉(zhuǎn)換過程。有限元狀態(tài)機(jī)還可以用于系統(tǒng)的可靠性分析和測(cè)試,同時(shí)也可以作為軟件工程中的一種建模工具,幫助代碼實(shí)現(xiàn)、測(cè)試和調(diào)試等方面。
在實(shí)際應(yīng)用中,有限元狀態(tài)機(jī)通常是通過自動(dòng)化工具生成,并且可以用于控制系統(tǒng)、網(wǎng)絡(luò)通信協(xié)議、軟件接口等方面的設(shè)計(jì)和測(cè)試。由于有限元狀態(tài)機(jī)具有自動(dòng)化生成和完備性檢查等優(yōu)點(diǎn),因此它已經(jīng)成為了一種廣泛應(yīng)用的建模與測(cè)試工具。
請(qǐng)站在web前端的視角下進(jìn)行介紹
從前端開發(fā)的角度來看,有限元狀態(tài)機(jī)可以用于構(gòu)建復(fù)雜的交互式Web應(yīng)用程序。在Web應(yīng)用程序中,有限元狀態(tài)機(jī)可以描述用戶界面的不同狀態(tài)(例如,登錄、注冊(cè)、未登錄等),以及狀態(tài)之間的轉(zhuǎn)換(例如,從登錄狀態(tài)轉(zhuǎn)換到注冊(cè)狀態(tài))。
通常情況下,前端工程師使用JavaScript實(shí)現(xiàn)有限元狀態(tài)機(jī),包括定義狀態(tài)、定義狀態(tài)之間的轉(zhuǎn)換規(guī)則、處理狀態(tài)轉(zhuǎn)換時(shí)的行為等。使用有限元狀態(tài)機(jī)可以幫助前端工程師更好地掌控應(yīng)用程序的狀態(tài),從而減少代碼中的復(fù)雜條件分支和無法預(yù)測(cè)的行為。同時(shí),有限元狀態(tài)機(jī)也可以幫助前端團(tuán)隊(duì)共同理解應(yīng)用程序的狀態(tài)和轉(zhuǎn)換規(guī)則,從而更好地協(xié)作開發(fā)和維護(hù)Web應(yīng)用程序。
總之,有限元狀態(tài)機(jī)是一種非常有用的前端開發(fā)技術(shù),可以幫助前端工程師更好地構(gòu)建和管理Web應(yīng)用程序的狀態(tài)和行為,提高應(yīng)用程序的可靠性和用戶體驗(yàn)。
回到正題,我可以一點(diǎn)一點(diǎn)的從頭開始去解析 markdown 文本。想象這么一個(gè)簡(jiǎn)單的狀態(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)實(shí)要更復(fù)雜一點(diǎn),我們有小代碼塊和大代碼塊。有限元狀態(tài)機(jī)的妙處就在這里,當(dāng)處在小代碼塊狀態(tài)的時(shí)候,我們不需要操心大代碼塊和正常文本的事,他的下一個(gè)狀態(tài)只能是遇到小代碼塊的閉合標(biāo)簽,進(jìn)入文本狀態(tài)。
理解了這些,再來看我的源碼,才會(huì)發(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),即還沒有消費(fèi)過文本 while (source) { // 當(dāng)文本被解析消費(fèi)完后,就是個(gè)空字符串了,就能跳出循環(huán) let char = source.charAt(0) // 取第 0 個(gè)字 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)義符,跳過下一個(gè)字符 source = source.slice(2) } else if (char === '`') { // 以 ` 開頭。表示小代碼塊開始。 state = States.codeStartSm source = source.slice(1) } else { // 其他情況,直接消費(fèi)當(dāng)前字符 source = source.slice(1) } inStart = false break case States.codeStartSm: if (char === '`') { // 遇到第二個(gè) `,表示代碼塊結(jié)束 state = States.text source = source.slice(1) } else if (char === '\\') { // 遇到轉(zhuǎn)義符,跳過下一個(gè)字符 source = source.slice(2) } else { // 其他情況,直接消費(fèi)當(dāng)前字符 source = source.slice(1) } break case States.codeStartBig: if (/^\n```/.test(source)) { // 遇到第二個(gè) ```,表示代碼塊結(jié)束 state = States.text source = source.replace(/^\n```/, '') } else { // 其他情況,直接消費(fèi)當(dāng)前字符 source = source.slice(1) } break } } return state !== States.text } export default isInCode
到這里,就已經(jīng)實(shí)現(xiàn)了一個(gè) chatgpt 消息渲染了。喜歡的話點(diǎn)個(gè)贊吧!謝謝!
The above is the detailed content of Detailed example of vue3 realizing the typewriter effect of chatgpt. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Vue.js is suitable for small and medium-sized projects and fast iterations, while React is suitable for large and complex applications. 1) Vue.js is easy to use and is suitable for situations where the team is insufficient or the project scale is small. 2) React has a richer ecosystem and is suitable for projects with high performance and complex functional needs.

Vue.js is not difficult to learn, especially for developers with a JavaScript foundation. 1) Its progressive design and responsive system simplify the development process. 2) Component-based development makes code management more efficient. 3) The usage examples show basic and advanced usage. 4) Common errors can be debugged through VueDevtools. 5) Performance optimization and best practices, such as using v-if/v-show and key attributes, can improve application efficiency.

Vue.js is mainly used for front-end development. 1) It is a lightweight and flexible JavaScript framework focused on building user interfaces and single-page applications. 2) The core of Vue.js is its responsive data system, and the view is automatically updated when the data changes. 3) It supports component development, and the UI can be split into independent and reusable components.

React's main functions include componentized thinking, state management and virtual DOM. 1) The idea of ??componentization allows splitting the UI into reusable parts to improve code readability and maintainability. 2) State management manages dynamic data through state and props, and changes trigger UI updates. 3) Virtual DOM optimization performance, update the UI through the calculation of the minimum operation of DOM replica in memory.

Vue.js' role in web development is to act as a progressive JavaScript framework that simplifies the development process and improves efficiency. 1) It enables developers to focus on business logic through responsive data binding and component development. 2) The working principle of Vue.js relies on responsive systems and virtual DOM to optimize performance. 3) In actual projects, it is common practice to use Vuex to manage global state and optimize data responsiveness.

Vue.js is closely integrated with the front-end technology stack to improve development efficiency and user experience. 1) Construction tools: Integrate with Webpack and Rollup to achieve modular development. 2) State management: Integrate with Vuex to manage complex application status. 3) Routing: Integrate with VueRouter to realize single-page application routing. 4) CSS preprocessor: supports Sass and Less to improve style development efficiency.

Vue.js and React each have their own advantages: Vue.js is suitable for small applications and rapid development, while React is suitable for large applications and complex state management. 1.Vue.js realizes automatic update through a responsive system, suitable for small applications. 2.React uses virtual DOM and diff algorithms, which are suitable for large and complex applications. When selecting a framework, you need to consider project requirements and team technology stack.

Vue.js is a progressive JavaScript framework suitable for building complex user interfaces. 1) Its core concepts include responsive data, componentization and virtual DOM. 2) In practical applications, it can be demonstrated by building Todo applications and integrating VueRouter. 3) When debugging, it is recommended to use VueDevtools and console.log. 4) Performance optimization can be achieved through v-if/v-show, list rendering optimization, asynchronous loading of components, etc.
