参考视频:
https://www.bilibili.com/video/BV15741177Eh
https://www.bilibili.com/video/BV1zq4y1p7ga
Vue (读音 /vjuː/,类似于 view),不要读错。
Vue是一个渐进式的框架,什么是渐进式的呢?
渐进式意味着你可以将Vue作为你应用的一部分嵌入其中,带来更丰富的交互体验。
如果你希望将更多的业务逻辑使用Vue实现,那么Vue的核心库以及其生态系统。比如Core+Vue-router+Vuex,也可以满足你各种各样的需求。
Vue有很多特点和Web开发中常见的高级功能
MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。当然这些事 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。
ViewModel 作为 MVVM 的核心,是它把当前页面的数据源(Model)和页面的结构(View)连接在了一起。
当数据源发生变化时,会被 ViewModel 监听到,VM 会根据最新的数据源自动更新页面的结构
当表单元素的值发生变化时,也会被 VM 监听到,VM 会把变化过后最新的值自动同步到 Model 数据源中
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子(回调)的函数,这给了用户在不同阶段添加自己的代码的机会。
生命周期函数
注意:在实际开发中,created 是最常用的生命周期函数!
内容渲染指令用来辅助开发者渲染 DOM 元素的文本内容。常用的内容渲染指令有如下 3 个:
vue 提供的 {{ }} 语法,专门用来解决 v-text 会覆盖默认文本内容的问题。这种 {{ }} 语法的专业名称是插值表达
式(英文名为:Mustache)。
同时{{ }}语法里可写一些简单的表达式
v-text 指令和插值表达式只能渲染纯文本内容。如果要把包含 HTML 标签的字符串渲染为页面的 HTML 元素,
则需要用到 v-html 这个指令:
{{ this will not be compiled }}
页面显示:{{ this will not be compiled }}
可以用v-bind绑定class并赋予一个对象,对象中有多个键值对,键为class名,值为Boolean类型
这种方式可以有很多的应用,如:
注意这种写法,vue会对类名进行合并,并不会覆盖。
同时也可以利用v-bind:style来绑定一些CSS内联样式
但是注意在写CSS属性名的时候,比如font-size等带有中划线‘-’
{{message}}
注意:在 vue 提供的模板渲染语法中,除了支持绑定简单的数据值之外,还支持 Javascript 表达式的运算,例如:
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 } 模式添加侦听器
这三个指令与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-if和v-show对比
如果在遍历的过程中不需要使用索引值
语法格式: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。
小补充:
因为Vue是响应式的,所以当数据发生变化时,Vue会自动检测数据变化,视图会发生对应的更新。
Vue中包含了一组观察数组编译的方法,使用它们改变数组也会触发视图的更新。
v-mode原理
v-model其实是一个语法糖,它的背后本质上是包含两个操作:
1.v-bind绑定一个value属性
2.v-on指令给当前元素绑定input事件
v-model 绑定checkbox
复选框分为两种情况:单个勾选框和多个勾选框
单个勾选框:
v-model即为布尔值。
此时input的value并不影响v-model的值。
多个复选框:
当是多个复选框时,因为可以选中多个,所以对应的data中属性是一个数组。
当选中某一个时,就会将input的value添加到数组中。
补充:值绑定
动态的给value赋值,通过v-bind:value动态的给value绑定值
v-mode修饰符
lazy修饰符:
默认情况下,v-model默认是在input事件中同步输入框的数据的。
也就是说,一旦有数据发生改变对应的data中的数据就会自动发生改变。
lazy修饰符可以让数据在失去焦点或者回车时才会更新:
number修饰符:
默认情况下,在输入框中无论我们输入的是字母还是数字,都会被当做字符串类型进行处理。
但是如果我们希望处理的是数字类型,那么最好直接将内容当做数字处理。
number修饰符可以让在输入框中输入的内容自动转成数字类型:
trim修饰符:
如果输入的内容首尾有很多空格,通常我们希望将其去除
trim修饰符可以过滤内容左右两边的空格
过滤器常用于文本的格式化。例如:
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}`
})
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 侦听器:
我们知道,在模板中可以直接通过插值语法显示一些data中的数据。
但是在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示
比如我们有firstName和lastName两个变量,我们需要显示完整的名称。
但是如果多个地方都需要显示完整的名称,我们就需要写多个{{firstName}} {{lastName}}
我们可以将上面的代码换成计算属性:
计算属性是写在实例的computed选项中的。
methods和computed看起来都可以实现我们的功能,
那么为什么还要多一个计算属性这个东西呢?
原因:计算属性会进行缓存,如果多次使用时,计算属性只会调用一次
如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。
但如果,我们把一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。
每个 .vue 组件都由 3 部分构成,分别是:
让 style 中支持 less 语法
如果希望使用 less 语法编写组件的 style 样式,可以按照如下两个步骤进行配置:
① 运行 npm install less -D 命令安装依赖包,从而提供 less 语法的编译支持
② 在 < style> 标签上添加 lang=“less” 属性,即可使用 less 语法编写组件的样式
vue 中注册组件的方式分为“全局注册”和“局部注册”两种,其中:
使用 app.component() 方法注册的全局组件,直接以标签的形式进行使用即可,例如:
应用场景:
如果某些组件在开发期间的使用频率很高,推荐进行全局注册;
如果某些组件只在特定的情况下会被用到,推荐进行局部注册。
组件注册时名称的大小写
在进行组件的注册时,定义组件注册名称的方式有两种:
① 使用kebab-case 命名法(俗称短横线命名法,例如my-swiper 和 my-search)
② 使用 PascalCase 命名法(俗称帕斯卡命名法或大驼峰命名法,例如MySwiper 和 MySearch)
短横线命名法的特点:
必须严格按照短横线名称进行使用
帕斯卡命名法的特点:
既可以严格按照帕斯卡名称进行使用,又可以转化为短横线名称进行使用
解决组件样式冲突的问题
默认情况下,写在.vue 组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。
为了提高开发效率和开发体验,vue 为 style 节点提供了 scoped 属性,从而防止组件之间的样式冲突问题
如果给当前组件的 style 节点添加了 scoped 属性,则当前组件的样式对其子组件是不生效的。如果想让某些样式对子组件生效,可以使用 /deep/ 深度选择器。
props 是组件的自定义属性,组件的使用者可以通过props 把数据传递到子组件内部,供子组件内部进行使用。
可以使用v-bind 属性绑定的形式,为组件动态绑定props 的值
组件中如果使用“camelCase (驼峰命名法)”声明了props 属性的名称,则有两种方式为其绑定属性的值:
props 验证
使用对象类型的 props 节点,可以对每个 prop 进行数据类型的校验
如果某个prop 属性值的类型不唯一,此时可以通过数组的形式,为其指定多个可能的类型
如果组件的某个prop 属性是必填项,必须让组件的使用者为其传递属性的值
在封装组件时,可以为某个prop 属性指定默认值
在封装组件时,可以为prop 属性指定自定义的验证函数,从而对prop 属性的值进行更加精确的控制
动态组件指的是动态切换组件的显示与隐藏。vue 提供了一个内置的 < component> 组件,专门用来实现组件的动态渲染。
① < component> 是组件的占位符
② 通过is 属性动态指定要渲染的组件名称
③ < component is=“要渲染的组件的名称”>
使用 keep-alive 保持状态
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
它们有两个非常重要的属性:
include - 字符串或正则表达,只有匹配的组件会被缓存
exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存
默认情况下,切换动态组件时无法保持组件的状态。此时可以使用vue 内置的
< keep-alive> 组件保持动态组件的状态。
在项目开发中,组件之间的关系分为如下 3 种:
① 父子关系
② 兄弟关系
③ 后代关系
父组件通过v-bind 属性绑定向子组件共享数据。同时,子组件需要使用props 接收数据。
父组件在使用子组件期间,可以使用v-model 指令维护组件内外数据的双向同步:
兄弟组件之间实现数据共享的方案是EventBus。可以借助于第三方的包 mitt 来创建eventBus 对象,从而实 现兄弟组件之间的数据共享。
具体步骤:
安装 mitt 依赖包
在项目中创建公共的 eventBus 模块如下:
在数据接收方,调用bus.on(‘事件名称’, 事件处理函数) 方法注册一个自定义事件。
在数据发送方,调用bus.emit(‘事件名称’, 要发送的数据) 方法触发自定义事件。示例代码如下:
后代关系组件之间共享数据,指的是父节点的组件向其子孙组件共享数据。此时组件之间的嵌套关系比较复杂, 可以使用provide 和 inject 实现后代关系组件之间的数据共享。
父节点通过 provide 共享数据
父节点的组件可以通过provide 方法,对其子孙组件共享数据:
子孙节点通过 inject 接收数据
子孙节点可以使用 inject 数组,接收父级节点向下共享的数据。
父节点使用provide 向下共享数据时,可以结合 computed 函数向下共享响应式的数据。如果父级节点共享的是响应式的数据,则子孙节点必须以**.value** 的形式进行使用。
vuex 是终极的组件之间的数据共享方案。在企业级的vue 项目开发中,vuex 可以让组件之间的数据共享变得高效、清晰、且易于维护。
ref 用来辅助开发者获取 DOM 元素或组件的引用。 每个 vue 的组件实例上,都包含一个 $refs 对象,里面存储着对应的 DOM 元素或组件的引用。默认情况下, 组件的 $refs 指向一个空对象。
当文本框展示出来之后,如果希望它立即获得焦点,则可以为其添加ref 引用,并调用原生 DOM 对象的 .focus() 方法即可
this.$nextTick(cb) 方法
组件的 $nextTick(cb) 方法,会把cb 回调推迟到下一个 DOM 更新周期之后执行。通俗的理解是:等组件的 DOM 异步地重新渲染完成后,再执行cb 回调函数。从而能保证cb 回调函数可以操作到最新的 DOM 元素。
获取组件的的具体元素
this.$refs.组件的ref名.$el.元素名
插槽(Slot)是 vue 为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的 部分定义为插槽。
在封装组件时,可以通过 < slot> 元素定义插槽,从而为用户预留内容占位符。
后备内容 :< slot>中的内容表示,如果没有在该组件中插入任何其他内容,就默认显示该内容
注意:如果在封装组件时没有预留任何 < slot> 插槽,则用户提供的任何自定义内容都会被丢弃。
如果在封装组件时需要预留多个插槽节点,则需要为每个 < slot> 插槽指定具体的name 名称。这种带有具体名称的插槽叫做“具名插槽”。
注意:没有指定 name 名称的插槽, 会有隐含的名称叫做 “default”。
在向具名插槽提供内容的时候,我们可以在一个 < template> 元素上使用v-slot 指令,并以v-slot 的参数的 形式提供其名称。
具名插槽的简写形式
跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容(v-slot:) 替换为字符#。例如v-slot:header 可以被重写为#header
在封装组件的过程中,可以为预留的 < 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-router
npm install vue-router --save
步骤二: 在模块化工程中使用它(因为是一个插件, 所以可以通过Vue.use()来安装路由功能)
第一步:导入路由对象,并且调用 Vue.use(VueRouter)
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
第二步:创建路由实例,并且传入路由映射配置
第三步:在Vue实例中挂载创建的路由实例
第三步: 使用路由: 通过 < router-link> 和< router-view>
< router-link>: 该标签是一个vue-router中已经内置的组件, 它会被渲染成一个**< a>标签**.
< router-view>: 该标签会根据当前的路径, 动态渲染出不同的组件.
网页的其他内容, 比如顶部的标题/导航, 或者底部的一些版权信息等会和< router-view>处于同一个等级,在路由切换时, 切换的是挂载的组件, 其他内容不会发生改变.
在routes中新配置一个映射。
path配置的是根路径: /
redirect是重定向, 也就是我们将根路径重定向到/home的路径下, 这样就可以得到我们想要的结果了.
改变路径的方式有两种:
默认情况下, 路径的改变使用的URL的hash.
如果希望使用HTML5的history模式(路径中不会出现‘#’), 进行如下配置即可:
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
进行路由跳转也可以通过执行对应的JavaScript代码,vue-router 提供的编程式导航的 API可以实现
this.$router.push('hash 地址')
this.$router.go(数值 n)
动态路由指的是:把 Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性。在 vue-router 中使用 英文的冒号(:) 来定义路由的参数项。
$route.params 参数对象
通过动态路由匹配的方式渲染出来的组件中,可以使用 $route.params 对象访问到动态匹配的参数值。
路由懒加载的作用:
路由懒加载的主要作用就是将路由对应的组件打包成一个个的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')
通过路由实现组件的嵌套展示,叫做嵌套路由。
比如在home页面中, 我们希望通过/home/news和/home/message访问一些内容,
一个路径映射一个组件, 访问这两个路径也会分别渲染两个组件。
实现嵌套路由步骤:
传递参数主要有两种类型: params和query
配置路由格式: /router/:id
传递的方式: 在path后面跟上对应的值
传递后形成的路径: /router/123, /router/abc
配置路由格式: /router, 也就是普通配置
传递的方式: 对象中使用query的key作为传递方式
传递后形成的路径: /router?id=123, /router?id=abc
传递参数方式:
获取参数
获取参数通过 $route 对象获取,分别为 this. $router.params 和 this. $router.query
在使用了 vue-router 的应用中,路由对象会被注入每个组件中,赋值为 this. $route ,并且当路由切换时,路由对象会被更新。
通过name 属性为路由规则定义名称的方式,叫做命名路由。
注意:命名路由的name 值不能重复,必须保证唯一性!
使用命名路由实现声明式导航
为 < router-link> 标签动态绑定to 属性的值,并通过name 属性指定要跳转到的路由规则。期间还可以用 params 属性指定跳转期间要携带的路由参数。
使用命名路由实现编程式导航
什么是导航守卫?
vue-router提供的导航守卫主要用来监听路由的进入和离开的,导航守卫可以控制路由的访问权限
vue-router提供了beforeEach和afterEach的钩子函数, 它们会在路由即将改变前和改变后触发.
声明全局导航守卫
全局导航守卫会拦截每个路由规则,从而对每个路由进行访问权限的控制。可以按照如下的方式定义全局导航守卫:
导航钩子的三个参数解析:
to: 即将要进入的目标的路由对象.
from: 当前导航即将要离开的路由对象.
next: 调用该方法后, 才能进入下一个钩子.
注意:
① 在守卫方法中如果不声明next 形参,则默认允许用户访问每一个路由!
② 在守卫方法中如果声明了 next 形参,则必须调用 next() 函数,否则不允许用户访问任何一个路由!
next 函数的 3 种调用方式
结合 token 控制后台主页的访问权限
router-view 如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存:
注意:exclude里面每个组件名字用逗号分隔,不能加空格
补充:
当组件通过 keep-alive 缓存下来的时候,可以使用vue提供的两个钩子函数
注意:通过keep-alive页面缓存时,created函数只会在第一次触发,后面激活页面只会调用activated函数;页面被缓存下来的时候,就不会触发destroyed函数取而代之触发的是deactivated钩子函数
官方解释:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式
Vuex 是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间数据的共享。
① 能够在 vuex 中集中管理共享的数据,易于开发和后期维护
② 能够高效地实现组件之间的数据共享,提高开发效率
③ 存储在 vuex 中的数据都是响应式的,能够实时保持数据与页面的同步
一般情况下,只有组件之间共享的数据,才有必要存储到 vuex 中;对于组件中的私有数据,依旧存储在组件自身的 data 中即可。
npm install vuex --save
import Vue form 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
// state 中存放的就是全局共享的数据
state: { count: 0 }
})
new Vue({ el: '#app',
render: h => h(app),
router,
// 将创建的共享数据对象,挂载到 Vue 实例中
// 所有的组件,就可以直接从 store 中获取全局的数据了
store
})
Vuex 中的主要核心概念如下:
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本身返回另一个函数。
当应用变得非常复杂时,store对象就有可能变得相当臃肿.
为了解决这个问题, Vuex允许我们将store分割成模块(Module), 而每个模块拥有自己的state、mutation、action、getters等
代码如下:
Module局部状态
上面的代码中, 我们已经有了整体的组织结构, 下面我们来看看具体的局部模块中的代码如何书写。
注意:
例如取moduleA对象内部的state的值的count应该用this.$ store.a.state.count。
但是虽然doubleCount和 increment都是定义在不同module对象内部的,但是在调用的时候, 依然是通过this.$store来直接调用的。
当我们的Vuex帮助我们管理过多的内容时, 好的项目结构可以让我们的代码更加清晰,可以如下图的项目结构。