Vue.js学习

声明:本文章仅供个人或他人学习参考,无其他目的

参考视频:
https://www.bilibili.com/video/BV15741177Eh

https://www.bilibili.com/video/BV1zq4y1p7ga

1.简单认识Vuejs

Vue (读音 /vjuː/,类似于 view),不要读错。

Vue是一个渐进式的框架,什么是渐进式的呢?
渐进式意味着你可以将Vue作为你应用的一部分嵌入其中,带来更丰富的交互体验。

如果你希望将更多的业务逻辑使用Vue实现,那么Vue的核心库以及其生态系统。比如Core+Vue-router+Vuex,也可以满足你各种各样的需求。

Vue有很多特点和Web开发中常见的高级功能

  • 解耦视图和数据
  • 可复用的组件
  • 前端路由技术
  • 状态管理
  • 虚拟DOM

2.MVVM

MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。当然这些事 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。
在这里插入图片描述
ViewModel 作为 MVVM 的核心,是它把当前页面的数据源(Model)和页面的结构(View)连接在了一起。
当数据源发生变化时,会被 ViewModel 监听到,VM 会根据最新的数据源自动更新页面的结构
当表单元素的值发生变化时,也会被 VM 监听到,VM 会把变化过后最新的值自动同步到 Model 数据源中

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.Vue生命周期

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子(回调)的函数,这给了用户在不同阶段添加自己的代码的机会。

3.1生命周期图示

在这里插入图片描述
在这里插入图片描述

3.2组件的生命周期

在这里插入图片描述

生命周期函数

在这里插入图片描述

注意:在实际开发中,created 是最常用的生命周期函数!

4.Vue 的指令

4.1 内容渲染指令

内容渲染指令用来辅助开发者渲染 DOM 元素的文本内容。常用的内容渲染指令有如下 3 个:

  • v-text
  • {{ }}
  • v-html
    在这里插入图片描述
    注意:v-text 指令会覆盖元素内默认的值。

vue 提供的 {{ }} 语法,专门用来解决 v-text 会覆盖默认文本内容的问题。这种 {{ }} 语法的专业名称是插值表达
式(英文名为:Mustache)。
在这里插入图片描述
同时{{ }}语法里可写一些简单的表达式
在这里插入图片描述
v-text 指令和插值表达式只能渲染纯文本内容。如果要把包含 HTML 标签的字符串渲染为页面的 HTML 元素,
则需要用到 v-html 这个指令:

4.2 属性绑定指令

  • v-once
    只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
  • v-pre
    跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
    例如:
 <span v-pre>{{ this will not be compiled }}</span>

页面显示:{{ this will not be compiled }}

  • v-cloak
    这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。
  • v-blind
    如果需要为元素的属性动态绑定属性值,则需要用到 v-bind 属性绑定指令。
    由于 v-bind 指令在开发中使用频率非常高,因此,vue 官方为其提供了简写形式(简写为英文的 : )。
    在这里插入图片描述

4.3v-bind动态绑定class

可以用v-bind绑定class并赋予一个对象,对象中有多个键值对,键为class名,值为Boolean类型
在这里插入图片描述
这种方式可以有很多的应用,如:
在这里插入图片描述

数组中可以放多个三元表达式
在这里插入图片描述

在这里插入图片描述
注意这种写法,vue会对类名进行合并,并不会覆盖。

同时也可以利用v-bind:style来绑定一些CSS内联样式
但是注意在写CSS属性名的时候,比如font-size等带有中划线‘-’

  1. 可以使用驼峰式: fontSize
  2. 可以使用短横线分隔: ‘font-size’
 <div id="app">
  <div v-bind:style="{color:currentColor,fontSize:fontsize+'px'}">{{message}}</div>
</div>

注意:在 vue 提供的模板渲染语法中,除了支持绑定简单的数据值之外,还支持 Javascript 表达式的运算,例如:
在这里插入图片描述

  • v-on
    作用:绑定事件监听器
    缩写:@
    预期:Function | Inline Statement | Object
    参数:event

在这里插入图片描述

v-on可以简写为@
在这里插入图片描述
v-on传递参数问题
当通过methods中定义方法,以供@click调用时,需要注意参数问题:
情况一:如果该方法不需要额外参数,那么方法后的()可以不添加。
但是注意:如果方法本身中有一个参数,那么会默认将原生事件event参数传递进去
情况二:如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件。
在这里插入图片描述
在这里插入图片描述

