Electron 和 Angular 项目升级

Electron 和 Angular 项目升级: Angular4+Electron1.7.8 升级到 Angular13+Electron2

原项目 Angular 和 Electron 版本:

  • @angular/cli: 1.4.9
  • @angular/core: 4.4.6
  • Electron: 1.7.8

升级后 Angular 和 Electron 版本:

  • Angular: 13.3.1
  • Electron: 21.2.1

流程:

angular-electron 这是一个结合 Angular 和 Electron 的项目。以此为基准环境,不需要从头构建。这个项目的 Angular13 对应的 electron 为 v18,再手动到升级 electron21.2.1,不升级也没有问题。将之前项目的核心代码复制过来,查看每一个文件,找出所有的问题。原项目文件结构如图:

升级前项目目录结构

将 src 复制到对应目录下。主进程文件 main.ts 放置再 app 文件夹中。
原先主进程文件 main.ts 在项目根目录下,主进程 main.ts 中使用的静态资源在 src/assets 中。现在主进程 main.ts 更换位置,所以主进程中使用的静态资源需要放置在 app 中,并且打包编译后需要用到。
现目录文件结构如图:

升级后项目目录结构

然后开始检查代码,首先发现的是一些语法问题,这些因语法检查而发生错误的代码,并不代码写得不对。比如变量命令需要遵循驼峰规则,字符串需要单引号,语句结束需要分号等待。这样错误应该暂时忽略。而报错的原因是,新项目的 package.json 会安装一些 eslint 相关的插件解析语法问题,建议删除这些插件或者不安装这些插件。如果保留这些插件,则代码需要符合 TypeScript 语法规则。根据 Angular 更新指南Electron 重大更改,以及 Angular 中文官网Electron 官网 检查所有文件后,总结问题如下:
Angular 问题:

Q1: import { Http } from '@angular/http'; 报错找不到模块“@angular/http”或其相应的类型声明。ts(2307)
A1: 如果使用了旧版的 HttpModule 和 Http 服务,请切换到 HttpClientModule 和 HttpClient 服务。HttpClient 简化了默认的 ergonomics(不再需要映射(map)到JSON),并且现在支持类型化的返回值和拦截器。修改为 import { HttpClient } from '@angular/common/http';

Q2: import { Renderer } from "@angular/core"; 报错 ““@angular/core”” 没有导出的成员“Renderer”。 你是否指的是“Renderer2”?ts(2724)。
A2: 无论在哪里使用了 Renderer,现在都使用 Renderer2。

Q3: import { Observable } from 'rxjs/Rx'; 找不到模块“rxjs/Rx”或其相应的类型声明。ts(2307)。
A3: 修改为 import { Observable } from 'rxjs'; Subscription、Subject、Observable 类似。

Q4: import { RequestOptions, Headers } from '@angular/http'; 找不到模块“@angular/http”或其相应的类型声明。ts(2307)。
A4: 已被 HttpRequest, HttpHeaders 代替。 import { HttpHeaders } from '@angular/common/http';

Q5: this.http.post().map((res:any)=>res.json()).catch((error: any) => Observable.throw())
A5: this.http.post().pipe(catchError((error: HttpErrorResponse) => { return throwError(error || 'Server error'); })); 不再需要映射(map)到JSON,请看这里 making-a-jsonp-request

Q6: renderer.listenGlobal() Renderer2 没有 listenGlobal 方法。
A6: 使用 renderer.listen() 函数来实现全局事件。

Q7: this.renderer.invokeElementMethod(this.el.nativeElement, "focus"); Renderer2 没有 invokeElementMethod 方法。
A7: does not use invokeElementMethod,以下是链接中代码。

invokeElementMethod(eleRef: ElementRef, method: string) {
    if (isPlatformBrowser(this.platformId)) {
        eleRef.nativeElement[method]();
    }
}
this.invokeElementMethod(this.el.nativeElement, "focus");

Q8: import { ComponentFactoryResolver } from "@angular/core"; “ComponentFactoryResolver”已弃用。ts(6385) let subFactory = this._cfr.resolveComponentFactory(data.component);let componentRef: any = this.detailHost.viewContainerRef.createComponent(subFactory);
A8: 一个简单的注册表,它将 Components 映射到生成的 ComponentFactory 类,该类可用于创建组件的实例。用于获取给定组件类型的工厂,然后使用工厂的 create() 方法创建该类型的组件。Angular 不再需要组件工厂。请使用可以直接使用 Component 类的其他 API。弃用可以不改或者修改为: let componentRef: any = this.detailHost.viewContainerRef.createComponent(data.component);

Q9: @ViewChild(DetailContentDirective) detailHost: DetailContentDirective; createView() 输出 this.detailHost 为 undefined, 应该为 {viewContainerRef: ViewContainerRef_},低版本 angular 中可能会在 ngAfterViewInit() 之前完成。
A9: 官方文档中指出,视图查询是在 ngAfterViewInit() 回调函数被调用之前设置的。因此,不应该依赖在 ngAfterViewInit() 之前访问视图查询。

Q10: import 'zone.js/dist/zone-mix' 报错 Module not found: Error: Can’t resolve ‘fs’。
A10: 修改为 import 'zone.js'

Q11: import 'rxjs/add/operator/map'; 报错 Module not found: Error: Package path ./add/operator/map is not exported from package。
A11: map catch 与 http 返回值有关,将 http 的 response 的数据通过 map 映射为 json,选择不通过 map 映射。

