【electron+vue3+ts实战便笺exe】⼆、electron+vue3开发内容不要让⾃⼰的上限成为你的底线
本来以为有万字的。。没想到才堪堪近6000字。为了⽔⽂的嫌疑,只挑了重点的地⽅讲,⽐如component内的组件就挑了右键弹窗去说明,建议在看本⽂的时候边查看项⽬,有不懂的可以在下⽅评论,谢谢。
接上篇配置篇,这⾥更新了⼀下vue3的版本3.0.4,本篇⽂章只讲开发内容,主要还是vue3⽅⾯,长⽂警告。ps:smartblue这个主题好好看。。。
router
增加meta中的title属性,显⽰在软件上⽅头部
import { createRouter, createWebHashHistory } from 'vue-router';
import { RouteRecordRaw } from 'vue-router';
import main from '../views/main.vue';
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'main',
component: main,
children: [
{
path: '/',
name: 'index',
component: () => import('../views/index.vue'),
meta: {
title: 'I便笺'
}
},
{
path: '/editor',
name: 'editor',
component: () => import('../views/editor.vue'),
meta: {
title: ''
}
},
{
path: '/setting',
name: 'setting',
component: () => import('../views/setting.vue'),
meta: {
title: '设置'
}
}
]
}
];
const router = createRouter({
history: v.BASE_URL),
routes
});
export default router;
utils
/* eslint-disable @typescript-eslint/ban-types */
import { winURL } from '@/config';
import { BrowserWindow, remote } from 'electron';
type FunctionalControl = (this: any, fn: any, delay?: number) => (...args: any) => void;
type DebounceEvent = FunctionalControl;
type ThrottleEvent = FunctionalControl;
/
/ 防抖函数
export const debounce: DebounceEvent = function(fn, delay = 1000) {
let timer: NodeJS.Timeout | null = null;
return (...args: any) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
};
// 节流函数
export const throttle: ThrottleEvent = function(fn, delay = 500) {
let flag = true;
return (...args: any) => {
if (!flag) return;
flag = false;
setTimeout(() => {
fn.apply(this, args);
flag = true;
}, delay);
};
};
/
/ 创建窗⼝
export const createBrowserWindow = (bwopt = {}, url = '/', devTools = true): BrowserWindow | null => {
let childrenWindow: BrowserWindow | null;
childrenWindow = new remote.BrowserWindow(bwopt);
if (v.NODE_ENV === 'development' && devTools) {
childrenWindow.webContents.openDevTools();
}
childrenWindow.loadURL(`${winURL}/#${url}`);
<('closed', () => {
childrenWindow = null;
});
return childrenWindow;
};
// 过渡关闭窗⼝
export const transitCloseWindow = (): void => {
document.querySelector('#app')?.ve('app-show');
document.querySelector('#app')?.classList.add('app-hide');
};
// uuid
export const uuid = (): string => {
const S4 = () => {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
};
return S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4();
};
main.vue
main.vue⽂件主要是作为⼀个整体框架,考虑到页⾯切换时候的动效,分为头部和主体部分,头部作为⼀个单独的组件处理,内容区域使⽤router-view渲染。
html部分,这⾥和vue2.x有点区别的是,在vue2.x中可以直接
// bad
<transition name="fade">
<keep-alive>
<router-view />
</keep-alive>
</transition>
上⾯的这种写法在vue3中会在控制台报异常,记不住写法的可以看看控制台
<router-view v-slot="{ Component }">
<transition name="main-fade">
<div class="transition" :key="routeName">
<keep-alive>
<component :is="Component" />
</keep-alive>
</div>
</transition>
</router-view>
然后就是ts部分了,使⽤vue3的写法去写,script标签注意需要写上lang="ts"代表是ts语法。router的写法也不⼀样,虽然在vue3中还能写vue2的格式,但是不推荐使⽤。这⾥是获取route的name属性,来进⾏⼀个页⾯过渡的效果。
<script lang="ts">
electron vue教程import { defineComponent, ref, onBeforeUpdate } from 'vue';
import { useRoute } from 'vue-router';
import Header from '@/components/header.vue';
export default defineComponent({
components: {
Header
},
setup() {
const routeName = ref(useRoute().name);
onBeforeUpdate(() => {
routeName.value = useRoute().name;
});
return {
routeName
};
}
});
</script>
less部分
<style lang="less" scoped>
.main-fade-enter,
.main-fade-leave-to {
display: none;
opacity: 0;
animation: main-fade 0.4s reverse;
}
.main-fade-enter-active,
.main-fade-leave-active {
opacity: 0;
animation: main-fade 0.4s;
}
@keyframes main-fade {
from {
opacity: 0;
transform: scale(0.96);
}
to {
opacity: 1;
transform: scale(1);
}
}
</style>
以上就是main.vue的内容,在页⾯刷新或者进⼊的时候根据useRouter().name的切换进⾏放⼤的过渡效果,后⾯的内容会更简洁⼀点。header.vue
onBeforeRouteUpdate
头部组件还有⼀个标题过渡的效果,根据路由导航获取当前路由的mate.title变化进⾏过渡效果。vue3中路由守卫需要从vue-route导⼊使⽤。import { onBeforeRouteUpdate, useRoute } from 'vue-router';
...
onBeforeRouteUpdate((to, from, next) => {
title.value = to.meta.title;
currentRouteName.value = to.name;
next();
});
computed
这⾥是计算不同的路由下标题内边距的不同,⾸页是有个设置⼊⼝的按钮,⽽设置页⾯是只有两个按钮,computed会返回⼀个你需要的新的值// 获取⾸页的内边距
const computedPaddingLeft = computed(() => {
return currentRouteName.value === 'index' ? 'padding-left: 40px;' : '';
});
emit⼦传⽗和props⽗传⼦
vue3没有了this,那么要使⽤emit怎么办呢?在⼊⼝setup中有2个参数
setup(props, content) {}
props是⽗组件传给⼦组件的内容,props常⽤的emit和props都在content中。
这⾥需要注意的是,使⽤props和emit需要先定义,才能去使⽤,并且会在vscode中直接调⽤时辅助弹窗显⽰
props⽰例
emit⽰例
export default defineComponent({
props: {
test: String
},
emits: ['option-click', 'on-close'],
// 如果只⽤emit的话可以使⽤es6解构
// 如:setup(props, { emit })
setup(props, content) {
console.st, it('option-click'));
}
})
electron打开窗⼝
import { browserWindowOption } from '@/config';
import { createBrowserWindow, transitCloseWindow } from '@/utils';
...
const editorWinOptions = browserWindowOption('editor');
// 打开新窗⼝
const openNewWindow = () => {
createBrowserWindow(editorWinOptions, '/editor');
};
electron图钉固定屏幕前⾯
先获取当前屏幕实例
这⾥需要注意的是,需要从remote获取当前窗⼝信息
判断当前窗⼝是否在最前⾯isAlwaysOnTop(),然后通过setAlwaysOnTop()属性设置当前窗⼝最前⾯。import { remote } from 'electron';
...
// 获取窗⼝固定状态
let isAlwaysOnTop = ref(false);
const currentWindow = CurrentWindow();
isAlwaysOnTop.value = currentWindow.isAlwaysOnTop();
// 固定前⾯
const drawingPin = () => {
if (isAlwaysOnTop.value) {
currentWindow.setAlwaysOnTop(false);
isAlwaysOnTop.value = false;
} else {
currentWindow.setAlwaysOnTop(true);
isAlwaysOnTop.value = true;
}
};
electron关闭窗⼝
这⾥是在utils封装了通过对dom的样式名操作,达到⼀个退出的过渡效果,然后再关闭。
/
/ 过渡关闭窗⼝
export const transitCloseWindow = (): void => {
document.querySelector('#app')?.ve('app-show');
document.querySelector('#app')?.classList.add('app-hide');
};
noteDb数据库
yarn add nedb @types/nedb
数据储存在nedb中,定义字段,并在根⽬录的shims-vue.d.ts加⼊类型
/**
* 储存数据库的
*/
interface DBNotes {
className: string; // 样式名
content: string; // 内容
readonly createdAt: Date; // 创建时间,这个时间是nedb⾃动⽣成的
readonly uid: string; // uid,utils中的⽅法⽣成
readonly updatedAt: Date; // update,⾃动创建的
readonly _id: string; // ⾃动创建的
}
对nedb的封装
⾃我感觉这⾥写的有点烂。。。勿喷,持续学习中
这⾥的QueryDB是shims-vue.d.ts定义好的类型
这⾥的意思是QueryDB<T>是⼀个对象,然后这个对象传⼊⼀个泛型T,这⾥keyof T获取这个对象的key(属性)值,?:代表这个key可以是undefined,表⽰可以不存在。T[K]表⽰从这个对象中获取这个K的值。
type QueryDB<T> = {
[K in keyof T]?: T[K];
};
import Datastore from 'nedb';
import path from 'path';
import { remote } from 'electron';
/**
* @see www.npmjs/package/nedb
*/
class INoteDB<G = any> {
/**
* 默认储存位置
* C:\Users\{Windows User Name}\AppData\Roaming\i-notes
*/
// dbPath = path.join(Path('userData'), 'db/inote.db');
// dbPath = './db/inote.db';
dbPath = this.path;
_db: Datastore<Datastore.DataStoreOptions> = this.backDatastore;
get path() {
if (v.NODE_ENV === 'development') {
return path.join(__dirname, 'db/inote.db');
}
return path.join(Path('userData'), 'db/inote.db');
}
get backDatastore() {
return new Datastore({
/**
* autoload
* default: false
* 当数据存储被创建时,数据将⾃动从⽂件中加载到内存,不必去调⽤loadDatabase
* 注意所有命令操作只有在数据加载完成后才会被执⾏
*/
autoload: true,
filename: this.dbPath,
timestampData: true
});
}
refreshDB() {
this._db = this.backDatastore;
}
insert<T extends G>(doc: T) {
return new Promise((resolve: (value: T) => void) => {
this._db.insert(doc, (error: Error | null, document: T) => {
if (!error) resolve(document);
});
});
}
/**
* db.find(query)
* @param {Query<T>} query: object类型,查询条件,可以使⽤空对象{}。
* ⽀持使⽤⽐较运算符($lt, $lte, $gt, $gte, $in, $nin, $ne)
* 逻辑运算符($or, $and, $not, $where)
* 正则表达式进⾏查询。
*/
find(query: QueryDB<DBNotes>) {
return new Promise((resolve: (value: DBNotes[]) => void) => {
this._db.find(query, (error: Error | null, document: DBNotes[]) => {
if (!error) resolve(document as DBNotes[]);
});
});
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。