【问题标题】:Laravel + Vue, best practicesLaravel + Vue,最佳实践
【发布时间】:2021-07-03 19:37:40
【问题描述】:

我正在 Laravel 中构建一个简单的发票系统。所以很明显我需要建立一个视图,当我能够编辑它时。这是一个表格,我可以在其中动态添加和删除行。

所以我的想法是:好的,我可以使用 jQuery 轻松做到这一点,但是添加具有多个输入的行,特别是当我使用 tailwind 时,这意味着有很多奇怪的类,会很混乱,所以我'将尝试使用 Vue。我没有这方面的经验,但总的来说它看起来很简单。

然后我做了一个 Vue 组件,其中包含 <table><tr>s,里面有输入:

<document-items-table :items='@json($document->items)' />

它不是 SPA,所以我不想在里面进行 AJAX 调用,我已经加载了我的文档,所以我通过 vue prop 作为 Json 传递了文档项。而且效果很好。

接下来是,在每个文档行中,我添加了删除一行的删除按钮。我还有一个添加空行的按钮。

我的组件如下所示:

<template>
    <div class="w-full">
        <div class="relative flex flex-col min-w-0 break-words w-full rounded bg-white">
            <div class="block w-full overflow-x-auto">
                <table class="items-center w-full bg-transparent border-collapse pb-4">
                    <thead>
                    <tr>
                        <th style="width: 30px" class="pl-6 pr-2 align-middle border-b border-solid py-3 text-xs uppercase border-l-0 border-r-0 whitespace-nowrap font-semibold text-left bg-blueGray-50 text-blueGray-500 border-blueGray-100">
                            No.
                        </th>
                        <th style="min-width: 600px" class=" align-middle border-b border-solid py-3 text-xs uppercase border-l-0 border-r-0 whitespace-nowrap font-semibold text-left bg-blueGray-50 text-blueGray-500 border-blueGray-100">
                            Name
                        </th>
                        <th style="width: 60px" class="align-middle border-b border-solid py-3 text-xs uppercase border-l-0 border-r-0 whitespace-nowrap font-semibold text-left bg-blueGray-50 text-blueGray-500 border-blueGray-100">
                            Quantity
                        </th>
                        <th style="width: 60px"  class="align-middle border-b border-solid py-3 text-xs uppercase border-l-0 border-r-0 whitespace-nowrap font-semibold text-left bg-blueGray-50 text-blueGray-500 border-blueGray-100">
                            Unit
                        </th>
                        <th style="width: 120px"  class="align-middle border-b border-solid py-3 text-xs uppercase border-l-0 border-r-0 whitespace-nowrap font-semibold text-left bg-blueGray-50 text-blueGray-500 border-blueGray-100">
                            Price
                        </th>
                        <th style="width: 60px"  class="align-middle border-b border-solid py-3 text-xs uppercase border-l-0 border-r-0 whitespace-nowrap font-semibold text-left bg-blueGray-50 text-blueGray-500 border-blueGray-100">
                            Tax rate
                        </th>
                        <th style="width: 60px"  class="align-middle border-b border-solid py-3 text-xs uppercase border-l-0 border-r-0 whitespace-nowrap font-semibold text-left bg-blueGray-50 text-blueGray-500 border-blueGray-100">

                        </th>
                    </tr>
                    </thead>
                    <tbody class="border-b-4 border-white">
                    <tr v-for="(item, i) in this.itemsLocal" :key="item.id">
                        <td class="border-t-0 pl-6 align-middle border-l-0 border-r-0 text-xs whitespace-nowrap py-1">
                            {{ i + 1 }}
                        </td>
                        <td class="border-t-0 align-middle border-l-0 border-r-0 text-xs whitespace-nowrap py-1">
                            <item-input name="title" id="title" :value="item.title" />
                        </td>
                        <td class="border-t-0 align-middle border-l-0 border-r-0 text-xs whitespace-nowrap py-1">
                            <item-input class="w-full" name="quantity" id="quantity" :value="item.quantity" />
                        </td>
                        <td class="border-t-0 align-middle border-l-0 border-r-0 text-xs whitespace-nowrap py-1">
                            <item-input class="w-full" name="unit" id="unit" :value="item.unit" />
                        </td>
                        <td class="border-t-0 align-middle border-l-0 border-r-0 text-xs whitespace-nowrap py-1">
                            <item-input class="w-full" name="price" id="price" :value="item.price / 100" />
                        </td>
                        <td class="border-t-0 align-middle border-l-0 border-r-0 text-xs whitespace-nowrap py-1">
                            <item-input class="w-full" name="tax" id="tax" :value="item.tax_rate" />
                        </td>
                        <td class="border-t-0 align-middle border-l-0 border-r-0 text-xs whitespace-nowrap py-1">
                            <button @click="() => deleteRow(item.id)" class="p-2 px-4 bg-rose-500 text-white rounded"><i class="fas fa-times fa-sm"></i> </button>
                        </td>
                    </tr>
                    </tbody>
                </table>
            </div>
        </div>
        <div class="w-full text-center">
            <button @click="addRow" class="bg-lightBlue-600 px-4 py-2 text-white rounded text-sm font-bold">Add row</button>
        </div>
    </div>
