作者:Anthony Gore
原文链接:How To (Safely) Use A jQuery Plugin With Vue.js
在一个 UI 中同时使用 Vue.js 和 jQuery 并不是一个好主意。如果可能的话要避免这种情形。
不过既然你已经看到这里了,估计你是非要同时用 Vue 和 jQuery 不可了。或许是客户坚持要使用某个 jQuery 插件而你又没时间去把它用 Vue 重写。
只要你谨慎的操作,还是可以同时使用 Vue 和 jQuery 的。这篇文章里我将演示如何在 Vue 项目中使用一个 jQuery UI 的 Datepicker 插件。
而且为了小小的炫一下技,我还要在 jQuery 插件和 Vue 之间传递数据!
点击这里查看示例代码 JS Bin
jQuery UI Datepicker
同时使用 jQuery 和 Vue 存在的问题
为什么说这种用法会有潜在的风险呢?
可以说 Vue 是一个占有欲非常强的框架,她总想完全控制你交给她的这部分 DOM 元素(传递给 el 属性的元素)。如果 jQuery 修改了 Vue 管辖下的元素,例如为某个元素添加了一个 class, Vue 无法感知到这次修改,所以下次 Vue 更新 DOM 时会直接覆盖 jQuery 所做的修改。
解决方案:把 jQuery 插件包装成 Vue 组件
既然 Vue 和 jQuery 绝不会分享同一份 DOM,我们必须让 Vue 分割出一块区域交给 jQuery。
看起来把 jQuery 插件包装成 Vue 组件是个办法,因为:
- 我们可以利用 Vue 生命周期勾子来设置或卸载 jQuery 代码
- 我们可以通过组件接口使用 props 和 events 来与 Vue 应用通信
- 组件可以通过设置
v-once
避免被 Vue 更新
设置 jQuery UI Datepicker
首先要做的当然是在你的项目中引入 jQuery UI 插件。然后你只需要把 datepicker 插件绑定到一个 input
元素上:
1 | Date: <input id="datepicker"> |
选中并初始化 Datepicker
1 | $('#datepicker').datepicker(); |
Datepicker 组件
我们的 datepicker 组件很简单,template 只有一个 input
元素:
1 | Vue.component('date-picker', function() { |
1 | <div id="app"> |
注意:组件只是为 jQuery 插件提供一个包装。不要试图为组件元素添加任何 data 属性或 Vue directives 或 slots。
插件实例化
在这里我们将使用 this.$el
而不是 input
元素的 ID 来选择插件元素,每个 Vue 组件都可以像这样来访问到自己的根元素。我们这里的根元素就是 input
。
然后我们可以把这个元素引用传递给 jQuery 选择器并调用 datepicker
方法。例:$(this.$el).datepicker()
。
注意我们的代码是写在在 Vue 组件生命周期的 mounted
阶段,因为在此之前 this.$el
还是 undefined 状态。
1 | Vue.component('date-picker', function() { |
卸载
类似的,卸载 datepicker 插件同样也会用到生命周期勾子。注意我们必须在 beforeDestroy
阶段执行卸载,因为在这个阶段 input
元素仍然存在于 DOM 中,我们才能选中它(在 destroy
阶段它将变成 undefined)。
1 | Vue.component('date-picker', { |
通过 props 传递参数
为了使我们的组件可复用,最好让它能接受自定义设置参数,例如通过设置属性 dateFormat
来制定日期格式。通过 props
可以实现这一目标:
1 | Vue.component('date-picker', { |
1 | <div id="app"> |
让 jQuery 接收更新
比方说你不满足于只是传给 dateFormat
prop 一个字符串,而是把 dateFormat
设置成 Vue 根实例上的一个属性,例:
1 | var vm = new Vue({ |
1 | <div id="app"> |
这样一来,dateFormat
就变成了一个响应式的 data 属性。你可以随时在应用中更改它的值:
1 | // 更改日期显示格式 |
由于 dateFormat
prop 是 datepicker 组件的 mounted
钩子的依赖项,更新它的值将导致组件重新渲染。这就不妙了。因为 jQuery 已经在 input
元素上设置好了 datepicker 并且添加了自己的 class 和 事件监听。组件重新渲染将导致 input
元素被替换而且 jQuery 所做的设置也被重置了。
我们需要将组件设置为接收数据不重新渲染…
v-once
v-once
指令用于缓存一个包含大量静态内容的组件。导致的结果就是这个组件渲染不会受数据更新的影响。
我们的组件正好用到这个特性,因为它有效的避免了被 Vue 重新渲染。因此我们有理由相信 jQuery 将可以在应用的生命周期中无障碍的操作这个 input
元素。
1 | <div id="app"> |
从 jQuery 向 Vue 传递数据
如果我们不能在 app 中获取到 datepicker 选中的日期,那它就没啥用了。让我们来实现这个目标。
首先给 root 实例一个 date
属性:
1 | new Vue({ |
1 | <div id="app"> |
datepicker 插件有一个 onSelect
回调函数来处理选中日期的操作。通过它我们可以在组件中使用自定义事件发出数据。
1 | mounted: function() { |
在根实例上监听自定义事件并接收新数据:
1 | <div id="app"> |
1 | new Vue({ |
感谢这个 Stack Overflow 回答 提供的灵感