溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JavaScript全棧開發(fā)

發(fā)布時間:2020-06-16 18:56:52 來源:PHP中文網(wǎng) 閱讀:963 作者:元一 欄目:web開發(fā)

JavaScript(簡稱“JS”) 是一種具有函數(shù)優(yōu)先的輕量級,解釋型或即時編譯型的高級編程語言。雖然它是作為開發(fā)Web頁面的腳本語言而出名的,但是它也被用到了很多非瀏覽器環(huán)境中,JavaScript 基于原型編程、多范式的動態(tài)腳本語言,并且支持面向?qū)ο?、命令式和聲明式(如函?shù)式編程)風(fēng)格。JavaScript是一種屬于網(wǎng)絡(luò)的高級腳本語言,已經(jīng)被廣泛用于Web應(yīng)用開發(fā),常用來為網(wǎng)頁添加各式各樣的動態(tài)功能,為用戶提供更流暢美觀的瀏覽效果。通常JavaScript腳本是通過嵌入在HTML中來實現(xiàn)自身的功能的。 

CRD指的是Create、Read、Delete。針對的數(shù)據(jù)對象,就是一個Todo對象,看起來是這樣的:

{id:1,subject:"Loving"}

如果是多個數(shù)據(jù)對象,看起來是這樣的:

[
  {id:1,subject:"Loving"},
  {id:1,subject:"Writing"},
  {id:1,subject:"Preying"}
]

這個看起來很簡單平實的JS對象,會在一組組函數(shù)、模塊和對象之間流動,甚至跨越網(wǎng)絡(luò)邊界,從內(nèi)存到硬盤。它會被存儲在Mongodb內(nèi),也會從Mongodb提取出來,在用戶界面、HTTP客戶端,HTTP服務(wù)器傳遞。

整個App看起來就是一臺機(jī)器,可以說代碼在運(yùn)轉(zhuǎn)這個機(jī)器,但是也不妨說是數(shù)據(jù)在驅(qū)動這個它。

使用Vuejs腳手架,快速搭建Todo App界面

我們給自己命題,做一個TODO應(yīng)用,它看起來是這樣的:

JavaScript全棧開發(fā)

用戶可以看到一個編輯框,和一個清單。

  1. 在編輯框內(nèi)加入新的todo項目,按回車即可加入到列表
  2. 列表內(nèi)每個項目有一個按鈕,點擊按鈕,可以刪除當(dāng)前項目

環(huán)境要求

說起來搭建JS全棧開發(fā)環(huán)境,涉及到的東西真的不少。大的選擇是這樣的:

  1. 前端采用Vuejs
  2. 后端采用Nodejs
  3. 存儲使用Mongodb。

大的選擇定了,小的配套也就跟著來,前端配套的話需要一系列的技術(shù),特別是前端,對應(yīng)著Vuejs,配套管理路由、狀態(tài)、組件的都有相應(yīng)的技術(shù)手段。自己搭配的話,還是非常麻煩的。

幸好Vuejs還有一個前端腳手架工具vue-cli,它可以把以上的組件整合起來到一個工程內(nèi)。一個最為基礎(chǔ)的vue-cli工程腳手架的創(chuàng)建,現(xiàn)在得需要160M左右的空間占用。在我的電腦和網(wǎng)絡(luò)情況下,需要2分半的時間才會完成。

前端 Vuejs

  1. vue-router,前端路由管理
  2. vuex,前端數(shù)據(jù)管理,專業(yè)一點的說法,就是狀態(tài)管理,這些數(shù)據(jù),可能是屬性,數(shù)組,對象等等,可以跨組件訪問,而不像是data函數(shù)那樣提供的數(shù)據(jù)只能被本組件訪問,可以想到,稍微大一點的前端工程都必須前端狀態(tài)管理的。
  3. axios,前端HTTP訪問,以promise的形式,封裝了類似fetch,AJAX的能力
  4. buefy,前端微型框架,可以使用自定義標(biāo)簽使用自定義組件,并且CSS框架為Bulma
  5. Bulma,盡管使用了微框架,只是讓對CSS framework的了解降到最低,但是不是說就不需要了解了。還是得學(xué)習(xí)的。Bulma相對于老牌的Bootstrap,是不需要依賴于JS框架,也沒有任何JS代碼,因此可以和任何一框架很好的結(jié)合,比如這里的Vuejs。這就是我選擇它的原因

