Vue 3 와 Electron 은 데스크 톱 엔 드 응용 에 대한 상세 한 설명 을 실현 합 니 다.

개인 수필 을 편리 하 게 기록 하기 위해 저 는 최근 에LaravelVue 3.0블 로그 시스템 을 훑 었 습 니 다.그 중에서markdown-it기반markdown편집기 Vue 구성 요소v-md-editor를 사 용 했 습 니 다.나 는 그것 으로 작성 하 는 것 이 매우 편리 하 다 고 생각한다.뒤에 한 가지 생각 이 있 습 니 다.이 구성 요 소 를 바탕 으로Electron데스크 톱 엔 드 애플 리 케 이 션 을 실현 하 는 것 도 좋 은 선택 입 니 다.
주제:VS Code 는 Electron 으로 개 발 된 데스크 톱 애플 리 케 이 션 입 니 다.저 는 지금 모 바 일 개발 을 제외 하고 모두 VS Code 로 개 발 했 습 니 다.각종 플러그 인 개발 이 정말 편리 합 니 다.
다음은 제 가 여러분 을 데 리 고 한 걸음 한 걸음 이 기능 을 실현 하 겠 습 니 다.
Vue CLI Vue 프로젝트 구축
선택 한 디 렉 터 리 에서 실행markdown사용자 정의 템 플 릿 선택(기본 Vue 3 템 플 릿 선택 가능)

Vue 3 와 TypeScript 를 선택 하 십시오.다른 옵션 은 자신의 항목 에 따라 선택 여 부 를 결정 합 니 다.
vue3 + TypeScript
실행markdown효과 보기
效果
Vue 프로젝트 를 markdown 편집기 로 개조
실행vue create electron-vue3-mark-downv-md-editor 설치
TypeScript 형식 정의 파일 추가
v-md-editor 라 이브 러 리 에 TypeScript 형식 정의 파일 이 없 기 때문에 저 는 shims-vue.d.ts 라 는 파일 뒤에 직접 추 가 했 습 니 다.물론 새 파일 에 설명 을 추가 할 수도 있 습 니 다(tsconfig.json 에서 이 파일 을 찾 을 수 있 으 면 OK).

declare module "*.vue" {
  import type { DefineComponent } from "vue";
  const component: DefineComponent<{}, {}, any>;
  export default component;
}

<!--       -->
declare module "@kangc/v-md-editor/lib/theme/vuepress.js";
declare module "@kangc/v-md-editor/lib/plugins/copy-code/index";
declare module "@kangc/v-md-editor/lib/plugins/line-number/index";
declare module "@kangc/v-md-editor";
declare module "prismjs";
App.vue 개조

<template>
  <div>
    <v-md-editor v-model="content" height="100vh"></v-md-editor>
  </div>
</template>

<script lang="ts">
//    
import VMdEditor from "@kangc/v-md-editor";
import "@kangc/v-md-editor/lib/style/base-editor.css";
import vuepress from "@kangc/v-md-editor/lib/theme/vuepress.js";
import "@kangc/v-md-editor/lib/theme/style/vuepress.css";
//     
import Prism from "prismjs";
import "prismjs/components/prism-json";
import "prismjs/components/prism-dart";
import "prismjs/components/prism-c";
import "prismjs/components/prism-swift";
import "prismjs/components/prism-kotlin";
import "prismjs/components/prism-java";

//   
import createCopyCodePlugin from "@kangc/v-md-editor/lib/plugins/copy-code/index";
import "@kangc/v-md-editor/lib/plugins/copy-code/copy-code.css";
//   
import createLineNumbertPlugin from "@kangc/v-md-editor/lib/plugins/line-number/index";
VMdEditor.use(vuepress, {
  Prism,
})
  .use(createCopyCodePlugin())
  .use(createLineNumbertPlugin());

import { defineComponent, ref } from "vue";
export default defineComponent({
  name: "App",
  components: { VMdEditor },
  setup() {
    const content = ref("");

    return { content };
  },
});
</script>

<style>
/*        */
.v-md-icon-save,
.v-md-icon-fullscreen {
  display: none;
}
</style>

