学习目标
- 单页应用是什么
- 前端路由是什么
- React-router 的简单使用
单页应用
在古老的 web 开发领域,从一个页面跳转到另一个页面,通常会从服务器重新请求对应的页面。比如,我们从一个购物网站的商品列表页 www.shop.com
, 跳转到商品详情页 www.item.com
。
在访问 www.shop.com
时,会请求 shop.html
以及相应的资源,而在访问 www.item.com
时,会请求 item.html
以及相应的资源。这种页面之前的跳转以及资源请求,往往会给用户在页面之间的切换造成割裂感,带来不好的用户体验。而单页应用则是另外一种模型,它会通过访问不同的页面 url ,动态的重写当前页面,来实现页面之间的切换。也就是说,在单页应用的模式中,我们往往只是请求了一次 index.html
以及相应的资源,而页面之间的切换,往往是通过 js 的逻辑进行控制的。
现在常见的开发模式,一般是多页与单页共同使用。而更多的关于单页应用与多页应用的信息,优劣势比较,大家可以自行查阅资料。
路由
路由在不同的领域可能有不同的定义。但是在前端开发领域,我们可以简单的理解为,它是一种保证 url 和页面同步的技术。更进一步说,前端路由,是指保证我们在开发单页应用时,不同的 url 对应不同页面(组件)的技术。
单页应用的 url 与页面同步
打开我们之前的工程,在 src
目录下先建立一个 App.js(因为还没有学 ts ,所以先用 js 来写,下同)。
假设我们拥有两个页面,商品列表页,和商品详情页,所以我们来声明下这两个组件:
const Shop = () => {
return <div>商品列表页</div>;
};
const ShopItem = () => {
return <div>商品详情页</div>;
};
接着我们来写下页面的整体结构,有一个导航列表,点击对应的页面,跳转到对应的页面(此处我们采用哈希 hash 路由)。导航列表下面是对应的不同页面,默认为空。
const App = () => {
return (
<div>
<ul>
<li><a href="#/shop">Shop</a></li>
<li><a href="#/shop-item">ShopItem</a></li>
</ul>
{/* 此处放页面内容 */}
</div>
);
};
之后,我们需要:
- state 来记录当前页面的路由,当路由变化时,触发 App 的 re-render
- 页面内容根据当前的路由 state 来渲染不同的页面内容
- 在跳转到不同的路由时更改路由 state ,来保证路由 state 与 url 是同步的。这里有两种做法:
- 在跳转时将路由 state 更改(a 标签点击事件)
- 利用浏览器的
hashchange
事件,在对应的回调函数里更改 state
这里我们选用第二种(监听 hashchange 事件)方法:
const Page = ({ route }) => {
if (route === '/shop') {
return <Shop />;
} else if (route==='/shop-item') {
return <ShopItem />;
}
return null;
};
const App = () => {
const [route, setRoute] = useState('');
useEffect(() => {
window.addEventListener('hashchange', () => {
const r = window.location.hash.substr(1);
setRoute(r);
});
}, []);
return (
<div>
<ul>
<li><a href="#/shop">Shop</a></li>
<li><a href="#/shop-item">ShopItem</a></li>
</ul>
<Page route={route} />
</div>
);
};
之后,我们更改下入口文件 src/index.tsx
中的引用:
+ import App from './App';
ReactDOM.render(
<React.StrictMode>
- <div>123</div>
+ <App />
</React.StrictMode>,
document.getElementById('root')
);
最后,我们npm start
运行项目。
如此一来,我们就实现了 url 与页面的同步。
但是呢,上面的代码是有一些改进空间的。如果当路由多了之后,我们需要改动的地方有:
- APP 组件中的列表加入新的项目
- Page 组件中增加比较逻辑
为了避免这种情况,造成的维护困难,我们是可以进一步分离出一个路由配置层的(虽然我们之后不大可能还是自己来实现路由,但是此处还是提供一种让代码更健壮的思路):
// 我们将这个路由配置层,称为路由表 routes ,它是一个数组:
const routes = [
{
path: '/shop',
component: Shop,
text: 'Shop',
},
{
path: '/shop-item',
component: ShopItem,
text: 'ShopItem',
}
];
// 抽象一个由 path 获得 hash 路径的方法
const getHashPath = (path) => {
return `#${path}`;
};
const Page = ({ route }) => {
// 将路由表,转换成一个路由表 map ,key 为 path ,value 为 path 对应的路由配置
const routesMap = routes.reduce((res, route) => {
const { path } = route;
res[path] = route;
return res;
}, {});
if (routesMap[route]) {
const Route = routesMap[route].component;
return <Route />;
}
return null;
};
...
<ul>
{
routes.map((route) => {
const { path, text } = route;
return (
<li key={path}>
<a href={getHashPath(path)}>{text}</a>
</li>
);
})
}
</ul>
...
之后,我们再添加路由时,只需要修改路由表即可。
React Router
因为我们选用的技术栈是 React ,而 React 周边生态中有一个非常好用并且广受欢迎的路由库,那就是 React Router。所以我们的路由库,自然就选用的是 React Router。
因为 React Router 配置相关的内容比较多,虽然会简单的说一部分,但是更好的学习方式还是看文档:reactrouter.com/web/guides/…
接着我们来简单过一下常用的几个组件:
<Router>
一个通用的路由器组件,来保持你的UI与URL同步<BrowserRouter>
使用 HTML5 history API 的路由器组件<HashRouter>
使用 url 中哈希部分 的路由器组件(不推荐使用了)<Link>
导航组件<Route>
当 url 路径与配置路径匹配时,呈现对应 UI 的组件<Switch>
渲染第一个被 location 匹配到的子元素的<Route>
或者<Redirect>
接下来是实战环节。
首先,我们需要先安装 React Router :
npm install react-router-dom
接下来我们使用 React Router 配合 HTML5 history API 的形式,来改造下我们之前的例子:
const App = () => {
return (
<div>
<BrowserRouter>
<ul>
{
routes.map((route) => {
const { path, text } = route;
return (
<li key={path}>
<Link to={path}>{text}</Link>
</li>
);
})
}
</ul>
<Switch>
{
routes.map((route) => {
const { path, component } = route;
return <Route key={path} path={path} component={component} exact />;
})
}
</Switch>
</BrowserRouter>
</div>
);
};
其中,我们删除了很多用来保持 url 与 组件匹配的代码,而是利用 React Router 配置来实现。
结语
好了,本期的内容到这里就差不多了,不算难,配置相关的内容会多一些。我个人除了常用的配置外,不是很推荐去死记硬背这些配置内容的,用的时候去翻下文档就好了。
工程地址:github.com/VarHug/stud…
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!