天天看點

子產品化開發與規範化标準子產品化開發與規範化标準

子產品化開發與規範化标準

一.子產品化規範

1.1

Nodejs

中的**

CommonJS

**規範:

1.一個檔案就是一個子產品

2.每個子產品都有單獨的作用域

3.通過

module.exports導出

成員

4.通過

require函數載入

子產品

5.以

同步

模式加載子產品

1.2

浏覽器

中的**

AMD

**規範(require.js)

1.用

define

定義子產品

2.用

require

加載子產品

AMD使用起來相對複雜,子產品JS檔案請求頻繁

define('conor',['Module1', ‘Module2’], function (Module1, Module2) {
  function foo () {
    Module1.test();
  }
  return {foo: foo}
});
require(['conor'], function (math) {
  math.add(2, 3);
});
           

1.3

浏覽器

中的**

CMD

**規範(淘寶推出的seaJs)

1.CMD和AMD是同期出現的,使用也類似. 不過

CMD文法上更貼近CommonJS

2.對于依賴的子產品

AMD是提前執行

CMD是延遲執行

。不過RequireJS從2.0開始,也改成可以延遲執行(根據寫法不同,處理方式不通過)。

3.

AMD推崇依賴前置

(在定義子產品的時候就要聲明其依賴的子產品),CMD推崇依賴就近(隻有在用到某個子產品的時候再去require——按需加載)。
define(function (requie, exports, module) {
    //依賴可以就近書寫
    var a = require('./a');
    a.test();
    //軟依賴
    if (status) {
        var b = requie('./b');
        b.test();
    }
});
           

1.4

ES6

中的推出的**

ESModule

**規範

目前最主流的前端子產品化方案,也是接下來主要學習的

二.ESModule文法特性

基本特性:

1.ESM

預設是嚴格模式忽略'use strict',

