【问题标题】:v-model on an element outside the componentv-model 在组件外的元素上
【发布时间】:2018-07-26 13:46:08
【问题描述】:

是否可以在 DOM 中位于 Vue 实例根元素之外的元素上使用 v-model?

即,您能否提供以下内容:

$(function(){
  Vue.component('custom-element', {
    template: '#custom-template'
  })

  var vm = new Vue({
    el: "#app"
    data: {
      selected: ""
    }
  })
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.13/dist/vue.js"></script>


<select id="SportSelector" v-model="selected">
  <option value="football"> Football </option>
  <option value="tennis"> Tennis </option>  
</select>

<div id="app">
  <custom-element> 

  </custom-element>
</div>

<script type="text/x-template" id="custom-template">
  <div>
    <p> {{selected}} </p>
  </div>
</script>

选择在 Vue 的根元素(“app”)之外的地方。这是我的问题的简化版本,但我相信突出了我遇到的困难。

我看到这个答案在谈论 centralised state management 在 Vue 之间共享数据,但是必须将选择包装在一个完全不同的 Vue 中似乎有点繁重,我觉得我错过了一些重要的东西! (对于 Vue 来说还是很新的)。 Vue 与之交互的所有内容都必须位于 Vue 实例的根元素下吗?

【问题讨论】:

  • 您可以在根元素内创建一个不可见/空的自定义组件,该组件将控制它之外的元素。但仅出于可维护性的原因,不应该这样做。是的,Vue 或类似库的工作方式,由库控制的所有内容都应该在一个 Vue 实例中。
  • 为什么你的应用程序根目录之外有东西?理想情况下,您提供给 Vue 的元素会包裹您的整个页面。将&lt;select&gt; 置于 Vue 控制之外的目的是什么?
  • 可以在 Vue 外根据 select 设置 Vue 数据值,但不能在 select 上使用v-model。此外,我完全不同意 Vue 需要始终包裹整个页面的上述评论。 Vue 的一大优势在于它很容易集成到现有页面中。
  • @Bert 我们至少可以同意它应该包装任何你想做的反应数据绑定吗?这是一个 XY 问题。 OP 说“在 Vue 之间共享数据”,对我来说,这意味着 OP 正在让一个 Vue 上下文尝试进入另一个 Vue 上下文,这似乎是个坏主意。
  • @zero298 在我看来,OP 并没有试图在 Vues 之间共享数据,而是有利于使用状态管理系统,他接着说他真的不想把选择放在另一个 Vue 中。在很多情况下,例如使用 CMS,其中一些布局并不完全在您的控制之下,但您仍然可能希望对 UI 的某些元素使用 Vue。

标签: javascript vue.js vuejs2


【解决方案1】:

不,您不能在 Vue 上下文之外的元素上使用 v-model。原因是 Vue 编译成一个渲染函数来渲染它所控制的元素的内容;它不会呈现该元素之外的任何内容,除非您涉及特定代码或像 vue-portal 这样的库。

也就是说,您可以在选择更改时更新 Vue,使用标准 javascript 设置 Vue 数据属性。我还清理了一些事情,比如将选定的数据传递给你的组件(这是必要的;组件不能直接访问它们的父数据)。

$(function(){
  Vue.component('custom-element', {
    props: ["selected"],
    template: '#custom-template'
  })

  var vm = new Vue({
    el: "#app",
    data: {
      selected: ""
    }
  })
  
  //get a reference to the select
  const sports = document.querySelector("#SportSelector")
  //add a listener that updates the Vue data when the select changes
  sports.addEventListener("change", evt => vm.selected = evt.target.value)
  //initialize Vue with the current selected value
  vm.selected = sports.value
  
  
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.13/dist/vue.js"></script>


<select id="SportSelector">
  <option value="football"> Football </option>
  <option value="tennis"> Tennis </option>  
</select>

<div id="app">
  <custom-element :selected="selected"> 

  </custom-element>
</div>

<script type="text/x-template" id="custom-template">
  <div>
    <p> {{selected}} </p>
  </div>
</script>

【讨论】:

    【解决方案2】:

    除非你对选择进行了一些时髦的交互,否则将它添加到你的 Vue 实例中。 Vue 实例应该封装表单的所有模板和逻辑。

    如果您尝试在同一个元素上混合使用 jQuery 和 Vue,您可能不会玩得开心。

    【讨论】:

      【解决方案3】:

      您可以使用portal-vue 来完成此操作。它允许您将组件中的代码放置在 DOM 中的任何位置。

      将它添加到您的视图实例中:

      import PortalVue from 'portal-vue';
      Vue.use(PortalVue);
      

      在您的组件中,您定义要在组件外部呈现的内容:

      <portal to="destination">
        <p>This slot content will be rendered wherever the <portal-target> with name 'destination'
          is  located.</p>
      </portal>
      

      然后您可以将其放置在 DOM 中的任何位置:

      <portal-target name="destination">
        <!--
        This component can be located anywhere in your App.
        The slot content of the above portal component will be rendered here.
        -->
      </portal-target>
      

      来自https://github.com/LinusBorg/portal-vue的示例代码

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-02-14
        • 2018-08-25
        • 2018-04-28
        • 2019-10-05
        • 2021-07-01
        • 2020-06-07
        • 2019-06-16
        相关资源
        最近更新 更多