0%

Vue学习笔记(四)- Vue Router

4 Vue Router

4.1 认识路由

  • 说起路由你想起了什么?

    • 路由是一个网络工程里面的术语。
    • 路由( routing )就是通过互联的网络把信息从源地址传输到目的地址的活动. –维基百科额
  • 啥玩意?没听懂

    • 在生活中,我们有没有听说过路由的概念呢?当然了,路由器嘛.口路由器是做什么的?你有想过吗?

    • 路由器提供了两种机制:路由和转送

      • 路由是决定数据包从来源到目的地的路径
      • 转送将输入端的数据转移到合适的输出端.
    • 路由中有一个非常重要的概念叫路由表.

      • 路由表本质上就是一个映射表,决定了数据包的指向

后端路由阶段

  • 早期的网站开发整个HTML是由服务器进行渲染的

    • 服务器直接生产渲染好对应的HTML页面,返回给客户端进行展示
  • 但是,一个网站,这么多页面服务器如何处理呢?

    • 一个页面有自己对应的网址,也就是URL
    • URL会发送到服务器,服务器会通过正则对该URL进行匹配,并且最后交给一个controller进行处理
    • controller进行各种处理, 最终生成HTML或者数据, 返回给前端
    • 这就完成了一个IO操作
  • 上面的这种操作,就是后端路由

    • 当我们页面中需要请求不同的路径内容时,交给服务器来进行处理,服务器渲染好整个页面,并且将页面返回给客户端
    • 这种情况下渲染好页面, 不需要单独加载任何的js和css, 可以直接交给浏览器展示,这样也有利于SEO的优化
  • 后端路由的缺点

    • 这个页面的编写都是由后端人员来编写维护的
    • 另一种情况是前端开发人员想要开发页面,需要学习一门后端语言
    • 通常情况下HTML代码和后端逻辑代码混在一起,非常混乱

前端路由阶段

  • 随着Ajax的出现,有了前后端分离的开发模式.
  • 后端只提供API来返回数据,前端通过Ajax获取数据,并且可以通过JavaScript将数据渲染到页面中.
  • 这样做最大的优点就是前后端责任的清晰,后端专注于数据上,前端专注于交互和可视化上口并且当移动端(iOS/Android)出现后,后端不需要进行任何处理,依然使用之前的一套API即可.
  • 目前很多的网站依然采用这种模式开发.

单页面富应用阶段

  • 其实SPA应用最主要的特点就是在前后端分离的基础上加了一层前端路由
  • 也就是前端来维护一套路由规则
  • 前端路由的核心是什么呢?改变URL,但是页面不进行整体的刷新

4.2 认识vue-router

  • 目前前端流行的三大框架,都有自己的路由实现: Angular的ngRouter, React的ReactRouter, Vue的vue-router

  • 当然,我们的重点是vue-router

    • vue-router是vue.js的官方路由插件,它和vue.js是深度集成的,适合用于构建单页面应用
    • 我们可以访问其官方网站对齐进行学习
  • vue-router是基于路由和组件的

    • 路由用于设定访问路径,将路径和组件映射起来
    • 在vue-router的单页面应用中,页面的路径的改变就是组件的切换

4.3 安装vue-router

安装和使用vue-router主要有以下步骤:

因为我们已经学习了webpack,后续开发中我们主要是通过工程化的方式进行开发的.所以在后续,我们直接使用npm来安装路由即可.


  • 步骤一:安装vue-router

    npm install vue-router --save

  • 步骤二:在模块化工程中使用它(因为是一个插件,所以可以通过Vue.use)来安装路由功能

    • 第一步:导入路由对象,并且调用Vue.use(VueRouter)
    • 第二步:创建路由实例,并且传入路由映射配置
    • 第三步:在Vue实例中挂载创建的路由实例

router/index.js

// 配置路由相关信息
import VueRouter from 'vue-router'
import Vue from 'vue'

// 1. 通过Vue.use(插件),安装插件
Vue.use(VueRouter)

// 2. 创建VueRouter对象
const routes = [

]
const router = new VueRouter({
// 配置路由和组件之间的映射关系
routes
})

// 3.将router对象传入到Vue实例
export default router

main.js

