主頁 > 企業開發 > Electron-ChatGPT桌面端ChatGPT實體|electron25+vue3聊天AI模板EXE

Electron-ChatGPT桌面端ChatGPT實體|electron25+vue3聊天AI模板EXE

2023-06-09 08:11:31 企業開發

基于electron25+vite4+vue3仿制chatgpt客戶端聊天模板ElectronChatGPT

electron-chatgpt 使用最新桌面端技術Electron25.x結合Vite4.x全家桶技術開發跨端模仿ChatGPT智能聊天程式模板,支持經典+分欄兩種布局、暗黑+明亮主題模式,集成electron封裝多視窗及通訊功能,

技術堆疊

  • 編碼工具:vscode
  • 框架技術:electron25+vite4+vue3+pinia2
  • 組件庫:veplus (基于vue3自定義組件庫)
  • 打包工具:electron-builder^23.6.0
  • 除錯工具:electron-devtools-installer^3.2.0
  • 代碼高亮:highlight.js^11.7.0
  • markdown組件:vue3-markdown-it
  • 本地快取:pinia-plugin-persistedstate^3.1.0
  • electron結合vite插件:vite-plugin-electron^0.11.2

專案結構

基于electron最新版本融合vite4.x技術搭建模仿chatgpt桌面端程式,

如果對electron+vite4創建跨端應用及多開視窗感興趣,可以去看看之前的這兩篇分享文章,

https://www.cnblogs.com/xiaoyan2017/p/17436076.html

https://www.cnblogs.com/xiaoyan2017/p/17442502.html

隨著electron快速迭代更新,加上vite極速編譯,二者配合創建的應用運行速度超快,

Vue3桌面UI組件庫

考慮到專案比較輕量級,所以采用自研vue3組件庫ve-plus

關于veplus組件庫這里不作過多介紹,之前有過一篇分享文章,大家可以去看看,

https://www.cnblogs.com/xiaoyan2017/p/17170454.html

專案布局

專案整體大致分為頂部導航工具列+左側會話記錄/操作鏈接+右側會話區/編輯框等模塊,

<template>
    <div class="vegpt__layout flexbox flex-col">
        <!-- //頂部工具列 -->
        <Toolbar />
        
        <div class="ve__layout-body flex1 flexbox">
            <!-- //側邊欄 -->
            <div class="ve__layout-menus flexbox" :class="{'hidden': store.config.collapse}">
                <aside class="ve__layout-aside flexbox flex-col">
                    <ChatNew />
                    <Scrollbar class="flex1" autohide size="4" gap="1">
                        <ChatList />
                    </Scrollbar>
                    <ExtraLink />
                    <Collapse />
                </aside>
            </div>

            <!-- //主體區域 -->
            <div class="ve__layout-main flex1 flexbox flex-col">
                <Main />
            </div>
        </div>
    </div>
</template>

Electron主行程入口

根目錄下新建 electron-main.js 作為主行程入口檔案,

/**
 * 主行程入口
 * @author YXY
 */

const { app, BrowserWindow } = require('electron')

const MultiWindow = require('./src/multiwindow')

// 屏蔽安全警告
// ectron Security Warning (Insecure Content-Security-Policy)
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'

const createWindow = () => {
    let win = new MultiWindow()
    win.createWin({isMainWin: true})
}

app.whenReady().then(() => {
    createWindow()
    app.on('activate', () => {
        if(BrowserWindow.getAllWindows().length === 0) createWindow()
    })
})

app.on('window-all-closed', () => {
    if(process.platform !== 'darwin') app.quit()
})

使用electron的vite插件,在vite.config.js中配置入口,

import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import electron from 'vite-plugin-electron'
import { resolve } from 'path'
import { parseEnv } from './src/utils/env'