后端 cli-service

為了給前端開發(fā)提供工具鏈和開發(fā)便利性,我們常常需要webpack&babel。有了它們,就可以使用ES6的語法,以及代碼更新后自動刷新等。這些都是非常便利的特性,用了就離不開的。有了vue-cli,對webpack&babel的了解可以降到最低,但是也不能不學(xué),稍微需要一些定制的配置,也是必須要掌握的,起碼得知道如何啟動一個開發(fā)服務(wù)器,已經(jīng)發(fā)布build,還有把前端服務(wù)經(jīng)過proxyChain跳轉(zhuǎn)到后端服務(wù)去等等。所幸是在這個教程內(nèi),你不需要學(xué)習(xí)太多就可以把案例跑起來。

App Server + Mongodb

接下來看后端,一般習(xí)慣就是使用Nodejs+Express.js的搭配。這個沒有多少說的,都是老東西了。為了訪問Mongodb,也需要一套框架,基于Callback的,或者基于Promise+Await+Async的,也是需要選擇的。

為了便于理解,我會用一個最小的案例完成整個開發(fā)過程,就是案例在現(xiàn)實中并不存在,但是也是有用的,就是你可以當(dāng)它們是模板,直接拷貝代碼,然后填充你的內(nèi)容。天下代碼一大抄嘛,沒有什么不對的,畢竟這些寫代碼是最快的。這個案例的數(shù)據(jù)模型就是對一個{id,name}的對象進(jìn)行CRD(創(chuàng)建刪除列表)。

安裝運(yùn)行環(huán)境

安裝環(huán)境相對簡單,特別是如果使用Mac OS X的話。有一些工具鏈可以幫助快速搭建環(huán)境。當(dāng)然Windows也并不多麻煩就是了,它常常提供的是一個安裝程序,大部分時間,你需要的就是點擊下一步。

這里以MAC為例,講解安裝。

mongodb

安裝和運(yùn)行Mongodb Daemon:

brew install mongodb
mongodb

訪問驗證,首先執(zhí)行Mongodb Shell:

mongo

輸入命令,查詢數(shù)據(jù)庫清單:

> show dbs
local           0.000GB

能夠看到這些信息,說明mongodb安裝成功。

Node.js

安裝并驗證:

$brew install nodejs
$node -v
10.7.0

能夠看到這些信息,說明Node.js安裝成功。

開始前端編碼

安裝編碼環(huán)境

首先安裝vue-cli,方法和一般的NPM模塊一樣的,我們安裝的版本是3.0:

npm i @vue/cli

查看版本:

vue -V
3.0.0

看到如下信息,說明成功。然后創(chuàng)建App的腳手架代碼:

vue create todoapp

注意,在此創(chuàng)建過程中,命令行會指示你做出選擇,我們會選擇Manual select feature,然后選擇router和vuex,其他不選。然后并執(zhí)行此代碼:

cd todoapp
npm run serve

可以在瀏覽器中訪問localhost:8080看到Vue的啟動畫面。說明創(chuàng)建腳手架成功。

此時,vue-cli已經(jīng)幫助安裝了vuex和router的模塊依賴。本節(jié)一次性的安裝全部剩余的全部NPM依賴,免得以后用一個安裝一個,麻煩而且啰嗦。

  npm install buefy --save
npm install axios --save

buefy是一個基于Bulma的Vuejs用戶界面組件庫。這次的UI的CSS方案,使用Bulma,Vuejs如果想要以定制組件的形式去使用它的話,那么需要安裝Buefy模塊。實際上,我研究過Bulma和 Bootstrap,還寫了一本免費(fèi)的電子書,我覺得Bulma相對于Bootstrap的優(yōu)勢在于1.不依賴任何JS框架2.用戶接口設(shè)計更加簡明。這就是我現(xiàn)在選擇使用Bulma的原因。

Axios是一個封裝了HTTPClient的庫,提供了promise接口。我們使用它訪問后端的HTTP Server的數(shù)據(jù)。之前提到的數(shù)據(jù)對象,就是由Axios提取到客戶端,也會是通過Axios把數(shù)據(jù)對象提交到服務(wù)器的。

