CoreCRM 開發實錄 —— 前后端分離的重構


雖然2月初就回來了,可 CoreCRM 一直到5月才開始恢復開發,期間是各種生活中的意外和不方便。

1. 為什么要重構

首先是一件很值得高興的事情:CoreCRM 有了第一位 contributor!Larry 是我原來的一位實習生,現在在某公司做前端開發。因為 Larry 的加入,我就不再是一個人戰斗了。當然,也就得考慮怎么進行合作的事情了。

年前的開發計划是:使用 Bootstrap 按照悟空CRM的樣子先弄出一個可以用的版本來。然后再使用一些比較好的后台前端框架來代替 Bootstrap。在和 Larry 討論之后,他覺得 Bootstrap 已經有點跟不上時代,以后擴展 UI 都會有一些困難。加上 Bootstrap 是 jQuery 作為交互基礎的,和 VueJS 這樣的框架也是不太配合。正好 Larry 的公司正在使用螞蟻金服出品的 Ant Design 開發后台程序,他感覺這個框架設計的不錯:組件豐富、設計合理、社區活躍、文檔豐富(還是中文)。Ant Design 基於 React Component,也是當前非常流行的前端框架,經過多年的發展,據說其組件的豐富程度已經與 jQuery 不遑多讓。

鑒於之前我使用 VueJS 做全頁渲染的經驗,我認為 React 這東西,如果不做 Server-Side Rendering(SSR),用戶體驗不會太好。如何集成 SSR,其實有兩種方案:

  1. 完全使用 nodejs 來做前端服務器,ASP.NET Core 做 API 服務器
  2. 集成 node 到 ASP.NET Core 里,通過 ASP.NET Core 提供一些 Web 的服務

去年我也曾經用了一周的時間去研究幾個 ASP.NET Core 的 React Server-side Rendering 方案,可一個人的精力畢竟有限,要同時使用兩種語言開發,腦子的轉換效率是一個問題。最后我放棄了 React,轉而使用 ASP.NET Core 的 Razor 來做頁面渲染了。只對其中一些動態的部分做了 VueJS 組件。不過這次情況不一樣了,有 Larry 做前端的開發,我可以把更多的精力放到后面的API 開發和架構的優化上。而且,前后分離之后,還可以在對方工作滯后的情況下繼續開發。所以我決定對整個項目進行重構。

2. 重構的嘗試

近些年前端技術發展迅速,各種 hot reload 橫行,在開發的時候要方便和高效的多。那么應該怎么來實現 SSR 呢?我進行了三次嘗試:

2.1 Microsoft.AspNetCore.SpaServices

做為 ASP.NET Core 團隊的作品,感覺上是比 Facebook 做的 ReactJS.NET 更好用一些。特別是和 ASP.NET Core 的互通性上,應該有一些優勢。不過,ReactJS.NET 的 SSR 已經內置,寫起來要容易一些。

一開始,我嘗試使用 aspnetcore-spa 這個 yeoman generator,可這貨居然還是使用的 typescript 做為主語言。雖然我之前也學了一點 typescript,但與別人合作的時候,就不能只考慮自己的技術棧了。為了不增加 Larry 的學習成本,以使得項目能夠盡快進入開發,我決定還是自己搞一個基於 es6 的 ClientApp。方法當然也很簡單:用 dva-cli 創建一下就 OK 了。

在完成了 ClientApp 的創建之后,需要添加一個 boot-server.js 來實現 SSR:

var { match } = require('react-router');
var { createServerRenderer } = require('aspnet-prerendering');

module.exports = createServerRenderer(function(params) {
    return new Promise(function (resolve, reject) {
        var re = /^\/([^\/]*)(/[^\/]*)?/;
        var matched = params.location.path.match(re);
        var controller = matched[1];
        var codeFile = './dist/' + controller;
        var App = require(codeFile); // eslint-disable-line

        var path = matched[2] === '' ? '/' : matched[2]; // this line is buggy.
        match({
            routes: App.routes,
            location: path
        }, function (err, redirectLocation, renderProps) {
            if (err) throw new Error("Route match failed: " + err);

            if (redirectLocation) new Error("I don't know how to redirect.");

            var initialState = {};
            resolve({ html: App.renderHTML(initialState, renderProps)});
        })
    });
});

params 里包含了一些從 Razor 傳進來的數據,比如訪問的路由、初始化數據等。這里本來應該直接使用 params.location.path來匹配路由,進行渲染的,可是我並沒有使用完全的 SPA (Single Page Application) 架構,而是有所分離,所以就需要使用正則表達式分離 controller 和 action,然后再進行頁內的匹配。現在 repo 里的代碼只是實現了單頁的測試載入,還沒有正式的使用起來。

2.2 koa + Web API Server

本來以為上面這個方案就已經可以了。前端使用了 dva + antd + roadhog,可以直接運行一個 webpack-dev-server 直接進行開發。然后我再轉到 Razor 里做為一個 Controller 的 View。不過,Larry 希望能完全脫離 ASP.NET Core 運行前端代碼。我也考慮了使用
SpaServices 可能會有一些限制,比如:需要處理路由、不能使用 ES6,同時運行的時候也還是需要安裝 Node,其實和一個獨立的前端 Server 並沒有什么區別。所以我又嘗試把前端的 Server 完全分離出來。

這一步可能做的有點太激進了,我嘗試使用 Web API 的模板重新創建了 CoreCRM 這個 project。結果就悲劇了:Web API 是非常輕量的框架,里面什么也沒有。加上如果要分離前后端 Server,比較好的權限驗證方案是使用 JSON-Web-Token,不然還需要在兩個 Server 之前同步 session,也是比較麻煩的事情。而搞一套 JWT 的驗證機制,也不是很容易。已經有的解決方案不是太簡單,就是太復雜……

回歸 SpaServices

上面這些困難加起來,讓我覺得這里面的學習成本現在不可接受。所以就先放棄了這個方案。在經過一點設計和開發之后,我發現這其實和使用 koa 並沒有太大的差別。問題總是可以通過一些服務間的交互來解決的。只是現在這個節點,使用 SpaServices 更容易上手一點。待到項目有一個可用的版本,后面可以嘗試以其它的方式進行重構,也不是不可能的事情。畢竟這個項目至少還有2.0版。

3. 經驗

“選擇”總是一件很困難的事情。特別是每個選項都各有利弊的時候,選擇就更加困難。每一種組合都是一種可能,每一種組合有都有它的局限。差別可能就在團隊成員之間是不是能順暢的合作。如果合作出現問題,能不能及時調整。

希望這次調整能給項目帶來更多的活力。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM