路由的概念相信大部分同學並不陌生,我們在用 Vue
開發實際專案的時候都會用到 Vue-Router
這個官方外掛程式來幫我們解決路線的問題。它的作用就是根據不同的路徑映射到不同的視圖。本文不再講述路由的基礎使用和API
,不清楚的同學可以自行查閱官方文件vue-router3 對應 vue2 和 vue-router4 對應 vue3。
今天我們主要是談談Vue-Router
的實作原理,有興趣的夥伴可以繼續往下看,大佬請停下來。
本文vue-router 版本為3.5.3
#路由
既然我們在分析路由,我們先來說說什麼是路由,什麼是後端路由、什麼是前端路由。
路由就是根據不同的url
位址展示不同的內容或頁面,早期路由的概念是在後端出現的,透過伺服器端渲染後回傳頁面,隨著頁面越來越複雜,伺服器端壓力越來越大。後來ajax
非同步刷新的出現使得前端也可以對url
進行管理,此時,前端路由就出現了。 【學習影片分享:vue影片教學、web前端影片】
#我們先說後端路由
##。後端路由
後端路由又可稱為伺服器端路由,因為對於伺服器來說,當接收到客戶端發送的HTTP請求,就會根據所請求的
URL,來找到對應的映射函數,然後執行該函數,並將函數的回傳值傳送給客戶端。
URL的映射函數就是一個檔案讀取操作。對於動態資源,映射函數可能是一個資料庫讀取操作,也可能是進行一些資料的處理,等等。
HTML頁面。早期的
jsp就是這種模式。
前端路由
剛剛也介紹了,在前後端沒有分開的時候,服務端都是直接將整個HTML 返回,用戶每次很小的操作都會造成頁面的整個刷新(再加上之前的網路速度還很慢,所以使用者體驗可想而知)。
ajax(Asynchronous JavaScript And XML) 這個技術,這樣用戶每次的操作就可以不用刷新整個頁面了,用戶體驗就大大提升了。
SPA單頁應用程式 就出現了。單頁應用程式不只是在頁面互動是無刷新的,連頁面跳轉都是無刷新的。既然頁面的跳轉是無刷新的,也就是不再向後端請求回傳
HTML頁。
HTML頁面,那該怎麼做呢?所以就有了現在的前端路由。
可以理解為,前端路由就是將先前服務端根據 url 的不同返回不同的頁面的任務交給前端來做。在這個過程中,js會即時偵測url的變化,從而改變顯示的內容。
前端路由優點是使用者體驗好,使用者操作或頁面跳轉不會刷新頁面,並且能快速展現給使用者。缺點是首屏載入慢,因為需要js動態渲染展示內容。而且由於內容是
js動態渲染的所以不利於
SEO。
Vue-Router原理分析階段。
install.js,這個方法會在
Vue.use(VueRouter)的時候被調用。
// install.js import View from './components/view' import Link from './components/link' export let _Vue export function install (Vue) { // 不会重复安装 if (install.installed && _Vue === Vue) return install.installed = true _Vue = Vue const isDef = v => v !== undefined // 为router-view组件关联路由组件 const registerInstance = (vm, callVal) => { let i = vm.$options._parentVnode // 调用vm.$options._parentVnode.data.registerRouteInstance方法 // 而这个方法只在router-view组件中存在,router-view组件定义在(../components/view.js @71行) // 所以,如果vm的父节点为router-view,则为router-view关联当前vm,即将当前vm做为router-view的路由组件 if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) { i(vm, callVal) } } Vue.mixin({ beforeCreate () { // 这里只会进来一次,因为只有Vue根实例才会有router属性。 if (isDef(this.$options.router)) { // 所以这里的this就是Vue根实例 this._routerRoot = this this._router = this.$options.router this._router.init(this) // 将 _route 变成响应式 Vue.util.defineReactive(this, '_route', this._router.history.current) } else { // 子组件会进入这里,这里也是把Vue根实例保存带_routerRoot属性上 this._routerRoot = (this.$parent && this.$parent._routerRoot) || this } // 为router-view组件关联路由组件 registerInstance(this, this) }, destroyed () { // destroyed hook触发时,取消router-view和路由组件的关联 registerInstance(this) } }) // 在原型上注入$router、$route属性,方便快捷访问 Object.defineProperty(Vue.prototype, '$router', { // 上面说到每个组件的_routerRoot都是Vue根实例,所以都能访问_router get () { return this._routerRoot._router } }) // 每个组件访问到的$route,其实最后访问的都是Vue根实例的_route Object.defineProperty(Vue.prototype, '$route', { get () { return this._routerRoot._route } }) // 注册router-view、router-link两个全局组件 Vue.component('RouterView', View) Vue.component('RouterLink', Link) const strats = Vue.config.optionMergeStrategies // use the same hook merging strategy for route hooks strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created }主要做了以下幾件事:
避免重复安装
为了确保 install
逻辑只执行一次,用了 install.installed
变量做已安装的标志位。
传递Vue引用减少打包体积
用一个全局的 _Vue
来接收参数 Vue
,因为作为 Vue
的插件对 Vue
对象是有依赖的,但又不能去单独去 import Vue
,因为那样会增加包体积,所以就通过这种方式拿到 Vue
对象。
注册全局混入
Vue-Router
安装最重要的一步就是利用 Vue.mixin
,在beforeCreate
和destroyed
生命周期函数中注入路由逻辑。
Vue.mixin
我们知道就是全局 mixin
,所以也就相当于每个组件的beforeCreate
和destroyed
生命周期函数中都会有这些代码,并在每个组件中都会运行。
Vue.mixin({ beforeCreate () { if (isDef(this.$options.router)) { this._routerRoot = this this._router = this.$options.router this._router.init(this) Vue.util.defineReactive(this, '_route', this._router.history.current) } else { this._routerRoot = (this.$parent && this.$parent._routerRoot) || this } registerInstance(this, this) }, destroyed () { registerInstance(this) } })
在这两个钩子中,this
是指向当时正在调用钩子的vue实例
。
这两个钩子中的逻辑,在安装流程中是不会被执行的,只有在组件实例化时执行到钩子时才会被调用
先看混入的 beforeCreate
钩子函数
它先判断了this.$options.router
是否存在,我们在new Vue({router})
时,router
才会被保存到到Vue根实例
的$options
上,而其它Vue实例
的$options
上是没有router
的,所以if
中的语句只在this === new Vue({router})
时,才会被执行,由于Vue根实例
只有一个,所以这个逻辑只会被执行一次。
对于根 Vue
实例而言,执行该钩子函数时定义了 this._routerRoot
表示它自身(Vue
根实例);this._router
表示 VueRouter
的实例 router
,它是在 new Vue
的时候传入的;
另外执行了 this._router.init()
方法初始化 router
,这个逻辑在后面讲初始化的时候再介绍。
然后用 defineReactive
方法把 this._route
变成响应式对象,保证_route
变化时,router-view
会重新渲染,这个我们后面在router-view
组件中会细讲。
我们再看下else
中具体干了啥
主要是为每个组件定义_routerRoot
,对于子组件而言,由于组件是树状结构,在遍历组件树的过程中,它们在执行该钩子函数的时候 this._routerRoot
始终指向的离它最近的传入了 router
对象作为配置而实例化的父实例(也就是永远等于根实例)。
所以我们可以得到,在每个vue
组件都有 this._routerRoot === vue根实例
、this._routerRoot._router === router对象
对于 beforeCreate
和 destroyed
钩子函数,它们都会执行 registerInstance
方法,这个方法的作用我们也是之后会介绍。
添加$route、$router
属性
接着给 Vue
原型上定义了 $router
和 $route
2 个属性的 get
方法,这就是为什么我们可以在任何组件实例上都可以访问 this.$router
以及 this.$route
。
Object.defineProperty(Vue.prototype, '$router', { get () { return this._routerRoot._router } }) Object.defineProperty(Vue.prototype, '$route', { get () { return this._routerRoot._route } })
我们可以看到,$router
其实返回的是this._routerRoot._router
,也就是vue
根实例上的router
,因此我们可以通过this.$router
来使用router
的各种方法。
$route
其实返回的是this._routerRoot._route
,其实就是this._router.history.current
,也就是目前的路由对象,这个后面会细说。
注册全局组件
通过 Vue.component
方法定义了全局的 <router-link></router-link>
和 <router-view></router-view>
2 个组件,这也是为什么我们在写模板的时候可以直接使用这两个标签,它们的作用我想就不用笔者再说了吧。
钩子函数的合并策略
最后设置路由组件的beforeRouteEnter
、beforeRouteLeave
、beforeRouteUpdate
守卫的合并策略。
总结
那么到此为止,我们分析了 Vue-Router
的安装过程,Vue
编写插件的时候通常要提供静态的 install
方法,我们通过 Vue.use(plugin)
时候,就是在执行 install
方法。Vue-Router
的 install
方法会给每一个组件注入 beforeCreate
和 destoryed
钩子函数,在beforeCreate
做一些私有属性定义和路由初始化工作。并注册了两个全局组件,然后设置了钩子函数合并策略。在destoryed
做了一些销毁工作。
下面我们再来看看Vue-Router
的实例化。
分析init方法
前面我们提到了在 install
的时候会执行 VueRouter
的 init
方法( this._router.init(this)
),那么接下来我们就来看一下 init
方法做了什么。
init (app: any /* Vue component instance */) { // ... this.apps.push(app) // ... // main app previously initialized // return as we don't need to set up new history listener if (this.app) { return } this.app = app const history = this.history if (history instanceof HTML5History || history instanceof HashHistory) { const handleInitialScroll = routeOrError => { const from = history.current const expectScroll = this.options.scrollBehavior const supportsScroll = supportsPushState && expectScroll if (supportsScroll && 'fullPath' in routeOrError) { handleScroll(this, routeOrError, from, false) } } // 1.setupListeners 里会对 hashchange或popstate事件进行监听 const setupListeners = routeOrError => { history.setupListeners() handleInitialScroll(routeOrError) } // 2.初始化导航 history.transitionTo( history.getCurrentLocation(), setupListeners, setupListeners ) } // 3.路由全局监听,维护当前的route // 当路由变化的时候修改app._route的值 // 由于_route是响应式的,所以修改后相应视图会同步更新 history.listen(route => { this.apps.forEach(app => { app._route = route }) }) }
这里主要做了如下几件事情:
设置了路由监听
const setupListeners = routeOrError => { history.setupListeners() handleInitialScroll(routeOrError) }
这里会根据当前路由模式监听hashchange
或popstate
事件,当事件触发的时候,会进行路由的跳转。(后面说到路由模式的时候会细说)
初始化导航
history.transitionTo( history.getCurrentLocation(), setupListeners, setupListeners )
进入系统会进行初始化路由匹配,渲染对应的组件。因为第一次进入系统,并不会触发hashchange
或者popstate
事件,所以第一次需要自己手动匹配路径然后进行跳转。
路由全局监听
history.listen(route => { this.apps.forEach(app => { app._route = route }) })
当路由变化的时候修改app._route
的值。由于_route
是响应式的,所以修改后相应视图会同步更新。
总结
这里主要是做了一些初始化工作。根据当前路由模式监听对应的路由事件。初始化导航,根据当前的url渲染初始页面。最后切换路由的时候修改_route
,由于_route
是响应式的,所以修改后相应视图会同步更新。
分析VueRouter实例化
实例化就是我们new VueRouter({routes})
的过程,我们来重点分析下VueRouter
的构造函数。
constructor (options: RouterOptions = {}) { // ... // 参数初始化 this.app = null this.apps = [] this.options = options this.beforeHooks = [] this.resolveHooks = [] this.afterHooks = [] // 创建matcher this.matcher = createMatcher(options.routes || [], this) // 设置默认模式和做不支持 H5 history 的降级处理 let mode = options.mode || 'hash' this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false if (this.fallback) { mode = 'hash' } if (!inBrowser) { mode = 'abstract' } this.mode = mode // 根据不同的 mode 实例化不同的 History 对象 switch (mode) { case 'history': this.history = new HTML5History(this, options.base) break case 'hash': this.history = new HashHistory(this, options.base, this.fallback) break case 'abstract': this.history = new AbstractHistory(this, options.base) break default: if (process.env.NODE_ENV !== 'production') { assert(false, `invalid mode: ${mode}`) } } }
这里主要做了如下几件事情:
初始化参数
我们看到在最开始有些参数的初始化,这些参数到底是什么呢?
this.app
用来保存根 Vue
实例。
this.apps
用来保存持有 $options.router
属性的 Vue
实例。
this.options
保存传入的路由配置,也就是前面说的RouterOptions
。
this.beforeHooks
、 this.resolveHooks
、this.afterHooks
表示一些钩子函数。
this.fallback
表示在浏览器不支持 history
新api
的情况下,根据传入的 fallback
配置参数,决定是否回退到hash
模式。
this.mode
表示路由创建的模式。
创建matcher
matcher
,匹配器。简单理解就是可以通过url
找到我们对应的组件。这一块内容较多,这里笔者就不再详细分析了。
确定路由模式
路由模式平时都会只说两种,其实在vue-router
总共实现了 hash
、history
、abstract
3 种模式。
VueRouter
会根据options.mode
、options.fallback
、supportsPushState
、inBrowser
来确定最终的路由模式。
如果没有设置mode
就默认是hash
模式。
确定fallback
值,只有在用户设置了mode:history
并且当前环境不支持pushState
且用户没有主动声明不需要回退(没设置fallback
值位undefined
),此时this.fallback
才为true
,当fallback
为true
时会使用hash
模式。(简单理解就是如果不支持history
模式并且只要没设置fallback
为false
,就会启用hash
模式)
如果最后发现处于非浏览器环境,则会强制使用abstract
模式。
实例化路由模式
根据mode
属性值来实例化不同的对象。VueRouter
的三种路由模式,主要由下面的四个核心类实现
-
History
- 基础类
- 位于
src/history/base.js
-
HTML5History
- 用于支持
pushState
的浏览器 src/history/html5.js
- 用于支持
-
HashHistory
- 用于不支持
pushState
的浏览器 src/history/hash.js
- 用于不支持
-
AbstractHistory
- 用于非浏览器环境(服务端渲染)
src/history/abstract.js
HTML5History
、HashHistory
、AbstractHistory
三者都是继承于基础类History
。
这里我们详细分析下HTML5History
和HashHistory
类。
HTML5History类
当我们使用history
模式的时候会实例化HTML5History类
// src/history/html5.js ... export class HTML5History extends History { _startLocation: string constructor (router: Router, base: ?string) { // 调用父类构造函数初始化 super(router, base) this._startLocation = getLocation(this.base) } // 设置监听,主要是监听popstate方法来自动触发transitionTo setupListeners () { if (this.listeners.length > 0) { return } const router = this.router const expectScroll = router.options.scrollBehavior const supportsScroll = supportsPushState && expectScroll // 若支持scroll,初始化scroll相关逻辑 if (supportsScroll) { this.listeners.push(setupScroll()) } const handleRoutingEvent = () => { const current = this.current // 某些浏览器,会在打开页面时触发一次popstate // 此时如果初始路由是异步路由,就会出现`popstate`先触发,初始路由后解析完成,进而导致route未更新 // 所以需要避免 const location = getLocation(this.base) if (this.current === START && location === this._startLocation) { return } // 路由地址发生变化,则跳转,如需滚动则在跳转后处理滚动 this.transitionTo(location, route => { if (supportsScroll) { handleScroll(router, route, current, true) } }) } // 监听popstate事件 window.addEventListener('popstate', handleRoutingEvent) this.listeners.push(() => { window.removeEventListener('popstate', handleRoutingEvent) }) } // 可以看到 history模式go方法其实是调用的window.history.go(n) go (n: number) { window.history.go(n) } // push方法会主动调用transitionTo进行跳转 push (location: RawLocation, onComplete?: Function, onAbort?: Function) { const { current: fromRoute } = this this.transitionTo(location, route => { pushState(cleanPath(this.base + route.fullPath)) handleScroll(this.router, route, fromRoute, false) onComplete && onComplete(route) }, onAbort) } // replace方法会主动调用transitionTo进行跳转 replace (location: RawLocation, onComplete?: Function, onAbort?: Function) { const { current: fromRoute } = this this.transitionTo(location, route => { replaceState(cleanPath(this.base + route.fullPath)) handleScroll(this.router, route, fromRoute, false) onComplete && onComplete(route) }, onAbort) } ensureURL (push?: boolean) { if (getLocation(this.base) !== this.current.fullPath) { const current = cleanPath(this.base + this.current.fullPath) push ? pushState(current) : replaceState(current) } } getCurrentLocation (): string { return getLocation(this.base) } } export function getLocation (base: string): string { let path = window.location.pathname const pathLowerCase = path.toLowerCase() const baseLowerCase = base.toLowerCase() // base="/a" shouldn't turn path="/app" into "/a/pp" // https://github.com/vuejs/vue-router/issues/3555 // so we ensure the trailing slash in the base if (base && ((pathLowerCase === baseLowerCase) || (pathLowerCase.indexOf(cleanPath(baseLowerCase + '/')) === 0))) { path = path.slice(base.length) } return (path || '/') + window.location.search + window.location.hash }
可以看到HTML5History
类主要干了如下几件事。
继承于
History类
,并调用父类构造函数初始化。实现了
setupListeners
方法,在该方法中检查了是否需要支持滚动行为,如果支持,则初始化滚动相关逻辑,监听了popstate事件
,并在popstate
触发时自动调用transitionTo
方法。实现了
go、push、replace
等方法,我们可以看到,history
模式其实就是使用的history api
。
// 可以看到 history模式go方法其实是调用的window.history.go(n) go (n: number) { window.history.go(n) } // push、replace调用的是util/push-state.js,里面实现了push和replace方法 // 实现原理也是使用的history api,并且在不支持history api的情况下使用location api export function pushState (url?: string, replace?: boolean) { ... const history = window.history try { if (replace) { const stateCopy = extend({}, history.state) stateCopy.key = getStateKey() // 调用的 history.replaceState history.replaceState(stateCopy, '', url) } else { // 调用的 history.pushState history.pushState({ key: setStateKey(genStateKey()) }, '', url) } } catch (e) { window.location[replace ? 'replace' : 'assign'](url) } } export function replaceState (url?: string) { pushState(url, true) }
总结
所以history
模式的原理就是在js
中路由的跳转(也就是使用push
和replace
方法)都是通过history api
,history.pushState
和 history.replaceState
两个方法完成,通过这两个方法我们知道了路由的变化,然后根据路由映射关系来实现页面内容的更新。
对于直接点击浏览器的前进后退按钮或者js
调用 this.$router.go()
、this.$router.forward()
、this.$router.back()
、或者原生js
方法history.back()
、history.go()
、history.forward()
的,都会触发popstate
事件,通过监听这个事件我们就可以知道路由发生了哪些变化然后来实现更新页面内容。
注意history.pushState
和 history.replaceState
这两个方法并不会触发popstate
事件。在这两个方法里面他是有手动调用transitionTo
方法的。
接下来我们再来看看HashHistory类
HashHistory类
当我们使用hash
模式的时候会实例化HashHistory类
。
//src/history/hash.js ... export class HashHistory extends History { constructor (router: Router, base: ?string, fallback: boolean) { super(router, base) // check history fallback deeplinking if (fallback && checkFallback(this.base)) { return } ensureSlash() } setupListeners () { if (this.listeners.length > 0) { return } const router = this.router const expectScroll = router.options.scrollBehavior const supportsScroll = supportsPushState && expectScroll if (supportsScroll) { this.listeners.push(setupScroll()) } const handleRoutingEvent = () => { const current = this.current if (!ensureSlash()) { return } this.transitionTo(getHash(), route => { if (supportsScroll) { handleScroll(this.router, route, current, true) } if (!supportsPushState) { replaceHash(route.fullPath) } }) } // 事件优先使用 popstate // 判断supportsPushState就是通过return window.history && typeof window.history.pushState === 'function' const eventType = supportsPushState ? 'popstate' : 'hashchange' window.addEventListener( eventType, handleRoutingEvent ) this.listeners.push(() => { window.removeEventListener(eventType, handleRoutingEvent) }) } // 其实也是优先使用history的pushState方法来实现,不支持再使用location修改hash值 push (location: RawLocation, onComplete?: Function, onAbort?: Function) { const { current: fromRoute } = this this.transitionTo( location, route => { pushHash(route.fullPath) handleScroll(this.router, route, fromRoute, false) onComplete && onComplete(route) }, onAbort ) } // 其实也是优先使用history的replaceState方法来实现,不支持再使用location修改replace方法 replace (location: RawLocation, onComplete?: Function, onAbort?: Function) { const { current: fromRoute } = this this.transitionTo( location, route => { replaceHash(route.fullPath) handleScroll(this.router, route, fromRoute, false) onComplete && onComplete(route) }, onAbort ) } // 也是使用的history go方法 go (n: number) { window.history.go(n) } ensureURL (push?: boolean) { const current = this.current.fullPath if (getHash() !== current) { push ? pushHash(current) : replaceHash(current) } } getCurrentLocation () { return getHash() } } function checkFallback (base) { const location = getLocation(base) if (!/^\/#/.test(location)) { window.location.replace(cleanPath(base + '/#' + location)) return true } } function ensureSlash (): boolean { const path = getHash() if (path.charAt(0) === '/') { return true } replaceHash('/' + path) return false } // 获取 # 后面的内容 export function getHash (): string { // We can't use window.location.hash here because it's not // consistent across browsers - Firefox will pre-decode it! let href = window.location.href const index = href.indexOf('#') // empty path if (index < 0) return '' href = href.slice(index + 1) return href } function getUrl (path) { const href = window.location.href const i = href.indexOf('#') const base = i >= 0 ? href.slice(0, i) : href return `${base}#${path}` } function pushHash (path) { if (supportsPushState) { pushState(getUrl(path)) } else { window.location.hash = path } } function replaceHash (path) { if (supportsPushState) { replaceState(getUrl(path)) } else { window.location.replace(getUrl(path)) } }
可以看到HashHistory
类主要干了如下几件事。
继承于
History类
,并调用父类构造函数初始化。这里比HTML5History
多了回退操作,所以,需要将history
模式的url
替换成hash
模式,即添加上#
,这个逻辑是由checkFallback
实现的实现了
setupListeners
方法,在该方法中检查了是否需要支持滚动行为,如果支持,则初始化滚动相关逻辑。 监听了popstate事件或hashchange事件
,并在相应事件触发时,调用transitionTo
方法实现跳转。
通过
const eventType = supportsPushState ? 'popstate' : 'hashchange'
我们可以发现就算是hash
模式优先使用的还是popstate
事件。
实现了
go、push、replace
等方法。
我们可以看到,hash
模式实现的push、replace
方法其实也是优先使用history
里面的方法,也就是history api
。
// 可以看到 hash 模式go方法其实是调用的window.history.go(n) go (n: number) { window.history.go(n) } // 在支持新的history api情况下优先使用history.pushState实现 // 否则使用location api function pushHash (path) { if (supportsPushState) { pushState(getUrl(path)) } else { window.location.hash = path } } // 在支持新的history api情况下优先使用history.replaceState实现 // 否则使用location api function replaceHash (path) { if (supportsPushState) { replaceState(getUrl(path)) } else { window.location.replace(getUrl(path)) } }
总结
在浏览器链接里面我们改变hash
值是不会重新向后台发送请求的,也就不会刷新页面。并且每次 hash
值的变化,还会触发hashchange
这个事件。
所以hash
模式的原理就是通过监听hashchange
事件,通过这个事件我们就可以知道 hash
值发生了哪些变化然后根据路由映射关系来实现页面内容的更新。(这里hash
值的变化不管是通过js
修改的还是直接点击浏览器的前进后退按钮都会触发hashchange
事件)
对于hash
模式,如果是在浏览器支持history api
情况下,hash
模式的实现其实是和history
模式一样的。只有在不支持history api
情况下才会监听hashchange
事件。这个我们可以在源码中看出来。
总结
总的来说就是使用 Vue.util.defineReactive
将实例的 _route
设置为响应式对象。在push, replace
方法里会主动更新属性 _route
。而 go,back,forward
,或者通过点击浏览器前进后退的按钮则会在 hashchange
或者 popstate
的回调中更新 _route
。_route
的更新会触发 RoterView
的重新渲染。
对于第一次进入系统,并不会触发hashchange
或者popstate
事件,所以第一次需要自己手动匹配路径然后通过transitionTo
方法进行跳转,然后渲染对应的视图。
以上是一文聊聊Vue-Router的實作原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Vue.js是一種漸進式JavaScript框架,適用於構建複雜的用戶界面。 1)其核心概念包括響應式數據、組件化和虛擬DOM。 2)實際應用中,可以通過構建Todo應用和集成VueRouter來展示其功能。 3)調試時,建議使用VueDevtools和console.log。 4)性能優化可通過v-if/v-show、列表渲染優化和異步加載組件等實現。

