VUE开发者必须知道的实用技术点!

前言

vue 做为目前前端三大框架之一,对于前端开发者能够说是必备技能。掌握这些实用小技巧,可让你事半功倍。javascript

一、路由懒加载,能让你首次加载更快

路由懒加载可让咱们的包不须要一次把全部的页面的加载进来,只加载当前页面的路由组件就行。html

举个栗子🌰,若是这样写,加载的时候会所有都加载进来。前端

const router = new VueRouter({
  routes:[
    {
      path: '/',
      name: 'Home',
      component: Home
    },
    {
      path: '/about',
      name: 'About',
      component: About
    }
  ]
})
复制代码

因此,应该避免上面的写法,尽可能使用懒加载。vue

路由的懒加载能够分为如下三种写法。java

  • Vue异步组件webpack

  • es6的importes6

  • webpack提供的require.ensure()web

    // 一、Vue异步组件 VueRouter({ routes:[ { path: '/about', name: 'About', component: resolve => reqire(['path路径'], resolve) } ] })vuex

    // 二、es6的import VueRouter({ routes:[ { path: '/about', name: 'About', component: () => import('path路径') } ] })数组

    // 三、webpack提供的require.ensure() VueRouter({ routes:[ { path: '/about', name: 'About', component: r => require.ensure([],() => r(require('path路径')), 'demo') } ] })

二、异步组件

异步组件可让咱们在须要一些组件时才将它加载进来,而不是一初始化就加载进来,这跟路由懒加载是一个概念。

之前是这么引入组件的

import BureauDetail from './components/ChildFirst'
import addBureau from './components/ChildSecond'

//在vue的comoinents中
components: {
  ChildFirst,
  ChildSecond 
}
复制代码

若是不是一开始就要加载的组件,咱们可使用组件懒加载

//在vue的comoinents中
components: {
  BureauDetail: () => import('./components/ChildFirst'),
  addBureau: () => import('./components/ChildSecond')
},
复制代码

异步组件还有一种比较完善的写法

export default {
  components:{
    ChildFirst:()=>({
      component:import(/* webpackChunkName: "ChildFirst" */ './Async'),
      delay:200, // 延迟几毫秒,默认200
      timeout:3000, // 加载几毫米以后就超时,触发error组件
      loading:LoadingComponent, // 组件未加载回来前显示
      error:ErrorComponent // 组件超时时显示
    })
  }
}
复制代码

三、require.context()引入多个组件

经常用来在组件内引入多个组件, require.context(directory, useSubdirectories, regExp)

原始写法:

// 原始写法
import titleCom from '@/components/home/titleCom'
import bannerCom from '@/components/home/bannerCom'
import cellCom from '@/components/home/cellCom'
components: {
  titleCom, bannerCom, cellCom
}
复制代码

这样就写了大量重复的代码,利用 require.context 能够写成

const path = require('path')
const files = require.context('@/components/home', false, /\.vue$/)
const modules = {}
files.keys().forEach(key => {
  const name = path.basename(key, '.vue')
  modules[name] = files(key).default || files(key)
})
components: modules
复制代码

在main.js中引入大量公共组件,利用 require.context 能够写成

import Vue from 'vue'
// 自定义组件
const requireComponents = require.context('../views/components', true, /\.vue/)
// 打印结果
// 遍历出每一个组件的路径
requireComponents.keys().forEach(fileName => {
  // 组件实例
  const reqCom = requireComponents(fileName)
  // 截取路径做为组件名
  const reqComName =reqCom.name|| fileName.replace(/\.\/(.*)\.vue/,'$1')
  // 组件挂载
  Vue.component(reqComName, reqCom.default || reqCom)
})
复制代码

四、在父组件里监听子组件的生命周期

好比有父组件 Parent 和子组件 Child,若是父组件监听到子组件挂载 mounted 就作一些逻辑处理,常规的写法可能以下:

// Parent.vue
<Child @mounted="doSomething"/>

// Child.vue
mounted() {
  this.$emit("mounted");
}
复制代码

此外,还有一种特别简单的方式,子组件不须要任何处理,只须要在父组件引用的时候经过@hook 来监听便可,@hook也能够监听其它的生命周期事件,代码以下:

<Child @hook:mounted="doSomething" /> 
<Child @hook:updated="doSomething" />
复制代码

五、computed中使用this?

