假设咱们有这么一个功能,后台返回图片的名称,前端须要本身拼接路径获取本地图片,假定这些资源是存在咱们前端的 assets/images
,若是你采起传统的字符串拼接的方式:html
//template
<img :src="'@/assets/images' + imgUrl" alt="">
复制代码
发现图片没法显示,打开控制台审查元素,发现路径并无正确解析,这个跟 webpack
编译打包有关系,在编译过程当中目录结构改变致使的。前端
咱们只须要一个 require
方法就能够完美解决这个问题:vue
//template
<img :src="require('@/assets/images' + imgUrl)" alt="">
复制代码
刷新瞧瞧,是否是能够了~webpack
配置路由的时候,开发环境下不须要使用 lazy-loading
加载 , 仅在生产环境使用便可,由于开发模式使用 lazy-loading
会致使 webpack
热更新比较慢。web
能够建立2个 js
,分别为 _import_development.js
和 _import_production.js
用来加载咱们的组件vue-router
ps
: 个人页面是在 views
文件夹下vuex
// _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
,大概就变成下面的样子了。express
// 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 }
使用时就能够
//固定值
<div class="" v-background="'red'">Hello World</div>
//动态传值
<div class="" v-background="color">Hello World</div>
复制代码
其中 color
绑定的是 data
里面的 color
值。
若是你以为这么还不够灵活,我想动态修改参数能够吗?固然没问题,请看下面:
Vue.directive('style', {
inserted: function (el,binding) {
el.style[binding.arg] = binding.value;
}
})
复制代码
这么一来,你想修改啥直接写就是啦。
// 修改背景色
<div class="" v-style:[`background`]="'red'">Hello World</div>
复制代码
若是我想批量修改,so easy
传个对象就行了,以下
Vue.directive('style', {
inserted: function (el,binding) {
for( let key in binding.value){
el.style[key] = binding.value[key]
}
}
})
// 批量修改
<div class="" v-style="{ color : 'white' , background : 'red'}">Hello World</div>
复制代码
过滤器一般在** 双花括号插值**和 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)
})
复制代码
调用的时候只须要这么传入便可:
<div class="">{{ timestamp | formatDate('YYYY/MM/DD HH:mm:ss') }}</div>
复制代码
过滤器第一个参数仍然是原始的值,YYYY/MM/DD HH:mm:ss
做为第二个参数传到了 format
中,这样的拓展性是否是更好了呢~
$attrs
官方解释是包含了父做用域中不做为 prop
被识别 (且获取) 的特性绑定 (class
和 style
除外)。当一个组件没有声明任何 prop
时,这里会包含全部父做用域的绑定 (class
和 style
除外),而且能够经过 v-bind="$attrs"
传入内部组件——在建立高级别的组件时很是有用。
理解起来一头雾水,其主要意思是父组件往子组件传没有在props
里声明过的值时,子组件能够经过$attrs
接受,且只包含父组件没有在props
里声明的值。
一般咱们若是须要从父组件接收传递不少个值,那么咱们就须要在 props
里声明须要接受的值,若是孙子组件也须要,那么就又要重复在props
中声明,显得很是繁琐。
例如咱们如今有 A
,B
,C
三个组件,是父子孙的关系,若是 B
、C
组件都要从 A
组件中继承一系列的属性:
// com-a
<template>
<com-b :title='title' :desc='desc' :date='date'></com-b>
</template>
// com-b
<template>
<com-c v-bind='$attrs'></com-c>
</template>
复制代码
这时候咱们在 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: '<App/>'
})
复制代码
以后,在咱们在须要监控的组件的生命周期中绑定一下:
// 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
关键字建立
<template functional>
/***/
</template>
复制代码
另外一种是经过 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
<template functional>
<ul>
<li v-for="item in props.todoList">{{ item.title }}</li>
</ul>
</template>
复制代码
若是咱们经过 render
函数来建立的话(.js
文件,不是 .vue
文件), 那么应该是这样:
export default {
functional: true,
props: {
todoList: {
type: Array,
default: () => []
}
},
render: function (createElement, context) {
let oLi = context.props.todoList.map(item => {
return createElement('li', {
domProps: {
innerHTML: item.title
}
})
})
return createElement('ul', {}, oLi)
}
}
复制代码
咱们经过 import
在父组件导入一下,而后看看结果
效果同样,虽然 template
方式看起来简单的多,可是不少时候我仍是比较倾向于 render
方式,由于它在修改或者条件判断的时候会比较方便,也省去了很多 v-if
,v-show
这些指令,看起来更加优雅。
若是你定义了一系列的牛X的公共组件,然而你每次须要频繁的去 import xxx from '../xxx'
,还要
components : {
ComponentA
ComponentB
...
}
复制代码
下面有种一劳永逸的方法,让你解放双手~
须要借助 webpack
里面的 require.context
方法,简单了解一下,经过它获取一个特定的上下文,而后从中读取指定目录下的文件和文件内容。
该方法有三个参数 directory
,useSubdirectories
和useSubdirectories
directory {String}
-读取文件的路径useSubdirectories {Boolean}
-是否遍历文件的子目录regExp {RegExp}
-匹配文件的正则若是我要遍历 components
目录下的全部公共组件,我就能够这么作:
首先在components
目录下建立一个 baseComponent.js
文件,
// baseComponent.js
import Vue from 'vue'
const autoRequireComponent = require.context('./', false, /.vue$/)
autoRequireComponent.keys().forEach(file => {
//获取组件配置信息
const componentConfig = autoRequireComponent(file).default
//获取组件的名称 , 将 ./UButton.vue 名称替换成 UButton
const componentName = file.replace(/^\.\//, '').replace(/\.\w+$/, '')
//注册组件
Vue.component(componentName, componentConfig)
})
复制代码
其中 autoRequireComponent.keys()
返回的就是一个文件名称的数组,相似于 ['UButton','UInput']
,而后遍历并经过 Vue.component
方法注册到全局
最后在 main.js
中 import
一下就搞定了,如今你能够在任意组件中调用你的公共组件了!
该方法在批量处理一些文件的时候会有奇效!
以上就是我平常开发中遇到或者总结到的一些东西,若是你有更好更优雅的方式,记得分享哈~