假設咱們有這麼一個功能,後臺返回圖片的名稱,前端須要本身拼接路徑獲取本地圖片,假定這些資源是存在咱們前端的 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
一下就搞定了,如今你能夠在任意組件中調用你的公共組件了!
該方法在批量處理一些文件的時候會有奇效!
以上就是我平常開發中遇到或者總結到的一些東西,若是你有更好更優雅的方式,記得分享哈~