JS模塊化教程

尚硅谷JS模塊化教程

當項目功能越來越多,代碼量便也會越來越多,後期的維護難度會增大,此時在JS方面就會考慮使用模塊化規範去管理。
本視頻內容涵蓋:理解模塊化,爲什麼要模塊化,模塊化的優缺點以及模塊化規範。並且將帶領大家學習開發中最流行的commonjs, AMD, ES6、CMD規範。
建議同學們學習完模塊化規範以後再學習項目構建,以更好的武裝自己的技能。

(01. 尚硅谷_JS模塊化_入門介紹&)

01. 尚硅谷_JS模塊化_入門介紹&

(02. 尚硅谷_JS模塊化_模塊進化史&)

02. 尚硅谷_JS模塊化_模塊進化史&

全局winow添加moule3屬性,module3是一個對象裏面有個方法是foo。

暴露的是module3對象

直接調用,module4是一個函數

## 模塊化進化史教程
1. 全局function模式
  * module1.js
    ```
    //數據
    let data = 'atguigu.com'
    
    //操作數據的函數
    function foo() {
      console.log(`foo() ${data}`)
    }
    function bar() {
      console.log(`bar() ${data}`)
    }
    ```
  * module2.js
    ```
    let data2 = 'other data'
    
    function foo() {  //與另一個模塊中的函數衝突了
      console.log(`foo() ${data2}`)
    }
    ```
  * test1.html
    ```
    <script type="text/javascript" src="module1.js"></script>
    <script type="text/javascript" src="module2.js"></script>
    <script type="text/javascript">
    
      let data = "修改後的數據"
      foo()
      bar()
    </script>
    ```
   * 說明:
      * 全局函數模式: 將不同的功能封裝成不同的全局函數
      * 問題: Global被污染了, 很容易引起命名衝突
2. namespace模式
  * module1.js
    ```
    let myModule = {
      data: 'atguigu.com',
      foo() {
        console.log(`foo() ${this.data}`)
      },
      bar() {
        console.log(`bar() ${this.data}`)
      }
    }
    ```
  * module2.js
    ```
    let myModule2 = {
      data: 'atguigu.com2222',
      foo() {
        console.log(`foo() ${this.data}`)
      },
      bar() {
        console.log(`bar() ${this.data}`)
      }
    }
    ```
  * test2.html
    ```
    <script type="text/javascript" src="module2.js"></script>
    <script type="text/javascript" src="module22.js"></script>
    <script type="text/javascript">
      myModule.foo()
      myModule.bar()
    
      myModule2.foo()
      myModule2.bar()
    
      myModule.data = 'other data' //能直接修改模塊內部的數據
      myModule.foo()
    
    </script>
    ```
  * 說明
    * namespace模式: 簡單對象封裝
    * 作用: 減少了全局變量
    * 問題: 不安全
3. IIFE模式
  * module3.js
    ```
    (function (window) {
      //數據
      let data = 'atguigu.com'
    
      //操作數據的函數
      function foo() { //用於暴露有函數
        console.log(`foo() ${data}`)
      }
    
      function bar() {//用於暴露有函數
        console.log(`bar() ${data}`)
        otherFun() //內部調用
      }
    
      function otherFun() { //內部私有的函數
        console.log('otherFun()')
      }
    
      //暴露行爲
      window.myModule = {foo, bar}
    })(window)
    ```
  * test3.html
    ```
    <script type="text/javascript" src="module3.js"></script>
    <script type="text/javascript">
      myModule.foo()
      myModule.bar()
      //myModule.otherFun()  //myModule.otherFun is not a function
      console.log(myModule.data) //undefined 不能訪問模塊內部數據
      myModule.data = 'xxxx' //不是修改的模塊內部的data
      myModule.foo() //沒有改變
    
    </script>
    ```
  * 說明:
    * IIFE模式: 匿名函數自調用(閉包)
    * IIFE : immediately-invoked function expression(立即調用函數表達式)
    * 作用: 數據是私有的, 外部只能通過暴露的方法操作
    * 問題: 如果當前這個模塊依賴另一個模塊怎麼辦?
4. IIFE模式增強
  * 引入jquery到項目中
  * module4.js
    ```
    (function (window, $) {
      //數據
      let data = 'atguigu.com'
    
      //操作數據的函數
      function foo() { //用於暴露有函數
        console.log(`foo() ${data}`)
        $('body').css('background', 'red')
      }
    
      function bar() {//用於暴露有函數
        console.log(`bar() ${data}`)
        otherFun() //內部調用
      }
    
      function otherFun() { //內部私有的函數
        console.log('otherFun()')
      }
    
      //暴露行爲
      window.myModule = {foo, bar}
    })(window, jQuery)
    ``` 
  * test4.html
    ```
    <script type="text/javascript" src="jquery-1.10.1.js"></script>
    <script type="text/javascript" src="module4.js"></script>
    <script type="text/javascript">
      myModule.foo()
    </script>
    ```
  * 說明
    * IIFE模式增強 : 引入依賴
    * 這就是現代模塊實現的基石
      