前端編碼

首先,我們從狀態(tài)開始。我們之前提到的Vuex,是Vuejs管理狀態(tài)的官方插件。所謂的狀態(tài),就是應(yīng)用程序的數(shù)據(jù)對象們。也就是我們提到的Todo對象和Todo對象集合。我們在App用戶界面上看到的很多數(shù)據(jù)都是來自于狀態(tài)對象。狀態(tài)對象在src/store.js。不僅僅是的應(yīng)用狀態(tài)信息,還有和對這些的操作函數(shù)。既然需要一個todo項目清單,因此應(yīng)該加入如下代碼:

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const defaultTodo = [
      {id:1,subject:'Eating'},
      {id:2,subject:'Loving'},
      {id:3,subject:'Preying'},
    ]
function indexById(todos,id){
  for (var i = 0; i < todos.length; i++) {
    if (id == todos[i].id)
      return i
  }
  return -1
}
import axios from 'axios'
export default new Vuex.Store({
  state: {
    msg:'Todo App',
    todos:defaultTodo
  },
  mutations: {
    add(state,subject){
      var todo = {id:subject,subject:subject}
      state.todos.push(todo)
    },
    remove(state,id){
      state.todos.splice(indexById(state.todos,id),1)
    },
    reload(state){
      state.todos = defaultTodo
    }
  },
  actions: {
  add: (context, link) => {
      context.commit("add", link)
    },
    remove: (context, link) => {
      context.commit("remove", link)
    },
    reload: (context) => {
      context.commit("reload")
    }
  }
})

其中的state.todos屬性,就是我們的主要的數(shù)據(jù)對象了。state.msg這是提供了一個App的標(biāo)題字符串。mutations屬性內(nèi)是對數(shù)據(jù)修改提供的方法,比如

  1. 我們需要添加一個todo,使用add()方法,相應(yīng)的
  2. 刪除一個todo,使用remove()方法
  3. 刷新一個todo列表,就會使用load()方法

有時候,對數(shù)據(jù)的修改可能是比較消耗時間的,因此為了避免阻塞客戶端的主線程,這個對象也提供了異步的方法,actions對象內(nèi)就是對應(yīng)修改操作的異步方法,這里的方法功能上和mutations一致,但是是異步的。Vuex提供了類似:

context.commit()

的語法,提供和actions和mutations方法的對接。第一個參數(shù)是mutations的方法名稱,之后的參數(shù)最為mutations方法的參數(shù)傳遞給mutations方法。

特別說下,mutations內(nèi)的add()方法,其中用戶界面會提供一個Todo.subject屬性,而ID是需要自動生成的,我們這里臨時使用subject的值作為id,就是一個偷懶,只要subject不要輸入重復(fù),也暫時可以蒙混過關(guān)。因為知道本項目內(nèi)的后臺存儲會采用Mongodb,在Mongodb內(nèi)插入一個新的對象后,會自動生成一個ID,我們的Todo對象的id會采用這個ID。這里就沒有必要自己生成了。

在src/views/home.vue內(nèi),粘貼為如下代碼:

<template>
  <p class="home">
    <h2>{{msg}}</h2>
    <NewTodo></NewTodo>
    <TodoList></TodoList>
  </p>
</template>
<script>
import NewTodo from '@/components/NewTodo.vue'
import TodoList from '@/components/TodoList.vue'
import {mapState,mapActions} from 'vuex'
export default {
  name: 'home',
  computed:mapState(['todos','msg']),
  components: {
    TodoList,NewTodo
  },
  data(){
      return{newtodo:''}
  },
  methods:{
      ...mapActions([
      'remove',
      'add'
    ]),
      add1:function(){
          this.add(this.newtodo)
          this.newtodo = ''
      }
  }
}
</script>

...mapState,mapActions的解說。

就是說,我們這個Todo App劃分為為兩個組件,其中一個組件負(fù)責(zé)顯示編輯框,并接受回車事件,把新的Todo項目加入到應(yīng)用狀態(tài)內(nèi)。另外一個組件負(fù)責(zé)顯示全部Todo項目,并接受刪除事件,刪除指定的Todo項目。它們分別是NewTodo組件和TodoList組件:

