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.声明组件名 

1. 避免通俗语义化    例如:   <footer> 会被解析为标签    需要写成    <Footer />

 2. 定义一个子组件想引用vue实例中的data 属性,在做属性绑定的时候使用了驼峰,结果拿不到值,我这个使用的是线上的版本利用vue-devtools 发现props中的dataMsg为undefined,这个时候的改为datamsg就正确了

3.组件之间通信 , 传递对象属性的value值 , 需要用wacth来监听 props接收到的值 , 不然为undefined

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钩子函数

 24.vue-router中params传值问题

1.path定义了动态传值 , 则params为必传项 , 否则router-view不显示

router/index.js
{ path:
'/park1/:id', name: 'park1', component: () => import( /* webpackChunkName: "about" */ '../views/park1.vue') }


.vue文件
<router-link 
     :to="{ path: '/park1', params: { id: 1 } }"
>park1</router-link>
或者  路径拼接  :to="'/park1/'+1"

1.如果是用name来匹配路由

相当于post请求,参数不会再地址栏中显示。

存在页面刷新, params返回参数会丢失 , query并不会出现这种情况

拓展:解决用name来匹配路由页面,刷新params丢失问题

1.使用query传递数据

2.将数据在前一页存到 localstoratgel里

存数据

router.beforeEach((to, from, next) => {
  //通过该方式可以保存 VueRouter 的数据不被刷新
  localStorage.setItem('routerParams', JSON.stringify(to.params));
  next()
})

取数据

created() {  //如果是被keep-alive 标签包裹 , 可以在activated 钩子函数中获取参数
this.params = JSON.parse(localStorage.getItem("routerParams"));
 },

25.props规范

数组形式来接收数据 ,开发时的工作量会小一些 , 但不推荐 , 不利于后续维护项目

props:['name','type','list']

建议使用对象形式 , 

props:{
  name: String, //字符串类型   type: {   validator:
function(value) {//这个值必须匹配下列字符串中的一个     return ["success","warning","danger"].includes(value);

    }
  },
  list: {
    type: Array,
    //对象或数出默认值必须从一个工厂函数获取
    default: O)→>[]
  }
,
}

26. v-for循环时 , 可以直接通过传入对象直接更改 , 不用传下标然后找到数组中对应对象 , 再进行修改

例子:

<template>
  <div>
    <ul>
      <li v-for="item in data" :key="item.id">
        <button @click="add(1, item)">+</button>
        <span>{{ item.num }}</span>
        <button @click="add(-1, item)" v-if="item.num > 0">-</button>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  props: {},
  data() {
    return {
      data: [
        {
          num: 0,
          id: 0,
        },
        {
          num: 100,
          id: 2,
        },
        {
          num: 99,
          id: 3,
        },
      ],
    };
  },
  components: { list },
  created() {},
  mounted() {},
  methods: {
    add(num, v) {  //直接对传入的数组对象进行修改 , data数组中的对象的value值也会修改 
      v.num += num;
    },
  },
  watch: {},
  computed: {},
};
</script>

<style scoped lang="less">
</style>

27. computed中不要使用箭头函数

 

28. 更新组件

更新组件上传入的key即可,用 new Date().getTime()

 <a-popover  placement="bottomRight"  :key='showPopoverKey'>
 data() {
    return {
       showPopoverKey:new Date().getTime(),
    }
  },
 methods: { 
    clickUpdate(){ //点击更新组件
        this.showPopoverKey:new Date().getTime()
    }
 }

29. data赋值

已antd vue为例:

this.form.validateFields((errroe, values) => {
        if (!errroe) {
          Object.assign(this, values);
        }
      });

30. 重置路由

解决vue-router中的缓存问题

// 替换现有router的routes
router.matcher = new VueRouter({
  routes: newRoutes
}).matcher

31. 根组件绑定bus


//根组件绑定
new Vue({
  render: h => h(App),
  data() {
    return {
      bus: new Vue()
    }
  },
  router,
  store
}).$mount('#app')

//使用
//A组件订阅
this.$root.bus.$on('fromUpdate', () => {
      this.getTableList()
    })
//B组件发布
this.$root.bus.$emit('fromUpdate')

32. filters中获取this

<template>
  <div>{{a|sum(that)}}</div>
</template>
 
<script>
  export default {
    name: "test",
    data() {
      return {
        that: this,
        a: 1,
        b: 2
      }
    },
    filters: {
      sum(a, that) {
        console.log(that);
        return a + that.b;
      }
    },
  }

33.解决$on多次被调用

this.$baseEventBus.$off('setColor')
this.$baseEventBus.$on('setColor', colorObj => { 
    console.log(colorObj )
})

 

34.解决重置data 

直接赋值:   this.$data = this.$options.data()  

报错:Avoid replacing instance root $data. Use nested data properties instead

原因:如果你的vue实例的data方法里通过this引用了其他vue实例上的方法/属性等,需要改成下面这样,修复this指向问题导致的报错。

 解决方案

Object.assign(this.$data, this.$options.data.call(this));

35.点击按钮选择文件

             <input
                type="file"
                ref="inputFile"
                accept=".xls,.xlsx,.txt"
                @change="readFile($event)"
                style="display: none"
              />
               <a-button type="primary" @click="clickInput"> 导入字段描述 </a-button>
 
 methods: {
    clickInput() {
      this.$refs.inputFile.dispatchEvent(new MouseEvent('click'))
    },
  }

36.jsx绑定点击事件并传参

jsx写法示例

 <span class="yuan" onClick={() => this.connectRight(row)}></span>

37.watch 只监听一次

 mounted() {
    const unwatch = this.$watch("needWatch", function (val) {
      // 处理业务逻辑
      // 之后调用unwatch, 取消监听
      unwatch();
    });
  },

38.route参数更新

直接修改 $route.query.id = 111, 页面 url 未更新,

 

 

 

 

 

 

...

 

posted @ 2021-03-24 15:59  混名汪小星  阅读(68)  评论(0编辑  收藏  举报