陳鍾誠

Version 1.0

採用 AJAX 方式的通訊範例

和上面使用表單的 post/get 方式類似,我們可以透過 JavaScript 來送出訊息給 Server 端並接收回應,這種《傳送/接收》方式,稱為 AJAX (Asynchronous JavaScript and XML, 非同步的JavaScript與XML技術)。

前端工程師常用兩種方法來《傳送/接收》 AJAX post/get 訊息,一種是使用 jQuery 套件,另一種是不依賴 jQuery,而是直接採用原生的瀏覽器 JavaScript API (被戲稱為 Vanilla.js) 。

使用 jQuery 的 post 程式碼:

<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
$.ajax({
  type: 'POST',
  url: "path/to/api",
  data: "banana=yellow",
  success: function (data) {
    alert("Success: " + data);
  },
});
</script>

使用原生的 JavaScript API 的 post 程式碼:

var r = new XMLHttpRequest();
r.open("POST", "path/to/api", true);
r.onreadystatechange = function () {
  if (r.readyState != 4 || r.status != 200) return;
  alert("Success: " + r.responseText);
};
r.send("banana=yellow");

如果您想了解完整的 AJAX 範例,可以參考 f6 當中的程式碼

重要程式碼:

前端: https://github.com/ccckmit/f6/blob/master/web/ex5-blogInAjax/server.js

var Koa = require('koa')
var Router = require('koa-router')
var logger = require('koa-logger')
var koaStatic = require('koa-static')
var bodyParser = require('koa-bodyparser')

var app = new Koa()
var router = new Router()

app.use(bodyParser())

var posts = []

app.use(logger())

router
  .get('/', listPosts)
  .get('/post/:id', getPost)
  .post('/post', createPost)

async function listPosts (ctx) {
  ctx.body = JSON.stringify(posts)
}

async function getPost (ctx) {
  var id = parseInt(ctx.params.id)
  console.log('getpost: id=%d posts=%j', id, posts)
  var post = posts[id]
  if (!post) ctx.throw(404, 'invalid post id')
  ctx.body = await JSON.stringify(post)
}

async function createPost (ctx) {
  console.log('createPost:rawBody=%s', ctx.request.rawBody)
  console.log('createPost:body=%j', ctx.request.body)
  var post = ctx.request.body
  var id = posts.push(post) - 1
  post.created_at = new Date()
  post.id = id
  ctx.body = JSON.stringify(post)
}

app.use(router.routes()).use(koaStatic('../')).listen(3000)

client : https://github.com/ccckmit/f6/blob/master/web/ex5-blogInAjax/main.js

var content, title
// var posts = []

async function main () {
  f6.route(/^$/, list)
    .route(/^post\/new/, add)
    .route(/^post\/(\w+?)/, show)
    .route(/^post/, create)
  await f6.onload(init)
}

function init() {
  title = f6.one('title')
  content = f6.one('#content')
}

async function add () {
  title.innerHTML = 'New Post'
  content.innerHTML = `
  <h1>New Post</h1>
  <p>Create a new post.</p>
  <form>
    <p><input id="addTitle" type="text" placeholder="Title" name="title"></p>
    <p><textarea id="addBody" placeholder="Contents" name="body"></textarea></p>
    <p><input type="button" value="Create" onclick="create()"></p>
  </form>
  `
}

async function create () {
  var post = {
    title: f6.one('#addTitle').value,
    body: f6.one('#addBody').value,
    created_at: new Date()
  }
  console.log(`create:post=${JSON.stringify(post)}`)
  await f6.ojax({method: 'POST', url: '/post'}, post)
//  posts.push(post)
  f6.go('') // list #
}

async function show (m) {
  var id = parseInt(m[1])
  var post = await f6.ojax({method: 'GET', url: `/post/${id}`})
//  var post = posts[id]
  content.innerHTML = `
  <h1>${post.title}</h1>
  <p>${post.body}</p>
  `
}

async function list () {
  var posts = await f6.ojax({method: 'GET', url: '/'})
  title.innerHTML = 'Posts'
  content.innerHTML =
  `<h1>Posts</h1>
  <p>You have <strong>${posts.length}</strong> posts!</p>
  <p><a href="#post/new">Create a Post</a></p>
  <ul id="posts">
    ${posts.map(post => `
      <li>
        <h2>${post.title}</h2>
        <p><a href="#post/${post.id}">Read post</a></p>
      </li>
    `).join('\n')}
  </ul>
  `
}

main()