Vue開發中的實用技巧

動態導入本地圖片

假設咱們有這麼一個功能,後臺返回圖片的名稱,前端須要本身拼接路徑獲取本地圖片,假定這些資源是存在咱們前端的 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:指令綁定的前一個值,僅在 updatecomponentUpdated 鉤子中可用。
  • 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解決數據多級傳遞

$attrs 官方解釋是包含了父做用域中不做爲 prop 被識別 (且獲取) 的特性綁定 (classstyle 除外)。當一個組件沒有聲明任何 prop 時,這裏會包含全部父做用域的綁定 (classstyle 除外),而且能夠經過 v-bind="$attrs" 傳入內部組件——在建立高級別的組件時很是有用。

理解起來一頭霧水,其主要意思是父組件往子組件傳沒有在props裏聲明過的值時,子組件能夠經過$attrs接受,且只包含父組件沒有在props裏聲明的值。

一般咱們若是須要從父組件接收傳遞不少個值,那麼咱們就須要在 props 裏聲明須要接受的值,若是孫子組件也須要,那麼就又要重複在props中聲明,顯得很是繁瑣。

例如咱們如今有 ABC 三個組件,是父子孫的關係,若是 BC 組件都要從 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 就能夠獲取父組件傳遞的 titledescdate,此外咱們只要給 C 組件綁定 v-bind='$attrs' , 同理,C 組件內部也就能夠經過 $attrs 獲取到 A 裏面的值了~

ps: BC 組件的 DOM 上會綁定 A 傳過來的屬性,Vue 內部默認是這麼處理的,要去掉的話給 BC組件加上 inheritAttrs : false 屬性便可。

$listeners 的用法也比較相似,不贅述了~

跨組件通訊的另外一種方式

想到跨組件通訊,可能會想到 eventBusvuex 之類的方法,實際上咱們能夠藉助 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-ifv-show 這些指令,看起來更加優雅。

自動化導入component

若是你定義了一系列的牛X的公共組件,然而你每次須要頻繁的去 import xxx from '../xxx',還要

components : {
    ComponentA
    ComponentB
    ...
}
複製代碼

下面有種一勞永逸的方法,讓你解放雙手~

須要藉助 webpack 裏面的 require.context 方法,簡單瞭解一下,經過它獲取一個特定的上下文,而後從中讀取指定目錄下的文件和文件內容。

該方法有三個參數 directoryuseSubdirectoriesuseSubdirectories

  • 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.jsimport 一下就搞定了,如今你能夠在任意組件中調用你的公共組件了!

該方法在批量處理一些文件的時候會有奇效!

以上就是我平常開發中遇到或者總結到的一些東西,若是你有更好更優雅的方式,記得分享哈~

相關文章
相關標籤/搜索