<NewTodo></NewTodo>
<TodoList></TodoList>

這兩個組件的代碼實現(xiàn),分別在文件src/components/NewTodo.vuesrc/components/TodoList.vue內(nèi)。NewTodo代碼:

<template>
  <p class="home">
    <form @submit.prevent="add1">
        <input type="text" name="newTodo" placeholder="new todo" v-model="newtodo">
    </form>
  </p>
</template>
<script>
import {mapState,mapActions} from 'vuex'
export default {
  name: 'newtodo',
  computed:mapState(['todos','msg']),
  data(){
      return{newtodo:''}
  },
  methods:{
      ...mapActions([
      'add'
    ]),
      add1:function(){
          this.add(this.newtodo)
          this.newtodo = ''
      }
  }
}
</script>

TodoList代碼:

<template>
  <p class="hello">
    <ul>
      <li v-for="(todo,index) in todos" v-bind:key="todo.id">
        {{todo.subject}}<button @click="remove(todo.id)" class="rm">remove</button>
      </li>
    </ul>
  </p>
</template>
<script>
import {mapState,mapActions} from 'vuex'
export default {
  name: 'todolist',
  computed:mapState(['todos','msg']),
  components: {
  },
  methods:{
    ...mapActions([
      'remove','reload'
    ])
  },
  mounted(){
    this.reload()
  }
}
</script>
<style scoped>
</style>

在src/main.js文件內(nèi),添加如下代碼,引入Buefy:

import Buefy from 'buefy'
import 'buefy/lib/buefy.css'
Vue.use(Buefy)

現(xiàn)在可以使用Buefy組件了。我們可以把NewTodo組件內(nèi)的標(biāo)準(zhǔn)的input變成組件化的input,把標(biāo)簽換成b-input即可。代碼如下:

<b-input type="text" name="newTodo" placeholder="new todo" v-model="newtodo"></b-input>

現(xiàn)在看瀏覽器,input變成了比較有吸引力的Bulma風(fēng)格的控件了。訪問網(wǎng)絡(luò)使用axios。需要首先找到src/home.vue在代碼的開頭部分引用此庫:

import axios from 'axios'

在Vue單頁組件內(nèi)使用此庫了。比如在src/home.vue內(nèi)代碼對象中加入新方法:

mounted(){
  var url  = 'https://api.coindesk.com/v1/bpi/currentprice.json'
  axios ({
        url:url,
        method: 'get',
    })
    .then( res => {console.log(res.data.chartName)} )
    .catch( err => cosole.error(err))
}

我們來看看適應(yīng)效果。啟動cli-service:

npm run serve

然后打開瀏覽器,輸入地址localhost:8080,如果可以在瀏覽器內(nèi)看到我們期望的用戶界面,并且都可以看到console打印了Bitcoin,那么就說明用戶界面代碼和初步的訪問HTTP網(wǎng)絡(luò)的axios代碼以及狀態(tài)管理功能都是成功了的。

后端編碼

現(xiàn)在,我們已經(jīng)可以看到UI了,但是用戶界面內(nèi)的數(shù)據(jù)來自于客戶端,而不是來自于服務(wù)器。我們的數(shù)據(jù)當(dāng)然應(yīng)該來源于服務(wù)器的了。因此我們需要啟動給一個自己的服務(wù)器,這個服務(wù)器可以接受客戶在界面上錄入的新的Todo對象,也可以提供后端數(shù)據(jù)庫內(nèi)的Todo清單。

為了測試的目的,常常需要準(zhǔn)備一個todo應(yīng)用的后臺JSON服務(wù),可以通過HTTP方式,提供todo項目的增加刪除修改和查詢。

這樣的服務(wù)器,使用了nodejs作為服務(wù)器端,并且使用了兩個node模塊,可以使用npm安裝它們:

npm install express body-parser

body-parser是一個中間件,可以解析請求內(nèi)容并把解析結(jié)果放到req.body屬性內(nèi)。最常見的做法就是解析json內(nèi)容。

