<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>安陌乐轩</title><description>Amlx&apos;s Blog</description><link>https://blog.amlx.top/</link><language>zh_CN</language><item><title>服务器安装宝塔面板后SSH连接失败</title><link>https://blog.amlx.top/posts/bt-ssh/</link><guid isPermaLink="true">https://blog.amlx.top/posts/bt-ssh/</guid><description>OpenSSL版本不匹配导致SSH服务无法启动。</description><pubDate>Wed, 15 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;问题原因&lt;/h2&gt;
&lt;p&gt;SSH服务依赖的OpenSSL库版本与编译时使用的版本不一致，这通常发生在系统更新后。&lt;/p&gt;
&lt;h2&gt;解决方案&lt;/h2&gt;
&lt;h3&gt;方案1：重新安装SSH服务（推荐）&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 备份当前SSH配置
cp -r /etc/ssh /etc/ssh_backup

# 重新安装OpenSSH服务端
# 对于CentOS/RHEL：
yum remove openssh-server -y &amp;amp;&amp;amp; yum install openssh-server -y

# 对于Ubuntu/Debian：
apt remove openssh-server -y &amp;amp;&amp;amp; apt install openssh-server -y
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;方案2：重新配置并重建SSH&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 重新生成SSH主机密钥
rm -f /etc/ssh/ssh_host_*
ssh-keygen -A

# 重启SSH服务
systemctl restart sshd
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;方案3：检查OpenSSL版本并修复&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 检查当前OpenSSL版本
openssl version

# 检查SSH依赖的OpenSSL库
ldd /usr/sbin/sshd | grep ssl

# 重新安装OpenSSL（谨慎操作）
# CentOS/RHEL：
yum reinstall openssl openssl-libs -y

# Ubuntu/Debian：
apt reinstall openssl libssl-dev -y
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;完整修复步骤&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# 1. 停止SSH服务
systemctl stop sshd

# 2. 重新安装SSH服务（选择适合你系统的命令）
# CentOS/RHEL:
yum remove openssh-server -y
yum install openssh-server -y

# Ubuntu/Debian:
apt remove openssh-server -y
apt install openssh-server -y

# 3. 恢复配置文件（使用你之前修改的配置）
# 将之前备份的配置复制回来，或者重新配置关键参数
cp /etc/ssh_backup/sshd_config /etc/ssh/sshd_config

# 4. 确保关键配置正确（参考之前的修改）
# Port 26508
# PermitRootLogin yes
# PasswordAuthentication yes
# PubkeyAuthentication yes
# UsePAM yes

# 5. 重新生成主机密钥
ssh-keygen -A

# 6. 启动SSH服务
systemctl start sshd
systemctl enable sshd

# 7. 检查状态
systemctl status sshd
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;如果上述方法都不行&lt;/h2&gt;
&lt;h3&gt;方案4：使用包管理器修复依赖&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 对于yum系系统
yum clean all
yum update
yum reinstall openssh-server openssh-clients

# 对于apt系系统
apt update
apt --fix-broken install
apt reinstall openssh-server openssh-client
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;方案5：编译安装最新版本（最后手段）&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 下载最新OpenSSH源码编译
cd /tmp
wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.5p1.tar.gz
tar -xzf openssh-9.5p1.tar.gz
cd openssh-9.5p1
./configure
make &amp;amp;&amp;amp; make install
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;验证修复&lt;/h2&gt;
&lt;p&gt;修复后检查：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 检查SSH服务状态
systemctl status sshd

# 检查端口监听
netstat -tlnp | grep 26508

