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 中由于需要展示内容而涉及的业务逻辑。
Vue.js学习_第1张图片
ViewModel 作为 MVVM 的核心,是它把当前页面的数据源(Model)和页面的结构(View)连接在了一起。
当数据源发生变化时,会被 ViewModel 监听到,VM 会根据最新的数据源自动更新页面的结构
当表单元素的值发生变化时,也会被 VM 监听到,VM 会把变化过后最新的值自动同步到 Model 数据源中

Vue.js学习_第2张图片
Vue.js学习_第3张图片
Vue.js学习_第4张图片

3.Vue生命周期

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

3.1生命周期图示

Vue.js学习_第5张图片
Vue.js学习_第6张图片

3.2组件的生命周期

Vue.js学习_第7张图片

生命周期函数

Vue.js学习_第8张图片

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

4.Vue 的指令

4.1 内容渲染指令

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

  • v-text
  • {{ }}
  • v-html
    Vue.js学习_第9张图片
    注意:v-text 指令会覆盖元素内默认的值。

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

4.2 属性绑定指令

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

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

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

4.3v-bind动态绑定class

可以用v-bind绑定class并赋予一个对象,对象中有多个键值对,键为class名,值为Boolean类型
Vue.js学习_第13张图片
这种方式可以有很多的应用,如:
Vue.js学习_第14张图片

数组中可以放多个三元表达式
Vue.js学习_第15张图片

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

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

  1. 可以使用驼峰式: fontSize
  2. 可以使用短横线分隔: ‘font-size’
 
{{message}}

注意:在 vue 提供的模板渲染语法中,除了支持绑定简单的数据值之外,还支持 Javascript 表达式的运算,例如:
Vue.js学习_第16张图片

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

在这里插入图片描述

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

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 } 模式添加侦听器

Vue.js学习_第20张图片

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

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

Vue.js学习_第21张图片
v-if的原理:
v-if后面的条件为false时,对应的元素以及其子元素不会渲染。也就是根本没有不会有对应的标签出现在DOM中。

Vue.js学习_第22张图片

上述案例小问题:

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

  • 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在原数组的索引值。

Vue.js学习_第24张图片

v-for也可以遍历对象,可以用value-key的形式来获取
Vue.js学习_第25张图片
结果:
Vue.js学习_第26张图片

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

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

Vue.js学习_第27张图片

  • v-mode
    Vue中使用v-model指令来实现表单元素和数据的双向绑定
    Vue.js学习_第28张图片
    解释:
    当我们在输入框输入内容时
    因为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事件
Vue.js学习_第29张图片

v-model 绑定radio
Vue.js学习_第30张图片

v-model 绑定checkbox
复选框分为两种情况:单个勾选框和多个勾选框
单个勾选框:
v-model即为布尔值。
此时input的value并不影响v-model的值。
多个复选框:
当是多个复选框时,因为可以选中多个,所以对应的data中属性是一个数组。
当选中某一个时,就会将input的value添加到数组中。
Vue.js学习_第31张图片

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

补充:值绑定
动态的给value赋值,通过v-bind:value动态的给value绑定值
Vue.js学习_第32张图片

v-mode修饰符

  1. lazy修饰符:

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

  2. number修饰符:

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

  3. trim修饰符:

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

Vue.js学习_第33张图片

5.过滤器和侦听器

5.1过滤器(Filters)

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

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

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

在这里插入图片描述
过滤器可以串联地进行调用
Vue.js学习_第35张图片

过滤器传参
Vue.js学习_第36张图片
注意:过滤器仅在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 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。

Vue.js学习_第37张图片

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 选 项,代码示例如下:
Vue.js学习_第38张图片

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

6.计算属性

6.1什么是计算属性?

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

Vue.js学习_第39张图片
在这里插入图片描述

6.2计算属性的setter和getter

每个计算属性都包含一个getter和一个setter
Vue.js学习_第40张图片

6.3methods和computed的区别

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

7.组件化开发

7.1组件化思想:

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

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() 方法注册的全局组件,直接以标签的形式进行使用即可,例如:
Vue.js学习_第42张图片

  • 被局部注册的组件,只能在当前注册的范围内使用
    Vue.js学习_第43张图片

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

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

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

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

如果给当前组件的 style 节点添加了 scoped 属性,则当前组件的样式对其子组件是不生效的。如果想让某些样式对子组件生效,可以使用 /deep/ 深度选择器
Vue.js学习_第44张图片

7.4组件的 props

props 是组件的自定义属性,组件的使用者可以通过props 把数据传递到子组件内部,供子组件内部进行使用。
Vue.js学习_第45张图片
Vue.js学习_第46张图片
可以使用v-bind 属性绑定的形式,为组件动态绑定props 的值
Vue.js学习_第47张图片
组件中如果使用“camelCase (驼峰命名法)”声明了props 属性的名称,则有两种方式为其绑定属性的值:
Vue.js学习_第48张图片

props 验证

使用对象类型的 props 节点,可以对每个 prop 进行数据类型的校验
Vue.js学习_第49张图片
如果某个prop 属性值的类型不唯一,此时可以通过数组的形式,为其指定多个可能的类型
Vue.js学习_第50张图片
如果组件的某个prop 属性是必填项,必须让组件的使用者为其传递属性的值
Vue.js学习_第51张图片
在封装组件时,可以为某个prop 属性指定默认值
Vue.js学习_第52张图片
在封装组件时,可以为prop 属性指定自定义的验证函数,从而对prop 属性的值进行更加精确的控制
Vue.js学习_第53张图片

7.5动态组件

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

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

Vue.js学习_第54张图片

使用 keep-alive 保持状态

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

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

Vue.js学习_第55张图片

8.组件之间的数据共享

8.1 组件之间的关系

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

