Vue学习 -- router路由

news/2024/7/10 0:35:37 标签: vue, router, history, pushState, hash

使用Vue开发项目,肯定对它的路由系统不会陌生。

学习router

  • 直观感受
  • 概念
  • 区别
    • 优势
    • 缺点
  • History模式
  • hash模式
    • hash即URL中"#"字符后面的部分
    • 继续改造
      • 修改my-router-link
      • 修改main.js

直观感受

页面切换没有A标签那种明显的跳转以及新页面打开时的刷新等

概念

就是只有一个Web页面的应用,只加载单个HTML页面。在用户与应用程序交互时,动态更新该页面的Web应用程序。我们称之为SPA(单页应用程序)

区别

1、传统多页面程序:每次请求服务器返回的都是一个完整的页面

2、 单页应用程序:只有第一次会加载页面, 以后的每次请求, 仅仅是获取必要的数据.然后, 由页面中js解析获取的数据, 展示在页面中

优势

1 减少了请求体积,加快页面响应速度,降低了对服务器的压力

2 更好的用户体验,让用户在web app感受native app般的流畅

缺点

因为技术使用了ajax,导致页面不利于SEO,但是可以通过其他技术去规避

(SEO原则:搜索引擎的蜘蛛只识别href的一般超链接,而不识别JavaScript代码,遇到一般超链接就会爬进去,遇到JavaScript不会爬进去。即,搜索引擎抓不到AJAX动态加载的内容。)

History模式

HTML5 History API提供了一种功能,能让开发人员在不刷新整个页面的情况下修改站点的URL,就是利用 history.pushState API 来完成 URL 跳转而无须重新加载页面;

vuerouterlearn_30">新建vue-router-learn

自取的demo名称,具体demo地址

目录结构

vue-router-learn
├── README.md
├── .gitignore
├── node_modules
├── index.html
├── package.json
├── src
│ ├── main.js
│ └── pages
└── webpack.config.js

想知道这样的目录树如何自动生成,戳这里 安装tree

index.html

<body>
  <div id="app">
    {{ message }}
  </div>
  <!-- 注意这里的  script地址 -->
  <script src="/dist/build.js"></script>
</body>

大家可能好奇,刚才的根目录上也没有 dist 文件啊?不要着急往下看

package.json

{
  "name": "vue-router-learn",
  "private": true,
  "scripts": {
    "dev": "webpack-dev-server --inline --hot --open",
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules",
    "predeploy": "npm run build"
  },
  "dependencies": {
    "vue": "^2.3.3"
  },
  "devDependencies": {
    "babel-core": "^6.0.0",
    "babel-loader": "^7.0.0",
    "babel-preset-env": "^1.6.1",
    "cross-env": "^4.0.0",
    "css-loader": "^0.28.1",
    "file-loader": "^0.11.1",
    "surge": "^0.19.0",
    "vue-loader": "^12.0.3",
    "vue-template-compiler": "^2.3.3",
    "webpack": "^2.5.1",
    "webpack-dev-server": "^2.4.5"
  }
}
//下载所有依赖
cnpm i 
//启动项目执行 
npm run dev

这里会用到webpack-dev-server ,其中有这么一句解释:

It uses webpack-dev-middleware under the hood, which provides fast in-memory access to the webpack assets.

main.js

import Vue from 'vue'

const routes = {
  '/': 'Home',
  '/about': 'About'
}
var app = new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue!',
      currentRoute: window.location.pathname
    },
    computed: {
      ViewComponent () {

        const matchingView = routes[this.currentRoute];

        return matchingView
        ? require('./pages/' + matchingView + '.vue')
        : require('./pages/404.vue')

      }
    },
    render (h) { return h(this.ViewComponent) }
  })

其中如果不借助 webpack 直接使用import会报错:

Cannot use import statement outside a module

因为import 是es6的语法,而浏览器并不支持es6语法,当然你也可以在script 上添加type=“model”,或者频繁的使用script

<script src="./node_modules/vue/dist/vue.js" ></script>
 <script src="./src/main.js" ></script>

但显然不是我们想要的。所以还是借助webpack比较好

pages 存放页面

目前有三个页面 404、home、about

//404页
<template>
  <h1>这是404</h1>
</template>

//home 页
<template>
  <section>
    <nav>
      <a href="/">home页</a>
      <a href="/about">about页</a>
    </nav>
    <h1>这是home页</h1>
  </section>
</template>

//about 页
<template>
  <section>
    <nav>
      <a href="/">home页</a>
      <a href="/about">about页</a>
    </nav>

    <h1>这是about页</h1>
  </section>
