Vue の学習 --- フロントエンド ルーティング


この記事は vue2.x VueRouter3 開発環境 をもとに書いています

ルーティングについて: 動的コンポーネントが切り替わっても、アドレス バーのアドレスは変化せず、ルーティングによってこの欠点を解決できます。

vue-cil スキャフォールディング ツールを通じて作成したプロジェクトはシングルページ アプリケーションです。シングルページ アプリケーションをシングル ページ アプリ、または短縮してスパと呼びます。シングル ページとは、HTML コードが 1 つだけあることを意味します

Vue.js ルーティングを使用すると、異なる URL を介して異なるコンテンツ (異なるコンポーネント) にアクセスでき、単一ページ アプリケーションの複数ページ切り替え効果を実現できます。(URL変更によるコンポーネントの切り替え)

vue-router Vue.js ルーティングはライブラリにロードする必要がありますvue-router 。ライブラリは vue から独立しており、利用できる場合と利用できない場合があります。異なるコンテンツ (異なるコンポーネント) にアクセスするために異なる URL を実現したい場合は、vue-router を使用できます。または vue-router を使用しない場合 動的コンポーネントによって実現できることは、アドレス バーが変更されないこと、またはページ切り替えが実現できることです。

1.vue-routerをインストールする

vue の背後に vue-router コア ファイルをロードすると、自動的にインストールされます。

<script src="/path/to/vue.js"></script>

<script src="/path/to/vue-router.js"></script>

または、npm modular import を使用します (理解しています)。

npm install 'vue-router'

モジュラー プロジェクトで使用する場合は、次Vue.use()の方法でルーティング機能を明示的にインストールする必要があります。

import Vue from 'vue'

import VueRouter from 'vue-router'

グローバル スクリプト タグ (手動でインストール) を使用する場合は必要ありません。

@vue/cli スキャフォールディングに基づいて作成されたプロジェクトの場合、vue create でプロジェクトを作成するときに VueRouter にチェックを入れると、作成されたプロジェクトに VueRouter が自動的に導入されます。

1.1 インストール手順

cmd を開き、プロジェクトを作成するフォルダーの下にプロジェクトを作成します。

vue create vue-router-demo

作成時に選択する必要があります: 機能を手動で選択 --> Babel、Router、CSS プリプロセッサ --> 2.x --> はい --> Sass/SCSS(dart-sass あり) --> package.json 内 - - > はい

2. vue-router ライブラリを含むプロジェクトを作成します

通常のプロジェクトよりルーターフォルダーの数が多い

ルーターフォルダーの下にあるindex.jsファイル

import Vue from 'vue'
// 1.导入vue-router插件
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
// 2.注册vue-router插件
Vue.use(VueRouter)

// 3.定义路由表
const routes = [
  {
    
    
    path: '/',
    name: 'home',
    component: HomeView
  },
  {
    
    
    path: '/about',
    name: 'about',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
  }
]

// 4.实例化路由对象
const router = new VueRouter({
    
    
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})
// 5.导出路由对象
export default router

index.js ファイルは main.js ファイルで使用されます

import Vue from 'vue'
import App from './App.vue'
// 1.导入路由对象
import router from './router'

Vue.config.productionTip = false

new Vue({
    
    
  router,//2.挂载路由对象到根实例上(在整个项目的所有组件中都可以通过组件实例this获取到这个路由对象:this.$router)
  render: h => h(App)
}).$mount('#app')

3. 基本的なルーティング例

2 つのジャンプ リンクの配置は、vue-router プラグインの組み込みコンポーネントです

 <div id="app">
    <nav>
      <!-- router-link 是uve-router插件内置的组件,最终渲染为a标签,用于跳路由 , to表示要跳去往的地址 -->
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </nav>
    <router-view/>
  </div>

4. ルーティングコア

動作の実装プロセス: router-link のアドレスを取得してクリックし、アドレス バーに入力します。次に、そのアドレスと将来切り替えられる可能性のあるアドレスを比較し、等しい場合は、router-link-active を実行します。クラスが自動的に追加され、同時にクラス router-link-exact-activ を通じてハイライト効果が実現されます。

ルーティング関連コンポーネント

router-link ルーティング ライブラリの組み込みコンポーネントは、最終的にクリック可能なタグにレンダリングされ、クリックするとルートにジャンプし、任意のコンポーネントで使用できます。

