1.前言
上篇文章说了vue-router的路由的过渡,编程式导航,导航守卫,接下来再看看其他的用法!
2.路由组件传参
如果 props 被设置为 true,route.params 将会被设置为组件属性,我们还是用之前的例子,先把user路由配置把props设置为true!
import Vue from 'vue'
import Router from 'vue-router'
import About from '@/components/About'
import Help from '@/components/Help'
import Home from '@/components/Home'
import Work from '@/components/Work'
import Company from '@/components/Company'
import ContactUs from '@/components/ContactUs'
import User from '@/components/User'
Vue.use(Router)
let router = new Router({
linkActiveClass: "active",
mode: 'history',
routes: [{
path: '/',
component: Home,
},
{
path: '/about',
component: About,
children: [{
path: '',
name: 'work',
component: Work,
},
{
path: 'company',
name: 'company',
component: Company,
},
{
path: 'contactUs',
name: 'contactUs',
component: ContactUs,
}
]
},
{
path: '/help',
name: 'help',
component: Help,
},
{
path: '/user/:id?',
name: 'user',
component: User,
props: true
}
]
});
export default router
再修改下use组件设置 props: ["id"]!
<template>
<div>
<p>user</p>
<div v-for="list in userList" :key="list.id" class="user">
<router-linkexact :to="{name: 'user',params: { id: list.id }}">{{ list.name }}</router-link>
</div>
<div v-if="user" class="userInfo">
<p>姓名:{{ user.name }}</p>
<p>性别:{{ user.sex }}</p>
<p>年龄:{{ user.age }}</p>
</div>
<div>
<span>route.params的值为{{ id }}</span>
</div>
</div>
</template>
<script>
let data = [
{
id: "123",
name: "张三",
sex: "男",
age: 18,
sprot: "running",
skill: "javascript",
},
{
id: "345",
name: "李四",
sex: "男",
age: 28,
sprot: "footabll",
skill: "java",
},
{
id: "456",
name: "小丽",
sex: "女",
age: 20,
sprot: "swimming",
skill: "node.js",
},
];
export default {
name: "User",
props: ["id"],
data() {
return {
userList: data,
user: {}
};
},
beforeRouteUpdate(to, from, next) {
//获取当前路由的动态id,然后找到对应的数据来展示
this.getUser(to.params.id);
next();
},
created() {
this.getUser(this.id);
},
methods: {
getUser(id) {
let item = this.userList.find((us) => {
return us.id == id;
});
this.user = item;
}
},
};
</script>
看下执行效果
当props不是个布尔值是一个对象,它会被按原样设置为组件属性,我们设置 props: {test:100}来测试下
{
path: '/user/:id?',
name: 'user',
component: User,
props: {test:100}
},
export default {
name: "User",
props: ["test"],
//......
}
props还可以设置一个函数,函数的参数为route路由对象
{
path: '/user/:id?',
name: 'user',
component: User,
props: (route)=>{
console.log(route);//路由对象
return {test:route.params.id}
},
},
export default {
name: "User",
props: ["test"],
//......
}
3.路由数据获取
上面的用户的信息数据是自己自定义的,假如我们需要的数据需要从后台获取的,那么我们应该写在导航完成之前还是导航之后呢?其实两种方式都可以,我们先来试下导航完成之前,在组件的 beforeRouteEnter 钩子函数中获取数据,当数据获取成功后再调用 next 方法。
<template>
<div v-if="userList">
<p>user</p>
<div v-for="list in userList" :key="list.id" class="user">
<router-link exact :to="{ name: 'user', params: { id: list.id } }">{{
list.name
}}</router-link>
</div>
<div v-if="user" class="userInfo">
<p>姓名:{{ user.name }}</p>
<p>性别:{{ user.sex }}</p>
<p>年龄:{{ user.age }}</p>
</div>
<div>
<span>props传入的对象的值为{{ test }}</span>
</div>
</div>
</template>
<script>
export default {
name: "User",
props: ["test"],
data() {
return {
userList: "",
user: {},
};
},
beforeRouteEnter(to, from, next) {
fetch(
"https://www.fastmock.site/mock/a2fa6c91407cb7233915a219ea216972/cena/get/test",
{ method: "get" }).then((response) => {
return response.json();
}).then((data) => {
console.log(data);
next((vm) => {
vm.userList = data;
vm.getUser(vm.$route.params.id);
});
});
},
methods: {
getUser(id) {
let item = this.userList.find((us) => {
return us.id == id;
});
this.user = item;
},
},
};
</script>
上面我用的是fetch来获取的数据,来看下请求的数据
如果在导航完成之后来获取数据就需要用到组件的 created 钩子函数,因为这个函数在实例创建完成后被立即调用!我们来试下
<template>
<div v-if="userList">
<p>user</p>
<div v-for="list in userList" :key="list.id" class="user">
<router-link exact :to="{ name: 'user', params: { id: list.id } }">{{
list.name
}}</router-link>
</div>
<div v-if="user" class="userInfo">
<p>姓名:{{ user.name }}</p>
<p>性别:{{ user.sex }}</p>
<p>年龄:{{ user.age }}</p>
</div>
<div>
<span>props传入的对象的值为{{ test }}</span>
</div>
</div>
</template>
<script>
export default {
name: "User",
props: ["test"],
data() {
return {
userList: "",
user: {},
};
},
created() {
fetch(
"https://www.fastmock.site/mock/a2fa6c91407cb7233915a219ea216972/cena/get/test",
{ method: "get" }).then((response) => {
return response.json();
}).then((data) => {
console.log(data);
this.userList = data;
this.getUser(this.$route.params.id);
});
},
methods: {
getUser(id) {
let item = this.userList.find((us) => {
return us.id == id;
});
this.user = item;
},
},
};
</script>
两种方法获取都可以,如果在导航完成之后获取数据可以加loading效果,让用户体验更好!
4.路由懒加载
懒加载就是按需加载,开始不加载,等用到你的时候再加载,那么路由懒加载的意思是把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件!这里用到两个东西,第一个是vue异步组件加载,第二个是webpack代码分割功能,下面来逐个进行分析,先说vue异步组件加载,还是看例子更好说明,我们现在把help做个2秒之后加载
import Vue from 'vue'
import Router from 'vue-router'
import About from '@/components/About'
import Help from '@/components/Help'
import Home from '@/components/Home'
import Work from '@/components/Work'
import Company from '@/components/Company'
import ContactUs from '@/components/ContactUs'
import User from '@/components/User'
Vue.use(Router)
let router = new Router({
linkActiveClass: "active",
mode: 'history',
routes: [{
path: '/',
component: Home,
},
{
path: '/about',
component: About,
children: [{
path: '',
name: 'work',
component: Work,
},
{
path: 'company',
name: 'company',
component: Company,
},
{
path: 'contactUs',
name: 'contactUs',
component: ContactUs,
}
]
},
{
path: '/help',
name: 'help',
component: (resolve)=>{
setTimeout(()=>{
alert('开始加载组件');
resolve(Help)
},2000)
}
},
{
path: '/user/:id?',
name: 'user',
component: User
}
]
});
export default router
或者我们也可以这样写,上面就不需要引入Help组件了(import Help from '@/components/Help'),直接用require,webpack会自动帮你加载
{
path: '/help',
name: 'help',
component: (resolve) => {
setTimeout(()=>{
alert('开始加载组件');
resolve(require('@/components/Help'))
},2000)
}
}
两种效果都一样,看下面效果,2秒之后渲染出help组件
再来说说webpack代码分割功能,之前是所有的js代码都加载一个app.js文件里面,因为配置是这样,不懂的看下我之前写的
vue全家桶系列之vue-cli
上面演示的代码有home,about,help,user 4个组件,我现在不想它都弄在一个app.js文件,我想每个组件都对应一个js文件(并且按需加载),那么就要用到webpack代码分割功能了,这里有两种写法,先看第一种写法require.ensure,require.ensure是webpack 1支持的异步加载方式
import Vue from 'vue'
import Router from 'vue-router'
import Work from '@/components/Work'
import Company from '@/components/Company'
import ContactUs from '@/components/ContactUs'
Vue.use(Router)
let router = new Router({
linkActiveClass: "active",
mode: 'history',
routes: [{
path: '/',
component: (resolve) => {
return require.ensure([],()=>{
resolve(require('@/components/Home'));
})
},
},
{
path: '/about',
component: (resolve) => {
return require.ensure([],()=>{
resolve(require('@/components/About'));
})
},
children: [{
path: '',
name: 'work',
component: Work,
},
{
path: 'company',
name: 'company',
component: Company,
},
{
path: 'contactUs',
name: 'contactUs',
component: ContactUs,
}
]
},
{
path: '/help',
name: 'help',
alias: "/123",
component: (resolve) => {
return require.ensure([],()=>{
resolve(require('@/components/Help'));
})
}
},
{
path: '/user/:id?',
name: 'user',
component: (resolve) => {
return require.ensure([],()=>{
resolve(require('@/components/User'));
})
}
}
]
});
export default router
来看下效果,你可以看到当切换导航匹配到这个组件时候,组件才加载,加载方式是用过head头部插入script标签的形式来异步加载的(动态插入的),你看到的0.1,2,3.js都是异步chunk的名字,异步chunk默认没有名字,其默认值是[id].js,这也是为什么在例子中看到的是全是数子命名的js,如果有更多的异步chunk,那么命名会变成数值依次增加命名
上面需要有点注意的是观察面板中Initiator字段,可以发现它是由app.js产生的请求
可能你会觉得产生的资源名字都是数字id,没有什么可读性,我们需要需要一些有意义的名字,以便以管理,那么这时就用到require.ensure第三个参数了,require.ensure的三个参数依次是(依赖,回调函数,[chunk名字]),由于代码比较多,我就只修改一个组件作为例子
{
path: '/user/:id?',
name: 'user',
component: (resolve) => {
return require.ensure([],()=>{
resolve(require('@/components/User'));
},"User")
}
}
看下面执行效果,chunk的名字都改变了,也变得有可读性了!
那么我想把4个组件都放到一个异步chunk里面,那就把require.ensure第三个参数都改成一样的名字呗!这里不列出修改的代码了直接看结果吧
再来看下第二种webpack代码分割功能 import,这个单词大家比较熟悉了,这里注意需要注意的是如果你使用的是 Babel,你将需要添加 syntax-dynamic-import 插件,才能使 Babel 可以正确地解析语法,我们项目里面是这个"stage-2"(babel-preset-stage-2)里面
下面我们来修改下我们的代码
import Vue from 'vue'
import Router from 'vue-router'
import Work from '@/components/Work'
import Company from '@/components/Company'
import ContactUs from '@/components/ContactUs'
Vue.use(Router)
let router = new Router({
linkActiveClass: "active",
mode: 'history',
routes: [{
path: '/',
component: () => {
return import('@/components/Home')
},
},
{
path: '/about',
component: () => {
return import('@/components/About')
},
children: [{
path: '',
name: 'work',
component: Work,
},
{
path: 'company',
name: 'company',
component: Company,
},
{
path: 'contactUs',
name: 'contactUs',
component: ContactUs,
}
]
},
{
path: '/help',
name: 'help',
alias: "/123",
component: () => {
return import('@/components/Help')
}
},
{
path: '/user/:id?',
name: 'user',
component: () => {
return import('@/components/User')
}
}
]
});
export default router
那么需要你希望异步chunk的名字有可读性,你也可以自己设置名字
import Vue from 'vue'
import Router from 'vue-router'
import Work from '@/components/Work'
import Company from '@/components/Company'
import ContactUs from '@/components/ContactUs'
Vue.use(Router)
let router = new Router({
linkActiveClass: "active",
mode: 'history',
routes: [{
path: '/',
component: () => {
return import(/* webpackChunkName: "Home" */ '@/components/Home')
},
},
{
path: '/about',
component: () => {
return import(/* webpackChunkName: "About" */ '@/components/About')
},
children: [{
path: '',
name: 'work',
component: Work,
},
{
path: 'company',
name: 'company',
component: Company,
},
{
path: 'contactUs',
name: 'contactUs',
component: ContactUs,
}
]
},
{
path: '/help',
name: 'help',
alias: "/123",
component: () => {
return import(/* webpackChunkName: "Help" */ '@/components/Help')
}
},
{
path: '/user/:id?',
name: 'user',
component: () => {
return import(/* webpackChunkName: "User" */ '@/components/User')
}
}
]
});
export default router
如果你希望多个组件在一个异步chunk里面,把webpackChunkName改成一样的名字即可,由于webpack2开始引入了improt函数,并且官方也更推荐它,注意这个import和正常es6语法import不同,通过import函数加载的模块及其依赖会被异步进行加载,并返回一个promise对象
5.History模式服务器配置
之前做的所有的例子都是在开发环境下的,当我们完成项目后是需要发布在生产环境下的(放到服务器上给用户看的),接下来我们来试试,看下生产环境的命令 npm run build,其实是用 node.js去执行 buld目录下的build.js配置文件
执行npm run build之后你会发现项目多了个dist文件夹,这个就是webpack帮我们打包后的目录
那直接复制这个dist文件到本地的nginx服务器环境目录下,来运行下发现报错,index.html插入的css.js全找不到
那是因为你把整个dist拷贝过来的,路径前面应该再加个dist目录(src='/dist/static/js/...')如果只是拷贝dist里面的文件那就没问题,那么我们应该该下生产环境下的配置文件(默认路径是在根目录下的)
再发布下,发现正常访问了,但是又发现当切换导航的时候,刷新下直接报错404 Not Found
那是因为在服务器环境,当你刷新页面后,服务器会去根目录下找这个help文件,显然是找不到的,所以history模式需要前后端配合,因为history并没有去请求服务器该路径下的资源,一旦刷新就会挂了,所以需要后端将不存在的路径请求重定向到dist文件下的index.html!那么需要修改下nginx的配置
改完配置之后重启nginx,看下面效果,刷新不在报错正常显示
这里主要演示了下nginx如何配置,还有Apache,Node.js等如何配置可以去官网看下,官网有详细的配置教程,这里就不多说了!接下来后面的文章我们来说下vuex,如果觉得写的可以,能帮助到你,可以点个关注!多谢!