摘要:前言這是我第一個(gè)基于的項(xiàng)目作品,目的很簡(jiǎn)單,學(xué)以致用,將之前的前端知識(shí)積累加上目前流行的前端框架,以項(xiàng)目的形式展現(xiàn)出來(lái)。即將屬性和請(qǐng)求返回?cái)?shù)據(jù)對(duì)象合并到空對(duì)象,然后賦值給這里加上即提供了一種可擴(kuò)展的機(jī)制,倘若原來(lái)的屬性中有預(yù)定義的其他屬性。
前言
這是我第一個(gè)基于 Vue 的項(xiàng)目作品,目的很簡(jiǎn)單,學(xué)以致用,將之前的前端知識(shí)積累加上目前流行的前端框架,以項(xiàng)目的形式展現(xiàn)出來(lái)。
源代碼:https://github.com/nanyang24/...
演示地址:https://ele.n-y.io/
Vue有自己的腳手架構(gòu)建工具vue-cli,使用起來(lái)非常方便,使用webpack來(lái)集成各種開(kāi)發(fā)便捷工具,比如:
Hot-reload Vue的熱更新,修改代碼之后無(wú)需手動(dòng)刷新網(wǎng)頁(yè),對(duì)前端開(kāi)發(fā)來(lái)說(shuō)非常方便
PostCss,再也不用去管兼容性的問(wèn)題了,只針對(duì)chrome這樣的現(xiàn)代瀏覽器寫(xiě)css代碼,會(huì)自動(dòng)編譯生成兼容多款瀏覽器的css代碼
ESlint,統(tǒng)一代碼風(fēng)格,規(guī)避低級(jí)錯(cuò)誤,對(duì)于有代碼潔癖的人來(lái)說(shuō)是不可或缺的
Bable,ES2015出來(lái)已經(jīng)有一段時(shí)間了,但是不少瀏覽器還沒(méi)有兼容ES6,有了bable,放心使用ES6語(yǔ)法,它會(huì)自動(dòng)轉(zhuǎn)義成ES5語(yǔ)法
SCSS,一款 CSS預(yù)處理器,編譯后成正常的CSS文件。為CSS增加一些編程的特性
…
除此之外,vue-cli已經(jīng)使用node配置了一套本地服務(wù)器和安裝命令等,本地運(yùn)行和打包只需要一個(gè)命令就可以搞定,非常的方便
實(shí)現(xiàn)功能Goods、Ratings、Seller 組件視圖均可上下滾動(dòng)
商品頁(yè) 點(diǎn)擊左側(cè)menu,右側(cè)list對(duì)應(yīng)跳轉(zhuǎn)到相應(yīng)位置
點(diǎn)擊list查看商品詳情頁(yè),父子組件的通信
評(píng)論內(nèi)容可以篩選查看
購(gòu)物車組件,包括添加刪除商品及動(dòng)效,購(gòu)物控件與購(gòu)物車組件之間為兄弟組件通信,點(diǎn)擊購(gòu)物車圖標(biāo),展示已選擇的商品列表
商家實(shí)景圖片可以左右滑動(dòng)
loaclStorage 緩存商家信息(id、name)
組件關(guān)系├──app.vue │ ├──header.vue--頭部組件 │ │ ├──star.vue--星星評(píng)分組件 │ ├──goods.vue--商品組件 │ │ ├──shopcart.vue--購(gòu)物車組件,包括小球飛入購(gòu)物車動(dòng)畫(huà) │ │ ├──cartcontrol.vue--購(gòu)買(mǎi)加減圖標(biāo)控件--選中數(shù)量返回給父組件goods,goods響應(yīng)后,重新計(jì)算選中數(shù)量,將數(shù)據(jù)發(fā)送給購(gòu)物車組件, │ │ ├──food.vue--商品詳情頁(yè) │ │ │ ├──ratingselect.vue--評(píng)價(jià)內(nèi)容篩選組件 │ ├──ratings.vue--評(píng)論組件 │ │ ├──ratingselect.vue--評(píng)價(jià)內(nèi)容篩選組件 │ ├──seller.vue--商家組件 獨(dú)立組件 ├──split.vue--關(guān)于分割線組件項(xiàng)目結(jié)構(gòu)
common/---- 文件夾存放的是通用的css和fonts components/---- 文件夾用來(lái)存放 Vue 組件 router/---- 文件夾存放的是vue-router相關(guān)配置(linkActiveClass,routes注冊(cè)組件路由) build/---- 文件是 webpack 的打包編譯配置文件 config/---- 文件夾存放的是一些配置項(xiàng),比如我們服務(wù)器訪問(wèn)的端口配置等 dist/---- 該文件夾一開(kāi)始是不存在,在項(xiàng)目經(jīng)過(guò) build 之后才會(huì)生成 prod.server.js---- 該文件是測(cè)試是模擬的服務(wù)器配置,用來(lái)運(yùn)行dist里面的文件,在config/index.js中,build對(duì)象中添加一條端口設(shè)置port:9000, App.vue---- 根組件,所有的子組件都將在這里被引用 index.html---- 整個(gè)項(xiàng)目的入口文件,將會(huì)引用我們的根組件 App.vue main.js---- 入口文件的 js 邏輯,在 webpack 打包之后將被注入到 index.html 中開(kāi)發(fā)過(guò)程問(wèn)題匯總: 1、better-scroll 插件在移動(dòng)端使用時(shí)需要設(shè)置 click:true,否則移動(dòng)端滑動(dòng)無(wú)效 2、分開(kāi)設(shè)置css樣式:
圖標(biāo)icon.css--文字圖標(biāo)樣式,通過(guò)icommon.io網(wǎng)站 將svg圖片轉(zhuǎn)成文字圖標(biāo)樣式
公共base.css--處理設(shè)備像素比的一些樣式,針對(duì)border-1px問(wèn)題,不同設(shè)備像素比,顯示的線條粗細(xì)不同
工具mixin.css--設(shè)置border-1px樣式和背景樣式
移動(dòng)端 border-1px 實(shí)現(xiàn)原理當(dāng)樣式像素一定時(shí),因手機(jī)有320px,640px等.各自的縮放比差異,所以設(shè)備顯示像素就會(huì)有1Npx,2Npx。
公式:設(shè)備上像素 = 樣式像素 * 設(shè)備像素比
為了保證設(shè)計(jì)稿高度還原,采用 media + scale 的方法解決
屏幕寬度: 320px 480px 640px 設(shè)備像素比: 1 1.5 2 通過(guò)查詢它的設(shè)備像素比 devicePixelRatio 在設(shè)備像素比為1.5倍時(shí), round(1px 1.5 / 0.7) = 1px 在設(shè)備像素比為2倍時(shí), round(1px 2 / 0.5) = 1px
實(shí)現(xiàn)代碼
// SCSS 語(yǔ)法 @mixin border-1px($color) { position: relative; &::after { display: block; position: absolute; left: 0; bottom: 0; width: 100%; border-top: 1px solid $color; content: ""; } } @mixin border-none() { &::after{ display: none; } } @media (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5) { .border-1px { &::after { -webkit-transform: scaleY(0.7); transform: scaleY(0.7); } } } @media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2) { .border-1px { &::after { -webkit-transform: scaleY(0.5); transform: scaleY(0.5); } } }3、sticky-footer布局
在 header 組件的詳情頁(yè)采用 sticky-footer 布局,主要特點(diǎn)是如果頁(yè)面內(nèi)容不夠長(zhǎng)的時(shí)候,頁(yè)腳塊粘貼在視窗底部;如果內(nèi)容足夠長(zhǎng)時(shí),頁(yè)腳塊會(huì)被內(nèi)容向下推送
實(shí)現(xiàn):父級(jí) position:fixed,內(nèi)容設(shè) 為padding-bottom:64px,頁(yè)腳相對(duì)定位,margin-top:-64px,clear:both
為了保證兼容性,父級(jí)要清除浮動(dòng)
參考:
https://www.cnblogs.com/shico...
https://www.w3cplus.com/css3/...
// 左側(cè)固定width:80px,右側(cè)自適應(yīng) parent: display:fiexd; child-left: flex:0 0 80px child-right: flex:12、元素寬度自適應(yīng)設(shè)備寬度,且元素要求等寬高樣式
例如:商品詳情頁(yè)面的商品圖片展示樣式
// stylus語(yǔ)法 .img_header { position:relative width:100% // width是 設(shè)備寬度 height:0 padding-top:100% // 高度設(shè)為0,使用padding撐開(kāi) .img { position:absolute //定位布局 top:0 left:0 width:100% height:100% } }5、背景模糊效果
filter:blur(10px),注意,所有在內(nèi)的子元素也會(huì)模糊,包括文字,所以采用定位布局,背景多帶帶占用一個(gè)層,ios有一個(gè)設(shè)置backdrop-filter:blur(10px),只會(huì)模糊背景,但不支持android
6、transition過(guò)渡在購(gòu)買(mǎi)控件中使用transition過(guò)渡效果,實(shí)現(xiàn)添加減少按鈕的動(dòng)效,和小球飛入購(gòu)物車的動(dòng)效(模仿貝塞爾曲線的效果)
vue2.x里面定義了transition過(guò)渡狀態(tài),
name - string, 用于自動(dòng)生成 CSS 過(guò)渡類名。
例如:name: "fade" 將自動(dòng)拓展為.fade-enter,.fade-enter-active等。默認(rèn)類名為 "v" fade-enter fade-enter-active fade-leave fade-leave-active
包括transition過(guò)渡的鉤子函數(shù)
before-enter before-leave before-appear enter leave appear after-enter after-leave after-appear enter-cancelled leave-cancelled (v-show only) appear-cancelled7、seller組件: 問(wèn)題一:seller頁(yè)面中商品商家實(shí)景圖片橫向滾動(dòng)
解決方案:每個(gè) li 要 display:inline-block,因?yàn)閣idth不會(huì)自動(dòng)撐開(kāi)父級(jí)ul,所以需要將計(jì)算后的寬度賦值給ul的width,(每一張圖片的width+margin)*圖片數(shù)量-一個(gè)margin,因?yàn)樽詈笠粡垐D片沒(méi)有margin
同時(shí)new BScroll里面要設(shè)置scrollX: true,eventPassthrough: "vertical", // 滾動(dòng)方向橫向
問(wèn)題分析:出現(xiàn)這種現(xiàn)象是因?yàn)閎etter-scroll插件是嚴(yán)格基于DOM的,數(shù)據(jù)是采用異步傳輸?shù)模?yè)面剛打開(kāi),DOM并沒(méi)有被渲染,所以,要確保DOM渲染了,才能使用 better-scroll,
解決方案:用到mounted鉤子函數(shù),同時(shí)必須搭配this.$nextTick()
問(wèn)題分析:出現(xiàn)這種情況是因?yàn)閙ounted函數(shù)在整個(gè)生命周期中只會(huì)只行一次
解決方案:使用watch方法監(jiān)控?cái)?shù)據(jù)變化,并執(zhí)行滾動(dòng)函數(shù) this._initScroll();this._initPicScroll();
使用window.localStorage保存和設(shè)置緩存信息,封裝在store.js文件內(nèi)
//將頁(yè)面信息保存到localStorage里 export function saveToLocal(id, key, value) { let store = window.localStorage._store_; // 新定義一個(gè)key值_store_,存放要保存的數(shù)據(jù)對(duì)象 // _store_ { // store[id]: { // key: value // } // } if (!store) { store = {}; store[id] = {}; } else { store = JSON.parse(store); // String格式--> json格式 if (!store[id]) { store[id] = {}; } } store[id][key] = value; window.localStorage._store_ = JSON.stringify(store); // 將json格式轉(zhuǎn)成String格式,存放到window.localStorage._store中 } //將localStorage信息設(shè)置到頁(yè)面中 export function loadFromLocal(id, key, defaults) { let store = window.localStorage._store_; if (!store) { // 一開(kāi)始是沒(méi)有的,因?yàn)闆](méi)有點(diǎn)擊事件,所以顯示默認(rèn)數(shù)據(jù) return defaults; } store = JSON.parse(store)[id]; // 將json格式-->String格式 // console.log(store); // {"isFavorite":true} if (!store) { return defaults; } let ret = store[key]; return ret || defaults; }9、解析url,得到商家信息,包括id,name,在獲取數(shù)據(jù)時(shí),直接賦值,商家的id或name會(huì)被丟掉
使用window.localStorage.search獲取url地址,并進(jìn)行解析
封裝在util.js文件內(nèi)
/** * 解析URL參數(shù) * @example ?id=12345&a=b * @return Object {id:12345, a:b} **/ export function urlParse() { let url = window.location.search; let obj = {}; let reg = /[?&][^?&]+=[^?&]+/g; let arr = url.match(reg); // ["?id=12345", "&a=b"] if (arr) { arr.forEach((item) => { let temArr = item.substring(1).split("="); let key = decodeURIComponent(temArr[0]); let value = decodeURIComponent(temArr[1]); obj[key] = value; }); } return obj; };
我們需要將得到的 id 和 name 帶到數(shù)據(jù)中,實(shí)際上在獲取數(shù)據(jù)的時(shí)候,并沒(méi)有帶著id和name,這時(shí)就要用到 es6 語(yǔ)法中Object.assign(),官方解釋為:可以把任意多個(gè)的源對(duì)象自身的可枚舉屬性拷貝給目標(biāo)對(duì)象,然后返回目標(biāo)對(duì)象。
this.seller = Object.assign({}, this.seller, response.data); //即將vm.seller屬性和請(qǐng)求返回?cái)?shù)據(jù)對(duì)象合并到空對(duì)象,然后賦值給vm.seller,這里加上this.seller即提供了一種可擴(kuò)展的機(jī)制,倘若原來(lái)的屬性中有預(yù)定義的其他屬性。10、goods,ratings,seller組件之間切換時(shí)會(huì)重新渲染
解決方案:在 app.vu 內(nèi)使用 keep-alive,保留各組件狀態(tài),避免重新渲染
Vue 使用技巧 1、vue-router
使用
// app.vuehome about
// router: index.js import Vue from "vue"; import Router from "vue-router"; import goods from "components/goods/goods.vue"; import ratings from "components/ratings/ratings.vue"; import seller from "components/seller/seller.vue"; Vue.use(Router); const routes = [{ path: "/", redirect: "/goods" }, { path: "/goods", component: goods }, { path: "/ratings", component: ratings }, { path: "/seller", component: seller }]; export default new Router({ routes, linkActiveClass: "active" });2、axios
在vue1.x的時(shí)候,vue的官方推薦HTTP請(qǐng)求工具是vue-resource,但是在vue2.0的時(shí)候?qū)⑼扑]工具改成了axios。
如果想像以前使用 vue-resource 那樣 this.$http.get 調(diào)用,要這樣定義:
Vue.prototype.$http = axios;
通過(guò) this.$http.get 來(lái)定義通過(guò)vue實(shí)例來(lái)發(fā)送get請(qǐng)求,然后通過(guò)then后面的回調(diào)函數(shù)將請(qǐng)求成功的數(shù)據(jù)接收,通過(guò)狀態(tài)碼來(lái)判斷是否成功以及復(fù)制給vue的數(shù)據(jù)對(duì)象。由于這里是用的mock數(shù)據(jù)(模擬后臺(tái)數(shù)據(jù)),所以用的模擬狀態(tài)碼。
const ERR_OK = 0;//表示沒(méi)有錯(cuò)誤信息,即獲取數(shù)據(jù)成功 this.$http.get("/api/seller").then((response) => { response = response.data; if (response.errno === ERR_OK) { this.seller = Object.assign({}, this.seller, response.data); } });3、組件間通訊
vue是組件式開(kāi)發(fā),所以組件間通訊是必不可少的
父?jìng)髯? props
子傳父: $emit
兄弟通訊: 1. event bus: 利用一個(gè)中間組件來(lái)作為信息傳遞中介;2. vuex: 信息樹(shù)
父?jìng)髯? propsvue提供了一種方式,即在子組件定義 props 來(lái)接受父組件傳遞來(lái)的數(shù)據(jù)對(duì)象。
// 父組件子傳父: $emit// 子組件 header.vue props: { seller: { type: Object } }
如果是子組件想傳遞數(shù)據(jù)給父組件,需要派發(fā)自定義事件,使用 $emit 派發(fā),
父組件使用v-on接收監(jiān)控(v-on可以簡(jiǎn)寫(xiě)成@)
// 子組件 RatingSelect.vue,派發(fā)自定義事件isContent,將this.onlyContent數(shù)據(jù)傳給父級(jí) this.$emit("isContent", this.onlyContent); this.$emit("selRatings", this.selectType); // 父組件 foodInfo.vue 在子組件的模板標(biāo)簽里,使用v-on監(jiān)控isContent傳過(guò)來(lái)的數(shù)據(jù)非父子組件之間通信
大型項(xiàng)目可以用 Vue官方推薦的vuex
EventBus :https://n-y.io/vue-eventbus/
子組件A $emit 派發(fā)具體事件,由父組件 @ 監(jiān)聽(tīng)得到數(shù)據(jù)
父組件再利用 $refs 直接訪問(wèn)子組件B的方法,間接實(shí)現(xiàn)數(shù)據(jù)從子組件A傳遞至子組件B
4、組件提取管理將相同樣式或功能的區(qū)塊多帶帶提出來(lái),作為一個(gè)組件。
另外組件中用到的圖片等資源就近維護(hù),即可以考慮在組件文件夾中新建images文件夾。
抽離組件遵循原則:
要盡量遵循單一職責(zé)原則,復(fù)用性更高,不要設(shè)置額外的margin等影響布局的東西
想要達(dá)到這種目的,有兩種方法,一種是利用重定向,另一種是利用vue-router的導(dǎo)航式編程。
1、重定向//在router的index.js文件中設(shè)置,要多寫(xiě)一個(gè)對(duì)象,指向目標(biāo)組件 Vue.use(Router); const routes = [{ path: "/", redirect: "/goods" // 重定向 }, { path: "/goods", component: goods }, { path: "/ratings", component: ratings }, { path: "/seller", component: seller }]; export default new Router({ routes, linkActiveClass: "active" });2、導(dǎo)航式編程
router.push("/Goods");項(xiàng)目難點(diǎn) 1、關(guān)于購(gòu)物車添加按鈕的動(dòng)畫(huà) html代碼
生成一個(gè)動(dòng)畫(huà)小球的div,并且生成五個(gè)小球,五個(gè)是為了生成一定數(shù)量的小球來(lái)作為操作使用,按照小球動(dòng)畫(huà)的速度,一般來(lái)說(shuō)五個(gè)也可以保證有足夠的小球數(shù)量來(lái)運(yùn)行動(dòng)畫(huà)
動(dòng)畫(huà)的內(nèi)容分別是外層和內(nèi)層,外層控制動(dòng)畫(huà)小球的軌道和方向,內(nèi)層控制動(dòng)畫(huà)小球自身的運(yùn)行狀態(tài)
動(dòng)畫(huà)使用vue的js鉤子實(shí)現(xiàn)
因?yàn)樾∏騽?dòng)畫(huà)只有一個(gè)方向(只執(zhí)行單方向從上到下滾落),所以只用了before-enter,enter,after-enter
用v-show控制小球的可見(jiàn)性,在動(dòng)畫(huà)執(zhí)行期間可見(jiàn),其余時(shí)候隱藏
js代碼//用了兩種方式的動(dòng)畫(huà),css和js鉤子//外層動(dòng)畫(huà) //內(nèi)層動(dòng)畫(huà)
設(shè)置了balls數(shù)組來(lái)代表五個(gè)小球
設(shè)置了dropBalls數(shù)組正在運(yùn)行的小球
data(){ return { balls: [ {show: false}, {show: false}, {show: false}, {show: false}, {show: false} ], dropBalls: [] } },
只要觸發(fā)了drop事件,不止是drop事件里面的代碼會(huì)執(zhí)行,另外幾個(gè)vue的js監(jiān)聽(tīng)鉤子也會(huì)一起按順序執(zhí)行
觸發(fā)了 drop 事件
beforeDrop 開(kāi)始執(zhí)行
dropping 開(kāi)始執(zhí)行
afterDrop 開(kāi)始執(zhí)行
drop 事件的觸發(fā)可以通過(guò)點(diǎn)擊 cartcontrol 組件的添加小球按鈕 addCart 事件觸發(fā)使用 $emit ,也可以父組件 this.$refs.shopcart.drop(target); 直接觸發(fā)
這么做的目的是實(shí)現(xiàn),在子組件 cartcontrol 點(diǎn)擊之后,可以將具體點(diǎn)擊的 dom 傳給父組件 goods 然后再傳給子組件 shopcart,(因?yàn)槟壳八麄冎g的通道就是這樣,shopcart子組件并沒(méi)有導(dǎo)入cartcontrol子組件,所以沒(méi)有直接通訊)這樣就實(shí)現(xiàn)了多個(gè)組件之間的通訊(也可以使用 EventBus 和 vuex),從而可以實(shí)現(xiàn)需求,例如這里就是實(shí)現(xiàn)點(diǎn)擊子組件 cartcontrol 后添加一個(gè)動(dòng)畫(huà),將小球滑落到另外一個(gè)組件shopcart
$emit 是觸發(fā)當(dāng)前實(shí)例上的事件。附加參數(shù)都會(huì)傳給監(jiān)聽(tīng)器回調(diào)。
methods: { drop(el) { //觸發(fā)一次事件就會(huì)將所有小球進(jìn)行遍歷 for (let i = 0; i < this.balls.length; i++) { let ball = this.balls[i]; if (!ball.show) { //將false的小球放到dropBalls ball.show = true; ball.el = el; //設(shè)置小球的el屬性為一個(gè)dom對(duì)象 this.dropBalls.push(ball); return; } } }, beforeDrop(el){ //這個(gè)方法的執(zhí)行是因?yàn)檫@是一個(gè)vue的監(jiān)聽(tīng)事件 let count = this.balls.length; while (count--) { let ball = this.balls[count]; if (ball.show) { let rect = ball.el.getBoundingClientRect(); //獲取小球的相對(duì)于視口的位移(小球高度) let x = rect.left - 32; let y = -(window.innerHeight - rect.top - 22); //負(fù)數(shù),因?yàn)槭菑淖笊辖峭碌牡姆较? el.style.display = ""; //清空display el.style.webkitTransform = `translate3d(0,${y}px,0)`; el.style.transform = `translate3d(0,${y}px,0)`; //處理內(nèi)層動(dòng)畫(huà) let inner = el.getElementsByClassName("inner-hook")[0]; //使用inner-hook類來(lái)單純被js操作 inner.style.webkitTransform = `translate3d(${x}px,0,0)`; inner.style.transform = `translate3d(${x}px,0,0)`; } } }, dropping(el, done) { //這個(gè)方法的執(zhí)行是因?yàn)檫@是一個(gè)vue的監(jiān)聽(tīng)事件 /* eslint-disable no-unused-vars */ let rf = el.offsetHeight; //觸發(fā)重繪html this.$nextTick(() => { //讓動(dòng)畫(huà)效果異步執(zhí)行,提高性能 el.style.webkitTransform = "translate3d(0,0,0)"; el.style.transform = "translate3d(0,0,0)"; //處理內(nèi)層動(dòng)畫(huà) let inner = el.getElementsByClassName("inner-hook")[0]; //使用inner-hook類來(lái)單純被js操作 inner.style.webkitTransform = "translate3d(0,0,0)"; inner.style.transform = "translate3d(0,0,0)"; el.addEventListener("transitionend", done); //Vue為了知道過(guò)渡的完成,必須設(shè)置相應(yīng)的事件監(jiān)聽(tīng)器。 }); }, afterDrop(el) { //這個(gè)方法的執(zhí)行是因?yàn)檫@是一個(gè)vue的監(jiān)聽(tīng)事件 let ball = this.dropBalls.shift(); //完成一次動(dòng)畫(huà)就刪除一個(gè)dropBalls的小球 if (ball) { ball.show = false; el.style.display = "none"; //隱藏小球 } } }
關(guān)于 transitionend
關(guān)于drop方法,是實(shí)現(xiàn)每一個(gè)ball的show屬性和el屬性處理,并且點(diǎn)擊一次會(huì)自動(dòng)將一個(gè)小球放到 dropBalls 數(shù)組里面,放到里面就代表的是一個(gè)小球已經(jīng)被開(kāi)始執(zhí)行動(dòng)畫(huà),但是由于動(dòng)畫(huà)是異步的,所以先主動(dòng)設(shè)置.
關(guān)于 getBoundingClientRect (位移的計(jì)算是從左上角開(kāi)始)
使用 getBoundingClientRect 獲取到當(dāng)前元素的坐標(biāo),然后需要位移的left減去元素的寬獲取真正的最終位移x坐標(biāo)
使用 getBoundingClientRect 獲取到當(dāng)前元素的坐標(biāo),然后需要當(dāng)前屏幕的高度減去元素的 top 再減去元素本身的高度獲取到真正的最終位移 y 坐標(biāo),并且這個(gè)是負(fù)數(shù),因?yàn)槭菑淖笊辖峭碌姆较?/p>
關(guān)于html重繪
因?yàn)闉g覽器對(duì)于重繪是有要求并且是有隊(duì)列完成的,這是主要為了性能,雖然動(dòng)畫(huà)隱藏了小球display none,但沒(méi)有觸發(fā)html重繪,或者說(shuō)沒(méi)有立即觸發(fā)html重繪,所以需要手動(dòng)
let rf = el.offsetHeight; 這是一個(gè)手動(dòng)觸發(fā)html重繪的方法
網(wǎng)頁(yè)性能管理詳解
高性能JavaScript 重排與重繪
css代碼.ball-container .ball position: fixed //小球動(dòng)畫(huà)必須脫離html布局流 left: 32px bottom: 22px z-index: 200 transition: all 0.4s cubic-bezier(0.49, -0.29, 0.75, 0.41) .inner width: 16px height: 16px border-radius: 50% background: rgb(0, 160, 220) transition: all 0.4s linear
關(guān)于cubic-bezier(0.49, -0.29, 0.75, 0.41),是動(dòng)畫(huà)拋物曲線(貝塞爾曲線)的配置,基于css3實(shí)現(xiàn),參考貝塞爾曲線與CSS3動(dòng)畫(huà)、SVG和canvas的基情 ,至于拋物線放在外層就是為了控制內(nèi)層的元素的軌道和方向的.
2、星星組件star.vue整個(gè)流程是:
綁定星星類型的class(48,36,24尺寸),使用starType
使用class來(lái)顯示星星,有3種類型,全星,半星,無(wú)星,使用star-item代表星星本身,然后分別使用on,off,half代表三種不同類型的星星
一個(gè)span代表一個(gè)星星項(xiàng)目,并且使用v-for循環(huán)將星星項(xiàng)目輸出
最后形成的星星html就類似這樣
html部分
js部分
設(shè)置常量是為了方便解耦
星星計(jì)算比較巧妙(根據(jù)分?jǐn)?shù)轉(zhuǎn)換為星星數(shù))
對(duì)于分?jǐn)?shù)score進(jìn)行乘以2然后向下取整,然后再除以2,是為了獲取所有星星的數(shù)量,并且這個(gè)數(shù)量是0.5倍數(shù)的,例如4.6 2就是9.2,然后向下取整是9,然后再除以2就是4.5,那么就可以得到一個(gè)0.5倍數(shù)的星星數(shù),可以轉(zhuǎn)換為4個(gè)全星+一個(gè)半星
對(duì)于非整數(shù)的星星算作是半個(gè)星星,需要知道是否有存在這種情況,所以分?jǐn)?shù)score%1 ,例如 8 % 1是0,8.5 % 1就不是0,并且這個(gè)半星只會(huì)出現(xiàn)一次,因?yàn)榘胄菭顟B(tài)就只要一個(gè)
沒(méi)有星星的部分是要補(bǔ)全的,這里使用while循環(huán)來(lái)處理這種情況
css部分引入mixin.styl是為了使用bg-image的mixin,因?yàn)橹白隽艘粋€(gè)mixin是專門(mén)處理2x和3x圖片的轉(zhuǎn)換
因?yàn)檫@里有3種類型的星星圖片,分別是48尺寸,36尺寸,24尺寸,所以對(duì)于每一個(gè)類別的圖片分別使用一種class做對(duì)應(yīng)
每一種星星的尺寸都是有一種相對(duì)應(yīng)的圖片的,例如48尺寸的星星就會(huì)有,并且圖片放在相對(duì)應(yīng)的vue文件目錄下
star48_half@2x.png star48_half@3x.png star48_off@2x.png star48_off@3x.png star48_on@2x.png star48_on@3x.png3、ratingselect組件 html代碼
備注:父組件food.vue傳入的數(shù)據(jù)
方法有:@select="selectRating" @toggle="toggleContent",通過(guò)將字組件的方法和父組件的方法進(jìn)行關(guān)聯(lián),這樣就能夠?qū)崿F(xiàn)跨組件通訊和操作
屬性有::selectType="selectType":onlyContent="onlyContent" :desc="desc":ratings="food.ratings",這是通過(guò)pros傳入到子組件的屬性,將父組件的數(shù)據(jù)傳到子組件里面,也帶有一種通過(guò)父組件來(lái)初始化子組件屬性的意思.
@click="select(2,$event)" select方法傳入類型和事件,然后在methods里面調(diào)用父組件的方法,實(shí)現(xiàn)子組件控制父組件的目的
:class="{"active":selectType ===2}" 根據(jù)類型來(lái)確定顯示的class,實(shí)現(xiàn)不同類型顯示不同樣式的目的
positives.length 使用計(jì)算屬性自動(dòng)計(jì)算類型數(shù)組的長(zhǎng)度,用來(lái)顯示不同類型的數(shù)量
@click="toggleContent" :class="{"on":onlyContent}"
toggleContent 控制是否展示有內(nèi)容的rate,也是在methods里面調(diào)用父組件的方法,實(shí)現(xiàn)子組件控制父組件的目的
綁定on這個(gè)class來(lái)控制該按鈕的樣式
JS代碼const POSITIVE = 0; //設(shè)置顯示常量 const NEGATIVE = 1; const ALL = 2; export default{ props: { ratings: { //傳入ratings數(shù)組,跟food.ratings關(guān)聯(lián) type: Array, default(){ return []; } }, selectType: { //跟selectType關(guān)聯(lián),通過(guò)在父組件里面設(shè)置這3個(gè)值來(lái)實(shí)現(xiàn)控制子組件的操作 type: Number, default: ALL }, onlyContent: { //跟onlyContent關(guān)聯(lián) type: Boolean, default: true }, desc: { //跟desc關(guān)聯(lián) type: Object, default(){ return { all: "全部", positive: "滿意", negative: "不滿意" } } } }, computed: { positives(){ //自動(dòng)過(guò)濾rateType(正面的rate) return this.ratings.filter((rating) => { //js的filter函數(shù)會(huì)返回一個(gè)處理后的(為true)結(jié)果的結(jié)果數(shù)組 return rating.rateType === POSITIVE; }) }, negatives(){ //自動(dòng)過(guò)濾rateType(反面的rate) return this.ratings.filter((rating) => { return rating.rateType === NEGATIVE; }) } }, methods: { select(type, event) { // 選擇rateType并且通知父組件 if (!event._constructed) { return; } this.$emit("select", type); // 派發(fā)事件,父組件監(jiān)聽(tīng)此事件 }, toggleContent(event) { // 選擇是否顯示有內(nèi)容的rate,并且通知父組件 if (!event._constructed) { return; } this.$emit("toggle"); } } }4、商品區(qū)域goods.vue HTML
v-for使用已經(jīng)很常見(jiàn)了,不過(guò)這里需要了解,vue1和2有區(qū)別,現(xiàn)在是用vue2,所以index變量傳遞會(huì)變成現(xiàn)在這種模式 (item,index) in goods
vue傳遞原生事件使用$event
:class="{"current":currentIndex === index}" 是vue的綁定class的使用方法,通過(guò)綁定一個(gè)class變量來(lái)直接操作,并且這里的邏輯會(huì)跟js代碼里面對(duì)應(yīng)
通過(guò)currentIndex和index做對(duì)比,來(lái)確認(rèn)是否添加current類,他們之間的對(duì)比關(guān)系也就是 menu 區(qū)域和 foods 區(qū)域的顯示區(qū)域的對(duì)比關(guān)系
通過(guò)添加current類來(lái)實(shí)現(xiàn)當(dāng)前頁(yè)面的區(qū)域的樣式變化
currentIndex是一個(gè)計(jì)算屬性,可以隨時(shí)變化并且直接反應(yīng)到dom上(看js里面邏輯)
v-show 和 v-if 的區(qū)別官網(wǎng)已經(jīng)說(shuō)過(guò)
v-if 是“真正的”條件渲染,因?yàn)樗鼤?huì)確保在切換過(guò)程中條件塊內(nèi)的事件監(jiān)聽(tīng)器和子組件適當(dāng)?shù)乇讳N毀和重建。
v-if 也是惰性的:如果在初始渲染時(shí)條件為假,則什么也不做——直到條件第一次變?yōu)檎鏁r(shí),才會(huì)開(kāi)始渲染條件塊。
一般來(lái)說(shuō), v-if 有更高的切換開(kāi)銷,而 v-show 有更高的初始渲染開(kāi)銷。因此,如果需要非常頻繁地切換,則使用 v-show 較好;如果在運(yùn)行時(shí)條件不太可能改變,則使用 v-if 較好。
$refs 的使用是vue操作dom的一種方式:
ref 被用來(lái)給元素或子組件注冊(cè)引用信息。引用信息將會(huì)注冊(cè)在父組件的 $refs 對(duì)象上。
如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素; 如果用在子組件上,引用就指向組件實(shí)例:
hook鉤子類的使用,需要結(jié)合js里面的語(yǔ)法來(lái)看,這個(gè)類只是用來(lái)操作,不會(huì)產(chǎn)生dom的渲染,方便js控制清晰.
JS
這里最關(guān)鍵的是menu和food兩個(gè)區(qū)域的對(duì)應(yīng)處理:
在vue實(shí)例生命周期的開(kāi)始created分別加載_initScroll和_calculateHeight
通過(guò) _calculateHeight 計(jì)算foods內(nèi)部每一個(gè)塊的高度,組成一個(gè)數(shù)組listHeight
在 _initScroll 里面,設(shè)置了bscroll插件的一個(gè)監(jiān)聽(tīng)事件scroll,將food區(qū)域當(dāng)前的滾動(dòng)到的位置的y坐標(biāo)設(shè)置到一個(gè)vue實(shí)例屬性 scrollY this.scrollY = Math.abs(Math.round(pos.y));
通過(guò)計(jì)算屬性currentIndex,獲取到food滾動(dòng)區(qū)域?qū)?yīng)的menu區(qū)域的子塊的索引,然后通過(guò)設(shè)置一個(gè)class來(lái)做樣式切換變化:class="{"current":currentIndex === index},實(shí)現(xiàn)聯(lián)動(dòng)
另外當(dāng)點(diǎn)擊menu 區(qū)域的時(shí)候,會(huì)觸發(fā)selectMenu事件,也會(huì)根據(jù)點(diǎn)擊到的menu子塊的索引然后去觸發(fā)food區(qū)域滾動(dòng)到對(duì)應(yīng)的高度區(qū)塊區(qū)間this.foodsScroll.scrollToElement(el, 300);
這樣完成整個(gè)對(duì)應(yīng).
拋物線小球動(dòng)畫(huà)橫跨多個(gè)vue組件(也可以說(shuō)是橫跨了多個(gè)DOM)
better-scroll需要安裝,npm安裝,具體參看better-scroll官網(wǎng)
關(guān)于在selectMenu中點(diǎn)擊,在pc界面會(huì)出現(xiàn)兩次事件,在移動(dòng)端就只出現(xiàn)一次事件的問(wèn)題:
原因:
bsScrooler會(huì)監(jiān)聽(tīng)事件(例如touchmove,click之類),并且阻止默認(rèn)事件(prevent stop),并且他只會(huì)監(jiān)聽(tīng)移動(dòng)端的,pc端的沒(méi)有監(jiān)聽(tīng)
在pc頁(yè)面上 bsScroller也派發(fā)了一次click事件,原生也派發(fā)了一次click事件
解決:
針對(duì)bsScroole的事件,有_constructed: true,所以做處理,return 掉非bsScroll的事件
SCSS 預(yù)處理器 css預(yù)處理器index.scss是SCSS文件的入口文件,里面使用 @import 引入各種SCSS文件
@import "./base"; @import "./mixin"; @import "./icon.css";
在入口文件main.js中全局引用index.scss
import "common/css/index.scss";關(guān)于ESlint
eslint 是一個(gè)js代碼風(fēng)格檢查器,配合vue-cli腳手架中的熱更新,可以很方便的定位和提示錯(cuò)誤。在公司多人協(xié)作開(kāi)發(fā)時(shí)可以確保代碼風(fēng)格保持一致,可以很方便的閱讀他人的代碼。
手機(jī)測(cè)試網(wǎng)頁(yè)技巧將 localhost 換成自己的ip (Windows在命令行執(zhí)行ipconfig查看,mac執(zhí)行ifconfig查看)
然后復(fù)制地址欄地址,進(jìn)入草料二維碼,然后生成二維碼,然后用手機(jī)掃一掃就可以查看了,前提是,你手機(jī)和電腦必須在同一個(gè)局域網(wǎng)。
項(xiàng)目運(yùn)行克隆項(xiàng)目到本地 git clone git@github.com:nanyang24/eleme-vue.git 安裝依賴 npm install 本地開(kāi)發(fā),開(kāi)啟服務(wù)器,瀏覽器訪問(wèn)http://localhost:8080 npm run dev 構(gòu)建生產(chǎn) npm run build 運(yùn)行打包文件 node prod.server.js 會(huì)看到 Listening at http://localhost:9000 在瀏覽器中打開(kāi)即可學(xué)習(xí)參考
Vue2.0 官網(wǎng): https://vuefe.cn/v2/guide/
vue-cli:https://github.com/vuejs/vue-cli
vue-router 2.0文檔:https://router.vuejs.org/zh-cn/
axios:https://github.com/axios/axios
webpack官網(wǎng):https://webpack.js.org/
better-scroll 插件使用:https://github.com/ustbhuangy...
SCSS 預(yù)處理器: https://www.sass.hk/
ESlint 代碼風(fēng)格檢查:http://eslint.org/docs/rules/
ES6 入門(mén): http://es6.ruanyifeng.com/
Sticky footers http://www.w3cplus.com/css3/c...
Flex 彈性布局: http://www.ruanyifeng.com/blo...
設(shè)備像素比:http://www.zhangxinxu.com/wor...
貝塞爾曲線生成器:http://cubic-bezier.com/#.17,...
localStorage 本地存儲(chǔ): http://www.cnblogs.com/st-les...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/107581.html
摘要:項(xiàng)目地址郵箱求工作杭州項(xiàng)目介紹來(lái)公司實(shí)習(xí),由于技術(shù)棧原因,學(xué)習(xí)一下,以此項(xiàng)目來(lái)做練習(xí)。項(xiàng)目暫時(shí)只能在開(kāi)發(fā)環(huán)境運(yùn)行。 項(xiàng)目地址 https://github.com/Gitjinfeiy... 郵箱 1912144808@qq.com(求工作 杭州) 項(xiàng)目介紹 來(lái)公司實(shí)習(xí),由于技術(shù)棧原因,學(xué)習(xí)一下angular2,以此項(xiàng)目來(lái)做練習(xí)。 項(xiàng)目暫時(shí)只能在開(kāi)發(fā)環(huán)境運(yùn)行。...
摘要:前言本文的前身是源自上的項(xiàng)目但由于該項(xiàng)目上次更新時(shí)間為年月日,很多內(nèi)容早已過(guò)期或是很多近期優(yōu)秀組件未被收錄,所以小肆今天重新更新了內(nèi)容并新建項(xiàng)目。提交的項(xiàng)目格式如下項(xiàng)目名稱子標(biāo)題相關(guān)介紹如果收錄的項(xiàng)目有錯(cuò)誤,可以通過(guò)反饋給小肆。 前言 本文的前身是源自github上的項(xiàng)目awesome-github-vue,但由于該項(xiàng)目上次更新時(shí)間為2017年6月12日,很多內(nèi)容早已過(guò)期或是很多近期優(yōu)...
摘要:一個(gè)基于全家桶開(kāi)發(fā)的仿知乎日?qǐng)?bào)單頁(yè)應(yīng)用項(xiàng)目地址源碼地址項(xiàng)目在線地址在線地址模式下推薦使用移動(dòng)端模式瀏覽去觀看如果覺(jué)得做得還不錯(cuò)或者項(xiàng)目源碼對(duì)您有幫助希望您小抬右手到右上角點(diǎn)一個(gè)您的支持是作者長(zhǎng)期更新維護(hù)的動(dòng)力項(xiàng)目起源從二月份開(kāi)始學(xué)習(xí)學(xué)習(xí)了 Vue-News 一個(gè)基于vue全家桶開(kāi)發(fā)的仿知乎日?qǐng)?bào)單頁(yè)應(yīng)用 項(xiàng)目github地址:源碼地址 項(xiàng)目在線地址:在線地址 (PC模式下推薦使用chro...
摘要:毫無(wú)疑問(wèn),設(shè)計(jì)模式于己于他人于系統(tǒng)都是多贏的設(shè)計(jì)模式使代碼編寫(xiě)真正工程化設(shè)計(jì)模小書(shū)前端掘金這是一本關(guān)于的小書(shū)。 JavaScript 常見(jiàn)設(shè)計(jì)模式解析 - 掘金設(shè)計(jì)模式(Design pattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過(guò)分類編目的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。使用設(shè)計(jì)模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。毫無(wú)疑問(wèn),設(shè)計(jì)模式于己于他人于系統(tǒng)都是多贏的;設(shè)計(jì)...
閱讀 1456·2021-09-28 09:43
閱讀 4351·2021-09-04 16:41
閱讀 1982·2019-08-30 15:44
閱讀 3878·2019-08-30 15:43
閱讀 834·2019-08-30 14:21
閱讀 2088·2019-08-30 11:00
閱讀 3379·2019-08-29 16:20
閱讀 2010·2019-08-29 14:21