이 파일 도 간단 합 니 다.전체 페이지 는 편집기npm run serve입 니 다.이 markdown 편집 기 는 하 이 라이트 디 스 플레이,코드 디 스 플레이 줄 번호,버튼 등 플러그 인 이 있 습 니 다.물론 다른 플러그 인 을 추가 하여 이 markdown 편집기 의 기능 을 풍부하게 할 수 있 습 니 다.
효 과 는 다음 과 같다.
编辑器效果
Vue CLI Plugin Electron Builder
나 는 Vite 2.0 으로 Electron 프로젝트 를 만 들 려 고 시 도 했 지만 비슷 한 Vite 와 Electron 이 결 합 된 도 구 를 찾 지 못 해 Vite 2.0 의 유혹 을 포기 했다.동료 가 있 으 면 추천 이 있 으 면 나 눠 보 세 요.npm i @kangc/v-md-editor@next -S을 사용 하여 설 치 했 습 니 다.저 는 13.0.0 의 Electron 최신 버 전 을 선 택 했 습 니 다.
저 는 보통 가장 높 은 버 전 을 선택 합 니 다.사실 이 버 전 은 구덩이 가 있 습 니 다.제 가 나중에 이 구 덩이 를 소개 해 드릴 까요?하하.
效果
우 리 는 많은 의존 라 이브 러 리 를 새로 추가 하고<v-md-editor v-model="content" height="100vh"></v-md-editor>파일 도 추가 하 는 것 을 보 았 다.간단하게 소개 하면 이 파일 은 메 인 스 레 드 에서 실 행 됩 니 다.다른 페이지 는 모두 렌 더 링 스 레 드 입 니 다.렌 더 링 스 레 드 는 제한 이 많 습 니 다.일부 기능 은 메 인 스 레 드 에서 만 실 행 될 수 있 습 니 다.여 기 는 구체 적 으로 전개 되 지 않 습 니 다.
실행vue add electron-builder효과 보기
效果
이로써 데스크 톱 애플 리 케 이 션 의 효 과 를 볼 수 있 고 Vue 코드 를 수정 하면 서 데스크 톱 애플 리 케 이 션 도 수 정 된 효 과 를 실시 간 으로 볼 수 있 습 니 다.
최적화 기능
전체 화면 표시 시작
스크린 도입

import { screen } from "electron";
창 을 만 들 때 screen 크기 로 설정

<!-- background.ts -->
async function createWindow() {
  const { width, height } = screen.getPrimaryDisplay().workAreaSize;
  const win = new BrowserWindow({
    width,
    height,
    //   ...
  });
    //   ...
}
이렇게 응용 프로그램 이 시 작 될 때 전체 화면 이 표 시 됩 니 다.
메뉴 표시 줄 수정
메뉴 표시 줄 정의

<!-- background.ts -->

const template: Array<MenuItemConstructorOptions> = [
  {
    label: "MarkDown",
    submenu: [
      {
        label: "  ",
        accelerator: "CmdOrCtrl+W",
        role: "about",
      },
      {
        label: "    ",
        accelerator: "CmdOrCtrl+Q",
        role: "quit",
      },
    ],
  },
  {
    label: "  ",
    submenu: [
      {
        label: "    ",
        accelerator: "CmdOrCtrl+O",
        click: (
          item: MenuItem,
          focusedWindow: BrowserWindow | undefined,
          _event: KeyboardEvent
        ) => {
            // TODO:              
        },
      },
      {
        label: "  ",
        accelerator: "CmdOrCtrl+S",
        click: (
          item: MenuItem,
          focusedWindow: BrowserWindow | undefined,
          _event: KeyboardEvent
        ) => {
          // TODO:       
        },
      },
    ],
  },
  {
    label: "  ",
    submenu: [
      {
        label: "  ",
        accelerator: "CmdOrCtrl+Z",
        role: "undo",
      },
      {
        label: "  ",
        accelerator: "Shift+CmdOrCtrl+Z",
        role: "redo",
      },
      {
        type: "separator",
      },
      {
        label: "  ",
        accelerator: "CmdOrCtrl+X",
        role: "cut",
      },
      {
        label: "  ",
        accelerator: "CmdOrCtrl+C",
        role: "copy",
      },
      {
        label: "  ",
        accelerator: "CmdOrCtrl+V",
        role: "paste",
      },
    ],
  },
  {
    label: "  ",
    role: "window",
    submenu: [
      {
        label: "   ",
        accelerator: "CmdOrCtrl+M",
        role: "minimize",
      },
      {
        label: "   ",
        accelerator: "CmdOrCtrl+M",
        click: (
          item: MenuItem,
          focusedWindow: BrowserWindow | undefined,
          _event: KeyboardEvent
        ) => {
          if (focusedWindow) {
            focusedWindow.maximize();
          }
        },
      },
      {
        type: "separator",
      },
      {
        label: "    ",
        accelerator: (function () {
          if (process.platform === "darwin") {
            return "Ctrl+Command+F";
          } else {
            return "F11";
          }
        })(),
        click: (
          item: MenuItem,
          focusedWindow: BrowserWindow | undefined,
          // eslint-disable-next-line @typescript-eslint/no-unused-vars
          _event: KeyboardEvent
        ) => {
          if (focusedWindow) {
            focusedWindow.setFullScreen(!focusedWindow.isFullScreen());
          }
        },
      },
    ],
  },
  {
    label: "  ",
    role: "help",
    submenu: [
      {
        label: "    ",
        click: function () {
          shell.openExternal("http://electron.atom.io");
        },
      },
    ],
  },
];
구체 적 으로 참조Electron Menu를 어떻게 정의 합 니까?
파일 을 열 고 저장 하 는 것 은 아직 실현 되 지 않 았 습 니 다.나중에 이 루어 집 니 다.
메뉴 표시 줄 설정

