子產品化概述——「子產品化」隻是思想
最主流的代碼整合方式,安裝功能不同劃分不同子產品。
- 子產品化演變過程
- 子產品化規範
- 常用的子產品化打包工具
- 基于子產品化工具建構現代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'