【问题标题】:Vue 3 Composition API - Object is possibly 'null'Vue 3 Composition API - 对象可能是\'null\'
【发布时间】:2022-11-25 04:03:42
【问题描述】:

我在用Vue 3 组合 API打字稿使用HammerJS package 实现缩放。

我正在尝试遵循一个用 JavaScript 编写的工作示例代码笔: https://codepen.io/bakho/details/GBzvbB 在 Vue 中实现。

然而,当我试图让它在我的 Vue 应用程序中工作时遇到了一些问题,我不确定如何解决它。

以下是以下错误:

// Object is possibly 'null'.
imageContainer.value.offsetWidth;

// Object is possibly 'null'.
imageContainer.value.appendChild(displayImage);

// Object is possibly 'null'.
imageContainer.value.addEventListener...

这是完整的源代码:

<template>
  <h1>Image Zoom</h1>
  <div class="imageContainer" ref="imageContainer"></div>
</template>

<script lang="ts">
import Hammer from "hammerjs";
import { defineComponent } from "vue";
import { ref } from 'vue';

export default defineComponent({
  setup() {
    const imageUrl = "https://source.unsplash.com/random";
    const imageContainer = ref(null)

    let minScale = 1; let maxScale = 4; let imageWidth : any; let imageHeight : any; let containerWidth : any;
    let containerHeight : any; let displayImageX = 0; let displayImageY = 0; let displayImageScale = 1;
    let displayDefaultWidth : any; let displayDefaultHeight 
    let rangeX = 0; let rangeMaxX = 0; let rangeMinX = 0;
    let rangeY = 0; let rangeMaxY = 0; let rangeMinY = 0;

    // let displayImageRangeY = 0;

    let displayImageCurrentX = 0;
    let displayImageCurrentY = 0;
    let displayImageCurrentScale = 1;

    function resizeContainer() {
      containerWidth = imageContainer.value.offsetWidth;
      containerHeight = imageContainer.value.offsetHeight;
      if (displayDefaultWidth !== undefined && displayDefaultHeight !== undefined) {
        displayDefaultWidth = displayImage.offsetWidth;
        displayDefaultHeight = displayImage.offsetHeight;
        updateRange();
        displayImageCurrentX = clamp(displayImageX, rangeMinX, rangeMaxX);
        displayImageCurrentY = clamp(displayImageY, rangeMinY, rangeMaxY);
        updateDisplayImage(
          displayImageCurrentX,
          displayImageCurrentY,
          displayImageCurrentScale
        );
      }
    }
    resizeContainer();

    function clamp(value, min, max) {
      return Math.min(Math.max(min, value), max);
    }

    function clampScale(newScale) {
      return clamp(newScale, minScale, maxScale);
    }

    const displayImage = new Image();
        displayImage.src = imageUrl;
        displayImage.onload = function(){
        imageWidth = displayImage.width;
        imageHeight = displayImage.height;
        imageContainer.value.appendChild(displayImage);
        displayImage.addEventListener('mousedown', e => e.preventDefault(), false);
        displayDefaultWidth = displayImage.offsetWidth;
        displayDefaultHeight = displayImage.offsetHeight;
        rangeX = Math.max(0, displayDefaultWidth - containerWidth);
        rangeY = Math.max(0, displayDefaultHeight - containerHeight);
    }

    imageContainer.value.addEventListener('wheel', e => {
        displayImageScale = displayImageCurrentScale = clampScale(displayImageScale + (e.wheelDelta / 800));
        updateRange();
        displayImageCurrentX = clamp(displayImageCurrentX, rangeMinX, rangeMaxX)
        displayImageCurrentY = clamp(displayImageCurrentY, rangeMinY, rangeMaxY)
        updateDisplayImage(displayImageCurrentX, displayImageCurrentY, displayImageScale);  
    }, false);

    function updateDisplayImage(x, y, scale) {
        const transform = 'translateX(' + x + 'px) translateY(' + y + 'px) translateZ(0px) scale(' + scale + ',' + scale + ')';
        displayImage.style.transform = transform;
        displayImage.style.webkitTransform = transform;
        displayImage.style.transform = transform;
    }

    function updateRange() {
        rangeX = Math.max(0, Math.round(displayDefaultWidth * displayImageCurrentScale) - containerWidth);
        rangeY = Math.max(0, Math.round(displayDefaultHeight * displayImageCurrentScale) - containerHeight);
        
        rangeMaxX = Math.round(rangeX / 2);
        rangeMinX = 0 - rangeMaxX;

        rangeMaxY = Math.round(rangeY / 2);
        rangeMinY = 0 - rangeMaxY;
    }

    const hammertime = new Hammer(imageContainer);
    hammertime.get('pinch').set({ enable: true });
    hammertime.get('pan').set({ direction: Hammer.DIRECTION_ALL });

    hammertime.on('pan', ev => {  
        displayImageCurrentX = clamp(displayImageX + ev.deltaX, rangeMinX, rangeMaxX);
        displayImageCurrentY = clamp(displayImageY + ev.deltaY, rangeMinY, rangeMaxY);
        updateDisplayImage(displayImageCurrentX, displayImageCurrentY, displayImageScale);
    });

    hammertime.on('pinch pinchmove', ev => {
        displayImageCurrentScale = clampScale(ev.scale * displayImageScale);
        updateRange();
        displayImageCurrentX = clamp(displayImageX + ev.deltaX, rangeMinX, rangeMaxX);
        displayImageCurrentY = clamp(displayImageY + ev.deltaY, rangeMinY, rangeMaxY);
        updateDisplayImage(displayImageCurrentX, displayImageCurrentY, displayImageCurrentScale);
    });

    hammertime.on('panend pancancel pinchend pinchcancel', () => {
        displayImageScale = displayImageCurrentScale;
        displayImageX = displayImageCurrentX;
        displayImageY = displayImageCurrentY;
    }); 

    return {};
  },
});
</script>

