这次我们要说点不一样的Vue中h5移动端开发知识点__Vue.js
发布于 3 年前 作者 banyungong 937 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

主题列表:juejin, github, smartblue, cyanosis, channing-cyan, fancy, hydrogen, condensed-night-purple, greenwillow, v-green, vue-pro, healer-readable, mk-cute, jzman, geek-black, awesome-green, qklhk-chocolate

贡献主题:https://github.com/xitu/juejin-markdown-themes

theme: channing-cyan highlight:

父子组件的传值及调用修改数据

在某个狂风暴雨的早晨,经过漫长的讨论研究的深度学习后,解决了一个传值过程中的深度大坑,写下此篇文章记录一下。

在大家工作中开发vue项目最常见的就是组件的封装,既然要封装组件要调用组件就避免不了组件的传值,根据官方文档权威介绍,父组件导入子组件后,父组件在components中定义接收子组件,然后在data中定义要传递给子组件的数据,并且在调用子组件上绑定该数据,话不多说,上代码:

<template>
	<Form :formData="formData" @handleChange="handleChange" />
</template>
<script>
	import Form from "@/components/Form/form.vue"
  export default {
    components: {
    	Form
  	},
    data(){
      retrun {
        formData: {
          nameValue: "",
          phoneValue: null,
          codeValue: null,
          reasonValue: ""
      	}
      }
    },
    methods:{
      handleChange(res){
        console.log(res)
      },
    },
  }
</script>

相信大家没有跳跃阅读文章的话会注意到我在子组件身上绑定了一个handleChange的方法,这是Vue中子组件调用父组件方法改变传递进来的数据的方法,因为官方定义组件传值是单向数据流即子组件通过props接收到的数据自己本身是不可以修改的,否则就会破坏单向数据流,代码如下:

<template>
  <div>
    <Name :nameValue="formData.nameValue" @handleChange="handleChange" />
    <div class="line"></div>
    <Phone :phoneValue="formData.phoneValue" @handleChange="handleChange" />
    <div class="line"></div>
    <Code :codeValue="formData.codeValue" @handleChange="handleChange" />
    <div class="line"></div>
    <Reason :reasonValue="formData.reasonValue" @handleChange="handleChange" />
  </div>
</template>
<script>
import Name from "./name/name.vue";
import Phone from "./phone/phone.vue";
import Code from "./code/code.vue";
import Reason from "./reason/reason.vue";
export default {
  props: {
    formData: {
      type: Object,
      default: () => {
        return {};
      }
    }
  },
  components: {
    Name,
    Phone,
    Code,
    Reason
  },
  methods: {
    handleChange(res) {
      this.$emit("handleChange", res);
    }
  },
  watch: {
    formData(newFormdata) {
      console.log("一级子组件改变了", newFormdata);
    }
  }
};
</script>

ios设备input框输入问题及解决方法

大家可以看到这事我们的一级子组件,不变的样子,不变的配方,接下来就是我们这篇文章的产出原因以及重点,众所周知,大家在写一些表单的时候都会对每一个input框给一个验证规则,那大家在写后台的时候常用的element ui 自带了组件验证规则可以用,那在写移动端项目的时候对样式有更精细化要求的时候就需要我们自己手写input框及验证规则,一般来说我们会在blur失焦的时候去触发验证,但是既然是深度学习那一定不会这么简单的对吧哈哈哈,作者遇到的是要求在输入的时候触发验证检测不超过多少个字符,那小伙伴们第一想到的肯定是@input事件,可能在日常编程过程中大家并没有注意到这点,那就是移动端项目在ios设备和安卓设备的区别,可能会有部分小伙伴说都是h5页面能有什么区别呢,那就想错了啊,在ios设备中有一个独特的输入机制,那就是中文输入,假设我们限制最大输入四个字符,那在安卓中我们先拼音xiaowang然后选中小王是可以正常输入的,但是在ios设备上规则是不一样的,当我们输入xiaowang的时候因为拼音会首先占用输入框,导致当输入到xiao的时候已经占满了我们所给的四个字符的空间,当我们打完所有拼音选中小王之后你会惊奇的发现只有xiao在输入框中,作者翻阅了网上大部分文章后发现大家都用了相同的方法!!!(这里作者要吐槽CSDN和博客园!!!上面的文章就好像卡卡西写的一样,全是拷贝一模一样,都不验证一些文章的真实可行性!!!)当我们输入中文的时候其实也提供了专门监听的方法compositionend和compositionstart,而CSDN和博客园作者所阅读过的文章中都是将输入中文和其他字符混为一谈,这会导致很多的问题,所以我在经过研究后改进了方法,老规矩,上代码:

<template>
  <div class="container">
    <div class="name">
      <img :src="img" alt="" />
      <span>真实姓名</span>
    </div>
    <input
      type="text"
      placeholder="请输入真实姓名"
      class="nameInput"
      :value="nameValue"
      [@input](/user/input)="handleInput"
      @compositionend="end"
      @compositionstart="start"
    />
  </div>
