如何在 jQuery 项目中嵌入 Vue


在 2019 年的今天,目前 React, Angular, Vue 主流三大以数据驱动的 JavaScript 前端框架,大致上能说是三分天下了。

而之前基于 jQuery 的开发,甚至基于 jQuery 进行二次开发以实现某种意义上的数据驱动的一些框架,基本上就剩下能维护就维护,不能维护就抛弃之的状态了。

而维护旧的 jQuery 代码,恐怕,也不是那么容易的一件事情。

如果在以前没有这些新玩意,不知道有如今这些更好的解决方案,那还勉强能接受,如今要是写过新东西,再让你回去,恐怕无论谁都是一万个不愿意。无论是从何种角度或者基于何种考虑,尽早用上新东西,才是解放生产力和减少无谓的 Bug 的唯一正道。

而面对旧 jQuery 代码(恐怕很少机会能遇到曾经不用 jQuery 的),一口吃,肯定是吃不下去,就连我这段时间优化三个 80% 相似度的页面代码,我都在完成一个之后就极其地不愿意再继续改剩下两个,虽然剩下的两个页面结构类似,代码也基本上仔细确认一下应该可以利用上已经改好的,但是同一条山路如果没有必要你也不会再去走第二遍,更何况是面对这些几乎没有什么整洁度可言的代码呢。

那么怎么办呢?需求又在不断增加和更新,你要怎么办?

不去改旧的,就放那里吧。要更新,就用新的来替代,要加需求,就用新的来做。

jQuery 的代码无论多么糟糕,就都不要再去动它了,让我们再起个新炉灶吧。

可是起了新炉灶,新旧代码之间还是避免不了要有交互,比如增加一个点击触发一个新的 Dialog。

术语解释下呢,那就是 jQuery 代码如何调用 Vue ?或者说就是 Vue 如何嵌入 jQuery 项目中?

说是 Vue 嵌入 jQuery,其实是基于先来后到,代码量多少来说的,为的是方便理解这种渐进式开发的空间感。

怎么在 Vue 代码中调用 jQuery 方法,这其实有很多人都给出来样板代码,但是更重要一点其实是如果已经用上了 Vue,就真的不要再在里面写 jQuery 了,无论怎么讲都是不合适的。

所以我觉得,问这个问题的大部分人,可能遇到的问题就是原先以 jQuery 为主,然后现在想换到 Vue 来,而旧代码则是设法利用起来,而不是又要再写一波。

如果原先的项目代码整洁度高,我想切换过来应该问题不是很大,数据部分,函数和对象什么的都能迁移过来,而渲染部分,现在已经交给 Vue 就不用自己写了,这可能应该是理想状态,然而……

然而绝大多数我想你需要面对的应该都是"一切万物皆基于选择器与事件的漫长的面条式"代码,而这几乎是不可能有多少可以重复利用的。

这样导致如果想以 Vue 作为基本盘,复用已有的 jQuery 代码成为不可能。

那么这样该怎么办呢?

所以你应该大致能理解了,为啥叫把 Vue 嵌入 jQuery,就是 jQuery 的基本盘不动,而一些小东小西的,开始用 Vue 来写,这样逐渐逐渐地把整个项目吃下来。因为基本盘如果要做一些改动的话,改动也不会太大,加个字段改个颜色,不会有太大问题,而一些周边小组件啥的,可以从主页面抽离的,比如点击一个按钮触发一个 Dialog,就完全可以用 Vue 来重写了,而且由于结构设计相对更为合理,需要对旧代码的兼容性修改,也相对会少很多,在这同时,还能顺便将原旧代码做一些隔离性优化,比如某行旧的 CSS 影响了新写的 Vue 组件,那么就可以去优化那些原先写的不那么合理的 CSS 代码。

说了这么多,那么到底该如何写呢?

思路其实很简单的:Vue 实例(例如叫 vm)上面 methods 里面的那些方法(函数),都是可以通过 vm 进行调用的。

虽然其实你甚至都可以改 vm 的数据什么的,但是这已经不是本文的目标所在了,要想让 vm 来帮忙完成一些操作,调用 vm 上的方法(函数)是最合理的选择了。

具体实现,如果你是用 Tornado 与 x-template, 大概类似下面这样:

// Vue 组件 a-component.html
<script type="text/x-template" id="a-component">
    <div>
        // 组件 html 代码
    </div>
</script>

<script type="application/javascript">
    Vue.component("a-component", {
        template: '#a-component',
        data: function () {
            return {}
        },
        props: {
            params: {},
            is_xx: {},
        },
        watch: {},
        computed: {},
        methods: {
            a_func: function (params) {
                // 组件功能代码
            }
        },
        filters: {},
    })
</script>

<style>
    // 这里还有一些 CSS,注意生效范围,要限制在组件内
</style>
// 页面 a-page.html
// 一些旧的 css 和 js 引入
// 然后引入 Vue 相关的东西
// 一些旧 html 代码,先不动它

<div id="app">

    <a-component
            :params="params"
            :is_xx="is_xx"
            @close="c_func"
    ></a-component>

</div>

// 引入 Vue 组件
{% include 'components/a-component.html' %}

<script>
    Vue.prototype.$someapi = someapi;
    let vm = new Vue({
        el: '#app',
        data: {
            params: {},
            is_xx: false,
        },
        watch: {},
        computed: {},
        methods: {
            d_func: function () {},
            b_func: function (params) {
                // 用来被外界调用的函数方法 b_func,可以带参数进来
                this.is_xx = true
                // ...
            },
            c_funx: function () {
                this.is_xx = false
            },
        },
        filters: {},
        created: function () {
            this.d_func()
        },
    })

</script>


// 旧 JS 文件 a-js.js
(function () {
    // 在某个事件的回调函数或者普通函数中调用 Vue 的功能 b_func
    e_func: function() {
        let params = {}
        vm.b_func(params)
    }
})()

这样从周边开始逐渐逐渐地吃掉旧功能代码,最后如果某天想要动基本盘,那个时候,剩下的东西或许就不会那么困难了。

只是,真的要时间。

而时间这个问题,没有标准答案,只能是说,看具体情况吧。