Bootstrap

Vue3+vite+electron实现打包及访问本机硬盘文件

viteElectron

  • Vite是一种新型前端构建工具,能够显著提升前端开发体验。由尤大推出,其发动态表示“再也回不去webpack了...”
  • Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入Chromium和Node.js到二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发 经验。
  • 因此,若要使用viteelectron,还需要自己来配置。
     

一. 创建一个vite项目

1.安装vite

yarn create vite

2.创建项目

yarn create vite electron-vue --template vue

3.进入并运行项目

cd electron
yarn install
yarn dev


二.配置Electron

1.官方文档 

Electron官网的快速入门文档中,有官方给出的利用html、javascript、css来创建一个electron应用的案例,vite+electron的方案也借鉴其中。 

2.安装 

首先安装electron至vite应用。 

yarn add --dev electron

 如果安装不上,可能会是网络问题,使用手机wifi连接然后进行安装。如果还是不行更换淘宝源。

3.配置文件 

1)在vite.config.js文件里

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

// https://vitejs.dev/config/
export default defineConfig({
  base: process.env.ELECTRON == "true" ? "./" : "./",
  plugins: [vue()],
  css: {
    preprocessorOptions: {
      less: {
        math: "always", // 括号内才使用数学计算
        globalVars: {
          // 全局变量
          // mainColor: "red",
        },
      },
    },
  },
});

 2)electron主进程文件夹

在项目根目录下创建一个electron文件夹,并在下面创建一个mian.js和preload.js文件

mian.js

// electron/mian.js
const { app, BrowserWindow } = require("electron");
const path = require("path");
const isDev = process.env.IS_DEV == "true" ? true : false;

function createWindow() {
  //创建浏览器窗口。
  const mainWindow = new BrowserWindow({
    width: 480,
    height: 800,
    webPreferences: {
      contextIsolation: false,
      nodeIntegration: true,
      webSecurity: false,
      preload: path.join(__dirname, "preload.js"),
    },
  });

// 加载 index.html
  mainWindow.loadURL(
    isDev
      ? "http://localhost:5173"
      : `file://${path.join(__dirname, "../dist/index.html")}`
  );
    // 是否打开开发工具
  if (isDev) {
    mainWindow.webContents.openDevTools();
  }
}

// 这段程序将会在 Electron 结束初始化
// 和创建浏览器窗口的时候调用
// 部分 API 在 ready 事件触发后才能使用。
app.whenReady().then(() => {
  createWindow();
  app.on("activate", function () {
     // 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他
    // 打开的窗口,那么程序会重新创建一个窗口。
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
});

// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在
// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。
app.on("window-all-closed", () => {
  if (process.platform !== "darwin") {
    app.quit();
  }
});

3)preload.js 

   preload是electron提供 的一个中间层功能,electron在构造嵌入html窗口的过程中可在preload.js中写一些代码得到node.js提供的fs对象

// electron/preload.js

// 所有Node.js API都可以在预加载过程中使用。
// 它拥有与Chrome扩展一样的沙盒。
window.ipcRenderer = require("electron").ipcRenderer;
window.shell = require("electron").shell;
window.addEventListener("DOMContentLoaded", () => {
  const replaceText = (selector, text) => {
    const element = document.getElementById(selector);
    if (element) element.innerText = text;
  };

  for (const dependency of ["chrome", "node", "electron"]) {
    replaceText(`${dependency}-version`, process.versions[dependency]);
  }
});

 4)在package.json对象中添加下列属性

 "build": {
    "appId": "com.my-website.my-app",
    "productName": "MyApp",
    "copyright": "Copyright © 2019 ${author}",
    "mac": {
      "category": "public.app-category.utilities"
    },
    "nsis": {
      "oneClick": false,
      "allowToChangeInstallationDirectory": true
    },
    "files": [
      "dist/**/*",
      "electron/**/*"
    ],
    "directories": {
      "buildResources": "assets",
      "output": "dist_electron"
    }
  }

