Element-ui?Layout布局(Row和Col組件)的實(shí)現(xiàn)
我們?cè)趯?shí)際開(kāi)發(fā)中遇到一些布局的時(shí)候會(huì)用到Layout布局,這個(gè)布局只要配置一些參數(shù)就能夠達(dá)到很好的布局效果甚至可以響應(yīng)式,那里面的具體是怎么實(shí)現(xiàn)的呢,讓我們?nèi)テ书_(kāi)Element-UI的源碼,學(xué)習(xí)里面的一些細(xì)節(jié)吧。
基本說(shuō)明以及用法
Element-UI的Layout布局是通過(guò)基礎(chǔ)的24分欄,迅速簡(jiǎn)便地創(chuàng)建布局。根據(jù)不同的組合,很快的就能夠生成一個(gè)很美觀(guān)的響應(yīng)式布局。具體的用法如下:
<el-row> <el-col :span="24"><div class="grid-content bg-purple-dark"></div></el-col> </el-row>
由上述的示例代碼可以看出Row組件主要是創(chuàng)建每行分欄的布局方式,比如之間的一些間隔、對(duì)齊方式等。而Col則創(chuàng)建每個(gè)分欄,分欄的長(zhǎng)度、偏移量等。我們可以進(jìn)行自由組合每個(gè)分欄,從而達(dá)到一種響應(yīng)式效果。
Row組件的分析
render函數(shù)
我們都知道vue除了可以使用template模板編寫(xiě)組件外,有時(shí)候我們還可以直接使用render函數(shù)去編寫(xiě)一個(gè)組件。因?yàn)閠emplate模板最終也是編譯成了render函數(shù)。
為什么會(huì)有render函數(shù)的寫(xiě)法?比如現(xiàn)在有個(gè)需求:根據(jù)動(dòng)態(tài)的level生成從h1-h6字體大小的標(biāo)題的時(shí)候,我們?nèi)绻褂胻emplate去實(shí)現(xiàn)的話(huà)我們頁(yè)面中可能會(huì)出現(xiàn)很多類(lèi)似這樣的偽代碼:
<template> <h1 v-if="level === 1"> <slot></slot> </h1> <h2 v-else-if="level === 2"> <slot></slot> </h2> <h3 v-else-if="level === 3"> <slot></slot> </h3> <h4 v-else-if="level === 4"> <slot></slot> </h4> <h5 v-else-if="level === 5"> <slot></slot> </h5> <h6 v-else-if="level === 6"> <slot></slot> </h6> </template>
但是如果是使用render函數(shù)則是比較簡(jiǎn)單:
Vue.component('anchored-heading', { render: function (createElement) { return createElement( 'h' + this.level, // 標(biāo)簽名稱(chēng) this.$slots.default // 子節(jié)點(diǎn)數(shù)組 ) }, props: { level: { type: Number, required: true } } })
這里還有一個(gè)代碼優(yōu)化點(diǎn)是。this.$slots.default存儲(chǔ)的就是插槽內(nèi)容,不需要寫(xiě)那么多遍。
源碼分析
Row組件的源碼比較簡(jiǎn)單,因?yàn)槲覀兛梢酝ㄟ^(guò)tag這個(gè)prop對(duì)其指定一個(gè)渲染標(biāo)簽,所以組件是直接使用render渲染函數(shù)進(jìn)行編寫(xiě)。 render函數(shù)部分如下:
render(h) { return h(this.tag, { class: [ 'el-row', this.justify !== 'start' ? `is-justify-${this.justify}` : '', this.align !== 'top' ? `is-align-${this.align}` : '', { 'el-row--flex': this.type === 'flex' } ], style: this.style }, this.$slots.default); }
如上的源碼可以得出Row主要是控制class名來(lái)進(jìn)行控制內(nèi)容布局的。這里有g(shù)utter屬性能夠控制行內(nèi)列的間隔數(shù)。如果說(shuō)我們?cè)O(shè)置為gutter=20,那么每個(gè)列項(xiàng)都進(jìn)行左右間距10px,那么就會(huì)出現(xiàn)個(gè)問(wèn)題:第一個(gè)列項(xiàng)跟最后一個(gè)列項(xiàng)會(huì)出現(xiàn)左右的間距。那該如何讓第一個(gè)跟最后一個(gè)左右間隔去掉這個(gè)10px呢?Row的處理方案是這個(gè)行左右各偏-10px,所以用了一個(gè)計(jì)算屬性來(lái)設(shè)置樣式:
computed: { style() { const ret = {}; if (this.gutter) { ret.marginLeft = `-${this.gutter / 2}px`; ret.marginRight = ret.marginLeft; } return ret; } },
Col組件的分析
組件分析
Col主要是為了設(shè)置每一列的長(zhǎng)度以及偏移量。主要的屬性是span、offset;同樣這個(gè)組件也是采用render函數(shù)去編寫(xiě),首先我們看如何通過(guò)span、offset去控制列的,源碼如下:
render(h) { let classList = []; let style = {}; ... ['span', 'offset', 'pull', 'push'].forEach(prop => { if (this[prop] || this[prop] === 0) { classList.push( prop !== 'span' ? `el-col-${prop}-${this[prop]}` : `el-col-${this[prop]}` ); } }); ... return h(this.tag, { class: ['el-col', classList], style }, this.$slots.default); }
從這可以看出,col的列寬是通過(guò)不同class名去做控制的。我們找到對(duì)應(yīng)的.scss文件,發(fā)現(xiàn)他使用了sass@for循環(huán)語(yǔ)句去計(jì)算不同格子的寬度:
@for $i from 0 through 24 { .el-col-#{$i} { width: (1 / 24 * $i * 100) * 1%; } .el-col-offset-#{$i} { margin-left: (1 / 24 * $i * 100) * 1%; } .el-col-pull-#{$i} { position: relative; right: (1 / 24 * $i * 100) * 1%; } .el-col-push-#{$i} { position: relative; left: (1 / 24 * $i * 100) * 1%; } }
同理offset也是使用相同的邏輯。這樣我們就可以根據(jù)不同的span、跟offset混合組合不同風(fēng)布局了,是不是感覺(jué)背后的邏輯是如此的簡(jiǎn)單呢。我們?cè)偎伎家粋€(gè)問(wèn)題就是如果我們要控制一組相同的列寬間隔,需要一個(gè)個(gè)的去做設(shè)置么?答案是不用的,我們可以借助上述的Row組件中的gutter屬性去做統(tǒng)一設(shè)置。那怎么實(shí)現(xiàn)的呢?源碼如下:
computed: { gutter() { let parent = this.$parent; while (parent && parent.$options.componentName !== 'ElRow') { parent = parent.$parent; } return parent ? parent.gutter : 0; } }
我們通過(guò)往上遍歷父組件,如果父組件的組件名為ElRow,則取到gutter值,然后讓組件左右內(nèi)邊距設(shè)置對(duì)應(yīng)的值就好了:
if (this.gutter) { style.paddingLeft = this.gutter / 2 + 'px'; style.paddingRight = style.paddingLeft; }
這樣我們就解決了統(tǒng)一列寬設(shè)置的問(wèn)題;
響應(yīng)式布局
這里我們用到了css3中的媒體查詢(xún)來(lái)進(jìn)行響應(yīng)式布局,相應(yīng)尺寸分別是xs、sm、md、lg 和 xl。使用代碼如下:
<el-row :gutter="10"> <el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="1"><div class="grid-content bg-purple"></div></el-col> <el-col :xs="4" :sm="6" :md="8" :lg="9" :xl="11"><div class="grid-content bg-purple-light"></div></el-col> <el-col :xs="4" :sm="6" :md="8" :lg="9" :xl="11"><div class="grid-content bg-purple"></div></el-col> <el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="1"><div class="grid-content bg-purple-light"></div></el-col> </el-row>
說(shuō)明:xs:<768px 響應(yīng)式柵格數(shù)或者柵格屬性對(duì)象,sm:≥768px 響應(yīng)式柵格數(shù)或者柵格屬性對(duì)象,md:≥992px 響應(yīng)式柵格數(shù)或者柵格屬性對(duì)象,lg:≥1200px 響應(yīng)式柵格數(shù)或者柵格屬性對(duì)象,xl:≥1920px 響應(yīng)式柵格數(shù)或者柵格屬性對(duì)象.
背后的邏輯就是不同屏幕尺寸所展示的格子數(shù)是不一樣的,而且是根據(jù)屏幕寬度進(jìn)行響應(yīng)式。首先,我們看是如何進(jìn)行不同的class綁定的:
['xs', 'sm', 'md', 'lg', 'xl'].forEach(size => { if (typeof this[size] === 'number') { classList.push(`el-col-${size}-${this[size]}`); } else if (typeof this[size] === 'object') { let props = this[size]; Object.keys(props).forEach(prop => { classList.push( prop !== 'span' ? `el-col-${size}-${prop}-${props[prop]}` : `el-col-${size}-${props[prop]}` ); }); } });
這里面xs等屬性也是可以使用對(duì)象。所以會(huì)有個(gè)處理對(duì)象的邏輯;以上的js處理的邏輯比較簡(jiǎn)單,我們?cè)倏匆幌耤ss是怎么處理這個(gè)媒體查詢(xún)的邏輯的。
在分析css的時(shí)候,我們先了解一個(gè)概念,那就是sass中的內(nèi)置方法map-get。map-get($map,$key)函數(shù)的作用就是可以通過(guò)$key取到對(duì)應(yīng)的value值,可以理解為就是一個(gè)映射關(guān)系。如果不存在則不會(huì)編譯對(duì)應(yīng)的css。舉個(gè)??:
$social-colors: ( dribble: #ea4c89, facebook: #3b5998, github: #171515, google: #db4437, twitter: #55acee ); .btn-dribble{ color: map-get($social-colors,facebook); } // 編譯后 .btn-dribble { color: #3b5998; }
第二個(gè)是sass內(nèi)置方法inspect(value),這個(gè)方法就是一個(gè)返回一個(gè)字符串的表示形式,value是一個(gè)sass表達(dá)式。舉個(gè)??:
$--sm: 768px !default; $--md: 992px !default; $--lg: 1200px !default; $--xl: 1920px !default; $--breakpoints: ( 'xs' : (max-width: $--sm - 1), 'sm' : (min-width: $--sm), 'md' : (min-width: $--md), 'lg' : (min-width: $--lg), 'xl' : (min-width: $--xl) ); @mixin res($breakpoint){ $query:map-get($--breakpoints,$breakpoint) @if not $query { @error 'No value found for `#{$breakpoint}`. Please make sure it is defined in `$breakpoints` map.'; } @media #{inspect($query)} { @content; } } .element { color: #000; @include res(sm) { color: #333; } } // 編譯后的css .element { color: #000; } @media (min-width: 768px) { .element { color: #333; } }
好了,我相信聰明的你已經(jīng)很好的掌握了這兩個(gè)方法,那我們?nèi)タ匆幌耬lement是怎么去實(shí)現(xiàn)的吧。
其實(shí)上述的第二個(gè)例子已經(jīng)道出一二,我們看一下源碼:
$--sm: 768px !default; $--md: 992px !default; $--lg: 1200px !default; $--xl: 1920px !default; $--breakpoints: ( 'xs' : (max-width: $--sm - 1), 'sm' : (min-width: $--sm), 'md' : (min-width: $--md), 'lg' : (min-width: $--lg), 'xl' : (min-width: $--xl) ); /* Break-points -------------------------- */ @mixin res($key, $map: $--breakpoints) { // 循環(huán)斷點(diǎn)Map,如果存在則返回 @if map-has-key($map, $key) { @media only screen and #{inspect(map-get($map, $key))} { @content; } } @else { @warn "Undefeined points: `#{$map}`"; } } @include res(xs) { @for $i from 0 through 24 { .el-col-xs-#{$i} { width: (1 / 24 * $i * 100) * 1%; } .el-col-xs-offset-#{$i} { margin-left: (1 / 24 * $i * 100) * 1%; } } } @include res(sm) { ... } @include res(md) { ... } @include res(lg) { ... } @include res(xl) { ... }
這樣我們就會(huì)在不同的屏幕尺寸下進(jìn)行不同的長(zhǎng)度以及間隔的展示了,這樣去寫(xiě)我們的媒體查詢(xún)是不是很棒呢?
到此這篇關(guān)于Element-ui Layout布局(Row和Col組件)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Element Layout布局內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue通過(guò)watch對(duì)input做字?jǐn)?shù)限定的方法
本篇文章主要介紹了vue通過(guò)watch對(duì)input做字?jǐn)?shù)限定的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07vue-vuex中使用commit提交mutation來(lái)修改state的方法詳解
今天小編就為大家分享一篇vue-vuex中使用commit提交mutation來(lái)修改state的方法詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09vue-pdf插件實(shí)現(xiàn)pdf文檔預(yù)覽方式(自動(dòng)分頁(yè)預(yù)覽)
這篇文章主要介紹了vue-pdf插件實(shí)現(xiàn)pdf文檔預(yù)覽方式(自動(dòng)分頁(yè)預(yù)覽),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03vue+Element實(shí)現(xiàn)分頁(yè)效果
這篇文章主要為大家詳細(xì)介紹了vue+Element實(shí)現(xiàn)分頁(yè)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09vue解決一個(gè)方法同時(shí)發(fā)送多個(gè)請(qǐng)求的問(wèn)題
今天小編就為大家分享一篇vue解決一個(gè)方法同時(shí)發(fā)送多個(gè)請(qǐng)求的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09解析Vue2 dist 目錄下各個(gè)文件的區(qū)別
本篇文章主要介紹了解析Vue2 dist 目錄下各個(gè)文件的區(qū)別,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11