Vue开发中的实用技巧
动态导入本地图片
假设我们有这么一个功能,后台返回图片的名称,前端需要自己拼接路径获取本地图片,假定这些资源是存在我们前端的 assets/images,如果你采取传统的字符串拼接的方式:
//template
发现图片无法显示,打开控制台审查元素,发现路径并没有正确解析,这个跟 webpack 编译打包有关系,在编译过程中目录结构改变导致的。
我们只需要一个 require 方法就可以完美解决这个问题:
//template
刷新瞧瞧,是不是可以了~
开发和生产的路由配置
配置路由的时候,开发环境下不需要使用 lazy-loading 加载 , 仅在生产环境使用即可,因为开发模式使用 lazy-loading 会导致 webpack 热更新比较慢。
可以创建2个 js ,分别为 _import_development.js 和 _import_production.js 用来加载我们的组件
ps : 我的页面是在 views 文件夹下
// _import_development.js
module.exports = file => require('@/views' + file + '.vue').default;
// _import_production.js
module.exports = file => () => import('@/views/' + file + '.vue')
之后我们可以在我们的路由文件中通过当前运行环境( process.env.NODE_ENV )来加载不同的导入文件js,大概就变成下面的样子了。
// router.js
const _import = require('./_import_' + process.env.NODE_ENV);
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Index',
component: _import('/index')
}
],
mode: 'history'
})
带参数的自定义指令
我们平时书写自定义指令大部分都是以下的这种方式:
Vue.directive('background', {
inserted: function (el) {
// 修改背景色
el.style.backgroundColor = 'red'
}
})
这样可能在某些场景下显得不够灵活,其实我们是可以给指令传递参数的,我们可以将上面的代码改成下面这样:
Vue.directive('background', {
inserted: function (el,binding) {
// 修改背景色
el.style.backgroundColor = binding.value
}
})
其中第二个参数 binding 是一个对象,包含下面这些属性:
name:指令名,不包括 v- 前缀。
value:指令的绑定值,例如:v-background="'red'" 中,绑定值为 red。
oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。
expression :字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
arg :传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }
使用时就可以
//固定值
// com-b
这时候我们在 B 组件中通过 $attrs 就可以获取父组件传递的 title、desc和date,此外我们只要给 C 组件绑定 v-bind='$attrs' , 同理,C 组件内部也就可以通过 $attrs 获取到 A 里面的值了~
ps: B、C 组件的 DOM 上会绑定 A 传过来的属性,Vue 内部默认是这么处理的,要去掉的话给 B、C组件加上 inheritAttrs : false 属性即可。
$listeners 的用法也比较类似,不赘述了~
跨组件通信的另一种方式
想到跨组件通信,可能会想到 eventBus , vuex 之类的方法,实际上我们可以借助 vue 本身的依赖注入这种方案优雅实现
我们首先需要在 main.js 中,定义一个 eventHub , 这是我们的关键点
// main.js
new Vue({
el: '#app',
router,
store,
components: {
App
},
data: {
eventHub: new Vue()
},
template: ' '
})
之后,在我们在需要监控的组件的生命周期中绑定一下:
// com-a
mounted () {
this.$root.eventHub.$on('update',(data)=>{
console.log(data);
})
},
beforeDestroy () {
this.$root.eventHub.$off('update')
}
其他组件要触发改事件只需要一句话:
emitEevent () {
this.$root.eventHub.$emit('update',{ msg : 'hello world' })
}
值得注意的是,一定要在 beforeDestroy 生命周期中通过 $off 取消监听,不然会重复监听导致触发多次,如果只需要触发一次事件的话,$once 绑定会更加不错。
函数式组件
Vue 里的函数式组件和 React 中的无状态组件有些类似,如果说一个组件没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期方法,那么这时候我们可以考虑使用函数式组件。
函数式组件跟普通组件相比,因为没有状态管理,声明周期,只是函数,所以渲染开销低很多,以此可以优化我们的代码。
通常函数式组件的声明方式有2种(局部组件为例):
一种是模版渲染方式加上 functional 关键字创建
/***/
另一种是通过 render 渲染函数,并加上 functional 属性来标识创建,这种方式比模版更接近编译器,更加底层,渲染会更加迅速。
export default {
functional: true,
// Props 是可选的
props: {
// ...
},
render: function (createElement, context) {
// ...
}
}
关于 render 函数,由于篇幅太长,这边不在赘述,想要了解更多细节和配置参数,可以参考官网的解释
由于函数式组件没有实例,为了弥补这个问题,组件需要的一切都是通过 context 参数传递,它是一个包括如下字段的对象:
props:提供所有 prop 的对象
children: VNode 子节点的数组
slots: 一个函数,返回了包含所有插槽的对象
scopedSlots: 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。
data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件
parent:对父组件的引用
listeners: 一个包含了所有父组件为当前组件注册的事件监听器的对象
injections: 如果使用了 inject 选项,则该对象包含了应当被注入的属性。
看起来雨里雾里,其实没那么高深!
我们来个例子更直观,我们现在需要渲染一个列表,没有具体的交互,仅做展示使用,为了优化代码,我们决定使用函数式组件来渲染。
列表数据格式如下:
// list-data
[{
id : 1 ,
title : '学习Vue'
},{
id : 2 ,
title : '学习React'
},{
id : 3 ,
title : '学习Angular'
}]
如果我们通过模版方式来做的的话,我们可以定义如下:
// todoList.vue
Hello World
//动态传值
Hello World
其中 color 绑定的是 data 里面的 color 值。
如果你觉得这么还不够灵活,我想动态修改参数可以吗?当然没问题,请看下面:
Vue.directive('style', {
inserted: function (el,binding) {
el.style[binding.arg] = binding.value;
}
})
这么一来,你想修改啥直接写就是啦。
// 修改背景色
Hello World
如果我想批量修改,so easy 传个对象就好了,如下
Vue.directive('style', {
inserted: function (el,binding) {
for( let key in binding.value){
el.style[key] = binding.value[key]
}
}
})
// 批量修改
Hello World
带参数过滤器
过滤器通常在** 双花括号插值**和 v-bind 表达式 中使用,经常是为了来格式化一些文本之类的。
它跟自定义指令一样,也是可以带参数的,不过过滤器比起指令要简单的多。
假设我们需要将后端传过来的时间戳格式化一下,一般的这么写就可以了:
// ps: 这里引入了一个 moment 包
Vue.filter('formatDate',function (val) {
return moment.unix(val).format('YYYY-MM-DD HH:mm:ss')
})
后来为了让用户可以自定义显示格式,后端增加了一个formate字段,我们不得不修改我们的过滤器,这时候就需要给过滤器加参数,来解决这个问题
Vue.filter('formatDate',function (val,format) {
return moment.unix(val).format(format)
})
调用的时候只需要这么传入即可:
{{ timestamp | formatDate('YYYY/MM/DD HH:mm:ss') }}
过滤器第一个参数仍然是原始的值,YYYY/MM/DD HH:mm:ss 作为第二个参数传到了 format 中,这样的拓展性是不是更好了呢~
$attrs解决数据多级传递
$attrs 官方解释是包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。
理解起来一头雾水,其主要意思是父组件往子组件传没有在props里声明过的值时,子组件可以通过$attrs接受,且只包含父组件没有在props里声明的值。
通常我们如果需要从父组件接收传递很多个值,那么我们就需要在 props 里声明需要接受的值,如果孙子组件也需要,那么就又要重复在props中声明,显得非常繁琐。
例如我们现在有 A,B,C 三个组件,是父子孙的关系,如果 B、C 组件都要从 A 组件中继承一系列的属性:
// com-a
- {{ item.title }}
发表评论 (审核通过后显示评论):