js-模块化

1.什么是模块化:

将一个复杂的程序依据一定的规则(规范)封装成几个块(文件),并进行组合在一起块的内部数据/实现是私有的,只是向外部暴露一些接口(方法)与外部其他模块通信。

发展过程:

(1)无模块化(最早是将所有的js代码写在一个文件里,这样不利于阅读查找)

script标签引入文件,相互罗列,但是被依赖的放在前面,否则使用就会报错。

<script src="jquery.js"></script>
<script src="jquery_scroller.js"></script>
<script src="main.js"></script>
<script src="other1.js"></script>
<script src="other2.js"></script>
<script src="other3.js"></script>

即简单的将所有的js文件统统放在一起,但是这些文件的顺序还不能出错,比如jquery需要先引入,才能引入jquery插件,才能在其他的文件中使用jquery。

缺点:

  • 污染全局作用域(global)
  • 很容易命名冲突
  • 维护成本高
  • 依赖关系不明显
  • 耦合度高(关联性强,不利于后期维护)(什么是耦合?‘高耦合,牵一发而动全身’:‘低耦合,关联性相对低’)

(2)简单封装:Namespace模式(把一部分数据放在对象里,通过对象.属性操作数据)

     特点:减少了global上的变量的数目,但本质是对象,不安全。

(3)匿名闭包:IIFE模式(把一些逻辑写到立即执行函数里),这样相对安全。

(4)引入依赖

2.为什要模块化

(1)降低复杂度,降低耦合性,减少他们之间的复杂性。

(2)部署方便(比如js1模块专门正对轮播图,但这个版块没有轮播图,则这个版块完全没有必要引入)。

3.模块化的好处

(1)避免命名冲突(减少命名空间的污染)

(2)更好的分离,按需加载

(3)更高的复用性

(4)高可维护性(方便维护)

4.页面引入加载script

但现实是:

要加载多个script标签,要发起多次请求,依赖模糊,难以维护。

5.规范

CommonJS(node根据该规范编写)

规范:

(1)每一个文件都可以当做一个模块

(2)在服务器端:模块的加载是运行时同步(会等待,阻塞)加载的

(3)在浏览器端:模块需要提前编译打包处理

语法:

  引入模块:require(xxx)

    第三方模块:xxx为模块名

    自定义模块:xxx为模块文件路径

  暴露模块:

    (1)module.exports=value(exoports本身是一个空对象,module.exports=value是给exports对象添加属性或方法。)

    (2)exports.xxx=value(exports.xxx=value是直接用value新对象来覆盖原来的空对象。)

        module.exports={foo:'bar'};//true

        module.exports.foo='bar';//true

        export={foo:'bar'};//false,相当于重新依赖了exports

    问题:暴露的模块本质是    exports对象

优点:

  • 解决了依赖、全局变量污染的问题

缺点:

  • CommonJS用同步的方式加载模块。在服务端,模块文件都存在本地磁盘,读取非常快,所以这样做不会有问题。但是在浏览器端,限于网络原因,CommonJS不适合浏览器端模块加载,更合理的方案是使用异步加载,比如下边AMD规范。

AMD(专门用于浏览器端的模块化规范,模块的加载是异步的,实现(浏览器端):Require.js)

 语法:

  定义暴露模块:

    //定义没有依赖的模块:

    define(function(){

      return 模块

    })

    //定义有依赖的模块:

    define(['module1','module2'],function(m1,m2){

      return 模块

    })

区别于CommonJS,AMD规范的 被依赖模块是异步加载的,而定义的模块是被当作回调函数来执行的,依赖于require.js模块管理工具库。当然,AMD规范不是采用 匿名函数自调用的方式来封装,我们依然可以利用闭包的原理来实现模块的私有成员和公有成员:
define(['module1', 'module2'], function(m1, m2) {
    let x = 1;
    function add() {
        x += 1;
        return x;
    }
    return { add };
})

  引入使用模块:

    require(['module1','module2'],function(m1,m2){

      使用 m1/m2

    })