Router-View ルーティング ライブラリの組み込みコンポーネントは、単なるプレースホルダ コンポーネントです。コンポーネント自体はラベルを表示しません。現在のルーティング アドレスと正常に一致するコンポーネントが Router-View でレンダリングされます。

router-view は動的コンポーネントであり、スペースを占有し、アニメーション コンポーネントでラップすることができます。

ルーティング関連オブジェクト

Vue ルート インスタンスのルーター設定でルーター インスタンスを渡すことにより、次の属性メンバーが各サブコンポーネントに挿入されます。

**ルーティングをスキップできるルータ ルーティング管理オブジェクト、* * このオブジェクトは、任意のコンポーネントのコンポーネント インスタンスを通じて取得できます。 ルーティングをスキップできるルータ ルーティング管理オブジェクト、** 任意のコンポーネントのオブジェクトです。コンポーネントインスタンスを通じて取得されます。ルータールーティング管理オブジェクト_このオブジェクトの助けを借りて、ルートをジャンプすることができますこのルーターのオブジェクトは、任意のコンポーネントのコンポーネント インスタンスを通じて取得でき、そのコア メソッドは次のとおりです

this.$router.push()

this.$router.go()

this.$router.replace()

$route ルーティング パラメータを取得できるルーティング オブジェクト。その中心的なプロパティは次のとおりです。

