vue引入外部js变量初始化data导致指针共用

287 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情

故事的开始

某一天,接到一个需求开发oa系统,oa你们都知道,会有很多表单场景:我负责的oa流程恰巧表单超级无敌多字段,起码有200多个字段,然后我觉得如果全部写在在vue的data里面就会使得代码太多了,所以我打算把它写成了一个js文件,然后使用es6的module导出这个模块使用,这样就会简洁很多,就像下面这样:

没抽出文件之前

  • 结构:oa.vue
export default {
  data() {
    return {
      form: {
        // 姓名
        name: '',
        // 年龄
        age: '',
        // ... 还有200个字段,反正就很长,加上都要写注释,更长
      }
    }
  }
}

抽出文件之后

  • 结构:oa.vue,field.js
// 通过引入使用更加简洁
import field from './field.js'
export default {
  data() {
    return {
      form: filed
    }
  }
}
// 全部都放来这里集中就可以了
export default {
  // 姓名
  name: '',
  // 年龄
  age: '',
  // ... 还有200个字段,反正就很长,加上都要写注释,更长
}

故事的继续

这样一看,好像没什么问题呀,现在说具体一点场景:

  • 我的oa.vue是一个弹出框的表单,点击弹出就出现,点击关闭就会销毁,因为我给dialog加了一个v-if,我以为每次弹出都会重新加载我field.js的原始字段和内容,就是每次都是初始值,这也是预期的

(第一次打开弹框)

(第一次打开弹框修改了值)

  • 但结果并非如此,在表单里面弹出第一次,对里面的值做了修改之后,第二次再弹出来的时候保留了上次的值,这个是不符合这次的预期的,

(第二次打开弹框,里面的值没有恢复初始的字符串'joe')

让我们看看代码具体是咋样:

结构:home.vue(主文件,引用了oa.vue组件),oa.vue(弹框表单),field.js(字段文件)

<template>
  <div id="">
    <h1 @click="visible = true">open</h1>
    <!-- 这里引用了oa.vue组件,通过v-if让每次弹框打开都是全新加载的组件 -->
    <OA v-if="visible" :visible="visible" @close="visible = false"/>
  </div>
</template>

<script>
import OA from "./oa";
export default {
  name: "Home",
  components: {OA},
  data() {
    return {
      visible: false,
    }
  },
}
</script>
<template>
  <div class="">
    <el-dialog :visible="show">
      <h1 @click="$emit('close')">close</h1>
      <el-input v-model="form.name"></el-input>
    </el-dialog>
  </div>
</template>
<script>
import filed from "./filed";
export default {
  naem: 'OA',
  props: ['visible'],
  data() {
    return {
      form: filed,
      show: this.visible
    }
  },
}
</script>
export default {
    name: 'joe',
    age: 25,
}

故事的结尾

我们来分析一下原因,我开始以为是组件没有被销毁,但是后来我证实组件的确通过v-if指令销毁了,所以我把目标转向了那个field.js文件,具体分析之后就是独立出来的field.js变成了一个 全局变量,你通过引用并且改变了一个引用类型的某个值,那么你下次使用的时候,必然是看到改变后的值(就和浅复制一个道理),如下图:

解决方案

既然是引用变量导致的问题的,那么就解决他,什么深复制很多方法都可以解决,这里比较简单的方法就是不要导出变量,我们导出方法,方法里面通过return对象的方式,如下:

// 导出一个方法,里面再return对象
export default function () {
  return {
    name: 'joe',
    age: 25
  }
}
import field from './field.js'
export default {
  data() {
    return {
      form: filed() // 这里要调用方法才行
    }
  }
}