v-on修饰符
.stop - 调用 event.stopPropagation()。
.prevent - 调用 event.preventDefault()。
.capture - 添加事件侦听器时使用 capture 模式。
.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
.{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
.native - 监听组件根元素的原生事件。
.once - 只触发一次回调。
.left - (2.2.0) 只当点击鼠标左键时触发。
.right - (2.2.0) 只当点击鼠标右键时触发。
.middle - (2.2.0) 只当点击鼠标中键时触发。
.passive - (2.3.0) 以 { passive: true } 模式添加侦听器

在这里插入图片描述

  • v-if、v-else-if、v-else

这三个指令与JavaScript的条件语句if、else、else if类似。
Vue的条件指令可以根据表达式的值在DOM中渲染或销毁元素或组件

在这里插入图片描述
v-if的原理:
v-if后面的条件为false时,对应的元素以及其子元素不会渲染。也就是根本没有不会有对应的标签出现在DOM中。

在这里插入图片描述

上述案例小问题:

如果我们在有输入内容的情况下,切换了类型,我们会发现文字依然显示之前的输入的内容。
但是按道理讲,我们应该切换到另外一个input元素中了。
在另一个input元素中,我们并没有输入内容。
为什么会出现这个问题呢?
问题解答:
这是因为Vue在进行DOM渲染时,出于性能考虑,会尽可能的复用已经存在的元素,而不是重新创建新的元素。
在上面的案例中,Vue内部会发现原来的input元素不再使用,直接作为else中的input来使用了。
解决方案:
如果我们不希望Vue出现类似重复利用的问题,可以给对应的input添加key
并且我们需要保证key的不同
在这里插入图片描述

  • v-show
    v-show用法与v-if大致一样,不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS property display。
    注意,v-show 不支持 < template> 元素,也不支持 v-else。

v-if和v-show对比

  1. v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
  2. v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
  3. 相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
  4. 一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
    较好;如果在运行时条件很少改变,则使用 v-if 较好。
  • v-for
    当我们有一组数据需要进行渲染时,我们就可以使用v-for来完成。
    v-for的语法类似于JavaScript中的for循环。
    格式如下:item in items的形式

如果在遍历的过程中不需要使用索引值
语法格式:v-for=“movie in movies”
依次从movies中取出movie,并且在元素的内容中,我们可以使用Mustache语法,来使用movie
如果在遍历的过程中,我们需要拿到元素在数组中的索引值呢?
语法格式:v-for=(item, index) in items
其中的index就代表了取出的item在原数组的索引值。

在这里插入图片描述

v-for也可以遍历对象,可以用value-key的形式来获取
在这里插入图片描述
结果:
在这里插入图片描述

组件的key属性
官方推荐我们在使用v-for时,给对应的元素或组件添加上一个:key属性。
我们需要使用key来给每个节点做一个唯一标识。
key的作用主要是为了高效的更新虚拟DOM

<div v-for="item in items" v-bind:key="item.id">
  <!-- 内容 -->
</div>

小补充:
因为Vue是响应式的,所以当数据发生变化时,Vue会自动检测数据变化,视图会发生对应的更新。
Vue中包含了一组观察数组编译的方法,使用它们改变数组也会触发视图的更新。

在这里插入图片描述

  • v-mode
    Vue中使用v-model指令来实现表单元素和数据的双向绑定
    在这里插入图片描述
    解释:
    当我们在输入框输入内容时
    因为input中的v-model绑定了message,所以会实时将输入的内容传递给message,message发生改变。
    当message发生改变时,因为上面我们使用Mustache语法,将message的值插入到DOM中,所以DOM会发生响应的改变。
    所以,通过v-model实现了双向的绑定。

v-mode原理
v-model其实是一个语法糖,它的背后本质上是包含两个操作:
1.v-bind绑定一个value属性
2.v-on指令给当前元素绑定input事件
在这里插入图片描述

v-model 绑定radio
在这里插入图片描述

v-model 绑定checkbox
复选框分为两种情况:单个勾选框和多个勾选框
单个勾选框:
v-model即为布尔值。
此时input的value并不影响v-model的值。
多个复选框:
当是多个复选框时,因为可以选中多个,所以对应的data中属性是一个数组。
当选中某一个时,就会将input的value添加到数组中。
在这里插入图片描述

v-model 绑定select
在这里插入图片描述

补充:值绑定
动态的给value赋值,通过v-bind:value动态的给value绑定值
在这里插入图片描述

v-mode修饰符

  1. lazy修饰符:

    默认情况下,v-model默认是在input事件中同步输入框的数据的。
    也就是说,一旦有数据发生改变对应的data中的数据就会自动发生改变。
    lazy修饰符可以让数据在失去焦点或者回车时才会更新

  2. number修饰符:

    默认情况下,在输入框中无论我们输入的是字母还是数字,都会被当做字符串类型进行处理。
    但是如果我们希望处理的是数字类型,那么最好直接将内容当做数字处理。
    number修饰符可以让在输入框中输入的内容自动转成数字类型

  3. trim修饰符:

    如果输入的内容首尾有很多空格,通常我们希望将其去除
    trim修饰符可以过滤内容左右两边的空格

在这里插入图片描述

5.过滤器和侦听器

5.1过滤器(Filters)

过滤器常用于文本的格式化。例如:
hello -> Hello
过滤器应该被添加在 JavaScript 表达式的尾部,由“管道符”进行调用
在这里插入图片描述
过滤器可以用在两个地方:插值表达式v-bind 属性绑定。

定义过滤器:
在这里插入图片描述

私有过滤器和全局过滤器:
在 filters 节点下定义的过滤器,称为“私有过滤器”,因为它只能在当前vm 实例所控制的el 区域内使用。 如果希望在多个vue 实例之间共享过滤器,则可以按照如下的格式定义全局过滤器:

在这里插入图片描述
过滤器可以串联地进行调用
在这里插入图片描述

过滤器传参
在这里插入图片描述
注意:过滤器仅在vue 2.x 和 1.x 中受支持,在vue 3.x 的版本中剔除了过滤器相关的功能。
如果使用的是 2.x 版本的 vue,则依然可以使用过滤器相关的功能
如果项目已经升级到了 3.x 版本的vue,官方建议使用计算属性方法代替被剔除的过滤器功能

补充:使用全局过滤器对时间进行格式化

Vue.filter('dateFormat', function (originVal) {
  const dt = new Date(originVal)
  const y = dt.getFullYear()
  // getMonth是从0开始的,padStart(a,b)代表有a位,不足用b补充
  const m = (dt.getMonth() + 1 + '').padStart(2, '0')
  const d = (dt.getDate() + '').padStart(2, '0')

  const hh = (dt.getHours() + '').padStart(2, '0')
  const mm = (dt.getMinutes() + '').padStart(2, '0')
  const ss = (dt.getSeconds() + '').padStart(2, '0')

  return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
})

5.2侦听器(watch)

watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。

在这里插入图片描述

watch侦听器例子:
监听username 值的变化,并使用axios 发起Ajax 请求,检测当前输入的用户名是否可用:

watch: {
// 监听 username 值的变化
async username(newVal) {
if (newVal === '') return
// 使用 axios 发起请求,判断用户名是否可用
const { data: res } = await axios.get('https://www.escook.cn/api/finduser/' + newVal)  console.log(res)
}
}

immediate 选项
默认情况下,组件在初次加载完毕后不会调用 watch 侦听器。如果想让 watch 侦听器立即被调用,则需要使 用 immediate 选项。示例代码如下:

watch: {  
	username: {
	// handler 是固定写法,表示当 username 的值变化时,自动调用 handler 处理函数
	handler: async function (newVal) {  if (newVal === '') return
	const { data: res } = await axios.get('https://www.escook.cn/api/finduser/' + newVal)  console.log(res)
	},
	// 表示页面初次渲染好之后,就立即触发当前的 watch 侦听器
	immediate: true
	}
}

deep 选项
如果 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选 项,代码示例如下:
在这里插入图片描述

监听对象单个属性的变化
如果只想监听对象中单个属性的变化,则可以按照如下的方式定义 watch 侦听器:
在这里插入图片描述

6.计算属性

6.1什么是计算属性?

我们知道,在模板中可以直接通过插值语法显示一些data中的数据。
但是在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示
比如我们有firstName和lastName两个变量,我们需要显示完整的名称。
但是如果多个地方都需要显示完整的名称,我们就需要写多个{{firstName}} {{lastName}}
我们可以将上面的代码换成计算属性:
计算属性是写在实例的computed选项中的。

在这里插入图片描述
在这里插入图片描述

6.2计算属性的setter和getter

每个计算属性都包含一个getter和一个setter
在这里插入图片描述

6.3methods和computed的区别

methods和computed看起来都可以实现我们的功能,
那么为什么还要多一个计算属性这个东西呢?
原因:计算属性会进行缓存,如果多次使用时,计算属性只会调用一次

7.组件化开发

7.1组件化思想:

如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。
但如果,我们把一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。
在这里插入图片描述

7.2组件组成结构

每个 .vue 组件都由 3 部分构成,分别是:

  • template -> 组件的模板结构
  • script -> 组件的 JavaScript 行为
  • style -> 组件的样式
    其中,每个组件中必须包含 template 模板结构,而 script 行为和 style 样式是可选的组成部分。
    注意: 是 vue 提供的容器标签,只起到包裹性质的作用,它不会被渲染为真正的 DOM 元素。

让 style 中支持 less 语法
如果希望使用 less 语法编写组件的 style 样式,可以按照如下两个步骤进行配置:
① 运行 npm install less -D 命令安装依赖包,从而提供 less 语法的编译支持
② 在 < style> 标签上添加 lang=“less” 属性,即可使用 less 语法编写组件的样式

7.3组件的注册

vue 中注册组件的方式分为“全局注册”和“局部注册”两种,其中:

  • 被全局注册的组件,可以在全局任何一个组件内使用

使用 app.component() 方法注册的全局组件,直接以标签的形式进行使用即可,例如:
在这里插入图片描述

  • 被局部注册的组件,只能在当前注册的范围内使用
    在这里插入图片描述

应用场景:
如果某些组件在开发期间的使用频率很高,推荐进行全局注册
如果某些组件只在特定的情况下会被用到,推荐进行局部注册

组件注册时名称的大小写
在进行组件的注册时,定义组件注册名称的方式有两种:
① 使用kebab-case 命名法(俗称短横线命名法,例如my-swiper 和 my-search)
② 使用 PascalCase 命名法(俗称帕斯卡命名法大驼峰命名法,例如MySwiper 和 MySearch)

短横线命名法的特点:
必须严格按照短横线名称进行使用
帕斯卡命名法的特点:
既可以严格按照帕斯卡名称进行使用,又可以转化为短横线名称进行使用

解决组件样式冲突的问题
默认情况下,写在.vue 组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题
为了提高开发效率和开发体验,vue 为 style 节点提供了 scoped 属性,从而防止组件之间的样式冲突问题

如果给当前组件的 style 节点添加了 scoped 属性,则当前组件的样式对其子组件是不生效的。如果想让某些样式对子组件生效,可以使用 /deep/ 深度选择器
在这里插入图片描述

7.4组件的 props

props 是组件的自定义属性,组件的使用者可以通过props 把数据传递到子组件内部,供子组件内部进行使用。
在这里插入图片描述
在这里插入图片描述
可以使用v-bind 属性绑定的形式,为组件动态绑定props 的值
在这里插入图片描述
组件中如果使用“camelCase (驼峰命名法)”声明了props 属性的名称,则有两种方式为其绑定属性的值:
在这里插入图片描述

props 验证

使用对象类型的 props 节点,可以对每个 prop 进行数据类型的校验
在这里插入图片描述
如果某个prop 属性值的类型不唯一,此时可以通过数组的形式,为其指定多个可能的类型
在这里插入图片描述
如果组件的某个prop 属性是必填项,必须让组件的使用者为其传递属性的值
在这里插入图片描述
在封装组件时,可以为某个prop 属性指定默认值
在这里插入图片描述
在封装组件时,可以为prop 属性指定自定义的验证函数,从而对prop 属性的值进行更加精确的控制
在这里插入图片描述

7.5动态组件

动态组件指的是动态切换组件的显示与隐藏。vue 提供了一个内置的 < component> 组件,专门用来实现组件的动态渲染。

① < component> 是组件的占位符
② 通过is 属性动态指定要渲染的组件名称
③ < component is=“要渲染的组件的名称”>

在这里插入图片描述

使用 keep-alive 保持状态

keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
它们有两个非常重要的属性:
include - 字符串或正则表达,只有匹配的组件会被缓存
exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存

默认情况下,切换动态组件时无法保持组件的状态。此时可以使用vue 内置的
< keep-alive> 组件保持动态组件的状态。

在这里插入图片描述

8.组件之间的数据共享

8.1 组件之间的关系

在项目开发中,组件之间的关系分为如下 3 种:
① 父子关系
② 兄弟关系
③ 后代关系

在这里插入图片描述

8.2 父组件向子组件共享数据

父组件通过v-bind 属性绑定向子组件共享数据。同时,子组件需要使用props 接收数据。
在这里插入图片描述
在这里插入图片描述

8.3 子组件向父组件共享数据

子组件通过自定义事件的方式向父组件共享数据。
在这里插入图片描述
在这里插入图片描述

8.4 父子组件之间数据的双向同步

父组件在使用子组件期间,可以使用v-model 指令维护组件内外数据的双向同步:

在这里插入图片描述

8.5 兄弟组件之间的数据共享

兄弟组件之间实现数据共享的方案是EventBus。可以借助于第三方的包 mitt 来创建eventBus 对象,从而实 现兄弟组件之间的数据共享。

在这里插入图片描述
具体步骤:

安装 mitt 依赖包
在这里插入图片描述

在项目中创建公共的 eventBus 模块如下:

在这里插入图片描述

在数据接收方,调用bus.on(‘事件名称’, 事件处理函数) 方法注册一个自定义事件

在这里插入图片描述

在数据发送方,调用bus.emit(‘事件名称’, 要发送的数据) 方法触发自定义事件。示例代码如下:

在这里插入图片描述

8.6 后代关系组件之间的数据共享

后代关系组件之间共享数据,指的是父节点的组件向其子孙组件共享数据。此时组件之间的嵌套关系比较复杂, 可以使用provideinject 实现后代关系组件之间的数据共享。

父节点通过 provide 共享数据

父节点的组件可以通过provide 方法,对其子孙组件共享数据:

在这里插入图片描述

子孙节点通过 inject 接收数据

子孙节点可以使用 inject 数组,接收父级节点向下共享的数据

在这里插入图片描述
父节点对外共享响应式的数据

父节点使用provide 向下共享数据时,可以结合 computed 函数向下共享响应式的数据。如果父级节点共享的是响应式的数据,则子孙节点必须以**.value** 的形式进行使用。

在这里插入图片描述
在这里插入图片描述

8.7 vuex

vuex 是终极的组件之间的数据共享方案。在企业级的vue 项目开发中,vuex 可以让组件之间的数据共享变得高效清晰、且易于维护

在这里插入图片描述

9. ref 引用

ref 用来辅助开发者获取 DOM 元素或组件的引用。 每个 vue 的组件实例上,都包含一个 $refs 对象,里面存储着对应的 DOM 元素或组件的引用。默认情况下, 组件的 $refs 指向一个空对象。

9.1使用 ref 引用 DOM 元素

在这里插入图片描述

9.2使用 ref 引用组件实例

在这里插入图片描述
让文本框自动获得焦点

当文本框展示出来之后,如果希望它立即获得焦点,则可以为其添加ref 引用,并调用原生 DOM 对象的 .focus() 方法即可

在这里插入图片描述

this.$nextTick(cb) 方法

组件的 $nextTick(cb) 方法,会把cb 回调推迟到下一个 DOM 更新周期之后执行。通俗的理解是:等组件的 DOM 异步地重新渲染完成后,再执行cb 回调函数。从而能保证cb 回调函数可以操作到最新的 DOM 元素。
在这里插入图片描述

获取组件的的具体元素

this.$refs.组件的ref名.$el.元素名

10.插槽 slot

插槽(Slot)是 vue 为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的 部分定义为插槽

在封装组件时,可以通过 < slot> 元素定义插槽,从而为用户预留内容占位符。

后备内容 :< slot>中的内容表示,如果没有在该组件中插入任何其他内容,就默认显示该内容

在这里插入图片描述

注意:如果在封装组件时没有预留任何 < slot> 插槽,则用户提供的任何自定义内容都会被丢弃。

10.1具名插槽

如果在封装组件时需要预留多个插槽节点,则需要为每个 < slot> 插槽指定具体的name 名称。这种带有具体名称的插槽叫做“具名插槽”。

在这里插入图片描述
注意:没有指定 name 名称的插槽, 会有隐含的名称叫做 “default”。

在向具名插槽提供内容的时候,我们可以在一个 < template> 元素上使用v-slot 指令,并以v-slot 的参数的 形式提供其名称。

在这里插入图片描述

具名插槽的简写形式
跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容(v-slot:) 替换为字符#。例如v-slot:header 可以被重写为#header

10.2作用域插槽

在封装组件的过程中,可以为预留的 < slot> 插槽绑定props 数据,这种带有props 数据的 < slot> 叫做“作用 域插槽”

一句话:父组件替换插槽的标签,但是内容由子组件来提供

举个例子:

我们先提一个需求:
子组件中包括一组数据,比如:pLanguages: [‘JavaScript’, ‘Python’, ‘Swift’, ‘Go’, ‘C++’]
需要在多个界面进行展示:
某些界面是以水平方向一一展示的,
某些界面是以列表形式展示的,
某些界面直接展示一个数组
内容在子组件,希望父组件告诉我们如何展示,怎么办呢?
利用slot作用域插槽就可以了

 //子组件
<span>
  <slot v-bind:user="user">
    {{ user.lastName }}
  </slot>
</span>
 //父组件
<current-user>
  <template v-slot:default="slotProps">
    {{ slotProps.user.firstName }}
  </template>
</current-user>

作用域插槽对外提供的数据对象,可以使用解构赋值简化数据的接收过程

在这里插入图片描述

在封装表格组件时,可以通过作用域插槽把表格每一行的数据传递给组件的使用者
在这里插入图片描述

在这里插入图片描述

11. 路由(vue-router)

11.1安装和使用vue-router

步骤一: 安装vue-router

npm install vue-router --save

步骤二: 在模块化工程中使用它(因为是一个插件, 所以可以通过Vue.use()来安装路由功能)
第一步:导入路由对象,并且调用 Vue.use(VueRouter)

import Vue from 'vue' 
import VueRouter from 'vue-router' 

Vue.use(VueRouter)

在这里插入图片描述

第二步:创建路由实例,并且传入路由映射配置
第三步:在Vue实例中挂载创建的路由实例
在这里插入图片描述

使用vue-router的步骤:
第一步: 创建路由组件
在这里插入图片描述

第二步: 配置路由映射: 组件和路径映射关系
在这里插入图片描述

第三步: 使用路由: 通过 < router-link> 和< router-view>
在这里插入图片描述

< router-link>: 该标签是一个vue-router中已经内置的组件, 它会被渲染成一个**< a>标签**.
< router-view>: 该标签会根据当前的路径, 动态渲染出不同的组件.
网页的其他内容, 比如顶部的标题/导航, 或者底部的一些版权信息等会和< router-view>处于同一个等级,在路由切换时, 切换的是挂载的组件, 其他内容不会发生改变.

在这里插入图片描述

11.2 路由的默认路径

在这里插入图片描述
在routes中新配置一个映射。
path配置的是根路径: /
redirect是重定向, 也就是我们将根路径重定向到/home的路径下, 这样就可以得到我们想要的结果了.

11.3 HTML5的History模式

改变路径的方式有两种:

  • URL的hash
  • HTML5的history

默认情况下, 路径的改变使用的URL的hash.
如果希望使用HTML5的history模式(路径中不会出现‘#’), 进行如下配置即可:
在这里插入图片描述
在这里插入图片描述

11.4 router-link属性

to: to用于指定跳转的路径
tag: tag可以指定< router-link>之后渲染成什么组件, 比如上面的代码会被渲染成一个< li>元素, 而不是< a>
replace: replace不会留下history记录, 所以指定replace的情况下, 后退键返回不能返回到上一个页面中
active-class: 当< router-link>对应的路由匹配成功时, 会自动给当前元素设置一个router-link-active的class, 设置active-class可以修改默认的名称.(可以通过这个类名进行css设计等,在进行高亮显示的导航菜单或者底部tabbar时, 会使用到该类)

在这里插入图片描述

设置该class具体的名称也可以通过router实例的属性进行修改,可以避免我们一个个去设置active-class
在这里插入图片描述
在这里插入图片描述

11.5 路由代码跳转

进行路由跳转也可以通过执行对应的JavaScript代码,vue-router 提供的编程式导航的 API可以实现

  • 跳转到指定 Hash 地址,从而展示对应的组件:
 this.$router.push('hash 地址')
  • 实现导航历史的前进、后退:
 this.$router.go(数值 n)

在这里插入图片描述

11.6 动态路由

动态路由指的是:把 Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性。在 vue-router 中使用 英文的冒号(:) 来定义路由的参数项。

在这里插入图片描述
在这里插入图片描述

$route.params 参数对象
通过动态路由匹配的方式渲染出来的组件中,可以使用 $route.params 对象访问到动态匹配的参数值

在这里插入图片描述

在这里插入图片描述

11.7 路由懒加载

路由懒加载的作用:
路由懒加载的主要作用就是将路由对应的组件打包成一个个的js代码块,
只有在这个路由被访问到的时候, 才加载对应的组件,避免了当打包构建应用时,Javascript 包会变得非常大,影响页面加载。

路由懒加载的效果:

在这里插入图片描述
懒加载的方式:

方式一: 结合Vue的异步组件和Webpack的代码分析.

const Home = resolve => { require.ensure(['../components/Home.vue'], () => { resolve(require('../components/Home.vue')) })};

方式二: AMD写法

const About = resolve => require(['../components/About.vue'], resolve);

方式三: 在ES6中, 我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割(推荐)

const Home = () => import('../components/Home.vue')

11.8 嵌套路由

通过路由实现组件的嵌套展示,叫做嵌套路由。

比如在home页面中, 我们希望通过/home/news和/home/message访问一些内容,
一个路径映射一个组件, 访问这两个路径也会分别渲染两个组件。
在这里插入图片描述
实现嵌套路由步骤:

  1. 创建对应的子组件, 并且在路由映射中配置对应的子路由.
    ① 声明子路由链接子路由占位符
    ② 在父路由规则中,通过 children 属性嵌套声明子路由规则
  2. 在组件内部使用< router-view>标签.

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

11.9 参数传递

传递参数主要有两种类型: paramsquery

  • params的类型:

配置路由格式: /router/:id
传递的方式: 在path后面跟上对应的值
传递后形成的路径: /router/123, /router/abc

  • query的类型:

配置路由格式: /router, 也就是普通配置
传递的方式: 对象中使用query的key作为传递方式
传递后形成的路径: /router?id=123, /router?id=abc

传递参数方式:

  1. < router-link>
    在这里插入图片描述
  2. JavaScript代码
    在这里插入图片描述
  3. 使用 props 接收路由参数
    为了简化路由参数的获取形式,vue-router 允许在路由规则中开启props 传参。
    在这里插入图片描述

获取参数

获取参数通过 $route 对象获取,分别为 this. $router.params 和 this. $router.query
在使用了 vue-router 的应用中,路由对象会被注入每个组件中,赋值为 this. $route ,并且当路由切换时,路由对象会被更新。

在这里插入图片描述
$ route和$router的区别

  • $ router为VueRouter实例,想要导航到不同URL,则使用$router.push方法
  • $ route为当前router跳转对象里面可以获取name、path、query、params等
    在这里插入图片描述

11.10命名路由

通过name 属性为路由规则定义名称的方式,叫做命名路由。
注意:命名路由的name 值不能重复,必须保证唯一性!

在这里插入图片描述
使用命名路由实现声明式导航
为 < router-link> 标签动态绑定to 属性的值,并通过name 属性指定要跳转到的路由规则。期间还可以用 params 属性指定跳转期间要携带的路由参数。

在这里插入图片描述

使用命名路由实现编程式导航

在这里插入图片描述

11.11 导航守卫

什么是导航守卫?
vue-router提供的导航守卫主要用来监听路由的进入和离开的,导航守卫可以控制路由的访问权限
vue-router提供了beforeEachafterEach的钩子函数, 它们会在路由即将改变前和改变后触发.

在这里插入图片描述

声明全局导航守卫
全局导航守卫会拦截每个路由规则,从而对每个路由进行访问权限的控制。可以按照如下的方式定义全局导航守卫:

在这里插入图片描述

导航钩子的三个参数解析:
to: 即将要进入的目标的路由对象.
from: 当前导航即将要离开的路由对象.
next: 调用该方法后, 才能进入下一个钩子.

注意:
① 在守卫方法中如果不声明next 形参,则默认允许用户访问每一个路由
② 在守卫方法中如果声明了 next 形参,则必须调用 next() 函数,否则不允许用户访问任何一个路由

next 函数的 3 种调用方式

  • 直接放行:next()
  • 强制其停留在当前页面:next(false)
  • 强制其跳转到登录页面:next(‘/login’)

结合 token 控制后台主页的访问权限

在这里插入图片描述

11.12 keep-alive与 vue-router

router-view 如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存:
在这里插入图片描述
在这里插入图片描述
注意:exclude里面每个组件名字用逗号分隔,不能加空格

补充:

当组件通过 keep-alive 缓存下来的时候,可以使用vue提供的两个钩子函数

  • activated:被 keep-alive 缓存的组件激活时调用。
  • deactivated:被 keep-alive 缓存的组件失活时调用。

注意:通过keep-alive页面缓存时,created函数只会在第一次触发,后面激活页面只会调用activated函数;页面被缓存下来的时候,就不会触发destroyed函数取而代之触发的是deactivated钩子函数

12.Vuex

12.1Vuex是做什么的?

官方解释:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式

Vuex 是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间数据的共享

在这里插入图片描述

12.2 使用 Vuex 统一管理状态的好处

① 能够在 vuex 中集中管理共享的数据,易于开发和后期维护
② 能够高效地实现组件之间的数据共享,提高开发效率
③ 存储在 vuex 中的数据都是响应式的,能够实时保持数据与页面的同步

一般情况下,只有组件之间共享的数据,才有必要存储到 vuex 中;对于组件中的私有数据,依旧存储在组件自身的 data 中即可。

Vuex状态管理图例:在这里插入图片描述

12.3 Vuex 的基本使用

  1. 安装 vuex 依赖包
npm install vuex --save
  1. 我们先创建一个文件夹store,并且在其中创建一个index.js文件, 在index.js文件中写入如下代码:
import Vue form 'vue'
import Vuex from 'vuex'  
Vue.use(Vuex)

const store = new Vuex.Store({
// state 中存放的就是全局共享的数据
state: { count: 0 }
})

  1. 将 store 对象挂载到 vue 实例中。
    来到main.js文件,导入store对象,并且放在new Vue中。这样,在其他Vue组件中,我们就可以通过this.$store的方式,获取到这个store对象了
new Vue({  el: '#app',
render: h => h(app),
router,
// 将创建的共享数据对象,挂载到 Vue 实例中
// 所有的组件,就可以直接从 store 中获取全局的数据了
store
})

12.4 Vuex 的核心概念

Vuex 中的主要核心概念如下:

  • State
  • Mutation
  • Action
  • Getter

State
State 提供唯一的公共数据源,所有共享的数据都要统一放到 Store 的 State 中进行存储。

// 创建store数据源,提供唯一公共数据
const store = new Vuex.Store({
  state: { count: 0 }
})

组件访问 State 中数据的第一种方式

this.$store.state.全局数据名称

组件访问 State 中数据的第二种方式

// 1. 从 vuex 中按需导入 mapState 函数
import { mapState } from 'vuex'

通过刚才导入的 mapState 函数,将当前组件需要的全局数据,映射为当前组件的 computed 计算属性:

// 2. 将全局数据,映射为当前组件的计算属性
computed: {
...mapState(['count'])
}

Mutation
Mutation 用于变更 Store中 的数据。

① 只能通过 mutation 变更 Store 数据,不可以直接操作 Store 中的数据。
② 通过这种方式虽然操作起来稍微繁琐一些,但是可以集中监控所有数据的变化

// 定义 Mutation
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
add(state) {
// 变更状态
state.count++
}
}
})

