概述
--
-
項目中會用到的插件 vue-router vue-resource
-
打包工具 webpack
-
依賴環境 node.js
start 安裝vue開發的模板
# 全局安裝 vue-cli
$ npm install -g vue-cli # 創建一個基於 "webpack" 模板的新項目 $ vue init webpack my-project # 安裝依賴,走你 $ cd my-project $ npm install $ npm run dev
-
文件解釋:
-
build中配置了webpack的基本配置、開發環境配置、生產環境配置
-
config中配置了路徑端口值等
-
node_modules為依賴的模塊
-
src放置組件和入口文件
-
static放置靜態資源文件
-
index.html文件入口
-
-
webpack中的一些解釋:
-
new HtmlWebpackPlugin 這個插件的作用是把output輸出的文件自動插入到html里
-
這里不使用elint檢查代碼
-
step1 配置路由
-
這里使用vue-router 中文基本用法可參見http://router.vuejs.org/zh-cn...
-
把原來腳手架中的new Vue換成了路由實現,最容易忘記的一點是Vue.use(VueRouter);
-
這里的Vue.extend()暫時先定義兩個臨時的組件,main.vue為入口文件,組件內要添加路由視圖標簽<router-view></router-view>
-
這里的router.start(app,"#app")的app是require進來main,'#app'是添加從index.html的id為app入口
現在的效果是:
-this is bar 上面那部分是main.vue里面的,
-this is bar 則是有router-view渲染出來的
step2 提取路由
-
app中要切換多個路由為了不代碼耦合將map映射部分提取到一個router.js文件中
-
這里要后續要引用zepto開發,所以這里要在webpack.base.conf.js中做一個配置
externals: {
'zepto': 'Zepto' },
*上截圖解釋這個參數,所以要加html中加如zepto的鏈接,然后在其他地方就可以引用了*
3.只是簡單地把組件和映射放到router.js中,然后在app.js中傳入router
router.js
app.js增加的代碼
現在頁面還是和之前一樣沒有變化,基本框架和路由搭建完成,然后就可以開始封裝組件
step3 main.vue組件編寫
1.app的主頁底部一般都有幾個tab鍵是固定不變的,這里實現四個tab鍵分別是首頁,發現,通知,我 2.這里使用mobile sui搭建ui,在main.vue<style>中引用sui樣式
@import './assets/css/sm.css'; @import './assets/css/sm-extend.min.css';
這樣已經能呈現一個底部導航,但是不太符合vue組件化的概念,畢竟重復了四次的tab代碼,所有這里要用slot進行內容分發
3.這里要理解slot元素,先上一張官方的解釋
step4 slot的使用
<slot> 就是外部調用時,標簽中的內容。如果外部調用時沒有提供內容的話,那么它就會使用自己默認提供的內容,非常方便。
-這個字面意思確實難以理解,用代碼解釋 -首先定義Bar.vue組件替代最外層的nav
-
然后在main.vue import 進來引用
-
原來的nav標簽就會變成這樣寫
-
先看現在的效果
-
一切正常,但是如果把Bar.vue中的slot注釋,就沒有這些導航圖標了,所以我可以理解為使用了slot可以把不在Bar.vue的template中的代碼引進來,不使用就直接使用Bar.vue的template模板了
-
現在可以把里面的item元素也弄成一個BarItem.vue組件
-
這里要知道一個新的指令v-link和它的activeClass配合
v-link 是一個用來讓用戶在 vue-router 應用的不同路徑間跳轉的指令。http://router.vuejs.org/zh-cn... 詳情看這里
先上代碼BarItem.vue
-
script中的props是在main.vue傳進來的參數,v-link中的replcace:true 是用了router.replace()而不是router.go()也就是不能后退(首頁標簽頁切換就不讓用戶有后退的功能了),activeClass是當路由激活時加上的類
-
main.vue現在的代碼
-
對了,記得把BarItem.vue引進來喔
-
現在的效果還是像之前一樣,但是已經實現組件化
step5 HomeTab 路由切換
-
新建search.vue、message.vue、me.vue、home.vue,然后在router.js中做相應的配置
-
這里動態組件載入就是常說的懶加載組件
當你在使用 Webpack 或者 Browserify 時,在基於異步組件編寫的 Vue 項目時,也可以較為容易的實現惰性加載組件。不再是之前所述的直接引用一個組件,現在需要像下面這樣通過定義一個函數返回一個組件:
-
resolve這個參數有點難理解,實際就是用異步加載,用AMD風格的寫法是
require(['./MyComponent.vue'], function (MyComponent) { // code here runs after MyComponent.vue is asynchronously loaded .})
-
五個路由都寫好就可以隨意切換tab了
-
想要達到這種效果
-
homeTab這部分也是可以提取出組件作為各個tab的頭部
-
贊一下vue的錯誤提示,一開始死活顯示不了,這錯誤提示還是很明顯的
-
用組件記得在js components中注冊
-
還有這個提示,注冊了變量沒
step6 HomeTab 內容+下拉刷新
-
這里的ui用的是sui,包括下拉刷新也是用sui的組件,詳情:http://http://m.sui.taobao.or...
-
下拉刷新會有dom元素的操作,不能用jq的思想,所以要用到自定義指令
自定義指令提供一種機制將數據的變化映射為 DOM 行為
pullToRefresh.js
用到的鈎子函數:
bind:只調用一次,在指令第一次綁定到元素上時調用。
unbind:只調用一次,在指令從元素上解綁時調用。
在注冊之后,便可以在 Vue.js 模板中這樣用(記着添加前綴 v-),當作為屬性指令的時候
用到的指令實例屬性:
-
el: 指令綁定的元素。
-
vm: 擁有該指令的上下文 ViewModel。
-
expression: 指令的表達式,不包括參數和過濾器。
-
params:自定義指令可以接收一個 params 數組,指定一個特性列表,Vue 編譯器將自動提取綁定元素的這些特性。this.params[key]會自動保持更新。
data-ptr-distance="55"可以配置下拉刷新的下拉距離,sui的配置
-
在app.js中注冊自定義指令
import pullToRefresh from './directives/pullToRefresh' //directive Vue.directive('pullToRefresh', pullToRefresh);
-
自定義指令完成
-
然后是要將這部分的ui封裝成組件放在兩個tab里
-
就是封裝官網上的這段代碼
-前面已經說過怎么去拆分組件了,只要你覺得合理,子父組件之間能通信想怎么拆都可以,當然以復用為原則,直接上我自己的拆分方式,可以對比一下跟第二篇的源碼,都會同步到github,無論怎么定義記得要在components里面注冊--vue函數中通過this.$el來獲取當前元素
-
千萬要注意一個概念叫做片段實例
-
就是模板中只有一個頂級元素,否則會對你下面獲取當前元素產生影響,如果你在函數中想要獲取當前元素用this.$el得到的不是一個節點,而是一個空文本元素,那么你就要去檢查你的模板是不是有問題了
-
-
這里最重要的是把剛剛的指令引入進來了
-
前面有提到作為屬性要加v-前綴
-
現在可以去書寫你下拉刷新是要執行的函數了,在methods方法中定義
那個$.showIndicator的效果其實就是這個
-
之前有一點沒有提到,就是sui需要執行一個init()方法才能有上面的效果,而且是在ready方法中init,這是基本配置,所以要在入口文件main.vue加個.page,否則會報找不到id的錯誤
-
現在的效果是下拉可以增加一個.card,但是切換路由的時候再回到頁面就不見了。所以要綁一個變量存數據。今天這個自定義指令挺難理解的,先keep一份代碼吧
step7解決(3)中刷新在兩個tab中都添加元素的問題
-
(3)中的情況是無論在動態tab下拉刷新還是在前端tab下拉刷新同時會增加dom元素
-
主要是因為這一句話,這里的this是指向整個template的根目錄,這也正好可以解釋片段實例為什么獲取不到元素了
-
main.vue
-
-
那就必須是添加到當前指令下的.card-container,所以要獲取到當前指令元素,可以在自定義指令中獲得並傳參
-
PullToRefresh.js
-
-
現在可以main.vue 中的函數中獲取這個參數,利用它獲取當前元素的.card-container
step8不再操作dom只關心數據
-
vue是為數據而生的,不該過多的操作dom,而之前的所有刷新都是append了一次元素
-
為了模擬有真實是的數據時的情況,把將定義兩個數組將分別對應兩個tab里面的數組task1,task2,並在每次下拉時push進去,這時候我們就不在需要操作dom元素了,只需要關心data的變化
-
為了讓存放在不同的數組中在自定義指令的元素上加入了這些屬性
-
然后在pullToRefresh.js中將它設置到自定義屬性中,當然要記得在params中傳入特性,(這里我總感覺我這方法有點麻煩,如果在指令中傳參我還沒有明確,希望有知道的大神指點一二)
-
每次下拉函數都會push一個數組
-
html中用v-for輸出數組
-
基本的home.vue已經全部完成了
-
當我很開心的以為結束了今天的任務的時候,我切換一下底部菜單,然后再回到home tab時,整個人都不好了,剛剛下拉的數據全部沒了,回到剛開始的頁面
-
我本想着要自定義方法來存儲,可是參考資料里面是並不用這么麻煩的,尋尋覓覓了很久很久,終於遇到了神奇的它---keep-alive
-
先來了解一下動態組件
-
當用戶關閉component時,將該component卸載,再次打開時重新加載。
-
-
-
-
這個項目里在router-view標簽中加上keep-live就可以緩存組件了
-
擴展:根據官方的這個案例,通過:is和keep-alive可以實現隨意切換是否緩存這個組件
-
-
-
現在無論怎么點tab鍵都能保留下拉刷新的那幾個內容了
step9 個人中心布局
-
前面step1到step8已經將首頁和基本菜單欄完成,現在先做個人中心頁也就是底部菜單中‘我’這個tab*
-
首頁點擊去看詳情和寫新動態加到列表中放到后面再完成
-
總體預覽
-
之前做過一個HeaderTab的組件,現在可以拿來用了
- 如果你遇到底部切換頁面可以正常顯示,但是直接刷新瀏覽器卻不起作用,那么你看看是不是沒有$init()
-
第二部分是圖片,sui中1rem等於20px
-
第三部分是個人信息,為了模擬以后從后台獲取數據,將這部分抽離出來定義一個UserDetail組件並將數據存儲在一個數據中,這個數組存放在me.vue中
-
子父之間的通信:雖然之前也有提到過,但是之前都是在父組件上傳遞一個字符串,當時的栗子是這樣的
-
然后在子組件的props中注冊status就可以直接用了,但是今天要傳遞的是一個數組data 如果直接寫成data="userData" vue是不能識別你這個是字符串還是變量數組的
-
所以這里要用v-bind:user-data='userData',這里還有一個坑,如果你寫的是v-bind:userData='userData'就會報錯了。
-
組件中的樣式我定義的比較隨便,直接用標簽來定義樣式,因為這里加了scope只作用在當前組件,vue會自動處理加一串標記數字,這樣再也不用為命名擔心了
-
第三部分的tab與第二部分類似,也是抽離一個組件,這里定義數組將內容與結構分離,用v-for循環輸出結構,如果要改內容直接改數組,不用在一大堆的html結構里面去找文字了
//這里之所以數組套數組是因為考慮到sui的.row樣式結構需要 //path就是路由的路徑了,都要添加到router.js文件中 lists :[ [{ title:'動態', icon:'icon-app', path:'/me/moment' }, { title:'訪客', icon:'icon-friends', path:'/me/friends' }], [{ title:'文章', icon:'icon-menu', path:'/me/articles' }, { title:'最佳實現', icon:'icon-browser', path:'/me/practice' }], [{ title:'閱讀', icon:'icon-code', path:'/me/read' }, { title:'收藏列表', icon:'icon-star', path:'/me/love' }] ]
<user-refer v-bind:lists='lists'> </user-refer> <div v-for="list in lists" class="row"> <div class="col-50"> <a class="tab-item" v-link="{ path: list[0].path}"> <!-- 這里不同於首頁的tab標簽切換,所以不需要replace這個參數,讓路由可以后退 --> <span class="icon" v-bind:class="list[0].icon"></span><br> <span class="tab-label">{{list[0].title}}</span> </a> </div> <div class="col-50"> <a class="tab-item" v-link="{ path: list[1].path}"> <span class="icon" v-bind:class="list[1].icon"></span><br> <span class="tab-label">{{list[1].title}}</span> </a> </div> </div> </div>
-
第三部分的tab鍵點擊進去的詳情還是用路由實現,下面這一部分可以用組件封裝,路由不用加replace=true參數,返回按鈕加上個人中心的路由,詳細內容無非就是下拉列表,大致跟首頁一樣,可以看step1-step8
step10 利用vue-resource 獲取數據
-
將首頁的數據變為動態獲取的
-
首先定義一個json文件,注意格式,否則解析不了會為null,建議可以把自己的json文件在線檢測一下
-
news.json
-
-
這個文件放在static/data,要在app.js中做相應的配置
import VueResource from 'vue-resource'
-
參數解釋:
-
proces.env.NODE_ENV 在vue-cli搭建的時候bulid文件里面配置好的可以去研究一下
-
Vue.http.options.emulateJSON = true;
If your web server can't handle requests encoded as application/json , you can enable the emulateJSON option. This will send the request as application/x-www-form-urlencoded MIME type, as if from an normal HTML form.--- 給你個眼神自己領悟吧,相信我的翻譯還不如自己百度,哈哈
-
然后使用$http去獲取數據,返回值response,利用$set設置 Vue 實例的屬性值,也就是之前的假數據task1
-
task1數據改變就會引起視圖的變化,現在效果是這樣的,那么改變一下以前下拉push進去的假數據
-
先每次刷新都能獲取一次數據了,基本模擬了后端傳輸數據的效果
點擊進入詳情頁面
-
希望每個列表都能點擊去看文章詳情,那么要監聽一下click事件,在子組件中,找到CardCon.vue
<div class="card" v-on:click="goDetail"> <slot></slot> </div>
-
定義goDetail方法,並且使用dispatch冒泡的父組件,傳入當前列表的索引,方便獲取數據
methods:{
goDetail (){
this.$dispatch('GoDetailRouter', $(this.$el).index()) } }
-
$emit $dispatch $brocast
-
在父組件中的events中監聽子組件的事件,msg就是傳入的參數,利用路由go方法跳轉到詳情頁
events :{//監聽子組件 GoDetailRouter (msg){ router.go({ name: 'detail', params: { Lcontent:encodeURIComponent(this.$data.task1[msg].Lcontent) } }); } }
router.js
'/home/detail/:Lcontent': { name:'detail', component (resolve) { require(['./views/detail'], resolve) } }
-
這里的Lcontent是傳入的參數,我這里傳入的是詳情的文字,實際開發一般是傳一個id然后從后台獲取相應的數據
-
增加detail.vue,要了解router鈎子函數才能了解下面這一段代碼
route: {
data: function (transition) { this.$set('Lcontent',decodeURIComponent(this.$route.params.Lcontent)); } }
切換鈎子子函數
-
由於如果我在ready中去獲取參數,因為keep-alive將組件緩存所以只會執行一次,但是router的data切換鈎子會在每次路由切換的時候調用,保證了當前的參數是最新的