模块化开发
一、模块化的优缺点
优点:
相比于使用一个js文件,这种多个js文件实现最简单的模块化的思想是进步的。
缺点:
污染全局作用域。 因为每一个模块都是暴露在全局的,简单的使用,会导致全局变量命名冲突,当然,我们也可以使用命名空间的方式来解决。 对于大型项目,各种js很多,开发人员必须手动解决模块和代码库的依赖关系,后期维护成本较高。 依赖关系不明显,不利于维护。
二、模块化规范
1、一个文件就是一个模块
2、每个模块都有单独的作用域
3、通过module.exports导出成员
4、通过require函数载入模块
三、commonJS
CommonJS 加载模块是同步的,所以只有加载完成才能执行后面的操作。像Node.js主要用于服务器的编程,加载的模块文件一般都已经存在本地硬盘,所以加载起来比较快,不用考虑异步加载的方式,所以CommonJS规范比较适用。但如果是浏览器环境,要从服务器加载模块,这是就必须采用异步模式。所以就有了 AMD CMD 解决方案。
1、AMD
AMD即ASynchronous Module Definition ,异步模块定义,它是一个在浏览器端模块化开发的规范,define是定义模块,require是调用模块。
define("alpha", ["require", "exports", "beta"], function (require, exports, beta) { exports.verb = function() { return beta.verb(); //或者: return require("beta").verb(); }});
优点:
适合在浏览器环境中异步加载模块。可以并行加载多个模块。
缺点:
AMD使用起来相对复杂,js频繁引用,提高了开发成本,并且不能按需加载,而是必须提前加载所有的依赖。
2、CMD
CMD是SeaJS 在推广过程中对模块定义的规范化产出。
CMD和AMD的区别有以下几点:
1.对于依赖的模块AMD是提前执行,CMD是延迟执行。不过RequireJS从2.0开始,也改成可以延迟执行(根据写法不同,处理方式不通过)。
2.AMD推崇依赖前置(在定义模块的时候就要声明其依赖的模块),CMD推崇依赖就近(只有在用到某个模块的时候再去require——按需加载)。
//AMDdefine(['./a','./b'], function (a, b) { //依赖一开始就写好 a.test(); b.test();});//CMDdefine(function (requie, exports, module) { //依赖可以就近书写 var a = require('./a'); a.test(); ... //软依赖 if (status) { var b = requie('./b'); b.test(); }});
四、ES Modules
一、基本特性
通过给script 添加type = modeule的属性,就可以ES Module 的标准执行其中的Js代码了
1、自动采用严格模式
2、每个ES Module都是运行在单独的私有作用域中
3、ESM 是通过 CORS 的方式请求外部的 JS 模块的
附加:CORS
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。
CORS与JSONP的使用目的相同,但是比JSONP更强大。
JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
4、ESM 的 script 标签会延迟执行脚本
网页渲染完成后再执行script标签
二、ES Modules 导入和导出
//html//app.js// import {default as nameNew} from './module.js'// import {fooHello} from './module.js'import {hello2} from './module.js'import aaa from './module.js' //默认导出// console.log(fooHello())console.log(hello2())console.log(aaa)//module.jsvar name = "123";export function hello() { console.log('hello')}export const hello2 = () =>{ console.log('hello2')}class Person{}// export{// name as default, //default 默认导出// hello as fooHello,// hello2// }export default name //默认导出,aaa
注意点:
1、导入的成员是只读成员,不能修改,虽然声明的时候用var 声明的(项目中可以应用到配置文件中)
2、导出的时候不是字面量对象,导入的时候也不是结构赋值
3、导出的并不是导出成员的值,而是地址,而不是值
附加:browser-sync 的用法
1、yarn init
2、yarn add browser-sync --dev
3、进入项目的根目录运行browser-sync start --server --files '**/*.js',提示browser-sync 不是内部或外部命令
原因:node只有--global or -g才是全局安装的包,一般才会出现在PATH里,才可以直接运行命令,否则就在./node_modules/.bin/, 需要加上路径才可以运行,也可以在package.json里添加入口,这个入口的环境变量默认包含node_modules/.bin, 可以直接
1. "scripts": {2. "watch": "browser-sync start --server --files '**/*.js'" //监视的js文件3. }
运行: npm run watch
三、ES Modules 浏览器环境Polyfill
https://unpkg.com/browse/[email protected]/dist/
Polyfill 是一块代码(通常是 Web 上的 JavaScript),用来为旧浏览器提供它没有原生支持的较新的功能。
这种引用IE不识别
开发阶段的话,可以通过转为es5的语法,nomodule 是用于,在识别type= modeule的浏览器,如果识别就不加在这些js.
四、ES Modules 在node环境中的使用
1、在node环境中的使用
第一,需要将.js的文件改为.mjs文件
第二,启动时需要额外添加 `--experimental-modules` 参数; node --experimental-modules index.mjs
//可以加载内置模块import fs from 'fs'fs.writeFileSync('./bar.txt','nohaoya')//支持,可以加载内置模块的成员import {writeFileSync} from 'fs'writeFileSync('./b.txt','nohaoya')//支持,支持导入第三方插件,yarn add lodash// import _ from 'lodash'// console.log(_.camelCase('ES Module')) //驼峰命名法//不支持,第三方的插件,都是支持默认导出的,不能导出其中的某个方法// import {camelCase} from 'lodash'// console.log(camelCase('es module'))
第三,ES Module 中引入 Common.js
common.js中始终只可以导出一个默认成员,
module.exports = {
foo: 'common.js'
}
exports.foo = 'common.js'
两种写法都可以
ES Module中可以导入Common.js,
import mod from './common.js'
//不能直接提取成员,
import { foo } from './common.js'
第四、在原生node环境中,common.js不能通过require的方式载入ES module
const mod = require('./module.js') //错误
总结:ES Modules中可以导入 CommonJS
Common.js中不能导入 ES Modules
CommonJS始终只会导出一个默认对象
注意import不是解构导出对象
第五、新版node
在新版的node环境中,需要在package.json中添加{type: "module"},就可以不用把文件名改为.mjs
commonjs使用,可以把.js改为.cjs
第六、使用babel
yarn add @babel/node @babel/core @babel/preset-env --dev
yarn babel-node index.js --presets=@babel/preset-env
(如果不想用参数,可以在文件中添加 .babelrc 的文件,{"preset" : ["@babel/preset-env"]},
运行yarn babel-node index.js
如果不用preset,可以yarn add @babel/plugin-transform-modules-connomjs,{"plugin" : ["@babel/plugin-transform-modules-connomjs"]},