requireJS 实战
require 实战
下面我将化整为零的去讲解requireJS在一个项目的具体使用方式以及需要注意的事项。
引入requireJS
通过 <script> 标签,将require.js 文件引入到当前的 HTML 页面中
参数配置
requireJS 常用的方法与命令也就两个, 因此requireJS使用起来非常简单。
require
define
其中define是用于定义模块, 而require是用于载入模块以及载入配置文件。
在requireJS中一个文件就是一个模块, 并且文件名就是该模块的ID, 其表现则是以key/value的键值对格式, key即模块的名称(模块ID), 而value则是文件(模块)的地址,
因此多个模块便有多个键值对值, 这些键值对再加上一些常用的参数, 便是require的配置参数, 这些配置参数我们通常会单独保存在一个JS文件中, 方便以后修改、调用, 所以这个文件我们也称之为\”配置文件\”。
下面是requireJS的基本参数配置:
require.config() 是用于配置参数的核心方法, 它接收一个有固定格式与属性的对象作为参数, 这个对象便是我们的配置对象。
在配置对象中 baseUrl 定义了基准目录, 它会与 paths中模块的地址自动进行拼接, 构成该模块的实际地址, 并且当配置参数是通过script标签嵌入到html文件中时, baseUrl默认的指向路径就是该html文件所处的地址。
paths 属性的值也是一个对象, 该对象保存的就是模块key/value值。其中key便是模块的名称与ID, 一般使用文件名来命名, 而value则是模块的地址, 在requireJS中, 当模块是一个JS文件时,
是可以省略 .js 的扩展名, 比如 \”index.js\” 就可以直接写成 \”index\” 而当定义的模块不需要与 baseUrl 的值进行拼接时, 可以通过 \”/\” 与 http:// 以及 .js 的形式来绕过 baseUrl的设定。
示例:
实际上, 除了可以在require.js加载完毕后, 通过require.config()方法去配置参数, 我们也可以在require.js加载之前, 定义一个全局的对象变量 require 来事先定义配置参数。
然后在require.js被浏览器加载完毕后, 便会自动继承之前配置的参数。
不论是在require.js加载之前定义配置参数, 还是之后来定义, 这都是看看我们需求而言的, 这里我们举例的配置参数都是放入到script标签中, 然后嵌入到HTML页面的内嵌方式,
在实际使用时, 我们更多的则是将该段配置提取出来单独保存在一个文件中, 并将其取名为 app.js, 而这个 app.js 便是我们后面常说到的配置文件。
另外还有一个\”接口文件\”的概念, requireJS中, 所谓接口文件指的便是require.js加载完毕后第一个加载的模块文件。
加载配置文件
现在我们知道require的配置有两种加载方式,一种是放入到script标签嵌入到html文件中,另一种则是作为配置文件 app.js 来独立的引入。
独立的引入配置文件也有两种方式,一种是通过script标签加载外部JS文件形式:
另一种方式则是使用 require 提供的 data-main 属性, 该属性是直接写在引入require.js的script标签上, 在require.js 加载完毕时, 会自动去加载配置文件 app.js。
通过 data-main 去加载入口文件, 便会使配置对象中的 baseUrl 属性默认指向地址改为 app.js 所在的位置, 相比之下我更加推荐这种方式, 因为它更可能的方便快捷。
当我们的项目足够的庞大时, 我也会推荐将入口文件作为一个普通的模块, 然后在这个模块中, 根据业务的不同再去加载不同的配置文件。
定义模块
在我们选择requireJS来模块化开发我们的项目或者页面时, 就要明确的知道我们以后所编写的代码或者是某段功能, 都是要放在一个个定义好的模块中。
下面是requireJS定义模块的方法格式:
define([id,deps,] callback);
ID:模块的ID, 默认的便是文件名, 一般无需使用者自己手动指定。
deps:当前模块所以依赖的模块数组, 数组的每个数组元素便是模块名或者叫模块ID。
callback:模块的回调方法, 用于保存模块具体的功能与代码, 而这个回调函数又接收一个或者多个参数, 这些参数会与模块数组的每个数组元素一一对应, 即每个参数保存的是对应模块返回值。
根据 define() 使用时参数数量的不同, 可以定义以下几种模块类型:
简单的值对
当所要定义的模块没有任何依赖也不具有任何的功能, 只是单纯的返回一组键值对形式的数据时, 便可以直接将要返回的数据对象写在 define方法中:
这种只为保存数据的模块,我们称之为“值对”模块,实际上值对模块不仅可以用于保存数据,还可以保存我们的配置参数,然后在不同的业务场景下去加载不同的配置参数文件。
示例:
非依赖的函数式定义
如果一个模块没有任何的依赖, 只是单纯的执行一些操作, 那么便可以直接将函数写在 define方法中:
依赖的函数式定义
这种带有依赖的函数式模块定义, 也是我们平时常用到的, 这里我们就结合实例, 通过上面所举的 index 模块为例:
从上面的示例中我们可以看出 index 模块中, 依赖了 \’jquery\’ 模块, 并且在模块的回调函数中, 通过 $ 形参来接收 jquery模块返回的值, 除了 jquery 模块, index模块还依赖了 utils 模块,
因为该模块没有在配置文件中定义, 所以这里以附加路径的形式单独引入进来的。
载入模块
在说载入模块之前, 我们先聊聊\”模块依赖\”。模块与模块之间存在着相互依赖的关系, 因此就决定了不同的加载顺序, 比如模块A中使用到的一个函数是定义在模块B中的,
我们就可以说模块A依赖模块B, 同时也说明了在载入模块时, 其顺序也是先模块A, 再模块B。
在require中, 我们可以通过 require() 方法去载入模块。其使用格式如下:
require(deps[,callback]);
deps:所要载入的模块数组。
callback:模块载入后执行的回调方法。
这里就让我们依然使用上述的 index 模块为例来说明
示例:
requireJS 通过 require([]) 方法去载入模块, 并执行模块中的回调函数, 其值是一个数组, 数组中的元素便是要载入的模块名称也就是模块ID,
这里我们通过 require([\’index\’]) 方法载入了 index 这个模块, 又因为该模块依赖了 jquery 模块, 所以接着便会继续载入jquery模块, 当jquery模块加载完成后,
便会将自身的方法传递给形参 $ 最后执行模块的回调方法, alert出$参数具体内容。
这里我们可以小小的总结一下,实现模块的载入除了 require([],fn) 的主动载入方法,通过依赖也可以间接载入对应的模块,但是相比较而言require方式载入模块在使用上更加灵活,它不仅可以只载入模块不执行回调,也可以载入模块然后执行回调,还可以在所定义的模块中,按需载入所需要用到的模块,并且将模块返回的对象或方法中保存在一个变量中,以供使用。
这种按需载入模块, 也叫就近依赖模式, 它的使用要遵循一定的使用场景:
当模块是非依赖的函数式时,可以直接使用
当模块是具有依赖的函数式时, 只能够以回调的形式处理。
当然聪明伶俐的你, 一定会想到这样更好的办法:
模块的返回值
require中定义的模块不仅可以返回一个对象作为结果,还可以返回一个函数作为结果。实现模块的返回值主要有两种方法:
return 方式
如果通过return 返回多种结果的情况下:
exports导出
这里有一个注意的地方, 那就是非依赖性的模块, 可以直接在模块的回调函数中, 加入以下三个参数:
require:加载模块时使用。
exports:导出模块的返回值。
modules:定义模块的相关信息以及参数。
非标准模块定义
在 require.config() 方法的配置对象中有一个 shim 属性, 它的值是一个对象, 可以用于声明非标准模块的依赖和返回值。
所谓的 \”非标准模块\” 指的是那些不符合的AMD规范的JS插件。
下面我们先看看基本的 shim 配置参数:
这里需要注意的是如果所加载的模块文件是符合AMD规范, 比如通过 define 进行定义的, 那么require默认的优先级将是标准的, 只有在不符合标准的情况下才会采用shim中定义的参数。
在 index 模块执行时:
上面的示例中, 关于 shim 中有三个重要的属性, 它们分别是:
deps: 用于声明当前非标准模块所依赖的其它模块, 值是一个数组, 数组元素是模块的名称或者是ID。
exports:用于定义非标准模块的全局变量或者方法。值一般是一个字符串。
init:用于初始, 处理, 非标准模块的全局变量或者是方法, 常用于当非标准模块存在多个全局变量以及方法, 值是一个函数。
常用参数
在 require.config 中还存在其他的常用属性设置。
urlArgs
RequireJS获取资源时附加在URL后面的额外的query参数。作为浏览器或服务器未正确配置时的“cache bust”手段很有用。使用cache bust配置的一个示例:
javascript:;urlArgs: \”bust=\” + (new Date()).getTime()
在开发中这很有用,但请记得在部署到生成环境之前移除它。
scriptType
指定RequireJS将script标签插入document时所用的type=\”\”值。默认为“text/javascript”。想要启用Firefox的JavaScript 1.8特性,可使用值“text/javascript;version=1.8”。
waitSeconds
通过该参数可以设置requireJS在加载脚本时的超时时间,它的默认值是7,即如果一个脚本文件加载时长超过7秒钟,便会放弃等待该脚本文件,从而报出timeout超时的错误信息,考虑到国内网络环境不稳定的因素,所以这里我建议设置为0。当然一般不需要去改动它,除非到了你需要的时候。
deps
用于声明require.js在加载完成时便会自动加载的模块, 值是一个数组, 数组元素便是模块名。
callback
当deps中的自动加载模块加载完毕时,触发的回调函数。
config
config属性可以为模块配置额外的参数设定,其使用格式就是以模块名或者模块ID为key,然后具体的参数为value。
这里要引起我们注意的地方就是依赖的\’module\’模块, 它是一个预先定义好的值, 引入该值, 在当前模块下便可以调用module对象, 从该对象中执行 config() 方法便可以生成改模块的参数对象。
实战:
app.js为入口函数, 其代码为:
实战2:
文件功能
require.js:
其中requirejs的核心代码就是require.js文件,可以从官方网站上下载:
config.js:
用于配置requirejs的相关内容,可以设置文件目录,加载模块命名匹配,以及一些依赖关系等等。
index.html:
我们的测试页面或者网址首页。
/lib/a.js和/lib/b.js 以及 /others/c.js
是测试的模块js文件。
index.html
其中, data-main指定主要的配置文件, src为requirejs的文件。
baseUrl指定所有文件的主要目录, paths配置模块名字以及其匹配的加载路径。
当有需要使用某些模块时, 就可以通过require([xxx],function(xxx){xxx});的方式使用。
模块化文件, 一般是一个功能是一个文件。文件的名字, 就是上面requireconfig中配置的模块名字。require加载文件时, 会自动加上.js后缀。
当某些模块依赖其他模块时, 可以通过define([xxx],function(xxx){yyy});的方式添加依赖关系, require会在异步加载后, 自动调整次序。
当访问index.html时, 会先加载require.js然后把需要加载的文件都通过appendChild的方式, 添加到index.html的底部。
因此会先弹出index对话框, 当执行config.js的后半部分代码时, 会依次使用a.js b.js c.js, 因此也会依次弹出三个对话框。
了解 JS 的加载顺序和方式,实现 Ready 方法
页面加载 JS 顺序或方式不同,可能会导致功能失效、错误的产生或加载解析时间过长,拖慢整个页面展示。
了解页面元素的加载顺序,找到 JS 执行失败原因。有时明明没有问的代码,可就获取不到元素值或信息,这可能是JS执行时间过早或过晚,而导致的 JS 执行失败。我们来看一幅图,了解defer、async属性对JS下载、执行顺序的影响
- 默认情况下,浏览器解析到JS文件就会立即下载文件,并执行文件,JS提前执行,获取不到页面元素,并导致页面解析中断,拖慢整个页面的加载。
- 添加async属性后,就会异步下载JS文件并执行,执行时间不可控,JS执行时间过早或过晚,而导致的 JS 执行错误或失败。
- 添加defer属性后,就会异步下载JS文件,等页面解析完成后再执行JS
很多时候我们不把JS放在head中,而把JS放到body的最后面也就很好解释了?
- 首先,JS的下载和执行会中断页面的解析,拖慢整个页面展示,
- 然后,放在head中,页面元素还没有加载,JS方法就无法获取或处理页面上的元素,这一点很容易忽视,
- 但是,我们还有ready方法。
jQuery中的ready方法会在页面解析后运行,语法如下:
自定义方法,通过监听DOMContentLoaded实现ready方法
当然还有我的onload方法,可以在页面完成所有加载后再执行
可以看到,JS的执行顺序决定着程序是否正常工作。加载过早,可能无法获取到页面元素,而太晚,页面元素无法交互。对于执行的顺序,要以当前的程序功能而定。
网站:https://ichochy.com/
源文:https://ichochy.com/posts/20200807/
本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com
文章为作者独立观点不代本网立场,未经允许不得转载。