</template>

效果

在这里插入图片描述
这是我们已经完成了一个简易的页面切换,当点击home/about 按钮时,URL会发生改变,然后通过window.location.pathname获得对应的router,最后通过render展示对应的组件,不太了解main.js写法的可以戳这里–看template部分

继续改造

但是他照样是通过A标签跳转的,并且每次都会刷新页面,所以咱们还需要借助 history.pushState API

routerjs_188">router.js

export default {
  '/': 'Home',
  '/about': 'About'
}
//main.js   
//顶部加一行 import 引入 router文件
//保持不变
import routes from './router'

routerlink_204">新建my-router-link

仿照router路由,新建一个my-router-link组件

<template>
  <a
    :href="to"
    :class="[{ active: isActive },'a']"
    @click="go"
  >
    <slot></slot>
  </a>
</template>

<script>
  import routes from '../router'

  export default {
    props: {
      to: {
        type:String,
        default:''
      }
    },
    computed: {
      isActive () {
        return this.to === this.$root.currentRoute
      }
    },
    methods: {
      go (event) {
        event.preventDefault()
        this.$root.currentRoute = this.to
        window.history.pushState(
          null,
          routes[this.to],
          this.to
        )
      }
    }
  }
</script>

<style scoped>
  .a{
    color: #333;
  }
  .active {
    color: cornflowerblue;
  }
</style>

routerlink_255">引用my-router-link

// home页面

<template>
  <section class="about-wrap">
    <nav>
      <my-router-link to="/">home页</my-router-link>
      <my-router-link to="/about">about页</my-router-link>
    </nav>

    <h1>这是home页</h1>
  </section>
</template>

<script>
import MyRouterLink from '../components/my-router-link.vue'
export default {
  components:{
    MyRouterLink
  }
}
</script>

//about 页面同上
//只是把h1标签内容替换成 这是about页,方便切换页面时,有直观区别

效果

在这里插入图片描述
可惜csdn 没办法添加动效,所以看着和上一张效果图一样,其实是有区别大,我在这里先解释一下,感兴趣的小伙伴自己运行一下就知道了。

区别 这时点击tab按钮,URL改变,页面切换,但是不会像以前一样刷新了。

historypushState_289">history.pushState

每执行一次都会增加一条历史记录,一共接收3个参数

history.pushState(data,title,url)

  • data:要设置的history.state的值,可以是任意类型的值,可根据此值进行判断执行想要的操作。

  • title:现在大多数浏览器不支持或者忽略这个参数,最好用null代替。

  • url:地址栏的值,若不需要可用空来代替。

historyreplaceState_300">history.replaceState(扩展知识)

replaceState()是用来修改当前的历史记录(history实体),而不是创建一个新的历史记录,所以当执行完history.replaceState()后,点击返回按钮照样会返回上一个一面。

当需要更新一个state对象或者当前history实体时,可以用replaceState()来实现。

popstate 监听

其实到这里添加路由已经差不多了,但是点击浏览器返回时,会发现页面没有反应,这里还需要对路由进行一下监听

改造后的main.js

//main.js
import Vue from 'vue'
import routes from './router'


var app = new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue!',
      currentRoute: window.location.pathname
    },
    computed: {
      ViewComponent () {

        const matchingView = routes[this.currentRoute];

        return matchingView
        ? require('./pages/' + matchingView + '.vue')
        : require('./pages/404.vue')

      }
    },
    render (h) { return h(this.ViewComponent) }
  })

  window.addEventListener('popstate', () => {
  	//当点击浏览器返回按钮时,更新currentRoute,改变引用的pages页面
    app.currentRoute = window.location.pathname;
  })

history_404_341">history 刷新404问题

因为刷新是实实在在地去请求服务器的,history模式最终的路由都体现在url的pathname中,这部分是会传到服务器端的,因此需要服务端对每一个可能的path值都作相应的映射

// -e filename 如果 filename存在,则为真
location /{
    root   /**/**/文件目录;
    index  index.html index.htm;
    if (!-e $filename) {
        rewrite ^/(.*) /index.html last;
        break;
    }
}

hash_356">hash模式

hashURL_357">hash即URL中"#"字符后面的部分

1、使用浏览器访问网页时,如果网页URL中带有hash,页面就会定位到id(或name)与hash值一样的元素的位置;

2、hash还有另一个特点,它的改变不会导致页面重新加载;

3、hash值浏览器是不会随请求发送到服务器端的;