import router from './router'
。。。

new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')

4.4 使用vue-router的步骤

  1. 创建路由组件

    home.vue

    <template>
    <div class="home">
    <h2>我是首页</h2>
    <p>我是首页内容,嘿嘿嘿</p>
    </div>
    </template>
    <script>
    export default {
    name: "home"
    }
    </script>

    about.vue

    <template>
    <div class="about">
    <h2>我是关于</h2>
    <p>我是关于内容,哈哈哈</p>
    </div>
    </template>

    <script>
    export default {
    name: "about"
    }
    </script>
  2. 配置路由映射:组件和路径映射关系

    import Home from '../components/Home'
    import About from '../components/About'

    Vue.use(VueRouter)

    const routes = [
    {
    path: '/home',
    component: Home
    },
    {
    path: '/about',
    component: About
    }
    ]
  3. 使用路由:通过

<template>
<div id="app">
<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
<router-view></router-view>
</div>
</template>

在路由切换时,切换的是<router-view>挂载的组件,其他内容不会发生改变

将默认的hash模式改为history模式

我们前面说过改变路径的方式有两种:

  • URL的hash
  • HTML5的history
  • 默认情况下,路径改变使用的是URL的hash

如果希望使用HTML的history模式,在vue-router的配置中加入如下配置即可:

// router/index.js
const router = new VueRouter({
// 配置路由和组件之间的映射关系
routes,
mode: 'history' // 指定使用history模式
})

router-link的补充

  • 在前面的<router-link>中,我们只用了一个属性:to, 用于跳转的路径
  • <router-link>还有一些其他属性:
    • tag: tag可以指定<router-link>之后渲染成什么组件,比如上面的代码会被渲染成一个<li>,而不是<a>
    • replace: replace不会留下history记录, 所以指定replace的情况下,后退键不能反悔到上一个页面中
    • active-class: 当<router-link>对应的路由匹配成功时,会自动给当前元素设置一个router-link-active的class, 设置active-class可以修改默认的名称
      • 在进行高亮显示的导航菜单或者底部tabbar时,会使用到该类
      • 但是通常不会修改类的属性,会直接使用默认的router-link-active即可

4.5 动态路由

在某些情况下,一个页面的path路径可能是不确定的,比如我们进入用户界面时,希望是如下的路径:

  • /user/aaaa 或 /user/bbbb
  • 除了有前面的/user之外,后面还跟上了用户的ID
  • 这种path和component的匹配关系,我们称之为动态路由(也是路由传递数据的一种方式)

router/index.js

{
path: '/user/:userId',
component: User
}

app.vue

<router-link :to="/user/ + userId" tag="button">用户</router-link>

user.vue

<template>
<div>
<h2>我是用户界面</h2>
<p>我是用户的相关信息,嘿嘿嘿</p>
<p>userId: {{userId}}</p>
</div>
</template>

<script>
export default {
name: 'User',
computed: {
userId () {
return this.$route.params.userId
}
}
}
</script>

显示效果:

image-20200212220917826

4.6 vue-router打包文件及路由懒加载

vue-router打包文件解析

使用npm run build命令可以使文件打包,打包完成后会在当前目录下生成一个dist目录

在dist目录下有一个js目录,内容如下:

appXXX: 当前应用程序开发的所有代码(业务代码)
manifestXXX: 为了打包的代码做底层支撑
vendorXXX: 第三方提供的包,比如vue/vue-router/axios

路由懒加载

官方解释:

  • 当打包构建应用时,JavaScript包会变得非常大,影响页面加载
  • 如果我们能把不同的路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了

官方在说什么呢:

  • 首先我们知道路由中通常会定义很多不同的页面
  • 这个页面最后被打包在哪里呢?一般情况下是放在一个js文件中
  • 但是,页面这么多,放在一个js文件中,必然会造成这个页面非常的哒
  • 如果我们一次性从服务器请求下来这个页面,可能需要花费一定的时间,甚至用户的电脑上海出现了短暂的空白情况
  • 如何避免这种情况呢?使用路由懒加载就可以了

路由懒加载做了什么?

  • 路由懒加载主要作用个就是将路由对应的组件打包成一个个的js代码块
  • 只有在这个路由被访问到的时候,才加载对应文件

