当前位置: 首页 > wzjs >正文

怎么做一个简单的网站wordpress看文

怎么做一个简单的网站,wordpress看文,wordpress md风格主题,常熟做网站的公司在上一篇文章需求思考及桌面应用开发技术选型中,已经确定了工具的技术方案,现在开始我们要实际动手写代码啦😄 OPEN-IMAGE-TINY,一个基于 Electron VUE3 的图片压缩工具,项目开源地址:https://github.com/…

在上一篇文章需求思考及桌面应用开发技术选型中,已经确定了工具的技术方案,现在开始我们要实际动手写代码啦😄

OPEN-IMAGE-TINY,一个基于 Electron + VUE3 的图片压缩工具,项目开源地址:https://github.com/0604hx/open-image-tiny

🧑‍💻 开发环境及依赖

本地 node 版本为 22.14.0

组件/框架版本说明
VSCode最新版代码编辑器
electron36.3.1直接用最新版本
rsbuild1.3.21基于 rspack 的打包工具,快
vue3.5.14
Naive UI3.41.0我常用的UI库
sharp0.34.22025年后的版本才支持 avif
electron-builder26.0.12electron打包工具
dayjs1.11.13日期格式化
lucide-vue-next0.511.0图标库
pinia3.0.2vue状态管理

🛠️ 搭建基本项目框架

代码逻辑

Electron 是一个基于 Chromium 和 Node.js 的框架,用于构建跨平台的桌面应用程序。它的核心原理可以概括为以下几个关键点:

  1. 多进程

    • 主进程:管理窗口、生命周期,使用 Node.js。
    • 渲染进程:每个窗口是一个网页(Chromium),默认可调用 Node.js API。
  2. 核心机制

    • Chromium:渲染页面,支持 HTML/CSS/JS。
    • Node.js:访问系统资源(如文件、网络)。
    • IPC 通信:主进程与渲染进程通过 ipcMain/ipcRenderer 交互。
  3. 跨平台

    • 打包时包含 Chromium 和 Node.js,生成各平台(Windows/macOS/Linux)应用。

项目结构

OPEN-IMAGE-TINY
├── build 				# electron 打包产物
├── dist				# 前端打包产物
├── docs				#文档
├── electron			# electron 相关代码
│   ├── handler.js		# IPC逻辑
│   ├──  main.js 		# 程序入口
│   ├──  preload.js		# 预加载脚本
│   └── tool.js 		# 工具类
├── public				# 前端资源
├── src					# 标准的 vue3 项目
├── package.json
└── rsbuild.config.mjs	# rsbuild 配置文件

页面布局

App.vue

<template><n-space vertical style="padding: 16px;"><n-alert :bordered="false" type="success" closable><template #icon> <Info /> </template>WebP 和 AVIF 是两种现代图像格式,目标都是减小文件大小、提升加载速度、同时保持较高画质。</n-alert><n-card size="small" hoverable class="text-center clickable" @click="toSelect"><div style="margin-bottom: 12px"><n-icon size="48" :depth="3"> <ImagePlus /> </n-icon></div><n-text style="font-size: 16px">点击或者拖动文件到该区域来上传</n-text><n-p depth="3" style="margin: 8px 0 0 0">支持的格式 {{ exts.join("、") }},最多 {{ max }} 张图片</n-p></n-card><n-card title="已选图片" size="small"><ImageList :images /></n-card><n-card size="small"><n-form inline :show-feedback="false"><n-form-item label="转换为"><n-select class="cell" :options v-model:value="transfer.target"></n-select></n-form-item><n-form-item label="质量值"><n-input-number class="cell" :min="0" :step="10" :max="100" v-model:value="transfer.quality" /></n-form-item></n-form></n-card><div class="text-center"><n-button @click="start" size="large" type="primary">开始图片转换</n-button></div></n-space>
</template><script setup>import { ref, reactive, toRaw } from 'vue'import { NCard, NSpace, NButton, NAlert, NUpload, NUploadDragger, NText, NP, NIcon, NForm, NFormItem, NSelect, NInputNumber, useMessage } from 'naive-ui'import { ImagePlus, CirclePlay, Info } from 'lucide-vue-next'import ImageList from '@/widget/images.vue'const max = 5const exts = ["JPG", "JPEG", "PNG", "WEBP", "AVIF"]const accept = exts.map(v=>`.${v.toLocaleLowerCase()}`).join(",")const options = exts.map(value=>({ value, label:value}))const message = useMessage()const images = ref([])const transfer = reactive({ target:"WEBP", quality:80 })const toSelect = ()=> {if(!(window.H && window.H.selectFiles))return message.error(`请在客户端内运行`)if(images.value.length >= max)return message.warning(`批量处理上限${max}个图片`)H.selectFiles(exts).then(files=>{if(Array.isArray(files)){/**@type {Array<Object>} */let imgs = images.valuefiles.forEach(f=>{if(imgs.some(v=> v.uuid == f.uuid))returnimgs.push(f)})if(imgs.length > max){imgs.length = maxmessage.info(`自动移除超范围的图片`)}images.value = imgs}})}const start = ()=>{let imgs = images.valueif(!imgs.length)    return message.warning(`请先选择图片`)for(let i=0;i<imgs.length;i++){let img = imgs[i]img.state = 1H.convert(img.path, toRaw(transfer)).then(d=>{if(d && !!d.size){img.output = d.pathimg.sized = d.sizeimg.used = d.usedimg.state = 2}else{img.state = 0img.fail = d?.fail}})}}
</script>

