[英] 教你在 Vue.js 中使用 jQuery 插件的正确姿势

3,518 阅读3分钟
原文链接: gambardella.info

Working with Vue can be awesome. But you might know that it can also give you headaches, when you try to combine it with jQuery plugins or other libraries.

The problem is that jQuery works fundamentally different than Vue. Vue renders everything based of the component’s core data. jQuery is mostly meant to do simple click handling and has powerful abilities manipulate the DOM.

When I looked for help myself I have found some unsatisfying solutions where components did do all the initialization work.
Let me show you how I would combine Vue.js and jQuery.

What’s the goal?

In most cases you can omit using jQuery and find a simple Vue based solution to your problem. Modals, sliders and so on are pretty simple with a Vue component and CSS.

So the goal is to use powerful jQuery plugins that cannot be written quickly in Vue.

We will …

  • … use Vue directives to build a bridge to jQuery.
  • … initialize the plugin when the element is attached.
  • … destroy it when the element is detached.
  • … send events to notify the component.
  • … receive events from the component and pass them to the plugin.

Tutorial Time

I’ve picked Fengyuan Chen’s cropper plugin because it’s a very well written jQuery plugin that you probably cannot rewrite in the next 60 min. using Vue.js alone.
It’s complex and demonstrates how Vue.js can interact with it.

DEMO: vue-jquery-cropper-demo.firebaseapp.com/

I’m going to describe how to get that working from the very beginning. Skip the parts you have already done.

Create the project

# install vue-cli
$ npm install -g vue-cli
# create a new project using the "webpack" boilerplate
$ vue init webpack vue-cropper

? Project name "vue-cropper"
? Project description "A Vue.js project"
? Author "Christian Gambardella "
? Use ESLint to lint your code? "Yes"
? Pick an ESLint preset "Standard"
? Setup unit tests with Karma + Mocha? "No"
? Setup e2e tests with Nightwatch? "No"

$ cd vue-cropper
$ npm install

Congratulations you have a Vue.js project.

Install jQuery and cropper.js

# install jQuery & cropper
$ npm install jquery cropper --save

Configure Webpack for jQuery and Vue directives

Add the jQuery source and directives folder to the Webpack alias map.
Normally Webpack would have included the compiled version of jQuery. The recommendation is to have the source included instead.

You can see that the Vue webpack template has added the components folder already. I usually add a bunch of other folders like directives, mixins, etc. For this example we only need to add directives.

This will help us importing dependencies without having to know the exact path. This is also beneficial when you refactor your app along the way. You don’t need to manage relative paths.

Edit build/webpack.base.conf.js and add the highlighted lines.

resolve: {
  extensions: ['', '.js', '.vue'],
  fallback: [path.join(__dirname, '../node_modules')],
  alias: {
    'src': path.resolve(__dirname, '../src'),
    'assets': path.resolve(__dirname, '../src/assets'),
    'components': path.resolve(__dirname, '../src/components'),
    'jquery': path.resolve(__dirname, '../node_modules/jquery/src/jquery'),
    'directives': path.resolve(__dirname, '../src/directives')
  }
},

Don’t forget to add a comma at the end of the components line.

Prepare the App Component

I’m going to start with the component because I believe it’s easier to understand the beauty and simplicity of the component. You can start writing directives later immediately because you then know how to use them in components.

Replace the template in src/App.vue

Replace the script in src/App.vue

A few important things to notice here.

  • We don’t import jQuery nor do we have to deal with its initialization in the component.
  • The Cropper’s options are available as raw data.
  • The Cropper will be initialized when this gets rendered and will be destroyed when the view isn’t visible anymore.
  • The directive’s name will be available in the template as a kebab cased version. MyCropper would become my-cropper.
    To initialize it, we have to add v-my-cropper to an element in the component’s template.

Create the Cropper directive

This is the heart of this tutorial. We’re going to create a Cropper directive that deals with the lower level code DOM manipulation. In this case we’re going to initialize the cropper.

Custom directives provide a mechanism for mapping data changes to arbitrary DOM behavior.
http://vuejs.org/guide/custom-directive.html

Create src/directives/Cropper.js

import jQuery from 'jquery'
import 'cropper'

export default {
  deep: true,

  bind () {},

  update (options) {
    // Destroy in case it has been initialized already.
    jQuery(this.el).cropper('destroy')
    // Initializing directly after destroying
    // didn't work. Wrapping it in a setTimeout
    // seems to do the trick.
    setTimeout(() => {
      jQuery(this.el).cropper(options)
    }, 0)
  },

  unbind () {
    jQuery(this.el).cropper('destroy')
  }
}

The very core of every Vue.js directive is a plain object with bind, update and unbind functions.

  • bind: Will be called once the element is attached to the document.
  • update: Called once after bind and every time when the wired data changes. In this case cropOptions is an object. The Vue.js directive needs to know if it is an object. This is why we need to add deep: true in this case.
  • unbind: Called when the DOM element is about to be removed. We destroy everything here and remove all event listeners.

There will be a couple of changes in Vue 2. The vm instance is going to be passed as a function argument and won’t be set to this.

The directive’s update function will only be called on update and not initially after bind.