# npm 6.x
npm init @vitejs/app vite-react-ts-antd-starter --template react-ts
# npm 7+, 注意--:
npm init @vitejs/app vite-react-ts-antd-starter -- --template react-ts
新建pages文件夹,在文件夹下新建三个Home.tsx、About.tsx、NotFound.tsx文件:
Home.tsx:
const Home = () => {
return <div>
home pages
</div>
}
export default Home
About.tsx:
const Home = () => {
return <div>
about pages
</div>
}
export default Home
NotFound.tsx:
const Home = () => {
return <div>
404 pages
</div>
}
export default Home
npm i react-router-dom
由于改版本改动较大,所有使用详见文档 reactrouter v6
新建router文件夹,在router文件夹下新建routes.ts:
import Home from "./../pages/Home"
import About from "./../pages/About"
import NotFound from "./../pages/NotFound"
const Routes: any = [
{
path: "/",
element: Home,
},
{
path: "about",
element: About,
},
{
path: "404",
element: NotFound,
},
]
export default Routes;
新建history.ts:
import { createBrowserHistory } from "history";
import React from "react";
import { Router } from "react-router-dom";
export const history = createBrowserHistory();
interface IHistoryRouteProps {
history: typeof history;
}
export const IHistoryRouter: React.FC<IHistoryRouteProps> = ({ history, children }) => {
const [state, setState] = React.useState(
{
action: history.action,
location: history.location
}
);
React.useLayoutEffect(() => {
history.listen(setState);
}, [history]);
return React.createElement(Router, Object.assign({ children, navigator: history }, state));
}
修改main.tsx:
import React from 'react'
import ReactDOM from 'react-dom'
import { history, IHistoryRouter } from './router/history'
import './index.css'
import App from './App'
ReactDOM.render(
<React.StrictMode>
<IHistoryRouter history={history}>
<App />
</IHistoryRouter>
</React.StrictMode>,
document.getElementById('root')
)
修改App.tsx:
import React from "react";
import { useRoutes } from 'react-router-dom';
import Routes from "./router/routes"
const App: React.FC = (): JSX.Element => {
let routes: any = [];
Routes.forEach((route: any, index: number) => {
const Ele = route.element;
const router = {
path: route.path,
element: <Ele />,
};
routes.push(router);
});
const router = useRoutes([...routes]);
return <>{router}</>;
};
export default App;
npm i less -D
安装antd:
npm i antd-mobile
安装antd icon:
npm install -S antd-mobile-icons
按需引入:
npm i vite-plugin-imp -D
详细配置信息,见vite配置文档
修改vite.config.ts:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import vitePluginImp from "vite-plugin-imp"
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
react(),
vitePluginImp(
{
optimize: true,
libList: [
{//antd
libName: "antd-mobile",
libDirectory: "es",
style: (name) => `antd/es/${name}/style`,
}
],
},
),
],
css: {
modules: {//css modules
localsConvention: "camelCaseOnly",
},
preprocessorOptions: {
less: {
javascriptEnabled: true,
}
}
}
})
配置完css Modules后,在下面直接配置代理:
//...
server: {
proxy: {
"/api/": {
target: "/",
changeOrigin: true,
}
}
}
//...
安装typescript版本的node:
npm i @types/node -D
接着直接根据vite文档配置:
// ...
import path from 'path'
//...
export default defineConfig({
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
}
},
//...
然后配置tsconfig中的compilerOptions:
//...
"paths": {
"@/*": ["./src/*"]
}
//...
然后在Home.tsx中测试以上配置:
import { history } from "../router/history";
import { Button } from "antd-mobile";
import { CheckOutline } from "antd-mobile-icons"
import styles from "./Home.module.less"
console.log("%c环境变量:", "color: blue; font-size: 20px;");
console.log(import.meta.env);
const Home = () => {
const handleClick = () => {
history.push("/about");
}
return <div className={styles.home}>
<CheckOutline />
<Button color="primary" onClick={handleClick}>跳转</Button>
<div className={styles.div}>
Hello Indeex</div>
</div>
}
export default Home
Home.module.less:
.Home{
background-color: blueviolet;
.div{
background-color: lightcoral;
}
}
npm i -D postcss-pxtorem
修改vite.config:
plugins: [
react(),
vitePluginImp(
{
optimize: true,
libList: [
{//antd-mobile
libName: 'antd-mobile',
style: (name) => `antd-mobile/es/${name}/style/css.js`
}
],
},
),
],
根目录新建postcss.config.js:
module.exports = {
"plugins": {
"postcss-pxtorem": {
rootValue: 37.5,//对应375稿
propList: ["*"],
selectorBlackList: [".norem"]
}
}
}
在src目录下新建utils文件夹,在utiles文件夹下新建rem.js:
此步骤可直接使用 npm i -S lib-flexible
;
(function(win, lib) {
var doc = win.document;
var docEl = doc.documentElement;
var metaEl = doc.querySelector('meta[name="viewport"]');
var flexibleEl = doc.querySelector('meta[name="flexible"]');
var dpr = 0;
var scale = 0;
var tid;
var flexible = lib.flexible || (lib.flexible = {});
if (metaEl) {
console.warn('将根据已有的meta标签来设置缩放比例');
var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
if (match) {
scale = parseFloat(match[1]);
dpr = parseInt(1 / scale);
}
} else if (flexibleEl) {
var content = flexibleEl.getAttribute('content');
if (content) {
var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
if (initialDpr) {
dpr = parseFloat(initialDpr[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
if (maximumDpr) {
dpr = parseFloat(maximumDpr[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
}
}
if (!dpr && !scale) {
var isAndroid = win.navigator.appVersion.match(/android/gi);
var isIPhone = win.navigator.appVersion.match(/iphone/gi);
var devicePixelRatio = win.devicePixelRatio;
if (isIPhone) {
// iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)) {
dpr = 2;
} else {
dpr = 1;
}
} else {
// 其他设备下,仍旧使用1倍的方案
dpr = 1;
}
scale = 1 / dpr;
}
docEl.setAttribute('data-dpr', dpr);
if (!metaEl) {
metaEl = doc.createElement('meta');
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
if (docEl.firstElementChild) {
docEl.firstElementChild.appendChild(metaEl);
} else {
var wrap = doc.createElement('div');
wrap.appendChild(metaEl);
doc.write(wrap.innerHTML);
}
}
function refreshRem() {
var width = docEl.getBoundingClientRect().width;
//此处以适配范围为1920---5760为例
if (width / dpr < 1920) {
width = 1920 * dpr;
} else if (width / dpr > 5760) {
width = 5760 * dpr;
}
var rem = width / 19.2; //使用时1rem=100px
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}
win.addEventListener('resize', function() {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}, false);
win.addEventListener('pageshow', function(e) {
if (e.persisted) {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}
}, false);
if (doc.readyState === 'complete') {
doc.body.style.fontSize = 12 * dpr + 'px';
} else {
doc.addEventListener('DOMContentLoaded', function(e) {
doc.body.style.fontSize = 12 * dpr + 'px';
}, false);
}
refreshRem();
flexible.dpr = win.dpr = dpr;
flexible.refreshRem = refreshRem;
flexible.rem2px = function(d) {
var val = parseFloat(d) * this.rem;
if (typeof d === 'string' && d.match(/rem$/)) {
val += 'px';
}
return val;
}
flexible.px2rem = function(d) {
var val = parseFloat(d) / this.rem;
if (typeof d === 'string' && d.match(/px$/)) {
val += 'rem';
}
return val;
}
})(window, window['lib'] || (window['lib'] = {}));
然后在main.ts中引入:
//...
import "@/utils/rem";
//...
修改Home.module.less文件检测效果:
.Home{
width: 100%;
background-color: blueviolet;
.div{
width: 375px;
background-color: lightcoral;
}
}
npm i -S axios
可直接在需要的地方引入,也可以自行封装
根目录新增.env文件:
NODE_ENV=development
VITE_APP_NAME=dev-indeex
和和.env.prod文件:
NODE_ENV=production
VITE_APP_NAME=prod-indeex
以修改环境变量名,修改后在src下新增env.d.ts:
interface ImportMetaEnv {
readonly VITE_APP_NAME: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
然后重启npm或yarn,通过:
console.log(import.meta.env.VITE_APP_NAME);
获取自定义的环境名,也可以不修改环境变量名,使用默认的。
以上就可以使用在移动端,如果需要PC端,需要更换antd-mobile UI库并去掉移动端CSS配置。