vite官网:cn.vitejs.dev/guide/why.h…
学习链接:www.bilibili.com/video/BV1GN…
构建工具到底承担什么责任:
- 模块化开发及多种模块化;
- 处理代码兼容性,比如babel语法降级,语法转换等;
- 提高项目性能,在打包的过程中,帮我们压缩文件,代码分割等;
- 优化开发体验
1.它会帮助我们自动监听文件变化,调用相应的集成工具进行重新打包,然后在浏览器重新运行;
2.开发服务器,解决跨域问题;
构建工具让我们可以不用每次都关心代码在浏览器如何运行,我们只需要首次给构建工具提供一个配置文件即可。
那么vite相较于webpack的优势是什么呢?
当我们开始构建越来越大型的应用时,需要处理的 JavaScript 代码量也呈指数级增长。包含数千个模块的大型项目相当普遍。基于 JavaScript 开发的工具就会开始遇到性能瓶颈:通常需要很长时间(甚至是几分钟!)才能启动开发服务器,即使使用模块热替换(HMR),文件修改后的效果也需要几秒钟才能在浏览器中反映出来。如此循环往复,迟钝的反馈会极大地影响开发者的开发效率和幸福感。
如上面引自vite官网的话所述,当项目越来越大时,所要处理的js代码便会越来越多,便会造成需要很长时间才会将服务器成功启动。
原因:
webpack是支持多种模块化格式,就得抓取并构建整个应用,形成统一的格式(__webpack_require__),再提供服务。
vite是只支持ESM,就不存在上述过程了,Vite 只需要在浏览器请求源码时进行转换并按需提供源码就好了。
------------------------------------------12.15--------------------------------------------
vite启动项目
首先呢,新建一个文件目录,在里面新建以下文件
<!DOCTYPE html>
<html lang="en">
<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">
<title>Document</title>
</head>
<body>
<script src="./main.js" type="module"></script>
</body>
</html>
import { count } from './counter.js';
console.log(count);
export const count = 0;
然后在浏览器上打开index.html文件,会发现控制台成功的输出了count的值,接着我们初始一下项目,再下载一下lodash,并在counter.js里引入一下lodash
1.yarn init -y
初始化文件设置
2.yarn add lodash
安装lodash
3.导入lodash
import _ from 'loash';
export const count = 0;
这时你就会发现浏览器报错了,提示你要以ESM导入资源的格式引入,而在默认情况下,esmodule导入资源的时候,要么是绝对路径,要么是相对路径。
报错的原因是ESM不会帮我们去搜索node_modules,便导致lodash加载不出来。
那么,接下来就让我们安装vite来帮我们解决这个问题。
1.yarn add vite -D
安装vite
2.更改配置文件
{
"name": "vite-demo",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"dev": "vite"
},
"dependencies": {
"lodash": "^4.17.21"
}
}
3.yarn dev
启动项目
打开本地项目链接,就会发现lodash被成功加载了。
由此可以看出,vite和webpack一样是开箱即用的,不需要任何额外的配置就可以使用vite来帮助我们处理构建工作。
vite的预加载
vite是通过路径补全来解决上一小节lodash加载不了的问题的。
找寻依赖的过程是自当前目录依次向上查找的过程。
在找到包之后,会发现包会通过立即函数,commonjs等形式导出,当用commonjs规范导出时ESM便不认识了,那这时vite便提出了依赖预构建。
依赖预构建调用esbuild,将其他规范的代码转换为ESM,然后放在node_modules/.vite/deps,同时对ESM规范的各个模块进行统一集成。
各个模块统一集成便是指将多层模块依赖集成生成一个或多个模块,浏览器只需要请求几次就好,而不是遇到一个导入便请求一次。
第三方库源码
经过vite处理过的请求资源
------------------------------------------12.16--------------------------------------------
Vite语法提示及环境变量
关于语法提示方面:
- webstorm无需特殊处理,会自动将vite配置项的语法进行补全。
- 如是其他编辑器,则需要进行特殊处理。
特殊处理方式:
1.从vite里引入defineConfig
import { defineConfig } from 'vite'
export default defineConfig({
optimizeDeps: {
exclude: [] //不进行依赖预构建
}
})
2.@type 类型标记注释
/** @type {import('vite').UserConfig} */
const config = {
optimizeDeps: {
exclude: [] //不进行依赖预构建
}
}
3.官网推荐
/** @type {import('vite').UserConfig} */
export default {}
关于环境变量方面:
项目根目录下新建三个文件夹:
1.vite.base.config.js
import { defineConfig } from 'vite'
export default defineConfig({
})
2.vite.dev.config.js
import { defineConfig } from 'vite'
export default defineConfig({
})
3.vite.prod.config.js
import { defineConfig } from 'vite'
export default defineConfig({
})
接下来在vite.config.js文件里将上述文件抛出的配置引入
import { defineConfig } from 'vite';
import BaseViteConfig from'./vite.base.config';
import DevViteConfig from'./vite.dev.config';
import ProdViteConfig from'./vite.prod.config';
const EnvResolver = {
"serve": () => Object.assign({}, BaseViteConfig, DevViteConfig),
"build": () => Object.assign({}, BaseViteConfig, ProdViteConfig),
}
export default defineConfig(({command}) => {
return EnvResolver[command]();
})
command就是表示当前所处环境的字段,通过判断这个字段来导出不同的配置。
而command是build还是server是根据我们终端所敲入的命令决定的。
环境变量的配置:
环境变量:根据当前环境产生质的变化的变量,比如请求路径、参数及baseUrl等。可通过配置化环境变量,降低本地开发及线上频繁更改字段值而导致事故的风险。
vite是通过内置的第三方库dotenv解析对应的环境变量,并将其注入到process对象下。(但vite考虑到和其他配置的一些冲突问题,不会直接将变量注入到process对象下)
冲突问题:vite的配置文件可以配置root与envDir然后去读取环境变量的文件,如果process对象下的环境变量读取地址与envDir不一致,便会导致precess对象下的环境变量不会被用到。
所以vite便为我们提供了loadEnv方法来手动确认env文件,这时我们便可以在服务端对环境变量进行处理了
import { defineConfig, loadEnv } from 'vite';
import BaseViteConfig from'./vite.base.config';
import DevViteConfig from'./vite.dev.config';
import ProdViteConfig from'./vite.prod.config';
const EnvResolver = {
"serve": () => {
return Object.assign({}, BaseViteConfig, DevViteConfig)
},
"build": () => {
return Object.assign({}, BaseViteConfig, ProdViteConfig)
},
}
export default defineConfig(({command, mode}) => {
const env = loadEnv(mode, process.cwd(), "");
console.log(command, env, mode);
return EnvResolver[command]();
})
接下来让我们在与vite配置文件同级的目录下新建以下配置文件:
- .env文件,可配置全局变量。
- .env.development文件,可配置开发环境变量(默认vite将开发环境命名为development)。
- .env.production文件,可配置生产环境变量(默认vite将生产环境命名为production)。
test1 = 111
test2 = 222
test3 = 333
若我们不想用默认的环境名,我们也可以通过mode来自定义环境的名字
yarn dev --mode devlop
启动服务后,我们便会看到控制台将环境变量打印出来啦
- 开发环境
- 生产环境
其具体原理便是,当我们调用loadEnv时,他会做如下几件事:
- 直接找到.env文件并解析环境变量,放进一个对象里
- 将mode传进来的这个变量值进行拼接(.env.[mode]),并根据目录(process.cwd)取对应的文件并进行解析,放进一个对象里
- 最后对象进行合并,我们可以理解为
const baseEnvConfig = .env的配置;
const modeEnvConfig = .env.mode的配置;
const lastEnvConfig = {...baseEnvConfig, ...modeEnvConfig};
以上是在服务端用到环境变量,如果我们在客户端的话,vite会将对应的环境变量注入到import.meta.env里,
但是vite在这里做了一层拦截,防止我们将隐私的变量直接注入到import.meta.env里,所以只有已VITE开头的变量才可以成功注入到客户端。
若我们不想用VITE前缀,可以在对vite进行配置,更改envPrefix字段的值。
结下来然我们在counter.js文件里将环境变量引入并打印
import _ from 'lodash-es';
console.log(_, import.meta.env);
export const count = 0;
启动服务后,我们可以看到,环境变量并没有打印出来,这是因为我们没有将环境变量的前缀更改为VITE。
VITE_test1 = 111
- 未更改前缀
- 更改前缀
自定义前缀
对vite进行配置,并更改.env文件里的环境变量配置后,便会得到我们想要的环境变量啦
import { defineConfig } from 'vite'
export default defineConfig({
envPrefix: "ENV_"
})
ENV_test1 = 111
控制台打印:
------------------------------------------12.26--------------------------------------------
vite服务器搭建原理
实现简单的服务器
当执行yarn dev命令行时,都做了些什么事情,以下便对服务器的基础原理进行简单的认识。
首先新建一个文件夹vite-dev-server,对项目进行初始化,同时下载node框架koa。
mkdir vite-dev-derver
cd vite-dev-derver
yarn init -y
yarn add koa
接下来就是在当前目录下新建index.html文件.
<!DOCTYPE html>
<html lang="en">
<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">
<title>Document</title>
</head>
<body>
</body>
</html>
然后在当前目录下新建index.js文件,将koa导入并起一个后端服务,并将index.html文件内容返回给前端。
const Koa = require("koa");
const fs = require("fs");
const path = require("path")
const app = new Koa();
app.use(async (ctx) => {
console.log("ctx", ctx.request, ctx.response);
if (ctx.request.url === '/') {
const indexContent = await fs.promises.readFile(path.resolve(__dirname, "./index.html"), {encoding: "utf-8"});
console.log("indexContent", indexContent);
ctx.body = indexContent;
}
})
app.listen(5174, () => {
console.log("listen on 5174")
})
配置package.json文件,使得可以用通过yarn dev的方式将服务启动。
{
"scripts": {
"dev": "node index.js"
}
}
服务启动后,在浏览器访问5714端口,请求成功,就会发现页面成功渲染后端传过来的html了。
通过以上操作,我们的前后端流程就走通了。
那么vite如何让浏览器识别.vue文件呢
首先呢,我们需要在当前目录下新建main.js文件,App.vue文件,并在index.html内将main.js导入
import App from './App.vue';
console.log(App)
<body>
<script type="module" src="./main.js"></script>
</body>
这时重启服务,就会发现main.js资源请求失败,这个因为服务端没有对/main.js路径做处理。
那么,我们就需要在index.js文件内做出以下修改,来响应main.js的请求。现在是手动对每个路径做处理,等项目做大了之后,会通过中间件做处理的。
if (ctx.request.url === '/main.js') {
const mainContent = await fs.promises.readFile(path.resolve(__dirname, "./main.js"), {encoding: "utf-8"});
console.log("mainContent", mainContent);
ctx.body = mainContent;
ctx.response.set("Content-Type", "text/javascript")
}
重启服务,我们就又会发现App.vue资源请求报错了,其原因同上请求main.js报错,所以就又要更改index.js文件,配置相应路径的处理。
到这里,就引出了,浏览器是怎么认识.vue文件的原理了。
if (ctx.request.url === '/App.vue') {
const vueContent = await fs.promises.readFile(path.resolve(__dirname, "./App.vue"), {encoding: "utf-8"});
中间包含一系列操作
console.log("vueContent", vueContent);
ctx.body = vueContent;
ctx.response.set("Content-Type", "text/javascript")
}
中间包含的一系列操作:获取到.vue文件内容后,并将其中间vue文件内容替换成js文件内容。(AST语法分析:通过createElemtent将vue模版构建成原生dom);
最后通过Content-Type告诉浏览器,即使是.vue文件,也要当成js文件解析。
综上所述,vite 当我们敲下 yarn dev 命令后就做了以上操作,将项目跑了起来。
同时我们也可以看出来浏览器不会管文件后缀名是什么,它是通过后端返回的Content-Type来决定用什么方式解析的。
css处理
vite是天生支持对css文件的直接处理的。
接下来回到vite-demo目录下,新建index.css文件,并在main.js文件内引入。
html, body {
width: 100%;
height: 100%;
background-color: aquamarine;
}
import "./index.css"
启动服务,可以看到页面的背景颜色改变了,在此之前我们并没有对vite配置进行更改,可以看出vite是天生就支持对css文件进行处理的。
大体原理便是:
- vite读取main.js时,引用了css文件
- 直接去使用fs模块读取index.css的内容
- 穿件style标签,将index.css文件的内容复制到style标签内
- 将style标签嵌入到index.html的header标签中
- 最后将css文件内容转换为js脚本(方便热更新或css模块化),同时设置Content-Type为js,让浏览器以js脚本的形式解析css后缀的文件
css模块化
多人协同开发的场景下,可能会导致css类名重复,造成样式覆盖,最后导致样式错乱的情况。而cssmodule便是解决这个问题的。
在vite-demo文件夹下新建四个文件,分别是A.js,B.js,A.module.css,B.module.css,并在main.js中将A.js,b.js引入。
import ACss from "./A.module.css";
console.log("ACss", ACss);
const div = document.createElement("div");
document.body.appendChild(div);
div.className = ACss.footer;
import BCss from "./B.module.css";
console.log("BCss", BCss);
const div = document.createElement("div");
document.body.appendChild(div);
div.className=BCss.footer;
.footer {
width: 200px;
height: 200px;
background-color: beige;
}
.footer {
width: 100px;
height: 200px;
background-color: lightblue;
}
import './A'
import './B'
通过查看元素我们可以发现class的名字拼接上了一串哈希值,
通过控制台打印发现,导入的css变成了一个对象,其key及value值分别为我们原本的class类名与拼接哈希值后的class类名。
通过以上规律,我们将calss类名的赋值改为div.className=BCss.footer变量的方式,就会发现css样式被完全隔离开来了。这就是css的模块化
而css模块化的大致原理如下(基于node):
- 读取文件发现时已moudule.css命名,便会开启模块化(module是模块化的约定)
- 将所有类名进行一定规则的替换
- 创建映射对象(footer: "_footer_1w47u_1")
- 将替换后的内容塞到style标签内,然后将style嵌入到header标签内
- 将moudle.css内容替换为js脚本
- 将映射对象在脚本中进行倒出
------------------------------------------12.27--------------------------------------------
vite中的css配置
以上是默认的vite对css的处理,接下来通过手动更改vite的css配置,来进行其他额外的处理。
modules模块化
- localsConvention:更改映射对象里的key的展现形式(驼峰/中划线)。
export default defineConfig({
css: {
modules: {
localsConvention: 'camelCase'
}
}
})
配置生效后,就会发现,对象里多个驼峰式的类名及其映射。
此时,在js向dom赋值类名时,便可采用驼峰命名了,而不是中划线footer-content,符合代码编写时的规范。
div.className = ACss.footerContent;
- scopeBehaviour:配置当前模块时模块化还是全局化(有无哈希值)
export default defineConfig({
css: {
modules: {
scopeBehaviour: 'local'
}
}
})
local:模块化
global:全局化
- generateScopedName:生成类名规则(映射对象value的展示形式,可配置为函数或字符串)
export default defineConfig({
css: {
modules: {
generateScopedName: '[name]_[local]_[hash:5]'
}
}
})
配置生效后就会看到value的展现形式变成了本地文件名+本地类名+哈希值了
通过该网址https://github.com/webpack/loader-utils#interpolatename ,查询'[name][local][hash:5]'配置项及规则释义。
generateScopedName除了上述的方式外,还可配置成函数。
该函数会有三个入参:
函数reture的值便是映射对象的value值。
export default defineConfig({ css: {
modules: {
generateScopedName: (name) => {
return name
}
}
}
})
- hashPrefix:配置的字符串会参与到哈希值的生成中去
export default defineConfig({
css: {
modules: {
hashPrefix: 'demo',
}
}
})
- globalModulePaths:不想参与到css模块化的路径(比如通用的css样式或第三方库样式)
export default defineConfig({
css: {
modules: {
globalModulePaths: [],
}
}
})
preprocessorOptions预处理器配置
配置形式为key(预处理器的名less/sass等)+config(配置对象)
less官方文档:less.bootcss.com/usage/#less…,在这里可以知道config可以配置哪些参数。
- math:支持计算
export default defineConfig({
css: {
preprocessorOptions: {
less: {
math: 'always',
}
}
}
})
- globalVars:定义全局变量
export default defineConfig({
css: {
preprocessorOptions: {
less: {
globalVars: {
mainColor: 'red'
}
}
}
}
})
devSourcemap文件索引
当代码被压缩和编译后,如果程序出错,则不会提示正确的错误信息位置,设置了sourcemap后,会生成一个索引文件,然后找出报错信息的正确位置。
export default defineConfig({
css: {
devSourcemap: true
}
})
postcss
postcss的作用便是保证css在制定起来万无一失。
流程:css--->postcss(编译--->语法降级--->前缀补全)--->浏览器
我们知道css本身也是支持变量的,通过var()的方式,但是他的兼容性不太好,在低版本的浏览器中可能不会生效。
但是我们的预处理器(less/sass等)并不能处理这个问题,来进行语法降级。
这时postcss便发挥了作用,它会直接将var(变量名)替换为对应的值,来兼容不同的浏览器。
那么postcss做了哪些预处理器不能做的事情呢:
- 对未来css属性的语法降级
- 前缀补全
demo案例
新建postcss-demo文件夹,然后初始化项目
yarn init -y
安装依赖postcss,postcss-cli
yarn add postcss postcss-cli -D
当前目录下新建index.css文件
:root {
--globalColor: 'blue'
}
div {
background-color: var(--globalColor);
}
使用命定行,对css进行编辑,编译后的结果存到result.css文件内
(--o是指输出的意思,可以查看更多的命令在该地址:www.npmjs.com/package/pos…)
npx postcss index.css --o result.css
运行成功后,打开result.css文件我们可以发现,css被并没有被降级,这是因为我们没有书写配置文件
安装预设(编译/语法降级等插件)
yarn add postcss-preset-env -D
配置文件postcss.config.js
const postcssPresetEnv = require("postcss-preset-env");
module.exports = {
plugins: [postcssPresetEnv()]
}
接下来运行编译命令后,打开result.css文件就会发现css语法被成功降级了
vite的postcss配置
回到vite-demo目录下,安装依赖
yarn add postcss-preset-env -D
编辑vite.base.config.js文件
const postcssPresetEnv = require("postcss-preset-env");
export default defineConfig({
css: {
postcss: {
plugins: [postcssPresetEnv()]
}
}
})
更改A.mobule.less样式,使用css新语法clamp及user-select
.footer-content {
width: clamp(100px, 30%, 300px);
height: 200px / 2;
background-color: beige;
user-select: none;
}
服务启动成功后,可以看到语法被成功降级和补全了
也可通过配置postcss.config.js来实现vite的postcss配置相同的功能,但是postcss.config.js优先级大于vite.base.config.js.
以上便是vite对postcss的一个简单配置的两种方法。
------------------------------------------12.28--------------------------------------------