vue小细节

1. 组件缓存 keep-aive  属性  exclude  

include - 字符串或正则表达式。只有名称匹配的组件会被缓存

exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。

max - 数字。最多可以缓存多少组件实例。

<keep-alive exclude="Detail"> 
      <router-view />
 </keep-alive>

2. es6加载路由组件

const Detail = () => import('../views/detail/Detail.vue');

3.避免路由跳转报错

//replace  避免报错
const VueRouterReplace = VueRouter.prototype.replace
VueRouter.prototype.replace = function replace(to) {
  return VueRouterReplace.call(this, to).catch(err => err)
}

//push 避免报错
const VueRouterPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(to) {
  return VueRouterPush.call(this, to).catch(err => err)
}

4.声明组件名 , 避免通俗语义化 例如 Footer ,需改为FooterView

5.组件点击   @click.native

<back-top @click.native="backClick" v-show="isBackShow"></back-top>

6.this.$refs获取子组件相关 , 改变子组件数据

<tab-control
      :titles="[{ title: '流行' }, { title: '精选' }, { title: '新款' }]"
      @itemClick="tabClick"
      ref="tabControl1"
      v-show="isFixed"
    ></tab-control>  //子组件
this.$refs.tabControl1.courrentIndex = index;  //改变子组件数据
this.$refs.tabControl1.$el.getBoundingClientRect()
        .top //获取子组件距离顶部高度

7.具名插槽 , 类名不要用 中划线  来间隔

 8.Vuex,每次调用mutation之后向localstorage存值,防止刷新丢失

9.vue.config.js  每次更改完之后需要重新启动项目

10.  用样式穿透来解决ui框架后引入导致样式覆盖问题  .home  >>> .children

 11.vue使用this.$refs.xx在mounted中获取DOM元素为undefined 

12.使用 @hook 即可监听组件生命周期,组件内无需做任何改变

<template>
    <List @hook:mounted="listenMounted" />
</template>

13.手动挂载组件

import vue from 'vue'
import Message from './Message.vue'

// 构造子类
let Messageconstructor = vue.extend(Message)
// 实例化组件
let messageInstance = new Messageconstructor()
// $mount可以传入选择器字符串,表示挂载到该选择器
// 如果不传入选择器,将渲染为文档之外的的元素,你可以想象成 document.createElement()在内存中生成dom
messageInstance.$mount()
// messageInstance.$el获取的是dom元素
document.body.appendChild(messageInstance.$el)

14.wacth高阶使用

  14-1.在组件创建后 watch    属性immediate设置为true  能够立即执行  

export default {
    data() {
        return {
            name: 'Joe'
        }
    },
    watch: {
        name: {
            handler: 'sayName',
            immediate: true
        }
    },
    methods: {
        sayName() {
            console.log(this.name)
        }
    }
}

    14-2.在监听对象时,对象内部的属性被改变时无法触发 watch  , 设置属性deep为true , 为其设置深度监听

export default {
    data: {
        studen: {
            name: 'Joe',
            skill: {
                run: {
                    speed: 'fast'
                }
            }
        }
    },
    watch: {
        studen: {
            handler: 'sayName',
            deep: true
        }
    },
    methods: {
        sayName() {
            console.log(this.studen)
        }
    }
}

 14-3  触发监听执行多个方法

export default {
    data: {
        name: 'Joe'
    },
    watch: {
        name: [
            'sayName1', //字符串形式
            function(newVal, oldVal) {  //函数形式  
                this.sayName2()
            },
            {           //对象形式
                handler: 'sayName3',
                immaediate: true
            }
        ]
    },
    methods: {
        sayName1() {
            console.log('sayName1==>', this.name)
        },
        sayName2() {
            console.log('sayName2==>', this.name)
        },
        sayName3() {
            console.log('sayName3==>', this.name)
        }
    }
}

  14-4  wacth监听多个变量

watch本身无法监听多个变量。但我们可以将需要监听的多个变量通过计算属性返回对象,再监听这个对象来实现“监听多个变量”

export default {
    data() {
        return {
            msg1: 'apple',
            msg2: 'banana'
        }
    },
    compouted: {
        msgObj() {
            const { msg1, msg2 } = this
            return {
                msg1,
                msg2
            }
        }
    },
    watch: {
        msgObj: {
            handler(newVal, oldVal) {
                if (newVal.msg1 != oldVal.msg1) {
                    console.log('msg1 is change')
                }
                if (newVal.msg2 != oldVal.msg2) {
                    console.log('msg2 is change')
                }
            },
            deep: true
        }
    }
}

    14-4  注销 watch

        1.为什么要注销 ?
