子產品化開發與規範化标準
一.子產品化規範
1.1 Nodejs
中的** CommonJS
**規範:
Nodejs
CommonJS
1.一個檔案就是一個子產品
2.每個子產品都有單獨的作用域
3.通過
module.exports導出
成員
4.通過
require函數載入
子產品
5.以
模式加載子產品
同步
1.2 浏覽器
中的** AMD
**規範(require.js)
浏覽器
AMD
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)
浏覽器
CMD
1.CMD和AMD是同期出現的,使用也類似. 不過2.對于依賴的子產品
CMD文法上更貼近CommonJS
,
AMD是提前執行
CMD是延遲執行
。不過RequireJS從2.0開始,也改成可以延遲執行(根據寫法不同,處理方式不通過)。
3.
(在定義子產品的時候就要聲明其依賴的子產品),CMD推崇依賴就近(隻有在用到某個子產品的時候再去require——按需加載)。
AMD推崇依賴前置
define(function (requie, exports, module) {
//依賴可以就近書寫
var a = require('./a');
a.test();
//軟依賴
if (status) {
var b = requie('./b');
b.test();
}
});
1.4 ES6
中的推出的** ESModule
**規範
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檔案名字尾需要用在node12.10.0版本中可以在package.json中寫入一個
.mjs
字段這樣ESM的字尾可以正常的用.js但是CommonJS的話要用
type:"module"
.cjs
字尾
ESM中可以導入CommonJS子產品類但
CommonJS子產品類始終導出一個
不能在commonjs中require載入ESM
,是以接收的時候不能用
預設成員
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去處理.