<style>
.imageContainer {
  width: 96%;
  height: 96%;
  max-width: 800px;
  max-height: 600px;
  position: absolute;
  overflow: hidden;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  margin: auto;
  background: #2b2b2c;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.imageContainer > img {
  display: block;
  max-width: 100%;
  max-height: 100%;
  cursor: move;
  touch-action: none;
}
</style>

谁能告诉我哪里出了问题,为什么会导致这个Object is possibly 'null'

【问题讨论】:

  • const imageContainer = ref(null)。这是你的声明,你将它声明为null,这就是为什么它可以为空。
  • 即使我将 const imageContainer = ref(null) 替换为 const imageContainer = document.querySelector('.imageContainer'),我仍然遇到完全相同的错误。
  • 您可能不应该使用 querySelector tbh,而应该使用 template $refs
  • 这正是我创建ref="imageContainer" 以及错误Object is possibly 'null' 的原因。

标签: typescript vue.js vuejs3 vue-composition-api


【解决方案1】:

所以这是该错误的原因:

  • const imageContainer = ref(null) -> 您将值设置为 null 并且 TypeScript 警告您对该对象的属性的任何访问都可能引发错误,因为初始值为 null 并且您正尝试在中使用 ref元素可以存在或不必存在的模板。

  • const imageContainer = document.querySelector('.imageContainer') -> 您正在查询存在或不存在的 HTML 元素,这意味着您还可以获得 null 作为值,TypeScript 再次警告您对该对象的任何属性的访问都可能引发错误

解决方案:

  • 你必须实际从setup函数返回imageContainer变量,这样Vue在模板中将它与ref绑定,并且可以在onMounted函数内部访问该变量,因为它是一个ref,这意味着它还没有安装到 DOM 中。 setup 在 Vue.js 组件生命周期中的 createdmounted 挂钩之前被调用,您无权访问那里的任何 DOM 内容。
import { ref, onMounted } from 'vue'

export default {
  setup() {
    const imageContainer = ref(null)

    onMounted(() => {
      // here you access imageContainer variable
    })

    return {
      imageContainer 
    }
  }
}

【讨论】:

  • 我已经试过了,但还是不行。出现相同的Object is possibly 'null' 错误。
  • 我更新了答案告诉我它是否有效
【解决方案2】:

这是打字稿错误。问题是您没有处理变量为空的可能性。您需要做的就是正确定义类型并处理 null 情况。阅读 Vue 文档中的 Typing ref 以了解我的意思。

以下是您的案例的一些示例:

// Define the ref as an HTMLElement or null
const imageContainer = ref<HTMLElement | null>(null);

...

// Set the width/height based on the imageContainer, or use 0 if it is null.
containerWidth = imageContainer.value?.offsetWidth ?? 0;
containerHeight = imageContainer.value?.offsetHeight ?? 0;

...

// Only call appendChild/addEventListener if the ref is not null.
imageContainer.value?.appendChild(displayImage);

如果在出现错误的地方应用此方法,则应该可以解决问题。

【讨论】:

    猜你喜欢
    • 2021-10-25
    • 1970-01-01
    • 2022-01-26
    • 2020-06-16
    • 2020-04-25
    • 2021-05-31
    • 2021-08-15
    • 2022-11-29
    • 2021-10-22
    相关资源
    最近更新 更多