Vue2 --- 脚手架编程 -- 基础语法

0. ref 属性

1. 普通标签绑定ref属性,用来获取Dom元素

<template>
  <div class="hello">
    <!-- 1. 使用 ref 绑定标签 -->
    <h1 ref="title">{{ msg }}</h1>
    <button @click="showDom">点我输出上面的Dom元素</button>
  </div>
</template>

<script>
export default {
  name: 'school-info',
  data() {
    return {
      msg: "你好"
    }
  },
  methods: {
    showDom() {
      // 2. 所有被ref绑定Dom元素都被存储在了$refs里
      console.log(this.$refs.title);
    }
  }
}
</script>

1. 父向子传递数据

1. 子组件接收数据

1. 简单声明接收数据

R
StudentInfo.vue

<template>
    <div>
        <h1>{{title}}</h1>
        <h2>学生姓名: {{name}}</h2>
        <h2>学生性别: {{sex}}</h2>
        <h2>学生年龄: {{age}}</h2>
    </div>
</template>

<script>
    export default {
        name: "Student-info",
        data(){
            return {
                title: "这是学生信息页面"
            }
        },
        // 声明这个页面需要向外部接收的数据
        props:["name","sex","age"]
    }
</script>

<style scoped>

</style>

2. 对参数做限制

1. 可以限制传递的参数类型

如果接收的数据类型错误,会在控制台报错,需要的是什么类型,传过来的是什么类型

<template>
    <div>
        <h1>{{title}}</h1>
        <h2>学生姓名: {{name}}</h2>
        <h2>学生性别: {{sex}}</h2>
        <h2>学生年龄: {{age+1}}</h2>
    </div>
</template>

<script>
    export default {
        name: "Student-info",
        data() {
            return {
                title: "这是学生信息页面"
            }
        },
        // 声明这个页面需要向外部接收的数据,并限制传进来的类型
        props: {
            name:String,
            age:Number,
            sex:String
        }
    }
</script>

<style scoped>

</style>

2. 更多限制条件

<template>
    <div>
        <h1>{{title}}</h1>
        <h2>学生姓名: {{name}}</h2>
        <h2>学生性别: {{sex}}</h2>
        <h2>学生年龄: {{age+1}}</h2>
    </div>
</template>

<script>
    export default {
        name: "Student-info",
        data() {
            return {
                title: "这是学生信息页面"
            }
        },
        // 声明这个页面需要向外部接收的数据,并限制传进来的类型
        props: {
            name: {
                type: String,   // 声明参数类型
                require: true,  // 声明此参数为必传参数
            },
            age: {
                type: Number,
                default: 99,   // 设置如果没有传此参数的默认值
            },
            sex: {
                type: String,
                require: true,
            }
        }
    }
</script>

<style scoped>

</style>

3. 修改接收的参数

默认情况下子组件内接收到的参数是不允许修改的,但是有的时候可能需要对这个参数进行修改,prop的优先级比data中的参数高,如果data中定义的参数和prop冲突,那么优先使用prop中的参数

<template>
    <div>
        <h1>{{title}}</h1>
        <h2>学生姓名: {{name}}</h2>
        <h2>学生性别: {{sex}}</h2>
        <h2>学生年龄: {{myAge}}</h2>  <!-- 2.读数据为 data 中定义的 -->
        <button @click="updateAge">修改学生年龄</button>
    </div>
</template>

<script>
    export default {
        name: "Student-info",
        data() {
            return {
                title: "这是学生信息页面",
                myAge: this.age   // 1. 定义要修改的数据,数据来源于prop中的age
            }
        },
        methods:{
            updateAge(){  
                this.myAge++  // 3. 调用方法修改data中的数据
            }
        },
        props: {
            name: {
                type: String,   
                require: true,  
            },
            age: {
                type: Number,
                default: 99,   
            },
            sex: {
                type: String,
                require: true,
            }
        }
    }
</script>

<style scoped>

</style>

2. 父组件传递数据

SchoolInfo.vue

<template>
    <div>
        <!-- 使用自定义属性向子组件传递 name,sex,age 参数,不允许自定义Vue内置的一些属性 -->
        <student-info name="小明" sex="男" age="18"/>
    </div>
</template>

<script>
    import StudentInfo from "@/components/StudentInfo";
    export default {
        name: "schoolInfo",
        components: {StudentInfo},
        comments:{
            StudentInfo
        }
    }
</script>

<style scoped>

</style>

3. 参数的类型

1. 传递字符串类型

<template>
    <div>
        <!-- age="18" 传的是字符串类型 -->
        <student-info name="小明" sex="男" age="18"/>
    </div>
</template>

2. 传递数值类型

<template>
    <div>
        <!-- :age="18" 前面加了冒号: ,传的是数值类型 -->
        <student-info name="小明" sex="男" :age="18"/>
    </div>
</template>

2. 子向父传递数据

1. 自定义函数携带数据

1. 父组件中定义一个函数,并将此函数传递到子组件中

App.vue

<template>
    <div id="app">
        <div class="todo-container">
            <div class="todo-warp">
                <Header :receiveTodos="receiveTodos"/>
            </div>
        </div>
    </div>
</template>

<script>

    import Header from "@/components/TodoHeader";

    export default {
        name: 'App',
        components: {
            Header
        },
        data() {
            return {
                // 1. 定义事件列表
                todos: [
                    {id: "1", title: "吃饭", done: false},
                    {id: "2", title: "睡觉", done: true},
                    {id: "3", title: "打豆豆", done: false},
                ]
            }
        },
        methods:{
            receiveTodos(todoObj){
                console.log("我是App组件,我接受到了来自子组件的参数: ",todoObj)
            }
        }
    }
</script>

<style>
</style>

Header.vue

<template>
    <div class="todo-header">
        <!-- 1. 绑定回车键按下再抬起的事件,并将input中输入的值和title双向绑定 -->
        <input type="text" placeholder="请输入你的任务名称,按回车确认" v-model="title" @keyup.enter="addTodo">
    </div>
</template>

<script>
    // 3.2 导入nanoid包,用来生成唯一随机字符串
    import {nanoid} from 'nanoid'

    export default {
        name: "HeaderInfo",
        props:["receiveTodos"],  // 1. 声明接收来自App组件的参数名
        data() {
            return {
                title: ""
            }
        },
        methods: {
            addTodo() {
                console.log(this.title)
                const todoObj = {id: nanoid(), title: this.title, done: false}
                // 2. 调用receiveTodos(todObj), 并将用户生成的todo对象传到receiveTodos(todObj)函数中
                // 控制台可以看到父组件App接收到了子组件传递过来的参数
                this.receiveTodos(todoObj)
            }
        }
    }
