Vue2
插值表达式
-
利用表达式进行插值渲染,将数据渲染到页面中。
-
语法:
{{ 表达式 }}
-
PS:
- 使用的数据要存在
- 支持的是表达式,不是语句
if
、for
- 不能在标签属性中使用
{{ }}
v-show和v-if
v-show
底层原理:切换css
的display: none
来控制显示隐藏- 场景:频繁切换显示隐藏的场景
v-if
底层原理:根据判断条件控制元素的创建和移除(条件渲染)- 场景:要么显示,要么隐藏,不频繁切换的场景
v-on
- 作用:注册事件 = 添加监听 + 提供处理逻辑
- 语法①:
v-on:事件名="内联语句"
- 简写:
@事件名
- 简写:
- 语法②:
v-on:事件名="methods中的函数名"
methods
函数内的this
指向Vue
实例
v-bind
- 作用:动态设置
html
的标签属性,如src、url、title...
- 语法:
v-bind:属性名="表达式"
- 可以简写,如
v-bind:src
可以简写为:src
v-for
-
作用:基于数据循环,多次渲染整个元素(数组、对象、数字…
-
语法:
v-for="(item,index) in 数组"
item
每一项,index
下标index
可以省略,v-for="item in 数组"
-
key
作用:给元素添加唯一标识,便于Vue
进行列表项的正确排序服用v-for
的默认行为会尝试原地修改元素(就地复用)key
的值只能是字符串或数字类型<li v-for="(item,index) in xxx" :key="唯一值">
v-model
-
作用:给表单元素使用,双向数据绑定,可以快速获取或设置表单元素内容
- 数据变化,视图自动更新
- 试图变化,数据自动更新
-
语法:
v-model='变量'
-
v-model
应用于其他表单元素,它会根据控件类型自动选取正确的方法来更新元素radio
前置理解:name:
给单选框加上name
属性,就可以分组(同一组互相会互斥value:
给单选框加上value
属性,用于提交给后台的数据
select
前置理解:option
需要设置value
值,提交给后台select
的value
值,关联了选中的option
的value
值
-
原理:本质上是个语法糖
-
例如应用到输入框上,就是
value
属性和input
事件的合写。 -
数据变,视图跟着变
:value
-
视图变,数据跟着变
@input
-
$event
用于在模板中,获取事件的形参
-
记事本小案例
- 1、列表渲染
v-for
key
的设置{{}}
插值表达式
- 2、删除功能
v-on
调用传参filter
过滤,覆盖修改原数组
- 3、添加功能
v-model
绑定unshift
修改原数组添加
- 4、底部统计和清空
- 数组
.length
累计长度 - 覆盖数组清空列表
v-show
控制隐藏
- 数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="./css/index.css" />
<title>记事本</title>
</head>
<body>
<!-- 主体区域 -->
<section id="app">
<!-- 输入框 -->
<header class="header">
<h1>记事本</h1>
<!-- @keyup.enter 监听键盘回车事件 -->
<input @keyup.enter="add" v-model="todoName" placeholder="请输入任务" class="new-todo" />
<button @click="add" class="add">添加任务</button>
</header>
<!-- 列表区域 -->
<section class="main">
<ul class="todo-list">
<li class="todo" v-for="(item,index) in list" :key="item.id">
<div class="view">
<span class="index">{{ index + 1 }}.</span> <label>{{ item.name }}</label>
<button @click="del(item.id)" class="destroy"></button>
</div>
</li>
</ul>
</section>
<!-- 统计和清空 如果没有任务了 底部隐藏掉 v-show -->
<footer class="footer" v-show="list.length > 0">
<!-- 统计 -->
<span class="todo-count">合 计:<strong> {{ list.length }} </strong></span>
<!-- 清空 -->
<button @click="clear" class="clear-completed">
清空任务
</button>
</footer>
</section>
<!-- 底部 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
//添加功能
//1.通过v-model绑定输入框,实时获取表单元素的内容
//2.点击按钮,进行新增,往数组最前面加 unshift
const app = new Vue({
el: '#app',
data: {
todoName: '',
list:[
{id: 1, name: '跑步800m'},
{id: 2, name: '打球1h'},
{id: 3, name: '看半本书'},
]
},
methods: {
del(id){
this.list = this.list.filter(item => item.id !== id)
},
add(){
if(this.todoName.trim() === ''){
alert('请输入任务')
return
}
this.list.unshift({
id: +new Date(),
name: this.todoName
})
this.todoName = ''
},
clear(){
this.list = []
}
},
})
</script>
</body>
</html>
指令的修饰符
@keyup.enter
:监听键盘回车事件- 相当于帮我们加了
e.key === enter
这个判断
- 相当于帮我们加了
v-model
修饰符.trim
:去首尾空格.number
:尝试转成数字(能转的就转
@事件名.stop
:阻止冒泡@事件名.prevent
:阻止默认行为
v-bind对样式控制的增强
-
操作
class
-
语法:
:class="对象/数组"
-
对象:键就是类名,值就是布尔值。值为
true
,就有这个类,否则就没有<div class="box" :class="{类名1: 布尔值, 类名2: 布尔值}"></div>
-
使用场景:一个类名,来回切换(京东导航栏
-
数组:数组中所有的类,都会添加到盒子里,本质是一个
class
列表<div class="box" :class="[类名1, 类名2]"></div>
-
使用场景:批量添加或删除类
-
-
-
操作
style
-
语法:
:style="样式对象"
<div class="box" :style="{css属性名1: css属性值, css属性名2: css属性值}"></div>
-
使用场景:某个具体属性的动态设置(进度条
-
计算属性
-
概念:基于现有的数据,计算出来的新属性。依赖的数据变化,自动重新计算。
-
语法:
-
1、声明在
computed
配置项中,一个计算属性对应一个函数computed: { 计算属性名 () { 基于现有数据,编写求值逻辑 return 结果 } }
-
2、使用起来和普通属性一样:
{{ 计算属性名 }}
-
-
computed vs methods
computed
作用:封装了一段对于数据的处理,求得一个结果。- 缓存特性(提升性能):计算属性会对计算出来的结果缓存,再次使用直接读取缓存。只有依赖项变化了,才会自动重新计算,然后再次缓存。
methods
作用:给实例提供一个方法,调用以处理业务逻辑。
-
计算属性默认的缩写,只能读取访问,不能修改。如果要修改,就要写计算属性的完整写法。
- 执行
get
会返回求值的结果 - 被修改赋值时会执行
set
,修改的值传递给set
方法的形参
computed: { 计算属性名: { get(){ 一段代码逻辑(计算逻辑 return 结果 }, set(修改的值){ 一段代码逻辑(修改逻辑 } } }
- 执行
watch监听器
-
作用:监视数据变化,执行一些业务逻辑或异步操作。
-
语法:
-
1.简单写法:简单类型数据,直接监视
watch:{ //该方法会在数据变化时,触发执行 数据属性名(newValue, OldValue){ 一些业务逻辑或异步操作 }, '对象.属性名'(newValue, OldValue){ 一些业务逻辑或异步操作 } }
oldValue
一般不用,可以不写
-
2.完整写法:添加额外配置项
deep: true
:对复杂类型深度监视immediate: true
:初始化立刻执行一次handler
方法
watch: { 监视对象: { deep: true, //监视多个 深度监视 immediate: true, //一进入页面就触发handler handler(newValue){ 一些业务逻辑或异步操作 } } }
-
生命周期
-
Vue
生命周期:一个Vue
实例从创建到销毁的整个过程。- 1.创建:响应式数据
- 发送初始化渲染请求(越早越好
- 2.挂载:渲染模板
- 操作
dom
(至少dom
要渲染出来 - 3.更新:数据修改,更新视图(循环多次
- 4.销毁:销毁实例
-
生命周期函数(钩子函数):自动运行的一些函数
记账清单案例
-
基本渲染:
- 1.
created
请求数据(封装渲染方法) - 2.拿到数据,存到
data
的响应式数据中 - 3.结合数据进行渲染
v-for
- 4.消费统计 => 计算属性
computed: { totalPrice(){ return this.list.reduce((sum, item) => sum + item.price, 0) } }, async created(){ // const res = await axios.get('https://applet-base-api-t.itheima.net/bill', { // params: { // creator: 'kk' // } // }) this.list = res.data.data this.getList() },
- 1.
-
添加功能:
- 1.收集表单数据
v-model
- 2.给添加按钮注册点击事件,发送添加请求
- 3.重新渲染
async add(){ if(!this.name){ alert('請輸入消費名稱') return } if(typeof this.price !== 'number'){ alert('请输入正确数字') return } //發送請求 const res = await axios.post('https://applet-base-api-t.itheima.net/bill,', { creator: 'kk', name: this.name, price: this.price }) //重新渲染 this.getList() this.name = '' this.price = '' },
- 1.收集表单数据
-
删除功能:
- 1.注册点击事件,传参数
id
- 2.根据
id
发送删除请求 - 3.重新渲染
async del(id){ const res = await axios.delete(`https://applet-base-api-t.itheima.net/bill/${id}`) this.getList() }
- 1.注册点击事件,传参数
-
饼图渲染:
- 1.初始化一个饼图
echarts.init(dom)
mounted
钩子实现 - 2.根据数据实时更新饼图
echarts.setOption({...})
mounted () { this.myChart = echarts.init(document.querySelector('#main')) this.myChart.setOption({ // 大标题 title: { text: '消费账单列表', left: 'center' }, // 提示框 tooltip: { trigger: 'item' }, // 图例 legend: { orient: 'vertical', left: 'left' }, // 数据项 series: [ { name: '消费账单', type: 'pie', radius: '50%', // 半径 data: [ // { value: 1048, name: '球鞋' }, // { value: 735, name: '防晒霜' } ], emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' } } } ] }) }, async getList(){ const res = await axios.get('https://applet-base-api-t.itheima.net/bill', { params: { creator: 'kk' } }) this.list = res.data.data //更新图表 this.myChart.setOption({ // 数据项 series: [ { data: this.list.map(item => ({ value: item.price, name: item.name })) } ] }) },
- 1.初始化一个饼图
工程化开发&脚手架Vue CLI
-
Vue CLI
是一个全局命令工具,可以快速搭建一个开发Vue
的标准化基础架子(集成了webpack
配置 -
使用步骤:
-
1.全局安装(一次):
npm i @vue/cli -g
或yarn global add @vue/cli
-
2.查看
Vue
版本:vue --version
-
3.创建项目架子:
vue create 项目名(不能有中文)
-
4.启动项目:
npm run serve
或yarn serve
- 这个不一定都是
serve
,要去看package.json
- 这个不一定都是
-
-
项目目录介绍
组件化开发&根组件
- 组件化:一个页面被拆分成一个个组件,每个组件都有自己独立的结构、样式、行为
- 组件分类:普通组件、根组件
- 根组件:整个应用最上层的组件,包裹所有普通小组件
- 三部分组成:
template
:结构(有且只有一个根元素script
:js逻辑style
:样式(可支持less
,需要装包- 1.给
style
加上lang="less"
- 2.安装依赖包:
npm i less less-loader --save
或yarn add less less-loader -D
- 1.给
- 三部分组成:
普通组件的注册
-
1.局部注册:只能在注册的组件内使用
-
①创建
.vue
文件(三个部分 -
②在使用的组件内导入并注册
import HmHeader from './components/HmHeader.vue'; export default { components: { //组件名:组件对象 HmHeader: HmHeader } }
-
-
使用:当成
html
标签使用<组件名></组件名>
-
PS:组件名规范:大驼峰命名法
-
2.全局注册:所有组件内都能使用
-
①创建
.vue
文件 -
②
main.js
中进行全局注册//导入代码往顶部编写 import HmButton from './components/HmButton.vue' Vue.config.productionTip = false //进行全局注册 //Vue.component('组件名', 组件对象) Vue.component('HmButton', HmButton)
-
scoped样式冲突
- 默认情况:写在组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。
- 全局样式:默认组件中的样式作用到全局
- 局部样式:可以给组件加上
scoped
属性,可以让样式只作用于当前组件
scoped
原理:- 1.给当前组件模板的所有元素,都会被添加上一个自定义属性
data-v-hash值
区分不同的组件
- 2.
css
选择器后面,会自动处理,添加上属性选择器[data-v-hash值]
data是一个函数
- 一个组件的
data
选项必须是个函数,保持每个组件实例,维护独立的一份数据对象。 - 每次创建新的组件实例,都会新执行一次
data
函数,得到一个新对象。
组件通信
-
组件通信:组件与组件之间的数据传递
- 组件的数据是独立的,无法直接访问其他组件的数据
-
组件关系:
- 父子关系
- 非父子关系
-
组件通信方案
-
父子通信:
- 1.父组件通过
props
将数据传递给子组件- 父组件给子组件标签,添加属性的方式,传值xxx
- 子组件通过
props
进行接收xxx - 渲染使用
{{ xxx }}
- 2.子组件利用
$emit
通知父组件修改更新$emit
触发事件,给父组件发送消息通知- 父组件监听事件
- 提供处理函数,形参中获取参数
- 1.父组件通过
-
非父子通信 -event bus事件总线
-
作用:非父子之间进行简易消息传递。复杂场景还是用
Vuex
-
EventBus
-
provide&inject
:跨层级共享数据
-
props详解
-
定义:组件上注册的一些自定义属性
-
作用:向子组件传递数据
- 可以传递任意数量的
prop
- 可以传递任意类型的
prop
- 可以传递任意数量的
-
props
校验为组件的prop
指定验证要求,不符合的控制台会有错误提示 -
语法:
- 类型校验:
props: {校验的属性名: 类型},
- 非空校验
- 默认值
- 自定义校验
//完整写法 props:{ 校验的属性名: { type: 类型, required: true, //是否必填 default: 默认值, validator(value){ //自定义校验逻辑 return 是否通过校验 } } }
- 类型校验:
-
prop&data
单向数据流,共同点:都可以给组件提供数据data
的数据是自己的,可以随便改prop
的数据是外部的,不能直接改,要遵循单向数据流- 单向数据流:父组件的
prop
更新,会单向向下流动,影响到子组件,这个数据的流动是单向的
- 单向数据流:父组件的
- 谁的数据谁负责
表单类组件封装&v-model
简化代码
-
表单类组件封装
-
1.父传子:数据应该是从父组件
props
传递过来的,v-model
要拆解绑定数据 -
2.子传父:监听输入,子传父传值给父组件修改
-
直白地说,子组件没有办法直接使用
v-model
,因为数据是父组件的。如果要实现这样的功能,就要把语法糖拆开,相当于用了v-model
的原理
-
-
父组件
v-model
简化代码,实现父组件与子组件数据双向绑定- 1.子组件中:
props
通过value
接收,事件触发input
- 2.父组件中:
v-model
给组件直接绑定数据(:value + @input)
- 子组件已经配置好,才能使用
v-model
- 子组件已经配置好,才能使用
- 1.子组件中:
.sync修饰符
-
作用:可以实现子组件与父组件的双向绑定,简化代码
-
特点:
prop
属性名可以自定义,非固定为value
-
场景:封装弹框类的基础组件,
visible
属性true
显示false
隐藏 -
本质:就是
:属性名
和@update:属性名
合写
ref 和 $refs
- 作用:利用
ref
和$refs
可以用于获取dom
元素或组件实例 - 特点:查找范围是当前组件,更精确稳定
querySelector
查找范围是整个页面
- 用法:
- 1.获取
dom
- 目标标签 - 添加
ref="xxx"
属性 - 通过
this.$refs.xxx
获取目标标签
- 目标标签 - 添加
- 2.获取组件
- 目标组件 - 添加
ref="xxx"
属性 - 通过
this.$refs.xxx.组件方法()
获取目标组件,就可以调用组件对象里面的方法
- 目标组件 - 添加
- 1.获取
Vue异步更新、$nextTick
- 需求:编辑标题,编辑框自动聚焦
- 1.点击编辑,显示编辑框
- 2.让编辑框立刻获取焦点
- 问题:“显示之后”立刻获取焦点是不能成功的!因为
Vue
是异步更新DOM
(提升性能) $nextTick
:等DOM
更新后,才会触发执行此方法里的函数体- 语法:
this.$nextTick(函数体)
自定义指令
-
自己定义的指令,可以封装一些
dom
操作,扩展额外功能 -
语法:
- 全局注册
- 局部注册
inserted
会在指令所在的元素,被插入到页面时触发
-
指令的值
- 语法:在绑定指令时,可以通过等号的形式为指令绑定具体的参数值
- 通过
binding.value
可以拿到指令值,指令值修改会触发update
函数 update
钩子可以监听指令值的变化,进行dom
更新操作
封装v-loading
指令
-
场景:在开发过程中,发送请求需要时间,在请求的数据未回来的时候,页面会处于空白状态,这样用户体验不好
-
需求:封装
v-loading
指令,实现加载中的效果 -
分析:
- 1.本质
loading
效果就是一个蒙层,盖在了盒子上 - 2.数据请求中,添加蒙层
- 3.数据请求完毕,移除蒙层
- 1.本质
-
实现:
- 1.准备一个
loading
类,通过伪元素定位,设置宽高,实现蒙层 - 2.添加移除蒙层,本质只需要添加移除类
- 3.结合自定义指令的语法进行封装复用
directives: { loading: { inserted(el, binding){ binding.value ? el.classList.add('loading') : el.classList.remove('loading') }, update(el, binding){ binding.value ? el.classList.add('loading') : el.classList.remove('loading') } } }
- 1.准备一个
插槽
-
作用:让组件内部的一些结构支持自定义
-
插槽分类:
- 默认插槽:定制一处结构
- 具名插槽:定制多处结构
-
作用域插槽是插槽的一个传参语法
-
默认插槽:一个定制位置
-
基本语法:
-
1.在要定制的结构部分,改用
<slot></slot>
占位 -
2.使用组件时,
<MyDialog></MyDialog>
标签内部,传入结构替换slot
-
-
后备内容(默认值):封装组件时,可以在
<slot>
标签内放置内容,作为默认显示内容
-
-
具名插槽:一个组件内有多处结构需要外部传入标签
- 语法:
- 1.多个
slot
使用name
属性来区分名字 - 2.
template
包裹起来,v-slot: 插槽名
来分发标签,v-slot: 插槽名
可以简化成#插槽名
- 1.多个
- 一旦插槽取名字,就表明是具名插槽,定向分发
- 语法:
-
作用域插槽:定义
slot
的同时,是可以传值的。给插槽上可以绑定数据,将来使用组件时可以用。-
场景:封装表格组件
- 1.父传子,动态渲染表格内容
- 2.利用默认插槽,定制操作列
- 3.删除或查看都需要使用当前项的
id
,属于组件内部的数据,通过作用域插槽传值绑定,进而使用
-
使用步骤:
- 1.给
slot
标签,以添加属性的方式传值 - 2.所有添加的属性都会被收集到一个对象中
- 3.在
template
中,通过#插槽名="obj"
接收,默认插槽名是default
- 1.给
-
单页面应用SPA
- 页面按需更新,这样开发效率高,性能高,用户体验好
- 按需更新,首先要明确访问路径和组件的对应关系
路由
-
Vue
中路由:路径和路由的映射关系 -
VueRouter
:修改地址栏路径时,切换显示匹配的组件- 使用(5 + 2):
- 1.下载:vue2下载3.x,vue3下载4.x
npm i install vue-router@3.6.5
- 2.引入:
import VueRouter from 'vue-router'
- 3.安装注册:
Vue.use(VueRouter)
- 4.创建路由对象:
const router = new VueRouter()
- 5.注入,将路由对象注入到
new Vue
实例中,建立关联 - 2个核心步骤:
- ①创建需要的组件(views目录),配置路由规则
- ②配置导航,配置路由出口(路径匹配的组件显示的位置)
- PS:
route 一条路由规则 { path: 路径, component: 组件 }
<router-view></router-view>
:路由出口 → 匹配的组件所展示的位置
-
组件分类更易维护
src/views
存放页面组件-页面展示-配合路由用src/components
存放复用组件-展示数据-常用于复用
-
快速引入组件:基于@代指
src
目录,从src
目录出发找组件 -
Vue
路由 - 重定向- 问题:网页打开,
url
默认是/
路径,未匹配到组件显示空白 - 说明:重定向 —-> 匹配
path
后,强制跳转path
路径 - 语法:
path: 匹配路径, redirect: 重定向到的路径
- 问题:网页打开,
-
Vue
路由 - 404- 作用:路径找不到匹配时,给出个提示页面
- 位置:配在路由最后
- 语法:
path: '*'(任意路径), component: NotFound
-
Vue
路由 - 模式设置hash
路由(默认):会有#
history
路由(常用):一旦采用history
,地址栏就没有#
,需要后台配置访问规则- 在路由对象
mode
里面设置
声明式导航
-
声明式导航
router-link
,取代a标签-
①能跳转,配置
to
属性必须指定路径,to
不需#
-
②能高亮,默认会自动添加两个高亮类名
router-link-active
:模糊匹配,用得多- 例如:
/find
,则/find/xxx、/find/one
也能匹配 router-link-exact-active
:精确匹配- 例如:
/find
,则只有/find
能匹配
-
定制两个高亮类名
const router = new Router({ routes: [...], linkActiveClass: "类名1", linkExactActiveClass: "类名2" })
-
-
声明式导航 - 跳转传参
-
1.查询参数传参(比较适合传多个参数
- 语法格式:
to="/path?参数名=值"
- 对应页面组件接收传递过来的值:
$route.query.参数名
- 语法格式:
-
2.控制路由传参(传单个参数方便
-
①配置动态路由
const router = new Router({ routes: [ ..., { path: '/search/:参数名', component: Search } ], })
-
②配置导航链接:
to="/path/参数值"
-
③对应页面接收传递过来的值:
$route.params.参数名
-
比如:在配置路由
path: '/serach/:words'
,没有传参数会匹配不到组件,显示空白。如果不传参数也希望匹配,就可以加个可选符?
,即path: '/serach/:words?'
-
-
编程式导航
-
编程式导航 - 基本跳转
-
①
path
路径跳转(简易方便)this.$router.push('路由路径') 或 this.$router.push({ path: '路由路径' })
-
②
name
命名路由跳转(适合path
路径长的场景):需要给路由取名字{name: '路由名', path: '/xxx', component: xxx} this.$router.push{ name: '路由名' }
-
-
编程式导航 - 路由传参
-
1.
path
路径跳转传参-
query
传参的语法格式:this.$router.push('/path?参数名1=值1&参数名2=值2') this.$router.push({ path: '路径', query: { 参数名1: '参数值1', 参数名2: '参数值2' } })
-
对应页面组件接收传递过来的值:
$route.query.参数名
-
动态路由传参(需要配动态路由)的语法格式:
-
PS:
push
里面是``,不是引号this.$router.push(`/path/参数值`) this.$router.push({ path: '/路径/参数值', })
-
对应页面接收传递过来的值:
$route.params.参数名
-
-
2.
name
命名路由跳转传参-
query
传参的语法格式:this.$router.push({ name: '路径名字', query: { 参数名1: '参数值1', 参数名2: '参数值2' } })
-
对应页面组件接收传递过来的值:
$route.query.参数名
-
动态路由传参(需要配动态路由)的语法格式:
this.$router.push({ name: '路由名', params: { 参数名:'参数值', } })
-
对应页面接收传递过来的值:
$route.params.参数名
-
-
组件缓存 keep-alive
- 内置组件,当它包裹动态组件时,会缓存不活动的组件,而不是销毁它们。
- 它自身不会渲染成一个DOM元素,也不会出现在父组件链中。
- 优点:在组件切换过程中,把切换出去的组件保存在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验。
- 三个属性:
:include="组件名数组"
:只有匹配的组件会被缓存- 如果没有配置
name
,才会去找文件名作为组件名 - 被缓存的组件多了两个生命周期钩子:
activated
:激活时,组件被看到触发deactivated
:失活时,离开页面组件看不见触发
- 组件缓存后就不会执行组件的
created、mounted、destroyed
钩子了
- 如果没有配置
exclude
:任何匹配的组件都不会被缓存max
:最多可以缓存多少组件实例,用==LRU
算法==(最久没有被访问的)来替换组件
Vuex
-
vuex
是一个状态管理工具,状态就是数据,就是一个插件,帮我们管理vue
通用的数据(多组件共享的数据) -
场景:
- 某个状态在很多个组件来使用(个人信息
- 多个组件共同维护一份数据(购物车
-
优势:
- 共同维护一份数据,数据集中化管理
- 响应式变化
- 操作简单,
vuex
提供了一些辅助函数
-
state
状态- 1.
State
提供唯一的公共数据源,所有共享的数据都要统一放到Store
中的State
中存储,在state
对象中可以添加我们要共享的数据 - 2.使用数据:
- 通过
store
直接访问(一层一层往下找- 模板中:
{{ $store.state.xxx }}
- 组件逻辑中:
this.$store.state.xxx
- js模块中:
store.state.xxx
- 模板中:
- 通过辅助函数:
mapState
能把store
中的数据自动映射到组件的计算属性中- 导入
mapState
:import { mapState } from 'vuex'
- 数组方式引入
state
:...mapState(['属性'])
- 展开运算符
...
映射
- 导入
- 通过
- 1.
-
vuex
同样遵循单向数据流,组件中不能直接修改仓库的数据,可以通过mutations
来修改数据- 通过
strict: true
可以开启严格模式来检测错误代码
- 通过
-
mutations
:必须是同步的,便于监测数据变化,记录调试-
使用:
- 1.定义
mutations
对象,对象中存放修改state
的方法 - 2.组件中提交调用
mutations
:this.$store.commit('
mutations中的方法名')
- 1.定义
-
传参语法
-
1.提供
mutations
函数(带参数 - 提交载荷payload
)mutations: { ... xxx (state, 参数) { 方法逻辑 } }
-
PS:
mutations
参数有且只能有一个,如果需要多个参数,可以包装成一个对象或数组 -
2.页面中提交调用
mutations
:this.$store.commit('xxx', 参数)
-
-
-
mapMutations
:把位于mutations
中的方法提取出来,映射到组件methods
中。相当于把仓库里的数据和方法拿到了当前组件内,就可以直接使用了。 -
actions
:处理异步操作-
需求:一秒之后,修改
state
的count
-
语法:
-
1.提供
actions
方法 -
不能直接操作
state
,操作state
还是需要commit mutation
actions: { setAsynCount (context, num) { //context 上下文 此处未分模块,可以当成store仓库 //setTimeout模拟异步 以后大部分场景是发请求 setTimeout (() => { context.commit('方法名', num) }, 1000) } }
-
2.页面中
dispatch
调用:this.$store.dispatch('setAsynCount', 参数)
-
也有且只能有一个参数
-
-
-
mapActions
:把位于actions
中的方法提取出来,映射到组件methods
中。相当于把仓库里的数据和方法拿到了当前组件内,就可以直接使用了。 -
getters
:类似于计算属性,需要从state
派生出一些状态,这些状态是依赖state
的,此时会用到getters
-
1.定义
getters
getters: { //第一个参数是state //getters函数必须要有返回值,返回的就是getters的值 xxx (state) { return xxxx } }
-
2.访问
getters
-
通过
store
:{{ $store.getters.xxx }}
-
通过
mapGetters
映射computed: { ...mapGetters(['xxx']) } {{ xxx }}
-
-
-
PS:
mapMutations
和mapActions
都是映射方法,而mapState
和mapGetters
都是映射属性
模块 module
- 模块
module
:由于vuex
使用单一状态树,应用的所有状态会集中到一个比较大的对象,当应用变复杂时,store
对象就会可能变得很臃肿。 - 使用模块中的数据:
- 1.直接通过模块名访问
$store.state.模块名.xxx
- 2.通过
mapState
映射- 默认根级别的映射
mapState(['xxx'])
- 子模块的映射
mapState('模块名', ['xxx'])
- 需要开启命名空间,在子模块的js文件的
export
加上namespaced: true
- 需要开启命名空间,在子模块的js文件的
- 默认根级别的映射
- 1.直接通过模块名访问
- 分模块后,
getters
的函数里state
参数指的是子模块的state
- 默认模块中的
mutation
和actions
会被挂载到全局,需要开启命名空间,才会挂载到子模块- 调用子模块中的
mutation
:- 直接
store
调用:$store.commit('模块名/xxx', 额外参数)
- 通过
mapMutations
映射:- 根级别:
mapMutations(['xxx'])
- 子模块:
mapMutations('模块名', ['xxx'])
- 根级别:
- 直接
context
上下文,默认提交的是自己模块的mutation
和actions
- 调用子模块中的
- PS:
actions
跟这个没区别,只不过是直接调用是:$store.dispatch('模块名/xxx', 额外参数)