关于seajs虽然已经有很长时间没写JavaScript,但很多时候看到一些应用还是会带着好奇心去研究一下。之前是看腾讯的朋友网,它的webchat做的很不错(虽然ff下有bug,也有消息丢失的情况,但总体的设计和体验上还是很不错的),抓包大致看了看请求和部分代码。 它的聊天框使用div + contenteditable属性实现了一个简单的“富”文本编辑器,但好像仅支持表情(源码里有正则,对指定的img节点的src进行过滤)。我分别在chrome和ie下使用了它的聊天框,发现IE下在粘贴会先出现粘贴的内容,然后所粘贴的内容被全部转换为文本。 一般情况下,如果仅使用一个textarea来实现ubb这样的输入,插入的表情不能立即显示出来,这个体验其实不好,但如果支持富文本输入,则可能导致页面因为节点的问题而发生错位等等…所以要实现的功能就是,支持插入特定的表情和特定的节点,除此之外的所有的节点都将被替换/删除掉。
朋友网前台页面使用Seajs装载jQuery来进行开发的,大概查看了一下编写的代码,写的比较优雅。上面表情插入的聊天框只要监听它的onpaste(低版本的opera监听它的oninput)进行处理。 由用户选择的表情,将不会被替换掉。
下面有一张关于各浏览器字符输入与剪贴板的事件的支持图:
而关于Seajs,很早就看过,但从来没用过,诸如CommonJS、RequireJS这些东东,都没有太多精力去使用和研究过。不过在查看朋友网的部分JS代码后,对于seajs就很好奇,想看看源码写几个例子,权当学习一下JavaScript的模块开发方式。在08年团队开发页面时,前台JavaScript会采用类似这样的写法:xx.include(‘…/dom.js’);xx.include([‘a.js’, ‘b.js’, …]);也算是有了JavaScript模块化开发的雏形,但还不是很成熟。仔细查看了一下seajs的源码,确实写的挺赞的。
我在写例子之前是参考过这篇文章的:《JavaScript模块化开发库之seajs》 Seajs适用于分模块化开发的应用,比如类似QZone,点击“装扮空间”时需要动态加载CSS、JS,在资源加载完成后渲染装扮,监听事件、回调…使用seajs来开发的话会很方便,主体的逻辑结构也将十分清晰。
Seajs的世界里,一个文件就是一个模块,而如果模块过多,那文件同样也将比较多,那随之而来的JS请求也将变多。而如果你在一个页面就定义了很多模块,那么有几个问题要注意…
假设直接在html页面里定义了一个名为domx的模块 1: define('./domx', [], function(require, exports, module) { 2: var mod; 3: 4: return mod = { 5: 'a' : function() { 6: alert('a'); 7: }, 8: 'b' : function() { 9: alert('b'); 10: } 11: }; 12: }) 一个文件只能书写一个模块(可定义多个模块),书写多个模块时,最后一个会替换之前的定义方法<在加载资源时发生>。 1: define(function(require, exports, module) { 2: alert('start-1'); 3: }); 如果html仅有上述代码,上面的alert(‘start-1’)并不会执行,这个可以从源码那里找到答案<详见define函数的定义>: 1: (function(util, data, fn) { 2: 3: /** 4: * Defines a module. 5: * @param {string=} id The module id. 6: * @param {Array.<string>|string=} deps The module dependencies. 7: * @param {function()|Object} factory The module factory function. 8: */ 9: function define(id, deps, factory) { 10: var argsLen = arguments.length; 11: 12: // define(factory) 13: if (argsLen === 1) { 14: factory = id; 15: id = undefined; 16: } 17: // define(id || deps, factory) 18: else if (argsLen === 2) { 19: factory = deps; 20: deps = undefined; 21: 22: // define(deps, factory) 23: if (util.isArray(id)) { 24: deps = id; 25: id = undefined; 26: } 27: } 28: 29: // Parse dependencies 30: if (!util.isArray(deps) && util.isFunction(factory)) { 31: deps = parseDependencies(factory.toString()); 32: } 33: 34: // Get url directly for specific modules. 35: if (id) { 36: var url = util.id2Uri(id); 37: } 38: // Try to derive url in IE6-9 for anonymous modules. 39: else if (document.attachEvent && !util.isOpera) { 40: 41: // Try to get the current script 42: var script = util.getCurrentScript(); 43: if (script) { 44: url = util.unParseMap(util.getScriptAbsoluteSrc(script)); 45: } 46: 47: if (!url) { 48: util.log('Failed to derive URL from interactive script for:', 49: factory.toString()); 50: 51: // NOTE: If the id-deriving methods above is failed, then falls back 52: // to use onload event to get the url. 53: } 54: } 55: 56: var mod = new fn.Module(id, deps, factory); 57: 58: if (url) { 59: util.memoize(url, mod); 60: data.packageMods.push(mod); 61: } 62: else { 63: // Saves information for "memoizing" work in the onload event. 64: data.anonymousMod = mod; 65: } 66: 67: } 68: 69: 70: function parseDependencies(code) { 71: // Parse these `requires`: 72: // var a = require('a'); 73: // someMethod(require('b')); 74: // require('c'); 75: // ... 76: // Doesn't parse: 77: // someInstance.require(...); 78: var pattern = /(?:^|[^.])\brequire\s*\(\s*(["'])([^"'\s\)]+)\1\s*\)/g; 79: var ret = [], match; 80: 81: code = removeComments(code); 82: while ((match = pattern.exec(code))) { 83: if (match[2]) { 84: ret.push(match[2]); 85: } 86: } 87: 88: return util.unique(ret); 89: } 90: 91: 92: // http://lifesinger.github.com/lab/2011/remove-comments-safely/ 93: function removeComments(code) { 94: return code 95: .replace(/(?:^|\n|\r)\s*\/\*[\s\S]*?\*\/\s*(?:\r|\n|$)/g, '\n') 96: .replace(/(?:^|\n|\r)\s*\/\/.*(?:\r|\n|$)/g, '\n'); 97: } 98: 99: 100: fn.define = define; 101: 102: })(seajs._util, seajs._data, seajs._fn); 当书写模块define(fn);时,define的实参长度为1,即argsLen===1,而id等于undefined时,url的值也将为undefined,所以此时的这个匿名函数将会被绑定到seajs._data.anonymousMod ==> 一个fn.Module的实例({id:undefined, dependencies:[], factory:xxx});它是不会被执行的…
假设现在加载一个dom模块==>seajs.use(‘dom’),它将会请求一个dom.js,如果dom.js未定义名为dom的模块或是未使用define(fn)时,当文件被加载后,调用seajs在内部调用fetch方法时,之前那个mod(id为undefined)的id将会被替换为http:///dom.js。代码function(require, exports, module) { alert('start-1');}将绑定到dom模块上,实际开发中defind(fn)这样的代码应当放在单独的JS文件中,让它作为模块的定义方法。
Seajs的简单使用示例: 1、定义模块<在JS中定义> 1: define('./dom', [], function(require, exports, module) { 2: var mod; 3: 4: return mod = { 5: 'a' : function() { 6: alert('a'); 7: }, 8: 'b' : function() { 9: alert('b'); 10: } 11: }; 12: }) 或者 1: 2: define(function(require, exports, module) {//or define([], function(require, exports, module) { 3: 4: exports.a = function() { 5: alert('dom-a'); 6: }; 7: 8: exports.b = function() { 9: alert('dom-b'); 10: }; 11: 12: });
2、使用模块 1: seajs.use('./dom', function(dom) { 2: dom.a(); 3: }); 注:第一种方式,其它在调用的时候也会转换为第二种方式。多个模块调用如下 1: define('./domx', [], function(require, exports, module) { 2: var mod; 3: 4: return mod = { 5: 'a' : function() { 6: alert('a'); 7: }, 8: 'b' : function() { 9: alert('b'); 10: } 11: }; 12: }); 13: 14: define('./events', [], function(require, exports, module) { 15: var mod; 16: 17: return mod = { 18: 'c' : function() { 19: alert('d'); 20: }, 21: 'd' : function() { 22: alert('d'); 23: } 24: }; 25: }); 26: 27: seajs.use(['./domx', './events'], function(domx, events) { 28: domx.a(); 29: events.c(); 30: }); 回调函数里的参数与依赖的模块是一一对应的关系
Seajs内部实现了依赖关系,让开发者可以将更多的精力放到应用的业务中去。假设test.html页面引用了dom.js,而dom.js 依赖event.js 而event.js依赖cache.js,那么加载顺序将是 dom.js –> event.js –> cache.js 而各模块的执行顺序是cache.js –> event.js –> dom.js
在dom下定义如下两种写法,差异较大。 方式一: 1: define('./dom', function(require, exports, module) { 2: 3: var cache = require('event'); 4: 5: exports.a = function() { 6: alert('dom-a'); 7: }; 8: 9: exports.b = function() { 10: alert('dom-b'); 11: }; 12: 13: alert('dom'); 14: 15: }); 方式二: 1: define('./dom', [], function(require, exports, module) { 2: 3: var cache = require('event'); 4: 5: exports.a = function() { 6: alert('dom-a'); 7: }; 8: 9: exports.b = function() { 10: alert('dom-b'); 11: }; 12: 13: alert('dom'); 14: 15: }); 使用方式二,event.js并不会被请求,而使用方式一,seajs会调用parseDependencies方法,解析出模块的依赖关系。 1: function parseDependencies(code) { 2: // Parse these `requires`: 3: // var a = require('a'); 4: // someMethod(require('b')); 5: // require('c'); 6: // ... 7: // Doesn't parse: 8: // someInstance.require(...); 9: var pattern = /(?:^|[^.])\brequire\s*\(\s*(["'])([^"'\s\)]+)\1\s*\)/g; 10: var ret = [], match; 11: 12: code = removeComments(code); 13: while ((match = pattern.exec(code))) { 14: if (match[2]) { 15: ret.push(match[2]); 16: } 17: } 18: 19: return util.unique(ret); 20: }
参考链接: 分类: Javascript |
|
来自: CevenCheng > 《高性能Web》