因为我们的组件是经常要被销毁的,比如我们跳一个路由,从一个页面跳到另外一个页面,那么原来的页面的 watch 其实就没用了,
这时候我们应该注销掉原来页面的 watch 的,不然的话可能会导致内置溢出。好在我们平时 watch 都是写在组件的选项中的,他会随着组件的销毁而销毁。
        2.什么情况下需要注销?
  如果我们使用下面这样的方式写 watch,那么就要手动注销了,这种注销其实也很简单
const unWatch = app.$watch('text', (newVal, oldVal) => {
  console.log(`${newVal} : ${oldVal}`);
})

        3.如何注销?

const unWatch = app.$watch('text', (newVal, oldVal) => {
  console.log(`${newVal} : ${oldVal}`);
})
unWatch(); // 手动注销watch

  app.$watch调用后会返回一个值,就是unWatch方法,你要注销 watch 只要调用unWatch方法就可以了。

 

15.函数式组件: 

一般适合只依赖于外部数据的变化而变化的组件,因其轻量,渲染性能也会有所提高。

创建函数式组件也很简单,只需要在模板添加 functional 声明即可

子组件

<template functional>
    <div class="list">
        <div class="item" v-for="item in props.list" :key="item.id" @click="props.itemClick(item)">
            <p>{{item.title}}</p>
            <p>{{item.content}}</p>
        </div>
    </div>
</template>

父组件

<template>
    <div>
        <List :list="list" :itemClick="item => (currentItem = item)" />
    </div>
</template>
import List from '@/components/List.vue' export default { components: { List }, data() { return { list: [{ title: 'title', content: 'content' }], currentItem: '' } } }

16.路由参数解耦

将路由的 props 属性设置为 true 后,组件内可通过 props 接收到 params 参数

const router = new vueRouter({
    routes: [{
        path: '/user/:id',
        component: User,
        props: true
    }]
})

另外还可以通过函数模式来返回 props

const router = new vueRouter({
    routes: [{
        path: '/user/:id',
        component: User,
        props: (route) => ({
            id: route.query.id
        })
    }]
})

可通过 props 接收参数

export default {
    props: ['id'],
    methods: {
        getParamsId() {
            return this.id
        }
    }
}

16.params传参问题   

params传参时,如果没有在路由中定义参数,也是可以传过去的,同时也能接收到,但是一旦刷新页面,这个参数就不存在了。

这对于需要依赖参数进行某些操作的行为是行不通的.

// 定义的路由中,只定义一个id参数
{
    path: 'detail/:id',
    name: 'Detail',
    components: Detail
}

// template中的路由传参,
// 传了一个id参数和一个token参数
// id是在路由中已经定义的参数,而token没有定义
<router-link :to="{name: 'Detail', params: { id: 1, token: '123456' }}">前往Detail页面</router-link>

// 在详情页接收
created () {
    // 以下都可以正常获取到
    // 但是页面刷新后,id依然可以获取,而token此时就不存在了
    const id = this.$route.params.id;
    const token = this.$route.params.token;
}

17.定时器问题

通过$once这个事件侦听器器在定义完定时器之后的位置来清除定时器

const timer = setInterval(() =>{
    // 某些定时器操作
}, 500);
// 通过$once来监听定时器,在beforeDestroy钩子可以被清除。
this.$once('hook:beforeDestroy', () => {
    clearInterval(timer);
})

 18.props验证

props: {
  status: {
    type: String, //数据类型
    required: true, //是否验证
    validator: function (value) { //验证函数
      return [   //验证结果
        'success',
        'error',
      ].indexOf(value) !== -1
    }
  }
}

 19.路由组件复用

在开发当中,有时候我们不同的路由复用同一个组件,默认情况下,我们切换组件,Vue出于性能考虑可能不会重复渲染。

但是我们可以通过给router-view绑定一个key属性来进行切换的时候路由重复渲染。

<template>
    <router-view :key="$route.fullPath"></router-view>
</template>

20批量属性继承

使用$props将父组件的所有的props传递到子组件

场景 :    从父组件接收传递下来的数据使用props接收,再将这些props数据传递到子组件

<template>
    <!-- 将从父组件接收到的props数据传递到子组件
        使用v-bind="$props" 批量传递
    -->
    <childComponent
        v-bind="$props"
    />
