Vue.js——基於$.ajax實現資料的跨域增刪查改

來源:互聯網
上載者:User

標籤:

概述

之前我們學習了Vue.js的一些基礎知識,以及如何開發一個組件,然而那些樣本的資料都是local的。
在實際的應用中,幾乎90%的資料是來源於服務端的,前端和服務端之間的資料互動一般是通過ajax請求來完成的。

說起ajax請求,大家第一時間會想到jQuery。除了擁有強大的DOM處理能力,jQuery提供了較豐富的ajax處理方法,它不僅支援基於XMLHttpRequest的ajax請求,也能處理跨域的JSONP請求。

之前有讀者問我,Vue.js能結合其他庫一起用嗎?答案當然是肯定的,Vue.js和jQuery一起使用基本沒有衝突,盡可放心大膽地使用。

本文的主要內容如下:

  • 同源策略和跨域概念
  • 跨域資源共用
  • JSONP概念
  • REST Web Services
  • 基於$.ajax實現跨域GET請求
  • 基於$.ajax實現JSONP請求
  • 基於$.ajax實現跨域POST請求
  • 基於$.ajax實現跨域PUT請求
  • 基於$.ajax實現跨域DELETE請求

本文的服務端程式和用戶端程式是部署在不同伺服器上的,本文所有樣本請求都是跨域的。
原始碼已放到GitHub,如果您覺得本篇內容不錯,請點個贊,或在GitHub上加個星星!

CORS GET JSONP GET CORS POST CORS PUT 完整CURD樣本 GitHub Source 基礎概念

在進入本文正題之前,我們需要先瞭解一些基礎概念(如果你已經對這些基礎有所瞭解,可跳過此段落)。

同源策略和跨域概念

同源策略(Same-orgin policy)限制了一個源(orgin)中載入指令碼或指令碼與來自其他源(orgin)中資源的互動方式。
如果兩個頁面擁有相同的協議(protocol),連接埠(port)和主機(host),那麼這兩個頁面就屬於同一個源(orgin)。

同源之外的請求都可以稱之為跨域請求。
下表給出了相對http://store.company.com/dir/page.html同源檢測的樣本:

URL 結果 原因
http://store.company.com/dir2/other.html 成功  
http://store.company.com/dir/inner/another.html 成功  
https://store.company.com/secure.html 失敗 協議不同
http://store.company.com:81/dir/etc.html 失敗 連接埠不同
http://news.company.com/dir/other.html 失敗 主機名稱不同

我們可以簡單粗暴地理解為同一網站下的資源相互訪問都是同源的,跨網站的資源訪問都是跨域的。 跨域資源共用

跨域資源共用(CORS)是一份瀏覽器技術的規範,提供了Web伺服器從不同網域傳來沙箱指令碼的方法,以避開瀏覽器的同源策略,是JSONP模式的現代版。與JSONP不同,CORS除了支援GET方法以外,還支援其他HTTP方法。用CORS可以讓網頁設計師用一般的XMLHTTPRequest,這種方式的錯誤處理比JSONP要來的好。另一方面,JSONP可以在不支援CORS的老舊瀏覽器上運作,現代的瀏覽器都支援CORS。

在網頁http://caniuse.com/#feat=cors上列出了主流瀏覽器對CORS的支援情況,包含PC端和移動端的瀏覽器。

JSONP概念

由於同源策略,一般來說不允許JavaScript跨域訪問其他伺服器的頁面對象,但是HTML的<script>元素是一個例外。利用 <script> 元素的這個開放策略,網頁可以得到從其他來源動態產生的 JSON資料,而這種使用模式就是所謂的 JSONP。用 JSONP 抓到的資料並不是 JSON,而是任意的JavaScript,用 JavaScript 直譯器執行而不是用 JSON 解析器解析。

REST Web Services簡介

HTTP協議是Web的標準之一,HTTP協議包含一些標準的操作方法,例如:GET, POST, PUT, Delete等,用這些方法能夠實現對Web資源的CURD操作,下表列出了這些方法的操作定義。

