vite如何处理项目中的资源
web开发服务器
实现简易的开发服务器
- 初始化项目
## 新建文件夹
vite_dev_server## 初始化工程
npm init -y## 安装服务框架
npm i koa## 新建入口文件
index.js## 新增启动脚本
{"scripts": {"dev": "node index.js"}
}
const Koa = require("koa")const app = new Koa()// 接收到请求后触发use注册的回调函数
app.use((ctx) => {// ctx: 请求上下文; request: 请求信息; response:响应信息;console.log("ctx", ctx.request, ctx.response);
})// 启动服务
app.listen(5173, () => {console.log("vite dev serve liste on 5173");
})
- 新建index.html页面返回给浏览器
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body>hello! my is node sever.
</body></html>
const Koa = require("koa")
const fs = require("fs")
const path = require("path")const app = new Koa()// 接收到请求后触发use注册的回调函数
app.use(async (ctx) => {// ctx: 请求上下文; request: 请求信息; response:响应信息;// console.log("ctx", ctx.request, ctx.response);if (ctx.request.url === "/") {const indexContent = await fs.promises.readFile(path.resolve(__dirname, "./index.html")) // 读文件ctx.response.body = indexContent // 设置响应体ctx.response.set("Content-Type", "text/html") // 设置响应体格式, application/json text/html text/javascript}
})// 启动服务
app.listen(5173, () => {console.log("vite dev serve liste on 5173");
})
- 新建main.js, 在index.html中引入, 响应给浏览器
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body>hello! my is node sever.<script type="module" src="/main.js"></script>
</body></html>
console.log("main js hihi");
const Koa = require("koa")
const fs = require("fs")
const path = require("path")const app = new Koa()// 接收到请求后触发use注册的回调函数
app.use(async (ctx) => {// ctx: 请求上下文; request: 请求信息; response:响应信息;// console.log("ctx", ctx.request, ctx.response);if (ctx.request.url === "/") {const indexContent = await fs.promises.readFile(path.resolve(__dirname, "./index.html")) // 读文件ctx.response.body = indexContent // 设置响应体ctx.response.set("Content-Type", "text/html") // 设置响应体格式, application/json text/html text/javascript}if (ctx.request.url === "/main.js") {const mainContent = await fs.promises.readFile(path.resolve(__dirname, "./main.js")) // 读文件ctx.response.body = mainContent // 设置响应体ctx.response.set("Content-Type", "text/javascript") // 设置响应体格式, application/json text/html text/javascript}
})// 启动服务
app.listen(5173, () => {console.log("vite dev serve liste on 5173");
})
- 新建App.vue, 在main.js中引入,响应给浏览器
开发服务返回给浏览器的vue文件已经是编译后的文件了, 我们也要这样做
// 我这里请求失败了, 大概是这个输出有问题
console.log("app vue haha")
import "/App.vue"console.log("main js hihi");
const Koa = require("koa")
const fs = require("fs")
const path = require("path")const app = new Koa()// 接收到请求后触发use注册的回调函数
app.use(async (ctx) => {// ctx: 请求上下文; request: 请求信息; response:响应信息;console.log("ctx", ctx.request, ctx.response);if (ctx.request.url === "/") {const indexContent = await fs.promises.readFile(path.resolve(__dirname, "./index.html")) // 读文件ctx.response.body = indexContent // 设置响应体ctx.response.set("Content-Type", "text/html") // 设置响应体格式, application/json text/html text/javascript}if (ctx.request.url === "/main.js") {const mainContent = await fs.promises.readFile(path.resolve(__dirname, "./main.js")) // 读文件ctx.response.body = mainContent // 设置响应体ctx.response.set("Content-Type", "text/javascript") // 设置响应体格式, application/json text/html text/javascript}if (ctx.request.url === "/app.vue") {const appContent = await fs.promises.readFile(path.resolve(__dirname, "./App.vue")) // 读文件// /**// * 如果vue文件, 我们要做处理, 转译为JS返回给前端, 这个过程肯定是非常复杂的, 这里了解一下原理// * 1. 简单理解就是要进行字符串替换: appContent.toString().find("<template>") 如果匹配到了内容, 就要把全部内容替换为JS代码// * 2. 实际过程: 经过AST语法分析 ==> 得到所有元素 ==> 使用 Vue.createElement() 方法 ==> 把得到的元素重新构建成原生的DOM// * 3. 核心就是把vue文件转译为JS文件, 因为浏览器只认识JS文件// */ctx.response.body = appContent // 设置响应体ctx.response.set("Content-Type", "text/javascript") // 设置响应体格式, application/json text/html text/javascript}
})// 启动服务
app.listen(5173, () => {console.log("vite dev serve liste on 5173");
})
处理css
了解Vite怎么处理css
vite天生就支持对css文件的直接处理,
- 新建index.css
html,
body {width: 100%;height: 100%;background-color: aqua;
}
- 在main.js中引入css
import './index.css'
- 启动项目
- 下面是vite处理css的步骤
- vite在读取到main.js中引用到了index.css
- 直接去使用fs模块去读取index.css中文件内容
- 直接创建一个style标签,将index.css中文件内容直接copy进style标签里
- 将style标签插入到index.html的head中
- 将该css文件中的内容直接替换为js脚本(方便热更新或者css模块化), 同时设置Content-Type为js, 从而让浏览器以JS脚本的形式来执行该css后置的文件
如何避免样式冲突?
- 会什么会冲突:
- 一个组件最外层的元素类名一般取名: wrapper
- 一个组件最底层的元素雷明明我们一般取名: footer
- 你取了footer这个名字,别人因为没有看过你这个组件的源代码,也可能去取名footer这个类名
- 最终可能会导致样式被覆盖(因为类名重复),这就是我们在协同开发的时候很容易出现的问题
- 模拟样式冲突:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><div id="div"></div><script src="./main.js" type="module"></script>
</body></html>
#div {width: 200px;height: 200px;background-color: red;
}
#div {width: 200px;height: 200px;background-color: green;
}
import './a.css'
import './b.css'
- 如何解决协作开发中的样式冲突问题:
- cssmodule就是来解决这个问题的
- module.css(module是一种约定,表示需要开启css模块化)
- 他会将你的所有类名进行一定规则的替换(将div替换成div_10b22_1)
- 同时创建一个映射对象{div:"div_10b22_1"}
- 将替换过后的内容塞进style标签里然后放入到head标签中(能够读到index.html的文件内容)
- a.module.css内容进行全部抹除,替换成JS脚本
- 将创建的映射对象在脚本中进行默认导出
#div {width: 200px;height: 200px;background-color: red;
}
#div {width: 200px;height: 200px;background-color: green;
}
import './a.module.css'
import './b.module.css'
vite处理less或css
- 当vite识别到.less结尾的文件后, 先把less语法转译为css语法
- 按照处理css的步骤继续处理
vite.config.js中的css配置(module篇)
在vite.config.js中我们通过css属性去控制整个vite对于css的处理行为
- localConvention: 修改生成的配置对象的key的展示形式 (驼峰还是中划线形式)
- camelCase: 驼峰命名
- camelCaseOnly: 只保留驼峰命名
- dashes: 中划线命令
- dashesOnly: 只保留中划线命名(理论上是默认值)(实测结果如下, 不知道咋回事)
- 默认值: 只保留中划线命名
#div {width: 200px;height: 200px;background-color: red;
}#div-conteainer {width: 100px;
}
import css from './a.module.css'
console.log(css);
import { defineConfig } from 'vite'export default defineConfig({css: {modules: { // 对css模块化的默认行为进行覆盖localsConvention: "dashesOnly"}}
})
- scopeBehaviour: 配置当前的模块化行为是模块化 还是全局化
tocal: 开启模块化css, 拼接哈希值 (默认值)
global: 全局化css (关闭模块化css)
import { defineConfig } from 'vite'export default defineConfig({css: {modules: { // 对css模块化的默认行为进行覆盖localsConvention: "camelCaseOnly"}}
})
- generateScopedName: 生成的类名的规则
- 可以配置为函数,也可以配置成字符串规则: https://github.com/webpack/loader-utils#interpolatename
import { defineConfig } from 'vite'export default defineConfig({css: {modules: { // 对css模块化的默认行为进行覆盖,最终丢给postcss覆盖其modules配置generateScopedName: "[name]_[local]_[hash:5]",// generateScopedName: (name, filename, css) => {// // name: 代表此刻css文件中的类型// // filename: 当前css文件的绝对路径// // css: 当前的样式// return `${name}_${Math.random().toString(36).substr(3, 8)}`// },}}
})
- hashPrefix: 生成的hash会根据你的类名+一些其他的字符串(文件名+ 他内部随机生成一个字符串)去进行生成,如果你想要你生成hash更加的独特一点,你可以配置hashPrefix
import { defineConfig } from 'vite'export default defineConfig({css: {modules: {hashPrefix: "wang"}}
})
- rglobalModulePaths: 你不想参与到css模块化的路径
import { defineConfig } from 'vite'export default defineConfig({css: {modules: {globalModulePaths: ["./b.module.css"]}}
})
vite配置文件中css配置(preprocessorOptions篇)
主要是用来配置css预处理的一些全局参数
# 假设没有使用构建工具,我们又想去编译less文件的话yarn add less; # 内含lessc的编译器# 你只要安装了node,你就可以使用node index.js
# 你只要安装了less 你就可以使用lessc去编译less文件
- math: 处理表达式的范围
import { defineConfig } from 'vite'export default defineConfig({css: {preprocessorOptions: { // 格式key + config, key代表预处理的名less: { // 整个配置对象都会最终给到less的执行参数(全局参数)中去math: "always", // 默认只会处理()内的表达式, 配置后处理所有表达式},sass: {}}}
})
#div {padding: 20px / 2; // 全部处理margin: (20px / 2); // 默认处理
}
#配置后的执行效果等同于
npm lessc --math="always" index.module.less
- globalVars: 定义全局变量
很多工程都是这样使用全局变量
@mainColor: red
@import url(./variables.less);#div {padding: 20px / 2; // 全部处理margin: (20px / 2); // 默认处理background-color: @mainColor;
}
也可以这样定义全局变量, 效果一样
import { defineConfig } from 'vite'export default defineConfig({css: {preprocessorOptions: { // 格式key + config, key代表预处理的名less: { // 整个配置对象都会最终给到less的执行参数(全局参数)中去globalVars: { // 全局变量mainColor: "red"}},sass: {}}}
})
devSourcemap 文件索引
- 假设我们的代码被压缩或者被编译过了,这个时候假设程序出错,他将不会产生正确的错误位置信息
- 如果设置了sourceMap,他就会有一个索引文件 map, 提示我们正确的代码位置信息
import { defineConfig } from 'vite'export default defineConfig({css: {devSourcemap: false // 默认false, 开启true}
})
- 未开启的效果, 只能定位到style标签, 因为编译后就是把sytle标签插入到HTML中
- 开启后的效果, 可以定位到正确的源码文件
postcss
vite天生对postcss有非常良好的支持
- postcss 他的工作就是保证css执行起来是万无一失的
- 我们写的css代码(怎么爽怎么来)--> 交给postcss处理 --->调用less sass等预处理器将扩展语法编译为css语法 ->再将高级css语法进行降级--> 在进行前缀补全 --> 浏览器执行
- 我们写的is代码(怎么爽怎么来)-->babel -->将ts语法转换js语法 -->做一次语法降级 -->浏览器客户端去执行
- css的新提案 (未来css将支持css变量)
:root {--globalColor: lightblue;
}#div {width: 200px;height: 200px;background-color: var(--globalColor);
}
演示下单独使用postcss
- 初始化工程
npm init -y
- 安装依赖
npm install postcss-cli postcss -D
- 新建index.css
:root {--globalColor: red;
}div {background-color: var(--globalColor);
}
- 编译css文件
npx postcss index.css -o result.css
# 得到的编译结果, 我们没做任何配置, 原样输出:root {--globalColor: red;
}div {background-color: var(--globalColor);
}
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmNzcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSIsImZpbGUiOiJyZXN1bHQuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiOnJvb3Qge1xyXG4gIC0tZ2xvYmFsQ29sb3I6IHJlZDtcclxufVxyXG5cclxuZGl2IHtcclxuICBiYWNrZ3JvdW5kLWNvbG9yOiB2YXIoLS1nbG9iYWxDb2xvcik7XHJcbn0iXX0= */
- 安装预设的配置插件
npm install postcss-preset-env -D
- 添加配置文件
const postcssPresetEnv = require("postcss-preset-env")//预设环境里面是会包含很多的插件
// 语法降级-->postcss-low-level
// 编译插件postcss-compiler
// ... 省的自己一个一个安装了module.export = {plugins: [postcssPresetEnv(/* pluginOptions */)]
}
- 重新编译 (做了语法兼容处理)
:root {--globalColor: red;
}div {background-color: red;background-color: var(--globalColor);
}
- 为什么把postcss称为后处理器?
- 业内把less/sass称为前处理器, 作用是先一步把扩展的css语法转译为原生css语法
- 把postcss称为后处理器, 作用是后一步把原生css语法中的高级语法做降级处理
- 实际上postcss本身就可以集成处理less/sass法语的插件
- 只是随着生态的发展, 在脚手架中集成less/sass, 再把原生css交给postcss处理, 已经形成共识, 所以postcss处理less/sass语法的插件也就没必要维护了
再vite中配置postcss
- 安装预设的配置插件
npm install postcss-preset-env -D
- 配置
import { defineConfig } from 'vite'
const postcssPresetEnv = require("postcss-preset-env") // 所以的ES规范最终也会编译为commonJS规范. 因为这里是node环境export default defineConfig({css: {postcss: {plugins: [postcssPresetEnv()]}}
})
- 代码
:root {--globalColor: lightblue;
}#div {width: 200px;height: 200px;background-color: var(--globalColor);/* 新语法: 盒子宽度设置为父元素宽度的30%, 但最小不小于100px, 最大不大于200px, *//* postcss会帮我们语法降级, vite内部会维护一个主流浏览器的属性支持表, 根据这个表决定是否降级 */width: clamp(100px, 30%, 200px);user-select: none;
}#div-conteainer {width: 100px;
}
- vite也可以识别 postcss.config.js 配置文件, 优先级低于配置属性
处理静态资源
什么是静态资源 ?
- 对于服务端, 除了动态API以外,其它资源都被视作静态资源
- vite对静态资源基本上是开箱即用的
vite怎么加载静态资源
{"name": "张三","age": 18
}
// 学习vite怎么加载静态资源
// import imgUrl from "./assets/images/DE.jpg?url"
import imgUrl from "./assets/images/DE.jpg?raw"
import json from "./a.json"const img = document.createElement("img")
img.src = imgUrl
document.body.append(img)// 在vite中, json文件导入后得到对象, 其他工具会的得到JSON字符串
console.log("json=", json);// 在url模式下(默认), 拿到图片的绝对路径 /assets/images/DE.jpg
// 在raw模式下, 拿到图片文件的 Buffer (二进制字符串) (也就是原始文件)
console.log("imgUrl=", imgUrl);
import './imageLoader.js'
路径别名的配置
- 怎么配置
import { defineConfig } from 'vite'
import path from 'path'export default defineConfig({resolve: {alias: {"@": path.resolve(__dirname, ""),"@assets": path.resolve(__dirname, "./assets")}}
})
import imgUrl from "@assets/images/DE.jpg?url"
- 实现原理: 服务端会进行字符串替换, 使用到@符号的地方会被替换为绝对路径
vite对svg资源的处理
- 概念
- svg: scalable vector graphics 可伸缩矢量图形(新的图片格式)
- 优势
- 优点: svg是不会失真, 尺寸小
- 缺点: 没法很好的去表示层次丰富的图片信息
- 我们在前端领域里更多的是用svg 去做图标
- 使用方式
import svgIcon from './assets/images/AAVE.svg'
import svgRwa from './assets/images/AAVE.svg?raw'// 1.第一种使用svg的方式
// const img = document.createElement("img")
// img.src = svgIcon
// document.body.appendChild(img)// 2.第二种使用方式
document.body.innerHTML = svgRwa
const svgEl = document.getElementsByTagName("svg")[0]
svgEl.onmouseenter = function () {this.style.fill = "red"
}