require,import區別?

最近在用vue,裡面有使用require的方式引入文件,也可以import,感覺這兩個都是一樣的,在Google搜索了下,require是AMD規範的框架,import是nodeJS的一種語法,這是否意味著import可以取代require

-----------------------------------------------------------------------------

追問:我之前的問題我感覺還是沒有表達清楚我的思想,還有一點問題,seaJS,requireJS,import這些模塊載入的方式,是否都是基於node的模塊環境下的,都是對於對前端模塊化思想的實踐,只是執行的時間,執行的規範不同,載入方式的差別


遵循的模塊化規範不一樣

模塊化規範:即為 JavaScript 提供一種模塊編寫、模塊依賴和模塊運行的方案。誰讓最初的 JavaScript 是那麼的裸奔呢——全局變數就是它的模塊化規範。

require/exports 出生在野生規範當中,什麼叫做野生規範?即這些規範是 JavaScript 社區中的開發者自己草擬的規則,得到了大家的承認或者廣泛的應用。比如 CommonJS、AMD、CMD 等等。import/export 則是名門正派。TC39 制定的新的 ECMAScript 版本,即 ES6(ES2015)中包含進來。

出現的時間不同

require/exports 相關的規範由於野生性質,在 2010 年前後出生。AMD、CMD 相對命比較短,到 2014 年基本上就搖搖欲墜了。一開始大家還比較喜歡在瀏覽器上採用這種非同步小模塊的載入方式,但並不是銀彈。隨著 Node.js 流行和 Browsersify 的興起,運行時非同步載入逐漸被構建時模塊合併分塊所替代。Wrapper 函數再也不需要了。 2014 年 Webpack 還是新玩意,現在已經是前端必備神器了。

Browsersify、Webpack 一開始的目的就是打包 CommonJS 模塊。

CommonJS 作為 Node.js 的規範,一直沿用至今。由於 npm 上 CommonJS 的類庫眾多,以及 CommonJS 和 ES6 之間的差異,Node.js 無法直接兼容 ES6。所以現階段 require/exports 任然是必要且實必須的。出自 ES6 的 import/export 相對就晚了許多。被大家所熟知和使用也是 2015 年之後的事了。 這其實要感謝 babel(原來項目名叫做 6to5,後更名為 babel) 這個神一般的項目。由於有了 babel 將還未被宿主環境(各瀏覽器、Node.js)直接支持的 ES6 Module 編譯為 ES5 的 CommonJS —— 也就是 require/exports 這種寫法 —— Webpack 插上 babel-loader 這個翅膀才開始高飛,大家也才可以稱 " 我在使用 ES6! "

這也就是為什麼前面說 require/exports 是必要且必須的。因為事實是,目前你編寫的 import/export 最終都是編譯為 require/exports 來執行的。

require/exports 和 import/export 形式不一樣

require/exports 的用法只有以下三種簡單的寫法:

const fs = require("fs")
exports.fs = fs
module.exports = fs

而 import/export 的寫法就多種多樣:

import fs from "fs"
import {default as fs} from "fs"
import * as fs from "fs"
import {readFile} from "fs"
import {readFile as read} from "fs"
import fs, {readFile} from "fs"

export default fs
export const fs
export function readFile
export {readFile, read}
export * from "fs"

require/exports 和 import/export 本質上的差別

形式上看起來五花八門,但本質上:

  1. CommonJS 還是 ES6 Module 輸出都可以看成是一個具備多個屬性或者方法的對象;
  2. default 是 ES6 Module 所獨有的關鍵字,export default fs 輸出默認的介面對象,import fs from "fs" 可直接導入這個對象;
  3. ES6 Module 中導入模塊的屬性或者方法是強綁定的,包括基礎類型;而 CommonJS 則是普通的值傳遞或者引用傳遞。

1、2 相對比較好理解,3 需要看個例子:

// counter.js
exports.count = 0
setTimeout(function () {
console.log("increase count to", ++exports.count, "in counter.js after 500ms")
}, 500)

// commonjs.js
const {count} = require("./counter")
setTimeout(function () {
console.log("read count after 1000ms in commonjs is", count)
}, 1000)

//es6.js
import {count} from "./counter"
setTimeout(function () {
console.log("read count after 1000ms in es6 is", count)
}, 1000)

分別運行 commonjs.js 和 es6.js:

? test node commonjs.js
increase count to 1 in counter.js after 500ms
read count after 1000ms in commonjs is 0
? test babel-node es6.js
increase count to 1 in counter.js after 500ms
read count after 1000ms in es6 is 1


require / exports 是 CommonJS(在Node中實現) , import / export 是 ES2015 的模塊

使用 vue 必然會用到 webpack,webpack 1 對 CommonJS 默認支持,對 ES2015 也只要使用 babel 就可以了。

現在,推薦使用 ES2015 ,畢竟已經是標準了。


ECMAScript 6入門 阮老師的文章里有更加詳細的解釋,有空可以看看

---------

import靜態編譯,import的地址不能通過計算

require就可以,例如 const url = "a" + "b";

Import url 直接報錯了

require(url)不會報錯

所以require都會用在動態載入的時候


