简单介绍下modJS

近期把团队的项目构建迁移到fis3,同时把模块加载器也由之前的requirejs切换到了modJS。

有些同学可能不了解modJS,这里做个简单的介绍。

那么,modJS是什么呢?

简单的说,modJS是百度fex-team提供的一个轻量级的模块加载器,类似requirejs。

但modJS并不完全兼容规范amd/cmd,事实上,只支持非常简单的全局方法define(id,factory)。

另外factory提供了3个参数require/exports/module,用于引用和导出模块。

modJS源码只有200行左右,相比之下requirejs的源码达到了2000+行。

除了体量非常小之外,modJS配合fis3的fis3-hook-commonjs插件,可以在纯前端项目中实现类似nodejs一样的开发体验。

那么,我们为什么要使用modJS呢?

有以下几点:

  1. 加载器更小

上面已经提到,200+行和2000+行的差异

  1. 配合构建工具,开发体验更好

之前开发时,需要将每一个模块的代码单独放在define内部,并且需要申明每一个依赖。 而现在,只需要使用类似nodejs的方式编写代码,需要使用某个依赖模块时,直接require('id')即可。 发布编译时,由构建工具统一添加define包裹,自动添加模块id(默认根据路径生成,也可以在fis3的配置中声明格式)。整体的开发体验更好一些。

此外,js文件打包及异步依赖的问题,也可以通过生成resourceMap来解决。

一般情况下,modJS配合fis3已经可以满足大部分需求,并且官方还提供了完整支持amd规范的esl-mod.js。

如果不够用,也可以在modJS的基础上定制一些功能来满足需求,因为源码只有200行,代码也很清晰简单。

我们的项目在切换到modJS时,由于项目比较复杂(2000+文件),一些特殊的需求最终也是通过定制modJS得到解决,这里分享几点:

遇到哪些问题

  • 同步(直接require)的依赖需要打包,异步依赖使用require.async加载

requirejs将所有依赖模块在define方法的参数中声明,所以可以保证先异步加载需要的模块,最后再执行factory。

而modJS设计之初,便考虑到稍微大型点的前端项目都会打包模块js减少请求优化性能,依赖的模块其实早已合并打包,并不需要在define中声明后再异步加载。

所以需要将所有异步的模块以require.async方法来加载。

以我们的项目为例,首次加载时,会加载3个打包的js文件,分别是基础库(modjs、jquery、badjs)、base.js(其他打包的通用模块,初始化一些变量)、mod.main.js(当前页面用到的逻辑打包)。 当用户点击其他页面(非刷新)时,再异步加载该页面用到的mod.main.js(其他页面打包合并后的js),这部分js的模块id和url由构建工具统一打到resourceMap中。

  • 不支持直接引用远程url,包括同步和异步方式都不支持。

由于历史原因,项目中存在一些如下形式的代码:

define(id,['http://a','http://b'],function(a,b){
    a.xxx(b)
    ...
})

通过脚本统一替换为commonJS规范后,旧代码变成了这样:

var a = require('http://a');
var b = require('http://b');
a.xxx(b)
...

这种同步方式的代码目前没什么好的解决办法,最后重构代码解决。

异步的方式稍微好一些,可以通过构建将远程url打入到resourceMap。

  1. 异步加载方法require.async([deps],callback)中的callback触发

modJS加载异步模块时,将callback回调加入一个队列,然后将依赖模块的script标签打入html的head。 但是callback并不是通过script的onload事件触发,而是通过依赖模块的define方法触发。

当我们只是想用require.async异步加载一个非amd规范的脚本时(例如一些第三方的组件、jquery插件等),由于该脚本并没有被define方法包裹,所以无法触发回调的逻辑。

虽然理论上不应该有这个问题,一个项目的模块化方案应该统一,每一个模块都应该使用define包裹,但现实就这样。

  1. 同时异步加载多个模块时,脚本的加载顺序问题

modJS异步加载时,会将多个script脚本同时插入到head,并不会维护脚本的加载顺序。

这个问题,怎么说,理论上模块的依赖关系都应该由加载器来管理,而不应该出现脚本的加载依赖问题。但现实就这样。

当然,如果你愿意的话也可以将源码改成这样的形式:

require.async(moduleA,function(){
    require.async(moduleB,function(){
        ...do something
        ...囧
        ...do something
    });
});

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏河湾欢儿的专栏

启动项目时有node-sass的错误

然后就出现了一堆错误 解决方案首先就是使用cnpm或者yarn来安装 我这里用的yarn 然后下载依赖 结果发现还是报错 找了半天才想起来这是一年前的...

421
来自专栏java架构师

java基础-servlet-1

servlet,是运行于服务器端的小程序。它既可以运行在http服务器端,也可以运行在mail等其他服务器端。我是这样理解,tomcat把接收到的客户端请求,转...

2516
来自专栏史上最简单的Spring Cloud教程

Openresty最佳案例 | 第7篇: 模块开发、OpenResty连接Redis

Lua模块开发 在实际的开发过程中,不可能把所有的lua代码写在一个lua文件中,通常的做法将特定功能的放在一个lua文件中,即用lua模块开发。在lualib...

2497
来自专栏Java呓语

Centos 6.5 邮箱服务器搭建

若提示命令未找到,参考链接:bash-mail-command-not-found/ 。

481
来自专栏前端真相

如何发布自己的NPM包(模块)?

按照提示填写初始化信息,我的模块名称为:finitxu-npm-test,初始版本号:v1.0.0。

1148
来自专栏PHP技术

svn自动检出修改的文件列表

开发完一个功能,我们需要把该功能修改的所有文件找出来,增量更新到发布环境,如果使用svn log查看svn记录,没有排重,信息比较详细,不容易筛选,这样将会花费...

1916
来自专栏Java帮帮-微信公众号-技术文章全总结

Linux查看日志命令【面试+工作】

1764
来自专栏王小雷

解决The Network Adapter could not establish the connection

解决1 主机与虚拟机ping不通 解决2 状态: 失败 -测试失败: IO 错误: The Network Adapter could not establis...

2685
来自专栏性能与架构

Docker容器间网络如何互联

image.png 1. 通过IP互联 容器带有虚拟网桥,可以有自己的ip,容器间就可以通过ip进行互相通信 启动两个容器 分别ssh登陆,ifconfig查...

3355
来自专栏瓜大三哥

伪路径

1.什么是伪路径? 存在于设计之中,之所以叫伪路径,是因为这样的路径并未发挥真正的功能,在时序分析时不需要我们去分析。 2.为什么设置伪路径? 去除无效的时序...

2158

扫码关注云+社区