</script>

<style scoped>
</style>

2. 自定义事件传递参数

自定义事件是只给组件来使用的

1. 定义自定义事件

App.vue

<template>
    <div id="app">
        <h1>这里是APP: </h1>
        <!-- 1. 用 v-on:事件名="函数名" / @事件名="函数名" 定义一个自定义事件,如果时间被触发了就调用 getStudentName()函数-->
        <student-info @getName="getStudentName"/>
    </div>
</template>

<script>
    import SchoolInfo from "@/components/SchoolInfo";
    import StudentInfo from "@/components/StudentInfo";

    export default {
        name: 'App',
        components: {
            SchoolInfo,
            StudentInfo
        },
        methods:{
            // ...options 接收其他参数,是个数组
            getStudentName(name,...options){
                console.log("App 组件的getStudentName被调用了",name)
            }
        }
    }
</script>

<style>
    #app {
        font-family: Avenir, Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
    }
    h1 {
        background-color: red;
    }
</style>

2. 触发自定义事件

Student.vue

<template>
    <div class="student">
        <h2>学生姓名: {{name}}</h2>
        <h2>学生年龄: {{age}}</h2>
        <button @click="sendStudentName">点击发送学生姓名</button>
    </div>
</template>

<script>
    export default {
        name: "Student-info",
        data() {
            return {
                name: "小明",
                age: 18
            }
        },
        methods:{
            sendStudentName(){
                // 使用this.$emit(事件名,参数)触发Student组件实例对象上的getName事件,并传递学生姓名参数
                this.$emit("getName",this.name)
            }
        }
    }
</script>

<style scoped>
    .student{
        background-color: aqua;
    }
</style>

3. ref 标识子组件,并绑定自定义事件

子组件标签绑定 ref 属性,用来获取子组件实例对象,是组件间通信的基础

用 id 绑定组件标签只会获得组件的Dom结构,无法获得组件实例对象

App.vue

<template>
    <div id="app">
        <h1>这里是APP: </h1>
        <!-- 1. 使用ref 获取子组件实例对象的名称-->
        <student-info ref="student"/>
    </div>
</template>

<script>
    import StudentInfo from "@/components/StudentInfo";

    export default {
        name: 'App',
        components: {
            StudentInfo
        },
        // 2. 声明 mouted() 函数给子组件绑定自定义事件
        mounted() {
            // this.$refs.student,获取到子组件实例对象
            // .$on("getName",this.getStudentName) 给子组件实例对象绑定 getName事件,当这个getName事件被触发的时候执行回调函数getStudentName()
            this.$refs.student.$on("getName",this.getStudentName)
        },
        methods: {
            getStudentName(name,...options) {
                console.log("App 组件的getStudentName被调用了", name)
            }
        },
    }
</script>

<style>
    #app {
        font-family: Avenir, Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
    }

    h1 {
        background-color: red;
    }
</style>

Student.vue

<template>
    <div class="student">
        <h2>学生姓名: {{name}}</h2>
        <h2>学生年龄: {{age}}</h2>
        <button @click="sendStudentName">点击发送学生姓名</button>
    </div>
</template>

<script>
    export default {
        name: "Student-info",
        data() {
            return {
                name: "小明",
                age: 18
            }
        },
        methods:{
            sendStudentName(){
                // 1. 触发Student组件实例对象上的getName事件,并传递学生姓名参数
                this.$emit("getName",this.name)
            }
        }
    }
</script>

<style scoped>
    .student{
        background-color: aqua;
    }
</style>

4. ref 的优势

这种方法更灵活,比如我要在App挂载后等ajax请求发送得到结果后,再给Student组件实例对象绑定自定义事件

App.vue

<template>
    <div id="app">
        <h1>这里是APP: </h1>
        <!-- 1. 使用ref 获取子组件实例对象的名称-->
        <student-info ref="student"/>
    </div>
</template>

<script>
    import StudentInfo from "@/components/StudentInfo";

    export default {
        name: 'App',
        components: {
            StudentInfo
        },
        mounted() {
            // 3秒后再绑定点击事件,这里模拟ajax请求的网络延迟
            setTimeout(() => {
                this.$refs.student.$on("getName", this.getStudentName)
            }, 3000)
        },
        methods: {
            getStudentName(name,...options) {
                console.log("App 组件的getStudentName被调用了", name)
            }
        },
    }
</script>

<style>
    #app {
        font-family: Avenir, Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
    }

    h1 {
        background-color: red;
    }
</style>

5. 自定义事件也可以用事件修饰符

设置自定义事件只触发一次

App.vue

<template>
    <div id="app">
        <h1>这里是APP,学生姓名是: {{studentName}} </h1>
        <student-info ref="student"/>
    </div>
</template>

<script>
    import StudentInfo from "@/components/StudentInfo";

    export default {
        name: 'App',
        components: {
            StudentInfo
        },
        
        mounted() {
            // 1. 使用$once() 控制只触发一次
            this.$refs.student.$once("getName", this.getStudentName)
        },
        methods: {
            getStudentName(name,...options) {
                console.log("App 组件的getStudentName被调用了", name)
            }
        },
    }
</script>

<style>
    #app {
        font-family: Avenir, Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
    }

    h1 {
        background-color: red;
    }
</style>

6. 解绑自定义事件

Student.vue

<template>
    <div class="student">
        <h2>学生姓名: {{name}}</h2>
        <h2>学生年龄: {{age}}</h2>
        <button @click="sendStudentInfo">点击发送学生信息</button>
        <button @click="unbind">解绑一个自定义事件</button>
        <button @click="unbinds">解绑多个自定义事件</button>
        <button @click="unbindAll">解绑所有自定义事件</button>

    </div>
</template>

<script>
    export default {
        name: "Student-info",
        data() {
            return {
                name: "小明",
                age: 18
            }
        },
        methods: {
            sendStudentInfo() {
                this.$emit("getName", this.name)
                this.$emit("getAge", this.age)
            },
            unbind() {
                // 解绑一个自定义事件
                this.$off("getName")
            },
            unbinds() {
                // 解绑多个自定义事件
                this.$off(["getName","getAge"])
            },
            unbindAll(){
                // 解绑所有的自定义事件
                this.$off()
            }
        }
    }
</script>

<style scoped>
    .student {
        background-color: aqua;
    }
</style>

如果当前组件或者Vue实例对象被销毁了,其所绑定的自定义事件也会被销毁

