Vuejs渡劫系列一:日常开发中必须掌握的细节(keng)

1,193 阅读6分钟

日常开发中必须掌握的细节(keng)

Vuejs渡劫系列的首篇文章,主要针对一年多日常开发中的,对Vuejs的一些技术细节的总结,新手看了可保平安,老鸟大婶看了可温故知新!

从开始观望Vue 1.0到项目迁移到Vue 2.x,到现在项目改造成Vue-SPA已经有一年的时间,不得不说Vue带来了很多前所未有的新鲜感和卓越的开发效率,但Vue(或者说MVVM框架)总不像jQuery来的那样纯粹简单,因此,一些未知的新鲜感总会隐藏着未知的劫难(keng),因此该系列取名为 Vuejs渡劫———该系列不漫谈每个API,只是对博主开发过程中遇到问题和Vue学习需要注意的点进行简要描述。

下面的注意点,是本人日常工作遇到过的疏忽或者一些比较要注意的Vue细节,不一定是坑,但是总会需要值得注意。注意的点会根据项目的推进 不定期更新 喜欢的朋友们可以保持关注!

安装

谨慎使用命令行工具(CLI)

Vue-Cli是官方提供的命令行工具,个人不建议上手就用,因为其需要你对Node.js,Webpack等工具有相当一定程度的了解。个人先后使用过seajs/spm3,require.js,webpack 1.x等工具,但也无法说精通loaders的机制,而且CLI集成了Vue-loaders和其他loaders的依赖关系,错综复杂,新手可以通过script link的方式或者NPM的方式去使用:

script

<script  type="text/javascript" src="https://unpkg.com/vue@2.4.2/dist/vue.js"></script>

NPM

npm install vue

全局 API

Vue.extend 创建“子类”

extend 是一个很实用的方法,尤其是在对.vue单文件进行unit-test(单元测试)的时候。由于单文件模式下,一般只会在 App.vue 的入口处对vue实例,各个子类的模块,通过import或者require的方式进行加载。

通过利用 Vue.extend 创建子类,并挂载到Dom元素上:

import Vue from 'vue'
import Hello from '@/components/Hello'

describe('Hello.vue', () => {
  it('should render correct contents', () => {
    const Constructor = Vue.extend(Hello)
    const vm = new Constructor().$mount()
    expect(vm.$el.querySelector('#J_main div').textContent)
      .to.equal('Welcome to Your Vue.js App')
  })
})

nextTick: DOM更新完成回调

有时候,我们调用的方法需要DOM落地之后才可以执行,但是我们并不知道更改了data上的数据之后,DOM什么时候才就绪,这个时候我们就需要用 Vue.nextTickthis.$nextTick,其中后者为实例对象上的方法。

该方法可以去处理数据变更后,执行的DOM操作,如chart的绘制,一些jQuery基于DOM的插件等,下面是一个数据变更后,c3.js画图的应用:

this.trendChart.status = 'hasData';
this.$nextTick(function() {                  
    c3.generate({
        bindto: '#J_trend',
        size: {
              height: 350
            }
            ……
        });
});

选项/数据

data选项的一些禁忌

data选项是Vue定义中的一个最为重要的部分,虽然data是看似是简单json对象,但在使用的过程依旧有些需要注意的地方:

单文件中的使用

值得注意的是,单文件中,data 严格要求是一个 function,一个返回JSON对象的函数;

data的hook处理

博主在做一款数据平台的产品过程中,每个页面都拥有相同的data属性,重复定义,不仅会导致代码量提高,同时在vue单文件中也不够直观,此时我们可以通过外部的hook函数,进行初始化,在对外导出:

import hook from '@/components/hook';
var opts = {
  data: {
    //your datas
  },
  ...rest
};
opts = hook(opts);
export default opts;

不要对data使用箭头函数

如data: () => { return { a: this.myProp }})。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例(同理 methodswatch 的观察函数定义也是),this.myProp 将是 undefined

使用computed 获取vuex公共变量

在使用 vuex 对状态进行管理的时候,在获取state上的值时,请在 computed 选项中声明获取,否则数据同步会有问题:

computed :{
      viewType () {
        return this.$store.state.user.viewType
      }
    }

数组更新问题

由于Vue使用的是defineProperty的方式进行监测变化,因此Vue无法监测到数组以下的变动:

