侧边栏壁纸
  • 累计撰写 53 篇文章
  • 累计创建 12 个标签
  • 累计收到 8 条评论

目 录CONTENT

文章目录

React 项目搭建

Kirito
2024-09-09 / 0 评论 / 0 点赞 / 162 阅读 / 16305 字 / 正在检测是否收录...

Vite+React+SCSS+PostCSS+Zustand+React Router+ESlint+Prettier+Ant Design

1.初始化项目

npm create vite@latest <project-name>

2.安装默认依赖

npm install

3.SCSS

npm install sass -D

npm install @types/node

新建styles文件夹,src/styles,新建 base.scssglobal.scss文件

修改main、App样式文件后缀与引用

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import { fileURLToPath, URL } from 'node:url'

// https://vitejs.dev/config/
export default defineConfig({
	plugins: [react()],
	resolve: {
		alias: {
			'@': fileURLToPath(new URL('./src', import.meta.url))
		}
	},
	css: {
		preprocessorOptions: {
			scss: {
				additionalData: '@use "@/styles/global.scss" as *;'
			}
		}
	}
})


4.PostCSS

git:[@github/postcss/postcss]
npm install tailwindcss cssnano autoprefixer postcss-preset-env -D

tailwindcss:本质上是一个工具集,包含了大量类似 flexpt-4text-center 以及 rotate-90 等工具类,可以组合使用并直接在 HTML 代码上实现任何 UI 设计。

cssnano:为你的 CSS 文件做多方面的的优化, 以确保最终生成的文件 对生产环境来说体积是最小的。

autoprefixer:一个后处理CSS的工具,它使用Can I Use的数据来决定给CSS选择器添加什么前缀。这意味着开发者只需编写标准的CSS代码,Autoprefixer会根据目标浏览器的需要自动添加相应的前缀。

postcss-preset-env:整合了很多的常用插件到一起,并帮你完成了基本的配置。

书写配置文件 postcss.config.jstailwind.config.js

// postcss.config.js
export default {
	plugins: {
		autoprefixer: {
			//css兼容前缀
			overrideBrowserslist: [
				'Android 4.1',
				'ios 7.1',
				'Chrome >31',
				'not ie <=11', //不考虑IE浏览器
				'ff >= 30', //仅新版本用'ff >= 30
				'>1%', //全球统计有超过1%的使用了使用'> 1%'
				'last 2 version' //所有主流浏览器最近2个版本
			],
			grid: true //开启grid布局的兼容(浏览器IE除外其它都能兼容grid,可以关闭开启)
		},
		'postcss-import': {},
		cssnano: {},
		'postcss-preset-env': {
			/* use stage 3 features + css nesting rules */
			stage: 3,
			features: {
				'nesting-rules': true
			}
		}
	}
}
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
	content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
	theme: {
		colors: {
			black: '#1f1f1f',
			white: '#f0f0f0'
		},
		extend: {}
	},
	plugins: []
}

引入tailwindcss

// src/styles/base.scss
@tailwind base;
@tailwind components;
@tailwind utilities;

:root,
html,
body {
	margin: 0;
	padding: 0;
	font-size: 16px;
	font-family: 'PingFangSC-Medium';
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

html,
body,
#root {
	display: flex;
	flex-direction: column;
	width: 100%;
	height: 100%;
}

解决VS Code对于 @tailwind报错 Unknown at rule @tailwind

// .vscode/settings.json
{
  "css.customData": [".vscode/tailwindcss.json"]
}
// .vscode/tailwindcss.json
{
  "version": 1.1,
  "atDirectives": [
    {
      "name": "@tailwind",
      "description": "Use the `@tailwind` directive to insert Tailwind's `base`, `components`, `utilities` and `screens` styles into your CSS.",
      "references": [
        {
          "name": "Tailwind Documentation",
          "url": "https://tailwindcss.com/docs/functions-and-directives#tailwind"
        }
      ]
    },
    {
      "name": "@apply",
      "description": "Use the `@apply` directive to inline any existing utility classes into your own custom CSS. This is useful when you find a common utility pattern in your HTML that you’d like to extract to a new component.",
      "references": [
        {
          "name": "Tailwind Documentation",
          "url": "https://tailwindcss.com/docs/functions-and-directives#apply"
        }
      ]
    },
    {
      "name": "@responsive",
      "description": "You can generate responsive variants of your own classes by wrapping their definitions in the `@responsive` directive:\n```css\n@responsive {\n  .alert {\n    background-color: #E53E3E;\n  }\n}\n```\n",
      "references": [
        {
          "name": "Tailwind Documentation",
          "url": "https://tailwindcss.com/docs/functions-and-directives#responsive"
        }
      ]
    },
    {
      "name": "@screen",
      "description": "The `@screen` directive allows you to create media queries that reference your breakpoints by **name** instead of duplicating their values in your own CSS:\n```css\n@screen sm {\n  /* ... */\n}\n```\n…gets transformed into this:\n```css\n@media (min-width: 640px) {\n  /* ... */\n}\n```\n",
      "references": [
        {
          "name": "Tailwind Documentation",
          "url": "https://tailwindcss.com/docs/functions-and-directives#screen"
        }
      ]
    },
    {
      "name": "@variants",
      "description": "Generate `hover`, `focus`, `active` and other **variants** of your own utilities by wrapping their definitions in the `@variants` directive:\n```css\n@variants hover, focus {\n   .btn-brand {\n    background-color: #3182CE;\n  }\n}\n```\n",
      "references": [
        {
          "name": "Tailwind Documentation",
          "url": "https://tailwindcss.com/docs/functions-and-directives#variants"
        }
      ]
    }
  ]
}

