[筆記] 使用VUE建立RESTful API

前言:

本篇是紀錄Vue RESTful API的簡單學習筆記
學習的內容來自 Alex大大的 Vue 全家桶與 RESTful API 串接入門介紹

這個VUE's RESTful API系列的影片有兩部,長達七個小時,當初在YouTube上發現,Alex大大解說的方式,我可以聽得懂,就順勢入坑。

當初第一次看的時候,也是花了好幾天才把影片的範例給做完
但由於本身並不是寫前端的,只是偶爾寫一下python
所以學過的東西沒在用,很容易忘記...記得第一次看的時候是過年前
三月再回頭看,發現已經忘記...再看一次...
四月底再回頭看...發現又忘記...且之前寫的檔案也不知丟哪了...
才想到寫blog來記錄學習過程的重要...我也好久沒更新blog...

在開始貼程式碼前,總結一下這次每看必忘的學習過程...
最大的原因大概就是,我是直接學VUE,看這系列影片之前
有看過前端的教學影片, AMOS大大的 :
金魚都能懂的網頁設計入門 - 金魚都能懂了你還怕學不會嗎

But... 對JavaScript的了解,只停留在變數、迴圈、判斷式
很多東西如 dispatch、axios、promise、then、箭頭函式...等
都是在VUE的教學影片中看到...還以為這些都是VUE的特有功能...
再加上看完影片後,當下覺得可以理解影片的內容
但從沒自己找個side project練練手...加上金魚的記憶能力... XDD
anyway 要學VUE...還是得先從JavaScript開始。

複製貼上程式碼之前
1.用NPM 安裝 json-server

npm i -g json-server

2.啟動 json-server (終端機下)

json-server 你的json檔案.json

3.注意事項:
VS Code 套件 live server 的 auto-reload 會產生的坑
解決方式:請看VCR...

以下就開始複製貼上程式碼...
只有新增跟刪除資料的功能,沒有使用VUE Router跟VUEX

網頁畫面:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue RESTful API</title>
  </head>
  <body>
    <div id="app">
      <p>
        <!-- trim 去頭尾空白 -->
        <input type="text" v-model.trim="input" /><a
          href="javascript:;"
          v-on:click="cerateHandler"
          >Create</a
        >
      </p>
      <ol>
        <li v-for="(content,index) in contents">
          {{content.content}} <a href="javascripts:;">Update</a>
          <a href="javascript:;" v-on:click="deleteHandler(index)">Delete</a>
        </li>
      </ol>
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.0.1/vue-router.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js"></script>
  </body>
</html>
<script>
  new Vue({
    el: "#app",
    // ES6寫法
    data: {
      input: "",
      contents: [],
    },
    methods: {
      cerateHandler() {
        if (!this.input) {
          return false;
        }

        // console.log("cerateHandler on clicked", this.input);
        // axios.post 塞資料到api
        axios
          .post("http://localhost:3000/contents", {
            content: this.input,
          })
          .then((res) => {
            // 清空輸入欄位 , 資料輸出到網頁上
            // push 是陣列 新增資料的用法 (contents是陣列)
            (this.input = ""), this.contents.push(res.data);
          });
      },
      // axios 刪除功能
      deleteHandler(index) {
        let target = this.contents[index];
        // console.log("target= ", target);
        axios
          .delete(http://localhost:3000/contents/${target.id})
          .then((res) => {
            this.contents.splice(index, 1);
          });
      },
    },
    mounted() {
      axios.get("http://localhost:3000/contents").then((res) => {
        // console.log(res.data);
        this.contents = res.data;
      });
    },
  });
</script>

接下來是完整的程式,有使用到VUE Router跟VUEX

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue RESTful API</title>
  </head>
  <body>
    <div id="app">
      <router-view></router-view>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.0.1/vue-router.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js"></script>
  </body>
</html>
<script>
  let List = {
    template: `<div>
        <p>
          <input type="text" v-model.trim="input" /><a
            href="javascript:;" v-on:click="cerateHandler"> Create </a>
        </p>
        <ol>
          <li v-for="(item,index) in contents" :key="item.id">
            {{item.content}}
            <a href="javascripts:;" v-on:click="updateHandler(index)"> Update </a>
            <a href="javascript:;" v-on:click="deleteHandler(index)"> Delete </a>
          </li>
        </ol>
      </div>`,
    // ES6寫法
    data: function () {
      return {
        input: "",
        // contents: [],
      };
    },
    computed: {
      contents() {
        return this.$store.state.contents;
      },
    },
    methods: {
      cerateHandler() {
        if (!this.input) {
          return false;
        }

        // console.log("cerateHandler on clicked", this.input);
        // axios.post 塞資料到api
        axios
          .post("http://localhost:3000/contents", {
            content: this.input,
          })
          .then((res) => {
            // 清空輸入欄位 , 資料輸出到網頁上
            // push 是陣列 新增資料的用法 (contents是陣列)
            this.input = "";
            // this.contents.push(res.data);
            //上面不能push資料到data 開嚴格模式會報錯 ,沒開會讓你刪沒報錯 但是不合法 要走mutation 如下
            this.$store.commit("addContent", res.data);
          });
      },
      // axios 刪除功能
      deleteHandler(index) {
        let target = this.contents[index];
        // console.log("target= ", target);
        //改成 action => mutation
        // axios
        //   .delete(http://localhost:3000/contents/${target.id})
        //   .then((res) => {
        //     this.contents.splice(index, 1);
        //   });

        // 先進 action 再進  mutation
        //CONTENT_DELETE 就是給action用的
        this.$store.dispatch("CONTENT_DELETE", { target });
      },
      // axios 更新功能
      updateHandler(index) {
        // console.log(this);
        let target = this.contents[index];
        // this.$router.push({ path: /update/${target.id} });
        // id不是亂取的 是設定 router 時 設定的  path: "/update/:id" <=這個id
        //用name params寫法
        this.$router.push({ name: "update", params: { id: target.id } });
        // 寫法2
        // this.$router.push({path:'/update/'+target.id})
      },
    },
    mounted() {
      // axios.get("http://localhost:3000/contents").then((res) => {
      //   // console.log(res.data);
      //   this.contents = res.data;
      // });
    },
  };

  let Edit = {
    template: <div><p><input type="text" v-model.trim="input" /><a href="javascripts:;" v-on:click="updateHandler">Update</a></p></div>,
    data() {
      return {
        input: "",
      };
    },
    computed: {
      content() {
        return this.$store.state.contents.find((item) => {
          return item.id == this.$route.params.id;
        });
      },
    },
    methods: {
      updateHandler() {
        this.$store
          .dispatch("CONTENT_UPDATE", {
            id: this.content.id,
            input: this.input,
          })
          .then(() => {
            this.$router.push({ path: "/" });
          });
      },
    },
    mounted() {
      if (!this.content) return this.$router.replace({ path: "/" });
      this.input = this.content.content;
    },
  };

  let store = new Vuex.Store({
    strict: true,
    state: {
      contents: [],
    },
    mutations: {
      SET_CONTENTS(state, data) {
        state.contents = data;
      },
      addContent(state, data) {
        state.contents.push(data);
      },
      deleteContent(state, data) {
        state.contents.splice(data, 1);
      },
      updateContent(state, { item, input }) {
        item.content = input;
      },
    },
    actions: {
      CONTENTS_READ: (context) => {
        // console.log("context= ", context);
        // 讀資料 要return
        return axios.get("http://localhost:3000/contents").then((res) => {
          context.commit("SET_CONTENTS", res.data);
        });
      },
      CONTENT_DELETE: (context, { target }) => {
        let index = context.state.contents.indexOf(target);
        if (index == -1) return false;

        return axios
          .delete("http://localhost:3000/contents/" + target.id)
          .then((res) => {
            context.commit("deleteContent", index);
          });
      },
      CONTENT_UPDATE: (context, { id, input }) => {
        console.log("UPDATE");
        let item = context.state.contents.find((item) => {
          return item.id == id;
        });
        if (!item) return false;

        return axios
          .patch("http://localhost:3000/contents/" + item.id, {
            content: input,
          })
          .then((res) => {
            context.commit("updateContent", { item, input });
          });
      },
    },
  });

  // 設定 vue router
  let router = new VueRouter({
    //由上往下走~有順序之分
    routes: [
      {
        path: "/",
        name: "list",
        component: List,
      },
      {
        path: "/update/:id",
        name: "update",
        component: Edit,
      },
      {
        path: "*",
        redirect: "/",
      },
    ],
  });

  new Vue({
    el: "#app",
    // 掛載router ES6
    router,
    store,

    mounted() {
      // this.$store.dispatch("CONTENTS_READ");
      this.$store.dispatch("CONTENTS_READ");
    },
  });
</script>

以上就是,這兩部影片的內容...做完這個範例,不能說就可以完全了解VUE的運作,只能有一個初步的認識...離可以自行做一個side project還有一段路要走...


Reference:

  • Alex 宅幹嘛 👨‍💻 Vue 全家桶與 RESTful API 串接入門介紹 part1
  • Alex 宅幹嘛 👨‍💻 Vue 全家桶與 RESTful API 串接入門介紹 part2