- react 的 router
- rem
- 使用组件库 antd-mobile
react 的 router
先构建页面
-
目录
- components 公用组件
- layout 布局组件
- utils 封装工具 rem request
- pages/views 路由对应的页面
-
移动端是要配置自适应的 rem + 弹性盒
-
淘宝方案
- https://github.com/amfe/lib-flexible
/* 通过js来动态添加rem */ (function(designWidth, maxWidth) { var doc = document, win = window, docEl = doc.documentElement, remStyle = document.createElement("style"), tid; function refreshRem() { var width = docEl.getBoundingClientRect().width; maxWidth = maxWidth || 540; width > maxWidth && (width = maxWidth); var rem = (width * 100) / designWidth; remStyle.innerHTML = "html{font-size:" + rem + "px;}"; } if (docEl.firstElementChild) { docEl.firstElementChild.appendChild(remStyle); } else { var wrap = doc.createElement("div"); wrap.appendChild(remStyle); doc.write(wrap.innerHTML); wrap = null; } //要等 wiewport 设置好后才能执行 refreshRem,不然 refreshRem 会执行2次; refreshRem(); 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 = "16px"; } else { doc.addEventListener( "DOMContentLoaded", function(e) { doc.body.style.fontSize = "16px"; }, false ); } })(375, 750); // 备注: 这里的375本身就应该写成750 ,但是写成750之后,我们设计稿的尺寸要/50,不好算,我就想,除以100更好算,所以我改成了375
-
网易方案
function font() { document.documentElement.style.fontSize = document.documentElement.clientWidth / 3.75 + "px"; } font(); window.onresize = font;
-
阿里 rem
(function(doc, win) { const docEl = doc.documentElement, resizeEvt = "orientationchange" in window ? "orientationchange" : "resize", recalc = function() { const clientWidth = docEl.clientWidth; if (!clientWidth) return; let max = 24; let min = 9.3125; let size = 20 * (clientWidth / 320); size = Math.min(size, max); size = Math.max(size, min); docEl.style.fontSize = size + "px"; console.log(docEl.style.fontSize, "em= ====="); }; if (!doc.addEventListener) return; win.addEventListener(resizeEvt, recalc, false); doc.addEventListener("DOMContentLoaded", recalc, false); })(document, window);
sass
注意: sass 文件之间引用使用@import , 在路径上还得加一个~
@import ‘~/…/…/…/assets/global_style/theme.scss’;
router
import React, { useState, Fragment } from "react";
import "./index.scss";
import { NavLink, BrowserRouter } from "react-router-dom";
function Tabbar() {
const [list] = useState([
//? tabbar 的数据
{
id: 1,
text: "首页",
className: "fa-home",
path: "/home"
},
{
id: 2,
text: "分类",
className: "fa-th-large",
path: "/category"
},
{
id: 3,
text: "购物车",
className: "fa-shopping-cart",
path: "/shopcar"
},
{
id: 4,
text: "我的",
className: "fa-user-o",
path: "/mine"
}
]);
function renderTabbarItem() {
//? 渲染列表函数
return list.map(elm => (
<li key={elm.id}>
{/* <BrowserRouter> */}
<NavLink to={elm.path}>
<div>
<i className={"fa " + elm.className}></i>
</div>
<div>{elm.text}</div>
</NavLink>
{/* </BrowserRouter> */}
</li>
));
}
return (
<Fragment>
<footer>
<ul>{renderTabbarItem()}</ul>
</footer>
{/* <i className="fa fa-home"></i>
<i className="fa fa-user-o"></i>
<i className="fa fa-shopping-cart"></i>
<i className="fa fa-th-large"></i> */}
</Fragment>
);
}
export default Tabbar;
XXX
import React, { useState } from "react";
import { Route, Switch, Redirect, BrowserRouter } from "react-router-dom";
import Home from "../views/home";
import Category from "../views/category";
import Shopcar from "../views/shopcar";
import Mine from "../views/mine";
function RouterComp() {
const [routes] = useState([
{
id: 1,
path: "/home",
component: Home
},
{
id: 2,
path: "/category",
component: Category
},
{
id: 3,
path: "/shopcar",
component: Shopcar
},
{
id: 4,
path: "/mine",
component: Mine
}
]);
function renderRoutes() {
return routes.map(item => (
<Route
key={item.id}
path={item.path}
component={item.component}
exact={item.exact}
></Route>
));
}
return (
//* Route是一个路由展示组件,通过component属性来确定渲染哪一个组件
//* Switch组件一次只渲染一个Route
// * 可以实现类似按需加载组件的作用,可以起到一定的性能优化作用
//* exact 是路由完全匹配
//* Redirect 是重定向组件 from 来源 to 目标 / /home
// <BrowserRouter>
<Switch>
<Redirect from="/" to="/home" exact></Redirect>
{renderRoutes()}
{/* <Router path="/home" component={Home} exact=></Router> */}
</Switch>
//{/* </BrowserRouter> */}
// <Home></Home>
);
}
export default RouterComp;
- react-router 5 提供了两种路由形式 HashRouter BrowserRouter
- HashRouter #/home hashchange 事件 兼容高
- BrowserRouter /home h5 popchange 事件 兼容低
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { BrowserRouter as Router } from "react-router-dom";
// ? react-router 5提供了两种路由形式 HashRouter BrowserRouter
// * HashRouter #/home hashchange事件 兼容高
// * BrowserRouter /home h5 popchange事件 兼容低
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById("root")
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
React 二级路由如何
在路由对应的组件中写入导航和路由展示区域就行了
// src> category > index.js
import React, { useState } from "react";
import "./index.scss";
import { Route, NavLink, Redirect } from "react-router-dom";
import Picture from "./children/picture";
import Text from "./children/text";
import Comment from "./children/comment";
function Category() {
const [navLink] = useState([
{
id: 1,
text: "图片",
path: "/category/picture"
},
{
id: 2,
text: "文字",
path: "/category/text"
},
{
id: 3,
text: "评论",
path: "/category/comment"
}
]);
function renderNavLink() {
return navLink.map(item => {
return (
<NavLink to={item.path} key={item.id}>
{item.text}
</NavLink>
);
});
}
return (
<article className="category-box">
<section className="category-nav">{renderNavLink()}</section>
<section className="category-main">
<h3>列表</h3>
<Redirect from="/category" to="/category/text" exact></Redirect>
<Route path="/category/picture" component={Picture}></Route>
<Route path="/category/text" component={Text}></Route>
<Route path="/category/comment" component={Comment}></Route>
</section>
</article>
);
}
export default Category;
Switch 的作用是可以延续到二级路由中的
编程式导航 ---- 使用 js 来跳转页面
只有使用了Route的组件身上才有路由属性、
我们想在LayOut组件中监听整个项目路由变化情况,但是LayOut组件不是路由组件
1.什么路由组件?
- 由Route组件处理的才是路由组件
- 分析: 如果能够将LayOut组件变成路由组件那就可以了 ?
- 解决: 使用 react-router-dom 提供的 高阶`组件 withRouter , 将LayOut 组件变成伪路由组件
// App.js
import React from 'react';
import './App.scss';
import Layout from './layout/Layout';
import './utils/rem.js'
import { withRouter } from 'react-router-dom'
function App( props ) {
return (
<div className="App">
<Layout {...props}></Layout>
</div>
);
}
export default withRouter(App);
//src> layout > index.js
import React from 'react';
import './App.scss';
import Layout from './layout/Layout';
import './utils/rem.js'
import { withRouter } from 'react-router-dom'
function App( props ) {
return (
<div className="App">
<Layout {...props}></Layout>
</div>
);
}
export default withRouter(App);
// <!-- src> component> tab> index.js -->
import React from 'react';
import './tab.scss';
// import 'antd-mobile/dist/antd-mobile.css';
function Tab ( props ) {
function goBack() {
console.log('goBack')
props.history.go(-1)
}
return (
<header>
{props.goback_flag && <i onClick={goBack}><</i>}
{/* <i><</i> */}
<h3>猫眼电影</h3>
</header>
)
}
export default Tab
使用组件库 antd-mobile
- 下载 组件库
- 安装 antd-mobile -D
% yarn add antd-mobile -D
- 使用#
- 入口页面 (html 或 模板) 相关设置:
引入 FastClick 并且设置 html meta (更多参考 #576)
引入 Promise 的 fallback 支持 (部分安卓手机不支持 Promise)
<!-- public> index.html -->
<!DOCTYPE html>
<html>
<head>
<!-- set `maximum-scale` for some compatibility issues -->
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
/>
<script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></script>
<script>
if ("addEventListener" in document) {
document.addEventListener(
"DOMContentLoaded",
function() {
FastClick.attach(document.body);
},
false
);
}
if (!window.Promise) {
document.writeln(
'<script src="https://as.alipayobjects.com/g/component/es6-promise/3.2.2/es6-promise.min.js"' +
">" +
"<" +
"/" +
"script>"
);
}
</script>
</head>
<body></body>
</html>
- 按需加载
引入 react-app-rewired 并修改 package.json 里的启动配置:
$ npm install react-app-rewired --save-dev
/* package.json */
"scripts": {
- "start": "react-scripts start",
+ "start": "react-app-rewired start",
- "build": "react-scripts build",
+ "build": "react-app-rewired build",
- "test": "react-scripts test --env=jsdom",
+ "test": "react-app-rewired test --env=jsdom",
}
- 然后在项目根目录创建一个 config-overrides.js 用于修改默认配置。
module.exports = function override(config, env) {
// do stuff with the webpack config...
return config;
};
- 使用 babel-plugin-import, babel-plugin-import 是一个用于按需加载组件代码和样式的 babel 插件(原理),现在我们尝试安装它并修改 config-overrides.js 文件。
% yarn add babel-plugin-import -D
const { override, fixBabelImports, addLessLoader } = require("customize-cra");
module.exports = override(
fixBabelImports("babel-plugin-import", {
libraryName: "antd-mobile",
style: true
}),
addLessLoader({
ident: "postcss",
javascriptEnabled: true,
modifyVars: { "@primary-color": "#1DA57A" }
})
);
注意 : antd 默认引入样式是 less,所以需要手动配置为 CSS,配置方法如下:
第一种方法:在 package.json 中配置,这种方法成功的前提是 webpack 里 query 下配置 babelrc:true, 这样就会使用 babelrc 文件中的配置
"babel": {
"presets": [
"react-app"
],
"plugins": [
[
"import",
{
"libraryName": "antd",
"style": "css"
}
]
]
}
- 第二种方法:在 webpack.config.dev 和 webpack.config.prod 中配置:
module: {
strictExportPresence: true,
rules: [
{
oneOf: [
// Process JS with Babel.
{
test: /\.(js|jsx|mjs)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
plugins: [
// 引入样式为 css
// style为true 则默认引入less
['import', { libraryName: 'antd', style: 'css' }],
]
}
}
]
}
]
}