嚴格模式下this指向undefined`

2.ESM的每個子產品都有

單獨的私有作用域

3.ESM是通過

cors

的方式請求外部JS子產品的

4.ESM會

延遲執行

腳本
<!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>Document</title>
</head>
<body>
    <!-- 通過給script添加type="module",就可以用ESModule的标準執行其中的JS代碼了 -->
    <script type="module">
        console.log('ESModule文法特性');
    </script>

    <!-- ESM預設是嚴格模式忽略'use strict',嚴格模式下this指向undefined -->
    <script type="module">
        var foo = '123'
        console.log(foo);
        console.log(this);
    </script>

    <!-- 非嚴格模式this指向window -->
    <script>
        console.log(this);
    </script>
    
    <!-- ESM的每個子產品都有單獨的私有作用域 -->
    <script type="module">
        console.log(foo);//報錯
    </script>

    <!-- ESM是通過cors的方式請求外部JS子產品的 -->
    <script type="module" src="https://code.jquery.com/jquery-3.0.0.min.js"></script>

    <!-- 正常情況下的script代碼會阻塞p标簽的渲染,而ESM會延遲執行腳本 -->
    <script>
        alert('123')
    </script>
    
    <script type="module">
        alert('123')
    </script>
    
    <p>需要顯示的内容</p>
</body>
</html>
           

2.1 ES Modules導入導出

用export導出,import導入.共有以下幾種方式

2.1.1 export 單個導出

//子產品導出檔案
export var name = 'foo module'
export function hello () {
    console.log('hello')
}
export class Person {}
           
//子產品導入檔案
//import {name,hello,Person} from './module' 		//報錯
//import {name,hello,Person} from 'module.js'   //報錯,以字母開頭會被認為是第三方子產品
//子產品引入必須是完整的./開頭的相對路徑  or /開頭的覺得路徑 or 一個cdn連結
import {name,hello,Person} from './module.js' 
console.log(name)
console.log(hello)
console.log(Person)

//或者
import * as mod from './module.js' 
console.log(mod.name)
console.log(mod.hello)
console.log(mod.Person)
           

2.1.2 export 統一導出

//子產品導出檔案
var name = 'foo module'
function hello () {
    console.log('hello Boy')
}
class Person {}

// as重命名,如果是default的話導入的時候就必須重命名
// 注意:這裡導出的花括符不是對象的字面量,import的引入也不是對象的解構.而是ESM的一種固有的寫法.
// 如果真的要導出一個對象的話可以用export default
export{name as default,hello as helloBoy,Person}
           
//子產品導入檔案
import {default as fooName,helloBoy,Person} from './module.js' 
console.log(fooName)
console.log(helloBoy)
console.log(Person)
           

2.1.3 預設導出

// 子產品導出檔案
var name = 'foo module'
var obj = {name}
// 這樣寫的話,可以用任意名接收這個對象
export default obj
           
//子產品導入檔案
//注意:這裡導入的nameobj不是被拷貝的值而是一個位址,它會随着導出子產品中變量的變化而變化.且這個值無法修改.
import nameobj from './module.js' 
console.log(nameobj.name)
           

2.1.4 隻執行子產品而不導入

//子產品導入檔案
import {} from './module.js' 
//或者
import './module.js' 
           

2.1.5 運作時按需使用子產品

var modulePath = './module.js'
import {name} from modulePath; //報錯

if(true){
	import {name} from './module.js'; //報錯
}

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

2.1.6 預設導出和正常導出一起使用

//子產品導出檔案
var name = 'foo module'
function hello () {
    console.log('hello Boy')
}
class Person {}
export{hello as helloBoy,Person}
export default name
           
//子產品導入檔案
import {helloBoy,Person,default as name} from './module.js' 
//或者
import name,{helloBoy,Person} from './module.js' 
console.log(name)
console.log(Person)
           

2.1.7 導出導入成員

//子產品導入檔案
export {helloBoy,Person,default as name} from './module.js' 
//這裡就通路不到helloBoy,Person,name
           

三. ES Modules相容性

将浏覽器中不識别的esmodule代碼去交給babel轉換,然後對那些import下來的檔案去進行ajax請求把請求下來的檔案再去進行轉換進而支援esm .

但是這樣子的話在那些支援的環境中就會運作兩次代碼. 這個問題可以通過添加

nomodule

屬性解決 .

這種方式效率很低下,隻适合在開發環境中.

在生産環境中還是要預先把代碼都編譯好再部署

.
<!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>Document</title>
</head>
<body>
    <script nomodule src="https://unpkg.com/[email protected]/dist/babel-browser-build.js"></script>
    <script nomodule src="https://unpkg.com/[email protected]/dist/browser-es-module-loader.js"></script>
    <script nomodule src="https://unpkg.com/[email protected]/dist/polyfill.min.js"></script>
    <script type="module">
        import {foo} from './module.js';
        console.log(foo);
    </script>
</body>
</html>
           

四.在node中使用ES Modules

在node中如果要使用ESM,js檔案名字尾需要用

.mjs

在node12.10.0版本中可以在package.json中寫入一個

type:"module"

字段這樣ESM的字尾可以正常的用.js但是CommonJS的話要用

.cjs

字尾

ESM中可以導入CommonJS子產品類但

不能在commonjs中require載入ESM

CommonJS子產品類始終導出一個

預設成員

,是以接收的時候不能用

{}

node的内置子產品相容了ESM的提取成員方式

運作node --experimental-modules app.mjs

//node中使用ESM

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

import fs from 'fs';
fs.writeFileSync('./foo.txt','es module working!');

// 内置子產品相容了ESM的提取成員方式
import {writeFileSync} from 'fs';
writeFileSync('./bar.txt','es module working!');

import _ from 'lodash';
console.log(_.camelCase('SSssSS'))

//不支援,因為第三方子產品都是導出預設成員
// import { camelCase } from 'lodash'
// console.log(camelCase('ES Modules'))

// ESM中可以導入CommonJS子產品類
// import mod from './commonjs.js'
// console.log(mod)

//不能直接提取
// import {foo} from './commonjs.js'
// console.log(foo) //報錯

export const foo = 'es module export value'
           
//node中使用ESM

// module.exports = {
//     foo:'commonjs exports value'
// }

// 不能在node的commonjs中通過require載入ESM,但是在webpack中可以
// const mod = require('./index.mjs')
// console.log(mod)

//CommonJS始終導出一個預設成員,是以接收的時候不能用{}
exports.off="commonjs exports value"
           
//ESM中使用node

//加載子產品函數:require
//子產品對象:module
//導出對象别名:exports
//目前檔案的絕對路徑:__filename
//目前檔案所在目錄:__dirname

//ESM中沒有CommonJS中的以上這些子產品全局成員,前三種可以用import和export代替
//__filename和__dirname可以通過 import.meta.url
console.log(import.meta.url)
import {fileURLToPath} from 'url';
const __filename = fileURLToPath(import.meta.url);
console.log(__filename)

//__dirname
import {dirname} from 'path';
const __dirname = dirname(__filename);
console.log(__dirname)
           

五.在node中使用ES Modules相容性問題

在node低版本中ESM同樣需要轉譯,這裡可以使用babel去處理.