CMD(专门用于浏览器端,模块的加载是异步的,模块使用时才会加载执行,实现(浏览器端):Sea.js

AMD 推崇依赖前置,CMD 推崇依赖就近。

CMD集成了CommonJS和AMD的的特点,支持同步和异步加载模块。CMD加载完某个依赖模块后并不执行,只是下载而已,在所有依赖模块加载完成后进入主逻辑,遇到require语句的时候才执行对应的模块,这样模块的执行顺序和书写顺序是完全一致的。因此,在CMD中 require函数同步加载模块时没有HTTP请求过程。

 基本语法:

  //定义没有依赖的模块:

  define(function(require,exports,module){

    exports.xxx = value

    module.exports = value

  })

  //定义有依赖的模块:

  define(function(require,exports,module){

    //引入依赖模块(同步)

    var module2 = require("./module2")

    //引入依赖模块(异步)

    require.async("./module3",function(m3){

    })

    //暴露模块

    exports.xxx = value

  })

  引入使用模块:

   require(function(require){

    var m1 = require('./module1')

    var m4 = require('./module4')

    m1.show()

    m4.show()

  })

ES6

说明:依赖模块需要编译打包处理;

特点:

  • 模块化规范输出的是一个值的拷贝,ES6模块输出的是值的引用。
  • 模块化规范是运行时加载,ES6模块是编译时输出接口。

语法:

  (1)导出模块:export xxx

  (2)引入模块:import xxx from "url"

实现(浏览器端):

  (1)使用Babel将ES6编译为ES5(有些浏览器不兼容ES6)

  (2)使用Browserify编译打包js


ES6模块化详:

1.首先页面引用,需要在<script></script>里添加type='module'

2.导出模块分为默认导出和指定导出

        let name ='wangwu';
        let age=18;
        let module1 =function(){
            console.log(`你好,我是$(name),今年$(age)岁了。`)
        };
        export default module1;//默认导出
        export {name,age,modue1}//指定导出

 两者的区别主要在于引用的时候:

  • 如果是用默认导出export default,接收的时候可以使用任意名字来接收,如下所示,导出的叫module1,被接收的名字叫x:
import x from 'js/module1.js';
  • 如果要一次导出多个,需要用 { } 包裹,接收的时必须与导出时同名:
import {name,age,module1} from 'js/module1.js';
  • 如果一个模块既有默认导出,又有指定导出
export default module1;//默认导出
export {name,age,module1};//指定导出

    接收时可以使用:

import x,{name,age,module1} from 'js/module1.js';//默认导出的依然可以随意命名

3.如果是同级目录,必须添加./

如果有两个模块里导出的内容是一样的,此时console.log,就会报错

import x,{name,age,module1} from './module1.js';
import y,{name,age,module1} from './module2.js';
console.log(name);//此时module1,module2都导出了name报错
import x,{name as name1} from './module1.js';
import y,{name as name2} from './module2.js';
//as相当于重命名
console.log(name1);
console.log(name2);

 4.只有import会只执行代码,不会执行函数

  有一个新的module.js:

console.log('这是module3');
function x(){
  console.log('这是module3里面的函数');
}

   然后在main.js里面直接引用:

impotr './module3.js';

   可以看到只打印了'这是module3',而函数没有执行。

5.集体导入

  如果模块里导出的东西太多,又不想引用的时候一个一个的写出来,可以这样:

import * as x from './module1.js';//这里的*可理解为css里的通配符,意思是所有

   此时打印出x,可以看到

 module1.js里面导出的都在x下了,现在可以用x.age(),x.module1()来调用。


ES6-Babel-Browserify使用教程

1.定义package.json文件

{
  "name":"es6-babel-browserify",
  "version":"1.0.0"   
}

2.安装babel-cli,babel-preset-es2015和browserify(cli:command line interface)

* npm install babel-cli browserify -g (babel全局安装)

* npm install babel-preset-es2015 --save-dev 

* preset 预设(将es6转换成es5的所有插件打包)

3.定义 .babelrc 文件(rc:run control 运行时控制文件,就是运行时要读的文件)

{
  "presets":["es2015"]  
}
...

 4.编码

* js/src/module1.js  分别暴露

 参考引用原文:https://www.jianshu.com/p/ae4e566212ff

 参考引用原文:https://www.imooc.com/article/43781?block_id=tuijian_wz

 参考引用原文:https://www.jianshu.com/p/2be0e7956854

猜你喜欢

转载自www.cnblogs.com/czh64/p/11950819.html