vue2 组件通信
props + emits
props
:用于接收父组件传递给子组件的数据。可以定义期望从父组件接收的数据结构和类型。‘子组件不可更改该数据’emits
:用于定义组件可以向父组件发出的事件。这允许父组件监听子组件的事件并作出响应。(比如数据更新)
props检查属性
属性名 | 类型 | 描述 | 默认值 |
---|---|---|---|
type | Function | 指定 prop 应该是什么类型,如 String , Number , Boolean , Object , Array , Function , 或它们的构造函数。 | 无 |
default | any / Function | 指定 prop 的默认值。可以是一个值,也可以是一个返回值的函数。 | 无 |
required | Boolean | 指定 prop 是否必须传递。 | false |
validator | Function | 一个验证函数,用于更复杂的验证逻辑。函数应该返回一个布尔值,表示验证是否通过。 | 无 |
说明
type
: 确保prop
符合预期的数据类型,有助于在开发过程中捕获错误。default
: 当父组件没有传递相应的prop
时,提供一个回退值,确保组件即使在没有接收到外部数据的情况下也能正常运行。required
: 标记为true
时,如果父组件没有提供该prop
,Vue 会在控制台中显示警告,这有助于避免因缺少必要数据而导致的错误。validator
: 提供了一个更灵活的验证机制,允许开发者定义复杂的验证逻辑,确保prop
符合特定的条件或规则。
示例
父
<template>
<div>
<!--
使用对象语法统一传递多个 props
<child-component v-bind="propsData"></child-component>
-->
<child-component :size.sync="size":height="height" @my_event ="height = height + $event"></child-component>
//:size.sync = :size="size" + @update:size="size = $event"
//vue3 废弃
</div>
</template>
<script>
// 导入子组件
import ChildComponent from './ChildComponent.vue'; // 确保文件路径正确
export default {
data() {
return {
propsData: {
size: 10,
myMessage: "test",
height: 50,
age: 10
}
};
},
components: {
ChildComponent // 注册子组件
}
};
</script>
子–Children
<script>
export default {
// props: ['size', 'myMessage'], //简易声明
props: {
size: Number,
// 类型检查
height: Number,
// 类型检查 + 其他验证
age: {
type: Number, // 指定 prop 类型为 Number
default: 0, // 如果没有提供 age,将使用默认值 0
required: true, // 指定 age 必须传递
validator: value => {
return value > 0; // 验证 age 必须大于 0
}
}
},
methods:{
ChangeElements(){
this.$emit('update:size',v);
//:size.sync="size"
this.$emit('my_event',v);
//:height="height" @my_event ="height = height + $event"
}
}
}
</script>
.sync
- 一个语法糖
- :value.sync=‘value’ <==> :value=“calue” @update:value=“value = $event”
说明:
- 父组件模板:使用
v-bind
指令(或简写为:
)和对象语法来统一传递propsData
对象中的所有属性作为 props。v-bind="propsData"
会将propsData
对象中的所有属性作为独立的 props 传递给子组件。 - 父组件数据:在
data
返回的对象中定义了一个propsData
对象,包含了所有要传递给子组件的属性。 - 子组件 props:子组件定义了
size
、myMessage
、height
和age
四个 props,这些 props 用于接收从父组件传递的数据。
--------非父子通信-------
provide + inject
- 跨层级共享数据
父
- 普通类型
非响应式
- 复杂类型
响应式
- 引用传递
<script>
export default{
provide(){
return{
//普通类型[非响应式]
age:120,
//复杂类型[响应式]
a:{b:2,c:3}
}
}
}
</script>
后代
<script>
export default{
inject:['age','a'],
created(){
console.log(this.age,this.a);
this.age = 20; //报错不可更改
this.a.b = 110;//界面会响应,父类的age同步更改
}
}
</script>
event bus
- 事件总线, 在非父子组件之间通信
事件总线
//创建一个都可以访问到的事件总线(空的 Vue实例)
import Vue from 'vue'
const Bus = new Vue();
export default Bus
A组件
<script>
import Bus from 'bus地址'
export default{
created(){
Bus.$on('AMsg',(msg)=>{
console.log(msg)
})
}
}
</script>
B组件
<script>
import Bus from 'bus地址'
export default{
methods:{
sendAMsg(){
Bus.$emit('AMsg',"你的消息")
}
}
}
</script>
Vuex
-
是什么
-
复杂场景组件之间通信
-
vuex是vue的一个状态管理工具,状态就是数据
-
大白话: vuex是一个插件,可以帮助我们管理
vue通用数据(多组件数据共享)
-
-
场景
- 某个状态在多个组件使用(个人信息)
- 多个组件 共同维护 一份数据(购物车)
-
优势
- 数据集中化管理
- 响应式
-
vuex遵循单向数据流
初始配置
-
安装 vuex
npm install vuex@next --save # 对于 Vue 3
npm install vuex --save # 对于 Vue2
-
新建vue模块
- 新建
store/index.js
文件 专门存放Vuex
- 新建
-
创建仓库
// store/index.js // 导入 Vuex,它是 Vuex 状态管理库的核心 import Vuex from 'vuex'; // 创建一个新的 Vuex.Store 实例 // 这个实例将作为全局状态管理器 export default new Vuex.Store({ // 定义应用的状态(state) state: { // 一个名为 count 的状态,初始值为 0 count: 0 }, // 定义更改状态的方法(mutations) mutations: { // increment 方法用于更新 count 状态 // state 是 Vuex state 对象,v 是传递给 mutation 的参数 increment(state, v) { // 将 count 状态更新为传入的参数 v state.count = v; } }, // 定义异步提交状态更改的方法(actions) actions: { // increment 方法用于异步提交 increment mutation increment({ commit }) { // 调用之前定义的 increment mutation commit('increment',10); } }, // 定义从状态派生的状态(getters) getters: { // doubleCount 方法返回 count 的两倍 // state 是 Vuex state 对象 doubleCount(state) { // 返回 count 状态的两倍 return state.count * 2; } } });
-
挂载main.js
// main.js
import Vue from 'vue';
import App from './App.vue';
import store from './store/index';
// 使用 Vuex 插件
Vue.use(Vuex);
new Vue({
store,
render: h => h(App)
}).$mount('#app');
配置项
new Vuex.Store({配置项})
配置项 | 类型 | 描述 |
---|---|---|
state | Object | 根状态对象,包含所有的状态数据 |
getters | Object | 根getter函数,允许从state派生出一些状态 |
mutations | Object | 同步函数,用于变更state中的状态数据在 ;Vue 3 中,mutations 应改为 actions 和 commit ,因为 Vue 3 的 Vuex 4 中不再使用 mutations 。 |
actions | Object | 异步操作或复杂逻辑的提交(调用mutation)的包裹函数 |
modules | Object | 命名空间模块对象,用于将store分割成模块 |
plugins | Array | 自定义插件数组,用于进一步处理 actions |
strict | Boolean | 是否开启严格模式,开启后在mutation之外修改state将抛出错误 |
devtools | Boolean / Object | 是否启用 Vue Devtools 支持,可以传递选项对象 |
state
-
根状态对象,包含所有的状态数据
-
核心配置项(必要的)
-
获取数据
- 通过
this.$store.属性名
- 通过 import 导入 store 然后使用
- 通过
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex); // 使用 Vuex 插件
const store = new Vuex.Store({
state: {
count: 1
},
});
getters
- 根getter函数,允许从state派生出一些状态
- 对数据进一步,派生状态
- 使用
$store.getters.filterList()
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex); // 使用 Vuex 插件
const store = new Vuex.Store({
state: {
list: [1,2,3,4,5,6,7,8,9]
},
getters:{
//过滤 <= 5 的数据
filterList(state){
return state.list.filter(item=> item > 5)
}
}
});
mutations/actions
-
用于变更state中的状态数据
-
actions:异步操作或复杂逻辑的提交(调用mutation)的包裹函数
-
Mutations:通常使用全小写字母,并且可以使用下划线来分隔单词,例如
update_count
。- 第一个参数是
state
也就是状态 ,通过state 来调用数据
- 第一个参数是
-
Actions:可以使用驼峰命名法,并且通常在名称中包含动词,以表明它们执行的操作,例如
updateCount
。- 第一个参数是context 是上下文,和
state
相似
- 第一个参数是context 是上下文,和
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex); // 使用 Vuex 插件
const store = new Vuex.Store({
state: {
count: 10
},
actions: {
async updateCount({ commit }, n) {
commit('update_count', n);
},
async updateCount2(context, n) {
context.commit('update_count', n);
}
},
mutations: {
//可以同名,但是不推荐
update_count(state, n) {
state.count = n;
}
}
});
export default store;
modules
分模块
- Vue 使用单一状态树,当项目比较大时,状态会集中,从而导致项目臃肿
- 将vuex配置文件导入 根配置下的 modules 就可以了
导入模块
// store/modules/user.js
const state = { ... };
const mutations = { ... };
const actions = { ... };
const getters = { ... };
export default {
state,
mutations,
actions,
getters
};
// store/index.js 根模块
import Vuex from 'vuex';
import user from './modules/user';
const store = new Vuex.Store({
modules: {
user
}
});
export default store;
使用
-
子模块state
$store.state.模块名.属性名
- mapState映射:
mapState('模块名',['属性名']);
this.属性名
就可以访问数据了
-
子模块getters
$store.getters['模块名/属性名称']
- mapGetters映射:
mapGeters(‘模块名’,[‘属性名’,‘属性2’])
this.属性名
就可以获得数据 模块名/属性名称
:是因为命名是时候就是这命名的
-
子模块mutations
$store.commit['模块名/属性名称']
- mapMutations映射:
mapMutations(‘模块名’,[‘函数名’,‘函数2’])
this.函数名()
就可以调用函数了 模块名/属性名称
:是因为命名是时候就是这命名的
-
子模块actions
-
$store.dispatch['模块名/属性名称']
-
mapActions映射:
mapActions(‘模块名’,[‘函数名’,‘函数名2’])this.函数名()
就可以调用函数了 -
模块名/属性名称
:是因为命名是时候就是这命名的
-
数据的更改
this.$emit()
是 Vue 组件中用来触发自定义事件的方法。当调用this.$emit('eventName', param)
时,它会向父组件发出一个事件,父组件可以监听这个事件并做出响应。- 同步:
- 而在 Vuex 中,
this.$store.commit('mutationName', param)
用于触发状态变更。这里的commit
方法用于调用 store 中定义的 mutations,mutations 是同步函数,用于更改 store 中的状态(state)。
- 而在 Vuex 中,
- 异步:
this.$store.dispatch('actionName', payload)
来分发 actions。这会告诉 Vuex 执行相应的 action 函数。
辅助函数
mapState
- 将store中的数据,
自动
映射到 组件的计算属性中
- 可以直接
this.属性名
来访问数据
//引入mapState
import { mapState } from 'vuex';
//使用
export default {
computed: {
// 使用对象展开运算符将所有映射的状态转为计算属性
...mapState([
// 映射 this.count 为 store.state.count
'count'
]),
// 映射 this.doubleCount 为 store.getters.doubleCount
...mapState({
'doubleCount': state => state.count * 2
})
}
}
//-------------------
computed:{ ...mapState['状态名'] }
//# 等价
computed:{ '状态名'(){
return this.$store.state.count
}}
mapMutatuons
- 将mutations同步函数 中的方法,映射到
methods
中 - 可以直接
this.函数名(n)
来调用函数
//引入 mapMutatuons
import {mapMutatuons} from 'vuex'
//使用
export default {
methods: {
...mapMutations([
'update_count' // 直接使用 mutation 名称
]),
...mapMutations({
'set_count': 'update_count' // 为 mutation 提供一个别名
}),
increment() {
this.update_count(1); // 调用 Vuex store 的 mutation
},
decrement() {
this.set_count(-1); // 使用别名调用 mutation
}
}
}
//---------------------------------
methods:{
...mapMutations(['方法名'])
}
//等价
methods:{
'方法名'(参数){
this.$store.commit('方法名',参数)
}
}
mapActions
- 将actions异步操作中的方法,映射到
methods
中 - 可以直接
this.函数名(n)
来调用函数
//导入mapActions
import { mapActions } from 'vuex';
//使用
export default {
methods: {
...mapActions([
'UpdateCount' // 将 `this.update_count` 映射为 `this.$store.update_count('actionName',obj)`
]),
...mapActions({
'mappedActionName': 'UpdateCount' // 将 `this.mappedActionName` 映射为 `this.$store.dispatch('update_count')`
}),
}
}
//--------------------
methods:{
..,mapActions([方法名])
}
# 等价
methods:{
'方法名'(参数){
this.$store.dispatch('方法名',参数)
}
}
mapGetters
- 将getters中的函数 中的方法,映射到
computed
中 - 可以直接
this.函数名(n)
来调用函数
示例
<template>
</template>
<script>
import {mapState,mapMutations,MapActions,MapGetters} form 'vuex'
export default{
name:'test',
computed:{
...mapState(['count']),
...maoGetters(['filterList'])
},
methods:{
...mapMutations('updata_count'),
...MapActions('UpdataCount')
}
}
</script>
$refs获取dom
- ref和$refs
- 获取
dom元素
或组件实例
- ref和$refs=> 当前组件内 (更精确)
- document => 当前页 (范围广)
- 和 id 的用法相似
<template>
<div ref="MyDom" id="MyDom"></div>
<TestEl ref="MyEl"></TestEl>
</template>
<script>
export default{
mounted(){
// dom元素
this.$ref.MyDom
//等价
document.getElementById('myDom')
//组件实例
let vm = this.$ref.MyEl
vm.get_methods();
//可以调用TestEl里面的数据
},
components:{
TestEl
}
}
</script>
$nextTick异步跟新
- Vue采用的是异步跟新
<template>
<div>
<div v-if="isShow" ref="_1"> ... </div>
<div v-else ref="_2"> ... </div>
</div>
</template>
<script>
export default{
data(){return {isShow:true}},
methods:{
change(){
this.isShow = false;
this.$ref._2.focus();
//警告this.$ref._2为 undefined
//因为采用的是异步跟新,所以当前还没有跟新 _2这个元素
this.$nextTick(()=>{ //当异步跟新完成后的回调
this.$ref._2.focus();
})//可以的
}
}
}
</script>