在这里插入图片描述

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

父组件通过v-bind 属性绑定向子组件共享数据。同时,子组件需要使用props 接收数据。
Vue.js学习_第56张图片
Vue.js学习_第57张图片

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

子组件通过自定义事件的方式向父组件共享数据。
Vue.js学习_第58张图片
Vue.js学习_第59张图片

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

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

Vue.js学习_第60张图片

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

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


具体步骤:

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

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

Vue.js学习_第61张图片

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

Vue.js学习_第62张图片

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

Vue.js学习_第63张图片

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

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

父节点通过 provide 共享数据

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

Vue.js学习_第64张图片

子孙节点通过 inject 接收数据

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

Vue.js学习_第65张图片
父节点对外共享响应式的数据

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

Vue.js学习_第66张图片
Vue.js学习_第67张图片

8.7 vuex

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

Vue.js学习_第68张图片

9. ref 引用

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

9.1使用 ref 引用 DOM 元素

Vue.js学习_第69张图片

9.2使用 ref 引用组件实例

Vue.js学习_第70张图片
让文本框自动获得焦点

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

Vue.js学习_第71张图片

this.$nextTick(cb) 方法

组件的 $nextTick(cb) 方法,会把cb 回调推迟到下一个 DOM 更新周期之后执行。通俗的理解是:等组件的 DOM 异步地重新渲染完成后,再执行cb 回调函数。从而能保证cb 回调函数可以操作到最新的 DOM 元素。
Vue.js学习_第72张图片

获取组件的的具体元素

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

10.插槽 slot

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

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

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

Vue.js学习_第73张图片

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

10.1具名插槽

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

Vue.js学习_第74张图片
注意:没有指定 name 名称的插槽, 会有隐含的名称叫做 “default”。

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

Vue.js学习_第75张图片

具名插槽的简写形式
跟 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>

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

Vue.js学习_第76张图片

在封装表格组件时,可以通过作用域插槽把表格每一行的数据传递给组件的使用者
Vue.js学习_第77张图片

Vue.js学习_第78张图片

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.js学习_第79张图片

第二步:创建路由实例,并且传入路由映射配置
第三步:在Vue实例中挂载创建的路由实例
Vue.js学习_第80张图片

使用vue-router的步骤:
第一步: 创建路由组件
Vue.js学习_第81张图片

第二步: 配置路由映射: 组件和路径映射关系
Vue.js学习_第82张图片

第三步: 使用路由: 通过 < router-link> 和< router-view>
Vue.js学习_第83张图片

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

Vue.js学习_第84张图片

11.2 路由的默认路径

Vue.js学习_第85张图片
在routes中新配置一个映射。
path配置的是根路径: /
redirect是重定向, 也就是我们将根路径重定向到/home的路径下, 这样就可以得到我们想要的结果了.

11.3 HTML5的History模式

改变路径的方式有两种:

  • URL的hash
  • HTML5的history

默认情况下, 路径的改变使用的URL的hash.
如果希望使用HTML5的history模式(路径中不会出现‘#’), 进行如下配置即可:
Vue.js学习_第86张图片
Vue.js学习_第87张图片

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
Vue.js学习_第88张图片
Vue.js学习_第89张图片

11.5 路由代码跳转

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

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

Vue.js学习_第90张图片

11.6 动态路由

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

Vue.js学习_第91张图片
在这里插入图片描述

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

Vue.js学习_第92张图片

Vue.js学习_第93张图片

11.7 路由懒加载

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

路由懒加载的效果:

Vue.js学习_第94张图片
懒加载的方式:

方式一: 结合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访问一些内容,
一个路径映射一个组件, 访问这两个路径也会分别渲染两个组件。
Vue.js学习_第95张图片
实现嵌套路由步骤:

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

Vue.js学习_第96张图片
Vue.js学习_第97张图片
Vue.js学习_第98张图片
Vue.js学习_第99张图片

11.9 参数传递

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

  • params的类型:

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

  • query的类型:

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

传递参数方式:

  1. < router-link>
    Vue.js学习_第100张图片
  2. JavaScript代码
    Vue.js学习_第101张图片
  3. 使用 props 接收路由参数
    为了简化路由参数的获取形式,vue-router 允许在路由规则中开启props 传参。
    Vue.js学习_第102张图片

获取参数

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

Vue.js学习_第103张图片
$ route和$router的区别

  • $ router为VueRouter实例,想要导航到不同URL,则使用$router.push方法
  • $ route为当前router跳转对象里面可以获取name、path、query、params等
    Vue.js学习_第104张图片

11.10命名路由

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

Vue.js学习_第105张图片
使用命名路由实现声明式导航
为 < router-link> 标签动态绑定to 属性的值,并通过name 属性指定要跳转到的路由规则。期间还可以用 params 属性指定跳转期间要携带的路由参数。

Vue.js学习_第106张图片

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

Vue.js学习_第107张图片

11.11 导航守卫

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

Vue.js学习_第108张图片

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

Vue.js学习_第109张图片

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

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

next 函数的 3 种调用方式

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

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

Vue.js学习_第110张图片

11.12 keep-alive与 vue-router

router-view 如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存:
Vue.js学习_第111张图片
Vue.js学习_第112张图片
注意:exclude里面每个组件名字用逗号分隔,不能加空格

补充:

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

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

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

12.Vuex

12.1Vuex是做什么的?

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

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

Vue.js学习_第113张图片

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

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

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

Vuex状态管理图例:Vue.js学习_第114张图片

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本身返回另一个函数。
Vue.js学习_第115张图片

12.5 Module

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

代码如下:

Vue.js学习_第116张图片

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

Vue.js学习_第117张图片

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

12.6 项目结构

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

Vue.js学习_第118张图片

你可能感兴趣的:(vue.js,学习,前端)