7. 自定义事件的回调

谁触发的这个自定义事件,自定义事件中的回调就是谁,methods中定义的函数中的this永远是当前组件的实例对象

App.vue

<template>
    <div id="app">
        <h1>这里是APP,学生姓名是 {{studentName}}</h1>
        <student-info ref="student"/>
    </div>
</template>

<script>
    import StudentInfo from "@/components/StudentInfo";

    export default {
        name: 'App',
        components: {
            StudentInfo
        },
        data() {
            return {
                studentName: ""
            }
        },
        mounted() {
            // 1. 正常使用回调,在getStudentName中赋值this.studentName(可以)
            // this.$refs.student.$on("getName", this.getStudentName)

            // 2.直接在这里赋值,这里的this是触发getName这个自定义事件的实例对象(不可以)
            // this.$refs.student.$on("getName", function (name) {
            //     console.log(this)
            //     this.studentName = name
            // })
            // 3. 修改上面的问题,将函数改为剪头函数,箭头函数是没有自己的this,会找外部的mouted()的this
            // 这个this就是app组件实例对象,即可赋值他的studentName
            this.$refs.student.$on("getName", name =>{
                console.log(this)
                this.studentName = name
            })
        },
        methods: {
            getStudentName(name) {
                console.log("App 组件的getStudentName被调用了", name)
                this.studentName = name
            },
        },
    }
</script>

<style>
    #app {
        font-family: Avenir, Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
    }

    h1 {
        background-color: red;
    }
</style>

8. 组件绑定原生 DOM 事件

方法一: 子组件内触发原生DOM事件

App.vue

<template>
    <div id="app">
        <h1>这里是APP</h1>
        <!-- 绑定原生click 原生Dom事件 -->
        <student-info @click="show"/>
    </div>
</template>

<script>
    import StudentInfo from "@/components/StudentInfo";

    export default {
        name: 'App',
        components: {
            StudentInfo
        },
        methods: {
            show(){
                alert(12345)
            }
        },
    }
</script>

<style>
    #app {
        font-family: Avenir, Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
    }

    h1 {
        background-color: red;
    }
</style>

Student.vue

<template>
    <div class="student">
        <h2>学生姓名: {{name}}</h2>
        <h2>学生年龄: {{age}}</h2>
        <button @click="sendStudentInfo">点击发送学生信息</button>
    </div>
</template>

<script>
    export default {
        name: "Student-info",
        data() {
            return {
                name: "小明",
                age: 18
            }
        },
        methods: {
            sendStudentInfo() {
                // 必须在子组件中触发这个原生DOM事件
                this.$emit("click")
            }
        }
    }
</script>

<style scoped>
    .student {
        background-color: aqua;
    }
</style>

方法二: 用修饰符指定组件绑定的事件为原生事件

App.vue

<template>
    <div id="app">
        <h1>这里是APP</h1>
        <!-- 使用native事件修饰符来表明当前事件为原生DOM事件,事件绑定在子组件的最外层div标签上 -->
        <student-info @click.native="show"/>
    </div>
</template>

<script>
    import StudentInfo from "@/components/StudentInfo";

    export default {
        name: 'App',
        components: {
            StudentInfo
        },
        methods: {
            show(){
                alert(12345)
            }
        },
    }
</script>

<style>
    #app {
        font-family: Avenir, Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
    }

    h1 {
        background-color: red;
    }
</style>

Student.vue 中不用再触发DOM事件

<template>
    <div class="student">
        <h2>学生姓名: {{name}}</h2>
        <h2>学生年龄: {{age}}</h2>
        <button @click="sendStudentInfo">点击发送学生信息</button>
    </div>
</template>

<script>
    export default {
        name: "Student-info",
        data() {
            return {
                name: "小明",
                age: 18
            }
        },
        methods: {
            sendStudentInfo() {
                console.log(this.name)
            }
        }
    }
</script>

<style scoped>
    .student {
        background-color: aqua;
    }
</style>

3. 全局事件总线(任意组件传递)

任意组件之间的通信,更适用于爷爷和孙子之间传递数据

1. 实现思路

**流程: **

在A组件中给X绑定一个自定义事件demo,并将回调函数定义在A组件中,B组件想给A组件传数据,B组件触发X上的自定义事件demo,并将数据传到X,X所对应的回调就要执行,就将数据以参数的形式传给A组件中定义的回调函数参数中

**思路: **

  1. 必须要求X被所有的组件看到: Vue的原型对象上

main.js

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false
Vue.prototype.x = {a:1,b:2}

new Vue({
  render: h => h(App),
}).$mount('#app')
  1. 必须能调用到$on $off $emit 函数: Vue的实例对象(vm)或VueComponent的实例对象(vc)
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
    render: h => h(App),
    beforeCreate() {
        Vue.prototype.$bus = this;   // 安装全局事件总线,$bus就是当前应用的vm
    }
}).$mount('#app')

2. 案例

main.js 安装全局事件总线

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
    render: h => h(App),
    beforeCreate() {
        Vue.prototype.$bus = this  // 安装全局事件总线,$bus就是当前应用的vm
    }
}).$mount('#app')

App.vue 接收数据方绑定自定义事件和回调函数

<template>
    <div id="app">
        <h1>这里是APP,接收到来自Student组件的学生姓名: {{studentName}}</h1>
        <student-info/>
    </div>
</template>

<script>
    import StudentInfo from "@/components/StudentInfo";

    export default {
        name: 'App',
        components: {
            StudentInfo
        },
        data() {
            return {
                studentName: ""
            }
        },
        mounted() {
            // 1. 给全局事件总线绑定 getName自定义事件,并指定回调函数,如果是直接写在绑定自定义事件的$on()里,必须用剪头函数 =>
            this.$bus.$on("getName", (name) => this.studentName = name)
        },
        beforeDestroy() {
            this.$bus.off("getName")  // 2. 记得解绑自定义事件,事件总线上的自定义事件越来越多
        }
    }
</script>

<style>
    h1 {
        background-color: red;
    }
</style>

Student.vue 发送数据方触发自定义事件并传递参数

<template>
    <div class="student">
        <h2>学生姓名: {{name}}</h2>
        <h2>学生年龄: {{age}}</h2>
        <button @click="sendStudentInfo">点击发送学生信息</button>
    </div>
</template>

<script>
    export default {
        name: "Student-info",
        data() {
            return {
                name: "小明",
                age: 18
            }
        },
        methods:{
            sendStudentInfo(){
                // 触发全局事件总线上绑定的getName,并执行对其绑定getName自定义事件的回调函数
                this.$bus.$emit("getName",this.name)
            }
        }
    }
