天天看點

前端工程化——子產品化概述

子產品化概述——「子產品化」隻是思想

最主流的代碼整合方式,安裝功能不同劃分不同子產品。

  • 子產品化演變過程
  • 子產品化規範
  • 常用的子產品化打包工具
  • 基于子產品化工具建構現代WEB應用
  • 打包工具的優化技巧

子產品化的演進過程

Stage 1 - 檔案劃分方式

完全依靠約定
├── index.html
├── module-01.js
└── module-02.js
           
  • 通過scriptsrc屬性引入子產品
  • 污染全局作用域,容易産生命名沖突
  • 子產品成員可以随意修改
  • 無法管理子產品依賴關系

Stage 2 - 命名空間方式

依靠約定,子產品暴露全局對象, 所有成員都暴露在該對象下面,解決了污染全局作用域問題
var moduleA = {
    name: 'module-a'
    method1: function() {
        console.log(this.name + 'method1')
    }
    method2: function() {
        console.log(this.name + 'method2')
    }
}
           
  • 通過scriptsrc屬性引入子產品
  • 子產品成員可以随意修改
  • 無法管理子產品依賴關系

Stage 3 - IIFE

立即執行函數方式,為子產品提供私有空間。實作了私有成員的概念。通過自執行函數傳參的方式可以實作簡單的子產品化依賴處理
;
(function() {
    var name = 'module-a'

    function method1() {
        console.log(name + 'method1')
    }

    function method2() {
        console.log(name + 'method2')
    }
    window.moduleA = {
        method1: method1,
        method2: method2
    }
})(jQuery)
           
  • 通過scriptsrc屬性引入子產品
  • 無法管理子產品依賴關系

子產品化規範的出現

子產品化标準+子產品化加載器
  • 解決通過scriptsrc屬性引入子產品不受控制
  • 解決無法管理子產品依賴關系

CommonJs規範

以同步的模式加載子產品,在node環境中不會存在問題,但是在浏覽器環境會導緻效率低下
  • 一個檔案就是一個子產品
  • 每個子產品都有單獨的作用域
  • 通過module.exports 導出成員
  • 通過require函數載入子產品

AMD規範 + Require.js

異步模式加載子產品
  • 使用相對複雜
  • 子產品js檔案請求頻繁
  • 基本三方子產品都支援amd規範

define 定義子產品

// 參數1 子產品名字
// 參數2 聲明依賴項
// 參數3 callback函數 函數形參中接收定義依賴項 
// 傳回函數值為導出子產品
define('module1', ['jquery', './module2'], function($, module2) {
    return {
        start: function() {
            $('body').animate({
                margin: '200px'
            })
            module2()
        }
    }
})
           

require 加載子產品

require(['./module2'], function(module1) {
    module1.start()
})
           

子產品化标準規範——子產品化的最佳實踐

  • node
    遵循CommonJs規範
  • web環境
    遵循ES modules規範

ES modules

基本特性

  • 自動采用嚴格模式,忽略 ‘use strict’
  • 每個module 都運作在私用作用域當中
  • 通過CORS方式請求外部JavaScript子產品,不支援檔案通路
  • 标簽會延遲加載執行腳本

導入導出

直接導出

export const foo = 'es modules'
export function hello() {
    console.log('hello')
}
export class Person {}
           

檔案末尾導出

const foo = 'es modules'

function hello() {
    console.log('hello')
}
class Person {}
export {
    foo,
    hello,
    Person
}
           

别名導出

const foo = 'es modules'
export {
    foo as myFoo
}
           

導出預設成員

const foo = 'es modules'
export default foo
           

正常引入

import {
    foo,
    hello,
    Person
} from './module.js'
console.log(foo)
console.log(hello)
console.log(Person)
           

别名處理

import {
    foo as myFoo
} from './module.js'
console.log(myFoo)
           

引入預設成員

導入導出注意事項

  • 末尾導出非對象字面量導出, 而是固定的文法
  • 導出的為引用關系不是複制一個新的對象
  • 子產品引入是并不是對象解構,而是固定的文法
  • 子產品引入的子產品是隻讀的存在不能被修改

導入用法(原生)

  • 不能省略檔案字尾名
  • 不能省略index
後續通過打包工具打包子產品時可以省略
  • 不能省略 ./ 相對路徑,否則視為三方依賴包
  • 可以使用項目的絕對路徑
  • 可以使用網絡資源

導出一個子產品裡的所有方法

import * as mod from './module.js'
console.log(mod)
           
  • import 隻能存在最頂層不能嵌套函數或if語句
  • import 的 from 後面不能是變量

動态導入

import('./module.js').then(module => {
    console.log(module)
})
           

預設成員與命名成員同時導入

import name, {
    age
} from './module.js'
           

導出導入成員

// 常用于index 檔案,做導出零散檔案使用
export {
    foo,
    bar
}from './module.js'