Vue.js 2.0學(xué)習(xí)教程之從基礎(chǔ)到組件詳解
前言
最近這段時間里不停的做著Vue的技術(shù)分享,雖然不是什么深層次的代碼底能架構(gòu),如果底層架構(gòu)真說出來,我就不會做Vue.js 2.0從基礎(chǔ)到組件了,就會分享Vue從底層到還是到底層。我相信能來看我這次分享的朋友都是在工作開發(fā)層面上面臨著一些問題。此次分享我們先拋開Vue - router和Vuex,很多朋友都覺得Vuex和router比較難,大錯特錯!
對Vue 2.0的介紹
我對Vue 2.0認(rèn)知,能it前端框架的認(rèn)知,在我們不算底層原理的情況下,什么才是精髓,那就是基礎(chǔ)的方法和一些api介紹,隨著現(xiàn)代數(shù)據(jù)量龐大,業(yè)務(wù)邏輯也變得更加復(fù)雜,隨著數(shù)據(jù)情景的不同展示,jquery,angular1.0等一系列框架,已經(jīng)滿足不了開發(fā)的需求了,如何用數(shù)據(jù)驅(qū)動去管理數(shù)據(jù),在我認(rèn)知里,前后端聯(lián)調(diào),對接口,通過什么?那過json數(shù)據(jù)來傳遞著一切的信息。
我們操作dom來分析數(shù)據(jù),那就是用屠龍刀去切菜,用數(shù)據(jù)驅(qū)動去改變數(shù)據(jù),那才叫細(xì)功出好活。還有我們?nèi)绾胃玫耐ㄟ^組件來讓一個復(fù)雜的頁面劃分為代碼精簡,易維護(hù),可復(fù)用,闊展性強(qiáng)的組件集合!
如果是你如何去劃分這個頁面
- 對頭部進(jìn)行一個組件的化分。(通過
prop:{[img]}
來進(jìn)行頭圖的劃分。) - input框的劃分。
- 圖型驗(yàn)證碼和圖型input框。
- 短信組件和短信input框。
- 協(xié)議服務(wù)的彈窗。
- button的劃分。
- 語音驗(yàn)證碼的劃分。
這樣一算你們會發(fā)現(xiàn)一個小小的登陸就劃分出6個組件,可能給你的感覺分的太細(xì),那我也感覺分的太細(xì),那我為什么要分的那么細(xì)呢,那就是增強(qiáng)可復(fù)用性,可闊展性。
那我何去解這個組件太過于細(xì)分的問題,我們可以合并那些東西,以我一眼看過去,唯一能合并的就是中間一套注冊體系,我們把2,3,4,5,7,這幾個細(xì)組件合并到login.Vue組件里,在這個層面上,我們只要暴露出四個輸入框內(nèi)容向外傳遞的數(shù)據(jù),這樣一個頁面整體就我們拆開來了,對于每個頁面的代碼量就減少了,對于維護(hù),改bug是一個很大的幫助。
組件從基礎(chǔ)開始
Vue的在項(xiàng)目中如何去做好一個體系問題,最主要的就是template里整體的組織,如何用好的組織體系方便的展現(xiàn)復(fù)雜的邏輯操作,我個人認(rèn)為而不是通過new Vue去操控整體,反正new Vue里的一切選項(xiàng)是著template這個組織體系走的,如果是一個房子,template就是地基,new Vue里的選項(xiàng)就是水泥石頭。
1、模板語法
能用javascript表達(dá)式則用表達(dá)式,我覺得表達(dá)式是給人感覺最清楚 的,能結(jié)合模板去正確使用表達(dá)式來解析那是最明了的。
<p @click='show = false'></p>
上面一眼就能讓人明白,不用往下看就明白我所要改變數(shù)據(jù)是為了什么,就這一行模板語法用javascript表達(dá)式寫讓你能明白一切。
2、修飾賦
修飾符(Modifiers)是以半角句號 . 指明的特殊后綴,用于指出一個指令應(yīng)該以特殊方式綁定。
在修飾賦當(dāng)中,我們?nèi)绾戊`活運(yùn)用修飾符去減速少代碼量,不要忘記對于組件事件要加.native。比如對組織事件冒泡和阻止默認(rèn)事件都是很方便書寫,直在template中書,例如@touchmove.prevent.stop。
雖然計算屬性在大多數(shù)情況下更合適,但有時也需要一個自定義的 watcher 。這是為什么 Vue 提供一個更通用的方法通過 watch 選項(xiàng),來響應(yīng)數(shù)據(jù)的變化。當(dāng)你想要在數(shù)據(jù)變化響應(yīng)時,執(zhí)行異步操作或開銷較大的操作,這是很有用的。
基于這個官方的理解再總結(jié)我個人的整體理解。給出computed和watch的總結(jié),記住這幾點(diǎn)的總結(jié),在做項(xiàng)目的時候想想這些總結(jié),選擇你的應(yīng)用方法
- computed : 多個數(shù)據(jù)或者一個數(shù)據(jù)來維護(hù)返回一個狀態(tài)值 ,只要其中一個或多個數(shù)據(jù)發(fā)生了變化,則會從新計算整個函數(shù)體,從新返回狀態(tài)值。
- watch : 只有一個一個,只要這個數(shù)據(jù)發(fā)生變化,就會在返回兩個參數(shù),第一個是當(dāng)前的值,第二個是變化前的值,每當(dāng)變化的時候,則會觸發(fā)函數(shù)體的里的邏輯行為,來進(jìn)邏輯后續(xù)操作。
其實(shí)我覺得計算屬性也好,computed,watch這幾個都不是有多難,如果淺層面上看很容易理解,如果從深層面上看,很多小伙伴會存在什么問題,就是會濫用,混用,這些計算屬性,雖然最后結(jié)果都能實(shí)現(xiàn),就像條條大路通羅馬,你走的是最遠(yuǎn)的路,想返回可能都難
我就舉以上幾個簡單的例子,但是我想說的就是兩點(diǎn)基礎(chǔ)非常重要。
要如何結(jié)合三大特性:
- computed vs watch
- v-for
- v-if v-else-if v-else
- template外層包裹模板
如果你看到這個需求,你花3分種時間如何去用以上三大特性做出兩種解決方法你就是Vue精通者。
- 用v-for進(jìn)行數(shù)據(jù)循環(huán),用template包v-if 進(jìn)行標(biāo)紅和排名標(biāo)識。
- 用v-fot進(jìn)行數(shù)據(jù)循環(huán),用methods結(jié)合:class進(jìn)行票紅和排名標(biāo)識。
我還是感覺第二個方便點(diǎn)。
如何去寫組件,從什么地方開始寫起,如果你對基礎(chǔ)api已經(jīng)了如指掌了,那你就可以開手動組件了,組件要學(xué)會利用幾個組件的很大的特性,最大的特性我就不用說了,用props接數(shù)據(jù),用$emit去觸發(fā)事件 用v-on去接收自定義事件,有時候你會發(fā)現(xiàn)有時候通過父組件傳遞來的數(shù)據(jù),我們在組件內(nèi)部一直需要改動,那我們不得不用watch方法去復(fù)制一個復(fù)本,再進(jìn)行操作,那我們有什么好辦法,如更面臨更多組件套組件的模式下,如果利用特性api去做最簡便的寫法,那就是$parent、$children、$root、$el、$refs 這五大特性。
組件規(guī)范
- 基于模塊開發(fā)
- Vue 組件命名
- 組件表達(dá)式簡單化
- 組件 props 原子化
- 驗(yàn)證組件的 props
- 將 this 賦值給 component 變量
- 組件結(jié)構(gòu)化
- 組件事件命名
- 避免 this.$parent
- 謹(jǐn)慎使用 this.$refs
- 使用組件名作為樣式作用域空間
- 提供組件 API 文檔
- 提供組件 demo
- 對組件文件進(jìn)行代碼校驗(yàn)
基于模塊開發(fā)
始終基于模塊的方式來構(gòu)建你的 app,每一個子模塊只做一件事情。
Vue.js 的設(shè)計初衷就是幫助開發(fā)者更好的開發(fā)界面模塊。一個模塊是應(yīng)用程序中的一個部分。
怎么做?
每一個 Vue 組件(等同于模塊)首先必須專注于解決一個單一的問題,的, 可復(fù)用的, 微小的 and 可測試的。
如果你的組件做了太多的事或是變得臃腫,請將其拆分成更小的組件并保持單一的原則。一般來說,盡量保證每一個文件的代碼行數(shù)不要超過 100 行。也請保證組件可的運(yùn)行。比較好的做法是增加一個單獨(dú)的 demo 示例。
Vue 組件命名
組件的命名需遵從以下原則:
- 有意義的: 不過于具體,也不過于抽象
- 簡短: 2 到 3 個單詞
- 具有可讀性: 以便于溝通交流
同時還需要注意:
- 必須符合自定義元素規(guī)范: 使用連字符分隔單詞,切勿使用保留字。
- app- 前綴作為命名空間: 如果非常通用的話可使用一個單詞來命名,這樣可以方便于其它項(xiàng)目里復(fù)用。
為什么?因?yàn)榻M件是通過組件名來調(diào)用的。所以組件名必須簡短、富有含義并且具有可讀性。
如何做?
<!-- 推薦 --> <app-header></app-header> <user-list></user-list> <range-slider></range-slider> <!-- 避免 --> <btn-group></btn-group> <!-- 雖然簡短但是可讀性差. 使用 `button-group` 替代 --> <ui-slider></ui-slider> <!-- ui 前綴太過于寬泛,在這里意義不明確 --> <slider></slider> <!-- 與自定義元素規(guī)范不兼容 -->
組件表達(dá)式簡單化
Vue.js 的表達(dá)式是 100% 的 Javascript 表達(dá)式。這使得其功能性很強(qiáng)大,但也帶來潛在的復(fù)雜性。因此,你應(yīng)該盡量保持表達(dá)式的簡單化。
為什么?
- 復(fù)雜的行內(nèi)表達(dá)式難以閱讀。
- 行內(nèi)表達(dá)式是不能夠通用的,這可能會導(dǎo)致重復(fù)編碼的問題。
- IDE 基本上不能識別行內(nèi)表達(dá)式語法,所以使用行內(nèi)表達(dá)式 IDE 不能提供自動補(bǔ)全和語法校驗(yàn)功能。
怎么做?
如果你發(fā)現(xiàn)寫了太多復(fù)雜并難以閱讀的行內(nèi)表達(dá)式,那么可以使用 method 或是 computed 屬性來替代其功能。
<!-- 推薦 --> <template> <h1> {{ `${year}-${month}` }} </h1> </template> <script type="text/javascript"> export default { computed: { month() { return this.twoDigits((new Date()).getUTCMonth() + 1); }, year() { return (new Date()).getUTCFullYear(); } }, methods: { twoDigits(num) { return ('0' + num).slice(-2); } }, }; </script> <!-- 避免 --> <template> <h1> {{ `${(new Date()).getUTCFullYear()}-${('0' + ((new Date()).getUTCMonth()+1)).slice(-2)}` }} </h1> </template>
組件 props 原子化
雖然 Vue.js 支持傳遞復(fù)雜的 JavaScript 對象通過 props 屬性,但是你應(yīng)該盡可能的使用原始類型的數(shù)據(jù)。盡量只使用JavaScript 原始類型(字符串、數(shù)字、布爾值) 和 函數(shù)。盡量避免復(fù)雜的對象。
為什么?
- 使得組件 API 清晰直觀
- 只使用原始類型和函數(shù)作為 props 使得組件的 API 更接近于 HTML(5) 原生元素。
- 其它開發(fā)者更好的理解每一個 prop 的含義、作用
- 傳遞過于復(fù)雜的對象使得我們不能夠清楚的知道哪些屬性或方法被自定義組件使用,這使得代碼難以重構(gòu)和維護(hù)。
怎么做?
組件的每一個屬性單獨(dú)使用一個 props,并且使用函數(shù)或是原始類型的值。
<!-- 推薦 --> <range-slider :values="[10, 20]" min="0" max="100" step="5" :on-slide="updateInputs" :on-end="updateResults"> </range-slider> <!-- 避免 --> <range-slider :config="complexConfigObject"></range-slider>
驗(yàn)證組件的 props
在 Vue.js 中,組件的 props 即 API,一個穩(wěn)定并可預(yù)測的 API 會使得你的組件更容易被其他開發(fā)者使用。
組件 props 通過自定義標(biāo)簽的屬性來傳遞。屬性的值可以是 Vue.js 字符串( :attr="value"
或 v-bind:attr="value"
)或是不傳。你需要保證組件的 props 能應(yīng)對不同的情況。
為什么?
驗(yàn)證組件 props 可以保證你的組件永遠(yuǎn)是可用的(防御性編程)。即使其他開發(fā)者并未按照你預(yù)想的方法使用時也不會出錯。
怎么做?
- 提供默認(rèn)值
- 使用 type 屬性校驗(yàn)類型
- 使用 props 之前先檢查該 prop 是否存在
<template> <input type="range" v-model="value" :max="max" :min="min"> </template> <script type="text/javascript"> export default { props: { max: { type: Number, // 這里添加了數(shù)字類型的校驗(yàn) default() { return 10; }, }, min: { type: Number, default() { return 0; }, }, value: { type: Number, default() { return 4; }, }, }, }; </script>
將 this 賦值給 component 變量(
在 Vue.js 組件上下文中,this指向了組件實(shí)例。因此當(dāng)你切換到了不同的上下文時,要確保 this 指向一個可用的 component 變量。
換句話說,不要在編寫這樣的代碼 const self = this;
,而是應(yīng)該直接使用變量 component。
為什么?
將組件 this 賦值給變量 component可用讓開發(fā)者清楚的知道任何一個被使用的地方,它代表的是組件實(shí)例。
怎么做?
<script type="text/javascript"> export default { methods: { hello() { return 'hello'; }, printHello() { console.log(this.hello()); }, }, }; </script> <!-- 避免 --> <script type="text/javascript"> export default { methods: { hello() { return 'hello'; }, printHello() { const self = this; // 沒有必要 console.log(self.hello()); }, }, }; </script>
組件結(jié)構(gòu)化
按照一定的結(jié)構(gòu)組織,使得組件便于理解。
為什么?
- 導(dǎo)出一個清晰、組織有序的組件,使得代碼易于閱讀和理解。同時也便于標(biāo)準(zhǔn)化。
- 按首字母排序?qū)傩?,data, computed, watches 和 methods 使得屬性便于查找。
- 合理組織,使得組件易于閱讀。(name; extends; props, data and computed; components; watch and methods; lifecycle methods, 等)
- 使用 name 屬性。借助于Vue devtools可以讓你更方便的測試。
- 合理的 CSS 結(jié)構(gòu),如 BEM 或 oocss - 詳情?;。
- 使用單文件 .Vue 文件格式來組件代碼。
怎么做——組件結(jié)構(gòu)化
<template lang="html"> <div class="Ranger__Wrapper"> <!-- ... --> </div> </template> <script type="text/javascript"> export default { // 不要忘記了 name 屬性 name: 'RangeSlider', // 組合其它組件 extends: {}, // 組件屬性、變量 props: { bar: {}, // 按字母順序 foo: {}, fooBar: {}, }, // 變量 data() {}, computed: {}, // 使用其它組件 components: {}, // 方法 watch: {}, methods: {}, // 生命周期函數(shù) beforeCreate() {}, mounted() {}, }; </script> <style scoped> .Ranger__Wrapper { /* ... */ } </style>
組件事件命名
Vue.js 提供的處理函數(shù)和表達(dá)式都是綁定在 ViewModel 上的,組件的每一個事件都應(yīng)該按照一個好的命名規(guī)范來,這樣可以避免不少的開發(fā)問題,具體可見如下 為什么。
為什么?
- 開發(fā)者可以隨意給事件命名,即使是原生事件的名字,這樣會帶來迷惑性。
- 過于寬松的事件命名可能與DOM模板不兼容。
怎么做?
- 事件命名也連字符命名
- 一個事件的名字對應(yīng)組件外的一組意義操作,如:upload-success, upload-error 以及 dropzone-upload-success, dropzone-upload-error (如果需要前綴的話)。
- 事件命名應(yīng)該以動詞(如 client-api-load)或是形容詞(如 drive-upload-success)結(jié)尾。(出處)
避免this.$parent
Vue.js 支持組件嵌套,并且子組件可訪問父組件的上下文。訪問組件之外的上下文違反了基于模塊開發(fā)的第一原則。因此你應(yīng)該盡量避免使用 this.$parent
。
為什么?
- 組件必須相互保持,Vue 組件也是。如果組件需要訪問其父層的上下文就違反了該原則。
- 如果一個組件需要訪問其父組件的上下文,那么該組件將不能再其它上下文中復(fù)用。
怎么做?
- 通過 props 將值傳遞給子組件
- 通過 props 傳遞回調(diào)函數(shù)給子組件來達(dá)到調(diào)用父組件方法的目的
- 通過在子組件觸發(fā)事件來通知父組件
謹(jǐn)慎使用 this.$refs
Vue.js 支持通過 ref 屬性來訪問其它組件和 HTML 元素。并通過 this.$refs
可以得到組件或 HTML 元素的上下文。在大多數(shù)情況下,通過 this.$refs
來訪問其它組件的上下文是可以避免的。在使用的的時候你需要注意避免調(diào)用了不恰當(dāng)?shù)慕M件 API,所以應(yīng)該盡量避免使用 this.$refs。
為什么?
- 組件必須是保持的,如果一個組件的 API 不能夠提供所需的功能,那么這個組件在設(shè)計、實(shí)現(xiàn)上是有問題的。
- 組件的屬性和事件必須足夠的給大多數(shù)的組件使用。
怎么做?
- 提供良好的組件 API。
- 總是關(guān)注于組件本身的目的。
- 拒絕定制代碼。如果你在一個通用的組件內(nèi)部編寫特定需求的代碼,那么代表這個組件的 API 不夠通用,或者你可能需要一個新的組件來應(yīng)對該需求。
- 檢查所有的 props 是否有缺失的,如果有提一個 issue 或是完善這個組件。
- 檢查所有的事件。子組件向父組件通信一般是通過事件來實(shí)現(xiàn)的,但是大多數(shù)的開發(fā)者更多的關(guān)注于 props 從忽視了這點(diǎn)。
- Props向下傳遞,事件向上傳遞!以此為目標(biāo)升級你的組件,提供良好的 API 和 性。
- 當(dāng)遇到 props 和 events 難以實(shí)現(xiàn)的功能時,通過 this.$refs來實(shí)現(xiàn)。
- 當(dāng)需要操作 DOM 無法通過指令來做的時候可使用 this..$ref 而不是 JQuery,
document.getElement*
,document.queryElement
。
<!-- 推薦,并未使用 this.$refs --> <range :max="max" :min="min" @current-value="currentValue" :step="1"></range> <!-- 使用 this.$refs 的適用情況--> <modal ref="basicModal"> <h4>Basic Modal</h4> <button class="primary" @click="$refs.basicModal.close()">Close</button> </modal> <button @click="$refs.basicModal.open()">Open modal</button> <!-- Modal component --> <template> <div v-show="active"> <!-- ... --> </div> </template> <script> export default { // ... data() { return { active: false, }; }, methods: { open() { this.active = true; }, hide() { this.active = false; }, }, // ... }; </script> <!-- 如果可通過 emited 來做則避免通過 this.$refs 直接訪問 --> <template> <range :max="max" :min="min" ref="range" :step="1"></range> </template> <script> export default { // ... methods: { getRangeCurrentValue() { return this.$refs.range.currentValue; }, }, // ... }; </script>
使用組件名作為樣式作用域空間
Vue.js 的組件是自定義元素,這非常適合用來作為樣式的根作用域空間??梢詫⒔M件名作為 css 類的命名空間。
為什么?
- 給樣式加上作用域空間可以避免組件樣式影響外部的樣式
- 保持模塊名、目錄名、樣式根作用域名一樣,可以很好的將其關(guān)聯(lián)起來,便于開發(fā)者理解。
怎么做?
使用組件名作為樣式命名的前綴,可基于 BEM 或 OOCSS 范式。同時給style標(biāo)簽加上 scoped 屬性。加上 scoped 屬性編譯后會給組件的 class 自動加上唯一的前綴從而避免樣式的沖突。
<style scoped> /* 推薦 */ .MyExample { } .MyExample li { } .MyExample__item { } /* 避免 */ .My-Example { } /* not scoped to component or module name, not BEM compliant */ </style>
提供組件 API 文檔
使用 Vue.js 組件的過程中會創(chuàng)建 Vue 組件實(shí)例,這個實(shí)例是通過自定義屬性配置的。為了便于其他開發(fā)者使用該組件,對于這些自定義屬性即組件API應(yīng)該在 README.md 文件中進(jìn)行說明。
為什么?
- 良好的文檔可以讓開發(fā)者比較容易的對組件有一個整體的認(rèn)識,而不用去閱讀組件的源碼,也更方便開發(fā)者使用。
- 組件配置屬性即組件的 API,對于組件的用戶來說他們更感興趣的是 API 而不是實(shí)現(xiàn)原理。
- 正式的文檔會告訴開發(fā)者組件 API 變更以及向后的兼容性情況。
- README.md 是標(biāo)準(zhǔn)的我們應(yīng)該首先閱讀的文檔文件。代碼托管網(wǎng)站 (github/bitbucket/gitlab 等) 會默認(rèn)在倉庫中展示 該文件作為倉庫的介紹。
怎么做?
在模塊目錄中添加 README.md 文件:
range-slider/ ├── range-slider.Vue ├── range-slider.less └── README.md
在 README 文件中說明模塊的功能以及使用場景。對于 Vue 組件來說,比較有用的描述是組件的自定義屬性即 API 的描述介紹。
如何提高自己的寫組件能力
1、第一,多寫,多練,從簡單的開始寫,然后做總結(jié),不停的換寫法,寫到自己的很滿意為止,我可以這么說,就光上面那個login.Vue組件,我寫了五次,最后一次才讓我滿意,如何分分合合,這個也是對組件層面上一個考驗(yàn)
2、看他人源碼,比方說mintui,iview他們組件如何去組件的,通過看開源組件庫的代碼你會從中很多收益,你會發(fā)現(xiàn)有些不常的特性,如何之方便,你就能在組件層面上有更大的提高,漸漸的你的價值也會體現(xiàn),無論誰的Vue ui框架庫再好,也沒有自己寫一個好,因?yàn)楣δ苁乔ё內(nèi)f化的,你掌握了組件編寫能力,你就主動了,不是被動了,發(fā)現(xiàn)別人的ui邏輯和樣式不適合自己,怎么辦,輪子麻,自行輪子拿 過來改改加加厚那就是摩托車輪子,單車變摩托麻。
我覺得吧!無論學(xué)什么,學(xué)好基礎(chǔ)才是最重要,我的學(xué)習(xí)方式是從淺入深,而不是深入淺出,只有基礎(chǔ)打的好,才會有更好的解決方案,Vue一切的組件功能都圍繞著基礎(chǔ)。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
vue中實(shí)現(xiàn)子組件相互切換且數(shù)據(jù)不丟失的策略詳解
項(xiàng)目為數(shù)據(jù)報表,但是一個父頁面中有很多的子頁面,而且子頁面中不是相互關(guān)聯(lián),但是數(shù)據(jù)又有聯(lián)系,所以本文給大家介紹了vue中如何實(shí)現(xiàn)子組件相互切換,而且數(shù)據(jù)不會丟失,并有詳細(xì)的代碼供大家參考,需要的朋友可以參考下2024-03-03ElementUI實(shí)現(xiàn)el-table列寬自適應(yīng)的代碼詳解
這篇文章給大家介紹了ElementUI實(shí)現(xiàn)el-table列寬自適應(yīng)的詳細(xì)步驟,文中通過代碼示例給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-01-01在Vue中使用scoped屬性實(shí)現(xiàn)樣式隔離的原因解析
scoped是Vue的一個特殊屬性,可以應(yīng)用于<style>標(biāo)簽中的樣式,這篇文章給大家介紹在Vue中,使用scoped屬性為什么可以實(shí)現(xiàn)樣式隔離,感興趣的朋友一起看看吧2023-12-12