4、通过window.location.hash属性获取和设置hash值。

window.location.hash值的变化会直接反应到浏览器地址栏(#后面的部分会发生变化),同时,浏览器地址栏hash值的变化也会触发window.location.hash值的变化,从而触发onhashchange事件。

继续改造

hashhistory 主要修改路由部分,其他文件都可以保持不变,因此咱们复用上边的项目,加以修改

routerlink_371">修改my-router-link

//只需要修改 go 方法,将原有 History 部分注释掉
methods: {
  go (event) {
    event.preventDefault()
    this.$root.currentRoute = this.to
    
    //History 模式
    // window.history.pushState(
    //   null,
    //   routes[this.to],
    //   this.to
    // )

    //Hash模式
    window.location.hash =  this.to

  }
}

修改main.js

//注释掉History部分,添加hashchange 方法
 //History 模式
 // window.addEventListener('popstate', () => {
 //   app.currentRoute = window.location.pathname;

 // })

 //Hash模式
 window.addEventListener('hashchange',(e) =>{
   let pathname = e.newURL.split('/#')[1]
   app.currentRoute = pathname;
 },false);

1、当URL的片段标识符更改时,将触发hashchange事件(跟在#符号后面的URL部分,包括#符号)

2、hashchange事件触发时,事件对象会有hash改变前的URL(oldURL)和hash改变后的URL(newURL)两个属性

具体效果就不截图了,和上边实现的效果一样。


http://www.niftyadmin.cn/n/1558187.html

相关文章

iOS微信内存监控

作者&#xff1a;杨津&#xff0c;腾讯移动客户端开发 高级工程师商业转载请联系腾讯WeTest获得授权&#xff0c;非商业转载请注明出处。 原文链接&#xff1a;http://wetest.qq.com/lab/view/367.html WeTest 导读 目前iOS主流的内存监控工具是Instruments的Allocations&#…

Mac 代替Xshell ,超级好用的FinalShell

XShell很好用&#xff0c;Windows下用的顺手的人太多。但是Mac下XShell 无法使用&#xff0c;这里给大家推荐两款。 FileZilla Mac中文版 如果大家不用跳板机的话&#xff0c;建议安装FileZilla就好&#xff0c;简单好用.。 具体安装步骤请戳这里~ FinalShell 不用说大家…

js学习 - 算法(一)

题目&#xff1a; 根据一个总数&#xff0c;获知有多少种相邻数相加&#xff0c;等于该总数的可能&#xff0c;例&#xff1a; 总数&#xff1a; 21 符合要求&#xff1a; 12345626 67826 1011 分析 既然是相邻数相加&#xff0c;等于该总数&#xff0c;证明其 最大相邻数…

SqlConnection字符串和存储的对象

字符串&#xff1a;string connString"Data Source.;Initial Catalogtrip;Persist Security InfoTrue;User IDsa;Password123456"; 对象&#xff1a;SqlConnectionStringBuilder csbuidler new SqlConnectionStringBuilder(); csbuidler.DataSource ".";…

微信小程序 -- 调起设置界面

开发微信小程序时&#xff0c;经常会遇到授权问题&#xff0c;这时便会有两种情况&#xff1a; 1、拒绝授权 2、同意授权 拒绝授权问题 同意授权自然没有啥问题&#xff0c;这里主要讲一下&#xff0c;拒绝授权的问题。 拒绝授权后&#xff0c;我们再次触发授权&#xff0c;…

【转】标准C++类std::string的内存共享和Copy-On-Write技术

1、 概念 Scott Meyers在《More Effective C》中举了个例子&#xff0c;不知你是否还记得&#xff1f;在你还在上学的时候&#xff0c;你的父母要你不要看电视&#xff0c;而去复习功课&#xff0c;于是你把自己关在房间里&#xff0c;做出一副正在复习功课的样子…

微信小程序 -- 授权地理位置

获取地理位置需求&#xff0c;应该说是一个不太常用&#xff0c;但是非常常见的一个功能。 所以今天以小程序为例&#xff0c;一起学习一下&#xff0c;看看都有哪些方面需要注意。 授权地理位置配置manifest.jsonmanifest.json视图manifest.json源码授权请求authorize获取经纬…

Java线程池的选择

在java的concurrent.Executors主要提供两种线程池&#xff1a;无固定线程数但有限制任务队列的cachedThreadPool与有固定线程数但无任务队列限制的fixedThreadPool&#xff0c;这两种线程池是在小任务里面使用是正常的&#xff0c;但是一旦任务增大或者代码逻辑有问题&#xff…