【问题标题】:How to generate Vue components from Javascript如何从 Javascript 生成 Vue 组件
【发布时间】:2020-05-09 05:24:37
【问题描述】:

我是 Vue.js 的新手(具有计算机科学和编程方面的背景,包括交互式 Javascript 网页),因为我是一名教师,所以我有一个测验网站用来给我的学生布置作业。

我的代码库很乱,所以我决定将整个东西迁移到 Vue,我的想法是我可以为每种单独的问题类型使用一个组件——关注点分离等等。

但是,我似乎无法找到一种方法来动态生成适当的组件并将它们包含在我的页面中。

这是我的框架的简化版本,有两种问题类型。如果我将组件直接包含在 HTML 中,它们就可以正常工作。


Vue.component("Freetext",{
            props: ["prompt","solution"],
            data : function() {return { 
                        response:""
                    }},
            methods : { 
                check : function () {
                    if (this.solution == this.response) {
                        alert ("Correct!");
                        app.nextQuestion();
                    } else {
                        alert ("Try again!");
                    }
                }
            },
            template:'<span><h1>{{prompt}}</h1> <p><input type="text" v-model="response"></input></p>   <p><button class="LG_checkbutton" @click="check()">Check</button></p></span>'
        })
Vue.component("multi",{
            props : { prompt: String,
                 options : Array,
                 key_index : Number // index of correct answer
                },
            data : function() {return { 
                        response:""
                    }},
            methods : { 
                check : function (k) {
                    if (k == this.key_index) {
                        alert ("Correct!");
                        app.nextQuestion();
                    } else {
                        alert ("Try again!");
                    }
                }
            },
            template:'<span><h1>{{prompt}}</h1><button v-for="(v,k) in options" @click="check(k)">{{v}}</button></span>'
        })

</script>

<div id="app">

<Freetext prompt="Type 'correct'." solution="correct"></freetext>

<multi prompt="Click the right answer." :options='["right","wrong","very wrong"]' :key_index=0></multi>

</div>

<script>
    var app = new Vue({
            el: "#app",
            data : { 
                        questions:[ {type:"Multi",
                                     prompt: "Click the right answer.",
                                     options:["right","wrong","very wrong"],
                                     key:0},
                                    {type:"Freetext",
                                     prompt:"Type 'correct'.",
                                     solution:"correct"}
                                   ],
                        question_number:0
                    },
            methods : { 
                nextQuestion : function () {
                    this.question_number ++;
                }
            }
        })
</script>

但我想要做的是动态生成 div app 的内容,基于使用数据成员 app.question_number 作为 app.questions 的索引,以及所指示问题的 .type 成员(即app.questions[app.question_number].type)

如果我尝试制作表单的应用程序:

{{question}}
</div>
<script>
//...
            computed : {
                question : function () {
                    var typ = this.questions[this.question_number].type;
                    return "<"+typ+"></"+typ+">";
                }

...我只是得到纯文本,它没有被解析为 HTML。

如果我从控制台尝试document.getElementById("app").innerHTML = "&lt;multi prompt='sdf'&gt;&lt;/multi&gt;";,标签会显示在DOM 检查器中,并且不会被Vue 处理,即使我调用app.$forceUpdate()

有没有办法解决这个问题?

【问题讨论】:

  • 也许您需要再次阅读 Vue 的工作原理 - 可能有更好的方法(就 Vue 的工作原理而言)来完成您想要实现的目标
  • 您也可以像现在一样将所有组件添加到 html 中,但添加 v-if="type=='FreeText'" 并仅按问题类型显示正确的组件。
  • 你也可以使用 v-bind:is 像这个例子 jsfiddle.net/chrisvfritz/o3nycadu from vue docs "dynamic components"
  • &lt;component :is=... 正是我所需要的。谢谢。不知道为什么,但我查看的所有 Vue 资源都非常不清楚。我的代码现在运行良好。

标签: javascript vue.js vue-component


【解决方案1】:

我想可能是稍微不同的做法,Vue 支持“动态组件”的概念

https://vuejs.org/v2/guide/components-dynamic-async.html

这将让您定义在每个问题上使用的组件,看起来像

<component v-bind:is="question.component" :question="question"></component>

【讨论】:

  • 谢谢!这正是我一直在寻找的,我不知道我是如何在文档中没有发现它的。它让我可以选择在屏幕上同时显示一个动态更新的问题,或者在屏幕上显示多个 v-for 多个问题,或者一个接一个地显示。再次感谢——现在我的代码完成了我想做的事情。
  • @NiallTracey 因为您使用的是普通的 html/javascript 方法,所以您可能还缺少其他一些您可能会觉得有用的东西,我还在 stackoverflow.com/questions/50205341/… 上回答了另一个问题
  • 谢谢@Keith——这非常有帮助。所有关于组件的文档都一直在描述单文件组件版本,而忽略了指出这样做的方式/
【解决方案2】:

虽然 Keith 的回答适用于我需要做的大部分事情,但还有另一种我刚刚发现的方法来处理这个问题,我想我会分享一下,以防其他人正在寻找它:给一个块级别 HTML 元素具有 v-html 属性。

对我来说,这作为短期修复很方便,因为我正在迁移将动态 HTML 生成为字符串的代码库,并且我可以快速集成一些现有代码而无需完全重新编写。

例如,我有一个函数makeTimetable,它采用一个表示一周活动的自定义数据结构,并将其转换为顶部的天数和左侧的时间表,为所有活动设置适当的行跨度。 (这是一个有点复杂的函数,但它可以满足我的需要,目前并不值得重构。)

所以我可以这样使用:

<script type="text/x-template" id="freetext-template">
    <span>
        <div v-html="tabulated_timetable"></div>
        <p>{{prompt}}</p> 
        <p><input type="text" v-model="response"></input></p>
        <p><button class="LG_checkbutton" @click="check()">Check</button></p>
    </span>
</script>

<script>
var freetext = Vue.component("Freetext",{
            props: {"prompt":String,
                    "timetable":Object,
                    "solution":String,
            data : function() {return { 
                        response:""
                    }},
            computed : {
                   tabulated_timetable : function () {
                           return makeTimetable (this.timetable);
                   }},
            methods : { 
                check : function () {
                    if (this.solution == this.response) {
                        alert ("Correct!");
                        app.nextQuestion();
                    } else {
                        alert ("Try again!");
                    }
                }
            },
            template:'#freetext-template'
        })
</script>

(I suppose I could put `tabulated_timetable` in `methods` rather than `computed`, as it's set once and never changed, but I don't know if there would be any performance benefit to doing it that way.)

【讨论】:

    猜你喜欢
    • 2019-06-29
    • 2020-11-18
    • 2020-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-24
    • 2019-02-10
    • 2010-09-09
    相关资源
    最近更新 更多