gpt4 book ai didi

javascript - 有没有一种很好的方法可以将基于 JQuery 的小部件包装到一个可以在 Vue.js 中轻松使用的模块中?

转载 作者:行者123 更新时间:2023-12-04 08:31:54 27 4
gpt4 key购买 nike

我的一些同事使用 Vue.js 开始了一个相当复杂的 Web 应用程序。他们希望能够使用我过去使用 JQuery 从头开始​​制作的一些小部件,因为重新实现它们需要大量的精力/时间。
我知道如果您小心的话,可以安全地将 JQuery 与 Vue.js 一起使用,但是我能够找到的信息似乎归为相当模糊的博客文章,而且我的同事已经通知我,他们正在努力弄清楚如何去做吧。因此,我正在考虑是否可以找到一种方法将我的小部件很好地包装到一个可移植的跨框架库中(对于可以在 Vue.js 中使用的初学者)。例如,类似于人们如何创建提供跨语言 API 的绑定(bind)。理想情况下,它应该让人们很容易在 Vue.js 中使用它,并且应该消除潜在陷阱的危险。这样做有什么问题吗,是否有任何现有的工作可以利用,或者人们这样做的惯用方式?
对于添加的上下文,目前,小部件有一个接口(interface),其中包括一个构造函数(您可以在其中传递将附加到的父 DOM 元素的 id)、一个配置函数,并且它还在更改时发出多个信号/事件(虽然,这些可以由定期检查其状态的函数替换)。

最佳答案

就创建可移植和跨框架库而言,我认为 jQuery 只是一个依赖项,它允许您创建某些元素并执行某些任务,您可以根据目标框架的要求拦截和/或修改这些元素。因此,您实际上是在围绕它创建一个包装器 组件 ,因为当今排名前三的 JavaScript 框架(React、Vue、Angular)都是基于组件的。
主要区别之一(简单地说)是: react 系统与 DOM 操作。
现在,谈论将 jQuery 插件移植到 Vue — 我不是这两个库的专家,但我自己来自 jQuery,我想说这就像在 Vue 组件内部 data 上保留对小部件/插件实例的引用一样简单和/或 props 并让它有选择地公开相应的方法。方法公开部分是可选的原因与区分一个库与另一个库的原因相同——Vue 在库和框架之间扩展时更加通用。
在 jQuery 中,您将创建一个对象的实例并将其传递给它的公共(public)方法用法;而在 Vue 中,除了根实例(您是 could ,但您通常不必)之外,您不会显式创建实例——因为组件本身 是(内部构造的)实例 。维护其状态和数据是组件的责任;兄弟和/或父组件通常没有 直接 访问它们。
Vue 和 jQuery 的相似之处在于它们都支持状态/数据同步。使用 jQuery,很明显,因为所有引用都在全局范围内;对于 Vue,可以使用 v-model .sync 修饰符(在 Vue 3 中替换为 v-model 上的参数)。此外,他们还使用稍微不同的方法进行事件订阅。
让我们使用 jQuery Autocomplete widget 并为其添加一些 Vue 支持。我们将关注 3 件事(选项、事件和方法),并以它们各自的 3 项作为示例和比较。我不能在这里涵盖所有内容,但这应该会给你一些基本的想法。
设置:jQuery
为了符合您的规范,我们假设这个小部件/插件是 new 范围内的 window 类。
在 jQuery 中,您将编写以下内容(在 document 准备好或在关闭 <body> 标记之前包装在 IIFE 中):

var autocomplete = new Autocomplete({
source: [
'vue',
'react',
'angular',
'jquery'
],
appendTo: '#autocomplete-container',
disabled: false,

change: function(event, ui) { },
focus: function(event, ui) { },
select: function(event, ui) { }
});

// And then some other place needing manual triggers on this instance
autocomplete.close();

var isDisabled = autocomplete.option('disabled');