export default defineConfig(({ command, mode }) => {
  const viteEnv = loadEnv(mode, process.cwd())
  const env = parseEnv(viteEnv)

  return {
    plugins: [
      vue(),
      electron({
        // 主行程入口檔案
        entry: 'electron-main.js'
      })
    ],
    
    /*構建選項*/
    build: {
      /* minify: 'esbuild', // 打包方式 esbuild(打包快)|terser
      chunkSizeWarningLimit: 2000, // 打包大小警告
      rollupOptions: {
          output: {
              chunkFileNames: 'assets/js/[name]-[hash].js',
              entryFileNames: 'assets/js/[name]-[hash].js',
              assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',
          }
      } */
      
      // 如果打包方式是terser,則配置如下
      /* minify: "terser",
      terserOptions: {
        compress: {
          // 去掉所有console和debugger
          // drop_console: true,
          // drop_debugger: true,

          drop_console: command !== 'serve',
          drop_debugger: command !== 'serve',
          //pure_funcs:['console.log'] // 移除console.log
        }
      } */
    },
    esbuild: {
      // 打包去除 console.log 和 debugger
      drop: env.VITE_DROP_CONSOLE && command === 'build' ? ["console", "debugger"] : []
    },

    /*開發服務器選項*/
    server: {
      //
      port: env.VITE_PORT,
      // ...
    },

    resolve: {
      // 設定別名
      alias: {
        '@': resolve(__dirname, 'src'),
        '@assets': resolve(__dirname, 'src/assets'),
        '@components': resolve(__dirname, 'src/components'),
        '@views': resolve(__dirname, 'src/views')
      }
    }
  }
})

需要注意:由于目前Electron 尚未支持 "type": "module",需要在package.json中去掉,并且配置 "main": "electron-main.js", 入口,

Electron自定義無邊框視窗工具列

創建視窗的時候配置 frame: false 引數,創建的視窗則沒有系統頂部導航欄及邊框,拖拽區域/最大化/最小化及關閉按鈕均需要自定義操作,

通過設定css3屬性 -webkit-app-region: drag ,則可對自定義區域進行拖拽操作,設定后按鈕/鏈接點擊則會失效,這時通過對按鈕或鏈接設定-webkit-app-region: no-drag就可恢復事件回應,

不過設定-webkit-app-region: drag,點擊滑鼠右鍵,會出現上圖系統選單,經過一番除錯,windows下可以暫時通過如下方法屏蔽右鍵選單,

// 屏蔽系統右鍵選單
win.hookWindowMessage(278, () => {
    win.setEnabled(false)
    setTimeout(() => {
        win.setEnabled(true)
    }, 100)

    return true
})

components/titlebar目錄自定義工具列條,

control.vue自定義最大化/最小化/關閉按鈕

<template>
    <div class="vegpt__control ve__nodrag">
        <div class="vegpt__control-btns" :style="{'color': color}">
            <slot />
            <div v-if="isTrue(minimizable)" class="btn win-btn win-min" @click="handleMin"><i class="iconfont ve-icon-minimize"></i></div>
            <div v-if="isTrue(maximizable) && winCfg.window.resizable" class="btn win-btn win-maxmin" @click="handleRestore">
                <i class="iconfont" :class="isMaximized ? 've-icon-maxrestore' : 've-icon-maximize'"></i>
            </div>
            <div v-if="isTrue(closable)" class="btn win-btn win-close" @click="handleQuit"><i class="iconfont ve-icon-close"></i></div>
        </div>
    </div>
</template>
<template>
    <div >
        <div  :style="{'color': color}">
            <slot />
            <div v-if="isTrue(minimizable)"  @click="handleMin"><i ></i></div>
            <div v-if="isTrue(maximizable) && winCfg.window.resizable"  @click="handleRestore">
                <i  :></i>
            </div>
            <div v-if="isTrue(closable)"  @click="handleQuit"><i ></i></div>
        </div>
    </div>
</template>


<script setup>
    import { onMounted, ref } from 'vue'
    import { winCfg, setWin } from '@/multiwindow/actions'
    import { appStore } from '@/pinia/modules/app'
    import { isTrue } from '@/utils'

    const appState = appStore()

    const props = defineProps({
        // 標題顏色
        color: String,

        // 視窗是否可以最小化
        minimizable: { type: [Boolean, String], default: true },
        // 視窗是否可以最大化
        maximizable: { type: [Boolean, String], default: true },
        // 視窗是否可以關閉
        closable: { type: [Boolean, String], default: true }
    })

    // 是否最大化
    let isMaximized = ref(false)

    onMounted(() => {
        window.electronAPI.invoke('win__isMaximized').then(data =https://www.cnblogs.com/xiaoyan2017/archive/2023/06/09/> {
            console.log(data)
            isMaximized.value = data
        })
        window.electronAPI.receive('win__hasMaximized', (e, data) => {
            console.log(data)
            isMaximized.value = data
        })
    })

    // 最小化
    const handleMin = () => {
        window.electronAPI.send('win__minimize')
    }
    // 最大化/還原
    const handleRestore = () => {
        window.electronAPI.invoke('win__max2min').then(data =https://www.cnblogs.com/xiaoyan2017/archive/2023/06/09/> {
            console.log(data)
            isMaximized.value = data
        })
    }
    // 關閉表單
    const handleQuit = () => {
        if(winCfg.window.isMainWin) {
            MessageBox.confirm('應用提示', '是否最小化到托盤, 不退出程式?', {
                type: 'warning',
                cancelText: '最小化至托盤',
                confirmText: '殘忍退出',
                confirmType: 'danger',
                width: 300,
                callback: action => {
                    if(action == 'confirm') {
                        appState.$reset()
                        setWin('close')
                    }else if(action == 'cancel') {
                        setWin('hide', winCfg.window.id)
                    }
                }
            })
        }else {
            setWin('close', winCfg.window.id)
        }
    }