# 测试本地连接
ssh -p 26508 localhost
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;重要提醒&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;操作前确保宝塔终端可用&lt;/strong&gt;，因为重新安装SSH会暂时中断服务&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;备份现有配置&lt;/strong&gt;，避免丢失自定义设置&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;修复后测试连接&lt;/strong&gt;：&lt;code&gt;ssh -p 26508 root@你的服务器IP&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>uniapp中在app端获取视频第一帧作为封面</title><link>https://blog.amlx.top/posts/uniapp-video/</link><guid isPermaLink="true">https://blog.amlx.top/posts/uniapp-video/</guid><pubDate>Sun, 05 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;类似微信视频消息&lt;/h1&gt;
&lt;p&gt;需要在播放视频之前展示缩略图，因为app端没有dom，这里需要用到renderjs，通过创建video标签，然后通过canvas绘制图片的方式。
传入的url最好是base64格式，因为在浏览器中，由于安全限制，JavaScript无法直接访问本地文件系统路径（如file:///或_doc/...这样的路径），如果是网络格式的url则可以直接传入，下面是完整代码示例。&lt;/p&gt;
&lt;h2&gt;使用:&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;VideoGetPoster :videoUrl=&quot;content&quot; @change=&quot;changeGetPoster&quot;&amp;gt;&amp;lt;/VideoGetPoster&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;组件内部：&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;template&amp;gt;
    &amp;lt;view v-if=&quot;base64&quot; :src=&quot;base64&quot; :change:src=&quot;video.setPoster&quot;&amp;gt;&amp;lt;/view&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
    export default {
        name:&quot;video-get-poster&quot;,
        props: {
            videoUrl: {
            type: String,
            require: true
        }
    },
    data() {
        return {
            base64: &apos;&apos;
        };
    },
    mounted() {
        this.$tools.pathToBase64App(this.videoUrl, (base64) =&amp;gt; {
            this.base64 = base64
        })
    },
    methods: {
        getPoster(poster) {
            this.$emit(&apos;change&apos;, poster)
        }
    }
}
&amp;lt;/script&amp;gt;

&amp;lt;script lang=&quot;renderjs&quot; module=&quot;video&quot;&amp;gt;
    export default {
    methods: {
    setPoster(newV, oldV, ownerInstance) {
    let video = document.createElement(&quot;video&quot;);
    video.muted = true
    // 通过setAttribute给video dom元素添加自动播放的属性，因为视频播放才能获取封面图  
    video.autoplay = true;
    //允许跨域访问  
    video.crossOrigin = &apos;anonymous&apos;;
    // 上面我们只是创建了video标签，视频播放需要内部的source的标签，scr为播放源  
    video.innerHTML = &apos;&amp;lt;source src=&apos; + newV + &apos; type=&quot;video/mp4&quot;&amp;gt;&apos;
    // loadeddata 事件在媒体当前播放位置的视频帧（通常是第一帧）加载完成后触发。
    video.addEventListener(&apos;loadeddata&apos;, function() {
    let canvas = document.createElement(&quot;canvas&quot;),
    width = video.videoWidth, //canvas的尺寸和图片一样
    height = video.videoHeight;
    canvas.width = width;
    canvas.height = height;
    canvas.getContext(&quot;2d&quot;).drawImage(video, 0, 0, width, height); //绘制canvas
    let poster = canvas.toDataURL(&apos;image/jpeg&apos;); //转换为base64
    ownerInstance.callMethod(&apos;getPoster&apos;, poster)
});
}
}
}
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;本地文件路径转base64：&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;/**  
 * 文件转成base64字符串
 * @param {String} path // 文件地址
 * @param {Object} callback  // 返回base64字符串
 */