Q12: this.router.events.filter 类型“Observable<Event_2>”上不存在属性“filter”。ts(2339)。
A12: 修改后:

import { filter } from 'rxjs/operators';
this.router.events.pipe(
    filter(event => event instanceof NavigationEnd)
).subscribe(() => {});

Q13: Observable.fromEvent().subscribe((event) => {}); 类型“typeof Observable”上不存在属性“fromEvent”。ts(2339)。
A13: import { fromEvent } from 'rxjs'; 直接导入 fromEvent,不需要通过 Observable。

Electron 问题:

Q1: menu.popup(win) 类型“BrowserWindow”与类型“PopupOptions”不具有相同的属性。ts(2559)。
A1: menu.popup(browserWindow?: BrowserWindow, options?: PopupOptions); => popup(options?: PopupOptions)。

Q2: dialog.showOpenDialog({ title: "选择目录", properties: ['openDirectory'] }, (files) => {}) 类型“{ title: string; properties: string[]; }”的参数不能赋给类型“BrowserWindow”的参数。对象文字可以只指定已知属性,并且“properties”不在类型“BrowserWindow”中。ts(2345)。
A2: 结构修改后代码:

dialog.showOpenDialog(mainWindow, {
  properties: ['openFile', 'openDirectory']
}).then(result => {
  console.log(result.canceled)
  console.log(result.filePaths)
}).catch(err => {
  console.log(err)
})

Q3: require('electron-reload')(__dirname, {});
A3: 修改代码后重新加载程序: require('electron-reloader')(module);

Q4: 渲染进程中 import { remote } from 'electron'; 模块““electron””没有导出的成员“remote”。ts(2305) this.remote = window.require('electron').remote; 类型“typeof CrossProcessExports”上不存在属性“remote”。ts(2339) remote.shell(),remote.app(),remote.dialog()。包括 import { app } from 'electron';import { shell } from 'electron';
A4: remote 模块在 Electron 12 废弃,并将在 Electron 14 被移除 由@electronic/remote 模块替代。在主进程中执行 import { app, shell, dialog } from 'electron';,相关用法 1.remote.app.getPath() 改为 app.getPath()。2.shell.openItem() 改为 shell.openPath()。3.remote.dialog; dialog.showOpenDialog() 改为 dialog.showOpenDialogSync(),并且需要在主进程执行。

Q5: 所有渲染进程(angular)中使用 import * as fs from 'fs';let fs = require('fs');import * as fs from 'fs-extra';,fs.readFileSync 和 fs.writeFile 方法报错。
A5: 所有 fs.readFileSync 和 fs.writeFile 操作需要在主进程执行。

Q6: 当引入 jquery 与 webuploader 时,<script src="./assets/js/jquery-3.3.1.min.js"></script><script src="./assets/js/webuploader/webuploader.js"></script>,报错 webuploader 找不到 $。
A6: 由于 Electron 主进程修改了 module 对象,引入 jquery 失败,在引入jquery 之前 <script>delete window.module;</script>

Q7: <div><button>Action 1</button> <button>Action 2</button></div> 页面上两个button 之间的空格消失。
A7: <div ngPreserveWhitespaces><button>Action 1</button> <button>Action 2</button></div>。https://angular.cn/api/core/Component#preserving-whitespace

以上是一些主要问题,或许还有一些不那么重要的问题,但是可能也会引起程序错误。
问题分为 Angular 升级带来的问题,Electron 升级带来的问题,包括 Angular 升级引起第三方模块不兼容的问题。还有一些 css 样式可能会变化,以及一些 TypeScript 语法问题。

框架升级是一件非常麻烦的事,特别是跨许多大版本升级,需要注意的事项很多,如果项目很大,升级后需要完整测试。

要将 Angular 项目集成到 Electron 环境中,你可以按照以下步骤进行操作: 1. 创建一个新的 Electron 项目: - 在项目根目录下使用命令行工具运行 `npm init`,按照提示创建一个新的项目。 - 安装 Electron 依赖:`npm install electron --save-dev` 2. 创建 Electron 的主进程文件: - 在项目根目录下创建一个新的文件,命名为 `main.js`(或其他你喜欢的名称)。 - 在 `main.js` 中编写 Electron 主进程的代码,例如: ```javascript const { app, BrowserWindow } = require('electron'); function createWindow() { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true } }); win.loadURL(`file://${__dirname}/dist/index.html`); } app.on('ready', createWindow); ``` 在上面的示例中,我们创建了一个 Electron 窗口,并加载了 Angular 项目的 `index.html` 文件。 3. 修改 Angular 项目的配置文件: - 打开 Angular 项目的 `angular.json` 配置文件。 - 找到 `"architect"` -> `"build"` -> `"options"` -> `"outputPath"` 字段,将其修改为 `"dist"`,以确保 Angular 项目输出到 `dist` 目录下。 4. 构建 Angular 项目: - 在命令行中运行 `ng build` 命令,将 Angular 项目构建到 `dist` 目录下。 5. 启动 Electron 应用: - 在命令行中运行 `electron .` 或 `electron main.js`,启动 Electron 应用。 这样,你的 Angular 项目就会在 Electron 环境中运行起来了。当然,你还可以根据需要在 Electron 中添加其他的功能和特性,例如菜单、对话框、文件系统访问等。请参考 Electron 的官方文档和示例代码,以深入了解如何利用 Electron 强大的功能扩展你的应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值