</script>

在 index.vue 中引入 control.vue 操作按鈕,并支持自定義左側、標題等功能,

<template>
    <div class="vegpt__titlebar" :class="{'fixed': isTrue(fixed), 'transparent fixed': isTrue(transparent)}">
        <div class="vegpt__titlebar-wrapper flexbox flex-alignc ve__drag" :style="{'background': bgcolor, 'color': color, 'z-index': zIndex}">
            <slot name="left">
                <img src="/logo.png" height="20" style="margin-left: 10px;" />
            </slot>
            <div class="vegpt__titlebar-title" :class="{'center': isTrue(center)}">
                <slot name="title">{{ title || winCfg.window.title || env.VITE_APPTITLE }}</slot>
            </div>

            <!-- 控制按鈕 -->
            <Control :minimizable="minimizable" :maximizable="maximizable" :closable="closable">
                <slot name="btn" />
            </Control>
        </div>
    </div>
</template>

Electron創建系統托盤圖示

// 創建系統托盤圖示
createTray() {
    console.log('——+——+——Start Create Tray!')
    console.log(__dirname)
    console.log(join(process.env.ROOT, 'resource/tray.ico'))
    
    const trayMenu = Menu.buildFromTemplate([
        {
            label: '打開主界面',
            icon: join(process.env.ROOT, 'resource/home.png'),
            click: () => {
                try {
                    for(let i in this.group) {
                        let win = this.getWin(i)
                        if(!win) return
                        // 是否主視窗
                        if(this.group[i].isMainWin) {
                            if(win.isMinimized()) win.restore()
                            win.show()
                        }
                    }
                } catch (error) {
                    console.log(error)
                }
            }
        },
        {
            label: '設定中心',
            icon: join(process.env.ROOT, 'resource/setting.png'),
            click: () => {
                for(let i in this.group) {
                    let win = this.getWin(i)
                    if(win) win.webContents.send('win__ipcData', { type: 'CREATE_WIN_SETTING', value: null })
                }
            },
        },
        {
            label: '鎖屏',
            icon: join(process.env.ROOT, 'resource/lock.png'),
            click: () => null,
        },
        {
            label: '關閉托盤閃爍',
            click: () => {
                this.flashTray(false)
            }
        },
        {type: 'separator'},
        /* {
            label: '重啟',
            click: () => {
                // app.relaunch({ args: process.argv.slice(1).concat(['--relaunch']) })
                // app.exit(0)
            }
        }, */
        {
            label: '關于',
            click: () => {
                for(let i in this.group) {
                    let win = this.getWin(i)
                    if(win) win.webContents.send('win__ipcData', { type: 'CREATE_WIN_ABOUT', value: null })
                }
            }
        },
        {
            label: '關閉應用并退出',
            icon: join(process.env.ROOT, 'resource/quit.png'),
            click: () => {
                dialog.showMessageBox(this.main, {
                    title: '詢問',
                    message: '確定要退出應用程式嗎?',
                    buttons: ['取消', '最小化托盤', '退出應用'],
                    type: 'error',
                    noLink: false,  // true傳統按鈕樣式  false鏈接樣式
                    cancelId: 0
                }).then(res => {
                    console.log(res)

                    const index = res.response
                    if(index == 0) {
                        console.log('取消')
                    }if(index == 1) {
                        console.log('最小化托盤')
                        for(let i in this.group) {
                            let win = this.getWin(i)
                            if(win) win.hide()
                        }
                    }else if(index == 2) {
                        console.log('退出應用')

                        try {
                            for(let i in this.group) {
                                let win = this.getWin(i)
                                if(win) win.webContents.send('win__ipcData', { type: 'WIN_LOGOUT', value: null })
                            }
                            // app.quit 和 app.exit(0) 都可退出應用,
                            // 前者可以被打斷并觸發一些事件,而后者將強制應用程式退出而不觸發任何事件或允許應用程式取消操作,
                            app.quit()
                        } catch (error) {
                            console.log(error)
                        }
                    }
                })
            }
        }
    ])
    this.tray = new Tray(this.trayIco1)
    this.tray.setContextMenu(trayMenu)
    this.tray.setToolTip(app.name)
    this.tray.on('double-click', () => {
        console.log('double clicked')
    })

    // 開啟托盤閃爍
    // this.flashTray(true)
}

