• Vite ❤ Electron——基于Vite搭建Electron+Vue3的开发环境【一】


    背景

    目前社区两大Vue+Electron的脚手架:electron-vuevue-cli-plugin-electron-builder

    都有这样那样的问题,且都还不支持Vue3,然而Vue3已是大势所趋,

    Vite势必也将成为官方Vue脚手架,

    下图是尤雨溪在开发好Vite之后与webpack之父的对话

     所以开发一个Vite+Vue3+Electron的脚手架的需求日趋强烈

    我前段时间做了一个,

    但是发现了一些与Vite有关的问题,

    比如:Vite会把开发环境的process对象吃掉的问题

    这对于web项目来说问题不大,但对于我们的Electron项目来说,就影响很大了

    今天我就把这个思路和实现方式的关键代码发出来供大家参考,

    同时也希望Vue社区的贡献者们,能注意到这个问题

    (给Vue官方的各个项目提issue真的是太难了,Electron官方项目在这方面就做的很好,很open、很包容)

    环境

    先用Vite创建一个Vue3的工程,这就是你的实际项目工程

    接着安装几个Electron相关的依赖,最终我的工程下的依赖情况如下:

        "@vue/compiler-sfc": "^3.0.0",
        "vite": "^1.0.0-rc.9",
        "vue": "^3.0.2",
        "vue-router": "^4.0.0-rc.1",
        "electron": "^11.0.2",
        "electron-builder": "^22.9.1",
        "electron-updater": "^4.3.5",
        "postcss-scss": "^3.0.2",
     
        "sass": "^1.27.0",

    注意:这些依赖全部安装在devDependencies下

    各个库的版本发文时应该是最新的了,不过如果有更新的版本,你完全可以用,没影响。

    工程的目录结构大概是如下这样:

    [yourProject]

      node_modules  依赖包

      public  vite创建的目录,为vue服务的,实际没多大用

      release  打包后编译输出的目录,该目录的根目录下存放打包后的安装包

        bundled  该目录存放vue打包后的文件(html js css img等)

        win-unpacked  该目录存放编译后生成的可执行文件及相关的dll,不包含安装包

      resource  资源目录

        unrelease  该目录存放编译期需要的资源

        release   该目录存放编译后需要随安装包分发给客户的资源

      script     此目录存放各种脚本,比如编译脚本,启动脚本,签名脚本等

      src  源码目录

        render  渲染进程源码目录

        main  主进程源码目录

        common  两个进程都会用到的共用源码目录

      package.json  项目配置文件

      index.html  vue3的入口页面

      .gitignore

    接着在package.json中,增加两个命令:

      "scripts": {
        "start": "node ./script/dev.js",
        "release": "node ./script/release.js"
      },

    同时在script目录下创建相应的文件,接着我们就开始撰写者两个文件的代码了

    调试脚本

    通过Vite启动Web项目

    调试脚本首先要做的工作就是启动Vue项目

    让它跑在http://localhost下,这样我们修改渲染进程的代码时,

    会通过Vite的热更新机制实时反馈到界面上

    Vite除了提供cli的指令启动项目外,也提供了API,我这里就是直接调它的API来启动项目的

    关键代码如下:

    let vite = require("vite")
      createServer () {
        return new Promise((resolve, reject) => {
          let options = {
            root:process.cwd(),
            enableEsbuild: true
         };
          this.server = vite.createServer(options);
          this.server.on("error", (e) => this.serverOnErr(e));
          this.server.on("data", (e) => console.log(e.toString()));
          this.server.listen(this.serverPort, () => {
            console.log(`http://localhost:${this.serverPort}`);
            resolve();
          });
        });
      }, 

    其中this.serverPort是绑定在当前对象上的一个变量,意义是指定vite项目启动时使用的端口号

    启动成功后http server对象绑定到当前对象的server变量上

    如果启动过程中报错,则很有可能是端口占用,将执行如下逻辑:

      serverOnErr (err) {
        if (err.code === "EADDRINUSE") {
          console.log(
            `Port ${this.viteServerPort} is in use, trying another one...`
          );
          setTimeout(() => {
            this.server.close();
            this.serverPort += 1;
            this.server.listen(this.viteServerPort);
          }, 100);
        } else {
          console.error(chalk.red(`[vite] server error:`));
          console.error(err);
        }
      },

    这段逻辑就是递增端口号,再次尝试启动http server

    设置环境变量

    往往每个开发人员的环境变量都是不一样的

    有的开发人员需要连开发服务器A,有的开发人员需要连开发服务器B

    而且开发环境的环境变量、测试环境、生产环境的环境变量也不一样

    所以我把环境变量设置到几个单独的文件中

    方便区分不同的环境,也方便gitignore,避免不同开发人员的环境变量互相冲突

    开发环境的环境变量保存在src/script/dev.env.js中

    let env = require("./dev.env.js")

    生产环境的环境变量则为release.env.js

    这个文件的代码非常简单,如下:

    module.exports = {
      APP_VERSION: require("../package.json").version,
      ENV_NOW: "dev",
      PROTOBUF_SERVER: "******.com",
      SENTRY_SERVICE: "https://******.com/34",
      ELECTRON_DISABLE_SECURITY_WARNINGS: true
    }

    需要注意的是:ELECTRON_DISABLE_SECURITY_WARNINGS,

    这个环境变量是为了屏蔽Electron开发者调试工具那一大堆警告的

    (你如果开发过Electron应用,你应该知道我说的是什么)

     APP_VERSION是从项目的package.json中取的版本号,

    你当然可以不设置这个环境变量,通过Electron的API获取版本号

    app.getVersion() //主进程可用

    但通过ElectronAPI获取到的版本号,在开发环境下,是Electron.exe的版本号,不是你的项目的版本号

    打包编译后,这个问题是不存在的。

    ENV_NOW是当前的环境,开发环境下它的值为dev,打包编译后的生产环境它的值应为product,

    因为现在我们是讲如何构建开发环境,引用的是dev.env.js,

    等下一篇文章讲如何构建编译环境时,引用的就是release.env.js了,

    编译主进程代码

    Vite之所以快,有一个很重要的原因是它使用了esbuild模块来编译代码

    这里我们也使用esbuild来编译我们的主进程的代码

    前面说了主进程是放在src/main/目录下的

    这里我使用的是TypeScript开发,入口程序是app.ts,你完全可以使用Js开发,文件名也随你自定义

      buildMain () {
        let outfile = path.join(this.bundledDir, "entry.js");
        let entryFilePath = path.join(process.cwd(), "src/main/app.ts");
        //这个方法得到的结果:{outputFiles: [ { contents: [Uint8Array], path: '<stdout>' } ]}
        esbuild.buildSync({
          entryPoints: [entryFilePath],
          outfile,
          minify: false,
          bundle: true,
          platform: "node",
          sourcemap: false,
          external: ["electron"],
        });
        env.WEB_PORT = this.serverPort;
        let envScript = `process.env={...process.env,...${JSON.stringify(env)}};`
        let js = `${envScript}${os.EOL}${fs.readFileSync(outfile)}`;
        fs.writeFileSync(outfile, js)
      },

    esbuild会自动查找app.ts引用的其他代码,

    还有treeshaking机制保证你不会把无用的代码打包到输出目录

    我把sourcemap关掉了,因为调试主进程很困难,

    基本都是手动console.log信息调试的,朋友们有好的建议请赐教一下

    platform要指定成node,要不然esbuild会尝试帮你去找node.js内置的包,肯定找不到,就报错了

    同理,还要把electron设置成external

    在上一节设置的环境变量的基础上

    我们又增加了一个WEB_PORT的环境变量,

    Electron启动后,要根据这个变量去加载localhost的页面,

    这个变量是应用启动时确定的,是动态的,所以没办法设置到dev.env.js中

    输出代码前,我们把环境变量的值也附加在输出代码中了

    这样Electron进程启动时,会先设置好环境变量,再执行具体的业务代码

    (我们当然也可以通过其他方式设置环境变量,但这样做主要是为了和生产环境保持一致,看到下一篇文章你就会知道了)

    最终生成的代码会被输出到这个目录下面:

    bundledDir: path.join(process.cwd(), "release/bundled")

    稍后我们启动Electron时,也会让Electron加载这个目录下的入口程序。

    启动Electron

    Electron的node module并没有提供API给开发者调用以启动进程

    所以我们只能通过node的child_process模块来启动Electron的进程

    代码如下:

      createElectronProcess () {
        this.electronProcess = spawn(
          require("electron").toString(),
          [path.join(this.bundledDir, "entry.js")],
          {
            cwd: process.cwd(),
            env,
          }
        );
        this.electronProcess.on("close", () => {
          this.server.close();
          process.exit();
        });
        this.electronProcess.stdout.on("data", (data) => {
          data = data.toString();
          console.log(data);
        });
      },

    require("electron").toString()得到的是Electron的可执行文件的路径

    Windows环境下为:node_moduleselectrondistelectron.exe

    Mac环境下为:node_modules/electron/dist/Electron.app/Contents/MacOS/Electron

    path.join(this.bundledDir, "entry.js")为Electron进程指定了入口程序文件的地址

    cwd: process.cwd()是为Electron指定当前工作目录(此处又为Electron指定了一次环境变量,其实不指定也没关系)

    当Electron进程退出时,我们也关闭了Vite创建的http server

    主进程加载渲染进程页面

    此处最关键的逻辑就是这一句

        if (process.env.ENV_NOW === "dev") {
          await win.loadURL(`http://localhost:${process.env.WEB_PORT}/`);
        }

    process.env.WEB_PORT就是我们上文中设置的WEB_PORT变量

    这个逻辑当然还有else分支,那是下一篇博文的内容了

    敬请期待!

     

  • 相关阅读:
    js 前端开发 编程 常见知识点笔记
    重置 PowerShell 和 cmd 设置 样式 为系统默认值 powershell windows10
    useMemo和useCallback的区别 及使用场景
    数组去重,利用 ES6 的 reduce() 方法 和 include 判断 实现
    Java 中 Lombok 的使用,提高开发速度必备
    记录 windows 系统常用的 CMD 命令
    React Native 的 FlatList 组件 实现每次滑动一整项(item)
    Spring------mysql读写分离
    Webservice与CXF框架快速入门
    quartz
  • 原文地址:https://www.cnblogs.com/liulun/p/14072303.html
Copyright © 2020-2023  润新知