HTTP方法 資源處理 說明
GET 讀取資源(Read) 擷取被請求URI(Request-URI)指定的資訊(以實體的格式)。
POST 建立資源(Create) 在伺服器上建立一個新的資源,並返回新資源的URI。
PUT 更新資源(Update) 指定URI資源存在則更新資源,指定URI資源不存在則建立一個新資源。
DELETE 刪除資源(Delete) 刪除請求URI指定的資源。

在REST應用中,預設通過HTTP協議,並且使用GET、POST、PUT和DELETE方法對資源進行操作,這樣的設計風格和Web標準完全契合。

REST的最佳應用情境是公開服務,使用HTTP並遵循REST原則的Web服務,我們稱之為RESTful Web Service。RESTful Web Service從以下三個方面進行資源定義:

  • 直觀簡短的資源地址:URI,比如:http://example.com/resources/
  • 傳輸的資源:Web Service接受與返回的互連網媒體類型,比如JSON,XML等
  • 對資源的操作:Web Service在該資源上所支援的一系列要求方法(比如:GET,POST,PUT或Delete)

展示了RESTful Web Service的執行流程

本文的服務端程式是基於ASP.NET Web API構建的。
在瞭解了這些基礎知識後,我們就分別來構建服務端程式和用戶端程式吧。

服務端環境準備

如果您是前端開發人員,並且未接觸過ASP.NET Web API,可以跳過此段落。

建立Web API應用程式

添加Model, Controller

初始化資料庫

分別執行以下3個命令:

讓Web API以CamelCase輸出JSON

C#偏向於PascalCase的命名規範,而JavaScript則偏向於camelCase的命名規範,為了讓JavaScript接收到的JSON資料是camelCase的,在Global.asax檔案中添加以下幾行代碼:

var formatters = GlobalConfiguration.Configuration.Formatters;var jsonFormatter = formatters.JsonFormatter;var settings = jsonFormatter.SerializerSettings;settings.Formatting = Formatting.Indented;settings.ContractResolver = new CamelCasePropertyNamesContractResolver();

可以在以下地址訪問構建好的Web API:

http://211.149.193.19:8080/Help

訪問Customers資料:

http://211.149.193.19:8080/api/Customers

建立組件和AjaxHelper

本文的樣本仍然是表格組件的CURD,只不過這次咱們的資料是從服務端擷取的。
在實現資料的CURD之前,我們先準備好一些組件和Ajax協助方法。

建立和註冊simple-grid組件

simple-grid組件用於綁定和顯示資料

