简单介绍下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 条评论
登录 后参与评论

相关文章

来自专栏青玉伏案

JavaEE开发之SpringMVC中的路由配置及参数传递详解

在之前我们使用Swift的Perfect框架来开发服务端程序时,聊到了Perfect中的路由配置。而在SpringMVC中的路由配置与其也是大同小异的。说到路由...

21210
来自专栏python3

python 文件操作2

Somehow, it seems the love I knew was always the most destructive kind

761
来自专栏Core Net

C# 处理Word自动生成报告 三、设计模板

2825
来自专栏开发实战

swagger-bootstrap-ui 1.7.5 发布,Swagger前端 UI 实现

swagger-bootstrap-ui 1.7.5 发布了。swagger-bootstrap-ui 是 Swagger 的前端 UI 实现,目的是替换 Sw...

1433
来自专栏从零开始学自动化测试

python接口自动化2-发送post请求

前言 发送post的请求参考例子很简单,实际遇到的情况却是很复杂的,首先第一个post请求肯定是登录了,但登录是最难处理的。登录问题解决了,后面都简单了。 一...

2754
来自专栏康怀帅的专栏

C Linux 下的编译

本文简要介绍了 C 语言的编译命令。 Hello World! #include <stdio.h> int main() { printf("...

2875
来自专栏码匠的流水账

java9系列(三)模块系统精要

如果A依赖B,B依赖C,B方法返回的类型是C中的类型,那么需要A也可以使用C,则需要在A中声明依赖C。不过这样子非常费劲,所以java9内置了个transiti...

952
来自专栏闻道于事

SpringBoot常用配置

前言:springboot集成了主流的第三方框架,但是需要使用springboot那一套配置方式。但是我这里只列举了非常非常常用的,可以看已发的几篇博客,慢慢会...

4347
来自专栏cloudskyme

org.hibernate.type.StringType cannot be cast to org.hibernate.type.VersionType

两种情况,一种是有关键字不能解析 某张表有version字段,从hibernate tools生成的hbm文件内容: <version name="versio...

3038
来自专栏cs

c++那些事儿7.0 I/O流,文件操作

知识点综述: ---- C++ I/O: 在iostream头文件中定义 istream //通用输入流和其它输入流基类。 ...

3317

扫码关注云+社区