使用requirejs编写模块化代码

写在前面

最早接触javascript的时候,javascript代码直接扔在script标签里面就完事了。

反正代码不多,交互简单,逻辑不难,和HTML混在一起也未尝不可。

后来交互越来越复杂,代码越多越多了,我们就开始把JS代码独立到了单独的JS文件中。

公共的库引用在前,自己的逻辑代码引用在后,全局变量定义在HTML内部,在独立JS文件中直接使用变量就好。

我们会经常看到下面这种代码:

123456

<script src="1.js"></script>  <script src="2.js"></script>  <script src="3.js"></script>  <script src="4.js"></script>  <script src="5.js"></script>  <script src="6.js"></script>

通过script标签顺序去js管理依赖关系。

阮一峰老师在Javascript模块化编程(三):require.js的用法 一文中总结了这样写法的缺点:

12345

首先,加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的1.js要在2.js的前面),依赖性最大的模块一定要放到最后加载.当依赖关系很复杂的时候,代码的编写和维护都会变得困难。

而requirejs的诞生便是为了解决这个问题。

requirejs

官网把requirejs 下载回来之后。使用一般的方法引入:

1

<script src="js/require.js"></script>

但是这样的方法,还是可能在加载require.js的时候导致网页失去响应。解决方案一般有两种:

  1. 把上面的代码放到网页底部
  2. 使用异步的方法加载,如下:

1

<script src="js/require.js" defer async="true" ></script>

async属性 表明这个文件需要异步加载,避免网页失去响应。

不过IE下不支持这个属性,只支持defer,所以可以把defer也写上。

加载主模块

在上一步,我们已经引入了require了,那么require怎么知道我们究竟要加载什么东西呢?答案是使用data-main属性。 假设我们的主模块为js/home.js,引入代码应该如下:

12

<script src="js/require.js" data-main="js/home"></script>//require.js默认文件后缀为js,所以home.js可以写成home。

接下来我使用58HouseSearch 的代码来讲解重构过程。

在此项目里面,重构前大概就是JS变量漫天飞,js文件里面各种函数到处乱放。一开始用起来还没什么,后来加入了更多功能的时候,JS代码维护起来就疼不欲生了。因此托了个小伙伴帮忙使用模块化思想重构了一下JS代码。

上面说了,我们首先需要创建我们的模块,在这个项目里面,主模块叫home.js。

home.js中我们需要配置一下require.config.

12345678910111213141516171819

require.config({ baseUrl: '/DomainJS/', paths: { jquery: "lib/jquery-1.11.3.min", "AMUI": "lib/amazeui.2.7.1.min", "jquery.range": "lib/jquery.range", "es5": "lib/es5", "mapController": "mapController", "addToolbar": "addToolbar", }, shim: { "addToolbar": { deps: ["jquery"] }, "jquery.range": { deps: ["jquery"] } }});

在这里我主要配置了一下baseURL(所有模块的查找根路径),paths(名称映射),shim( 为那些没有使用define()来声明依赖关系、设置模块的”浏览器全局变量注入”型脚本做依赖和导出配置。)

关于require.config的详细内容可以看下下面这些文章:

  1. RequireJS进阶:配置文件的学习
  2. RequireJS进阶:模块的优化及配置的详解

配置做完了,我们也可以开始真正写我们的逻辑代码了,我们使用require来加载我们需要的库。 代码如下:

1234567891011121314151617181920212223242526272829303132333435

require(['domready!', 'jquery', 'AMUI', 'mapController', 'city', 'commuteGo'], function (doc, $, AMUI, mapController, city, commuteGo) { city.initAllCityInfo(); mapController.init(); $("input[name='locationType']").bind('click', mapController.locationMethodOnChange) $("input[name='vehicle']").bind('click', commuteGo.go) $('#Get58Data').bind('click', function(e) { e.preventDefault(); mapController.Get58DataClick(); e.stopPropagation(); }); $.ajax({ type: "post", url: "../Commom/GetPVCount", data: { }, success: function (result) { if (result.IsSuccess){ $("#lblPVCount").text(result.PVCount); }else { $("#lblPVCount").text(0); console.log(result.Error); } } }); $('#search-offcanvas').offCanvas({ effect: 'overlay' }); $(".amap-sug-result").css("z-index", 9999);})

忽略function里面的具体逻辑,加载如下:

123456