</script>

<style scoped>
    .student {
        background-color: aqua;
    }
</style>

4. 消息订阅与发布

任意组件之间通信,推荐使用事件总线

1. 实现思路

  1. 需要数据的组件订阅消息(定义回调函数)
  2. 传递数据的组件发布消息(执行函数)

2. 下载三方库

npm i pubsub-js

3. 使用

App.vue

<template>
    <div id="app">
        <h1>这里是APP,接收到来自Student组件的学生姓名: {{studentName}}</h1>
        <student-info/>
    </div>
</template>

<script>
    import StudentInfo from "@/components/StudentInfo";
    // 1. 引入pubsubjs
    import pubsub from 'pubsub-js'

    export default {
        name: 'App',
        components: {
            StudentInfo
        },
        data() {
            return {
                studentName: ""
            }
        },
        // 2. 在mounted中订阅消息,消息名为:getName
        mounted() {
            // msgName为消息名,如果msgName不需要用到,可以用_占位,data是携带的数据,并将返回的消息ID设置在vm身上,将来要取消订阅
            // 2.1 这里的函数如果是自定义函数,this是undefind,要改成箭头函数
            this.pubID = pubsub.subscribe("getName", (msgName, data) => {
                this.studentName = data
            })
            // 2.2 或者将回调函数体写在methods中
            this.pubID = pubsub.subscribe("getName", this.getStudentName)
        },
        // 3. 在 beforeDestroy 中取消订阅
        beforeDestroy() {
            pubsub.unsubscribe(this.pubID)
        },
        methods:{
            // 如果msgName不需要用到,可以用_占位
            getStudentName(msgName, name){
                this.studentName = name
            }
        }
    }
</script>

<style>
    h1 {
        background-color: red;
    }
</style>

Student.vue

<template>
    <div class="student">
        <h2>学生姓名: {{name}}</h2>
        <h2>学生年龄: {{age}}</h2>
        <button @click="sendStudentInfo">点击发送学生信息</button>
    </div>
</template>

<script>
    // 1. 引入pubsubjs
    import pubsub from 'pubsub-js'

    export default {
        name: "Student-info",
        data() {
            return {
                name: "小明",
                age: 18
            }
        },
        methods:{
            // 2. 在点击事件的回调函数中发布消息
            sendStudentInfo(){
                pubsub.publish("getName",this.name)
            }
        }
    }
</script>

<style scoped>
    .student {
        background-color: aqua;
    }
</style>

5. slot 插槽

也是一种组件间通信的方式,只不过父组件向子组件传递的不是数据,而是html结构,而子组件向父组件传递的是数据,不能单独使用,必须搭配使用,不能向之前的那些一样值传递数据

1. 不使用插槽

App.vue

<template>
  <div id="app" class="container">
    <Category :title="'美食'" :listData="foods"/>
    <Category :title="'游戏'" :listData="games"/>
    <Category :title="'电影'" :listData="films"/>
  </div>
</template>

<script>
import Category from "@/components/Category.vue";

export default {
  name: 'App',
  components: {
    Category
  },
  data() {
    return {
      foods:["火锅","烧烤","小龙虾","牛排"],
      games:["红色警戒","穿越火线","劲舞团","超级玛丽"],
      films:["<<教父>>","<<拆弹专家>>","你好,李焕英","天下无贼"],
    }
  }
}
</script>

<style>
.container {
  display: flex;
  justify-content: space-around;
}
</style>

Category.vue

<template>
  <div class="category">
    <h3>{{ title }}分类</h3>
    <ul>
      <li v-for="(item,index) in listData" :key="index">{{item}}</li>
    </ul>
  </div>
</template>
<script>
export default {
  // eslint-disable-next-line vue/multi-word-component-names
  name: "Category",
  props:["listData","title"]
}
</script>
<style scoped>
.category {
  background-color: aqua;
  width: 200px;
  height: 300px;
}

h3 {
  text-align: center;
  background-color: orange;
}
</style>

2. 需求

只在其中一个或两个组件中展示一张图片

<template>
  <div class="category">
    <h3>{{ title }}分类</h3>
    <ul>
      <li v-for="(item,index) in listData" :key="index">{{ item }}</li>
    </ul>
    <img v-show="title === '美食'" src="https://q5.itc.cn/q_70/images03/20240313/135f344687dd4387a15fe35dc5a3bfae.jpeg" alt="">
  </div>
</template>
<script>
export default {
  // eslint-disable-next-line vue/multi-word-component-names
  name: "Category",
  props: ["listData", "title"]
}
</script>
<style scoped>
.category {
  background-color: aqua;
  width: 200px;
  height: 300px;
}

h3 {
  text-align: center;
  background-color: orange;
}

img {
  width: 100%;
}
</style>

假如将来不同的不见不同的需求,那么要判断的条件就越来越多,比较混乱

3. 默认插槽

App.vue 将想向子组件中放的标签,放在父组件的子组件标签体中,在App组件中完成解析后再塞入Category组件中,如果样式写在App组件中,就是解析完后带着样式塞进Category,如果样式写在Category中,就是解析完后,再Category中控制样式

<template>
  <div id="app" class="container">
    <Category :title="'美食'">
      <!-- 声明向这个子组件中放的标签 -->
      <ul>
        <li v-for="(item,index) in foods" :key="index">{{ item }}</li>
      </ul>
      <img src="https://q5.itc.cn/q_70/images03/20240313/135f344687dd4387a15fe35dc5a3bfae.jpeg" alt="">
    </Category>>
    <Category :title="'游戏'">
      <!-- 声明向这个子组件中放的标签 -->
      <ul>
        <li v-for="(item,index) in games" :key="index">{{ item }}</li>
      </ul>
    </Category>>
    <Category :title="'电影'">
      <!-- 声明向这个子组件中放的标签 -->
      <!-- 加controls属性才能有点击播放的按钮 -->
      <video controls src="https://ksv-video-picture.cdn.bcebos.com/f0dbb327492e5a72a2862105d86ca8b5c0b016da.jpg?x-bce-process=image%2Fquality%2Cq_80&quot"></video>
    </Category>
  </div>
</template>

<script>
import Category from "@/components/Category.vue";

export default {
  name: 'App',
  components: {
    Category
  },
  data() {
    return {
      foods:["火锅","烧烤","小龙虾","牛排"],
      games:["红色警戒","穿越火线","劲舞团","超级玛丽"],
      films:["<<教父>>","<<拆弹专家>>","你好,李焕英","天下无贼"],
    }
  }
}
</script>

