前端工程化场景分析

85 阅读6分钟

前端模块化

模块化历史

  1. 最开始人们使用函数编程,但是这种方式,需要引入大量的js文件,并且致命的问题是不能使变量唯一化,会造成变量污染
  2. 之后人们开始通过命名空间的方式进行模块化开发,但是有个问题是,在外部可以访问到空间内部的变量,因为把变量挂载到了window上,这样不能保证模块内变量的唯一性
  3. 再后来人们开始使用闭包+自执行函数的方式进行模块化开发,这样既限制了外部任意修改内部的变量,有没有把变量挂载到全局,传参的话,可以在闭包里面把变量挂载到window上,要想获取模块内部的变量,只能在闭包里面书写获取的方法:例:
function module_A() {
 var salary = 10000;

 function fetchSalaryList() {
   return {
     statusCode: 200,
     data: [
       { name: "zhangsan", salary: 2000 },
       { name: "lisi", salary: 2500 },
       { name: "wangwu", salary: 3000 },
     ],
   };
 }

 function sumSalary(data) {
   var total = 0;
   data.forEach((item) => {
     total += item.salary;
   });
   return total;
 }

 return {
   // salary: salary,
   fetchSalaryList: fetchSalaryList,
   sumSalary: sumSalary,
   getS: function () { //获取属性的方法
     return salary;
   },
   setS: function (s) { //设置属性的方法
     salary = s;
   },
 };
}

var _module_a = module_A();
console.log(_module_a);
_module_a.salary = 50;//这里并不能直接修改闭包里面的变量,改的只是外部的属性
console.log(_module_a.salary);
console.log(_module_a.getS());//1000
_module_a.setS(66);
console.log(_module_a.getS());//66

通过闭包与自执行函数进行模块化

//在A文件
(function (global) {
  var name = "zhangsan";
  function getName() {
    return name;
  }
  global._module_B = { //这里把getName方法挂载在window._module_B上
    getName,
  };
})(window);
//在B文件中
(function (module_B) {
  var salary = 10000;

  function fetchSalaryList() {
    return {
      statusCode: 200,
      data: [
        { name: "zhangsan", salary: 2000 },
        { name: "lisi", salary: 2500 },
        { name: "wangwu", salary: 3000 },
      ],
    };
  }

  function sumSalary(data) {
    var total = 0;
    data.forEach((item) => {
      total += item.salary;
    });
    return total;
  }
  console.log("****salary", salary);
  const data = fetchSalaryList().data;
  const total = sumSalary(data);
  console.log("***total", total);
  console.log(module_B.getName());//在这里使用A文件的模块

  window._module_A = {
    salary,
    fetchSalaryList,
    sumSalary,
  };
})(window._module_B);//这里把window._module_B当作实参传递到这个模块中去,然后在这个模块中就可以使用
  1. 之后就是commonjs与ESModule
CommonJS特点

其原理就是闭包,是值的复制,并且只能在服务端运行,加载⽅式:同步加载 所有代码都运⾏在模块作⽤域,不会污染全局作⽤域

模块可以多次加载,但是只会在第⼀次加载时运⾏⼀次,然后运⾏结果就被缓存了,以后再加载,就直

接读取缓存结果。要想让模块再次运⾏,必须清除缓存

模块加载的顺序,按照其在代码中出现的顺序

模块输出的值是值的拷⻉:从⽽控制了数据的访问权限

3. require的内部处理流程

require命令是CommonJS规范之中,⽤来加载其他模块的命令。它其实不是⼀个全局命令,⽽是指向当

前模块的module.require命令,⽽后者⼜调⽤Node的内部命令Module._load。

Module._load = function(request, parent, isMain) {

  1. 检查 Module._cache,是否缓存之中有指定模块

/2. 如果缓存之中没有,就创建⼀个新的Module实例

  1. 将它保存到缓存

  2. 使⽤ module.load() 加载指定的模块⽂件,

读取⽂件内容之后,使⽤ module.compile() 执⾏⽂件代码

  1. 如果加载/解析过程报错,就从缓存删除该模块

  2. 返回该模块的 module.exports

};

⼀旦require函数准备完毕,整个所要加载的脚本内容,就被放到⼀个新的函数之中,这样可以避免污染

全局环境。该函数的参数包括require、module、exports,以及其他⼀些参数。

(function (exports, require, module, __filename, __dirname) {

// 你的代码被导⼊在这⾥

});
在浏览器中使⽤CommonJS(browserify介绍)
npm install browserify

browserify inputPath.js -o outputPath.j

ESModule规范详解

1. AMD规范介绍

AMD全称是Asynchronous Modules Definition异步模块定义,提供定义模块及异步加载该模块依赖的机

制,这和浏览器的异步加载模块的环境刚好适应(浏览器同步加载模块会导致性能、可⽤性、调试和跨

域访问等问题)。

AMD规范只定义了⼀个函数 "define",它是全局变量。模块通过 define 函数定义在闭包中,格式如

下:

define(id?: String, dependencies?: String[], factory: Function|Object);

//代表:require.js

2. CMD规范介绍

Common Module Definition,通⽤模块定义。

异步加载,可以像在 Node 环境中⼀样来书写模块代码。代码的书写格式如下:

define(function(require, exports, module) {

var $ = require('jquery');

exports.sayHello = function() {

$('#hello').toggle('slow');

};

});

//代表:sea.js
  1. ESModule介绍

在编译阶段确定依赖关系和输⼊输出。

export导出模块: export为普通导出、export default为默认导出

import加载模块

特点:

每⼀个模块只加载⼀次, 并执⾏⼀次,再次加载同⼀⽂件,直接从内存中读取;

每⼀个模块内声明的变量都是局部变量, 不会污染全局作⽤域;通过export导出模块,通过import导⼊模块

ES6模块只⽀持静态导⼊和导出,只可以在模块的最外层作⽤域使⽤import和export 4.支持异步导入

import("./person.js").then((mod) => {
  console.log("****mod", mod, mod === NewPerson);
});

ESModule和CommonJS的区别

image.png

npm+webpack原理

1.npm 介绍

特点:所有模块都在仓库集中管理,统一分发使用 在package.json文件里面记录模块信息,依赖关系等 通过publish命令发布到仓库 通过install进行安装

package.json

xlit2ck2oi.feishu.cn/docx/Rmn4dB…

.npmrc文件 (可以在当前项目里面覆盖掉本电脑的npm配置)

例如:在.npmrc文件里设置 registy='www.baidu.com' //这样在这个项目里就会把原本的淘宝镜像覆盖掉

2.webpack 介绍

code spilting
 code spiltting是webpack一个常用特性,就是webpack会把多种静态资源通过依赖关系转换成一个文件,从而提高用户的访问性能,但是有些你经常修改的文件,如果每次修改都要重新打包,那么将会非常不友好,所以可以通过code splitting将这个文件拆分出来。
核心概念
  Entry:webpack的入口文件,从这个文件开始查找依赖关系
  Output:输出的文件应该放在哪里
  Module:模块,webpack是由多个模块组成,会从entry递归查找出所有的模块
  Chunk:代码块,一个chunk有多个module组合而成,用于代码合并与分割,就是code splitting 分割的最小单位
  Loader:可以让webpack处理那些非javascript文件,因为webpack只能处理js文件,先把所有模块转化为webpack可以处理的模块,然后webpack就可以打包处理为了转换浏览器不支持的代码,比如es6可以转换成ES5