从0学习Vue3(9)

162 阅读5分钟

4.Vue全家桶

平时说到全家桶,肯定就是想到肯德基。但这里的Vue全家桶,是基于Vue开发必备的一套组件,具体包含了如下几项:

  1. vue-cli 项目构建工具
  2. vue-router 路由管理工具
  3. vuex 全局变量状态管理工具
  4. axios http 请求工具
  5. 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 项目名称非常简便。这里可以顺便讲一下:创建项目时会有一些选项

  1. 选择配置,里面有默认vue2和vue3还有一个手动配置(可以根据需求配置),那我就选默认vue3辽。然后里面的

    babel: 专门把ES6语法转成浏览器兼容的ES5语法。

    eslint: 用来检查语法、按一定规则自动修复。

  2. 指定以后再安装插件的时候,是使用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>

这样就实现了一个简单的路由跳转啦。在这个过程中我出现过一些问题,经过我的总结有两个:

  1. "multi-word"是eslint的缘故,组件的命名会被限制,所以我才会这样起名字。
  2. "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 导航到不同的位置

导航有两种做法:

  1. 声明式:<router-link :to="...">
  2. 编程式: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添加新纪录,正如它的名字所暗示的那样--它取代了当前的条目。

依旧是分两种做法:

  1. 声明式:<router-link :to="..." replace>
  2. 编程式: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
     }
 ]