触发 mutations 的第一种方式:this.$store.commit()

// 触发mutation
methods: {  handle1() {
// 触发 mutations 的第一种方式
this.$store.commit('add')
}
}

可以在触发 mutations 时传递参数:

// 定义Mutation
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
addN(state, step) {
// 变更状态
state.count += step
}
}
})
// 触发mutation  methods: {
handle2() {
// 在调用 commit 函数,
// 触发 mutations 时携带参数
this.$store.commit('addN', 3)
}
}

触发 mutations 的第二种方式:

// 1. 从 vuex 中按需导入 mapMutations 函数
import { mapMutations } from 'vuex'

通过刚才导入的 mapMutations 函数,将需要的 mutations 函数,映射为当前组件的 methods 方法:

// 2. 将指定的 mutations 函数,映射为当前组件的 methods 函数
methods: {
...mapMutations(['add', 'addN'])
}

Action
Action 用于处理异步任务。
如果通过异步操作变更数据,必须通过 Action,而不能使用 Mutation,但是在 Action 中还是要通过触发 Mutation 的方式间接变更数据。

// 定义 Action
const store = new Vuex.Store({
// ...省略其他代码
mutations: {
add(state) {  state.count++
}
},
actions: {
addAsync(context) {
setTimeout(() => {  context.commit('add')
}, 1000)
}
}
})

