構(gòu)建Vue大型應(yīng)用的10個(gè)最佳實(shí)踐(小結(jié))
這些是我構(gòu)建大型Vue項(xiàng)目得出的最佳實(shí)踐,這些技巧將幫助你更高效的編碼,并且使其更容易維護(hù)和協(xié)作。
在我今年的自由職業(yè)生涯中我有幸開(kāi)發(fā)了一些大型Vue程序。我所說(shuō)的這些項(xiàng)目使用了大量Vuex stores 😰 ,很多Vue組件(有數(shù)百個(gè))和很多視圖(pages)😄。對(duì)我而言這是非常有意義的經(jīng)歷,我發(fā)現(xiàn)了很多使擴(kuò)展的方法。同時(shí)我還需要修復(fù)一些亂七八糟的錯(cuò)誤用法。🍝
所以我將分享我的10個(gè)最佳實(shí)踐,如果你有大型項(xiàng)目需要開(kāi)發(fā)推薦你使用他們。
1. 使用Slots可以使你的組件更強(qiáng)大和便于理解。
最近我寫(xiě)了一篇文章some important things you need to know regarding slots in Vue.js。 主要講了為什么使用Slots可以提高組件復(fù)用且易于維護(hù)。
但是這和大型Vue項(xiàng)目有啥關(guān)系呢。我將描繪一個(gè)使用他們解決你痛點(diǎn)的藍(lán)圖。
假如我要開(kāi)發(fā)一個(gè)popup。起初看起來(lái)沒(méi)有什么難點(diǎn),僅僅是包括標(biāo)題,描述和一些按鈕。 所以把所有東西都當(dāng)作props不就完了嗎。 我用了三個(gè)自定義props,并且click按鈕的時(shí)候觸發(fā)一個(gè)事件。 就是這么簡(jiǎn)單! 😅
隨著項(xiàng)目的不斷發(fā)展,業(yè)務(wù)需要顯示許多其他新內(nèi)容:表單字段,不同的按鈕(取決于顯示在哪個(gè)頁(yè)面上)、卡片、頁(yè)腳和列表。通過(guò)添加更多的props可以解決這個(gè)問(wèn)題。😩但是隨著業(yè)務(wù)發(fā)展,組件變的太復(fù)雜了。因?yàn)樗撕芏嘧咏M件,需要觸發(fā)很多個(gè)事件。🌋我遇到了坑爹的問(wèn)題,修改了一個(gè)功能后影響了其他page上的功能。我創(chuàng)造了一個(gè)怪物而不是一個(gè)可維護(hù)的組件!
但是,一開(kāi)始使用slots可能會(huì)更好。最終我使小組件來(lái)重構(gòu)這個(gè)組件。使他更容易維護(hù)、好理解、好擴(kuò)展。
<template>
<div class="c-base-popup">
<div v-if="$slot.header" class="c-base-popup__header">
<slot name="header">
</div>
<div v-if="$slot.subheader" class="c-base-popup__subheader">
<slot name="subheader">
</div>
<div class="c-base-popup__body">
<h1>{{ title }}</h1>
<p v-if="description">{{ description }}</p>
</div>
<div v-if="$slot.actions" class="c-base-popup__actions">
<slot name="actions">
</div>
<div v-if="$slot.footer" class="c-base-popup__footer">
<slot name="footer">
</div>
</div>
</template>
<script>
export default {
props: {
description: {
type: String,
default: null
},
title: {
type: String,
required: true
}
}
}
</script>
根據(jù)經(jīng)驗(yàn)在我看來(lái),由熟練使用slots的開(kāi)發(fā)人員構(gòu)建項(xiàng)目對(duì)將來(lái)項(xiàng)目的可維護(hù)性有更重要的意義。
⚠️ 經(jīng)驗(yàn)規(guī)則,當(dāng)子組件中使用了父組件的props時(shí),應(yīng)該使用slots。
2.好好組織的你Vuex store
通常,一個(gè)新的Vue開(kāi)發(fā)人員學(xué)習(xí)Vuex是因?yàn)閮蓚€(gè)問(wèn)題:
- 當(dāng)一個(gè)組件需要重一個(gè)比較遠(yuǎn)的組件(嵌套層級(jí)或者兄弟組件:譯者注)訪(fǎng)問(wèn)數(shù)據(jù)時(shí)。
- 需要持有一個(gè)銷(xiāo)毀組件的數(shù)據(jù)時(shí)。
這樣他創(chuàng)建第一個(gè)Vuex store,學(xué)習(xí)moudles的用法并且組織程序時(shí)。💡
問(wèn)題是創(chuàng)建modules時(shí)沒(méi)有單一模式可循。我強(qiáng)烈建議一定要想清楚如何組織他們。據(jù)我所知,很多人更喜歡按照功能來(lái)組織他們(我也是:譯者注)。例如:
- Auth.
- Blog.
- Inbox.
- Settings.
就我來(lái)說(shuō),用從API獲得的數(shù)據(jù)模型組織更容易理解。例子:
- Users
- Teams
- Messages
- Widgets
- Articles
用那個(gè)取決于自己,但是從長(zhǎng)遠(yuǎn)來(lái)看組織良好的Vuex store更具生產(chǎn)力。這樣也容易是新人融入并且繼承你的初衷。
3.用Actions調(diào)用api和提交數(shù)據(jù)。
我的大部分(不是全部)API調(diào)用都在Vuex的actions中,你一定想知道為什么這樣做。🤨
簡(jiǎn)單的說(shuō)是因?yàn)槔?shù)據(jù)時(shí)需要在store中commit。還有就是他提供了我喜歡的封裝和復(fù)用。其他原因就是:
- 如果我在兩個(gè)地方(blog和home page)獲取第一個(gè)頁(yè)面的數(shù)據(jù)。我只需使用不同的參數(shù)調(diào)用即可得到數(shù)據(jù),初次之外沒(méi)有重復(fù)的代碼被調(diào)用。
- 如果需要?jiǎng)?chuàng)建一些邏輯來(lái)避免重復(fù)拉取數(shù)據(jù),只需要在一個(gè)地方拉取一次。除了給服務(wù)器減負(fù)以外,我還可以在任何地方使用這些數(shù)據(jù)。
- 我可以在actions中最終Mixpanel事件,基于維護(hù)性使問(wèn)題變的容易分析。我的代碼中大部分action中只有一個(gè)Mixpanel調(diào)用,😂我不必關(guān)注數(shù)據(jù)和發(fā)送這種工作方式太爽了。
4.使用mapState、mapGetters、mapMutations和mapAction精簡(jiǎn)代碼。
當(dāng)你在組件中訪(fǎng)問(wèn)state/getters或者調(diào)用actions/mutations時(shí)通常需要?jiǎng)?chuàng)建多個(gè)計(jì)算屬性。使用mapState,mapGetters,mapMutations和mapActions可以將來(lái)自store modules中的數(shù)據(jù)集中在一個(gè)地方,這樣可以精簡(jiǎn)代碼并且更好理解。
// NPM
import { mapState, mapGetters, mapActions, mapMutations } from "vuex";
export default {
computed: {
// Accessing root properties
...mapState("my_module", ["property"]),
// Accessing getters
...mapGetters("my_module", ["property"]),
// Accessing non-root properties
...mapState("my_module", {
property: state => state.object.nested.property
})
},
methods: {
// Accessing actions
...mapActions("my_module", ["myAction"]),
// Accessing mutations
...mapMutations("my_module", ["myMutation"])
}
};
你想要的這里都有Vuex官方文檔。
5. 快使用API Factories
我通常會(huì)創(chuàng)建this.$api助手,可以在任何地方訪(fǎng)問(wèn)我的API入口。我的項(xiàng)目根目錄有一個(gè)api文件夾有我的所有類(lèi)(如下)。
api ├── auth.js ├── notifications.js └── teams.js
每一個(gè)都是一類(lèi)接口的分組,這是我在Nuxt應(yīng)用中使用插件的方式初始化。(和標(biāo)準(zhǔn)Vue應(yīng)用程序中的過(guò)程非常相似)。
// PROJECT: API
import Auth from "@/api/auth";
import Teams from "@/api/teams";
import Notifications from "@/api/notifications";
export default (context, inject) => {
if (process.client) {
const token = localStorage.getItem("token");
// Set token when defined
if (token) {
context.$axios.setToken(token, "Bearer");
}
}
// Initialize API repositories
const repositories = {
auth: Auth(context.$axios),
teams: Teams(context.$axios),
notifications: Notifications(context.$axios)
};
inject("api", repositories);
};
export default $axios => ({
forgotPassword(email) {
return $axios.$post("/auth/password/forgot", { email });
},
login(email, password) {
return $axios.$post("/auth/login", { email, password });
},
logout() {
return $axios.$get("/auth/logout");
},
register(payload) {
return $axios.$post("/auth/register", payload);
}
});
這樣我可以方便的在組件或Vuex操作中調(diào)用他們,如下:
export default {
methods: {
onSubmit() {
try {
this.$api.auth.login(this.email, this.password);
} catch (error) {
console.error(error);
}
}
}
};
6. 使用$config訪(fǎng)問(wèn)環(huán)境變量(模板中特別有用)。
項(xiàng)目中定義了一些全局配置變量:
config ├── development.json └── production.json
我通常使用this.$config獲取,尤其是當(dāng)我在模板中時(shí)。 一如既往擴(kuò)展Vue對(duì)象非常容易:
// NPM
import Vue from "vue";
// PROJECT: COMMONS
import development from "@/config/development.json";
import production from "@/config/production.json";
if (process.env.NODE_ENV === "production") {
Vue.prototype.$config = Object.freeze(production);
} else {
Vue.prototype.$config = Object.freeze(development);
}
7.遵守一個(gè)commit命名規(guī)則。
在項(xiàng)目發(fā)展的過(guò)程中,經(jīng)常需要關(guān)注組件的變更歷史。如果團(tuán)隊(duì)中有人沒(méi)有遵守commit慣例,那么將很難理解他們所做的事情。
我總是使用并推薦Angular commit message guidelines。我所有的項(xiàng)目中都會(huì)使用他,通常團(tuán)隊(duì)中的其他人也會(huì)發(fā)現(xiàn)他的好處。
遵守這些規(guī)則使commit更加可讀,在查看項(xiàng)目歷史時(shí)使得commit更加容易追蹤。簡(jiǎn)言之,他是這樣用的:
git commit -am "<type>(<scope>): <subject>" # Here are some samples git commit -am "docs(changelog): update changelog to beta.5" git commit -am "fix(release): need to depend on latest rxjs and zone.js"
看他們的README file更新更多。
8.始終在生產(chǎn)環(huán)境中凍結(jié)Package的版本。
我知道...所有軟件包都應(yīng)遵循 the semantic versioning rules.。但實(shí)際情況并非如此。😅
為了避免一個(gè)依賴(lài)影響了整個(gè)項(xiàng)目在半夜被拖起來(lái),凍結(jié)所有程序包的版本可以使你一覺(jué)睡到天明并且工作愉快。 😇
這很簡(jiǎn)單:避免以^開(kāi)頭的版本:
{
"name": "my project",
"version": "1.0.0",
"private": true,
"dependencies": {
"axios": "0.19.0",
"imagemin-mozjpeg": "8.0.0",
"imagemin-pngquant": "8.0.0",
"imagemin-svgo": "7.0.0",
"nuxt": "2.8.1",
},
"devDependencies": {
"autoprefixer": "9.6.1",
"babel-eslint": "10.0.2",
"eslint": "6.1.0",
"eslint-friendly-formatter": "4.0.1",
"eslint-loader": "2.2.1",
"eslint-plugin-vue": "5.2.3"
}
}
9. 顯示一個(gè)大的數(shù)據(jù)時(shí)應(yīng)該使用Vue虛擬滾動(dòng)條。
在頁(yè)面中顯示多行或需要循環(huán)大量數(shù)據(jù)時(shí),你已經(jīng)注意到該頁(yè)面渲染速度很快變慢。 要解決此問(wèn)題,您可以使用vue-virtual-scoller。
npm install vue-virtual-scroller
他只渲染列表中的可見(jiàn)項(xiàng)并且復(fù)用組件和dom元素,以使其盡可能高效。 如此簡(jiǎn)單就像一個(gè)魔法! ✨
<template>
<RecycleScroller
class="scroller"
:items="list"
:item-size="32"
key-field="id"
v-slot="{ item }"
>
<div class="user">
{{ item.name }}
</div>
</RecycleScroller>
</template>
10.追蹤第三方程序包的大小
多人合作一個(gè)項(xiàng)目時(shí),如果沒(méi)人關(guān)注安裝的依賴(lài)包數(shù)量很快變的難以置信。為了避免程序變慢(尤其是在移動(dòng)網(wǎng)絡(luò)環(huán)境),我這VSC中使用import cost package這樣就可以編輯器中看到導(dǎo)入的包有多大,并且找出大的原因。
例如在最近的項(xiàng)目中,導(dǎo)入了整個(gè)lodash庫(kù)(壓縮后24kB)。 有啥子問(wèn)題? 僅僅使用cloneDeep方法。 通過(guò)import cost package找到了問(wèn)題,我們通過(guò)以下方式解決了該問(wèn)題:
npm remove lodash npm install lodash.clonedeep
在使用的地方導(dǎo)入:
import cloneDeep from "lodash.clonedeep";
為了進(jìn)一步優(yōu)化,我們還可以使用Webpack Bundle Analyzer包通過(guò)樹(shù)狀圖來(lái)可視化Webpack輸出文件的大小。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 用vue構(gòu)建多頁(yè)面應(yīng)用的示例代碼
- vue構(gòu)建單頁(yè)面應(yīng)用實(shí)戰(zhàn)
- 新手vue構(gòu)建單頁(yè)面應(yīng)用實(shí)例代碼
- vue2.0構(gòu)建單頁(yè)應(yīng)用最佳實(shí)戰(zhàn)
- 使用 vue.js 構(gòu)建大型單頁(yè)應(yīng)用
- 詳解vue-cli快速構(gòu)建vue應(yīng)用并實(shí)現(xiàn)webpack打包
- 使用vue構(gòu)建移動(dòng)應(yīng)用實(shí)戰(zhàn)代碼
- 使用typescript構(gòu)建Vue應(yīng)用的實(shí)現(xiàn)
相關(guān)文章
Vue實(shí)現(xiàn)兩種路由權(quán)限控制方式
路由權(quán)限控制常用于后臺(tái)管理系統(tǒng)中,對(duì)不同業(yè)務(wù)人員能夠訪(fǎng)問(wèn)的頁(yè)面進(jìn)行一個(gè)權(quán)限的限制。本文主要介紹了兩種Vue 路由權(quán)限控制,具有一定的參考價(jià)值,感興趣的可以了解一下2021-10-10
Vue3中Composition?API和Options?API的區(qū)別
Vue3的Composition API和Options API是Vue.js框架中的兩種不同的API,本文主要介紹了Vue3中Composition?API和Options?API的區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
解決vue-cli項(xiàng)目打包出現(xiàn)空白頁(yè)和路徑錯(cuò)誤的問(wèn)題
今天小編就為大家分享一篇解決vue-cli項(xiàng)目打包出現(xiàn)空白頁(yè)和路徑錯(cuò)誤的問(wèn)題。具有很好的參考價(jià)值。希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09
Vue3中使用reactive時(shí),后端有返回?cái)?shù)據(jù)但dom沒(méi)有更新的解決
這篇文章主要介紹了Vue3中使用reactive時(shí),后端有返回?cái)?shù)據(jù)但dom沒(méi)有更新的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
vue 彈框產(chǎn)生的滾動(dòng)穿透問(wèn)題的解決
這篇文章主要介紹了vue 彈框產(chǎn)生的滾動(dòng)穿透問(wèn)題,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-09-09
Vue3中解決組件間css層級(jí)問(wèn)題的最佳實(shí)踐
<Teleport> 是 Vue 3 中引入的一個(gè)內(nèi)置組件,用于將組件的內(nèi)容渲染到 DOM 中的指定位置,而不受組件層級(jí)結(jié)構(gòu)的限制,本文給大家介紹了Vue3使用Teleport解決組件間css層級(jí)問(wèn)題的最佳實(shí)踐,需要的朋友可以參考下2025-02-02

