用vuejs3, vue-router, composition API, json-server做一个博客

这个教程里你会学到:

  1. 如何使用composition API
  2. 如何使用vue-router
  3. 如何利用json-server做一个假服务器
  4. 如何外部化setup方法里面的函数然后引入以便复用
  5. 如何接收props在组件中并且处理这个数据

1. 创建工程环境

此项目需要nodejs的支持,如果你还没有安装那么就到官网去下载安装下。我们需要用到NPM这个nodejs的依赖管理器。此工程需要用到vue-cli创建,如果你还没有安装,那么就全局安装下。

npm install -g @vue/cli

这样我们就可以用vue cli来创建我们的项目了。

vue create blog

查看下package.json, 确保你安装的vue版本是vuejs 3, 否则利用不了composition API这个特性。接下来我们来安装vue-router和bootstrap:

vue add router
npm install bootstrap

接下来我们通过src/main.js来引入bootstrap css。

import '../node_modules/bootstrap/dist/css/bootstrap.min.css'

这样我们就可以在项目种直接引用bootstrap css了。

接下来我们来做一套Json数据,然后通过json server插件来使其变成一个服务器的API接口,通过这个接口可以请求博客列表和单个的博客内容。

在src目录下创建文件src/data/posts.json,填充下下面的内容:

{
  "posts":[
    {
      "id": 1,
      "title": "this is post title 1",
      "time": "2021-05-06 15:00:00",
      "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
    },
    {
      "id": 2,
      "title": "this is post title 2",
      "time": "2021-05-06 17:03:00",
      "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
    },
    {
      "id": 3,
      "title": "this is post title 3",
      "time": "2021-05-06 18:20:00",
      "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
    },
    {
      "id": 4,
      "title": "this is post title 4",
      "time": "2021-05-06 19:30:00",
      "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
    },
    {
      "id": 5,
      "title": "this is post title 5",
      "time": "2021-05-06 20:43:00",
      "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
    },
    {
      "id": 6,
      "title": "this is post title 6",
      "time": "2021-05-06 21:48:00",
      "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
    }
  ]
}

执行命令:

json-server --watch src/data/posts.json

这样会自动生成API接口,如:

http://localhost:3000/posts   --博客列表
http://localhost:3000/posts/1   --单个博文

我们可以请求这个接口来模拟服务器数据。

2. 输出博客列表

首先我们先创建一个vue组件来显示列表种的单个博客,通过把这个组件引入到Home.vue中来显示博客列表。创建组件:

src/components/Singlepost.vue

在Home这个大的组件中引入:

src/views/Home.vue:
<template>
  <div class="home container">
<!--如果有大于0个post, 那么就输出,否则显示no posts found 用v-for来直接遍历出单个post-->
    <div v-if="posts.length">
    <Singlepost v-for="post in posts" :key="post.id" :post="post"></Singlepost>
    </div>
    <p v-else>no posts found</p>
  </div>
</template>
<script>
//把Singlepost组件引入
import Singlepost from "../components/Singlepost";
import {ref} from "vue";
export default {
  name: 'Home',
  components: {
    Singlepost
  },
  setup(){
    const posts = ref([]);
    let load = async () => {
         //这里其实可以用try和catch来获取可能出现的错误
         let res = await fetch('http://localhost:3000/posts')
       //ref有个属性叫value,赋值这个value
         posts.value = await res.json()
    };
    load();
    return {
      posts
    }
  }
}
</script>

单个博文的视图组件:

src/components/SinglePost.vue
<template>
  <div class="container">
    <div class="row justify-content-center">
      <div class="col-9">
        <div class="card mb-4">
          <div class="card-header text-start">
<!--注意:to的写法,本人传入的是名称post和一个参数id,模板对应的是src/views/Post.vue-->
            <router-link :to="{name:'post', params:{id:post.id}}">{{post.title}}</router-link>-{{post.time}}
          </div>
          <div class="card-body text-start">
            {{excerpt}}
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import {computed} from "vue";
export default {
  name: "Singlepost",
  props:["post"],
  setup(props){
    //注意一下props.post这个格式来引入props数据
    const excerpt = computed(() => {
      //在首页显示的是略文,显示全文的前50个字符,其余用...替代,在单个博文页面的时候再全部显示
      return props.post.content.substring(0, 50)+"...";
    })
    return {
      excerpt
    }
  }
}
</script>

接下来我们修改下src/router/index.js使得单个博文的路由:

在routes = []里面添加:

import Post from "../views/Post.vue";
...
{
  path:'/post/:id',
  name:'post',
  component: () => Post,
  //接收props的时候注意下,接收的是id这个prop
  props:true
}
...

别忘了创建src/views/Post.vue这个文件。

现在我们看下首页:

image

3. 编写单个博文组件

现在我们来写src/views/Post.vue

<template>
  <div class="container">
    <div class="row justify-content-center">
      <div class="col-9">
        <div class="card mb-4">
          <div class="card-header text-start">
            {{post.title}}-{{post.time}}
          </div>
          <div class="card-body text-start">
<!-- 输出博客全文-->
            {{post.content}}
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import {ref} from "vue";
export default {
  name: "Post",
  props:["id"],
  setup(props){
   //在setup()里引入props
    const post = ref({})
    const load = async () => {
       let res = await fetch("http://localhost:3000/posts/"+props.id)
       post.value = await res.json()
    }
    load()
    return {
      post
    }
  }
}
</script>
<style scoped>
</style>

这个文件比较好理解,所以就不详细解释了。

4.外部化setup()里面的方法以便复用

通常情况下我会创建一个composables这个文件夹,创建getPosts.js。

src/composables/getPosts.js
import {ref} from "vue";

const getPosts = () => {
    const posts = ref([]);

    let load = async () => {
        let res = await fetch('http://localhost:3000/posts')
        posts.value = await res.json()
        console.log(posts.value)
    };

    return {
        posts, load
    }
}

export default getPosts

这样我们在home.vue里引入的时候就更简洁了。

<template>
  <div class="home container">
<!--如果有大于0个post, 那么就输出,否则显示no posts found 用v-for来直接遍历出单个post-->
    <div v-if="posts.length">
    <Singlepost v-for="post in posts" :key="post.id" :post="post"></Singlepost>
    </div>
    <p v-else>no posts found</p>
  </div>
</template>

<script>
//把Singlepost组件引入
import Singlepost from "../components/Singlepost";
import getPosts from "../composables/getPosts";
export default {
  name: 'Home',
  components: {
    Singlepost
  },
  setup(){

    const {posts, load} = getPosts();

    load();

    return {
      posts
    }
  }
}
</script>

如果你的逻辑比较多,就可以用这个方法来外部化众多的逻辑。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容