官方文档:https://cn.vitejs.dev/
为什么选 Vite
-
缓慢的服务器启动
- 缓慢的更新
使用
一个开发服务器,它基于 原生 ES 模块 提供了 丰富的内建功能,如速度快到惊人的 模块热更新(HMR)。
一套构建指令,它使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。
搭建react+vite+ts
yarn create vite vite-project
// 启动项目
yarn dev
//可配置不同环境启动
配置vite.config.ts
export default defineConfig((mode: ConfigEnv): UserConfig => {
const env = loadEnv(mode.mode, process.cwd());
const viteEnv = wrapperEnv(env);
return {
resolve: {
alias: {
"@": resolve(__dirname, "./src"),
},
},
css: {
preprocessorOptions: {
less: {
javascriptEnabled: true,
additionalData: `@import "@/styles/var.less";`,
modifyVars: {
"@primary-color": "#4377FE", //设置antd主题色
},
},
},
},
server: {
host: "0.0.0.0", // 服务器主机名,如果允许外部访问,可设置为"0.0.0.0"
port: viteEnv.VITE_PORT,
open: viteEnv.VITE_OPEN,
cors: true,
// 代理跨域地址配置(官网几种写法)
proxy: {
// 字符串简写写法
'/foo': 'http://localhost:4567',
// 选项写法
'/api': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
},
// 正则表达式写法
'^/fallback/.*': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/fallback/, '')
},
// 使用 proxy 实例
'/api': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
configure: (proxy, options) => {
// proxy 是 'http-proxy' 的实例
}
},
// Proxying websockets or socket.io
'/socket.io': {
target: 'ws://localhost:3000',
ws: true
}
}
},
// plugins
plugins: [
react(),
vitePluginImp({
libList: [
{
libName: "antd",
style: (name) => `antd/es/${name}/style`,
},
],
}),
createHtmlPlugin({
inject: {
data: {
title: viteEnv.VITE_GLOB_APP_TITLE,
},
},
}),
viteEnv.VITE_BUILD_GZIP &&
viteCompression({
verbose: true,
disable: false,
threshold: 10240,
algorithm: "gzip",
ext: ".gz",
}),
],
esbuild: {
pure: [],
},
build: {
outDir: "dist",
minify: "esbuild",
rollupOptions: {
output: {
chunkFileNames: "assets/js/[name]-[hash].js",
entryFileNames: "assets/js/[name]-[hash].js",
assetFileNames: "assets/[ext]/[name]-[hash].[ext]",
},
},
},
};
});
更改main.js
import ReactDOM from "react-dom";
import App from "./App";
import "./index.css";
import "@/language/index"; // 引入多语言配置文件
// 引入redux状态管理
import { PersistGate } from "redux-persist/integration/react";
import { Provider } from "react-redux";
import { store, persistor } from "@/redux";
ReactDOM.render(
,
document.getElementById("root")
);
App.js
import { connect } from "react-redux";
import { HashRouter } from "react-router-dom";
import AuthRouter from "@/router/utils/authRouter";
import Router from "@/router/index";
import useTheme from "@/hooks/useTheme";
import i18n from "i18next";
import "moment/dist/locale/zh-cn";
import { I18nextProvider } from "react-i18next";
const App = (props: any) => {
const { themeConfig } = props;
useTheme(themeConfig);
return (
);
};
const mapStateToProps = (state: any) => state.global;
const mapDispatchToProps = {};
export default connect(mapStateToProps, mapDispatchToProps)(App);
添加eslint/prettier/stylelint
// package.json
"scripts": {
"dev": "vite",
"serve": "vite",
"build:dev": "tsc && vite build --mode development",
"build:test": "tsc && vite build --mode test",
"build:pro": "tsc && vite build --mode production",
"preview": "vite preview",
"lint:eslint": "eslint --fix --ext .js,.ts,.tsx ./src",
"lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,scss,html,md}\"",
"lint:stylelint": "stylelint --cache --fix \"**/*.{less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/"
}
// .eslintrc.js
module.exports = {
extends: [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/jsx-runtime",
"plugin:react-hooks/recommended",
"prettier",
"plugin:prettier/recommended",
],
env: {
browser: true,
commonjs: true,
es6: true,
},
globals: {
process: true,
__dirname: true,
},
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaFeatures: {
jsx: true,
modules: true,
},
sourceType: "module",
ecmaVersion: 6,
},
plugins: ["react", "standard", "promise", "@typescript-eslint"],
settings: {
"import/ignore": ["node_modules"],
react: {
version: "detect",
},
},
rules: {
"no-var": "error",
"no-multiple-empty-lines": ["error", { max: 1 }],
"no-use-before-define": "off",
"prefer-const": "off",
"no-irregular-whitespace": "off",
"@typescript-eslint/no-unused-vars": "warn",
"react-hooks/rules-of-hooks": "off",
"react-hooks/exhaustive-deps": "off",
},
};
// .prettier.js
module.exports = {
printWidth: 130,
tabWidth: 4,
useTabs: true,
semi: true,
singleQuote: false,
trailingComma: "none",
jsxSingleQuote: false,
jsxBracketSameLine: false,
arrowParens: "avoid",
rangeStart: 0,
rangeEnd: Infinity,
requirePragma: false,
insertPragma: false,
proseWrap: "preserve",
htmlWhitespaceSensitivity: "css",
endOfLine: "auto",
};
// .stylelint.js
module.exports = {
extends: [
"stylelint-config-standard",
"stylelint-config-prettier",
"stylelint-config-recess-order",
],
plugins: ["stylelint-less"],
rules: {
indentation: null,
"no-descending-specificity": null,
"property-no-unknown": null,
"no-empty-source": null,
"at-rule-no-unknown": null,
"selector-pseudo-class-no-unknown": [
true,
{
ignorePseudoClasses: ["global", "v-deep", "deep"],
},
],
},
};
react-router-dom 使用v6版本
// 配置路由
import React from "react";
import { Navigate, useRoutes } from "react-router-dom";
import { RouteObject } from "./types";
const metaRouters = import.meta.glob("./modules/*.tsx"); //导入modules 文件夹下所有路由文件
import lazyLoad from "./utils/lazyLoad";
import Layout from "@/layouts/index"; //布局
// * 处理路由
export const routerArray: RouteObject[] = [];
Object.keys(metaRouters).forEach((item) => {
Object.keys(metaRouters[item]).forEach((key: any) => {
routerArray.push(...metaRouters[item][key]);
});
});
export const routes: RouteObject[] = [
{
path: "/",
element: ,
},
{
element: ,
children: [
{
path: "/home",
element: lazyLoad(React.lazy(() => import("@/pages/Home/index"))),
meta: {
requiresAuth: false,
title: "首页",
key: "home",
},
},
],
},
...routerArray,
{
path: "*",
element: ,
},
];
const Routes = () => useRoutes(routes);
export default Routes;
Layout
import { useEffect } from "react";
import { Outlet } from "react-router-dom";
import { Layout } from "antd";
import { connect } from "react-redux";
import LayoutMenu from "./components/Menu";
import LayoutHeader from "./components/Header";
import "./index.less";
const LayoutIndex = (props: any) => {
const { Sider, Content } = Layout;
// 渲染布局可自定义
return (
);
};
const mapStateToProps = (state: any) => state.menu;
const mapDispatchToProps = { };
export default connect(mapStateToProps, mapDispatchToProps)(LayoutIndex);
不同环境变量配置
// .env
VITE_GLOB_APP_TITLE = 'VITE-PROJECT'
# port
VITE_PORT = 3000
VITE_OPEN = true
VITE_REPORT = false
VITE_BUILD_GZIP = false
VITE_DROP_CONSOLE = true
国际化配置
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
i18n.use(initReactI18next).init({
resources: {
en: {
translation: ''" // 对应语言文件
},
zh: {
translation: '',
},
vi: {
translation: '',
},
},
fallbackLng: "zh",
debug: false,
interpolation: {
escapeValue: false,
},
});
export default i18n;