import { Menu } from "electron";
app.on("ready", async () => {
  //   ...
  //     
  Menu.setApplicationMenu(Menu.buildFromTemplate(template));
});
background.ts갈고리 함수 에 Menu 를 설정 합 니 다.
효과.
菜单效果
편집기 가 markdonw 파일 의 내용 을 엽 니 다.
주 스 레 드 에서 파일 을 선택 하여 렌 더 링 스 레 드 에 파일 경 로 를 전달 합 니 다.

<!-- background.ts -->
dialog
  .showOpenDialog({
    properties: ["openFile"],
    filters: [{ name: "Custom File Type", extensions: ["md"] }],
  })
  .then((res) => {
    if (res && res["filePaths"].length > 0) {
      const filePath = res["filePaths"][0];
      //          
      if (focusedWindow) {
        focusedWindow.webContents.send("open-file-path", filePath);
      }
    }
  })
  .catch((err) => {
    console.log(err);
  });
npm run electron:serve파일 을 여 는방법.입 니 다.md 파일 만 여 는 것 을 지정 하 였 습 니 다.
파일 경 로 를 얻 은 후ready이 방법 을 통 해 파일 경 로 를 렌 더 링 스 레 드 에 전달 합 니 다.
렌 더 링 스 레 드 에서 파일 경 로 를 가 져 오고 파일 내용 을 읽 으 며 markdown 편집기 에 값 을 부여 합 니 다.

<!-- App.vue -->
import { ipcRenderer } from "electron";
import { readFileSync } from "fs";

export default defineComponent({
  //   ...
  setup() {
    const content = ref("");
    
    onMounted(() => {
      // 1.
      ipcRenderer.on("open-file-path", (e, filePath: string) => {
        if (filePath && filePath.length > 0) {
          // 2.
          content.value = readFileSync(filePath).toString();
        }
      });
    });

    return { content };
  },
});

vue 노드 지원 추가

<!-- vue.config.js -->
module.exports = {
  pluginOptions: {
    electronBuilder: {
      nodeIntegration: true,
    },
  },
};

효과.
效果图
markdonw 의 내용 을 파일 에 저장 합 니 다.
주 스 레 드 가 렌 더 링 스 레 드 에 편집기 내용 을 가 져 오 라 는 요청 을 시작 합 니 다.

<!-- background.js -->
if (focusedWindow) {
    focusedWindow.webContents.send("get-content", "");
}
렌 더 링 스 레 드 메 인 스 레 드 가 편집기 의 내용 을 되 돌려 줍 니 다.

<!-- App.vue -->
onMounted(() => {
    ipcRenderer.on("get-content", () => {
        ipcRenderer.send("save-content", content.value);
    });
});
메 인 스 레 드 는 내용 을 받 고 파일 에 저장 합 니 다.

<!-- background.ts -->
//     
ipcMain.on("save-content", (event: unknown, content: string) => {
  if (openedFile.length > 0) {
    //          
    try {
      writeFileSync(openedFile, content);
      console.log("    ");
    } catch (error) {
      console.log("    ");
    }
  } else {
    const options = {
      title: "    ",
      defaultPath: "new.md",
      filters: [{ name: "Custom File Type", extensions: ["md"] }],
    };
    const focusedWindow = BrowserWindow.getFocusedWindow();
    if (focusedWindow) {
      dialog
        .showSaveDialog(focusedWindow, options)
        .then((result: Electron.SaveDialogReturnValue) => {
          if (result.filePath) {
            try {
              writeFileSync(result.filePath, content);
              console.log("    ");
              openedFile = result.filePath;
            } catch (error) {
              console.log("    ");
            }
          }
        })
        .catch((error) => {
          console.log(error);
        });
    }
  }
});
효과.
效果图
포장 하 다.
응용 프로그램의 이름과 그림 설정

