移動端web開發初探之Vuejs的簡單實戰
這段時間在做的東西,是北郵人論壇APP的註冊頁。這個註冊頁是內嵌的網頁,因為打算安卓和IOS平臺同時使用。因此實際上就是在做移動端的web開發了。
在這過程中遇到了不少有意思的東西。
DEMO的github地址在這裏
內容提要:
- meta標簽
- Vuejs的簡單實戰
- CSS移動端全屏背景
- CSS移動端動畫初探
meta標簽
這點與在PC端寫前端有著很大的區別,移動端的meta標簽簡直多。我就說說我所用到的標簽。
<!-- 1、如果支持Google Chrome Frame:GCF,則使用GCF渲染;2、如果系統安裝ie8或以上版本,則使用最高版本ie渲染;3、否則,這個設定可以忽略。 --> <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1"> <!-- 對視窗縮放等級進行限制,使其適應移動端屏幕大小 --> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 當把這個網頁添加到主屏幕時的標題(僅限IOS) --> <meta name="apple-mobile-web-app-title" content="北郵人論壇註冊"> <!-- 添加到主屏幕後全屏顯示 --> <meta name="apple-touch-fullscreen" content="yes" />
尤其是第二個meta標簽,是移動端適配非常重要的一句話。
整體布局
整體的布局大致是4-5頁橫向布局。第一頁是用來填寫註冊信息的。後面的幾頁是用來選擇關註的版面的。
註冊信息頁
關註版面頁
整體架構
前端采用的架構大致是這樣:
- 滑動切換頁面的效果來自swiper.js
- 頁面內容的渲染采用Vue.js
- 頁面布局和樣式采用純css,部分效果采用css3
- ajax部分采用vue-resource
後端支撐的框架來自php的laravel,當然,這不是本文的重點,僅提及一下。
是的這次的開發中,已經看不到jquery的身影了——這也是前端以後發展後的結果——慢慢地脫離jquery的依賴。不過jquery給前端帶來的改變和發展是無人能替代的。
swiper.js的應用
引入swiper.js來進行頁面的切換效果純粹是因為這次開發的周期要求比較短,要考慮效果和兼容性兼備的情況下,我就偷懶找了一個動畫庫。
不過這個動畫庫的效果我還是算比較滿意的。而整體來說使用也相當方便。尤其是,swiper.js是可以不依賴jquery的。
使用起來也比較方便。我簡要說說用法。
首先需要在頁面頂部的head標簽裏加入swiperjs的css文件:
<link rel="stylesheet" href="css/swiper.min.css')">
然後在頁面底部可以引入和寫下相應的js:
<script src="{{ asset('js/swiper.min.js') }}"></script> <!-- Initialize Swiper --> <script> var swiper = new Swiper('.swiper-container', { pagination: '.swiper-pagination', paginationType: 'progress', noSwipingClass: 'swiper-no-swiping', allowSwipeToNext: true, allowSwipeToPrev: true, }); </script>
解釋一下,創建一個swiper的對象,然後這對象的容器是class叫做swiper-container的一個html元素。對其的配置是:
- pagination: ‘.swiper-pagination’, 顯示頁碼
- paginationType: ‘progress’, 頁碼顯示格式為進度條,可以參見頂部藍白色的進度條
- noSwipingClass: ‘swiper-no-swiping’, 不允許進行觸摸滑動的元素的class名稱
- allowSwipeToNext: true, 允許向後滑動
- allowSwipeToPrev: true, 允許向前滑動
相應的HTML代碼可以如下:
<!-- swiper生效的容器 -->
<div class="swiper-contanier">
<div class="swiper-wrapper">
<!-- 具體滑動的頁面 -->
<div class="swiper-slide"></div>
<div class="swiper-slide"></div>
<div class="swiper-slide"></div>
<div class="swiper-slide"></div>
</div>
</div>
<!-- 進度條 -->
<div class="swiper-pagination"></div>
有些頁面是不能直接讓用戶通過觸摸來前後滑動的,而必須通過點擊按鈕觸發。比如第一頁註冊頁,這個頁面就是必須填寫完信息然後點擊下一步進行驗證後然後才可以滑動到後面的內容。所以只要將這個頁面所在的class裏加上swiper-no-swiping這個class就可以實現無法觸摸滑動切換頁面了。
然後我們可以通過swiper.slideNext(bool,time)這個方法來進行手動控制向後翻頁的動作以及控制動畫的時長。這個內容會在vue裏說到。
Vue.js的簡單實戰
由於本次的開發只是在原有的北郵人開放平臺的項目的基礎上加入一個快速註冊的功能,所以Vue.js的引入並不是為了將整個項目重構,而只是為了嘗試一下脫離jquery的情況下對於開發來說不同的體驗是什麽,能學到什麽。
關於Vue.js的用法、特性什麽的不是本文的重點,題外話就不說了。說說本次實現思路。因為我的Vuejs實際上也只是算入門級別,只能說會用但是還沒到能駕馭的程度。
首先整個頁面包在class叫做swiper-contanier的元素中。因為這個元素包括了滑動所需的所有東西,我們實際上可以簡單的把它看成是一個SPA(Single Page Application),用一個Vue的實例就可以接管整個頁面了(這麽說是不負責的,因為實際上Vue是組件化的思想)。
先創建一個基本的Vue的示例,並將其和swiper-container綁定起來:
var vm = new Vue({
el: ".swiper-contanier", // 將這個實例與html元素綁定起來
data: {}, // 所需要變動、關註的數據,也是vue的核心
ready: function(){}, // vue提供的鉤子,用於在vue渲染視圖完成後立即觸發
methods: {} // 方法,用於操作、更新、改變數據而改變視圖
})
註冊頁實現
上面所說,我們的頁面是構建在一個Vue的實例上的。因此不同類型的兩種頁面如何用一個實例來接管呢?在這裏我的實現方式是用兩類數據來分別表示。
我們分析一下註冊頁:
實際上註冊頁的中間部分是重復的元素,他們都是input標簽+顯示文字標簽(對,尤其註意這裏並不是用placeholder實現的)。效果:
所以這中間的部分實際上可以看成是一個列表,可以用Vue的v-for來渲染。列表裏所不同的只是顯示的文字不同以及input框的類型不同(有text類型的,有password類型的),所以用數據綁定的方式我們可以將這個頁面的數據格式安排如下:
data: {
main: [
{"name":"username","info":"用戶名(以英文開頭+英文數字)","type":"text"},
{"name":"passwd","info":"設置密碼","type":"password"},
{"name":"passwd_confirm","info":"在輸入一遍密碼","type":"password"},
{"name":"gwno","info":"校園網賬戶(默認是學號)","type":"text"},
{"name":"gwpwd","info":"校園網密碼(默認是身份證後六位)","type":"password"},
],
}
同時我們創建一個便於和前端視圖進行雙向綁定的數據對象userInfo:
data: {
main: [...],
userInfo: {
username: "",
passwd: "",
passwd_confirm: "",
gwno: "",
gwpwd: ""
}
}
而在前端的話我們就可以用這個數據來進行視圖渲染:
<ul class="user-info">
<li v-for="item in main" style="position: relative;">
<!-- input輸入框 -->
<!-- 此處用了v-model將數據和視圖進行了雙向綁定 -->
<input class="effect" type="{{item.type}}" v-model="userInfo[item.name]">
<!-- 提示信息 -->
<label>
<span>{{item.info}}</span>
</label>
<!-- input框的底下的線條 -->
<span class="focus-border cube"></span>
</li>
</ul>
<a href="#"><button>下一步</button></a>
於是一個輸入的列表就很容易做出來了。然後既然是表單,就需要驗證。而此處做的驗證實際上有這麽幾點:
- 用戶名是否合法/重復?
- 兩次輸入的密碼是否相同?
- 校園網賬戶的密碼是否正確?
其中只有第二點兩次密碼輸入是否相同可以用前端直接判斷,而第一、三點都是需要通過ajax的方式向後臺發送驗證請求的。為了能夠體現和辨別錯誤與否,我們在main下的每個條例裏加入了一個error屬性,並規定如下三種狀態:
- true,代表有錯誤,提示錯誤
- false,代表正確,提示正確
- normal,代表默認,顯示默認值
於是我們可以在method下寫一些方法來進行判斷。
checkUserId: function(msg){
if (msg !== ""){
this.$http.post('url'+msg,function(data){
if (data.success){
this.main[0].error = false;
} else{
this.main[0].error = true;
}
})
} else{
this.main[0].error = "normal";
}
},
checkUserPwd: function(){
if (this.userInfo.passwd_confirm !== ""){
this.userInfo.passwd == this.userInfo.passwd_confirm && this.userInfo.passwd_confirm != "" ? this.main[2].error = false : this.main[2].error = true;
}
},
當然這個只是一個引入的功能,我們再聚合一下:
check: function(msg,i){
var index = i;
this.userInfo[msg] != "" ? this.main[index].effect = true : this.main[index].effect = false;
switch (msg){
case "username":
this.checkUserId(this.userInfo[msg]);
break;
case "passwd":
this.userInfo.passwd !== "" ? this.main[1].error = false : this.main[1].error = "normal";
this.checkUserPwd();
break;
case "passwd_confirm":
this.checkUserPwd();
break;
case "gwno":
this.userInfo.gwno !== "" ? this.main[3].error = false : this.main[3].error = "normal";
break;
case "gwpwd":
this.userInfo.gwno !== "" ? this.main[4].error = false : this.main[4].error = "normal";
break;
}
}
這樣通過一個check的method我們就可以將整個表單的驗證的方法容納進來了。(此處對於校園網賬號的驗證會放到提交表單的函數中)。為了能在視圖中體現正確、錯誤、正常的不同形態,我們需要對前端的一些結構進行一些修改。我們需要給main下的每個條例加入錯誤信息,也即errorInfo。
所以至此整個main的結構是這樣:
main: [
{"name":"","info":"","type":"","error":"","errorInfo":""},
...
]
然後我們需要加入提交驗證的部分:
submitReg: function(){
var flag = 0;
// 用於判斷表單是否都是正確的
this.main.map(function(obj){
obj.error == false ? flag += 1 : flag +=0;
})
if (flag == 5){
this.$http.post('url',this.userInfo)
.then(function(res){
if (res.success){
swiper.slideNext(false,300); // 驗證正確就可以進入下一頁
} else{
this.main[4].error = true;
}
})
}
},
將視圖部分修改如下:
<ul class="user-info">
<li v-for="item in main" style="position: relative;">
<!-- 綁定blur事件 -->
<input @blur="check(item.name,$index)" class="effect" type="{{item.type}}" v-model="userInfo[item.name]">
<!-- 根據error類型切換不同的標簽顯示 -->
<label>
<!-- 此處用到了v-show的方法 -->
<span v-show="item.error == 'normal'">{{item.info}}</span>
<span style="color: red;" v-show="item.error == true">{{item.errorInfo}}</span>
<span v-show="item.error == false">{{item.info}}√</span>
</label>
<span class="focus-border cube"></span>
</li>
</ul>
<!-- 點擊下一步的同時提交表單信息 -->
<a href="#"><button @click="submitReg">下一步</button></a>
至此,整個註冊頁的布局和功能性的部分都已經做完了。不過這只是一塊比較簡單的部分,我們用到了vuejs的的v-for進行列表渲染,用到了v-model進行數據的雙向綁定,用到了method進行一些數據的處理,用到了v-show進行條件顯示,一個基本的頁面下我們已經能嘗試這麽多vue的特性了。而相比於jquery的dom操作,我覺得vue在此處最好的地方在於,表單的提交很方便。由於雙向綁定了數據,只需要後臺把數據格式規定好給我,我按照後端的數據結構整理一下我前端的數據結構然後就可以直接提交給後端了。而且省去了很多dom操作的地方。
不過除了vue的部分,我還想來說說兩個東西,跟css有關:
- 全屏背景
- 輸入框文字提升效果,並涉及到移動端動畫效果的處理
全屏背景
不僅僅是簡單的background-size: cover那麽簡單了,還需要進行小小的處理。先說說我希望實現的效果吧。我希望的效果是整個背景能夠填充整個頁面,並且在頁面元素上下滾動的情況下,背景固定而不隨著元素滾動。
放到往常我可能會這麽寫:
body,html{
height: 100%;
}
body{
background: url(bg.png) center 0 no-repeat;
background-size: cover;
}
但是這樣的話在移動端會出現比較嚴重的後果,那就是一旦頁面元素的高度大於整個頁面後,滾動頁面元素的時候,背景也會隨之而動。而且背景會被撐開。這不是我所希望的。
這裏用到一個小技巧,用上:before的方法。
body:before {
content: "";
position: fixed;
z-index: -1;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: url(bg.png) center 0 no-repeat;
background-size: cover;
}
這個用上before的偽元素的方法是一個很有奇效的小技巧。大家不妨可以試試。這樣的話在移動端也能完美實現背景固定而且顯示全屏。
移動端動畫簡單初探
在做PC端的web的動畫效果的時候,由於PC端的性能足夠,所以在寫一些效果的時候往往沒有考慮到性能的問題。這次在開發的時候就遇到了動畫效果實現上不小的問題。大家先來看看這兩個動畫的比較:上面一個動畫和下面一個動畫:
第一個:
https://codepen.io/molunerfinn/pen/zBGrxx
第二個:
https://codepen.io/molunerfinn/pen/oLXjKz
會發現這兩個東西的效果相差甚大。實際上,二者實現的最終效果是一致的,都是讓小球按一條口型路線運動。但是為何顯示上來說,第一個這麽流暢,而第二個有明顯卡頓呢?
這裏涉及到很多東西,不光光是重繪(repaint),還有軟硬件性能上面的問題。感興趣的話可以參考這些文章:
- Web動畫性能指南
- 高性能CSS3動畫
- CSS動畫的性能優化
在移動端上的效果如果沒有優化的話,實際上大致就是第二種的效果——讓人看起來有所卡頓。簡單來說,在移動端,有些效果是由瀏覽器來渲染的——那麽這些效果如果比較復雜,而移動端的瀏覽器性能又不太足夠的情況下,效果就比較卡頓。有些效果可以由GPU來渲染,那麽這些效果渲染起來就相對來說比較流暢。而我們還可以人為觸發GPU硬件渲染,通過 transform的translate3d屬性就能實現硬件渲染從而俗稱硬件加速。
舉個簡單例子。如果讓一個元素從(0,0)->(100px,0),正常思路是left: 0->left: 100px,然後再用transition屬性進行過渡。不過這樣的話在移動端上效果就很感人,因為涉及到性能問題。但是如果我們用另外一種方式: transform: translate3d(100px,0,0)的話,就可以讓這個動畫效果由GPU去渲染,那麽這樣的話,在移動端的效果也是完全可以接受的流暢。
實際上,能夠觸發GPU渲染的動作有opacity,transform,transition,animation等等。但是像top,left,color,size等屬性的變化則不會觸發GPU渲染。
本文中描述的實例,是當焦點集中到input框的時候,文本上移並且有顏色變化。本來想實現的是文本大小還有變化。但是由於上面說的情況,涉及到size變化的時候,效果就會大打折扣。無奈之下我只能砍掉這個效果了。而實現文本上移實際上就是采用了transform的translate3d的方式,將其往上移動,並配合transition進行了一下過渡處理罷了。
具體的CSS實現大致如下:
input:focus ~ label,.trans {
color: #fff;
transition: 0.3s;
-webkit-transform: translate3d(0, -20px, 0);
-moz-transform: translate3d(0, -20px, 0);
-ms-transform: translate3d(0, -20px, 0);
transform: translate3d(0, -20px, 0);
}
實際上也不難不是麽?
關註版面頁的實現
實際上從註冊頁的實現中已經可以瞅見關註版面頁實現的方法了。實際上關註版面頁不過也是列表的渲染,在數據裏定義好相應的屬性就行了。然後用一個picked的屬性來看看是否被選中即可。最後完成註冊的時候,將所有選中的列表組裝成相應的數組提交到後臺就行了。因為註冊頁裏這些方法已經說過了,所以就不再贅述了。
總結
簡單總結一下,這次移動端的開發中學習到的東西。
- Vuejs的簡單使用,從DOM操作->數據綁定
- 全屏背景的實現
- 流暢動畫效果的實現
實際上,兼容性方面還有不少的東西需要訴說,不過限制於篇幅以及本文的主要內容並不是在糾結移動端的兼容性的所以並沒有在兼容性方面進行記錄。
在排版上我還是沒有用上比較流行的flex,對於Vue還沒有使用組件化開發的思路。這些都是需要改進的部分。不過這次的開發過程中經過美工的指點,將我之前寫頁面的時候註意不到的很多細節部分,比如線條尺寸,元素間隔,顏色搭配等東西給指了出來,這些東西都不是簡簡單單就實現的。總之,有時間的話,我想我可以將設計方面的知識融入到我的前端開發中,想必能讓我的作品更加地符合審美和用戶的體驗吧~
DEMO的github地址在這裏
文章作者: Molunerfinn
文章鏈接: https://molunerfinn.com/vuejs-1/
版權聲明: 本博客所有文章除特別聲明外,均采用 CC BY-NC-SA 4.0 許可協議。轉載請註明來自 MARKSZのBlog!
移動端web開發初探之Vuejs的簡單實戰