在react中使用vite的一些配置,包含less全局变量、proxy、别名等常见配置方式

3,644 阅读7分钟

最近换了新的工作,因为新公司的项目使用微前端的集成方案,一个项目被从组件层面拆分出来不少单独的模块,所以经常需要新创建项目。也是因为以上的背景,让我有更多的机会尝试不同的工程打包方案,其中一个便是以速度著称的vite。

下面我将工作中遇到的一些问题,已经配置方式记录在这里,以便自己,或是其他看到的朋友作为参考

1、vite.config.js

使用vite,首先要在项目根目录创建vite.config.js的文件,如果使用ts的话,也可以是.ts文件。 这里其实重点想要说的是,vite属于新兴的工具,所以对于他的配置项,我们必然是很陌生的,但是官方很贴心的,给我们提供了方案——一个可以给出配置提示的方法:

import { defineConfig } from 'vite'
import reactRefresh from '@vitejs/plugin-react-refresh'
import path from 'path';

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

如上,只要引用defineConfig,包裹vite的config,你在键入options的时候,就可以看到提示啦。 还有需要注意的一点,vite内置less,和sass,但是如果使用默认配置,那么在给less,和sass文件起名字的时候,文件名后缀要使用.module.less,这样vite才能正确识别

项目中实用、常用配置

文件别名配置resolve.alias

import { defineConfig } from 'vite'
import reactRefresh from '@vitejs/plugin-react-refresh'
import path from 'path';

export default defineConfig({
  plugins: [reactRefresh()],
  resolve: {
    alias: [
      { find: "src", replacement: path.resolve(__dirname, 		    'src') },
    ]
  },
})

less设置全局变量css.preprocessorOptions

import { defineConfig } from 'vite'
import reactRefresh from '@vitejs/plugin-react-refresh'
import path from 'path';

export default defineConfig({
  plugins: [reactRefresh()],
  css: {
    preprocessorOptions: {
      less: {
        additionalData: `@import "${path.resolve(__dirname, 'src/theme.module.less')}";`,
        javascriptEnabled: true,
      }
    },
  },
})

打包

打包这里问题比较多,首先要确定自己的项目,是什么场景。比如:是一个完成的应用,需要将html打包进项目的,可以使用默认配置。如果自己的项目是SDK,或者是一个react组件。那么就需要使用另外一种库模式来打包。还有node环境的npm包,比如脚手架之类需要执行某些node命令行的配置,都会有所不同

常规配置-rollupOptions

一般的打包配置,可以使用rollupOptions,这部分具体有什么配置项,有哪些参数,我这里偷个懒,大家可以去rollup官方文档去查(虽然我知道这样比较烦人,但是这部分比较容易查,所以就不写啦,我重点写一点,不太容易查到的配置) 这里只分享一个我遇到的,更改css构建产物文件名的配置

import { defineConfig } from 'vite'
import reactRefresh from '@vitejs/plugin-react-refresh'
import path from 'path';

export default defineConfig({
  plugins: [reactRefresh()],
  build: {
    rollupOptions: {
      output:{
		  entryFileNames: `[name].${timestamp}.js`,
	      chunkFileNames: `[name].${timestamp}.js`,
	      // css文件名
	      assetFileNames: `[name].${timestamp}.[ext]`
	      // 比如你想构建出来的css为dist/index.css,那么你可以这样
	      //  assetFileNames: `index.[ext]`
	  }
	}
  },
})

库模式

这个模式适用于,打包一个不含html文件的场景

import { defineConfig } from 'vite'
import reactRefresh from '@vitejs/plugin-react-refresh'
import path from 'path';

export default defineConfig({
  plugins: [reactRefresh()],
  build: {
    lib: {
      // 入口文件
      entry: path.resolve(__dirname, 'src/index.tsx'),
      // umd、iife的格式vite要求必须要有name作为导出的全局变量
      name: "SpecialEffect",
      // 导出格式,默认为["iife","umd"]
      formats: ['iife'],
      // js打包名称,当然这部分官方文档更加详细
      fileName: () => "index.js"
    },
	
	// umd格式下,支持将不需要打包的第三方库,排除在外,并指定全部环境
	// 提供的全局变量代替,比如以下的例子,不将react打包,由全局React
	// 变量提供react库
	rollupOptions: {
      external: ['react'],
      output: {
        globals: {
          react: 'React'
        }
      }
    }
  },
})

iife、umd、es的区别

这几种都是文件打包导出的一种格式,也可以说是一种写法。iife是一个自执行的闭包函数的写法

// iifefunction name(){
	// index.js
 })()

umd与iife大体相同,区别在于执行顺序有一定变化,umd会优先执行外部环境的代码之后,再执行闭包。比如在打包时,需要排除vue或者react,并指定全局变量提供,如果这种场景下使用iife,可能会出现,react库还没有挂载到window.React变量上面,你导出的闭包函数就已经初始化执行了。这个时候会因为你的项目因为读不到依赖的react而产生报错。 而umd、iife的格式都支持script标签直接引入资源