代碼如下(文件名為:jsonserver.js):

  var express = require('express');
  var app = express();
  var path = require('path')
  var bodyParser = require('body-parser')
  app.use(bodyParser.json())
  var todos = []
  var public = path.join(__dirname, '/')
  app.use('/',express.static(public))
  const defaultTodo = [
    {id:1,subject:'Eating'},
    {id:2,subject:'Loving'},
    {id:3,subject:'Preying'},
  ]
  function rs(){
    todos = defaultTodo
  }
  function indexById(id){
    for (var i = 0; i < todos.length; i++) {
      if (id ==todos[i].id)return i
    }
    return -1
  }
  rs()
  app.delete('/api/todo/:id', function (req, res) {
    var userkey = +req.params.id
    todos.splice(indexById(userkey),1)
    res.end( JSON.stringify(todos));
    rs()
  })
  app.get('/api/todos', function (req, res) {
    res.end( JSON.stringify(todos));
  })
  app.post('/api/todo', function (req, res) {
    todos.push(req.body)
    res.end(JSON.stringify(todos))
    rs()
  })
  var server = app.listen(8081, function () {
    var host = server.address().address
    var port = server.address().port
    console.log("listening at http://%s:%s", host, port)
  })

可以使用命令執(zhí)行:

node jsonserver.js

Curl命令驗證

可以通過curl命令驗證服務(wù)的有效性:

  1. GET操作

     $curl http://localhost:8081/todo/1
     $curl http://localhost:8081/todos
  2. DELETE操作

      $ curl -X "DELETE" http://localhost:8081/api/todo/1
  3. POST操作

    $curl -X POST  -H "Content-Type: application/json" -d '{"subject":"s4"}' http://localhost:8081/api/todo

前端HTML驗證

創(chuàng)建一個index.html文件,并放置到和jsonserver.js代碼同一目錄,代碼如下:

<a href='/todos'>todos</a>
<a href='/todo/1'>todo/1</a>
<button onclick='remove()'>remove 1</button>
<button onclick='create()'>create</button>
<script>
  function remove(){
    fetch (
      '/api/todo/1',
      {
        method: 'DELETE',
      }
    )
    .then( res => console.log(res.json()))
    .catch( err => cosole.error(err))
  }
  function create(){
    fetch (
      '/api/todo',
      {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({id: "4", subject: "s4"})
      }
    )
    .then( res => console.log(res.json()))
    .catch( err => cosole.error(err))
  }
</script>

可以提供創(chuàng)建,刪除,列表的測試,其中部分結(jié)果在console內(nèi)顯示。

說起來,JS訪問HTTP的庫真的是不少,這里 提到的庫都有9種。其中的fetch api使用起來非常的簡潔趁手,可是它不支持IE。如果你需要支持IE的話,使用Axios更好。這就是為什么Vuejs官方推薦Axios的原因吧:

The Fetch API is a powerful native API for these types of requests. You may have heard that one of the benefits of the Fetch API is that you don’t need to load an external resource in order to use it, which is true! Except… that it’s not fully supported yet, so you will still need to use a polyfill. There are also some gotchas when working with this API, which is why many prefer to use axios for now. This may very well change in the future though.

axios訪問方法

相比fetch,使用axios必須依賴于外部文件。為了方便,我們直接使用unpkg網(wǎng)站提供的庫文件。

axios的語法和fetch的大同小異,看著也是比較簡潔美觀的。以下代碼,把create和remove函數(shù)的內(nèi)部實現(xiàn)換掉,其他不變。

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<a href='/todos'>todos</a>
<a href='/todo/1'>todo/1</a>
<button onclick='remove()'>remove 1</button>
<button onclick='create()'>create</button>
<script>
  function remove(){
    axios ({
        url:'/api/todo/1',
        method: 'DELETE',
    })
    .then( res => console.log(res.json()))
    .catch( err => cosole.error(err))
  }
  function create(){
    axios ({
        method: 'POST',
        url:'/api/todo',
        headers: {
          'Content-Type': 'application/json'
        },
        data: JSON.stringify({subject: "s4"})
    })
    .then( res => console.log(res.json()))
    .catch( err => cosole.error(err))
  }
</script>

現(xiàn)在,后端也是可以跑起來了的。

整合:前端后端

建立cli-service到App Server的通道