5.全局状态管理——Zustand

git:[@github/pmndrs/zustand]
npm install zustand

书写模版Store文件

// src/store/base.ts
import { create } from 'zustand'

type State = {
	counter: number
}

type Action = {
	increment: () => void
	decrement: () => void
}

export const useStore = create<State & Action>((set) => {
	return {
		counter: 0,
		increment: () => set((state) => ({ counter: state.counter + 1 })),
		decrement: () => set((state) => ({ counter: state.counter - 1 }))
	}
})

App组件使用

import { useStore } from './store/base'

function App() {
	const counter = useStore((state) => state.counter)
	const increment = useStore((state) => state.increment)
	const decrement = useStore((state) => state.decrement)

	return (
		<div className='flex flex-col h-full w-full justify-center items-center gap-2'>
			<div className='text-3xl font-bold'>{counter}</div>
			<button className='w-32 px-2 py-1 border-2' onClick={increment}>
				+1
			</button>
			<button className='w-32 px-2 py-1 border-2' onClick={decrement}>
				-1
			</button>
		</div>
	)
}

export default App

6.路由——React Router

git:[@github/remix-run/react-router]
npm install react-router-dom

初始化路由文件

// src/routes/index.tsx
import { lazy } from 'react'
import { createBrowserRouter, RouteObject, RouterProvider } from 'react-router-dom'

const Home = lazy(() => import('@/pages/Home'))

const routes: RouteObject[] = [
	{
		path: '/',
		element: <Home />
	}
]

// 创建路由
const router = createBrowserRouter(routes)

// 导出路由
const Routes = () => <RouterProvider router={router} />

export default Routes

改写 main.tsx文件,书写Home组件

// src/main.tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './styles/base.scss'
import Routes from './routes/index.tsx'

createRoot(document.getElementById('root')!).render(
	<StrictMode>
		<Routes />
	</StrictMode>
)
// src/pages/Home/index.tsx
import { useStore } from '@/store/base'

function Home() {
	const counter = useStore((state) => state.counter)
	const increment = useStore((state) => state.increment)
	const decrement = useStore((state) => state.decrement)

	return (
		<div className='flex flex-col h-full w-full justify-center items-center gap-2'>
			<div className='text-3xl font-bold'>{counter}</div>
			<button className='w-32 px-2 py-1 border-2' onClick={increment}>
				+1
			</button>
			<button className='w-32 px-2 py-1 border-2' onClick={decrement}>
				-1
			</button>
		</div>
	)
}

export default Home

7.代码格式化校验——ESlint、Prettier