<style>
.container {
  display: flex;
  justify-content: space-around;
}
</style>

Category.vue

<template>
  <div class="category">
    <h3>{{ title }}分类</h3>
 	<!-- 使用slot标签占位,vue渲染的时候会将父组件中在子组件标签体中定义好的标签放在这里,可以设置默认值 -->
    <slot>我是默认值,当父组件没有传递标签结构时,我会出现</slot>
  </div>
</template>
<script>
export default {
  name: "Category",
  props: ["title"]
}
</script>
<style scoped>
.category {
  background-color: aqua;
  width: 200px;
  height: 300px;
}

h3 {
  text-align: center;
  background-color: orange;
}
video{
  width: 100%;
}
img {
  width: 100%;
}
</style>

4. 具名插槽

具有名字的插槽,当Category组件中出现多个插槽,并且位置有序,就需要有名字了

App.vue

<template>
  <div id="app" class="container">
    <Category :title="'美食'">
      <!-- 声明这个标签放在 slotHeader 插槽的位置 -->
      <ul slot="slotHeader">
        <li v-for="(item,index) in foods" :key="index">{{ item }}</li>
      </ul>
      <!-- 声明这个标签放在 slotFooter 插槽的位置 -->
      <img slot="slotFooter" src="https://q5.itc.cn/q_70/images03/20240313/135f344687dd4387a15fe35dc5a3bfae.jpeg" alt="">
    </Category>>
    <Category :title="'游戏'">
      <!-- 声明这个标签放在 slotHeader 插槽的位置 -->
      <ul slot="slotHeader">
        <li v-for="(item,index) in games" :key="index">{{ item }}</li>
      </ul>
    </Category>>
    <Category :title="'电影'">
      <!-- 声明这个标签放在 slotFooter 插槽的位置 -->
      <video slot="slotFooter" controls src="https://ksv-video-picture.cdn.bcebos.com/f0dbb327492e5a72a2862105d86ca8b5c0b016da.jpg?x-bce-process=image%2Fquality%2Cq_80&quot"></video>
    </Category>
  </div>
</template>

<script>
import Category from "@/components/Category.vue";

export default {
  name: 'App',
  components: {
    Category
  },
  data() {
    return {
      foods:["火锅","烧烤","小龙虾","牛排"],
      games:["红色警戒","穿越火线","劲舞团","超级玛丽"],
      films:["<<教父>>","<<拆弹专家>>","你好,李焕英","天下无贼"],
    }
  }
}
</script>

<style>
.container {
  display: flex;
  justify-content: space-around;
}
video{
  width: 100%;
}
</style>

Category.vue

<template>
  <div class="category">
    <h3>{{ title }}分类</h3>
    <!-- 定义两个具名插槽 -->
    <slot name="slotHeader">我是默认值,当父组件没有传递标签结构时,我会出现</slot>
    <slot name="slotFooter">我是默认值,当父组件没有传递标签结构时,我会出现</slot>
  </div>
</template>
<script>
export default {
  name: "Category",
  props: ["title"]
}
</script>
<style scoped>
.category {
  background-color: aqua;
  width: 200px;
  height: 300px;
}

h3 {
  text-align: center;
  background-color: orange;
}
video{
  width: 100%;
}
img {
  width: 100%;
}
</style>

根据声明的标签,和插槽的位置匹配插入结构,没有插入结构的出现默认值

如果外层是用template标签包裹的,可以有第二种写法

<template>
  <div id="app" class="container">
    <Category :title="'美食'">
      <ul slot="slotHeader">
        <li v-for="(item,index) in foods" :key="index">{{ item }}</li>
      </ul>
      <img slot="slotFooter" src="https://q5.itc.cn/q_70/images03/20240313/135f344687dd4387a15fe35dc5a3bfae.jpeg"
           alt="">
    </Category>
    <Category :title="'游戏'">
      <ul slot="slotHeader">
        <li v-for="(item,index) in games" :key="index">{{ item }}</li>
      </ul>
    </Category>
    <Category :title="'电影'">
      <video slot="slotHeader" controls
             src="https://ksv-video-picture.cdn.bcebos.com/f0dbb327492e5a72a2862105d86ca8b5c0b016da.jpg?x-bce-process=image%2Fquality%2Cq_80&quot"></video>
      <!-- 只有 template 标签包裹的标签,可以使用 v-slot:插槽名 指定插槽 -->
      <template v-slot:slotFooter>
        <div class="foot">
          <a href="">推荐</a>
          <a href="">经典</a>
          <a href="">热门</a>
        </div>
          <h4> 欢迎观影! </h4>
      </template>
    </Category>
  </div>
</template>

<script>
import Category from "@/components/Category.vue";

export default {
  name: 'App',
  components: {
    Category
  },
  data() {
    return {
      foods: ["火锅", "烧烤", "小龙虾", "牛排"],
      games: ["红色警戒", "穿越火线", "劲舞团", "超级玛丽"],
      films: ["<<教父>>", "<<拆弹专家>>", "你好,李焕英", "天下无贼"],
    }
  }
}
</script>

<style>
.container {
  display: flex;
  justify-content: space-around;
}

video {
  width: 100%;
}
.foot{
  display: flex;
  justify-content: space-around;
}
</style>

5. 作用域插槽

需求: 页面中有三个游戏分类,数据也是一样的,但是要生成里面结构的时候,一个是无需列表,一个是有序列表,一个全是h4 标题

App.vue 使用Category组件,App组件中定义Category组件中的结构

<template>
  <div id="app" class="container">
    <Category :title="'游戏'">
        <!-- 1. 外侧必须包裹 template 标签,用scope属性起个名字 -->
      <template scope="name1">
        <ul>
          <!-- 取数据时,使用上面scope定义的名字.games.即name1.games -->
          <li v-for="(item,index) in name1.games" :key="index">{{ item }}</li>
        </ul>
      </template>
    </Category>
    <Category :title="'游戏'">
        <!-- 可以不起名字,使用解构赋值语法 -->
      <template scope="{games}">
        <ol>
          <li v-for="(item,index) in games" :key="index">{{ item }}</li>
        </ol>
      </template>
    </Category>
    <Category :title="'游戏'">
        <!-- 可以使用 slot-scope 属性,跟scope是一样的 -->
      <template slot-scope="name3">

        <h4 v-for="(item,index) in name3.games" :key="index">{{ item }}</h4>
      </template>
    </Category>

  </div>
</template>

