【问题标题】:Vue2: prop does not get boundVue2:道具未绑定
【发布时间】:2018-06-08 07:55:41
【问题描述】:

注意:我是 Vue 的新手,但在发布之前,我已经查阅了文档并寻找了其他问题。我找不到答案。我还安装了 Vue 开发工具来帮助调试,但无法解决这个看似简单的问题。

我想制作一个简单的 Vue 单文件组件来测试并查看我的 rollup 捆绑设置是否有效(例如,以确保我对 Vue 组件作为模块和导出的理解是正确的)。

所以我的简单组件由两个较小的组件组​​成——一个带有向上 V 形的按钮或一个向下 V 形的按钮。我们的想法是让它位于页面中间,然后单击移动到下一个section 标记。

到目前为止,我得到这个功能非常好(见demo)。

但是,我想将一个属性传递给两个子组件 - 一个 offsetSelector。无论我如何尝试设置属性,即使在父级别(例如v-bind:offsetSelector=".navbar":offsetSelector=".navbar"offsetSelector=".navbar" 等),我也无法设置它。

有人可以帮我弄清楚为什么这没有被绑定吗?

arrow-to-section.vue

<template>
  <div>
    <up :offsetSelector="offsetSelector"></up>
    <down :offsetSelector="offsetSelector"></down>
  </div>
</template>

<script>
import down from './section-down.vue';
import up from './section-up.vue';

export default {
  components: { down, up },
  props: [ 'offsetSelector' ],
  data: function () {
    return { }
  }
}
</script>

<style scoped>
div {
  position: fixed;
  top:50%;
  margin-left: 10px;
}

div > button {
  display: block;
  margin-bottom: 10px !important;
}
</style>

section-down.vue

<template>
  <button type="button" name="button" v-on:click="animateScrollTo">
    <i class="fa fa-chevron-down"></i>
  </button>
</template>

<script>


export default {
  props: [ 'offsetSelector' ],
  data: function () {
    return {
      offset: 0
    }
  },
  computed: {

  },
  methods: {
    setOffset: function() {
      this.offset = this.offsetSelector == undefined
      ? 0
      : $(this.offsetSelector).outerHeight() == undefined
        ? 0
        : $(this.offsetSelector).outerHeight()
    },

    animateScrollTo: function() {

      this.setOffset()


      var sections = document.querySelectorAll("section")
      var current = undefined;
      var curOffset = this.offset

      console.log('vue-down-offset', curOffset, this.offsetSelector)

      sections.forEach(function(s, i){
        var winScroll = $(window).scrollTop() - curOffset
        var curScroll = $(s).offset().top

        if ( winScroll < curScroll && current == undefined)
        { current = s }
      })

      if (current != undefined) {
        $('html, body').animate({
            scrollTop: $(current).offset().top - curOffset
        }, 1000, function() {});
      }
    }

  }
}
</script>

<style scoped>
button {
  background: radial-gradient(rgb(0, 198, 255), rgb(0, 114, 255));
  background-color: transparent;
  border: transparent 0px solid;

  margin: 0;
  padding: 0px;

  width:32px;
  height:32px;

  border-radius: 50%;
}

i { font-size: 16px; }

button:focus {
  outline: transparent 0px solid;
}
</style>

section-up.vue

<template>
  <button type="button" name="button" v-on:click="animateScrollTo">
    <i class="fa fa-chevron-up"></i>
  </button>
</template>

<script>


export default {
  props: [ 'offsetSelector' ],
  data: function () {
    return { offset: 0 }
  },
  computed: { },
  methods: {
    setOffset: function() {
      this.offset = this.offsetSelector == undefined
      ? 0
      : $(this.offsetSelector).outerHeight() == undefined
        ? 0
        : $(this.offsetSelector).outerHeight()
    },

    animateScrollTo: function() {
      this.setOffset()

      var sections = document.querySelectorAll("section")
      var current = undefined;
      var curOffset = this.offset
      console.log('vue-up-offset', curOffset, this.offsetSelector)

      sections.forEach(function(s, i){
        var winScroll = $(window).scrollTop()
        var curScroll = $(s).offset().top - curOffset

        if ( winScroll > curScroll)
        { current = s }
      })
      if (current == undefined) {
        current = document.querySelector("body")
      }

      if (current != undefined) {
        $('html, body').animate({
            scrollTop: $(current).offset().top - curOffset
        }, 1000, function() {});
      }
    }

  }
}
</script>

