中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

vue3如何實現chatgpt的打字機效果

發布時間:2023-04-20 09:37:00 來源:億速云 閱讀:249 作者:iii 欄目:編程語言

這篇文章主要介紹“vue3如何實現chatgpt的打字機效果”,在日常操作中,相信很多人在vue3如何實現chatgpt的打字機效果問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”vue3如何實現chatgpt的打字機效果”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

共識

首先要明確一點,chatgpt 返回的文本格式是 markdown 的,最基本的渲染方式就是把 markdown 文本轉換為 HTML 文本,然后 v-html 渲染即可。這里的轉換和代碼高亮以及防 XSS 攻擊用到了下面三個依賴庫:

  • marked 將markdwon 轉為 html

  • highlight 處理代碼高亮

  • dompurify 防止 XSS 攻擊

同時我們是可以在 markdown 中寫 html 元素的,這意味著我們可以直接把光標元素放到最后!

將 markdown 轉為 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"; // 用于判斷是否顯示光標

const props = defineProps({
  // 輸入的 markdown 文本
  text: {
    type: String,
    default: ""
  },
  // 是否需要顯示光標?比如在消息流結束后是不需要顯示光標的
  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 轉為 html
  function trans(text) {
    return DOMPurify.sanitize(marked.parse(text));
  }
  
  // 光標元素,可以用 css 美化成你想要的樣子
  const cursor = '<span></span>';
  if (props.showCursor) {
    // 判斷 AI 正在回的消息是否有未閉合的代碼塊。
    const inCode = mdInCode(props.text)
    if (inCode) {
      // 有未閉合的代碼塊,不顯示光標
      return trans(props.text);
    } else {
      // 沒有未閉合的代碼塊,將光標元素追加到最后。
      return trans(props.text + cursor);
    }
  } else {
    // 父組件明確不顯示光標
    return trans(props.text);
  }
})

</script>

<template>
  <!-- tailwindcss:leading-7 控制行高為1.75rem -->
  <div v-html="html" class="markdown leading-7">
  </div>
</template>

<style>
/** 設置代碼塊樣式 **/
.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;
}

/** 小代碼塊樣式,對應 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;
}

/** 光標樣式 **/
.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>

可以發現最基本的 markdown 顯示還是挺簡單的,話就不多說了,都在注釋里。

我想你也許對判斷消息中的代碼塊是否未閉合更感興趣,那么就繼續看下去吧!

代碼塊是否未閉合

markdown 有兩種代碼塊,一種是 `code` ,另一種是 " code ",我叫他小代碼塊和大代碼塊。

一開始我是想用正則去判斷的,但是奈何有點復雜,我實在想不出應該如何去編寫正則,讓 chatgpt 寫的正則也會判斷失敗,而且還要考慮到轉義符,就算寫出了正則,估計也會很復雜和難以維護。

這里有請 chatgpt 簡單介紹一下有限元狀態機:

有限元狀態機(finite element state machine)是一種基于狀態機的建模技術,它可以將系統的行為描述為一組狀態和狀態之間的轉換。每個狀態可以對應系統內的一種狀態,而狀態之間的轉換則表示系統內部的轉換過程。有限元狀態機還可以用于系統的可靠性分析和測試,同時也可以作為軟件工程中的一種建模工具,幫助代碼實現、測試和調試等方面。

在實際應用中,有限元狀態機通常是通過自動化工具生成,并且可以用于控制系統、網絡通信協議、軟件接口等方面的設計和測試。由于有限元狀態機具有自動化生成和完備性檢查等優點,因此它已經成為了一種廣泛應用的建模與測試工具。

請站在web前端的視角下進行介紹

從前端開發的角度來看,有限元狀態機可以用于構建復雜的交互式Web應用程序。在Web應用程序中,有限元狀態機可以描述用戶界面的不同狀態(例如,登錄、注冊、未登錄等),以及狀態之間的轉換(例如,從登錄狀態轉換到注冊狀態)。

通常情況下,前端工程師使用JavaScript實現有限元狀態機,包括定義狀態、定義狀態之間的轉換規則、處理狀態轉換時的行為等。使用有限元狀態機可以幫助前端工程師更好地掌控應用程序的狀態,從而減少代碼中的復雜條件分支和無法預測的行為。同時,有限元狀態機也可以幫助前端團隊共同理解應用程序的狀態和轉換規則,從而更好地協作開發和維護Web應用程序。

總之,有限元狀態機是一種非常有用的前端開發技術,可以幫助前端工程師更好地構建和管理Web應用程序的狀態和行為,提高應用程序的可靠性和用戶體驗。

回到正題,我可以一點一點的從頭開始去解析 markdown 文本。想象這么一個簡單的狀態轉換流程:

  • 初始狀態為文本狀態。

  • 遇到代碼塊標記,文本狀態轉換到代碼塊開始狀態。

  • 再次遇到代碼塊標記,從代碼塊開始狀態轉換到文本狀態。

不過現實要更復雜一點,我們有小代碼塊和大代碼塊。有限元狀態機的妙處就在這里,當處在小代碼塊狀態的時候,我們不需要操心大代碼塊和正常文本的事,他的下一個狀態只能是遇到小代碼塊的閉合標簽,進入文本狀態。

理解了這些,再來看我的源碼,才會發現他的精妙。

const States = {
    text: 0, // 文本狀態
    codeStartSm: 1, // 小代碼塊狀態
    codeStartBig: 2, // 大代碼塊狀態
}

/**
 * 判斷 markdown 文本中是否有未閉合的代碼塊
 * @param text
 * @returns {boolean}
 */
function isInCode(text) {
    let state = States.text
    let source = text
    let inStart = true // 是否處于文本開始狀態,即還沒有消費過文本
    while (source) { // 當文本被解析消費完后,就是個空字符串了,就能跳出循環
        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 === '\\') {
                    // 遇到轉義符,跳過下一個字符
                    source = source.slice(2)
                } else if (char === '`') {
                    // 以 ` 開頭。表示小代碼塊開始。
                    state = States.codeStartSm
                    source = source.slice(1)
                } else {
                    // 其他情況,直接消費當前字符
                    source = source.slice(1)
                }
                inStart = false
                break
            case States.codeStartSm:
                if (char === '`') {
                    // 遇到第二個 `,表示代碼塊結束
                    state = States.text
                    source = source.slice(1)
                } else if (char === '\\') {
                    // 遇到轉義符,跳過下一個字符
                    source = source.slice(2)
                } else {
                    // 其他情況,直接消費當前字符
                    source = source.slice(1)
                }
                break
            case States.codeStartBig:
                if (/^\n```/.test(source)) {
                    // 遇到第二個 ```,表示代碼塊結束
                    state = States.text
                    source = source.replace(/^\n```/, '')
                } else {
                    // 其他情況,直接消費當前字符
                    source = source.slice(1)
                }
                break
        }
    }
    return state !== States.text
}

export default isInCode

到此,關于“vue3如何實現chatgpt的打字機效果”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

将乐县| 阿尔山市| 调兵山市| 霍林郭勒市| 永昌县| 廉江市| 祥云县| 金门县| 清水县| 奉新县| 桐梓县| 宁化县| 广昌县| 益阳市| 德阳市| 汽车| 中超| 巍山| 尚志市| 沅江市| 洞口县| 广宁县| 岐山县| 耒阳市| 抚远县| 涟水县| 柏乡县| 商南县| 盐津县| 明光市| 靖江市| 大新县| 卢龙县| 嘉善县| 封开县| 惠安县| 宣汉县| 景谷| 扎囊县| 邵阳县| 孟村|