webpack深入浅出系列
ES6模块与CommonJS模块的主要区别
加载时机 :ES6模块
是编译时输出 ,即模块在编译阶段就已经确定了依赖关系;而CommonJS模块
是运行时才加载 ,即运行到某个require语句时才加载对应的模块。
(也就是说,es6的模块化是在编写程序的时候就开始有优化校验和静态分析,而commonjs只有在程序运行的时候再找所谓的对应关系)
语法差异 :ES6模块使用export
和import
进行模块的导出和导入;而CommonJS模块使用module.exports
和exports
导出,使用require
导入。
设计哲学 :ES6模块更注重静态分析和代码优化,而CommonJS模块更多考虑了运行时的行为。
es6模块导入需要完整的文件名后缀以及在script标签中写入type=module
,而CommonJS模块又不需要等等。更是由于前端开发中存在多种模块化标准(如 ES6 模块、CommonJS、AMD 等)以及不同类型的资源(如 JavaScript、CSS、图片等),Webpack工具 就通过自己的方式来统一管理这些模块和资源。
webpack基本概念
Webpack
是一个静态资源打包工具。
- 它会以一个或多个文件作为打包的入口,将我们整个项目所有文件编译组合成一个或多个文件输出出去。
- 输出的文件就是编译好的文件,就可以在浏览器引入运行了。
- 我们将 Webpack 输出的文件叫做 bundle(打包)。
在前端开发中,Bundle 是指通过打包工具(如 Webpack、Vite、Rollup 等)将项目的多个模块和资源文件(如 JavaScript、CSS、图片等)整合为一个或多个打包文件,以便在浏览器中更高效地加载和执行。
Bundle 的目的是优化代码的加载和执行,避免重复请求和依赖的冗余加载。通常,打包工具会根据依赖关系将模块整合在一起,进行以下几种优化处理:
- 合并文件:将分散的文件合并为一个或多个较少的文件,减少浏览器请求次数。
- 压缩代码:去除空格、注释,短化变量名,减少文件体积。
- 代码拆分(Code Splitting):根据页面访问情况,将代码拆分成多个 Bundle 文件,按需加载,提高首屏加载速度。
- Tree Shaking:移除未使用的代码,减少打包文件大小。
- 缓存优化:通过生成带有哈希值的文件名,确保修改后的文件在浏览器中被正确更新缓存。
示例
假设项目中有多个模块文件,如
index.js
、style.css
、utils.js
等。通过 Webpack 配置打包后,它们会被打包成一个或多个 Bundle 文件,比如main.bundle.js
、vendor.bundle.js
等,这些文件将被插入到 HTML 中。为什么 Bundle 重要?
- 提升加载效率:通过压缩和合并,减少网络请求数量。
- 提高代码复用性:共享库或通用功能可以打包到单独的文件中,多个页面共享使用。
- 便于代码维护 :打包工具会自动管理模块依赖,避免手动控制加载顺序或依赖冲突。
打包(Bundle)可以说是现代前端项目优化和模块管理的核心步骤,尤其在大型应用中表现尤为显著。
Webpack 本身功能是有限的:
只有两种模式以及多种配置
开发模式 :仅能编译 JS 中的 ES Module 语法
生产模式 :能编译 JS 中的 ES Module 语法,还能压缩优化JS 代码
Webpack 本身功能比较少,只能处理 js 资源,一旦遇到 css 等其他资源就会报错。
所以我们学习 Webpack,就是主要学习如何处理其他资源。
5 大核心概念
-
entry(入口)
指示 Webpack 从哪个文件开始打包
-
output(输出)
指示 Webpack 打包完的文件输出到哪里去,如何命名等
-
loader(加载器)
webpack 本身只能处理 js、json 等资源,其他资源需要借助 loader,Webpack 才能解析
-
plugins(插件)
扩展 Webpack 的功能
-
mode(模式)主要由两种模式:
开发模式:development
生产模式:production
Webpack 配置文件
在项目根目录下新建文件:webpack.config.js
javascript
module.exports = {
// 入口
entry: "",
// 输出
output: {},
// 加载器
module: {
rules: [],
},
// 插件
plugins: [],
// 模式
mode: "",
};
Webpack 是基于 Node.js 运行的,所以采用 Common.js 模块化规范
修改配置文件部分示例
javascript
// Node.js的核心模块,专门用来处理文件路径
const path = require("path");
module.exports = {
// 入口
// 相对路径和绝对路径都行
entry: "./src/main.js",
// 输出
output: {
// path: 文件输出目录,必须是绝对路径
// path.resolve()方法返回一个绝对路径
// __dirname 当前文件的文件夹绝对路径
path: path.resolve(__dirname, "dist"),
// filename: 输出文件名
filename: "main.js",
},
// 加载器
module: {
rules: [],
},
// 插件
plugins: [],
// 模式
mode: "development", // 开发模式
};
++此时不能处理样式等资源++
Webpack 能力都通过 webpack.config.js 文件进行配置,来增强 Webpack 的功能。
Webpack 是一种现代 JavaScript 应用的打包工具,通过将代码及其依赖项打包成静态文件,方便在浏览器中加载和运行。以下是 Webpack 的一些核心概念:
1. Entry(入口)
-
作用:Webpack 开始构建的起点,定义依赖关系图的根文件。
-
配置 :指定入口文件的路径,通常是应用的主文件,比如
index.js
。 -
多入口:支持多入口配置,用于生成多个 Bundle 文件,适合多页面应用。
-
例子 :
javascriptmodule.exports = { entry: './src/index.js', // 单入口 // 或者多入口 entry: { app: './src/app.js', admin: './src/admin.js' } };
2. Output(输出)
-
作用:定义 Webpack 如何命名、生成打包后的文件及输出路径。
-
主要参数 :
path
:打包文件的输出路径。filename
:打包后文件的名称。
-
例子 :
javascriptmodule.exports = { output: { path: __dirname + '/dist', filename: '[name].bundle.js' // [name] 用于多入口生成不同文件名 } };
3. Loaders(加载器)
-
作用:处理非 JavaScript 文件,如 CSS、图片、字体等,将它们转换为 Webpack 能够理解的模块。
-
常用 Loaders :
babel-loader
:将 ES6+ 转译成 ES5。css-loader
:负责将 Css 文件编译成 Webpack 能识别的模块,从而允许 JavaScript 中import
CSS 文件。style-loader
:会动态创建一个 Style 标签,里面放置 Webpack 中 Css 模块内容。也就是把 CSS 注入到 DOM 中。此时样式就会以 Style 标签的形式在页面上生效。file-loader
:处理文件资源,返回文件 URL。
-
例子 :
javascriptmodule.exports = { module: { rules: [ { test: /\.js$/, use: 'babel-loader' }, // 用来匹配 .css 结尾的文件 // use 数组里面 Loader 执行顺序是从右到左 { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] } };
4. Plugins(插件)
-
作用:执行范围更广的任务,如优化、压缩、资源管理等。
-
常用 Plugins :
HtmlWebpackPlugin
:生成 HTML 文件并自动注入打包后的资源。DefinePlugin
:在编译时创建全局常量。MiniCssExtractPlugin
:将 CSS 提取到单独文件。
-
例子 :
javascriptconst HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }) ] };
5. Mode(模式)
-
作用 :定义打包的模式,有
development
(开发)和production
(生产)两种模式。 -
影响 :
development
模式:启用代码映射和调试,未压缩的输出。production
模式:启用优化、Tree Shaking、代码压缩等。
-
例子 :
javascriptmodule.exports = { mode: 'development' // 或 'production' };
6. Chunk(代码块)
- 作用 :Webpack 根据模块之间的依赖关系生成的代码块。通常将主代码和第三方依赖(如
node_modules
)打包成不同的 Chunk,以实现更高的缓存和按需加载。 - 配置 :通过
optimization.splitChunks
来拆分代码块。
7. Module(模块)
- 作用:模块是应用的独立单元,Webpack 会递归解析入口文件所依赖的模块,形成依赖关系图。
- 支持的模块类型:ES6 模块、CommonJS、AMD 模块等。
8. Dependency Graph(依赖图)
- 作用:Webpack 会从入口文件出发递归构建所有模块的依赖关系图,确保所有依赖项都被打包在最终的输出文件中。
9. Tree Shaking
- 作用:移除未使用的代码,减少打包后的文件体积。
- 启用条件 :需要使用
ES6
模块语法(import
/export
),并且在production
模式下默认启用。
Webpack 的这些概念帮助开发者灵活地控制模块化、打包、优化等过程,在开发和生产环境中构建高效的前端应用。
开发模式介绍
开发模式顾名思义就是我们开发代码时使用的模式。
这个模式下我们主要做两件事:
- 编译代码,使浏览器能识别运行:
开发时我们有样式资源、字体图标、图片资源、html 资源等,webpack 默认都不能处理这些资源,所以我们要加载配置来编译这些资源 - 代码质量检查,树立代码规范
提前检查代码的一些隐患,让代码运行时能更加健壮。
提前检查代码规范和格式,统一团队编码风格,让代码更优雅美观。
其实模块化在前端程序设计中有很多的体现的地方
前端模块化是指将代码分解成独立、可复用的模块,以便于维护、扩展和复用。随着项目复杂度的增加,模块化显得尤为重要。前端的模块化可以分为以下几类:
1. 文件模块化
- 传统的文件划分 :根据功能将文件划分成不同文件夹,例如
components
、utils
、services
等,这种方式在项目初期非常普遍。 - 按页面/功能划分 :把每个页面作为一个模块,如
userPage.js
、adminPage.js
。在大型项目中,通常按功能划分多个目录层次,提升代码结构的清晰度。
2. 模块化方案
- ES6 模块 (ESM) :使用
import
和export
,这是目前最主流的模块化标准,得到了浏览器和 Node.js 的支持。 - CommonJS :通过
require
和module.exports
实现的模块化方案,主要用于 Node.js 后端开发。 - AMD (Asynchronous Module Definition) :通过
define
和require
来定义模块,适合于浏览器端的异步模块加载,经典库有RequireJS
。 - UMD (Universal Module Definition):兼容 AMD、CommonJS 和全局变量方式,适用于一些跨平台的库。
3. 组件化框架的模块化
- React :基于组件的开发模型,每个组件就是一个独立的模块。使用
Hooks
、Context
等可以在组件之间共享状态和逻辑。 - Vue :通过
SFC
(单文件组件) 将 HTML、CSS、JavaScript 集成在一个文件中,每个.vue
文件就是一个模块。 - Angular :采用模块化的结构,通过
NgModules
来组织代码。每个NgModule
是一组组件、指令、服务的集合。
4. CSS 模块化
- CSS Modules:生成局部作用域的 CSS,避免全局样式污染。
- 预处理器 (如 SASS/SCSS):提供嵌套、变量、混入等功能,让 CSS 更具模块化能力。
- CSS-in-JS :将 CSS 写在 JavaScript 中,如
styled-components
、Emotion
,适用于 React 等框架,可以将样式与逻辑模块化结合。
5. 打包工具的模块化
- Webpack :通过
entry
、output
、module
等配置实现模块化打包。loaders
和plugins
使得资源可以模块化地加载和处理。 - Vite:基于 ESM 的打包工具,启动速度快,适合现代化前端项目。
- Rollup:专注于打包库文件,生成更小的文件体积,非常适合编写 npm 库时使用。
6. 路由模块化
- 动态路由加载:根据用户访问的路由按需加载对应模块,可以减少初次加载时间。
- 嵌套路由 :将不同的页面划分为嵌套路由结构,方便分层控制页面和组件,常见于
Vue Router
和React Router
。
7. 状态管理模块化
- Redux :把状态拆分为不同的
slice
,每个模块的状态独立,便于管理和维护。 - Vuex :将状态存储到各个模块中,并提供
getters
、mutations
、actions
,适合 Vue 项目。 - Context API / Zustand:对 React 项目来说,可以通过 Context API 创建共享的状态模块;Zustand 提供更轻量的解决方案。
8. API 层的模块化
- 接口封装 :将每个功能的接口封装成独立模块,如
userAPI.js
、productAPI.js
,便于调用和统一管理。 - 服务层 (Service):将所有与后端的交互逻辑集中在一个服务层中,和具体的业务模块解耦。
模块化在现代前端开发中不可或缺,通过合理的模块划分和使用,可以显著提升项目的可维护性和扩展性。