修改 package.json 的 scripts 属性 

 "scripts": {
      "dev": "vite --host",
      "build": "vite build",
      "serve": "vite preview",
      "electron": "wait-on tcp:5173 && cross-env IS_DEV=true electron .",
      "electron:dev": "concurrently -k \"cross-env BROWSER=none npm run dev\" \"npm run electron\"",
      "electron:build.win": "npm run build && electron-builder --win --dir",
      "electron:build.linux": "npm run build && electron-builder --linux appImage",
      "electron:build.test": "npm run build && electron-builder --dir",
      "electron:build.exe": "npm run build && electron-builder --win"
  },

 修改 package.json 的顶部属性,最重要的是要添加 main 指明入口

{
  "name": "vite-electron",
  "author": "github@lathesky",
  "version": "0.0.0",
  "main": "electron/electron.js",
}

 到此,vue+vite+electron打包流程完毕。接下来是实现使用electron完成操控本机电脑文件夹。

三.访问本机盘符并进行操作

1.了解如何操控
涉及到的读文件的方法就是使用node.js提借的fs类库,但是他默认读取的文件都是在磁盘某个路径,比如传的参数 是d:\xxx文件这样的,主进程可以读取electron文件夹下面的文件,vue是读不到electron文件夹下面的文件,所以用主进程读文件再把结果发到vue页面上。

1.2 什么是主进程

  主进程 就是electron容器,这样理解就行了,不用深研究,涉及一个对象ipcmain

 要操作本机文件就得使用node.js,要使用node.js的一些方法就必须得在mian.js创建窗口时开启node API。在webpreferences里面

function createWindow() {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 480,
    height: 800,
    webPreferences: {
      contextIsolation: false,//是否开启隔离上下文
      nodeIntegration: true,//渲染进程使用node api
      webSecurity: false,
      preload: path.join(__dirname, "preload.js"),
    },
  });

  // and load the index.html of the app.
  // win.loadFile("index.html");
  mainWindow.loadURL(
    isDev
      ? "http://localhost:5173"
      : `file://${path.join(__dirname, "../dist/index.html")}`
  );
  // Open the DevTools.
  if (isDev) {
    mainWindow.webContents.openDevTools();
  }
}

webpreferences里面的这四项都加上,由于nodeIntegration这项非常关键,preload这个选项就是配置preload.js文件在哪个地方,我的项目 preload.js文件和main.js在同级目录,所以截图当中写法就行。其中path是引入的node.js的path变量 

const path = require("path") 

2.主进程通信相关
在electron文件夹中的main.js最上方从electron引入ipcMain主进程对象 

const { app, BrowserWindow, ipcMain } = require("electron");

 在electron文件下mian.js文件末尾添加监听代码,注意是独立的方法块,不包含在任何代码块中, 是一个单独的javascript代码。

ipcMain.handle("getDirectoryContent", (event, directoryPath) => {
  return new Promise((resolve, reject) => {
    fs.readdir(directoryPath, (err, filesList) => {
      if (err) {
        reject(err);
        return;
      }
      resolve(filesList);
    });
  });
});

 再添加一个读取文件的方法electron文件夹中的main.js当中引入node.js的fs类。

const fs = require("fs");

2.1 preLoad.js代码 

window.ipcRenderer = require('electron').ipcRenderer;

只写这一行就够了,目前就是从electron里面得到ipcRender对象,这样这个对象已要赋值给html window对象的一个自定义属性_ipcRenderer了,如果不赋值给window,使用import引入的话,会找不到这个方法。

 2.2 在Vue页面进行使用
 

<script>
import { ref } from "vue";
export default {
  setup() {
    const items = ref([]);
    const fs = require("fs");
    let directoryPath = "/Users/xxx/Desktop/xxxx"; // 替换为实际的目录路径
    async function getCatalogue(path) {
      try {
        const filesList = await window.ipcRenderer.invoke(
          "getDirectoryContent",
          path
        );
        //此时filesList即是访问指定路径的文件夹数据
        //利用循环使用fs的statSync方法 区分访问的文件夹目录里面 那些是文件夹和文件
        const itemsList = await Promise.all(
          filesList.map(async (file) => {
            const filePath = `${path}/${file}`;
            const state = fs.statSync(filePath);
            return {
              name: file,
              isDirectory: state.isFile(),
            };
          })
        );
        items.value = itemsList;
      } catch (err) {
        console.error(err);
      }
      return Promise.resolve();
    }
    getCatalogue(directoryPath);
    return {
      items,
    };
  },
};
</script>

这样 就完成了访问本机电脑文件的流程。如果要操作文件夹,同样是使用node里面的API进行操作

;