require(['domready!', 'jquery', 'AMUI', 'mapController', 'city', 'commuteGo'], function (doc, $, AMUI, mapController, city, commuteGo){//todo});

第一个参数为一个数组,表示所依赖的模块,此处为[‘domready!’, ‘jquery’, ‘AMUI’, ‘mapController’, ‘city’, ‘commuteGo’];

第二个参数为回调函数,当前面指定的模块都全部加载成功之后,便调用此函数。加载的模块会以参数形式传入此函数,从而在回调函数内部就可以使用这些模块啦。

require()异步加载所需模块的时候,此时浏览器并不会失去响应;当前面的模块加载成功之后,执行回调函数才会运行我们的逻辑代码,因此解决了依赖性问题。

讲完了模块加载,我们下面讲一下模块编写。

AMD模块编写

require.js加载的模块的采用的AMD规范。所以我们的模块必须按照AMD的规定来写。

关于AMD规范详情可以看这个文章:Javascript模块化编程(二):AMD规范

模块有两个情况,不依赖其他模块和依赖其他模块。

不依赖其他模块

直接define定义,使用function回调。

58HouseSearch/DomainJS/helper.js

123456789101112

define(function () { //获取URL中的参数 var getQueryString= function (name) { var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); var r = window.location.search.substr(1).match(reg); if (r != null) return unescape(r[2]); return null; } return { getQueryString: getQueryString, };})

依赖其他模块

define中如同require一样,用数组表明需要加载的模块,function回调。

58HouseSearch/DomainJS/marker.js

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455

define(['mapSignleton', 'city', 'transfer'], function(mapSignleton, city, transfer) { var _map = mapSignleton.map; var _workMarker = null; var _markerArray = []; var load = function(x, y, locationName) { _workMarker = new AMap.Marker({ map: _map, title: locationName, icon: 'http://webapi.amap.com/theme/v1.3/markers/n/mark_r.png', position: [x, y] }); } var add = function(address, rent, href, markBG) { new AMap.Geocoder({ city: city.name, radius: 1000 }).getLocation(address, function(status, result) { if (status === "complete" && result.info === 'OK') { var geocode = result.geocodes[0]; var rentMarker = new AMap.Marker({ map: _map, title: address, icon: markBG ? 'IMG/Little/' + markBG : 'http://webapi.amap.com/theme/v1.3/markers/n/mark_b.png', position: [geocode.location.getLng(), geocode.location.getLat()] }); _markerArray.push(rentMarker); rentMarker.content = "<div><a target = '_blank' href='" + href + "'>房源:" + address + " 租金:" + rent + "</a><div>" rentMarker.on('click', function(e) { transfer.add(e, address); }); } }) }; var clearArray = function() { if (_markerArray && _markerArray.length > 0) _map.remove(_markerArray); _markerArray = []; } var clear = function() { if (_workMarker) { _map.remove(_workMarker); } } return { load: load, add: add, clearArray: clearArray, clear: clear };});

这样的话,一个供require调用的模块也就写好了。

最后感谢小伙伴Larry Sean 帮忙重构代码。

全文完。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏更流畅、简洁的软件开发方式

【自然框架】 页面里的父类—— 改进和想法、解释

1、 从Control到GridView继承了多少层? ? (这个图可不是现做的,这是以前为了写QuickPager分页控件而弄的。http://www.cn...

1995
来自专栏枕边书

用C写一个web服务器(二) I/O多路复用之epoll

前言 继续更新“用 C 写一个 web 服务器”项目(上期链接:用C写一个web服务器(一) 基础功能),本次更新选择了 I/O 模型的优化,因为它是服务器的基...

17810
来自专栏非著名程序员

Android 混淆从入门到精通

? 简介 作为Android开发者,如果你不想开源你的应用,那么在应用发布前,就需要对代码进行混淆处理,从而让我们代码即使被反编译,也难以阅读。混淆概念虽然容...

3119
来自专栏cloudskyme

Apache nutch1.5 & Apache solr3.6

第1章引言 1.1nutch和solr Nutch 是一个开源的、Java 实现的搜索引擎。它提供了我们运行自己的搜索引擎所需的全部工具。 Solr 拥有像 w...

3444
来自专栏Google Dart

AngularDart Material Design 屑 顶

“chip”小部件呈现“chip”格式的对象 - 带阴影的圆形框,通常用于水平列表。可以使用任何实现HasUIDisplayName接口的对象,或者根本不使用任...

844
来自专栏一名合格java开发的自我修养

(理论篇)从基础文件IO说起虚拟内存,内存文件映射,零拷贝

  为了快速构建项目,使用高性能框架是我的职责,但若不去深究底层的细节会让我失去对技术的热爱。   探究的过程是痛苦并激动的,痛苦在于完全理解甚至要十天半月甚至...

1023
来自专栏iKcamp

手把手教你撸一个 Webpack Loader

文:小 boy(沪江网校Web前端工程师) 本文原创,转载请注明作者及出处 ? 经常逛 webpack 官网的同学应该会很眼熟上面的图。正如它宣传的一样,w...

3814
来自专栏hightopo

原 Web SCADA 电力接线图工控组态

1385
来自专栏宋凯伦的技术小栈

【Angular JS】正确调用JQuery与Angular JS脚本 - 修复Warning: Tired to load angular more than once

  自己正在做一个小网站,使用Angular JS + Express JS + Mongo DB,在开发过程中,遇到一些问题,所以整理出来。希望对大家都有帮助...

1989
来自专栏Jerry的SAP技术分享

JavaScript面试系列:JavaScript设计模式之桥接模式和懒加载

Java程序员面试系列-什么是Java Marker Interface(标记接口)

642

扫码关注云+社区