Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
耘開源,致力于基礎平臺開發,點擊上方“關注”,每天為你分享開發技巧和開源干貨
其實vue也用了好幾年了,都是從腳手架開始入手的,可能隨著年紀,學習的欲望越來越少了,一直想看看vue-router是怎么運作的,但是一直都沒有下手.
最近打算自己搞一個基礎開發平臺,需要自己下手了,這個地方總歸是要懂的,花了幾天時間,從迷糊到清晰,慢慢的了解了這塊的運作方式,把這塊原理試著闡釋一下:
在我看來本質就是一個前端的攔截器,然后內部跳轉,這也是單頁應用的核心,不刷新可以完美地搞定所有事情,但是又和以前的ajax有所區別,可以更好的工程化的解決問題,用來滿足現在越來越重的前端需求
2.vue的組件,這個算是一個成功的,不過以前我寫過flex,可能現在大家不熟悉,那個時候是用actionScript和xml混合協作的是不是很熟悉,沒錯我感覺現在的這幾個vue和angular都參考了那個,而且flash gameover之后那幾個大神好像都去了google,沒多久angular就面世了,一樣的動態綁定,一樣的動態刷新,但是組件算是前進了一大步.他脫胎于class但是優于class,讓我們可以單個組件封裝了一個完整的html和js以及css的功能
3.路由雖然官方的demo是
<router-link to="/foo">Go to Foo</router-link>
但是我感覺開發應該很少這樣用,畢竟我們都是代碼小能手,這種固定的東西如何我們天天改的需求,所以常用的還是
router.push({ path: 'register', query: { plan: 'private' }})// 我們可以自由的傳參和修改地址
但是我們路由總要一個地方管理,這里我們的主角就要出現了VueRouter,我們可以定義好我們的全局路由管理用了吧我們整個項目的路由統一管理
const router=new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user',
component: User
}
]
})
但是我們的我們沒辦法把路由都寫死在前端,當然我們也可以這樣做,但是開始簡單,后期維護就負責,而且各自權限管理,也把我們搞得昏頭暗地的,我們自然而然地想到了,后臺系統把路由組裝好,然后再給前端,前端根據后臺顯示的路由加載界面,這樣既可以節省性能,又可以完整的控制權限.
動態加載路由,需要使用官方的api,直接調用函數就可以了,一開始沒找到,一直在自己添加原始的路由,結果路由一直匹配不到
router.addRoute
addRoute(route: RouteConfig): ()=> void
后臺組裝路由,包括基礎的對象,主要是path和name,meta用來添加備用信息
@Data
public class IViewMenu {
/**
* 數據ID
*/
private String id;
/**
* 前端路徑
*/
private String path;
/**
* 名稱
*/
private String name;
private String redirect;
private String component;
private IViewMenuMeta meta;
private List<IViewMenu> children;
@Data
static private class IViewMenuMeta {
/**
* 訪問權限
*/
private String access;
/**
* 圖標
*/
private String icon;
/**
* 分組標題
*/
private String title;
/**
* 是否緩存
*/
private boolean notCache=false;
/**
* 關閉tab回調函數
*/
private String beforeCloseName;
}
}
4.import,組件的動態加載
我們每一個界面都是一個單獨的組件,但是我們現在希望路由規則是后臺傳過來的,那我們的組件也是根據后臺的規則動態加載的,import是我們的常用規則的
// 常用的組件就是這樣加載的,其他都是正常屬性,component需要我們到前端來加載
{
path: '/login',
name: 'login',
meta: {
title: 'Login - 登錄',
hideInMenu: true
},
component: ()=> import('@/view/base/login/login.vue')
}
// 因為eslint 是不允許import在函數使用的,我們就要單獨把這個建一個別名
export const _import=file=> ()=> import('@/view/' + file + '.vue')
然后在.eslintignore這個文件里面把這個文件給忽略,不然編譯會一直gg
整體流程是就
登錄--->加載菜單-->加載組件-->顯示主頁 核心代碼如下:
import Vue from 'vue'
import Router from 'vue-router'
import {routers as routes, _import} from './routers'
import store from '@/store'
import iView from 'view-design'
import { getToken, canTurnTo } from '@/libs/util'
import { isURL } from '@/libs/validate'
import { getRouters } from '@/api/user'
import config from '@/config'
import Main from '@/components/main'
const { homeName }=config
Vue.use(Router)
const router=new Router({
routes,
isAddRouters: false,
mode: 'history'
})
const LOGIN_PAGE_NAME='login'
const turnTo=(to, access, next)=> {
if (canTurnTo(to.name, access, routes)) next() // 有權限,可訪問
else next({ replace: true, name: 'error_401' }) // 無權限,重定向到401頁面
}
const addRouters=(data)=> {
if (!router.isAddRouters) {
data.forEach(d=> {
loadComponent([d])
routes.push(d)
router.addRoutes([d]) // 動態添加路由
router.isAddRouters=true
})
}
}
const loadComponent=(data)=> {
data.forEach(route=> {
if (route.menu==='3') {
route['component']=Main
} else if (isURL(route.path)) {
route.meta.iframeUrl=route.href
route['meta']['type']='iframe'
} else if (route.target==='iframe') {
route['meta']['iframeUrl']=`${process.env.VUE_APP_SERVER_URL}${route.href}`
} else {
try {
route['component']=_import(route.path) || null
} catch (e) {
console.log(e)
}
}
if (route.children && route.children.length > 0) {
loadComponent(route.children)
}
})
}
const gotoAssess=(to, store, next)=> {
if (store.state.user.hasGetInfo) {
turnTo(to, store.state.user.access, next)
} else {
store.dispatch('getUserInfo', store.state.user.userId).then(user=> {
// 拉取用戶信息,通過用戶權限和跳轉的頁面的name來判斷是否有權
//限訪問;access必須是一個數組,如:['super_admin'] ['super_admin', 'admin']
turnTo(to, user.access, next)
}).catch(()=> {
next({
name: LOGIN_PAGE_NAME
})
})
}
}
/**
* 核心功能是beforeEach,全局的攔截器,可以幫助我們獲取到用戶跳轉之前的所有請求
* 用戶to過來大部分都是自動匹配過來的,匹配可以規矩path也可以根據name匹配
*匹配可以包括多個,上下級都有
*/
router.beforeEach((to, from, next)=> {
iView.LoadingBar.start()
const token=getToken()
if (!token && to.name !==LOGIN_PAGE_NAME) {
// 未登錄且要跳轉的頁面不是登錄頁
next({
name: LOGIN_PAGE_NAME // 跳轉到登錄頁
})
} else if (!token && to.name===LOGIN_PAGE_NAME) {
// 未登陸且要跳轉的頁面是登錄頁
next() // 跳轉
} else if (token && to.name===LOGIN_PAGE_NAME) {
// 已登錄且要跳轉的頁面是登錄頁
next({
name: homeName // 跳轉到homeName頁
})
} else {
if (router.isAddRouters) {
gotoAssess(to, store, next)
} else {
// 沒獲取用戶信息,說明沒有獲取路由菜單
getRouters(store.state.user.userId).then(routerArr=> {
addRouters(routerArr)
gotoAssess(to, store, next)
})
}
}
})
router.afterEach(to=> {
iView.LoadingBar.finish()
window.scrollTo(0, 0)
})
export default router
描述的可能不是很全,自己遇到的幾個問題都再重點突出以下
期待你的關注,這里是悟耘開源,開源項目包括EasyPoi(快速完成Office操作工具包,開源中國GVP項目),風鈴(基于springboot的快速開發平臺),代碼生成器等開源項目,悟耘開源致力于基礎平臺開發,希望幫助大家提高開發速度,提升開發質量,讓大家遠離996。關注我,獲取分享開源路上的感悟和開發相關技術分享。
<script src="./vue.js"></script>
// 千萬注意 :引入路由一定要在引入vue之后,因為vue-router是基于vue工作的
<script src="./node_modules/vue-router/dist/vue-router.js"></script>
# 1. 入口
// 方式1 : url地址為入口 調試開發用
輸入url地址 改變哈希值 `01-路由的基本使用.html#/one`
// 方式2 : 聲明式導航 : router-link+to (見下面介紹)
# 2. 路由規則
// path : 路由路徑
// component : 將來要展示的路由組件
routes: [
{ path: '/one', component: One },
{ path: '/two', component: Two }
]
# 3. 組件
// 使用返回值的這個組件名稱
const One=Vue.component('one', {
template: ` <div> 子組件 one </div> `
})
# 4. 出口
<!-- 出口 組件要展示的地方-->
<router-view></router-view>
# 總結
拿到入口哈希路徑, 根據路由匹配規則,找到對應的組件,顯示到對應的出口位置
<!--
router-link 組件最終渲染為 a標簽, to屬性轉化為 a標簽的href屬性
to 屬性的值 , 實際上就是哈希值,將來要參與路由規則中進行與組件匹配
-->
<router-link to="/one">首頁</router-link>
const One={
template: `<div> 子組件 one </div> `
}
<div id="app">
<!-- 1 路由入口:鏈接導航 -->
<router-link to="/one">One</router-link>
<router-link to="/two">Two</router-link>
<!-- 4 路由出口:用來展示匹配路由視圖內容 -->
<router-view></router-view>
</div>
<!-- 導入 vue.js -->
<script src="./vue.js"></script>
<!-- 導入 路由文件 -->
<script src="./node_modules/vue-router/dist/vue-router.js"></script>
<script>
// 3 創建兩個組件
const One={
template: '<h1>這是 one 組件</h1>'
}
const Two={
template: '<h1>這是 two 組件</h1>'
}
// 0 創建路由對象
const router=new VueRouter({
// 2. 路由規則
routes: [
{ path: '/one', component: One },
{ path: '/two', component: Two }
]
})
const vm=new Vue({
el: '#app',
//0. 不要忘記,將路由與vue實例關聯到一起!
router
})
</script>
<a href="#/one" class="router-link-exact-active router-link-active">One</a>
<a href="#/two" class="">Two</a>
.router-link-exact-active,
.router-link-active {
color: red;
font-size: 50px;
}
const router=new VueRouter({
routes: [],
// 修改默認高亮的a標簽的類名
// red 是已經存在過的
linkActiveClass: 'red'
})
導入 : 列表三個手機都要點擊進去詳情頁, 只需要一個組件,顯示不同的數據即可
# 入口
<router-link to="/detail/1">手機1</router-link>
<router-link to="/detail/2">手機2</router-link>
<router-link to="/detail/3">手機3</router-link>
<router-link to="/detail">手機4</router-link> 沒有參數如何????
# 規則
routes: [
// 2 . 路由規則
{ path: '/detail/:id?', component: Detail }
]
# 獲取參數的三種方式
const Detail={
template: `
// 方式1 : 組件中直接讀取
<div> 顯示詳情頁內容....{{ $route.params.id }} </div>
`,
created() {
// 方式2 : js直接讀取
// 打印只會打印一次,因為組件是復用的,每次進來鉤子函數只會執行一次
console.log(this.$route.params.id)
},
// 方式3 : 監聽路由的參數,為什么不需要深度監聽,因為一個路徑變化,就會對應一個對新的路由對象(地址變)
watch: {
$route(to, from) {
console.log(to.params.id)
}
}
}
# 演示 :
<router-link to="/detail/4?age=21#one">detail</router-link>
{ path: '/detail/:id?', component: detail }
在組件內 created打印 this.$route
> fullPath: "/detail/4?id=001#one"
> hash : "#one"
> params : {id:'4'}
> query : {age : 21}
> path : '/detail/4'
導入 : url測試 parent 和child, 想讓child 在 parent 中顯示
const parent={
template: `<p>parent <router-view> </router-view> </p>`
}
const child={
template: `<p>child</p>`
}
const router=new VueRouter({
routes: [
{
path: '/parent',
component: parent,
children: [
{ path: '/child', component: child }
]
}
]
})
# 命名
routes: [
{
path: '/parent',
name: 'parent',
component: parent
}
]
# 入口鏈接 + 跳轉 (使用 path 和 name 的轉換)
<!-- 方式1 : url手動寫 -->
<!-- 方式2 : 入口鏈接 聲明式導航 -->
<router-link to="/parent">點擊</router-link>
<router-link :to="{ name : 'parent' }">點擊</router-link> # 忘了 帶 : 原始對象類型
<!-- 方式3 : 編程式導航 -->
fn() {
// this.$router.push('/parent')
this.$router.push({
name: 'parent'
})
}
導入 : 有時候想同時 (同級) 展示多個視圖,
需求 : 訪問 / 根目錄 同時展示以下三個組件
const header={
template: `<p>header </p>`
}
const main={
template: `<p>main </p>`
}
const footer={
template: `<p>footer </p>`
}
# 以前的那個方式只能顯示三個 header
# 演示之前的效果
routes: [
{
path: '/',
components: {
default: header,
m: main,
f: footer
}
}
]
<router-view> </router-view>
<router-view name="m"> </router-view>
<router-view name="f"> </router-view>
redirect: '/header'
redirect: { name: 'header' }
redirect: to=> {
// console.log(to)
return {
name: 'about'
}
}
# 入口
<router-link to="/header/3">123</router-link>
# 規則
routes: [
{
path: '/header/:id',
component: header,
}
]
# 獲取參數
const header={
template: `<p>header {{ $route.params.id }} </p>`
}
# 入口
<router-link to="/header/3">123</router-link>
# 規則
routes: [
{
path: '/header/:id',
component: header,
// 如果 props 被設置為 true,route.params 將會被設置為組件屬性
props: true
}
]
# 獲取參數
const header={
// 參數 id 當成參數
props: ['id'],
template: `<p>header {{ id }} </p>`
}
# 入口
<router-link to="/header">123</router-link>
# 規則
routes: [
{
path: '/header',
component: header,
props: { foo: '0000' }
}
]
# 組件
const header={
props: ['foo'],
template: `<p>header {{ foo }} </p>`
}
# 同對象模式一樣
# 區別是props值不一樣
props: to=> {
return { foo: '0000' }
}
實際生活中的應用界面,通常由多層嵌套的組件組合而成。同樣地,URL 中各段動態路徑也按某種結構對應嵌套的各層組件。
借助 vue-router,使用嵌套路由配置,就可以很簡單地表達這種關系。
<div id="app">
<router-view></router-view>
</div>
const User={
template: '<div>User {{ $route.params.id }}</div>'
}
const router=new VueRouter({
routes: [
{ path: '/user/:id', component: User }
]
})
這里的 <router-view> 是最頂層的出口,渲染最高級路由匹配到的組件。同樣地,一個被渲染組件同樣可以包含自己的嵌套 <router-view>。例如,在 User 組件的模板添加一個 <router-view>
const router=new VueRouter({
routes: [
{ path: '/user/:id', component: User,
children: [
{
// 當 /user/:id/profile 匹配成功,
// UserProfile 會被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
},
{
// 當 /user/:id/posts 匹配成功
// UserPosts 會被渲染在 User 的 <router-view> 中
path: 'posts',
component: UserPosts
}
]
}
]
})
注意,以 / 開頭的嵌套路徑會被當作根路徑。 這讓你充分的使用嵌套組件而無須設置嵌套的路徑。
routes: [
{
path: '/header',
component: header,
meta: {
title: 'XXXX'
}
}
]
created() {
document.title=this.$route.meta.title
}
const one={
template: `
<div> <button @click="handleClick('back')">返回 上一頁</button>
<button @click="handleClick('push')">跳轉 two頁</button>
<button @click="handleClick('replace')">替換 two頁</button>
</div>`,
methods: {
handleClick(type) {
if (type=='back') {
// 返回
this.$router.back()
} else if (type=='push') {
// 跳轉 有歷史記錄
this.$router.push('/two')
} else {
// 替換 沒有歷史記錄
this.$router.replace('/two')
}
}
}
}
const two={
template: `<p>two </p>`
}
Vue 2 中配置路由是一個重要的步驟,它可以讓你的單頁應用 (SPA) 在不同的頁面之間進行跳轉而無需重新加載整個頁面。
以下是 Vue 2 中配置路由的基本步驟:
安裝 Vue Router
首先,你需要安裝 Vue Router 包。可以使用以下命令通過 npm 或 yarn 進行安裝:
Bash
npm install vue-router
或者
Bash
yarn add vue-router
創建路由配置
創建一個路由配置對象,該對象包含要定義的路由規則。路由規則是將 URL 與要顯示的組件進行映射的規則。
JavaScript
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
const routes=[
{ path: '/', component: Home },
{ path: '/about', component: About },
{ path: '/contact', component: Contact },
];
const router=new VueRouter({
routes,
});
export default router;
在這個例子中,我們定義了三個路由規則:
/:將映射到 Home 組件
/about:將映射到 About 組件
/contact:將映射到 Contact 組件
在根實例中注冊路由
在你的 Vue 根實例中,你需要將路由實例注冊到 Vue 實例上。這將使 Vue 能夠感知路由并相應地更新視圖。
JavaScript
import Vue from 'vue';
import App from './App.vue';
import router from './router';
new Vue({
router,
render: function (createElement) {
return createElement(App);
},
}).$mount('#app');
使用路由鏈接
要從一個路由導航到另一個路由,可以使用 router-link 組件。router-link 組件就像一個普通的 HTML 鏈接,但它會觸發路由導航而不是重新加載頁面。
HTML
Home
About
Contact
使用路由參數
可以使用路由參數來傳遞數據給路由組件。路由參數在 URL 中定義,可以使用 :param 語法。
JavaScript
const routes=[
{ path: '/user/:id', component: User },
];
在 User 組件中,可以使用 $route.params 對象訪問路由參數。
JavaScript
export default {
template: `
User ID: {{ $route.params.id }}
`,
};
使用路由守衛
路由守衛可以用來在路由導航發生之前或之后執行代碼。這可以用于諸如身份驗證、授權和數據預取等任務。
JavaScript
const router=new VueRouter({
routes,
beforeResolve: (to, from, next)=> {
// 在導航發生之前執行代碼
},
});
這些只是 Vue 2 中配置路由的基礎知識。有關更高級的主題,請參閱 Vue Router 文檔:
*請認真填寫需求信息,我們會在24小時內與您取得聯系。