<template id="grid-template"><table><thead><tr><th v-for="col in columns">{{ col | capitalize}}</th></tr></thead><tbody><tr v-for="(index,entry) in dataList"><td v-for="col in columns">{{entry[col]}}</td></tr></tbody></table></template><script src="js/vue.js"></script><script>Vue.component(‘simple-grid‘, {template: ‘#grid-template‘,props: [‘dataList‘, ‘columns‘]})</script>
建立和註冊modal-dialog組件

資料的建立和編輯將使用模態對話方塊,modal-dialog組件的作用就在於此。

<template id="dialog-template"><div class="dialogs"><div class="dialog" v-bind:class="{ ‘dialog-active‘: show }"><div class="dialog-content"><div class="close rotate"><span class="iconfont icon-close" @click="close"></span></div><slot name="header"></slot><slot name="body"></slot><slot name="footer"></slot></div></div><div class="dialog-overlay"></div></div></template><script>Vue.component(‘modal-dialog‘, {template: ‘#dialog-template‘,props: [‘show‘],methods: {close: function() {this.show = false}}})</script>
AjaxHelper

基於$.ajax聲明一個簡單的AjaxHelper構造器,AjaxHelper構造器的原型對象包含5個方法,分別用於處理GET, POST, PUT, DELETE和JSONP請求。

function AjaxHelper() {this.ajax = function(url, type, dataType, data, callback) {$.ajax({url: url,type: type,dataType: dataType,data: data,success: callback,error: function(xhr, errorType, error) {alert(‘Ajax request error, errorType: ‘ + errorType +  ‘, error: ‘ + error)}})}}AjaxHelper.prototype.get = function(url, data, callback) {this.ajax(url, ‘GET‘, ‘json‘, data, callback)}AjaxHelper.prototype.post = function(url, data, callback) {this.ajax(url, ‘POST‘, ‘json‘, data, callback)}AjaxHelper.prototype.put = function(url, data, callback) {this.ajax(url, ‘PUT‘, ‘json‘, data, callback)}AjaxHelper.prototype.delete = function(url, data, callback) {this.ajax(url, ‘DELETE‘, ‘json‘, data, callback)}AjaxHelper.prototype.jsonp = function(url, data, callback) {this.ajax(url, ‘GET‘, ‘jsonp‘, data, callback)}AjaxHelper.prototype.constructor = AjaxHelper
實現GET請求發送get請求

var ajaxHelper = new AjaxHelper()var demo = new Vue({el: ‘#app‘,data: {gridColumns: [‘customerId‘, ‘companyName‘, ‘contactName‘, ‘phone‘],gridData: [],apiUrl: ‘http://localhost:15341/api/Customers‘},ready: function() {this.getCustomers()},methods: {getCustomers: function() {// 定義vm變數,讓它指向this,this是當前的Vue執行個體var vm = this,callback = function(data) {// 由於函數的範圍,這裡不能用thisvm.$set(‘gridData‘, data)}ajaxHelper.get(vm.apiUrl, null, callback)}}})

由於用戶端和服務端Web API是分屬於不同網站的,它們是不同的源,這構成了跨域請求。
這時請求是失敗的,瀏覽器會提示一個錯誤:
No ‘Access-Control-Allow-Origin‘ header is present on the requested resource. Origin ‘http://127.0.0.1::8020‘ is therefore not allowed access.         

跨域解決方案

現在碰到了請求跨域的問題,結合前面講的一些概念,我們大致可以猜到解決跨域請求的兩種方式:

  • 在服務端啟用CORS。
  • 讓無服務端擁有處理JSONP的能力。

這兩種跨域解決方案的區別是什麼呢?

  • JSONP只支援GET請求;CORS則支援GET、POST、PUT、DELETE等標準的HTTP方法
  • 使用JSONP時,服務端要處理用戶端請求的callback參數("callback"這個名稱是可以指定的);而使用CORS則不需要提供這樣的處理。
  • JSONP從服務端擷取到的是script檔案;CORS則是一段XML或JSON或其他格式的資料
  • JSONP支援IE8, IE9複古的瀏覽器;CORS則支援現代主流的瀏覽器

選擇JSONP還是CORS?除了極少數的情況,我們都應當選擇CORS作為最佳的跨域解決方案。

啟用CORS

在Nuget Package Manager Console下輸入以下命令:

Install-Package Microsoft.AspNet.WebApi.Cors

在CustomersController上添加EnableCors特性,origins、headers和methods都設為*

[EnableCors(origins: "*", headers: "*", methods: "*")]public class CustomersController : ApiController{ }

Web API的各項配置說明及原理,下面這個地址已經講得很清楚了:
http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api

重新整理頁面,現在資料可以正常顯示了。

View Demo

在Chrome開發工具的Network選項下,可以看到Response Header的Content-Type是application/json。

文章開頭也說了,CORS的跨域方式支援現代的主流瀏覽器,但是不支援IE9及以下這些古典的瀏覽器。

如果我們偏要在IE9瀏覽器中(Vue.js不支援IE8及以下的瀏覽器,所以不考慮IE 6,7,8)實現跨域訪問,該怎麼做呢?
答案是使用JSONP請求。

實現JSONP請求

將getCustomers方法的get請求變更為jsonp請求:

getCustomers: function() {// 定義vm變數,讓它指向this,this是當前的Vue執行個體var vm = this,callback = function(data) {// 由於函數的範圍,這裡不能用thisvm.$set(‘gridData‘, data)}ajaxHelper.jsonp(vm.apiUrl, null, callback)}

重新整理頁面,請求雖然成功了,但畫面沒有顯示資料,並且彈出了請求錯誤的訊息。

讓WebAPI支援JSONP請求

在Nuget Package Manager中輸入以下命令:

Install-Package WebApiContrib.Formatting.Jsonp

然後在Global.asax的Application_Start()方法中註冊JsonpMediaTypeFormatter:

// 註冊JsonpMediaTypeFormatter,讓WebAPI能夠處理JSONP請求config.Formatters.Insert(0, new JsonpMediaTypeFormatter(jsonFormatter));

重新整理頁面,使用JSONP請求也能夠正常顯示資料了。

注意:使用JSONP時,服務端返回的不再是一段JSON了,而是一個script指令碼。

在IE9下查看該頁面,simple-grid組件也能正常顯示資料:

View Demo

實現POST請求1. 建立表單對話方塊

添加一個Create按鈕,然後使用modal-dialog組件建立一個表單對話方塊:

<div id="app"><!--...已省略--><div class="container"><div class="form-group"><button @click="this.show = true">Create</button></div></div><modal-dialog v-bind:show.sync="show"><header class="dialog-header" slot="header"><h1 class="dialog-title">Create New Customer</h1></header><div class="dialog-body" slot="body"><div v-show="item.customerId" class="form-group"><label>Customer Id</label><input type="text" v-model="item.customerId" disabled="disabled" /></div><div class="form-group"><label>Company Name</label><input type="text" v-model="item.companyName" /></div><div class="form-group"><label>Contact Name</label><input type="text" v-model="item.contactName" /></div><div class="form-group"><label>Phone</label><input type="text" v-model="item.phone" /></div><div class="form-group"><label></label><button @click="createCustomer">Save</button></div></div></modal-dialog></div>

注意:在建立Customer時,由於item.customerId為空白,所以item.customerId關聯的表單不會顯示;在修改Customer時,item.customerId關聯的表單會顯示出來。另外,item.customerId是不可編輯的。

2. 修改Vue執行個體
var demo = new Vue({el: ‘#app‘,data: {show: false,gridColumns: [{name: ‘customerId‘,isKey: true}, {name: ‘companyName‘}, {name: ‘contactName‘}, {name: ‘phone‘}],gridData: [],apiUrl: ‘http://localhost:15341/api/Customers‘,item: {}},ready: function() {this.getCustomers()},methods: {// ... 已省略createCustomer: function() {var vm = this,callback = function(data) {vm.$set(‘item‘, {})// 添加成功後,重新載入頁面資料vm.getCustomers()}// 將vm.item直接POST到服務端ajaxHelper.post(vm.apiUrl, vm.item, callback)this.show = false}}})

修改Vue執行個體的data選項:

  • 添加show屬性:用於顯示或隱藏表單對話方塊
  • 修改gridColumns屬性:列包含兩個屬性,name表示列名稱,isKey表示列是否為主鍵列
  • 添加item屬性:用於新增Customer或修改Customer

添加createCustomer方法:

createCustomer: function() {var vm = this,callback = function(data) {vm.$set(‘item‘, {})// 添加成功後,重新載入頁面資料vm.getCustomers()}// 將vm.item直接POST到服務端ajaxHelper.post(vm.apiUrl, vm.item, callback)this.show = false}

View Demo

實現PUT請求1. 修改sample-grid的template

給主鍵列添加連結,綁定click事件,事件指向loadEntry方法,loadEntry方法用於載入當前選中的資料。

<template id="grid-template"><table><thead><tr><th v-for="col in columns">{{ col.name | capitalize}}</th></tr></thead><tbody><tr v-for="(index,entry) in dataList"><td v-for="col in columns"><span v-if="col.isKey"><a href="javascript:void(0)" @click="loadEntry(entry[col.name])">{{ entry[col.name] }}</a></span><span v-else>{{ entry[col.name] }}</span></td></tr></tbody></table></template>

2. 修改simple-grid的methods選項

在simple-grid的methods選項下,添加一個loadEntry方法,該方法調用$.dispatch向父組件派發事件load-entry,並將key作為事件的入參,load-entry是綁定在父組件的事件名稱。

Vue.component(‘simple-grid‘, {template: ‘#grid-template‘,props: [‘dataList‘, ‘columns‘],methods: {loadEntry: function(key) {this.$dispatch(‘load-entry‘, key)}}})
3. 修改Vue執行個體的HTML

在Vue執行個體的simple-grid標籤上綁定自訂事件load-entryload-entry事件指向loadCustomer方法,loadCustomer方法用於載入選中的Customer資料。

<div id="app"><!--...已省略--><div class="container"><simple-grid :data-list="gridData" :columns="gridColumns" v-on:load-entry="loadCustomer"></simple-grid></div><!--...已省略--></div>

我們應將2和3結合起來看,闡述了從點擊simple-grid資料的連結開始,到最終開啟對話方塊的完整過程:

注意:load-entry是Vue執行個體的事件,而不是simple-grid組件的事件,儘管load-entry是寫在simple-grid標籤上的。詳情請參考上一篇文章的編譯範圍

由於在建立模式和修改模式下標題內容是不同的,因此需要修改modal-dialog的slot="header"部分。
根據item.customerId是否有值確定修改模式和建立模式,修改模式下顯示"Edit Customer - xxx",建立模式下顯示"Create New Customer"

<modal-dialog v-bind:show.sync="show"><header class="dialog-header" slot="header"><h1 class="dialog-title">{{ item.customerId ? ‘Edit Customer - ‘ + item.contactName : ‘Create New Customer‘ }}</h1></header></modal-dialog>
4. 修改Vue執行個體

為data選項添加title屬性,用於顯示對話方塊的標題。

var demo = new Vue({el: ‘#app‘,data: {// ...已省略title: ‘‘// ...已省略}// ...已省略})

然後追加3個方法:loadCustomer, saveCustomerupdateCustomer

loadCustomer: function(customerId) {var vm = thisvm.gridData.forEach(function(item) {if (item.customerId === customerId) {// 使用$.set設定itemvm.$set(‘item‘, item)return}}),vm.$set(‘show‘, true)},saveCustomer: function() {this.item.customerId ? this.updateCustomer() : this.createCustomer()this.show = false},updateCustomer: function() {// 定義vm變數,讓它指向this,this是當前的Vue執行個體var vm = this,callback = function(data) {// 更新成功後,重新載入頁面資料vm.getCustomers()}// 將vm.item直接PUT到服務端ajaxHelper.put(vm.apiUrl + ‘/‘ + vm.item.customerId, vm.item, callback)}

saveCustomer方法根據 item.customerId是否有值確定修改模式和建立模式,如果是建立模式則調用 createCustomer方法,如果是修改模式則調用 updateCustomer方法。

另外,需要將Save按鈕的click事件綁定到saveCustomer方法。

<div class="dialog-body" slot="body"><!--...已省略--><div class="form-group"><label></label><button @click="saveCustomer">Save</button></div><!--...已省略--></div>

5. 添加$watch

使用$watch跟蹤data選項show屬性的變化,每當關閉對話方塊時就重設item。

demo.$watch(‘show‘, function(newVal, oldVal){if(!newVal){this.item = {}}})

為什麼要這麼做呢?因為每次開啟對話方塊,不知道是以建立模式還是修改模式開啟的,如果不重設item,倘若先以修改模式開啟對話方塊,再以建立模式開啟對話方塊,建立模式的對話方塊將會顯示上次開啟的資料。

View Demo

實現DELETE請求1. 修改simple-grid組件

在methods選項中添加方法deleteEntry

deleteEntry: function(entry) {this.$dispatch(‘delete-entry‘, entry)}

調用$.dispatch向父組件派發事件delete-entry

2. 修改Vue執行個體的HTML

在simple-grid標籤上綁定自訂事件delete-entry,該事件指向deleteCustomer方法。

<div id="app"><!--...已省略--><div class="container"><simple-grid :data-list="gridData" :columns="gridColumns" v-on:load-entry="loadCustomer" v-on:delete-entry="deleteCustomer"></simple-grid></div><!--...已省略--></div>

View Demo

總結

本篇介紹了同源策略、跨域、CORS和REST等概念,並結合Vue.js和$.ajax實現了一個簡單的CURD跨域樣本。
下一篇,我們將使用Vue的外掛程式vue-resource來完成這些工作。

Vue.js——基於$.ajax實現資料的跨域增刪查改

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.