每次執(zhí)行命令:npm run serve,都會啟動vue定制腳手架的服務(wù)器代碼,它會提供不少方便開發(fā)的特性。但是我希望一部分URL可以轉(zhuǎn)發(fā)到我自己的服務(wù)器內(nèi)。比如把所有的/api打頭的URL全部轉(zhuǎn)過來。只要在工程根目錄內(nèi)加入此文件vue.config.js,錄入如下內(nèi)容:

module.exports = {
  devServer: {
    proxy: {
      "/api": {
        target: "http://localhost:8181",
        secure: false
      }
    }
  }
};

我們自己的測試服務(wù)器在這里:

var http = require('http');
http.createServer(function (req, res) {
  res.write('Hello World!'); 
  res.end(); 
}).listen(8181);

我們的定制服務(wù)器,就可以監(jiān)聽8181的本地機(jī)器端口,等待客戶端的匹配的URL轉(zhuǎn)發(fā)過來,并轉(zhuǎn)發(fā)我們服務(wù)器的響應(yīng)代碼到客戶端。

但是正常開發(fā)過程中,是需要自己的服務(wù)器端代碼的,如何在利用Vue腳手架服務(wù)器的方便性的基礎(chǔ)上,加入自己的代碼呢。做法是另外做一個定制的服務(wù)器,然后讓vue定制腳手架的服務(wù)器轉(zhuǎn)發(fā)URL到此服務(wù)器。

為了測試的目的,我們把函數(shù)mounted修改為:

mounted(){
  var url  = '/api/1'
  axios ({
        url:url,
        method: 'get',
    })
    .then( res => {console.log(res.data)} )
    .catch( err => console.error(err))
}

即可看到瀏覽器console內(nèi)打印Hello World!。

整合前端到后端

我們已經(jīng)通過配置,要求cli-service轉(zhuǎn)移全部api打頭的URL到App Server。只要在工程根目錄內(nèi)加入此文件vue.config.js,錄入如下內(nèi)容:

module.exports = {
  devServer: {
    proxy: {
      "/api/*": {
        target: "http://localhost:8181/api",
        secure: false
      }
    }
  }
};

現(xiàn)在,我們可以修改前端的Axios使用代碼,分別替代前端代碼的數(shù)據(jù)裝入、數(shù)據(jù)刪除、數(shù)據(jù)添加的代碼,讓這些代碼可以支持網(wǎng)絡(luò)操作。為了避免網(wǎng)絡(luò)操作代碼和業(yè)務(wù)邏輯代碼混合在一起,我決定包裝三個網(wǎng)絡(luò)操作函數(shù),并把它們放置到src/store.js文件內(nèi):

import axios from 'axios'
function httpadd(subject,cb){
  axios ({
        method: 'POST',
        url:'/api/todo',
        headers:[{'Content-Type':'application/json'}],
        data: {subject:subject}
      })
      .then( res => cb(res.data))
      .catch( err => console.error(err))
}
function httpremove(id,cb){
  axios ({
        url:'/api/todo/'+id,
        method: 'delete',
      })
      .then( res => {
          cb()
      })
      .catch( err => console.error(err))
}
function httpreload(cb){
  axios ({
        url:'/api/todos',
        method: 'get',
      })
      .then( res => {
          cb(res.data)
      })
      .catch( err => console.error(err))
}

分別完成添加、刪除、查找的任務(wù),當(dāng)完成工作后,都會調(diào)用一個callback函數(shù),在此函數(shù)內(nèi),可以消費(fèi)訪問網(wǎng)絡(luò)后得到的響應(yīng)數(shù)據(jù)。

然后把文件內(nèi)src/store.js的mutations對象改成如下代碼:

 mutations: {
  add(state,subject){
    httpadd(subject,function(todo){
      state.todos.push(todo)
    })
  },
  remove(state,id){
    httpremove(id,function(){
      state.todos.splice(indexById(state.todos,id),1)  
    })
  },
  reload(state){
    httpreload(function(todos){
      // console.log(todos)
      state.todos = todos
    })
    // state.todos = defaultTodo
  }
},

最后,在TodoList內(nèi)加入一個新函數(shù),并通過mapActions引入src/store.js的load()函數(shù)到當(dāng)前對象內(nèi):

  methods:{
    ...mapActions([
      'remove','load'
    ])
  },
  mounted(){
    this.load()
  }

以便在啟動后調(diào)用this.load()裝入它。

