博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
浏览器加载 CommonJS 模块的原理与实现
阅读量:6857 次
发布时间:2019-06-26

本文共 2831 字,大约阅读时间需要 9 分钟。

就在这个周末, 超过了 ,成为地球上最大的软件模块仓库。

npm 的模块都是 JavaScript 语言写的,但浏览器用不了,因为不支持 CommonJS 格式。要想让浏览器用上这些模块,必须转换格式。

本文介绍浏览器加载 CommonJS 的原理,并且给出一种非常简单的。

一、原理

浏览器不兼容CommonJS的根本原因,在于缺少四个Node.js环境的变量。

  • module
  • exports
  • require
  • global

只要能够提供这四个变量,浏览器就能加载 CommonJS 模块。

下面是一个简单的示例。

var module = {
exports: {
} }; (function(module, exports) {
exports.multiply = function (n) {
return n * 1000 }; }(module, module.exports)) var f = module.exports.multiply; f(5) // 5000

上面代码向一个立即执行函数提供 module 和 exports 两个外部变量,模块就放在这个立即执行函数里面。模块的输出值放在 module.exports 之中,这样就实现了模块的加载。

二、Browserify 的实现

知道了原理,就能做出工具了。 是目前最常用的 CommonJS 格式转换的工具。

请看一个例子,main.js 模块加载 foo.js 模块。

// foo.jsmodule.exports = function(x) {
console.log(x); }; // main.jsvar foo = require("./foo"); foo("Hi");

使用下面的命令,就能将main.js转为浏览器可用的格式。

$ browserify main.js > compiled.js

Browserify到底做了什么?安装一下,就能看清楚了。

$ npm install browser-unpack -g

然后,将前面生成的compile.js解包。

$ browser-unpack < compiled.js[ {
"id":1, "source":"module.exports = function(x) {\n console.log(x);\n};", "deps":{
} }, {
"id":2, "source":"var foo = require(\"./foo\");\nfoo(\"Hi\");", "deps":{
"./foo":1}, "entry":true } ]

可以看到,browerify 将所有模块放入一个数组,id 属性是模块的编号,source 属性是模块的源码,deps 属性是模块的依赖。

因为 main.js 里面加载了 foo.js,所以 deps 属性就指定 ./foo 对应1号模块。执行的时候,浏览器遇到 require('./foo') 语句,就自动执行1号模块的 source 属性,并将执行后的 module.exports 属性值输出。

三、Tiny Browser Require

虽然 Browserify 很强大,但不能在浏览器里操作,有时就很不方便。

我根据 的内部实现,做了一个纯浏览器的 CommonJS 模块加载器 。完全不需要命令行,直接放进浏览器即可,所有代码只有30多行。

它的逻辑非常简单,就是把模块读入数组,加载路径就是模块的id。

function require(p){
var path = require.resolve(p); var mod = require.modules[path]; if (!mod) throw new Error('failed to require "' + p + '"'); if (!mod.exports) {
mod.exports = {
}; mod.call(mod.exports, mod, mod.exports, require.relative(path)); } return mod.exports; }require.modules = {
};require.resolve = function (path){
var orig = path; var reg = path + '.js'; var index = path + '/index.js'; return require.modules[reg] && reg || require.modules[index] && index || orig; };require.register = function (path, fn){
require.modules[path] = fn; };require.relative = function (parent) {
return function(p){
if ('.' != p.charAt(0)) return require(p); var path = parent.split('/'); var segs = p.split('/'); path.pop(); for (var i = 0; i < segs.length; i++) {
var seg = segs[i]; if ('..' == seg) path.pop(); else if ('.' != seg) path.push(seg); } return require(path.join('/')); }; };

使用的时候,先将上面的代码放入页面。然后,将模块放在如下的立即执行函数里面,就可以调用了。

还是以前面的 main.js 加载 foo.js 为例。

require.register("./foo.js", function(module, exports, require){
module.exports = function(x) {
console.log(x); }; }); var foo = require("./foo.js"); foo("Hi");

注意,这个库只模拟了 require 、module 、exports 三个变量,如果模块还用到了 global 或者其他 Node 专有变量(比如 process),就通过立即执行函数提供即可。

(完)

转载地址:http://koiyl.baihongyu.com/

你可能感兴趣的文章
Nginx实战基础篇六 通过源码包编译安装部署LNMP搭建Discuz论坛
查看>>
如何根据指定软件版本制作属于自己的puppet yum源
查看>>
Linux下架设rsync服务器
查看>>
windows 屏幕坐标 窗口坐标 客户区坐标 逻辑坐标 设备坐标之间的关系及转换
查看>>
在Foreda8上试安装Apchehttpd-2.4.6.tar.gz
查看>>
基于S3C2410的VIVI移植
查看>>
Entity Framwork one to one problem
查看>>
[转] Attach、Detach和DeleteObject
查看>>
[转] C# 获取程序运行目录
查看>>
【OpenCV学习】极坐标变换
查看>>
使用open***构建安全***
查看>>
模仿支付宝支付密码框思路
查看>>
docker 容器的网络配置
查看>>
开源监控软件的配置:cacti,nagios
查看>>
Python多线程之threading模块
查看>>
第 6 章 单区域 OSPF
查看>>
Android图片开发内幕--基础篇
查看>>
LVS DR模式
查看>>
ZooKeeper 的安装 以及配置说明
查看>>
iOS10推送通知(本地&远程)/Swift
查看>>