<script>
import Category from "@/components/Category.vue";

export default {
  name: 'App',
  components: {
    Category
  },
}
</script>

<style>
.container {
  display: flex;
  justify-content: space-around;
}

video {
  width: 100%;
}

.foot {
  display: flex;
  justify-content: space-around;
}
</style>

Category.vue 将数据传给插槽的使用者App组件

<template>
  <div class="category">
    <h3>{{ title }}分类</h3>
    <!-- 将 Category 组件中的数据传递给插槽的使用者 App 组件,让其可以循环这个games,可以传多个,用名字.msg即可取到 -->
    <slot :games="games" msg="hello">我是默认值,当父组件没有传递标签结构时,我会出现</slot>
  </div>
</template>
<script>
export default {
  name: "Category",
  props: ["title"],
  data() {
    return {
      games: ["红色警戒", "穿越火线", "劲舞团", "超级玛丽"],
    }
  }
}
</script>
<style scoped>
.category {
  background-color: aqua;
  width: 200px;
  height: 300px;
}

h3 {
  text-align: center;
  background-color: orange;
}
video{
  width: 100%;
}
img {
  width: 100%;
}
</style>

6. Vuex

1. 是什么

专门在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写).也是一种组件间通信的方式,且适用于任何组件间通信

2. 什么时候使用Vuex

  1. 多个组件依赖于同一个状态(数据)
  2. 来自不同组件的行为需要变更同一状态(数据)

3. 对比

1. 全局事件总线

2. Vuex

4. 工作原理图

Actions --- 存储数据操作函数的一个对象,同时可以发送ajax请求到其他的后端服务器,得到一些数据然后将函数和数据commit给Mutations进行加工,如果不发送ajax请求,可以省略这一步,直接commit给Mutations

Mutations --- 存储着数据操作函数和存储数据的State的一个对象

State --- vuex用来存储数据的一个对象

Store --- 管理所有的vuex组件

Dispatch --- store提供的方法,用来提交数据操作函数和参数

Commit ---store提供的方法,用来将数据操作函数和参数,提交给Mutations

5. 环境搭建

1. 安装Vuex

# vue2必须指定vuex的3版本
npm install vuex@3

2. 引入Vuex

main.js

import Vue from 'vue'
import App from './App.vue'
// 1. 导入Vuex
import Vuex from 'vuex'

Vue.config.productionTip = false

// 2. 使用Vuex插件
Vue.use(Vuex)

new Vue({
  render: h => h(App),
}).$mount('#app')

3. 创建store

src目录下创建store/index.js

src/store/index.js

// 该文件用于创建Vuex中最为核心的store
// 1. 导入Vue
import Vue from 'vue'
// 2. 导入Vuex
import Vuex from 'vuex'
// 3. 应用Vuex插件
Vue.use(Vuex)

// 准备actions对象,用于响应组件中的动作
const actions = {}

// 准备mutations对象,用于操作数据
const mutations = {}

// 准备state对象,用于存储数据
const state = {}



// 创建并暴露store
export default  new Vuex.Store({
    // 配置项
    actions,
    mutations,
    state,
})

4. 配置store

main.js

import Vue from 'vue'
import App from './App.vue'

// 1. 引入store,默认引入index.js 可以省略
import store from './store'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  // 2. 配置store,Vux在store中引入及使用了
  store: store
}).$mount('#app')

6. 案例

src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const actions = {
    // 2. 定义操作sum的方法,context为上下文对象,其中保存着所有的数据及操作函数
    add: function (context, value) {
        // 2. 提交给mutations操作数据
        context.commit("ADD", value)
    },
    sub: function (context, value) {
        context.commit("SUB", value)
    },
    // 当sum值为奇数时再加
    addOdd:function (context,value){
      if (context.state.sum % 2){
          context.commit("ADD",value)
      }
    },
    // 在actions中调用ajax请求,这里使用定时器模拟
    addWait:function (context,value){
      setTimeout(()=>{
          context.commit('ADD',value)
      },500)
    }
}

// 一般mutations中的函数大写,用来区分和actions中的函数
// 只定义真实操作数据的逻辑,没有业务逻辑
const mutations = {
    ADD: function (state, value) {
        state.sum += value
    },
    SUB: function (state, value) {
        state.sum -= value
    }
}

const state = {
    // 1. 定义数据
    sum: 0
}

export default new Vuex.Store({
    actions,
    mutations,
    state,
})

App.vue

<template>
  <div id="app" class="container">
    
    <h3>求和结果为:{{ sum }}</h3>
    <h3>放大十倍结果为:{{ bigSum }}</h3>
    <div class="warpper">
      <Add/>
      <Sub/>
    </div>

  </div>
</template>

<script>
import Add from "@/components/Add.vue";
import Sub from "@/components/Sub.vue";

export default {
  name: 'App',
  components: {
    Add,
    Sub
  },
  computed: {
    sum() {
      // 1. 展示数据,数据是存在Vuex中的state中的,使用$store.state调用
      return this.$store.state.sum
    },
    bigSum() {
      return this.$store.getters.bigSum
    },
  }
}
</script>

<style>
.warpper {
  display: flex;
}
</style>

Add.vue

<template>
  <div class="category">
    <button @click="increment">+</button>
    <button @click="incrementWait">延时+</button>
    <button @click="incrementOdd">奇数+</button>
  </div>
</template>
<script>
export default {
  name: "Add",
  methods:{
    increment(){
      // 1. 提交sum的操作函数名
      // this.$store.dispatch('add',1)
      // 如果没有复杂的业务逻辑,很直接的操作数据,也可以直接调用mutations中定义的ADD
      this.$store.commit("ADD",1)
    },
    // 模拟网络请求后得到数据后再具体操作
    incrementWait(){
      this.$store.dispatch('addWait',1)
    },
    incrementOdd(){
      this.$store.dispatch("addOdd",1)
    },
  }
}
</script>
<style scoped>
button{
  margin-left: 30px;
}
</style>

Sub.vue

<template>
  <button @click="decrement">-</button>
</template>

<script>
export default {
  // eslint-disable-next-line vue/multi-word-component-names
  name: "Sub",
  methods:{
    decrement(){
      // 1. 提交sum的操作函数名 
      // this.$store.dispatch("sub",1)
      // 2. 如果没有复杂的业务逻辑,很直接的操作数据,也可以直接调用mutations中定义的SUB
      this.$store.commit("SUB",1)
    }
  }
}
</script>
<style scoped>
button{
  margin-left: 20px;
}
</style>

7. getters