autocomplete.search('ue'); // Matches 'vue' and 'jquery' ;)
在父作用域中的某处预定义或动态创建目标元素:
<input type="search" class="my-autocomplete" />
移植到 Vue
由于您没有提到正在使用的任何特定版本的 Vue,我将假设 Macross(最新稳定版本:2.6.12,ATTOW)带有 ES 模块;否则,请尝试 ES 模块 compatible build
对于 Vue 中的这个特殊用例,我们想在 mounted 钩子(Hook)中实例化这个插件,因为这是我们的目标元素将被创建并可以真正构建的地方。在图表 here 中了解有关生命周期 Hook 的更多信息。
创建组件: Autocomplete.vue
<template>
<!--
Notice how this `input` element is added right here rather than we requiring
the parent component to add one, because it's now part of the component. :)
-->
<input type="search" class="my-autocomplete" />
</template>

<script>
export default {
// Basically, this is where you define IMMUTABLE "options", so to speak.
props: {
source: {
type: Array,
default: () => []
},

disabled: {
type: Boolean,
default: false
}
},

// And this is where to prepare and/or specify the internal options of a component.
data: () => ({
instance: null
}),

mounted() {
// `this` here refers to the local Vue instance

this.instance = new Autocomplete({
source: this.source,
disabled: this.disabled,
appendTo: this.$el // Refers to the `input` element on the template,

change: (event, ui) => {
// You can optionally pass anything in the second argument
this.$emit('change', this.instance);
},

focus: (event, ui) => {
this.$emit('focus', this.instance, event);
},

select: (event, ui) => {
this.$emit('select', this, event, ui);
}
});
},

methods: {
close() {
this.instance.autocomplete('close');
},

getOption(optionName) {
return this.instance.autocomplete('option', optionName);
},

search(keyword) {
this.instance.autocomplete('search', keyword);
}
}
}
</script>
使用组件: Parent.vue(或其他)
<template>
<div class="parent">
<autocomplete
ref="autocomplete"
:source="items"
:disabled="disabled"
@change="onChange"
@focus="onFocus"
@select="onSelect">
</autocomplete>
</div>
</template>

<script>
import Autocomplete from 'path/to/your-components/Autocomplete.vue';