<style scoped>
button {
  background: radial-gradient(rgb(0, 198, 255), rgb(0, 114, 255));
  background-color: transparent;
  border: transparent 0px solid;

  margin: 0;
  padding: 0px;

  width:32px;
  height:32px;

  border-radius: 50%;
}


button:focus {
  outline: transparent 0px solid;
}

i { font-size: 16px; }
</style>

demo.html

<style media="screen">
    section {
      height: 100vh;
    }
  </style>


  <body>
    <nav class="navbar navbar-light sticky-top">
      <a class="navbar-brand" href="../../">
          <img src="../../data/vdsm.svg" alt="logo" style="width:150px;">
          <h4 class="lead"> vue components </h4>
      </a>
      <crumbs id="crumbs"></crumbs>
    </nav>
    <arrowToSection id="arrowToSection" offset-selector=".navbar"></arrowToSection>

    <section style="background-color:#5433FF;"></section>
    <section style="background-color:#20BDFF;"></section>
    <section style="background-color:#A5FECB;"></section>
    <section style="background-color:#86fde8;"></section>

  </body>

  <script type="text/javascript" src="component.js"></script>

component.js

let arrowToSection = vdsm.arrowToSection
new Vue({
  el: '#arrowToSection',
  template: '<arrowToSection/>',
  components: { arrowToSection }
})

代码库

repo 可以在here找到。

两个子组件:

  • /src/scripts/modules/section-up.vue
  • /src/scripts/modules/section-down.vue

我正在处理的组件:

  • /src/scripts/modules/arrow-to-section.vue

组件演示:

  • /demos/arrow-to-section/index.html

【问题讨论】:

  • 无需查看其余代码(您应该将其包含在问题中,请参阅MCVE 和“如何提问”):v-bind 的值必须是有效的 JavaScript表达式,而.navbar 绝对不是一个。如果您想传递this 的成员,请删除点。如果您想传递一个字符串,请将其放在单引号中,例如 :prop="'.navbar'"
  • @Frax 我试过单引号,但没用。
  • 您使用的正是:offset-selector="'.navbar'",但它不起作用?这很令人惊讶。另外,在单文件组件中可以使用:offsetSelector="'.navbar'",这样更易​​读。
  • 从我在代码中看到的,不是:offset-selector,而是offset-selector(缺少冒号);)
  • @Frax 我尝试了以下组合(为简洁起见,我将 offsetSelector 截断为 oS 或 o-s)。 :o-s="'.navbar'"v-bind:o-s="'.navbar'"o-s="'.navbar'":oS="'.navbar'"v-bind:oS="'.navbar'"oS="'.navbar'",它们都不起作用。在单文件组件中,我不想硬设置属性,因为它可能会根据用例而改变...

标签: vue.js vuejs2


【解决方案1】:

我还没有完全阅读您的存储库,但从我可以看到的答案可以在这里找到:https://vuejs.org/v2/guide/components-props.html

HTML 属性名称不区分大小写,因此浏览器会解释 任何大写字符为小写。这意味着当您使用 在 DOM 模板中,camelCased 的道具名称需要使用它们的 kebab-cases (连字符分隔)等价物:

基本上,props 列表中的 prop 是 camelCase,但在 html 属性中它是 kebap-case。我希望这会有所帮助:)

【讨论】:

  • 注意:它实际上是双向的。 camelCase 仍然从属性中读取。
  • 我尝试将 offsetSelector 切换到 offset-selector 但这对我不起作用:/
  • @webnoob 你能看一下吗?我对为什么这不起作用感到非常迷茫:/
  • 查看 Frax 的评论。
  • 实际上,您几乎遇到了真正的问题,并最终引导我找到解决方案:“prop”是在纯 HTML 中定义的,根本没有在组件中定义,因此 Vue 甚至从未尝试读取它.
【解决方案2】:

为什么它不起作用

