前言
webpack是一个现代化的前端打包工具,用于构建和打包Web应用程序的静态资源。通过将多个模块和文件打包成一个或者多个最终可部署文件,可以提高应用程序的性能和加载速度。它有以下功能:
- 模块打包:webpack支持将应用程序拆分成模块,并通过模块之间的依赖关系进行组织。它可以处理javascript、CSS和图片等等,将它们打包成最终的静态资源文件;
- 资源优化:一个web应用程序包含了很多文件,功能模块也很复杂,这时就要压缩优化文件,拆分模块,实现按需加载。而Webpack提供了多个的优化功能,比如压缩、代码分离、文件合并、Tree Shaking(消除未使用的代码)等等,可以解决以上问题;
- 开发环境支持:提供很多开发环境下的实用工具,比如自动刷新、热模块替换(HMR)、Source Map(调试工具)、DevServer(配置临时服务器)等等,可以高效开发和调试应用程序;
- 扩展性和生态系统:可以通过插件(plugin)和加载器(loader)来自定义和扩展功能。
在了解了webpack的作用以及功能之后,下面就让我们来学习webpack的使用!
webpack中文文档:https://webpack.docschina.org/
在学习的过程要多看文档,多看文档,多看文档!文档可以解决很多问题,可以解决你的一些疑惑。
安装
在使用之前,首先安装webpack
npm i webpack webpack-cli webpack-dev-server -D
webpack :前端打包工具。
webpack-clii:与 Webpack 配合使用的命令行工具。它提供了一些命令,用于在命令行界面中与 Webpack 进行交互。通过 Webpack-cli,你可以运行 Webpack 的构建命令、配置文件,执行不同的构建任务,管理和控制 Webpack 的行为。
webpack-dev-server:是一个基于 Express 的开发服务器,用于在开发环境中快速搭建一个本地服务器。它提供了文件监听、自动刷新、热模块替换(HMR)等功能。
核心概念
入口(entry)
用来定义web应用程序的入口文件,webpack会从入口文件开始寻找哪些依赖是入口文件需要的。
我们需要新建一个webpack配置文件 webpack.config.js,在这个文件中就可以自定义我们的入口文件。
const path = require('path')
module.exports = {
entry: './src/index.js', // 入口文件路径,一个入口文件
//如果是多个入口文件,就用对象包裹起来
/*
entry:{
a: './src/index.js',
b: './src/app.js',
}
*/
};
注意:这里用的是node.js模块导入导出方式(CommonJs规范),这是因为Webpack是基于node.js运行的工具。还有这里涉及到的文件路径,都是按照node.js模块寻址规则来的。所以出入口文件路径一定要处理好!!!
输出(output)
从字面上意思就可以看出,这个属性是定义出口文件,也就是打包之后文件的输出路径。
如果你的文件目录结构复杂,一定要注意输出文件的路径!
module.exports = {
// 入口文件
entry: './src/index.js',
// 输出文件
output: {
path: path.resolve(__dirname, 'dist'), // 输出文件路径
filename: 'bundle.js', // 打包后生成js文件的文件名
},
}
entry、output实践
在了解了入口、输出文件之后,就让我们来实践一下吧!来验证一下理论知识。
首先我的文件目录结构是这样的:
a.js
export default function add(a, b) {
console.log(a + b);
}
index.js
import add from './app/a'
add(1, 2)
在准备好这些之后,还要配置一下package.json文件,配置好运行webpack打包命令。
在执行npm run build
命令之后,控制台会输出一下内容,同时也会生成对应bundle.js
文件,但是注意看,控制台出现了一个警告,意思是说模式还没有设置,并且给出了解决方案:设置mode为development(开发环境模式)或者production(生产环境模式)。所以只要在配置文件里加上mode属性即可。这样就不会有这个警告出现了。
加载器(loader)
从上面的例子可以看出,webpack可以打包js文件,那如果我们想处理css文件,又或者是stylus、less、sass、hbs文件呢?这时候就可以loader加载器进行处理。
比如这里是使用webpack加载css文件,首先安装相对应的loader,
npm install --save-dev style-loader css-loader
之后就可以将loader引入到你的配置文件中:
module.exports = {
module: {
rules: [
{
test: /\.css$/i, // 正则匹配css文件
use: ["style-loader", "css-loader"], // 这里要注意loader引入的顺序,先style-loader后 css-loader
},
],
},
};
编写好css文件,然后在入口文件index.js
引入css文件,记得在html文件也要引入打包生成的bundle.js
文件,因为无论是js文件还是css文件还是stylus文件,最后打包了都汇集在一个文件里面。
效果图:
又比如你想用webpack加载stylus文件,首先先安装对应的loader,
npm install --save-dev stylus-loader // 如果你没有安装style-loader css-loader这两个,记得要安装上
之后就可以将loader引入到你的配置文件中:
module.exports = {
module: {
rules: [
{
test: /\.styl$/i,
use: [
{
loader: "style-loader",
},
{
loader: "css-loader",
},
{
loader: "stylus-loader",
}
]
},
],
},
};
这里要注意loader的加载顺序,使用方法跟上面加载css文件差不多的,一步步照着来就行。
也可以用webpack来管理图片,这里就不展开来说了。
插件(plugin)
loader用来处理某些特殊的文件或模块,插件可以用于处理更广的任务。webpack提供的插件接口非常强大,使插件可以完成各种任务。
loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。
想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建它的一个实例。
// webpack.common.js
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
module.exports = {
entry: {
app: './app/main.js'
},
plugins: [
// new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './index.html',
}),
new BundleAnalyzerPlugin()
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, '../dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.hbs$/,
loader: "handlebars-loader"
},
{
test: /\.styl$/i,
use: [
{
loader: "style-loader",
},
{
loader: "css-loader",
},
{
loader: "stylus-loader",
}
]
},
]
}
};
上面代码是根据我的项目来创建的一个配置文件
html-webpack-plugin
插件可以创建一个html文件,如果你不设置任何参数,那么它就会生成最简单的html文件,比如像这样
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>webpack App</title>
</head>
<body>
<script src="index_bundle.js"></script>
</body>
</html>
但如果你想以某个html文件为模板生成一个新的html文件,那就需要添加template
参数
new HtmlWebpackPlugin({
template: './index.html',
}),
clean-webpack-plugin
插件用来清除一些不必要的文件,每次打包都会更新dist文件夹。在打包的过程中如果你发现的图片、css文件等文件消失不见了的时候,请看一下有没有使用了这个插件,因为有可能被清理掉了。
webpack-bundle-analyzer
插件可以用来分析各个模块之间的依赖关系,以便于后面代码分离、压缩代码等操作
开发工具 webpack-dev-server
是一个基于 Express 的开发服务器,用于在开发环境中快速搭建一个本地服务器。它提供了文件监听、自动刷新、热模块替换(HMR)等功能。
它的配置:
module.exports = {
// 开发环境下的临时服务器
devServer: {
static: 'public', // 静态资源路径
hot: true, // 是否开启热更新
host: 'localhost',
port: 8080,
open: true, // 是否自动打开,设置为true,会自动打开html页面
},
};
在开发环境使用webpack-dev-server的时候,webpack-dev-server 在编译之后不会写入到任何输出文件,而是将 bundle 文件保留在内存中,然后将它们 serve 到 server 中,就好像它们是挂载在 server 根路径上的真实文件一样。如果你的页面希望在其他不同路径中找到 bundle 文件,可以通过 dev server 配置中的 publicPath 选项进行修改。
代码调试 source map
当 webpack 打包源代码时,可能会很难追踪到 error(错误)和 warning(警告)在源代码中的原始位置。例如,如果将三个源文件(a.js
,b.js
和 c.js
)打包到一个 bundle(bundle.js
)中,而其中一个源文件包含错误,那么堆栈跟踪就会直接指向到 bundle.js。但是你可能需要准确地知道错误来自于哪个源文件,所以这种提示这通常不会提供太多帮助。
为了更容易地追踪 error 和 warning,JavaScript 提供了 source map 功能,可以将编译后的代码映射回原始源代码。source map 会直接告诉你错误来源于哪一个源代码。
module.exports = {
devtool: 'inline-source-map',
};
开发生产环境双配置
开发环境和生产环境使用的加载器和插件肯定会有所不同,下面对配置文件进行优化,实现相同配置优化,把开发环境和生产环境独有的配置项独立出来。
比如这个是开发生产环境都使用同一个配置文件:
// 使用node.js的模块导入和导出方式
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
// 生产环境下
entry: './src/index.js',
// 生产环境下
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
// assetModuleFilename: 'images/[hash][ext][query]'
},
// 开发环境下 保留源码
devtool: 'inline-source-map',
// 开发环境下的临时服务器
devServer: {
proxy: { // proxy URLs to backend development server
'/': 'http://localhost:8080'
},
static: path.join(__dirname, '/dist/'),
compress: true,
historyApiFallback: true,
hot: true,
open: true
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.styl$/,
use: [
{
loader: "style-loader",
},
{
loader: "css-loader",
},
{
loader: "stylus-loader",
}
]
},
{
test: /\.hbs$/,
loader: "handlebars-loader",
},
{
test: /\.(png|jpg|gif)$/,
type: 'asset/resource'
}
]
},
plugins: [
new HtmlWebpackPlugin({
title: "Webpack",
template: "index.html"
}),
new CleanWebpackPlugin()
]
}
所以需要把原来的webpack.config.js
配置文件拆分三个或者四个文件:
(下面的划分是根据我的项目来的)
webpack.common.js
公共环境的配置文件webpack.dev.js
开发环境下的配置文件webpack.prod.js
生产环境下的配置文件webpack.parts.js
各个配置零件的配置文件
webpack.common.js
公共环境的配置文件
// webpack.common.js
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
module.exports = {
entry: {
app: './app/main.js'
},
plugins: [
// new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './index.html', // 注意路径
}),
new BundleAnalyzerPlugin()
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, '../dist') // 注意路径
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.hbs$/,
loader: "handlebars-loader"
},
{
test: /\.styl$/i,
use: [
{
loader: "style-loader",
},
{
loader: "css-loader",
},
{
loader: "stylus-loader",
}
]
},
]
}
};
webpack.dev.js
开发环境下的配置文件
// webpack.dev.js
const { merge } = require('webpack-merge');
const path = require('path')
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
// 开发环境下 保留源码 inline-source-map
devtool: 'eval',
// 开发环境下的临时服务器
devServer: {
static: 'dist',
hot: true,
host: 'localhost',
port: 8080,
open: true,
},
});
webpack.prod.js
生产环境下的配置文件
// webpack.prod.js
const common = require('./webpack.common');
const {merge} = require('webpack-merge');
// const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin');
module.exports = merge(common,{
mode: "production", //development|production
output: {
filename: '[name].[hash:7].js'
},
// 排除某些依赖包不被打包,只在运行时从CDN引入
externals: {
// jquery: 'jQuery',
// wangeditor: 'wangEditor'
},
plugins:[
// new UglifyjsWebpackPlugin()
],
// 将公共模块单独打包,使得它们可以被缓存和复用
optimization: {
splitChunks: {
cacheGroups: { // 用于配置缓存组的对象
commons: {
test: /[\\/]node_modules[\\/]/, // 匹配node_modules目录下的模块
name: 'vendors', // 提取的公共模块的文件名
chunks: 'all', // 对所有的代码进行优化
},
},
},
},
})
在进行划分了之后,在package.json
文件中新增三条快捷命令
"build": "npx webpack --config ./config/webpack.prod.js",
"dev": "npx webpack --config ./config/webpack.dev.js",
"start": "webpack serve --config ./config/webpack.dev.js"
在划分了三个文件之后,涉及到文件路径的地方一定要多加注意,因为很容易就会出错,项目目录结构越复杂就越容易出错!!!
报错记录
Invaild options object.Dev Server has been initalized…
这个报错的意思是某个属性找不到,错误的原因是我用的时候webpack 5+版本,而我用了webpack 4+的但webpack 5+已经弃用了的属性。
生产环境下打包没有在相对应的dist文件夹里面生成js和html文件
我的项目目录结构:
原因分析:经过分析是输出文件路径的问题
我当时的输出文件路径是path: path.resolve(__dirname, 'dist')
,而webpack.dev.js
等配置文件是在config文件夹里面的,这样的话输出文件是在config文件夹下的dist下,但明显不是,打包都的输出文件要在与Index.html同目录的dist文件夹,所以需要修改路径为path: path.resolve(__dirname, '../dist')
webpack-dev-server在编译后找不到css文件和图片
原因分析:
首先在开发环境使用webpack-dev-server的时候,webpack-dev-server 在编译之后不会写入到任何输出文件,而是将 bundle 文件保留在内存中,然后将它们 serve 到 server 中,就好像它们是挂载在 server 根路径上的真实文件一样;
其次我的开发环境配置里面可访问的资源路径是public,而输出文件路径是dist,当编译之后,服务器就会以/dist/为根目录,访问里面的资源,而dist文件里有没有css和image文件,所以访问不到资源。
所以我把可访问的静态资源路径和输出文件路径都设置一样,最后就可以正常访问了。
Refused to apply style from ‘http://localhost:8080/css/index.css’ because its MIME type (‘text/html’) is not a supported stylesheet MIME type, and strict MIME checking is enabled.
原因分析:我在html文件引入了css文件,而我的css文件又存放在输出文件里,由于我又使用clean-webpack-plugin
插件,每次打包的时候都会更新dist文件夹里的内容,所以把css文件给清理掉,导致无法引入css文件。所以我把这个插件给注释掉,重新生成一份css文件就可以了。
最后
这是我入门学习webpack之后整理的一些学习笔记和踩的一些坑记录下来,如有不对的地方或者需改善的地方,欢迎大家批评指正!