export default {
data: () => ({
items: [
'vue',
'react',
'angular',
'jquery'
],
disabled: false
}),

methods: {
onChange() {
},

onFocus() {
},

onSelect() {
}
},

mounted() {
// Manually invoke a public method as soon as the component is ready
this.$refs.autocomplete.search('ue');
},

components: {
Autocomplete
}
}
</script>
我们还没有到那里!我特意省略了上面例子的 "two-way binding" 部分,让我们现在仔细看看。但是,这一步是可选的,只有当您需要在组件(父 ↔ 子)之间同步数据/状态时才应该执行,例如:您在组件上有一些逻辑,当某些值获得时,将输入的边框颜色设置为红色进入。现在,由于您正在修改作为 prop 绑定(bind)到该组件的父状态(例如 invaliderror ),您需要通过 $emit -ting 新值通知他们其更改。
因此,让我们进行以下更改(在同一个 Autocomplete.vue 组件上,为简洁起见,省略了其他所有内容):
{
model: {
prop: 'source',
event: 'modified' // Custom event name
},

async created() {
// An example of fetching remote data and updating the `source` property.
const newSource = await axios.post('api/fetch-data').then(res => res.data);

// Once fetched, update the jQuery-wrapped autocomplete
this.instance.autocomplete('option', 'source', newSource);

// and tell the parent that it has changed
this.$emit('modified', newSource);
},

watch: {
source(newData, oldData) {
this.instance.autocomplete('option', 'source', newData);
}
}
}
我们基本上是 watch -ing“急切地”进行数据更改。如果愿意,您可以使用 $watch 实例方法懒惰地执行此操作。
父端所需的更改:
<template>
<div class="parent">
<autocomplete
ref="autocomplete"
v-model="items"
:disabled="disabled"
@change="onChange"
@focus="onFocus"
@select="onSelect">
</autocomplete>
</div>
</template>
这将启用上述双向绑定(bind)。你可以对其他你需要“ react ”的 Prop 做同样的事情,比如这个例子中的 disabled Prop ——只是这次你会使用 .sync 修饰符;因为在 Vue 2 中,不支持多个 v-model。 (如果你还没有走得太远,我建议你一直使用 Vue 3 🙂)。
最后,您可能需要注意一些警告和常见问题:
  • 由于 Vue 异步执行 DOM 更新,因此它可能正在处理一些在 next event loop "tick" 之前不会生效的内容,请在 Async Update Queue 上阅读更多内容。
  • 由于 JavaScript 的限制,存在 Vue 无法检测到的更改类型 。但是,有 ways to circumvent them 可以保持 react 性。
  • this 对象为 undefinednull 或在嵌套方法或外部函数中引用时出现意外情况。转到 the docs 并搜索“箭头函数”以获取完整说明以及如何避免遇到此问题。

  • 我们为自己创建了一个 Vue 移植版本的 jQuery Autocomplete!同样,这些只是帮助您入门的一些基本想法。
    现场演示

    const Autocomplete = Vue.extend({
    template: `
    <div class="autocomplete-wrapper">
    <p>{{label}}</p>
    <input type="search" class="my-autocomplete" />
    </div>
    `,

    props: {
    source: {
    type: Array,
    default: () => []
    },

    disabled: {
    type: Boolean,
    default: false
    },

    label: {
    type: String
    }
    },

    model: {
    prop: 'source',
    event: 'modified'
    },

    data: () => ({
    instance: null
    }),

    mounted() {
    const el = this.$el.querySelector('input.my-autocomplete');

    this.instance = $(el).autocomplete({
    source: this.source,
    disabled: this.disabled,

    change: (event, ui) => {
    // You can optionally pass anything in the second argument
    this.$emit('change', this.instance);
    },

    focus: (event, ui) => {
    this.$emit('focus', this.instance, event);
    },

    select: (event, ui) => {
    this.$emit('select', this, event, ui);
    }
    });
    },

    methods: {
    close() {
    this.instance.autocomplete('close');
    },

    getOption(optionName) {
    return this.instance.autocomplete('option', optionName);
    },

    search(keyword) {
    this.instance.autocomplete('search', keyword);
    },

    disable(toState) {
    this.instance.autocomplete('option', 'disabled', toState);
    }
    },

    watch: {
    source(newData, oldData) {
    this.instance.autocomplete('option', 'source', newData);
    },

    disabled(newState, oldState) {
    this.disable(newState);
    }
    }
    });

    new Vue({
    el: '#app',

    data: () => ({
    items: [
    'vue',
    'react',
    'angular',
    'jquery'
    ],
    disabled: false
    }),

    computed: {
    computedItems: {
    get() {
    return this.items.join(', ');
    },
    set(val) {
    this.items = val.split(', ')
    }
    }
    },

    methods: {
    onChange() {
    // Do something
    },

    onFocus() {},

    onSelect(instance, event, ui) {
    console.log(`You selected: "${ui.item.value}"`);
    }
    },

    components: {
    Autocomplete
    }
    })
    #app {
    display: flex;
    justify-content: space-between;
    }

    #app > div {
    flex: 0 0 50%;
    }
    <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" />
    <link rel="stylesheet" href="/resources/demos/style.css" />

    <script src="https://vuejs.org/js/vue.min.js"></script>
    <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>

    <div id="app">
    <autocomplete
    v-model="items"
    :disabled="disabled"
    label='Type something (e.g. "ue")'
    @change="onChange"
    @focus="onFocus"
    @select="onSelect">
    </autocomplete>

    <div>
    <p>Edit this comma-separated list of items and see them reflected on the component</p>

    <textarea
    v-model.lazy="computedItems"
    cols="30"
    rows="3">
    </textarea>
    </div>
    </div>

    附言如果这些小部件实际上在全局 window 范围 中,您正在使用 ESLint,您需要确保它们被指定为全局变量;否则, no-undef 规则将对已访问但未在同一文件中定义的变量发出警告。解决方法见 this post
    P.P.S.如果您需要将它们作为插件发布,请参阅: Writing a Plugin(别担心,不需要太多额外的工作)。

    关于javascript - 有没有一种很好的方法可以将基于 JQuery 的小部件包装到一个可以在 Vue.js 中轻松使用的模块中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64979260/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com