5. 頁面加載多個js的問題
  * 頁面:
    ```
    <script type="text/javascript" src="module1.js"></script>
    <script type="text/javascript" src="module2.js"></script>
    <script type="text/javascript" src="module3.js"></script>
    <script type="text/javascript" src="module4.js"></script>
    ```
  * 說明
    * 一個頁面需要引入多個js文件
    * 問題:
        * 請求過多
        * 依賴模糊
        * 難以維護
    * 這些問題可以通過現代模塊化編碼和項目構建來解決

(03. 尚硅谷_JS模塊化規範_commonjs基於服務器端(node)應用&)

03. 尚硅谷_JS模塊化規範_commonjs基於服務器端(node)應用&

## Node.js模塊化教程
1. 下載安裝node.js
2. 創建項目結構
  ```
  |-modules
    |-module1.js
    |-module2.js
    |-module3.js
  |-app.js
  |-package.jsonA
    {
      "name": "commonJS-node",
      "version": "1.0.0"
    }
  ```
3. 下載第三方模塊
  * npm install uniq --save
4. 模塊化編碼
  * module1.js
    ```
    module.exports = {
      foo() {
        console.log('moudle1 foo()')
      }
    }
    ```
  * module2.js
    ```
    module.exports = function () {
      console.log('module2()')
    }
    ```
  * module3.js
    ```
    exports.foo = function () {
      console.log('module3 foo()')
    }
    
    exports.bar = function () {
      console.log('module3 bar()')
    }
    ```
  * app.js 
    ```
    /**
      1. 定義暴露模塊:
        module.exports = value;
        exports.xxx = value;
      2. 引入模塊:
        var module = require(模塊名或模塊路徑);
     */
    "use strict";
    //引用模塊
    let module1 = require('./modules/module1')
    let module2 = require('./modules/module2')
    let module3 = require('./modules/module3')
    
    let uniq = require('uniq')
    let fs = require('fs')
    
    //使用模塊
    module1.foo()
    module2()
    module3.foo()
    module3.bar()
    
    console.log(uniq([1, 3, 1, 4, 3]))
    
    
    ```
5. 通過node運行app.js
  * 命令: node app.js
  * 工具: 右鍵-->運行

不能無限暴露,下面的會把上面的覆蓋

第二種暴露方式,不斷向exports對象添加屬性

(04. 尚硅谷_JS模塊化規範_commonjs基於瀏覽器端應用&)

04. 尚硅谷_JS模塊化規範_commonjs基於瀏覽器端應用&

瀏覽器執行打包生成的build.js文件

## Browserify模塊化使用教程
1. 創建項目結構
  ```
  |-js
    |-dist //打包生成文件的目錄
    |-src //源碼所在的目錄
      |-module1.js
      |-module2.js
      |-module3.js
      |-app.js //應用主源文件
  |-index.html
  |-package.json
    {
      "name": "browserify-test",
      "version": "1.0.0"
    }
  ```
2. 下載browserify
  * 全局: npm install browserify -g
  * 局部: npm install browserify --save-dev
3. 定義模塊代碼
  * module1.js
    ```
    module.exports = {
      foo() {
        console.log('moudle1 foo()')
      }
    }
    ```
  * module2.js
    ```
    module.exports = function () {
      console.log('module2()')
    }
    ```
  * module3.js
    ```
    exports.foo = function () {
      console.log('module3 foo()')
    }
    
    exports.bar = function () {
      console.log('module3 bar()')
    }
    ```
  * app.js (應用的主js)
    ```
    //引用模塊
    let module1 = require('./module1')
    let module2 = require('./module2')
    let module3 = require('./module3')
    
    let uniq = require('uniq')
    
    //使用模塊
    module1.foo()
    module2()
    module3.foo()
    module3.bar()
    
    console.log(uniq([1, 3, 1, 4, 3]))
    ```
* 打包處理js:
  * browserify js/src/app.js -o js/dist/bundle.js 
* 頁面使用引入:
  ```
  <script type="text/javascript" src="js/dist/bundle.js"></script> 
  ```

(05. 尚硅谷_JS模塊化規範_AMD規範_NoAMD&)

05. 尚硅谷_JS模塊化規範_AMD規範_NoAMD&

一個頁面一個模塊

