前端js模块化

544 阅读4分钟

模块化

所谓的模块化就是将一个复杂的程序根据一定的规则封装成几个块,块内部的数据是私有的,只用通过向外暴露一些方法或者属性的方式,供外部其他模块使用,可以避免命名冲突问题,按需加载,提高复用性和可维护性。

模块化的发展

无模块化-->IIFE--CommonJS规范-->AMD规范-->CMD规范-->ES6模块化

无模块化

多个js文件被分到不同的文件中,并且同一模板中可以引用不同的文件

//在一个html文件中引入
<script src="dep1.js"></script>
<script src="dep2.js"></script>
<script src="dep3.js"></script>

缺点:污染全局作用域不利于多人开发

IIFE(自执行函数 )

利用函数的作用域实现简单的模块化

let testdata= (function(data){
  var test='测试数据'
   return {
   test:test
   }
 
 })(参数)
//使用
testdata.test//值为测试数据

commonjs

cjs由node进行制定通常用于服务端,通过module和export对外暴露模块中的方法或者属性,其他模块则通过require的方式去引入和使用对应模块的属性或者方法。

var data={name:'我是测试数据'}
var name='name'
var age=12
module.exports=data
exports.name=name
exports.age=age

//引入
var data =require('./main.js)
var {name,age}=require('./main.js')

优点:commonjs首先在服务端实现,从架构的层间解决了全局变量污染的问题 缺点:主要是针对于服务器端 并不适用于浏览器端(commonjs对于异步执行不那么的友好,因为服务器端是同步执行的 读取文件都存在于云盘或者磁盘中的,对于异步读取的需求不是那么的大)

AMD

主要是通过异步加载和允许访问回调的方式实现模块化

//定义
//通过define函数进行实现  define函数有三个参数  模块名,模块依赖,和模块的实现,并且模块名和模块依赖可以省略,当模块名称省略后就形成了匿名模块 程序默认指定文件名为该匿名模块的名称
define([moduleName],[[dep1,dep2,dep3]],callback/object){
//模块实现

return{
 ///对外暴露方法或者变量
}
}

//使用
require(['模块名称'],function(data){
//引入模块成功的回调
})

///在amd中兼容同步代码
define(function(require){
var data=require('./dep')
var data2=require('./dep2')
、、、
return {
//对外暴露
}

})


UMD (兼容amd和cmj)

umd是一种兼容amd规范和cmj规范的通用规范,其原理就是通过判断全局变量module和define是否满足条件实现,如果存在module就是cmj规范 否则就是amd规范

(function(window, factory){
    if(typeof module === "objects"&&module.exports&&typeof define === "function"){
        // commonjs
        module.exports = factory();
    }else if(typeof define === "function" && define.amd){
        // amd
        define(factory);
    }else{
        window.moduleA = factory();
    }
})(window, function(){
    // 返回module
    let modlueA = {
        name : "张三",
        setName(newName){
            thie.name = newName;
        },
        getName(){
            return this.name;
        }
    }
    return modlueA;
})


cmd(按需加载,主要应用的框架 sea.js)

 define('module', function(require, exports, module) {
    let $ = require('jquery');
    // jquery相关逻辑

    let dependencyModule1 = require('./dependecyModule1');
    // dependencyModule1相关逻辑
  })

优点:按需加载 依赖就近 缺点:依赖于打包 加载逻辑存在每个模块中,扩大了模块的体积

es6模块化

通过export进行方法和变量的导出通过import进行导入

//引入
import data from './test.js'
import {name,age} from '.test1.js'
//导出
var userinfo={
   name:'测试'
   age:12
}
var age=12
//导出函数
export functon test(){

}
export userinfo
export default =age

模板引入

<script type="module" src='./main.js'></script>

node中使用的话

import {test,data} form '`./main.mjs`'

动态模块加载

  1. export promise
new Promise((resolve)=>{
import data from './test'
    resolve(data)
}).then(data=>{
进行异步的加载
})

2.Es11最新的规范

import('./test.js').then(data){
//data是加载后的数据
}

优点:通过一种最统一的形态整合了js的模块化 缺点:本质上还是运行时的依赖分析

前端工程化(新的解决模块化的思路)

工程化的方案 grunt gulp webpack 工程化的实现(原理)

//code
require.config(依赖标识)

define('a',['e'],()=>{
 ///引入其他模块
 let b = require('b')
 let c=require('c')
})

1.通过预编译的方式扫描程序中模块的依赖关系生成依赖关系表

{
a:['e','b','c']
}

2.重新生成依赖数据模板

//code
require.config({
deps:{
a:['e','b','c']
}
})

define('a',['e'],()=>{
 ///引入其他模块
 let b = require('b')
 let c=require('c')
})

3.执行工具采用模块化的方式解决模块化处理依赖

///amd方式
define('a',['e','b','c'],function(data1,data2,data3){
//逻辑代码
export.run =function(){}
}

)

优点:构建生成配置,运行时执行,最终转化成处理依赖,可以拓展