wallleap / myblogs

blog, Issue
https://myblog.wallleap.cn
4 stars 1 forks source link

JS 组织代码方式演变——模块化 #98

Open wallleap opened 1 year ago

wallleap commented 1 year ago

title: JS 组织代码方式演变——模块化 date: 2020-05-15 19:33 updated: 2020-05-15 19:33 cover: //cdn.wallleap.cn/img/pic/cover/202302GKk2ms.jpg category: 技术杂谈 tags:

看别人的代码的时候,有些人写的代码一团糟,可是有些人的代码进行合理的分块,看起来很容易阅读理解。

1、是什么

2、模块化的进化过程

代码之间会相互依赖(A 需要 B 中的变量/函数),应该怎样对代码进行组织呢

(1)通过注释进行组织

/**
 * 获取元素
 */
getElement(el)
/**
 * 删除元素
 */
removeElement(node)

(2)用函数组织代码

function getElement(el) {}
function removeElement(node) {}
/* 在合适的时候调用 */
const red = getElement('.red')
removeElement(red)

(3)用命名空间(namespace) 组织代码

var app = app || {}
app.person = () => {}
app.user.sub = () => {}
app.friends = () => {}
app.person()
app.user.sub()
app.friends()

(4)立即执行函数

// 提前引入依赖
!function user(dom, axios, time) {
  // do something
}(dom, axios, time)  // 直接传入依赖
/* 有名字 */
const personModule = (function(dom, axios, time) {
  // do something
  return {
    getPerson,
    updatePerson
  }
})(dom, axios, time)

(5)CommonJS

(6)AMD (异步模块定义)

(7)ES Modules(ES 模块)

3、模块化演示

1、CommonJS 基于服务器端(Node.js)

2、CommonJS 基于浏览器端应用(Browserify)

3、AMD 规范

(1)NoAMD

(2)AMD(require.js)

  1. 下载 require.js, 并引入

  2. 创建项目结构

    • js
      • libs
      • require.js
      • modules
      • alerter.js
      • dataService.js
      • main.js
    • index.html
  3. 定义require.js的模块代码

    • dataService.js

      // 定义没有依赖的模块
      define(function () {
      let msg = 'dataService.js'
      
      function getMsg() {
        return msg.toUpperCase()
      }
      // 暴露模块
      return {getMsg}
      })
    • alerter.js

      // 定义有依赖的模块
      define(['dataService', 'jquery'], function (dataService, $) {
      let msg = 'alerter.js'
      
      function showMsg() {
        $('body').css('background', 'gray')
        alert(dataService.getMsg() + ', ' + msg)
      }
      
      return {showMsg}
      })
  4. 应用主(入口) js:main.js

    (function () {
        //配置
        require.config({
            //基本路径 出发点在根目录下
            baseUrl: "js/",
            //模块标识名与模块路径映射
            paths: {
                "alerter": "modules/alerter",
                "dataService": "modules/dataService",
            }
        })
        //引入使用模块
        require( ['alerter'], function(alerter) {
            alerter.showMsg()
        })
    })()
  5. 页面使用模块:index.html

    <script data-main="js/main" src="js/libs/require.js"></script>
  6. 使用第三方基于 require.js 的框架(jquery)

    • 将 jquery 的库文件导入到项目:

      • js/libs/jquery-1.10.1.js
    • 在 main.js 中配置 jquery 路径

        paths: {
            'jquery': 'libs/jquery-1.10.1'  // jQuery定义的返回AMD模块名为小写
        }
    • 在 alerter.js 中使用 jquery

        define(['dataService', 'jquery'], function (dataService, $) {
            var name = 'xfzhang'
            function showMsg() {
                $('body').css({background : 'red'})
                alert(name + ' '+dataService.getMsg())
            }
            return { showMsg }
        })
  7. 使用第三方不基于 require.js 的框架(angular/angular-messages)

    • 将 angular.js 和 angular-messages.js 导入项目

      • js/libs/angular.js
      • js/libs/angular-messages.js
    • 在 main.js 中配置

      (function () {
      require.config({
        //基本路径
        baseUrl: "js/",
        //模块标识名与模块路径映射
        paths: {
          //第三方库
          'jquery' : 'libs/jquery-1.10.1',
          'angular' : 'libs/angular',
          'angular-messages' : 'libs/angular-messages',
          //自定义模块
          "alerter": "modules/alerter",
          "dataService": "modules/dataService"
        },
        /*
        配置不兼容AMD的模块
        exports : 指定导出的模块名
        deps  : 指定所有依赖的模块的数组
        */
        shim: {
          'angular' : {
            exports : 'angular'
          },
          'angular-messages' : {
            exports : 'angular-messages',
            deps : ['angular']
          }
        }
      })
      //引入使用模块
      require( ['alerter', 'angular', 'angular-messages'], function(alerter, angular) {
        alerter.showMsg()
        angular.module('myApp', ['ngMessages'])
        angular.bootstrap(document,["myApp"])
      })
      })()
    • 页面:

      <form name="myForm">
      用户名: <input type="text" name="username" ng-model="username" ng-required="true">
      <div style="color: red;" ng-show="myForm.username.$dirty&&myForm.username.$invalid">用户名是必须的</div>
      </form>

