学完vue其实已经有一段时间了,但是内容实在是有点多且碎,因此在听了两遍课之后把笔记补上,对整个项目做一个总结
项目传送门
关键词 vue
JSON验证
Ajax
准备工作
具体内容看这
1.安装终端:一般来说,使用powershell
或者cmd
即可,Git Bash
有些指令不兼容
2.安装nodejs
,安装你LTS版本即可
3.开发环境
安装@vue/cli
脚手架
在powershell输入以下命令:
npm i -g @vue/cli
4.vue相比react,具有一个图形化的界面,如下图所示:
1).打开方式:打开一个终端并且输入:
vue ui
页面上方有一个vue项目管理器,在那里进行创建,创建可以指定路径,如:打算在E盘vue文件夹创建,输入:
E:\vue\
后面的斜杠一定要有,否则不生效
2).预设选择vue3
3).对于插件,router
插件与vuex
插件(实现多个组件之间维护同一组数据,类似于redux
),两个都有安装按钮
依赖:安装bootstrap
5.调试:在任务中进行调试,页面下具有许多功能:
build:打包
serve:编写
vue框架的结构概述:
views
:写各种页面
router
:初始状态下有两个路由,/
,about
components
:存储组件
main.js
入口,整个组件挂载到app元素上
注:后端渲染与前端渲染:
后端渲染:每打开一个页面,服务器发送请求并且返回回来
前端渲染;只有在第一次打开(无论是什么页面),服务器将所有元素返回,同时打包在js文件中,当打开第二个或第三个等页面后,用返回的js文件直接将新页面渲染出来
vue的基本概念
1.vue的文件组成部分
html
js
css
2.vue的特点
1).将所有东西全部放在一起,同一个页面会包含所有东西
css可以加scope特性:添加此特性,每个选择器在返回给前端的时候会有一个随机值,每个组件的随机值是不一样的,这样不同组件之间的css选择器就不会影响到
2).组件化框架
一个页面可能会有不同的部分,每一个部分都可以用一个单独的组件来实现
引入方式:输入如下代码:
<HelloWrold msg=" "/>
2022.08.08更新
项目概述
本项目是一个简易的社交软件
当前项目的版本日志如下:
本次版本为该应用的首次版本,具体更新内容如下:
1.本次新增真实模拟用户登录与退出及注册功能
2.本次新增个人发帖与删除帖子功能,修改帖子功能敬请期待
3.本次新增用户对用户列表内用户进行关注与取消关注的功能,并真实显示用户关注数的增减
4.全新的用户动态页面设计,页面丰富美观
5.更多功能更新敬请期待
本次工程项目笔记重点侧重于对实现用户动态页面以及个人发帖删帖功能、通过ajax实现用户的动态注册与登录的记录,其他功能的实现暂不记录
项目当前的实现效果如下:
登录用户主页
发帖操作
用户列表
任意其他用户的主页
登录页面
注册页面
具体项目编写过程
整个页面及组件结构如下图所示:
导航栏编写
与react一样,通过引入bootstrap,在bootstrap中找合适的导航栏样式并配置即可
但使用时需要安装依赖:@popperjs/core
重点
由于每一个页面都有一个导航栏,因此需要引入到根组件中
导出;
export default {
name:"App",
components:{
NavBar,
}
}
在根文件中导入
export default{
name:"App"
component:{
NavBar,
}
}
将contanier的-fluid去掉就能将整个导航栏靠中间
初始创建页面,实现六个组件
对于页面,先暂时将页面内容用卡片括起来,以保持美观
整体定义在ContentBase.vue组件中,方便整体修改
1.在某个组件引入另一个组件,如果组件之间有子元素,子元素如何获取
方法:在被引入组件中通过slot
属性,将所有子元素渲染进去,react中采用this.props.children
,其中可以渲染任意内容
页面初始状态设计(以下内容部分在引入api后便不再使用,只是作为静态页面属性)
1.创建路由
引入在index.js中,具体引入格式为(以引入用户列表页面为例):
import UserListView from '../views/UserListView.vue'
{
path: '/userList/',
name: 'userlist',
component: UserListView
},
2.导航栏点击切换页面
对于点击切换页面功能,如果按照最普通的<a >
链接写法,每次点开都会刷新一下,向服务器请求数据,
此非前端渲染,在这里建议使用前端渲染,页面流量会大大降低
<router-link class="nav-link" :to="{name:'userlist',params{}}">用户列表</router-link>
在vue中,如果想给某一个标签绑定一个属性,用:
以上代码在使用时,params{}
如果没有参数列表也不能省略(不过亲测没有也没啥事hh)
3.实现用户动态页面(本用户与其他用户均适用)
对于此页面,想要追求得到的效果如下所示:
一般一个正常的社交平台在发帖之后,帖子传到信息区,并且按照队列的顺序依次排列
为实现这种效果,定义几个vue文件:
UserProfileInfo
UserProfilePosts
UserProfileWirte
用户信息框
实现的最终效果如下图所示
所运用的方法是简单的的css
先将用户区与帖子列表区按照3:9的比例分开,可以用简化写法:
div.row .(div.col-3+div.col-9)
对于头像,需要圆形,且是自适应大小,可以用如下写法:
img-fluid:图片自适应大小
border-radius:设为圆形
数据交互
对于不同的用户而言,其头像、粉丝数、是否关注等等数据,不同的用户数据之间不一样,所以最好是定义成参数
存储参数的函数:setup()
一般来说,定义变量可以有两种方式:
ref
:当变量一般需要重新复制时用
reactive
:ref比reactive运行效率低一些,尽量还是用reactive
定义变量名为user,存储下当前静态用户的一些信息,由于镜头用户不会变,因此可以用reactive
注:由于后续引入ajax的api,因此以上的内容在后续改成动态页面后不会使用
如何在不同组件之间传信息(父-子)
类似于react,但语法不同,下面是react的传递信息的基本原理
vue原理与之类似,只是说语法不同:
比如,在UserProfileView中绑定属性
<USerProfileInfo :user="user"/>
想要在子组件USerProfileInfo
接收到父组件传来的user,在export中定义props接收来的参数
props:{
user:{
type:Object, # Object记得大写
required:true,
},
},
注:绑定属性时,:
是v-bind
的缩写
如何希望数值能够实现动态计算
使用computed
属性
(当调用api后,则就不再需要此属性)
具体用法为:
setup(props){
last fullName=computed(()=>props.user.lastName+' '+props.user.firstName);
return {
fullName;
}
}
setup
中由于没有this
,因此需要引入props
实现关注按钮(引用api后不再使用)
关注按钮的功能,即为当如果关注时,显示取消关注,当未关注时,显示关注
实现此效果需要用到v-if
来做判断
具体代码为:
<button v-if="!user.is_followed" >+关注</button> # 显示+关注/未关注
<button v-if="user.is_followed" >取消关注</button> # 取消关注
当关注状态修改之后需要更新状态,用v-on:click
来绑定函数
可以简写为:
@click="follow"
由于状态是传过来的,关注状态存储在父组件中,因此子组件不能够直接被修改
需要子组件-父组件传递信息
触发父组件绑定函数
cotext.emit();
帖子列表(调用api后部分实现的代码也不需要)
首先先按照上述的步骤做法设置好
当用{{post.posts}}
取值时,发现出现传过来的都是对象,没有转化成一条条列表,如果需要转化成content
需要用到循环,在vue中,循环的写法与普通的for循环写法一致,在UserProfilePosts
中写
v-for
# 语句如下:
<div v-for "post in posts">
通过以上语句的写法,可以让post里的任意posts,都有对应的div
注:在写循环时出现保存,加上:key=item
即可,以保证key
是唯一的(vue的内部属性)
v-for
的另一种写法(下标写法):
v-for="(post,index)in posts.posts":key="index"
关于这两种写法的选择:
如果数组不变可以用下标,变则不推荐用1下标
发帖模块实现
同样的,按照上述方法,涉设置基本样式并配置组件、引入等操作
vue获取textarea中的内容
同样的,需要用到setup()
变量,来获取content
的值
用到属性v-model
:让某个标签内容和context内容相绑定
然后通过触发函数,将内容生成帖子,由于帖子值存储在父组件中,因此需要子组件向父组件传递信息
最新的帖子排在最前面
用以下代码:
post.posts.unshift({
id:
UserId:
content:
})
然后再用context.emit()
触发父组件
整个实现的逻辑如下:
为何会放在最上面的逻辑为:
云端API调用以实现功能
好友列表页面
前言:从这里开始,本次项目正式开始使用API,从云端将用户进行获取
API地址
通过GET方法获取API,访问JSON信息,,用REST Framework
与JWT验证进行后端验证
用ajax实现数据的获取,首先先装上jquery,引入$
对象
npm i jQuery # 安装jQuery
ajax的一个特点是同一个链接但是可能对应不同的方法对应不同的函数
用户列表的界面设计(包括个人主页与列表)
列表中每一项的设计都是如下的格式
每一个用户都是一个卡片的形式
优化:
用户列表中的每一项应该可以点击,因此加上特效,当点击时,鼠标变成小手形状(css样式)
cursor:pointer;
鼠标悬浮效果
box-shadow: 2px 2px 10px lightgrey; # 鼠标加上阴影
transition: 500ms;
点击一个用户之后,链接后面加上ID:修改路由改成以下格式:
path: '/userprofile/:userId/',
name: 'userprofile',
component: UserProfileView
对于任意不存在,输出的是404
使用以下代码:
path:'/:catchAll(.*)',
redirect:'/404/',
(建议路由链接一般用/
来做结尾) UseRoute来访问参数
个人用户列表的个人判断
只有当登录之后,才能打开该页面
通过下面语句,来决定登录
const userId =ParseInt(route.params.userId)
由于有些界面,需要进行执行登录后,才能打开
登录页面
动态获取用户名和密码
需要对两个变量username
和password
进行双向绑定,需要用到v-model
同时,发出错误信息也需要响应式变量(但不需要双向绑定)
作为一个表单,需要手写一下提交操作
const login =()=>{
console.log(username.value,password.value);
注意在访问变量值时,需要加.value
当提交在页面曾输出时,会闪过又刷新一下信息:原因在于表单只绑定了一个提交前的事件,执行完之后依旧要执行提交后的事件,阻止方式
@submit.prevent
实现真正登录
以上这些登录只是伪登录,前端页面的登录,实现真正的登录也就是和后端进行全局交互才可
由于很多页面都需要使用的用户信息,因此需要使用全局变量
使用:vuex
在上半部分,了解了父组件与子组件之间传递信息的方式,但是如果两个同级组件之间传递信息,一般方式如下图所示:
vue提供了一种机制vuex
vuex
类似于redux
,维护一个state
,当两个组件想要交互,只需要向全局数据进行交互即可
由于登录信息在不同组件之间都需要你使用,因此将登录信息存在vuex
中
vuex
位于store文件夹的index.js文件中
,其中有几项,在此介绍如下:
state
:存储所有数据
getter
:当向获取state
里面内容时,且需要计算才能获取(不能改)
action
:定义对state
的各种操作
在action
里面是不能直接对state
进行修改的
mutations
:所有对state进行直接修改的操作一定要在mutations
执行,但是在vue
里,限制不能执行异步操作
modules
:为了对state
进行分割,当state
中内容过多,实现分割,方便阅读
若提取访问user
的用户名,可用
store.state.user.username
登录方式的演进
传统的登录方式如下图所示:
当server
接收到请求,从请求取出seeson_id
在数据库中查看是否存在,若存在,从数据库中查到对应用户
如何判断登录:sesson_id
,一般存在cookie
,js不能访问到,当跨域访问时,很难维护登录状态
新的登录方式如下图:
此为JWT
方法,全名又称JSON Web token
JWT
不会存在服务器中,但是可以验证,用户信息存在JWT
里,里面有项内容为userId
验证方法为:
在这对这张图做出一下解释:客户端传回来公钥,服务器在后面补上私钥,用同样的哈希函数去求下加密值是多少,求出来的与传回来的是否一样,如果一样,信息合法
这样保证了加密是安全的
真验证的编写
使用JWT验证,有两个返回值:
refresh
:获取新令牌,post
方法在http body
,更加安全
access
:令牌JWT,直接获取认证
在user.js
写下登录的action
,是一个ajax
当外部要去调用文件中action
的某个名字,需要用到dispatch
获取用户名,以实现JWT
验证
在access
中获取user_id
,解码出来,运用jwt_decode
进行装包,导入包的方式如下:
import jwt_decode from 'jwt-decode'
获取用户名之后
获取到用户名之后,需要将信息更新到state里面,action不能直接更新,需要在mutations
下更新,当mutations
创建完,用context.commit
调用,access
由于每5min过期,因此需要定期刷新。
成功后,跳转到首页(在LoginView
中)
LoginView
部分代码最终如下:
const login =()=>{
error_message.value="";
//console.log(username.value,password.value);
store.dispatch("login",{
username:username.value,
password:password.value,
success(){
//console.log("success");
router.push({name:'userlist'});
},error(){
//console.log("failed");
error_message.value="用户名或密码错误";
}
});
};
登录之后,将自己的用户名放在左上角
需要实现一个效果,当点击用户名,进入个人页面,当点击退出,进入登录界面
状态栏的变化,需要用v-if做一下登录判断,如果是伪未登录,则为初始的状态栏,如果登陆了则为新的状态栏
改变部分如下:
<li class="nav-item">
<!--<router-link class="nav-link " :to="{name:'userprofile',params:{userId:$store.state.user.id}}">{{$store.state.user.username}}</router-link>-->
<router-link class="nav-link " :to="{name:'userprofile',params:{userId:$store.state.user.id}}">{{$store.state.user.username}}</router-link>
</li>
<li class="nav-item">
<a class="nav-link" >|</a>
</li>
<li class="nav-item">
<router-link class="nav-link" :to="{name:'login'}" style="cursor: pointer;" @click="logout">退出</router-link>
</li>
退出的逻辑
删除JWT
即可,在组件中直接调用mutation
的API
,引入useStore
,
action
中设为`dispatch’
用户主页面动态展示
无论是个人还是用户的页面,默认当个人用户登录才能点开,如果没有则直接跳转到登录页面
在UserListView
中写入参数open_user_profile
,对于登录与不登录的两种情况,进行分别的判断与跳转
注:由于vue在后台可以直接判断逻辑,对函数进行封装,来分辨是定义还是函数,因此函数传参数,直接加括号即可
open_user_profile(user,id)
拉下页面信息(全部的用户的,包括个人与其他)
在前面写静态页面时,当前页面是写死的,因此目前无论如何点击任何用户都只会显示自己,现在要显示全部的用户的全部的页面信息,需要从云端拉下来,拉取得信息包括:用户信息
, 历史帖子
在UserPorfileView
的文件中,用ajax
获取api
,
user.id=resp.id;
user.username=resp.username;
user.photo=resp.photo;
user.followerCount=resp.followerCount;
user.is_followed=resp.is_followed;//指的是当前登录的用户是否关注当前正在看的用户
//console.log(resp);
获取每个用户发过的文章
posts.count=resp.length;
posts.posts=resp;
发布帖子
一定是只有自己的页面才可以添加,需要判断登录页面与当前页面是否是自己
const is_me=computed(()=>userId===store.state.user.id)
<UserProfileWrite v-if="is_me" @post_a_post="post_a_post"/>
用v-if
判断即可,记得注意数据类型,应强制转换成int
出现问题:当打开他人的userProfile
时,点击自己左上角没有反应
原因在于:判断链接是否相同时,不包含参数判断
在APP.vue
在<router-viue />
里添加:key="router fullpath"用完整路径来判重
添加操作
引入一个api,加headers
进行验证
更新有两种操作:
在后端返回结果之后,再在前端更新,也可以直接在前端更新
后者可以让用户操作更加流畅,但是如果提交有bug,可能会有数据不一致发生
删除帖子
众所周知,删除不是所有时候都能删除,比如删除别人的内容,也就是说,删除和修改只有在自己页面才可以操作
修改在UserProfilePosts
中,加上辅助计算属性is_me
,同时,判断是不是自己,需要找到当前用户是谁
记得导入Usestore
删除事件:以实现真正删除(前端)
删除事件由于存在父组件,因此在父组件操作函数,即UserProfileViews
主要是根据post_id
,将某个post
进行删除
判断当
posts.id!=post_id
时,此时应该保留
子组件再通过context.emit
函数引入
后端删除
调用api,通过ajax进行后端操作:
if(resp.result==="success"){
//后端操作成功,则前端显示实现真正删掉
context.emit('delete_a_post',post_id);
}
注册页面:
类似于登录功能,调用api,此api会返回如下信息:
success
用户名不能为空
两个密码不一致
用户名已存在
密码不存在
输入状态语句:
error_message=resp
或
error_message.value=""
将这几个语句放进success
里的原因是,当向api发送请求成功,触发success
属性,无论此账号是否已经注册,该代码只是来判断请求发送成功即触发.
进一步优化:成功之后直接登录
如同login逻辑,调用store的dispatch即可
username:username.value,
password:password.value,
success(){
//console.log("success");
router.push({name:'userlist'});
},error(){
//console.log("failed");
error_message.value="系统异常,请稍后重试";
}
});
关注功能:
服务器的api并没有区分取消关注还是添加关注,而是一个变换,双向的变换:
关注→取消
取消→关注
同样的也是调用api,用authorication
进行授权,授权完成后,前端页面渲染关注操作,展示关注成功的渲染代码
success(resp){
if(resp.result==="success"){
//授权成功之后则在前端渲染
//console.log("follow",resp);
context.emit("follow");
}
取消关注与之同理
综上所述
整个项目实现了如下一个东西
当用户点开该网页
首先进入首页页面,只有进行登录与注册,才能打开好友信息与个人信息
点击注册,如果直接注册成功则直接登录
登录后跳转好友列表页面
点击任意好友,可点击其帖子,并执行关注与取关操作
右上角是 用户名|退出
此时用户名点进去是个人主页,在里面可以进行发帖,删帖,修改帖子(暂未开放)等操作
退出操作点击即可退出,退回到初始登录界面
以上是整个项目的功能操作的基本介绍
后续更新计划
1.部署至云服务器(在学完spring后端框架课后更新)
2.开发修改帖子功能(随缘更新hh)
很有帮助
太棒了,大佬