vite学习笔记
目录
Vite 通过在一开始将应用中的模块区分为 依赖 和 源码 两类,改进了开发服务器启动时间。
-
依赖 大多为在开发时不会变动的纯 JavaScript。一些较大的依赖(例如有上百个模块的组件库)处理的代价也很高。依赖也通常会存在多种模块化格式(例如 ESM 或者 CommonJS)。
Vite 将会使用 esbuild 预构建依赖。esbuild 使用 Go 编写,并且比以 JavaScript 编写的打包器预构建依赖快 10-100 倍。
-
源码 通常包含一些并非直接是 JavaScript 的文件,需要转换(例如 JSX,CSS 或者 Vue/Svelte 组件),时常会被编辑。同时,并不是所有的源码都需要同时被加载(例如基于路由拆分的代码模块)。
Vite 以 原生 ESM 方式提供源码。这实际上是让浏览器接管了打包程序的部分工作:Vite 只需要在浏览器请求源码时进行转换并按需提供源码。根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理。
vite做了什么
模块按需加载(webpack首次加载需要从入口文件开始根据依赖图加载全部js,耗时长;vite基于浏览器,首次加载时只按需加载需要显示在当前页面的模块,)
依赖预构建(
vite
借助esbuild
进行了预构建,对项目依赖进行扫描,将得到的依赖结果缓存起来,并存放在node_module/.vite/deps
目录下。(一个文件))热更新
HMR
(热更新指的是自动对页面上更改的模块进行替换,以达到刷新页面数据的效果,这个效果甚至是无感的。)**ts解析
css解析
代码压缩
vite配置文件!(重要)
vite.config.js
export default{ optimizeDeps:{ exclude:['']//项目中不需要依赖与构建的依赖 } }
一、语法提示
一、
import {defineConfig} from 'vite' export default defineConfig({ optimizeDeps:{ exclude:['']//项目中不需要依赖与构建的依赖 } })
二、
/** @type import('vite').defineConfig */ const config = { optimizeDeps:{ exclude:['']//项目中不需要依赖与构建的依赖 } } export default config
二、关于环境的处理(生产/开发环境)
vite.config.js()
这里使用策略模式 分配开发环境和生产环境的不同配置
Object.assign(target, …sources) 对象合并,将源对象中的属性复制到目标对象中,他将返回目标对象target。当对象中只有一级属性,此方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝(这一点和**展开运算符、**JSON.parse(JSON.stringify(obj))一样)
import {defineConfig} from 'vite' import {viteDevConfig} from 'vite.dev.config.js' import {viteProdConfig} from 'vite.pro.config.js' import {viteBaseConfig} from 'vite.base.config.js' **//策略模式** const envResolver = { "build": ()=>Object.assign({},viteBaseConfig,viteProdConfig),//生产环境的依赖 "serve": ()=>Object.assign({},viteBaseConfig,viteDevConfig),//开发环境的依赖 } export default defineConfig((command:"build"|"serve")=>{ return envResolver[command] })
vite.dev.config.js
import {defineConfig} from 'vite' export default devConfig({ optimizeDeps:{ ... } })
三、关于环境变量的处理
根据当前代码环境(开发/测试/生产…)变化,产生变化的变量即环境变量
vite中的环境变量处理:dotenv,这个内置的工具库会在项目运行时在项目目录中寻找.env文件并自动读取文件中的对应环境变量
.env.development
BASE_URL = 'https://abc'
.env.production
BASE_URL = 'https://abc'
但不会并将其注入到process对象下(node关于当前进程的一个对象(node全局),vite就是在node环境下运行的);
原因:vite考虑到和其它配置的一些冲突问题,涉及到config.js中的一些配置项(为了防止冲突,环境变量不会直接放入,而是通过loadEnv手动确认导入,才进行放置。)
-root
-envDir(配置环境变量的文件地址)
(vite考虑到congig配置中有可能更改环境变量文件地址,因此在这之前读取process就会是无意义的)
vite提供的相关措施:调用vite的loadEnv来手动确认env文件(三个参数:mode,env目录,文件名)
export default defineConfig((command:"build"|"serve",mode)=>{ const envProcess = loadEnv(mode,process.cwd(),"") // process.cwd():node运行工作目录(顶层) // **loadEnv**来手动确认env文件后,会向envProcess 中注入环境变量,默认.env文件 return envResolver[command] })
mode:例如
npm run dev --mode development
,则会将mode
设置为development
,也就是说,mode会和后面的env文件名拼接,再根据目录去寻找对应环境下的环境变量文件!!
然后将文件中的变量添加到envProcess 中(会将公共.env和另一个环境.env组合成一个新的)。
.env:所有环境都需要用到的环境变量
.env.development:开发环境(默认情况下)
.env.production:生产环境上面讲的是服务端,如果是客户端,vite会寻找.env文件直接将对应的环境变量注入到**
import.meta.env
中,但是vite做了一个拦截,防止我们将一些隐私性的变量直接放到import.meta.env
** 中,如果环境变量不是以VITE_
开头的,则不会注入到**import.meta.env
,**客户端获取不到,所以每个环境下的环境变量如果需要再客户端使用,需要加VITE_
前缀,如果想要更改这个VITE_前缀,可以配置envPrefix
import {defineConfig} from 'vite' export default devConfig({ optimizeDeps:{ ... }, envPrefix:"ENV" //客户端校验的环境变量名的默认前缀 })
四、vite中对css以及css模块化的处理、css配置
- vite处理css文件
- vite读取到某个模块引用了css文件
- 用fs模块去读取css文件中的内容
- 创建一个style标签,将文件中的内容放进标签中
- 将style标签的直接插入到index.js的head中
- 将该css文件直接替换为js脚本(方便热更新或css模块化),同时设置该文件Content-type为js,让浏览器以js脚本的形式来执行css文件
- 模块化(.module.css,module.sass)
- 是基于node的
- 读取文件,(.module)
- 将文件中的所有类名根据一定规则进行替换
- 同时创建一个映射对象{footer:_footer_d12_sda}
- 将替换后的内容放进style标签中在放到indexjs的head中
- 默认导出映射对象
- 将该css文件直接替换为js脚本(方便热更新或css模块化),同时设置该文件Content-type为js,让浏览器以js脚本的形式来执行css文件
vite配置文件的css配置
1、modules模块化相关
通过配置css属性对css的行为进行相关配置
- localsConvention——修改生成的配置对象的key的展示形式
(“camelCase”:驼峰(驼峰和短横线都会显示) ;“camelCaseOnly”:只显示驼峰;“dashes”:短横线;“dashesOnly”)
- scopeBehavior——配置当前模块化行为是模块化还是全局化
(“local”-有哈希-模块化:可以保证产生不同的哈希值来保证样式不被覆盖;“global”-取消模块化(一般不用))
- generateScopedName——生成的类名的规则/展示格式,也可以配置为函数
("[name][locat]**[hash:5]“)
- hashPrefix——生成的哈希前缀
- globalModulePaths——不想参与到css模块化的文件路径
vite.config.js
import {defineConfig} from 'vite' export default devConfig({ optimizeDeps:{ ... }, envPrefix:"ENV" //客户端校验的环境变量名的默认前缀, css:{ modules:{//对css模块化的默认行为进行覆盖 localsConvention:"camelCase", scopeBehavior:"local", generateScopedName:"[name]_[locat]*_*[hash:5]", hashPrefix:"" } } })
2、preprocessorOptions 预处理器相关
主要用来配置css预处理器的一些全局参数
vite.config.js
import {defineConfig} from 'vite' export default devConfig({ optimizeDeps:{ ... }, envPrefix:"ENV" //客户端校验的环境变量名的默认前缀, css:{ modules:{//对css模块化的默认行为进行覆盖 ... }, preprocessorOptions:{ //key+config less:{ math:"always", globalVars:{//全局变量 mainColor:"red", } }, sass:{}, }, devSorcemap:true //代码编译打包后出错,会定位到源文件出错的位置,默认为false } })
3、postcss
postcss的相关配置,postcss—相当于less和sass的后处理器,解决它们解决不了的问题
插件:postcss-preset-env: 支持css变量和一些未来css语法 自动补全(—webkit)
如果没有在配置文件中写,会自动找到项目目录下的postcss.config.js文件
vite.config.js
import {defineConfig} from 'vite' import postcssPresetEnv from 'postcss-preset-env' export default devConfig({ optimizeDeps:{ ... }, envPrefix:"ENV" //客户端校验的环境变量名的默认前缀, css:{ modules:{//对css模块化的默认行为进行覆盖 ... }, preprocessorOptions:{ //key+config ... }, devSorcemap:true,//代码编译打包后出错,会定位到源文件出错的位置,默认为false postcss:{ plugins:[**postcssPresetEnv()**] } } })
五、vite对静态资源的处理及别名配置
vite加载静态资源
json,图片,svg,视频等都称为静态资源,vite对这些静态资源都是开箱即用的
别名配置
resolve下配置alias
import {defineConfig} from 'vite' import postcssPresetEnv from 'postcss-preset-env' import path from 'path' export default devConfig({ resolve:{ //别名配置 alias:{ "@":path.resolve(__dirname,"./src"), "@assets":path.resolve(__dirname,"./src/assets") }, }, optimizeDeps:{ ... }, envPrefix:"ENV" //客户端校验的环境变量名的默认前缀, })
原理:node服务器通过导入vite的配置对象,config.resolve.alias,得到别名和路径的键值对,当读取对象时,如果遇到**@时,会对其做字符串替换**,将切割后的路径替换进去
vite配置文件中对静态资源在生产环境的一些配置
在build配置项
- rollupOptions- 配置rollup的一些构建策略
- assetsInlineLimit - 如果图片小于4kb,则转为base64图片,大于4kb,转为静态资源文件
- outDir- 打包输出的文件夹名
- assetsDir- 打包输出的静态资源的文件夹名
- emptyOutDir- 默认是true,打包时清楚上一次的打包输出目录
打包后的静态资源有hash——浏览器会缓存相同文件名的文件,为避免每次打包出来的东西名字一致,所以vite默认打包后的资源文件名会根据一定规则加上hash(根据文件内容去生成hash) 利用这个hash算法,可以更好的去控制浏览器的 缓存机制
vite.config.js
import {defineConfig} from 'vite' import postcssPresetEnv from 'postcss-preset-env' import path from 'path' export default devConfig({ resolve:{ //别名配置 alias:{ "@":path.resolve(__dirname,"./src"), "@assets":path.resolve(__dirname,"./src/assets") }, }, optimizeDeps:{ ... }, envPrefix:...//客户端校验的环境变量名的默认前缀, build:{ rollupOptions:{//配置rollup的一些构建策略(vite在打包时会将一些事情交给rollup处理) output:{ //hash是由文件名和文件内容组合计算得来的结果 assetFileNames:"[hash].[name]" } }, assetsInlineLimit:4096 ,//如果图片小于4kb,则转为base64图片,大于4kb,转为静态资源文件 outDir:"Mydist", //打包输出的文件夹名 assetsDir:"static", // 打包输出的静态资源的文件夹名 emptyOutDir:true, //默认是true,打包时清楚上一次的打包输出目录 } })
六、 vite插件
插件是什么?
vite会在不同的生命周期去调用不同的插件,以达到不同的目的
vite从开始执行到结束就是其生命周期
插件 API | Vite 官方中文文档 (vitejs.cn)
中间件/插件作用?——在不同的生命周期去调用不同的插件/中间件,以达到不同的目的
插件使用
vite.config.js
import vitePlugin from 'vite-plugin-feature' import rollupPlugin from 'rollup-plugin-feature' import {defineConfig} from 'vite' export default defineConfig({ plugins: [vitePlugin(), rollupPlugin()] })
vite插件钩子
**
config
配置之前**
configResolved
整个配置文件解析流程完毕后调用**
configureServer
服务器相关**
configurePreviewServer
打包后生产环境预览相关**
transformIndexHtml
转换 index.HTML相关**
handleHotUpdate
热更新(自定义热更新行为)universal hooks (vite和rollup通用的钩子)
options
buildStart
…
插件顺序
一个 Vite 插件可以额外指定一个
enforce
属性(类似于 webpack 加载器)来调整它的应用顺序。enforce
的值可以是pre
或post
。解析后的插件将按照以下顺序排列:- Alias
- 带有
enforce: 'pre'
的用户插件 - Vite 核心插件
- 没有 enforce 值的用户插件
- Vite 构建用的插件
- 带有
enforce: 'post'
的用户插件 - Vite 后置构建插件(最小化,manifest,报告)
常用插件
- vite-aliases
帮助我们自动生成别名:检测你当前目录下包括src在内的所有文件夹,并帮助我们去生成别名
- vite-plugin-html
转换 HTML 字符串
- vite-plugin-mock
做mock数据:模拟数据
mockjs,vite-plugin-mock的依赖项就是mockjs
mock github文档:Getting Started · nuysoft/Mock Wiki (github.com)
vite.config.js
import vitePlugin from 'vite-plugin-feature' import rollupPlugin from 'rollup-plugin-feature' import {defineConfig} from 'vite' import {viteMockServe} from 'vite-plugin-mock' // export default defineConfig({ plugins: [vitePlugin(), rollupPlugin(),viteMockServe()] //viteMockServe会默认找到项目根目录中的mock文件 })
vite-plugin-mock插件的viteMockServe会默认找到项目根目录中的mock文件
项目中就可以直接使用mock文件中配置的api地址并获取其中配置的返回数据
-
mock数据使用-mock
Mock.mock( { // 属性 list 的值是一个数组,随机生成 1 到 10 个元素 "userList|1-10": [ { // 随机生成1-10个★ "string|1-10": "★", // 随机生成1-100之间的任意整数 "number|1-100": 1, // 生成一个浮点数,整数部分大于等于 1、小于等于 100,小数部分保留 1 到 10 位。 "floatNumber|1-100.1-10": 1, // 随机生成一个布尔值,值为 true 的概率是 1/2,值为 false 的概率同样是 1/2。 "boolean|1": true, // 随机生成一个布尔值,值为 false 的概率是 2 / (2 + 5),值为 true 的概率是 5 / (2 + 5)。 'bool|2-5': false, // 从属性值 object 中随机选取 2-4 个属性 "object|2-4": { "310000": "上海市", "320000": "江苏省", "330000": "浙江省", "340000": "安徽省" }, // 通过重复属性值 array 生成一个新数组,重复次数为 2 "array|2": [ "AMD", "CMD", "UMD" ], // 执行函数 function,取其返回值作为最终的属性值,函数的上下文为属性 'name' 所在的对象。 'foo': '哇哈哈哈哈', 'name': function () { return this.foo }, // 根据正则表达式 regexp 反向生成可以匹配它的字符串。用于生成自定义格式的字符串。 'regexp': /\d{5,10}/, "id|+1": 1, "id1": Random.id(), "string": Random.string(5), // "jPXEu" "string2": '@string(5)', // "jPXEu" // 生成随机邮箱地址 可以指定域名,例如 163.com "email": Random.email('163.com'), // "l.fvilfpz@163.com" "email2": '@email()', // "l.fvilfpz@163.com" // 返回一个随机的布尔值。 "boolean": Random.boolean(), // true "boolean2": '@boolean()', // true // 生成 60-100 随机整数 "point": Random.integer(60, 100), // 69 "point2": '@integer(60, 100)', // 98 // // 生成一个浮点数,整数部分大于等于 1、小于等于 100,小数部分保留 3 到 5 位。 "floatNumber": Random.float(1, 100, 3, 5), // 60.695 "floatNumber2": '@float(1, 100, 3, 5)', // 19.29368 // 随机日期 "date": Random.datetime('yyyy-MM-dd'), // "2017-05-01" "date2": "@datetime()", // "1973-06-12 13:05:18" // 随机时间 "time": Random.time(), // "21:33:01" "time2": "@time()", // "21:33:01" // 当前日期 "now": Random.now('year'), // "2023-01-01 00:00:00" "now2": "@now('year')", // "2023-01-01 00:00:00" // 随机生成图片 Random.image( size, background, foreground, format, text ) "img": Random.image('200x100', '#16d46b', '#fff', 'png', 'Hello'), // "http://dummyimage.com/200x100/16d46b/fff.png&text=Hello" // 随机生成颜色,格式为 '#RRGGBB'。 "color": Random.color(), // "#94f279" "color2": '@color()', // "#94f279" // 随机生成颜色,格式为 'rgb(r, g, b, a)'。 "rgbaColor": Random.rgba(), // "rgba(242, 121, 183, 0.22)" // 随机生成一段文本 文本中句子的个数为 2 到 5。默认值为 3 到 7 "paragraph": Random.paragraph(2, 5), // "Ymkp nvyryy vieq hlqdb pplbbikbd mtqiq uue jdufhkxy wpybjqi djico jxqkwvw kbmsscpfw owtgsqwn." "paragraph2": '@paragraph(2, 5)', // "Ymkp nvyryy vieq hlqdb pplbbikbd mtqiq uue jdufhkxy wpybjqi djico jxqkwvw kbmsscpfw owtgsqwn." // 随机生成一段中文文本 参数同 Random.paragraph( min?, max? ) "cparagraph": Random.cparagraph(), // "重工边政应信江半实金改北反调程五八。张资圆向规成新家天交对传许。军较军七养多认维市般况验式华行证。" "cparagraph2": '@cparagraph(2, 5)', // "重工边政应信江半实金改北反调程五八。张资圆向规成新家天交对传许。军较军七养多认维市般况验式华行证。" // 随机生成一个句子,第一个单词的首字母大写。 句子中单词的个数为 2 到 5 。默认值为 12 到 18 "sentence": Random.sentence(2, 5), // "Yyfvs genrdeiyf." "sentence2": '@sentence(2, 5)', // "Yyfvs genrdeiyf." // 随机生成一段中文文本,参数同 Random.sentence( min?, max? ) "csentence": Random.csentence(2, 5), // "积现。" "csentence2": '@csentence(2, 5)', // "积现。" // 随机生成一个单词,单词中字符的个数为 2 到 5 个。默认值为 3 到 10 "word": Random.word(2, 5), // "nlgcl" "word2": '@word(2, 5)', // "nlgcl" // 随机生成一个汉字,汉字中字符串的长度为 2 到 5 个。默认值为 1 "cword": Random.cword(2, 5), // "系即感" "cword2": '@cword(2, 5)', // "系即感" // 随机生成一句标题,其中每个单词的首字母大写。单词中字符的个数为 2 到 5。默认值为 3 到 7 "title": Random.title(2, 5), // "Vmpx Rizds Smguoqki" "title2": '@title(2, 5)', // "Vmpx Rizds Smguoqki" // 随机生成一句中文标题,参数同 Random.title( min?, max? ) "ctitle": Random.ctitle(2, 5), // "其感期" "ctitle2": '@ctitle(2, 5)', // "其感期" // 随机生成一个常见的英文名 "firstName": Random.first(), // "Michelle" "firstName2": '@first()', // "Jose" // 随机生成一个常见的英文姓。 "lastName": Random.last(), // "Taylor" "lastName2": '@last()', // "Clark" // 随机生成一个常见的英文姓名。括号里的布尔值,指示是否生成中间名(可选)。 "name": Random.name(true), // "Donald Eric Jackson" "name2": '@name(true)', // "Donald Eric Jackson" // 随机生成一个常见的中文姓 "cfirstName": Random.cfirst(), // "任" "cfirstName2": '@cfirst()', // "郭" // 随机生成一个常见的中文名。 "clastName": Random.clast(), // "芳" "clastName2": '@clast()', // "芳" // 随机生成一个常见的中文姓名。 "cname": Random.cname(), // "程强" "cname2": '@cname()', // "程强" // 随机生成一个URL。可以指定url协议,域名和端口号。例如'http' nuysoft.com。 'url': Random.url('http', 'nuysoft.com'), // "http://nuysoft.com/ysq" 'url2': '@url()', // "http://nuysoft.com/ysq" // 随机生成一个 IP 地址 'IP': Random.ip(), // "112.127.151.37" 'IP2': '@ip()', // "233.144.17.219" // 随机生成一个(中国)大区。 "region": Random.region(), // "华北" "region2": '@region()', // "华北" // 随机生成一个(中国)省(或直辖市、自治区、特别行政区)。 "province": Random.province(), // "澳门特别行政区" "province2": '@province()', // "澳门特别行政区" // 随机生成一个(中国)市。括号里的布尔值,指是否生成所属的省(可选) "city": Random.city(true), // "广东省 肇庆市" "city2": '@city()', // "广东省 肇庆市" // 随机生成一个(中国)县。括号里的布尔值,指是否生成所属的省、市(可选) "county": Random.county(true), // "江苏省 常州市 其它区" "county2": '@county()', // "江苏省 常州市 其它区" // 随机生成一个邮政编码(六位数字)。 "zip": Random.zip(), // "806124" "zip2": '@zip()', // "806124" // 把字符串的第一个字母转换为大写。 "capitalize": Random.capitalize('hello'), // "Hello" "capitalize2": '@capitalize("hello")', // "Hello" // 把字符串转换为大写。 "upper": Random.upper('hello'), // "HELLO" "upper2": '@upper("hello")', // "HELLO" // 把字符串转换为小写。 "lower": Random.lower('HELLO'), // "hello" "lower2": '@lower("HELLO")', // "hello" // 从数组中随机选取一个元素并返回。 "pick": Random.pick(['a', 'e', 'i', 'o', 'u']), // "e" "pick2": '@pick(["a", "e", "i", "o", "u"])', // "e" // 打乱数组中元素的顺序,并返回。 "shuffle": Random.shuffle(['a', 'e', 'i', 'o', 'u']), // ['o', 'a', 'i', 'e', 'u'] "shuffle2": '@shuffle(["a", "e", "i", "o", "u"])', // ['o', 'a', 'i', 'e', 'u'] // 随机生成一个 18 位身份证。 "id": Random.id(), // 112.127.151.37 "id2": '@id()' // 97.46.129.222 ———————————————— csdn原文链接:https://blog.csdn.net/Mme061300/article/details/130343270 }, ], })
mock/index.js
import Mock from 'mock' export const userData = Mock.mock( { // 属性 userList的值是一个数组,随机生成 1 到 10 个元素 "userList|1-10": [ { // 随机生成1-10个★ "name": "@cname", ... }, ], }) ———————————————— export default = ([ // 命中 /api/test?a=1 { method:"get" url: '/api/test', validator: { query: { a: 1 } }, body: { message: 'query.a === 1' }, response:()=>{ return { msg:"sucess", code:200, data:userData } } }, // 命中 /api/test/abc { url: '/api/test/abc', body: { message: 'query.a === 2' } }, ])
vite与ts的结合
Vite 天然支持引入
.ts
文件。Vite 仅执行
.ts
文件的转译工作,并 不 执行任何类型检查(只编译,不会执行类型检查)Vite 使用 esbuild 将 TypeScript 转译到 JavaScript
所以如果想要在打包构建时进行类型检查,可以在 构建脚本中运行
tsc --noEmit
,即://package.json "scripts":{ "build":"tsc --noEmit && vite build" }
如果需要ts类型报错提示,需要安装插件
vite-plugin-checker
npm i vite-plugin-checker -D
npm i typescript -D
使用ts需要有一个tsconfig.json文件配置一些ts的检查手段和检查规则
vite.config.js
import checker from 'vite-plugin-checker' import {defineConfig} from 'vite' export default defineConfig({ plugins: [checker ({ typescript:true }),] })
tsconfig.json
//配置一些ts的检查手段和检查规则 { "compilerOptions":{ "skipLibCheck": ture,//·是否跳过node_modules目录的验查 "module":"ESNext" //配置 } }
typescript使用环境变量时,tsconfig.json默认es语法是es3,无法识别import.meta.env,所以需要在配置文件中配置"module"为"ESNext"
至此,在ts文件中再引用import.meta.env,但还是出现报错,原因是:
Vite 默认的类型定义是写给它的 Node.js API 的。要将其补充到一个 Vite 应用的客户端代码环境中,请添加一个
d.ts
声明文件:需要一个声明文件vite-env.d.ts
//vite-env.d.ts //三斜线指令 /// <reference types="vite/client" /> interface ImportMetaEnv {//能让环境变量有提示(可有可无) readonly VITE_PROXY_TARGET: string; }
同时,将
vite/client
添加到tsconfig
中的compilerOptions.types
下://配置一些ts的检查手段和检查规则 { "compilerOptions":{ "moduleResolution":"node", "skipLibCheck": ture,//·是否跳过node_modules目录的验查 "module":"ESNext", //配置 "types": ["vite/client"], "lib":["ES2017","DOM"], // 配置es版本 } }
这将会提供以下类型定义补充:
- 资源导入 (例如:导入一个
.svg
文件) import.meta.env
上 Vite 注入的环境变量的类型定义import.meta.hot
上的 HMR API 类型定义
vite性能优化(也是前端主要的性能优化)
- 开发时的构建速度优化,即启动时间
-vite是按需加载,所以不需要太关注这个
- 页面性能指标(与代码有关)
-首屏渲染时长 fcp(first content paint →页面中第一个元素的渲染时长)
-懒加载
-http优化(协商缓存,强缓存彻底弄懂强缓存与协商缓存 - 简书 (jianshu.com) )
index.html文件采用协商缓存,理由就是要用户每次请求index.html不拿浏览器缓存,直接请求服务器,这样就保证资源更新了,用户能马上访问到新资源,如果服务端返回304,说明资源没有更新,这时候再拿浏览器的缓存的index.html
-页面中最大元素的渲染时长 lcp(largest content paint)
- js逻辑
**-**副作用的清除(组件挂载渲染时清除副作用)
-写法上的一些注意事项,例如requestAnimationFrame,requestIdleCallback,这两个API主要用来 卡浏览器帧率
-requestIdleCallback: 传一个回调函数进去,如果再16.6ms内执行完有剩余时间则会执行回调
-浏览器的帧率: 16.6ms去更新一次 (执行js 以及 重排重绘…)-react concurrent mode ,react18 concurrency 可终端渲染
-防抖、节流等,最好还是用工具库lodash,因为它是最佳实践;数组forEach 如果数据较大,建议也用lodash提供的forEach
-作用域的控制
for(let i = 0,len = arr.length,i<len,i++){ }
- CSS
–关注继承属性,能继承就不要重读写
-尽量避免css嵌套过深
-尽量减少重排重绘
- 构建优化vite(rollup)
-优化体积: ↓↓↓↓压缩,treeshaking,图片资源压缩,cdn加载,分包… ↓↓ ↓↓ ↓↓
vite性能优化(构建部分)
一、分包策略
分包 就是把一些不会常规更新的代码进行单独打包处理
例如:lodash库的一些代码在打包时是不会变的,但每次打包都需要重新构建,为优化这种情况,可以将这些不会常规更新的代码进行单独打包处理
vite内部其实已经内置好了,自动优化了这些分包策略
二、gzip压缩
文件资源过大,影响http传输,将静态资源进行压缩,以达到减小体积的目的
vite服务端-收到文件,进行压缩
客户端-收到压缩后的文件,进行解压缩
插件:vite-plugin-compression
配置即可,打包后会生成.zp 文件,服务端会做相应配置,在读取到zp文件时会将其解压缩,这个过程也会消耗一些时间,所以如果体积不是很大,就不用使用gzip压缩,否则可能适得其反
三、动态导入
webpack - vite
vite是按需加载的,webpack在构建时会将全部依赖加载
动态导入 与 按需加载 类似 ,动态导入时es6的新特性
代码分割
import ("a.js").then(res=>{con`在这里插入代码片`sole.log(res)})
动态导入后,打包后的代码,会将动态导入的代码分割开来
动态导入路由
routes:[ {`在这里插入代码片` path:'/home' component:import ("a.js"), } ]
四、CDN加速(content delivery network 内容分发网络)
将项目依赖的代码全部写成cdn的形式,保证代码的小体积
vite处理跨域问题
不满足同源策略(协议、域名、端口任意一个不相同,仅发生在浏览器)
//vite处理跨域 import {defineConfig} from 'vite' export default defineConfig({ server:{ proxy:{ // 使用 proxy 实例 '/api': { //key+描述对象,遇到'`在这里插入代码片`/api'开头的请求时,都会将其代理到target属性对应的地址 target: 'http://jsonplaceholder.typicode.com', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, ''), //路径重写,将'api换成空串' //configure: (proxy, options) => { // proxy 是 'http-proxy' 的实例 //} }, } } })
即客户端去请求vite的开发服务器,服务器收到请求,根据config代理配置重写请求路径,从服务端发送请求,由于同源策略仅发生在浏览器,服务器端之间的请求不会受限制,所以vite开发服务器收到请求后,向重写后的地址发送请求,直接把相应结果给到浏览器(客户端)——当然以上都是关于开发环境的跨域解决
生产环境——一般是交给后端或运维去处理跨域 -ngnix:代理服务(与本地开发服务器类似) / -配置身份标记(Access-Control-Allow-Origin)
- vite处理css文件