You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Vue.js中正确初始化jQuery DataTables API

Vue 集成 jQuery DataTables 初始化问题及正确实现方式

问题背景

在ASP.NET MVC应用中通过CDN加载Vue.js,尝试集成jQuery DataTables API,遇到以下问题:

  • 数据已成功加载,编辑/更新按钮功能正常
  • 表格初始化异常:搜索功能无法作用于数据,顶部显示“No data available in table”提示
  • 尝试在fetch函数中调用updateDataTable()后,无数据提示消失、搜索可用,但出现初始化警告,且表格行重复(一行带编辑按钮,一行不带)
  • 已通过setTimeout实现临时解决方案,但想了解正确的初始化方式,同时疑惑Vue实例中混用jQuery是否属于不良实践

现有代码

Markup(HTML)

<div id="app-datatable" class="container">
    <h3 class="mt-3">Add User</h3>
    <hr>
    <div class="row">
        <div class="col">
            <label>User ID</label>
            <input type="number" class="form-control" v-model="id" :disabled="editingIndex !== -1">
        </div>
        <div class="col">
            <label>User name</label>
            <input type="text" class="form-control" v-model="name" :disabled="editingIndex === -1">
        </div>
        <div class="col">
            <label>User Email</label>
            <input type="text" class="form-control" v-model="email" :disabled="editingIndex === -1">
        </div>
        <div class="col">
            <button v-if="editingIndex !== -1" class="btn btn-secondary" @@click="cancelEdit">Cancel</button>
            <button v-if="editingIndex !== -1" class="btn btn-success" @@click="updateUser">Update</button>
        </div>
    </div>

    <button class="btn btn-info mt-2" @@click="addUser">Add User</button>
    <hr>

    <div class="row">
        <div class="col">
            <table id="user-table" class="display table-bordered nowrap" cellspacing="0" width="100%">
                <thead>
                    <tr>
                        <th>User ID</th>
                        <th>User Name</th>
                        <th>User Email</th>
                        <th>Edit</th>
                    </tr>
                </thead>
                <tbody>
                    <tr v-for="(user, index) in dataTableData" :key="user.id">
                        <td>{{ user.id }}</td>
                        <td>{{ user.name }}</td>
                        <td>{{ user.email }}</td>
                        <td>
                            <button class="btn btn-primary" @@click="editUser(index)">Edit</button>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
</div>

Script(Vue代码)

const columns = [
    { title: 'User ID', data: 'id' },
    { title: 'User Name', data: 'name' },
    { title: 'User Email', data: 'email' },
    { title: 'Edit', orderable: false }
];

const columnDefs = [
    // Define columnDefs as needed
];
new Vue({
    el: '#app-datatable',
    data() {
        return {
            id: '',
            name: '',
            email: '',
            dataTableData: [],
            editingIndex: -1,
        };
    },
    methods: {
        addUser() {
            this.dataTableData.push({
                id: this.id,
                name: this.name,
                email: this.email
            });
            this.clearForm();
        },

        editUser(index) {
            this.editingIndex = index;
            const user = this.dataTableData[index];
            this.id = user.id;
            this.name = user.name;
            this.email = user.email;
        },

        cancelEdit() {
            this.editingIndex = -1;
            this.clearForm();
        },

        updateUser() {
            if (this.editingIndex !== -1) {
                const user = this.dataTableData[this.editingIndex];
                user.id = this.id;
                user.name = this.name;
                user.email = this.email;
                this.cancelEdit();
            }
        },
        initializeDataTable() {
            this.dataTable = $('#user-table').DataTable({
                "paging": false,
                "searching": true,
                "ordering": false,
                "info": false,
                "bAutoWidth": false,
                "processing": true,
                "autoWidth": false,
                "bAutoWidth": false,
                "retrieve": true,
                "responsive": true,
                "stateSave": false,
                "order": [],
                "columns": columns,
                "columnDefs": columnDefs
            });
        },
        fetchData() {
            const url = 'https://jsonplaceholder.typicode.com/users';
            fetch(url, {
                method: "GET",
            })
                .then((response) => {
                    response.json().then((data) => {
                        this.dataTableData = data
                    });
                })
                .catch((err) => {
                    console.error(err);
                });
        },
        updateDataTable() {
            // Update DataTable with new data
            if (this.dataTable) {
                this.dataTable.clear().rows.add(this.dataTableData).draw();
            }
        },
        clearForm() {
            this.id = '';
            this.name = '';
            this.email = '';
        },

    },
    mounted() {
        this.initializeDataTable();
        this.fetchData();
    },
});

临时解决方案代码

initializeDataTable() {
    setTimeout(() => {
        const dataTableData = $('#user-table').DataTable({
            "paging": false,
            "searching": true,
            "ordering": false,
            "info": false,
            "bAutoWidth": false,
            "processing": true,
            "autoWidth": false,
            "bAutoWidth": false,
            "retrieve": true,
            "responsive": true,
            "stateSave": false,
            "order": [],
            "columns": columns,
            "columnDefs": columnDefs
        });
    }, 300);
},

正确初始化方案

核心问题分析