在computed属性中经过this.xxx去拿data里面的数据,和methods里面的方法吧,或许还会经过this. s t o r e 去拿 v u e x s t a t e , c o m m i t 等,甚至,还会经过 t h i s . store去拿vuex的state,和commit等,甚至,还会经过this. route去获取路由里面的数据吧。

其实,咱们能够避免这些丑陋的this,它甚至会给咱们带来看不见的性能问题。

实现上,咱们经过this能访问到的数据,在computed的第一个参数上都能结构出来。

export default {
  watch: {
    haha({$attrs,$route,$store,$listeners,$ref}){
     // 还能结构不少属性,可自行打印看看
     return 
   }
  }
}
复制代码

六、初始化的时候,让watch当即执行

watch每当监听的数据变化时都会执行回调进行后续操做。

可是当 watch 一个变量的时候,在页面或者组件初始化时并不会执行,以下面的例子,你须要在 created 的时候手动调用一次。

created() {
  this.getList();
},
watch: {
  keyWord: 'getList',
}
复制代码

上面这样的作法可使用,但很麻烦,咱们能够添加 immediate 属性,这样初始化的时候就会自动触发

watch 有三个参数

  • handler:其值是一个回调函数。即监听到变化时应该执行的函数

  • deep:其值是 true 或 false;确认是否深刻监听。当要监听数组或对象等引用类型数据时,可使用deep属性

  • immediate:其值是 true 或 false,确认是否以当前的初始值执行 handler 的函数

    watch: { keyWord: { handler(val) {}, immediate: true } }

七、递归组件本身调用本身

递归组件: 组件在它的模板内能够递归的调用本身,只要给组件设置 name 组件就能够了。

注意:必须给一个条件来限制数量,不然会抛出错误: max stack size exceeded

<template>
  <div v-for="(item,index) in treeArr"> {{index}} <br/>
      <tree :item="item.arr" v-if="item.flag"></tree>
  </div>
</template>
<script>
export default {
  // 必须定义name,组件内部才能递归调用
  name: 'tree',
  data(){
    return {}
  },
  // 接收外部传入的值
  props: {
     item: {
      type:Array,
      default: ()=>[]
    }
  }
}
</script>
复制代码

看Element ui源码时,封装的tree控件、级联选择其,也是这么使用的。

八、object.freeze性能优化

vue 2.0版本会经过 object.defineProperty 对数据进行劫持,遇到数组和对象必须循环遍历全部的域值才能劫持每个属性。

vue 3.0版本会经过Proxy构造函数来进行数据劫持,来实现视图响应数据的变化

然而有些时候咱们的组件就是纯粹的数据展现,不会有任何改变,咱们就不须要 vue 来劫持咱们的数据,在大量数据展现的状况下,这可以很明显的减小组件初始化的时间。

因此,咱们能够经过 object.freeze 方法来冻结一个对象,这个对象一旦被冻结,vue就不会对数据进行劫持了。

Object.freeze() 能够冻结一个对象,冻结以后不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。该方法返回被冻结的对象。

<p v-for="item in list">{{ item.value }}</p>

export default {
  data: {
        // vue不会对list里的object作getter、setter绑定
        list: Object.freeze([
            { value: 1 },
            { value: 2 }
        ])
    },
    created () {
        // 界面不会有响应
        this.list[0].value = 100;

        // 下面两种作法,界面都会响应
        this.list = [
            { value: 100 },
            { value: 200 }
        ];
        this.list = Object.freeze([
            { value: 100 },
            { value: 200 }
        ]);
    }
}
复制代码

九、修改引用类型的数据,视图不更新?

当vue的data里边声明或者已经赋值过的对象或者数组(数组里边的值是对象)时,向对象中添加新的属性,若是更新此属性的值,是不会更新视图的。

