文章目录
- 自定义webpack打包的入口与出口
- css-loader的安装与配置
- style-loader的安装, 引入style标签
- less 与 less-loader的安装与配置, 解析less文件
- file-loader 与 打包图片
- url-loader 与 base64编码
- webpack5对图片的打包方式: asset module type
- 打包字体图标
- clean-webpack-plugin 一个在打包时, 自动清理原先打包目录的插件
- html-webpack-plugin 打包index.html 以及 仿vue自定义html模板
- copy-webpack-plugin 还原vue 对 favicon.icon的打包复制配置
- mode配置项
- Babel
###webpack全局安装
npm i webpack@5.37.1 webpack-cli@4.7.0 -g
webpack --v
###webpack 初使用(全局打包)
//文件目录
├── 项目目录
│ ├── src
│ │ ├── js
│ │ │ ├── math.js
│ │ │ └── format.j
│ │ └── index.js
├ └── index.html
// index.js
import { sum } from './js/math'
const { dateFormat } = require('./js/format')
console.log(sum(1, 2, 3, 4, 5), dateFormat('2021/10/22'));
// format.js
function dateFormat(str) {
return new Date(str)
}
module.exports = {
dateFormat
}
// math.js
export function sum(...args) {
const arr = [...args]
return arr.reduce((pre, curr) => pre + curr, 0)
}
在项目目录下, 终端输入webpack, 即生成dist文件, 将文件导入index.html中, 在server中打开
总结:
将不同模块化js文件通过 webpack 打包后生成的js文件能被browser识别
###局部安装webpack与配置局部打包命令
以下命令均在项目根目录下运行:
局部安装:
npm i webpack@5.37.1 webpack-cli@4.7.0 -D
配置局部打包命令
1: 生成项目包配置文件 package.json
npm init -y
2: 在package.json中配置script
"scripts": {
"build": "webpack"
}
3: 运行 npm run build
总结:
1: 为什么需要局部安装webpack
答: 每个脚手架(vue react) 都是用不同版本的webpack作为打包工具, 如果使用全局webpack可能会产生版本兼容性问题
2: 为什么需要配置局部打包命令, 直接使用webpack不行吗?
答: 直接使用webpack实际上使用的还是电脑全局安装的webpack进行打包, 而局部打包命令少见或繁琐(npx webpack), 所以需要配置一个通俗的打包命令
自定义webpack打包的入口与出口
// webpack 默认从项目目录下的src文件下的index.js开始打包, 并打包至项目目录下dist/main.js (自动生成)
//若想指定打包的入口(entry) 和 出口(output) 则需要在项目目录下新建并配置webpack.config.js文件
const { join } = require('path')
module.exports = {
entry: "./src/main.js", //指定入口文件名为main.js
output: {
path: join(__dirname, "./build"), //语法要求出口目录必须为绝对路径, 这里采用node.js语法拼接
filename: 'js/bundle.js' //指定打包后的文件名
}
}
css-loader的安装与配置
// webpack只内置了对js文件的打包, 如果想打包其他资源, 需要安装基于webpack的插件
// css-loader的安装
// 1: 项目目录下 运行该命令
npm i css-loader
// 2:在webpack.config.js中配置模块规则
module.exports = {
module: {
rules:[ //规则有很多, 所以用数组
{ test:'/\.css$/', use:[ 'css-loader' ] } //每个正则对应一个文件, 可能不仅需要一个loader,所以用正则
]
}
}
style-loader的安装, 引入style标签
// 安装好cssloader后, css样式并不出来, 原因是 css-loader只负责解析css模板, 样式生效还需要引入外联css文件或嵌入style标签// style.loader可以将css-loader解析出来的css以style标签的形式嵌入html找那个步骤一: 安装style-loader npm i style-loader -D步骤二: 配置 webpack.config.jsmodule.exports = { module: { rules: [ { test: /\.css$/, use: ["style-loader", "css-loader"] } ] //use内的顺序有严格要求, 对cssloader的加载顺序, 以数组内从后向前加载. 应该是先解析css,再挂载 } //style标签, 所以先写style-loader 后写css-loader}
less 与 less-loader的安装与配置, 解析less文件
// 想解析less文件需要先安装less, 该插件提供less compile的能力 , 再安装less-loader 这个loader提供了模块化的能力// 安装步骤1: npm i less less-loader -D2:配置webpack.config.jsonmodule.exports = { module: { rules: [ { test: /\.less$/, use: ["style-loader", "css-loader", "less-loader"] } //注意顺序 ] }}
###postcss-loader 与 其插件 postcss-preset-env
postcss-loader是一个转换css的加载器, postcss-preset-env可以为css自动添加浏览器兼容前缀, 识别一些新的css语法, 并将其转换成兼容的css语法, 比如 color:#12345678 ,传统的16进制只能容纳六位, 新特性可以增加到8位, 最后两位代表alpha(透明度), postcss-preset-env可以将其转换成rgba的形式// 写法一: 插件和loader配置在一起module.exports = { module: { rules: [{ test: /\.css$/, use: [ "style-loader", "css-loader", { loader: "postcss-loader", options: { postcssOptions: { plugins: [require("postcss-preset-env")] } } } ] } ] }}//写法二 :postcss-loader配置与webpack配置分离, webpack.config.js中只配置postcss,插件配置写在postcss.config.js中//webpack.config.js中的配置module.exports = { module: { rules: [{ test: /\.css$/, use: [ "style-loader", "css-loader", "postcss-loader" ] } ] }}//postcss.config.js中的配置module.exports = { plugins: [ require('postcss-preset-env') ]}
file-loader 与 打包图片
第一步:
安装: npm i file-loader -D
第二步:
配置webpack.config.js
module.exports = {
module: {
rules: [
{ test: /\.(png|svg|jpe?g|gif)/, use: ['file-loader '] }
]
}
}
记录: 老师安装file-loader后打包图片, 就会在指定的出口目录生成依赖的图片, 但我生成了两张
###file-loader 与 src引入图片
// 通过src 修改图片img图片的路径时应该使用这种方式
import imgSrc from "../img/xxx.png" //直接导入图片, 并将图片赋值给img元素的src
const imgEl = document.createElement('img')
imgEl.src = imgSrc
###file-loader的配置项 修改图片输出的目录和输出图片名称
// webpack.config.js配置项
module.exports = {
module:{
rules:[
{
test:/\.(jpe?g|png|gif)/,
use:{
loader:"fileloader",
options:{
outputPath:'image' // 输出的图片模块将在指定出口目录下,生成一个image目录存放图片
name:'[name]_[hash:6].[ext]' // 内置占位符[name] 图片的原名称 [hash:6] 截取6位的hash值
//语法糖 name:'image/[name]_[hash:6].[ext]'
} //防止文件冲突, [ext] extense的缩写, 后缀的意思,取元文件的后缀
}
}
]
}
}
url-loader 与 base64编码
// url-loader 与 file-lorder用法基本一致, url-loader的优势在于, 能将小图片以base64的编码格式进行编码, 嵌入js或css文件中, 伴随js, css文件一起下载到客户端, 从而减少了因图片的网络请求, 是一种优化性能的手段
// webpack.config.js 配置
module.exports = {
module:{
rules:[
{
test:/\.(png|jpe?g|gif|svg)/,
use:{
loader:"url-loader",
options:{
outputPath:'image',
name:'[name]_[hash].[ext]',
limit:100 * 1024 // limit 用以规定低于多少大小的文件编码成base64, 单位是比特, 这里指定100kb
} //以下被编码, 过大的图片不适合编码成base64
}
}
]
}
}
webpack5对图片的打包方式: asset module type
// 在webpack5中已经不推荐使用 url-loader 或者 file-loader对图片进行打包, 取而代之的是内置的asset module type 资源模型类型, 配置方式如下
module.exports = {
module:{
rules:[
{
test:/\.(png|jpe?g|gif)/,
type:'asset', // 不再使用use配置项, 使用type
generator:{ //生成器
filename:'img/[name]_[hash:6][ext]' //asset中的[ext]包括了.
},
parser:{ //解析器
maxSize:100 * 1024
}
}
]
}
}
打包字体图标
// 通过file-loader打包module.exports = { module:{ rules:[ { test:/\.(eot|ttf|woff|svg)$/, use:{ loader:"file-loader", options:{ outputPath:"fonts", name:"[name]_[hash].[ext]" } } } ] }}//通过 asset/resource打包, 等同于file-loadermodule.exports = { module:{ rules:[ { test:/\.(eot|ttf|woff|svg)$/, type:'asset/resource', generator:{ filename:'fonts/[name]_[hash:6][ext]' //再次强调, asset中的[ext]前面不用加. } } ] }}
##插件
clean-webpack-plugin 一个在打包时, 自动清理原先打包目录的插件
// 第一步: 安装插件 npm i clean-webpack-plugin -D// 第二步: 配置webpack.config.jsconst { CleanWebpackPlugin } = require('clean-webpack-plugin')module.exports = { plugins:[ new CleanWebpackPlugin() ]}
注: `大多数!`插件都是暴露一个类(构造函数), 使用方式即为new
html-webpack-plugin 打包index.html 以及 仿vue自定义html模板
// html-webpack-plugin 为webpack内置的插件, 可以自动生成一个index.html 并自动导入对应的依赖// vue 的index目录示意简图├──项目目录 ├── node_nodules ├── public │ ├── index.html │ └── favicon.icon .....<!-- vue3 index.html 模板 --><!DOCTYPE html><html lang="cn-ZH"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <link rel="icon" href="<%= BASE_URL %>favicon.ico"> <title><%= htmlWebpackPlugin.options.title %></title> </head> <body> <noscript> <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <div id="app"></div> <!-- built files will be auto injected --> </body></html>
其中 <%= BASE_URL %> 在webpack内置插件DefinePlugin中定义, title读取的是HtmlWebpackPlugin插件配置项的title内容
// 配置DefinePlugin 和 HtmlWebpackPlugin//第一步:下载依赖插件 npm i html-webpack-plugin -D//第二步: webpack.config.js中配置const HtmlWebpackPlugin = require('html-webpack-plugin')const { DefinePlugin } = require('webpack')module.exports = { plugins:[ new HtmlWebpackPlugin({ title:'想出现在index.html title标签的内容', // filename:'index.html', //在filename中可以指定输出的文件目录和文件名, 默认生成在根目录下index.html template:'./public/index.html' }), new DefinePlugin({ BASE_URL:"'./'" //注意! 这里只写一对引号引用的是上下文的变量, 使用两对引号引用的是字符串 }) ]}
copy-webpack-plugin 还原vue 对 favicon.icon的打包复制配置
//实现目标: 对public的目录下所有文件(除被html-webpack-plugin管理的index.html文件) 在打包时进行copy// 第一步: npm i copy-webpack-plugin -D//第二步: 配置webpack.config.jsconst CopyWebpackPlugin = require('copy-webpack-plugin')module.exports = { plugins:[ //目录下除了index.html必须有文件才能用, 否则会报错 new CopyWebpackPlugin({ patterns:[ // 匹配规则(模式)可能有很多, 所以是数组 { from:'public', to:'./', globOptions:{ //是 glob 不是 global ignore:['**/index.html'] } } ] }) ]}
mode配置项
// mode配置项有两个值 "developement","production" 当选择dev模式时, 再指定devtool为 "source-map" 即能与入口js文件同时生成一个map映射文件, 当项目跑起来后, 如果有错误, 方便错误信息的定位,调试.module.exports = { mode: 'development', devtool:'source-map'}
Babel
什么是babel : 一个独立的库,本质上是编译器, 将js从es6转成es5, ts转成js..等, 可以脱离webpack使用, 而webpack也集成了babel. babel自身也依赖了许多插件, 来对不同的语法进行转换(比如箭头函数转普通函数即是一个插件), 一般使用babel的预设插件能 达到预期的转换效果
// 独立使用 babel// 使用步骤: //1:安装babelnpm i @babel/core @babel/cli -D//2:安装babel预设插件npm i @babel/preset-env -D//3:局部调用babelnpx babel demo.js --out--dir dist --presets=@babel/preset-env //将当前目录的某js文件使用预设插件编译到指定文件夹或者npx babel demo.js --out-file test.js --presets=@babel/preset-env //将当前目录的某js文件使用预设插件编译到当前目录,指定文件名------------------------------------------------------------------------------------------------------------// 编译前的文件const demo = 1;const handler = () => console.log('this is a demo')//编译后的文件"use strict";var demo = 1;var handler = function handler() { return console.log('this is a demo');}
Babel编译大致原理
源代码 --> 词法分析生成数组 ---> 语法分析生成AST树 ---> 遍历AST树并运用插件进行编译 ---> 生成新的AST语法树 ---> 生成目标代码
babel-loader 链接babel与模块化的loader
// 第一步: 安装npm i babel-loader @babel/core @babel/preset-env -D//第二步配置 webpack.config.jsmodule.exports = { module:{ rules:[ { test:/\.js$/, use:{ loader:'babel-loader', options:{ presets:[ '@babel/preset-env' ] } } } ] }}// 或者单独配置 babel.config.jsmodule.exports = { presets:[ "@babel/preset-env" ]}
vue-loader 打包单文件组件(.vue)
// vue-loader依赖@vue/compiler-sfc, 并且vue-loader的使用需要配置插件详情如下// sfc: single file components 单文件组件编译器// 步骤一: 安装npm i vue@nextnpm i vue-loader@next @vue/compiler-sfc -D// 配置webpack.config.jsimport { VueLoaderPlugin } from 'vue-loader/dist/index'import { DefinePlugin } from 'webpack'module.exports = { module:{ rules:[ { test:/\.vue/, loader:'vue-loader' } ] }, plugins:[ new VueLoaderPlugin(), new DefinePlugin({ __VUE_OPTIONS_API__:true, // 是否在vue3中兼容vue2api __VUE_PROD_DEVTOOLS__:false //是否关闭开发时提示 }) ]}
自动编译 watch
// 当每次修改代码, 都要重新编译, 非常繁琐, 可以配置watch, 来达到一次编译(npm run build)后, 每次修改代码自动编译// 方式一: 在package.json中配置{ "scripts":{ "build":"webpack --watch" }}// 方式二: 在webpack.config.js中配置module.exports = { watch:true}
webpack-dev-server
// 什么是 webpack-dev-server?webpack-dev-server 可以启动一台基于express框架编写的服务器, 将当前资源进行打包, 自动部署到服务器当中, 并且每次修改代码, 不需要重新打包, server会自动打包部署, webpack-dev-server的优势还在于, 并不是将资源打包到磁盘当中, 而是内存当中// webpack-dev-server的安装与部署// 第一步:安装npm i webpack-dev-server -D //注意: 需要搭配 webpack5和webpack-cli 4.9以上// 第二步 :配置项目包管理文件 package.json{ scripts:{ "build": "webpack", "serve": "webpack serve" }}// 第三步: 运行webpack-dev-servernpm run serve
配置 webpack-dev-server
// contentBase 选项可以在 资源并不在服务器时, 客户端又向服务器索要了资源, 指定一个目录, 让服务器从这里拿资源返回给客户端// 在webpack.config.json中配置:module.export = { devServer:{ contentBase:'./public' //指定public文件夹为资源替代目录 }}
HMR
// 什么是HMR : hot module replace 模块热替换, 在webpack中每一个文件(css,img,js,vue..) 被视为一个模块, 当一个模块中内容 发生改变时候, 整个页面都会刷新, 有时候我们不希望这么做, HMR的功能即是让一个模块的内容被修改时, 只修改页面中该模块的内容, 其 他的模块内容并不改变// webpack-dev-server提供了HMR的能力, 配置如下// 1: 指定需要配置HMR的模块, 如为demo.js配置热替换main.js :import somthing from './demo.js'module.hot && module.hot.accept('./demo.js', ()=>{ // 回调函数不是必须的, 当热替换时被调用 console.log('demo.js进行了HMR')})// 2: 在webpack.config.js中配置module.exports = { target:'web', devServer:{ hot:true }}
HMR 原理
开启HMR后, 启动webpack-dev-server, 实际上开启了两个服务器, 一个是基于express写的服务器, 用于响应http请求, 一个是soket长链接服务器, 该服务器可以与客户端之间建立长链接, 当服务器数据被修改时, 主动向浏览器发送修改的数据, 浏览器拿到数据后做出相应模块的修改
webpack-dev-server 其他配置项
// 在webpack.config.js中module.exports = { devServer:{ host:'127.0.0.1', //指定域名/ip port:'8090', //指定端口号 open:true, //是否自动打开默认浏览器, 默认为false compress:true //是否以gzip的形式向客户端发送数据,默认为false 一般不开启, 自己玩没意义 } //开启后查看浏览器接受的js文件的响应头, 发现Content-Encoding:gzip}
webpack-dev-server 配置网络请求代理
为什么要配置代理?答: 当打包好的资源发送到客户端, 其中的js文件中有网络请求, 并且跨域了, 浏览器就会受到浏览器同源策略的影响拿不到数据, 但同源策略是浏览器的, 服务器之间的通信不受到同源策略的影响, 此时, 浏览器只需要委托webpack-dev-server向目的地发送请求, 拿到数据, 再返回给浏览器, 客户端就能顺利的拿到数据// 配置示例:module.export = { devServer:{ // proxy: "http://127.0.0.1:9999" 只能代理一个服务器, 所有的请求都被发送到这个服务器 "/api":{ target:"http://127.0.0.1:9999", pathRewrite:{ "^apt":"" }, changeOrange:true }, "/list":{ target:"http://127.0.0.1:9999", pathRewrite:{ "^/list":"" }, changeOrange:true }, }}
resolve 配置项
// resolve 用于配置weback打包项目时, 如何解析路径的, extensions指定解析的扩展名, 配置上.vue后, 引入组件不需要加.vue扩展 名,alias可以为一个路径起别名, 导入时可以使用别名来代替路径const { join } = require('path')module.exports = { resolve:{ extensions:['.js','.json','.vue','.ts'], alias:{ '@':join(__dirname, './src') } }}
拆分webpack.config.js
文件目录├── config│ ├── webpack.comm.config.js│ ├── webpack.dev.config.js├─ └── webpack.prod.config.js// package.json 文件配置{ "scripts":{ "serve":"webpack serve --config ./config/webpack.dev.config.js" "build":"webpack --config ./config/webpack.prod.config.js" }}// webpack.comm.config.js文件module.exports = { target:'web', entry:"./src/index.js", output:{ filename:'js/bundle.js' }, resolve:{ extensions:[], alias:[] }, module:{ rules:[] }, plugins:[]}// webpack.dev.config.js 文件const const { merge } = require('webpack-merge')const comm = require('./webpack.comm.config')module.exports = merge(comm, { mode: 'development', devtool: 'source-map', devServer: {}})// weback.prod.config.jsconst { CleanWebpackPlugin } = require("clean-webpack-plugin")const CopyWebpackPlugin = require("copy-webpack-plugin");const { merge } = require('webpack-merge')const comm = require('./webpack.comm.config')module.exports = merge(comm,{ mode: 'production', plugins: [ new CleanWebpackPlugin(), new CopyWebpackPlugin(), ]})
publicPath 打包后直接打开index.html
module.exports = { publciPath:'./'}// 打包后的文件不部署在服务器上, 直接点开是有问题的, 主要是index.html引用文件路径, 如果双击点开, 使用file协议读取文件是读不到
vue-cli的安装与更新
// 全局安装vue-clinpm i @vue/cli -g// 更新 vue-clinpm updata @vue/cli -g// 创建脚手架vue create 项目名称