一、环境搭建
使用vue-cli脚手架搭建Vue项目
全局安装
npm install -g @vue/cli
查看版本是否正确
vue --version
新建一个空文件夹,用Git Bash进入这个文件夹,然后输入命令
vue create .
(该命令表示在当前目录下创建一个vue项目)接下来会提示我们选择一些配置选项,比如是否安装Router、Babel、Vuex 等,按需选择即可
注意:可能我们会遇到命令行下无法 Use arrow keys 的情况,这个时候我们可以使用命令 winpty vue.cmd create .
来解决这个问题
- 运行项目在本地预览
npm run serve
当在浏览器中出现以下界面时,就表示我们的vue项目搭建成功啦
安装路由
前提:你在上一步选择配置选项的时候没有选择安装 Router
- 安装:
npm install --save vue-router
- 在router.js中引用:
import router from 'vue-router'
Vue.use(router)
- 在vue实例中注入
new Vue({
router, // 注入router
render: h => h(App)
}).$mount('#app')
引入 Axios
- 安装:
npm install axios
- 在main.js中引入Axios:
import Axios from 'axios'
- 在main.js中把Axios挂载到Vue原型上:
Vue.prototype.$http = Axios
二、cnode 社区的基本构架
将 cnode 社区网页分为以下几个组件
- Header 头部
- PosltList 列表
- Article 文章的详情页
- SlideBar 侧边栏
- UserInfo 用户个人信息
- Psgination 分页组件
三、开始写代码
以下只记录了我认为在项目中相对重要的部分
1. PosltList 组件
API接口:https://cnodejs.org/api/v1/topics 获取帖子列表
<script>
export default {
name: "PostList",
data() {
return {
isLoading: false, // 默认不显示loading动画
posts: [] // 代表当前页面的所有内容列表数组
};
},
methods: {
getData() {
this.$http
.get("https://cnodejs.org/api/v1/topics", {
page: 1,
limit: 20
})
.then(response => {
this.isLoading = false; // 加载成功之后去除 loading 动画
console.log(response);
this.posts = response.data.data;
})
.catch(error => {
// 处理请求失败
console.log(error);
});
}
},
beforeMount() { // 在挂载开始之前被调用:相关的 render 函数首次被调用
this.isLoading = true; // 在页面加载成功之前显示 lodaing 动画
this.getData(); // 在页面加载之前获取数据
}
};
</script>
若请求成功,则会在控制台打印出如下结果
[图片上传失败...(image-b11aef-1569763055194)]
对console.log(response)
的结果进行分析
- 头像:author.avatar_url
- 回复量/浏览量 :reply_count/visit_count
- 帖子的标题:title
- 最后回复时间:last_reply_at
- 帖子分类:
- top: 代表是否置顶
- good: 代表是否精华
- tab 是表示除了置顶和精华之外的其余分区
- tab 又可分为
- share 分享
- ask 问答
- job 招聘
filter 的使用
过滤帖子的类别
PostList中
<!-- 帖子的分类 -->
<span :class="[{put_good:(post.good === true),put_top:(post.top===true),'topiclist-tab':(post.good !== true && post.top !== true)}]">
<span>{{post | tabFormat}}</span>
</span>
main.js中
// 处理帖子分类的文字
Vue.filter('tabFormat',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 '招聘'
}
})
过滤最后回复时间
PostList中
<!-- 最后回复时间 -->
<span class="last_reply">{{post.last_reply_at | formatDate}}</span>
main.js中
Vue.filter('formatDate',function(string){ // 传入 post.last_reply_at 作为参数,注意它为字符串
if(!string) return ''
var date = new Date(string)
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) + '年前'
}
})
2. Article 组件
API:https://cnodejs.org/api/v1/topics/ + 帖子ID
Article接收PostList传递的参数post.id
,获取参数用 this.$route.params.id
methods:{
getArticleData(){
this.$http.get(`https://cnodejs.org/api/v1/topics/${this.$route.params.id}`)// ES6 语法,字符串拼接
.then((response)=>{
this.isLoading = false
console.log(response)
this.posts = response.data.data;
})
.catch((error)=>{
console.log(error)
})
}
}
监听路由的变化(解决点击侧边栏title不跳转的问题)
watch:{ // 监听路由的变化,暂时不是很理解为什么要监听路由的变化
'$route'(to,from){
this.getArticleData()
}
}
引入外部的markdown.css把变为makdown格式
/*引入外部CSS时要去掉scoped*/
<style>
@import url('../assets/markdown-github.css');
...
</style>
3. Pagination 分页组件
这里要用到子组件(Pagination)向父组件(PostList)传递数据
首先要在PostList组件中自定义一个事件 @handleList="renderList"
<template>
<!--分页按钮-->
<Pagination @handleList="renderList"></Pagination><!--自定义事件 handleList-->
</template>
<script>
import Pagination from './Pagination' // 引入组件
export default {
name: "PostList",
components:{
Pagination // 注册组件
},
data() {
return {
postPage:1
}
},
methods: {
getData() {
this.$http
.get("https://cnodejs.org/api/v1/topics", {
params:{ // 注意get请求一定要加params
page: this.postPage, // 把请求的参数变为更新后的 postPage
limit: 20
}
})
.then(...)
.catch(...)
},
renderList(value){ // 用 value 接收从 Pagination 中传递过来的参数 currentPage
this.postPage = value
this.getData()
}
}
</script>
然后在Paginnation组件中传递参数给上面的自定义事件
<template>
<div class="pagination">
<button @click="changeBtn">首页</button>
<button @click="changeBtn">上一页</button>
<button v-if="judge" class="pagebtn">......</button>
<button v-for="btn in pagebtns"
:class="[{currentPage:btn===currentPage},'pagebtn']"
@click="changeBtn(btn)">
{{btn}}
</button>
<button @click="changeBtn">下一页</button>
</div>
</template>
<script>
export default {
name:"Pagination",
data(){
return{
pagebtns:[1,2,3,4,5,'......'],
currentPage:1
}
},
methods:{
changeBtn(page){
this.currentPage = page // 让currentPage = 当前点击的按钮数字(btn)
this.$emit('handleList',this.currentPage) // 把currentPage传递给父组件PostList
}
}
</script>
4. SlideBar 侧边栏组件
使用计算属性
<li v-for="userinfo in replylimited"> <!--这里使用了计算属性-->
<router-link :to="{
name:'post_content',
params:{
id:userinfo.id
}
}">
{{userinfo.title}}
</router-link>
</li>
export default {
name:"SlideBar",
data() {
return {
userinfos: [], // 代表当前页面的所有内容
}
}
computed:{
topiclimited(){
if(this.userinfos.recent_topics){
return this.userinfos.recent_topics.slice(0,5) // 只截取前5条topic
}
},
replylimited(){
if(this.userinfos.recent_replies){
return this.userinfos.recent_replies.slice(0,5) // 只截取前5条topic
}
}
}
}
5. App.vue
<template>
<div id="app">
<Header></Header>
<div class="main">
<!--注意先后顺序,SlideBar要放在main的上面-->
<router-view name="SlideBar"></router-view>
<router-view name="main"></router-view>
</div>
</div>
</template>
<script>
import Header from './components/Header'
import PostList from './components/PostList'
import SlideBar from './components/SlideBar'
export default {
name:"App",
components:{
Header,
PostList
}
}
</script>
四、项目中 Vue Router 的使用
router.js 中的内容
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
name: 'root',
path: '/',
components: {
main: PostList
}
},
{
name: 'post_content',
path: '/topic/:id&author=:name', // 接收PostList传过来的id和name
components: {
main: Article,
SlideBar: SlideBar
}
},
{
name: 'user_info',
path: '/userinfo/:name', // 接收Article传过来的name
components: {
main: UserInfo
}
}
]
})
PostList 组件
<router-link :to="{
name:'post_content',
params:{
id:post.id, // 点击PostList时就把id通过路由传给Article
name:post.author.loginname // 点击PostList时就把name通过路由传递给SlideBar
}
}">
<span>{{post.title}}</span>
</router-link>
Article
<router-link :to="{
name:'user_info',
params:{
name:reply.author.loginname // 点击头像把loginname通过路由传递给Userinfo
}
}">
<img :src="reply.author.avatar_url" alt="头像" style="width:30px;height:30px;">
</router-link>
SlideBar
<router-link :to="{
name:'user_info',
params:{
name:userinfos.loginname // 点击侧边栏头像把loginname通过路由传递给Userinfo
}
}">
<img :src="userinfos.avatar_url" alt="头像">
</router-link>
<router-link :to="{
name:'post_content',
params:{
id:userinfo.id // 点击侧边栏标题把id通过路由传递给Article
}
}">
{{userinfo.title}}
</router-link>
Userinfo
<router-link :to="{
name:'post_content',
params:{
id:userinfo.id // 点击用户信息界面的title标题把id通过路由传递给Article
}
}">
{{userinfo.title}}
</router-link>
五、部署到GitHub
- 在GitHub上新建一个空仓库
- 在项目根目录中新建一个
vue.config.js
文件
并在该文件中设置正确的 publicPath,添加以下内容即可
module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? '/cnode-demo/' // 这里应该设置为和你的仓库名相同
: '/'
}
- 用GitBash打开该项目,并在命令行中输入
npm run build
此时我们会发现项目中多了一个dist目录
-
将项目上传到GitHub上
如果仅仅只是为了预览项目,那么只需要把刚才生成的dist目录上传到我们第一步中新建的那个仓库中就可以进行预览了,但同时我们也可以将整个项目上传
注意!!!
上传项目时最好不要把node_modules文件夹也一起上传,因为这会使我们的项目变得非常大。
我们应设置忽略上传node_modules,在.gitignore文件中写上一行/node_modules/
,即可防止 node_modules 目录被提交 预览项目
将项目上传成功并设置GitHub Pages后,在URL路径中加一个/dist/就可以预览我们的项目啦