阅读 96

学习使用Vue必须掌握的开发技巧

动态指令参数

将指令参数动态传递给组件。 假设你有一个按钮组件,并且在某些情况下想监听单击事件,而在其他情况下想监听hover事件。这就是动态参数的使用场景了:

<template>
	...
	<aButton @[someEvent]="handleSomeEvent()" />...
</template>
<script>
  ...
 data(){
    return{
      ...
      // 事件类型
      someEvent: someCondition ? "click" : "hover"
    }
  },
  methods: {
    handleSomeEvent(){
      // 事件逻辑
    }
  }  
</script>
复制代码

     

相同路由的组件切换不刷新问题

开发人员经常遇到的情况是,多个路由解析为同一个Vue组件。问题是,Vue出于性能原因,默认情况下共享组件将不会重新渲染,如果你尝试在使用相同组件的路由之间进行切换,则不会发生任何变化。

 const routes = [
  {
    path: "/a",
    component: MyComponent
  },
  {
    path: "/b",
    component: MyComponent
  },
]; ```
复制代码

如果你的目的是:路由切换时重新渲染这些组件,则可以通过在 router-view 组件中提供 :key 属性来实现。 原理是:key的值不一样,在下一次渲染时会认为这两个是不一样的组件,所以会再次渲染

<template>
	<router-view :key="$route.path"></router-view>
</template>
复制代码

     

侦听子组件的生命周期

可以使用 $on 来监听子组件的生命周期钩子 生命周期钩子发出自定义事件这一事实意味着父组件可以监听其子级的生命周期钩子。 它将使用正常模式来监听事件(@event),并且可以像其他自定义事件一样进行处理。 <child-comp @hook:mounted="someFunction" />

     

样式穿透

在开发中修改第三方组件样式是很常见,但由于 scoped 属性的样式隔离,组件内对第三方UI库(比如element UI)的更改不起作用。这里就可以样式穿透。 注意:在css预处理器(less/sass)中使用才生效。

我们可以使用 >>> 或 /deep/ 解决这一问题:

外层的第三方UI库

<style>
.el-checkbox {
  display: block;
  font-size: 26px; .el-checkbox\_\_label { font-size: 16px;
  }
}
</style>
复制代码

在内名前加一个/deep/,即可以完成对三方样式的修改,并使其生效。并且对全局样式没有影响。

<style>
/deep/ .el-checkbox {
  display: block;
  font-size: 26px; .el-checkbox\_\_label { font-size: 16px;
  }
}
</style>
复制代码

render函数的作用:   Vue推荐在绝大数情况下实用模板创建你的HTML,然而当某些特殊场景使用template创建HTML会显得代码繁琐冗长,如根据一个为level的prop动态创建标题的 组件,你可能想到这样写:

//Parent.vue
<Child :level="1" >hello world</Child>
//Child.vue
<template>
  <div>
    <h1 v-if="level===1">
      <slot></slot>
    </h1>
    <h2 v-if="level===2">
      <slot></slot>
    </h2>
    <h3 v-if="level===3">
      <slot></slot>
    </h3>
    <h4 v-if="level===4">
      <slot></slot>
    </h4>
    <h5 v-if="level===5">
      <slot></slot>
    </h5>
    <h6 v-if="level===6">
      <slot></slot>
    </h6>
   </div>
</template>

<script>
  export default{
    props:["level"]
  }
</script>
复制代码

显然template用在这里不合适,我们来看一下用render函数重写这个组件

//Parent.vue
<template>
  <div>
    <Child :level="1">Hello World</Child> 
  </div>
</template>
<script>
  export default {
    component:{
      child:{
        render(creatElement){
          return creatElement(
            'h'+this.level, //标签名称
            {
              style:{
                color:'#f60'
              }
            }, //标签中的属性
            this.$slots.default //子节点数组
          ) 
        },
        props:{
          level:{
            required:true,
            type:Number
          }
        }
      }
    }
  }
</script>
复制代码

     

强大的render函数

 Vue推荐在绝大数情况下实用模板创建你的HTML,然而当某些特殊场景使用template创建HTML会显得代码繁琐冗长, 比如根据一个为level的prop动态创建标题的 组件,你可能想到这样写:

//Parent.vue
<Child :level="1" >hello world</Child>
//Child.vue
<template>
  <div>
    <h1 v-if="level===1">
      <slot></slot>
    </h1>
    <h2 v-if="level===2">
      <slot></slot>
    </h2>
    <h3 v-if="level===3">
      <slot></slot>
    </h3>
    <h4 v-if="level===4">
      <slot></slot>
    </h4>
    <h5 v-if="level===5">
      <slot></slot>
    </h5>
    <h6 v-if="level===6">
      <slot></slot>
    </h6>
   </div>
</template>

<script>
  export default{
    props:["level"]
  }
</script>
复制代码

显然template用在这里不合适,我们来看一下用render函数重写这个组件

//Parent.vue
<template>
  <div>
    <Child :level="1">Hello World</Child> 
  </div>
</template>
<script>
  export default {
    component:{
      child:{
        render(creatElement){
          return creatElement(
            'h'+this.level, //标签名称
            {
              style:{
                color:'#f60'
              }
            }, //标签中的属性
            this.$slots.default //子节点数组
          ) 
        },
        props:{
          level:{
            required:true,
            type:Number
          }
        }
      }
    }
  }
</script>
复制代码

createElement本身也是一个函数,且有三个参数:    1. 要创建的节点: (必填) { string | object | function },可以是要创建的HTML标签名称,也可以是组件对象,也可以是解析上述任何一种的一个异步函数    2. 标签中的属性: (可选),详细解释如下

 { 
  // 和`v-bind:class`一样的 API
  // 接收一个字符串、对象或字符串和对象组成的数组
  'class': {
    foo: true,
    bar: false
  },
  // 和`v-bind:style`一样的 API
  // 接收一个字符串、对象或对象组成的数组
  style: {
    color: 'red',
    fontSize: '14px'
  },
  // 正常的 HTML 特性
  attrs: {
    id: 'foo'
  },
  // 组件 props
  props: {
    myProp: 'bar'
  },
  // DOM 属性
  domProps: {
    innerHTML: 'baz'
  },
  // 事件监听器基于 `on`
  // 所以不再支持如 `v-on:keyup.enter` 修饰器
  // 需要手动匹配 keyCode。
  on: {
    click: this.clickHandler
  },
  },
  // 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
  // 赋值,因为 Vue 已经自动为你进行了同步。
  directives: [
    {
      name: 'my-custom-directive',
      value: '2',
      expression: '1 + 1',
      arg: 'foo',
      modifiers: {
        bar: true
      }
    }
  ],


} 
复制代码

   3. 子虚拟节点: (可选) { string | array } 由createElement()构建而成,可以使用字符串来生成“文本虚拟节点”      

长列表性能优化

我们知道vue会通过object.defineProperty对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要vue来劫持我们的数据。 在大量数据展示的情况下,这能够很明显的减少组件初始化的时间,那如何禁止vue劫持我们的数据呢? 可以通过object.freeze方法来冻结一个对象,一旦被冻结的对象就再也不能被修改了。

export default {
  data: () => ({
    users: {}
  }),
  async created() {
    const users = await axios.get("/api/users");
    this.users = Object.freeze(users);
  }
};
复制代码

另外需要说明的是,这里只是冻结了users的值,引用不会被冻结,当我们需要reactive数据的时候,我们可以重新给users赋值。

export default {
  data: () => ({
    users: []
  }),
  async created() {
    const users = await axios.get("/api/users");
    this.users = Object.freeze(users);
  },
  methods:{
    // 改变值不会触发视图响应
    this.users[0] = newValue
    // 改变引用依然会触发视图响应
    this.users = newArray
  }
};
复制代码

     

组件中使用定时器及销毁问题

如果我们在页面A中使用了一个定时器,当从页面A跳转到页面B时,如果不手动清除这个定时器,那么它仍旧会执行,这不是我们所期望的。 通常能想到的常规解决办法就是,在data属性中定义一个timer,在代码中启动定时器,然后在组件销毁的时候清除定时器。具体代码如下:

data(){
    return{
        timer:null
    }
},
methods:{
    onStartTimer(){
        this.timer = setInterval( () => {
            // 执行一些操作
        }, 1000)
    }
},
beforeDestroy() {
    clearInterval(this.timer);        
    this.timer = null;
}
复制代码

但是这里有潜在的问题:

  1. 它需要在这个组件实例中保存这个timer,其实最好的做法应该是只有局部函数里面可以访问到它,而不是全局。 不然其他其他可能会误碰到timer

解决办法: Vue官方给出的解决办法是:在定义timer的时候使用$once指令监听beforeDestroy这个钩子函数。具体代码为:

  methods:{
        onStartTimer(){
            const timer = setInterval( () => {
                // 执行一些操作
            }, 1000)
            this.$once('hook:beforeDestroy', () => {            
                clearInterval(timer);                                    
            })
        }
    },
复制代码

这样就解决了上面所列的两个问题。类似的这种在离开页面时需要销毁的组件都可以采用此方法

文章分类
前端
文章标签