路由懒加载的效果

在使用懒加载之前,引用后直接使用:

const routes = [
{
path: '/',
redirect: '/home'
},
{
path: '/home',
component: Home
},
{
path: '/about',
component: About
},
{
path: '/user/:userId',
component: User
}
]

打包出来的文件:

image-20200212225125036

采用懒加载方式编写:

const routes = [
{
path: '/',
redirect: '/home'
},
{
path: '/home',
component: () => import('../components/Home')
},
{
path: '/about',
component: () => import('../components/About')
},
{
path: '/user/:userId',
component: () => import('../components/User')
}
]

打包出来的文件:

image-20200212225838940

懒加载的几种方式

方式一:结合Vue的异步组件和Webpack的代码分析:

const Home = resolve => { require.ensure(['../componts/Home.vue'], () => 
{ resolve(require('../components/Home.Vue')) }) };

(代码冗长繁琐不推荐)

方式二: AMD写法

const About = resolve => require(['../components/About.vue'], resolve);

方式三:在ES6中,我们可以有更加简单的写法来组织Vue异步组件和WebPack的代码分割

const Home = () => import('../components/Home.vue')

4.7 路由嵌套

嵌套路由是一个很常见的功能

  • 比如在home中,我们希望通过/home/news/home/message访问一些内容
  • 一个路径映射一个组件,访问这两个路径也会分别渲染两个组件

实现嵌套路由的两个步骤

  • 创建对应的子组件,并且在路由映射中配置对应的子路由

    {
    path: '/home',
    component: () => import('../components/Home'),
    children: [
    {
    path: 'news',
    component: () => import('../components/HomeNews')
    },
    {
    path: 'message',
    component: () => import('../components/HomeMessage')
    }
    ]
    }
  • 在组件内部使用<router-view>标签

<template>
<div class="home">
<h2>我是首页</h2>
<p>我是首页内容,嘿嘿嘿</p>
<router-link to="/home/news">新闻</router-link>
<router-link to="/home/message">消息</router-link>
<router-view></router-view>
</div>
</template>

4.8 vue-router路由的参数传递

传递参数的方式

传递参数主要有两种类型:params和query

params的类型

  • 配置路由格式: /router/:id

    {
    path: '/user/:userId',
    component: () => import('../components/User')
    }
  • 传递的方式: 在path后面跟上对应的值

    <router-link :to="/user/ + userId" tag="button">用户</router-link>
  • 传递后形成的路径:/route/123, /router/abc

    <template>
    <div>
    <h2>我是用户界面</h2>
    <p>我是用户的相关信息,嘿嘿嘿</p>
    <p>userId: {{userId}}</p>
    </div>
    </template>

    <script>
    export default {
    name: 'User',
    computed: {
    userId () {
    return this.$route.params.userId
    }
    }
    }
    </script>

query的类型

  • 配置路由格式:/router,也就是普通配置

    {
    path: '/profile',
    component: () => import('../components/Profile')
    }
  • 传递的方式:对象中使用query的key作为传递方式

    <router-link :to="{path: 'profile', query: {name: 'freedom', age: 18}}" tag="button">档案</router-link>
  • 传递后形成的路径:/router?id=123,/router?id=abc

    <template>
    <div>
    <h2>我的档案</h2>
    <p>{{$route.query.name}}</p>
    <p>{{$route.query.age}}</p>
    </div>
    </template>

$route和$router的区别

  • $router为VueRouter实例,想要导航到不同URL,则使用$router.push方法
  • $route为当前router跳转对象,里面可以获取name、path、query、params等

4.9 导航守卫

