vite 学习笔记

237 阅读14分钟

B站link# Vite世界指南(带你从0到1深入学习 vite)

2022-11-28

vite 依赖预购建解决的问题:

  1. 不同的第三方包会有不同的导出格式
  2. 对路径的处理上可以直接使用 .vite/deps, 方便路径重写
  3. 叫做网络多包传输的性能问题(也是原生esmodule规范不敢支持node_modlues的原因之一)有了依赖预购建之后

依赖预构建仅会在开发模式下应用,并会使用 esbuild 将依赖转为 ESM 模块。在生产构建中则会使用 @rollup/plugin-commonjs

vite 配置文件处理细节

语法提示

import {defineConfig} from 'vite'
export default defineConfig({
  optimizeDeps:{
    exclude:[], //将指定数组中的依赖不进行依赖预构建
})
/** @type import("vite").UserConfig */
const viteConfig = {
  optimizeDeps: {
      exclude:[]
  }
}

环境处理

//策略模式
const envResolver = {
    "build": () => ({...viteBaseConfig, ...viteProdConfig}),
    "serve": () => Object.assign({},viteBaseConfig, viteDevConfig)
}

export default defineConfig(config: ({comand: "build" | ""serve"}) => {
//是build 还是serve 主要取决于我们敲的命令是开启开发环境还是生产环境
    return envResolver[command]()
})

2022-11-30

环境变量配置

环境变量:会根据当前的代码环境产生值的变化的变量就叫做环境变量

代码环境: 1.开发环境 2.测试环境 3.预发布环境 4.灰度环境 5.生产环境

举个例子: 百度地图sdk, 小程序sdk

APP_KEY: 测试环境和生产还有开发环境是不一样的key

我们去请求第三方sdk接口的时候需要带上一个身份信息

我们在和后端对接的时候,前端在开发环境中请求的后端API和生产环境的后端API地址, 不是同一个

测试和开发 'https://test.api/'
生产: ’https://api‘

.env.development

APP_KEY=110
BASE_URL= http://test.api/

.env.production

APP_KEY=112
BASE_URL= http://api/

在vite中的环境变量处理:

vite 内置了dotenv 这个第三方库

dotenv 会自动读取.env文件,并解析这个文件的对应环境变量,并将其注入到process(node下的全局变量)对象下(但是vite考虑到和其他配置的一些冲突问题,不会直接注入到process对象下)

涉及到vite.config.js中的一些配置

  • root
  • envDir: 用来配置当前环境变量的文件地址

vite提供了补偿措施: 可以调用vite的loadEnv 来手动确认env 文件

process.cwd 方法: 返回当前node 进程的工作目录

.env: 所有环境都需要用到的环境变量

.env.develop:开发环境需要用到的环境变量(默认情况下vite将我们的开发环境取名为development)

.env.production:生产环境需要用到的环境变量

yarn dev --development 会将mode设置为 development

补充一个小知识: 为什么vite.config.js 可以书写成esmodule的形式,这是因为vite在读取这个vite.config.js的时候会事先node去解析文件语法,如果发现是esmodule 规范会直接将esmodule替换成commonjs 规范

//策略模式
const envResolver = {
    "build": () => ({...viteBaseConfig, ...viteProdConfig}),
    "serve": () => Object.assign({},viteBaseConfig, viteDevConfig) // 新配置里是可能会被配置envdir .envA 覆盖
}

export default defineConfig(config: ({comand: "build" | "serve", mode: "string"}) => {
//是build 还是serve 主要取决于我们敲的命令是开启开发环境还是生产环境
    
    // 第二个参数不是必须要使用process.cwd()
    const env = loadEnv(mode, process.cwd(), prefix: "")
    return envResolver[command]()
})

2022-12-01

当我们调用loadenv时,会做如下几件事情:

  1. 直接找到.env文件 , 并解析其中的环境变量 并放到一个对象里
  2. 会将传进来的mode这个变量的值进行拼接, .env.development,并根据我们提供的目录去取对应的配置文件并进行解析,并放进一个对象
  3. 可以理解为
 const baseEnvConfig = 读取.env的配置
 const modeEnvConfig = 读取env相关配置
 const lastEnvConfig = {...baseEnvConfig, ...modeEnvConfig}

如果是客户端,vite会将对应的环境变量注入到import.meta.env里面去

vite做了一个拦截,为了防止我们将隐私性的变量直接送进import.meta.env中,所以做了一层拦截,如果环境变量不是以VITE开头的,就不会帮我们注入到客户端去

如果想要更改这个前缀,可以去使用envPrefix配置

import {defineConfig} from 'vite'
export default defineConfig({
 optimizeDeps:{
   exclude:[], //将指定数组中的依赖不进行依赖预构建
 },
 envPrefix:"ENV_", //配置vite注入客户端环境变量校验的env前缀
})
const getUserPosition = () => {
    axios.post({
        params:{
            APP_KEY: import.meta.env.ENV_APP_KEY
        }
    })
}

2022-12-05

[原理篇]vite 是怎么让浏览器可以识别 .vue文件的

yarn install vue

yarn create 实际上就等于在安装create-vite脚手架 然后使用脚手架的指令去构建项目

yarn create vite my-vue-app --template vue

来实现一套简单的vite的开发服务器

  1. 解决我们刚刚这个问题
  2. 对开发服务器的原理层面有一个基础简单的认识

会涉及到node的一些知识 koa: node 端的一个框架

const Koa = require('koa') //不能使用esmodule, 必须使用commonjs
const fs = require('fs') //node 的内置模块(原生模块)
const path = require('path')

//document.getElementById(id) --> 是浏览器环境注入给JS的特殊能力
//不同的宿主环境会给js赋予一些不同的能力

const app = new Koa()

//node 最频繁做的事情就是在处理请求和操作文件

//当请求来临以后会直接进去到use注册的回调函数中
app.use(async (ctx) => {
  //context 上下文
  //request  : 请求信息,
  //response :响应信息
  console.log('ctx', ctx.request, ctx.response)

  //实际开发中是用中间件, 这里只有4个文件,只是演示

  if (ctx.request.url === '/') {
    //这意味着在向我们要跟路径的东西

    //path.resolve(__dirname,"./index.html") 返回一个绝对路径
    const indexContent = await fs.promises.readFile(path.resolve(__dirname,'./index.html')
    ) 
    //在服务端一般不会这么用

    console.log('indexContent', indexContent.toString())
    ctx.response.body = indexContent //作为响应体发给对应的请求的人

    //响应体是填充好了,要以什么形式发送? 或者希望对方拿到东西的时候以什么方式解析
    // json ---> application/json  text/html  text/javascript
    ctx.response.set('Content-Type', 'text/html')
  }

  if (ctx.request.url === '/main.js') {
    const mainJsContent = await fs.promises.readFile(
      path.resolve(__dirname, './main.js')
    ) //在服务端一般不会这么用
    ctx.response.body = mainJsContent //作为响应体发给对应的请求的人

    //响应体是填充好了,要以什么形式发送? 或者希望对方拿到东西的时候以什么方式解析
    // json ---> application/json  text/html  text/javascript
    ctx.response.set('Content-Type', 'text/javascript')
  }

  //正常情况下会进行正则匹配
  if (ctx.request.url === '/App.vue') {
    const mainVueContent = await fs.promises.readFile(
      path.resolve(__dirname, './App.vue')
    ) //在服务端一般不会这么用

    //如果是Vue文件,会做一个字符替换:mainVueContent.toString().find("<template></template>")
    // 如果匹配到了,就直接全部进行字符串替换
    //【实际比较复杂,会将.vue 转换 .js】

    ctx.response.body = mainVueContent //作为响应体发给对应的请求的人

    //响应体是填充好了,要以什么形式发送? 或者希望对方拿到东西的时候以什么方式解析

    // 即使看到了.vue文件,也要用JS的方式去解析
    ctx.response.set('Content-Type', 'text/javascript')
  }

  // 比如后台给我们一个获取用户信息的接口 api/getUserInfo post
  if (ctx.request.url === '/api/getUserInfo') {
    //去数据库找到用户信息然后返回给前端
  }
})

app.listen(5173, () => {
  console.log('vite dev server on 5173')
})

2022-12-07

vite 中处理css

vite天生就支持对css文件的直接处理

  1. vite 在读取main.js中引用到了 index.css
  2. 直接去使用fs模块去读取index.css中文件内容
  3. 直接创建一个style标签,将index.css中文件内容直接copy进style标签里
  4. 将style标签插入到index.html的head中
  5. 将该css文件中的内容直接替换为js脚步(方便热更新或者css模块化),同时设置Content-Type为js 从而让浏览器以JS脚本的形式来执行该CSS后缀的文件

场景:

最终可能会导致样式被覆盖(因为类名重复),这就是在协同开发的时候很容易出现的问题

解决方案:

cssmodule

image.png

大概原理:

全部都是基于node

  1. module.css (module 是一种约定,表示需要开启css模块化)
  2. 会将所有类型进行一定规则的替换(将footer 替换成 _footer_122st_1)
  3. 同时创建一个映射对象(foote:"_footer_122st_1")
  4. 将替换后的内容塞进style标签里然后放入到head标签中(能够读到index.html文件中的内容)
  5. 将componentA.module.css内容进行全部抹除,替换成JS脚本
  6. 将创建的映射对象在脚本中进行默认导出

less(预处理器): less 给我们提供了一些方便且非常实用的方法

2022-12-08

vite.config.js 中css 配置(module篇)

在vite.config.js 中我们通过css属性去控制整个vite 对于css的处理行为

  • localConvention: 修改生成的配置对象的key的展示形式(驼峰还是中划线)
  • scopeBehaviour: 配置当前的模块化行为是模块化还是全局化(有hash就是开启了模块化的一个标志,因为可以保证产生不同的hash值来控制我们的样式类名不被覆盖)
  • generateScopeName:生成类名的规则(可以配置为函数,也可以配置成字符串规则)
  • hashPrefix: 生产hash的时候会根据类名去生成,可以配置hashPrefix,这个字符串会参与到最终的hash生成(hash: 只要字符串有一个字不一样,那么生成的hash就完全不一样,但是只要字符串完全一样,生成的hash就会一样)
  • globalModulePaths: 代表你不想参与到css模块化的路径
import {defineConfig} from 'vite'
export default defineConfig({
  optimizeDeps:{
    exclude:[], //将指定数组中的依赖不进行依赖预构建
  },
  envPrefix:"ENV_", //配置vite注入客户端环境变量校验的env前缀
  css:{ 
  //modules 配置最终会丢给postcss modules
      modules:{ // 是对css 模块化的默认行为进行覆盖
          localConvention:'camelCase', //配置转换类名格式
          scopeBehaviour:'local'global),//local代表开启模块化
          //generateScopeName:'[name]-[local]-[hash:5]',
          //还可以配置成函数
          generateScopeName: (name,filename, css) => {
              //name => 代表此刻css文件中的类型
              //filename => 是当前css文件的绝对路径
              //css=> 当前的样式
              //配置成函数以后,返回值就决定了最终显示的类型
              return `${name}_${Math.randam().toString(36).substr(3,8)}` 
          }
          hashPrefix:"hello",
          globalModulePaths:[], //代表你不想参与到css模块化的路径
          
      }
  }
})

2022-12-12

vite 配置文件中css配置流程(preprocessorOptions)

主要是用来配置css预处理的一些全局参数

假设没有使用构建工具, 我们又想去编译less文件的话

yarn add less # lessc的编译器

你只要安装了node, 你就可以使用node index.js 你只要安装了less 你就可以使用lessc去编译less文件

npx lessc --math="always" index.module.css

less是可以定义变量的

import {defineConfig} from 'vite'
export default defineConfig({
  optimizeDeps:{
    exclude:[], //将指定数组中的依赖不进行依赖预构建
  },
  envPrefix:"ENV_", //配置vite注入客户端环境变量校验的env前缀
  css:{ 
  //modules 配置最终会丢给postcss modules
      modules:{ // 是对css 模块化的默认行为进行覆盖
          
      },
      preprocessorOptions: {
          less:{ //整个的配置对象都会最终给到less的执行参数(全局参数)中去
          //在webpack 里就给less-loader 去配置就好了
            // 去less 文档里面找相关参数
            math:"always",
            globalVars:{
                mainColor: "red"
            }
          },
          sass:{
          // 去sass 文档里面找相关参数
          },
          devSourceMap: true //开启css的sourceMap(文件索引)
      }
  }
})

sourceMap

文件之间的索引:

假设我们的代码被压缩或者被编译过了, 这个时候假设程序出错, 他将不会产生正确的错误位置信息 如果设置了sourceMap, 他就会有一个索引文件map

sourceMap解决的问题极其的微小, 但是他的实现过程非常的复杂

postcss

vite天生对postcss有非常良好的支持

全屋净水系统有一个了解

水龙头里来的水是自来水

自来水 从 管道里 先到这个全屋净水系统 给全屋净水系统做一些插槽 ---> 去除砂砾 --> 净化细菌微生物 ---> ... --> 输送到水龙头 --> 我们可以喝的纯净水 (为了保证到我们嘴里喝的水是万无一失)

postcss 他的工作基本和全屋净水系统一致: 保证css在执行起来是万无一失的

目前 less 和 sass 等一系列预处理器的postcss 插件已经停止维护了, less 插件 ,自己去用less 和 sass 编译完了,然后把编译结果给我

所以业内就产生了一个新的说法:postcss是后处理器

babel --> 帮助我们让js执行起来万无一失

class App {} // es6的写法

function App() {} // es3的语法

浏览器的兼容性, 预处理器并不能够解决这些问题:

  1. 对未来css属性的一些使用降级问题
  2. 前缀补全: Google非常卷 --webkit

2022-12-20

使用postcss

  1. 安装依赖
yarn add postcss-cli postcss -D
  1. 书写描述文件

postcss配置文件的格式

  • postcss.config.js
// 类似于全屋净水系统的加插槽
// 预设环境里面是会包含很多的插槽
// 语法降级 --> postcss-low-level
// 编译插件 --> postcss-compiler
// ...
const postcssPresetEnv = requir('postcss-preset-env');

//预设就是一次性的把这些必要的插件都装上了
//做语法的编译 less语法 sass语法
module.export = {
    plugins:[postcssPresetEnv(/* pluginOptions */)]
}

vite配置文件中css配置流程(postcss篇)

直接在css.postcss中进行配置, 该属性直接配置的就是postcss的配置

  • postcss-preset-env: 支持css变量和一些未来css语法 自动补全(autoprefixer)
import {defineConfig} from 'vite'

onst postcssPresetEnv = requir('postcss-preset-env');

export default defineConfig({
  optimizeDeps:{
    exclude:[], //将指定数组中的依赖不进行依赖预构建
  },
  envPrefix:"ENV_", //配置vite注入客户端环境变量校验的env前缀
  css:{ 
  //modules 配置最终会丢给postcss modules
      modules:{ // 是对css 模块化的默认行为进行覆盖
          
      },
      preprocessorOptions: {
          less:{ //整个的配置对象都会最终给到less的执行参数(全局参数)中去
          //在webpack 里就给less-loader 去配置就好了
            // 去less 文档里面找相关参数
            math:"always",
            globalVars:{
                mainColor: "red"
            }
          },
          sass:{
          // 去sass 文档里面找相关参数
          },
          devSourceMap: true //开启css的sourceMap(文件索引)
          // vite 的诞生一定会让postcss再火一次
          // 也可以不在这里配置,可以在post.config,js里面配置; 如果都配置了,这里优先级更高
          postcss:{
              plugins: [postcssPresetEnv({
              //就好比现在让postcss知道,有一些全局变量,需要记录下来
                  importFrom: path.resolve(__dirname, './variable.css ')
              })]
          }
      }
  }
})

css 新用法

width: clamp(100px, 30%, 200px)

2022-12-21

为什么我们在服务端处理路径的时候一定要用path

vite加载静态资源

什么是静态资源?

图片, 视频资源

除了动态API以外, 百分之九十九资源都被视作静态资源 API --> 来了一个请求 /getUserName

vite对静态资源基本上是开箱即用的, 除了一些特殊情况(svg)

image.png

用到哪个导入哪个,减少打包体积

image.png

图片的时候挂raw没有什么意义,svg的时候有意义 image.png

export default defineConfig({
    resolve:{
        //别名实用,避免目录过深
        alias:{
            "@": path.resolve(__dirname, './src'),
            "@asset": path.resolve(__dirname, './src/assets')
        }
    }
})

2023-01-02

resolve.alias原理

vite中处理svg资源

vite对svg依旧是开箱即用的

svg: scalable vector graphics 可伸缩矢量图形 (新的图片格式)

传统图片格式: jpg, jpeg...

  1. svg是不会失真的
  2. 尺寸小

缺点: 没法很好的去表示层次丰富的图片信息

我们在前端领域里更多的是用svg 去做图标

vite在生产环境对静态资源的处理

当我们将工程进行打包以后, 会发现找不到原来的资源

baseUrl: "/"

打包后的静态资源为什么要有hash

浏览器是有一个缓存机制 静态资源名字只要不改, 那么他就会直接用缓存的

刷新页面: 请求的名字是不是同一个 读取缓存

所以我们要尽量去避免名字一致

hash算法: 将一串字符串经过运算得到一个新的乱码字符串 全世界独一无二(uuid才是独一无二的 )

利用好hash算法 可以让我们更好的去控制浏览器的缓存机制

base64图片

export default defineConfig({
  optimizeDeps:{
    exclude:[], //将指定数组中的依赖不进行依赖预构建
  },
  envPrefix:"ENV_", //配置vite注入客户端环境变量校验的env前缀
  css:{ 
  },
  build:{
      rollupOptions:{ //配置rollup的一些构建策略
          output:{
              assetFileNames: "[hash].[name].[ext]"
          }
      },
      assetsInlineLimit: 4096000, //4000kb
      outDir:"testDist",
      assetsDir:"static"
  }
})

vite插件

插件是什么?

vite会在生命周期的不同阶段中去调用不同的插件以达到不同的目的

  1. 生命周期: 其实就和我们人一样, vite从开始执行到执行结束, 那么着整个过程就是vite的生命周期

webpack: 输出html文件的一个插件 清除输出目录: clean-webpack-plugin

中间件 , 插件

redux中间件是干嘛的: redux会在整个生命周期的不同阶段去调用不同的中间件以达到不同的目的

vite-aliases

插件学习由简入繁

vite-aliases可以帮助我们自动生成别名: 检测当前目录下包括src在内的所有文件夹, 并帮助我们去生成别名

{ "@": "/ /src", "@aseets": "/ /src/assets", "@components": "/**/src/components", }