前端模块化

文献 www.manongjc.com/detail/51-l…

js加载机制(假设总是本地脚本index.js下载更快)

  • index.js
try {
    console.log(_.VERSION);
} catch (error) {
    console.log('Lodash Not Available');
}
console.log(document.body ? 'YES' : 'NO');
示例1
  • index.html
<head>
  <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.min.js"></script>
  <script src="./index.js"></script>
</head>

<body>
</body>
  • 控台结果
4.17.10
NO
  • 结果说明:浏览器加载脚本是采用同步模型的,都会阻塞浏览器的解析器,位于该 script 标签以上的 DOM 元素是可用的,位于其以下的 DOM 元素不可用
示例2 script上加上async属性
  • index.html
<head>
  <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.min.js" async></script>
  <script src="./index.js" async></script>
</head>

<body>
</body>
  • 控台结果
Lodash Not Available
YES
  • 结果说明:async不会阻塞 HTML 解析器, async 脚本每个都会在下载完成后立即执行,无关 script 标签出现的顺序
示例3 script上加上defer属性
  • index.html
<head>
  <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.min.js" defer></script>
  <script src="./index.js defer></script>
</head>

<body>
</body>
  • 控台结果
4.17.10
YES
  • 结果说明:defer不会阻塞 HTML 解析器, – defer 脚本会根据 script 标签顺序先后执行

模块化之前 变量污染,请求多个script脚本性能浪费,依赖模糊

// 全局定义变量 容易变量污染
function foo(){
}
function bar(){
}
// Namespace模式: 减少全局变量数量,但还是存在污染问题,还是可以通过对象改写数据,不安全
var myUtils = {
  foo:function(){},
  bar:function(){}
}

// 匿名闭包(IIFE模式):
var module = (function () {
    var age = 9;
    var foo = function () {
        console.log('age', age);
    }
    return {
        foo,
        age
    }
})()
module.age++
console.log('module.age', module.age);// 10
module.foo()  // 9

// IIFE模式,传入依赖
var module = (function ($, window) {
    var age = 9;
    var foo = function () {
        console.log('age', age);
    }
    return {
        foo,
        age
    }
})(jQuery, window)

模块化的诞生

目的:为避免全局污染,命名冲突,按需加载,提高复用和可维护性

  • 分类:commonjs amd es6

commonjs

  • CommonJS期初主要用于Node.js环境,采用同步加载模式,如果想运行于浏览器端,需要运行其编译后的产物
  • commonjs表现形式:对外暴露 exports / module.exports,引入则用var module = require('xxx.js')
 // module1.js 1个文件可以有多个exports
 exports.foo = function(){
   console.log('module1---foo')
 }
 exports.bar = function(){
   console.log('module1---bar')
 }
 
 // module2.js 1个文件只有1个module.exports
 module.exports = {
   foo(){
      console.log('module2---foo')
   }
 }
 // 入口文件 main.js
 var module1 = require('./module1.js')
 var module2 = require('./module2.js')
 module1.foo()
 module2.foo()
 
 //index.html需要运行main.js编译后的产物才不会报错

amd(Requirejs)

  • amd 运行于浏览器端,通过异步加载模块,主要解决全局命名空间污染,相互依赖脚本加载顺序问题
  • amd表现形式:定义模块define([‘依赖项’],function(依赖对象){}),引用则通过requirejs
// module1.js (没有依赖的模块)
define(function(){
  var age = 9;
  function getAge(){
    return age;
  }
  // 暴露
  return {
    getAge
  }
})

// module2.js(有依赖的模块)
define(['module1'], function(module1){
  function showAge(){
    console.log('module2---showAge', module1.getAge)
  }
  return {
    showAge
  }
})

// main.js
requirejs.config({
  // baseUrl:'', // 基础路径
  paths:{
    module1: './module1', // key:define第一个参数的模块命名, 第二个参数是该模块对应的文件路径
    module2: './module2'
  }
})
requirejs(['module2'], function(module2){
  module2.showAge()
})

// index.html require.js是提前准备好的库
<script data-main="./main.js" src="./lib/require.js">

es6模块

  • es6模块 模块的导入是静态的,并且模块只有在被引用时才会执行,支持 Tree Shaking, 需要经过编译后运行于浏览器端
  • es6表现形式:export / export default,引入import {命名一致} from '' / import utils from ''
// 导出变量、函数和类 
export const variable = 42; 
export function add(a, b) { return a + b; } 
export class Person { /* ... */ }

// 默认导出 
export default function() { 
  console.log('This is the default export.'); 
}

// 命名导入 
import { variable, add, Person } from './module.js';
// 默认导入 
import myFunction from './module.js';

es6模块的延伸(vite开发环境运行快也是基于现代浏览器对ESM的支持)

  • 本身es6模块需要经过编译,浏览器运行编译后的产物才不会报import等语法错误,但是基于现代浏览器对## <script type=module>的支持,使得浏览器以 ES Module 的方式加载脚本
  • 默认情况下 ES 脚本是 defer 的,无论内联还是外联,- 给 script 标签显式指定 async 属性,可以覆盖默认的 defer 行为
  • 安全策略更严格,非同域脚本的加载受 CORS 策略限制

原文链接:https://juejin.cn/post/7261101376686702652 作者:悦悦妍妍

(0)
上一篇 2023年7月30日 上午10:10
下一篇 2023年7月30日 上午10:20

相关推荐

发表回复

登录后才能评论