<!-- vue.config.js -->
module.exports = {
  pluginOptions: {
    electronBuilder: {
      nodeIntegration: true,
      //      
      builderOptions: {
        appId: "com.johnny.markdown", 
        productName: "JJMarkDown",  //      
        copyright: "Copyright © 2021", //    
        mac: {
          icon: "./public/icon.icns", // icon
        },
      },
    },
  },
};

icon.icns 는 1024*1024 그림 을 준비 하고 같은 등급 의 디 렉 터 리 아래showOpenDialog폴 더 를 만 듭 니 다.
다양한 크기 의 그림 파일 만 들 기

sips -z 16 16 icon.png -o icons.iconset/icon_16x16.png
sips -z 32 32 icon.png -o icons.iconset/[email protected]
sips -z 32 32 icon.png -o icons.iconset/icon_32x32.png
sips -z 64 64 icon.png -o icons.iconset/[email protected]
sips -z 128 128 icon.png -o icons.iconset/icon_128x128.png
sips -z 256 256 icon.png -o icons.iconset/[email protected]
sips -z 256 256 icon.png -o icons.iconset/icon_256x256.png
sips -z 512 512 icon.png -o icons.iconset/[email protected]
sips -z 512 512 icon.png -o icons.iconset/icon_512x512.png
sips -z 1024 1024 icon.png -o icons.iconset/[email protected]
icon.icns 라 는 아이콘 파일 가 져 오기

iconutil -c icns icons.iconset -o icon.icns
포장 하 다.

npm run electron:build
결실
dmg
획득 한 dmg 파일 은 직접 설치 하여 사용 할 수 있 습 니 다.
코드

<!-- background.ts -->
"use strict";

import {
  app,
  protocol,
  BrowserWindow,
  screen,
  Menu,
  MenuItem,
  shell,
  dialog,
  ipcMain,
} from "electron";
import { KeyboardEvent, MenuItemConstructorOptions } from "electron/main";
import { createProtocol } from "vue-cli-plugin-electron-builder/lib";
import installExtension, { VUEJS3_DEVTOOLS } from "electron-devtools-installer";
const isDevelopment = process.env.NODE_ENV !== "production";
import { writeFileSync } from "fs";

let openedFile = "";
//     
ipcMain.on("save-content", (event: unknown, content: string) => {
  if (openedFile.length > 0) {
    //          
    try {
      writeFileSync(openedFile, content);
      console.log("    ");
    } catch (error) {
      console.log("    ");
    }
  } else {
    const options = {
      title: "    ",
      defaultPath: "new.md",
      filters: [{ name: "Custom File Type", extensions: ["md"] }],
    };
    const focusedWindow = BrowserWindow.getFocusedWindow();
    if (focusedWindow) {
      dialog
        .showSaveDialog(focusedWindow, options)
        .then((result: Electron.SaveDialogReturnValue) => {
          if (result.filePath) {
            try {
              writeFileSync(result.filePath, content);
              console.log("    ");
              openedFile = result.filePath;
            } catch (error) {
              console.log("    ");
            }
          }
        })
        .catch((error) => {
          console.log(error);
        });
    }
  }
});

