小矮人Javascript模块加载器
https://github.com/miniflycn/webkit-dwarf
短小精悍的webkit浏览器Javascript模块加载器
Why
- 我们有许多仅基于webkit浏览器开发的应用
- 无论是使用requirejs还是seajs都需要先加载完模块加载器本身之后才能加载其他模块
- 无论出于首屏优化需要,还是手机2G优化需要,我们都需要一个足够小的模块加载器
- 内置CDN失败回源策略
AMD & CMD
require([module], callback);
这是AMD规范中模块引用的经典方式。实际上CMD规范在异步引用上也与之类似。
require.async([module], callback);
实际上它们差别主要在define上,如AMD规范中define是这么用的:
define(['./a', './b'], function (a, b) {
// do something
});
而CMD中是这样的:
define(['./a', './b'], function (require, exports, module) {
var a = require('./a'),
b = require('./b');
// do something
});
AMD规范加载与引用是一体的,而CMD规范加载与引用是分离的。
webkit-dwarf的规范呢?
requirejs和seajs出现的时候,线下构建还不是非常流行。但现在grunt的应用已经随处可见了.特别我们在browserify中看到其精妙的应用。
所以我们的目标是在线下编写的时候,尽量接近NodeJS模块编写风格,但构建后可以满足模块异步加载,串行引用。
线下规范
- 同步引用:
require(module)
module String 模块路径
返回 对应模块
- 异步引用:
使用require包裹函数体:
require(factory)
factory Function 加载后运行的函数
例如:
require(function () {
var a = require('./a');
// do something
});
dwarf将会异步加载完./a.js后,再调用factory
- 定义:
define(factory)
factory Function 模块的定义函数
例如:
./b.js
define(function (require, exports, module) {
var a = require('./a');
// do something
module.exports = function () {
console.log('module b exports');
}
})
线上规范
- 同步获取
require(module)
module String 要获取的模块名
返回 对应模块
例如:
require(['./test'], function () {
var test = require('./test');
// do somthing
}, function () {
throw new Error('Failed to load module');
});
- 异步加载
require(modules, success, fail)
modules Array 异步加载的模块数组
success Function 成功回调
fail Function 失败回调
例如:
require(['./test'], function () {
// do something
}, function () {
throw new Error('Failed to load module');
});
- 定义模块
define(module, dependencies, factory)
define(module, factory)
define(module, value)
module String 模块相对该js文件对应路径,因为有可能在一个js文件中定义多个模块
dependencies Array 依赖数组
factory Functino 模块初始化工厂
value String, Number or Object 模块值
例如:
define('./test', [./util], function (require, exports, module) {
var util = require('./util');
// do something
exports = module.exports = {
result: 'test'
};
});
技术细节
- 加载方式
我们使用async的srcipt节点插入head中进行加载。由于针对webkit浏览器,所以加载成功回调和失败回调都是现成的。
/**
* load
* @param {String} url
*/
load: function (url) {
var
node = document.createElement('script'),
self = this;
node.addEventListener('load', _onload, false);
node.addEventListener('error', _onerror, false);
node.type = 'text/javascript';
node.async = 'async';
node.src = url;
_head.appendChild(node);
function _onload() {
_onend();
return Def.make(this.src);
}
function _onerror() {
_onend();
_head.removeChild(node);
if (_base && !~url.indexOf(_localBase)) {
return self.load(url.replace(_base, _localBase));
} else {
return self.down();
}
}
function _onend() {
node.removeEventListener('load', _onload, false);
node.removeEventListener('error', _onerror, false);
}
},
- 文件路径
获取到正确的文件路径,才能正确判断依赖文件路径,现在主要有两种方法获取文件路径:
- getCurrentScript方案
- 通过srcipt onload将script.src带过去
document.currentScript是firefox 4.0开始出现的非标准API,而Chrome 29+已经支持,也已写入HTML 5规范中:
Returns the script element that is currently executing. In the case of reentrant script execution, returns the one that most recently started executing amongst those that have not yet finished executing.
Returns null if the Document is not currently executing a script element (e.g. because the running script is an event handler, or a timeout).
所以我们可以通过下面代码来获取路径:
function getCurrentScript() {
if (document.currentScript) {
return document.currentScript.src;
}
}
但毕竟只能满足部分浏览器,所以又产生了另一种通过error stack来获取当前脚本路径的方法:
function getCurrentScript() {
//取得正在解析的script节点
if (document.currentScript) { // firefox 4+
return document.currentScript.src;
}
// 参考 https://github.com/samyk/jiagra/blob/master/jiagra.js
var stack, i, node;
try {
a.b.c(); // 强制报错,以便捕获e.stack
} catch(e) {
stack = e.stack;
}
if (stack) {
i = stack.lastIndexOf(' at ');
var a = stack.slice(i + e.length).replace(/\s\s*$/, '').replace(/(:\d+)?:\d+$/i, '');
return a;
}
}
可这个方案无法支持safari,为了避免代码过大,虽然一般而言这会更有效率,但还是使用第二种方案来实现。
结果
| webkit-dwarf | 情况 |
|---|---|
| 源代码 | 7021b |
| uglify | 2844b |
| gizp | 662b |
| 支持 | 现代浏览器 & IE10+ |
小矮人Javascript模块加载器的更多相关文章
- RequireJS 是一个JavaScript模块加载器
RequireJS 是一个JavaScript模块加载器.它非常适合在浏览器中使用, 它非常适合在浏览器中使用,但它也可以用在其他脚本环境, 就像 Rhino and Node. 使用RequireJ ...
- JavaScript AMD 模块加载器原理与实现
关于前端模块化,玉伯在其博文 前端模块化开发的价值 中有论述,有兴趣的同学可以去阅读一下. 1. 模块加载器 模块加载器目前比较流行的有 Requirejs 和 Seajs.前者遵循 AMD规范,后者 ...
- 【模块化编程】理解requireJS-实现一个简单的模块加载器
在前文中我们不止一次强调过模块化编程的重要性,以及其可以解决的问题: ① 解决单文件变量命名冲突问题 ② 解决前端多人协作问题 ③ 解决文件依赖问题 ④ 按需加载(这个说法其实很假了) ⑤ ..... ...
- Webpack 常见静态资源处理 - 模块加载器(Loaders)+ExtractTextPlugin插件
Webpack 常见静态资源处理 - 模块加载器(Loaders)+ExtractTextPlugin插件 webpack系列目录 webpack 系列 一:模块系统的演进 webpack 系列 二: ...
- 实现一个类 RequireJS 的模块加载器 (二)
2017 新年好 ! 新年第一天对我来说真是悲伤 ,早上兴冲冲地爬起来背着书包跑去实验室,结果今天大家都休息 .回宿舍的时候发现书包湿了,原来盒子装的牛奶盖子松了,泼了一书包,电脑风扇口和USB口都进 ...
- JS模块加载器加载原理是怎么样的?
路人一: 原理一:id即路径 原则.通常我们的入口是这样的: require( [ 'a', 'b' ], callback ) .这里的 'a'.'b' 都是 ModuleId.通过 id 和路径的 ...
- 转: javascript模块加载框架seajs详解
javascript模块加载框架seajs详解 SeaJS是一个遵循commonJS规范的javascript模块加载框架,可以实现javascript的模块化开发和模块化加载(模块可按需加载或全部加 ...
- javascript模块加载框架seajs详解
SeaJS是一个遵循commonJS规范的javascript模块加载框架,可以实现javascript的模块化开发和模块化加载(模块可按需加载或全部加载).SeaJS可以和jQuery完美集成,使用 ...
- 使用RequireJS并实现一个自己的模块加载器 (二)
2017 新年好 ! 新年第一天对我来说真是悲伤 ,早上兴冲冲地爬起来背着书包跑去实验室,结果今天大家都休息 .回宿舍的时候发现书包湿了,原来盒子装的牛奶盖子松了,泼了一书包,电脑风扇口和USB口都进 ...
随机推荐
- 不一样的go语言-不同的语法之type
前言 在go语言中,type用于类型定义(type definition)与类型别名(type alias).这两者的差别从名字上已经可以初见端倪. 类型定义即定义新类型,是一个全新的类型,但 ...
- rabbitmq学习(七) —— springboot下的可靠使用
前面的学习都是基于原生的api,下面我们使用spingboot来整合rabbitmq springboot对rabbitmq提供了友好支持,极大的简化了开发流程 引入maven <depende ...
- 【java并发核心一】Semaphore 的使用思路
最近在看一本书<Java并发编程 核心方法与框架>,打算一边学习一边把学习的经验记下来,所粘贴的代码都是我运行过的,大家一起学习,欢迎吐槽. 估计也没多少人看我的博客,哈哈,那么我还是会记 ...
- SpringMVC中ModelAndView对象与“视图解析器”
摘要: spring MVC这个环境中,Spring MVC会依据controller(或者你叫它handler)中处理方法的返回值,进行解析,解析之后提供一个视图,作为响应. 标注了@Control ...
- Linux笔记 rm -rf 嘻嘻
学习目标:常用linux命令的使用 JAVAEE :后台应用都会涉及到linux系统,应用程序的部署,运维,分布式集群,大数据,云计算 虚拟机:虚拟出来的计算机 虚拟机软件:用来产生虚拟机的一个软件 ...
- 使用starUML一步一步画顺序图
顺序图:是UML中能表现出一个过程中各个详细步骤的模型图,过程可以理解为一个功能的执行过程. 下面我们以一个简单的影院管理系统中售票功能为例来一步一步完成顺序图的构建. 建模工具:starUML 版本 ...
- Python数据可视化系列-02-pyecharts可视化非常cool
pyecharts介绍 pyecharts网站 Pyecharts生成的图像,动态效果非常cool.在HTML上展示很是perfect.matplotlib用于科研,但是pyecharts用于展示和讲 ...
- Python中的MySQLConnector使用介绍
MySQL Connector/Python 是 MySQL 官方提供的 Python 连接 MySQL 数据库的驱动程序了,很多初学者对于 在python中连接mysql数据库还是有点为难了,下文我 ...
- 潭州课堂25班:Ph201805201 爬虫基础 第三课 urllib (课堂笔记)
Python网络请求urllib和urllib3详解 urllib是Python中请求url连接的官方标准库,在Python2中主要为urllib和urllib2,在Python3中整合成了url ...
- js获取判断苹果手机机型
原先获取不了苹果系列的型号,但转换思路,先判断是否是苹果,再用分辨率获取型号 //获取手机型号函数beginfunction getPhoneType(){ //正则,忽略大小写var pattern ...