问题根源是Vue的异步DOM更新机制与DataTables的同步初始化逻辑冲突:

  1. 执行initializeDataTable()时,Vue还未完成dataTableData的DOM渲染,DataTables读取到空表格,因此显示无数据提示
  2. 同时使用Vue的v-for渲染行和DataTables的rows.add(),会导致两者重复插入数据,出现双行问题

正确实现步骤

  1. 移除Vue模板中的v-for行渲染,让DataTables全权负责数据渲染,通过自定义列渲染实现编辑按钮
  2. 等待数据加载完成后再初始化DataTables,确保初始化时已有可用数据
  3. 通过DataTables API而非直接操作DOM来更新数据,避免与Vue的响应式机制冲突

修改后的代码

模板部分

<div id="app-datatable" class="container">
    <h3 class="mt-3">Add User</h3>
    <hr>
    <div class="row">
        <div class="col">
            <label>User ID</label>
            <input type="number" class="form-control" v-model="id" :disabled="editingIndex !== -1">
        </div>
        <div class="col">
            <label>User name</label>
            <input type="text" class="form-control" v-model="name" :disabled="editingIndex === -1">
        </div>
        <div class="col">
            <label>User Email</label>
            <input type="text" class="form-control" v-model="email" :disabled="editingIndex === -1">
        </div>
        <div class="col">
            <button v-if="editingIndex !== -1" class="btn btn-secondary" @click="cancelEdit">Cancel</button>
            <button v-if="editingIndex !== -1" class="btn btn-success" @click="updateUser">Update</button>
        </div>
    </div>

    <button class="btn btn-info mt-2" @click="addUser">Add User</button>
    <hr>

    <div class="row">
        <div class="col">
            <table id="user-table" class="display table-bordered nowrap" cellspacing="0" width="100%">
                <thead>
                    <tr>
                        <th>User ID</th>
                        <th>User Name</th>
                        <th>User Email</th>
                        <th>Edit</th>
                    </tr>
                </thead>
                <tbody></tbody>
            </table>
        </div>
    </div>
</div>

脚本部分

const columns = [
    { title: 'User ID', data: 'id' },
    { title: 'User Name', data: 'name' },
    { title: 'User Email', data: 'email' },
    { 
        title: 'Edit', 
        orderable: false,
        // 自定义编辑按钮渲染逻辑
        render: function(data, type, row, meta) {
            return `<button class="btn btn-primary edit-btn" data-index="${meta.row}">Edit</button>`;
        }
    }
];

const columnDefs = [
    // 按需定义列配置
];
new Vue({
    el: '#app-datatable',
    data() {
        return {
            id: '',
            name: '',
            email: '',
            dataTableData: [],
            editingIndex: -1,
            dataTable: null
        };
    },
    methods: {
        addUser() {
            const newUser = {
                id: this.id,
                name: this.name,
                email: this.email
            };
            this.dataTableData.push(newUser);
            // 通过DataTables API添加行
            this.dataTable.row.add(newUser).draw();
            this.clearForm();
        },

        editUser(index) {
            this.editingIndex = index;
            const user = this.dataTableData[index];
            this.id = user.id;
            this.name = user.name;
            this.email = user.email;
        },

        cancelEdit() {
            this.editingIndex = -1;
            this.clearForm();
        },

        updateUser() {
            if (this.editingIndex !== -1) {
                const user = this.dataTableData[this.editingIndex];
                user.id = this.id;
                user.name = this.name;
                user.email = this.email;
                // 通过DataTables API更新对应行
                this.dataTable.row(this.editingIndex).data(user).draw();
                this.cancelEdit();
            }
        },
        initializeDataTable() {
            this.dataTable = $('#user-table').DataTable({
                "paging": false,
                "searching": true,
                "ordering": false,
                "info": false,
                "bAutoWidth": false,
                "processing": true,
                "autoWidth": false,
                "retrieve": true,
                "responsive": true,
                "stateSave": false,
                "order": [],
                "columns": columns,
                "columnDefs": columnDefs,
                "data": this.dataTableData
            });

            // 事件委托绑定编辑按钮点击(按钮由DataTables动态生成)
            $('#user-table').on('click', '.edit-btn', (e) => {
                const index = parseInt($(e.target).data('index'));
                this.editUser(index);
            });
        },
        fetchData() {
            const url = 'https://jsonplaceholder.typicode.com/users';
            fetch(url, {
                method: "GET",
            })
                .then((response) => response.json())
                .then((data) => {
                    this.dataTableData = data;
                    // 数据加载完成后再初始化DataTables
                    this.initializeDataTable();
                })
                .catch((err) => {
                    console.error(err);
                });
        },
        clearForm() {
            this.id = '';
            this.name = '';
            this.email = '';
        },

    },
    mounted() {
        this.fetchData();
    },
});

Vue混用jQuery的说明

  • 并非绝对的不良实践,但需避免两者操作同一DOM区域,否则会因DOM更新机制不同导致冲突
  • 优先选择Vue生态内的表格组件(如Element UI Table、Vuetify Data Table等),更符合Vue响应式设计,减少维护成本
  • 若必须使用jQuery插件,需确保插件初始化时机在Vue完成DOM渲染之后,且通过插件API而非直接操作DOM来更新数据

内容的提问来源于stack exchange,提问作者Shane

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 03:40:54