介绍
cnode社区是一个中文的nodeJs社区:CNode:Node.js专业中文社区
![0576220052917487cd0eed80ccacd7bf.png](https://i-blog.csdnimg.cn/blog_migrate/0ae2b28dde9b186ccf94f073d3a2c636.jpeg)
实现思路
页面基本结构
头部、中间的列表、侧边栏、文章详情页、用户个人信息、分页
![a52f5d2cbe7ebe60595cee846fa76e34.png](https://i-blog.csdnimg.cn/blog_migrate/15f06f0671e9b7e81c5a38ecee7aab0f.jpeg)
![46658b74427e35caea5e8bc76166bdac.png](https://i-blog.csdnimg.cn/blog_migrate/f44e878429f81f9603bebd8283d43aea.jpeg)
![4994723f01d0563d26da5e18c6ae9867.png](https://i-blog.csdnimg.cn/blog_migrate/1ed59a27efc1ad6fc9f607a21135fa8e.png)
然后每个部分用组件渲染
组件模块
- Header 头部模块
- PostList 文章、帖子列表模块
- Article 文章详情模块
- SliderBar 侧边栏模块
- UserInfo 用户个人中心模块
- Pagination 分页模块
技术实现思路
使用 vue2 来开发,涉及到:
- 计算属性
- 内置指令和事件绑定
- 自定义事件和触发
- 路由跳转、监听
- 组件通信
- axios 获取接口
项目搭建
使用vue 脚手架搭建
// vue 脚手架搭建
vue init webpack cnode
// 使用 npm
npm run dev
npm run build
![fa1d869069cf59cd7a5577663bb85117.png](https://i-blog.csdnimg.cn/blog_migrate/b9ea4cab0a3f31a118ddf60a5d76095a.jpeg)
npm run dev 开启预览、删掉 helloworld 组件,开始开发
Header 组件
代码仓库:kongyi5/cnode
这个部分简单实现了样式,搜索框也省了
PostList 组件
这部分比较复杂,使用了加载动画、axios获取接口,使用过滤器等等。
一:官方接口:https://cnodejs.org/api/v1/topics;
![a9593eba88536809f0d65dcac8fd8f37.png](https://i-blog.csdnimg.cn/blog_migrate/aa538af78d604100645ba9f6b6c71393.jpeg)
- topics 是获取帖子列表,有两个参数,page显示页面内容、limit显示帖子数量;
- 头像:author.avatar_url
- 回复量/浏览量:reply_count / visit_count
- 帖子标题:title
- 时间:last_reply_at
- 帖子分类:top(是否置顶)、good(精华帖)、tab(除了置顶、精华外的其余帖子);share(分享)、ask(问答)、job(招聘)
发帖时间过滤:
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 '招聘'
}
})
Article 组件
这一部分使用了路由作为跳转,然后获取接口的帖子id,在点击帖子的时候把用户名和帖子id在路由里获取好然后传出去
this.$http.get(`https://cnodejs.org/api/v1/topic/${this.$route.params.id}`)
export default new Router({
routes: [
{
name:'post_content',
path:'/topic/:id&author=:name',
components:{
main:Article,
}
}
]
})
<router-link :to="{
name:'user_info',
params:{
name:reply.author.loginname
}
}">
<span>{{reply.author.loginname}}</span>
</router-link>
SlideBar 组件
还是使用路由渲染,但是是渲染两个组件,点击帖子时同时渲染 Article 和 SlideBar;
使用计算属性获取自定义信息数据:
computed: {
topcilimitby5() {
if (this.userinfo.recent_topics) {
return this.userinfo.recent_topics.slice(0, 5);
}
},
replylimitby5() {
if (this.userinfo.recent_replies) {
return this.userinfo.recent_replies.slice(0, 5);
}
}
},
检测路由变化的时候跳转:
watch: {
'$route' (to, from) {
this.getArticleData()
}
}
由于组件会进行复用,而请求data的初始化代码都是放在beforemount中.组件复用,这个方法永远只会执行一次,所有getdata也不会触发,因此把getData放在:
watch: {
'$route' (to, from) {
// 对路由变化作出响应...
console.log('变化了');
}
}
当路由变化就触发getdata,更新视图
UserInfo 组件
使用路由跳转、获取用户名然后渲染进 Article 和 SliderBar
{
name:'user_info',
path:'/userinfo/:name',
components:{
main:UserInfo
}
}
this.$http.get(`https://cnodejs.org/api/v1/user/${this.$route.params.name}`)
Pagination 组件
点击按钮显示上一页、下一页、首页、省略
// Pagination
<button v-if="jduge" class="pagebtn">......</button>
<button v-for="btn in pagebtns"
@click="changeBtn(btn)"
:class="[{currentPage:btn == currentPage},'pagebtn']">
{{btn}}
</button>
import $ from 'jquery'
changeBtn(page) {
//点击上一页,下一页,首页
if (typeof page != 'number') {
switch (page.target.innerText) {
case '上一页':
$('button.currentPage').prev().click();
break;
case '下一页':
$('button.currentPage').next().click();
break;
case '首页':
this.pagebtns = [1, 2, 3, 4, 5, '......'];
this.changeBtn(1);
break;
default:
break;
}
return;
}
this.currentPage = page;
if (page > 4) {
this.jduge = true;
} else {
this.jduge = false;
}
if (page == this.pagebtns[4]) {
this.pagebtns.shift(); //移除第一个元素
this.pagebtns.splice(4, 0, this.pagebtns[3] + 1); //添加最后一个
} else if (page == this.pagebtns[0] && page != 1) {
//先在第一个位置加一个
this.pagebtns.unshift(this.pagebtns[0] - 1);
//移除最后一个数字
this.pagebtns.splice(5, 1);
}
// 分页组件通信
this.$emit('handleList', this.currentPage);
}
}
使用父子组件通信,跟随按钮切换分页
// PostList
<!--分页-->
<pagination @handleList="renderList"></pagination>
import pagination from './Pagination'
data(){
return {
postPage: 1
}
}
getData() {
this.$http.get('https://cnodejs.org/api/v1/topics', {
params: {
page: this.postpage,
limit: 20
}
})
.then(res => ...)
renderList(value) {
this.postpage = value;
this.getData();
}
部署到 gitee/github Pages
![847572f6762025ab4eeef46722047e4f.png](https://i-blog.csdnimg.cn/blog_migrate/4b5b6d5f152f1e2c1444326832f8b437.jpeg)