## require.js使用教程
1. 下載require.js, 並引入
  * 官網: http://www.requirejs.cn/
  * github : https://github.com/requirejs/requirejs
  * 將require.js導入項目: js/libs/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 = 'atguigu.com'
    
      function getMsg() {
        return msg.toUpperCase()
      }
    
      return {getMsg}
    })
    ```
  * alerter.js
    ```
    define(['dataService', 'jquery'], function (dataService, $) {
      let name = 'Tom2'
    
      function showMsg() {
        $('body').css('background', 'gray')
        alert(dataService.getMsg() + ', ' + name)
      }
    
      return {showMsg}
    })
    ```
4. 應用主(入口)js: main.js
  ```
  (function () {
    //配置
    requirejs.config({
      //基本路徑
      baseUrl: "js/",
      //模塊標識名與模塊路徑映射
      paths: {
        "alerter": "modules/alerter",
        "dataService": "modules/dataService",
      }
    })
    
    //引入使用模塊
    requirejs( ['alerter'], function(alerter) {
      alerter.showMsg()
    })
  })()
  ```
        
5. 頁面使用模塊:
  <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'
          }
    ```
  * 在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.js導入項目
    * js/libs/angular.js
   
  * 在main.js中配置
    ```
    (function () {
      require.config({
        //基本路徑
        baseUrl: "js/",
        //模塊標識名與模塊路徑映射
        paths: {
          //第三方庫
          'jquery' : './libs/jquery-1.10.1',
          'angular' : './libs/angular',
          //自定義模塊
          "alerter": "./modules/alerter",
          "dataService": "./modules/dataService"
        },
        /*
         配置不兼容AMD的模塊
         exports : 指定與相對應的模塊名對應的模塊對象
         */
        shim: {
          'angular' : {
            exports : 'angular'
          }
        }
      })
      //引入使用模塊
      require( ['alerter', 'angular'], function(alerter, angular) {
        alerter.showMsg()
        console.log(angular);
      })
    })()
    ```
 

(06. 尚硅谷_JS模塊化規範_AMD規範_自定義模塊&)

06. 尚硅谷_JS模塊化規範_AMD規範_自定義模塊&

暴露接口是一個對象,對象裏面是一個方法

有依賴的時候,通過數組定義依賴的模塊

引入需要配置

baseUrl是根路徑,找到js下面的modules

引入規範require庫

(07. 尚硅谷_JS模塊化規範_AMD規範_第三方模塊&)

07. 尚硅谷_JS模塊化規範_AMD規範_第三方模塊&

jquery遇到AMD必須jquery小寫q才能返回jQuery對象

(08. 尚硅谷_JS模塊化規範_CMD規範應用&)

08. 尚硅谷_JS模塊化規範_CMD規範應用&

