本文基于工作项目开发,做的整理笔记
前段时间公司有的小伙伴刚开始学习vue,就直接着手用在新项目上,以项目实战步步为营,不断推进vue的学习和使用。时间短,需求多,又是刚刚上手,遇到的坑和困难也真不少,感觉每天都在疯狂地解决问题。说真的,每种技术的学习和使用,在实际项目的开发上得到了充分检验,个人能力也在快速的成长。
前一段时间,写过文章“Vue教程--使用官方脚手架构建实例”,主要是针对PC端,架构而写。当初的目的,也是想做为一个入门的教程,但是根据反馈和自己后面的感受,发现并不是很好,并没有做到真正的一步步上手。
今天决定专门针对Wap端去做这样一个demo,整体架构的搭建,并含有一些通用的功能。其中,部分知识点请回看前面那篇文章。对比来看,此篇应该更为详细,步步为营。
前提条件:
你已经了解vue的基础知识,尝试过使用vue-cli官方脚手架搭建项目。
编码环境:
system:OS X EI Capitan 10.12.5
npm:5.4.2
node:v8.8.0
vue-cli:@lastest
相关技术栈:
vue2 + vuex + vue-router + webpack + ES6/7 + fetch/axios + sass + flex + svg
相关地址:
项目代码github地址:https://github.com/YuxinChou/vue-wap-demo
项目在线地址:http://www.knowing365.com
(可用手机扫描下文中二维码,或用chrome浏览器模拟手机访问)
参考项目:https://github.com/bailicangdu/vue2-elm