</template>

<script>
export default {
    props: {
        items: Array,
    },
    mounted() {
    },

    data () {
        return {
            itemsLocal: [...this.items],
            newRowCount: 1
        }
    },
    methods: {
        deleteRow(itemID) {
            this.itemsLocal = _.reject(this.itemsLocal, ['id', itemID]);
        },
        addRow() {
            this.itemsLocal.push({
                id: -this.newRowCount
            })
            this.newRowCount++;
        }
    }
}
</script>

现在我有一些问题想问那些更熟悉 Vue 的人

  1. 使用 json 将 PHP 对象数组传递给 Vue 组件是一个干净的解决方案吗?
  2. 由于不允许修改props,并且我需要添加和删除行,我将我的itemsprops 克隆到data,然后添加和删除它们。有没有更好的解决方案?
  3. 在我的表格旁边,我将有一些汇总框,其中包含所有元素的一些“总价”。我想根据我输入这些输入的值动态更新这个值。我可以把它做成一个单独的组件,但是我知道在两个组件之间传递值没有好的方法,那么我应该如何解决呢?我是否应该再制作一个包含我的表格和摘要框的父包装器组件,将数据发送到该包装器,然后再发送到摘要组件?或者只是使用一些 jQuery 而不会打扰?
  4. 您看到我在这里使用的任何其他错误做法吗? (我知道 html id 和名称重复 - 我会处理它)

我尽量让它干净。这不是一个工作项目,而是一个提高我技能的事情。

【问题讨论】:

    标签: javascript php laravel vue.js


    【解决方案1】:
    1. 使用 JSON 作为 prop 发送数据可以很好地初始化组件,只需确保在需要的地方进行了正确的编码和解码。
    2. 您不能直接修改道具,但是一旦您将道具中的值分配给本地数据,您几乎可以使用该本地数据做您想做的事情。安装后,您实际上可以将 JSON 数据的值分配给相同类型的各个组件。在这些组件中,如果需要,您实际上可以将数据发送回父级。例如,如果您修改子组件中的值,您可以将其发送回父组件,以便父组件在其本地数据中始终具有更新的值,并且由于 vue 是响应式的,它也会将该值分配回给孩子的道具。所以间接地你会修改道具。我建议为它创建一个组件,因为它可以让您更轻松地创建和删除它的实例,并最大限度地减少您需要在父级别上完成的工作量。
    3. 对于计算,vue 有一个计算属性,可用于计算值并将其分配给可在组件中显示的数据。请注意这一点,因为如果您的计算量很大,它确实会减慢速度。也有观察者可以尝试解决这个问题,但我认为计算总价最容易。 Computed Vs Watched Documentation
    1. 很高兴您尝试学习最佳实践。我也在学习中。到目前为止,我唯一建议的是创建一个新的组件类型并在创建时初始化它们并将新的组件添加到列表中。操作和删除也会更容易。

    【讨论】:

    • 1.好的,谢谢 2。是的,我知道我可以做任何我想做的事情,但只是将道具复制到数据对我来说似乎有点混乱。就像你知道的,有点重复。 “我建议为它创建一个组件,因为它可以让您更轻松地创建和删除它的实例,并最大限度地减少您需要在父级别上执行的工作量” - 所以您的意思是每个 TR 的组件在我的情况下?这将如何使它更容易? 3. 是的,但我的问题是:如果“摘要框”将是一个不同的 vue 组件,那么从我的表中传递数据的最佳方法是什么?
    • 我希望这会有所帮助。如果您只想对这些行中的每一行执行简单的修改,那么您可能不需要新组件,但如果您正在处理复杂的数据和场景,将它们分解为组件并在其中包含相关的依赖项和方法组件对代码组织和可重用性有很大帮助。这是一个水果清单的例子。它不需要新组件。您不需要用于计算的新组件。 codesandbox.io/s/festive-darkness-z84ev?file=/src/components/…
    • @alanmcknee,这是另一个例子。我想向您展示其中的行是不同的组件。这个例子有水果和糕点。水果和糕点有不同的属性。通过将它们放在不同的组件中,我们可以将特定于该组件的任何计算和逻辑隔离到该组件。这些项目是通过引用传递的,因此项目实际上不会在父级和子级之间重复。 codesandbox.io/s/fruit-inventory-forked-fkd2y?file=/src/…
    猜你喜欢
    • 2023-02-02
    • 2022-09-28
    • 2021-06-19
    • 2018-11-30
    • 2016-03-23
    • 1970-01-01
    • 1970-01-01
    • 2015-05-31
    • 2015-04-16
    相关资源
    最近更新 更多