說說 Node 和 ES6 模塊化那些鮮爲人知的事兒

前言

模塊化已經成爲前端開發中的必然趨勢,模塊化帶來的好處以下:前端

  • 1.避免變量污染,命名衝突
  • 2.提升代碼複用率
  • 3.提升維護性
  • 4.依賴關係的管理

文章主要就node的commonjs規範和es6規範進行了淺層次的研究,探索node和es6模塊化一些鮮爲人知的小祕密。node

1.node模塊化

node模塊化遵循的是commonjs規範,CommonJs定義的模塊分爲: 模塊標識(module)、模塊導出(exports) 、模塊引用(require)。react

在node中,一個文件即一個模塊,使用exports和require來進行處理。es6

導出形式有2種,module.exports和exports導出。數組

// 第一種
module.exports = { // 須要導出的對象
 ....  
}
// 第二種
exports.a = 'hello world';
複製代碼

導入形式:緩存

const index = require('./index');
複製代碼

1.1.module是什麼?

那麼,問題來了,這個module究竟是什麼東西呢?bash

直接在文件中打印mouleapp

// index.js
console.log(module)
複製代碼

而後執行index.js異步

node index.js
複製代碼


module爲該模塊運行時生成的模塊標識對象,標識該模塊的一些信息。
module參數說明:

  • id爲當前文件
  • exports爲當前node文件模塊兒導出的值
  • parent爲父級調用的module對象,若是爲null則該文件沒有被調用
  • filename爲當前文件名
  • loaded是否被加載
  • children 引入模塊數組,數組項格式同module
  • paths爲node模塊兒 node_modules 模塊兒查找路徑,一直查到根目錄

在index.js中引入utils.js模塊,打印module對象,能夠看到children中包含了utils模塊的信息。模塊化

const utils = require("./utils");
複製代碼

module表示一個模塊運行時該模塊標識信息對象。

1.2.module.exports === exports?

exports表示該模塊運行時生成的導出對象。

module對象也有一個exports屬性,二者有什麼關係呢?

console.log(exports);
console.log(module.exports);
console.log(exports === module.exports);
複製代碼

咱們驚奇的發現module.exports與exports指向的都是空對象,且二者指向同一個對象!!!

如今咱們分別改寫module.exports和exports導出對象,而後在main.js中引入index.js,查看導入的對象。

index.js

module.exports = {
  name: "hello world"
};

exports.obj = {
  name: "welcome"
};
複製代碼

main.js

const obj = require("./index");

console.log(obj);
複製代碼

咱們驚奇的發現,導入的是module.exports導出的對象!!!

因爲咱們從新定義了modulex.export引用值,導入的是module.exports引用的對象,所以咱們能夠作出以下結論:

一個模塊真正導出的是module.exports的值,exports只是module.exports的一個引用。

1.3.模塊緩存與導出引用數據類型

問題:
1.屢次加載一個模塊,是否屢次執行源模塊呢?
2.導出引用數據類型時,修改導出對象的引用屬性值,那麼源模塊中屬性值是否發生改變呢?

咱們在main.js中修改導入對象name屬性,而後在index.js中異步打印導出對象。

index.js

const obj = {
  name: "hello world"
};

setTimeout(() => {
  console.log("index");
  console.log(obj);
}, 500);

module.exports = obj;
複製代碼

main.js

const obj1 = require("./index");
const obj2 = require("./index");

obj1.name = "welcome";

console.log("obj1:");
console.log(obj1);
console.log("obj2:");
console.log(obj2);
複製代碼

能夠看到,即便咱們2次導入源模塊,可是源模塊只加載了一次,咱們修改導出模塊值,源模塊的值也發生了變化。

結論:

  • 1.導入模塊時,會緩存導入模塊,後續再導入該模塊,從緩存中取導入模塊。
  • 2.當導出引用類型數據時,導出對象與導入對象指向的是同一個對象。

1.4.最佳實踐

針對module.exports與exports,最佳用法以下:

1.導出對象時用module.exports

function add(x, y){
    return x+y;
}
function multiply(x, y){
    return x*y;
}
module.exports = {
    add,
    multiply
}
複製代碼

2.導出變量或方法時用exports

exports.name = 'hello world';
exports.func = function() {
    console.log('hello world');
}
複製代碼

exports與module.exports最好不要同時使用,exports不要從新賦值到新對象,不然引用無效。

1.5.node模塊化總結

針對node commonjs規範模塊化,總結以下:

  • 1.module對象爲模塊運行時生成的標識對象,提供模塊信息;
  • 2.exports爲模塊導出引用數據類型時,modulex.exports與exports指向的是同一對象,require導入的是module.exports導出的對象;
  • 3.同一模塊導入後存在模塊緩存,後續屢次導入從緩存中加載;
  • 4.源模塊的引用與導入該模塊的引用是同一對象;
  • 5.最好不要同時使用module.exports與exports,導出對象使用module.exports,導出變量使用exports。

