文章目录
前言简单介绍1. webpack作用2. webpack语法规范
安装依赖及基本配置loader (加载器)1、处理 ts / js 文件1.1 babel配置文件1.2 browserslist配置文件
2、处理图片、字体、图标等资源文件3、处理**less、scss、css**文件3.1 css 文件分离3.2 处理 less / sass 文件3.3 css3 属性添加兼容前缀3.4 css 的压缩3.5 示例代码
4、处理html文件
总结
前言
此篇文章将简单介绍webpack的基本概念和使用方法,并通过实例演示如何使用webpack来构建一个不依赖前端框架的项目,同时也会介绍一些常用的webpack插件和优化技巧,希望本篇文章能够帮助大家更好地使用webpack进行项目开发。
简单介绍
常见打包工具有Grunt、Gulp、Parcel、Webpack、Rollup、Vite、ESBuild 等等,这篇文章只专注于webpack就不去对比其他打包工具了。webpack优点在于有着完善的基础建设,而且得益于webpack5的升级,对比以往版本也有了相当大的提升(极致编译速度,一文搞定webpack5升级),虽然它的保留率在逐年下降,但大概率还是目前使用率最多的构建工具(查看StateOfJS的调查结果)。
1. webpack作用
代码编译 如sass,TypeScript等代码虽然极大的提高了开发效率,但是本身并不被浏览器所识别,需要我们对其进行编译和打包,变成浏览器识别的代码模块化 使用高级语法 ES6 和 CommentJS,进行前端模块化开发,webpack 将高阶语法打包成浏览器识别的代码优化加载速度 压缩和合并代码来提高加载速度,压缩可以减少文件体积,代码合并可以减少http请求
2. webpack语法规范
webpack是用Node封装的,可以使用Node的语法,Node.js 的模块规范就是 CommonJS 模块规范,ES6模块化(ES Modules / ESM)是浏览器端和服务器端通用的规范,关于规范的区别这里也不展开了。
安装依赖及基本配置
下面正式进入webpack5的实战环节,我使用Node的版本为14.21.3,当然最好还是更新到比较新的Node版本进行webpack5的开发,不过Webpack 5 运行于 Node.js v10.13.0+ 的版本,我这里使用较低版本也是为了兼顾大多数的开发环境。首先创建基础的网页,html文件中放入图片及引用css、script等,目录结构如下:
├── webpack5 (项目根目录)
├── image (项目图片资源目录)
│ └── 略
├── favicon.ico(网页图标)
├── index.css
├── index.html
└── index.js
初始化一下项目:(会生成一个package.json文件)
npm init -y
安装webpack:
npm i webpack webpack-cli -D
创建运行指令:
在package.json中的scripts中创建build命令,webpack默认会找配置文件“webpack.config.js”
"scripts": {
"build": "webpack"
},
编写默认配置文件:webpack.config.js
const path = require('path'); //核心模块
module.exports = {
entry: 'index.js', //配置入口文件
output: {
filename: 'bundle.js', //打包出来的文件名称
path: path.resolve(__dirname, './dist'), //打包后的路径,必须是绝对路径
//path.resolve()拼接路径为绝对路径
//__dirname指当前文件根目录,dist是打包出来的目录名称
},
mode: 'production', //production生产模式(代码压缩),development开发模式(不压缩代码)
module: {}, //配置loader
plugins: [] //配置plugins
};
打包
运行了webpack打包命令后多了dist目录,类似下面这样子就证明成功使用webpack5打包了。
查看bundle.js文件会发现只有对index.js代码压缩后的内容,这是正常的,因为我们只是指定了“index.js”作为入口起点(entry point) ,让 webpack 使用其作为一个“模块”构建项目,其他资源文件(如html、css)要引入到 js 里面才能使用。 另外webpack本身就能打包 js 代码,但是其他类型的如css,图片等格式的文件没法打包,就需要在引入资源后利用第三方的模块进行打包,这就是loader(加载器) 的作用了,另外配置了多个loader,其按照从右到左(或从下到上)地取值(evaluate)/执行(execute)。
loader (加载器)
1、处理 ts / js 文件
npm i babel-loader @babel/core @babel/preset-env core-js @babel/plugin-transform-runtime @babel/preset-typescript -D
babel-loader: 使用 babel 将ES6+语法转换为 ES5@babel/corer: babel 编译的核心包@babel/preset-env: babel 编译的预设(告诉babel使用那种转码规则进行文件处理)core-js(通过 Polyfill 方式在目标环境中添加缺失的特性,让低版本的浏览器也可以支持promise这些新的API)@babel/plugin-transform-runtime 将所有文件用到的公用辅助函数抽离出来形成一个第三方文件,自动引入辅助函数,减少包的体积@babel/preset-typescript(支持ts语法)
module: {
rules: [
{
test: /\.js|ts$/,
use: {
loader: 'babel-loader',
options: {
exclude: /node_modules/, //除了这个文件夹之外的文件
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage', //按需引入(根据配置的目标环境找出需要的polyfill进行部分引入)
corejs: 3,
// 设置兼容目标浏览器版本,这里可以不写,将使用 browserslist 配置文件
// targets: '> 1%, not dead',
},
],
'@babel/preset-typescript',
], //预设
// 插件比预设先执行
plugins: ['@babel/plugin-transform-runtime'],
cacheDirectory: true, //开启缓存,第二次编译时,没改的部分使用缓存
},
},
},
]
}
1.1 babel配置文件
可以把babel-loader的配置单独抽离出来,配置成一个全局配置文件babel.config.js,也可以使用**.babelrc**文件进行颗粒度更细的配置,这两者可以一起使用,也可以独立使用,当同级存在时后者优先级更高,详细可看babeljs官网,或者理解 babel.config.js 和 babelrc。
1.2 browserslist配置文件
在package.json中添加browserslist:
"browserslist": [
">1%",
"last 10 versions",
"not dead"
]
2、处理图片、字体、图标等资源文件
module: {
rules: [
{
test: /\.(png|jpe?g|gif|webp|svga)(\?.*)?$/,
type: 'asset',
generator: {
filename: 'imgs/[name].[contenthash:4][ext]',
},
parser: {
// 生成Data URI 的条件,视为 inline 模块类型,否则会被视为 resource 模块类型
dataUrlCondition: {
// 当资源模块不超过 4kb 时,生成 DataURI,超过 4kb 时,单独打包成文件
maxSize: 4 * 1024, // 4b
},
},
},
{
test: /\.(eot|ttf|otf|woff2?)$/,
type: 'asset/resource',
generator: {
filename: 'font/[name].[contenthash:4][ext]',
},
},
]
}
3、处理less、scss、css文件
3.1 css 文件分离
npm i style-loader mini-css-extract-plugin
3.2 处理 less / sass 文件
处理sass
npm i sass sass-loader -D
处理less
npm i less less-loader -D
3.3 css3 属性添加兼容前缀
npm i postcss-loader autoprefixer -D
另外还需要有一份要兼容浏览器的清单,让postcss-loader知道要加哪些浏览器的前缀,browserslist配置文件其实就是这份清单。
3.4 css 的压缩
npm i css-minimizer-webpack-plugin -D
3.5 示例代码
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const devMode = process.env.NODE_ENV !== 'production';
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/,
//use里面执⾏顺序是从右往左的顺序,先模块化再引入
use: [
devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
url: true, //默认为true,可以处理css中的url图片路径
esModule: false,
},
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ['autoprefixer'],
},
},
},
],
},
{
test: /\.scss$/,
use: [
devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ['autoprefixer'],
},
},
},
'sass-loader',
],
},
{
test: /\.(png|jpe?g|gif|webp|svga)(\?.*)?$/,
type: 'asset',
generator: {
filename: 'imgs/[name].[contenthash:4][ext]',
},
parser: {
// 生成Data URI 的条件
dataUrlCondition: {
// 当资源模块不超过 4kb 时,生成 DataURI,超过 4kb 时,单独打包成文件
maxSize: 4 * 1024, // 4b
},
},
},
{
test: /\.(eot|ttf|otf|woff2?)$/,
type: 'asset/resource',
generator: {
filename: 'font/[name].[contenthash:4][ext]',
},
},
// ...
],
},
plugins: [
new MiniCssExtractPlugin({
// 长期缓存:使用 filename: "[contenthash].css" 启动长期缓存
filename: devMode ? 'css/[name].css' : 'css/[name].[contenthash].css',
}),
],
};
这里其实会有个问题,当我们指定mode的模式为“production”后,在index.js文件中打印process.env.NODE_ENV的值确实是“production”,然而在webpack.config.js文件中打印process.env.NODE_ENV的值会发现值是’undefined’,这里先卖个关子,解决办法留到后面的环境变量章节讲述。
4、处理html文件
webpack需要把最终构建好的静态资源都引入到一个html文件中,这样才能在浏览器中运行
npm i html-webpack-plugin html-loader@0.5.5 -D
html-webpack-plugin 插件的作用 以自己的index.html为模板生成一个 HTML 文件,并且引入所有webpack生成的资源链接
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
template: template: path.resolve(__dirname, './index.html'), //模板
filename: 'index.html', //打包的 HTML 文件名字
favicon: path.resolve(__dirname, './favicon.ico'), //网页图标
}),
]
}
html-loader的作用 由于webpack需要解析html模板,所以需要html-loader解析html文件。这里是目前唯一一个需要指定安装版本的包,因为其最新的v5.0.0的包支持的最低 Node.js 版本为18.12.0,查看各种版本的下载数量可以到npm官网查看。
module: {
rules: [
//...
{
test: /\.html$/i,
use: [
{
loader: 'html-loader',
options: {
esModule: false,
},
},
],
},
//...
]
}
因为webpack配置的打包入口只有index.js一个,记得在index.js文件中引入需要的资源,如
import './index.scss';
import './index.ts';
再次运行webpack打包,就可以打包出来如下图所示的目录文件了,本地运行这个index.html文件,能正常访问图片、应用样式以及控制台成功执行输出等就证明成功打包了。
然而其实这里有点小问题,一是资源报错找不到,二是重复加载资源。
针对其一,原因是原来的html页面引入了这些资源,现在将其去掉即可。 针对其二,由于在HtmlWebpackPlugin插件中配置了favicon选项,虽然将favicon.ico文件从根目录拷贝到打包目录下了,但是这个插件并没有智能地将原本html里引用的favicon.ico去掉,导致重复加载了,有两个办法:一是直接删掉html里引入的favicon.ico,二是使用copy-webpack-plugin这个插件,webpack只会将里面的资源复制到打包目录下,不会对其进行压缩编译等处理。
npm i copy-webpack-plugin -D
const CopyPlugin = require('copy-webpack-plugin');
module.exports = {
plugins: [
// ...
// public目录被webpack打包,一开始是什么样子,打包后还是什么样子
new CopyPlugin({
patterns: [
{
from: 'public', //拷贝的目录
},
],
}),
],
};
将favicon.ico从根目录移动到新创建的public文件夹中,再次运行打包命令即可。
总结
写到这里这篇文章篇幅实在是太长了,所以还有几章关于webpack的环境变量、插件、优化等内容我还是另开一篇文章讲述,后续文章请点击查看。 欢迎各位看官【点赞】、【收藏】,多多支持!也欢迎您【评论】留下宝贵意见,共同探讨一起学习~。
参考链接
发表评论