托盤圖示、右鍵選單圖示及打包圖示均在resource目錄下,

Electron打包腳本electron-builder

在根目錄新建一個electron打包組態檔electron-builder.json,

{
    "productName": "Electron-ChatGPT",
    "appId": "com.yxy.electron-chatgpt-vue3",
    "copyright": "Copyright ? 2023-present Andy",
    "compression": "maximum",
    "asar": true,
    "directories": {
        "output": "release/${version}"
    },
    "nsis": {
        "oneClick": false,
        "allowToChangeInstallationDirectory": true,
        "perMachine": true,
        "deleteAppDataOnUninstall": true,
        "createDesktopShortcut": true,
        "createStartMenuShortcut": true,
        "shortcutName": "ElectronVite4Vue3"
    },
    "win": {
        "icon": "./resource/shortcut.ico",
        "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}",
        "target": [
            {
                "target": "nsis",
                "arch": ["ia32"]
            }
        ]
    },
    "mac": {
        "icon": "./resource/shortcut.icns",
        "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}"
    },
    "linux": {
        "icon": "./resource",
        "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}"
    }
}

Electron主渲染行程通訊傳值

由于electron主渲染行程一般都是單視窗之間進行傳值,如果需要在多個視窗間傳值,如切換主題功能,則需要在渲染行程發送請求,主行程監聽后再發送請求給渲染行程(App.vue中監聽),

<div
    class="toolbar__item"
    :title="`切換 暗黑/明亮 模式(當前 ${appState.config.isDark ? '暗黑' : '明亮'}模式)`"
    @click="changeMode"
>
    <Icon :name="appState.config.isDark ? 've-icon-sunny' : 've-icon-yewan'" />
</div>

// 主題切換
const changeMode = () => {
    appState.config.isDark = !appState.config.isDark
    ipcRenderer.send('win__postData', appState.config.isDark)
}

在主行程中使用ipcMain.on監聽,

// 主/渲染行程傳參
ipcMain.on('win__postData', (event, args) => {
    mainWin.webContents.send('win__postData', args)
})

然后在渲染行程App.vue頁面監聽并處理通訊傳值,

/**
 * 接收主行程發送的事件
 */
ipcRenderer.on('win__postData', (e, data) => {
    console.log('——+——+——receive multiwin data:', data)

    switch(data.type) {
        // 退出登錄
        case 'WIN_LOGOUT':
            appState.$reset()
            break;
        // 布局切換
        case 'CHANGE_LAYOUT':
            appState.config.layout = data.value
            break;
        // 切換主題
        case 'CHANGE_MODE':
            appState.config.isDark = data.value
            appState.changeDark()
            break;
        // 側邊欄收縮
        case 'CHANGE_COLLAPSE':
            appState.config.collapse = data.value
            break;
      }
})

這樣就能簡單實作多視窗傳值了,如果大家有其他方法,歡迎一起交流學習哈~

Ok,基于electron25+vue3開發桌面端仿chatgpt聊天實體就先分享到這里,希望對大家有所幫助??

最后附上一個Vue3+Tauri跨端聊天專案

https://www.cnblogs.com/xiaoyan2017/p/16830689.html

 

本文為博主原創文章,未經博主允許不得轉載,歡迎大家一起交流 QQ(282310962) wx(xy190310)

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/554717.html

標籤:其他

上一篇:記一次BootCDN被黑產掛馬導致站點跳轉博彩網站的問題

下一篇:返回列表