整合:后端和數(shù)據(jù)庫

要完成后端到數(shù)據(jù)庫的整合,需要做如下的修改:

  1. 原本在后端App Server內(nèi)Todo數(shù)據(jù)數(shù)組,現(xiàn)在應(yīng)該從Mongodb獲得
  2. 原本在添加Todo對象的時候只是添加到AppServer對象內(nèi),現(xiàn)在需要同時寫入Mongodb
  3. 原本在刪除時只是從數(shù)組刪除,現(xiàn)在需要同時在Mongodb內(nèi)刪除

因此,現(xiàn)在我們需要添加三個函數(shù),分別做針對Mongodb的獲取清單、添加和刪除的工作:

var mongo = require('mongodb')
function insertDoc(subject,callback){
  const connectionString = 'mongodb://localhost:27017';
  (async () => {
      const client = await MongoClient.connect(connectionString,
          { useNewUrlParser: true });
      const dbo = client.db('todos');
      try {
         var res = await dbo.collection('todo').insertOne(
          {subject:subject})
         callback(undefined,res.insertedId)
      }
      catch(err){
        callback(err)
      }
      finally {
          client.close();
      }
  })().catch(err => console.error(err));
}
function deleteDoc(_id,callback){
  const MongoClient = mongo.MongoClient;
  const connectionString = 'mongodb://localhost:27017';
  (async () => {
      const client = await MongoClient.connect(connectionString,
          { useNewUrlParser: true });
      const dbo = client.db('todos');
      try {
              var myquery = {_id:new mongo.ObjectID(_id)}
              var r = await dbo.collection("todo").deleteMany(myquery)
        }
        catch(err){
        callback(err)
      }
      finally {
            client.close();
            callback()
        }
  })().catch(err => console.error(err));
}
function allDoc(callback){
  const MongoClient = mongo.MongoClient;
  const connectionString = 'mongodb://localhost:27017';
  (async () => {
      const client = await MongoClient.connect(connectionString,
          { useNewUrlParser: true });
      const dbo = client.db('todos');
      try {
             var r = await dbo.collection("todo").find().toArray()
             var ts = []
             for (var i = 0; i < r.length; i++) {
               ts.push({id:r[i]._id,subject:r[i].subject})
             }
             callback(undefined,ts)
        }
        catch(err){
        callback(err)
      }
      finally {
            client.close();
        }
  })().catch(err => console.error(err));
}

這三個函數(shù)的功能和使用方法如下:

  1. 函數(shù)allDoc會從Mongodb內(nèi)獲取全部todo集合,并通過callback傳遞這個集合給調(diào)用者函數(shù)。
  2. 函數(shù)deleteDoc會從Mongodb內(nèi)刪除指定id的todo條目,完成刪除后,通過callback通知調(diào)用者。
  3. 函數(shù)deleteDoc會向Mongodb內(nèi)添加一個todo條目,完成添加后,通過callback通知調(diào)用者,并傳遞新的todo對象給調(diào)用者。

這里的代碼本身并不復(fù)雜,但是因為涉及到如何訪問Mongodb,因此涉及到比較多的概念,這里不做具體的解釋,你可以先把它們用起來。如果完成了本教程后,希望對Mongodb的訪問做具體了解的話,可以查看后文附錄的“Mongodb快速參考”。

并且和App Server對應(yīng)的代碼接駁,把原來的路由代碼替換如下:

app.delete('/api/todo/:id', function (req, res) {
  var userkey = req.params.id
  deleteDoc(userkey,function(){
    todos.splice(indexById(userkey),1)
    res.end( JSON.stringify(todos));
  })
})
app.get('/api/todos', function (req, res) {
  allDoc(function(err,todos){
    res.end( JSON.stringify(todos));  
  })
})
app.post('/api/todo', function (req, res) {
  insertDoc(req.body.subject,function(err,_id){
    var obj ={id:_id,subject:req.body.subject}
  todos.push(obj)
  res.end(JSON.stringify(obj))
    rs()
  })
})

Mongodb快速參考

本文會把一個對象todo對象(有屬性{id,name})存儲到Mongodb,做查詢刪除的測試(Create Remove Delete = CRD)。這個測試包括使用Mongodb Shell,使用CallBack古典風(fēng)格的訪問代碼,以及使用Await/Async的現(xiàn)代風(fēng)格的代碼。完成這個這個驗證后,就可以掌握最初步的Mongodb了。

