vite搭建前端工程

vite简介

vite预构建

  • 将非 ESM 规范的代码转换为符合 ESM 规范的代码,另外就是将第三方依赖内部的多个文件合并为一个,减少 http 请求数量

  • 简单来说,vite在一开始将应用中的模块区分为依赖源码两类

  • 「依赖部分」更多指的是代码中使用到的第三方模块,比如 vue、lodash、react 等,vite 将会使用 esbuild 在应用启动时对于依赖部分进行预构建依赖

  • 「源码部分」 比如说平常我们书写的一个一个 js、jsx、vue等文件,这部分代码会在运行时被编译,并不会进行任何打包。vite 以 原生 ESM 方式提供源码

  • 在开发环境中vite是让浏览器接管了打包程序的部分工作,vite 只需要在浏览器请求源码时进行转换并按需提供源码。根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理

  • 在生产环境中,Vite利用Rollup对代码进行打包处理,并配合着tree-shaking/懒加载和chunk分割的方式为浏览器提供最后的代码资源

esbuild

  • Vite 是使用 Esbuild 来做预构建和内容转换的,同样规模的项目,使用 Esbuild 可以将打包速度提升10-100

  • Esbuild 是一款基于 Go 语言开发的 javascript 打包工具,最大的一个特征就是快

  • Esbuild支持多种模块格式,包括 CommonJSES6 模块、AMD等,使得它适用于任何类型的JavaScript项目

缓存

  • 在Vite中,缓存是一个重要的概念,因为它可以显著加快开发服务器的热重载速度

  • Vite使用了两级缓存:一级是内存中的模块缓存,二级是基于文件系统的缓存

  • 内存中的模块缓存:Vite会将每个模块的转换结果保存在内存中,以便在开发过程中快速访问

  • 基于文件系统的缓存:Vite会将经过转换的文件存储在node_modules/.vite/文件夹中,并且会在文件系统中缓存这些文件的依赖和变化

  • package.json中的dependencies列表;包管理器的package-lock.json文件;vite.config.js相关字段中的配置;当以上三个地方发生变化时,vite均会进行重新预购构建

  • 更改缓存文件的位置

// vite.config.js
export default defineConfig({
    cacheDir: './.cache'
})

模块热重载HMR

  • HMR 的全称叫做 Hot Module Replacement,即 模块热替换 或者 模块热更新
  • HMR 的作用就是在页面模块更新的时候,直接把页面中发生变化的模块替换为新的模块,同时不会影响其它模块的正常运作
  • Vite本身也实现了一套 HMR 系统,他是基于原生的 ESM 模块规范来实现,在文件发生改变时 Vite 会侦测到相应 ES 模块的变化,从而触发相应的 API,实现局部的更新
  • Vite 通过 import.meta.hot 对象暴露手动 HMR API

vite基础配置

配置文件

  • Vite 会自动解析项目根目录下名为 vite.config.js 的配置文件(也支持其他 JS 和 TS 扩展名)
  • 最基础的配置,vite.config.js
export default {
  // 配置选项
}
  • 显式地通过 --config 命令行选项指定一个配置文件
vite --config my-config.js
  • 可以使用 defineConfig 工具函数配合开发工具进行智能提示
import { defineConfig } from 'vite'

export default defineConfig({
    
})

vite-plugin-html

  • vite内置了非常多的插件,开发者无需关注过多的插件配置
  • vite-plugin-html 是一个基于ejs模板库动态控制整个html文件内容的一个插件
  • 插件安装
npm install vite-plugin-html -D
  • vite.config.js添加配置
import { CreateHtmlPlugin } from 'vite-plugin-html'

plugins: [
    createHtmlPlugin({
        minify: true,   // 是否压缩代码
        pages: [
            {    // 进行多页面打包配置
                filename: "index.html",     // 生成的文件名称
                template: "./index.html",   // 模板文件
                entry: "./src/main.js",     // 打包入口文件
                injectOptions: {            // 注入页面信息
                    data: {
                        title: "vite项目练习",
                    }
                }
            }
        ]
    }),
]
  • 在html中使用
<title><%= title %></title>

别名配置

  • vite对静态资源是开箱即用
  • 路径别名配置
resolve: {
    alias: {
        '@': path.resolve(__dirname, "./src"),
        '@assets': path.resolve(__dirname, "./src/assets")
    }  
},

vite打包配置

build: {
    rollupOptions: {
        external: [],   // 哪些依赖无需打包,例如使用cdn单独引入的包不需要参与打包 
        output: {       // 针对图片、样式文件、js文件进行分类规整
            assetFileNames(assetInfo) {
                if (assetInfo.name.endsWith('.css')) {
                    return 'css/[name]-[hash].css'
                }
                const imgExts = ['.png', '.jpg', '.jpeg', '.webp', '.gif', '.svg', '.ico']
                if (imgExts.some(ext => assetInfo.name.endsWith(ext))) {
                    return 'imgs/[name]-[hash].[ext]'
                }
                return 'assets/[name]-[hash].[ext]'
            }
        },
    },
    assetsInlineLimit: 4096, 	// 小于4kb会转成base64字符串放在打包文件里
    outDir: "dist",	            // 打包后的文件夹名称
    assetsDir: "js"	            // 静态资源(js)文件夹名称
}

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脚本的形式来执行css后缀的文件

  • vite中的css模块化(less、sass同理)
  1. xx.module.css(module的一种约定,表示需要开启css模块化)

  2. 它会将你的所有类名进行一定规则的替换,同时创建一个映射对象

  3. 将替换过后的内容塞进style标签里,然后放入到head标签中

  • index.module.css
.main-box {
  width: 200px;
  height: 200px;
  border: 1px solid orange;
}
  • main.js
import componentALess from "./css/index.module.css";

const div = document.createElement('div')
div.className = componentALess.mainBox
document.body.appendChild(div)

vite配置css

  • 在vite.config.js中我们通过css属性去控制整个vite对css的处理行为
// vite.config.js
import { defineConfig } from 'vite'

export default defineConfig({
    css: {
        modules: {
            /**
             * camelCase        {"main-box":"_main-box_1eq4v_1","mainBox":"_main-box_1eq4v_1"}
             * camelCaseOnly    {"mainBox":"_main-box_1eq4v_1"}
             * dashes           {"main-box":"_main-box_1eq4v_1","mainBox":"_main-box_1eq4v_1"}
             * dashesOnly       {"mainBox":"_main-box_1eq4v_1"}   
             */
            localsConvention: "camelCase",
            // local | global  配置为global时,无法通过import的方式进行引用
            scopeBehaviour: "local",
            hashPrefix: "",         // 配置hash生成规则,主要和 name 联用。
            globalModulePaths: []   // 配置不参与模块化的文件路径
        },
        devSourceMap: true,		   // 开启css的sourceMap(文件索引)
    }
})
  • less、sass等预处理器
# 使用sass
npm install sass -D
# 使用less
npm install less -D
  • less、sass等预处理器配置项
import { defineConfig } from 'vite'

export default defineConfig({
    css: {
        preprocessorOptions: {
            // 整个的配置对象都会最终给到less的执行参数(全局参数)中去
            less: {						// less相关配置
                math: 'always',
                globalVars: {	     	// 全局变量
                    mainColor: 'red',
                }
            },
            sass: {}				 // sass相关配置
        },
    }
})
  • postcss

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

  1. 对未来css属性的一些使用降级问题(如css变量, --global-color)

  2. 前缀补全(如–webkit)

  3. postcss将语法进行编译(嵌套语法、函数、变量)成原生css

  4. 目前less和sass等一系列预处理器的postcss插件已经停止维护了,需要自己使用less或sass编译器编译,然后将编译结果发给postcss,因此说postcss是后处理器

  • postcss-依赖安装
npm i postcss postcss-preset-env -D
  • postcss-使用方式一(推荐),项目根目录新建postcss.config.js文件,配置如下
module.exports = {
	plugins: [
		require('postcss-preset-env')({
			browsers: [	// 添加前缀的浏览器列表
				"> 1%",
				"Chrome > 31",
				"Firefox > 31",
				"ie >= 10",
				"last 10 versions"
			],
			autoprefixer: { grid: true },	// grid布局是否添加前缀
		}),
	],
};
  • postcss-使用方式二(了解即可),在vite.config.js中添加配置
import postcssPresetEnv  from 'postcss-preset-env'

css: {
    postcss: {
        plugins: [
            postcssPresetEnv({
                browsers: [	// 添加前缀的浏览器列表
                    "> 1%",
                    "Chrome > 31",
                    "Firefox > 31",
                    "ie >= 10",
                    "last 10 versions"
                ],
                autoprefixer: { grid: true },	// grid布局是否添加前缀
            }),
        ],
    },
}

vite环境变量

  • 在项目根目录下新建.env.dev, .env.test, .env.prod文件,分别写入不同的测试变量
/*
	.env.dev
		VITE_TEST = 'dev'
	.env.test
		VITE_TEST = 'test'
	.env.prod
		VITE_TEST = 'prod' 
*/
  • 修改命令行脚本
"scripts": {
  "dev": "vite --mode dev",
  "build:test": "vite build --mode test",
  "build:prod": "vite build --mode prod"
}
  • 业务代码中使用
console.log(import.meta.env.VITE_TEST)
  • 需要注意的是:文件名称中务必包含对应的mode,这样Vite会自动将配置文件的变量注入到业务代码中
  • 环境变量的其它配置
import { defineConfig } from 'vite'

export default defineConfig({
    envPrefix: "VITE_",	 // 以 envPrefix 开头的环境变量会通过 import.meta.env 暴露在你的客户端源码中,默认是VITE_
    envDir: '/', 		// 用于加载 .env 文件的目录。可以是一个绝对路径,也可以是相对于项目根的路径
    define: {		    // 定义全局常量替换方式。其中每项在开发环境下会被定义在全局,而在构建时被静态替换
        "process.env": process.env  // 解决部分依赖包process错误问题
    },
})

开发服务器

  • 开发服务器常用配置
server: {
    host: '0.0.0.0',	// 允许其他人访问
    port: 3000,     	// 自定义端口,默认为5173
    open: false,    	// 服务启动后,自动在浏览器中打开,默认是不打开的
    hmr: true,      	// 为开发服务启用热更新,默认是不启用热更新的
    proxy: {
        '/foo': 'http://localhost:4567',
        '/api': {
            target: 'http://jsonplaceholder.typicode.com',
            changeOrigin: true,
            rewrite: (path) => path.replace(/^\/api/, ''),
        },
    }
},

vite插件

@vitejs/plugin-vue2

  • @vitejs/plugin-vue2是一个专为Vue2.x设计的Vite插件,它让你能够在Vite构建工具中无缝地使用和编译Vue2组件

  • 特别要注意的是,@vitejs/plugin-vue2这个插件仅支持Vue版本大于等于2.7.0

  • vue2项目迁移vite打包,依赖包安装

npm install @vitejs/plugin-vue2 -D
  • vite.config.js增加插件配置
import vue from '@vitejs/plugin-vue2'
import { defineConfig } from 'vite'

export default defineConfig({
    plugins: [
        vue()
    ],
})

@vitejs/plugin-vue

  • @vitejs/plugin-vue 是一个 Vite 插件,提供了对 Vue3 单文件组件支持。
  • 它还提供了一些其他功能,例如自动导入 Vue 组件和样式等
  • 依赖包安装
npm install @vitejs/plugin-vue -D
  • 使用方法同@vitejs/plugin-vue2

@vitejs/plugin-legacy

[!NOTE]

默认情况vite的兼容目标是支持原生ES模块,原生ESM动态导入和import.meta的浏览器,官方预设的兼容目标如下:

  1. es2020
  2. edge 88
  3. firefox 78
  4. chrome 87
  5. safari 14

所以vite在打包后不会做任何兼容处理,这就导致了一些浏览器版本低一点的设备,打开网站就会出现白屏的情况

  • @vitejs/plugin-legacy是一个兼容低版本浏览器的官方插件

  • 他会自动生成传统浏览器的 chunk及与其相对应 ES 语言特性方面的 polyfill,兼容版的 chunk 只会在不支持原生 ESM 的浏览器中进行按需加载

  • 插件安装

npm install  @vitejs/plugin-legacy -D
  • vite.config.js增加配置
import legacyPlugin from '@vitejs/plugin-legacy'
import { defineConfig } from 'vite'

export default defineConfig({
    plugins: [
        legacyPlugin({
            targets:  ['defaults', 'Chrome >= 51', 'Edge >= 15', 'Safari >= 10'],
            renderLegacyChunks: true,   // 开启传统浏览器polyfill
            polyfills: true             // 针对传统浏览器的polyfill,采用默认的规则
        }),
    ],
    build: {
        target: 'es2015',
        minify: 'esbuild',	// 默认值是esbuild,当设置为 'terser' 时或者老版本必须先安装 npm add -D terser
        // terserOptions: {
        //     compress: {
        //         //  生产环境时移除console
        //         drop_console: true,
        //         drop_debugger: true,
        //     }
        // },
    },
})
  • 官方定义,在@vitejs/plugin-legacy没有明确指定插件配置参数targets时,他会去读取项目中的.browserslistrc文件,所以也可以在文件中进行定义
Chrome >= 51 
Edge >= 15 
Safari >= 10 
Firefox >= 54 
Opera >= 38 
iOS >= 10 
Android >= 5 
not IE <= 11

vite-plugin-mock

  • mockjs: 模拟海量数据的,vite-plugin-mock的依赖项就是mockjs
  • 插件安装
npm install vite-plugin-mock mockjs -D
  • vite.config.js中增加配置
import { ViteMockServe } from 'vite-plugin-mock'

plugins: [
    ViteMockServe({
        mockPath: 'mock'
	})
]
  • 项目根目录创建mock文件夹,与上边mockPath保持一致,在mock文件夹下创建index.js文件
