是否可以在 Dart 中懒惰地使用 JS 库?

Posted

技术标签:

【中文标题】是否可以在 Dart 中懒惰地使用 JS 库?【英文标题】:is it possible to lazily use JS libs with Dart? 【发布时间】:2019-08-02 10:20:59 【问题描述】:

我正在使用 chartjs(带有 dart 接口 https://pub.dartlang.org/packages/chartjs)并试图通过将 <script src="chartjs.js"></script> 注入头部并等待它的加载事件然后使用 lib 来推迟它。 我收到此异常:无法读取未定义的属性“图表”。

当脚本在 dart 之前的 html 头部内时,不会发生这种情况。

那么,是否可以在 Dart 加载后加载 JS 库?

【问题讨论】:

我有在初始化角度组件期间加载 js-library 的经验。 github.com/alexd1971/angular_grecaptcha/blob/master/lib/src/… 在 ngOnInit 脚本标签中创建并添加到 DOM。在 ngAfterViewInit 中等待脚本被加载并初始化组件。但不幸的是,这并不总是有效。例如这里:github.com/alexd1971/angular_flatpickr。我还没找到原因。 听起来像是等待加载完成的问题。我不久前使用过这种方法,它对我有用(虽然不是 Chartjs) 我尝试在 main.dart 上加载脚本以确保它已加载,但仍然没有工作 问题是dart加载后某些JS文件无法添加到dom中。刚刚通过在 main.dart.js 脚本后简单添加 <script src=chartjs></script> 对其进行了测试,它抛出了这个错误:Error: Mismatched anonymous define() module: function()var define,module,exports; 【参考方案1】:

这是 DDC 中的一个问题。 它将 require.js 添加到 HTML 并与其他库发生冲突。https://github.com/dart-lang/sdk/issues/33979

我找到的解决方案是从您要使用的第三方库中手动删除使用 requirejs 的标题部分。

以chartjs为例:https://cdn.jsdelivr.net/npm/chart.js@2.8.0/dist/Chart.js

你删除这两行:

typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(function()  try  return require('moment');  catch(e)   ()) :
typeof define === 'function' && define.amd ? define(['require'], function(require)  return factory(function()  try  return require('moment');  catch(e)   ()); ) :

然后文件可以懒惰地添加到DOM中而不会发生冲突。

这是我懒惰地获取脚本的代码:

class ClientUtils 
    static final _scriptFetched = <String, Future<bool>>;

    static ScriptElement _scr(String url) => new ScriptElement()
      ..async = true
      ..type = 'text/javascript'
      ..src = url;

    static Future<bool> fetchScript(String url,
            String contextCheck) async 
        bool shouldCheck = contextCheck?.isNotEmpty == true;

        hasContext() => js.context.hasProperty(contextCheck) &&
                    js.context[contextCheck] != null;

        if (shouldCheck && hasContext())
            return true;

        if (!_scriptFetched.containsKey(url)) 
            Completer<bool> c = new Completer<bool>();

            if (!shouldCheck) 
                ScriptElement s = _scr(url)
                    ..onLoad.forEach((Event e) 
                        c.complete(true);
                    );

                document.body.children.add(s);
             else 
                Timer.periodic(Duration(milliseconds: 300), (t) 
                    if (hasContext()) 
                        t.cancel();
                    
                    c.complete(true);
                );

                document.body.children.add(_scr(url));
            

            _scriptFetched[url] = c.future;
        
        return _scriptFetched[url];
    


【讨论】:

【参考方案2】:

找到了更好的方法!

让我们在 dart 加载后删除 define 变量,然后任何第三方库在添加异步时都可以工作:D

将此添加到您的 main():

import 'dart:js';

void main() 
  context.callMethod('fixRequireJs');

在你的 index.html 中:

    <script type="text/javascript">
      window.fixRequireJs = function()
      
        console.log('define is ', typeof define);
        if (typeof define == 'function') 
          console.log('removing define...');
          delete define;
          window.define = null;
        
      
    </script>

【讨论】:

【参考方案3】:

你可以试试deferred as语法:

import 'package:chartjs/chartjs.dart' deferred as chartjs;

void main() 
    chartjs.loadLibrary().then(()  ... );

【讨论】:

但这会使界面延迟,而不是实际的 js 脚本,对吧?

以上是关于是否可以在 Dart 中懒惰地使用 JS 库?的主要内容,如果未能解决你的问题,请参考以下文章

我如何懒惰地从Rust中的文件/流中读取多个JSON值?

在 Java 8 中懒惰地返回第一个非空列表

itertools.product 是不是懒惰地评估它的论点?

RxSwift:如何使用 shareReplay 懒惰地获取订阅

懒惰地导入 UI 模块/组件的最佳方法是啥

如何在 Kotlin 中无限懒惰地循环列表?