我使用的Nodejs是10.7 。操作系統(tǒng)環(huán)境為Mac OS X High Sierra。

準(zhǔn)備環(huán)境

安裝和運(yùn)行Mongodb Daemon

brew install mongodb
mongodb

訪問驗證

首先執(zhí)行Mongodb Shell:

mongo

輸入命令,查詢數(shù)據(jù)庫清單:

> show dbs
local           0.000GB

創(chuàng)建一個數(shù)據(jù)庫

use todos

(若database不存在,則會創(chuàng)建一個,此時若不做任何操作直接退出,則MongoDB會刪除該數(shù)據(jù)庫)

db.todos.insert({id:1,name:"reco"})
db.todos.insert({id:2,name:"rita"})

查詢 :

db.todos.find()

{ "_id" : ObjectId("5b727c0846b6c71a98d3af52"), "id" : 1, "name" : "reco" }
{ "_id" : ObjectId("5b727c7046b6c71a98d3af53"), "id" : 2, "name" : "reta" }

刪除記錄:

db.todo.remove({id:1})

刪除數(shù)據(jù)庫

db.todo.drop()

使用nodejs方式訪問Mongodb

使用nodejs執(zhí)行類似Shell對對象的CRD,代碼如下:

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/todos";
MongoClient.connect(url, function(err, db) {
  if (err) throw err;
  console.log("Database created!");
  var dbo = db.db("todos");
  // var myobj = { id: 1, name: "reco" };
  // dbo.collection("todo").insertOne(myobj, function(err, res) {
  //   if (err) throw err;
  //   console.log("1 document inserted");
  //   db.close();
  // });
   var myobj = [
    { id: 1, name: 'reco'},
    { id: 2, name: 'rita'},
  ];
  dbo.collection("todo").insertMany(myobj, function(err, res) {
    if (err) throw err;
    console.log("Number of documents inserted: " + res.insertedCount);
    dbo.collection("todo").find({}).toArray(function(err, result) {
      if (err) throw err;
      console.log(result);
        var myquery = { id: 1 };
      dbo.collection("todo").deleteMany(myquery, function(err, obj) {
        if (err) throw err;
        console.log("document deleted");
        db.close();
      });
    });    
  });
})

代碼非常簡單,無需更多解釋。此代碼使用了mongodb模塊,需要首先安裝:

npm init -y
npm i mongodb --save

然后使用node index.js運(yùn)行即可看到效果:

Database created!
Number of documents inserted: 2
[ { _id: 5b72ab9e3245f169ef5f43d2, id: 1, name: 'reco' },
  { _id: 5b72ab9e3245f169ef5f43d3, id: 2, name: 'rita' } ]
document deleted

利用高級異步特性

使用Await/Async特性,可以有效的減少代碼中的回調(diào)地獄現(xiàn)象。同樣的功能,可以使用這樣的代碼:

const MongoClient = require('mongodb').MongoClient;
const connectionString = 'mongodb://localhost:27017';
(async () => {
    const client = await MongoClient.connect(connectionString,
        { useNewUrlParser: true });
    const dbo = client.db('todos');
    try {
       var res = await dbo.collection('todo').insertMany(
        [{id:1,name:"reco"}, {id:2,name:"rita"}]);
       console.log("Number of documents inserted: " + res.insertedCount);
       var r = await dbo.collection("todo").find().toArray()
       console.log(r);
       var myquery = { id: 1 };
     var r = await dbo.collection("todo").deleteMany(myquery)
     console.log("document deleted");
    }
    finally {
        client.close();
    }
})().catch(err => console.error(err));

執(zhí)行此代碼,輸出如下:

Number of documents inserted: 2
[ { _id: 5b72ae8a1c674a6ac1c5aa6e, id: 1, name: 'reco' },
  { _id: 5b72ae8a1c674a6ac1c5aa6f, id: 2, name: 'rita' } ]
document deleted

以上就是直擊JavaScript全棧教程的詳細(xì)內(nèi)容,更多請關(guān)注億速云其它相關(guān)文章!
向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI