用户登录
9-1:开篇
在上一章中,我们说到:如果想要完成:关注、收藏、点赞、评论 这些功能的话,那么需要首先完成 用户登录 的功能。
那么这一章中,我们就来看一下,我们应该如何完成 用户登录 的功能实现。
首先我们先来看一下 用户登录 的业务逻辑。
对于 用户登录 来说,主要有两个登录的入口:
- 在 《我的页面》中
- 在调用需要登录权限的功能时
那么在明确了 用户登录 的业务逻辑之后,接下来我们就去实现用户登录的对应功能。
9-2:用户登录 - 登录页面基本样式
my.vue
<template>
<view class="my-container">
<!-- 用户未登录 -->
<block>
<image class="avatar avatar-img" src="/static/images/default-avatar.png" mode="scaleToFill" />
<view class="login-desc">登录后可同步数据</view>
<button class="login-btn" type="primary" @click="getUserInfo">微信用户一键登录</button>
</block>
</view>
</template>
<script>
export default {
name: 'my-login',
data() {
return {};
}
};
</script>
<style lang="scss" scoped>
.my-container {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 25%;
.avatar-img {
width: 78px;
height: 78px;
}
.login-desc {
color: $uni-text-color-grey;
font-size: $uni-font-size-base;
margin-top: $uni-spacing-col-big;
}
.login-btn {
margin-top: $uni-spacing-col-big;
width: 85%;
}
}
</style>
9-3:用户登录 - 封装登录组件
在开篇中,我们说到,对于 登录 功能来说提供了两个登录的入口。
那么大家想一下,现在我们已经在 我的 这个 tab页
中实现了 登录的 UI
,难道我还需要在另外一个页面中再去实现一遍吗?
这个肯定是不需要的对不对。所以我们希望可以复用登录的 UI
,而复用的方式则可以把 登录的 UI
封装称为一个 组件
创建登录组件 my-login
<template>
<view class="my-container">
<!-- 用户未登录 -->
<block>
<image class="avatar avatar-img" src="/static/images/default-avatar.png" mode="scaleToFill" />
<view class="login-desc">登录后可同步数据</view>
<button class="login-btn" type="primary" @click="getUserInfo">微信用户一键登录</button>
</block>
</view>
</template>
<script>
export default {
name: 'my-login',
data() {
return {};
}
};
</script>
<style lang="scss" scoped>
.my-container {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 25%;
.avatar-img {
width: 78px;
height: 78px;
}
.login-desc {
color: $uni-text-color-grey;
font-size: $uni-font-size-base;
margin-top: $uni-spacing-col-big;
}
.login-btn {
margin-top: $uni-spacing-col-big;
width: 85%;
}
}
</style>
my
<template>
<my-login />
</template>
<script>
export default {
name: 'login'
};
</script>
在调用需要登录权限的功能时,进入的登录页面 我们先不需要去创建,等到使用的时候,在创建就可以了。
9-4:用户登录 - 明确登录的实现思路
在实现登录的具体功能之前,为了避免一些没有开发经验的同学直接看代码一脸懵逼,我们需要先来明确一下登录的实现基本逻辑。
首先对于登录来说,我们会分为两个不同的端来进行适配实现:
- 微信小程序
- 非微信小程序(在讲解适配时实现)
我们这里先只讲解 【微信小程序的实现】,【非微信小程序】的实现将在后面的 适配环节进行。
微信小程序:
- 想要实现登录功能,那么我们需要调用登录接口来进行实现,而登录接口所需要的参数,我们可以直接通过
getUserProfile
方法进行获取。 - 调用登录接口成功,服务端会返回用户的
token
,这个token
为当前的用户身份令牌。(拥有 token) 则表示用户已经登录了。 - 而此处的
token
,我们需要在多个组件中进行使用,所以token
需要被保存到全局状态管理工具 - vuex
中,同时需要保存的还有通过getUserProfile
获取到的用户基本信息。 - 而当前的用户登录状态,我们希望可以一直保存(PS:不需要每次都进行登录)。所以在登录完成后,我们需要把
token
&&userinfo
保存到 本地存储中 - 最后,为了实现 数据与组件的分离,我们需要把与 与登录相关的逻辑 都封装在
vuex
中进行。
这些业务是 前端用户登录的标准逻辑,大家在以后的前端登录业务处理中,也可以按照此逻辑进行。
那么从下一小节开始,我们就按照这个逻辑实现一下对应的代码。
9-5:用户登录 - 封装 action 调用登录接口
在上一小节中,我们分析了【微信小程序】中进行登录的实现逻辑,那么从这一小节开始,我们就实现对应的功能。
首先我们需要先获取到用户的信息,用作登录时的请求参数:
my-login
<script>
import { mapActions } from 'vuex';
export default {
methods: {
...mapActions('user', ['login']),
/**
* 获取用户信息
*/
getUserInfo() {
// 展示加载框
uni.showLoading({
title: '加载中'
});
uni.getUserProfile({
desc: '登录后可同步数据',
success: async (obj) => {
// 调用 action ,请求登录接口
await this.login(obj);
},
fail: () => {
uni.showToast({
title: '授权已取消',
icon: 'error',
mask: true
});
},
complete: () => {
// 隐藏loading
uni.hideLoading();
}
});
}
}
};
</script>
api/user.js
import request from '../utils/request';
/**
* 用户登录
*/
export function login(data) {
return request({
url: '/sys/login',
method: 'POST',
data
});
}
store/user.js
import { login } from 'api/user';
export default {
namespaced: true,
state: () => {
return {};
},
actions: {
/**
* 完成登录
*/
async login(context, userProfile) {
console.log(userProfile);
// 用户数据
const rawData = userProfile.userInfo;
// 调用登录接口
const { data: res } = await login({
signature: userProfile.signature,
iv: userProfile.iv,
nickName: rawData.nickName,
gender: rawData.gender,
city: rawData.city,
province: rawData.province,
avatarUrl: rawData.avatarUrl
});
// TODO: 登录逻辑
console.log(res);
}
}
};
store/index.js
import user from './modules/user';
const store = new Vuex.Store({
modules: {
user
}
});
9-6:用户登录 - 保存用户登录状态
用户的登录状态需要被保存到 vuex
中,同时需要进行 本地存储。
store/user.js
import { login } from 'api/user';
const STORAGE_KEY = 'user-info';
const TOKEN_KEY = 'token';
export default {
namespaced: true,
state: () => {
return {
// 用户 token
token: uni.getStorageSync(TOKEN_KEY) || '',
// 用户信息
userInfo: uni.getStorageSync(STORAGE_KEY) || {}
};
},
mutations: {
/**
* 保存 token 到 vuex
*/
setToken(state, token) {
state.token = token;
this.commit('user/saveToken');
},
/**
* 保存 token 到 本地存储
*/
saveToken(state) {
uni.setStorage({
key: TOKEN_KEY,
data: state.token
});
},
/**
* 保存 用户信息 到 vuex
*/
setUserInfo(state, userInfo) {
state.userInfo = userInfo;
this.commit('user/saveUserInfo');
},
/**
* 保存 用户信息 到 本地存储
*/
saveUserInfo(state) {
uni.setStorage({
key: STORAGE_KEY,
data: state.userInfo
});
}
},
actions: {
/**
* 完成登录
*/
async login(context, userProfile) {
...
// 登录逻辑
this.commit('user/setToken', res.token);
this.commit('user/setUserInfo', JSON.parse(userProfile.rawData));
}
}
};
9-7:用户登录 - 完成已登录的用户视图
当 token
存在时,表示用户已经登录了,此时需要 展示用户登录完成的视图:
my-login
<template>
<view class="my-container">
<!-- 用户未登录 -->
<block v-if="!token">
...
</block>
<!-- 已登录 -->
<block v-else>
<image class="avatar avatar-img" :src="userInfo.avatarUrl" mode="scaleToFill" />
<view class="login-desc">{{ userInfo.nickName }}</view>
<button class="login-btn" type="default" @click="onLogoutClick">退出登录</button>
</block>
</view>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
name: 'my-login',
data() {
return {};
},
computed: {
...mapState('user', ['token', 'userInfo'])
},
};
</script>
9-8:用户登录 - 实现退出登录功能
store/user
export default {
...
mutations: {
...
/**
* 删除 token
*/
removeToken(state) {
state.token = '';
this.commit('user/saveToken');
},
...
/**
* 删除用户信息
*/
removeUserInfo(state) {
state.userInfo = {};
this.commit('user/saveUserInfo');
}
},
actions: {
...
/**
* 退出登录
*/
logout(context) {
this.commit('user/removeToken');
this.commit('user/removeUserInfo');
}
}
};
my-login
<template>
<view class="my-container">
...
<!-- 已登录 -->
<block v-else>
...
<button class="login-btn" type="default" @click="onLogoutClick">退出登录</button>
</block>
</view>
</template>
<script>
export default {
...
methods: {
...mapActions('user', ['login', 'logout']),
...
/**
* 退出登录
*/
onLogoutClick() {
uni.showModal({
title: '提示',
content: '退出登录将无法同步数据哦~',
success: ({ confirm, cancel }) => {
if (confirm) {
this.logout();
}
}
});
}
}
};
</script>
9-9:用户登录 - 判断用户登录状态
截止到目前为止, 用户登录 的功能其实就已经全部构建完毕了。
接下来我们就需要实现:
- 关注用户
- 文章点赞
- 文章收藏
- 文章评论
这四个对应的功能。
之前我们说过,想要实现这四个功能, 那么需要有一个前提条件就是:当前用户已登录。
所以说,我们就需要在用户使用这四个功能之前,来判断用户的登录状态。
也就是说,在 用户登录功能完成之后,我们其实还不可以立刻着手这四个功能的开发,我们还需要进行一步操作,那就是 判断用户的登录状态!
想要判断用户的登录状态,我们依然需要在 vuex
中进行:
store/user
export default {
...
actions: {
/**
* 进行登录判定
*/
isLogin(context) {
if (context.state.token) return true;
// TODO: 如果用户未登录,则引导用户进入登录页面
return false;
}
}
};
如果用户未登录,则引导用户进入登录页面,那么这一步功能如何进行实现呢?
9-10:用户登录 - 新建登录页面,处理当前场景
开篇的时候,我们说过,对于 登录 来说,包含有两个入口:
- 在 《我的页面》中
- 在调用需要登录权限的功能时
那么此时,就是使用到第二个场景的时候了。
我们创建一个新页面,叫做 login-page
,在这个页面中,导入 my-login
组件:
<template>
<my-login />
</template>
<script>
export default {
data() {
return {};
}
};
</script>
<style lang="scss"></style>
当 进行登录判定,用户未登录时,进入 login-page
页面:
export default {
...
actions: {
/**
* 进行登录判定
*/
async isLogin(context) {
if (context.state.token) return true;
// 如果用户未登录,则引导用户进入登录页面
const [error, res] = await uni.showModal({
title: '登录之后才可以进行后续操作',
content: '立即跳转到登录页面?(登录后回自动返回当前页面哦~~~)'
});
const { cancel, confirm } = res;
if (confirm) {
uni.navigateTo({
url: '/subpkg/pages/login-page/login-page'
});
}
return false;
}
}
};
在 关注 用户时,调用该 action
:
blog-detail.vue
<template>
...
<!-- 关注按钮 -->
<button class="follow" size="mini" @click="onFollowClick">关注</button>
...
</template>
<script>
...
import { mapActions } from 'vuex';
export default {
...
methods: {
...
...mapActions('user', ['isLogin']),
/**
* 关注按钮点击事件
*/
async onFollowClick() {
// 进行登录判定
const isLogin = await this.isLogin();
if (!isLogin) {
return;
}
}
}
};
</script>
9-11:用户登录 - 监听登录成功的状态,返回之前页面
在上一节,我们已经完成了 在调用需要登录权限的功能时,进入登录页面 ,但是当我们登录完成之后,我们 还需要返回之前页面,因为只有这样才能完成我们的功能闭环,所以在这一小节中,我们就去完成这一块的功能:
my-login
:在登录成功后,发送事件
<script>
export default {
methods: {
/**
* 获取用户信息
*/
getUserInfo() {
...
uni.getUserProfile({
desc: '登录后可同步数据',
success: async (obj) => {
// 调用 action ,请求登录接口
await this.login(obj);
// 登录成功之后,发送事件
this.$emit('onLoginSuccess');
}
...
});
},
}
};
</script>
login-page
:监听登录成功的事件,并返回上一个页面
<template>
<my-login @onLoginSuccess="onLoginSuccess" />
</template>
<script>
export default {
methods: {
/**
* 登录成功的事件回调
*/
onLoginSuccess() {
uni.navigateBack({ delta: 1 });
}
}
};
</script>
9-12:用户登录 - 处理登录时无 loading 的 bug
异步操作没有处理:
/**
微信一键登录点击事件
*/
getUserInfo() {
// 展示加载框
uni.showLoading({
title: '加载中'
});
// 获取用户信息,调用 login 接口
uni.getUserProfile({
desc: '登录后可同步数据',
success: async (obj) => {
// 调用登录接口
await this.login(obj);
// 登录成功,发送事件
this.$emit('onLoginSuccess');
},
fail: () => {
uni.showToast({
title: '授权已取消',
icon: 'error',
mask: true
});
},
complate: () => {
// 关闭加载
uni.hideLoading();
}
});
},
9-13:文章操作 - 关注用户
在彻底完成了 登录 相关的内容之后,接下来就可以回过头去实现这四个功能了。
首先我们先去实现 关注用户 的功能:
定义 api
接口:
api/user
/**
* 关注用户
*/
export function userFollow(data) {
return request({
url: '/user/follow',
data
});
}
关注用户接口需要传递 token
请求头,所以我们可以在 request.js
中,传递当前的 token
:
import store from '../store';
function request({ url, data, method }) {
return new Promise((resolve, reject) => {
uni.request({
header: {
Authorization: store.state.user.token
},
});
});
}
export default request;
在 blog-detail
中调用 接口:
<template>
...
<!-- 关注按钮 -->
<button
class="follow"
size="mini"
:type="articleData.isFollow ? 'primary' : 'default'"
:loading="isFollowLoading"
@click="onFollowClick"
>
{{ articleData.isFollow ? '已关注' : '关注' }}
</button>
...
</template>
<script>
...
import { userFollow } from 'api/user';
export default {
...
data() {
return {
...
// 关注用户的 loading
isFollowLoading: false
};
},
methods: {
/**
* 关注按钮点击事件
*/
async onFollowClick() {
// 进行登录判定
const isLogin = await this.isLogin();
if (!isLogin) {
return;
}
// 关注用户
// 开启 button 的 loading
this.isFollowLoading = true;
const { data: res } = await userFollow({
author: this.author,
isFollow: !this.articleData.isFollow
});
// 修改用户数据
this.articleData.isFollow = !this.articleData.isFollow;
// 关闭 button 的 loading
this.isFollowLoading = false;
}
}
};
</script>
9-14:文章操作 - 处理发表评论的 UI
监听 article-operate
中的 输入框点击事件:
<template>
<view class="operate-container">
<!-- 输入框 -->
<view class="comment-box" @click="onCommitClick">
...
</view>
</view>
</template>
<script>
import { mapActions } from 'vuex';
export default {
name: 'article-operate',
props: {},
data() {
return {};
},
methods: {
...mapActions('user', ['isLogin']),
/**
* my-search 的点击事件
*/
async onCommitClick() {
// 进行登录判定,登录之后允许发布评论
if (!(await this.isLogin())) {
return;
}
this.$emit('commitClick');
}
}
};
</script>
在 blog-detail
中展示弹出层:
<template>
...
<!-- 底部功能区 -->
<article-operate @commitClick="onCommit" />
<!-- 输入评论的popup -->
<uni-popup ref="popup" type="bottom"> 发表评论的弹出层 </uni-popup>
...
</template>
<script>
...
export default {
...
methods: {
...
/**
* 发布评论点击事件
*/
onCommit() {
// 通过组件定义的ref调用uni-popup方法
this.$refs.popup.open();
}
}
};
</script>
创建 发表评论 的弹出层组件 - article-comment-commit
:
<template>
<view class="comment-container">
<uni-easyinput
v-model="value"
type="textarea"
placeholder="说点什么..."
:maxlength="50"
:inputBorder="false"
></uni-easyinput>
<button class="commit" type="primary" :disabled="!value" size="mini" @click="onBtnClick">
发送
</button>
</view>
</template>
<script>
export default {
name: 'article-comment-commit',
data() {
return {
value: ''
};
},
methods: {
/**
* 发送按钮点击事件
*/
onBtnClick() {}
}
};
</script>
<style lang="scss" scoped>
.comment-container {
background-color: $uni-bg-color;
text-align: right;
padding: $uni-spacing-row-base;
position: relative;
}
</style>
在 blog-detail
中使用 article-comment-commit
组件:
<template>
...
<!-- 底部功能区 -->
<article-operate @commitClick="onCommit" />
<!-- 输入评论的popup -->
<uni-popup ref="popup" type="bottom">
<article-comment-commit />
</uni-popup>
...
</template>
9-15:文章操作 - 处理评论框的显示问题
现在评论框已经可以显示出来了,但是目前 评论框的显示存在两个问题:
- 输入内容之后,关闭评论框,再次展示评论框时,之前输入的内容依然存在
- 在真机中,软键盘会遮挡评论框的展示
那么在本小节中,我们就来处理一下这两个问题:
1. 输入内容之后,关闭评论框,再次展示评论框时,之前输入的内容依然存在:
原因:
当 popup
关闭时,article-comment-commit
组件 并未销毁,依然存在
解决方案:
监听 popup
的关闭事件,通过 v-if
控制 article-comment-commit
组件的销毁
<template>
...
<!-- 输入评论的popup -->
<uni-popup ref="popup" type="bottom" @change="onCommitPopupChange">
<article-comment-commit v-if="isShowCommit" />
</uni-popup>
</template>
<script>
...
export default {
...
data() {
return {
...
// popup 的显示状态
isShowCommit: false
};
},
methods: {
...
/**
* 发布评论的 popup 切换事件
*/
onCommitPopupChange(e) {
// 修改对应的标记,当 popup 关闭时,为了动画平顺,进行延迟处理
if (e.show) {
this.isShowCommit = e.show;
} else {
setTimeout(() => {
this.isShowCommit = e.show;
}, 200);
}
}
}
};
</script>
2. 在真机中,软键盘会遮挡评论框的展示
原因:
软键盘弹出,占用了底部空间
解决方案:
检测软键盘的弹出事件,动态修改 article-comment-commit
组件的位置
article-comment-commit
<template>
<view class="comment-container" :style="{ bottom: bottom + 'px' }">
...
</view>
</template>
<script>
export default {
data() {
return {
bottom: 0
};
},
created() {
// 检测软键盘的变化
uni.onKeyboardHeightChange(({ height }) => {
this.bottom = height;
});
},
};
</script>
9-16:文章操作 - 发表评论
在一切准备就绪之后,最后就可以实现 发表评论 的功能了。
在 api/user
中,定义发表评论的接口:
/**
* 发表评论
*/
export function userArticleComment(data) {
return request({
url: '/user/article/comment',
method: 'POST',
data
});
}
在 article-comment-commit
中调用接口,发表评论:
<template>
<button class="commit" type="primary" :disabled="!value" size="mini" @click="onBtnClick">
发送
</button>
</template>
<script>
import { userArticleComment } from 'api/user';
export default {
name: 'article-comment-commit',
props: {
articleId: {
type: String,
required: true
}
},
...
methods: {
/**
* 发送按钮点击事件
*/
async onBtnClick() {
// 展示加载框
uni.showLoading({
title: '加载中'
});
// 异步处理即可
await userArticleComment({
articleId: this.articleId,
content: this.value
});
uni.showToast({
title: '发表成功',
icon: 'success',
mask: true
});
// 发表成功之后的回调
this.$emit('success');
}
}
};
</script>
在 blog-detail
中传递 id
,处理评论成功之后的操作:
<template>
<!-- 输入评论的popup -->
<uni-popup ref="popup" type="bottom" @change="onCommitPopupChange">
<article-comment-commit
v-if="isShowCommit"
:articleId="articleId"
@success="onSendSuccess"
/>
</uni-popup>
</template>
<script>
export default {
...
methods: {
...
/**
* 发表评论成功
*/
onSendSuccess() {
// 关闭弹出层
this.$refs.popup.close();
this.isShowCommit = false;
}
}
};
</script>
9-17:文章操作 - 回显评论数据
article-comment-commit
:评论发布成功,传递评论数据对象
/**
* 发送按钮点击事件
*/
async onBtnClick() {
...
// 异步处理即可
const { data: res } = await userArticleComment({
articleId: this.articleId,
content: this.value
});
...
// 发表成功之后的回调
this.$emit('success', res);
}
article-comment-list
:增加添加评论项的方法
/**
* 为 comment 增加一个评论
*/
addCommentList(data) {
this.commentList.unshift(data);
}
blog-detail
:评论成功后,调用添加评论项的方法
/**
* 发表评论成功
*/
onSendSuccess(data) {
...
// 显示评论数据
this.$refs.mescrollItem.addCommentList(data);
}
9-18:文章操作 - 关于点赞和收藏的功能实现
因为 点赞 和 收藏 的功能实现和 关注,几乎一致,所以:
点赞 和 收藏 的功能作为课下作业让大家进行实现,视频中不在进行讲解。
实现代码会在 文档 和 最终代码 中进行体现,供大家进行参考。
点赞功能实现代码:
api/user
/**
* 用户点赞
*/
export function userPraise(data) {
return request({
url: '/user/praise',
data
});
}
article-praise
<template>
<view class="praise-box" @click="onClick">
<image class="img" :src="getIsPraise" />
<text class="txt">点赞</text>
</view>
</template>
<script>
import { mapActions } from 'vuex';
import { userPraise } from 'api/user';
export default {
name: 'article-praise',
props: {
/**
* 数据源
*/
articleData: {
type: Object,
required: true
}
},
computed: {
getIsPraise() {
return this.articleData && this.articleData.isPraise
? '/static/images/praise.png'
: '/static/images/un-praise.png';
}
},
methods: {
...mapActions('user', ['isLogin']),
async onClick() {
// 进行登录判定,登录之后允许发布评论
if (!(await this.isLogin())) {
return;
}
// 展示加载框
uni.showLoading({
title: '加载中'
});
await userPraise({
articleId: this.articleData.articleId,
isPraise: !this.articleData.isPraise
});
// 关闭加载
uni.hideLoading();
// 更新数据
this.$emit('changePraise', !this.articleData.isPraise);
}
}
};
</script>
article-operate
<template>
<!-- 点赞 -->
<view class="options-box">
<article-praise :articleData="articleData" @changePraise="$emit('changePraise', $event)" />
</view>
</template>
<script>
export default {
name: 'article-operate',
props: {
articleData: {
type: Object,
required: true
}
},
};
</script>
blog-detail
<template>
<!-- 底部功能区 -->
<article-operate
:articleData="articleData"
@commitClick="onCommit"
@changePraise="onChangePraise"
/>
</template>
<script>
export default {
methods: {
...
/**
* 点赞处理回调
*/
onChangePraise(isPraise) {
this.articleData.isPraise = isPraise;
}
}
};
</script>
收藏功能实现代码:
api/user
/**
* 用户收藏
*/
export function userCollect(data) {
return request({
url: '/user/collect',
data
});
}
article-collect
<template>
<view class="collect-box" @click="onClick">
<image class="img" :src="getIsCollect" />
<text class="txt">收藏</text>
</view>
</template>
<script>
import { mapActions } from 'vuex';
import { userCollect } from 'api/user';
export default {
name: 'article-collect',
props: {
/**
* 数据源
*/
articleData: {
type: Object,
required: true
}
},
computed: {
getIsCollect() {
return this.articleData && this.articleData.isCollect
? '/static/images/collect.png'
: '/static/images/un-collect.png';
}
},
methods: {
...mapActions('user', ['isLogin']),
async onClick() {
// 进行登录判定,登录之后允许发布评论
if (!(await this.isLogin())) {
return;
}
// 展示加载框
uni.showLoading({
title: '加载中'
});
await userCollect({
articleId: this.articleData.articleId,
isCollect: !this.articleData.isCollect
});
// 关闭加载
uni.hideLoading();
// 更新数据
this.$emit('changeCollect', !this.articleData.isCollect);
}
}
};
</script>
article-operate
<template>
<!-- 收藏 -->
<view class="options-box">
<article-collect :articleData="articleData" @changeCollect="$emit('changeCollect', $event)" />
</view>
</template>
<script>
export default {
name: 'article-operate',
props: {
articleData: {
type: Object,
required: true
}
},
};
</script>
blog-detail
<template>
<!-- 底部功能区 -->
<article-operate
:articleData="articleData"
@commitClick="onCommit"
@changePraise="onChangePraise"
@changeCollect="onChangeCollect"
/>
</template>
<script>
export default {
methods: {
...
/**
* 收藏处理回调
*/
onChangeCollect(isCollect) {
this.articleData.isCollect = isCollect;
}
}
};
</script>
9-19:总结
在这一大章中,我们完成了整个的【用户登录】以及【文章详情】的功能。
针对于【用户登录】来说,我们使用 vuex
来对 【组件】和【数据】进行了分离。
所有与【用户登录】相关的数据操作,都被封装到了 vuex
之中,这样做的好处在于:我们可以在多个组件中对数据进行操作,而不需要担心其影响 单向数据流的简洁性。
而【文章详情】的剩余功能,我们在视频中完成了【关注】和【发布评论】两个功能,而把【点赞】和【收藏】留做了课下作业。这样做的目的是 可以让大家有能够独立思考,以及独立完成功能的机会。
那么到现在为止,我们项目还剩余 【热播】模块没有完成,那么从下一章开始,我们就要搞定【热播】模块啦。