• 简述在Vue脚手架中,组件以及父子组件(非父子组件)之间的传值


    1、组件的定义

    • 组成:
      • template:包裹HTML模板片段(反映了数据与最终呈现给用户视图之间的映射关系)
        • 只支持单个template标签;
        • 支持lang配置多种模板语法;
      • script:配置Vue和载入其他组件或者依赖库
        • 只支持单个script标签;
        • 支持通过import方式载入其他.vue后缀的组件文件;
      • style:设置样式
        • 支持多个style标签;
        • 支持scoped属性,css只应用于当前组件的元素中;
        • 支持lang配置多种预编译语法;
    • 局部组件:这里是一个三级地址组件
      • 首先看一下目录:在./src/components/下面新建立一个文件夹,此时命名为chinaAddress,在里面建立入口文件index.js,将你写好的组件通过import导入

                         

      • 在index.js里面进行全局注册组件;install是一个默认的方法
        import chinaAddress from './src/main';
        
        /* istanbul ignore next */
        chinaAddress.install = function(Vue) {
            Vue.component(chinaAddress.name, chinaAddress);
        };
        
        export default chinaAddress;
        View Code

        src里面的main.vue代码如下:

        <template>
            <div class="area">
                <el-select v-model="provinceCode" :placeholder="$t('message.common.province')" class="select" clearable @clear="clear">
                    <el-option v-for="item in provinceList" :key="item.value" :label="item.name" :value="item.value">
                    </el-option>
                </el-select>
                <div>&nbsp;{{$t('message.common.provinceOrDirectly')}}&nbsp;</div>
                <el-select v-model="cityCode" :placeholder="$t('message.common.city')" class="select">
                    <el-option v-for="item in cityList" :key="item.value" :label="item.name" :value="item.value">
                    </el-option>
                </el-select>
                <div>&nbsp;{{$t('message.common.city')}}&nbsp;</div>
                <el-select v-model="countryCode" :placeholder="$t('message.common.country')" class="select">
                    <el-option v-for="item in countryList" :key="item.value" :label="item.name" :value="item.value">
                    </el-option>
                </el-select>
                <div>&nbsp;{{$t('message.common.districtOrCountry')}}&nbsp;</div>
            </div>
        </template>
        <script>
        import chinaAddressData from './chinaAddressData'
        export default {
            name: 'chinaAddress',
            data() {
                return {
                    provinceCode: '', //省code
                    cityCode: '', //市code
                    countryCode: '', //县code
                    provinceList: [], //省表数据
                    cityList: [], //具体省下面市列表数据
                    countryList: [], //具体市下面县列表数据
                    addressCode: [], //组件内部省市县code数组
                }
            },
            props: {
                value: {
                    type: Array,
                    default () {
                        return []
                    }
                },
            },
            computed: {
        
            },
            components: {
        
            },
            watch: {
                value(newVal, oldVal) {
                    if (newVal.length) {
                        const [provinceCode, cityCode, countryCode] = newVal
                        this.provinceCode = provinceCode ? provinceCode : ''
                        this.cityCode = cityCode ? cityCode : ''
                        this.countryCode = countryCode ? countryCode : ''
                    }
                },
                provinceCode(newVal, oldVal) {
                    if (newVal) {
                        this.cityList = chinaAddressData.filter(val => val.parent === newVal)
                        this.cityCode = this.cityList[0].value
                    }
                    this.emitValueChange()
                },
                cityCode(newVal, oldVal) {
                    if (newVal) {
                        this.countryList = chinaAddressData.filter(val => val.parent === newVal)
                        this.countryCode = this.countryList[0].value
        
                    }
                    this.emitValueChange()
                },
                countryCode(newVal, oldVal) {
                    this.emitValueChange()
                }
            },
            mounted() {
        
            },
            created() {
                this.provinceList = chinaAddressData.filter(val => !val.parent)
            },
            methods: {
                emitValueChange() {
                    this.$nextTick(() => {
                        this.addressCode = [].concat(this.provinceCode, this.cityCode, this.countryCode).filter(val => val)
                        this.$emit('input', this.addressCode)
                        const addressInfo = {
                            provinceCode: '',
                            provinceName: '',
                            cityCode: '',
                            cityName: '',
                            countryCode: '',
                            countryName: '',
                        }
                        if (this.addressCode.length) {
                            const [provinceCode, cityCode, countryCode] = this.addressCode
                            addressInfo.provinceCode = provinceCode
                            addressInfo.cityCode = cityCode
                            addressInfo.countryCode = countryCode
                            addressInfo.provinceName = chinaAddressData.find(val => val.value === provinceCode)['name']
                            addressInfo.cityName = chinaAddressData.find(val => val.value === cityCode)['name']
                            addressInfo.countryName = chinaAddressData.find(val => val.value === countryCode)['name']
                        }
                        this.$emit('change', addressInfo)
                    })
                },
                clear() {
                    this.provinceCode = ''
                    this.cityCode = ''
                    this.countryCode = ''
                    this.emitValueChange()
                }
            },
        
        }
        </script>
        <style lang="scss" scoped>
        .area {
            width: 100%;
            display: flex;
           .select {
               width: 26%;
           }
        }
        </style>
        View Code

        js文件为:

      • 在main.js里面进行全局注册:(没有这一步,这是全局组件)
        import   chinaAddress from 'url';
        Vue.use(chinaAddress)
      • 在组件里面使用:
        <china-address v-model="addressCodeList" @change="addressChange"></china-address>
      • 局部组件的步骤如下:
        • 定义:
          var com = {
          //局部组件的定义,当名字为驼峰式命名myCom   写成<my-com></my-com>
             template: "<div>局部组件com{{msg}}</div>",
             data() {//每个组件的实例都有独立的数据
               return {
                  msg: "hello",
                  n: this.v
               };
             },
              methods: {}
          };
        • 注册:(在父组件中注册)
          components: {
              Header,
              com
           },
    • 全局组件:
      • 定义:
        Vue.component("组件的名字",{
            template:"模板的内容"(换行时用模板字符串``)//配置项
        })

    2、数据传递方式(组件通信)

    • props传值($parent以及$children)这两种方式只能用于父子组件之间的通信
      • 父子组件之间的传值   
        • 父组件  -----> 子组件(参考资料https://www.cnblogs.com/Sky-Ice/p/9267192.html
          • 子组件不能修改父组件传递过来的值,因为数据时单向的(参考资料https://www.cnblogs.com/Sky-Ice/p/10456533.html
            在 Vue 中,父子组件的关系可以总结为 prop 向下传递,事件向上传递。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息。
            Prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是反过来不会。这是为了防止子组件无意间修改了父组件的状态,来避免应用的数据流变得
            难以理解。 解决办法:  原理:将要更改的值,传递给父组件,在父组件中更改,再传递给子组件 步骤:  先将值传递给子组件,子组件 props 接收并使用,然后通过 $emit 广播一个事件给父组件,并将值一并传递,父组件 @子组件广播过来的事件,
            并定义一个方法,在该方法中,改变传递过来的值,父组件又会将值传递给子组件,这样就形成了一个闭环,问题得以解决
          • 传值:
            • 在父组件上用v-bind绑定要传递的属性,若没有v-bind,则属性的值始终是 字符串     v-bind:v="msg"
              <template>
                <div id="example" style="margin-bottom:200px">
                  我是首页
                  <hr />这里是分界线
                  <Header v-bind:v="msg"></Header>
                </div>
              </template>
              <script>
              import Header from "./header";
              export default {
                name: "example",
                data() {
                  return {
                    msg: "我是你爸爸"
                  };
                },
                components: {
                  Header,//子组件
                },
                methods: {},
                mounted() {}
              };
              </script>
              View Code
            • 在子组件上用props属性进行接收:
              • 对象方式:(验证)
                props:{
                    v : {
                      type : String,//null表示可以为任意类型,Number为数值型,Object为一个对象,
                      default : 'hello',//不传之时指定默认值
                      required : true,//不传值则产生错误
                twoWay : true,//如果绑定类型不对将发出警告 } },
              • 数组方式:props : ["v"]
                <template>
                  <div id="header">
                    <p>我是父组件传递过来的数据:{{v}}</p>
                  </div>
                </template>
                <script>
                export default {
                  props:["v"],
                  name: "thesisHeader",
                  data() {
                    return {
                      search: "", //搜索框内容
                    };
                  },
                  methods: {},
                };
                View Code
          • 传方法:
            • 在使用子组件时,根据传值的方式将方法进行绑定,:run="run";
            • 在子组件中,依旧使用props进行接收,this.run()运行;  
            • 子组件还可以用这种方式调用父组件的   this.$parent.方法/属性
          • 传父组件本身:
            • 在父组件中,使用子组件时,动态绑定this,即:home="this";
            • 子组件接收props:["home"];
            • 子组件使用:this.home.父组件的属性或者方法
        • 子组件 -----> 父组件(参考资料https://www.cnblogs.com/Sky-Ice/p/9289922.html
          • 步骤:
            • 依旧是父组件Home
              <template>
                <div>
                  <div
                    id="example"
                    style="200px;height:200px;background:#ff0;font-size:25px;line-height:200px;text-align:center;margin:20px"
                  >我是父组件</div>
                  <Header></Header>
                </div>
              </template>
              <script>
              import Header from "./header";
              export default {
                name: "example",
                data() {
                  return {
                    msg: "我是你爸爸"
                  };
                },
                components: {
                  Header //子组件
                },
                methods: {},
                mounted() {}
              };
              </script>
              View Code
              与子组件Header
              <template>
                <div id="header">
                  <div
                    style="200px;height:200px;background:pink;font-size:25px;padding-top:100px;text-align:center;margin:20px"
                  >我是子组件</div>
                  <button @click="give">给父组件传值</button>
                </div>
              </template>
              <script>
              export default {
                name: "thesisHeader",
                data() {
                  return {
                    msg: "爸爸,我是您的宝贝女儿啊!"
                  };
                },
                methods: {
                  give(){
                    
                  }
                },
                mounted() {}
              };
              View Code
            • 子组件发送数据
               give() {
                    this.$emit("receive", { msg: this.msg });//$emit用来触发自定义事件,第一个参数是事件的名称,第二个参数是传递给监听事件的数据
               }
            • 父组件在使用子组件的地方进行监听
              <Header @receive="getData"></Header>
            • 接收数据,并进行处理
              getData(data){
                  console.log(data)//{msg: "爸爸,我是您的宝贝女儿啊!"}
              }
          • 同理:传方法与自身都可以,像父组件给子组件的形式   当然,父组件还可以这么调用   this.$refs.名字.属性/方法
            //传方法 
            myMethod() { alert("我是属于子组件的方法"); }, give() { this.$emit("receive", { msg: this.msg, myMethod: this.myMethod }); }
            结果:{msg: "爸爸,我是您的宝贝女儿啊!", myMethod: ƒ} data.myMethod()执行
            //传组件本身
            give() { this.$emit("receive", { header: this }); }
            //父组件的接收处理方法
              getData(data){
                 console.log(data.header)//子组件本身
                 console.log(data.header.msg)//子组件属性
                 data.header.myMethod()//子组件方法
              }
      • 父子组件通信的练习:
        • 父组件
          <template>
            <div>
              <div
                id="example"
                style="200px;height:200px;background:#ff0;font-size:25px;line-height:200px;text-align:center;margin:20px"
              >我是父组件</div>
              <Header @receive="getData" :message="msg"></Header>
            </div>
          </template>
          <script>
          import Header from "./header";
          export default {
            name: "example",
            data() {
              return {
                msg: "我是你爸爸"
              };
            },
            components: {
              Header //子组件
            },
            methods: {
              getData(data){
                this.msg = data.changeMessage;
              }
            },
            mounted() {}
          };
          View Code
        • 子组件
          <template>
            <div id="header">
              <div
                style="200px;height:200px;background:pink;font-size:25px;padding-top:100px;text-align:center;margin:20px"
              >我是子组件
              <p>爸爸说:{{message}}</p>
              </div>
              <button @click="give">给父组件传值</button>
            </div>
          </template>
          <script>
          export default {
            props:["message"],
            name: "thesisHeader",
            data() {
              return {
                msg: "爸爸,我是您的宝贝女儿啊!"
              };
            },
            methods: {
              myMethod() {
                alert("我是属于子组件的方法");
              },
              give() {
                this.$emit("receive", {changeMessage : "照顾好自己"});
              }
            },
            mounted() {}
          };
          </script>
          View Code
    • 非父子组件之间的通信
      • provide/inject  
        provide/ inject 是vue2.2.0新增的api, 简单来说就是父组件中通过provide来提供变量, 然后再子组件中通过inject来注入变量。这里不论子组件嵌套有多深, 
        只要调用了inject 那么就可以注入provide中的数据,而不局限于只能从当前父组件的props属性中回去数据
      • ref/refs
      • eventBus( bus是空的vue的实例,把它放在根组件的data里面,并且bus上面有两个方法$emit,$on)
        eventBus 又称为事件总线,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。
        • 缺点:当项目较大,就容易造成难以维护的灾难;
        • 使用步骤
          • 在父组件中,存在两个兄弟组件,父组件A的代码为:
            <template>
              <div class="parent">
                <son></son>
                <grand-son></grand-son>
              </div>
            </template>
            <script>
            import son from "./son";
            import grandSon from "./grandson";
            export default {
              name: "parent",
              data() {
                return {};
              },
              components: {
                son,
                grandSon
              },
              methods: {}
            };
            </script>
            <style scoped>
            </style>
            View Code
          • 在子组件son中通过$emit发送事件
            <template>
              <div class="son">
                <p @click="handler">我要告诉我的弟弟,我多大了</p>
              </div>
            </template>
            <script>
            import eventBus from "../../eventBus/bus";
            export default {
              name: "son",
              data() {
                return {
                  age: "18"
                };
              },
              methods: {
                handler() {
                  eventBus.$emit("handlerData", { data: this.age });
                }
              }
            };
          • 在另一个子组件中通过$on来监听接收到的消息
            <template>
              <div class="grandson">我的哥哥今年{{age}}岁了</div>
            </template>
            <script>
            import eventBus from "../../eventBus/bus";
            export default {
              name: "",
              data() {
                return {
                  age: ""
                };
              },
              // inject: ["age"],
              methods: {},
              mounted() {
                eventBus.$on("handlerData", data => {
                  this.age = data.data;
                });
              }
            };
            </script>
            <style scoped>
            </style>
          • 如果想移除事件,通过
            eventBus.$off('事件名称',{})
      • vuex

            可以参考本博客写的,如果使用vuex:https://www.cnblogs.com/wxh0929/p/11984417.html

      • localStorage与sessionStorage
        • 实现方式
          window.localStorage.setItem(key,keyValue);
          window.localStorage.getItem(key)
        • 优点:代码实现简单,通信比较容易;缺点:数据和状态比较混乱,不太容易进行维护;
        • 注意:
          注意用JSON.parse() / JSON.stringify() 做数据格式转换 localStorage / sessionStorage可以结合vuex, 实现数据的持久保存,同时使用vuex解决
          数据和状态混乱问题.
    • 组件通信
    • slot分发内容

    3、混合

    作用:实现代码抽离复用 
        eg:当创建了多个vue对象,都要用到一个方法,不应该将这个方法复制到每个对象的methods里面,应该采用'mixin'
        <script>
            var myMixin = {
                methods : {//公共方法
                   test(){console.log(test)} 
                }
            }
            var vm1 = new Vue({
                el : "#box",
                mixins : [myMixin],
                template:{}
            })
            var vm2 = new Vue({...})//box2
        </script>

    注意:在混合的过程当中,如果出现同名的钩子函数,两者都会被调用,而混合的钩子函数在自己的钩子函数前面执行

    如果出现同名的methods、components等值为对象的选项时,则自己组件的选项优先(只执行自己组件内的);

    4、动态组件(参考资料https://www.cnblogs.com/xiaohuochai/p/7395694.html

    • 定义:多个组件使用同一个挂载点,然后动态的在它们之间切换;我们可以使用保留的<component>元素,动态的绑定到它的is属性;
      <template>
        <div id="home">
          <el-radio-group v-model="checkId" @change="change">
            <el-radio label="Header">头部</el-radio>
            <el-radio label="Footer">底部</el-radio>
          </el-radio-group>
          <div class="checkComponent">
            <component :is="checkId"></component>
          </div>
        </div>
      </template>
        </div>
      </template>
      <script>
      import Header from "./header";
      import Footer from "./footer";
      export default {
        name: "example",
        data() {
          return {
            checkId: "Header"
          };
        },
        components: {
          Header, //子组件
          Footer
        },
        methods: {
          change(value) {
            this.checkId = value;
          }
        }
      };
      </script>
      View Code
    • 特点
      • 缓存:<keep-alive>包裹动态组件时,可以把切换出去的组件保留在内存中,而不是销毁,从而保留它的状态或者避免重复渲染DOM,提高性能;
        <template>
          <div id="home">
            <el-radio-group v-model="checkId" @change="change">
              <el-radio label="Header">头部</el-radio>
              <el-radio label="Footer">底部</el-radio>
            </el-radio-group>
            <div class="checkComponent">
              <keep-alive>
                <component :is="checkId"></component>
              </keep-alive>
            </div>
          </div>
        </template>
          </div>
        </template>
        <script>
        import Header from "./header";
        import Footer from "./footer";
        export default {
          name: "example",
          data() {
            return {
              checkId: "Header"
            };
          },
          components: {
            Header, //子组件
            Footer
          },
          methods: {
            change(value) {
              this.checkId = value;
            }
          }
        };
        </script>
        View Code
      • include和exclude属性允许组件有条件的缓存
         <keep-alive include="Header">//只缓存header   footer不缓存,如果要缓存多个,用逗号分隔
           <component :is="checkId"></component>
         </keep-alive>
        • 字符串方式:include = "a,b"
        • 数组方式:v-bind:include = [ "a",“b” ]
        • 正则方式:v-bind:include = "/a | b /";
      • activated和deactivated钩子函数
        • 定义:使用<keep-alive>会将数据保留在内存中,如果要在每次进入页面的时候获取最新的数据,需要在activated阶段获取数据,承担原来created钩子中获取数据的任务;
    • 参考资料:https://www.jianshu.com/p/0b0222954483    组件实际应用:实现组件的有条件缓冲

    5、遇到的问题

  • 相关阅读:
    三级听力
    查找算法集(数组实现、链表实现)(转贴)
    男人一生必须要做10件事(转载)
    经典源码网(集合)
    ubuntu8.04下mplayer错误error:could not open required directshow codec drvc.dll
    asp.net 访问 iis的权限 问题
    OPENROWSET 说明
    vb多线程问题
    收缩数据库日志文件(转贴)
    Update 两个表之间数据更新
  • 原文地址:https://www.cnblogs.com/wxh0929/p/11206180.html
Copyright © 2020-2023  润新知