<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>前端架构 on Saiga</title>
    <link>http://localhost:1313/categories/%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84/</link>
    <description>Recent content in 前端架构 on Saiga</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <managingEditor>wuwenzen@outlook.com (wuwj)</managingEditor>
    <webMaster>wuwenzen@outlook.com (wuwj)</webMaster>
    <lastBuildDate>Sat, 15 Feb 2020 00:00:00 +0000</lastBuildDate>
    <atom:link href="http://localhost:1313/categories/%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>单页应用路由设计心得：守护好你的「返回键」</title>
      <link>http://localhost:1313/posts/2020-02-15-spa-routing-thoughts/</link>
      <pubDate>Sat, 15 Feb 2020 00:00:00 +0000</pubDate><author>wuwenzen@outlook.com (wuwj)</author>
      <guid>http://localhost:1313/posts/2020-02-15-spa-routing-thoughts/</guid>
      <description>&lt;p&gt;单页应用（SPA）带来了更流畅的交互体验，但也制造了一个经典问题：&lt;strong&gt;「返回键」不好使了。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;用户点浏览器返回，结果直接退回登录页，心态爆炸；&lt;/li&gt;&#xA;&lt;li&gt;或者在一个中台系统里，从列表点到详情、从详情点到编辑、再返回时，筛选条件全没了，只剩一个「空空如也」。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这篇文章就结合我自己踩过的坑，聊聊：&lt;strong&gt;如何在 SPA 里设计更「靠谱」的路由结构和返回行为。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;1-路由不是简单的-url-映射&#34;&gt;1. 路由不是简单的 URL 映射&lt;/h2&gt;&#xA;&lt;p&gt;很多项目的路由配置看起来像这样：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;routes&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  { &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/list&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;component&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;List&lt;/span&gt; },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  { &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/detail/:id&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;component&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Detail&lt;/span&gt; },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;];&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;能跑，但如果你问：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;列表页的筛选条件放哪？&lt;/li&gt;&#xA;&lt;li&gt;返回列表时要不要还原滚动位置？&lt;/li&gt;&#xA;&lt;li&gt;详情页打开方式是「覆盖当前」还是「新标签页」？&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这些问题路由本身没有回答。&lt;br&gt;&#xA;所以对我来说，路由不仅仅是 path → component 的映射，它还应该承载一些&lt;strong&gt;页面行为约定&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;2-基本路由结构从业务模块出发&#34;&gt;2. 基本路由结构：从「业务模块」出发&lt;/h2&gt;&#xA;&lt;p&gt;在中后台项目里，我现在更倾向按「业务模块」来规划路由：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;routes&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/order&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;component&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Layout&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;children&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      { &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;list&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;OrderList&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;component&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;OrderList&lt;/span&gt; },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      { &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;detail/:id&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;OrderDetail&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;component&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;OrderDetail&lt;/span&gt; },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      { &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;edit/:id&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;OrderEdit&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;component&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;OrderEdit&lt;/span&gt; },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;];&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;几个小习惯：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;路径短而语义化&lt;/strong&gt;：&lt;code&gt;/order/list&lt;/code&gt; 比 &lt;code&gt;/orderListPage&lt;/code&gt; 更统一。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;给路由起 name&lt;/strong&gt;：后面做编程式导航、标签页缓存都会用到。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;模块下的子路由聚在一起&lt;/strong&gt;：方便看清一个模块的整体结构。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;3-返回行为列表页的状态保留&#34;&gt;3. 返回行为：列表页的「状态保留」&lt;/h2&gt;&#xA;&lt;p&gt;最常见的场景：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;在订单列表页选择了各种条件，翻到第 4 页；&lt;/li&gt;&#xA;&lt;li&gt;点进某个订单详情；&lt;/li&gt;&#xA;&lt;li&gt;返回时希望停留在「刚才那一页 + 筛选条件」。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;如果不做额外处理，很多 SPA 会出现：&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