Vue.js適合小型到中型項目,而React更適用於大型、複雜應用。 1.Vue.js的響應式系統通過依賴追踪自動更新DOM,易於管理數據變化。 2.React採用單向數據流,數據從父組件流向子組件,提供明確的數據流向和易於調試的結構。

Vue.js適合中小型項目和快速迭代,React適用於大型複雜應用。 1)Vue.js易於上手,適用於團隊經驗不足或項目規模較小的情況。 2)React的生態系統更豐富,適合有高性能需求和復雜功能需求的項目。

實現 Vue 中 a 標籤跳轉的方法包括:HTML 模板中使用 a 標籤指定 href 屬性。使用 Vue 路由的 router-link 組件。使用 JavaScript 的 this.$router.push() 方法。可通過 query 參數傳遞參數,並在 router 選項中配置路由以進行動態跳轉。

Vue 中實現組件跳轉有以下方法:使用 router-link 和 <router-view> 組件進行超鏈接跳轉,指定 :to 屬性為目標路徑。直接使用 <router-view> 組件顯示當前路由渲染的組件。使用 router.push() 和 router.replace() 方法進行程序化導航,前者保存歷史記錄,後者替換當前路由不留記錄。

Vue 中 div 元素跳轉的方法有兩種:使用 Vue Router,添加 router-link 組件。添加 @click 事件監聽器,調用 this.$router.push() 方法跳轉。

Vue.js提供了三種跳轉方式:原生 JavaScript API:使用 window.location.href 進行跳轉。 Vue Router:使用 <router-link> 標籤或 this.$router.push() 方法進行跳轉。 VueX:通過 dispatch action 或 commit mutation 來觸發路由跳轉。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

SublimeText3 Linux新版
SublimeText3 Linux最新版

WebStorm Mac版
好用的JavaScript開發工具

禪工作室 13.0.1
強大的PHP整合開發環境

Atom編輯器mac版下載
最受歡迎的的開源編輯器