AMD规范
传统HTML文件的引入问题
一个HTML页面中,如果引用了多个js文件会发生什么样的事情?
比如我们下面的HTML文件中,引入了1.js和2.js,这两个文件中同时都设置了fun函数,那么此时执行哪一个?
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script src="./1.js"></script> <script src="./2.js"></script> <script> console.log(fun()) </script> </body> </html>
此时的输出结果为2.js文件,那是因为2.js是在1.js后面引入的
第一种方法,使用IIFE来解决
//1.js
(function fun(){
console.log("1.js文件")
})()
//2.js
(function fun(){
console.log("2.js文件")
})()
但是此时会报错
此时作用域隔离了,但是此时的全局变量也没了,如果想在html文件中执行fun函数怎么办?
将该变量或者函数设置为全局变量也就是给window对象设置属性
比如我们给1.js设置全局属性fun
(function () { window.fun=function fun(){ console.log("1.js文件") } })()
此时无论文件的引入顺序如何,结果都是输出1.js文件,因为只有一个全局的变量
但是IIFE隔离作用域也是有问题的,比如代码冗余的情况
比如1.js和2.js文件中,2.js想要引入1文件的函数(这个函数不是全局函数),不可以,必须自己单独再写一个,会造成代码冗余的情况
AMD规范-require.js
AMD全称Asynchronous Module Definition(异步模块定义),代表库就是require.js
我们可以查看阮一峰老师的博客:http://www.ruanyifeng.com/blog/2012/11/require_js.html
require.js的下载地址:https://requirejs.org/docs/release/2.3.6/minified/require.js
直接复制代码,自己新建一个require.js
require.js的基本使用
在index.html中引入
<!-- data-main属性的作用是,指定网页程序的主模块。 --> <script src="./js/require.js" data-main="./js/main.js"></script>
data-main属性的作用是,指定网页程序的主模块。
此时会输出
我们看一下如何进行进行暴露和引用的
我们在新建两个文件yuan.js和ju.js
yuan.js文件
define(function () { function mianji(r) { return Math.PI * r * r } return { mianji } })
ju.js文件
define(function () { function mianji(a,b) { return a * b } return { mianji } })
我们可以看到,yuan.js和ju.js用define()来包裹一个函数,我们称之为AMD的外壳,define是require.js封装的方法,返回的是一个对象,对象内部是暴露出去的内容
再来看main.js文件
require(['yuan', 'ju'], function (yuan, ju) { console.log(yuan.mianji(1)) console.log(ju.mianji(1,2)) })
main.js需要使用require方法来获取依赖文件,然后将依赖文件注入到回调函数当中,我们叫做“依赖注入”,通过这种方式来引入模块/文件
此时我们可以看到浏览器中输出:
注意:
- 依赖的名称是文件名,不需要加后缀名,并且需要加引号,注入的时候不需要加引号
- 注入的名字可以不用依赖的名字,是按照引入顺序去进行注入的
require(['yuan', 'fang'], function (x, y) { console.log(x.mianji(1)) console.log(y.mianji(1,2)) })
这两种引入的方法是等价的
别名的使用
我们看上面的代码的引入是没有路径的,那是因为我们是同目录引入,此时我们将yuan.js和ju.js的目录修位置,放到outer的文件夹内部
此时的依赖引入
require(['../outer/yuan', '../outer/ju'], function (yuan, ju) { console.log(yuan.mianji(1)) console.log(ju.mianji(1,2)) })
此时会发现引入是通过文件路径的,但是这样会影响代码的审查效率(美观性)和引入性
我们可以给路径设置别名
使用require对象的config方法类配置paths项
require.config({ paths: { "yuan": "../outer/yuan", "ju": "../outer/ju" } }) require(['yuan', 'ju'], function (yuan, ju) { console.log(yuan.mianji(1)) console.log(ju.mianji(1,2)) })
设置暴露口
比如我们想要引入第三方库,这个库如果要引入并使用,必须要有AMD规范的标准暴露口,如果没有的话,引入进来不能使用
require.config({ paths: { "yuan": "../outer/yuan", "ju": "../outer/ju", "jq": "./jquery", }, }) require(['yuan', 'ju', "jq"], function (x, y, $) { $("#div").css("background-color","blue") })
报错的原因是因为jquery的这个版本是不符合AMD规范的,没有对define方法做适配
解决办法是加shim
require.config({ paths: { "yuan": "../outer/yuan", "ju": "../outer/ju", "jq": "./jquery", }, shim: { "jq": { exports: "$", }, } }) require(['yuan', 'ju', "jq"], function (x, y, $) { $("#div").css("background-color","blue") })
shim是增加一个暴露口,目的是为了让不支持AMD规范的文件库,能够在AMD中运行
如果有多个依赖项,可以在shim中继续配置
require.config({ paths: { "yuan": "../outer/yuan", "ju": "../outer/ju", "jq": "./jquery", "jqu":"./jquery-ui" }, shim: { "jq": { exports: "$", }, "jqu":{ deps:["jq"] } } }) require(['yuan', 'ju', "jq","jqu"], function (x, y, $,jqu) { $("#div").css("background-color","blue") $("#div").draggable() })
上面的代码表示jquery-ui本身要依赖jquery,所以我们要配置deps
注意:require.config()接受一个配置对象,这个对象除了有前面说过的paths属性之外,还有一个shim属性,专门用来配置不兼容的模块。具体来说,每个模块要定义;exports值(输出的变量名),表明这个模块外部调用时的名称;deps数组,表明该模块的依赖性。