一、认识webpack(本篇基于webpack5进行说明,与webpack4可能有些地方不太一样)
概念
事实上随着前端的快速发展,目前前端的开发已经变的越来越复杂了 比如也会使用一些高级的特性来加快我们的开发效率或者安全性,比如通过ES6+、TypeScript开发脚本逻辑,
通过sass、less等方式来编写css样式代码;
(1)比如开发过程中,我们还希望实时的监听文件的变化来并且反映到浏览器上,提高开发的效率;
(2)比如开发完成后我们还需要将代码进行压缩、合并以及其他相关的优化;
等等….
但是对于很多的前端开发者来说,并不需要思考这些问题,日常的开发中根本就没有面临这些问题:
这是因为目前前端开发我们通常都会直接使用三大框架来开发:Vue、React、Angular;
但是事实上,这三大框架的创建过程我们都是借助于脚手架(CLI)的;
事实上Vue-CLI、create-react-app、Angular-CLI都是基于webpack来帮助我们支持模块化、less、
TypeScript、打包优化等的;
webpack到底是什么?
官方的解释是 webpack is a static module bundler for modern JavaScript applications. 也就是说webpack是一个为了将现代js应用程序的静态资源模块化打包工具。 这句话我们可以了解到webpack是可以将代码打包成静态资源部署在静态服务器上,也支持模块化开发,例如ESModule、CommonJs、AMD、CMD等等
这张图也很明确的说了webpack打包的范畴。
webpack的安装
webpack的安装分为两个部分:webpack、webpack-cli
那他们是什么关系呢?
(1)执行webpack命令,会执行node_modules下的.bin目录下的webpack; (2)webpack在执行时是依赖webpack-cli的,如果没有安装就会报错; (3)而webpack-cli中代码执行时,才是真正利用webpack进行编译和打包的过程; (4)所以在安装webpack时,我们需要同时安装webpack-cli(第三方的脚手架事实上是没有使用webpack-cli的,而是类似于自己的vue-service-cli的东西)
大概步骤就是在命令行先执行webpack命令 然后执行过程中系统会在node_modules中.bin目录下查找webpack命令,然后依赖webpack-cli,最后webpack打包程序。
webpack的默认打包
我们可以通过webpack进行打包,之后运行打包之后的代码
在目录下直接执行 webpack 命令
生成一个dist文件夹,里面存放一个main.js的文件,就是我们打包之后的文件:
这个文件中的代码被压缩和丑化了;
另外我们发现代码中依然存在ES6的语法,比如箭头函数、const等,这是因为默认情况下webpack并不清楚我们打包后的文件是否需要转成ES5之前的语法,后续我们需要通过babel来进行转换和设置;
我们发现是可以正常进行打包的,但是有一个问题,webpack是如何确定我们的入口的呢?
事实上,当我们运行webpack时,webpack会查找当前目录下的 src/index.js作为入口;
所以,如果当前项目中没有存在src/index.js文件,那么会报错;
当然,我们也可以通过配置来指定入口和出口
npx webpack --entry ./src/main.js --output-path ./build
创建局部webpack
前面我们创建webpack打包用的是全局webpack,但是在真实的开发中不同的项目需要不同的webpack,所以下面我们来创建局部的webpack,步骤如下:
第一步,在命令行下用npm init -y创建package.json,用于管理项目的信息,相关的库文件等等;
第二步,在命令行下(也就是在你的目录结构下)使用npm install webpack webpack-cli -D;
第三步,使用局部的webpacknpx webpack;
第四部,在package.json下的scripts里面加入如下代码:
"scripts": { "build": "webpack" };
第五步,使用npm run build来打包程序
我们先创建文件夹格式
在src文件夹下创建js文件,里面有两个format.js和math.js,
format.js 使用CommonJs来进行导出,
const format = function() {
return '$96.00'
}
module.exports = {
format
}
math.js
使用esmodule导出,(嘻嘻)
export function add(a, b) {
return a + b
}
我们在index.js中引入这两个js文件,
index.js
// es的导入与导出
import { add } from './js/math'
// common.js的导入与导出
const { format } = require('./js/format')
console.log(add(10, 20));
console.log(format());
下面我们来对webpack的进行简单的配置
配置webpack的文件名称是webpack.config.js 我们先对js文件进行打包
js的打包
在打包js 的过程中,会根据配置文件选择入口,从入口开始,会形成一系列的依赖图,然后从依赖图中寻找其他对应的文件。
const path = require('path')
module.exports = {
//入口文件
entry:'./src/index.js',
//生成的文件
output:{
//文件路径
path:path.resolve(__dirname,'./build')
//文件名
filename:'bundle.js'
},
}
然后我们开始将打包后的bundle.js引入到html里面,然后运行,得出结果。
css,less等的打包
我们先对css,less,sass,scss等样式来进行打包处理,尤其是less预处理的这类语言最终都会被转换成css文件形式,最后css进行渲染页面.
loader 的配置方式
配置方式表示的意思是在我们的webpack.config.js文件中写明配置信息:
module.rules中允许我们配置多个loader(因为我们也会继续使用其他的loader,来完成其他文件的加载);
这种方式可以更好的表示loader的配置,也方便后期的维护,同时也让你对各个Loader有一个全局的概览;
module.rules的配置如下:
rules属性对应的值是一个数组:[Rule]
数组中存放的是一个个的Rule,Rule是一个对象,对象中可以设置多个属性:
test属性:用于对 resource(资源)进行匹配的,通常会设置成正则表达式;
use属性:对应的值时一个数组:[UseEntry]
UseEntry是一个对象,可以通过对象的属性来设置一些其他属性
loader:必须有一个 loader属性,对应的值是一个字符串;
options:可选的属性,值是一个字符串或者对象,值会被传入到loader中;
query:目前已经使用options来替代;
传递字符串(如:use: [ 'style-loader' ])是 loader 属性的简写方式(如:use: [ { loader: 'style-loader'} ]);
loader属性: Rule.use: [ { loader } ] 的简写
在js文件夹下创建element.js用来创建dom结构
代码如下:
// import '../css/style.css'
import '../css/title.less'
const divEl = document.createElement('div')
divEl.className = 'title'
divEl.innerText = '你好,webpack'
document.body.appendChild(divEl)
在src文件下创建css文件夹,创建两个style.css
style.css
.title {
color: red;
font-size: 20px;
font-weight: 800;
}
title.less
@bgColor:red;
@textDecoration:pink;
.title{
background-color: @bgColor;
color:@textDecoration;
}
const path = require('path')
module.exports = {
//入口文件
entry:'./src/index.js',
//生成的文件
output:{
//文件路径
path:path.resolve(__dirname,'./build')
//文件名
filename:'bundle.js'
},
module:{
rules:[
{
test:/\.css$/,
//当然你需要先下载css-loader,由于是开发依赖,所以使用`npm install css-loader -D`来进行安装
use:[
'style-loader',
'css-loader'
]
},
{
test:/\.less$/,
//当然你需要先下载less-loader,由于是开发依赖,所以使用`npm install css-loader -D`来进行安装
use:[
'style-loader',
'css-loader',
'less-loader'
]
}
]
}
}
还有为什么上面会加入style-loader这个loader?
一方面是我们将css或者less解析完成以后,此时并没有插入到页面中,所以我们需要配置style-loader,从而让样式生效;至于放的位置,这也是有顺序的,这是因为loader 的加载顺序是从上到下,从右到左,从
以上只是展示了css和less的配置打包,sass,scss,styuls的配置都是一样的
然后npm run build 进行打包,然后运行
接下来我们对PostCSS进行配置
那么什么是postCss? PostCSS是一个通过JavaScript来转换样式的工具;
这个工具可以帮助我们进行一些CSS的转换和适配,比如自动添加浏览器前缀、css样式的重置;
尤其是浏览器前缀,开发的时候有时候为了兼容需要用的到 比如下面的代码
但是实现这些功能,我们需要借助于PostCSS对应的插件
先来安装postcss-laoder 使用命令npm install postcss-loader -D
在webpack.config.js文件里面加入postcss的loader配置
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, './build'),
filename: 'bundle.js'
},
module: {
// 规则
rules: [{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
require('autoprefixer')
]
}
}
}
]
}, {
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader'
]
}, ]
}
}
或者还有一种配置的方式 此时的webpack.config.js文件中就不需要把css的属性加入浏览器的前缀
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, './build'),
filename: 'bundle.js'
},
module: {
// 规则
rules: [{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-laoder'
/*{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
require('autoprefixer')
]
}
}
}*/
]
}, {
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader'
]
}, ]
}
}
那就是再创建一个新的文件postcss.config.js将plugins的配置放在另一个文件中,然后用CommonJs导出
module.exports = {
plugins:[
//require('autoprefixer')
require('postcss-preset-env')
]
}
图片或者文件的打包
file-loader或者img-loader打包图片
在element.js中加入图片盒子
import '../css/index.css'
// import '../css/index.less'
import '../css/image.css'
const divEl = document.createElement('div')
const div2 = document.createElement('div')
divEl.className = 'title'
divEl.innerText = '你好,webpack'
div2.className = 'image-bg'
document.body.appendChild(divEl)
document.body.appendChild(div2)
image.css
.image-bg {
background-image: url('../img/1.png');
background-size: cover;
width: 200px;
height: 200px;
}
webpack.config.js的配置
{
test: /\.(png|jpg|gif|svg|jpeg)$/,
use: 'img-loader'
}
但是上面的打包起来有些不方便,就是在图片比较多的时候,容易找不对对应的打包图片,也就是文件名不容易看,例如我按照上面的方式打包以后的图片会显示在build 文件夹下,比较乱。
我们这个时候就需要图片命名打包,
在图片的配置当中使用file-loader来进行图片命名
使用hash函数来对图片名进行不重复命名
{
test:/\.(png|jpg|gif|svg|jpeg)$/,
use:{
loader:'file-loader',
options:{
//指定图片的存放路径
outputPath:'img',
name:'[name]_[hash:6].[ext]'
}
}
}
当执行完npm run build的时候,build文件夹下回出现img,img文件夹下就是所保存的图片以及文件
url-loader来打包小图片
file-loader和url-loader相比,后者可以将较小的文件,转换成base64URI 当我们将项目打包以后,那么打包以后的文件就是js文件,图片文件,html文件,然后部署到服务器上,当我们网络请求服务的时候,会下载这些文件,尤其是有图片的时候,会不断的发送请求来展示图片,但是当图片数量比较多的时候,对服务器性能是一个不小的考验,所以这个时候我们一般将小的图片变成精灵图,还有小字体图,来优化性能;还有一方面我们可以使用base64URI来将图片转码,然后在请求过程中将码在转换成图片,这是一种高并发的优化;
{
test: /\.(png|jpg|gif|svg|jpeg)$/,
use: {
loader: 'url-loader',
options: {
name: 'img/[name]_[hash:6].[ext]',
// 表示小于100kb的用url-loader的base64URI编码
limit: 100 * 1024
}
}
}
图片编码之webpack5的asset方法
webpack5开始,我们可也直接使用资源模块类型asset module type 来代替上面的这些loader配置;
{
test: /\.(png|jpg|gif|svg|jpeg)$/,
type:'asset',
//生成的文件名
generator:{
filename:'img/[name]_[hash:6][ext]'
},
parser:{
///图片的大小限制
dataUrlCondition:{
maxSize:100 * 1024
}
}
}
字体的打包
首先我这里使用的是阿里云的字体图标库,可以下载以后引入到element.js文件中
{
test: /\.(eot|ttf|woff2?)$/,
use: {
loader: 'file-loader',
options: {
name: 'font/[name]_[hash:6].[ext]'
}
}
}
插件的安装与配置
但是按照上面的打包方式还是稍微有点问题
那就是每次在重新打包的新的东西的时候,我们需要先删除之前的打包的文件,不然的话每次打包的文件都会重叠;也就是每次打包新的之前把旧的build文件删除掉;我这个时候我们需要一个插件CleanWebpackPlugin; 插件是贯穿整个webpack的周期的,loader只是用于特定的类型进行转化,plugin可以用于执行更加广泛的任务,比如打包优化,资源管理,环境变量的注入;
CleanWebpackPlugin插件的使用
插件必须是我们手动进行导入;
const path = require('path')
// 导入cleanwebpackPlugin插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, './build'),
filename: 'bundle.js'
},
module: {
// 规则
rules: [{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
// 'css-loader', {
// loader: 'postcss-loader',
// options: {
// postcssOptions: {
// plugins: [
// require('autoprefixer')
// ]
// }
// }
// }
]
}, {
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader'
]
}, {
test: /\.(png|jpg|gif|svg|jpeg)$/,
use: {
loader: 'file-loader',
options: {
outputPath: 'img',
name: '[name]_[hash:6].[ext]'
}
}
}, {
test: /\.(png|jpg|gif|svg|jpeg)$/,
use: {
loader: 'url-loader',
options: {
name: 'img/[name]_[hash:6].[ext]',
// 表示小于100kb的用url-loader的base64URI编码
limit: 100 * 1024
}
}
}, {
test: /\.(png|jpg|gif|svg|jpeg)$/,
type: "asset",
generator: {
filename: 'img/[name]_[hash:6][ext]'
},
parser: {
dataUrlCondition: {
maxSize: 100 * 1024
}
}
}, {
test: /\.(eot|ttf|woff2?)$/,
use: {
loader: 'file-loader',
options: {
name: 'font/[name]_[hash:6].[ext]'
}
}
}]
},
plugins: [
// 一个一个的插件对象
// 使用这个插件
new CleanWebpackPlugin()
]
}
HtmlwebpackPlugin
接下来我们使用HtmlwebpackPlugin来自动生入口html文件,也就是我们这里的index.html
plugins:[
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
})
]
CopyWebpackPlugin
new CopyWebpackPlugin({
patterns: [{
from: 'src',
to: './',
globOptions: {
ignore: [
'**/index.html'
]
}
}]
})
CopyWebpackPlugin用来将文件复制到build文件夹下
mode和devtool配置
mode不只是帮助我们配置文件,在开发阶段会帮助我们配置默认的东西;
module.exports = {
// 选择模式,开发模式development(开发阶段)还是生产模式production(准备打包上线的时候)
mode: 'development',
// 映射文件,建立js文件,方便调试错误
devtool: 'source-map',
...
}
好了,以上就是webpack的一些简易配置。