/
router,路由,我理解的其实就是,让不一样的路径对应的不一样的视图。在vue里,其实视图就至关于组件。也就是让不一样的路径对应不一样的组件。换言之,router就是规定了路径和组件一一对应。这句多是理解vue-touter的核心。javascript
vue官网对render解释的很清楚,这边我简单的说下。 通常咱们写vue组件的时候,会写template,但任何template均可以用render函数代替,事实上,vue就是把template转换为render函数的,之因此咱们用template,是由于其易读易写。html
怎么用render函数替换template呢?
简单的举个例子,你的template是<h1 :title="blogTitle">{{ blogTitle }}</h1>
,对应的render函数以下vue
render: function (createElement) {
// `createElement`的参数通常有三个,第一个必填就是标签名或者组件名,第二个是这个标签的属性,第三个就是子节点。
return createElement('h1', {attrs:{title:this.blogTitle}}, [this.blogTitle])
}
复制代码
createElement
其实更准确的名字是createNodeDescription
,由于它所包含的信息会告诉 Vue 页面上须要渲染什么样的节点,包括及其子节点的描述信息。也就是“虚拟节点 (virtual node)”,简写为“VNode”。“虚拟 DOM”是对由 Vue 组件树创建起来的整个 VNode 树的称呼。java
开始总觉得,要想不一样路径就必需要vue-router
,其实没必要。vue官方文档有个很好的demo,这边挪用下逻辑。若是你只是须要简单的路由,也是能够直接这么用的。node
// <div id="app"><div>
const Home = { template: "<p>home page</p>" };
const About = { template: "<p>about page</p>" };
const NotFound = { template: "<p>Page not found</p>" };
// 路由 路径和组件一一对应
const routes = {'/':Home,'about':About}
var app = new Vue({
el: "#app",
data: {
// 拿到地址栏路径
currentRoute: window.location.pathname
},
computed: {
// 由routes获得路径对应的组件
ViewComponent() {
return routes[this.currentRoute] || NotFound;
}
},
// 这边用render了
render(h) {
return h(this.ViewComponent);
}
});
// 历史记录的处理
window.addEventListener('popstate', () => {
app.currentRoute = window.location.pathname
})
复制代码
vue-router官网,如下我简单的总结下。代码示例统一在末尾。webpack
vue-router
分两种模式,history
模式和hash
模式(地址栏有#),hash
模式利用#不会跳转,history
模式利用pushState
和popState
。router-link
和router-view
,<router-link to="/foo">Go to Foo</router-link>
至关于<a href="/foo">Go to Foo</a>
,事实上也的确会被渲染成a标签。router-view
是占位符的感受,会将路径匹配的组件显示在这里。this.$router
访问全部路由,访问当前路由this.$route
,多个r就是多的意思,这么记着吧,打印能够看看这两个path: '*'
匹配全部路由,一般放在最后匹配404(感受像express)。使用通配符的时候,this.$route.params.pathMatch = *所表示的部分
/user/create /user/list
这种。注意的坑是,以 /
开头的嵌套路径会被看成根路径,因此通常嵌套的路由是不要/
router.push(location, onComplete?, onAbort?)
至关于<router-link to="/foo">
,replace会替换掉当前的历史记录,go(n)表示前进或者后退。若是 history 记录不够用,那就默默地失败呗。router-view
跟slot类似,不写name的话,默认name是default,对应的router文件里,components变成对象,{name:组件}
{ path: '/b', component: B, alias: '/a' }
{ path: '/a', redirect: '/b' }
watch $route
对象来应对这些变化,或在组件内使用 beforeRouteUpdate
。一旦用钩子,请必定确保要调用 next
方法,不然钩子就不会被 resolved
。想要去每个路径都要判断时用router.beforeEach(to,from,next)
,next('/user')
能够直接中断去另外的路径,next(false)
中断跳转。若是只是想在去某个路径的时候作些事,能够在对应的路径下,写beforeEnter(to,from,next)
。若是想要禁止用户在还未保存修改前忽然离开,能够在组件内设置beforeRouteLeave (to, from , next)
,这里注意next的路径最好不是to.path否则会死循环=。=。/foo/bar
这个 URL 将会匹配父路由记录以及子路由记录。meta取的方式 遍历$route.matched 来检查路由记录中的 meta 字段<transition name="slide">
解决的就用这个吧scrollBehavior (to, from, savedPosition) { // return 指望滚动到哪一个的位置 }
/user/user-list
)path:'/user'
),拿到components,(如找到{default:Foo,a:Bar}
)<router-view/>
替换成Foo组件
,<router-view name="a"/>
替换成Bar组件
path:'/user'
下面的children,发现目标path:'user-list'
,拿到components,找到{default:Second,a:Zoo}
user-list
是user的children,因此将user.vue里面的<router-view/>
替换成Second组件
,<router-view name="a"/>
替换成Zoo组件
<!-- 示例1 -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 组件来导航. -->
<!-- 经过传入 `to` 属性指定连接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
<script> // 0. 若是使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter) // 1. 定义 (路由) 组件。也就是视图!!!! // 能够从其余文件 import 进来 const Foo = { template: '<div>foo</div>' } const Bar = { template: '<div>bar</div>' } // 2. 定义路由。也就是什么路径显示什么视图(组件)!!! // 每一个路由应该映射一个组件。 其中"component" 能够是 // 经过 Vue.extend() 建立的组件构造器, // 或者,只是一个组件配置对象。 // 咱们晚点再讨论嵌套路由。 const routes = [ { path: '/foo', component: Foo }, // 懒加载模式,会在合适的时机加载 bar.js,webpackChunkName就是将此组件的代码命名为bar.js 看控制台的network { path: '/bar', component: () => import(/* webpackChunkName: "bar" */ './views/Bar.vue'), }, // 注意 404 { path: '*', component: () => import(/* webpackChunkName: "404" */ './views/404.vue'), }, ] ] // 3. 建立 router 实例,而后传 `routes` 配置。 // 你还能够传别的配置参数, 不过先这么简单着吧。 const router = new VueRouter({ routes // (缩写) 至关于 routes: routes }) // 4. 建立和挂载根实例。!!!挂载在根实例下,也就是任何组件内均可以经过this.$router知道内容 // 记得要经过 router 配置参数注入路由, // 从而让整个应用都有路由功能 const app = new Vue({ router }).$mount('#app') // 如今,应用已经启动了! </script>
复制代码
// 示例1 js
// Home.vue
export default {
computed: {
username () {
// 咱们很快就会看到 `params` 是什么
return this.$route.params.username
}
},
methods: {
goBack () {
window.history.length > 1
? this.$router.go(-1)
: this.$router.push('/')
}
}
}
复制代码
/* 示例2:有嵌套路由的话 */
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
}
]
}
]
/* 示例3:router.push使用 */
// push的两种参数状况,尽可能用路径的形式,用name的话有时候子路由可能不太方便
// -> /user/123
router.push({ path: `/user/${userId}` })
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { userId }})
// replace
router.replace({ path: 'register', query: { userId }})
router.go(1)
router.go(-1)
/* 示例4:router-view多个的状况 */
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
/* 示例5:重定向和别名 */
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
// { path: '/b', component: B, alias: '/a' }
]
})
/* 示例6:导航守卫 */
const router = new VueRouter({ ... })
// router上面的钩子
router.beforeEach((to, from, next) => {
next()
// next(false)
// next('/')
})
router.afterEach((to, from) => {
// ...
})
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
// route上的钩子
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
// 组件内的钩子
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 由于当守卫执行前,组件实例还没被建立
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,可是该组件被复用时调用
// 举例来讲,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 因为会渲染一样的 Foo 组件,所以组件实例会被复用。而这个钩子就会在这个状况下被调用。
// 能够访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 能够访问组件实例 `this`
}
}
/* 示例7:meta的使用 */
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
children: [
{
path: 'bar',
component: Bar,
// a meta field
meta: { requiresAuth: true }
}
]
}
]
})
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
if (!auth.loggedIn()) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next() // 确保必定要调用 next()
}
})
/* 示例8:跳转路径的特效 */
<transition>
<router-view></router-view>
</transition>
const Foo = {
template: `
<transition name="slide">
<div class="foo">...</div>
</transition>
`
}
/* 示例9:根据路径的参数去请求数据 这边显示跳转以后请求数据 */
export default {
data () {
return {
loading: false,
post: null,
error: null
}
},
created () {
// 组件建立完后获取数据,
// 此时 data 已经被 observed 了
this.fetchData()
},
watch: {
// 若是路由有变化,会再次执行该方法
'$route': 'fetchData'
},
methods: {
fetchData () {
this.error = this.post = null
this.loading = true
// replace getPost with your data fetching util / API wrapper
getPost(this.$route.params.id, (err, post) => {
this.loading = false
if (err) {
this.error = err.toString()
} else {
this.post = post
}
})
}
}
}
/* 示例10:控制滚动条的位置 */
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}
复制代码
需求:github
vue create router-apply
,先选择history模式好了router-link router-view
/user
路径的时候跳转到/user/create-user
beforeEach
钩子// main.js
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.needLogin)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
if (!isLogin) {
next({
path: '/login',
// 方便登陆以后返回来
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next() // 确保必定要调用 next()
}
})
// router.js
{
path: '/user',
name: 'user',
component: () => import(/* webpackChunkName: "user" */ './views/User.vue'),
children: [
{
path: 'create-user',
alias: '',
component: () => import(/* webpackChunkName: "createUser" */ './views/CreateUser.vue')
},
{
path: 'user-list',
component: () => import(/* webpackChunkName: "userList" */ './views/UserList.vue')
}
],
meta: {
needLogin: true
}
}
复制代码