使用vue.js来搭建一个高仿的CNODE社区
1. cnnode社区的基本架构
项目模块组件:
Header 头部模块
PostList 列表模块
Article 文章模块
Slider侧边栏模块
UserInfo用户个人中心模块
Pagination分页组件的开发
主要用到的技术栈有:
vue.js计算属性
vue.js的内置指令和事件的绑定
vue.js的自定义事件和触发
vue-router路由的跳转和监听
父子组件之间的数据传递,
2.1 Header组件
[图片上传失败...(image-d6cfcc-1563502172085)]
- 将项目中要用到的图片和logo都存入assets文件夹中
- 创建一个Header组件,用img标签引入存储在assets文件中的logo
- 在li标签分别写出:新手入门、API、关于、注册、登录
- 将组件引入App.vue,就可以在页面显示
<template>
<div class="header">
<img src="../assets/cnodejs_light.svg" alt="">
<ul>
<li><a href="#">首页</a></li>
<li><a href="#">新手入门</a></li>
<li><a href="#">API</a></li>
<li><a href="#">关于</a></li>
<li><a href="#">注册</a></li>
<li><a href="#">登录</a></li>
</ul>
</div>
</template>
<script>
export default {
name: "Header"
}
</script>
<style scoped>
.header{
background-color: #444;
height: 50px;
}
img{
max-width: 120px;
margin-left: 50px;
margin-top: 10px;
}
ul{
list-style: none;
float: right;
margin: 4px;
}
li{
display: inline-block;
padding: 10px 15px;
}
a{
text-decoration: none;
color: #ccc;
font-size: 14px;
text-shadow: none;
}
</style>
header中使用了浮动定位
ul{list-style: none; float: right;}
li{display: inline-block; }
2.2 PostList组件
API接口:https://cnodejs.org/api/v1/topics 获取帖子列表
使用HiJson分析数据
拿到的参数分析
头像:author.avatar_url
回复量/浏览量 :reply_count/visit_count
帖子的标题:title
需要使用到过滤器:
时间:last_reply_at
帖子分类:top: 代表是否置顶
good: 代表是否精华
tab 是表示除了置顶和精华之外的其余分区
-share 分享
-ask 问答
-job 招聘
[图片上传失败...(image-bcbae7-1563502172085)]
在数据还未到来时,我们显示出一个正在加载的logo,用img标签引入一个动态图片,用v-if操作这个动态图的显示
<div class="loading" v-if="isLoading">
<img src="../assets/loading.gif" alt="">
</div>
在组件data中定义一个空数组,并且让加载logo默认不显示
data(){
return{
isLoading: false,
posts:[]
}
}
获取数据用到Axios中的get请求,引入之后将Axios全局挂载到Vue原型上,通过this.$http来对网页进行请求
发动请求,接受响应需要axios插件
npm install axios
获取数据用到Axios中的get请求,引入之后将Axios全局挂载到Vue原型上,通过this.$http来对网页进行请求
import Axios from 'axios'
Vue.prototype.$http = Axios;
在methods方法中定义一个方法getData,通过get方法获取数据,通过promise返回请求成功和请求失败的数据,并做对应的事
methods:{
getData(){
this.$http.get('https://cnodejs.org/api/v1/topics',{
params:{
page:1,
limit:20
}
})
.then(res=>{
this.isLoading = false //加载成功,去除动画
this.posts = res.data.data
// console.log(this.posts)
})
.catch(err=>{
//处理返回失败后的问题
console.log(err)
})
}
在页面加载之前,触发getData方法,并且在数据没有到来之前,显示加载logo,如代码所示
beforeMount(){
this.isLoading = true //加载成功之前显示加载动画
this.getData() //在页面加载之前获取数据
}
得到数据后,渲染页面用v-for,根据数据结构写出所需要的数据
<template>
<div class="PostList">
<!--数据没返回时加载-->
<div class="loading" v-if="isLoading">
<img src="../assets/loading.gif" alt="">
</div>
<!--列表-->
<div class="posts" v-else>
<ul>
<li>
<div class="toobar">
<span>全部</span>
<span>精华</span>
<span>分享</span>
<span>文档</span>
<span>招聘</span>
</div>
</li>
<li v-for="post in posts">
<!--头像-->
<img :src="post.author.avatar_url" alt="">
<!--回复/浏览-->
<span class="allcount">
<span class="reply_count">{{post.reply_count}}</span>
/{{post.visit_count}}
</span>
<!--分类-->
<span :class="[{put_good:(post.good == true),put_top:(post.top == true),
'topiclist-tab':(post.good != true && post.top != true)}]">
<span>
{{post | tabFormatter}}
</span>
</span>
<!--标题-->
<router-link :to="{
name:'post_content',
params:{id:post.id}
}">
<span>{{post.title}}</span>
</router-link>
<!--回复时间-->
<span class="last_reply">{{post.last_reply_at | formatDate}}</span>
</li>
</ul>
</div>
</div>
</template>
页面中有帖子分类,与日期格式处理,所以这里要用到过滤器,我们将过滤器代码写入main.js中以便于各个组件使用
//处理日期
Vue.filter('formatDate', function (str) {
if (!str) return '';
var date = new Date(str);
var time = new Date().getTime() - date.getTime(); //现在的时间-传入的时间 = 相差的时间(单位 = 毫秒)
if (time < 0) {
return '';
} else if ((time / 1000 < 30)) {
return '刚刚';
} else if (time / 1000 < 60) {
return parseInt((time / 1000)) + '秒前';
} else if ((time / 60000) < 60) {
return parseInt((time / 60000)) + '分钟前';
} else if ((time / 3600000) < 24) {
return parseInt(time / 3600000) + '小时前';
} else if ((time / 86400000) < 31) {
return parseInt(time / 86400000) + '天前';
} else if ((time / 2592000000) < 12) {
return parseInt(time / 2592000000) + '月前';
} else {
return parseInt(time / 31536000000) + '年前';
}
}
);
// 处理显示板块的文字
Vue.filter('tabFormatter',function (post) {
if(post.good == true){
return '精华'
}else if(post.top == true){
return '置顶'
}else if(post.tab == 'ask'){
return '问答'
}else if(post.tab == 'share'){
return '分享'
}else{
return '招聘'
}
});
因为头像和标题都会跳转到新的地址,所以用router-link来包裹,通过to来跳转到需要的路由和要传统的所需参数
<!--头像-->
<router-link :to="{
name:'user_info',
params:{name:post.author.loginname}
}">
<img :src="post.author.avatar_url" alt="">
</router-link>
<!--标题-->
<router-link :to="{
name:'post_content',
params:{
id:post.id,
name:post.author.loginname
}
}">
<span>{{post.title}}</span>
</router-link>
在roouter文件夹下的index.js文件中引入vue-router,然后定义路由名字,接收传递来的参数,引入其他组件,代码如下:
import Vue from 'vue'
import Router from 'vue-router'
import Article from '../components/Article'
import PostList from '../components/PostList'
import UserInfo from '../components/UserInfo'
Vue.use(Router);
export default new Router({
routes: [
{
name:'root',
path: '/',
components:{
main: PostList
}
},
{
name:'post_content',
path: '/topic/:id',
components:{
main: Article
}
},
{
name:'user_info',
path: '/userinfo/:name',
components:{
main: UserInfo
}
}
]
})
在父组件中引入router
import router from './router'
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
最后在App.vue组件中由router-view组件来接收,通过name的值来判定需要显示的组件
<template>
<div id="app">
<Header></Header>
<div class="main">
<router-view name="slidebar"></router-view>
<router-view name="main"></router-view>
</div>
</div>
</template>
<script>
import Header from './components/header.vue'
// import PostList from './components/PostList.vue'
// import UserInfo from './components/UserInfo.vue'
export default {
name: 'App',
components:{
Header
}
}
</script>
- 后面的Article组件、SlideBar组件、UserInfo组件写法都是一样的,有跳转的用router.link包裹,传递路由名字和参数,遇到需要遍历的就用v-for遍历,需要注意的有两个地方,如下
- 第一个是在Article父组件中,因为写完后会发现点击下图中两个区域(SlideBar组件)页面没有变化,路由的参数虽然变了,但并不会跳转。所以我们要在Article组件中用watch监听路由的变化以便进行跳转
监听路由代码:
watch:{
'$route'(to,from){
this.getArticleData()
}
}
* 第二个是在SlideBar组件中,因为只需要获取五条数据,所以要在计算属性中写两个方法,然后在模板中用v-for来渲染,例:v-for="list in topcilimitby5"
### 2.3 Article组件
API https://cnodejs.org/api/v1/topic/ + 帖子ID
### 2.4 Userinfo组件
API https://cnodejs.org/api/v1/user/+ username