一、认识webpack(静态的模块化打包工具)
1.【webpack解决的问题】:
- 模块化的方式来开发 (ESModule、common.js)
- 通过各种loader处理文件(less-loader,sass-loader,file-loader,vue-loader等)
- 热更新,提高开发的效率;
- 将代码进行压缩、合并以及其他相关的优化
2.【webpack的构建流程】:
1、初始化流程:初始化需要使用的插件和配置插件等执行环境所需要的参数;
2、编译构建流程:从entery出发,针对每个module 串行调用对应的Loader去翻译文件内容,再找到该Module依赖的Module,递归地进行编译处理。
3、输出流程:对编译后的module组合成chunk(大块代码),把chunk转为文件,输出到文件系统。
二、webpack安装
- 1.先安装Node.js,并且同时会安装npm;
- 2.创建全局的webpack;
npm install webpack webpack-cli -g # 全局安装
- 3.创建项目并使用webpack
//第一步:创建package.json文件,用于管理项目的信息、库依赖等
npm init
//第二步:安装局部的webpack
npm install webpack webpack-cli -D
//第三步:在package.json中创建scripts脚本
"scripts":{
"build":"webpack"
},
//第四步:创建文件夹src然后在src中创建index.js和index.html
//第五步:创建 webpack.config.js(配置入口和出口)
//第六步:开始打包
npx webpack / npm run build
会生成dist/bundle.js
三、配置文件webpack.config.js
1、基本配置
// 设置模式
// development 开发阶段, 会将DefinePlugin中process.env.NODE_ENV设置为development
// production 准备打包上线的时候, 设置production
mode: "development",
// 设置source-map, 建立js映射文件, 方便调试代码和错误
devtool: "source-map",
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "./build"),
filename: "js/bundle.js",
assetModuleFilename: "img/[name]_[hash:6][ext]"
},
2、Loader
- loader用于对模块的“源代码”进行转换,在import或“加载”模块时预处理文件
- webapck做的事情,仅仅是分析出各种模块的依赖关系,然后形成资源列表,最终打包生成到指定的文件中,
- webpack只支持对js和json文件打包,像css、sass、png等这些类型的文件的时候,webpack则无能为力,
- 这时候就需要配置对应的loader进行文件内容的解析。
1. 常见的loader
// loader是用于特定的模块类型进行转换;
module: {
rules: [{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-loader"],
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"],
},
// 一、理图片和字体等资源
// 1.webpack5之前:
// 1.1 file-loader/url-loader共有属性:
// [ext]: 处理文件的扩展名;
// [name]:处理文件的名称;
// [hash]:文件的内容,使用MD4的散列函数处理,生成的一个128位的hash值(32个十六进制);
// [contentHash]:在file-loader中和[hash]结果是一致的(在webpack的一些其他地方不一样,后面会讲到);
// [hash:<length>]:截图hash的长度,默认32个字符太长了;
// [path]:文件相对于webpack配置文件的路径;
// 1.2 url-loader的特有属性:
// [limit]: 1.url-loader对图片是否转base64的限制;小于20kb会转换base64之后可以和页面一起被请求’
{
test: /\.(jpe?g|png|gif|svg)$/, //图片
test: /\.(eot|ttf|woff2?)$/, //字体文件
loader: "file-loader" | "url-loader",
options: {
limit: 20 * 1024,
name: "img/[name].[hash:8]/[ext]"
}
},
// 2.webpack5之后:
// 资源模块类型(asset module type),来替代上面的这些loader
// 2.1.file-loader的name
// 方式一:修改output,添加assetModuleFilename属性;
// 方式二:在Rule中,添加一个generator属性,并且设置filename;
// 2.2.url-loader的limit效果
// 步骤一:将type修改为asset;
// 步骤二:添加一个parser属性,并且制定dataUrl的条件,添加maxSize属性;
{
test: /\.(jpe?g|png|gif|svg)$/,
type: "asset",
generator: {
filename: "img/[name]_[hash:6][ext]",
},
parser: {
dataUrlCondition: {
maxSize: 10 * 1024,
},
},
},
{
test: /\.(eot|ttf|woff2?)$/,
type: "asset/resource",
generator: {
filename: "font/[name]_[hash:6][ext]",
},
},
],
},
2. babel-loader
用法一:通过命令行使用
运行多个babel插件
1.安装@babel/core(babel的核心代码)和@babel/cli
npm install @babel/cli @babel/core -D
2.转换箭头函数
npm install @babel/plugin-transform-arrow-functions -D
npx babel src --out-dir dist --plugins=@babel/plugin-transform-arrow-functions
3.const 转成 var
npm install @babel/plugin-transform-block-scoping -D
npx babel src --out-dir dist --plugins=@babel/plugin-transform-block-scoping
运行一个babel插件(预设)
1.安装@babel/preset-env预设:
npm install @babel/preset-env -D
2.执行:npx babel src --out-dir dist --presets=@babel/preset-env
用法二:通过babel-loader设置
非预设
{
test: /\.js$/,
loader: "babel-loader",
options: {
plugins: [
"@babel/plugin-transform-block-scoping",
"@babel/plugin-transform-arrow-functions"
]
}
},
预设
{
test: /\.js$/,
loader: "babel-loader",
options: {
presets: [
"@babel/preset-env"
]
}
},
执行原理:
原生的代码会先被词法分析器进行分割然后生成tokens数组,生成的数组会经过语法分析,分析出哪些是关键字、运算符,并生成抽象语法树,对树进行遍历,对遍历时访问的不同关键字/运算符使用不同的插件转化,转化完生成新的树,最后生成目标源代码。
3.vue.loader
在Vue的开发过程中我们有三种方式来编写DOM元素:
方式一:template模板的方式,只有vue.esm-bundler.js的vue版本才能编译
import {createApp} from "vue/dist/vue.sem-bundler"
const App = createApp({
template: "#my-app",
components: {},
data() {
return {
title: "Hello World",
message: "哈哈哈"
}
}
});
const app = createApp(App);
app.mount("#app");
方式二:h函数直接就可以返回Vnode节点,然后通过render函数就可以编写DOM元素
方式三:通过.vue文件中的template来编写模板;则需要用vue-loader编译template
.vue的打包过程:
1.安装vue-loader
npm install vue-loader -D
2.在webpack.config.js中进行配置loader:
{
test:/\.vue$/,
loader:"vue-loader"
}
3.在webpack.config.js中进行配置对应的Vue插件
{
const {VUeLoaderPlugin} = require('vue-loader/dist/index');
new VueLoaderPlugin()
}
3、Plugin
-
Plugin是一种计算机应用程序,它和主应用程序互相交互,以提供特定的功能。
-
webpack中的plugin赋予其各种灵活的功能,例如打包优化,资源管理,环境变量注入等,它们会运行在webpack的不同阶段(钩子/生命周期),贯穿了webpack整个编译周期。目的在于解决loader无法实现的其他事。
// plugins是用于打包优化、资源管理、环境变量注入等;
plugins: [
//开启热更新HMR
new webpack.HotModuleReplacementPlugin(),
//CleanWebpackPlugin:重新打包时,会自动删除dist文件夹并重新生成新的
new CleanWebpackPlugin(),
//HtmlWebpackPlugin:自定义HTML模板,
//在打包结束后,自动生成一个html文件,并把打包生成的js模块引入到该html中
//[template]:指定我们要使用的模块所在的路径
new HtmlWebpackPlugin({
template: "./public/index.html",
title: "哈哈哈哈"
}),
//DefinePlugin允许在编译时创建配置的全局常量
new DefinePlugin({
BASE_URL: "'./'",
__VUE_OPTIONS_API__: true,//是否支持vue2的options
__VUE_PROD_DEVTOOLS__: false//是否在生产环境开启source-map
}),
//CopyWebpackPlugin在vue的打包过程中,public的目录下的文件会被复制到dist文件夹中
// from:设置从哪一个源中开始复制
// to:复制到的位置,可以省略,会默认复制到打包的目录下;
// globOptions:设置一些额外的选项,其中可以编写需要忽略的文件:
new CopyWebpackPlugin({
patterns: [{
from: "public",
to: "./",
globOptions: {
ignore: [
"**/index.html"
]
}
}]
}),
new VueLoaderPlugin()
],
4、dev-server(配置webpack-dev-server)
1、为什么要搭建本地服务器并开启热更新?
因为在浏览器运行代码有三种方式
- 1.通过npm run build编译的代码通过live server打开index.html。
不会监听文件变化 ;通过live server监听后自动刷新浏览器;不可实现部分更新
- 2.通过webpack watch mode;
会监听文件变化 ;通过live server监听后自动刷新浏览器;不可实现部分更新
配置中添加 watch: true;或者scripts:{ "watch":"webpack -- watch"}
- 3.通过webpack-dev-server
会监听文件变化 ;通过开启HMR不会刷新浏览器;可实现部分更新
webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中
2、webpack的[热更新]是如何做到的?原理是什么?
开启HMR模块热替换? HMR全称Hot Module Replacement,可以理解为模块热替换,指在应用程序运行过程中,替换,添加,删除模块,而无需重新刷新整个应用。例如,我们在应用运行过程中修改了某个模块,通过自动刷新会导致整个应用的整体刷新,那页面中的状态信息都会丢失,如果使用的是HMR,就可以实现只将修改的模块实时替换至应用中,不必完全刷新整个应用。
如何配置HRM:
1.修改webpack.config.js配置
devServer:{
hot:true
}
2.指定哪些模块发生更新时,进行HMR
- module.hot.accept("./js/element.js");指定模块
- vue.loader会在js文件中自动添加上面代码
原理:
1.通过webpack-dev-server创建两个服务器:提供静态资源的服务Express和Socket服务;
2.express server负责直接提供静态资源的服务(打包后的资源直接被浏览器请求和解析)
3.socket server 是一个websocket的长连接,双方可以通信
4.当 socket server 监听到对应的模块发生变化时,会生成两个文件.json(manifest文件)和.js文件(update chunk)
5.通过长连接,socket server 可以直接将这两个文件主动发送给客户端(浏览器)
6.浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新
5、全部配置代码:
const path = require("path");
const {
CleanWebpackPlugin
} = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const {
DefinePlugin
} = require("webpack");
const CopyWebpackPlugin = require('copy-webpack-plugin');
const {
VueLoaderPlugin
} = require('vue-loader/dist/index');
module.exports = {
// 设置模式
// development 开发阶段, 会将DefinePlugin中process.env.NODE_ENV设置为development
// production 准备打包上线的时候, 设置production
mode: "development",
// 设置source-map, 建立js映射文件, 方便调试代码和错误
devtool: "source-map",
lintOnSave,
//lintOnSave是否每次保存时开启eslint编译;
//1.false:关闭 2.true / warning:错误将显示到控制台命令行,而且编译并不会失败。
//3.default/error:错误将显示到浏览器页面上,且编译失败。
transpileDependencies: ["vux"], //兼容ie浏览器,指定node-module里面哪些依赖需要编译
entry: "./src/main.js",
assetsDir: 'static', // 放置静态资源
output: {
path: path.resolve(__dirname, "./build"),
filename: "js/bundle.js",
assetModuleFilename: "img/[name]_[hash:6][ext]"
},
//host:主机地址,默认是localhost(127.0.0.1)回环地址,不能通过ip被访问;
//设置为0.0.0.0时在同一个网段下的主机中,通过ip地址可以访问。
//port:端口号,默认是8080
//open:是否打开浏览器,默认是true
//compress:是否为静态文件开启gzip程序压缩 ,成功开启后将大大优化vue首页加载时长
//proxy:用于开发阶段解决跨域问题的方法,生产阶段一般用代理服务器以解决跨域访问的问题:
devServer: {
contentBase: "./public",
hot: true,
host: "0.0.0.0",
port: 7777,
open: true,
compress: true,
proxy: {
"/api": {
target: "http://localhost:8888", //要代理的目标地址
pathRewrite: {
"^/api": "" //默认情况下,/api会被写入到URL中,需要删除
},
secure: false, //默认不接受转发https且无证书的服务器上
//http和https的区别:https://blog.csdn.net/weixin_43975162/article/details/126885163
changeOrigin: true, //是否更新代理后请求的header中host地址,防止抓包
historyApiFallback: {
rewrites: {
from: "index.html"
}
}
}
}
},
//resolve用于设置模块如何被解析
resolve: {
modules: ["node_modules"], //设置模块的目录
mainFiles: ["index"],//引入文件时只需要引入对应文件夹,会默认拿index加下面后缀
extensions: [".js", ".json", ".mjs", ".vue", ".ts", ".jsx", ".tsx"], //按照顺序去加后缀名去加载无后缀的引入
alias: {
"@": path.resolve(__dirname, "./src"),
"js": path.resolve(__dirname, "./src/js")
}
},
// loader是用于特定的模块类型进行转换;
module: {
rules: [{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-loader"],
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"],
},
// 一、理图片和字体等资源
// 1.webpack5之前:
// 1.1 file-loader/url-loader共有属性:
// [ext]: 处理文件的扩展名;
// [name]:处理文件的名称;
// [hash]:文件的内容,使用MD4的散列函数处理,生成的一个128位的hash值(32个十六进制);
// [contentHash]:在file-loader中和[hash]结果是一致的(在webpack的一些其他地方不一样,后面会讲到);
// [hash:<length>]:截图hash的长度,默认32个字符太长了;
// [path]:文件相对于webpack配置文件的路径;
// 1.2 url-loader的特有属性:
// [limit]: 1.url-loader对图片是否转base64的限制;小于20kb会转换base64之后可以和页面一起被请求’
{
test: /\.(jpe?g|png|gif|svg)$/, //图片
test: /\.(eot|ttf|woff2?)$/, //字体文件
loader: "file-loader" | "url-loader",
options: {
limit: 20 * 1024,
name: "img/[name].[hash:8]/[ext]"
}
},
// 2.webpack5之后:
// 资源模块类型(asset module type),来替代上面的这些loader
// 2.1.file-loader的name
// 方式一:修改output,添加assetModuleFilename属性;
// 方式二:在Rule中,添加一个generator属性,并且设置filename;
// 2.2.url-loader的limit效果
// 步骤一:将type修改为asset;
// 步骤二:添加一个parser属性,并且制定dataUrl的条件,添加maxSize属性;
{
test: /\.(jpe?g|png|gif|svg)$/,
type: "asset",
generator: {
filename: "img/[name]_[hash:6][ext]",
},
parser: {
dataUrlCondition: {
maxSize: 10 * 1024,
},
},
},
{
test: /\.(eot|ttf|woff2?)$/,
type: "asset/resource",
generator: {
filename: "font/[name]_[hash:6][ext]",
},
},
//Babel是一个工具链,主要用于旧浏览器或者环境中将ECMAScript 2015+代码转换为向后兼容版本的 JavaScript;
// 包括:语法转换、源代码转换等;(将箭头函数转换为普通函数,)
{
test: /\.js$/,
loader: "babel-loader",
options: {
plugins: [
"@babel/plugin-transform-block-scoping",
"@babel/plugin-transform-arrow-functions"
]
}
},
{
test: /\.vue$/,
loader: "vue-loader"
}
],
},
// plugins是用于打包优化、资源管理、环境变量注入等;
plugins: [
//开启热更新HMR
new webpack.HotModuleReplacementPlugin(),
//CleanWebpackPlugin:重新打包时,会自动删除dist文件夹并重新生成新的
new CleanWebpackPlugin(),
//HtmlWebpackPlugin:自定义HTML模板
//[template]:指定我们要使用的模块所在的路径
new HtmlWebpackPlugin({
template: "./public/index.html",
title: "哈哈哈哈"
}),
//DefinePlugin允许在编译时创建配置的全局常量
new DefinePlugin({
BASE_URL: "'./'",
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false
}),
//CopyWebpackPlugin在vue的打包过程中,public的目录下的文件会被复制到dist文件夹中
// from:设置从哪一个源中开始复制
// to:复制到的位置,可以省略,会默认复制到打包的目录下;
// globOptions:设置一些额外的选项,其中可以编写需要忽略的文件:
new CopyWebpackPlugin({
patterns: [{
from: "public",
to: "./",
globOptions: {
ignore: [
"**/index.html"
]
}
}]
}),
new VueLoaderPlugin()
],
};
四、基于webpack的脚手架vue-cli
1.安装和使用(内置了webpack相关的配置)
1.安装Vue CLI
npm install @vue/cli -g
2.升级Vue CLI:
npm update @vue/cli -g
3.通过Vue的命令来创建项目
vue create 项目的名称
五、Vite(比webpack快的构建工具)
1.介绍
2.解析文件
- vite可以直接支持css的处理
- vite可以直接支持css预处理器,比如less
- vite直接支持postcss的转换:
- vite对TypeScript是原生支持的,它会直接使用ESBuild来完成编译: 这些都不需要想webpack一样还有下载很多loader了,vite直接就支持;
- vite不可以直接解析.vue文件,需要插件支持:@vitejs/plugin-vue
3.vite和webpack区别是什么?为什么vite更快?
1.区别是什么?
webpack无论是开发(热加载)还是构建(打包代码),都是一个流程——生成依赖图,打包代码到内存memfs或者生成dist文件;
而vite有两套系统,开发时用第一个保证快,生产时用第二个保证稳;
-
一个开发服务器,它基于 原生 ES 模块 提供了 丰富的内建功能,如速度快到惊人的 模块热更新(HMR)。
-
一套构建指令,它使用 Rollup 打包你的代码,并且它是预配置的,可以输出用于生产环境的优化过的静态资源。
vite在生产环境时仍需要打包成es5,毕竟有些浏览器可能还是不支持esm
- vite在开发时快的原因:
-
第一,vite基于的浏览器必须支持原生es-module;所以vite不会在开发时将你的es6等高阶语法转化为es5;
-
第二,使用基于go语言的esbuild进行依赖的预构建;
-
第三,源码按需加载
六、基于vite的脚手架@vitejs/create-app
-
这是个vue脚手架,快速创建vue项目的;npm安装好之后,使用create-app命令来初始化项目;
-
和基于webpack的vuecli差不多;