A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

先介绍一下什么是组件把:
创建组件的两种方式:

全局组件
// 组件就是vue的一个拓展实例
let component=Vue.extend({
     data(){
       return{
         //与vue实例中的data不同,子组件的data必须是一个方法,必须有返回值,且返回值是对象。
         //这样做可以使组件中的数据独立。
         //需要共享数据时可把return的对象声明全局变量
       }
     }
     template:'<div></div>'// 模板  这个组件的html元素
        // })
    //注册组件
Vue.component('hehe',component)

(简写)

Vue.component('组件名',{
                template:''// 模板  
        })

局部组件
new Vue({
                el:'#app',
                data:{
                        test:11
                },
                components:{
                        one:{
                            template:'<h1>这里是局部组件 </h1>'
                    }
                }

        })

需要注意的是子组件的命名无法识别驼峰命名法,当组件做为标签使用的时候需要用“-”和小写字母替换该大写字母。

<div id="app">
   <one-data></one-data>
</div>
new Vue({
                el:'#app',
                data:{
                        test:11
                },
                components:{
                        oneData:{
                            template:'<h1>这里是局部组件 </h1>'
                    }
                }

        })

1、父子属性传值
子组件不能直接使用父级data的内容,也不能直接修改父组件传递的值,但可以通过设置props属性来获得自身属性值。
原理:

通过子组件的props抛出一个组件标签属性
父组件通过该标签属性将参数传递给子组件
子组件内部通过该标签属性获取值
代码实现:(实现过程中踩过的坑)
1.template标签内必须有且只有一个根元素
2.子组件标签内要通过v-bind的方式绑定父级的data的值
3.子组件通过props定义的自身属性值(test)获取父组件的data中的内容(父组件:{{name}} ;子组件:{{test}})

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="base/vue.js"></script>
</head>
<body>
   <div id="myApp">
       {{name}}
       <son :test='name'></son>
   </div>
   <!--实现功能较复杂时,会单独写个template标签,通过id与子组件中的template相关联,否则字符串拼接会很麻烦-->
   <template id="tp">
       <div>{{test}}</div>
   </template>
</body>
<script>
    new Vue({
        el:'#myApp',
        data:{
            name:'加油鸭!'
        },
        components:{
            son:{
                template:'#tp',//类似于 el
                props:['test']
            }
        }
    })
</script>
</html>

再用全局组件写一个父子属性传值,实现点击父组件按钮,子组件div显示和隐藏
(全局组件无固定父子关系,该组件标签放在哪个标签内就是哪个标签的子组件)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>父向子传值</title>
    <script src="base/vue.js"></script>
</head>
<body>
   <div id="myApp">
   <!--c1就是myApp的子组件-->
       <c1></c1>
   </div>
   <template id="tp1">
       <div>
           <h4>我是c1,是父级</h4>
           <button @click="change">点我c2变</button>
           {{state}}
           <hr>
           <!--写在c1的template内,就是c1的子组件-->
           <c2 :changed='state'></c2>
       </div>
   </template>
   <template id="tp2">
       <div>
           我是c2,是子级
           <div v-show='changed'><h1>我是div</h1></div>
        </div>
   </template>
</body>
<script>
    Vue.component("c1",{
        template:'#tp1',
        data(){
            return {
                state:true
            }
        },
        methods:{
            change(){
                this.state=!this.state
            }
        }
    })
    Vue.component("c2",{
        template:'#tp2',
        props:['changed']
    })
    let vm=new Vue({
        el:'#myApp'
    })
</script>
</html>

2、子父事件传值
子组件不能直接修改父组件传递的值,可间接通过事件修改

原理

子组件创建自定义事件
自定义事件的处理函数是父组件的函数
子组件通过$emit触发属于自己的自定义事件
代码实现:(点击子组件按钮,父组件div显示和隐藏)
代码中有一些思路注释:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>子向父传值</title>
    <script src="base/vue.js"></script>
</head>
<body>
   <div id="myApp">
       <c1></c1>
   </div>
   <template id="tp1">
       <div>
           <h4>我是c1,是父级</h4>
           <div v-show='state'><h1>我是div</h1></div>           
           <hr>
           <!--c2自定义了一个changed事件处理父组件的函数change-->
           <c2 @changed='change'></c2>
       </div>
   </template>
   <template id="tp2">
       <div>
           我是c2,是子级
           <!--子组件点击按钮,父组件显示隐藏,当然有点击事件啦,
           所以给button加点击事件触发一个函数,发现c2没有设置methods,
           所以去c2的实例中加一个methods并设置函数sclick,
           再通过sclick函数把其自定义事件changed抛出,
           changed里面就是c1的处理函数-->
           <button @click="sclick">点我c1变</button>
        </div>
   </template>
</body>
<script>
    Vue.component("c1",{
        template:'#tp1',
        data(){
            return {
                state:true
            }
        },
        methods:{
            change(){
                this.state=!this.state
            }      
        }

    })
    Vue.component("c2",{
        template:'#tp2',
        methods:{
            sclick(){
                this.$emit('changed')
            }
        }
    })
    let vm=new Vue({
        el:'#myApp'
    })
</script>
</html>

3、非父子传值

原理

创建公共汽车实例
实例上注册事件,事件的处理函数就是要改变数据的处理函数
触发事件
(1)公有的父元素作为桥接-----结合父子属性传值和子父事件传值来实现(适用于亲兄弟)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>兄弟传值</title>
    <script src="base/vue.js"></script>
</head>
<body>
   <div id="myApp">
       我是父级
       {{state}}
       <hr>
       <!--c1和c2都是div的子组件-->
       <!--子父事件传值-->
       <c1 @emit='change'></c1>
       <hr>
       <!--父子属性传值-->
       <c2 :test='state'></c2>
   </div>
   <template id="tp1">
       <div>
           我是c1,是c2的兄弟
           <!--间接通过父级改变了兄弟级的值-->
           <button @click='emit'>点我我的兄弟和父级都会发生改变</button>
        </div>
   </template>
   <template id="tp2">
       <div>我是c2,是c1的兄弟
           <p>{{test}}</p>
       </div>
   </template>
</body>
<script>
    Vue.component('c1',{
        template:'#tp1',
        methods:{
            emit(){
                this.$emit('emit')
            }
        }
    })
    Vue.component('c2',{
        template:'#tp2',
        props:["test"]
    })
    new Vue({
        el:'#myApp',
        data:{
            state:false
        },
       methods:{
           change(){
               this.state=!this.state
           }
       }
    })
</script>
</html>

(2)公共汽车传值(声明一个公共实例作为过滤)

修改谁的值就去触发谁的方法


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>兄弟之间通过公共汽车传值</title>
    <script src="base/vue.js"></script>
</head>
<body>
    <div id="myApp">
        <father></father>
    </div>
    <template id="father">
        <div>我是父组件
            <hr>
            <son1></son1>
            <hr>
            <son2></son2>
        </div>

    </template>
    <template id="son1">
        <div>{{name}}
        <!--点击事件触发一下sclick函数,相当于把抛出的change函数触发了-->
            <button @click='sclick'>点我</button>
        </div>
    </template>
    <template id="son2">
        <div>{{name}}</div>
    </template>
</body>
<script>
    let Bus=new Vue();
    new Vue({
        el:'#myApp',
        components:{
            father:{
                template:'#father',
                components:{
                    son1:{
                        template:'#son1',
                        data(){
                            return{
                                name:'我是子组件1'
                            }
                        },
                        methods:{
                            sclick(){
                            //触发test事件,再传一个参数給change函数中的val
                            //name值就从“我是子组件2”通过点击事件变成了"加油鸭!!!!"
                                Bus.$emit('test',"加油鸭!!!!")
                            }
                        }
                    },
                    son2:{
                        template:'#son2',
                        data(){
                            return{
                                name:'我是子组件2'
                            }
                        },
                        methods:{
                            change(val){
                                this.name=val
                            }
                        },
                        mounted(){
                        //只要mounted执行了,说明组件已经准备就绪
                        //注册一个函数,函数名叫test,处理函数就是methods中的change函数
                        //简单来说,只要想办法触发test事件就相当于执行了change函数
                            Bus.$on('test',this.change)
                        }
                    }
                }
            }
        }
    })
</script>
</html>

(3)vuex(集中式的数据状态管理器)
简单来说,就是用来管理全局变量的,不管哪里进行修改,页面都会随之变化。比如我们手机端的选择所在区域,假设你选了北京,跳到其他页面还是显示的北京就可以用vuex来实现。
解决的问题:
帮助我们管理共享状态。
vuex的相关配置步骤:

npm install vuex --save-dev
引入:`import Vuex from ‘vuex’
使用:Vue.use(Vuex)
设置vuex
var store=new Vuex.Store({
       state:{
       },
        mutations:{
        },
        actions:{
        },
        getters:{
        }
})

将数据放入到实例中
new Vue({
  el: '#app',
  router,
  store,
  components: { App},
  template: '<App/>',
})

vuex中的模块:

state:存储状态,可通过this.$store.state获取我们在vuex中声明的全局变量的值
import Vuex from 'vuex'
Vue.use(Vuex)
let store=new Vuex.Store({
    state:{
        name:1,
    }
})
export default store;
//一般我们会新建一个store文件夹,
//在该文件夹的js文件中通过暴露出store,
//再通过在main.js中引入该文件的方式来使用vuex

在组件中我们只需要通过{{this.$store.state.name}}就可以渲染name的值
写一个组件:

<template>
    <div id="list">
        {{msg}}
        {{this.$store.state.name}}
        <hr>
    </div>
</template>

<script>
export default {
    name:'List',
    data(){
        return {
            msg:'list',
        }
    }
}
</script>
<style>
#list div{
    width: 50px;
    height: 50px;
    background: red;
}
</style>


有木有觉得{{this.$store.state.name}}太长了,可不可以直接写{{name}}:

<template>
    <div id="list">
        {{msg}}
        {{name}}
        <hr>
    </div>
</template>

<script>
export default {
    name:'List',
    data(){
        return {
            msg:'list',
        }
    },
    computed:{
        name(){
            return this.$store.state.name
        }
    }
}
</script>
<style>
#list div{
    width: 50px;
    height: 50px;
    background: red;
}
</style>


有小伙伴会说了,这样也不简单呀,那这样呢?

<template>
    <div id="list">
        {{msg}}
        {{name}}
        <hr>
    </div>
</template>

<script>
import {mapState} from 'vuex'
export default {
    name:'List',
    data(){
        return {
            msg:'list',
        }
    },
    //可不可以不用拓展操作符?可以呀
    //computed:mapState(['name'])
    //可是这样你如果还有其他的方法要在computed中操作就没法操作了
    computed:{
        ...mapState(['name'])
    }
}
</script>
<style>
#list div{
    width: 50px;
    height: 50px;
    background: red;
}
</style>

如果遇到需要改名的情况,可通过对象的形式:

{{_name}} //改后的名字进行页面渲染
...mapState({_name:"name"})
1
2
后面的方法基本思路一样,接下来就都用简写形式啦!
2. mutations:修改状态

import Vuex from 'vuex'
Vue.use(Vuex)
let store=new Vuex.Store({
    state:{
        name:1,
    },
     mutations:{
        CHANGE_NUM(state,name){
            state.name=name
        }
    },
})
export default store;

我们来做一个小效果,点击按钮改变状态值从1变为888

<template>
    <div id="list">
         <div @click='CHANGE_NUM(888)'>change</div>
        {{msg}}
        {{_name}}
        <hr>
    </div>
</template>

<script>
import {mapState,mapMutations} from 'vuex'
export default {
    name:'List',
    data(){
        return {
            msg:'list',
        }
    },
    methods:{
          //非简写形式,点击事件的调用方法换为getName
         //getName(){
         //    this.$store.commit('CHANGE_NUM',888)
         //     }
        ...mapMutations(['CHANGE_NUM']),
    },
    computed:{
        ...mapState({_name:'name'})
    }
}
</script>
<style>
#list div{
    width: 50px;
    height: 50px;
    background: red;
}
</style>




3. actions:专门用来做异步操作
通过计时器模拟一个异步操作,并通过commit触发mutations

import Vuex from 'vuex'
Vue.use(Vuex)
let store=new Vuex.Store({
    state:{
        name:1,
    },
     mutations:{
        CHANGE_NUM(state,name){
            state.name=name
        }
    },
     actions:{
       //逻辑处理及异步请求
       getNumApi({commit},params){
           setTimeout(()=>{
               commit('CHANGE_NUM',params)
           },2000)
       }
    }
})
export default store;

再来实现一个点击按钮改变值的效果:

<template>
    <div id="list">
         <div @click='getNumApi("xixi")'>change</div>
        {{msg}}
        {{_name}}
        <hr>
    </div>
</template>

<script>
import {mapState,mapActions} from 'vuex'
export default {
    name:'List',
    data(){
        return {
            msg:'list',
        }
    },
    methods:{
        //非简写的写法:(通过dispatch触发相应的action)
         //emitActions(params){
        //     this.$store.dispatch('getNumApi',params)
        // }
        ...mapActions(['getNumApi'])
    },
    computed:{
        ...mapState({_name:'name'})
    }
}
</script>
<style>
#list div{
    width: 50px;
    height: 50px;
    background: red;
}
</style>

2秒后name值改变了

4. getters:类似于计算属性computed,里面进行一些计算操作

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
let store=new Vuex.Store({
    state:{
        name:1
    },
    mutations:{
        CHANGE_NUM(state,name){
            state.name=name
        }
    },
    getters:{
        double(state){
            return state.name*2
        }
    },
    actions:{
       //逻辑处理及异步请求
       getNumApi({commit},params){
           setTimeout(()=>{
               commit('CHANGE_NUM',params)
           },2000)
       }
    }
})
export default store;

再来做点击事件:

<template>
    <div id="list">
         <div @click='getNumApi("xixi")'>change</div>
        {{msg}}
        {{_name}}
        {{double}}
        <hr>
    </div>
</template>

<script>
import {mapState,mapMutations,mapActions,mapGetters} from 'vuex'
export default {
    name:'List',
    data(){
        return {
            msg:'list',
        }
    },
    methods:{
        ...mapActions(['getNumApi'])
    },
    computed:{
        ...mapState({_name:'name'}),
         ...mapGetters(['double'])
    }
}
</script>
<style>
#list div{
    width: 50px;
    height: 50px;
    background: red;
}
</style>


由于字符串不能进行乘法操作,所以打印结果是NaN

5. modules:模块
在实际开发中,我们会把以上四个模块,分别写在四个js文件中,在通过引入、暴露的形式写在一个index.js文件下。

import actions from './actions'
import state from './state'
import mutations from './mutations'
import getters from './getters'

export default {
    state,
    mutations,
    getters,
    actions
}

再把这5个文件放在store文件夹下的shopcar文件夹下(名字随意),在再store文件夹下,新建一个index.js文件,并通过modules的形式展现:

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
import shopcar from './shopcar'
let store=new Vuex.Store({
    modules:{
        shopcar:shopcar
    }
})
export default store


---------------------
作者:谢小丹
来源:CSDN
原文:https://blog.csdn.net/weixin_43747906/article/details/84981199
版权声明:本文为博主原创文章,转载请附上博文链接!

1 个回复

倒序浏览
奈斯
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马