Resolve
前面学习时使用了各种各样的模块依赖,这些模块可能来自于自己编写的代码,也可能来自第三方库,在 Webpack
中,resolve
是用于解析模块依赖的配置项,它决定了 Webpack
如何找到项目中的模块文件
-
resolve
作用:-
当你在代码中
import
或require
一个模块时、resolve
配置可以帮助Webpack
定义如何解析这些模块路径,特别是在处理文件扩展名、路径别名、模块目录等方面 -
webpack
使用 enhanced-resolve 来解析文件路径
-
-
解析三种文件路径:
-
绝对路径:这种情况下,不需要再做进一步解析
-
相对路径:
在这种情况下,使用
import
或require
的资源文件所处的目录,被认为是上下文目录在
import/require
中给定的相对路径,会拼接此上下文路径,来生成模块的绝对路径 -
模块路径:
在
resolve.modules
中指定的所有目录检索模块,默认值目录是['node_modules']
,所以默认会从node_modules
中查找文件可以通过设置别名的方式来替换初始模块路径
-
-
解析引入文件和文件夹:
-
引入文件:
如果文件具有扩展名,则直接打包文件
否则,将使用
resolve.extensions
选项作为文件扩展名解析 -
引入文件夹:
在文件夹中根据
resolve.mainFiles
配置选项中指定的文件顺序查找resolve.mainFiles
的默认值是['index']
再根据
resolve.extensions
来解析扩展名
-
-
resolve
常用属性:
-
extensions
:解析到文件时自动添加扩展名,告诉
Webpack
在导入语句中省略文件扩展名时,它应该查找哪些扩展名默认值是
['.wasm', '.mjs', '.js', '.json']
如果代码中想添加加载
.vue
或者jsx
或者ts
等文件时,必须自己写上扩展名 -
alias
:用于定义路径别名,可以通过别名缩短路径,可以使用@
代替长路径 -
modules
:用于指定模块查找的目录,默认情况下Webpack
只会查找node_modules
文件夹,通过modules
选项,可以为Webpack
指定其他的查找目录 -
mainFields
:告诉Webpack
在解析模块的package.json
时优先使用哪些字段,通常用于库的package.json
中,常见的字段有main
,module
和browser
-
mainFiles
:指定文件查找时的入口文件名,默认是index
-
symlinks
:控制Webpack
是否遵循符号链接(symlink
),默认为true
-
Mode配置
前面我们学习webpack
打包相关文件时,有个警告我们一直没有看,这就是需要配置mode
,接下来学习mode
在 Webpack
中,mode
是一个重要的配置项,用于设置构建模式。它影响 Webpack
的行为和输出,决定了构建时是否开启优化(如压缩代码、调试工具等)
mode
有三个值:
-
development
(开发模式):用于开发环境,提供调试支持和快速构建
-
特点:
开启调试功能
输出的代码不会被压缩,以便于调试
启用
eval
作为devtool
,生成代码映射(source maps
),方便调试源码开启了增量编译,构建速度较快
-
内置优化:
devtool: 'eval'
(快速生成source maps
)启用更有利于调试的错误提示
-
-
production
(生产模式):用于生产环境,进行代码压缩和各种优化,生成小体积的高效代码
-
特点:
启用各种优化功能,以减少输出的文件体积和提高性能
代码会被压缩(通过
TerserPlugin
)自动去除未引用的代码(
Tree Shaking
)生成优化的
source maps
(可选,通常关闭) -
内置优化:
TerserPlugin
(用于压缩 JavaScript 代码)mode: 'production'
会自动将devtool
设为false
,取消source map
以减少文件体积通过
Tree Shaking
去除未引用代码通过
DefinePlugin
将process.env.NODE_ENV
设置为"production"
-
-
none
(无模式):禁用所有内置的优化,用于完全自定义配置的场景
-
特点:
禁用所有默认优化
适用于自定义优化的场景
-
更多的mode
配置:
搭本地服务器
目前开发的代码,为了运行需要有两个操作:npm run build
编译相关的代码,通过live server
或者直接通过浏览器,打开index.html
代码查看效果。这个操作会影响开发效率,希望做到当文件发生变化时,可以自动的完成编译和展示
为了完成自动编译,webpack
提供了几种可选的方式:
-
webpack watch mode
-
webpack-dev-server
(常用) -
webpack-dev-middleware
在 Webpack
中搭建本地服务器通常通过使用 webpack-dev-server
来实现。webpack-dev-server
是一个方便的开发工具,它为你提供一个轻量级的 HTTP
服务器,并能实时监控文件的变化,自动重新加载页面
-
npm install webpack-dev-server -D
:安装 -
修改配置文件,启动时加上
serve
参数 -
webpack-dev-server
在编译之后不会写入到任何输出文件,而是将打包文件保留在内存中 -
事实上
webpack-dev-server
使用了一个库叫memfs
(memory-fs
webpack
自己写的) -
都配置完可以执行
npm run serve
,但如果你前面配置loader
和plugins
都是看我前面的文章练习的,这时会打包成功,但是访问http://localhost:8080/
是空白的
配置选项说明:
devServer
:打包后不会生成打包文件-
static.directory
:本地服务器所提供服务的文件目录 -
compress
:是否是否为静态文件启用gzip
压缩,默认false
,提高页面的加载性能
-
host
:设置主机地址,默认值是localhost
,如果希望其他地方也可以访问,可以设置为0.0.0.0
localhost
和0.0.0.0
的区别:-
localhost
: 本质上是一个域名,通常情况下会被解析成127.0.0.1
-
127.0.0.1
: 回环地址(Loop Back Address
),表达的意思其实是我们主机自己发出去的包,直接被自己接收 -
正常的数据库包经过
应用层 - 传输层 - 网络层 - 数据链路层 - 物理层
,而回环地址是在网络层直接就被获取到了,是不会经过数据链路层和物理层的 -
比如我们监听
127.0.0.1
时,在同一个网段下的主机中,通过ip
地址是不能访问的 -
0.0.0.0
: 监听IPV4
上所有的地址,再根据端口找到不同的应用程序,比如我们监听0.0.0.0
时,在同一个网段下的主机中,通过ip
地址是可以访问的
-
-
port
:指定服务器监听的端口号(例如9000
),不指定默认为8080
-
open
:启动服务器时自动打开浏览器,自动导航到localhost:port
-
openPage
:自动打开配置的页面 -
hot
:开启模块热替换(HMR
),默认已经开启,实现页面的局部更新而不重新加载整个页面,开启后浏览器中可以看到如下效果,修改入口文件还是会全局刷新的
HMR
的全称是Hot Module Replacement
,翻译为模块热替换,它是指在应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个页面HMR
通过如下几种方式,来提高开发的速度:-
不重新加载整个页面,这样可以保留某些应用程序的状态不丢失
-
只更新需要变化的内容,节省开发的时间
-
修改了
css、js
源代码,会立即在浏览器更新,相当于直接在浏览器的devtools
中直接修改样式
如果发现当修改了某一个模块的代码时,依然是刷新的整个页面,这是因为需要去指定哪些模块发生更新时进行
HMR
,在实际项目中我们不需要自己设置的vue
开发中,使用vue-loader
,此loader
支持vue
组件的HMR
,提供开箱即用的体验react
开发中,有React Hot Loader
,实时调整react
组件(目前React
官方已经弃用了,改成使用react-refresh
)
-
-
historyApiFallback
:如果启用了HTML5
的History API
(用于单页应用),需要该配置项来重定向所有404
到首页。否则,在子页面刷新时可能找不到对应资源
-
配置整体代码参考:
const path = require("path");
const { VueLoaderPlugin } = require("vue-loader/dist/index");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { DefinePlugin } = require("webpack");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
clean: true,
filename: "index.js",
path: path.resolve(__dirname, "build"),
},
resolve: {
alias: {
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils'),
},
extensions: ['.js', '.vue', '.json'],
},
devServer: {
static: {
directory: path.resolve(__dirname, 'build'), // 服务器内容的目录
},
compress: true, // 启用 gzip 压缩
port: 9000, // 指定服务器端口号
open: true, // 自动打开浏览器
open: ['main.html'], // 自动打开浏览器并定向到http://localhost:9000/main.html
hot: true, // 热更新
historyApiFallback: true, // 支持HTML5的历史API(单页应用中很有用)
},
plugins: [
new VueLoaderPlugin(),
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: "haahha",
template: "./public/index.html",
filename: "main.html",
minify: {
collapseWhitespace: true, // 移除空白
removeComments: true, // 移除注释
removeAttributeQuotes: true, // 移除属性的引号
},
}),
new DefinePlugin({
BASE_URL: '"./"',
}),
],
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-loader"],
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader", "postcss-loader"],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: "asset",
generator: {
filename: "image/[name].[hash:8][ext]",
},
parser: {
dataUrlCondition: {
maxSize: 100 * 1024, // 这时使用的图片没有超过这个值他就不会被打包成文件,而是转base64使用
},
},
},
{
test: /\.m?js$/,
use: [
{
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
],
},
{
test: /\.vue$/,
loader: "vue-loader",
},
],
},
};
区分环境配置
-
目前我们所有的
webpack
配置信息都是放到一个配置文件中的:XXXX.config.js
-
但某些配置是在开发环境需要使用的,某些配置是在生成环境需要使用的,当然某些配置是在开发和生成环境都会使用的
-
当配置越来越多时这个文件会变得越来越不容易维护,最好对配置进行划分,方便我们维护和管理
如何区分不同的配置:
-
建立
config
文件夹编写开发和生产配置文件 -
抽取公共的配置,并与开发和生产配置合并
-
添加不同的打包脚本
// wk.comm.config.js
const path = require("path");
const { VueLoaderPlugin } = require("vue-loader/dist/index");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { DefinePlugin } = require("webpack");
module.exports = {
entry: "./src/index.js",
output: {
filename: "index.js",
path: path.resolve(__dirname, "build"),
},
resolve: {
alias: {
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils'),
},
extensions: ['.js', '.vue', '.json'],
},
plugins: [
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
title: "haahha",
template: "./public/index.html",
filename: "main.html",
minify: {
collapseWhitespace: true, // 移除空白
removeComments: true, // 移除注释
removeAttributeQuotes: true, // 移除属性的引号
},
}),
new DefinePlugin({
BASE_URL: '"./"',
}),
],
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-loader"],
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader", "postcss-loader"],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: "asset",
generator: {
filename: "image/[name].[hash:8][ext]",
},
parser: {
dataUrlCondition: {
maxSize: 100 * 1024, // 这时使用的图片没有超过这个值他就不会被打包成文件,而是转base64使用
},
},
},
{
test: /\.m?js$/,
use: [
{
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
],
},
{
test: /\.vue$/,
loader: "vue-loader",
},
],
},
};
// wk.dev.config.js
const path = require("path");
const { merge } = require('webpack-merge')
const commonConfig = require('./wk.comm.config')
module.exports = merge(commonConfig, {
mode: "development",
devServer: {
static: {
directory: path.resolve(__dirname, 'build'), // 服务器内容的目录
},
compress: true, // 启用 gzip 压缩
port: 9000, // 指定服务器端口号
open: true, // 自动打开浏览器
open: ['main.html'], // 自动打开浏览器并定向到http://localhost:9000/main.html
hot: true, // 热更新
historyApiFallback: true, // 支持HTML5的历史API(单页应用中很有用)
},
});
// wk.prod.config.js
const { merge } = require('webpack-merge')
const commonConfig = require('./wk.comm.config')
module.exports = merge(commonConfig, {
mode: "production",
output: {
clean: true,
},
});