代码之家  ›  专栏  ›  技术社区  ›  TinyTiger

Vue 3可组合控制台错误:TypeError:无法读取未定义的属性(读取'iError')

  •  0
  • TinyTiger  · 技术社区  · 4 年前

    我有一个Vue 3/TypeScript组合程序,可以将图像上传到Firebase存储:

    interface ImageUpload {
      uploadTask?: UploadTask;
      downloadURL?: string;
      progress?: number;
      error?: Error;
      isCanceled: boolean;
      isRunning: boolean;
      isPaused: boolean;
      isSuccess: boolean;
      isError: boolean;
    }
    
    export const useImageUpload = (file: File) => {
      const imageUpload = ref<ImageUpload>({
        uploadTask: undefined,
        downloadURL: undefined,
        progress: undefined,
        error: undefined,
        isRunning: true,
        isCanceled: false,
        isPaused: false,
        isSuccess: false,
        isError: false,
      });
    
      // Create the file metadata
      const metadata = {
        contentType: 'image/jpeg',
      };
    
      // Upload file and metadata to the object 'images/mountains.jpg'
      const filePath = `user_images/${user.uid}/${file.name + '_' + uid()}`;
      const storageReference = storageRef(store, filePath);
      const uploadTask = uploadBytesResumable(storageReference, file, metadata);
    
      //Put the task into the ref
      imageUpload.value.uploadTask = uploadTask;
    
      // Listen for state changes, errors, and completion of the upload.
      const unsubscribe = uploadTask.on(
        'state_changed',
        (snapshot) => {
          // Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
          const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
          imageUpload.value.progress = progress;
          console.log(`Upload is ${progress}% done`);
          switch (snapshot.state) {
            case 'canceled':
              imageUpload.value.isCanceled = true;
              imageUpload.value.isRunning = false;
              console.log('Upload is canceled');
              break;
            case 'paused':
              imageUpload.value.isPaused = true;
              imageUpload.value.isRunning = false;
              console.log('Upload is paused');
              break;
            case 'running':
              imageUpload.value.isPaused = false;
              imageUpload.value.isRunning = true;
              console.log('Upload is running');
              break;
          }
        },
        (error) => {
          // Upload error
          imageUpload.value.isError = true;
          imageUpload.value.isRunning = false;
          imageUpload.value.error = error;
          console.log(error);
        },
        () => {
          // Upload completed successfully, now we can get the download URL
          getDownloadURL(uploadTask.snapshot.ref)
            .then((downloadURL) => {
              imageUpload.value.downloadURL = downloadURL;
              imageUpload.value.isSuccess = true;
              imageUpload.value.isRunning = false;
              console.log('File available at', downloadURL);
            })
            .catch((error) => {
              // getDownloadURL error
              imageUpload.value.isError = true;
              imageUpload.value.isRunning = false;
              if (error instanceof Error) {
                imageUpload.value.error = error;
              }
              console.log(error);
            });
        }
      );
      // Unsub the listener when the composable is not in use
      watchEffect((onInvalidate) => {
        onInvalidate(() => {
          unsubscribe();
        });
      });
    
      return imageUpload;
    };
    

    我在Vue组件中使用它,如下所示:

    <template>
      <div>
        <q-img :src="picUrl" />
        <q-file
          ref="picFileRef"
          v-model="picFile"
          style="display: none"
          @update:model-value="handlePicUpdate"
        />
        <div v-if="imageUpload.isError">
          {{ imageUpload.error?.message }}
        </div>
        <div v-if="imageUpload.isRunning">Loading...</div>
        <q-btn type="button" label="Upload Image" @click="handleUploadClick" />
        <q-btn type="button" label="Pause" @click="handlePauseClick" />
        <q-btn type="button" label="Resume" @click="handleResumeClick" />
      </div>
    </template>
    
    <script setup lang="ts">
    import { ref, Ref, watchEffect } from 'vue';
    import { useImageUpload } from 'src/composables/storage';
    import { QFile } from 'quasar';
    import { UploadTask } from 'firebase/storage';
    
    const picFile = ref<File>();
    const picUrl = ref<string>();
    const picFileRef = ref() as Ref<QFile>;
    
    interface ImageUpload {
      uploadTask?: UploadTask;
      downloadURL?: string;
      progress?: number;
      error?: Error;
      isCanceled: boolean;
      isRunning: boolean;
      isPaused: boolean;
      isSuccess: boolean;
      isError: boolean;
    }
    
    let imageUpload: Ref<ImageUpload>; // Had to declare this at the top level so I could access it's properties in the template and in other functions
    
    const handleUploadClick = () => {
      picFileRef.value.pickFiles();
    };
    const handlePicUpdate = () => {
      if (picFile.value) {
        imageUpload = useImageUpload(picFile.value);
        watchEffect(() => {
          console.log('progress', imageUpload.value.progress);
          console.log('isRunning', imageUpload.value.isRunning);
          console.log('isPaused', imageUpload.value.isPaused);
          if (imageUpload.value.downloadURL) {
            picUrl.value = imageUpload.value.downloadURL;
          }
        });
      }
    };
    const handlePauseClick = () => {
      imageUpload.value.uploadTask?.pause();
    };
    const handleResumeClick = () => {
      imageUpload.value.uploadTask?.resume();
    };
    </script>
    

    我的IDE(Visual Studio代码)中没有错误,但当我运行它时,会出现这个控制台错误,以及其他3个看起来非常相似的错误:

    WebsitePage.vue?bfce:21 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'isError')
        at Proxy.render (WebsitePage.vue?bfce:21:1)
        at renderComponentRoot (runtime-core.esm-bundler.js?9e79:893:1)
        at ReactiveEffect.componentUpdateFn [as fn] (runtime-core.esm-bundler.js?9e79:5030:1)
        at ReactiveEffect.run (reactivity.esm-bundler.js?0a2b:167:1)
        at setupRenderEffect (runtime-core.esm-bundler.js?9e79:5156:1)
        at mountComponent (runtime-core.esm-bundler.js?9e79:4939:1)
        at processComponent (runtime-core.esm-bundler.js?9e79:4897:1)
        at patch (runtime-core.esm-bundler.js?9e79:4489:1)
        at ReactiveEffect.componentUpdateFn [as fn] (runtime-core.esm-bundler.js?9e79:5037:1)
        at ReactiveEffect.run (reactivity.esm-bundler.js?0a2b:167:1)
    

    如果这些错误是因为 let imageUpload: Ref<ImageUpload> 没有设置任何默认值,如何设置默认值?

    有没有更好的方法让我不用申报 让图像上传:Ref<ImageUpload> 在高层?

    0 回复  |  直到 4 年前
        1
  •  0
  •   TinyTiger    4 年前

    通过初始化非可选的 imageUpload 财产。

    所以删除这一行:

    let imageUpload: Ref<ImageUpload>;
    

    let imageUpload = ref<ImageUpload>({
      isCanceled: false,
      isRunning: false,
      isPaused: false,
      isSuccess: false,
      isError: false,
    });
    
    推荐文章