- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) > web開(kāi)發(fā) > JavaScript >
- Vue項目中常用的實(shí)用技巧匯總
在 Vue 項目開(kāi)發(fā)中,很容易產(chǎn)生一些問(wèn)題,比如代碼重復、繁雜等,其實(shí) Vue 項目開(kāi)發(fā)中有很多技巧可以使用,本文將列出一些簡(jiǎn)單且很好用的幾個(gè)技巧,幫助我們寫(xiě)出漂亮的代碼。用到的技術(shù)棧是 Vue2.0 + TypeScript + vue-property-decorator + ElementUI。將用到以下幾個(gè)技巧:
先聊聊如何傳遞 Prop,可以分為靜態(tài)和動(dòng)態(tài)的 Prop:
<!-- 靜態(tài)的prop --> <blog-post title="My journey with Vue"></blog-post> <!-- 動(dòng)態(tài)的prop --> <blog-post v-bind:title="post.title"></blog-post> <!-- 動(dòng)態(tài)的prop傳遞可以簡(jiǎn)寫(xiě)成 --> <blog-post :title="post.title"></blog-post> <!-- 需要傳遞多個(gè)prop的時(shí)候,可以一起寫(xiě)在v-bind上 --> <blog-post v-bind="{ editable, title: post.title}"></blog-post>
了解了 Props 的傳遞方式,在看看官方文檔是怎么定義 $attrs 的, 在尤大大的文檔中這樣介紹了 $attrs:
$attrs: 包含了父作用域中不作為 prop 被識別 (且獲取) 的 attribute 綁定 class 和 style 除外)。當一個(gè)組件沒(méi)有聲明任何 prop 時(shí),這里會(huì )包含所有父作用域的綁定 (class 和 style 除外),并且可以通過(guò) v-bind="$attrs" 傳入內部組件
$attrs 包含了傳入到父作用域中沒(méi)有在 props 聲明的其他 props,因此我們可以用 $attrs 去代替那些父組件中不需要的而子組件需要的 props, 通過(guò) v-bind="$attrs" 統一傳遞給后代。這樣就避免了一個(gè)個(gè)聲明然后再一個(gè)個(gè)傳遞。
<blog-post v-bind="$attrs"></blog-post>
上面這一行代碼就通過(guò) v-bind="$attrs" 的方式將本作用域中不作為 prop 的其他屬性傳遞給了 blog-post 組件。
父組件通過(guò) $attrs 傳遞給后代組件后,后代組件如果想通過(guò)觸發(fā)事件來(lái)更新父組件狀態(tài)該如何處理?如果一級一級地往上 emit 事件,會(huì )不會(huì )弄得代碼太繁瑣復雜了?在 Vue 中可以通過(guò) $listeners 解決這個(gè)問(wèn)題,先看看官方文檔關(guān)于 $listeners 的說(shuō)明:
包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監聽(tīng)器。它可以通過(guò) v-on="$listeners" 傳入內部組件——在創(chuàng )建更高層次的組件時(shí)非常有用。
文檔中說(shuō)了 $listeners 包含了父作用域中的事件監聽(tīng)器。意思就是 $listeners 表示了父組件中的事件監聽(tīng)器集合,只要是觸發(fā)父組件的事件,而不是自己的,就可以用一個(gè) v-on="$listeners"表示。
<!-- 父組件(第一層組件) --> <componentA @on-change="handleChange" v-bind="{ editable, title: post.title}" /> <!-- 中間層的組件 --> <Child v-bind="$attrs" v-on="$listeners"/> <!-- 數據傳遞的目標組件,事件觸發(fā)的組件 --> <div @click="handleClick">{{ title }} </div> <script> export default { props: { title: String } handleClick() { this.$emit('on-change', 'New Title'); } } </script>
上面的代碼示例中,中間層的組件內通過(guò) v-bind="$attrs" 將其余的 Prop 傳遞給了 Child 組件,再通過(guò) v-on="$listeners" 綁定父作用域中的事件監聽(tīng)器,一旦 emit 就會(huì )傳給了父組件。
有很多這樣的場(chǎng)景,父組件需要傳遞數據給子組件,且在子組件觸發(fā)數據更新的時(shí)候,馬上反饋給父組件,父組件數據更新,單向數據流向子組件,最后子組件更新。通常情況用 props + $emit 的方式去更新?tīng)顟B(tài),但是這種處理方式有點(diǎn)笨拙,且不易維護,所以可以通過(guò)實(shí)現數據的“雙向綁定”來(lái)提高代碼的可維護性??梢酝ㄟ^(guò)這以下方式去實(shí)現:
在 v-bind prop的時(shí)候添加 .sync 修飾符,賦新值的時(shí)候用 this.$emit('update:propName', newValue)
<!-- .sync是 v-on:update這種模式的一種縮寫(xiě) --> <Child v-on:update:title="title" /> <!-- 相當于 --> <Child :title.sync="title" />
如果要更新上述代碼中的 title 值,只需要 this.$emit('update:title', '新標題'),完成數據更新。
model 是2.2.0+ 新增的選項,一個(gè)組件上的 v-model 默認會(huì )利用名為 value 的 Prop 和名為 input 的事件, 而 model 選項可以規定 Prop 名稱(chēng)和事件名稱(chēng)來(lái)實(shí)現 v-model,好處是在實(shí)現 v-model 的同時(shí)也避免了 Prop 和事件名的沖突。
<!-- 父組件 --> <Model v-model="checked"/> <!-- Model組件 --> <div @click="handleClick"> <p>自定義組件的 v-model</p> checked {{checked}} </div> <script lang="ts"> export default { model: { prop: 'checked', event: 'change' }, props: { checked: Boolean }, methods: { handleClick() { this.$emit('change', !this.checked); } }
在上述代碼中,只需要在 model 選項中添加 prop 和 event,就可以實(shí)現了 v-model。而在 Vue + TS 項目中 vue-property-decorator 中提供了 Model 的裝飾器,需要這么寫(xiě):
@Model('change', { type: Boolean }) readonly checked!: boolean handleClick() { this.$emit('change', !this.checked); }
只需要通過(guò) .sync 和 model 就可以實(shí)現數據的“雙向綁定”,這樣書(shū)寫(xiě)代碼可以一定程度上減少我們的代碼,而且另代碼變得更優(yōu)雅且可維護。
Mixins 可以用于兩種場(chǎng)景:
首先寫(xiě)一個(gè)公共的 mixin 文件, 把高復用的狀態(tài)和函數寫(xiě)進(jìn)去。
export default class CommonMixins extends Vue{ public paginations = { pageSize: 20, total: 0, currentPage: 1, } handleChangePageSize (pageSize: number, cb: Function) { this.paginations.pageSize = pageSize; cb(); } handleChangePageNum (currentPage: number, cb: Function) { this.paginations.currentPage = currentPage; cb(); } }
vue-property-decorator 提供了 Mixins 的裝飾器,在業(yè)務(wù)頁(yè)面中引入 Mixin 只需要往里 Mixins 傳入 , 可以傳多個(gè),表示混入多個(gè) Mixin。
<script lang="ts"> import { Component, Mixins } from 'vue-property-decorator'; import CommonMixins from "./common-mixin"; import PermissionMixins from "./permission-mixin"; @Component({}) export default class Parent extends Mixins(CommonMixins, PermissionMixins) { } </script>
如果只需要一個(gè)的話(huà),也可以直接繼承
<script lang="ts"> import { Component, Mixins } from 'vue-property-decorator'; import CommonMixins from "./common-mixin"; @Component({}) export default class Parent extends CommonMixins { } </script>
在遇到功能點(diǎn)多,代碼量大的頁(yè)面時(shí),我們可以利用 Mixin 抽離一些功能點(diǎn),通過(guò)文件去管理這些功能,這樣會(huì )比較方便去管理代碼。
組件在加載都是同步的,但當頁(yè)面內容很多,有些組件并不需要一開(kāi)始就加載出來(lái)的比如彈窗類(lèi)的組件,這些就可以用動(dòng)態(tài)組件,當用戶(hù)執行了某些操作后再加載出來(lái),這樣可以提高主模塊加載的性能, 在 Vue 中可以使用 component 動(dòng)態(tài)組件, 依 is 的值,來(lái)決定哪個(gè)組件被渲染。
<template> <div> 主頁(yè)面 <br/> <button @click="handleClick1">點(diǎn)擊記載組件1</button><br/> <button @click="handleClick2">點(diǎn)擊記載組件2</button><br/> <component :is="child1"></component> <component :is="child2"></component> </div> </template> <script lang="ts"> import { Component, Vue } from 'vue-property-decorator'; @Component({}) export default class AsyncComponent extends Vue { public child1:Component = null; public child2:Component = null; handleClick1() { this.child1 = require('./child1').default; } handleClick2() { this.child2 = require('./child2').default; } } </script>
示例代碼中,只有當點(diǎn)擊的時(shí)候才會(huì )去加載組件。component 還可以配合 v-show 去控制顯示和隱藏,這樣這個(gè)component 只會(huì ) mounted 一次,優(yōu)化性能。
有很多場(chǎng)景想更改 UI 組件樣式,然后怕影響別人的使用,加上 scoped 后又不能生效,可以使用 ::v-deep 深度作用選擇器去修改組件作用域內的 CSS 的樣式。在 CSS 中我們可以使用 >>> 操作符,但在預處理器中的寫(xiě)法就要用 /deep/ 或 ::v-deep。
<style scoped> >>> .ivu-tabs-tabpane { background: #f1f1f1; } </style> <style lang="scss" scoped> /deep/ .ivu-tabs-tabpane { background: #f1f1f1; } </style> <style lang="scss" scoped> ::v-deep .ivu-tabs-tabpane { background: #f1f1f1; } </style>
::v-deep 和 /deep/ 作用是一樣的,但不推薦使用 /deep/, 在 Vue3.0 中將不支持 /deep/ 這種寫(xiě)法。
裝飾器增加了代碼的可讀性,清晰地表達了意圖,而且提供一種方便的手段,增加或修改類(lèi)的功能,比如給類(lèi)其中的方法提供防抖的功能。
import debounce from 'lodash.debounce'; export function Debounce(delay: number, config: object = {}) { return (target: any, prop: string) => { return { value: debounce(target[prop], delay, config), }; }; }
這樣的好處是使用起來(lái)非常方便,另外增加了代碼的可讀性。
@Debounce(300) onIdChange(val: string) { this.$emit('idchange', val); }
關(guān)于 require.context,webpack 文檔是這么描述的:
可以給這個(gè)函數傳入三個(gè)參數:一個(gè)要搜索的目錄,一個(gè)標記表示是否還搜索其子目錄, 以及一個(gè)匹配文件的正則表達式。
webpack 會(huì )在構建中解析代碼中的 require.context() 。如果想引入一個(gè)文件夾下面的所有文件,或者引入能匹配一個(gè)正則表達式的所有文件,這個(gè)功能就會(huì )很有幫助
根據這個(gè)提示,我們可以引用到一個(gè)文件夾下面的所有文件,由此可以利用獲取的文件信息去做一些操作,比如在注冊組件的時(shí)候,原本我們注冊組件的時(shí)候需要一個(gè)個(gè)引入并且一個(gè)個(gè)注冊,而且后面想加新的,又要再寫(xiě)上,現在可以通過(guò) require.context 去優(yōu)化這一段代碼。
// import WmsTable from './wms-table/table/index'; import Table from './table/index.vue'; import CustomHooks from './custom-hooks/custom-hooks-actions/index'; import SFilter from './s-filter/filter-form'; import WButton from './button/index'; import CreateForm from './createForm/create-form/CreateForm.vue'; import Action from './table/action-table-column.vue'; import DetailItem from './detail-item.vue'; Vue.component('w-filter', SFilter); Vue.component('w-button', WButton); Vue.component('custom-hooks', CustomHooks); Vue.component('create-form', CreateForm); Vue.component('w-table', Table); Vue.component('w-table-action', Action); Vue.component('zonetime-date-picker', ZonetimeDatePicker); Vue.component('detail', DetailItem);
注冊全局組件的時(shí)候,不需要一個(gè)一個(gè) import,和一個(gè)個(gè)去注冊,使用 require.context 可以自動(dòng)導入模塊,這樣的好處在于,當我們新建一個(gè)組件,不用自己再去手寫(xiě)注冊,而在一開(kāi)始就幫我們自動(dòng)完成。
const contexts = require.context('./', true, /\.(vue|ts)$/); export default { install (vm) { contexts.keys().forEach(component => { const componentEntity = contexts(component).default; if (componentEntity.name) { vm.component(componentEntity.name, componentEntity); } }); } };
本文介紹了在 Vue 實(shí)戰中經(jīng)常用到的一些技巧,這些技巧的目的都是為了提升開(kāi)發(fā)效率,比如簡(jiǎn)單地實(shí)現雙向數據綁定和數據跨級傳遞等,另外也可以提高代碼的可維護性、可讀性,比如很實(shí)用的裝飾器和利用 Mixin 拆分代碼和管理功能點(diǎn)。
到此這篇關(guān)于Vue項目中常用的實(shí)用技巧匯總的文章就介紹到這了,更多相關(guān)Vue項目常用技巧內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。
Copyright ? 2009-2022 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 特網(wǎng)科技 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 百度云 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站