標籤雲
其他(160654) Python(38218) JavaScript(25488) Java(18210) C(15237) 區塊鏈(8270) C#(7972) AI(7469) 爪哇(7425) MySQL(7238) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5873) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4588) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2435) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1984) 功能(1967) HtmlCss(1956) Web開發(1951) C++(1933) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1880) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • Electron-ChatGPT桌面端ChatGPT實體|electron25+vue3聊天AI模板E

    基于electron25+vite4+vue3仿制chatgpt客戶端聊天模板ElectronChatGPT。 electron-chatgpt 使用最新桌面端技術Electron25.x結合Vite4.x全家桶技術開發跨端模仿ChatGPT智能聊天程式模板。支持經典+分欄兩種布局、暗黑+明亮主題模 ......

    uj5u.com 2023-06-09 08:11:31 more
  • 記一次BootCDN被黑產掛馬導致站點跳轉博彩網站的問題

    近期發現公司某些站點出現偶爾跳轉博彩網站的現象,經過排查發現該現象為供應鏈投毒攻擊,BootCDN上的靜態資源無一例外均被污染, 當外站引入BootCDN的靜態資源時,如果請求攜帶的Referer頭為指定值(涉及公司隱私不便透露),User-Agent頭為手機瀏覽器UA,觸發惡意代碼注入。 例如:h ......

    uj5u.com 2023-06-09 08:05:55 more
  • VUE | Element組件庫的 el-collapse 標簽的用法

    Collapse 折疊面板:通過折疊面板收納內容區域。 1. 基礎用法 可以折疊展開多個面板,面板之間互不影響。 示例代碼 <el-collapse v-model="activeNames" @change="handleChange"> <el-collapse-item title="一致性 ......

    uj5u.com 2023-06-09 08:05:38 more
  • 記錄--7 個沙雕又帶有陷阱的 JS 面試題

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 為了保證的可讀性,本文采用意譯而非直譯。 在 JS 面試中,經常會看到一些簡單而又沙雕的題目,這些題目包含一些陷阱,但這些在我們規范的編碼下或者業務中基本不會出現。 有些面試官就是這樣,不專注于制定代碼的標準和規范上,卻用不規范的代碼去檢 ......

    uj5u.com 2023-06-09 08:05:21 more
  • JavaScript如何解決單執行緒缺陷——webWorker

    # 解決JavaScript單執行緒問題——webWorkers > 參考檔案 [使用 Web Workers - Web API 介面參考 | MDN (mozilla.org)](https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Worker ......

    uj5u.com 2023-06-09 08:05:14 more
  • 原生AJAX的學習

    基礎知識 知識點梳理見圖: 自己動手實踐案例 案例1: 訪問本地檔案 <!DOCTYPE html> <html> <body> <div id="demo"> <h1>XMLHttpRequest 物件</h1> <button type="button" onclick="loadDoc()"> ......

    uj5u.com 2023-06-09 08:05:04 more
  • 記一次BootCDN被黑產掛馬導致站點跳轉博彩網站的問題

    近期發現公司某些站點出現偶爾跳轉博彩網站的現象,經過排查發現該現象為供應鏈投毒攻擊,BootCDN上的靜態資源無一例外均被污染, 當外站引入BootCDN的靜態資源時,如果請求攜帶的Referer頭為指定值(涉及公司隱私不便透露),User-Agent頭為手機瀏覽器UA,觸發惡意代碼注入。 例如:h ......

    uj5u.com 2023-06-09 08:04:45 more
  • 記錄--7 個沙雕又帶有陷阱的 JS 面試題

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 為了保證的可讀性,本文采用意譯而非直譯。 在 JS 面試中,經常會看到一些簡單而又沙雕的題目,這些題目包含一些陷阱,但這些在我們規范的編碼下或者業務中基本不會出現。 有些面試官就是這樣,不專注于制定代碼的標準和規范上,卻用不規范的代碼去檢 ......

    uj5u.com 2023-06-09 08:04:33 more
  • 原生AJAX的學習

    基礎知識 知識點梳理見圖: 自己動手實踐案例 案例1: 訪問本地檔案 <!DOCTYPE html> <html> <body> <div id="demo"> <h1>XMLHttpRequest 物件</h1> <button type="button" onclick="loadDoc()"> ......

    uj5u.com 2023-06-09 08:04:19 more
  • 50道常見高頻大廠面試題

    > https://www.bilibili.com/video/BV1os4y1E7YT/?p=15&share_source=copy_web&vd_source=a867c73df17cfff8d16caf9e5b58a1aa ## 給你一個站點目標以后如何滲透? 1. **資訊收集**:收集 ......

    uj5u.com 2023-06-09 08:03:35 more