????最近利用vue重構了有贊商城,這個是我的第一個vue項目锅纺,歷時了兩個星期完成,通過這個項目了解了vue框架的基本語法以及生命周期等特性,并且了解了vue-loader、vue-cli秽浇、vue-devtools慷垮、vue-router、vuex的基本使用方法雾叭,收獲頗深,因此想在此簡單地敘述一下重構有贊商城的基本思路以及當中的一些重要操作落蝙,另外當作對自己項目的回顧以及相關vue知識的總結织狐。
????以下是本次項目的代碼鏈接和預覽鏈接:
????代碼鏈接:https://github.com/xuanzebin/youzan-demo
????預覽鏈接:http://rebuildyouzan.xyz/dist/
????首先這次重構有贊商城使用的是一個多頁面應用的重構思路,因此在進行重構之前要對項目文件進行一些配置和調(diào)整掘殴,具體的操作的話可以點擊以下這個鏈接進行查看:基于vue-cli搭建一個多頁面應用
????在完成了多頁面應用的基礎結構的搭建之后赚瘦,會出現(xiàn)項目根目錄下有一個src文件夾,src文件里有components奏寨、modules起意、pages三個文件夾的情況,而components文件夾是用來放置一些共用的vue組件的病瞳,而modules文件夾里是放置一些共用的css揽咕、js模塊,至于最后的pages文件夾則是用來放置有贊商城的不同頁面的文件套菜,每個頁面都會在pages內(nèi)呈一個單獨的文件夾亲善,里面會放置關于這個頁面的獨有的所有文件。
????我們在這里先說明一下逗柴,重構過程中所有獲取到的數(shù)據(jù)蛹头,都是通過在RAP上編寫對應的接口,然后通過axios發(fā)送異步請求來獲取到的模擬的數(shù)據(jù)戏溺,這是模仿真實的開發(fā)環(huán)境下的操作渣蜗,具體的實現(xiàn)過程的話可以參考RAP以及我在github上面的源碼的src/modules/js文件夾下的api.js文件。
????以下是重構有贊商城的所需的頁面列表旷祸,一共有六個頁面耕拷,分別為:
????1.首頁
????2.目錄分類頁
????3.商品搜索列表頁
????4.商品詳情頁
????5.購物車頁面
????6.個人中心地址管理頁面
????我們逐個頁面來說說他們的重構思路
一、首頁
????首頁的整體結構是托享,頂部一個無縫輪播組件骚烧,中間是三個推薦的商店鏈接浸赫,再下面是一個“最熱商品推薦”的商品列表,然后最底下是一個底部導航欄組件一共四部分赃绊,對于中間三個推薦商店鏈接我這邊暫不做處理既峡,原因是關于并沒有重構商店頁面的計劃。
1凭戴、無縫輪播組件
????那我們首先來說一下輪播組件涧狮,首先我們需要在src目錄下的compnents文件夾里新建一個輪播組件文件炕矮,輪播的話我們會直接選擇使用swiper插件提供的輪播組件庫么夫,我們只需把它封裝到一個組件文件中即可,具體的操作在這里我就不詳細說明了肤视,這里只強調(diào)兩個需要注意的問題:
????1.應不應該在輪播組件放入圖片數(shù)據(jù)呢档痪?
????回答:不應該,原因是為了使得輪播組件獨立出來邢滑,在不同的組件中得以復用腐螟,并且使其可以適應不同規(guī)格不同數(shù)量的圖片,因此我們的輪播組件只負責展示數(shù)據(jù)困后,不負責拿數(shù)據(jù)乐纸,數(shù)據(jù)應該通過props從父組件中獲取。
<Swipe :lists="bannerLists" name="swpie.vue" v-if="bannerLists"></Swipe>
????2.關于swiper的配置應將其寫在輪播組件的生命周期的哪一part呢摇予?
????回答:首先我們需要了解的是swiper是對DOM節(jié)點進行操作的汽绢,所以swiper的配置應該寫在組件的mounted生命周期鉤子里,因為在這個階段已經(jīng)在頁面上生成了該組件對應的DOM節(jié)點侧戴;另一方面宁昭,swiper組件里的數(shù)據(jù)是swiper的父組件異步獲取后傳遞給swiper的,因此應該等swiper拿到了傳遞的數(shù)據(jù)之后再對這個組件進行渲染酗宋,因此需要給這個組件添加一個v-if="bannerLists"
的判斷积仗,判斷swiper組件是否獲取到數(shù)據(jù),只有獲取到了數(shù)據(jù)才生成這個DOM節(jié)點蜕猫。
2寂曹、“最熱商品推薦”的商品列表
????關于這個“最熱商品推薦”的商品列表的重構也非常簡單,只需通過axios發(fā)送你想獲取的商品列表的頁數(shù)和每頁的展示商品的個數(shù)的請求到對應的RAP接口中回右,就可以獲取到對應的商品列表的數(shù)據(jù)隆圆,然后再通過v-for
把每個商品的圖片、名稱和價格渲染到頁面中即可楣黍。
????同樣的匾灶,這里有兩個值得注意的問題:
????1.獲取到的價格的格式并不統(tǒng)一,如何來使得這些價格的格式統(tǒng)一起來租漂?
????回答:這里需要用到vue實例里的一個自帶屬性filters
來對數(shù)據(jù)進行過濾阶女,在vue1.0的時候颊糜,filters里面會有自帶的過濾器,不過在vue2.0時被移除了秃踩,因此需要我們來自己寫所需的過濾器的過濾方式:
filters:{
currency(num){
num=num+''
let arr=num.split('.')
if (arr.length===1){
return num+'.00'
} else {
if (arr[1].length===1){
return num+'0'
} else return num
}
}
}
????只有在渲染頁面時衬鱼,只要對你想進行的數(shù)據(jù)后加上該過濾器即可:
<div class="price">¥{{list.price | currency}}</div>
????2.如何做到下來商品列表就發(fā)送對應的請求來更新一頁新的商品列表?
????回答:這里我們使用到了mint-ui憔杨,一個移動端分頁效果庫鸟赫,然后我們使用它文檔上面對應的infinite scroll的api來達到我們想要的效果,具體代碼如下:
<ul class="js-list js-lazy" data-src=""
v-infinite-scroll="getList"
infinite-scroll-disabled="loading"
infinite-scroll-distance="50"
>
<li v-for="list in lists" :key="list.id">
<div class="goods-item">
<a :href="'/goods.html?id='+list.id">
<div class="thumb img-box">
<img class="fadeIn" v-bind:src="list.img">
</div>
<div class="detail">
<div class="title">{{list.name}}</div>
<div class="price">¥{{list.price | currency}}</div>
</div>
</a>
</div>
</li>
</ul>
????上述代碼中消别,v-infinite-scroll="getList"
表示每當下拉到一定距離時就觸發(fā)methods里面的getList方法抛蚤;getList方法的具體代碼如下所示:
getList(){
if (this.allLoad) return
this.loading=true
axios.post(url.hostLists,{
pageNum:this.pageNum,
pageSize:this.pageSize
}).then((response)=>{
let currentList=response.data.lists
if (currentList.length<this.pageSize) this.allLoad=true
if (this.lists) {
this.lists=this.lists.concat(currentList)
} else {
this.lists=currentList
}
this.pageNum++
this.loading=false
})
}
????infinite-scroll-disabled="loading"
表示效果觸發(fā)的條件,若loading為false則表示可以觸發(fā)寻狂,若loading為true則表示不能觸發(fā)岁经,因此當loading為true時我們可以給底部添加一個加載效果,當數(shù)據(jù)獲取完畢蛇券,loading變?yōu)閒alse時缀壤,我們可以通過v-show="loading"
來讓加載效果消失;infinite-scroll-distance="50"
表示下拉的觸發(fā)距離纠亚,設置的數(shù)值越大塘慕,表示滾動條離底部的觸發(fā)距離越大,越容易觸發(fā)蒂胞。
3.底部導航欄組件
????底部導航欄和輪播組件一樣图呢,由于可以在其他地方進行復用,因此會把該組件放于components文件夾中啤誊,這里值得一提的是岳瞭,底部導航欄組件由于點擊不同的圖標,它會跳轉(zhuǎn)到不同的頁面蚊锹,因此會導致導航欄狀態(tài)的重新加載瞳筏,因此,若想要在不同的頁面讓導航欄呈現(xiàn)不同的狀態(tài)牡昆,我們需要在跳轉(zhuǎn)的時候傳入對應的查詢參數(shù)姚炕,然后在跳轉(zhuǎn)到不同的頁面時讀取這個參數(shù)來呈現(xiàn)對應的不同的狀態(tài),具體的代碼片段如下:
let {index}=qs.parse(window.location.search.substring(1))
export default {
data(){
return {
navConfig,
curIndex:parseInt(index,10) || 0
}
},
methods:{
changeNav(index,list){
location.href=`${list.href}?index=${index}`
}
}
}
????值得一提的是丢烘,在這里我們使用到了一個qs庫柱宦,這個庫可以方便我們提取出當前url后面的查詢參數(shù)。
????最后播瞳,由于在其他頁面中掸刊,filters屬性和底部導航欄組件都可以進行復用,所以這里我們利用mixins屬性赢乓,來對filters屬性和底部導航欄組件的注入進行打包忧侧,打包在一個js文件夾下的mixin.js文件中:
import Footnav from 'components/FootNav.vue'
let mixin={
filters:{
currency(num){
num=num+''
let arr=num.split('.')
if (arr.length===1){
return num+'.00'
} else {
if (arr[1].length===1){
return num+'0'
} else return num
}
}
},
components:{
Footnav
}
}
export default mixin
????當你的頁面需要使用到該過濾器石窑,或者底部導航欄時,只要對這個模塊進行引入蚓炬,并在mixins屬性中添加它即可:
new Vue({
...
mixins:[mixin]
})
二松逊、目錄分類頁
????目錄分類頁并無新的操作,和首頁的部分操作類似肯夏,就是利用axios從RAP接口中獲取數(shù)據(jù)并渲染到頁面中经宏,并對頁面中的一些焦點狀態(tài)進行v-show的處理,以及一些類名的處理驯击,我們可以從目錄分類頁中通過點擊熱銷商品進入商品詳情頁烁兰,通過點擊熱門品牌進入商品搜索列表頁,在進行這些頁面的跳轉(zhuǎn)時余耽,把一些關鍵的數(shù)據(jù)傳入查詢參數(shù)中以便跳轉(zhuǎn)頁面獲取即可缚柏。
三、商品搜索列表頁
????商品搜索列表頁同樣的基本操作與首頁和目錄分類頁類似碟贾,這里唯一不同的是,商品搜索列表頁在用戶下拉一定距離后轨域,會出現(xiàn)一個回到頂部的圖標袱耽,點擊圖標,用戶就可以直接回到頂部干发,在這里朱巨,我們使用了一個叫作Velocity.js的動畫庫,它是把css中的一些動畫效果進行封裝枉长,進而可以通過一些api輕松實現(xiàn)一些簡單的動畫效果冀续,例如上面所說的回到頂部,在項目中的代碼片段如下所示:
//引入Velocity
import Velocity from 'velocity-animate/velocity.js'
//在methods中加入對應方法
methods:{
scrollMove(){
if (window.scrollY>=290){
this.isShow=true
} else {
this.isShow=false
}
},
goToTop(){
Velocity(document.body, 'scroll', { duration: 500, easing: "easeOutQuart" });
this.isShow=false //回到頂部圖標消失
}
}
四必峰、商品詳情頁
????在商品詳情頁中洪唐,除了有對數(shù)據(jù)的獲取和頁面的渲染外,這里主要涉及到了三個新的操作:
????1.sku算法的應用
????2.sku頁面的載入和消失的動畫效果
????3.sku頁面展示時的穿透滾動問題的解決
????我們一個一個問題解決吼蚁,首先是sku算法凭需,由于此次商品詳情頁的選擇并不需要使用到它,因為商品的可選屬性只有一個肝匆,但是在實際情況下粒蜈,由于很多商品的可選屬性不止一個,因此是需要使用到sku算法的旗国,但在這里我就不多敘述關于sku算法的東西了枯怖,大家有興趣可以自行搜索:“淘寶sku算法解析”
????第二個問題,如何制作sku頁面載入和消失時的動畫效果呢能曾?這里我們使用到了vue提供的自帶transition的封裝組件度硝,可以通過這個組件來給任何元素和組件添加進入或者離開時的過渡设捐。這個組件提供了八個JavaScript鉤子函數(shù)以及六個過渡類名的切換,利用這些鉤子函數(shù)以及類名的切換就可以完成組件的過渡動畫了塘淑,這里列舉一個vue文檔上的典型例子給大家參考一下吧:
<div id="demo">
<button v-on:click="show = !show">
Toggle
</button>
<transition name="fade">
<p v-if="show">hello</p>
</transition>
</div>
new Vue({
el: '#demo',
data: {
show: true
}
})
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
????最后一個問題萝招,就是在遮罩層和彈出層出現(xiàn)之后(即sku頁面),對頁面進行上下拖動時存捺,背后的內(nèi)容層也會跟著一起拖動槐沼,這是典型的滾動穿透問題,在這里我的解決辦法是監(jiān)聽遮罩層和彈出層的出現(xiàn)捌治,當它們出現(xiàn)之后岗钩,我們更改內(nèi)容層的樣式,使其樣式變?yōu)椋?code>position:fixed;width:100%;這樣內(nèi)容層就不會再滾動了肖油,之后我們再通過設置:
scrollTop = document.scrollingElement.scrollTop
document.body.style.top = -scrollTop + 'px'
????來使得內(nèi)容層的滾動高度與遮罩層和彈出層出現(xiàn)前相同兼吓,并且,調(diào)整html元素的樣式:height:100%;overflow:hidden;
森枪,在關閉遮罩層和彈出層后视搏,還原這些修改樣式,即可使得滾動穿透的問題得以解決县袱。需要注意的是浑娜,還原這些樣式之后,原本內(nèi)容層滾動的高度就會丟失式散,因此我們要通過之前記錄下來內(nèi)容層滾動的高度筋遭,在還原樣式時將滾動高度也一并還原。
document.scrollingElement.scrollTop = scrollTop
????這樣滾動穿透的問題就算是徹底解決了暴拄,下面是全部的這部分的全部代碼片段:
watch:{
showSku(val,oldVal){
if (val){
scrollTop = document.scrollingElement.scrollTop
document.body.style.top = -scrollTop + 'px'
}
document.body.style.position=val?'fixed':'static'
// document.body.style.margin=val?`0 0 ${window.scrollY}px 0`:'0px'
document.querySelector('html').style.overflow=val?'hidden':'auto'
document.body.style.width=val?'100%':'auto'
document.querySelector('html').style.height=val?'100%':'auto'
if (!val){
document.scrollingElement.scrollTop = scrollTop
}
}
}
????下面展示一下WeUI上面的層級規(guī)范:
五漓滔、購物車頁面
????購物車頁面里面比較多的邏輯關系,在本篇博客就不一一枚舉了乖篷,大概說一下它的重構思路:
????1.首先獲取數(shù)據(jù)响驴,渲染到頁面這些是基本的操作
????2.獲取到數(shù)據(jù)之后,由于有一些屬性數(shù)據(jù)中沒有那伐,并且我們想要它在頁面中是呈響應式存在的踏施,因此從RAP接口獲取到數(shù)據(jù)之后不應該直接賦值給data里,而是應該先給數(shù)據(jù)增添屬性罕邀,再把增添后的數(shù)據(jù)賦值到data處畅形,具體代碼如下:
getLists(){
cart.getCartLists().then((response)=>{
let list=response.data.cartList
list.forEach(shop=>{
shop.checked=true
shop.editingStatus=false
shop.editingMsg='編輯'
shop.removeChecked=false
shop.goodsList.forEach(good=>{
good.checked=true
good.removeChecked=false
good.touchDelete=false
})
})
this.cartLists=list
})
}
????3.每次選擇了商店下的商品時,都利用數(shù)組的every方法來遍歷數(shù)組看是否全部商品都被選中了诉探,若選中則商店也被選中日熬,同理,若選擇了商店肾胯,則遍歷商店下的商品竖席,把商店下的商品全部選中耘纱。取消選中亦是同理。
????4.全選與否則利用計算屬性來處理毕荐,利用計算屬性的getter來獲取此時購物車的狀態(tài)來判斷是否被全選束析,利用計算屬性的setter來處理點擊全選時商店及商店下商品的狀態(tài)的改變。
????5.同樣的憎亚,利用計算屬性來計算正常狀態(tài)下選中商品的總價员寇,并返回選中商品的列表。同理第美,利用計算屬性來計算編輯狀態(tài)下的選中的商品的列表蝶锋,并以數(shù)組的形式返回。
????6.在編輯狀態(tài)下什往,我利用了計算屬性來對商品的數(shù)量的數(shù)據(jù)進行了監(jiān)測扳缕,若判斷出數(shù)量中存在非數(shù)字或者負數(shù)的情況,則會自動把數(shù)量的數(shù)據(jù)變成1别威。
????7.利用touchstart
和touchend
兩個事件來實現(xiàn)商品左拉刪除的功能躯舔,這兩個事件分別綁定start
和end
的方法,方法的具體代碼如下所示:
start(e,good){
good.startX=e.changedTouches[0].clientX
},
end(e,good,goodIndex,shopIndex,shop){
let endX=e.changedTouches[0].clientX
let left='0px'
if (good.startX-endX>100){
good.touchDelete=true
left='-60px'
Velocity(this.$refs[`goods-${shopIndex}-${goodIndex}`],{left})
shop.goodsList.forEach((otherGood,index)=>{
if (otherGood.touchDelete && index!==goodIndex) {
otherGood.touchDelete=false
Velocity(this.$refs[`goods-${shopIndex}-${index}`],{left:'0px'})
}
})
} else if (endX-good.startX>100) {
good.touchDelete=false
left='0px'
Velocity(this.$refs[`goods-${shopIndex}-${goodIndex}`],{left})
}
}
????8.當添加了左拉刪除的功能之后兔港,頁面會出現(xiàn)一個BUG庸毫,就是左拉之后,點擊該商品對應的商店下的編輯按鈕衫樊,刪除的按鈕會繼續(xù)被左拉,呈現(xiàn)一個比其他刪除按鈕長的BUG狀態(tài)利花,要如何處理呢科侈?
????處理辦法:通過給每個商品的一個具有唯一識別性的ref,然后在點擊編輯時炒事,找到已左拉的商品的對應的具有唯一識別性的ref臀栈,把它的左拉狀態(tài)還原即可,具體代碼如下所示:
shop.editingStatus=!shop.editingStatus
if (shop.editingStatus){
shop.goodsList.forEach((good,index)=>{
if (good.touchDelete){
good.touchDelete=false
this.$refs[`goods-${shopIndex}-${index}`][0].style.left='0px'
}
})
}
六挠乳、個人中心地址管理頁面
????最后是個人中心地址管理頁面权薯,在這個頁面中,我們會封裝addressService層和fetch層睡扬,addressService層主要是負責頁面中前后端交互的方法盟蚣,如添加地址、刪除地址卖怜、編輯地址和獲取地址等屎开,然后fetch層主要是負責從RAP接口中獲取數(shù)據(jù)并返回一個promise對象到service層中,具體的封裝方式和使用方式請自行查看源碼马靠。
????另外在這個頁面中奄抽,我們使用到了vue-router和vuex蔼两,接下來我將會簡要介紹它們在個人中心地址管理頁面中的使用方式。
????首先是vue-router逞度,他是用于構建單頁面應用的额划,是基于路由和組件,路由用于訪問特定的路徑档泽,然后特定的路徑與特定的組件相聯(lián)系相映射俊戳,傳統(tǒng)頁面中,是通過超鏈接來實現(xiàn)頁面的跳轉(zhuǎn)和切換的茁瘦,但在vue-router中品抽,則是路由的切換,即組件的切換甜熔。
????我們先來看看是如何配置一個routes圆恤、創(chuàng)建一個router實例并把它注入到vue實例中去的:
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
//構造配置
let routes=[{
path:'/',
components: require('../components/member.vue')
},{
path:'/address',
components:require('../components/address.vue'),
children:[{
path:'',
redirect: 'all'
},{
path:'all',
components:require('../components/all.vue')
},{
path:'form',
name:'form',
components:require('../components/form.vue')
}]
}]
//創(chuàng)建router實例
let router=new Router({
routes
})
export default router
import Vue from 'vue'
import router from './router'
import store from './vuex'
//根組件注入
let view=new Vue({
el:'#app',
router,
store
})
//router-view標簽作為配置路由后組件的容器
<div id="app">
<router-view></router-view>
</div>
????通過這樣路由的配置和注入,我們就可以實現(xiàn)單頁面下多組件的切換和嵌套了腔稀,如果上述有不懂的地方盆昙,請到vue-router的官網(wǎng)處查看文檔和說明。
????接著我們來講一下vuex焊虏,vuex是對SPA即單頁面應用進行數(shù)據(jù)的狀態(tài)管理淡喜,如果想了解具體vuex是什么還有它的用途,請點擊這篇文章:Vuex新手入門指南
????vuex其實也是組件間通信的一種方式诵闭,說起組件間的通信炼团,我們不如來一一列舉一下他們的方式有哪些:
????1.引用類型數(shù)據(jù)
????用法:如果父組件有一個數(shù)據(jù)類型是引用類型的數(shù)據(jù),當這個數(shù)據(jù)直接傳遞給子組件以后疏尿,在子組件對這個數(shù)據(jù)源進行修改的時候瘟芝,父組件中該數(shù)據(jù)也會同步修改。
????2.自定義事件
????即子組件內(nèi)部定義了一個自定義事件褥琐,可以用父組件在子組件上進行監(jiān)聽:
//子組件
this.$emit('change',18)
//父組件
<foo :obj="obj" @change="changeAge"></foo>
//父組件
methods:{
changeAge(age){
this.obj.age=age
}
}
????3.全局事件(global bus)
//bus.js
import Vue from 'vue'
const bus=new Vue()
export default bus
//觸發(fā)組件
import bus from 'js/bus.js'
bus.$emit('change',18)
//訂閱組件
import bus from 'js/bus.js'
bus.$on('change',(age)=>{
this.obj.age=age
})
????4.vuex狀態(tài)管理
????vuex的使用與vue-router有一點相似锌俱,具體代碼如下:
import Vue from 'vue'
//使用vuex插件
import Vuex from 'vuex'
Vue.use(Vuex)
import address from 'js/addressService.js'
//創(chuàng)建Store實例
const store=new Vuex.Store({
state:{
lists:null
},
mutations: {
init(state,lists){
state.lists=lists
}
},
actions: {
getLists({commit}){
address.getList().then(response=>{
commit('init',response.data.lists)
})
}
}
})
export default store
????之后同樣的在跟組件對store實例進行注入即可,在上述實例中敌呈,state屬性表示的是實例的狀態(tài)贸宏,類似vue實例里的data,需要高度注意的是磕洪,不允許直接修改state里面的值吭练,只允許定義一系列的類似事件的mutations來觸發(fā)進行state的管理。而mutations屬性里面存放的是同步事件褐鸥,因此是對數(shù)據(jù)進行同步管理线脚,要進行異步操作的話必須使用actions屬性;actions屬性里面存放一些異步的操作,在異步的操作進行完成之后再觸發(fā)mutations里面的同步事件來對state里面的數(shù)據(jù)的狀態(tài)進行同步的操作浑侥。
????在組件中姊舵,我們一般通過dispatch來觸發(fā)actions里面的異步事件進行異步操作,一般使用計算屬性來獲取state中的數(shù)據(jù)寓落,之所以使用計算屬性括丁,是因為狀態(tài)管理里的數(shù)據(jù)可能是變化的,因此我們希望它在頁面中是響應式的伶选,因此我們選擇使用計算屬性來對數(shù)據(jù)進行依賴的綁定史飞。
具體代碼如下:
computed:{
list(){
if(this.$store.state.lists){
return this.$store.state.lists
}
return false
}
},
created(){
if (!this.list){ //防止在新增地址或修改地址后多次觸發(fā)mutations中的init
this.$store.dispatch('getList')
}
}
????總之,vuex中狀態(tài)管理的過程可總結為以下流程:
????(1).通過dispatch(actionFnName)分發(fā)來觸發(fā)actions中的異步操作=>
????(2).待異步操作結束之后通過commit(mutationsFnName,data)來觸發(fā)mutations中的同步事件來進行同步操作=>
????(3).通過同步操作改變state中的數(shù)據(jù)的狀態(tài)=>
????(4).狀態(tài)改變后仰税,組件中的計算屬性因為綁定了該數(shù)據(jù)作為依賴构资,因此數(shù)據(jù)的改變會響應式地展示在頁面中,即頁面展示的數(shù)據(jù)也會得到同步的改變
????好了陨簇,關于這個項目的介紹到這里基本結束了吐绵,希望這些內(nèi)容對正在做或者準備做類似重構商城項目的你,會有所幫助~