美文网首页程序员
模块化的演进过程

模块化的演进过程

作者: 翔子丶 | 来源:发表于2021-01-19 15:39 被阅读0次
现代化项目
  • 要求前端有独立的项目
  • 采用数据驱动开发的方式增强可维护性
  • 复杂项目结构必须进行模块化管理
  • 重复规律性工作必须采取自动化工具实现

模块化的演进过程

  1. 文件划分方式

    web最原始的模块系统,每个功能及相关状态数据单独放在不同js文件中,约定每个文件是一个独立的模块

    ├── module-a.js
    ├── module-b.js
    └── index.html
    

    缺点:

    • 模块直接在全局工作,污染全局作用域
    • 没有私有空间,模块内成员可以在外部被访问或修改
    • 模块增多,容易产生命名冲突
    • 无法管理模块与模块之间依赖关系
    • 维护过程中很难分辨每个成员所属的模块
  2. 命名空间方式

    约定每个模块只暴露一个全局对象,将每个模块包裹到一个全局对象

    window.moduleA = {
     method1() {
         console.log('a')
        }
    }
    

    只解决了命名冲突问题,其他问题依旧存在

  3. IIFE

    立即执行函数为模块提供私有空间,将每个模块放在立即执行函数形成的私有作用域,需暴露的成员,通过全局对象挂载

    (function() {
     const name = 'a'
        function method1 () {
         console.log('a')
        }
        window.moduleA = {
         method1
        }
    })()
    

    解决全局污染和命名冲突问题

  4. IIFE依赖参数

    使用IIFE参数作为依赖声明使用 使模块之间依赖关系更明显

    (function ($) { // 通过参数明显表明这个模块的依赖
      var name = 'module-a'
      function method1 () {
        console.log(name + '#method1')
        $('body').animate({ margin: '200px' })
      }
      window.moduleA = {
        method1: method1
      }
    })(jQuery)
    
模块加载问题

时间久、项目大时难维护

更为理想的页面引入一个JS文件入口,用到的模块通过代码控制、按需加载

模块化标准规范
  • nodejs中遵循commonJS规范来组织模块
    • 约定一个文件就是一个模块
    • 每个模块有单独的作用域
    • 通过module.exports导出成员
    • 通过require函数载入模块
  • 浏览器环境中遵循ES Modules(ES6)规范

差异:CommonJs以同步模式加载模块,启动时加载模块,而执行当中不需要再加载,浏览器端使用会导致效率低下;早期浏览器使用AMD(Asynchronous Module Definition)规范,推出Require.js实现AMD规范

  • 定义模块

    // 定义一个模块 通过define函数去定义
    // param 模块名称,后期加载模块使用  数组,模块依赖项 函数,依赖项的导出成员一一对应
    // 模块需要导出成员,通过retuen方式实现
    define('module1', ['jquery', './module2'], function($, module2) {
      return {
            start: function () {
                $('body').animate({ margin: '200px' })
                module2()
            }
      }
    })
    
  • 载入模块

    // 载入一个模块
    require(['module1'], function (module1) {
      module1.start()
    })
    

AMD缺陷:

  • AMD使用起来相对复杂
  • 模块JS文件请求频繁
ES Module特性

ES Module模块需要使用http serve模式运行,直接运行html文件会导致跨域,browser-sync --files *./.js

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>ES Module - 模块的特性</title>
  </head>
  <body>
    <!-- 通过给 script 添加 type = module 的属性,就可以以 ES Module 的标准执行其中的 JS 代码了 -->
    <script type="module">
      console.log('this is es module') // this is a module
    </script>

    <!-- 1. ESM 自动采用严格模式,忽略 'use strict' -->
    <script type="module">
      console.log(this) // undefined 非严格模式下是Window对象
    </script>

    <!-- 2. 每个 ES Module 都是运行在单独的私有作用域中 -->
    <script type="module">
      var foo = 100
      console.log(foo)
    </script>
    <script type="module">
      console.log(foo) // foo is not defined
    </script>

    <!-- 3. ESM 是通过 CORS 的方式请求外部 JS 模块的,引入的文件必须支持CORS -->
    <script
      type="module"
      src="https://unpkg.com/jquery@3.4.1/dist/jquery.min.js"
    ></script>

    <!-- 4. ESM 的 script 标签会延迟执行脚本,等待网页渲染完成之后再去执行脚本,相当于defer属性 -->
    <script type="module" src="demo.js"></script>
    <p>需要显示的内容</p>
  </body>
