props可以控制传入的限制,使用validator
1 | const oneOf = (value, validList) => { |
provide/inject允许支持上下文特性,共享数据(类似react的context)
1 | // 祖先组件 |
使用mixins高度抽象各类方法,方便复用
使用mixins时,如果有定义created等函数的,会先执行mixins里的created,然后再执行组件里的created。如果mixins里有定义方法,然后组件里也定义了一个同样名称的方法,组件里的方法会覆盖mixins里的方法。
组件内使用$emit出来的事件,同样可以在本组件使用$on监听到
1 | // 同一个组件 |
Vue2.x废除了Vue1.x的$dispatch和$broadcast的方法,可以使用现有的$on和$emit实现
1 | /** |
dispatch是用于子组件向祖先组件派发事件,与Vue1.x的方法不同,模拟的方法需要指定组件的名称。使用方法:
1 | // 子组件 |
broadcast是用于祖先组件向子组件广播事件,与Vue1.x的方法不同,模拟的方法同样需要指定组件的名称。使用方法:
1 | // 祖先组件 |
5个很有用的找到任意组件的方法
1 | /** |
导入后,使用方法:
1 | findComponentUpward(this, 'y-checkbox-group'); |
利用eventBus实现跨组件通信
首先建立一个js文件,导出一个vue实例:
1 | import Vue from 'vue'; |
实例方法里有$emit和$on方法,同一个实例中$emit出来的事件可以在$on里监听到,利用这个原理可以实现跨组件通信:
- 分别导入这个实例
- 需要传出方:
Bus.$emit('eventBus', 'eventBus');
- 接收方:
Bus.$on('eventBus', (val) => {});
import一个.vue文件,其实返回的就是export的那个对象,具体形式是这样的:
1 | { |
其中render函数是通过Webpack的vue-loader编译出来的
:is可以绑定一个组件对象,或可以是一个String,比如标签名或组件名
v-bind=”{a:1, b:2}”可以绑定一个有属性的对象,然后props里使用
递归组件的使用
递归组件就是指组件在模板中调用自己,开启递归组件的必要条件,就是在组件中设置一个 name 选项。
实现一个递归组件的必要条件是:
- 要给组件设置 name;
- 要有一个明确的结束条件
1 | <template> |
vue 2.2.0以上,可以指定v-model的语法糖传入的指定值
子组件设置model:
1 | export default { |
然后子组件在接受props和设置事件时写入自定义的属性:
1 | export default { |
父组件(v-model写法):
1 | <input-number v-model="inputNumberVal" /> |
这样父子组件就完成了一次不是使用value/input的v-model语法糖
vue2.3.0+的.sync 修饰符
可以看做v-model使用的扩展版,可以绑定多个语法糖,本质还是在父组件上修改数据,并非在子组件
子组件里面的emit方式改变:
1 | export default { |
父组件,绑定的属性后加上.sync:
1 | <sync-number :number.sync="syncNumberVal" /> |
看起来要比 v-model 的实现简单多,实现的效果是一样的。v-model 在一个组件中只能有一个,但 .sync 可以设置很多个,且有以下限制:
- 不能和表达式一起使用(如
v-bind:title.sync="doc.title + '!'"
是无效的); - 不能用在字面量对象上(如
v-bind.sync="{ title: doc.title }"
是无法正常工作的)。
$nextTick原理
由于vue是异步执行DOM更新的,nextTick的作用就是将回调推到异步队列去执行,确保DOM已经更新完毕。分别使用了三种执行异步队列的方法,优雅降级:
- 如果浏览器支持Promise,使用promise.then延迟调用
- MutationObserver是h5新加的一个功能,其功能是监听dom节点的变动,在所有dom变动完成后,执行回调函数
- 以上都不支持就使用setTimeout延迟器
现在2.6版本的走的全是microtasks,microtasks比macrotasks的优先级高很多,如果遇到nextTick后获取的DOM不符合预期,直接使用setTimeout延迟20毫秒,确保DOM更新即可
We are reverting back to microtasks everywhere and will remove withMacroTask entirely in 2.6.
macrotasks:
- setTimeout
- setInterval
- setImmediate
- requestAnimationFrame
- I/O
- UI rendering
microtasks:
- process.nextTick
- Promises
- Object.observe
- MutationObserver
1 | setTimeout(() => { |
参考资料:
- https://juejin.im/post/5a1af88f5188254a701ec230
- https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
vue数组更新及小技巧
可以更新数组视图的操作有:
- push
- pop
- shift
- unshift
- splice
- sort
- reverse
不可更新数组的操作有:
- 当利用索引直接设置一个项时,例如:
this.items[index] = value
- 当修改数组的长度时,例如:
this.items.length = newLength
利用索引修改一个项,并且可以更新视图的技巧是,先复制一个完全一样的,然后修改对应的索引。虽然修改一个索引不会更新,但修改整个指向数据就会更新:
1 | handler(val) { |
使用了keep-alive后,在router-view里设定path属性可以加载多个参数不同,且使用同一个组件的路由
以上情况多在后台管理模式下常见,即多个标签切换,例如编辑,是根据params或query进行加载不同的详情,但由于使用的是同一个组件,在使用keep-alive后,会导致每次加载的都是已经缓存下来的组件,这时候如果在每个路由上都定义不同的path,即使是同一个组件,也会进行重新的加载
1 | <keep-alive :include="cachedViews"> |
其中cachedViews是一个组件内名字的数组,只有匹配上,才会被keep-alive
vue-router编程式路由跳转问题
vue-router在使用this.$router.push的时候,需要注意path和params不能公用,path会覆盖params,但可以使用query。如果需要使用params进行跳转,需要使用name+params的组合进行跳转
不要将this.$route传入某个地方
由于this.$route里的match属性存在循环引用的问题,假如每次路由跳转将整个this.$route传入vuex里进行管理,会造成内存泄露导致卡死的问题,需要重新建立一个完全独立的副本。注意由于循环引用,深复制是会失败的,建立副本直接每个值都赋值一遍就好,重要的也就是path,query,params,meta,fullPath这些
在better-scroll里使用定位问题
在使用better-scroll,如果需要使用一些特殊定位,置顶等效果,不要将需要定位的元素写在第一个子元素或第一个子元素里,由于better-scroll实例化之后的滚动效果只对第一个子元素生效,写在第一个子元素里实例化后的transform会使所有定位失效
slot的使用(新旧两种方法)
注意。slot不仅能向父组件传递当前作用域的数据,还能传递方法
子组件v-slot:
1 | <template> |
2.6.0以下引用方式:
1 | <v-slot> |
2.6.0以上引用方式:
1 | <v-slot> |
使用全局指令开发点击波纹效果
其实本质上就是点击的时候在元素里加入一个元素,元素做放大动画,需要css3支持
waves.js:
1 | import './waves.scss'; |
main.js
1 | import waves from '@/directive/waves/waves.js'; |
使用:
1 | <div class="btn" v-waves>测试waves</div> |
router初始化,回到new Router()的时候
由于前端有时需要做权限处理,这时候就需要做权限处理,使用动态路由的router.addRoutes(),动态添加路由。
有以下场景:new Router()的时候已经添加里基础的路由,也通过addRoutes添加了对应权限的路由。这时候角色退出登录,登入另一个角色,角色拥有其他权限,这时候就需要清除之前添加的路由,回到初始基础路由的时候再一次动态添加对应角色的路由
由于vue-router官方只有动态添加路由的api,没有提供删除动态路由的办法,有issue提供了以下方法:
初始创建的router实例,最终要返回的状态:
1 | const createRouter = () => new Router({ |
reset方法:
1 | // Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465 |
ElementUI的table切换错乱解决方案
ElementUI存在一个问题,譬如进行两个table切换或动态渲染table列,会有渲染错乱的问题,有两个解决方案
1、在每个el-table-column上设置独一无二的key值,千万不要设置一个随机数值
2、每个column都使用scope渲染
图片预览插件vue-photo-preview的实现
插件地址:https://github.com/826327700/vue-photo-preview
实现原理:本插件是使用photoSwipe这个插件进行二次开发,使用全局mixins,将设定特定属性的元素取出来,获取图片地址,再按photoSwipe的数据格式进行传入调用
父子组件的渲染和监听顺序问题
vue组件的渲染顺序是由内而外的,父组件的created要先于子组件的mounted,所以需要在父组件的created定义监听子组件的函数,使用监听一般都是父子组件关联比较大的
使用全局指令开发统一管理点击document
有些弹出额外框的组件,像日历选择,tooltip等,都会有一种需求,就是要求点击组件外的地方能够使组件收起来,而这个实现的原理其实就是在组件加载的时候,对document加入监听函数,然后在组件销毁的钩子里移除这个监听,避免组件移除后监听函数还存在,垃圾无法被回收而造成内存泄露。
可以想象如果每个组件都要这么写的话,每个判断点击的地方是否在组件外的逻辑都要重新写,是很麻烦的事,这时候就可以利用全局指令,哪里需要判断的,加上就可以
以下代码取自ElementUI,需要注意判断点击的地方是否在组件内的判断条件就可以,全局指令的应用参考vue官网即可
1 | import Vue from 'vue'; |
局部使用:
1 | directives: { Clickoutside }, |
注意,此指令可以对组件本身使用,也可以对元素本身使用