2.es6模塊化

接下來咱們來看下es6模塊化規範,es6的模塊化經過export與import來處理。

常見的導出形式:

export default obj;

export const name = "hello world";
// export { name1, name2, …, };
複製代碼

導入形式:

import obj, { name } from "./exp/exports";
複製代碼

2.1.export

在es6中,一個文件表示一個模塊,模塊經過export向外暴露接口,export經常使用於向外導出多個變量。

正確寫法:

export var name = 1;
// 等價於
var name = 1;
export { name }

// 導出多個變量
var a = 1;
var b = 2;
var c = 3;
export { a, b, c }

// 導出對象
export const obj = {
    name: 'hello world'
};

// 導出函數
export function fun1() {
    console.log('hello world');
}

// 直接導出引入模塊
export * from './index';
複製代碼

錯誤寫法:

var name = 1;
export name;
複製代碼

同時 引入模塊時,import導入變量名稱須要與export導出的變量一一對應。

import { a, b, c } from './index';
複製代碼

2.2.export default

export default表示一個模塊默認的對外接口,一個模塊只能有一個export default。

export default是export的語法糖,表示導出一個default接口。

const obj = {
  name: "hello world"
};

export default obj;
// 等價於
export { obj as default }; // as表示導出別名爲default的接口
複製代碼

引入默認導出

import obj from './index';
// 等價於
import { default as obj } from './index';
複製代碼

2.3.import * as xx

as表示別名,在import和export中能夠這樣用:

export

export { name as hello };
複製代碼

import

import { hello as name } from './index';
複製代碼

咱們有時候也會看到import * 和 export * 形式。 import

import * as obj './index';
複製代碼

看個例子,index.js導出obj和name變量,默認導出arr數組,在app.js中經過import * as xxx引入index.js。

index.js

const obj = {
  name: "hello world"
};

const name = "hello world";

const arr = [1, 2, 3];

export { obj, name };
export default arr;
複製代碼

app.js

import * as module from "./exp/exports";
console.log(module);
console.log(module.default);
console.log(module.name);
console.log(module.obj);
複製代碼

能夠看出咱們引入的是一個Module對象,描述該模塊導出的信息。
其中default表示默認導出,對應數組arr,name和obj分別表示導出的name和obj變量。

2.4.屢次導入和導出引用數據類型

當屢次導出對象變量,且修改導出變量對象時,會發生什麼呢?

index.js

const obj = {
  name: "hello world"
};

console.log("加載了index.js");
setTimeout(() => {
  console.log("index:");
  console.log(obj);
}, 500);

export default obj;
複製代碼

app.js

import obj1 from "./exp/index";
import obj2 from "./exp/index";

obj1.name = "welcome";
console.log("obj1:");
console.log(obj1);
console.log("obj2:");
console.log(obj2);
複製代碼

查看結果

不難看出,index.js組件import了2次,只加載了1次,而且源對象obj發生了改變。

結論:

  • 1.同一模塊import屢次,只會執行1次。
  • 2.導出引用數據類型時,導出對象與導入對象指向同一個變量,修改導出變量對象,源對象也會發生改變。

2.5.最佳實踐

1.當導出一個變量時,推薦使用export default。
例如:react中導出組件時,通常都使用export default。

// main.jsx

import React from 'react';

 class Main extends React.Component{
  render() {
    return (
      <div>Hello World.</div>
    )
  }
}

export default Main;
複製代碼

2.當導出多個變量時,推薦使用export。

// math.js

function add(a, b) {
  return a + b;
}

function reduce(a, b) {
  return a - b;
}

function multiply(a, b) {
  return a * b;
}

export { add, reduce, multiply };
複製代碼

3.當須要導出多個變量且須要默認導出時,能夠同時使用export和export default。

const obj = {
  name: "hello world"
};

const name = "hello world";

export { obj, name };
export default function getName() {
  return obj.name;
}
複製代碼

2.6.es6模塊化總結

針對es6規範模塊化,總結以下:

  • 1.es6經過export和import實現導出導入功能;
  • 2.es6 export支持導出多個變量,export default是export形式的語法糖,表示導出default接口;
  • 3.import * as xx from 'xx.js'導入的是Module對象,包含default接口和其餘變量接口;
  • 4.多個模塊導入屢次,只會執行一次;
  • 5.導出引用數據類型時,導出對象與導入對象指向同一個變量,修改導出變量對象,源對象也會發生改變。
  • 6.導出單個變量建議使用export default,導出多個變量使用export。

結語

以上就是博主探索的關於node和es6模塊化的一些小知識,以爲有收穫的能夠關注一波點贊一波,碼字不易,萬分感謝。

相關文章
相關標籤/搜索