src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const actions = {
    add: function (context, value) {
        context.commit("ADD", value)
    },
    sub: function (context, value) {
        context.commit("SUB", value)
    },
    addOdd:function (context,value){
      if (context.state.sum % 2){
          context.commit("ADD",value)
      }
    },
    addWait:function (context,value){
      setTimeout(()=>{
          context.commit('ADD',value)
      },500)
    }
}

const mutations = {
    ADD: function (state, value) {
        state.sum += value
    },
    SUB: function (state, value) {
        state.sum -= value
    }
}

const state = {
    sum: 0
}

// 选用:对数据的进一步加工
const getters = {
    // 用于将state中的数据加工
    bigSum(state){
        return state.sum * 10
    }
}

export default new Vuex.Store({
    actions,
    mutations,
    state,
    getters    // 配置getters
})

App.vue

<template>
  <div id="app" class="container">
    <h3>求和结果为:{{ sum }}</h3>
    <!-- 使用计算属性获得bigSum的值 -->
    <h3>放大十倍结果为:{{ bigSum }}</h3>
    <div class="warpper">
      <Add/>
      <Sub/>
    </div>

  </div>
</template>

<script>
import Add from "@/components/Add.vue";
import Sub from "@/components/Sub.vue";

export default {
  name: 'App',
  components: {
    Add,
    Sub
  },
  computed: {
    sum() {
      return this.$store.state.sum
    },
    // 取getters中对数据进一步加工后的值 
    bigSum() {
      return this.$store.getters.bigSum
    },
  }
}
</script>

<style>
.warpper {
  display: flex;
}
</style>

8. mapState

如果有多个state中定义的数据,使用mapState可以很简单的生成多个计算属性的函数

App.vue

<template>
  <div id="app" class="container">
    <!-- 1. 展示数据,数据是存在Vuex中的state中的,使用$store.state调用 -->
    <h3>求和结果为:{{ he }}</h3>
    <h3>放大十倍结果为:{{ bigSum }}</h3>
    <h3>学校:{{ xuexiao }}</h3>
    <h3>地址:{{ dizhi }}</h3>
    <div class="warpper">
      <Add/>
      <Sub/>
    </div>

  </div>
</template>

<script>
import Add from "@/components/Add.vue";
import Sub from "@/components/Sub.vue";
// 1. 导入mapState
import {mapState} from "vuex";
    
export default {
  name: 'App',
  components: {
    Add,
    Sub
  },
  computed: {
     // 原生写法
    sum() {
      return this.$store.state.sum
    },
    school(){
      return this.$store.state.school
    },
    address(){
      return this.$store.state.address
    },
    
    // 两种写法
    // 借助 mapState 生成多个计算属性,从state中获取数据
    // 对象写法,生成的计算属性的函数名和$state.state中定义的变量名根据对象中的key-value生成
    ...mapState({he:"sum",xuexiao:"school",dizhi:"address"}),
    
    // 借助 mapState 生成多个计算属性,从state中获取数据
    // 数组简写方法,生成的计算属性的函数名和变量名根据数组中的元素生成,函数名和$state.state中定义的变量名相同 
    ...mapState(["sum","school","address"]),
  }
}
</script>

<style>
.warpper {
  display: flex;
}
</style>

9. mapGetters

<template>
  <div id="app" class="container">
    <!-- 1. 展示数据,数据是存在Vuex中的state中的,使用$store.state调用 -->
    <h3>求和结果为:{{ he }}</h3>
    <h3>放大十倍结果为:{{ bigSum }}</h3>
    <h3>学校:{{ xuexiao }}</h3>
    <h3>地址:{{ dizhi }}</h3>
    <div class="warpper">
      <Add/>
      <Sub/>
    </div>

  </div>
</template>

<script>
import Add from "@/components/Add.vue";
import Sub from "@/components/Sub.vue";
// 1. 导入mapGetters
import {mapState,mapGetters} from "vuex";
    
export default {
  name: 'App',
  components: {
    Add,
    Sub
  },
  computed: {
    ...mapState(["sum","school","address"]),
      
	// 两种写法
    // 借助 mapState 生成多个计算属性,从getters中获取数据
    // 对象写法,生成的计算属性的函数名和$state.getters中定义的变量名根据对象中的key-value生成
    ...mapGetters({bigSum:"bigSum"})
      
    // 借助 mapState 生成多个计算属性,从getters中获取数据
    // 数组简写方法,生成的计算属性的函数名和变量名根据数组中的元素生成,函数名和$state.getters中定义的变量名相同 
    ...mapGetters(["bigSum"])
  }
}
</script>

<style>
.warpper {
  display: flex;
}
</style>

10. mapActions

Add.vue

<template>
  <div class="category">
    <button @click="increment(1)">+</button>
    <!-- 2. 手动将想要Add的value参数传到incrementWait中 -->
    <button @click="incrementWait(1)">延时+</button>
    <button @click="incrementOdd(1)">奇数+</button>
  </div>
</template>
<script>
// 1. 导入mapActions
import {mapActions,mapMutations} from "vuex";

export default {
  name: "Add",
  methods:{
    // mapMutations的原生写法
    increment(){
      this.$store.commit("ADD",1)
    },
      
    ...mapMutations({increment:"increment"})
    
    ...mapMutations(["increment"]) 
    
    // mapActions的原生写法
    incrementWait(){
      this.$store.dispatch('addWait',1)
    },
    incrementOdd(){
      this.$store.dispatch("addOdd",1)
    }
    
    // 两种写法
    // 3. 借助 mapState 生成多个方式,调用$store.dispatch(),对象写法
    ...mapActions({incrementWait:"incrementWait",incrementOdd:"incrementOdd"})
    
    // 3. 借助 mapState 生成多个方式,调用$store.dispatch(),如果事件回调函数和actions中定义的方法名相同,数组写法
    ...mapActions(["increment",incrementOdd]) 
  }
}
</script>
<style scoped>
button{
  margin-left: 30px;
}
</style>

11. mapMutations

Add.vue

<template>
  <div class="category">
    <!-- 2. 手动将想要Add的value参数传到increment中 -->
    <button @click="increment(1)">+</button>
    <button @click="incrementWait">延时+</button>
    <button @click="incrementOdd">奇数+</button>
  </div>
</template>
<script>
// 1. 导入mapMutations
import {mapMutations} from "vuex";

