如何从嵌套组件发出,同时保持引用函数外部变量的能力

Posted

技术标签:

【中文标题】如何从嵌套组件发出,同时保持引用函数外部变量的能力【英文标题】:How to emit from a nested component while maintaining ability to reference variables outside of function 【发布时间】:2020-01-07 08:59:44 【问题描述】:

我正在尝试构建一个图像上传应用程序,该应用程序将图像上传到 Firebase 存储,一路跟踪上传百分比,并在上传时更新图库以显示所有图像,包括新上传的图像。

我开始构建用于上传和跟踪上传进度的组件。但是,它不起作用,因为 onUpload() 方法无法更改 this.uploadPercentage 的值。

<template>
  <v-container style="height: 100%;">
      <v-row
        align-content="center"
        justify="center"
      >
        <h1>Image Uploader</h1>
      </v-row>
      <v-row
        align-content="center"
        justify="center"
      >
        <v-col cols="6">
          <v-progress-linear
            color="deep-purple accent-4"
            rounded
            
            :value="uploadPercentage"
            id="uploader"
          ></v-progress-linear>
        </v-col>
      </v-row>
      <v-row
        align-content="center"
        justify="center"
      >
        <v-col
          class="align-content-center"
          cols="4"
        >
          <v-file-input
            v-model="file"
            accept="image/*"
            label="choose an image"
            outlined
            @change="onFileChange"
            ></v-file-input>
        </v-col>
        <v-col
          class="align-content-center mt-2"
          cols="2"
        >
          <v-btn
            @click="onUpload"
          >
            Upload
            <v-icon right dark>mdi-cloud-upload</v-icon>
          </v-btn>
        </v-col>
      </v-row>
    </v-container>
</template>

<script>
import firebase from '@/firebase/init'

export default 
  data: () => (
    file: null,
    imageURL: null,
    uploadPercentage: 0
  ),
  methods: 
    onFileChange () 
      let reader = new FileReader()
      reader.onload = () => 
        reader.imagUrl = reader.result
      
      reader.readAsDataURL(this.file)
    ,
    onUpload () 
      // create a firebase storage ref
      var storageRef = firebase.storage().ref('public_wall/' + this.file.name)

      // upload file
      var task = storageRef.put(this.file)

      // update progress bar
      task.on('state_changed',
        function (snapshot) 
          console.log('uploading')
          var percentage = (snapshot.bytesTransferred / snapshot.totalBytes) * 100
          this.uploadPercentage = percentage
        ,

        function error (err) 
          console.log(err)
        ,

        function completed () 
          console.log('upload complete')
        

      )
    
  

</script>

我发现这是因为函数的范围,将 task.on() 中的快照函数更改为箭头函数我能够解决这个问题。

task.on('state_changed',
        snapshot => 
          console.log('uploading')
          var percentage = (snapshot.bytesTransferred / snapshot.totalBytes) * 100
          this.uploadPercentage = percentage
        ,

但是,我希望这个组件是一个嵌套组件,并在上传完成时发出一个信号。为此,我尝试将 task.on() 中的已完成函数更改为以下内容:

function completed () 
          console.log('upload complete function')
          this.$emit('upload-complete', 'hello')
        

这会引发错误:TypeError: this.$emit is not a function。

通过一些研究,我发现您不能在箭头函数中引用 this.$emit,但是,完成的函数不是箭头函数,将快照函数改回非箭头函数并不能解决问题,只会停止上传百分比跟踪器的工作。

我做错了什么,我该如何解决这个问题?

【问题讨论】:

【参考方案1】:

它是因为 This 指的是已完成函数中的其他内容。你可以这样做

onUpload () 
  let _self = this // saving reference to this
  // create a firebase storage ref
  var storageRef = firebase.storage().ref('public_wall/' + this.file.name)

  // upload file
  var task = storageRef.put(this.file)

  // update progress bar
  task.on('state_changed',
    function (snapshot) 
      console.log('uploading')
      var percentage = (snapshot.bytesTransferred / snapshot.totalBytes) * 100
      this.uploadPercentage = percentage
    ,

    function error (err) 
      console.log(err)
    ,

    function completed () 
      _self.$emit('upload-complete', 'hello')
      // accessing that reference using closure
    

  )

【讨论】:

以上是关于如何从嵌套组件发出,同时保持引用函数外部变量的能力的主要内容,如果未能解决你的问题,请参考以下文章

如何从嵌套命名空间中引用外部 C++ 命名空间?

JavaScript闭包

如何从另一个组件调用一个函数,同时保持另一个组件在 Angular 中的通用性?

JavaScript高级之闭包

为啥嵌套函数可以从外部函数访问变量,但不允许修改它们[重复]

python-闭包的理解