1.当你利用索引直接设置一个项时,例如: vm.items[indexOfItem] = newValue

2.当你修改数组的长度时,例如: vm.items.length = newLength

针对第一个问题,可以通过以下方式触发更新:

// Vue.set
  Vue.set(example1.items, indexOfItem, newValue)
// Array.prototype.splice
  example1.items.splice(indexOfItem, 1, newValue)

针对第二个问题,可以通过以下方式触发更新

example1.items.splice(newLength)

二维(N维)数组更新方式

如果我们需要更新一个二维数组的某个变量的值,如需要更新 items[i][j]数组下的name属性,我们需要把更改的 index 参考数组更新第一个问题的方式设置为最前面的维度数组索引,即

// Vue.set
let newVal = items[i][j];
    newVal[i][j].name = "new";
Vue.set(example1.items, i, newVal);

对象更新问题

如题,博主说的并不是你的对象更新[窃笑],这里说的是Vue中data的一个对象更新需要注意的点:我们不能后续异步的建立新的对象属性,如我们data中,有个man对象只有 name 的属性:

man:{
  name: "momo"
}

此时,如果我们需要添加一个age的属性,我们不能直接写 this.man.age = 26 而是需要通过 set方法进行增加监测对象:

Vue.set(vm.man, 'age', 26);

this.$set(this.man, 'age', 26);

除此以外,如果你执意要用 this.man.age = 26 这个方式更改,你可以通过提前配置这个属性为缺省值,如:

man:{
  name: "momo",
  age: ""
}

组件

组件props命名

HTML 特性是不区分大小写的。所以,当使用的不是字符串模板,camelCased (驼峰式) 命名的 prop 需要转换为相对应的 kebab-case (短横线隔开式) 命名:

Vue.component('child', {
  // camelCase in JavaScript
  props: ['myMessage'],
  template: '<span>{{ myMessage }}</span>'
})
<!-- kebab-case in HTML -->
<child my-message="hello!"></child>

单向数据流

Vue使用的是单向数据流,提倡 props down,event up方式,通过使用Props传递数据:

  Vue.component('child', {
  // 声明 props
  props: ['message'],
  // 就像 data 一样,prop 可以用在模板内
  // 同样也可以在 vm 实例中像“this.message”这样使用
  template: '<span>{{ message }}</span>'
})
<child message="hello!"></child>

子组件通过 $emit自定义方式,去唤起父组件变更数据:

this.$emit('say', message);
<child @say="toSay" message="hello!"></child>

通过 @say 对其自定义事件进行侦听,事件触发后,执行父组件的toSay()去驱动数据更新

methods:{
  toSay () {
    this.message = otherVal;
  }
}

点击子组件触发方法

如你使用了vue-router,并希望在 <router-link></router-link> 被点击的时候,调用当前的方法

<router-link v-on:click.native="doSomething"></router-link>

指令

v-for多层循环的时候,注意对象和索引值的命名

如你写了一个三层循环的组件,第一层为(items,index)那么第二层为 (item, idex) 然后第三层为 (itm, idx),…… [这么长,我哭了T T]

要注意,每层的变量都是当前整个data的全局环境,因此每一层的对象和索引值都要有所不同,否则变量名被覆盖,就无法取到正确的对象或索引。

不要通过 v-model与 表单控件的checked属性绑定

如题,博主曾经通过v-model的值 true/false去控制checked切换, 这里已经做了很多次尝试,最后还是没有好的成效,最后通过模拟 CheckBox或者 radio的方式解决。

增加v-cloak指令,免除渲染过程出现 的出现

由于数值渲染会依赖Vuejs和其他组件加载的后,才会正确显示,才过程中,如果html页面中通过表达式的方式引用,网速过慢的情况下,可能会看到 的情况,可以参考官方写入一个 v-cloak 指令,并对指令增加属性样式:

[v-cloak] {
  display: none;
}
<div v-cloak>
  {{ message }}
</div>

Vue在数据准备完成后,会主动移除 v-cloak 这个指令(属性),数据就会在正确的时间被正确地显示了。

总结

由于博主人老了,脑瓜er不灵光了,有些遇到的坑被填过就忘了,后续想起来,或者遇到其他问题,会继续更新文章,也欢迎小伙伴或者大婶们,多多指正和关注后续的系列文章。