const template: Array<MenuItemConstructorOptions> = [
  {
    label: "MarkDown",
    submenu: [
      {
        label: "  ",
        accelerator: "CmdOrCtrl+W",
        role: "about",
      },
      {
        label: "    ",
        accelerator: "CmdOrCtrl+Q",
        role: "quit",
      },
    ],
  },
  {
    label: "  ",
    submenu: [
      {
        label: "    ",
        accelerator: "CmdOrCtrl+O",
        click: (
          item: MenuItem,
          focusedWindow: BrowserWindow | undefined,
          // eslint-disable-next-line @typescript-eslint/no-unused-vars
          _event: KeyboardEvent
        ) => {
          dialog
            .showOpenDialog({
              properties: ["openFile"],
              filters: [{ name: "Custom File Type", extensions: ["md"] }],
            })
            .then((res) => {
              if (res && res["filePaths"].length > 0) {
                const filePath = res["filePaths"][0];
                //          
                if (focusedWindow) {
                  focusedWindow.webContents.send("open-file-path", filePath);
                  openedFile = filePath;
                }
              }
            })
            .catch((err) => {
              console.log(err);
            });
        },
      },
      {
        label: "  ",
        accelerator: "CmdOrCtrl+S",
        click: (
          item: MenuItem,
          focusedWindow: BrowserWindow | undefined,
          // eslint-disable-next-line @typescript-eslint/no-unused-vars
          _event: KeyboardEvent
        ) => {
          if (focusedWindow) {
            focusedWindow.webContents.send("get-content", "");
          }
        },
      },
    ],
  },
  {
    label: "  ",
    submenu: [
      {
        label: "  ",
        accelerator: "CmdOrCtrl+Z",
        role: "undo",
      },
      {
        label: "  ",
        accelerator: "Shift+CmdOrCtrl+Z",
        role: "redo",
      },
      {
        type: "separator",
      },
      {
        label: "  ",
        accelerator: "CmdOrCtrl+X",
        role: "cut",
      },
      {
        label: "  ",
        accelerator: "CmdOrCtrl+C",
        role: "copy",
      },
      {
        label: "  ",
        accelerator: "CmdOrCtrl+V",
        role: "paste",
      },
    ],
  },
  {
    label: "  ",
    role: "window",
    submenu: [
      {
        label: "   ",
        accelerator: "CmdOrCtrl+M",
        role: "minimize",
      },
      {
        label: "   ",
        accelerator: "CmdOrCtrl+M",
        click: (
          item: MenuItem,
          focusedWindow: BrowserWindow | undefined,
          // eslint-disable-next-line @typescript-eslint/no-unused-vars
          _event: KeyboardEvent
        ) => {
          if (focusedWindow) {
            focusedWindow.maximize();
          }
        },
      },
      {
        type: "separator",
      },
      {
        label: "    ",
        accelerator: (function () {
          if (process.platform === "darwin") {
            return "Ctrl+Command+F";
          } else {
            return "F11";
          }
        })(),
        click: (
          item: MenuItem,
          focusedWindow: BrowserWindow | undefined,
          // eslint-disable-next-line @typescript-eslint/no-unused-vars
          _event: KeyboardEvent
        ) => {
          if (focusedWindow) {
            focusedWindow.setFullScreen(!focusedWindow.isFullScreen());
          }
        },
      },
    ],
  },
  {
    label: "  ",
    role: "help",
    submenu: [
      {
        label: "    ",
        click: function () {
          shell.openExternal("http://electron.atom.io");
        },
      },
    ],
  },
];

protocol.registerSchemesAsPrivileged([
  { scheme: "app", privileges: { secure: true, standard: true } },
]);

async function createWindow() {
  const { width, height } = screen.getPrimaryDisplay().workAreaSize;
  const win = new BrowserWindow({
    width,
    height,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false,
    },
  });

  if (process.env.WEBPACK_DEV_SERVER_URL) {
    // Load the url of the dev server if in development mode
    await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string);
    if (!process.env.IS_TEST) win.webContents.openDevTools();
  } else {
    createProtocol("app");
    // Load the index.html when not in development
    win.loadURL("app://./index.html");
  }
}

// Quit when all windows are closed.
app.on("window-all-closed", () => {
  // On macOS it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== "darwin") {
    app.quit();
  }
});

app.on("activate", () => {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (BrowserWindow.getAllWindows().length === 0) createWindow();
});

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on("ready", async () => {
  if (isDevelopment && !process.env.IS_TEST) {
    // Install Vue Devtools
    try {
      await installExtension(VUEJS3_DEVTOOLS);
    } catch (e) {
      console.error("Vue Devtools failed to install:", e.toString());
    }
  }
  createWindow();
  //     
  Menu.setApplicationMenu(Menu.buildFromTemplate(template));
});

// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
  if (process.platform === "win32") {
    process.on("message", (data) => {
      if (data === "graceful-exit") {
        app.quit();
      }
    });
  } else {
    process.on("SIGTERM", () => {
      app.quit();
    });
  }
}
Vue 3 와 Electron 이 데스크 톱 엔 드 애플 리 케 이 션 을 실현 하 는 데 관 한 상세 한 설명 을 소개 합 니 다.더 많은 Vue 3 Electron 데스크 톱 엔 드 애플 리 케 이 션 내용 은 예전 의 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 많은 응원 부 탁 드 리 겠 습 니 다!

좋은 웹페이지 즐겨찾기