展示图片清单

images.vue

<template><n-table v-if="images.length" size="small" :bordered :bottom-bordered="false" single-column striped><thead><tr><th>文件名</th><th width="50px">宽度</th><th width="50px">高度</th><th width="65px">原始大小</th><th width="65px">转换后</th><th width="50px">压缩率</th><th width="30px"></th></tr></thead><tbody><tr v-for="(img, index) in images"><td><n-tooltip placement="bottom" :style><template #trigger><span class="clickable" @click="open(img.path)">{{ img.name }}</span></template>{{ img.path }}</n-tooltip></td><td>{{ img.width }}</td><td>{{ img.height }}</td><td>{{ filesize(img.size) }}</td><td> <span class="clickable" @click="open(img.output)">{{ filesize(img.sized) }}</span></td><td><n-tooltip v-if="img.sized" placement="bottom" :style><template #trigger><n-tag class="w-full" size="small" :bordered type="primary">{{ ratio(img) }}</n-tag></template><div><div><n-tag size="small" :bordered type="primary">路径</n-tag> {{img.output}}</div><div><n-tag size="small" :bordered type="primary">耗时</n-tag> {{img.used}}毫秒</div></div></n-tooltip><n-tooltip v-else-if="img.fail" :style placement="bottom"><template #trigger><n-tag class="w-full" size="small" :bordered type="error">失败</n-tag></template>{{ img.fail }}</n-tooltip></td><td class="text-center"><!-- <n-icon v-if="img.state==2"  class="clickable" :size color="#18a058" :component="CheckCircle" /> --><n-spin v-if="img.state==1" :size /><n-icon v-else class="clickable" :size :component="Trash"  @click="()=>images.splice(index, 1)"/></td></tr></tbody></n-table><n-text v-else depth="3">暂未选择图片</n-text>
</template><script setup>import { NTable, NIcon, NText, NSpin, NTooltip, NTag } from 'naive-ui'import { Trash, CheckCircle } from 'lucide-vue-next'const size = 18const bordered = falseconst style = { maxWidth: `${parseInt(window.innerWidth*0.8)}px` }const props = defineProps({images:{type:Array, default:[]},    //图片清单})const ratio = img=>{if(!(img.size && img.sized))    return ""return ((1-img.sized/img.size)*100).toFixed(2) + "%"}const open = path=> path && H.open(path)
</script>

图片转换代码

/*** @typedef {Object} ConvertConfig - 转换格式* @property {String} target - 目标格式* @property {Number} quality - 质量*//*** 转换图片格式* @param {String} origin* @param {String} target* @param {ConvertConfig} config*/
exports.convertFormat = async (origin, target, config)=>{const started = Date.now()const format = config.target.toLowerCase()const ext = path.extname(origin)if(`.${format}` == ext.toLowerCase()){console.debug(`${origin} 已经是 ${format} 格式,无需转换...`)return}if(!target){const dir = path.dirname(origin)const base = path.basename(origin, ext)target = path.join(dir, `${base}.${format}`)}let img = sharp(origin)try{await img.toFormat(format, { quality: config.quality }).toFile(target)}catch(e){img.destroy()let fail = e.message ?? econsole.error(`转换出错`, fail)return { fail }}return { path: target, size: statSync(target).size, used: Date.now() - started }
}

🧩 配置应用图标

我觉得应用图标是非常重要的一个要素,是用户开始使用应用的第一印象,值得下点功夫😄。我通过稿定设计用印章模版做了个图标。

另外还可以在iconfont-阿里巴巴矢量图标库中找现成的,通常改下颜色就能用。最后,将图片转换为 ico 格式。

📦 打包为 exe

首先我们在 package.json 中配置electron-builder

"build": {"appId": "open-image-tiny","productName": "图片压缩工具","artifactName": "${productName}-${os}-${arch}-${version}.${ext}","copyright": "Copyright © 2009-2025 集成显卡","asar": true,"compression": "maximum","asarUnpack": [],"files": ["dist/**/*","electron/**/*"],"directories": {"output": "build"},"win": {"icon": "./public/logo.ico","target": [{"target": "7z","arch": ["x64"]}]}
}

接着执行命令:

  1. pnpm ui:build:打包前端到 dist 目录
  2. pnpm package:7z:通过 electron-builder 打包到 build 目录,并压缩为 7z 格式(小新16Pro 2021款下耗时 2 分钟左右😂)


产物如下:

📷 运行预览


程序启动速度是蛮快的,内存占用情况:

❓问题集锦

依赖下载慢或者失败

可以通过设置国内镜像解决,在项目根目录创建.npmrc文件,写入以下内容:

electron_mirror=https://npmmirror.com/mirrors/electron/
electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/
sharp_binary_host=https://npmmirror.com/mirrors/sharp/
sharp_libvips_binary_host=https://npmmirror.com/mirrors/sharp-libvips/
registry=https://registry.npmmirror.com/

sharp 文件句柄占用

使用 sharp.js 打开 webp 格式文件时,即使通过调用其 destory 方法,该文件依然提示被程序占用,此时无法在资源管理器中删除😂。

目前还没有解决办法。

前端如何获取选择文件的绝对路径

通过 H5 file 标签无法获取所选文件的绝对路径,需要借助主进程。

// preload.js 注册相应函数
contextBridge.exposeInMainWorld("H", {selectFiles: (accept)=> ipcRenderer.invoke("select-files", accept)
})/*** main.js 中处理业务逻辑* @param {Electron.IpcMainInvokeEvent} e* @param {Array<String>} accept - 支持的格式*/
'select-files': async (e, accept=["JPG","JPEG","PNG","WEBP","AVIF"])=>{let files = dialog.showOpenDialogSync({title: `选择图片`,filters: [{ name:"图片", extensions:accept }],properties: ['openFile','createDirectory', 'multiSelections']})return files ?? []
}

前端依赖也被打包到 electron 中?

默认情况下,electron-builder 会将项目根目录下 package.json 中的 dependencies 依赖打包到最终产物,如果不希望前端依赖被打包,最简单的做法是把相关依赖转移到devDependencies


文章转载自:

http://Ek2iRcbh.qqrLz.cn
http://VjfhLtoG.qqrLz.cn
http://h0UPQ0a6.qqrLz.cn
http://6eYbJ9gU.qqrLz.cn
http://3ZeR9ief.qqrLz.cn
http://BwnEihKi.qqrLz.cn
http://IJChWnbX.qqrLz.cn
http://nBTdN38W.qqrLz.cn
http://2P62qldX.qqrLz.cn
http://rG17uNAO.qqrLz.cn
http://WOqPfZgB.qqrLz.cn
http://2GRIIjTp.qqrLz.cn
http://HFT93xWS.qqrLz.cn
http://hnijK90x.qqrLz.cn
http://NPzs9IvF.qqrLz.cn
http://SzdpeJsH.qqrLz.cn
http://H5lzNr2Z.qqrLz.cn
http://Zye2BD9E.qqrLz.cn
http://YcLO3w8l.qqrLz.cn
http://2nc8FsDD.qqrLz.cn
http://Kpx8WZhn.qqrLz.cn
http://CvlaaUEp.qqrLz.cn
http://rx1ourVd.qqrLz.cn
http://wTccw6jz.qqrLz.cn
http://fUQdFGPV.qqrLz.cn
http://UWkJ95wv.qqrLz.cn
http://ourdyT1q.qqrLz.cn
http://QO4tlAXj.qqrLz.cn
http://ua6cLXgB.qqrLz.cn
http://ZWW3LoTn.qqrLz.cn
http://www.dtcms.com/wzjs/688284.html

相关文章:

  • wordpress添加ssl天津seo排名效果好
  • 泸州网站建设做视频网站需要什么样的配置
  • 织梦网站定时长春网站优化方式
  • 可以做数理化的网站建筑木工模板承包报价单
  • 做网站手机软件wordpress最简洁主题
  • 怎样建设传奇网站空间世界优秀网页设计赏析
  • 黑链 对网站的影响建设网站前的需求分析
  • 网站宣传的好处拍摄公司宣传片制作
  • 网站用的服务器成都全程网络营销策划机构
  • 高端型网站wordpress 文章标题
  • 商城微网站模板网站什么也没动怎么不收录啦
  • 百度网盘优化搜索引擎的网站优化
  • iis网站正在建设中查找网站空间商
  • 只有网站才需要域名吗推广普通话宣传语100字
  • 网站建设如何传视频搭建网站要多少钱
  • 门户网站建设培训简报什么做直播网站好
  • 济南建站优化wordpress 医疗
  • 哈尔滨服务专业的建站网站建设与开发试题与答案
  • 电商网站 建社区江西h5响应式网站建设设计
  • 太原网站建设电话帝国和wordpress
  • 网站建设域名的购买网站编辑的工作职能有哪些
  • 接单做一个网站多少钱专业做网站开发
  • 如何做学校网站app怎样做医疗保健网站
  • pc和移动端网站跳转网站 建设 基本 数据库
  • 昆明做网站费用江西专业的网站建设公司
  • php网站建设全程实例自媒体135的网站是多少
  • 会用框架做网站能找到工作吗茂名平安建设网站
  • 网站标签怎么做跳转建立一个企业网站
  • 域名网站建设丰县建设网站
  • 拼团做的比较好的网站做美容网站