当前位置:   article > 正文

angular js 使用pdf.js_angular库 dynamic import玩法。让你成为module的主宰

angular js 使用pdf.js_angular库 dynamic import玩法。让你成为module的主宰

angular除了支持路由懒加载之外,在angular 8版本中支持es标准的动态import,可更自由的根据需求进行懒加载。

  1. import('/modules/my-module.js')
  2. .then((module) => {
  3. // Do something with the module.
  4. });

正好在搬一个复杂度比较高项目的砖时,由于砖山砖海(第三方依赖以及生产的代码量非常大,minify之后还有5m,以下体积假如没有特殊说明,都是minify之后的结果)。这里称这个项目为X项目X项目是语言编辑器项目,支持语句的高亮,智能自动补齐等。第三方依赖为monaco-editor, antlr4ts,前者本身的大小在2m左右,可以根据需要的features尽可能减小bundle体积到1.5m左右。antlr4ts提供语法语义支持,生成的parser文件非常庞大,在8w行,2m左右,vscode读取也会卡。

该项目使用ng-packagr进行打包,供下游angular项目使用。打包由于用了dynamic import,遇到了问题,后续篇章会详细介绍。首先介绍一下怎么在angular项目中使用dynamic import

angular 项目使用dynamic import

假如是新项目,在ng new生成的模板,可以直接通过如下步骤实现,不然请check tsconfig 的module值为esnext

  1. 定义你的dynamic lib,比如
  1. export class DynamicLib {
  2. cheers() {
  3. console.log('cheers');
  4. const date = (new Date()).toString();
  5. return `cheers at ${date}`;
  6. }
  7. }

2. 在需要动态载入的地方使用

  1. @Component({
  2. selector: 'app-root',
  3. template: `
  4. <p>
  5. <button (click)="loadLib()">dynamic load lib</button>
  6. {{libMsg}}
  7. </p>
  8. `,
  9. styleUrls: ['./app.component.sass']
  10. })
  11. export class AppComponent {
  12. name = 'Angular';
  13. libMsg = '';
  14. async loadLib() {
  15. const {DynamicLib} = await import('./dynamic-lib');
  16. const lib = new DynamicLib();
  17. this.libMsg = lib.cheers();
  18. }
  19. }

观察如下动图,在鼠标点击了按钮之后,才回去加载需要的库。

06e4bff52e4701957ceff64f5afb3515.gif

整个使用是非常简单的,import的结果是个module的promise,用vscode也可以提供舒适的module补齐,需要注意的是,你需要确保在应用的其他地方没有如import {DynamicLib} from './dynamic-lib';之类的抵消dynamic import作用的import语句,和路由懒加载不能将懒加载模快import进app.module是一个道理。

项目地址:

angular-dynamic-import​github.com

library应用

上节的demo看上去非常简单吧,要感谢angular-cli团队,把所有构建bundle的工作都做了。如果在angular-cli通过ng generate library my-lib生成的库项目中,使用动态import则不能成功,也就是X项目遇到的情况。参考该ng-packagr issue, 大体原因是

  1. ng-packagr 使用rollup来构建ts源文件,rollup当检测到代码中有dynamic import时,会自动生成multiple chunk,读取output.dir而不是ng-packagr定义的output.name导致rollup报错
  2. 即使ng-packagr修改rollup配置,rollup也会继续报错Error: UMD and IIFE output formats are not supported for code-splitting builds.,这是rollup本身不支持dynamic import构建umd格式代码,参考rollup维护者的评论:umd格式不支持dynamic import,可以观察如下umd dist的代码,就知道为什么umd支持动态载入会很困难。而ng-packagr遵从Angular Package Format格式,会构建umd格式。
  1. // lib.umd.js. umd格式并不是模块化的js系统,他像是IIFE,稳定并且自包含,可直接在浏览器中执行。
  2. //dynamic import天然得构建生成multiple chunk,需要模块支持。观察之前的动图,js是分块按需加载的
  3. (function (global, factory) {
  4. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('rxjs'), require('rxjs/operators')) :
  5. typeof define === 'function' && define.amd ? define('lib', ['exports', '@angular/core', 'rxjs', 'rxjs/operators'], factory) :
  6. (global = global || self, factory(global.lib = {}, global.ng.core, global.rxjs, global.rxjs.operators));
  7. }(this, function (exports, core, rxjs, operators) { 'use strict';
  8. ...
  9. ...
  10. }));

其实假如你的动态载入代码块体积不大,并不建议使用dynamic import,多个js文件在页面初次加载时耗时会比单个js文件更慢。分割模块本身需要根据具体情况出发,在我遇到的情况,的确需要优化实现dynamic import。

解决方案

在目前情况下,因为ng-packagr不支持dynamic import,可以选择了其他方案绕过ng-packagr直接构建带有dynamic import的代码。当然你也可以直接不用ng-packagr,使用诸如angular-library-seed等暴露构建配置的library源码进行开发,更改umd dist输出为es模块输出。当然,作为非常厌烦配webpack的我来说,还是有其他的方案:

1. 假如你动态载入的逻辑是纯粹typescript写的,可以从angular中分离出来,可以使用typescript-library-starter在projects目录下平行的再开发一个项目,并修改rollup.config.ts,删掉umd format的output,并且指定dist dir。然后你angular的lib可以peer dependency依赖这个库。虽然也需要配一点构建,但工作量非常小,后续维护成本也很小。angular相关的构建工作还是放在ng-packagr项目里。

X项目就是这么解决的,本身这项目还有跨前端技术方案的要求,同时支持angular和react,所以核心代码直接用typescript和原生dom操作来写(并不多),对你没有看错,其实很多项目也是用原生dom操作的,比如monaco-editorprojects目录下依次是

  • core
  • angular-editor
  • react-editor

后两者作为core逻辑的组件封装存在,没有dynamic import的代码。

2. 动态载入的库可以作为ng-packagr的submodule,在具体angular应用中载入,通过共享服务进行通知,比如服务定义一个Subject<DynamicLibrary>在angular应用中import之后调用Subject.next,然后在lib中得到。曲线救国,代码比较冗余。具体的实现参考

feat: dynamic import with ng-packagr project · kingfolk/angular-dynamic-import@9deb152​github.com
8281a1b779db69fb4316bc67bc781538.png

LibService作为中间通信的服务,来通知lib组件动态库加载完毕

59cd09425ff52d5c139ad93a2a763201.gif

总结,根据具体需要对应用进行切割。

  • 假如库是组件库,可以定义NgModule进行按需加载;
  • 假如是typescript库,可以使用typescript-library-starter进行开发(方法1)。
  • 假如真的执意需要将动态载入代码放在ng-packagr项目里,可参看方法2,略麻烦。

对,假如你看到这里我分享的一点浅见,抱歉题目有点忽悠大家进来看,但将我对import()的应用和限制分享完毕,希望对各位搬砖的有所用处。假如有更好的方法或者错误,可以留言,欢迎交流,扔鸡蛋。

其他引用补充材料

本文着重介绍了angular之于dynamic在library里的应用,其他关于dynamic import的具体背景等,可参看:

https://medium.com/lacolaco-blog/angular-dynamic-importing-large-libraries-8ec079603d0

为了解决温饱问题

目前本博主是自由职业,假如有有挑战的项目,欢迎私信,不限技术框架,不限前后端。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/324765
推荐阅读
相关标签
  

闽ICP备14008679号