我们来考虑一个需求:在一个SPA应用中,如何改变网页的标题呢?

  • 网页标题是通过来显示的,但是SPA只有一个固定的HTML,切换不同的页面时,标题并不会改变</li> <li>但是我们可以通过JavaScript来修改<title>的内容.window.document.title =新的标题.</li> <li>那么在Vue项目中,在哪里修改?什么时候修改比较合适呢?</li> </ul> <p>普通的修改方式:</p> <ul> <li><p>我们比较容易想到的修改标题的位置是每一个路由对应的组件vue文件中.</p> </li> <li><p>通过mounted声明周期函数,执行对应的代码进行修改即可.</p> </li> <li><p>但是当页面比较多时,这种方式不容易维护(因为需要在多个页面执行类似的代码).</p> </li> </ul> <p>有没有更好的办法呢?使用导航守卫即可</p> <ul> <li>vue-router提供的导航守卫主要用来监听监听路由的进入和离开的</li> <li>vue-router提供了beforeEach和afterEach的钩子函数,他们会在路由即将改变钱和改变后触发</li> </ul> <p>我们可以利用beforeEach来完成标题的修改</p> <ul> <li><p>首先,我们可以在钩子当中定义一些标题,可以利用meta来定义</p> <figure class="highlight javascript"><table><tr><td class="code"><pre><code class="hljs javascript">{<br> <span class="hljs-attr">path</span>: <span class="hljs-string">'/user/:userId'</span>,<br> <span class="hljs-attr">meta</span>: {<br> <span class="hljs-attr">title</span>: <span class="hljs-string">'用户'</span><br> },<br> <span class="hljs-attr">component</span>: <span class="hljs-function">() =></span> <span class="hljs-title function_">import</span>(<span class="hljs-string">'../components/User'</span>)<br> },<br> {<br> <span class="hljs-attr">path</span>: <span class="hljs-string">'/profile'</span>,<br> <span class="hljs-attr">meta</span>: {<br> <span class="hljs-attr">title</span>: <span class="hljs-string">'档案'</span><br> },<br> <span class="hljs-attr">component</span>: <span class="hljs-function">() =></span> <span class="hljs-title function_">import</span>(<span class="hljs-string">'../components/Profile'</span>)<br> }<br></code></pre></td></tr></table></figure> </li> <li><p>其次,利用个导航守卫修改我们的标题</p> <figure class="highlight javascript"><table><tr><td class="code"><pre><code class="hljs javascript">router.<span class="hljs-title function_">beforeEach</span>(<span class="hljs-function">(<span class="hljs-params">to, <span class="hljs-keyword">from</span>, next</span>) =></span> {<br> <span class="hljs-variable language_">document</span>.<span class="hljs-property">title</span> = to.<span class="hljs-property">meta</span>.<span class="hljs-property">title</span><br> <span class="hljs-title function_">next</span>()<br>})<br></code></pre></td></tr></table></figure> <blockquote> <p>notes: 导航钩子的三个参数解析</p> <ul> <li>to: 即将要进入的目标的路由对象</li> <li>from: 当前导航即将要离开的路由对象</li> <li>next:调用该方法后,才能进入下一个钩子</li> </ul> </blockquote> </li> </ul> <p><strong>导航守卫补充</strong></p> <p>补充一: 如果是后置钩子,也就是afterEach,不需要主动调用next()</p> <p>补充二: 上面我们使用的导航守卫,被称之为全局守卫,除此之外还有:</p> <ul> <li>路由独享的守卫</li> <li>组件内的守卫</li> </ul> <p>具体内容详见官网说明:<a target="_blank" rel="noopener" href="https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E7%BB%84%E4%BB%B6%E5%86%85%E7%9A%84%E5%AE%88%E5%8D%AB">https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#组件内的守卫</a></p> <h2 id="4-9-keep-alive相关问题"><a href="#4-9-keep-alive相关问题" class="headerlink" title="4.9 keep-alive相关问题"></a>4.9 keep-alive相关问题</h2><p><code>keep-alive</code>是Vue的一个内置组件,可以保留组件的状态,当组件被切换时不被销毁,避免重新渲染</p> <p><code>router-view</code>也是一个组件,如果直接被包在keep-alive里面,所有路径匹配到的视图组件都会被缓存</p> <figure class="highlight html"><table><tr><td class="code"><pre><code class="hljs html"><span class="hljs-tag"><<span class="hljs-name">keep-alive</span> <span class="hljs-attr">exclude</span>=<span class="hljs-string">"profile,user"</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">router-view</span>></span><span class="hljs-tag"></<span class="hljs-name">router-view</span>></span><br><span class="hljs-tag"></<span class="hljs-name">keep-alive</span>></span><br></code></pre></td></tr></table></figure> <blockquote> <p> 注:如果想排除某些组件,可以在keep-alive后面加上exclude参数,这样对应的组件就不会被缓存了</p> </blockquote> <p>keep-alive两个非常重要的属性:</p> <ul> <li>include: 字符串或者正则表达式,只有匹配的组件会被缓存</li> <li>exclude: 字符串或正则表达式,任何匹配的组件都不会被缓存</li> </ul> <h2 id="4-10-TabBar的实现"><a href="#4-10-TabBar的实现" class="headerlink" title="4.10 TabBar的实现"></a>4.10 TabBar的实现</h2><h3 id="4-10-1-大致实现思路"><a href="#4-10-1-大致实现思路" class="headerlink" title="4.10.1 大致实现思路"></a>4.10.1 大致实现思路</h3><ol> <li>如果在下方有一个单独的TabBar组件,你如何封装</li> </ol> <ul> <li><p>自定义TabBar组件,在APP中使用</p> </li> <li><p>让TabBar出于底部,并且设置相关的样式</p> </li> </ul> <ol start="2"> <li>TabBar中显示的内容由外界决定</li> </ol> <ul> <li><p>定义插槽</p> </li> <li><p>flex布局平分TabBar</p> </li> </ul> <ol start="3"> <li>自定义TabBarltem ,可以传入图片和文字</li> </ol> <ul> <li><p>定义TabBarltem ,并且定义两个插槽:图片、文字。</p> </li> <li><p>给两个插槽外层包装div,用于设置样式。</p> </li> <li><p>填充插槽,实现底部TabBar的效果</p> </li> </ul> </div> <div class="followme"> <p>欢迎关注我的其它发布渠道</p> <div class="social-list"> <div class="social-item"> <a target="_blank" class="social-link" href="/atom.xml"> <span class="icon"> <i class="fa fa-rss"></i> </span> <span class="label">RSS</span> </a> </div> </div> </div> <footer class="post-footer"> <div class="post-tags"> <a href="/tags/%E5%89%8D%E7%AB%AF/" rel="tag"># 前端</a> <a href="/tags/Vue/" rel="tag"># Vue</a> </div> <div class="post-nav"> <div class="post-nav-item"> <a href="/2021/06/24/Vue%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%EF%BC%88%E4%B8%89%EF%BC%89/" rel="prev" title="Vue学习笔记(三)-前端模块化"> <i class="fa fa-chevron-left"></i> Vue学习笔记(三)-前端模块化 </a></div> <div class="post-nav-item"> <a href="/2021/06/24/Vue%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%EF%BC%88%E4%BA%94%EF%BC%89/" rel="next" title="Vue学习笔记(五)- Promise和VueX"> Vue学习笔记(五)- Promise和VueX <i class="fa fa-chevron-right"></i> </a></div> </div> </footer> </article> </div> <script> window.addEventListener('tabs:register', () => { let { activeClass } = CONFIG.comments; if (CONFIG.comments.storage) { activeClass = localStorage.getItem('comments_active') || activeClass; } if (activeClass) { let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`); if (activeTab) { activeTab.click(); } } }); if (CONFIG.comments.storage) { window.addEventListener('tabs:click', event => { if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return; let commentClass = event.target.classList[1]; localStorage.setItem('comments_active', commentClass); }); } </script> </div> <div class="toggle sidebar-toggle"> <span class="toggle-line toggle-line-first"></span> <span class="toggle-line toggle-line-middle"></span> <span class="toggle-line toggle-line-last"></span> </div> <aside class="sidebar"> <div class="sidebar-inner"> <ul class="sidebar-nav motion-element"> <li class="sidebar-nav-toc"> 文章目录 </li> <li class="sidebar-nav-overview"> 站点概览 </li> </ul> <!--noindex--> <div class="post-toc-wrap sidebar-panel"> <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#4-Vue-Router"><span class="nav-number">1.</span> <span class="nav-text">4 Vue Router</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#4-1-%E8%AE%A4%E8%AF%86%E8%B7%AF%E7%94%B1"><span class="nav-number">1.1.</span> <span class="nav-text">4.1 认识路由</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-2-%E8%AE%A4%E8%AF%86vue-router"><span class="nav-number">1.2.</span> <span class="nav-text">4.2 认识vue-router</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-3-%E5%AE%89%E8%A3%85vue-router"><span class="nav-number">1.3.</span> <span class="nav-text">4.3 安装vue-router</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-4-%E4%BD%BF%E7%94%A8vue-router%E7%9A%84%E6%AD%A5%E9%AA%A4"><span class="nav-number">1.4.</span> <span class="nav-text">4.4 使用vue-router的步骤</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-5-%E5%8A%A8%E6%80%81%E8%B7%AF%E7%94%B1"><span class="nav-number">1.5.</span> <span class="nav-text">4.5 动态路由</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-6-vue-router%E6%89%93%E5%8C%85%E6%96%87%E4%BB%B6%E5%8F%8A%E8%B7%AF%E7%94%B1%E6%87%92%E5%8A%A0%E8%BD%BD"><span class="nav-number">1.6.</span> <span class="nav-text">4.6 vue-router打包文件及路由懒加载</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-7-%E8%B7%AF%E7%94%B1%E5%B5%8C%E5%A5%97"><span class="nav-number">1.7.</span> <span class="nav-text">4.7 路由嵌套</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-8-vue-router%E8%B7%AF%E7%94%B1%E7%9A%84%E5%8F%82%E6%95%B0%E4%BC%A0%E9%80%92"><span class="nav-number">1.8.</span> <span class="nav-text">4.8 vue-router路由的参数传递</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-9-%E5%AF%BC%E8%88%AA%E5%AE%88%E5%8D%AB"><span class="nav-number">1.9.</span> <span class="nav-text">4.9 导航守卫</span></a></li></ol></li></ol></div> </div> <!--/noindex--> <div class="site-overview-wrap sidebar-panel"> <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person"> <img class="site-author-image" itemprop="image" alt="QinTianJun" src="/images/logo.jpeg"> <p class="site-author-name" itemprop="name">QinTianJun</p> <div class="site-description" itemprop="description">一个菜鸟的成长之路</div> </div> <div class="site-state-wrap motion-element"> <nav class="site-state"> <div class="site-state-item site-state-posts"> <a href="/archives/"> <span class="site-state-item-count">23</span> <span class="site-state-item-name">日志</span> </a> </div> <div class="site-state-item site-state-categories"> <a href="/categories/"> <span class="site-state-item-count">7</span> <span class="site-state-item-name">分类</span></a> </div> <div class="site-state-item site-state-tags"> <a href="/tags/"> <span class="site-state-item-count">8</span> <span class="site-state-item-name">标签</span></a> </div> </nav> </div> <div class="links-of-author motion-element"> <span class="links-of-author-item"> <a href="https://github.com/qtj1215" title="GitHub → https://github.com/qtj1215" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i></a> </span> <span class="links-of-author-item"> <a href="mailto:freedom1215@foxmail.com" title="E-Mail → mailto:freedom1215@foxmail.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i></a> </span> <span class="links-of-author-item"> <a href="https://www.qintianjun.top/atom.xml" title="RSS → https://www.qintianjun.top/atom.xml"><i class="fas fa-rss fa-fw"></i></a> </span> </div> </div> </div> </aside> <div id="sidebar-dimmer"></div> </div> </main> <footer class="footer"> <div class="footer-inner"> <div class="copyright"> © <span itemprop="copyrightYear">2024</span> <span class="with-love"> <i class="fa fa-heart"></i> </span> <span class="author" itemprop="copyrightHolder">QinTianJun</span> </div> <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://pisces.theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Pisces</a> 强力驱动<br> <a href="https://beian.miit.gov.cn/" target="_blank">ICP备案号</a> <a href="https://beian.miit.gov.cn/" target="_blank">沪ICP备2023025470号-1</a> </div> </div> </footer> </div> <script src="/lib/anime.min.js"></script> <script src="/lib/velocity/velocity.min.js"></script> <script src="/lib/velocity/velocity.ui.min.js"></script> <script src="/js/utils.js"></script> <script src="/js/motion.js"></script> <script src="/js/schemes/pisces.js"></script> <script src="/js/next-boot.js"></script> </body> </html>