es提供的是一个模块的导出方案,我的一个SDK项目,云构建的时候,需要使用umd的格式,但是在本地调试,就会报错。需要使用es的格式,虽然我还没有确定到底产生的原因是什么,但是猜测和导出的资源引用方式有关

本地开发服务器的配置

import { defineConfig } from 'vite'
import reactRefresh from '@vitejs/plugin-react-refresh'
import path from 'path';

export default defineConfig({
  plugins: [reactRefresh()],
  server: {
  	// 端口
    port: 8001,
    // 是否开启https服务
    https: true,
    // 代理
    proxy: {
      '/project/delete': {
        target: 'https://www.your-request-url.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/project\/delete/, '')
      },
      '/project/update': {
        target: 'https://www.your-request-url.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/project\/update/, '')
      },
    }
  },
})

这里我想讲一下proxy的配置。作为一名工程相关的前端新人,相信遇到代理问题是最最难受的一件事,没有之一。我这里找了无数的文档,虽然他们说的都对,但是在我不会配置的时候,就是看不懂。而且所有文档几乎都千篇一律,实在无解。这里我讲这个问题,重新分享一下

proxy

{
	proxy:{
		'api':{
			target: 'https://www.your-request-url.com',
        	changeOrigin: true,
        	rewrite: (path) => path.replace(/^\api/, '')
		}
	}
}

以上配置可以解决proxy百分之九十的问题,但是这个含义却很少有人讲的清楚。 首先 "api"这个是一个标识符,虽然很多接口会提供给你类似这样的www.your-request-url.com/api/material 接口,但是这里的api和接口中的这个api千万不要当成一回事。因为这个标识符是可以自定义的。

我还是拿www.your-request-url.com/api/material 来举例,如果这个接口在服务端没有进行相应的header配置,本地调用这个接口,是会跨域的。这个时候,我们需要利用本地开发服务器的代理功能,使用本地开发服务器请求这个接口,再把请求的结果返回给我们。(这里要说一下,这种是解决接口本地跨域的问题,因为一般情况下,前端项目打包构建后的资源,将会部署到接口同域名下,所以部署后是不会跨域的。但是如果是第三方接口跨域,那么这个就不是proxy可以解决的问题了)

首先我们需要向本地开发服务发起请求,然后本地开发服务器请求地址www.your-request-url.com/api/material, 然后得到的结果再返回给我们。

发起请求的方式是,我们通过proxy定义一个标识符,例如aaa,然后在本地开发服务器的地址拼接上标识符aaa。也就是localhost:3000/aaa,这样就可以对本地开发服务器发起请求了。当你发起这个请求,本地开发服务器要做什么呢?他会根据配置中的target字段的地址,也就是www.your-request-url.com, 拼接上aaa标识符,然后再拼接上/api/material后面的参数,拼成你实际需要请求的地址:www.your-request-url.com/aaa/api/mat… 我们看到这个地址,当然知道,这个不对,因为多了个aaa,这个并不是我们希望的。所以要在 rewrite字段中,将aaa这个标识符,替换为空的字符串。那么这样得到的最终地址就是www.your-request-url.com/api/material 啦

当然这个标识符你不想自定义,想要使用接口中的那个api字段行不行呢,当然也可以,最后不需要rewrite就好了

上面说了一大堆。字有点多,可能也少有人耐心看。下面我就从头到位的说一下具体做法,代码:

import axios from 'axios';
import env from './env';

// env 环境变量,这里必须要判断当前的环境。本地开发,还是在生产、日常、预发等
// 是否是本地环境
const isLocal = env === "LOCAL"
// 本地开发baseURL使用标识符,浏览器会自动添加域名,也就是本地开发服务器的域名
const baseURL = isLocal ? "aaa" : "https://www.your-request-url.com"

axios({
	baseURL,
	url:"api/material",
	method:"GET",
	params:{}
})
.then(res => {
	// 发起请求
})

如果是本地环境,那么上面实际请求的地址就是类似localhost:3000/aaa/api/material 这种地址

下面proxy的配置

{
	proxy:{
		'aaa':{
			target: 'https://www.your-request-url.com',
        	changeOrigin: true,
        	rewrite: (path) => path.replace(/^\aaa/, '')
		}
	}
}

当本地服务器收到localhost:3000/aaa/api/material 的请求,便会将localhost:3000替换为target:www.your-request-url.com 的到 www.your-request-url.com/aaa/api/mat…, 然后再根据rewrite的配置,将aaa替换为空字符串。得到www.your-request-url.com/api/material 并发起请求。请求到的结果,返回给axios。拿到最终结果。

以上是关于vite的配置的一点总结。目前我也有一些问题,仍然没有解决。比如node环境打包脚手架的时候,无法识别#!/usr/bin/env node标识的问题,还有排除依赖中包含lodash和antd的时候,本地启动报错的问题等等。有vite构建问题的项目,暂时我采用了parcel和vite双打包的方式使用,vite来进行本地启动,parcel来进行build。后面如果我有了新的总结,将会更新在这里。也希望有其他的朋友有自己的总结也可以留言,交流