JavaScript筆記之import和require的區(qū)別與對比
前言
在 JavaScript 中,import 和 require 都用于引入模塊,但它們來自不同的模塊規(guī)范,在語法、加載時機(jī)、作用域、生態(tài)支持等方面差異很大。
1.模塊系統(tǒng)不同
require- CommonJS 規(guī)范
// Node.js 最早的模塊系統(tǒng)(服務(wù)器端)
const fs = require('fs');
const _ = require('lodash');
import- ES6 模塊規(guī)范
// 瀏覽器/現(xiàn)代 Node.js 環(huán)境
import fs from 'fs'; // 默認(rèn)導(dǎo)入
import { readFile } from 'fs'; // 命名導(dǎo)入
import * as fsModule from 'fs'; // 全部導(dǎo)入
| 規(guī)范 | 代表 | 出現(xiàn)背景 |
|---|---|---|
| CommonJS | require | Node.js 最早的模塊系統(tǒng)(服務(wù)器端) |
| ES Module(ESM) | import | ECMAScript 官方標(biāo)準(zhǔn)(瀏覽器 & Node) |
?? 核心結(jié)論:
require是 運(yùn)行時模塊加載機(jī)制,import是 編譯期模塊聲明機(jī)制。
2.加載時機(jī)與特性對比
require:運(yùn)行時加載
const moduleA = require('./a') // 執(zhí)行到這里才加載
特點(diǎn):
- 同步加載
- 執(zhí)行順序 = 代碼執(zhí)行順序
- 可條件、可動態(tài)
import:編譯時加載(靜態(tài)分析)
import moduleA from './a'
特點(diǎn):
- 在代碼執(zhí)行前就確定依賴關(guān)系
- 可被 bundler / JS 引擎優(yōu)化
- 支持 Tree Shaking
?? 這是 Vite / Rollup / Webpack 能做 Tree Shaking 的根本原因
| 特性 | require (CommonJS) | import (ES6) |
|---|---|---|
| 加載時機(jī) | 運(yùn)行時同步加載 | 編譯時靜態(tài)分析 |
| 位置要求 | 可在代碼任意位置 | 必須位于模塊頂部(除動態(tài)導(dǎo)入) |
| 靜態(tài)分析 | 不支持 Tree Shaking | 支持 Tree Shaking |
| 異步支持 | 同步加載 | 靜態(tài)導(dǎo)入同步,動態(tài)導(dǎo)入異步 |
| 懶加載 | 需配合特定語法 | 原生支持 import() 懶加載 |
import 只能寫在頂層
? 說的是:import x from 'y'import() 可以寫在任何地方
? 它是一個返回 Promise 的函數(shù)調(diào)用
3.懶加載實(shí)現(xiàn)方式對比
require的懶加載(Webpack 特定)
// 方式1:require.ensure (Webpack 特定)
const Home = resolve => {
require.ensure(['./views/Home.vue'], () => {
resolve(require('./views/Home.vue'));
});
};
// 方式2:動態(tài) require (Webpack)
const About = () => {
return new Promise(resolve => {
require(['./views/About.vue'], resolve);
});
};
import的懶加載(ES6 標(biāo)準(zhǔn))
// 標(biāo)準(zhǔn) ES6 動態(tài)導(dǎo)入
const Home = () => import('./views/Home.vue');
// 更復(fù)雜的懶加載配置
const UserProfile = () => ({
component: import('./UserProfile.vue'),
loading: LoadingComponent,
delay: 200,
timeout: 3000
});
4.實(shí)際打包效果
require的打包
示例 :require
// main.js
const utils = require('./utils')
utils.a()
打包結(jié)果(簡化)
function a() {}
function b() {}
const utils = { a, b }
utils.a()
?? 結(jié)論:
?? require 整包引入,無法安全刪除 b
import的打包
示例 :靜態(tài) import
// utils.js
export function a() {}
export function b() {}
// main.js
import { a } from './utils'
a()
打包結(jié)果(簡化)
function a() {}
// b 被刪除(Tree Shaking)
a()
?? 結(jié)論:
?? import 能在打包階段精準(zhǔn)刪除未使用代碼。
5、代碼拆包(code splitting)差異
import(動態(tài))
import('./About.vue')
打包結(jié)果(Webpack / Vite):
// 主 bundle
function loadAbout() {
return __loadChunk__('about').then(...)
}
// about.chunk.js(獨(dú)立) export default About
?? 天然支持拆包
require(動態(tài))
require('./About.vue')
?? 結(jié)果:
- Webpack:只能整體打包進(jìn)主 bundle
- Rollup / Vite:直接報(bào)錯或不支持
?? 無法可靠拆包
6、運(yùn)行時代碼結(jié)構(gòu)差異(很重要)
require 打包后(CommonJS Runtime)
(function(modules) {
function __webpack_require__(id) {
// 同步加載
}
})(modules)
特點(diǎn):
- 同步執(zhí)行
- module.exports
- 需要模擬 CommonJS 環(huán)境
import 打包后(ESM Runtime)
Webpack(轉(zhuǎn)換后)
__webpack_require__.d(exports, {
a: () => a
})
Vite(生產(chǎn))
import { a } from './chunk.js'
特點(diǎn):
- 支持 live binding
- 更貼近瀏覽器原生模塊
- 運(yùn)行時代碼更少
Vite 項(xiàng)目里的一個“隱形差異”(很重要)
Vite 的策略
- 開發(fā)環(huán)境:原生 ESM(幾乎不打包)
- 生產(chǎn)環(huán)境:Rollup 打包
結(jié)果:
import { debounce } from 'lodash-es'
?? 只打進(jìn) debounce
const _ = require('lodash')
?? 整個 lodash 被打包
包體積 & 性能對比(真實(shí)工程影響)
| 維度 | import | require |
|---|---|---|
| 主包體積 | 更小 | 更大 |
| Tree Shaking | ? | ? |
| 懶加載 | ?(import()) | ? |
| 執(zhí)行效率 | 更優(yōu) | 較差 |
| 構(gòu)建器優(yōu)化 | 極佳 | 受限 |
?? 在大型 Vue 項(xiàng)目里,差距可能是 幾十 KB ~ 數(shù)百 KB
7.現(xiàn)代項(xiàng)目中的使用場景
Node.js 項(xiàng)目
// ES6 模塊(Node.js 13+,package.json 中 type: "module")
import express from 'express';
import { createServer } from 'http';
// 或者 CommonJS(傳統(tǒng))
const express = require('express');
const http = require('http');
Vue/React 項(xiàng)目
// Vue Router 懶加載(推薦)
const router = new VueRouter({
routes: [
{
path: '/dashboard',
component: () => import('./views/Dashboard.vue') // ES6 import
}
]
});
// React 懶加載
const Dashboard = React.lazy(() => import('./components/Dashboard'));
混合使用(不推薦但可能遇到)
// 在 ES6 模塊中導(dǎo)入 CommonJS 模塊
import _ from 'lodash'; // lodash 是 CommonJS 模塊
// 在 CommonJS 模塊中導(dǎo)入 ES6 模塊(Node.js)
const fs = require('fs');
const es6Module = await import('./es6-module.mjs'); // 需要異步
// Webpack 環(huán)境中可以混合
const oldModule = require('./old-module.js'); // CommonJS
const newModule = import('./new-module.js'); // ES6
8.性能對比
// 測試示例:加載 10 個模塊
const modules = ['module1', 'module2', 'module3'];
// require - 同步,阻塞執(zhí)行
console.time('require');
modules.forEach(name => {
const module = require(`./${name}.js`);
});
console.timeEnd('require');
// import - 異步,非阻塞
console.time('import');
const promises = modules.map(name => import(`./${name}.js`));
await Promise.all(promises);
console.timeEnd('import');
9.Tree Shaking 差異
import支持靜態(tài)分析
// 只導(dǎo)入需要的部分,打包時未使用的代碼會被移除
import { Button, Input } from 'antd'; // Tree Shaking 生效
import 'antd/dist/antd.css';
// 動態(tài)導(dǎo)入也支持 Tree Shaking
const { Modal } = await import('antd');
為什么import能 Tree Shaking?
import { a } from './utils'
構(gòu)建器在 編譯階段 就知道:
- 引入了哪個模塊
- 使用了哪個導(dǎo)出
- 依賴關(guān)系是 靜態(tài)確定的
require不支持 Tree Shaking
// 整個模塊都會被加載
const antd = require('antd'); // 整個 antd 包都會被包含
const Button = antd.Button;
為什么require不行?
const mod = require('./' + name)
構(gòu)建器 無法確定:
- 到底 require 哪個文件
- require 的返回結(jié)構(gòu)
- 是否有副作用
?? 只能保守處理:全留
10.總結(jié)選擇建議
| 場景 | 推薦使用 | 原因 |
|---|---|---|
| 現(xiàn)代前端項(xiàng)目 | import | ES6 標(biāo)準(zhǔn),支持 Tree Shaking,更好的懶加載 |
| Node.js 新項(xiàng)目 | import (ES6 模塊) | 官方推薦,更好的異步支持 |
| Node.js 舊項(xiàng)目 | require | 保持兼容性 |
| 路由懶加載 | import() | 語法簡潔,標(biāo)準(zhǔn)支持 |
| 條件加載 | import() | 異步,可配合條件判斷 |
| 需要同步加載 | require 或靜態(tài) import | 立即需要模塊時 |
11.最佳實(shí)踐示例
// 1. 主應(yīng)用使用靜態(tài)導(dǎo)入
import Vue from 'vue';
import Router from 'vue-router';
// 2. 路由使用動態(tài)導(dǎo)入懶加載
const routes = [
{
path: '/',
component: () => import('./views/Home.vue')
},
{
path: '/about',
component: () => import('./views/About.vue')
}
];
// 3. 條件加載(按需加載)
if (user.needsAdminPanel) {
const AdminPanel = await import('./admin/AdminPanel.vue');
}
// 4. 預(yù)加載(提高用戶體驗(yàn))
const preloadModules = [
import('./views/Products.vue'), // 預(yù)加載可能訪問的頁面
import('./views/Contact.vue')
];
核心結(jié)論:
- 現(xiàn)代前端開發(fā)優(yōu)先使用 import/export
- 懶加載使用動態(tài) import()
- require 主要用于古早 Node.js 傳統(tǒng)項(xiàng)目或兼容性需求
總結(jié)
到此這篇關(guān)于JavaScript筆記之import和require區(qū)別與對比的文章就介紹到這了,更多相關(guān)JS import和require區(qū)別與對比內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript實(shí)現(xiàn)判斷時間間隔是否連續(xù)為一天
這篇文章主要為大家詳細(xì)介紹了如何使用JavaScript實(shí)現(xiàn)判斷時間間隔是否連續(xù)為一天,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2024-01-01
微信小程序?qū)崿F(xiàn)發(fā)送驗(yàn)證碼按鈕效果
這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)發(fā)送驗(yàn)證碼按鈕效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
Uni-app跨平臺開發(fā)應(yīng)用入門實(shí)戰(zhàn)
這篇文章主要為大家介紹了Uni-app跨平臺開發(fā)應(yīng)用入門實(shí)戰(zhàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
JS 精確統(tǒng)計(jì)網(wǎng)站訪問量的實(shí)例代碼
這篇文章介紹了JS精確統(tǒng)計(jì)網(wǎng)站訪問量的實(shí)例代碼,有需要的朋友可以參考一下2013-07-07
javascript輕松實(shí)現(xiàn)當(dāng)鼠標(biāo)移開時已彈出子菜單自動消失
本文為大家詳細(xì)介紹下使用javascript實(shí)現(xiàn)當(dāng)鼠標(biāo)移開時已彈出子菜單自動消失,具體如下,感興趣的朋友不要錯過2013-12-12

