Learning of various functional parts
input input
Use the @keydown keyboard to perform operations, and search with Enter and click
@="handleSend" @="newline"
Button loading loading icon: set template slot here
<el-button type="primary" :loading="loading" @click="handleSend" > <template #icon> <el-icon><Position /></el-icon> </template> send </el-button>
The responsibilities are separated, and the child components complete the page construction, what logic is needed to be used, and then communicate to the parent component through emit and is completed by the parent component. The entire page involves loading properties used by multiple components, stored by the settings repository.
message dialog box
A simple line of code puts user messages on the right
&.message-user { flex-direction: row-reverse; //Flip to realize user layout on the right .message-content { align-items: flex-end; } }
Line break attributeswhite-space: pre-wrap;
Keep whitespace characters and line breaks in the source code, otherwisewhite-space: normal;
(Default): Merge continuous whitespace characters, ignore newlines in the source code, and automatically wrap lines
settings settings panel
- The settings attributes are all set in the repository and are used for global use.
- Style Naming
w-full
This can always be easily understood and can be seen that the width is full. - How to modify the original style of element Plus in style.
- elementPlus Set dark mode
// <template> <div :class="{ 'dark': isDarkMode }"> <router-view /> </div> </template> <script setup> import { computed } from 'vue' import { useSettingsStore } from './stores/settings' const settingsStore = useSettingsStore() const isDarkMode = computed(() => ) </script> // { // Element Plus Dark Mode Variable Override --el-bg-color: var(--bg-color); --el-bg-color-overlay: var(--bg-color-secondary); --el-text-color-primary: var(--text-color-primary); --el-text-color-regular: var(--text-color-regular); --el-border-color: var(--border-color); // Element Plus component dark mode style overlay .el-input-number { --el-input-number-bg-color: var(--bg-color-secondary); --el-input-number-text-color: var(--text-color-primary); } .el-select-dropdown { --el-select-dropdown-bg-color: var(--bg-color); --el-select-dropdown-text-color: var(--text-color-primary); } .el-slider { --el-slider-main-bg-color: var(--primary-color); } } // import './assets/styles/'
Use scss to set dark mode:
1. Use pinia state management.
2. Click to switch in the settings panel to trigger the toggleDarkMode action (set in pinia), switch the isDarlMode state, and update the data-theme attributes of the elements.
3. The style of the dark mode is set in the scss variable
After refreshing, the style will change back to daytime mode, but at this time, the night mode is still selected in settings. Why is this?
A: Although the settings are stored in localStorage, the dark mode style is not properly initialized after the page is refreshed. We need to apply stored theme settings immediately when the app starts.
Solution: Set it once when adding mount.
onMounted(() => { // Initialize the topic according to the storage settings ('data-theme', ? 'dark' : 'light') })
Transfer data
axios、 XMLHttpRequest 、fetch
The following isAxios、XMLHttpRequestandFetchComparisons when sending HTTP requests include detailed analysis of usage, performance, compatibility and applicable scenarios:
Comparison of technologies used in front-end and back-end communication
AI conversations require streaming response processing, and communication technology ultimately adopts fetch.
Scene | Axios | XMLHttpRequest | Fetch |
---|---|---|---|
Quick development of simple requests | Excellent, concise grammar | Not suitable, cumbersome code | Good, concise grammar |
Global configuration requirements | Supported, provideddefaults Configuration |
Not supported | Need to be implemented manually |
Request/response interception processing | Native support for interceptors | Not supported | Need to be implemented manually |
Upload/download progress monitoring | Not supported | support | Not supported |
Compatible with older browsers | Supported bypolyfill
|
support | Not supported |
Streaming response processing | Not supported | Not supported | Support (combinedReadableStream ) |
Lightweight requirements | Suitable | Not suitable | Suitable |
Implement streaming data processing
1. Project implementation code
- Send a request
In src/utils/, the method is responsible for sending the request. Determine whether to request a stream response based on the value of the stream parameter.
async sendMessage(messages, stream = false) { // ... const response = await fetch(`${API_BASE_URL}/chat/completions`, { method: 'POST', headers: { ...createHeaders(), ...(stream && { 'Accept': 'text/event-stream' }) // If it is a streaming response, add the corresponding Accept header }, body: (payload) }) // ... if (stream) { return response // For streaming response, directly return the response object } // ... }
Handle streaming responses
In src/utils/, the processStreamResponse method is responsible for handling streaming responses. It uses the getReader method of ReadableStream to read the data step by step and decode the data using TextDecoder.
- Read stream data
- Decode data blocks
- Process the decoded data: first split into rows (arrays), then convert to json strings, then convert to js object, extract the content content in the object, update the message, and update the token usage
async processStreamResponse(response, { updateMessage, updateTokenCount }) { try { let fullResponse = ''; const reader = (); const decoder = new TextDecoder(); // 1. Read stream data while (true) { const { done, value } = await (); if (done) { ('Stream response is completed'); break; } //2. Decode the data block const chunk = (value); // Here each chunk is a possible array that may contain multiple arrays //3. Process the decoded data, first split it into rows (arrays), then convert it into a json string, then convert it into a js object, extract the content content in the object, update the message, and update the usage of token // 3.1 Split into rows const lines = ('\n').filter(line => () !== ''); for (const line of lines) { if (('data: ')) { // 3.2 Convert to json string const jsonStr = ('data: ', ''); // Check whether it is finished if (jsonStr === '[DONE]') { ('Stream response is completed, read is completed'); continue; } // 3.3 Convert to js object try { const jsData = (jsonStr); if ([0].) { const content = [0].; //3.4 Extract the content content from the object and update the message fullResponse += content; updateMessage(fullResponse); } // 3.5 update token usage if () { updateTokenCount(); } } catch (e) { ('Parse JSON failed:', e); } } } } } catch (error) { ('Stream processing error:', error); throw error; } },
Update the interface
In src/views/, the handleSend method calls processStreamResponse and updates the interface through the callback functions updateMessage and updateTokenCount.
const handleSend = async (content) => { // ... try { const response = await ( (0, -1).map(m => ({ role: , content: })), ); if () { // Will this change to synchronization when using await? I learned that after using await, I will wait for the subsequent function to be called before executing the subsequent code. Is that true? await (response, { updateMessage: (content) => (content), updateTokenCount: (usage) => (usage) }); } // ... } catch (error) { ('Sorry, an error occurred, please try again later. ') } finally { = false } }
2. Knowledge Points
2.1. Questions and answers
Several core concepts: Is () a method of ReadableStream? (), 4. Uint8Array, 5. Streams API,
(The returned by fetch is a problem derived from the ReadableStream object) What are the ways to deal with responses with fetch? What is the type?
2.1.1. Understanding of the answer
Concepts about streaming
ReadableStream
It is one of the core objects of StreamsAPI, which involves it because of network requests.It's one
ReadableStream
Object.
In-depth addition:Streams API
It is a Web API for streaming data processinggetReader()
yesReadableStream
A method of (because 1 soThere is also this method)
This method will return a reader object (here is the abbreviation), which can read data in the stream block by block and provide complete control of the stream (Note: After using getReader, the stream cannot be accessed elsewhere, meaning that only the reader object it returns can access the stream)
There is a method for the reader object()
Read data blocks in the stream asynchronously. The return value is as follows:
{ done: true/false, value: Uint8Array | undefined }
-
done
: Iftrue
, means that the stream has been read. -
value
: The currently read block data, usuallyUint8Array
, each element takes up 1 byte.
Uint8Array
It's a kindTyped arrays, efficiently process raw binary data, such as file blocks, images, and network responses.
Processing of binary data:
- Will
Uint8Array
Convert to a string, useTextDecoder
, coded asutf-8
、utf-16
. Use itdecode
Method converts byte data into a string. - Convert to image/video, use
Blob
, set the type image or video, and then convert the generated blob object to a URL, and you can use it.
Summary illustration:
Streams API ├── ReadableStream(type) → Provide streaming data blocks │ ├── getReader() → Get reader │ │ └── () → Read a single block of data {done, value} │ │ │ └── Data blocks are usually Uint8Array type │ └── TextDecoder → decoding Uint8Array as a string └── Blob → decoding Uint8Array For image video
fetch response method is extremely type
method | Return type | Common Scenes |
---|---|---|
() |
Promise<string> |
Text, HTML |
() |
Promise<Object> |
JSON API Response |
() |
Promise<Blob> |
Download pictures, videos, files |
() |
Promise<ArrayBuffer> |
Binary data, file parsing |
() |
Promise<FormData> |
Form Response (Rare) |
|
ReadableStream |
Real-time processing, progress tracking |
Notice:
The response body of fetch can only be read once (that is, it cannot be called at the same time()
and()
wait )
Even if the response has an error status (such as 404),fetch
No exception is thrown, it needs to be checked manually. The following code processing method:
let response = await fetch(''); if (!) { throw new Error(`HTTP error! status: ${}`); }
See the full code:github
This is the article about the implementation of Vue3 Ai chat dialog box. For more related contents of Vue3 Ai chat dialog box, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!