欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

webpack + vue 在dev和production模式下的小小区别

程序员文章站 2022-06-27 16:02:52
上周的某一天,和一位同样是前端技术极度爱好的开发者朋友聊天,他在提出了一个问题,他写的vue程序为什么在dev模式运行良好,而在production模式就直接报错了。这让我感到惊讶,还有这么神奇的事情。今就把这个历险记道给大伙听听,看能从中学习到什么? 一、还原现场 朋友在看到我的惊讶后,分分就把他 ......

     上周的某一天,和一位同样是前端技术极度爱好的开发者朋友聊天,他在提出了一个问题,他写的vue程序为什么在dev模式运行良好,而在production模式就直接报错了。这让我感到惊讶,还有这么神奇的事情。今就把这个历险记道给大伙听听,看能从中学习到什么?

一、还原现场

朋友在看到我的惊讶后,分分就把他出错的demo发给了,本地运行,事故现场重现: webpack + vue 在dev和production模式下的小小区别

二、排查嫌疑对象

既然现象是必现,要么是自己的代码出了问题,要么就是vue有Bug(心里莫名的偷笑,大伙都懂的)。

2.1 代码文件结构和源码展示

webpack + vue 在dev和production模式下的小小区别
从代码结构看,没有好说的。就是用vue-cli创建的模板开发项目,其保增加了service层而已。经过我多年来的经验,将嫌疑放到了service/index.js和components/HellowWorld.vue两个文件上。

  • service/index.js代码
let item = {};
item.result = 22222;
item.do = callBack => {
  return callBack({ result: this.a.result });
};

export default item;
  • HelloWorld.vue代码(只展示了js代码部分)
import service from "@/service/index"
export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  },
  mounted () {
    service.do(item => {
      this.msg = item.result
    })
  }
}

简要逻辑说明:

  • HelloWorld.vue引用了service/index.js文件
  • service/index.js中定义了do方法,且接受一个callback参数(使用了es6的箭头函数)
  • HelloWorld.vue在mounted方法中调用了do方法,且传入了一个函数表达式(也使用了箭头函数)

2.2 报错位置侦查

通过运行结果对比图,可以看出production模式下的运行是有报错,在达里我们放大他的报错位置:
webpack + vue 在dev和production模式下的小小区别
     看到这里,你是否有想破口大骂的冲动,怎么会this.a.result呢,这代码明显有错误吧。然后我迅速查阅了他给的demo代码,见service/index.js中的do方法,确实是怎么样写的。立刻,我略带鄙视的口吻质问我的那位朋友,你这个几年的代码白写了吧,居然能犯这么低级的错误。我直接把这个错误现场图扔给了他。   

     马上,他回了一个更为鄙视的表情,那为什么我的dev模式能正常运行呢。我立即无语且尴尬。因为确实他的dev模式运行是正常的,只有在production模式下才出的问题啊。

2.3 重点分析嫌疑对象

    经过上述的分析和折腾,我们可以初步确定问题点就在service/index.js中do方法中和this上。也就是说在dev模式下这个this.a上是有result这个属性的,而在production模式下this连这个a属性都没有了。

     作为老鸟的我,突然想到,dev模式和production模式都是运行在有sourcemap的的情况下的。这很不利用我们看编译后的代码。于是,我关闭了chrome浏览器的sourcemap功能,两种模式下代码如下:

  • dev模式下的运行代码:
    webpack + vue 在dev和production模式下的小小区别
    注意三个红框处的代码,webpack在dev模式下代码文件是没有合并成一个文件的,而是遵行commanJs规范,进行模式化加载的,而他对这个service/index.js这个模式导出时,用的名称正是a。也就是解释了在dev模式下this.a为什么会有效,他的this.a.result有值,则是因为他虽然是单文件模式化加载,但其文件中的js代码还是被bable做了转换,将箭头运算符转换为了es5可执行的代码。所以this.a.result是有值的。

 

  • production模式下的运行代码:
    webpack + vue 在dev和production模式下的小小区别
         看这段代码是否有些头大,其实从中我们只需要关心l这个变量的值,经测试发现,他的值不service/index.js中导出的对象,而是浏览器全局对象window。这就是为什么production模式下的代码不能正常运行的问题了。

三、我的推理和总结

      通过上述分析,可以大致推理出webpack在dev模式下是按照commonJs模式将各个文件独立模式化加载和引用,而Build之后,各个文件模块被合并成了一个,且对servcie/index.js进行了直接导出。再中上箭头函数对this指向的处理,就造成了this.a无效了。
     在这个demo中就算把service/index.js中的this指向处理好了,但其值还是会正常显示,原因在于vue组件中的mounted方法中也用到了箭头函数,其this的指向在运行时也会不正确。具体解释如下:

    注意,不要在实例属性或者回调函数中(如 vm.$watch('a', newVal => this.myMethod()))使用箭头函数。因为箭头函数绑定父级上下文,所以 this 不会像预想的一样是 Vue 实例,而且 this.myMethod 是未被定义的。

vue官网说明地址:https://cn.vuejs.org/v2/guide/instance.html

3.1 原因总结

  • this.a为什么可以访问,是因为webpack的dev下编译是单个文件模式化引用导致的
  • vue组件的mounted中用不可使用箭头函数,这个vue的特性,官网可见。
  • 示例代码下载