</template>
<script>
import img from "@/assets/images/choose.png";
export default {
  props: {
    nameValue: {
      type: String,
      default: ""
    }
  },
  data() {
    return {
      img,
      status: false
    };
  },
  methods: {
    // handleInput(e) {
    //   // setTimeout(() => {
    //   if (this.status) {
    //     return false;
    //   }
    //   console.log("target", e.target.value);
    //   const nameValue = e.srcElement.value.length > 3 ? e.srcElement.value.substring(0, 3) : e.srcElement.value;
    //   // const nameValue = e.target.value.length > 3 ? e.target.value.substring(0, 3) : e.target.value;
    //   console.log("nameValue", nameValue, typeof nameValue);
    //   this.$emit("handleChange", { nameValue, key: "nameValue" });
    //   // e.srcElement.value = nameValue;
    //   // this.localNameValue = nameValue;
    // }
    start() {
      this.status = true;
      console.log("start: ", this.status);
    },
    end(e) {
      this.status = false;
      console.log("end: ", this.status);
      console.log("执行过滤end");
      this.limitLength(e);
    },
    handleInput(e) {
      console.log("input: ", this.status);
      if (this.status) {
        console.log(e.target.value);
        return;
      }
      console.log(e.target.value);
      console.log("执行过滤input");
      this.limitLength(e);
    },
    limitLength(e) {
      const nameValue = e.srcElement.value.length > 3 ? e.srcElement.value.substring(0, 3) : e.srcElement.value;
      this.$emit("handleChange", { nameValue, key: "nameValue" });
      console.log(e.target.value);
    }
  },
  watch: {
    nameValue(newNameValue) {
      console.log(newNameValue);
      //   if (newNameValue.length > 3) {
      //     console.log("dayu", newNameValue.substring(0, 3));
      //   }
      //   const nameValue = newNameValue.length > 3 ? newNameValue.substring(0, 3) : newNameValue;
      //   this.$emit("handleChange", { nameValue, key: "nameValue" });
    }
  }
};
</script>

大家可以看到我在上面注释了很多代码,说出来都是泪。我将监听输入中文的两个方法单独拿出来封装,这是因为要将输入中文和其他字符进行区分开,先看handleInput方法,当我们输入的是非中文的时候则不会触发start和end方法则status为false,handleInput方法跳过If判断走limitLength方法判断长度,将改变的数据传递给一级子组件,一级子组件再将数据传递给父组件修改数据。

vue父子组件传值传递复杂类型数据深度讨论

注意注意!!!这里是本文的核心,传值深度讲解

大家看父组件的handleChange方法:

handleChange(res) {
      // const deepFormData = JSON.parse(JSON.stringify(this.formData));

      // deepFormData[res.key] = "";
      // deepFormData[res.key] = String(res[res.key]);
      // setTimeout(() => {
      //   console.log("之前", this.formData);
      //   this.formData = deepFormData;
      //   console.log("之后", this.formData);
      // }, 0);

      // console.log(res);
      console.log("之前", this.formData);
      this.formData[res.key] = "";
      console.log("置空", this.formData);
      setTimeout(() => {
        this.formData[res.key] = res[res.key];
        console.log("之后", this.formData);
      }, 0);
    }

先看我注释掉的内容,作者一开始所想的是父组件数据改才会触发修改将数据传递给子组件,然后直接this.formData去修改发现用watch监控不到数据的改变,那是因为对象Object是复杂类型,wacth去监听的时候只要Object的空间地址没有发生改变就不会触发watch,所以作者想到的是用JSON.parse(JSON.stringify)深拷贝去改变地址触发,这时候作者发现中文可以成功限制,但是数字不行,上图为证

相信大家已经看出来问题了,那就是我们实际的数据是123,但是显示仍然是1234,在作者疯狂console后终于发现了问题,当输入的是中文的时候因为地址栏仍然先是拼音所以每一次改变都是nameValue值的改变,所以监听nameVakue是可以触发的,但是当我们输入的是数字的时候因为超过三个字符的时候本身的nameValue已经是123了所以无论我们再怎么输入,系统本身发现一直是限制后的123就不会触发二级子组件的改变,所以就出现了问题,在作者发现问题后就开始想办法解决,那就是让nameValue一直可以监听到变化,现在我们看父组件handleChange没有注释的那一部分内容:

handleChange(res) {
      // const deepFormData = JSON.parse(JSON.stringify(this.formData));

      // deepFormData[res.key] = "";
      // deepFormData[res.key] = String(res[res.key]);
      // setTimeout(() => {
      //   console.log("之前", this.formData);
      //   this.formData = deepFormData;
      //   console.log("之后", this.formData);
      // }, 0);

      console.log(res);
      console.log("之前", this.formData);
      this.formData[res.key] = "";
      console.log("置空", this.formData);
      setTimeout(() => {
        this.formData[res.key] = res[res.key];
        console.log("之后", this.formData);
      }, 0);
    }
  },

虽然我们没有改变formData的地址空间,但是因为对象里面的数据发生了改变,虽然一级子组件接收到的是对象,但是二级子组件接收到的是一个普通的String类型,所以当我们父组件改变对象里面的数据的时候二级子组件仍然可以监听到变化,象印看过代码的一些小伙伴注意到了我有一步this.formData[res.key]=""的置空操作,这一步是确保每一次的数据都有变化,否则当你输入超过三个字符的时候,每一次都是123依然会有之前的问题,而当你每一次都先置空然后再改变就必定会出先从空数据到有数据的过程。而我将赋值操作放在定时器中是将赋值操作放置在异步队列中,保证先监听到置空的变化再执行赋值的变化。最终结果如图

文章结尾:CSDN和博客园误我大事!!!祝各位读者年年涨薪,年薪百万。

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: wy白菜 原文链接:https://juejin.im/post/6933415994727612429

回到顶部