打破CocosCreator3d不能使用npm包的魔咒!!!

背景

  • 我在开发EasyGameFramework这个框架的时候,目标之一是构建出来的模块包可以在CocosCreator3D上使用,使用体验和正常使用具有类型声明的Npm包一致。实在不行再用发布源码的方式。

  • 在js生态中npm有很多很强大很有用的模块包

  • 论坛上也有小伙伴对在CocosCreator3d上使用npm包有需求。

  • 但CocosCreator3D的模块机制和CocosCreator不同,所以暂不支持直接使用npm包。

    那怎么办呢?首先了解一下CocosCreator3d模块机制

CocosCreator3d模块机制

这个时候就得祭出调试的杀手级工具:Chrome Devtools

调试运行CocosCreator3d空项目

查看index.html

CocosCreator3d中使用了system.js,并且在index.html注册了一个叫 code-quality:cr的模块

而且加载了一个.import-map.json的东西

搜索.import-map.json看看是什么

通过以上的调试我们大概之道,CocosCreator3d使用了一个叫systemjs的库,然后呢,用来注册一些模块。这个systemjs可能是关键的东西,得了解一下。

SystemJS的官方github:https://github.com/systemjs/systemjs

别人的使用:SystemJS使用记录

了解过后,我们能知道它就是一个模块加载器,以及它的注册方式

  1. 通过一个map注册一堆模块

  2. 通过register接口注册一个模块

但是怎么处理引用的呢?

断点调试systemjs

找到systemjs,搜索register

搜索importMap之类的关键词

通过以上调试验证了我们的猜想:SystemJs,用来注册和加载模块的,是一种模块规范,需要做特殊处理才能注册进去和使用。但我们需要来写一个简单的systemjs规范的模块来验证一下。

手写一个简单的SystemJs的模块

  • simple-sysjs.js

    
    System.register('simple-sysjs', [], function (exports) {
    
    'use strict';
    
        return {
    
        execute: function () {
    
            var SimpleSysJs = exports('SimpleSysJs', /** @class */ (function () {
    
                function SimpleSysJs() {
    
                    console.log("我是简单的systemjs模块")
    
                }
    
                return SimpleSysJs;
    
                    }()));
    
                }
    
        };
    
    });
    
    
    
    
  • 声明文件:simple-sysjs.d.ts

    
    declare module 'simple-sysjs' {
    
    export class SimpleSysJs {
    
        }
    
    }
    
    
  • 为了保证这个脚本优先于其他脚本加载,将simple-sysjs.js设置为插件

  • 然后在编辑器新建脚本,写下

    
    import { SimpleSysJs } from "simple-sysjs"
    
    new SimpleSysJs()
    
    
  • 运行后可以看到输出:我是简单的systemjs模块

这个很简单的,大家都可以新建一个空C3d项目尝试一下。

经过验证尝试,我们知道,只要将npm包转成systemjs规范的文件就可以被我们的代码引用,以及正常使用了。但手写太麻烦了,有什么简单的办法呢?

如何快速地转换npm包为systemjs规范库

使用egf-cli

这个工具是我为EasyGameFramework打造的,目的是可以发布框架库为任意规范模块,给不同的引擎和项目使用。

有兴趣可以看看我之前写的文章

以及框架仓库:https://github.com/AILHC/EasyGameFrameworkOpen

使用这个工具对npm包有一定的要求

  1. 符合commonjs规范

  2. 最好有类型声明文件

  3. 最好能是typescript开发的

我以xstate(一个高star的状态机库)为例子

  • c3d项目下安装 xstate ,npm i xstate

  • 然后复制 EasyGameFrameOpen的package-template项目

  • 修改包名为

    
    "name": "@ailhc/xstate2c3d",
    
    
  • 修改构建命令为

    
    "build:system": "egf build -f system:@ailhc/xstate2c3d",
    
    
  • 删除其他ts文件,留下index.ts

  • 然后写一句:

    
    export * from "xstate"
    
    
  • 最后执行 yarn run build:system

  • 最后的最后,因为xstate里引用了node的变量,所以在构建出来的index.js要加一句

    
    sendParent: sendParent,
    
    sendUpdate: sendUpdate,
    
    spawn: spawn
    
          });
    
    var process = { env: { NODE_ENV: "production" } }
    
    
  • 复制lib和types到c3d项目,将index.js设置为插件

  • 在任意脚本引用就可以

    
    import { createMachine, interpret } from "@ailhc/xstate2c3d"
    
    // Stateless machine definition
    
    // machine.transition(...) is a pure function used by the interpreter.
    
    const toggleMachine = createMachine({
    
      id: 'toggle',
    
      initial: 'inactive',
    
      states: {
    
        inactive: { on: { TOGGLE: 'active' } },
    
        active: { on: { TOGGLE: 'inactive' } }
    
      }
    
    });
    
    
    
    // Machine instance with internal state
    
    const toggleService = interpret(toggleMachine)
    
      .onTransition((state) => console.log(state.value))
    
      .start();
    
    // => 'inactive'
    
    
    
    toggleService.send('TOGGLE');
    
    // => 'active'
    
    
    
    toggleService.send('TOGGLE');
    
    // => 'inactive'
    
    

    xstate2c3d的工程已经更新到EasyGameFrameOpen:https://github.com/AILHC/EasyGameFrameworkOpen

其他方式

  1. 如果npm包有构建systemjs,直接使用

  2. 如果没有构建systemjs,看是否有typescript源码,copy源码,使用egf-cli构建一个systemjs规范的js

  3. 如果是全局变量的库,直接设置为插件就可以了。

谢谢大家阅读我的文章,希望大家能有所收获。

## 框架开发系列文章

## 最后

欢迎关注我的公众号,更多内容持续更新

公众号搜索:玩转游戏开发

或扫码:img

QQ 群: 1103157878

博客主页: https://ailhc.github.io/

掘金: https://juejin.cn/user/3069492195769469

github: https://github.com/AILHC

4赞

如何看库是不是全局变量的

em,看库的使用示例,比如https://github.com/millermedeiros/js-signals/blob/master/dist/signals.js
这个,你可以看到它将变量绑定到全局
而且如果这个库有声明文件,你用的时候,是 new signal.Signal(),不用import的

这个工具咋用呀,然后复制 EasyGameFrameOpen的package-template项目到node_modules中吗
包名在哪里修改呀

不太清楚你需要干嘛,egf-cli的使用文章中有参考。或者你加群,加我qq私聊

加群了,麻烦通过一下,小时候我们的梦

大佬,关于这个systemjs,是必须先把所有模块都放到System这个对象里面吗?

不一定,看你想多模块合成一个导出还是,分别导出

手写有点麻烦

我的是用工具导出的