ルートオブジェクトのプロパティ 説明
$route.path 現在のルートに対応するパス。常に絶対パスとして解決されます。
$route.params 動的フラグメントと完全一致フラグメントを含むキー/値オブジェクト。ルーティング パラメーターがない場合、空のオブジェクトになります。
$route.query URL クエリ パラメータを表すキー/値オブジェクト。たとえば、 paths の場合/foo?user=1、 が存在します$route.query.user == 1。クエリ パラメータがない場合は空のオブジェクトが存在します。
$route.hash 現在のルートのハッシュ値 ( 付き#)、またはハッシュ値がない場合は空の文字列
$route.fullPath 解析が完了した後の URL (クエリ パラメータとハッシュのフル パスを含む)
$ルート名 現在のルートの名前 (存在する場合)
$route.redirectedFrom リダイレクトがある場合は、リダイレクト元のルートの名前

5. ルーティングテーブル

ルーティング テーブルは、index.js ファイルにあります。

オブジェクトはルーティング ルールです

ルーティングテーブルにあらかじめルーティングルールが定義されているため、ルーティングをスキップすることが可能

ルーティング ルールにはルーティング アドレス、ルーティング名があり、ルーティング アドレスに対応するコンポーネントをインポートします。

スキップルーティングが実現できる理由: 根本的な原因は、すでにルーティング テーブルに定義されているためです。ボタンが強調表示されるかどうかは、ジャンプ ルートの有無とは関係ありません。

ルート アドレスに対応するコンポーネントをインポートするには、次の 2 つの方法があります。

1. 非動的インポート。主な特徴は、すべてのコンポーネントが js にパッケージ化され、すべてのコンポーネントが最初にロードされることです。

 // import HomeView from '../views/HomeView.vue'
 component: HomeView//路由地址对应的组件

2. 動的インポート。実際のルートがジャンプされるときにコンポーネントのコンテンツが一時的にロードされます。これは、動的ロード、遅延ロード、または遅延ロードと呼ばれます。

動的インポート: ルートをスキップするタイミングとコンポーネントをロードするタイミング、一時ロード

 component: () => import('../views/HomeView.vue')

サンプルコード

// 3.定义路由表
const routes = [
  {
    
    //一个对象,就是一个路由规则
    path: '/',//路由地址
    name: 'home',//路由名字
    // 路由地址对应的组件有两种写法
    // 第一种:非动态导入,主要特点所有组件会被打包成一个js,一开始就会将所有组件加载
    // component: HomeView//路由地址对应的组件
    // 第二种:动态导入组件:在实际跳路由的时候才会临时加载组件的内容,被叫做动态加载又叫懒加载又叫延迟加载
    component: () => import('../views/HomeView.vue')

  },
  {
    
    
    path: '/about',
    name: 'about',
    component: () => import('../views/AboutView.vue')
  }
]

スラッシュで始まるルーティング アドレスは絶対ルーティング アドレスです。

ビューに配置されたコンポーネントはルートレベルのコンポーネントであり、コンポーネントに配置されたコンポーネントはページ内のコンポーネントです。

6. ルーティングをスキップする 2 つの方法

戻る:@click='$router.go(-1)

**最初のタイプ: **router-link、クリックされる要素は router-link でラップする必要があります

 <router-link to="/index/home">首页</router-link>

**2 番目の方法: **ルート管理オブジェクト this.$router を使用して、JS コードを通じてルートをジャンプします

<div @click="$router.push('/login')">去登陆</div>

ルートをスキップする方法は次のとおりです。

  1. router-link は、クリックされた要素を router-link でラップする必要があり、router-link は最終的にタグにレンダリングされます。
  2. $router.push (ルーティング アドレス) はブラウザ履歴に新しい履歴レコードを追加します
  3. $router.replace() はブラウザ履歴の現在の履歴を置き換えます
  4. $router.go(n) 履歴の切り替え、ブラウザの既存の履歴の切り替え

7. ルーティングパラメータをスキップする 3 つの方法

ルーティングパラメータの役割:

  1. ルートをジャンプした後に表示されるコンポーネントは動的にレンダリングする必要があります。
  2. コンポーネントが動的にレンダリングする必要があるデータはユーザーの操作に関連しており、ページのコンテンツが動的にレンダリングされる場合、ジャンプルートはクリック後のクリックに関連付けられ、パラメータはクリック後に渡されます。
  3. 多かれ少なかれパラメータを渡します。製品のすべてのデータを渡すことも、ID のみを渡すこともできます。

**タイプ 1:**動的ルーティング。ページ更新パラメータは失われません。パラメータはアドレス バーに表示されます。

ルーティングテーブル内:

{
    
    
    // 路由地址中带有 :xxx 的路由地址 都属于动态路由
    // path: '/detail/:name', // :name 是个占位符, 实际在跳路由的时候,会动态拼接参数 例如: '/detail/'+100
    path: '/detail/:name',
    name: 'detail',
    component: () => import('../views/Detail.vue')
  },

vue ファイル内:

<router-link :to="'/detail/'+888">详情</router-link>


<div @click="$router.push('/detail/'+888)">详情</div>

ルーティングパラメータを取得するthis.$router.params.name

**2 番目のタイプ: **パス + クエリ。ページが更新されてもパラメータは失われず、パラメータはクエリ文字列の形式でアドレス バーに表示されます。

ルーティングテーブル内:

{
    
    
    path: '/detail',
    name: 'detail',
    component: () => import('../views/Detail.vue')
  },

vue ファイル内:

<router-link :to="{path:'/detail',query:{name:888}}">详情</router-link>

<div @click="$router.push({path:'/detail',query:{name:888}})">详情</div>

ルーティングパラメータを取得するthis.$router.query.name

**3 番目のタイプ: **name + params、ページが更新されるとパラメータが失われ、パラメータが表示されなくなります。

ルーティングテーブル内:

{
    
    
    path: '/detail',
    name: 'detail',
    component: () => import('../views/Detail.vue')
  },

vue ファイル内:

<router-link :to="{name:'detail',params:{name:888}}">详情</router-link>

<div @click="$router.push({name:'detail',params:{name:888 }})">详情</div>

ルーティングパラメータを取得するthis.$router.params.name

8. ネストされたルーティング

ネストされたアウトレットでコンポーネントをレンダリングするには、VueRouter のパラメーターで子設定を使用する必要があります。

var router = new VueRouter({
    
    
    routes:[
        {
    
    
            path:'/article',
            component:Article,
            /*通过children设置嵌套路由规则*/
      		 children:[
{
    
     path:'/article/cate',component: Cate },
{
    
     path:'/article/list',component: List }
]
}
    ]
});

9. リダイレクトとエイリアス

//一级路由重定向
  {
    
    
    path:'/',
    redirect:'/index'  
  },
  //404路由
  {
    
    
    path:'*',
    component: () => import('../views/NotFound.vue') 
  }
//二级路由重定向
{
    
    
    path:'/index',
    redirect:'/index/home'  
  },
  //404路由
  {
    
    
    path:'*',
    component: () => import('../views/NotFound.vue') 
  }

10. ルーティングモード

ヒストリーモードとハッシュモード

11.ルーターとルーターとルータールート違い_

$router: ルーティング管理オブジェクト。ルーティングの管理を担当します(ジャンプルーティングを担当)

$route: 現在のルーティングのすべての情報 (現在のアドレス、伝送されるパラメータ) を含むルーティング オブジェクト

12. ルーティングガード機能とメタ情報

正当な経路であれば、ルーティングテーブルに存在する経路によって、現在の経路がジャンプできるかどうかを制御できますが、例えばクリックしてジャンプできるかどうかを判定する場合、判定後のルートにジャンプできるコードをどこに書けばよいのでしょうか。

vueで提供されているルートのガード関数を利用して、ルートをスキップできるかどうかを判断するコードを書くことができます。

ルーターのインスタンス メソッド (ルーター インスタンスに追加する必要がある) は、ナビゲーション ガード メソッドとも呼ばれます。

  1. ルーティングのためのグローバル プレガード方式

    router.beforeEach((to, from, next) => {
          
          
      //参数一: to 新的路由对象
      //参数二: from 旧的路由对象
      //参数三: next 路由控制方法, 调用该方法则允许路由跳转, 未调用该方法则不允许路由跳转
      next();//必须手动调用该函数,否则无法完成路由跳转
    })
    
  2. ルーティングのためのグローバルなポストガード方式

    router.afterEach((to,from)=>{
          
          
      console.log('afterEach');
    })
    
  3. ルート排他ガード機能(単一ルートがジャンプできるかどうかを判断するためにルートに追加されるだけ)

    beforeEnter: (to, from, next) => {
          
          
        console.log('beforeEnter');
          next();
        }
    

注: グローバルガード関数とエクスクルーシブガード関数は記述位置と名前が異なりますが、機能は類似しており、いずれもガードルートジャンプです

12.1 配線ガード機能の例

1. ページタイトルを動的に更新する

Web ページのタイトルを動的に更新します。ユーザーに現在どのページにいるかを伝え、それをルーティング メタ情報とともに使用し、js を介して現在のタイトルを設定します。

上記の機能を実現するには、各ルーティング オブジェクトにルーティング メタ情報を追加する必要があります。ルーティング メタ情報は常に現在のルーティング オブジェクトに保存されます。

例えば:

{
    
    
    path: '/index',
    name: 'index',
    meta: {
    
     
        title:'index',
        auth:true//用户登录后才可以跳路由,没有加是不需要登录就可以
          }, //路由元信息( 会永远保存在当前路由对象身上 )
    component: () => import('../views/Index.vue'),
 }

ケースコード:

//路由的全局前置守卫方法
router.beforeEach((to, from, next) => {
    
    
	//因为这个函数跳任何一个路由都会执行,所以当路由跳转的时候就会执行将路由元信息中的title的值赋给网页的标题,这样就可以实现动态更新网页标题
  //动态更新网页标题
  document.title = to.meta.title;
	next();
})

2. 個々のルートへのアクセスを制御する

注文ページやマイページへジャンプしたい場合はログインが必要です

//路由的全局前置守卫方法
router.beforeEach((to, from, next) => {
    
    
  //参数一: to 新的路由对象
  //参数二: from 旧的路由对象
  //参数三: next 路由控制方法, 调用该方法则允许路由跳转, 未调用该方法则不允许路由跳转

  //获取localStorage中的登陆凭证
  var token = localStorage.getItem('token');

  //只有在登陆以后 才可以跳转到 订单页
  //if(to.path == '/index/order'){ //想要跳往 订单页

  /*  if(to.path == '/index/order' || to.path == '/index/mine'){ //想要跳往 订单页 或者 我的页面
    if( token ){ //已经登陆
      next();
    }else{ //未登录
      next('/login');
    }
  }else{ //其他页面( 非订单页 )
    next();//必须手动调用该函数, 否则无法完成路由跳转
  } */
  
})

3. 自由にアクセスできるのはログインページのみで、他のページはログインする必要があります。

//路由的全局前置守卫方法
router.beforeEach((to, from, next) => {
    
    
  //参数一: to 新的路由对象
  //参数二: from 旧的路由对象
  //参数三: next 路由控制方法, 调用该方法则允许路由跳转, 未调用该方法则不允许路由跳转

  //获取localStorage中的登陆凭证
  var token = localStorage.getItem('token');
  
  // 对于后台管理系统这种应用, 只有登录页是可以随意访问的, 但是其他所有页面都是必须在登陆以后才可以访问
  if( to.path == '/login' ){
    
     //跳往 登录页
    if( token ){
    
     //已登录 , 重定向到 "/"
      next('/');
    }else{
    
     //未登录 , 允许跳往 登录页
      next(); 
    }
    // next();
  }else{
    
     //跳往 非登录页
    if( token ){
    
     //已登录 , 允许访问
      next(); 
    }else{
    
     //未登录 , 重定向到 "/login"
      next('/login');
    }
  }
})

統合されたコード

プロジェクト src フォルダーの下の router フォルダーにあるindex.js ファイル

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const routes = [ //定义多个一级路由
  {
    
    
    path: '/index',
    name: 'index',
    meta: {
    
     title:'index' }, //路由元信息( 会永远保存在当前路由对象身上 )
    component: () => import('../views/Index.vue'),
    children:[ //嵌套路由( 二级路由 )
      {
    
    
        path: '/index/home',
        name: 'home',
        meta: {
    
     title:'首页' }, //路由元信息( 会永远保存在当前路由对象身上 )
        component: () => import('../views/Index/Home.vue'),
      },
      {
    
    
        path: '/index/order',
        name: 'order',
        meta: {
    
     title:'订单' }, //路由元信息( 会永远保存在当前路由对象身上 )
        component: () => import('../views/Index/Order.vue')
      },
      {
    
    
        path: '/index/mine',
        name: 'mine',
        meta: {
    
     title:'我的' },
        component: () => import('../views/Index/Mine.vue')
      },
      //二级路由的重定向
      {
    
    
        path:'/index',
        redirect:'/index/home' 
      },
      //404路由
      {
    
    
        path:'*',
        component: () => import('../views/NotFound.vue') 
      }
    ]
  },
  {
    
    
    path: '/login',
    name: 'about',
    meta: {
    
     title:'登陆' },
    component: () => import('../views/Login.vue'),
    //路由独享的守卫函数
    // beforeEnter: (to, from, next) => {
    
    
    //   console.log('beforeEnter');
    //   next();
    // }
  },
  {
    
    
    // 路由地址中带有 :xxx 的路由地址 都属于动态路由
    // path: '/detail/:name', // :name 是个占位符, 实际在跳路由的时候,会动态拼接参数 例如: '/detail/'+100
    path: '/detail',
    name: 'detail',
    meta: {
    
     title:'详情' },
    component: () => import('../views/Detail.vue'),
    
  },
  //一级路由重定向
  {
    
    
    path:'/',
    redirect:'/index'  
  },
  //404路由
  {
    
    
    path:'*',
    component: () => import('../views/NotFound.vue') 
  }
]

const router = new VueRouter({
    
    
  mode: 'history', //设置路由模式, history , hash
  base: process.env.BASE_URL,
  routes
})

//设置实例方法( 导航守卫方法 )

//路由的全局前置守卫方法
router.beforeEach((to, from, next) => {
    
    
  //参数一: to 新的路由对象
  //参数二: from 旧的路由对象
  //参数三: next 路由控制方法, 调用该方法则允许路由跳转, 未调用该方法则不允许路由跳转

  //动态更新网页标题
  document.title = to.meta.title;

  //获取localStorage中的登陆凭证
  var token = localStorage.getItem('token');

  //只有在登陆以后 才可以跳转到 订单页
  //if(to.path == '/index/order'){ //想要跳往 订单页

  /*  if(to.path == '/index/order' || to.path == '/index/mine'){ //想要跳往 订单页 或者 我的页面
    if( token ){ //已经登陆
      next();
    }else{ //未登录
      next('/login');
    }
  }else{ //其他页面( 非订单页 )
    next();//必须手动调用该函数, 否则无法完成路由跳转
  } */
  
  // 对于后台管理系统这种应用, 只有登录页是可以随意访问的, 但是其他所有页面都是必须在登陆以后才可以访问
  if( to.path == '/login' ){
    
     //跳往 登录页
    if( token ){
    
     //已登录 , 重定向到 "/"
      next('/');
    }else{
    
     //未登录 , 允许跳往 登录页
      next(); 
    }
    // next();
  }else{
    
     //跳往 非登录页
    if( token ){
    
     //已登录 , 允许访问
      next(); 
    }else{
    
     //未登录 , 重定向到 "/login"
      next('/login');
    }
  }
})

//路由的全局后置守卫方法
// router.afterEach((to,from)=>{
    
    
//   console.log('afterEach');
// })

export default router

12.2 自己カプセル化されたログインおよび登録コンポーネント

プロジェクトファイルのsrcファイル下のrouterファイル下のindexファイルがルーティングテーブルを構成します。

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const routes = [ //定义多个一级路由
  {
    
    
    path: '/index',
    name: 'index',
    meta: {
    
     title:'index' }, //路由元信息( 会永远保存在当前路由对象身上 )
    component: () => import('../views/Index.vue'),
    children:[ //嵌套路由( 二级路由 )
      {
    
    
        path: '/index/home',
        name: 'home',
        meta: {
    
     title:'首页' }, //路由元信息( 会永远保存在当前路由对象身上 )
        component: () => import('../views/Index/Home.vue'),
      },
      {
    
    
        path: '/index/order',
        name: 'order',
        meta: {
    
     title:'订单' }, //路由元信息( 会永远保存在当前路由对象身上 )
        component: () => import('../views/Index/Order.vue')
      },
      {
    
    
        path: '/index/mine',
        name: 'mine',
        meta: {
    
     title:'我的' },
        component: () => import('../views/Index/Mine.vue')
      },
      //二级路由的重定向
      {
    
    
        path:'/index',
        redirect:'/index/home' 
      },
      //404路由
      {
    
    
        path:'*',
        component: () => import('../views/NotFound.vue') 
      }
    ]
  },
  {
    
    
    path: '/login',
    name: 'about',
    meta: {
    
     title:'登陆' },
    component: () => import('../views/Login.vue'),
    //路由独享的守卫函数
    // beforeEnter: (to, from, next) => {
    
    
    //   console.log('beforeEnter');
    //   next();
    // }
  },
  {
    
    
    path: '/register',
    name: 'register',
    meta: {
    
     title:'注册' },
    component: () => import('../views/Register.vue')
  },
  {
    
    
    // 路由地址中带有 :xxx 的路由地址 都属于动态路由
    // path: '/detail/:name', // :name 是个占位符, 实际在跳路由的时候,会动态拼接参数 例如: '/detail/'+100
    path: '/detail',
    name: 'detail',
    meta: {
    
     title:'详情' },
    component: () => import('../views/Detail.vue'),
    
  },
  //一级路由重定向
  {
    
    
    path:'/',
    redirect:'/index'  
  },
  //404路由
  {
    
    
    path:'*',
    component: () => import('../views/NotFound.vue') 
  }
]

const router = new VueRouter({
    
    
  mode: 'history', //设置路由模式, history , hash
  base: process.env.BASE_URL,
  routes
})

//设置实例方法( 导航守卫方法 )

//路由的全局前置守卫方法
router.beforeEach((to, from, next) => {
    
    
  //参数一: to 新的路由对象
  //参数二: from 旧的路由对象
  //参数三: next 路由控制方法, 调用该方法则允许路由跳转, 未调用该方法则不允许路由跳转

  //动态更新网页标题
  document.title = to.meta.title;

  //获取localStorage中的登陆凭证
  var token = localStorage.getItem('token');

  //只有在登陆以后 才可以跳转到 订单页
  //if(to.path == '/index/order'){ //想要跳往 订单页

  /*  if(to.path == '/index/order' || to.path == '/index/mine'){ //想要跳往 订单页 或者 我的页面
    if( token ){ //已经登陆
      next();
    }else{ //未登录
      next('/login');
    }
  }else{ //其他页面( 非订单页 )
    next();//必须手动调用该函数, 否则无法完成路由跳转
  } */
  
  //对于后台管理系统这种应用, 只有登录页是可以随意访问的, 但是其他所有页面都是必须在登陆以后才可以访问
  if( to.path == '/register' ){
    
     //跳往 注册页 , 允许跳转
    next();
  }else{
    
    
    if( to.path == '/login' ){
    
     //跳往 登录页
      if( token ){
    
     //已登录 , 重定向到 "/"
        next('/');
      }else{
    
     //未登录 , 允许跳往 登录页
        next(); 
      }
      // next();
    }else{
    
     //跳往 非登录页
      if( token ){
    
     //已登录 , 允许访问
        next(); 
      }else{
    
     //未登录 , 重定向到 "/login"
        next('/login');
      }
    }
  }
})

//路由的全局后置守卫方法
// router.afterEach((to,from)=>{
    
    
//   console.log('afterEach');
// })

export default router

プロジェクトフォルダー内のビューファイルに登録済みコンポーネントを作成します

<template>
  <div class="register">
    <div class="title">注册页</div>
    <div class="block">
      <input type="text" v-model="formData.phone" placeholder="输入手机号">
    </div>
    <div class="block">
      <input type="password" v-model="formData.pass" placeholder="输入密码">
    </div>
    <div class="block">
      <input type="password" v-model="formData.checkpass" placeholder="输入确认密码 ">
    </div>
    <div class="block">
      <input type="button" class="btn" value="注册" @click="register">
      <input type="button" class="btn" value="登录" @click="login">
    </div>
  </div>
</template>

<script>
// 导入封装的请求函数
import {
    
    user_register} from '../utils/api'
import {
    
     Toast } from 'vant';

export default {
    
    
  data(){
    
    
    return {
    
    
      formData:{
    
    
        phone:'',
        pass:'',
        checkpass:''
      }
    }
  },
  methods:{
    
    
    // 点击时先对输入的内容进行判断如果都符号再去发请求
    register(){
    
    
      // 先对输入的手机号通过正则进行判断,如果不符合则弹出弹框
      if(/^1[3-9]\d{9}$/.test(this.formData.phone) == false){
    
    
        Toast.fail({
    
     message: '请输入合法的手机号' });
        // 对密码和确认密码进行判断是否为空为空就提示
      }else if( !this.formData.pass || !this.formData.checkpass ){
    
    
        Toast.fail({
    
     message: '请输入密码/确认密码' });
      }else{
    
    //验证通过
        Toast.fail({
    
     message: '密码和确认密码不一致' });
        // 因为注册请求时要传账号和密码,formData中多了一个确认密码需要将多的内容删掉

        // 深拷贝formData,防止对原数据进行修改
        var newFormData = JSON.parse(JSON.stringify(this.formData));
        // 删除对象的checkpass属性
        delete newFormData.checkpass;

        // 发起注册请求,账号phone,密码pass必须传
        user_register(newFormData).then((res)=>{
    
    
          if(res.data.code == 200){
    
    //注册成功
            Toast.success('登陆成功!')
            // 跳转到登录页
            this.$router.push('/login');
          }else{
    
    
            Toast.fail('登陆失败!')
          }
        })
        
      }
    },
    login(){
    
    
      this.$router.push('/login');
    }
  }
}
</script>

<style lang="scss" scoped>
.register{
    
    
  margin: 50px 20px;
}
.register .title{
    
    
  font-weight: bold;
  text-align: center;
  line-height: 40px;
  font-size: 32px;
}
.register .block{
    
    
  margin: 20px 0;
}
.register .block input{
    
    
  height: 40px;
  border: 1px solid #ccc;
  border-radius: 20px;
  padding-left: 20px;
  box-sizing: border-box;
  outline: none;
  width: 100%;
  margin-bottom: 10px;
  font-size: 14px;
  color: #ccc;
}


.register .block .btn{
    
    
  padding-left: 0;
  height: 40px;
  border: 1px solid #ccc;
  border-radius: 20px;
  box-sizing: border-box;
  outline: none;
  width: 100%;
  margin-bottom: 15px;
  color: black;
  font-size: 16px;
}
</style>

プロジェクトフォルダー内のビューファイルにログインコンポーネントを作成します。

<template>
  <div class="login">
    <div class="title">登陆页</div>
    <div class="block">
      <input type="text" v-model="formData.phone" placeholder="输入手机号">
    </div>
    <div class="block">
      <input type="password" v-model="formData.pass" placeholder="输入密码">
    </div>
    <div class="block">
      <input type="button" value="登陆" @click="login">
    </div>
  </div>
</template>

<script>

import {
    
     Toast } from 'vant';
import {
    
    user_login} from '../utils/api'

export default {
    
    
  data(){
    
    
    return {
    
    
      formData:{
    
    
        phone:'',
        pass:''
      }
    }
  },
  methods:{
    
    
    login(){
    
    
      if( /^1[3-9]\d{9}$/.test( this.formData.phone ) == false ){
    
    
        alert('请输入合法的手机号');
      }else if( !this.formData.pass ){
    
    
        alert('请输入密码')
      }else{
    
     //验证通过 
        //发起登陆请求
        user_login( this.formData ).then((res)=>{
    
    
          if( res.data.code == 200 ){
    
    
            //保存用户信息,token
            localStorage.setItem('token',res.data.token);
            localStorage.setItem('userinfo', JSON.stringify( res.data.userinfo ) );
            //自动跳转到首页
            this.$router.push('/');
            Toast.success('登陆成功!')
          }else{
    
    
            Toast.fail('登陆失败!')
          }
        })
      }
    }
  }
}
</script>

<style lang='scss' scoped> 
.login{
    
    
  margin: 50px 20px;
}
.login .title{
    
    
  font-weight: bold;
  text-align: center;
  line-height: 40px;
}
.login .block{
    
    
  margin: 20px 0;
}
.login .block input{
    
    
  height: 40px;
  border: 1px solid #ccc;
  border-radius: 20px;
  padding-left: 20px;
  box-sizing: border-box;
  outline: none;
  width: 100%;
}
</style>

フォームコンポーネントとパッケージ化されたログインコンポーネント

<template>
  <div class="login">
    <div class="title">登录页</div>
    <van-form @submit="login">
      <van-field
        class="input-box"
        v-model="formData.phone"
        placeholder="输入手机号"
        :rules="[{ required: true, pattern:/^1[3-9]\d{9}$/,message: '请输入手机号' }]"
      />
      <van-field
      class="input-box"
        v-model="formData.pass"
        type="password"
        placeholder="输入密码"
        :rules="[{ required: true, pattern:/^\d{6}$/, message: '请输入密码' }]"
      />
      <div style="margin: 16px;">
        <van-button round block type="info" native-type="submit">登录</van-button>
      </div>
    </van-form>
    <button round block @click="reguster" class="zhuce" >点此注册新账号</button>
  </div>
</template>

<script>

// 导入组件
import {
    
     Toast } from 'vant';
import {
    
    user_login} from '../utils/api'

export default {
    
    
  data(){
    
    
    return {
    
    
      formData:{
    
    
        phone:'',
        pass:'',
      }
    }
  },
  methods:{
    
    
    login(){
    
    
      // 发起登录请求
      user_login( this.formData ).then((res)=>{
    
    
        if( res.data.code == 200 ){
    
    
          //保存用户信息,token
          localStorage.setItem('token',res.data.token);
          localStorage.setItem('userinfo', JSON.stringify( res.data.userinfo ) );
        }else{
    
    
          Toast.fail('登陆失败!')
        }
      })
    },
    reguster(){
    
    
      this.$router.push('/register');
    }
  }
}
</script>

<style lang='scss' scoped> 

.login{
    
    
  margin: 50px 20px;
}
.login .title{
    
    
  font-weight: bold;
  text-align: center;
  line-height: 40px;
  font-size: 32px;
}


.login .input-box{
    
    
  border: 1px solid #ccc;
  margin: 30px 0;
  border-radius: 30px;
}

.zhuce{
    
    
  position: absolute;
  right: 20px;
  padding: 5px 20px;
  border: none;
  font-size: 14px;
  color: lightblue;
  background-color: white;
}


</style>

プロジェクト内のaxiosの2次カプセル化、APIリクエストの一元管理

これらのファイルは両方とも、プロジェクトの src ファイル内に utils ファイルを作成します。

utils ファイル内に実装するファイルを作成します。

request.js ファイル

//对axios进行二次封装
import axios from 'axios'

import {
    
     Toast } from 'vant';

//实例化axios
var service = axios.create({
    
    
    timeout: 10*1000,
    baseURL: '/api'
})

//设置拦截器( 请求拦截器 )
service.interceptors.request.use((config)=>{
    
    
    //统一携带请求头
    config.headers['Authorization'] = 'Bearer' + ' ' + localStorage.getItem('token');
    //显示loading
    Toast.loading({
    
     message: '加载中...', duration: 0});
    
    return config;
},(error)=>{
    
    
    return Promise.reject(error);
})

//设置拦截器( 响应拦截器 )
service.interceptors.response.use((res)=>{
    
    
    //隐藏loading
    Toast.clear();

    return res;
},(error)=>{
    
    
    //隐藏loading
    Toast.clear();

    if( error.response.status == 401 ){
    
    
        alert('身份认证失败,登陆过期!')
    }
    else if( error.response.status == 404 ){
    
    
        alert('访问路径错误!')
    }
    else if( error.response.status == 500 ){
    
    
        alert('服务器内部错误')
    }
    return Promise.reject(error);
})

export default service;


api.js ファイルを使用するには、パッケージ化されたリクエストを使用するコンポーネントにインポートしてから使用する必要があります。

//导入设置过 拦截器的axios
import service from './request'

//一个请求封装一个函数
export function user_login( params = {
     
     } ){
    
    
    return service.post('/user/login', params );
}

export function user_register( params = {
     
     } ){
    
    
    return service.post('/user/register', params );
}

//...

//导出所有函数

プロジェクトの vue.config.js ファイルにプロキシを設定します

const {
    
     defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
    
    
  transpileDependencies: true,
  devServer:{
    
    
    proxy:{
    
    
      '/api':{
    
    
        target:'http://waimai.yantianfeng.com/',
        changeOrigin:true,
        pathRewrite:{
    
    }
      }
    }
  }
})

おすすめ

転載: blog.csdn.net/m0_53181852/article/details/127587390