</html>
ES Module导入和导出
  • 模块内的成员都存在与私有作用域中,外部访问需要暴露

    var name = 'foo module'
    
    function hello() {
      console.log('hello')
    }
    
    class Person {}
    
    // 重命名 name as fooName
    // export {
    //   name as default,
    //   hello as fooHello
    // }
    
    // 模块默认导出
    // export default name
    // import name from './module.js' 引入
    
    // var obj = { name, hello, Person }
    
    // 可以导出变量、函数、类 放在最后 比较直观
    export { name, hello, Person }
    
  • 导入导出注意事项

    // 导出
    // 这里的 `{ name, hello }` 不是一个对象字面量,
    // 它只是语法上的规则而已
    export { name, age }
    
    // export name // 错误的用法
    // export 'foo' // 同样错误的用法
    setTimeout(function () {
      name = 'ben'
    }, 1000)
    
    // 导入
    // CommonJS 中是先将模块整体导入为一个对象,然后从对象中结构出需要的成员
    // const { name, age } = require('./module.js')
    
    // ES Module 中 { } 是固定语法,就是直接提取模块导出成员
    import { name, age } from './module.js'
    console.log(name, age)
    
    // 导入成员并不是复制一个副本,而是直接导入模块成员的引用地址
    // 也就是说 import 得到的变量与 export 导入的变量在内存中是同一块空间。
    // 一旦模块中成员修改了,这里也会同时修改,
    setTimeout(function () {
      console.log(name, age)
    }, 1500)
    
    // 导入模块成员变量是只读的
    // name = 'tom' // 报错
    
    // 但是需要注意如果导入的是一个对象,对象的属性读写不受影响
    // name.xxx = 'xxx' // 正常
    // 1.需要填写完整名称
    import { name } from './module.js'
    // 2.不能省略.js或index.js
    import { lowercase } from './utils/index.js'
    // 3.不能省略./ 否则ES Module认为再加载第三方模块
    import { name } from 'module.js'
    import { name } from './module.js'
    import { name } from '/04-import/module.js'
    import { name } from 'http://localhost:3000/04-import/module.js'
    // 4.加载模块 并不提取
    import './module.js'
    // 5.导入全部 通过mod.xx拿到其中变量
    import * as mod from './module.js'
    console.log(mod.xxx)
    // 6.动态导入模块 Promise对象
    import('./module.js').then(function (module) {
      console.log(module)
    })
    // 7.同时导出默认成员命名成员
    import abc, { name, age } from './module.js'
    console.log(name, age, abc)
    
  • 导入导出成员

    // import { Button } from './button.js'
    // import { Avatar } from './avatar.js'
    // export { Button, Avatar }
    
    // default导出必须重命名 会作为当前index的默认导出
    export { default as Button } from './button.js'
    export { Avatar } from './avatar.js'
    
  • Polyfill兼容方案

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>ES Module 浏览器环境 Polyfill</title>
    </head>
    <body>
      <!-- Promise polyfill -->
      <!-- nomodule 在支持ES Module的浏览器回执行两次 nomodule只会在不支持ES Module的浏览器使用 -->
      <script nomodule src="https://unpkg.com/promise-polyfill@8.1.3/dist/polyfill.min.js"></script>
      <script nomodule src="https://unpkg.com/browser-es-module-loader@0.4.1/dist/babel-browser-build.js"></script>
      <!-- es-module-loader读取代码 通过babel去转换 -->
      <script nomodule src="https://unpkg.com/browser-es-module-loader@0.4.1/dist/browser-es-module-loader.js"></script>
      <script type="module">
        import { foo } from './module.js'
        console.log(foo)
      </script>
    </body>
    </html>
    

相关文章

  • 模块化的演进过程

    现代化项目 要求前端有独立的项目 采用数据驱动开发的方式增强可维护性 复杂项目结构必须进行模块化管理 重复规律性工...

  • 前端模块化的演进过程

    在技术日新月异的时代,像我们这些在框架和工具成熟期后进入这个行业的。对前端工程化工具生态的发展过程自然是要陌生一些...

  • Android项目模块化/组件化开发(非原创)

    文章大纲 一、项目模块化初步介绍二、项目模块化的两种模式与比较三、大型项目模块化的演进四、项目模块化总结五、参考文...

  • 【Android】应用架构演进

    预则立应用演进过程中可能出现混乱局面,对架构的考量应该放在项目开始之前参考:Android架构思考(模块化、多进程...

  • 第4章 ES6模块化

    目标 模块化系统演进 ES6模块化实现 模块化概述 在 ES6 之前,社区制定了一些模块加载方案,最主要的有 Co...

  • 第4章 ES6模块化

    目标 模块化系统演进 ES6模块化实现 模块化概述 在 ES6 之前,社区制定了一些模块加载方案,最主要的有 Co...

  • 项目组件化实践

    一、项目演进 混沌项目 -> 模块化 -> 组件化 混沌项目:所有代码在一个主工程中,仅仅做了分包。 模块化:项目...

  • webpack15个知识点

    一、模块化系统演进: 1、传统: 标签; 2、commonJS: 协同require()同步加载,再通过expor...

  • 模块化开发

    模块化的演进过程 最早是基于文件划分方式的,具体就是将每个模块的数据单独放在不同文件当中,去约定每一个文件就是一个...

  • Android make 基础

    1- adroid 演进过程 Android编译演进过程: Android7.0之前 使用GNU Make And...

网友评论

    本文标题:模块化的演进过程

    本文链接:https://www.haomeiwen.com/subject/hazqzktx.html