看了一下大家的回答怎麼沒人提這個:Imports are hoisted

比如這個例子:

// a.js
global.log = (str) =&> console.log(str);
import "./b";

// b.js
global.log("hello world");

由於 import 被提升所以這個例子會報 log undefined,用 babel 轉義一下會得到:

// a.js
"use strict";
require("./b");
global.log = function (str) {
return console.log(str);
};

// b.js
"use strict";
global.log("hello world");

更多參考:16. Modules


在vue裡面能夠使用import命令,並且可以通過Node.js起本地伺服器而不會報錯的原因是在於vue使用babel對ES6的模塊系統進行了編譯,將其轉換為CommonJS規範了

目前最新版本的Node仍然以CommonJS作為模塊規範,所以要使用ES6模塊系統暫時還是需要babel的

另外,CommonJS和ES6的模塊系統主要存在以下區別

如有兩個文件,a.js和b.js

CommonJS

  1. 通過require引入基礎數據類型時,屬於複製該變數。
  2. 通過require引入複雜數據類型時,數據淺拷貝該對象。
  3. 出現模塊之間的循環引用時,會輸出已經執行的模塊,而未執行的模塊不輸出(比較複雜)
  4. CommonJS模塊默認export的是一個對象,即使導出的是基礎數據類型

針對第一個,可以通過一個例子來說明

// a.js
let count = 1
let setCount = () =&> {
count++
}
setTimeout(() =&> {
console.log("a", count)
}, 1000)
module.exports = {
count,
setCount
}
// b.js
let obj = require("./a.js")

obj.setCount()
console.log("b", obj.count)

node b.js
b 1
a 2
可以看出,count在b.js文件中複製了一份,setCount只改變了a.js中count值

針對第二點,給出例子

// a.js
let obj = {
count: 1
}
let setCount = () =&> {
obj.count++
}
setTimeout(() =&> {
console.log("a", obj.count)
}, 1000)
module.exports = {
obj,
setCount
}
// b.js
let data = require("./a.js")

data.setCount()
console.log("b", data.obj.count)

node b.js
b 2
a 2
從以上可以看出,a.js和b.js實際上指向同一個obj對象

針對第三點,也可以給出一個例子

可以看看這篇博客的一個對於CommonJS和ES6模塊的總結。傳送門:commonjs模塊和es6模塊的區別 - unclekeith - 博客園

ES6模塊

  1. 不管是基礎(複雜)數據類型,都只是對該變數的動態只讀引用。動態在於一個模塊中變數的變化會影響到另一個模塊;只讀在於從某個模塊引入一個變數時,不允許修改該變數的值。對於複雜數據類型,可以添加屬性和方法,但是不允許指向另一個內存空間。
  2. 出現模塊之間的循環引用時,只要模塊存在某個引用,代碼就能夠執行。

針對第一點,可以給出實例。在改變變數時會報錯。

// b.js
export var a = 123
// a.js
import { a } from "./b.js"
a = 456

babel-node a.js
SyntaxError: "a" is read-only

針對第二點,這裡與CommonJS模塊機制不同的是,只要引用存在就可以執行代碼了。同樣可以看看上邊給出的一篇文章。


取代不了,上面說的都太抽象了,給你舉兩個常見的 case 你取代個試試看:

// case 1
if (bool) {
require("./foo")()
} else {
require("./bar")()
}

// case 2
for (const item of list) {
require(`./foo-${item}`)()
}


簡單來說import和require都JavaScript模塊規範,不過背後的實現區別還是蠻大的,而你在vue裡面使用import和require其實是沒有區別的(Webpack1),因為最後都會轉成require。


node 直接支持 require , 沒有直接支持 import 。

特別是 node8 ,已經支持 es6 絕大多數特性和 es7 的絕大多數特性,就是沒有 import。


JavaScript 標準參考教程(alpha)

ECMAScript 6入門

一起服用效果更好喲。

簡言之:import 是在編譯過程中執行,而common的require是同步。

還有import傳的是值引用,require是值拷貝。

----------------------------

import無論在node和瀏覽器上都不能直接使用吧,需要babel編譯


規範不一樣,require是CommonJs,AMD,CMD規範中定義的模塊請求方式,import是es6規範定義的模塊請求方式,從規範與實現定義來說 require是動態載入,import是靜態載入,從底層的運行來講,require是在程序運行的時候去解析而import是在編譯的時候去做解析請求包,require是請求整個包對象而import是只請求模塊中需要的請求的部分。現在import應該還只能算是ES6的語法規範,babel打包的話打出來應該還是跟require沒有本質區別的。


Using Node.js require vs. ES6 import/export

(我只是so的搬運工, 鏈接里的排名第一的答案寫得挺好的.


上面有同學講了歷史。我要講一下使用上的細微差別。import是read-only的。舉個例子

//// a.js
module.exports = 0;
//// main.js
var a0 = require("./a.js");
import a1 from "./a.js";
a0++; /// 1
a1++; ///報錯,babel直接編輯不過


推薦閱讀:

如果HTTP2普及了,Webpack、Rollup這種打包工具還有意義嗎?

TAG:前端開發 | 前端工程師 | Vuejs | webpack |