import mockJS from "mockjs"
module.exports = [
    {
        method: "post",
        url: "/api/users",
        response: ({body}) => {
           return {
               code: 200,
               msg: "success",
               data: mockJS.mock({
                   "data|100": [{
                       name: "@cname",
                       ename: mockJS.Random.name(),
                       "id|+1": 1,
                   }]
               })
           }
        }
    }
]

配置文件汇总

文件目录

│  .browserslistrc
│  .env.dev
│  .env.prod
│  .env.test
│  index.html
│  package-lock.json
│  package.json
│  postcss.config.js
│  vite.config.js
│
└─src
    │  app.vue
    │  main.js
    │
    ├─assets
    │      logo.png
    │
    └─css
           index.module.css

vite.config.js

import { defineConfig } from 'vite'
import { createHtmlPlugin } from 'vite-plugin-html'
import vue from '@vitejs/plugin-vue2'
import legacyPlugin from '@vitejs/plugin-legacy'
import path from 'path'

export default defineConfig({
    base: './',         // 公共基础路径,可以是绝对路径、相对路径或域名
    plugins: [
        legacyPlugin({
            renderLegacyChunks: true,   // 开启传统浏览器polyfill
            polyfills: true             // 针对传统浏览器的polyfill,采用默认的规则
        }),
        createHtmlPlugin({
            minify: true,   // 是否压缩代码
            pages: [
                {    // 进行多页面打包配置
                    filename: "index.html",     // 生成的文件名称
                    template: "./index.html",   // 模板文件
                    entry: "./src/main.js",     // 打包入口文件
                    injectOptions: {            // 注入页面信息
                        data: {
                            title: "vite项目练习",
                        }
                    }
                }
            ]
        }),
        vue()           // 支持vue开发插件
    ],
    resolve: {
        alias: {
            '@': path.resolve(__dirname, "./src"),
            '@assets': path.resolve(__dirname, "./src/assets")
        }
    },
    optimizeDeps: {
        exclude: [] // 将指定数组中的依赖不进行依赖预构建
    },
    css: {
        modules: {
            /**
             * camelCase        {"main-box":"_main-box_1eq4v_1","mainBox":"_main-box_1eq4v_1"}
             * camelCaseOnly    {"mainBox":"_main-box_1eq4v_1"}
             * dashes           {"main-box":"_main-box_1eq4v_1","mainBox":"_main-box_1eq4v_1"}
             * dashesOnly       {"mainBox":"_main-box_1eq4v_1"}   
             */
            localsConvention: "camelCase",
            // local | global  配置为global时,无法通过import的方式进行引用
            scopeBehaviour: "local",
            hashPrefix: "",         // 配置hash生成规则,主要和 name 联用。
            globalModulePaths: []   // 配置不参与模块化的文件路径
        },
        preprocessorOptions: {
            // 整个的配置对象都会最终给到less的执行参数(全局参数)中去
            less: {						// less相关配置
                math: 'always',
                globalVars: {	     	// 全局变量
                    mainColor: 'red',
                }
            },
            sass: {}				 // sass相关配置
        },
        devSourceMap: true,	        // 开启css的sourceMap(文件索引)
    },
    envPrefix: "VITE_",		 // 以 envPrefix 开头的环境变量会通过 import.meta.env 暴露在你的客户端源码中,默认是VITE_
    envDir: '/', 		    // 用于加载 .env 文件的目录。可以是一个绝对路径,也可以是相对于项目根的路径
    define: {			    // 定义全局常量替换方式。其中每项在开发环境下会被定义在全局,而在构建时被静态替换
        "process.env": process.env  // 解决部分依赖包process错误问题
    },
    build: {
        target: 'es2015',   // 设置最终构建的浏览器兼容目标
        minify: 'esbuild',  // 'terser' | 'esbuild',客户端构建默认为'esbuild'
        rollupOptions: {
            external: [],   // 哪些依赖无需打包,例如使用cdn单独引入的包不需要参与打包 
            output: {       // 针对图片、样式文件、js文件进行分类规整
                assetFileNames(assetInfo) {
                    if (assetInfo.name.endsWith('.css')) {
                        return 'css/[name]-[hash].css'
                    }
                    const imgExts = ['.png', '.jpg', '.jpeg', '.webp', '.gif', '.svg', '.ico']
                    if (imgExts.some(ext => assetInfo.name.endsWith(ext))) {
                        return 'imgs/[name]-[hash].[ext]'
                    }
                    return 'assets/[name]-[hash].[ext]'
                }
            },
        },
        assetsInlineLimit: 4096, 	// 小于4kb会转成base64字符串放在打包文件里
        outDir: "dist",	            // 打包后的文件夹名称
        assetsDir: "js"	            // 静态资源(js)文件夹名称
    },
})
  • 26
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青春~不散

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值