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> <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"
}
