鴻蒙OSUniApp開發富文本編輯器組件#三方框架 #Uniapp

使用UniApp開發富文本編輯器組件

富文本編輯在各類應用中非常常見,無論是內容創作平臺還是社交軟件,都需要提供良好的富文本編輯體驗。本文記錄了我使用UniApp開發一個跨平臺富文本編輯器組件的過程,希望對有類似需求的開發者有所啟發。

背景

前段時間接到一個需求,要求在我們的跨平臺應用中加入富文本編輯功能,支持基礎的文本格式化、插入圖片、鏈接等功能。考慮到項目使用UniApp開發,需要兼容多個平臺,市面上現成的富文本編輯器要么不支持跨平臺,要么功能過于復雜。于是我決定自己動手,開發一個功能適中、性能良好的富文本編輯器組件。

技術選型

為何不直接使用現有組件?

首先,我調研了幾個流行的富文本編輯器:

  1. quill.js - 功能強大,但在小程序環境中存在兼容性問題
  2. wangeditor - 針對Web端優化,小程序支持不佳
  3. mp-html - 專注于小程序,但編輯功能有限

UniApp官方提供的rich-text組件只具備富文本展示能力,不支持編輯。所以最終決定基于原生能力自己封裝一個輕量級的富文本編輯器組件。

核心技術點

  • 使用uni.createSelectorQuery獲取DOM節點
  • 基于contenteditable特性實現編輯功能
  • 自定義文本選區和格式化操作
  • 跨平臺樣式處理
  • 圖片上傳和展示

開發實現

1. 創建基礎組件結構

首先,我們需要創建一個基礎的編輯器組件結構:

