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.声明组件名
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
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文件
或者 路径拼接 :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" />
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 未更新,
...