export default {
  name: "Add",
  methods:{
    increment(){
      this.$store.commit("ADD",1)
    },
    // 两种写法
    // 3. 借助 mapState 生成多个方式,调用$store.commit(),对象写法
    ...mapMutations({increment:"increment"})
    
    // 3. 借助 mapState 生成多个方式,调用$store.commit(),如果事件回调函数和mutations中定义的方法名相同,数组写法
    ...mapMutations(["increment"]) 
  }
}
</script>
<style scoped>
button{
  margin-left: 30px;
}
</style>

或者手动调用函数传参数

<template>
  <div class="category">
    <button @click="increment">+</button>
    <button @click="incrementWait">延时+</button>
    <button @click="incrementOdd">奇数+</button>
  </div>
</template>
<script>
export default {
  name: "Add",
  methods:{
    // 1. 手动调用函数传递参数
    increment(){
      this.incrementFunc(1)
    },
    // 借助 mapState 生成多个方式,调用$store.commit()
    ...mapMutations({incrementFunc:"increment"})
      
    incrementWait(){
      this.$store.dispatch('addWait',1)
    },
    incrementOdd(){
      this.$store.dispatch("addOdd",1)
    }
  }
}
</script>
<style scoped>
button{
  margin-left: 30px;
}
</style>

12. 模块化拆分

src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

// 求和相关的配置
const countOptions = {
    namespaced:true,  // 加命名空间
    actions: {
        add: function (context, value) {
            context.commit("ADD", value)
        },
        sub: function (context, value) {
            context.commit("SUB", value)
        },
        addOdd: function (context, value) {
            if (context.state.sum % 2) {
                context.commit("ADD", value)
            }
        },
        addWait: function (context, value) {
            setTimeout(() => {
                context.commit('ADD', value)
            }, 500)
        }
    },
    mutations: {
        ADD: function (state, value) {
            state.sum += value
        },
        SUB: function (state, value) {
            state.sum -= value
        }
    },
    state: {
        sum: 0,
        school: "一中",
        address: "北京"
    },
    getters: {
        bigSum(state) {
            return state.sum * 10
        }
    }
}


// 人员相关的配置
const personOptions = {
    namespaced:true,    // 加命名空间
    actions: {
        add: function (context, value) {
            context.commit("ADD", value)
        },
        sub: function (context, value) {
            context.commit("SUB", value)
        },
        addOdd: function (context, value) {
            if (context.state.sum % 2) {
                context.commit("ADD", value)
            }
        },
        addWait: function (context, value) {
            setTimeout(() => {
                context.commit('ADD', value)
            }, 500)
        }
    },
    mutations: {
        ADD: function (state, value) {
            state.sum += value
        },
        SUB: function (state, value) {
            state.sum -= value
        }
    },
    state: {
        sum: 0,
        school: "一中",
        address: "北京"
    },
    getters: {
        bigSum(state) {
            return state.sum * 10
        }
    }
}

export default new Vuex.Store({
    modules:{
        countOptions,   // 求和相关的配置
        personOptions   // 人员相关的配置
    }
})

调用

// 原生调用
this.$store.state.countOptions.sum       	// 读取countOptions中的state数据
this.$store.getters["countOptions/bigSum"]  // 读取countOptions中的getters数据
this.$store.dispath("countOptions/incrementWait",1)
this.$store.commit("countOptions/incrementWait",1)

// 借助map
...mapState("countOptions",["sum","school","address"]),  // 读取 countOptions中state中的sum school address
...mapState("personOptions",["personList"]),  			// 读取 personOptions

...mapGetters("countOptions",["bigSum"]),  				// 读取 countOptions中getters中的bigSum
...mapGetters("personOptions",["personName"]),  		// personOptions的
    
...mapActions("countOptions",["incrementWait","incrementOdd"]),  // 调用 countOptions的actions中的incrementWait
...mapActions("personOptions",["personNameAdd"]),  			    // 调用 personOptions的

...mapMutations("countOptions",["increment"]),  			// 调用 countOptions的mutations中的incrementWait
...mapMutations("personOptions",["personList"]),  			// 调用 personOptions的

13. 文件拆分

src/store/count.js

// 求和相关的配置
export default {
    actions: {
        // 2. 定义操作sum的方法,context为上下文对象,其中保存着所有的数据及操作函数
        add: function (context, value) {
            // 2. 提交给mutations操作数据
            context.commit("ADD", value)
        },
        sub: function (context, value) {
            context.commit("SUB", value)
        },
        // 当sum值为奇数时再加
        addOdd: function (context, value) {
            if (context.state.sum % 2) {
                context.commit("ADD", value)
            }
        },
        // 在actions中调用ajax请求,这里使用定时器模拟
        addWait: function (context, value) {
            setTimeout(() => {
                context.commit('ADD', value)
            }, 500)
        }
    },
    // 一般mutations中的函数大写,用来区分和actions中的函数
    mutations: {
        // 3. 定义真正操作sum的方法
        ADD: function (state, value) {
            state.sum += value
        },
        SUB: function (state, value) {
            state.sum -= value
        }
    },
    state: {
        // 1. 定义数据
        sum: 0,
        school: "一中",
        address: "北京"
    },
    getters: {
        bigSum(state) {
            return state.sum * 10
        }
    }
}

src/store/person.js

// 人员相关的配置
export default {
    actions: {
        // 2. 定义操作sum的方法,context为上下文对象,其中保存着所有的数据及操作函数
        add: function (context, value) {
            // 2. 提交给mutations操作数据
            context.commit("ADD", value)
        },
        sub: function (context, value) {
            context.commit("SUB", value)
        },
        // 当sum值为奇数时再加
        addOdd: function (context, value) {
            if (context.state.sum % 2) {
                context.commit("ADD", value)
            }
        },
        // 在actions中调用ajax请求,这里使用定时器模拟
        addWait: function (context, value) {
            setTimeout(() => {
                context.commit('ADD', value)
            }, 500)
        }
    },
    // 一般mutations中的函数大写,用来区分和actions中的函数
    mutations: {
        // 3. 定义真正操作sum的方法
        ADD: function (state, value) {
            state.sum += value
        },
        SUB: function (state, value) {
            state.sum -= value
        }
    },
    state: {
        // 1. 定义数据
        sum: 0,
        school: "一中",
        address: "北京"
    },
    getters: {
        bigSum(state) {
            return state.sum * 10
        }
    }
}

src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

import countOptions from "@/store/count";
import personOptions from "@/store/person";

export default new Vuex.Store({
    modules:{
        countOptions,   // 求和相关的配置
        personOptions   // 人员相关的配置
    }
})
posted @ 2024-03-19 11:02  河图s  阅读(13)  评论(0)    收藏  举报