<template><view class="rich-editor"><view class="toolbar"><view v-for="(item, index) in tools" :key="index"class="tool-item":class="{active: activeFormats[item.format]}"@tap="handleFormat(item.format, item.value)"><text class="iconfont" :class="item.icon"></text></view></view><!-- 編輯區域 --><view class="editor-container":style="{ height: editorHeight + 'px' }"><viewclass="editor-body"contenteditable="true"@input="onInput"@blur="onBlur"@focus="onFocus"id="editor"ref="editor"></view></view><!-- 底部工具欄 --><view class="bottom-tools"><view class="tool-item" @tap="insertImage"><text class="iconfont icon-image"></text></view><view class="tool-item" @tap="insertLink"><text class="iconfont icon-link"></text></view></view></view>
</template><script>
export default {name: 'RichEditor',props: {value: {type: String,default: ''},height: {type: Number,default: 300},placeholder: {type: String,default: '請輸入內容...'}},data() {return {editorHeight: 300,editorContent: '',selectionRange: null,activeFormats: {bold: false,italic: false,underline: false,strikethrough: false,alignLeft: true,alignCenter: false,alignRight: false},tools: [{ format: 'bold', icon: 'icon-bold', value: 'bold' },{ format: 'italic', icon: 'icon-italic', value: 'italic' },{ format: 'underline', icon: 'icon-underline', value: 'underline' },{ format: 'strikethrough', icon: 'icon-strikethrough', value: 'line-through' },{ format: 'alignLeft', icon: 'icon-align-left', value: 'left' },{ format: 'alignCenter', icon: 'icon-align-center', value: 'center' },{ format: 'alignRight', icon: 'icon-align-right', value: 'right' }]}},created() {this.editorHeight = this.heightthis.editorContent = this.value},mounted() {this.initEditor()},methods: {initEditor() {const editor = this.$refs.editorif (editor) {editor.innerHTML = this.value || `<p><br></p>`}// 設置placeholderif (!this.value && this.placeholder) {this.$nextTick(() => {editor.setAttribute('data-placeholder', this.placeholder)})}},// 監聽輸入onInput(e) {// 獲取當前內容this.editorContent = e.target.innerHTMLthis.$emit('input', this.editorContent)this.saveSelection()},// 保存當前選區saveSelection() {const selection = window.getSelection()if (selection.rangeCount > 0) {this.selectionRange = selection.getRangeAt(0)}},// 恢復選區restoreSelection() {if (this.selectionRange) {const selection = window.getSelection()selection.removeAllRanges()selection.addRange(this.selectionRange)return true}return false},// 處理格式化handleFormat(format, value) {// 恢復選區if (!this.restoreSelection()) {console.log('No selection to format')return}// 根據不同格式執行不同操作switch(format) {case 'bold':case 'italic':case 'underline':case 'strikethrough':document.execCommand(format, false, null)breakcase 'alignLeft':case 'alignCenter':case 'alignRight':document.execCommand('justify' + format.replace('align', ''), false, null)breakdefault:console.log('未知格式:', format)}// 更新激活狀態this.checkActiveFormats()// 觸發內容變化this.editorContent = this.$refs.editor.innerHTMLthis.$emit('input', this.editorContent)},// 檢查當前激活的格式checkActiveFormats() {this.activeFormats.bold = document.queryCommandState('bold')this.activeFormats.italic = document.queryCommandState('italic')this.activeFormats.underline = document.queryCommandState('underline')this.activeFormats.strikethrough = document.queryCommandState('strikethrough')const alignment = document.queryCommandValue('justifyLeft') ? 'alignLeft' :document.queryCommandValue('justifyCenter') ? 'alignCenter' :document.queryCommandValue('justifyRight') ? 'alignRight' : 'alignLeft'this.activeFormats.alignLeft = alignment === 'alignLeft'this.activeFormats.alignCenter = alignment === 'alignCenter'this.activeFormats.alignRight = alignment === 'alignRight'},// 焦點事件onFocus() {this.saveSelection()this.checkActiveFormats()},onBlur() {this.saveSelection()},// 插入圖片insertImage() {uni.chooseImage({count: 1,success: (res) => {const tempFilePath = res.tempFilePaths[0]// 上傳圖片this.uploadImage(tempFilePath)}})},// 上傳圖片uploadImage(filePath) {// 這里應該是實際的上傳邏輯uni.showLoading({ title: '上傳中...' })// 模擬上傳過程setTimeout(() => {// 假設這是上傳后的圖片URLconst imageUrl = filePath// 恢復選區并插入圖片this.restoreSelection()document.execCommand('insertHTML', false, `<img src="${imageUrl}" style="max-width:100%;" />`)// 更新內容this.editorContent = this.$refs.editor.innerHTMLthis.$emit('input', this.editorContent)uni.hideLoading()}, 500)},// 插入鏈接insertLink() {uni.showModal({title: '插入鏈接',editable: true,placeholderText: 'https://',success: (res) => {if (res.confirm && res.content) {const url = res.content// 恢復選區this.restoreSelection()// 獲取選中的文本const selection = window.getSelection()const selectedText = selection.toString()// 如果有選中文本,將其設為鏈接文本;否則使用URL作為文本const linkText = selectedText || url// 插入鏈接document.execCommand('insertHTML', false, `<a href="${url}" target="_blank">${linkText}</a>`)// 更新內容this.editorContent = this.$refs.editor.innerHTMLthis.$emit('input', this.editorContent)}}})},// 獲取編輯器內容getContent() {return this.editorContent},// 設置編輯器內容setContent(html) {this.editorContent = htmlif (this.$refs.editor) {this.$refs.editor.innerHTML = html}this.$emit('input', html)}}
}
</script><style>
.rich-editor {width: 100%;border: 1rpx solid #eee;border-radius: 10rpx;overflow: hidden;
}.toolbar {display: flex;flex-wrap: wrap;padding: 10rpx;border-bottom: 1rpx solid #eee;background-color: #f8f8f8;
}.tool-item {width: 80rpx;height: 80rpx;display: flex;justify-content: center;align-items: center;font-size: 40rpx;color: #333;
}.tool-item.active {color: #007AFF;background-color: rgba(0, 122, 255, 0.1);border-radius: 8rpx;
}.editor-container {width: 100%;overflow-y: auto;
}.editor-body {min-height: 100%;padding: 20rpx;font-size: 28rpx;line-height: 1.5;outline: none;
}.editor-body[data-placeholder]:empty:before {content: attr(data-placeholder);color: #999;font-style: italic;
}.bottom-tools {display: flex;padding: 10rpx;border-top: 1rpx solid #eee;background-color: #f8f8f8;
}/* 引入字體圖標庫 (需要自行配置) */
@font-face {font-family: 'iconfont';src: url('data:font/woff2;charset=utf-8;base64,...') format('woff2');
}
.iconfont {font-family: "iconfont" !important;font-style: normal;
}
</style>

2. 處理平臺差異

UniApp支持多個平臺,但在富文本編輯方面存在平臺差異,特別是小程序限制較多。下面是一些關鍵的跨平臺適配處理:

// 跨平臺選區處理
saveSelection() {// #ifdef H5const selection = window.getSelection()if (selection.rangeCount > 0) {this.selectionRange = selection.getRangeAt(0)}// #endif// #ifdef MP-WEIXIN// 微信小程序不支持DOM選區,需使用特殊方法this.getEditContext().getSelectionRange({success: (res) => {this.selectionRange = res}})// #endif
},// 獲取編輯器上下文(微信小程序)
getEditContext() {// #ifdef MP-WEIXINreturn this.editorCtx || wx.createSelectorQuery().in(this).select('#editor').context(res => {this.editorCtx = res.context}).exec()// #endifreturn null
}

3. 增強圖片處理能力

富文本編輯器的一個關鍵功能是圖片處理,我們需要增強這方面的能力:

// 增強版圖片上傳處理
uploadImage(filePath) {uni.showLoading({ title: '上傳中...' })// 壓縮圖片uni.compressImage({src: filePath,quality: 80,success: res => {const compressedPath = res.tempFilePath// 上傳到服務器uni.uploadFile({url: 'https://your-upload-endpoint.com/upload',filePath: compressedPath,name: 'file',success: uploadRes => {try {const data = JSON.parse(uploadRes.data)const imageUrl = data.url// 插入圖片this.insertImageToEditor(imageUrl)} catch (e) {uni.showToast({title: '上傳失敗',icon: 'none'})}},fail: () => {uni.showToast({title: '上傳失敗',icon: 'none'})},complete: () => {uni.hideLoading()}})},fail: () => {// 壓縮失敗,使用原圖this.doUploadFile(filePath)}})
},// 插入圖片到編輯器
insertImageToEditor(imageUrl) {// #ifdef H5this.restoreSelection()document.execCommand('insertHTML', false, `<img src="${imageUrl}" style="max-width:100%;" />`)// #endif// #ifdef MP-WEIXINthis.getEditContext().insertImage({src: imageUrl,width: '100%',success: () => {console.log('插入圖片成功')}})// #endif// 更新內容this.$nextTick(() => {// #ifdef H5this.editorContent = this.$refs.editor.innerHTML// #endif// #ifdef MP-WEIXINthis.getEditContext().getContents({success: res => {this.editorContent = res.html}})// #endifthis.$emit('input', this.editorContent)})
}

4. 實現HTML與富文本互轉

編輯器需要支持HTML格式的導入導出,以便存儲和展示:

// HTML轉富文本對象
htmlToJson(html) {const tempDiv = document.createElement('div')tempDiv.innerHTML = htmlconst parseNode = (node) => {if (node.nodeType === 3) { // 文本節點return {type: 'text',text: node.textContent}}if (node.nodeType === 1) { // 元素節點const result = {type: node.nodeName.toLowerCase(),children: []}// 處理元素屬性if (node.attributes && node.attributes.length > 0) {result.attrs = {}for (let i = 0; i < node.attributes.length; i++) {const attr = node.attributes[i]result.attrs[attr.name] = attr.value}}// 處理樣式if (node.style && node.style.cssText) {result.styles = {}const styles = node.style.cssText.split(';')styles.forEach(style => {if (style.trim()) {const [key, value] = style.split(':')if (key && value) {result.styles[key.trim()] = value.trim()}}})}// 遞歸處理子節點for (let i = 0; i < node.childNodes.length; i++) {const childResult = parseNode(node.childNodes[i])if (childResult) {result.children.push(childResult)}}return result}return null}const result = []for (let i = 0; i < tempDiv.childNodes.length; i++) {const nodeResult = parseNode(tempDiv.childNodes[i])if (nodeResult) {result.push(nodeResult)}}return result
},// 富文本對象轉HTML
jsonToHtml(json) {if (!json || !Array.isArray(json)) return ''const renderNode = (node) => {if (node.type === 'text') {return node.text}// 處理元素節點let html = `<${node.type}`// 添加屬性if (node.attrs) {Object.keys(node.attrs).forEach(key => {html += ` ${key}="${node.attrs[key]}"`})}// 添加樣式if (node.styles) {let styleStr = ''Object.keys(node.styles).forEach(key => {styleStr += `${key}: ${node.styles[key]};`})if (styleStr) {html += ` style="${styleStr}"`}}html += '>'// 處理子節點if (node.children && node.children.length > 0) {node.children.forEach(child => {html += renderNode(child)})}// 關閉標簽html += `</${node.type}>`return html}let result = ''json.forEach(node => {result += renderNode(node)})return result
}

實戰案例:評論編輯器

下面是一個簡化版的評論編輯器實現,可以在社區或博客應用中使用:

<template><view class="comment-editor"><view class="editor-title"><text>發表評論</text></view><rich-editorv-model="commentContent":height="200"placeholder="說點什么吧..."ref="editor"></rich-editor><view class="action-bar"><view class="action-btn cancel" @tap="cancel">取消</view><view class="action-btn submit" @tap="submitComment">發布</view></view></view>
</template><script>
import RichEditor from '@/components/rich-editor/rich-editor.vue'export default {components: {RichEditor},data() {return {commentContent: '',replyTo: null}},props: {articleId: {type: [String, Number],required: true}},methods: {cancel() {this.commentContent = ''this.$refs.editor.setContent('')this.$emit('cancel')},submitComment() {if (!this.commentContent.trim()) {uni.showToast({title: '評論內容不能為空',icon: 'none'})return}uni.showLoading({ title: '發布中...' })// 提交評論this.$api.comment.add({article_id: this.articleId,content: this.commentContent,reply_to: this.replyTo}).then(res => {uni.hideLoading()if (res.code === 0) {uni.showToast({title: '評論發布成功',icon: 'success'})// 清空編輯器this.commentContent = ''this.$refs.editor.setContent('')// 通知父組件刷新評論列表this.$emit('submit-success', res.data)} else {uni.showToast({title: res.msg || '評論發布失敗',icon: 'none'})}}).catch(() => {uni.hideLoading()uni.showToast({title: '網絡錯誤,請重試',icon: 'none'})})},// 回復某條評論replyComment(comment) {this.replyTo = comment.idthis.$refs.editor.setContent(`<p>回復 @${comment.user.nickname}:</p>`)this.$refs.editor.focus()}}
}
</script><style>
.comment-editor {padding: 20rpx;background-color: #fff;border-radius: 10rpx;
}.editor-title {margin-bottom: 20rpx;font-size: 32rpx;font-weight: bold;
}.action-bar {display: flex;justify-content: flex-end;margin-top: 20rpx;
}.action-btn {padding: 10rpx 30rpx;border-radius: 30rpx;font-size: 28rpx;margin-left: 20rpx;
}.cancel {color: #666;background-color: #f3f3f3;
}.submit {color: #fff;background-color: #007AFF;
}
</style>

踩坑記錄

開發過程中遇到了不少坑,這里分享幾個關鍵問題及解決方案:

1. 小程序富文本能力受限

小程序不支持通過contenteditable實現的富文本編輯,需要使用平臺提供的editor組件。解決方案是使用條件編譯,H5使用contenteditable,小程序使用官方editor組件。

<!-- H5編輯器 -->
<!-- #ifdef H5 -->
<div class="editor-body"contenteditable="true"@input="onInput"id="editor"ref="editor"
></div>
<!-- #endif --><!-- 小程序編輯器 -->
<!-- #ifdef MP-WEIXIN -->
<editor id="editor" class="editor-body" :placeholder="placeholder"@ready="onEditorReady"@input="onInput"
></editor>
<!-- #endif -->

2. 選區處理差異

不同平臺的選區API差異很大,需要分別處理:

// 處理選區問題
getSelectionRange() {return new Promise((resolve) => {// #ifdef H5const selection = window.getSelection()if (selection.rangeCount > 0) {resolve(selection.getRangeAt(0))} else {resolve(null)}// #endif// #ifdef MP-WEIXINthis.editorCtx.getSelectionRange({success: (res) => {resolve(res)},fail: () => {resolve(null)}})// #endif})
}

3. 圖片上傳大小限制

多端應用中,圖片上傳和展示需要考慮不同平臺的限制:

// 處理圖片大小限制
async handleImageUpload(file) {// 檢查文件大小if (file.size > 5 * 1024 * 1024) { // 5MBuni.showToast({title: '圖片不能超過5MB',icon: 'none'})return null}// 壓縮圖片try {// H5與小程序壓縮方式不同// #ifdef H5const compressedFile = await this.compressImageH5(file)return compressedFile// #endif// #ifdef MPconst compressedPath = await this.compressImageMP(file.path)return { path: compressedPath }// #endif} catch (e) {console.error('圖片壓縮失敗', e)return file // 失敗時使用原圖}
}

性能優化

為了讓編輯器運行更流暢,我做了以下優化:

  1. 輸入防抖 - 減少頻繁更新導致的性能問題
  2. 延遲加載圖片 - 使用懶加載機制
  3. 減少DOM操作 - 盡量批量更新DOM
  4. 使用虛擬DOM - 在復雜場景下考慮使用Vue的虛擬DOM機制
// 輸入防抖處理
onInput(e) {if (this.inputTimer) {clearTimeout(this.inputTimer)}this.inputTimer = setTimeout(() => {// #ifdef H5this.editorContent = this.$refs.editor.innerHTML// #endif// #ifdef MP-WEIXINthis.editorContent = e.detail.html// #endifthis.$emit('input', this.editorContent)}, 300)
}

總結

通過這次開發實踐,我實現了一個跨平臺的富文本編輯器組件,總結幾點經驗:

  1. 平臺差異是最大挑戰,需要利用條件編譯提供各平臺最佳實現
  2. 功能要適中,不是所有Web富文本功能都適合移動端
  3. 性能優化很重要,尤其是在低端設備上
  4. 良好的用戶體驗需要細節打磨,如適當的反饋、容錯處理等

富文本編輯是一個復雜的課題,即使是成熟的Web編輯器也有各種問題。在移動端和小程序環境中,受限更多。我們的方案雖然不完美,但通過合理的取舍和平臺適配,已經能滿足大部分應用場景的需求。

后續還可以繼續完善這個組件,比如添加表格支持、代碼高亮、Markdown轉換等高級功能。希望本文對你有所啟發,歡迎在評論區交流討論!

參考資料

  1. UniApp官方文檔
  2. execCommand API參考
  3. ContentEditable詳解

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/83045.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/83045.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/83045.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

字符串檢索算法:KMP和Trie樹

目錄 1.引言 2.KMP算法 3.Trie樹 3.1.簡介 3.2.Trie樹的應用場景 3.3.復雜度分析 3.4.Trie 樹的優缺點 3.5.示例 1.引言 字符串匹配&#xff0c;給定一個主串 S 和一個模式串 P&#xff0c;判斷 P 是否是 S 的子串&#xff0c;即找到 P 在 S 中第一次出現的位置。暴力匹…

計算機組成原理:I/O

計算機組成:I/O I/O概述I/O系統構成I/O接口I/O端口兩種編址區分I/O數據傳送控制方式程序查詢方式獨占查詢中斷控制方式硬件判優法(向量中斷法)多重中斷嵌套DMA控制方式三種DMA方式DMA操作步驟內部異常和中斷異常和中斷的關系I/O概述 I/O系統構成 一個最基礎I/O系統的構成:CPU…

ssti模板注入學習

ssti模板注入原理 ssti模板注入是一種基于服務器的模板引擎的特性和漏洞產生的一種漏洞&#xff0c;通過將而已代碼注入模板中實現的服務器的攻擊 模板引擎 為什么要有模板引擎 在web開發中&#xff0c;為了使用戶界面與業務數據&#xff08;內容&#xff09;分離而產生的&…

NVMe簡介2

共分2部分&#xff0c;這里是第2部分。 NVMe數據結構 NVMe協議中規定每個提交命令的大小為64字節&#xff0c;完成命令大小為16字節&#xff0c;NVMe命令分為Admin和IO兩類&#xff0c;NVMe的數據塊組織方式有PRP和SGL兩種。提交命令的格式如圖5所示。 圖5 提交命令數據格 N…

高壓啟動電路--學習記錄

常見反激的啟動電路 優點&#xff1a;電路設計簡單&#xff0c;價格便宜 缺點&#xff1a;損壞大&#xff0c;輸入寬范圍的時候&#xff0c;為了保證低壓能正常啟動&#xff0c;啟動電阻阻值需要選小&#xff0c;那么高壓時損耗會非常大&#xff0c;設計的不好很容易在高壓時損…

VS打印printf、cout或者Qt的qDebug等傳出的打印信息

在vs中打印printf、cout或者Qt的qDebug等常見的打印信息有時也是必要的&#xff0c;簡單的敘述一下過程&#xff1a; 1、在vs中打開你的解決方案。 2、鼠標移動到你的項目名稱上&#xff0c;點擊鼠標右鍵&#xff0c;再點擊屬性&#xff0c;此刻會此項目的屬性頁。 3、在配置…

蒼穹外賣--新增菜品

1.需求分析和設計 產品原型 業務規則&#xff1a; 菜品名稱必須是唯一的 菜品必須屬于某個分類下&#xff0c;不能單獨存在 新增菜品時可以根據情況選擇菜品的口味 每個菜品必須對應一張圖片 接口設計&#xff1a; 根據類型查詢分類(已完成) 文件上傳 新增菜品 根據類型…

如何高效集成MySQL數據到金蝶云星空

MySQL數據集成到金蝶云星空&#xff1a;SC采購入庫-深圳天一-OK案例分享 在企業信息化建設中&#xff0c;數據的高效流轉和準確對接是實現業務流程自動化的關鍵。本文將聚焦于一個具體的系統對接集成案例——“SC采購入庫-深圳天一-OK”&#xff0c;詳細探討如何通過輕易云數據…

【springcloud學習(dalston.sr1)】使用Feign實現接口調用(八)

該系列項目整體介紹及源代碼請參照前面寫的一篇文章【springcloud學習(dalston.sr1)】項目整體介紹&#xff08;含源代碼&#xff09;&#xff08;一&#xff09; &#xff08;一&#xff09;Feign的理解 前面文章【springcloud學習(dalston.sr1)】服務消費者通過restTemplat…

SpringbBoot nginx代理獲取用戶真實IP

為了演示多級代理場景&#xff0c;我們分配了以下服務器資源&#xff1a; 10.1.9.98&#xff1a;充當客戶端10.0.3.137&#xff1a;一級代理10.0.4.105&#xff1a;二級代理10.0.4.129&#xff1a;三級代理10.0.4.120&#xff1a;服務器端 各級代理配置 以下是各級代理的基本配…

實驗九視圖索引

設計性實驗 1. 創建視圖V_A包括學號&#xff0c;姓名&#xff0c;性別&#xff0c;課程號&#xff0c;課程名、成績&#xff1b; 一個語句把學號103 課程號3-105 的姓名改為陸君茹1&#xff0c;性別為女 &#xff0c;然后查看學生表的信息變化&#xff0c;再把上述數據改為原…

typeof運算符和深拷貝

typeof運算符 識別所有值類型識別函數判斷是否是引用類型&#xff08;不可再細分&#xff09; //判斷所有值類型 let a; typeof a //undefined const strabc; typeof str //string const n100; typeof n //number const …

NAT/代理服務器/內網穿透

目錄 一 NAT技術 二 內網穿透/內網打洞 三 代理服務器 一 NAT技術 跨網絡傳輸的時候&#xff0c;私網不能直接訪問公網&#xff0c;就引入了NAT能講私網轉換為公網進行訪問&#xff0c;主要解決IPv4(2^32)地址不足的問題。 1. NAT原理 當某個內網想訪問公網&#xff0c;就必…

Git的安裝和配置(idea中配置Git)

一、Git的下載和安裝 前提條件&#xff1a;IntelliJ IDEA 版本是2023.3 &#xff0c;那么配置 Git 時推薦使用 Git 2.40.x 或更高版本 下載地址&#xff1a;CNPM Binaries Mirror 操作&#xff1a;打開鏈接 → 滾動到頁面底部 → 選擇2.40.x或更高版本的 .exe 文件&#xf…

【教程】Docker更換存儲位置

轉載請注明出處&#xff1a;小鋒學長生活大爆炸[xfxuezhagn.cn] 如果本文幫助到了你&#xff0c;歡迎[點贊、收藏、關注]哦~ 目錄 背景說明 更換教程 1. 停止 Docker 服務 2. 創建新的存儲目錄 3. 編輯 Docker 配置文件 4. 遷移已有數據到新位置 5. 啟動 Docker 服務 6…

PostgreSQL 配置設置函數

PostgreSQL 配置設置函數 PostgreSQL 提供了一組配置設置函數&#xff08;Configuration Settings Functions&#xff09;&#xff0c;用于查詢和修改數據庫服務器的運行時配置參數。這些函數為數據庫管理員提供了動態管理數據庫配置的能力&#xff0c;無需重啟數據庫服務。 …

sql server 2019 將單用戶狀態修改為多用戶狀態

記錄兩種將單用戶狀態修改為多用戶狀態&#xff0c;我曾經成功過的方法&#xff0c;供參考 第一種方法 USE master; GO -- 終止所有活動連接 DECLARE kill_connections NVARCHAR(MAX) ; SELECT kill_connections KILL CAST(session_id AS NVARCHAR(10)) ; FROM sys.dm_ex…

主機A向主機B發送一個長度為L字節的文件,假設TCP的MSS為1460字節,則在TCP的序號不重復使用的前提下,L的最大值是多少?

&#x1f4d8;題干回顧&#xff1a; 主機A向主機B發送一個長度為L字節的文件&#xff0c;假設TCP的MSS為1460字節&#xff0c;則在TCP的序號不重復使用的前提下&#xff0c;L的最大值是多少&#xff1f; 這個問題關鍵在于“TCP序號不重復使用”。 ? 正確答案是&#xff1a;D.…

一次因校時服務器異常引起的性能差異分析

一次因校時服務器異常引起的性能差異分析 一.背景知識1. **TSC 頻率**:硬件級高精度計時2. **gettimeofday**:用戶態時間接口3. **adjtimex**:系統時鐘的軟件校準4. **`clock_adjtime(CLOCK_REALTIME, {modes=ADJ_TICK})`**: 用于修改系統時鐘中斷間隔(`tick` 值)。5. 關系…

acwing 4275. Dijkstra序列

題目背景 輸入 輸出 完整代碼 #include<bits/stdc.h> using namespace std; int n,m,k,a[1010],dist[1010],g[1010][1010],st[1010];int dij(int u){memset(st,0,sizeof st);memset(dist,0x3f,sizeof dist);dist[u]0;for(int i0;i<n;i){int ta[i];for(int j1;j<n;…