4.Vue全家桶
平时说到全家桶,肯定就是想到肯德基。但这里的Vue全家桶,是基于Vue开发必备的一套组件,具体包含了如下几项:
- vue-cli 项目构建工具
- vue-router 路由管理工具
- vuex 全局变量状态管理工具
- axios http 请求工具
- UI框架(element-ui / vant / ivi)
4.1 vue-cli脚手架安装与项目搭建
我们可以自己用webpack手动地搭建一个vue的开发环境,帮助我们我们打包上线,但这样做很复杂。随着技术的不断升级,官方为我们开发了一个vue脚手架:默认已经帮我们搭建好了一套利用Webpack管理vue的项目结构。
命令安装:npm i -g @vue/cli 全局安装 咱电脑就会多一个vue create xxx的命令了
如出现下载缓慢请配置npm淘宝镜像:npm config set registry https://registry.npm.taobao.org
安装完了之后,我们用脚手架创建的项目就会自动有配置文件。可以通过vue -V来查看vue-cli的版本。创建项目是用vue create 项目名称非常简便。这里可以顺便讲一下:创建项目时会有一些选项
-
选择配置,里面有默认vue2和vue3还有一个手动配置(可以根据需求配置),那我就选默认vue3辽。然后里面的
babel: 专门把ES6语法转成浏览器兼容的ES5语法。
eslint: 用来检查语法、按一定规则自动修复。
-
指定以后再安装插件的时候,是使用yarn还是npm,习惯哪个选哪个即可。
这样就成功使用vue的脚手架创建项目了。
4.2 vue-router
4.2.1 vue-router基础
关于vue-router的基础呢,之前已经学习过了,在我的文章:juejin.cn/post/716334…。那么这里我们就实现在脚手架中使用vue-router:
先创建两个简单的组件供我跳转:
<!-- homeCom.vue -->
<template>
<h1>这是Home组件</h1>
</template>
<!-- aboutCom.vue -->
<template>
<h1>这是About组件</h1>
</template>
接着直接在main.js中设置路由:
//main.js
import { createApp } from 'vue'
import App from './App.vue'
import {createRouter,createWebHashHistory} from 'vue-router'
import Home from './components/homeCom.vue'
import About from './components/aboutCom.vue'
const router = createRouter({
history:createWebHashHistory(),
routes:[
{path:"/",component:Home},
{path:'/about',component:About}
]
})
createApp(App).use(router).mount('#app')
那么到App.vue中尝试一下:
<template>
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
<router-view></router-view>
</template>
这样就实现了一个简单的路由跳转啦。在这个过程中我出现过一些问题,经过我的总结有两个:
- "multi-word"是eslint的缘故,组件的命名会被限制,所以我才会这样起名字。
- "inject() can only be used inside setup()..."根据我的尝试,应该是vue-router的版本太高了,我把它降到4.0.12(因为我刚好在看一个教学视频,他是这个版本比较老一点)就可以了然后删了node_modules重新装了一遍,如果你们也遇到类似问题可以试一下。
好了,那当然把路由的设置全部丢在main.js里面完全不对,非常的耦合,所以上面只是为了简单地测试一下能否完成。那么既然可以做到,就可以把router相关的东西提出来,我们单独设置一个router.js文件来存放router的东西,然后再main.js中引入这个router来用就可以了,具体操作我的文章:juejin.cn/post/716334… 里面3.10也演示过了,其实一模一样就不过多阐述~
4.2.2 动态路由匹配
很多时候,我们需要将给定匹配模式的路由映射到同一个组件。例如,我们可能有一个User组件,它应该对所有用户进行渲染,但用户ID不同。在Vue Router中,我们可以在路径中使用一个动态字段来实现,我们称之为 路径参数。
先简单创建一个新的组件,userCom,来尝试接收路由传过来的不同的 id:
<template>
<h1>接收到了ID为{{$route.params.id}}</h1>
</template>
接着去router.js里边,添加这个路由:
const router = createRouter({
history:createWebHashHistory(),
routes:[
{path:"/",component:Home},
{path:'/about',component:About},
{path:'/user/:id',component:User} //在这里,记住要传参的写法是在后面加一个 /:xx
]
})
好嘞然后去App.vue试一试:
<template>
<router-link to="/">Home</router-link>|
<router-link to="/about">About</router-link>|
<router-link to="/user/12">User12</router-link>
<router-view></router-view>
</template>
这样我们在页面中就能看到效果啦,点击User12,下面的组件会展示userCom组件,并且显示接收到的ID为我们这边传过去的‘12’。
同时我们可以在同一个路由中设置多个 路径参数,它们会映射到$route.params上的相应字段。例如:
//在写router的时候
{path:'/user/:id/posts/:postId',component:User}
//在写router-link的时候
<router-link to="/user/12/21">User12</router-link>
//在组件中拿数据
<h1>{{$route.params.id}}</h1>
<h1>{{$route.params.postId}}</h1>
4.2.3 响应路由参数的变化
比如说,我们有一个路由路径是这样path:'/pages/:id',假设用户从/pages/1导航到/pages/2时,相同的组件实例被重复使用(都是pages组件) 。由于两个路由都渲染同个组件,比起销毁再创建,复用则显得 更加高效。不过,这也意味着组件的生命周期钩子不会被调用。
要对同一个组件中参数的变化做出响应的话,我们可以简单地watch$route对象上的任意属性:
同样的,我们创建一个pageCom.vue组件:
<template>
<h1>pageID:{{$route.params.id}}</h1>
</template>
<script>
export default {
beforeCreate(){ //这里为了做对比,在同个组件中切换路由的时候,是不会触发beforeCreate的
console.log('123')
},
created(){ //而会触发created
this.$watch(
() => this.$route.params, //$watch()第一个参数是 监视源
(toParams,previousParams) => {
//toParams为新的路由参数 previousParams为旧的路由参数
//这里面写的是,当路由变化时做出响应
console.log({toParams,previousParams})
})
}
}
</script>
或者,使用beforeRouteUpdate导航守卫:
<template>
<h1>pageID:{{$route.params.id}}</h1>
</template>
<script>
export default {
async beforeRouteUpdate(to,from){
//对路由变化做出响应
console.log({to,from})
}
}
</script>
4.2.4 路由的匹配语法
4.2.4.1 在参数中自定义正则
像 我们在定义 :id这样的参数时,它总是 一个数字,所以我们可以 在它后面加一个括号,为参数指定一个自定义的正则:
const routes = [
{path:'/:id(\d+)'} //代表它只能是数字,里面多的一个反斜杠是 转义字符 \
]
4.2.4.2 可重复的参数
如果我们需要匹配具有多个部分的路由,如/1/2/3,我们应该用*(0个或多个)和+(1个或多个)将参数标记为可重复:
const routes = [
{path:'/:id(\d+)*'} //我在id后面加了*
]
这样做,会为我们提供一个参数数组,而不是一个字符串,简单地说就是我们在组件拿数据的时候拿到的是一个数组。可以打印出来看看:
<template>
<h1>pageID:{{$route.params.id[0]}}</h1>
</template>
<script>
export default {
mounted(){
console.log(this.$route.params.id)
}
}
</script>
会发现打印出来的this.$route.params.id是一个数组。
4.2.4.3 可选的参数
我们可以在参数后面加一个?,意思就是可有可无:
const routes = [
{path:'/user/:userId?'}//意思传不传userId都可以
]
4.2.5 嵌套路由
一些应用程序的UI由多层嵌套的组件组成 。举个例子:我们有一个User组件,那在User组件里面又分了几块,比如我在个人中心点一下头像可以打开我的头像组件、点一个身份证可以看到角色等等。。也就是在组件里面嵌套组件:
那我们先建两个组件,头像avatar.vue 、角色role.vue,由于都是跟user相关的组件,所以在components下创建一个user文件夹,在里面创这两个组件比较好:
<!-- avatar.vue -->
<template>
<h1>头像</h1>
</template>
<!-- role.vue -->
<template>
<h1>管理员</h1>
</template>
接着把它们写到路由中:
const routes = [
{
path:'/user/:id',
component:User,
children:[
//注意这里的实际路径就应该是/user/:id/avatar了
//而且这里的path 不需要写斜杠 /
{path:'avatar',component:Avatar},
{path:'role',component:Role},
]
},
]
那么当然我们会想到,怎么把我们传给user的id,同样传给avatar和role呢?那我们就要用到v-bind绑定数据:
<!-- userCom.vue -->
<template>
<br/>
<router-link :to="'/user/'+$route.params.id+'/avatar'">头像</router-link> |
<router-link :to="'/user/'+$route.params.id+'/role'">角色</router-link>
<router-view></router-view>
</template>
绑定数据就要记得引号里面需要拼接字符串了。
4.2.6 命名路由和命名视图
4.2.6.1 命名路由
除了path之外,我们为任何路由提供name。拥有以下优点:
- 没有硬编码 的URL
params的自动编码 / 解码- 防止你在url中出现打字错误
- 绕过路径排序(如显示一个)
打个比方嗷:
const routes = [
{
path:'/user/:username',
name:'user',
component: User
}
]
有了name之后我们router-link也可以用比较不一样的写法:
<router-link :to="{name:'user',params:{username:'用户名'}}">
User
</router-link>
这样子写起来,就会非常清楚明了。
4.2.6.2 命名视图
视图是什么呢,就是我们的router-view。那在我们的一个.vue文件中呢,不一定只有一个router-view的,比如我想要展示多个视图而不是嵌套展示,就可以在界面中拥有多个单独命名的视图,否则全部视图都只会展示一样的内容,没有起名name会默认是default。
一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件,确保正确使用components配置。基于组件化开发思想,同一个页面我们会把它分成好几个组件吧,那就是在同一个路由下,会有很多个组件吧。那我们原来的写法一个路由下 只有一个组件,是不符合我们思想的,因此下面的起名的目的就是为了 一个路由下可以有多个组件。
我们原本是这样写:
const routes = [
{
path:'/',
name:'home',
component: Home //此时我们的路由中只有一个组件
}
]
既然我们要多个组件了component就得加个s:
const routes = [
{
path:'/',
name:'home',
components: {
default: Home,//默认是Home组件,因为router-view不写name就会默认找default指的这个组件
Left: User, //这里我就不新建组件了,假设我们页面的左边部分需要放一个User,就给它起个名 Left
}
}
]
那么在App.vue中就得可以这么写来拿到:
<template>
<!-- 第一个展示的就是home组件 -->
<router-view></router-view>
<!-- 第二个展示的就是User组件 -->
<router-view name="Left"></router-view>
</template>
4.2.7 编程式导航
4.2.7.1 导航到不同的位置
导航有两种做法:
- 声明式:
<router-link :to="..."> - 编程式:
router.push(...)
之前我们写在模板中的就是声明式的导航,这里我们学习编程式的:
//字符串路径
router.push('/user/1')
//带有路径的对象
router.push({path:'/user/1'})
//命名的路由,并加上参数,让路由建立url
router.push({name:'user',params:{id:1}})
//带查询参数,结果是 /user?id=1
router.push({path:'/user',query:{id:1}})
//带hash,结果是 /about#team
router.push({path:'/about',hash:'#team'})
注意: 如果提供了path,那么params会被忽略。
4.2.7.2 替换当前位置
它的作用类似于router.push,唯一不同的是,它在导航时不会向history添加新纪录,正如它的名字所暗示的那样--它取代了当前的条目。
依旧是分两种做法:
- 声明式:
<router-link :to="..." replace> - 编程式:
router.replace(...)
4.2.7.3 横跨历史
该方法采用了一个整数作为参数,表示在历史堆栈中前进或后退多少步,类似于window.history.go(n),例子:
//向前移动一条记录,与router.forward()相同
router.go(1)
//返回一条记录,与router.back()相同
router.go(-1)
//如果没有那么多条记录,静默失败
4.2.8 重定向和别名
4.2.8.1 重定向 redirect
重定向也是通过routes配置来完成:
//这样就是把 /a 重定向到 /b
const routes = [{path:'/a',redirect:'/b'}]
const routes = [{path:'/a',redirect: { name:'/b' }}]
4.2.8.2 别名 alias
const routes = [{path:'/user',component:User,alias:'/hello'}]
那么它们两有什么区别呢:
其实很好理解,重定向 顾名思义,我们访问的url已经变成了重定向的那个url,例如上面的例子,我们访问/a结果是我们会被重定向到/b。而别名只是给它起了一个名字显示在浏览器上方的地址栏里面,例如我们这里的例子,虽然地址栏里面写的是/hello但实际访问的依旧是/user。
4.2.9 路由组件传参
4.2.9.1 布尔模式
当props设置为true时,route.params将被设置为组件的props。
将props传递给路由组件,在我们的组件中使用$route会与路由紧密耦合,这限制了组件的灵活性,因为它只能用于特定的 URL。虽然这不一定是坏事,但我们可以通过props配置来解除这种行为:
之前传参和取参是这样:
//路由
const routes = [{path:'/user/:id',component:User}]
//组件
<div>{{$route.params.id}}</div>
我们可以变成props传参:
//路由 加一个 props:true
const routes = [{path:'/user/:id',component:User,props:true}]
<!-- 组件 -->
<template>
{{id}}
<router-link :to="'/user/'+id+'/avatar'">头像</router-link> |
<router-link :to="'/user/'+id+'/role'">角色</router-link>
<router-view></router-view>
</template>
<script>
export default {
//这里引入后,模板里就可以直接用了
props:['id']
}
</script>
4.2.9.2 命名视图
对于有命名视图的路由,我们必须为每个命名视图定义props配置:
const routes = [
{
path:'/user/:id',
components:{default:User,sidebar:Sidebar},
props:{default:true,sidebar:false}
}
]
4.2.9.3 对象模式
当props是一个对象时,它将原样设置为组件props。当props是静态的时候很有用。好像很绕,但是我实操一下就知道了:
const routes = [
{
path:'/user/:id',
component: User,
props: {id:100} //这样一写,我们在User组件里面拿到的id,只能是100,无论那边传多少,拿到的都是这里的100
}
]