目录
- webpack基础打包
- 1、webpack的安装
- 2、webpack的配置文件
- 3、webpack的依赖图
- 4、loader的使用(以css-loader为例)
- 5、webpack+sass
- 6、postcss
- 7、file-loader(webpack5中测试失败)
- 8、url-loader(webpack5中测试失败)
- 9、资源模块类型(asset module type)
- 10、加载字体文件(webpack.config.js)
- 11、认识Plugin
- 12、CleanWebpackPlugin
- 13、HtmlWebpackPlugin和DefinePlugin
- 14、CopyWebpackPlugin
- 15、mode和devtool(webpack.config.js)
- 16、babel
- 17、vue源码的打包
- 18、vue搭建本地服务器
- 19、webpack-merge
webpack基础打包
1、webpack的安装
- webpack使用前提
webpack依赖node环境
- webpack的安装
# 1、初始化项目(项目名不能包含中文)
npm init -y
# 2、安装webpack、webpack-cli
npm i webpack webpack-cli -D
- webpack的打包
* webpack:全局命令
* npx webpack:node_modules命令
* package.json -> "scripts" -> "build": "webpack":node_modules命令
2、webpack的配置文件
- 默认配置文件名(package.json)
"scripts": {
"build": "webpack --config webpack.config.js"
}
- 默认配置(webpack.config.js)
// path模块:由node环境提供,package.json中无需依赖
const path = require('path');
module.exports = {
// 打包的入口文件名
entry: './src/index.js',
output: {
/**
* path.resolve():合并路径
* __dirname:由node环境提供,当前配置文件所在路径
* ./dist:打包的输出路径
*/
path: path.resolve(__dirname, './dist'),
// 打包的出口文件名
filename: 'main.js'
}
};
3、webpack的依赖图
* webpack打包过程
- 根据配置文件找到入口文件
- 从入口文件开始,找到所有依赖的文件,形成一个依赖关系图(一种数据结构:图结构)
- 遍历图结构,打包一个一个文件(根据不同的文件类型,使用不同的loader解析)
* --save-dev和--save使用错误,打包不会有问题,为了规范尽量不要使用错误
4、loader的使用(以css-loader为例)
- loader的作用
webpack并不知道如何对css类型的文件进行加载,需通过对应的loader进行转换。
- loader的安装
npm i -D css-loader style-loader
- loader的使用方案
* 内联方式(import "css-loader!@/styles/index.css")
* CLI方式(webpack5中不再使用)
* 配置方式
- 配置方式使用loader(webpack.config.js)
module.exports = {
module: {
rules: [
{
test: /\.css$/,// 正则表达式用以匹配文件
// 1、loader写法(语法糖)
// loader: "css-loader"
// 2、完整写法
use: [
// loader是有加载顺序的(从后往前)
{loader: "style-loader", options: {}},
{loader: "css-loader", options: {}}
]
}
]
}
};
5、webpack+sass
- 安装
npm i -D sass sass-loader
- 配置(webpack.config.js)
module.exports = {
module: {
rules: [
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"]
}
]
}
};
6、postcss
- 安装
# 命令:npx postcss --use autoprefixer -o entry.css output.css
# npm i postcss postcss-cli -D
npm i postcss-loader -D
# 补全前缀
# npm i autoprefixer -D
# 转换一些现代的css特性(比如:#ffffffff),并补全前缀
npm i postcss-preset-env -D
- 配置(webpack.config.js)
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
"style-loader",
"css-loader",
{
loader: "postcss-loader",
// 配置方式一
/*options: {
postcssOptions: {
plugins: [
require("autoprefixer")
]
}
}*/
}
]
}
]
}
};
- 配置(postcss.config.js)
module.exports = {
plugins: [
require("postcss-preset-env")
]
}
7、file-loader(webpack5中测试失败)
- 安装
npm i -D file-loader
- 配置(webpack.config.js)
module.exports = {
module: {
rules: [
{
test: /\.(jpe?g|png|gif|svg)$/i,
use: [{
loader: "file-loader",
options: {
// outPath: "img",
name: "img/[name]_[hash:6].[ext]"
}
}]
}
]
}
};
- 文件的命名规则
* [ext]:处理文件的扩展名
* [name]:处理文件的名称
* [hash]:文件的内容,使用MD4的散列函数处理,生成的一个128位的hash值(32个十六进制)
* [contentHash]:在file-loader中和[hash]结果是一致的(在webpack的一些其他地方不一样)
* [hash:<length>]:截取hash的长度,默认32个字符太长了
* [path]:文件相对于webpack配置文件的路径
8、url-loader(webpack5中测试失败)
- 安装
npm i -D url-loader
- 配置(webpack.config.js)
module.exports = {
module: {
rules: [
{
test: /\.(jpe?g|png|gif|svg)$/i,
use: [{
loader: "url-loader",
options: {
name: "img/[name]_[hash:6].[ext]",
limit: 1024 * 100 // KB
}
}]
}
]
}
};
9、资源模块类型(asset module type)
- 配置(webpack.config.js)
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'js/main.js',
// assetModuleFilename: "img/[name]_[hash:6][ext]"
},
module: {
rules: [
{
test: /\.(jpe?g|png|gif|svg)$/i,
type: "asset",
generator: {
filename: "img/[name]_[hash:6][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 1024 * 100
}
}
}
]
}
};
- 4种模块类型(替代file-loader、url-loader)
* asset/resource:发送一个单独的文件并导出URL。之前通过使用file-loader实现
* asset/inline:导出一个资源的data URI。之前通过使用url-loader实现
* asset/source:导出资源的源代码。之前通过使用raw-loader实现
* asset:在导出一个data URI和发送一个单独的文件之间自动选择。之前通过使用url-loader,
并且配置资源体积限制实现
10、加载字体文件(webpack.config.js)
module.exports = {
module: {
rules: [
// file-loader测试失败
/*{
test: /\.(eot|ttf|woff2?)$/,
use: {
loader: "file-loader",
options: {
name: "font/[name]_[hash:6].[ext]"
}
}
},*/
{
test: /\.(eot|ttf|woff2?)$/,
type: "asset/resource",
generator: {
filename: "font/[name]_[hash:6][ext]"
}
}
]
}
};
11、认识Plugin
* Loader是用于特定的模块类型进行转换
* Plugin可以用于执行更加广泛的任务,比如打包优化、资源管理、环境变量注入等
12、CleanWebpackPlugin
- 安装
npm i -D clean-webpack-plugin
- 配置(webpack.config.js)
const {CleanWebpackPlugin} = require("clean-webpack-plugin")
module.exports = {
plugins: [
new CleanWebpackPlugin()
]
};
13、HtmlWebpackPlugin和DefinePlugin
- 安装
npm i -D html-webpack-plugin
# DefinePlugin是webpack内置插件,无需安装
- 配置(webpack.config.js)
const HtmlWebpackPlugin = require("html-webpack-plugin")
const {DefinePlugin} = require("webpack")
const processDefinePlugin = {}
Object.keys(process.env).forEach(key => {
processDefinePlugin[`process.env.${key}`] = JSON.stringify(process.env[key])
})
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html",
title: "孟美岐" // htmlWebpackPlugin.options.title
}),
new DefinePlugin({
...processDefinePlugin,
BASE_URL: "'./'" // 允许在编译时创建配置的全局常量
})
]
};
14、CopyWebpackPlugin
- 安装
npm i -D copy-webpack-plugin
- 配置(webpack.config.js)
const CopyWebpackPlugin = require("copy-webpack-plugin")
module.exports = {
plugins: [
new CopyWebpackPlugin({
patterns: [{
from: "public",
to: "./",
globOptions: {
ignore: ["**/index.html"]
}
}]
})
]
};
15、mode和devtool(webpack.config.js)
module.exports = {
/**
* 设置模式:
* - development:开发阶段,会设置development
* - production:准备打包上线的时候,设置production
*/
mode: "development",
// 设置source-map,建立js映射文件,方便调试代码和错误
devtool: "source-map"
};
16、babel
- 安装
##################
# 命令:npx babel index.js --out-dir dist --out-file main.js
# --plugins=@babel/plugin-transform-arrow-functions,
# @babel/plugin-transform-block-scoping
##################
# npm i -D @babel/core @babel/cli
# 转换箭头函数
# npm i -D @babel/plugin-transform-arrow-functions
# 转换块级作用域
# npm i -D @babel/plugin-transform-block-scoping
##################
# babel的预设
# 命令:npx babel index.js --out-file main.js --presets=@babel/preset-env
##################
npm i -D @babel/preset-env
# @babel/core也要安装(测试结果并不需要)
npm i -D babel-loader
- babel的底层原理
* babel可以看成是一个编译器
* 简化版的编译器工作流程
- 原生源代码
- 解析(parsing)
- 转换(transformation)
- 代码生成(code generation)
- 目标源代码
* 每个阶段具体的工作
- 原生源代码
- 词法分析(lexical analysis)
- tokens数组
- 语法分析(syntactic analysis)(也称为parsing)
- AST抽象语法树
- 遍历(traversal)
- 访问(visitor)
- 应用插件(plugin)
- 新的AST(新的抽象语法树)
- 目标源代码
- 配置(webpack.config.js)
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: {
loader: "babel-loader",
// 配置方式一
/*options: {
/!*plugins: [
"@babel/plugin-transform-arrow-functions",
"@babel/plugin-transform-block-scoping"
],*!/
presets: [
["@babel/preset-env", {}]
]
}*/
}
}
]
}
};
- 配置(babel.config.js)
/**
* babel.config.js(或者.json、.cjs、.mjs)文件(monorepos项目)(babel7推荐)
* .babelrc.js(或者.babelrc、.json、.cjs、.mjs)文件(rc:runtime compiler)
*/
module.exports = {
presets: [
"@babel/preset-env"
]
}
17、vue源码的打包
- 安装
# vue3需要加@next
npm i -S vue@next
##################
# 配置(webpack.config.js)
# const {DefinePlugin} = require("webpack")
# const {VueLoaderPlugin} = require("vue-loader/dist/index")
#
# module.exports = {
# module: {
# rules: [
# {
# test: /\.vue$/,
# loader: "vue-loader"
# }
# ]
# },
# plugins: [
# new DefinePlugin({
# // 配置用以消除警告
# __VUE_OPTIONS_API__: true,
# __VUE_PROD_DEVTOOLS__: false,
# }),
# new VueLoaderPlugin()
# ]
# };
##################
npm i -D vue-loader@next
# 测试结果并不需要
# npm i -D @vue/compiler-sfc
- vue打包后不同版本解析
* vue(.runtime).global(.prod).js
- 通过浏览器中的<script src="...">直接使用
- 我们之前通过CDN引入和下载的vue版本就是这个版本
- 会暴露一个全局的Vue来使用
* vue(.runtime).esm-browser(.prod).js
- 用于通过原生ES模块导入使用(在浏览器中通过<script type="module">来使用)
* vue(.runtime).esm-bundler.js
- 用于webpack、rollup和parcel等构建工具
- 构建工具中默认是vue.runtime.esm-bundler.js
- 如果我们需要解析模板template,那么需要手动指定vue.esm-bundler.js
* vue.cjs(.prod).js
- 服务端渲染使用
- 通过require()在node.js中使用
- 运行时+编译器 vs 仅运行时
* 在Vue的开发过程中我们有三种方式来编写DOM元素
- template模板的方式(需要通过源码中compiler部分代码来进行编译)
- render函数的方式,使用h函数来编写渲染的内容(h函数可以直接返回一个虚拟节点,
也就是VNode节点)
- 通过.vue文件中的template来编写模板(通过vue-loader对其进行编译和处理)
* 仅运行时不包含对template模板的编译代码,所以相对运行时+编译器更小一些
- 运行时+编译器版本
import {createApp} from "vue/dist/vue.esm-bundler.js"
/**
* index.html文件部分代码
* <template id="my-app">
* <h3>{{text}}</h3>
* </template>
*/
const app = createApp({
template: `#my-app`,
components: {},
data() {
return {
text: "这是一段中文"
}
}
})
app.mount("#app")
- webpack对SFC文件的支持
import {createApp} from "vue"
import App from "./App.vue"// 后缀名不能省略
/*
// App.vue文件代码
<template>
<h3>{{text}}</h3>
</template>
<script>
export default {
name: "App",
data() {
return {
text: "这是一段中文"
}
}
}
</script>
<style scoped>
h3 {
color: red;
}
</style>
*/
const app = createApp(App)
app.mount("#app")
18、vue搭建本地服务器
- webpack提供完成自动编译的几种方式
- webpack watch mode
- webpack-dev-server(常用)
- webpack-dev-middleware
- webpack watch
* 该模式下,webpack依赖图中的所有文件,只要有一个发生了更新,那么代码将被重新编译
* 两种方式开启watch:
// 方式一、package.json
{
"scripts": {
"build": "webpack --watch"
}
}
// 方式二、webpack.config.js
module.exports = {
watch: true,
}
- webpack-dev-server
* 安装:npm i -D webpack-dev-server
// 配置(package.json)
{
"scripts": {
"serve": "webpack serve",
}
}
// 开发阶段静态资源配置(webpack.config.js)(测试结果此配置已不再生效)
module.exports = {
devServer: {
// 本地服务找不到的资源,从此配置的目录中找
contentBase: "./public"
}
}
- webpack-dev-server流程解析
* webpack-dev-server不需与webpack watch一起使用
* webpack-dev-server不会写入到任何输出文件,而是将文件保留在内存中
(使用了一个库叫memfs。memory-fs是webpack自己写的,目前已经停更)
* 源代码 -> 通过webpackDevServer打包至内存(使用memfs库)
-> 本地服务(express框架)从内存中获取资源 -> 浏览器访问
- 模块热替换(HMR)
* 全称Hot Module Replacement,是指在应用程序运行过程中,替换、添加、删除模块,
而无需重新刷新整个页面。
* 社区成熟的解决方案
- vue开发中:vue-loader已经支持了.vue文件HMR
- react开发中:react hot loader(目前已弃用,改用react-refresh)
// 配置(webpack.config.js)
module.exports = {
target: "web",// node项目值为:"node"
devServer: {
hot: true // 默认就是true
},
}
// 操作模块成为热更新模块
import "./utils/index"
if (module.hot) {
module.hot.accept("./utils/index.js", () => {
console.log("发生了热更新")
})
}
- HMR的原理
* webpack-dev-server会创建两个服务:
- 提供静态资源的服务(express)
- Socket服务(net.Socket)
* HMR Socket Server,是一个socket的长连接
- 长连接有一个最好的好处是建立连接后双方可以通信(服务器可以直接发送文件到客户端)
- 当服务器监听到对应的模块发生变化时,
会生成两个文件.json(manifest文件)和.js文件(update chunk)
- 通过长连接,可以直接将这两个文件主动发送给客户端(浏览器)
- 浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,
并且针对修改的模块进行更新
- devServer的其它配置
module.exports = {
devServer: {
/**
* localhost(127.0.0.1)回环地址,不经过数据链路层和物理层,其他主机无法访问。
* 如果想要其他主机可以访问,设值为:"0.0.0.0"
*/
host: "localhost",
port: "8080", // 端口号
/**
* 打开浏览器,也可以在package.json中配置:
* {"script":{"serve":"webpack serve --open"}}
*/
open: true,
// 是否开启gzip压缩。浏览器默认支持gzip,会自动解压
// compress: false
},
}
- Proxy跨域代理
module.exports = {
devServer: {
proxy: {
'/api': {//拦截请求,此时url:/api/requesturl
// 在请求前加源地址,此时url:http://www.baidu.com:8080/api/requesturl
target: 'http://www.baidu.com:8080',
pathRewrite: {
// 重写请求,此时url:http://www.baidu.com:8080/requesturl
'^/api': ''
},
/**
* 修改request headers的host为target的值,
* 需后端通过request.getHeader("Host")方式查看
*/
changeOrigin: true,
secure: false // 默认true,不支持接收转发到https的服务器上
}
}
},
}
- resolve模块解析
module.exports = {
resolve: {
modules: ["node_modules"],// 默认查找模块的路径
mainFiles: ["idnex"],// 默认查找文件名
extensions: [".wasm", ".mjs", ".js", ".json"],// 默认查找文件后缀名
alias: {// 路径别名
"@": path.resolve(__dirname, "./src")
}
},
}
19、webpack-merge
- 安装
npm i -D webpack-merge
- 配置(webpack(.prod|.dev).config.json)
const {merge} = require("webpack-merge")
module.exports = merge(otherConfig(导入的其它配置),{}(当前要做的配置))
- 配置(package.json)
{
"scripts": {
"serve": "webpack serve --config ./webpack.dev.config.js",
"build": "webpack --config ./webpack.prod.config.js"
},
}