5、CMD规范

  1. 下载 sea.js, 并引入

  2. 创建项目结构

    |-js
      |-libs
        |-sea.js
      |-modules
        |-module1.js
        |-module2.js
        |-module3.js
        |-module4.js
        |-main.js
    |-index.html
  3. 定义sea.js的模块代码

    • module1.js

      // 没有依赖的模块
      define(function (require, exports, module) {
      //内部变量数据
      var data = 'atguigu.com'
      //内部函数
      function show() {
        console.log('module1 show() ' + data)
      }
      
      //向外暴露
      exports.show = show
      })
    • module2.js

      define(function (require, exports, module) {
      let msg = 'module2'
      function bar(){
          console.log(msg)
      }
        module.exports = bar
      /* module.exports = {
        msg: 'I Will Back'
      } */
      })
    • module3.js

      define(function(require, exports, module){
        let data = 'module3'
        function fun(){
            console.log(data)
        }
        exports.module3 = {fun}
      })
      /* define(function (require, exports, module) {
      const API_KEY = 'abc123'
      exports.API_KEY = API_KEY
      }) */
    • module4.js

      define(function(require, exports, module){
        let msg = 'module4'
        // 同步引入
        let module2 = require('./module2')
        module2()
        // 异步引入
        require.async('./module3', function(module3){
            module3.module3.fun()
        })
        function fun2(){
            console.log(msg)
        }
        exports.fun2 = fun2
      })
      /*define(function (require, exports, module) {
      //引入依赖模块(同步)
      var module2 = require('./module2')
      
      function show() {
        console.log('module4 show() ' + module2.msg)
      }
      
      exports.show = show
      //引入依赖模块(异步)
      require.async('./module3', function (m3) {
        console.log('异步引入依赖模块3  ' + m3.API_KEY)
      })
      })*/
    • main.js: 主(入口)模块

      define(function (require) {
      let module1 = require('./module1')
      console.log(module1.foo())
      let module4 = require('./module4')
      module4.fun2()
      })
      /*define(function (require) {
      var m1 = require('./module1')
      var m4 = require('./module4')
      m1.show()
      m4.show()
      })*/
  4. index.html:

    <!--
    使用seajs:
      1. 引入sea.js库
      2. 如何定义导出模块 :
        define()
        exports
        module.exports
      3. 如何依赖模块:
        require()
      4. 如何使用模块:
        seajs.use()
    -->
    <script type="text/javascript" src="js/libs/sea.js"></script>
    <script type="text/javascript">
      seajs.use('./js/modules/main')
    </script>

6、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
    • npm install babel-preset-es2015 --save-dev
    • preset 预设(将es6转换成es5的所有插件打包)
  3. 定义 .babelrc 文件(babel run control)

    {
    "presets": ["es2015"]
    }
  4. 编码

    • js/src/module1.js

      // 暴露模块 分别暴露
      export function foo() {
      console.log('module1 foo()');
      }
      export let bar = function () {
      console.log('module1 bar()');
      }
      export const DATA_ARR = [1, 3, 5, 1]
    • js/src/module2.js

      // 统一暴露——常规暴露
      let data = 'module2 data'
      
      function fun1() {
      console.log('module2 fun1() ' + data);
      }
      
      function fun2() {
      console.log('module2 fun2() ' + data);
      }
      
      export {fun1, fun2}
    • js/src/module3.js

      // 默认暴露:可以暴露任意数据类型,暴露什么数据类型接收到的就是什么数据
      // export default value
      export default() =>{
        console.log('我是默认暴露的箭头函数')
      } // 只能暴露一个default,因此暴露对象
      /* export default {
      name: 'Tom',
      setName: function (name) {
        this.name = name
      }
      } */
    • js/src/app.js

      // 引入其他的模块
      // 语法: import xxx from '路径'
      import {foo, bar} from './module1'
      import {DATA_ARR} from './module1'
      import {fun1, fun2} from './module2'
      
      import module3 from './module3'
      /* import person from './module3' */
      
      // 安装 npm install jquery@1
      import $ from 'jquery'
      
      $('body').css('background', 'red')
      
      foo()
      bar()
      console.log(DATA_ARR);
      fun1()
      fun2()
      
      module3()
      /* person.setName('JACK')
      console.log(person.name); */
  5. 编译

    • 使用 Babel 将 ES6 编译为 ES5 代码(但包含CommonJS语法):babel js/src -d js/lib
    • 使用 Browserify 编译 js:browserify js/lib/app.js -o js/lib/bundle.js
  6. 页面中引入测试

    <script type="text/javascript" src="js/lib/bundle.js"></script>
  7. 引入第三方模块(jQuery)

    1. 下载jQuery模块:

      • npm install jquery@1 --save // @后的是版本,1代表1.x.x中的最新版本
    2. 在app.js中引入并使用

      import $ from 'jquery'
      $('body').css('background', 'red')