官方的解釋是:A simple CLI for scaffolding Vue.js projects,
簡單翻譯一下,就是: 用簡單的命令行來生成vue.js項目腳手架。javascript
<!-- 全局安裝vue-cli --> npm install -g vue-cli
vue-cli預先定義了5個模板,根據你使用的打包工具的不一樣選擇不一樣的模板,一般咱們用的都是第一個webpack模板。每一個模板都預先寫好了不少依賴和基礎配置,能夠直接在此基礎上進行開發,很是方便。css
webpackhtml
webpack-simplevue
browserifyjava
browserify-simplewebpack
simpleios
安裝vue-cli後,就能夠下載咱們要的模板了。git
用法:vue init template-name project-name
這時會有不少提示,詢問你要安裝vue2仍是vue1,是否要安裝mocha,eslint等東西,根據你本身的須要安裝便可。安裝好後,會提示你怎麼開始,根據提示輸入命令就能夠啓動了。github
爲了適配各類屏幕,首先把淘寶的flexible引進來,在main.js裏面web
import './base/js/base.js'
其次,把樣式重置也引入進來, App.vue的style標籤裏面
@import './base/css/normalize.scss';
接着,引入字體圖標, 在App.vue的style標籤裏面
@import url('//at.alicdn.com/t/font_nfzwlroyg2vuz0k9.css')
爲了達到上圖的效果,咱們須要2個基本的組件,一個是購物車,一個是home頁面。購物車比較簡單,就一個頁面,主要看Home頁面。
home組件又分紅4個組件,一個是底部的導航,還有三個是上面的首頁,搜索和我的中心。
也便是說爲了達到圖片上的效果,目前咱們須要總共6個組件。
分別是:
1. 購物車
2. home 2.1 首頁 2.2 搜索 2.3 我的中心 2.4 底部導航
所以,新建6個.vue文件。爲了儘快把路由編寫出來,我喜歡隨便填充一點內容(主要是爲了知道在哪一個頁面),好比:
<div>首頁首頁首頁</div> <div>首頁首頁首頁</div> <div>首頁首頁首頁</div> <div>首頁首頁首頁</div> <div>首頁首頁首頁</div>
使用路由首先要引入Vue-router並use,並將配置好路由的vue-router實例掛載到new出來的Vue實例上,不過vue-cli已將幫咱們配置好了,只須要在其基礎上繼續開發就好了。
找到編寫路由的index.js文件:
首先引入6個組件:
import xxx from 'xxx/xxx' import car from '@/components/car'
你可能常常看到@這樣的東西,這實際上是webpack配置的別名。打開build文件夾下面的webpack.base.conf.js。
你也能夠本身再加別名,好比
alias: { ~': resolve('src/component') }
當webpack在import或者require語句中遇到~時,就會將其解析爲對應的路徑。使用別名可使得路徑更爲清晰,也能夠減小一些重複的代碼。
對比一下:
import car from '../../component/car.vue' import car from '~/car.vue'
不過,使用別名的壞處就是,編輯器無法智能的提示文件所在路徑了。
當頁面多了之後,打包後的文件會變得很大,大於1M也是很正常的。所以,首屏打開也會變慢,畢竟一會兒要加載以M爲單位的js文件。想要減小文件的大小,能夠把Vue等公共庫提取到vendor,從而利用瀏覽器的緩存效果。同時,也可讓路由按需加載,當須要用到的時候,纔去加載對應的組件,利用webpakc的異步加載能夠解決:
const Car = r => require.ensure([], () => r(require('@/components/car')), 'car')
也能夠像下面這麼寫:
const Car = resolve => require(['@/components/car'], resolve)
Vue2.3+的版本提供了更高級的異步組件寫法,想了解的能夠去官網看一下,這裏用的仍是舊的用法。
對着上面的結構圖,路由的結構其實大概已經瞭解了
{
path:'', redirect:"/home" }, { path:'/home', component:Main, children:[ { path:'', redirect:"index" }, { path:'index', component:Index }, { path:'search', component:Search }, { path:'vip', component:Vip } ] }, { path:'/car', component:Car, }
這裏咱們用了2個重定向,當路由爲空時,會重定向到/home
,而當home爲空時,又會重定向到index
,因此你只須要在瀏覽器輸入http://localhost:8088 ,就會自動跳轉到home下的首頁
能夠發現home組件由上下2部分組成,底部是固定的導航,上面的部分是動態切換的頁面。所以home組件的template寫出來應該是這樣的:
<template>
<div> <router-view></router-view> <foot-nav></foot-nav> </div> </template> <script> import footNav from '../components/foot-nav.vue' export default { components:{ footNav } } </script>
foot
導航組件相對來講也比較簡單,無非就是一個固定在底部的列表,每一個列表都寫好了對應的路由,點擊每個就會切換對應的頁面。若是路由層級比較深,寫起來可能會很長,如to="test1/test2/test3" ,考慮在配置路由的js中,給每一個路由添加name。這樣,在router-link中就只須要傳遞對應的name就能夠了。
<template> <div class="foot-nav-containner"> <ul class="bottom-nav"> <router-link tag="li" :to='{name:"index"}' class="bottom-nav__li iconfont icon-shouye bottom-nav__li--home"></router-link> <router-link tag="li" :to='{name:"search"}' class="bottom-nav__li iconfont icon-ss bottom-nav__li--search"></router-link> <router-link tag="li" :to='{name:"car"}' class="bottom-nav__li iconfont icon-shoppingcart bottom-nav__li--car"></router-link> <router-link tag="li" :to='{name:"vip"}' class="bottom-nav__li iconfont icon-gerenzhongxinxia bottom-nav__li--vip"></router-link> </ul> </div> </template>
index組件
index組件由輪播圖以及三個排行榜組成。3個排行榜除了數據和名字不一樣個之外,其餘的都同樣。因此,咱們總共須要2個組件就能夠。大體以下:
<template> <div id="container"> <輪播圖></輪播圖> <排行榜 :類型=1></排行榜> <排行榜 :類型=2></排行榜> <排行榜 :類型=3></排行榜> </div> </template>
輪播圖咱們用的是vue-awesome-swiper插件,使用方式同swiper基本一致,更多信息請github搜索。
在main.js中引入插件並使用:
import VueAwesomeSwiper from 'vue-awesome-swiper' Vue.use(VueAwesomeSwiper);
因爲可能不止一個頁面會用到輪播圖,因此咱們能夠把輪播圖提取出來。
新建一個swiper.vue文件
<template> <swiper class="swiper-box"> <swiper-slide class="swiper-item"></swiper-slide> <swiper-slide class="swiper-item"></swiper-slide> <swiper-slide class="swiper-item"></swiper-slide> <swiper-slide class="swiper-item"></swiper-slide> <div class="swiper-pagination" slot="pagination"></div> </swiper> </template>
<script>
export default { data(){ return{ swiperOption: { pagination: '.swiper-pagination', direction: 'horizontal', } } }, }; </script>
<style lang="scss" scoped> @import '../base/css/base.scss'; .swiper-box { width: 100%; height: 100%; margin: 0 auto; .swiper-item { height: 5rem; background: url() no-repeat center/cover; /* 使用Mixin來處理2x,3x圖 */ &:nth-of-type(1){ @include dpr-img("../assets/","vue"); } &:nth-of-type(2){ @include dpr-img("../assets/","swiper1"); } &:nth-of-type(3){ @include dpr-img("../assets/","swiper2"); } &:nth-of-type(4){ @include dpr-img("../assets/","swiper3"); } } } </style>
樣式方面就忽略了,要做爲一個組件,上面的寫法還存在問題,主要體如今:
問題1:輪播圖的配置參數寫在組件data裏面。
假若有2個頁面須要用到這個組件,1個組件須要自動輪播,一個組價不須要自動輪播,這樣的話,你可能會考慮對某個頁面作單獨處理,好比作一個if判斷之類的。可是,假若有不少頁面須要輪播圖,並且不一樣的地方不少,好比你想對a頁面輪播圖滑動到下一張後alert(1),對b頁面alert(2)等等等等,那該如何作呢?總不能一個一個判斷吧,因此正確的方法應該是把配置參數經過prop接受父組件傳遞過來的參數
<script>
export default { data(){ return{ } }, props:{ swiperOption:{ type:Object } } }; </script>
在父組件裏面import組件並傳遞參數
<template>
<div id="container"> <swiperComponent :swiperOption="swiperOption"></swiperComponent> </div> </template> <script> import swiperComponent from './swiper.vue' export default { data() { return { swiperOption: { pagination: '.swiper-pagination', direction: 'horizontal', }, } }, components:{ swiperComponent, } } </script>
如此一來,當哪一個頁面須要用到輪播圖,就在哪一個頁面寫好參數,並經過v-bind傳遞須要的參數。
問題2:輪播圖數量固定。
不可能每一個頁面都是4個輪播圖,而應該某個參數(一個數組)的長度來決定。父組件在經過ajax請求後得到該數組,並經過prop傳遞給swiper組件。
<template>
<swiper class="swiper-box"> <swiper-slide class="swiper-item" v-for="(v,i) in swiperList "></swiper-slide> <div class="swiper-pagination" slot="pagination"></div> </swiper> </template> props:{ swiperList:{ type:Array, default:[] } }
假如你是用的img標籤,則 :src="v.img"; 假如你是用background,則 :style="{backgroundImage:v.img}"
這樣,咱們的swiper組件基本已經解耦了。
新建一個電影排行榜組film.vue文件
排行榜組件結構以下:
(樣式基本人人會寫,再也不多說)
<template> <div class="film"> <h3 class="film__type"> <span>{{type}}</span> <router-link :to='{path:"/classify/"+url}'><span class="more"><em>更多</em><em class="iconfont icon-more"></em></span></router-link> </h3> <div class="film__list" :ref="el" :data-request="url"> <ul class="clearfix"> <router-link tag="li" v-for="(v,i) in array" :key="v.id" :to='{path:"/film-detail/"+v.id}'> <div class="film__list__img"><img v-lazy="v.images.small" alt=""></div> <div class="film__list__detail"> <h4 class="film__list__title">{{v.title}}</h4> <p class="film__list__rank">評分:{{v.rating.average}}</p> <p class="film__list__rank"> <span :class="{rankColor:v.rating.average>((i-0.5)*2)}" class="iconfont icon-rank" v-for="i in 5"></span> </p> </div> </router-link> </ul> <Loading v-show="!array[0]" class="loading-center"></Loading> </div> </div> </template>
爲了獲取真實的數據,咱們須要:
不少頁面都會用到豆瓣的api地址,因此能夠把相同的部分提取到一個文件
找一個地方,新建文件api.js
const api="https://api.douban.com/v2/movie/"; export default api;
選一個你熟悉的ajax庫,這裏用的是axios
在main.js裏面引入axions庫並use:
import axios from 'axios' Vue.use(axios); Vue.prototype.$ajax=axios;
咱們把他掛載到vue的原型上,以即可以在全部地方經過this.$ajax上使用,不過你也能夠不這麼作,隨我的喜歡。
若是你就這樣發請求到豆瓣,是獲取不到數據到,會提示你跨域,這也是先後端分離項目常見的問題。解決的方法有2個
1個是經過webpack的dev-server配置proxy, 不過有一個問題,就是隻在開發階段可使用,也便是當你開發完成後,npm run build生成打包後的文件,想再去服務器看效果就不行了。
2 是我如今用的,經過設置chrome來跨域,具體設置方法請參考
這篇文章。設置完後,之後因此的項目均可以使用,無需對每一個項目再單獨配置proxy代理了。
搞定前提條件後,接下來的無非是在create或者mounted生命週期發送一個請求,請求成功後把數據賦值給v-for綁定的data了。不過還有問題,就是滾動條的問題。假如你不作任何處理,那麼當獲取數據成功後,會渲染20個li(根據後臺返回的長度)。很明顯,ul的長度確定超過了整個屏幕的寬度,因此X軸會一直拉長,底下會出現滾動條,你能夠拖動到屏幕以外。可是,咱們但願的是屏幕的寬度保持不變,列表超過屏幕時隱藏元素,由咱們手動去滑動。
那麼ul的長度爲多少了?假如你隨便寫一個很長的長度,那麼滑動到後面就全是空白了。若是過短了,就滑動不了。所以ul長度由後臺返回的數量*(li的寬帶+li的padding-right,這裏的加法取決於你的LI的css結構)決定。所以,獲取完數據後,在nextTick時須要給UL的寬度從新賦值。爲了方便獲取元素的樣式,能夠在util.js寫一個方法
export default function(el,style){ return parseInt(window.getComputedStyle(el, false)[style]) }
在methods裏寫一方法計算ul應有的寬度,el即ul元素,能夠經過綁定ref來獲取。
freshWidth(el){
var width=getStyle(el.children[0],"width"); var padding=getStyle(el.children[0],"padding-right"); el.style.width=el.children.length*(width+padding+2)+"px"; }
爲了可以拖動,有2種解決方式:
第一種比較簡單,就是給Ul的外層div1設置固定長寬overflow:auto,當ul超出時,div就會出現滾動條,這樣就以拖動了。不過會出現滾動條,很礙眼。所以,給div1外面再套一層div2,並設置div2的高度低於div1,好比最爲層的div2高度80px,div1高度100px,並設置div2的overflow:hidden。如此一來,就能夠隱藏滾動條,而且能夠拖動了。
第二種是使用better-scroll庫, 使用better-scroll須要2層的結構
<div id="containner"> <ul id="scroller"></ul> </div> container層爲初始化的元素,須要設置overflow:hidden;初始化後,第一個子元素ul就可滾動了。 引入better-scroll 1.在mounted生命週期階段new BScroll({})並傳參數初始化; 2.發送請求獲取數據, <!-- 初始化的元素以及請求的參數咱們也都經過prop接受父組件傳遞過來 --> 3.將後臺數據賦值給array數組 4.調用nextTick方法並在回調函數中從新計算ul的長度後,refresh scroller
<!-- 父組件 -->
<filmComponent :el="filmType.topFilmData.scroller" :url="filmType.topFilmData.url" :type="filmType.topFilmData.type"> </filmComponent> <script> export default { data() { return { filmType:{ topFilmData:{ scroller:"scroll-top250", url:"top250", type:"top250" } } } }, components:{ filmComponent } } </script>
<!-- 子組件 -->
<script> import BScroll from 'better-scroll' import getStyle from '../base/js/util.js' import Loading from './loading.vue' import api from "../base/js/api.js" export default { data () { return { scroller:null,<!-- 存放scroll元素 --> array:[],<!-- 存放後臺返回的數組 --> }; }, components:{ Loading }, props:["el","url","type"], mounted(){ const el = this.$refs[this.el]; this.scroller=this.initScroll(el); const {request}=el.dataset; this.$ajax.get(`${api}${request}?start=${Math.floor(Math.random()*10)}`) .then((res)=>{ this.array=res.data.subjects; this.$nextTick(()=>{ this.freshWidth(el.children[0]); this.scroller.refresh(); }) }) }, methods:{ initScroll(el){ return new BScroll(el,{ click:true, probeType:3, scrollX:true, scrollY:false }) }, freshWidth(el){ var width=getStyle(el.children[0],"width"); var padding=getStyle(el.children[0],"padding-right"); el.style.width=el.children.length*(width+padding+2)+"px"; }, } }; </script>
咱們總共有3個排行榜,那麼只須要在父組件再寫2個標籤並在data中寫好參數,傳給子組件就能夠了
<filmComponent :el="filmType.topFilmData.scroller" :url="filmType.topFilmData.url" :type="filmType.topFilmData.type"> </filmComponent> <filmComponent :el="filmType.topFilmData.scroller" :url="filmType.topFilmData.url" :type="filmType.topFilmData.type"> </filmComponent>
這樣基本就就完成了,再稍微優化下,咱們有3個榜單,假設每一個榜單都加載20個數據,那麼獲取完數據後就會有3*20總共有60張圖片的請求。經常使用的優化方式就是等圖片進入可視區以後再去加載圖片,在以前給一張loading或者其餘圖片做爲站位。
使用vue-lazyload
來達到這個效果,相關配置參數請自行github搜索vue-lazyload
在main.js裏面
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload, {
preLoad: 1.3,
loading: require('@/assets/head.jpg'),<!-- 站位圖 --> attempt: 1 })
使用的方法很是簡單:
本來咱們是這麼寫的
<div><img :src="v.images.small" alt=""></div>
改爲下面這樣就好了
<img v-lazy="v.images.small" alt=""></div>
寫到這裏,基本配置以及首頁就差很少完成了,相關代碼已長傳到 https://github.com/linrunzheng/vueApp