触发 mutations 的第一种方式:this.$store.dispatch()

// 触发 Action  methods: {
handle() {
// 触发 actions 的第一种方式
this.$store.dispatch('addAsync')
}
}

触发 actions 异步任务时携带参数:

// 定义 Action
const store = new Vuex.Store({
// ...省略其他代码
mutations: {
addN(state, step) {  state.count += step
}
},
actions: {
addNAsync(context, step) {  setTimeout(() => {
context.commit('addN', step)
}, 1000)
}
}
})
// 触发 Action  methods: {
handle() {
// 在调用 dispatch 函数,
// 触发 actions 时携带参数
this.$store.dispatch('addNAsync', 5)
}
}

触发 actions 的第二种方式:

// 1. 从 vuex 中按需导入 mapActions 函数
import { mapActions } from 'vuex'

通过刚才导入的 mapActions 函数,将需要的 actions 函数,映射为当前组件的 methods 方法:

// 2. 将指定的 actions 函数,映射为当前组件的 methods 函数
methods: {
...mapActions(['addASync', 'addNASync'])
}

Getter
Getter 用于对 Store 中的数据进行加工处理形成新的数据。
① Getter 可以对 Store 中已有的数据加工处理之后形成新的数据,类似 Vue 的计算属性。
② Store 中数据发生变化,Getter 的数据也会跟着变化。

// 定义 Getter
const store = new Vuex.Store({  
state: {count: 0},
getters: {
showNum: state => {
return '当前最新的数量是【'+ state.count +'】'
}
}
})

使用 getters 的第一种方式:

this.$store.getters.名称

使用 getters 的第二种方式:

import { mapGetters } from 'vuex'

computed: {
...mapGetters(['showNum'])
}

getters传递参数

getters默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数。
在这里插入图片描述

12.5 Module

当应用变得非常复杂时,store对象就有可能变得相当臃肿.
为了解决这个问题, Vuex允许我们将store分割成模块(Module), 而每个模块拥有自己的state、mutation、action、getters等

代码如下:

在这里插入图片描述

Module局部状态
上面的代码中, 我们已经有了整体的组织结构, 下面我们来看看具体的局部模块中的代码如何书写。

在这里插入图片描述

注意:
例如取moduleA对象内部的state的值的count应该用this.$ store.a.state.count。
但是虽然doubleCount和 increment都是定义在不同module对象内部的,但是在调用的时候, 依然是通过this.$store来直接调用的。

12.6 项目结构

当我们的Vuex帮助我们管理过多的内容时, 好的项目结构可以让我们的代码更加清晰,可以如下图的项目结构。

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值