export function pathToBase64App(path, callback) {
	// 通过URL参数获取目录对象或文件对象
	 plus.io.resolveLocalFileSystemURL(path, function(entry) {
		 entry.file(function(file) {
			 var fileReader = new plus.io.FileReader()
			 fileReader.onload = function(evt) {
				 callback(evt.target.result)
			 }
			 fileReader.onerror = function(error) {
				console.log(&apos;failed: &apos;, error);
			 }
			 fileReader.readAsDataURL(file)
		 }, function(error) {
			 console.log(&apos;failed: &apos;, error);
		 })
	 }, function(error) {
		console.log(&apos;failed: &apos;, error);
	 })
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>vue3富文本编辑组件</title><link>https://blog.amlx.top/posts/vue3-editor/</link><guid isPermaLink="true">https://blog.amlx.top/posts/vue3-editor/</guid><pubDate>Tue, 23 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;下载：&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;npm install @wangeditor/editor @wangeditor/editor-for-vue@next
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;使用：&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;template&amp;gt;
    &amp;lt;div class=&quot;tool-bar&quot;&amp;gt;
        &amp;lt;!-- 工具栏 --&amp;gt;
        &amp;lt;Toolbar
            class=&quot;tool-style&quot;
            :editor=&quot;editorRef&quot;
            :defaultConfig=&quot;toolbarConfig&quot;
        /&amp;gt;
        &amp;lt;!-- 编辑器 --&amp;gt;
        &amp;lt;Editor
            class=&quot;content&quot;
            v-model=&quot;valueHtml&quot;
            :defaultConfig=&quot;editorConfig&quot;
            @onCreated=&quot;handleCreated&quot;
        /&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script setup&amp;gt;
import { onBeforeUnmount, ref, shallowRef} from &apos;vue&apos;
import &apos;@wangeditor/editor/dist/css/style.css&apos;
import { Editor, Toolbar } from &apos;@wangeditor/editor-for-vue&apos;

const editorRef = shallowRef()
const toolbarConfig= ref()
// 内容 HTML
const valueHtml = ref(&apos;&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;&apos;)
 
// 编辑器配置
const editorConfig = {
  placeholder: &apos;请输入内容...&apos;,
  MENU_CONF: { /* 菜单配置，下文解释 */ }
}
 
const handleCreated = (editor) =&amp;gt; {
  editorRef.value = editor // 记录 editor 实例，重要！
}
 
// 组件销毁时，及时销毁编辑器
onBeforeUnmount(() =&amp;gt; {
  const editor = editorRef.value
  if (editor == null) return
  editor.destroy()
})
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>vite+vue3+electron项目搭建教程</title><link>https://blog.amlx.top/posts/vite-vue3-electron/</link><guid isPermaLink="true">https://blog.amlx.top/posts/vite-vue3-electron/</guid><pubDate>Fri, 02 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;一、使用vite创建vue项目&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;在自己的项目文件夹下打开终端，输入以下命令创建vue项目&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;npm create vite@latest
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;是否需要使用TS看自己的需求，为了方便我这里就选用JS了
&lt;img src=&quot;./assets/create-vue.png&quot; alt=&quot;图片&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;进入项目目录，安装依赖&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;# 进入文件夹
cd &amp;lt;你的项目名称&amp;gt;

# 安装依赖
npm install 
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;二、安装electron&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;在项目中下载electron下载命令：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;# 如果安装失败建议使用cnpm，至于cnpm怎么安装使用，请自行搜索
npm install electron -D
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;定义入口文件&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;因为vue项目的默认入口文件是index.html，eletron项目的默认入口文件是main.js，所以这里我们就要解决两个框架搭配时，以哪个文件为入口。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;手动在vue项目中的根目录创建文件
&lt;img src=&quot;./assets/create-file.png&quot; alt=&quot;图片&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;然后在 &amp;lt;span style=&quot;color:red&quot;&amp;gt;package.json&amp;lt;/span&amp;gt; 中修改人口文件&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;main&quot;: &quot;electron/main/main.js&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;因为vue项目默认采用的是ES6的模块化，而electron基于nodejs，nodejs的模块化又是使用的commonjs，所以我们在 &amp;lt;span style=&quot;color:red&quot;&amp;gt;package.json&amp;lt;/span&amp;gt; 中的type标签，修改为：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;type&quot;: &quot;commonjs&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;三、运行electron项目&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;在自己创建的electron文件夹下的main.js文件里写入这段代码&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;const { app, BrowserWindow } = require(&apos;electron&apos;)
const createWindow = () =&amp;gt; {
    const win = new BrowserWindow({
        width: 800,
        height: 600
    })

    win.loadURL(&apos;http://localhost:5173&apos;)
    // win.loadFile(&apos;index.html&apos;)

}
app.whenReady().then(() =&amp;gt; {
    createWindow()
    app.on(&apos;activate&apos;, () =&amp;gt; {
        if (BrowserWindow.getAllWindows().length === 0) createWindow()
    })
})
app.on(&apos;window-all-closed&apos;, () =&amp;gt; {
    if (process.platform !== &apos;darwin&apos;) app.quit()
})
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;这段代码的大概意思就是创建了一个窗口，然后窗口里展示的内容就是一个网页，这里写的URL可以是本地地址也可以是线上地址，比如你写个http://www.baidu.com这样运行过后就是百度的客户端了，聪明的小伙伴应该知道我这里写http://localhost:5173的含义了吧&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;运行项目&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;在终端中依次执行以下命令&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;# 启动vue项目
npm run dev

# 启动electron，这里的命令是electron官方指定的
npx electron .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./assets/create-electron.png&quot; alt=&quot;图片&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;到这里就能看到vue项目成功的启动在一个桌面端应用程序了&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;四、完善必备的工具&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;以上集成方案是很原始的，在真实的项目开发中肯定是不可取的&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;缺点：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;第一：没有热刷新&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;第二：每次启动都需要执行两个命令&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;第三：打包后页面丢失&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;下面的步骤就是来解决这几个问题的&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;热更新工具&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;安装：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;npm install nodemon -D
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;重新更改scripts标签的启动命令，监控文件改变，代码如下：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;scripts&quot;: {
     &quot;electron&quot;: &quot;nodemon --exec electron . --watch ./ --ext .js,.html,.css,.vue&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;这个参数的含义是：用nodemon启动electron，并监控js、html、css、vue等文件的变动。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;命令行合并工具
现在开发环境，每次都需要执行“npm run dev”和“npx electron .”两个命令才能启动项目。所以我们希望能够有一个命令可以同时执行vue和electron。&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;安装：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;npm install npm-run-all -D
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;这个工具只需要在package.json中scripts标签增加命令：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;scripts&quot;: {
    &quot;electron:dev&quot;: &quot;npm-run-all --parallel dev electron&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;这时候运行&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;npm run electron:dev
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;打包工具&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;安装：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;npm install electron-builder -D
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;配置electron-builder：&lt;/li&gt;
&lt;li&gt;在scriptes标签里面增加命令：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;scripts&quot;: {
    &quot;electron:build&quot;: &quot;vite build &amp;amp;&amp;amp; electron-builder&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;打包配置&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;打包配置 package.json文件中增加配置&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;build&quot;: {
    &quot;productName&quot;: &quot;electron-vue&quot;,
    &quot;appId&quot;: &quot;electron-vue&quot;,
    &quot;asar&quot;: true,
    &quot;directories&quot;: {
      &quot;output&quot;: &quot;dist_electron/${version}&quot;
    },
    &quot;files&quot;: [
      &quot;dist&quot;,
      &quot;electron&quot;
    ],
    &quot;nsis&quot;: {
      &quot;oneClick&quot;: false,
      &quot;allowToChangeInstallationDirectory&quot;: true
    },
    &quot;mac&quot;: {
      &quot;category&quot;: &quot;your.app.category.type&quot;
    },
    &quot;win&quot;: {
      &quot;icon&quot;: &quot;./electron/log.ico&quot;,
      &quot;target&quot;: [
        {
          &quot;target&quot;: &quot;nsis&quot;,
          &quot;arch&quot;: [
            &quot;ia32&quot;
          ]
        }
      ]
    },
    &quot;linux&quot;: {}
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;这是打包的一个最基础的配置，简单讲解：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;productName：// 项目名，也是生成的安装文件名&lt;/li&gt;
&lt;li&gt;appId：软件的ID，用来上架各平台。&lt;/li&gt;
&lt;li&gt;nsis：软件安装包的交互行为，配置稍多，请自行查询。&lt;/li&gt;
&lt;li&gt;win.icon：// 应用图标&lt;/li&gt;
&lt;li&gt;win.target.arch：建议设置ia32，适配windows大部分版本。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;更详细的说明可以查阅官方文档&lt;/h3&gt;
&lt;p&gt;这时候就可以运行新加的命令打包了，但是打包过后还是发现页面是空白的，这是因为之前我们在main.js里面填写的是本地址http://localhost:5173，打包过后就没有这个地址了，所以我们要把地址换成 ../dist/index.html，这样就可以了
&lt;img src=&quot;./assets/create-folder.png&quot; alt=&quot;图片&quot; /&gt;
&lt;img src=&quot;./assets/create-main.png&quot; alt=&quot;图片&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最后还需要配置以下vite，让vite打包后的静态资源能够以本地file协议访问。vite配置属于前端知识点了，有兴趣的同学可以自行了解下。&lt;/li&gt;
&lt;li&gt;vite.config.js的配置如下：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import { defineConfig } from &apos;vite&apos;
import vue from &apos;@vitejs/plugin-vue&apos;
import {resolve} from &apos;path&apos;

export default defineConfig({
  plugins: [vue()],
  base:&apos;./&apos;,
  manifest:true,  //配置后才能让编译后的vue路径被正确识别
  resolve: {
    alias: {
      &apos;@&apos;: resolve(__dirname, &apos;./src&apos;)
    }
  },
  optimizeDeps: {
    exclude: [&apos;electron&apos;], // 告诉 Vite 排除预构建 electron，不然会出现 __diranme is not defined
  }
})
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;补充说明：&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;每次打包前都需要手动更改页面路径会显得很麻烦，所以可以利用electron提供的一个属性判断当前是开发环境还是生产环境，以下是详细代码&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;const { app } = require(&apos;electron&apos;)
const {join} = require(&apos;path&apos;)

// 映射页面
const env = app.isPackaged ? &apos;production&apos; : &apos;development&apos;
const indexHtml = {
    development: &apos;http://localhost:5173&apos;, // 开发环境
    production: join(__dirname, &apos;../dist/index.html&apos;) // 生产环境
}
win.loadURL(indexHtml[env])
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;ctrl + shift + i 打开控制台调试工具&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>使用fetch流式获取响应数据</title><link>https://blog.amlx.top/posts/fetch/</link><guid isPermaLink="true">https://blog.amlx.top/posts/fetch/</guid><pubDate>Thu, 13 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言简介：&lt;/h2&gt;
&lt;p&gt;流式获取一般在用于数据量比较大的情况，一次性返回会导致前端页面加载时间过长或者请求超时等问题，这时候我们就可以考虑使用流式的方式拿到部分数据并先展示，从而提升用户体验。
我这里的场景是在对接chatGPT语言模型的时候采取的这种方案，因为目前的大语言的模型的结果都是需要一点点计算的，如果提出的问题比较复杂就导致响应的时间过长。&lt;/p&gt;
&lt;h2&gt;这里我们采用浏览器原生的fetchAP来实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;fetch(&apos;&amp;lt;请求地址&amp;gt;&apos;, {
    method: &apos;POST&apos;, // 请求方法
    headers: {
        &quot;Content-Type&quot;: &quot;application/json&quot;,  // 请求头
        Authorization: token // 校验令牌，根据自己的服务器需求传
    },
    body: JSON.stringify({  // 请求体
        prompt: [{role: &apos;user&apos;, content: data}],
        incremental: true
    })
}).then(async result =&amp;gt; {
    const reader = result.body.getReader() // 创建读取器
    const textDecoder = new TextDecoder() // 创建解码器
    while(true){  // 循环读取内容
        /* 读取其中一部分内容 done 是否读取完成， value 读取到的内容 */
        const {done, value} = await reader.read()
        if(done){
            return
        }
        const str = textDecoder.decode(value) // 利用解码器把数据解析成字符串
        console.log(str) // 这时候str就是服务器返回的内容
    }
})
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>使用post请求建立长连接实现sse</title><link>https://blog.amlx.top/posts/post/</link><guid isPermaLink="true">https://blog.amlx.top/posts/post/</guid><pubDate>Thu, 13 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;ul&gt;
&lt;li&gt;最近遇到一个chatGPT的相关功能，需要做成文字一个个弹出的功能，如果使用普通的http请求会一次性得到结果，如果后台计算时间太长还可能会导致超时，影响用户体验。&lt;/li&gt;
&lt;li&gt;所以使用长连接SSE(Server Sent Event)，直译为服务器发送事件，顾名思义，也就是客户端可以获取到服务器发送的事件。我们常见的 http 交互方式是客户端发起请求，服务端响应，然后一次请求完毕；但是在 sse 的场景下，客户端发起请求，连接一直保持，服务端有数据就可以返回数据给客户端，这个返回可以是多次间隔的方式。&lt;/li&gt;
&lt;li&gt;但是问题是原生的EventSource 不能使用post方法，只能使用get方法，而且还不能自定义请求header，所以我们可以使用npm包：@microsoft/fetch-event-source&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;npm i @microsoft/fetch-event-source
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import {fetchEventSource} from &apos;@microsoft/fetch-event-source&apos;;

const controller = new AbortController()
const signal = controller.signal

fetchEventSource(&apos;请求的url&apos;, {
	method: &apos;POST&apos;,
	signal: signal,
	headers: {
        /* 请求头配置 */
    },
	body: JSON.stringify({
        /* 发送的内容 */
    }),
	onmessage(msg) {
        let result= JSON.parse(msg.data);  // 得到的数据
    },
	onerror(err){
        // 必须抛出错误才会停止
        throw err
    }
})
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>html中资源修饰符的用法及作用</title><link>https://blog.amlx.top/posts/html-modifier/</link><guid isPermaLink="true">https://blog.amlx.top/posts/html-modifier/</guid><pubDate>Sat, 28 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;ol&gt;
&lt;li&gt;async（script标签布尔属性）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;异步加载js文件，不阻塞html解析，当js加载完毕后，停止解析html，立即执行js，执行完毕后继续解析html&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script async src=&quot;js/index.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;defer（script标签布尔属性）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;html解析完成后在执行js，类似于DOMContentLoaded事件&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script defer src=&quot;js/index.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;reload（link标签rel属性）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;预先加载资源（任意资源），不执行任何操作，一般用来缓存时用，其它地方使用这个资源的时候就不会再次发送请求了&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;link rel=&quot;preload&quot; href=&quot;css/index.css&quot;&amp;gt;
&amp;lt;link rel=&quot;preload&quot; href=&quot;js/index.js&quot;&amp;gt;
&amp;lt;link rel=&quot;preload&quot; href=&quot;img/home-bg.png&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;prefetch（link标签rel属性）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;跟preload类似，只是拿取的时机不一致，preload是页面加载时立即获取，而prefetch是当浏览器空闲的时候获取&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;link rel=&quot;prefetch&quot; href=&quot;css/index.css&quot;&amp;gt;
&amp;lt;link rel=&quot;prefetch&quot; href=&quot;js/index.js&quot;&amp;gt;
&amp;lt;link rel=&quot;prefetch&quot; href=&quot;img/home-bg.png&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>WebSocket使用及优化（心跳机制与断线重连）</title><link>https://blog.amlx.top/posts/websocket/</link><guid isPermaLink="true">https://blog.amlx.top/posts/websocket/</guid><pubDate>Fri, 22 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;ul&gt;
&lt;li&gt;WebSocket在2008年被提出，其通信协议于2011被制定为标准
与http不同，websocket支持全双工通信（即：在客户端和服务之间双向通信）在websocket问世之前，客户端与服务器通常采用http轮询和Comet等方式保持长链接
然而，这么做无疑会对服务端造成资源消耗，因为HTTP请求包含较长的头文件，只传递了少许的有用信息，十分消耗资源。&lt;/li&gt;
&lt;li&gt;于是websocket便诞生了，它不仅节省资源和带宽，更是能实现长链接作用，只需客户端主动与服务端握手一次，即可进行实时通信，实现推送技术。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;const socketURL = &apos;ws://xxxx.com/&apos; // 连接地址
let ws = null    // websocket实例
let lockReconnect = false; // websocket连接状态，避免重复连接
let reconnectTimer = 2000  // 断开后2s自动重连

function setWebsocket() {
  ws = new WebSocket(socketURL)
  ws.addEventListener(&apos;open&apos;, () =&amp;gt; {
    alert(&apos;服务连接成功&apos;);
  })
  ws.addEventListener(&apos;close&apos;, () =&amp;gt; {
    alert(&apos;服务已断开&apos;);
    reconnect()
  })
  ws.addEventListener(&apos;error&apos;, () =&amp;gt; {
    alert(&apos;服务发生异常&apos;);
    reconnect()
  })
  ws.addEventListener(&apos;message&apos;, event =&amp;gt; {
    alert(&apos;收到消息：&apos;, event.data)
  })
}

// 重连
function reconnect() {
  if (lockReconnect) return;
  lockReconnect = true;
  setTimeout(function () {
    setWebsocket();
    lockReconnect = false;
  }, reconnectTimer);
}

// 页面关闭断开socket连接
window.onbeforeunload = function() {
  ws.close()
  return;
}

// 向外暴露
export {
    ws
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>JS监听页面关闭</title><link>https://blog.amlx.top/posts/window-close/</link><guid isPermaLink="true">https://blog.amlx.top/posts/window-close/</guid><pubDate>Thu, 05 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;JS可以监听浏览器页面的关闭，主要使用了window对象的onbeforeunload方法&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;在以前（旧版本的浏览器中），可以自定义提示文案&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;window.onbeforeunload = function (e) {
    var message = &apos;some word&apos;;
    e = e || window.event;

    if (e) {
        e.returnValue = message;
    }

    return message;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;但在新版本的浏览器中，为了安全性，已经不支持自定义弹窗&lt;/li&gt;
&lt;li&gt;能做的，只是调用浏览器自带的提示确认窗格&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt; window.onbeforeunload = function(e) {
    console.log(&apos;beforeunload&apos;)

    /** 只有以下情况不会弹出确认窗格
      * 不return
      * return;
      * return undefined;
      * return null;
      */
    
    /** 其他任意值都会弹出
      * return &apos;&apos;;
      * return false;
      * return 0;
      * return 1; 
      * .......
      */


    return 1;
 }
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>关于uniapp不同平台下100vh单位的区别</title><link>https://blog.amlx.top/posts/uniapp-safe/</link><guid isPermaLink="true">https://blog.amlx.top/posts/uniapp-safe/</guid><pubDate>Mon, 15 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;uni-app带来了诸多方便也带来各平台之间的差异。&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;在H5端css属性height: 100vh是相对于整个浏览器视口高度；&lt;/li&gt;
&lt;li&gt;非H5平台则是减掉原生导航栏和原生tabbar的高度，因此会造成H5端出现垂直滚动条，非H5平台则正常显示的问题。
解决方案: 利用uniapp提供的条件编译，H5端使用100vh - 44px(原生导航栏高度)- 50px(原生tabBar高度)，非H5平台端则直接使用100vh即可。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;.page{
    // #ifndef APP-PLUS
	height: calc(100vh - 44px - 50px); // 非APP平台下生效
	// #endif
	// #ifdef APP-PLUS
	height: calc(100vh);    // APP平台下生效
	// #endif
	background-color: #ededed;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item></channel></rss>