[vue]與signalr實現即時同步資料

介紹vue.js如何與signalr搭配,實現即時同步資料

前言

之前有寫過用angularjs與signalr的方式,而這篇則是紀錄vue.js的方式,然後簡單實現signalr的方式。

.net部份

我們先來看一下.net如何實現吧,首先請自行建立一個全新的空白專案,接著打開nuget安裝signalr

建著新增owin cors的部份


在根目錄新增Startup.cs

    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.UseCors(CorsOptions.AllowAll);
            app.MapSignalR(new HubConfiguration { EnableJSONP = true });
        }
    }

新增一個MessageModel,來傳遞消息內容的物件

    public class MessageModel
    {
        public string Message { get; set; }

        
        /// <summary>
        /// 設立static以保存內容
        /// </summary>
        public static List<MessageModel> Messages { get; set; } = new List<MessageModel>
        {
            new MessageModel {Message="預設的內容" }
        };
        
        public static void Add(string message)
        {
            Messages.Add(new MessageModel { Message = message });
        }
    }

新增一個Hubs的資料夾,此資料夾主要放一些signalr的物件,然後新增一個DefaultHub.cs

    public class DefaultHub:Hub
    {
        public void Get()
        {
            Clients.All.Get(MessageModel.Messages);
        }
    }

在預設的ValuesController.cs裡面,改成如下的程式碼

    public class ValuesController : ApiController
    {
        //建立一個對應DefaultHub的介面
        IHubContext hub = GlobalHost.ConnectionManager.GetHubContext<DefaultHub>();
        public IHttpActionResult Get(string message)
        {
            MessageModel.Add(message);
            //新增完成去觸發signlar,以通知client更新
            hub.Clients.All.Get(MessageModel.Messages);
            return Ok();
        }
    }

vue.js部份

因為signalr預設需要安裝jquery,所以我們就先安裝需要的package

npm i jquery signalr --S

接下來請特別注意,如果我們直接在component使用import jquery and signalr的話,會遇到如下的錯誤

"Uncaught Error: jQuery was not found. Please ensure jQuery is referenced before the SignalR client JavaScript file."

所以我們不如就直接在webpack動點手腳,把jquery定義為全局的方式來使用,首先打開webpack.base.conf.js,並改成如下

var path = require('path')
var utils = require('./utils')
var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')
const webpack = require('webpack')

function resolve(dir) {
  return path.join(__dirname, '..', dir)
}

function getService() {
  if (process.env.NODE_ENV === 'test') {
    return resolve('src/services/fake')
  } else {
    return resolve('src/services/real')
  }
}

module.exports = {
  entry: {
    app: process.env.NODE_ENV === 'development' ? './src/main.js' : './src/main.build.js'
  },
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      "pages": resolve('src/pages'),
      "components": resolve('src/components'),
      "services": getService(),
      "utilitys": resolve('src/utilitys'),
      "images": resolve('src/assets/images')
    }
  },
  // 下面這段是為jquery註冊為全局的
  plugins: [
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
      'window.jQuery': 'jquery',
      'root.jQuery': 'jquery'
    }),
  ],
  module: {
    rules: [
      {
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        enforce: 'pre',
        include: [resolve('src'), resolve('test')],
        options: {
          formatter: require('eslint-friendly-formatter')
        }
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test')]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[ext]')
        }
      }
    ]
  }
}

接著在起始的main.js加入了signalr

import 'signalr'

再來看一下我自行新增的Test.vue的部份

<template>
  <div>
    <ul>
      <li v-for="item in messages">
        {{item.Message}}
      </li>
    </ul>
  </div>
</template>

<script>
import 'signalr'

export default {
  name: 'test',
  data () {
    return {
      messages: []
    }
  },
  methods: {
    get () {
      //下面對應到網址的部份
      let hub = $.hubConnection('http://localhost:57128')
      //下面對應了.net的DefaultHub
      let proxy = hub.createHubProxy('DefaultHub')
      proxy.on('Get', data => this.messages = data)
      //一開始就先去呼叫Get,以確保畫面一開始就有預設的資料
      hub.start().done(() => proxy.invoke('Get'))
    }
  },
  created () {
    this.get()
  }
}
</script>

<style>

</style>

最後來看一下結果吧

結論

有了signalr要實現即時的網頁互動並不困難,但是為了signalr我們還要引入jquery,不過目前來說這也是一個不得不的選項了,如果有任何更好的做法再請提供建議和指教囉。