## sea.js簡單使用教程
1. 下載sea.js, 並引入
  * 官網: http://seajs.org/
  * github : https://github.com/seajs/seajs
  * 將sea.js導入項目: js/libs/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) {
      module.exports = {
        msg: 'I Will Back'
      }
    })
    ```
  * module3.js
    ```
    define(function (require, exports, module) {
      const API_KEY = 'abc123'
      exports.API_KEY = API_KEY
    })
    ```
  * module4.js
    ```
    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) {
      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>
  ```
        
    
       

(09. 尚硅谷_JS模塊化規範_ES6規範_基本使用&)

09. 尚硅谷_JS模塊化規範_ES6規範_基本使用&

引用的時候必須用對象的解構賦值的方式,否則是undefined

相當於在當前作用域下定義了foo變量

重新編譯打包

es6轉爲es5

重新打包編譯

## ES6-Babel-Browserify使用教程
1. 定義package.json文件
  ```
  {
    "name" : "es6-babel-browserify",
    "version" : "1.0.0"
  }
  ```
2. 安裝babel-cli, babel-preset-es2015和browserify
  * npm install babel-cli browserify -g 
	* npm install babel-preset-es2015 --save-dev 
	* preset 預設(將es6轉換成es5的所有插件打包)
3. 定義.babelrc文件 
	```
	{
    "presets": ["es2015"]
  }
	```
4. 編碼
  * js/src/module1.js  分別暴露
    ```
    export function foo() {
      console.log('module1 foo()');
    }
    export function bar() {
      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 {
      name: 'Tom',
      setName: function (name) {
        this.name = name
      }
    }
    ```
  * js/src/app.js
    ```
    import {foo, bar} from './module1'
    import {DATA_ARR} from './module1'
    import {fun1, fun2} from './module2'
    import person from './module3'
    
    import $ from 'jquery'
    
    $('body').css('background', 'red')
    
    foo()
    bar()
    console.log(DATA_ARR);
    fun1()
    fun2()
    
    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
  2). 在app.js中引入並使用
    ```
    import $ from 'jquery'
    $('body').css('background', 'red')
    ```

(10. 尚硅谷_JS模塊化規範_ES6規範_默認暴露&)

10. 尚硅谷_JS模塊化規範_ES6規範_默認暴露&

默認暴露只能暴露一次,可以暴露一個對象

module3就是一個函數

下載1版本jquery

## JS模塊化
* 模塊化的理解
* 什麼是模塊?
  * 將一個複雜的程序依據一定的規則(規範)封裝成幾個塊(文件), 並進行組合在一起
  * 塊的內部數據/實現是私有的, 只是向外部暴露一些接口(方法)與外部其它模塊通信
* 一個模塊的組成
  * 數據--->內部的屬性
  * 操作數據的行爲--->內部的函數
* 模塊化
  * 編碼時是按照模塊一個一個編碼的, 整個項目就是一個模塊化的項目
* 模塊化的進化過程
  * 全局function模式 : 
    * 編碼: 全局變量/函數
    * 問題: 污染全局命名空間, 容易引起命名衝突/數據不安全
    
  * namespace模式 : 
    * 編碼: 將數據/行爲封裝到對象中
    * 解決: 命名衝突(減少了全局變量)
    * 問題: 數據不安全(外部可以直接修改模塊內部的數據)
  * IIFE模式/增強
    * IIFE : 立即調用函數表達式--->匿名函數自調用
    * 編碼: 將數據和行爲封裝到一個函數內部, 通過給window添加屬性來向外暴露接口
    * 引入依賴: 通過函數形參來引入依賴模塊
      ```
      (function(window, module2){
        var data = 'atguigu.com'
        function foo() {
           module2.xxx()
           console.log('foo()'+data)
        }
        function bar() {
           console.log('bar()'+data)
        }
        
        window.module = {foo}
      })(window, module2)
      ```
* 模塊化規範
  * CommonJS
    * Node.js : 服務器端
    * Browserify : 瀏覽器端    也稱爲js的打包工具
    * 基本語法:
      * 定義暴露模塊 : exports
        ```
        exports.xxx = value
        module.exports = value
        ```
      引入模塊 : require
        ```
        var module = require('模塊名/模塊相對路徑')
        ```
    * 引入模塊發生在什麼時候?
      * Node : 運行時, 動態同步引入
      * Browserify : 在運行前對模塊進行編譯/轉譯/打包的處理(已經將依賴的模塊包含進來了), 
                  運行的是打包生成的js, 運行時不存在需要再從遠程引入依賴模塊
  * AMD : 瀏覽器端
    * require.js
    * 基本語法
      * 定義暴露模塊: define([依賴模塊名], function(){return 模塊對象})
      * 引入模塊: require(['模塊1', '模塊2', '模塊3'], function(m1, m2){//使用模塊對象})
      * 配置: 
        ```
        require.config({
          //基本路徑
          baseUrl : 'js/',
          //標識名稱與路徑的映射
          paths : {
            '模塊1' : 'modules/模塊1',
            '模塊2' : 'modules/模塊2',
            'angular' : 'libs/angular',
            'angular-messages' : 'libs/angular-messages'
          },
          //非AMD的模塊
          shim : {
            'angular' : {
                exports : 'angular'
            },
            'angular-messages' : {
                exports : 'angular-messages',
                deps : ['angular']
            }
          }
        })
        ```
  * CMD : 瀏覽器端
    * sea.js
    * 基本語法
      * 定義暴露模塊: 
        ```
        define(function(require, module, exports){
          通過require引入依賴模塊
          通過module/exports來暴露模塊
          exports.xxx = value
        })
        ```
      * 使用模塊seajs.use(['模塊1', '模塊2'])
  * ES6
    * ES6內置了模塊化的實現
    * 基本語法
      * 定義暴露模塊 : export
        * 暴露一個對象: 
          ```
          export default 對象
          ```
        * 暴露多個: 
          ```
          export var xxx = value1
          export let yyy = value2
          
          var xxx = value1
          let yyy = value2
          export {xxx, yyy}
          ```
              
      * 引入使用模塊 : import
        * default模塊:
          ```
          import xxx  from '模塊路徑/模塊名'
          ```
        * 其它模塊
          ```
          import {xxx, yyy} from '模塊路徑/模塊名'
          import * as module1 from '模塊路徑/模塊名'
          ```
    * 問題: 所有瀏覽器還不能直接識別ES6模塊化的語法  
    * 解決:
        * 使用Babel將ES6--->ES5(使用了CommonJS) ----瀏覽器還不能直接支行
        * 使用Browserify--->打包處理----瀏覽器可以運行
            
    
    

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章