</template>
<script>
export default {
    // 从父组件接收到的props数据
    props: ['value1','value2','value3','value4','value5'],
    data() {
        return {....}
    .....
    }
}

// childComponent.vue
<script>
export default {
    props: ['value1','value2','value3','value4','value5'],
    data() {
        return {....}
    .....
    },
    mounted() {
        // 子组件可以接收到数据
        console.log(this.value1)
        console.log(this.value2)
        console.log(this.value3)
        console.log(this.value4)
        console.log(this.value5)
    }
}
</scrript>

属性继承在开发表单组件时,使用$props就可以很好的解决批量属性传递问题。

21.组件懒加载(异步加载组件)

1.为什么要用

  所有的组件代码都会在首次渲染是下载 , 体验不佳

2.什么场景用

比如后台管理页面,在多个tab之间切换的时候,其余tab里面的组件就可以在需要的时候再异步加载~

有多个子路由的页面必用

3.如何用?

components: { test: () => import("./Test.vue") },

给加载js命名
 components: {
    test: () => import(/* webpackChunkName:'test' */ "./Test.vue"),  //给加载js命名
  },

处理加载状态的写法  

异步组件工程函数
const AsyncTest = () => ({ component: import(/* webpackChunkName:'test' */ "./Test.vue"), loading: LoadingComponent, //加载时显示的组件 error: ErrorComponent, //超时或错误时显示的组件 delay: 200, //延迟 timeout: 3000, //超时 });

22.解决async/await异步问题

 使用  await 1; 解决异步

例子:
async created(){
                try {  //捕获await错误信息 
                   this.couponArr = await this.getData({form_id:11,pageSize:9}).then(res=>{
                    if(res.length>8){
                        res.pop();
                        this.couponBtn=true;
                        return res;
                    }
                    return res;
                    });
                    this.storeArr1[this.currentIndex]= await this.getData({form_id:8,position_ename:'nav1-1',pageSize:4});
                    this.currentArr =  this.storeArr1[this.currentIndex];
                    this.storeArr2 = await this.getData({form_id:18,pageSize:5}).then(res=>{
                        if(res.length>4){
                            res.pop();
                            this.bandanBtn=true;
                            return res;
                        }
                        return res;
                    });
                    this.storeArr3 = await this.getData({form_id:8,position_ename:'nav3'});
                } catch (e) {
                    console.log(e)
                }
                
                await 1; //解决异步
                this.gundong()
                this.navFixed({
                    navParent: '.nav_box',
                    nav: '.nav',
                    navItem: '.nav li',
                    itemParent: '.tit',
                    active: 'active',
                    fixedTop: '0',
                    deviation: parseFloat($('.nav').height()),
                    animateTime: 500,
                    flag: 0
                })
                this.tab()
                this.xuanran();
                this.videoHandle();
                this.gtOrltVisible()
                this.swiperHandle()
                this.staticImg()
            },

23.滚动节流

 passive: 是告诉浏览器,监听器不会调用e.preventDefault()函数,不用来检查,可以提前生成手势,从而提高流畅性,通常用在move事件中

例子: 

<template>
  <div class="scroll">
    <div class="containers" @scroll.passive="scrollHandle">
      <div class="content">
        <ul>
          <li v-for="item in 100">{{ item }}</li>
        </ul>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "Scroll",
  components: {},
  data() {
    return {
      scrollHandle: () => {},
    };
  },
  created() {
    this.scrollHandle = this.throttle(this.scrollHandle2, 200);
  },
  methods: {
    scrollHandle2(e) {
      console.log(456);
    },
    throttle(func, delay = 800) {
      let lock = false;
      return (...args) => {
        if (lock) {
          return;
        }
        func(...args);
        lock = true;
        setTimeout(() => {
          lock = false;
        }, delay);
      };
    },
  },
};
</script>

<style scoped >
.containers {
  height: 500px;
  border: 1px solid #000;
  overflow: scroll;
}
</style>

 24.$destroy()方法问题

某种业务场景下,我们期望缓存的页面可以被销毁,就会调用$destroy方法,这回导致被销毁的页面,虽然会触发activated钩子函数,但是页面实际上没有被缓存,重新进入是初始dom结构的状态。

 24.数据更新,update()/beforeUpdate()未触发问题

页面使用到的数据改变时才会更新 updated 和 beforedate钩子函数

...

原文地址:https://www.cnblogs.com/wxyblog/p/14573897.html