Seajs源码解析系列(四)
来源:互联网 发布:梦泰软件教学 编辑:程序博客网 时间:2024/06/05 15:15
前言:前三章对Seajs的基础应用,核心模块以及路径解析功能都做了介绍,这一章则对Seajs剩下的几项功能做一个综合的介绍。主要包括Seajs事件机制,脚本加载以及模块依赖。
代码解析:
一、Seajs事件机制:
Seajs内部提供了以下几种事件类型:
seajs.on seajs.on(event, callback)
用来添加事件回调。
// 给 fetch 事件添加一个回调seajs.on('fetch', function(data) { ...});
seajs.off seajs.off(event?, callback?)
用来移除事件回调。
// 从 fetch 事件的回调中移除掉 fn 函数seajs.off('fetch', fn);// 移除掉 fetch 事件的所有回调seajs.off('fetch');// 移除掉所有事件的所有回调seajs.off();
seajs.emit seajs.emit(event, data)
用来触发事件。
// 触发 fetch 事件seajs.emit('fetch', { uri: uri, fetchedList: fetchedList });
以下就是Seajs事件机制的源码,注释十分详细,这里就不多做介绍了。
/** * util-events.js - The minimal events support */var events = data.events = {}// Bind eventseajs.on = function(name, callback) { var list = events[name] || (events[name] = []) list.push(callback) return seajs}// Remove event. If `callback` is undefined, remove all callbacks for the// event. If `event` and `callback` are both undefined, remove all callbacks// for all eventsseajs.off = function(name, callback) { // Remove *all* events if (!(name || callback)) { events = data.events = {} return seajs } var list = events[name] if (list) { if (callback) { for (var i = list.length - 1; i >= 0; i--) { if (list[i] === callback) { list.splice(i, 1) } } } else { delete events[name] } } return seajs}// Emit event, firing all bound callbacks. Callbacks receive the same// arguments as `emit` does, apart from (除了) the event name//触发事件//事件的回调函数链保存在seajs.data.events中;//以事件名称为key,Array对象保存的回调函数链为valuevar emit = seajs.emit = function(name, data) { var list = events[name] if (list) { // Copy callback lists to prevent modification list = list.slice() // Execute event callbacks, use index because it's the faster. for(var i = 0, len = list.length; i < len; i++) { list[i](data) } } return seajs}
二、Seajs脚本加载
seajs.request()方法用于从服务端请求对应的模块。
//从服务端请求模块 function sendRequest() { seajs.request(emitData.requestUri, emitData.onRequest, emitData.charset, emitData.crossorigin) }
源码如下:当浏览器支持webworker时,直接调用importScript加载函数脚本,否则的话,通过动态创建script标签的方式进行脚本加载。为了防止在IE中的内存泄漏,在脚本加载完onload之后,要及时移除该脚本。
/** * util-request.js - The utilities for requesting script and style files * ref: tests/research/load-js-css/test.html *///使用webworker加载js脚本if (isWebWorker) { function requestFromWebWorker(url, callback, charset, crossorigin) { // Load with importScripts var error try { importScripts(url) } catch (e) { error = e } callback(error) } // For Developers seajs.request = requestFromWebWorker}//创建script标签的方式加载js脚本else { var doc = document var head = doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement var baseElement = head.getElementsByTagName("base")[0] var currentlyAddingScript function request(url, callback, charset, crossorigin) { var node = doc.createElement("script") if (charset) { node.charset = charset } if (!isUndefined(crossorigin)) { node.setAttribute("crossorigin", crossorigin) } addOnload(node, callback, url) node.async = true node.src = url // For some cache cases in IE 6-8, the script executes IMMEDIATELY after // the end of the insert execution, so use `currentlyAddingScript` to // hold current node, for deriving url in `define` call currentlyAddingScript = node // ref: #185 & http://dev.jquery.com/ticket/2709 baseElement ? head.insertBefore(node, baseElement) : head.appendChild(node) currentlyAddingScript = null } function addOnload(node, callback, url) { var supportOnload = "onload" in node if (supportOnload) { node.onload = onload node.onerror = function() { emit("error", { uri: url, node: node }) onload(true) } } else { node.onreadystatechange = function() { if (/loaded|complete/.test(node.readyState)) { onload() } } } function onload(error) { // Ensure only run once and handle memory leak in IE //为了防止在IE中的内存泄漏,在脚本加载完onload之后,会及时移除该脚本 node.onload = node.onerror = node.onreadystatechange = null // Remove the script to reduce memory leak if (!data.debug) { head.removeChild(node) } // Dereference the node node = null callback(error) } } // For Developers seajs.request = request}
三、Seajs模块依赖
Seajs的模块依赖通过方法parseDependencies
实现,大体代码逻辑是通过使用正则匹配require
的方式得到依赖信息。
涉及到的正则表达式如下所示:
/[a-z_$]/i
:匹配所有字母,不区分大小写;/^[\w$]+/
:匹配所有字母,数字,不区分大小写;/^require\s*\(\s*(['"]).+?\1\s*\)/
:测试是否含有require(“xx”);/^require\s*\(\s*['"]/
:匹配require关键字;/^[\w$]+(?:\s*\.\s*[\w$]+)*/
:匹配第一个单词;/^\.\d+(?:E[+-]?\d*)?\s*/i
:获取.之后的整数或者是.之后的以科学计数法表达的数字;/^0x[\da-f]*/i
:匹配以0x开头[数字 a-f]的字符串;/^\d+\.?\d*(?:E[+-]?\d*)?\s*/i
:匹配小数或者以科学计数法表示的小数
源码如下所示:
/** * util-deps.js - The parser for dependencies * ref: tests/research/parse-dependencies/test.html * ref: https://github.com/seajs/searequire *///通过正则匹配require的方式得到依赖信息function parseDependencies(s) { if(s.indexOf('require') == -1) { return [] } var index = 0, peek, length = s.length, isReg = 1, modName = 0, parentheseState = 0, parentheseStack = [], res = [] while(index < length) { readch() if(isBlank()) { } else if(isQuote()) { dealQuote() isReg = 1 } else if(peek == '/') { readch() if(peek == '/') { index = s.indexOf('\n', index) if(index == -1) { index = s.length } } else if(peek == '*') { index = s.indexOf('*/', index) if(index == -1) { index = length } else { index += 2 } } else if(isReg) { dealReg() isReg = 0 } else { index-- isReg = 1 } } else if(isWord()) { dealWord() } else if(isNumber()) { dealNumber() } else if(peek == '(') { parentheseStack.push(parentheseState) isReg = 1 } else if(peek == ')') { isReg = parentheseStack.pop() } else { isReg = peek != ']' modName = 0 } } return res function readch() { //返回指定位置的字符 peek = s.charAt(index++) } function isBlank() { return /\s/.test(peek) } function isQuote() { return peek == '"' || peek == "'" } //取出""或者''之间的字符,即依赖模块的名称 function dealQuote() { var start = index var c = peek var end = s.indexOf(c, start) if(end == -1) { index = length } else if(s.charAt(end - 1) != '\\') { index = end + 1 } else { while(index < length) { readch() if(peek == '\\') { index++ } else if(peek == c) { break } } } if(modName) { res.push(s.slice(start, index - 1)) modName = 0 } } function dealReg() { index-- while(index < length) { readch() if(peek == '\\') { index++ } else if(peek == '/') { break } else if(peek == '[') { while(index < length) { readch() if(peek == '\\') { index++ } else if(peek == ']') { break } } } } } function isWord() { return /[a-z_$]/i.test(peek) } function dealWord() { var s2 = s.slice(index - 1) //匹配所有的a-z,A-Z,0-9 @by sxy var r = /^[\w$]+/.exec(s2)[0] parentheseState = { 'if': 1, 'for': 1, 'while': 1, 'with': 1 }[r] isReg = { 'break': 1, 'case': 1, 'continue': 1, 'debugger': 1, 'delete': 1, 'do': 1, 'else': 1, 'false': 1, 'if': 1, 'in': 1, 'instanceof': 1, 'return': 1, 'typeof': 1, 'void': 1 }[r] //测试是否含有require("xx"); modName = /^require\s*\(\s*(['"]).+?\1\s*\)/.test(s2) if(modName) { //匹配require关键字 r = /^require\s*\(\s*['"]/.exec(s2)[0] index += r.length - 2 } else { //匹配第一个单词 index += /^[\w$]+(?:\s*\.\s*[\w$]+)*/.exec(s2)[0].length - 1 } } function isNumber() { return /\d/.test(peek) || peek == '.' && /\d/.test(s.charAt(index)) } function dealNumber() { var s2 = s.slice(index - 1) var r if(peek == '.') { //TODO: 获取.之后的整数或者是.之后的以科学计数法表达的数字 // /^\.\d+(?:E[+-]?\d*)?\s*/i.exec(".671+22+s2ss21")[0] ==>.671 r = /^\.\d+(?:E[+-]?\d*)?\s*/i.exec(s2)[0] } //匹配以0x开头[数字 a-f]的字符串 else if(/^0x[\da-f]*/i.test(s2)) { r = /^0x[\da-f]*\s*/i.exec(s2)[0] } else { //匹配小数或者以科学计数法表示的小数 r = /^\d+\.?\d*(?:E[+-]?\d*)?\s*/i.exec(s2)[0] } index += r.length - 1 isReg = 0 }}
四、后记
这一章由于涉及到的内容比较多,相应的介绍的也比较潦草,其实Seajs的核心代码都已经在前面几章介绍完毕了,本章着重的描写的是其中的方法实现。至此,Seajs源码分析系列到此也要告一段落了,由于接触Seajs时间并不长,对其了解并没有太深入,以上很多见解都只是初窥门径罢了。
0 0
- Seajs源码解析系列(四)
- Seajs源码解析系列(三)
- (四)seajs.config中vars、alias、paths和map的作用,以及util-path路径解析源码
- SeaJS入门教程系列之使用SeaJS(二)
- SeaJS入门教程系列之SeaJS介绍(一)
- SeaJS入门教程系列之使用SeaJS(二)
- SeaJS入门教程系列之SeaJS介绍(一)
- SeaJS入门教程系列之使用SeaJS(二)
- SeaJS入门教程系列之SeaJS介绍(一)
- SeaJS入门教程系列之使用SeaJS(二)
- Hessian源码解析(四)
- Vue源码解析(四)
- 源码有毒:Jfinal源码解析(四)
- Android源码解析Handler系列第(四)篇 --- 打破Handler那些困惑事儿
- Android源码解析Handler系列第(四)篇 --- 打破Handler那些困惑事儿
- Android源码解析四大组件系列(四)---Activity启动详细流程
- seajs源码分析-运行机制浅析(一)
- Java集合源码解析(四)TreeMap源码解析
- Android Fragment 真正的完全解析
- 学习笔记---C语言数据类型
- java构造函数基本知识
- 深信服超融合基础架构管理平台--虚拟网络测试实践
- 原比例改变图片大小
- Seajs源码解析系列(四)
- linux 内存使用率高问题
- Linux中查看日志的常用命令
- Ubuntu svn 缺少一些.so文件
- excel VBA对特定背景颜色的单元格进行赋值
- angularJS 如何绑定file表单的change?
- NS3为脚本运行创建日志LOG
- 多种效果转换的轮播图
- 彩色转灰度算法(FPGA实现)