1.v-if 和 v-show 的使用场景

v-if真正 的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

v-show 就简单得多, 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 display 属性进行切换。

所以,v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。

|

<template>
  <div class="home">
    <a-com v-if="acomshow"></a-com>
    <b-com v-show="bcomshow"></b-com>
    <button @click="switchAcom">组件a</button>
    <button @click="switchBcom">组件b</button>
  </div>
</template>


<script>
const path = require('path')
let files = require.context('../components', false, /\.vue$/)
let components = {}
files.keys().forEach(key => {
  const name = path.basename(key, '.vue')
  components[name] = files(key).default || files(key)
})
console.log(components)
export default {
  name: 'Home',
  components,
  data(){
    return {
      acomshow:false,
      bcomshow:false,
    }
  },
  methods:{
    switchAcom() {
      console.log('切换a显示',this.acomshow = !this.acomshow)
    },
    switchBcom() {
      console.log('切换b显示',this.bcomshow = !this.bcomshow)
    }
  }
}
</script>
// 组件a
<template>
  <div class="a">
    <h1>这是 a 组件</h1>
  </div>
</template>


<script>
export default {
  name: 'aCom',
  created(){
    console.log('acreate触发')
  }
}
</script>
// 组件b
<template>
  <div class="b">
    <h1>这是 b 组件</h1>
  </div>
</template>
<script>
export default {
  name: 'bCom',
  created(){
    console.log('bcreate触发')
  }
}
</script>

2、computed 和 watch 的使用场景

computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;

watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;

运用场景:

  • 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
  • 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作,限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

computed:

|

<template>
  <div class="b">
    <h1>这是 b 组件</h1>
    <h2>{{text}}</h2>
  </div>
</template>
<script>
export default {
  name: 'bCom',
  data() {
    return {
      code:'资料',
      name:''
    }
  },
  created(){
    console.log('bcreate触发')
    this.name = '法外狂徒张三'
  },
  computed:{
    text(){
      let name = this.name + this.code
      return name
    }
  }
}
</script>

watch:

|

<template>
  <div class="a">
    <h1>这是 a 组件</h1>
    <h2>{{text}}</h2>
    <button @click="editText">更改text</button>
  </div>
</template>


<script>
export default {
  name: 'aCom',
  data() {
    return {
      text:'666'
    }
  },
  watch:{
    text(newVal,oldVal){
      console.log('text被修改了',newVal,oldVal)
    }
  },
  created(){
    console.log('acreate触发')
  },
  methods:{
    editText() {
      this.text = '更改了'
    }
  }
}
</script>

3、v-for遍历必须添加key,并避免与v-if同时使用

(1)v-for 遍历必须为 item 添加 key

在列表数据进行遍历渲染时,需要为每一项 item 设置唯一 key 值,方便 Vue.js 内部机制精准找到该条列表数据。当 state 更新时,新的状态值和旧的状态值对比,较快地定位到 diff 。

避免使用index,当你使用index作为key的时候,你这时候插入一条数,会影响列表后面的所有的key都发生变化,引起不必要的重新渲染。

(2)v-for 遍历避免同时使用 v-if

v-for 比 v-if 优先级高,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候,必要情况下应该替换成 computed 属性。

|

// v-for中使用v-if
<template>
  <div class="b">
    <span v-for="item in newlist" :key="item.key" v-if="item.value !== '3'">{{item.value}}</span>
  </div>
</template>
<script>
export default {
  name: 'cCom',
  data() {
    return {
      list:[
        {
          value:'1',
          key:'a'
        },
        {
          value:'2',
          key:'b'
        },
        {
          value:'3',
          key:'c'
        }
      ]
    }
  },
  created(){
    console.log('cCreate触发')
  },
}
</script>


// 使用computed
<template>
  <div class="b">
    <span v-for="item in newlist" :key="item.key">{{item.value}}</span>
  </div>
</template>
<script>
export default {
  name: 'cCom',
  data() {
    return {
      list:[
        {
          value:'1',
          key:'a'
        },
        {
          value:'2',
          key:'b'
        },
        {
          value:'3',
          key:'c'
        }
      ]
    }
  },
  computed:{
    newlist() {
      return this.list.filter((item)=>{
        console.log(item)
        return item.value !== "3"
      })
    }
  },
  created(){
    console.log('cCreate触发')
  },
}
</script>

4、require.context 自动化引用模块

在我们开发过程中,会将一个个大功能拆分成一个个小功能,除了能便于模块的复用,也让模块条理清晰,后期方便维护。

在使用这些一个个模块的时候,我们通常会将他们一个个引入进来然后再使用。当你使用require.content的时候,每当你书写了一个新的模块文件的时候,你只需要关注逻辑的编写即可,require.content会帮你自动引入。

需要注意:require.content是webpack带的,在构建过程中webpack会解析它。

文档:https://webpack.js.org/guides/dependency-management/#requirecontext

|

