JavaScript笔记之import和require的区别与对比
作者:爱睡觉的猫在睡觉
在JavaScript中,require和import都用于模块导入,这篇文章主要介绍了JavaScript笔记之import和require区别与对比的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
前言
在 JavaScript 中,import 和 require 都用于引入模块,但它们来自不同的模块规范,在语法、加载时机、作用域、生态支持等方面差异很大。
1.模块系统不同
require- CommonJS 规范
// Node.js 最早的模块系统(服务器端)
const fs = require('fs');
const _ = require('lodash');
import- ES6 模块规范
// 浏览器/现代 Node.js 环境
import fs from 'fs'; // 默认导入
import { readFile } from 'fs'; // 命名导入
import * as fsModule from 'fs'; // 全部导入
| 规范 | 代表 | 出现背景 |
|---|---|---|
| CommonJS | require | Node.js 最早的模块系统(服务器端) |
| ES Module(ESM) | import | ECMAScript 官方标准(浏览器 & Node) |
👉 核心结论:
require是 运行时模块加载机制,import是 编译期模块声明机制。
2.加载时机与特性对比
require:运行时加载
const moduleA = require('./a') // 执行到这里才加载
特点:
- 同步加载
- 执行顺序 = 代码执行顺序
- 可条件、可动态
import:编译时加载(静态分析)
import moduleA from './a'
特点:
- 在代码执行前就确定依赖关系
- 可被 bundler / JS 引擎优化
- 支持 Tree Shaking
📌 这是 Vite / Rollup / Webpack 能做 Tree Shaking 的根本原因
| 特性 | require (CommonJS) | import (ES6) |
|---|---|---|
| 加载时机 | 运行时同步加载 | 编译时静态分析 |
| 位置要求 | 可在代码任意位置 | 必须位于模块顶部(除动态导入) |
| 静态分析 | 不支持 Tree Shaking | 支持 Tree Shaking |
| 异步支持 | 同步加载 | 静态导入同步,动态导入异步 |
| 懒加载 | 需配合特定语法 | 原生支持 import() 懒加载 |
import 只能写在顶层
⛔ 说的是:import x from 'y'import() 可以写在任何地方
✅ 它是一个返回 Promise 的函数调用
3.懒加载实现方式对比
require的懒加载(Webpack 特定)
// 方式1:require.ensure (Webpack 特定)
const Home = resolve => {
require.ensure(['./views/Home.vue'], () => {
resolve(require('./views/Home.vue'));
});
};
// 方式2:动态 require (Webpack)
const About = () => {
return new Promise(resolve => {
require(['./views/About.vue'], resolve);
});
};
import的懒加载(ES6 标准)
// 标准 ES6 动态导入
const Home = () => import('./views/Home.vue');
// 更复杂的懒加载配置
const UserProfile = () => ({
component: import('./UserProfile.vue'),
loading: LoadingComponent,
delay: 200,
timeout: 3000
});
4.实际打包效果
require的打包
示例 :require
// main.js
const utils = require('./utils')
utils.a()
打包结果(简化)
function a() {}
function b() {}
const utils = { a, b }
utils.a()
📌 结论:
👉 require 整包引入,无法安全删除 b
import的打包
示例 :静态 import
// utils.js
export function a() {}
export function b() {}
// main.js
import { a } from './utils'
a()
打包结果(简化)
function a() {}
// b 被删除(Tree Shaking)
a()
📌 结论:
👉 import 能在打包阶段精准删除未使用代码。
5、代码拆包(code splitting)差异
import(动态)
import('./About.vue')
打包结果(Webpack / Vite):
// 主 bundle
function loadAbout() {
return __loadChunk__('about').then(...)
}
// about.chunk.js(独立) export default About
📌 天然支持拆包
require(动态)
require('./About.vue')
⚠️ 结果:
- Webpack:只能整体打包进主 bundle
- Rollup / Vite:直接报错或不支持
👉 无法可靠拆包
6、运行时代码结构差异(很重要)
require 打包后(CommonJS Runtime)
(function(modules) {
function __webpack_require__(id) {
// 同步加载
}
})(modules)
特点:
- 同步执行
- module.exports
- 需要模拟 CommonJS 环境
import 打包后(ESM Runtime)
Webpack(转换后)
__webpack_require__.d(exports, {
a: () => a
})
Vite(生产)
import { a } from './chunk.js'
特点:
- 支持 live binding
- 更贴近浏览器原生模块
- 运行时代码更少
Vite 项目里的一个“隐形差异”(很重要)
Vite 的策略
- 开发环境:原生 ESM(几乎不打包)
- 生产环境:Rollup 打包
结果:
import { debounce } from 'lodash-es'
👉 只打进 debounce
const _ = require('lodash')
👉 整个 lodash 被打包
包体积 & 性能对比(真实工程影响)
| 维度 | import | require |
|---|---|---|
| 主包体积 | 更小 | 更大 |
| Tree Shaking | ✅ | ❌ |
| 懒加载 | ✅(import()) | ❌ |
| 执行效率 | 更优 | 较差 |
| 构建器优化 | 极佳 | 受限 |
📌 在大型 Vue 项目里,差距可能是 几十 KB ~ 数百 KB
7.现代项目中的使用场景
Node.js 项目
// ES6 模块(Node.js 13+,package.json 中 type: "module")
import express from 'express';
import { createServer } from 'http';
// 或者 CommonJS(传统)
const express = require('express');
const http = require('http');
Vue/React 项目
// 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 模块中导入 CommonJS 模块
import _ from 'lodash'; // lodash 是 CommonJS 模块
// 在 CommonJS 模块中导入 ES6 模块(Node.js)
const fs = require('fs');
const es6Module = await import('./es6-module.mjs'); // 需要异步
// Webpack 环境中可以混合
const oldModule = require('./old-module.js'); // CommonJS
const newModule = import('./new-module.js'); // ES6
8.性能对比
// 测试示例:加载 10 个模块
const modules = ['module1', 'module2', 'module3'];
// require - 同步,阻塞执行
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支持静态分析
// 只导入需要的部分,打包时未使用的代码会被移除
import { Button, Input } from 'antd'; // Tree Shaking 生效
import 'antd/dist/antd.css';
// 动态导入也支持 Tree Shaking
const { Modal } = await import('antd');
为什么import能 Tree Shaking?
import { a } from './utils'
构建器在 编译阶段 就知道:
- 引入了哪个模块
- 使用了哪个导出
- 依赖关系是 静态确定的
require不支持 Tree Shaking
// 整个模块都会被加载
const antd = require('antd'); // 整个 antd 包都会被包含
const Button = antd.Button;
为什么require不行?
const mod = require('./' + name)
构建器 无法确定:
- 到底 require 哪个文件
- require 的返回结构
- 是否有副作用
👉 只能保守处理:全留
10.总结选择建议
| 场景 | 推荐使用 | 原因 |
|---|---|---|
| 现代前端项目 | import | ES6 标准,支持 Tree Shaking,更好的懒加载 |
| Node.js 新项目 | import (ES6 模块) | 官方推荐,更好的异步支持 |
| Node.js 旧项目 | require | 保持兼容性 |
| 路由懒加载 | import() | 语法简洁,标准支持 |
| 条件加载 | import() | 异步,可配合条件判断 |
| 需要同步加载 | require 或静态 import | 立即需要模块时 |
11.最佳实践示例
// 1. 主应用使用静态导入
import Vue from 'vue';
import Router from 'vue-router';
// 2. 路由使用动态导入懒加载
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. 预加载(提高用户体验)
const preloadModules = [
import('./views/Products.vue'), // 预加载可能访问的页面
import('./views/Contact.vue')
];
核心结论:
- 现代前端开发优先使用 import/export
- 懒加载使用动态 import()
- require 主要用于古早 Node.js 传统项目或兼容性需求
总结
到此这篇关于JavaScript笔记之import和require区别与对比的文章就介绍到这了,更多相关JS import和require区别与对比内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
