由于项目中用到了豆瓣api,涉及到跨域访问,就需要在config的index.js添加代理,例如
proxyTable: { // 设置代理,解决跨域问题 '/api': { target: 'https://api.douban.com/v2', changeOrigin: true, pathRewrite: { '^/api': '' } } },
安装axios
npm install --save axios vue-axios
在main.js引入axios
import axios from 'axios' import VueAxios from 'vue-axios' Vue.use(VueAxios, axios)
在各个组件里获取数据,就是这么简单
this.axios.get(api).then((response) => { console.log(response.data) })
由于豆瓣app首页的数据,我们不能直接通过api获取数据,只能先将数据保存下来进行访问,大家做项目的时候,访问静态数据json会遇到路径404,这里我们可以通过在dev-server.js里添加静态数据的路由,例如:我们先将豆瓣的首页数据先保存到/src/data/homeData.json,
dev-server.js 添加
/**************** 静态数据 start ****************/ var app = express() var homeData = require('../src/data/homeData.json') var apiRoutes = express.Router() apiRoutes.get('/homeData',function(req,res){ res.json({ errno: 0, data: homeData }) }) app.use('/api',apiRoutes) /**************** 静态数据 end *****************/
在组件里面访问
this.axios.get('/api/homeData').then((response) => { console.log(response.data) })
接下来我们将静态数据显示到首页中:静态数据分为热门和推荐,我么先fetchData获取数据,通过判断card.name,赋值给recommendData、hotData
export default { name: 'index', components: { mHeader, mSwipe, mCell, mCellMedia }, data() { return { recommendData: [], hotData: [] } }, created() { this.fetchData(); }, methods: { fetchData() { this.axios.get('/api/homeData').then((response) => { let data = response.data.data.recommend_feeds; let recommend = []; let hot = []; for (var i in data) { if (data[i].card && data[i].card.name == '为你推荐') { recommend.push(data[i]); } else { hot.push(data[i]); } } this.recommendData = recommend; this.hotData = hot; }) } } }
在Index.vue循环media-cell组件
<m-cell-media :author="item.target.author.name" :column="item.source_cn" :img="item.target.cover_url" v-for="(item,index) in hotData" :key="item.id"> <span slot="title">{{item.title}}</span> <span slot="describe">{{item.target.desc}}</span> </m-cell-media>
Index.vue
<template> <div> <!-- 头部 --> <header class="m-header is-fixed is-bg top-search"> <div class="search-wrap"> <img src="../../assets/images/ic_search_gray.png" alt=""> <span class="placeholder">影视 图书 唱片 小组 舞台剧等</span> <img src="../../assets/images/ic_scan_gray.png" alt=""> </div> <!-- <div class="m-header-button is-right" style="text-align: center; 50px;"> <a href="javascript:;"><img class="m-icon-img" src="../../assets/images/ic_chat_white.png" /></a> </div> --> </header> <!-- 主体 --> <div class="page-content"> <!-- 轮播图 --> <m-swipe swipeid="swipe01" :autoplay="1000" paginationDirection="right"> <div class="swiper-slide " slot="swiper-con"><img src="../../assets/images/banner/01.jpg" alt=""></div> <div class="swiper-slide " slot="swiper-con"><img src="../../assets/images/banner/02.jpg" alt=""></div> <div class="swiper-slide " slot="swiper-con"><img src="../../assets/images/banner/03.jpg" alt=""></div> </m-swipe> <!-- cell --> <m-cell title="提醒" icon> <img src="../../assets/images/ic_mine_notification.png" slot="icon"> <a href="javascript:;" slot="cell-right"><img src="../../assets/images/ic_arrow_gray_small.png" alt=""></a> </m-cell> <m-cell title="设置"> <a href="javascript:;" slot="cell-right"><img src="../../assets/images/ic_arrow_gray_small.png" alt=""></a> </m-cell> <!--热门--> <div class="hot-wrap"> <m-cell title="热门" label="hot"> <!--<a href="javascript:;" slot="cell-right">更多<img src="../../assets/images/ic_arrow_gray_small.png" alt=""></a>--> </m-cell> <m-cell-media :author="item.target.author.name" :column="item.source_cn" :img="item.target.cover_url" v-for="(item,index) in hotData" :key="item.id"> <span slot="title">{{item.title}}</span> <span slot="describe">{{item.target.desc}}</span> </m-cell-media> </div> <!--推荐--> <!-- <div class="recommend-wrap"> <m-cell title="推荐" label="recommend"></m-cell> <m-cell-media :author="item.target.author.name" :column="item.source_cn" :bg="item.target.cover_url" v-for="(item,index) in recommendData" :key="item.id"> <span slot="title">{{item.title}}</span> <span slot="describe">{{item.target.desc}}</span> </m-cell-media> </div> --> </div> </div> </template> <script> import mHeader from '../../components/header' import mSwipe from '../../components/swipe' import mCell from '../../components/cell' import mCellMedia from '../../components/cell-media' export default { name: 'index', components: { mHeader, mSwipe, mCell, mCellMedia }, data() { return { recommendData: [], hotData: [] } }, created() { this.fetchData(); }, methods: { fetchData() { this.axios.get('/api/homeData').then((response) => { let data = response.data.data.recommend_feeds; let recommend = []; let hot = []; for (var i in data) { if (data[i].card && data[i].card.name == '为你推荐') { recommend.push(data[i]); } else { hot.push(data[i]); } } this.recommendData = recommend; this.hotData = hot; }) } } } </script> <style lang="less"> // 头部 header.m-header { padding: 0 0 0 10px; } .is-fixed~.page-content { padding-top: 44px; padding-bottom: 50px; } .top-search { .search-wrap { // 100%; height: 30px; background: #fff; border-radius: 4px; display: flex; align-items: center; justify-content: space-between; color: #c0c0c0; padding: 0 12px; .placeholder { flex: 1; text-align: left; padding-left: 12px; } img { 20px; height: 20px; } } } // 热门 .hot-wrap, .recommend-wrap { padding-top: 12px; } </style>
header.vue
<template> <header class="m-header" :class="{'is-bg':bg,'is-fixed':fixed}"> <div class="m-header-button is-left" v-show="leftShow"> <slot name="left"></slot> </div> <h1 class="m-header-title" v-text="title"></h1> <div class="m-header-button is-right"> <slot name="right"></slot> </div> </header> </template> <script> /** * @param title - header显示的标题 * @param bg - header是否显示的标题背景 * @param {slot} [left] - 左侧的图片内容和文字 * @param {slot} [right] - 右侧的图片内容和文字 * @exmaple * <m-header title="豆瓣app" :bg="true"> * <a href="javascript:;" slot="left"><img class="m-icon-img" src="../../assets/images/ic_bar_back_white.png"/>返回</a> * <a href="javascript:;" slot="right">分享</a> * </m-header> */ export default { props: { title: { type: String, default: '' }, bg: { type: Boolean, default: false }, fixed: { type: Boolean, default: false }, leftShow: { type: Boolean, default: true } } } </script> <style lang="less"> /*导入颜色变量*/ @import "../assets/less/var.less"; .m-header { display: flex; align-items: center; height: 44px; padding: 0 10px; background: #fff; color: @headerDefaultColor; position: relative; &:after { content: ''; position: absolute; left: 0; bottom: 0; right: 0; height: 1px; background: @headerBorderColor; transform: scaleY(0.5); } a { color: @headerDefaultColor; } .m-header-button { 70px; align-items: stretch; &.is-left { text-align: left; } &.is-right { text-align: right; } .m-icon-img { 20px; height: 20px; } .margin-right-10 { margin-right: 10px; } } .m-header-title { flex: 1; text-align: center; font-size: 16px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } &.is-bg { background: @headerBg; color: #fff; a { color: #fff; } .m-header-title { color: #fff; } &:after { content: ''; position: absolute; left: 0; bottom: 0; right: 0; height: 0px; background: @headerBorderColor; transform: scaleY(0.5); } } &.is-fixed { position: fixed; left: 0; right: 0; top: 0; z-index: 9; } } </style>
swipe.vue
<template> <div class="swiper-container" :class="swipeid"> <div class="swiper-wrapper"> <slot name="swiper-con"></slot> </div> <!-- 分页器 --> <div :class="{'swiper-pagination':pagination}" :style="{'text-align':paginationDirection}"></div> </div> </template> <script> import '../assets/lib/swiper/js/swiper.js' export default { props: { swipeid: { type: String, default: '' }, effect: { type: String, default: 'slide' }, loop: { type: Boolean, default: true }, direction: { type: String, default: 'horizontal' }, autoplay: { type: Number, default: 5000, }, paginationType: { type: String, default: 'bullets' }, pagination: { type: Boolean, default: true }, paginationDirection:{ type:String, default:'center' } }, mounted() { var That = this; new Swiper('.'+That.swipeid, { //循环 loop: That.loop, //分页器 pagination: '.swiper-pagination', //分页类型 paginationType: That.paginationType, //fraction,progress,bullets //自动播放 autoplay: That.autoplay, //方向 direction: That.direction, //特效 effect: That.effect, //slide,fade,coverflow,cube //用户操作swiper之后,是否禁止autoplay autoplayDisableOnInteraction : false, }) } } </script> <style> @import '../assets/lib/swiper/css/swiper.css'; .swiper-container img{ 100% } .swiper-pagination-bullet-active { background: #fff; } </style>
cell.vue
<template> <div class="m-cell normal" :class="label"> <div class="m-cell-title"> <slot name="icon"></slot> {{title}} </div> <div class="m-cell-right"> <slot name="cell-right"></slot> </div> </div> </template> <script> export default { props: { title: { type: String, default: '' }, hot: { type: Boolean, default: false }, recommend: { type: Boolean, default: false }, icon: { type: Boolean, default: false }, label: { type: String, default: 'normal' } } } </script> <style lang="less"> .m-cell { position: relative; padding: 10px 5px 10px 15px; display: flex; justify-content: space-between; align-items: center; &:before { content: ''; position: absolute; left: 0; bottom: 0; right: 0; height: 1px; background: #eee; transform: scaleY(0.5); } .m-cell-title { font-size: 15px; img { 20px; height: 20px; } } .m-cell-right { font-size: 12px; a { color: #666; } img { 20px; height: 20px; } } &.normal {} &.hot { padding: 0px 5px 0px 15px; height: 22px; &:after { content: ''; position: absolute; 5px; left: 0; top: 0px; bottom: 0px; background: #ff8447; } &:before { height: 0 } } &.recommend { padding: 0px 5px 0px 15px; height: 22px; &:after { content: ''; position: absolute; 5px; left: 0; top: 0px; bottom: 0px; background: #42bd56; } &:before { height: 0 } } } </style>
cell-media.vue
<template> <div class="m-cell-media-wrap"> <a href="javascript:;"> <div class="m-cell-media-top"> <div class="m-cell-media"> <div class="m-cell-title m-ellipsis-2"> <slot name="title"></slot> </div> <div class="m-cell-detail m-ellipsis-2"> <slot name='describe'></slot> </div> </div> <div class="m-pull-right right-img" :style="{'background-image':'url('+img+')'}"> </div> </div> <div class="m-cell-media-bottom"> <p v-if="author">作者:{{author}}</p> <p v-if="column">{{column}}</p> </div> </a> </div> </template> <script> export default { props: ['author', 'column', 'img'] } </script> <style lang="less"> .m-cell-media-wrap { display: flex; flex-direction: column; padding: 18px 20px; position: relative; &:after { content: ''; position: absolute; left: 0; bottom: 0; right: 0; height: 1px; background: #eee; transform: scaleY(0.5); } .m-cell-media-top { display: flex; flex-direction: row; .m-cell-media { flex: 1; padding-right: 45px; } .m-cell-title { font-size: 17px; line-height: 22px; color: #333; font-weight: bold; } .m-cell-detail { font-size: 12px; padding-top: 12px; color: #939393; } .m-pull-right { 94px; height: 94px; overflow: hidden; background-position: center center; background-size: cover; img { 100%; } } } .m-cell-media-bottom { display: flex; justify-content: space-between; padding-top: 20px; margin-top: 12px; color: #bfbfbf; position: relative; &:before { content: ''; position: absolute; top: 0; left: 0; 20px; height: 1px; background: #eee; } } } </style>
var.less
//APP默认颜色 @defaultColor:#42bd56; //header @headerBg:@defaultColor; @headerDefaultColor:rgb(73,73,73); @headerBorderColor:#e4e4e4; //tabbar @tabbarBorderColor:#e4e4e4; @tabbarActiveColor: @defaultColor;
添加 reset.css
效果图