require.js+jQuery等改造vue

  • 背景介绍
  • 问题描述
  • 解决方案
  • 反思及说明
  • 写在最后


背景介绍

改造require.js+jQuery+bootstrap实现的一个老项目。
  原有项目使用require.js实现模块化,ui界面及操作主要是bootstrap配合jQuery,项目中部分实现代码嵌套在java代码中(大概就是这个意思),为方便后续维护,现将此项目计划改造为vue的项目。由于项目还会添加新的功能,原来的编码(实现)方式有点不合时宜——所有的页面都是通过前端发送数据请求后,后端返回的HTML代码片段:其中使用到require.js的模块加载方式,将需要的js文件,方法以及参数通过script标签包裹起来,然后返回给前端。
  由于项目中功能模块较多,无法一次性全部改造(后台接口计划改为restful形式),故只能部分模块先进行改造,以期找到一个合时宜的方案,也就形成了目前require.js+jQuery+vue的混搭方式。

问题描述

由于项目中还依然使用有大量的jQuery实现代码,在改造vue的过程中,遇到了jQuery绑定on事件时内部执行函数中变量报错的问题。

  • 点击a标签是,会弹出一个弹框,弹框有存在一个表单,一个img标签,img标签的作用是点击时方法请求,向后端请求一张图片,作为验证使用
  • 当点击按钮1时,type的值为1,此时给img绑定一个click事件,根据type值类确定控制台的输出
  • 当点击按钮2时,typede值为2,此时on事件内部的执行函数中,type的值并不会如预期一样显示为2,还包含了上一次的1,如下图,会在控制台输出两个值,显然不符合预期

示例代码:

<template>
  <div class="jquery-on-view">
    <div class="jqurey-view-title">
      <span>一个简单的测试,关于jQuery中使用on绑定事件</span>
    </div>
    <div class="jquery-view-content">
      <a href="javascript:void(0);" @click="clickFunc(1)" class="a-link-item">按钮1</a>
      <a href="javascript:void(0);" @click="clickFunc(2)" class="a-link-item">按钮2</a>
      <a href="javascript:void(0);" @click="clickFunc(3)" class="a-link-item">按钮3</a>
    </div>
    <el-dialog title="测试弹框" :visible.sync="dialogFormVisible" :close-on-click-modal="false">
      <el-form :model="form">
        <el-form-item label="名称" :label-width="formLabelWidth">
          <el-input v-model="form.name" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="描述" :label-width="formLabelWidth">
          <el-input v-model="form.desc" autocomplete="off"></el-input>
        </el-form-item>
      </el-form>
      <div class="click-view">
        <img src alt="点击图片" id="clickImg" class="click-img" />
      </div>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="dialogFormVisible = false">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import $ from 'jquery';
export default {
  name: "jqueryOn",
  data () {
    return {
      dialogFormVisible: false,
      form: {
        name: '',
        region: '',
        date1: '',
        date2: '',
        delivery: false,
        type: [],
        resource: '',
        desc: ''
      },
      formLabelWidth: '120px'
    }
  },
  methods: {
    clickFunc (type) {
      console.log(type)
      this.dialogFormVisible = true
      this.imgClickFunc(type, true)
    },
    imgClickFunc (type, isImg) {
      $('#clickImg').attr({ src: '//www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png' })
      if (isImg) {
        $('#clickImg').on('click', function () {
          if (type === 1) {
            console.log('this type is equal 1')
          } else if (type === 2) {
            console.log('this type is equal 2')
          } else {
            console.log('this type is equal 3')
          }
          $('#clickImg').attr({
            src: '//www.baidu.com/img/PCfb_5bf082d29588c07f842ccde3f97243ea.png'
          })
        })
      }
    }
  }
}
</script>

<style scoped>
.jquery-on-view {
  width: inherit;
  height: 100%;
  box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
}
.a-link-item {
  display: block;
  padding: 0 15px;
}
.click-view {
  text-align: end;
}
.click-img {
  width: 200px;
}
</style>

解决方案

  由于对jQuery不是很熟悉,查看jQuery的在线手册及其他网络资料,并没有类似问题。看着控制台中打印的输出,突然意识到,好像是每次代开弹框都会添加一个click事件,同时这个click事件是彼此独立的,故而会输出不符合预期。通过查阅jQuery在线手册,发现可以使用off()方法删除绑定的事件,这样每次打开弹框时先删除事件,然后在添加事件,就可以保持弹框打开时间段里,img标签只添加一个click事件。

imgClickFunc (type, isImg) {
      $('#clickImg').attr({ src: '//www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png' })
      if (isImg) {
        $('#clickImg').off()
        $('#clickImg').on('click', function () {
          if (type === 1) {
            console.log('this type is equal 1')
          } else if (type === 2) {
            console.log('this type is equal 2')
          } else {
            console.log('this type is equal 3')
          }
          $('#clickImg').attr({
            src: '//www.baidu.com/img/PCfb_5bf082d29588c07f842ccde3f97243ea.png'
          })
        })
      }
    }

反思及说明

 你可能会好奇,既然要使用vue改造,如何不全部使用vue的方式,给img绑定一个@click事件,用于触发相关的操作?
  事实上,按原有的实现逻辑,此处的弹框、弹框内的表单及其验证、以及数据请求等各种方法,都是在一个js文件中定义的,然后require.js会在初始化时加载这个文件,相当于将其中的方法变为全局方法,可以直接调用。改造vue时,尝试过添加@click事件,但是这会导致表单验证失效(验证不触发,原因未知),尝试使用自行封装的表单组件实现原有的表单验证及其功能,又存在拿不到必要参数的问题(原实现中所以的东西都在后台返回的HTML中)以及不确定验证规则的具体情况(给到的项目源码是打包后的的压缩文件 o(╥﹏╥)o),无奈只能选择继续使用原有的表单等,通过jQuery来添加绑定事件、属性等。
  上述的例子中,只是简单的将逻辑步骤简单的说明下,实际上业务逻辑相当多,但是由于原实现每次点击按钮时都会重新发送请求,从而获取新的HTML(旧的HTML会被删除,包括方法,元素等),故而不会存在点击时给一个标签添加了多个事件的问题。

写在最后

  关于本例,其实解决方案中还存在问题——当第一次打开弹框时,事件是已经绑定了的,但是实际上这时点击img并不会在控制台输出相应的值,在此打开弹框后才能输出符合预期的值,所以怀疑是jQuery中on()绑定事件和off()删除事件存在运行顺序活时机的差异,也尝试过使用one()绑定,但这样点击图片控制台是没有输出值的,故而放弃。
  通过先添加off()事件,在绑定on()事件,确实解决了项目中的问题,也不存在如上述示例代码中存在的第一次打开弹框时不触发输出的问题,至于原因,依旧是未知的。在此也希望有大佬可以答疑解惑,谢谢啦!
  此项目依旧确定要做整体迁移,后端接口要进行优化,按照restful的方式,同时还要将部分嵌入java中的前端代码修改,整体改造为vue的方式,所以此处只是临时的解决方案,后续整体迁移还会对此处进行调整优化。当前目标的是测试功能正常后保证项目的运行,毕竟路要一步步走、饭得一口口的吃!
tips:
    点滴记录,汇聚江河