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

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

一、还原现场

朋友在看到我的惊讶后,分分就把他出错的demo发给了,本地运行,事故现场重现: image

二、排查嫌疑对象

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

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

image
从代码结构看,没有好说的。就是用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模式下的运行是有报错,在达里我们放大他的报错位置:
image
     看到这里,你是否有想破口大骂的冲动,怎么会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模式下的运行代码:
    image
    注意三个红框处的代码,webpack在dev模式下代码文件是没有合并成一个文件的,而是遵行commanJs规范,进行模式化加载的,而他对这个service/index.js这个模式导出时,用的名称正是a。也就是解释了在dev模式下this.a为什么会有效,他的this.a.result有值,则是因为他虽然是单文件模式化加载,但其文件中的js代码还是被bable做了转换,将箭头运算符转换为了es5可执行的代码。所以this.a.result是有值的。

 

  • production模式下的运行代码:
    image
         看这段代码是否有些头大,其实从中我们只需要关心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的特性,官网可见。
  • 示例代码下载
posted @ 2018-04-21 16:41  小龙女先生  阅读(917)  评论(1编辑  收藏  举报