// 正常
<script>
import xxx from 'xxx/xxx.vue'
import xxx from 'xxx/xxx.vue'
import xxx from 'xxx/xxx.vue'
export default {
  name: 'Home',
  components:{
    xxx,
    xxx,
    xxx
  }
}
</script>
// 使用require.context
<script>
const path = require('path')
let files = require.context('../components', false, /\.vue$/)
let components = {}
files.keys().forEach(key => {
  const name = path.basename(key, '.vue')
  components[name] = files(key).default || files(key)
})
console.log(components)
export default {
  name: 'Home',
  components
}
</script>

5、slots 的新语法

使用带有“#”的新命名插槽缩写语法,在Vue 2.6.0+中可用

|

// 以前
<template>
  <Modal>
    <template  slot="footer">
        <div>I am the banner</div>
    </template >
  </Modal>
<template>
// 现在
<template>
  <Modal>
    <template #footer>
      <div>I am the banner</div>
    </template >
  </Modal>
<template>

6、$on('hook:')的妙用

当你在vue组件中,定义自定义事件监听器或者第三方插件,并且需要在beforeDestroy钩子中删除它以避免存在内存泄漏。那么这个$on('hook:') 是个很好的特性;

其次由于选项式的api,导致的关注点分离,使用$on('hook:') 能够更好的关联起来。

|

// 以前:
mounted () {
    window.addEventListener('resize', this.resizeHandler);
},
beforeDestroy () {
    window.removeEventListener('resize', this.resizeHandler);
}
// 使用hook:
mounted () {
  window.addEventListener('resize', this.resizeHandler);
  this.$on("hook:beforeDestroy", () => {
    window.removeEventListener('resize', this.resizeHandler);
  })
}

生命周期钩子发出自定义事件这一事实意味着父组件可以监听其子级的生命周期钩子。除此之外$on还可以侦听子组件的生命周期钩子

它将使用正常模式来监听事件(@event),并且可以像其他自定义事件一样进行处理。

|

<child-comp @hook:mounted="someFunction" />

7、$attrs和$listeners妙用,二次包装组件

系统中的大部分弹框右下角都是确定和取消两个按钮。

如果使用element-ui提供的Dialog或者是iview的modal,那么每一个弹框都要手动加按钮,不但代码量增多,而且后面如果按钮UI,需求发生变化,改动量也比较大。

所以将这个弹窗组件进行二次封装,将按钮直接集成在组件内部,就不用反复地去写了。

使用$attrs与$listeners将弹窗组件自身的属性和事件暴露到外部,也就是我们外面写的一圈的组件。

定义如下:

$attrs: 当组件在调用时传入的属性没有在props里面定义时,传入的属性将被绑定到$attrs属性内(class与style除外,他们会挂载到组件最外层元素上)。并可通过v-bind="$attrs"传入到内部组件中

$listeners: 当组件被调用时,外部监听的这个组件的所有事件都可以通过$listeners获取到。并可通过v-on="$listeners"传入到内部组件中。

|

// dcom.vue
<template>
  <div class="b">
    <el-dialog title="收货地址" v-bind="$attrs" v-on="$listeners">
      <template #footer class="dialog-footer">
        <el-button >取 消</el-button>
        <el-button type="primary" >确 定</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script>
export default {
  // 默认情况下父作用域的不被认作 props 的 attribute 绑定 (attribute bindings) 
  // 将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。
  // 通过设置 inheritAttrs 到 false,这些默认行为将会被去掉
  // inheritAttrs: false,
  name: 'dCom',
  data() {
    return {
    }
  },
  computed: {
  },
  created() {
  },
}
</script>

// home.vue
<template>
  <div class="home">
    <d-com :visible.sync="dcomshow"></d-com>
  </div>
</template>


<script>
import dcom from 'xxx/dcom.vue'
export default {
  name: 'Home',
  components:{dcom},
  data(){
    return {
      dcomshow:true,
    }
  },
  methods:{
  
  }
}
</script>

8、自定义v-model的实现,开发组件必备

在用Vue开发前端时,不论使用原生还是封装好的UI库,对于表单组件,一般都会使用到v-model。

虽然v-model是一个语法糖,但是吃到嘴里挺甜的啊。学会自定义v-model,还是很有必要的。

通过官方文档得知:一个组件上的v-model默认是通过在组件上面定义一个名为value的props,同时对外暴露一个名为input的事件。

文档:https://cn.vuejs.org/v2/guide/components-custom-events.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6%E7%9A%84-v-model

|

// temainput.vue
<template>
  <div class="b">
    <input :value="value" @change="handleChange"/>
  </div>
</template>
<script>
export default {
  name: 'temaInput',
  props:{
    value:{
      type:String,
      default:''
    }
  },
  methods:{
    handleChange(e) {
      this.$emit('input',e.target.value)
    }
  }
}
</script>


// home.vue
<template>
  <div>
    <tema-input v-model="inputText"/>
  </div>
</template>
<script>
export default {
  name: 'home',
  data(){
    return {
        inputText:'我是输入框内的文本'
    }
  }
}
</script>

9、巧妙的利用template

我们可以在以前一个条件if掉多个标签 是用一个div或者其他什么标签将需要if掉的标签包裹起来。

但这个包裹标签只是为了切换条件而存在的,导致元素层级多嵌套了一层,其实是没有存在的意义。

我们都知道在声明页面模板时,所有元素需要放在