目录
| - 0.传送门
| - 1.安装
| - 2.项目说明
| - 3.项目搭建
| - Step1. 初始化
| - Step2. 母版页Layout
| - Step3. 配置rem
| - Step4. 配置sass
| - Step5. 顶部导航header
| - Step6. 引入iconfont
| - Step7. 侧边菜单sidebar
| - Step8. 底部导航footer
| - Step9. 返回顶部backToTop(组件)
| - Step10. 仓库存储store
| - Step11. 侧边菜单状态保存
| - Step12. 搜索栏searchBar(组件)
| - Step13. 页面添加
| - Step14. 弹窗提示(组件)
| - ---------------------------------- 下内容为详解2
| - Step15. 完善login页面(fetch请求数据)
| - Step16. 合理引入svg
| - Step17. 用axios实现请求(取代原生fetch)
| - Step18. 登录状态存入仓库
| - Step19. 滚动加载更多(组件)
| - Step20. 回到指定位置(组件)
| - Step21. 完善消息列表页面
| - Step22. 顶部菜单改造(slot的使用)
| - --------------------------------- 下内容为详解3
| - Step23. 完善其他页面
| - Step24. 权限检查
| - Step25. 页面切换动画transition
| - Step26. 轮播展示(swiper)
| - Step26. 分享功能(vue-social-share)
| - Step28. ...
| - 4.项目部署
| - a)本地部署
| - b)服务器部署
| - 5.后续
Step15. 完善login页面(fetch请求数据)
更新一下login
页面,代码如下:
/**********************************************/
/* src/page/login/login.vue */
/**********************************************/
登录
还没有账号?立即注册
忘记密码
代码中,我们引用了src/service/getData.js
文件,它是一个接口服务文件。我们现在创建,在src
文件夹下创建service
文件夹,及getData.js
文件,代码如下:
/**********************************************/
/* src/service/getData.js */
/**********************************************/
import fetch from '../utils/fetch';
/**
* 账号密码登录
*/
export const accountLogin = (email, password) => fetch('/loginController/login', { email, password }, 'POST');
接着我们创建../utils/fetch.js
文件,代码如下:
/**********************************************/
/* src/utils/fetch.js */
/**********************************************/
import { baseUrl } from '../../config/dev.env';
export default async(url = '', data = {}, type = 'GET', method = 'fetch') => {
type = type.toUpperCase();
url = baseUrl + url;
if (type == 'GET') {
let dataStr = ''; //数据拼接字符串
Object.keys(data).forEach(key => {
dataStr += key + '=' + data[key] + '&';
});
if (dataStr !== '') {
dataStr = dataStr.substr(0, dataStr.lastIndexOf('&'));
url = url + '?' + dataStr;
}
}
if (window.fetch && method == 'fetch') {
let requestConfig = {
method: type,
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
},
cache: "force-cache"
}
if (type == 'POST') {
var params = "";
// 'Content-Type': 'application/json; charset=utf-8'
// Json串 JSON.stringify(data)
// params = JSON.stringify(data);
// 'Content-Type': 'multipart/form-data'
// formData拼接
// var formData = new FormData();
// for (var name in data) {
// formData.append(name, data[name]);
// }
// 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
// 字符串拼接 "email=aaa&password=xxxxx"
Object.keys(data).forEach(key => {
params += key + '=' + data[key] + '&';
});
if (params !== '') {
params = params.substr(0, params.lastIndexOf('&'));
}
Object.defineProperty(requestConfig, 'body', {
value: params
});
}
try {
const response = await fetch(url, requestConfig);
const responseJson = await response.json();
return responseJson
} catch (error) {
throw new Error(error)
}
} else {
// 非fetch
return new Promise((resolve, reject) => {
let requestObj;
if (window.XMLHttpRequest) {
requestObj = new XMLHttpRequest();
} else {
requestObj = new ActiveXObject;
}
let sendData = '';
if (type == 'POST') {
// Json串
// sendData = JSON.stringify(data);
// 字符串拼接
Object.keys(data).forEach(key => {
sendData += key + '=' + data[key] + '&';
});
if (sendData !== '') {
sendData = sendData.substr(0, sendData.lastIndexOf('&'));
}
}
requestObj.open(type, url, true);
requestObj.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
requestObj.send(sendData);
requestObj.onreadystatechange = () => {
if (requestObj.readyState == 4) {
if (requestObj.status == 200) {
let obj = requestObj.response
if (typeof obj !== 'object') {
obj = JSON.parse(obj);
}
resolve(obj)
} else {
reject(requestObj)
}
}
}
})
}
}
在config/dev.env.js
文件中添加一个项目配置变量baseUrl
,代码如下:
/**********************************************/
/* config/dev.env.js */
/**********************************************/
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
baseUrl: 'http://52.80.21.233:8040'
})
那么这个请求我们就做好了,试一试请求是否成功。
注意:这是一个这个项目demo中唯一的真实数据请求接口,其他类似请求如果要使用真实接口可参考这里。
Step16. 合理引入svg
在login
页面的代码里,我们放置了一大段svg绘制代码,不是很好,如果其他页面要用,可能还拿不到。这里,我们更好的是将整站的svg抽离成一个组件。
在src/components/common
中添加svg.vue
,代码如下:
/**********************************************/
/* src/components/common/svg.vue */
/**********************************************/
修改App.vue
,将它引入,代码如下:
/**********************************************/
/* src/App.vue */
/**********************************************/
接着,去掉login
页面那一段绘制代码,也能看到效果的。
Step17. 用axios实现请求(取代原生fetch)
有人可能说原生fetch
对浏览器的支持情况不太好,可否换一个呢?那么我也可以用axios
去实现请求。
在src/utils
中添加axios.js
文件,代码如下:
/****************************************** */
/* src/utils/axios.js */
/****************************************** */
import axios from 'axios';
import store from '../store';
import { baseUrl } from '../../config/dev.env';
export default (url = '', data = {}, type = 'GET', method = 'fetch') => {
return new Promise((resolve, reject) => {
const instance = axios.create({
baseURL: baseUrl,
timeout: 20000 //,
//headers: { 'authToken': store.getters.token } //如果后台请求都需要携带的话
});
instance({
url: url,
method: type,
params: data
})
.then(response => {
const res = response.data;
// 50001:token已过期
// 50002:token非法
if (res.retCode === "50001" || res.retCode === "50002") {
router.push({ path: '/login' })
reject(res);
}
resolve(res);
})
.catch(error => {
router.push({ path: '/404', query: { error: error } });
reject(error);
});
});
}
当然,要使用这种方式的话,我们还要安装axios
模块,执行:
$ npm install axios --save
修改src/service/getData.js
文件,使用axios
进行请求,代码如下:
/****************************************** */
/* src/service/getData.js */
/****************************************** */
// import fetch from '../utils/fetch';
import fetch from '../utils/axios';
/**
* 账号密码登录
*/
export const accountLogin = (email, password) => fetch('/loginController/login', { email, password }, 'POST');
Step18. 登录状态存入仓库
修改一下login
页面的代码,代码如下
/****************************************** */
/* src/page/login/login.vue */
/****************************************** */
// 其他地方不改
...
// 缓存用户数据
let email = response.data.adminInfo.email;
let token = response.data.tokenModel.token;
this.$store.commit('SET_AUTH_INFO',[
{
email: email,
token: token
}
]);
...
需要在store的相关文件,添加SET_AUTH_INFO
事件,修改如下:
/****************************************** */
/* src/store/modules/user.js */
/****************************************** */
import Cookies from 'js-cookie';
const app = {
state: {
email: '',
token: localStorage.getItem('token')
},
mutations: {
SET_AUTH_INFO: (state, info) => {
state.email = info.email;
state.token = info.token;
localStorage.token = info.token;
},
},
actions: {
}
};
export default app;
/****************************************** */
/* src/store/getters.js */
/****************************************** */
const getters = {
token: state => state.user.token,
email: state => state.user.email,
sidebar: state => state.app.sidebar,
scroll: state => state.app.scroll,
messages: state => state.common.messages,
contacts: state => state.common.contacts,
};
export default getters
/****************************************** */
/* src/store/index.js */
/****************************************** */
import Vue from 'vue';
import Vuex from 'vuex';
import user from './modules/user';
import app from './modules/app';
import getters from './getters';
Vue.use(Vuex);
const store = new Vuex.Store({
modules: {
user,
app
},
getters
});
export default store
测试一下,查看是否正确。
Step19. 滚动加载更多(组件)
因为messages
页面需要用到“滚动加载更多”这个功能,我们先看一下。
在src/components/common
中添加mixin.js
,里面的loadMore
代码如下:
/****************************************** */
/* src/components/common/mixin.js */
/****************************************** */
import { getStyle } from '../../utils/utils'
export const loadMore = {
directives: {
'load-more': {
bind: (el, binding) => {
let windowHeight = window.screen.height;
let height;
let setTop;
let paddingBottom;
let marginBottom;
let requestFram;
let oldScrollTop;
let scrollEl;
let heightEl;
let scrollType = el.attributes.type && el.attributes.type.value;
let scrollReduce = 2;
if (scrollType == 2) {
scrollEl = el;
heightEl = el.children[0];
} else {
scrollEl = document.body;
heightEl = el;
}
el.addEventListener('touchstart', () => {
height = heightEl.clientHeight;
if (scrollType == 2) {
height = height
}
setTop = el.offsetTop;
paddingBottom = getStyle(el, 'paddingBottom');
marginBottom = getStyle(el, 'marginBottom');
}, false)
el.addEventListener('touchmove', () => {
loadMore();
}, false)
el.addEventListener('touchend', () => {
oldScrollTop = scrollEl.scrollTop;
moveEnd();
}, false)
const moveEnd = () => {
requestFram = requestAnimationFrame(() => {
if (scrollEl.scrollTop != oldScrollTop) {
oldScrollTop = scrollEl.scrollTop;
moveEnd()
} else {
cancelAnimationFrame(requestFram);
height = heightEl.clientHeight;
loadMore();
}
})
}
const loadMore = () => {
if (scrollEl.scrollTop + windowHeight >= height + setTop + paddingBottom + marginBottom - scrollReduce) {
binding.value();
}
}
}
}
}
};
它引入了src/utils/utils.js
文件的getStyle
方法,我们现在添加一下,代码如下:
/****************************************** */
/* src/utils/utils.js */
/****************************************** */
/**
* 获取style样式
*/
export const getStyle = (element, attr, NumberMode = 'int') => {
let target;
// scrollTop 获取方式不同,没有它不属于style,而且只有document.body才能用
if (attr === 'scrollTop') {
target = element.scrollTop;
} else if (element.currentStyle) {
target = element.currentStyle[attr];
} else {
target = document.defaultView.getComputedStyle(element, null)[attr];
}
//在获取 opactiy 时需要获取小数 parseFloat
return NumberMode == 'float' ? parseFloat(target) : parseInt(target);
}
/**
* 滚动到指定位置
*/
export const scrollPosition = scroll => {
let top = scroll[location.pathname];
if (top != undefined) {
setTimeout(function() {
document.documentElement.scrollTop = top;
document.body.scrollTop = top;
}, 100);
}
}
其中,scrollPosition
方法为下面“回到指定位置”功能所需,我们先加着。
“滚动加载更多”的具体使用,稍后见“完善消息列表页面”部分内容。
Step20. 回到指定位置(组件)
我们先思考一下这个功能的核心问题,第1个就是页面离开前要保存滚动位置,第2个就是回到页面滚到指定位置。
针对第1个问题,我们可以监听滚动时间,实时保存这个值。这种较为容易实现。针对第2个问题就无需讨论。
在Layout
页面下,添加滚动监听,如下:
/**********************************************/
/* src/components/Layout.vue */
/**********************************************/
...
// 仅插入这段代码即可
mounted() {
window.onscroll = () => {
this.$store.dispatch('SetScroll');
};
}
...
修改src/store/modules/app.js
,添加SetScroll
相关代码,如下:
/**********************************************/
/* src/store/modules/app.js */
/**********************************************/
import Cookies from 'js-cookie';
const app = {
state: {
sidebar: !+Cookies.get('sidebarStatus'),
scroll: {},
},
mutations: {
TOGGLE_SIDEBAR: state => {
if (state.sidebar) {
Cookies.set('sidebarStatus', 1);
} else {
Cookies.set('sidebarStatus', 0);
}
state.sidebar = !state.sidebar;
},
SET_SCROLL: (state, data) => {
state.scroll[data.name] = data.top;
}
},
actions: {
ToggleSideBar: ({ commit }) => {
commit('TOGGLE_SIDEBAR')
},
SetScroll: ({ commit, state }) => {
let nameValue = location.pathname;
let topValue = document.documentElement.scrollTop || document.body.scrollTop;
commit('SET_SCROLL', { name: nameValue, top: topValue });
},
}
};
export default app;
/**********************************************/
/* src/store/getters.js */
/**********************************************/
const getters = {
token: state => state.user.token,
email: state => state.user.email,
sidebar: state => state.app.sidebar,
scroll: state => state.app.scroll
};
export default getters
当我们在某个页面需要实现这样的功能时,在该页面插入代码:
import {scrollPosition} from '@/config/utils'
...
mounted(){
// 滚动到上一次位置
scrollPosition(this.$store.getters.scroll);
},
...
具体使用,可以看下面“完善消息列表页面”内容。
Step21. 完善消息列表页面
修改messages
页面代码,如下:
/**********************************************/
/* src/page/messages/messages.vue */
/**********************************************/
消息
{{item.name}}
{{item.time}}
{{item.type}} {{item.content}}
Loading...
这个页面的列表,我们使用了的仓库存储,所以我们还要更新一下src/store/modules/common.js
文件,代码如下:
/**********************************************/
/* src/store/modules/common.js */
/**********************************************/
const common = {
state: {
messages: [],
contacts: []
},
mutations: {
GET_MESSAGES_LIST: (state, list) => {
state.messages = [...state.messages, ...list];
},
GET_CONTACTS_LIST: (state, list) => {
state.contacts = [...state.contacts, ...list];
},
},
actions: {
// 获取消息列表
GetMessagesList({ commit, state }) {
let list = [{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170928180518_0_KcXn.jpg",
name: "万丈-Infinite",
time: "上午11:47",
type: "",
content: "+1"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20171012184532_0_xTaB.jpg",
name: "切图者联盟",
time: "上午11:47",
type: "",
content: "dav:世界上最可怕的不是孤独终老,而是跟那个使你孤独的人终老。"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170928180152_0_ldsQ.jpg",
name: "群助手",
time: "上午11:40",
type: "[6个群有新消息]",
content: ""
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170920181437_0_YqPR.jpg",
name: "VR实验室",
time: "上午11:38",
type: "",
content: "收到"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170920181200_0_czDQ.jpg",
name: "我的电脑",
time: "上午11:36",
type: "",
content: "[图片]IMG_8724.PNG"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170919182858_0_5sfc.jpg",
name: "QQ看点",
time: "上午11:30",
type: "",
content: "健身最女王:除了彭于晏,也就他的身材让女人着迷!"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/ori/20171022233804_0_0QNg.jpg",
name: "D3.js",
time: "上午11:30",
type: "",
content: "w 加入本群"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170919182002_0_teEG.jpg",
name: "购物号",
time: "上午11:27",
type: "[新消息]",
content: "蘑菇街每日精选:秋冬好货双十一,全场49元封顶!"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/ori/20171012183756_0_i4am.jpg",
name: "夕阳",
time: "上午11:20",
type: "",
content: "知道"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/ori/20171012183530_0_CyI7.jpg",
name: "小红帽",
time: "上午11:19",
type: "",
content: "就这样处理吧"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/300/20170929190950_0_THbY.jpg",
name: "京东商城",
time: "上午11:17",
type: "[新消息]",
content: "京东每日精选:好货双十一,全场99元封顶!"
}
];
commit('GET_MESSAGES_LIST', list);
},
// 获取联系人列表
GetContactsList({ commit, state }) {
let list = [{
name: "特别关心",
online: 2,
total: 2,
closed: true,
list: [{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170928180518_0_KcXn.jpg",
name: "万丈-Infinite",
time: "上午11:47",
type: "",
content: "+1"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20171012184532_0_xTaB.jpg",
name: "切图者联盟",
time: "上午11:47",
type: "",
content: "dav:世界上最可怕的不是孤独终老,而是跟那个使你孤独的人终老。"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170928180152_0_ldsQ.jpg",
name: "群助手",
time: "上午11:40",
type: "[6个群有新消息]",
content: ""
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170920181437_0_YqPR.jpg",
name: "VR实验室",
time: "上午11:38",
type: "",
content: "收到"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170920181200_0_czDQ.jpg",
name: "我的电脑",
time: "上午11:36",
type: "",
content: "[图片]IMG_8724.PNG"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170919182858_0_5sfc.jpg",
name: "QQ看点",
time: "上午11:30",
type: "",
content: "健身最女王:除了彭于晏,也就他的身材让女人着迷!"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/ori/20171022233804_0_0QNg.jpg",
name: "D3.js",
time: "上午11:30",
type: "",
content: "w 加入本群"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170919182002_0_teEG.jpg",
name: "购物号",
time: "上午11:27",
type: "[新消息]",
content: "蘑菇街每日精选:秋冬好货双十一,全场49元封顶!"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/ori/20171012183756_0_i4am.jpg",
name: "夕阳",
time: "上午11:20",
type: "",
content: "知道"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/ori/20171012183530_0_CyI7.jpg",
name: "小红帽",
time: "上午11:19",
type: "",
content: "就这样处理吧"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/300/20170929190950_0_THbY.jpg",
name: "京东商城",
time: "上午11:17",
type: "[新消息]",
content: "京东每日精选:好货双十一,全场99元封顶!"
}
]
},
{
name: "我的好友",
online: 23,
total: 56,
closed: true,
list: [{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170928180518_0_KcXn.jpg",
name: "万丈-Infinite",
time: "上午11:47",
type: "",
content: "+1"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20171012184532_0_xTaB.jpg",
name: "切图者联盟",
time: "上午11:47",
type: "",
content: "dav:世界上最可怕的不是孤独终老,而是跟那个使你孤独的人终老。"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170928180152_0_ldsQ.jpg",
name: "群助手",
time: "上午11:40",
type: "[6个群有新消息]",
content: ""
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170920181437_0_YqPR.jpg",
name: "VR实验室",
time: "上午11:38",
type: "",
content: "收到"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170920181200_0_czDQ.jpg",
name: "我的电脑",
time: "上午11:36",
type: "",
content: "[图片]IMG_8724.PNG"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170919182858_0_5sfc.jpg",
name: "QQ看点",
time: "上午11:30",
type: "",
content: "健身最女王:除了彭于晏,也就他的身材让女人着迷!"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/ori/20171022233804_0_0QNg.jpg",
name: "D3.js",
time: "上午11:30",
type: "",
content: "w 加入本群"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170919182002_0_teEG.jpg",
name: "购物号",
time: "上午11:27",
type: "[新消息]",
content: "蘑菇街每日精选:秋冬好货双十一,全场49元封顶!"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/ori/20171012183756_0_i4am.jpg",
name: "夕阳",
time: "上午11:20",
type: "",
content: "知道"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/ori/20171012183530_0_CyI7.jpg",
name: "小红帽",
time: "上午11:19",
type: "",
content: "就这样处理吧"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/300/20170929190950_0_THbY.jpg",
name: "京东商城",
time: "上午11:17",
type: "[新消息]",
content: "京东每日精选:好货双十一,全场99元封顶!"
}
]
},
{
name: "职场工作",
online: 123,
total: 239,
closed: true,
list: [{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170928180518_0_KcXn.jpg",
name: "万丈-Infinite",
time: "上午11:47",
type: "",
content: "+1"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20171012184532_0_xTaB.jpg",
name: "切图者联盟",
time: "上午11:47",
type: "",
content: "dav:世界上最可怕的不是孤独终老,而是跟那个使你孤独的人终老。"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170928180152_0_ldsQ.jpg",
name: "群助手",
time: "上午11:40",
type: "[6个群有新消息]",
content: ""
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170920181437_0_YqPR.jpg",
name: "VR实验室",
time: "上午11:38",
type: "",
content: "收到"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170920181200_0_czDQ.jpg",
name: "我的电脑",
time: "上午11:36",
type: "",
content: "[图片]IMG_8724.PNG"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170919182858_0_5sfc.jpg",
name: "QQ看点",
time: "上午11:30",
type: "",
content: "健身最女王:除了彭于晏,也就他的身材让女人着迷!"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/ori/20171022233804_0_0QNg.jpg",
name: "D3.js",
time: "上午11:30",
type: "",
content: "w 加入本群"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/700/20170919182002_0_teEG.jpg",
name: "购物号",
time: "上午11:27",
type: "[新消息]",
content: "蘑菇街每日精选:秋冬好货双十一,全场49元封顶!"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/ori/20171012183756_0_i4am.jpg",
name: "夕阳",
time: "上午11:20",
type: "",
content: "知道"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/ori/20171012183530_0_CyI7.jpg",
name: "小红帽",
time: "上午11:19",
type: "",
content: "就这样处理吧"
},
{
image: "http://img.pingan.fusio.com.cn/materials/pic/300/20170929190950_0_THbY.jpg",
name: "京东商城",
time: "上午11:17",
type: "[新消息]",
content: "京东每日精选:好货双十一,全场99元封顶!"
}
]
}
];
commit('GET_CONTACTS_LIST', list);
},
}
};
export default common;
上面代码,包含了messages
页面和contacts
页面的数据。
更新一下src/store/getters.js
,代码如下:
/**********************************************/
/* src/store/getters.js */
/**********************************************/
const getters = {
token: state => state.user.token,
email: state => state.user.email,
sidebar: state => state.app.sidebar,
scroll: state => state.app.scroll,
messages: state => state.common.messages,
contacts: state => state.common.contacts,
};
export default getters
现在我们可以去看一下messages
页面的效果,也试试“滚动加载更多”和“回到指定位置”功能。
Step22. 顶部菜单改造(slot的使用)
在消息列表页面,我们引入header
的时候,用了slot
功能,这是一个非常有用的功能。详情可以阅读vue的API之slot使用。
我们通过分析几个页面的需求,需要用到几个slot,所以header
的代码如下:
/**********************************************/
/* src/components/header/head.vue */
/**********************************************/
由于文章篇幅限制,请继续阅读Vue教程--Wap端项目搭建从0到1(详解3)。
学习是一条漫漫长路,每天不求一大步,进步一点点就是好的。