调用方法: this.$set( target , key , value)

  • target: 要更改的数据源(能够是一个对象或者数组)

  • key 要更改的具体数据 (索引)

  • value 从新赋的值

    export default { name: 'App', data () { return { items: [ { message: "one", id: "1" }, { message: "two", id: "2" }, { message: "three", id: "3" } ] } }, mounted () { //此时对象的值更改了,可是视图没有更新 this.items[0] = { message:'first',id:'4'}

    // s e t 能够触发更新视图 t h i s . set 能够触发更新视图 this. set(this.items,0,art) }, methods: { handClick(){ let change = this.items[0] change.message="shen" // s e t 能够触发更新视图 t h i s . set 能够触发更新视图 this. set(this.items,0,change) } } }

十、.sync修饰符

.sync提供了一种与父组件沟通的思路!你若是只是单纯的在子组件当中修改父组件的某个数据时,建议使用sync,简单,快捷,不须要在传一个自定义方法来接收了

vue中咱们常常会用v-bind(缩写为:)给子组件传入参数。或者咱们会给子组件传入一个函数,子组件经过调用传入的函数来改变父组件的状态。举个例子🌰

//父组件 给子组件传入一个函数
 <MyFooter :age="age" @setAge="(res)=> age = res">
 </MyFooter>
 
 
 //子组件 经过调用这个函数来实现修改父组件的状态。
 mounted () {
    console.log(this.$emit('setAge',1234567));
 }
复制代码

如今只须要使用.sync就能够轻松更新赋组件的值

//父组件 将age传给子组件并使用.sync修饰符。
<MyFooter :age.sync="age">
</MyFooter>


//子组件 触发事件
 mounted () {
   console.log(this.$emit('update:age',1234567));
 }
复制代码

十一、Vue.mixin混入,复用代码

混入 (mixin) 提供了一种很是灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象能够包含任意组件选项。当组件使用混入对象时,全部混入对象的选项将被“混合”进入该组件自己的选项。

通俗的说,mixin能够定义公用的data,created,methods,computed,watch等。而后混入到你当前的vue文件中。

minxin还有一个强大之处就是合并选项,相同的变量/方法名会合并在一块儿,若是有相同名字,当前文件的变量或者方法会覆盖mixin文件的名字或者方法。

  • data对象在内部会进行递归合并,并在发生冲突时以组件数据优先。

  • 同名钩子函数将合并为一个数组,所以都将被调用。混入对象的钩子将在组件自身钩子以前调用。

  • 值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对。

一、定义一个 mixin.js

export default mixin {
 data() {
  return {
   name: 'mixin'
  }
 },
 created() {
  console.log('mixin...', this.name);
 },
 mounted() {},
 methods: {  //日期转换
   formatDate (dateTime, fmt = 'YYYY年MM月DD日 HH:mm:ss') {
     if (!dateTime) {
      return ''
     }
     moment.locale('zh-CN')
     dateTime = moment(dateTime).format(fmt)
     return dateTime
  }
 }
}
复制代码

二、在vue文件中使用mixin

import '@/mixin'; // 引入mixin文件
export default {
 mixins: [mixin],  //用法
 data() {
  return {
   userName: "adimin",
   time: this.formatDate(new Date()) //这个vue文件的数据源data里面的time就是引用混入进来的方法
  }
 }
} 
复制代码

十二、provide/inject

经常使用的父子组件通讯方式都是父组件绑定要传递给子组件的数据,子组件经过props属性接收,一旦组件层级变多时,采用这种方式一级一级传递值很是麻烦,并且代码可读性不高,不便后期维护。

vue提供了provide和inject帮助咱们解决多层次嵌套嵌套通讯问题。在provide中指定要传递给子孙组件的数据,子孙组件经过inject注入祖父组件传递过来的数据。

其实,provide 和 inject 主要为高阶插件/组件库提供用例。

读Element UI源码时,发现不少高阶组件使用了provide和inject,减小了不少父组件向子组件以及孙子组件一层层通讯的工做量。

elementUI组件库中,在el-form组件中将组件实例暴露给子孙组件

在el-form-item组件中注入el-form组件实例,而后就可使用el-form组件实例的方法、变量等等

一、在父组件中provide提供变量

<template>
  <div>
    <p>{{ title }}</p>
    <son></son>
  </div>
</template>
<script>
  import Son from "./son"
  export default {
    name: 'Father',
    components: { Son },
    // provide选项提供变量
    provide: {
      message: 'provided by father'
    },
    data () {
      return {
        title: '父组件'
      }
    },
    methods: { ... }
  }
</script>
复制代码

二、在子孙组件中,均可以使用inject来注入

<template>
  <div>
    <p>message:{{ message }}</p>
  </div>
</template>
<script>
export default {
  name: "GrandSon",
  inject: [ "message" ],
  data () {
    return {
      title: '孙组件'
    }
  },
  methods: { ... }
};
</script>
复制代码

▼更多精彩推荐,请关注javascript艺术▼

相关文章
相关标签/搜索