实际上,答案很简单:您没有在 arrowToSection 组件上放置任何道具。虽然在你的身体中有一个令人困惑的元素 &lt;arrowToSection&gt;,但 arrowToSection 组件并没有安装在它上面——至少没有直接安装。您在 component.js 中创建的匿名 Vue 实例中创建 arrowToSection 组件,此处为:template: '&lt;arrowToSection/&gt;'。而且你没有向它传递任何道具。

如何解决

要修复它,您需要在此处传递道具:

let arrowToSection = vdsm.arrowToSection
new Vue({
  el: '#arrowToSection',
  template: '<arrowToSection :offsetSelector="'.navbar'" />',
  components: { arrowToSection }
})

接下来,你可能会问“为什么不给这个匿名元素添加一个 prop,而使用 body HTML 中设置的值”。答案是,props 是在 Vue 组件之间传递值的方式。外部 HTML 不是 Vue 组件,您不能从那里传递道具。您在 #arrowToSection 元素上添加的属性只是普通属性(顺便说一句,整个元素被替换并且参数丢失)。

稍后注意:This example in documentation 显示属性显然是从 HTML 元素中读取的。这似乎与我的实验有些矛盾。如果您使用完整的 v-bind:prop="..." 表示法而不是 :prop="..." 简写,则传递道具可能会起作用。

propsData 的替代解决方案

如果你真的想使用道具,你可以使用propsData

let arrowToSection = vdsm.arrowToSection
new Vue({
  el: '#arrowToSection',
  props: ['offsetSelector'],
  propsData: {
    offsetSelector: '.navbar',
  },
  template: '<arrowToSection :offsetSelector="offsetSelector" />',
  components: { arrowToSection }
})

如您所见,这确实是人为的,没有任何意义。但是,您并不真正需要中间组件,因此,至少在实践中,您可以这样使用propsData

let arrowToSection = vdsm.arrowToSection
new Vue(Object.assign({}, arrowToSection, {
  el: '#arrowToSection',
  propsData: {
    offsetSelector: '.navbar',
  },
}))

免责声明:我不确定官方是否支持以这种方式使用组件。可能不是。但它有效。

【讨论】:

  • 我认为您应该删除您的问题,这与您当前对即将起作用的事情的错误实施有关:) 因此,如果这只是您发现的一个错字 - 不需要这类问题/答案。祝你好运!
  • @AndreyPopov 对不起,我不关注。为什么你把它写成我的问题我的答案?而且,实际上,我认为它非常有用,而且是一个有趣的问题。当然,有问题的长代码主要是噪音,但这不是问题,任何有类似问题的人都会只看问题标题和答案。而且这里的问题似乎是对 prop 语义的实际误解,因此将其称为错字在 IMO 中完全不合适。
  • @Frax 首先感谢您在这方面的时间和帮助。正如您可能已经注意到的那样,我对 vue 很陌生,并且无法弄清楚这样的事情可能会让我离开。所以谢谢。 +1 并回答。似乎我不明白如何正确使用单文件组件(公平地说,文档在这个问题上受到限制 - 主要链接到 webpack,我使用汇总)。有没有办法不制作匿名元素而只使用 vdsm.arrowToSection 组件?
  • @SumNeuron 你使用单文件组件看起来不错,使用匿名实例作为Vue结构的根似乎是标准模式,实际上相当不错。那么你真的不需要关心单文件组件到底是什么,这很好(我实际上不确定它在技术上是否总是相同的)。它们大多只是在new Vue({...}) 表达式中编写模板的好语法。
  • @SumNeuron 我正在阅读有关指令的内容,并找到了一个示例 here,其中读取了 html 元素的属性 。我不得不承认我很困惑。我在 JSFiddle 中对其进行了测试,但它不适用于我的道具。所以要么道具和指令有不同的行为,要么我错过了一些东西(或者,在最坏的情况下,这个例子是错误的)。
猜你喜欢
  • 1970-01-01
  • 2018-05-01
  • 2013-12-23
  • 2017-12-18
  • 2018-03-05
  • 2020-09-19
  • 2018-11-23
  • 1970-01-01
  • 2018-08-10
相关资源
最近更新 更多