npm install prettier eslint-config-prettier eslint-plugin-prettier eslint-plugin-react -D
// .eslintrc.cjs
module.exports = {
  //使用微信的某些能力时,导入min.js后使用/^wx\..*/报错的解决。
  globals: { wx: 'readonly' },
  env: {
    browser: true,
    es2021: true,
    node: true
  },
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:react/recommended', // 'plugin:vue/vue3-essential',
    'plugin:prettier/recommended',
    'plugin:react/jsx-runtime' // 'eslint-config-prettier'
  ],
  overrides: [
    {
      env: {
        node: true
      },
      files: ['.eslintrc.{js,cjs}'],
      parserOptions: {
        sourceType: 'script'
      }
    }
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module'
  },
  plugins: ['@typescript-eslint', 'react', 'prettier'],
  rules: {
    'prettier/prettier': 'error',
    'arrow-body-style': 'off',
    'prefer-arrow-callback': 'off',
    '@typescript-eslint/no-unused-vars': [
      'error',
      { varsIgnorePattern: '^_', argsIgnorePattern: '^_' }
    ]
  },
  settings: {
    react: {
      version: 'detect'
    }
  }
}
// .eslintrc.json
{
	"env": {
		"browser": true,
		"es2021": true
	},
	"extends": [
		"eslint:recommended",
		"plugin:@typescript-eslint/recommended",
		"plugin:react/recommended"
	],
	"parser": "@typescript-eslint/parser",
	"parserOptions": {
		"ecmaVersion": "latest",
		"sourceType": "module"
	},
	"plugins": ["@typescript-eslint", "react", "react-refresh"],
	"rules": {
		"@typescript-eslint/no-unused-vars": [
			"error",
			{ "varsIgnorePattern": "^_", "argsIgnorePattern": "^_" }
		],
		"react-refresh/only-export-components": [
			"warn",
			{ "allowConstantExport": true }
		]
	}
}
// .prettierrc.json
{
  "$schema": "https://json.schemastore.org/prettierrc",
  "semi": false,
  "tabWidth": 2,
  "singleQuote": true,
  "printWidth": 100,
  "trailingComma": "none",
  "endOfLine": "auto"
}

8.UI组件库——AntD

git:[@github/ant-design/ant-design]
npm install antd --save
// src/pages/Home/index.tsx
import { useStore } from '@/store/base'
import { Button } from 'antd'

function Home() {
  const counter = useStore((state) => state.counter)
  const increment = useStore((state) => state.increment)
  const decrement = useStore((state) => state.decrement)

  return (
    <div className="flex flex-col h-full w-full justify-center items-center gap-2">
      <div className="text-3xl font-bold">{counter}</div>
      <div>
        <Button onClick={increment}>+1</Button>
        <Button onClick={decrement}>-1</Button>
      </div>
    </div>
  )
}

export default Home

9.优化篇(非必要、仅参考)

vite.config.ts优化

npm install terser
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import { fileURLToPath, URL } from 'node:url'

// https://vitejs.dev/config/
export default defineConfig({
  server: {
    // dev环境端口号
    port: 8000,
    // 反向代理
    proxy: {
      '/api': {
        target: 'http://127.0.0.1:9000',
        changeOrigin: true,
        rewrite: (path) => path, // 地址转换
        headers: {} // 转发请求头
      }
    }
  },
  plugins: [react()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  },
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: '@use "@/styles/global.scss" as *;'
      }
    }
  },
  build: {
    target: 'modules', //浏览器兼容性modules|esnext
    sourcemap: false, //构建后是否生成source map文件
    minify: 'terser', // 混淆器,terser构建后文件体积更小
    // outDir: envConfig.VITE_OUTPUT_DIR,  //指定输出文件包名
    outDir: 'dist',
    assetsDir: 'assets', // 指定生成静态资源的存放路径
    chunkSizeWarningLimit: 1500, //警报门槛,限制大文件大小B为单位
    assetsInlineLimit: 4096, //小于此阈值的导入或引用资源将内联为base64编码,以避免额外的http请求,设置为0可以完全禁用此项
    // 清除console和debugger(minify: 'terser',)设置后这个terserOptions才有用
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    },
    rollupOptions: {
      output: {
        //自动分割包名输出 chunkSizeWarningLimit 配置大小
        chunkFileNames: 'js/[name]-[hash].js', //入口文件名
        entryFileNames: 'js/[name]-[hash].js', //出口文件名位置
        assetFileNames: 'static/[ext]/[name]-[hash].[ext]', //静态文件名位置
        manualChunks(id) {
          if (id.includes('node_modules')) {
            return id.toString().split('node_modules/')[1].split('/')[0].toString()
          }
        }
      }
    }
  }
})

vite-plugin-inspect

git:[@github/antfu-collective/vite-plugin-inspect]

检查 Vite 插件的中间状态。用于调试和编写插件。

npm i -D vite-plugin-inspect
import Inspect from 'vite-plugin-inspect'

export default defineConfig({
  ...,
  plugins: [
    Inspect({
      build: true,
      outputDir: '.vite-inspect'
    }),
    ...
  ],
  ...,
})

SVG优化

Vite环境下的SVG文件使用

rollup-plugin-visualizer

vite打包可视化工具

git:[@github/btd/rollup-plugin-visualizer]
npm i rollup-plugin-visualizer -D
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
  ...,
  plugins: [
    ...,
    visualizer({
      open: true,
      gzipSize: true,
      brotliSize: true,
      filename: 'buildStats.html'
    }),
    ...
  ],
  ...,
})
0

评论区