"Tách café" CoffeeScript đầu tiên của bạn: Phần 3: Sử dụng CoffeeScript ở phía máy khách

Loạt bài này khám phá ngôn ngữ lập trình phổ biến - CoffeeScript, được xây dựng dựa trên của JavaScript. CoffeeScript biên dịch thành JavaScript hiệu quả và phù hợp với nhiều hướng dẫn thực hành tốt nhất. Bạn có thể chạy JavaScript trong một trình duyệt web hoặc sử dụng nó với các công nghệ như Node.js dùng cho các ứng dụng máy chủ. Phần 1 của loạt bài này đã mô tả những bước đầu làm quen với CoffeeScript và giải thích những món quà dành cho các nhà phát triển. Phần 2 mô tả cách sử dụng CoffeeScript để giải quyết một số bài toán lập trình. Bài này giải thích cách tạo ra một ứng dụng hoàn chỉnh bằng cách sử dụng CoffeeScript.

Michael Galpin, Kỹ sư phần mềm, Google

Michael_GalpinMichael Galpin là kỹ sư phần mềm tại Google. Ông là đồng tác giả của cuốn sách Android in iPractice và là người đóng góp thường xuyên cho developerWorks. Để xem những dự định tương lai của ông, mời bạn đọc blog hoặc theo dõi ông trên Twitter @michaelg hay Google+ Michael Galpin.



04 01 2013

Giới thiệu

CoffeeScript là một ngôn ngữ lập trình mới được xây dựng dựa trên JavaScript. CoffeeScript cung cấp một cú pháp rõ ràng nên rất hấp dẫn với bất cứ ai thích Python hay Ruby và nó cũng cung cấp nhiều tính năng lập trình hàm lấy cảm hứng từ các ngôn ngữ như Haskell và Lisp.

Trong Phần 1 của loạt bài này, bạn đã tìm hiểu về những lợi ích của việc sử dụng CoffeeScript thay vì chỉ sử dụng JavaScript. Bạn thiết lập môi trường phát triển và đã chạy các kịch bản lệnh. Trong Phần 2, bạn đã khám phá ngôn ngữ lập trình CoffeeScript bằng cách dùng thử nhiều tính năng để giải quyết các bài toán toán học.

Trong bài này, bạn tạo ra một ứng dụng mẫu CoffeeScript. Cả hai mã phía khách và mã phía máy chủ sẽ đều được viết hoàn toàn bằng CoffeeScript. Bạn cũng có thể tải về mã nguồn được sử dụng trong bài viết này.


Ứng dụng tìm kiếm được viết bằng CoffeeScript

Các chữ viết tắt thường dùng

  • JSON: JavaScript Object Notation (Ký pháp đối tượng JavaScript)
  • UI: User interface (Giao diện người dùng)

Ứng dụng mẫu mà bạn sẽ tạo ra cho phép người dùng nhập một thuật ngữ tìm kiếm, tìm kiếm cả trên Google lẫn Twitter, rồi hiển thị các kết quả kết hợp. Phía máy khách của ứng dụng nhận dữ liệu do người dùng nhập vào và gửi nó đến máy chủ để tìm các kết quả. Khi máy chủ trả về các kết quả, phía máy khách tạo ra các phần tử giao diện người dùng (UI) cho các kết quả đó và hiển thị chúng trên màn hình. Bây giờ, hãy đừng lo lắng gì về phía máy chủ hoạt động như thế nào.

Bắt đầu bằng cách định nghĩa mô hình dữ liệu cho ứng dụng. Ứng dụng sẽ hiển thị các kết quả tìm kiếm, vì vậy hãy định nghĩa một lớp SearchResult. Liệt kê 1 cho thấy định nghĩa này.

Liệt kê 1. Lớp SearchResult cơ sở
class SearchResult
    constructor: (data) ->
        @title = data.title
        @link = data.link
        @extras = data

    toHtml: -> "<a href='#{@link}'>#{@title}</a>"
    toJson: -> JSON.stringify @extras

Lớp SearchResult khá đơn giản. Nó:

  • Bắt đầu với một hàm tạo (constructor) định nghĩa hai biến thành viên: title (tiêu đề) và link (liên kết).
  • Tìm kiếm hai giá trị này trong đối tượng dữ liệu được chuyển tới hàm tạo.
  • Lưu trữ phần còn lại của dữ liệu được chuyển tới một biến thành viên có tên là extras.

    Điều này sẽ thuận tiện vì bạn biết bạn sẽ có hai loại kết quả tìm kiếm khác nhau trong ứng dụng (các kết quả từ Google và các kết quả từ Twitter).

  • Định nghĩa hai phương thức cho lớp SearchResult:
    • toHtml, tạo ra một chuỗi HTML đơn giản từ cá thể SearchResult, lợi dụng phép nội suy chuỗi của CoffeeScript.
    • toJson, dùng để chuyển đối tượng SearchResult thành một chuỗi JSON.

Lớp trong Liệt kê 1 cung cấp các tính năng cơ bản của một kết quả tìm kiếm. Bạn sẽ nhận được nhiều dữ liệu hơn từ các kết quả tìm kiếm của cả Google lẫn Twitter. Mô hình hóa kết quả này bằng cách tạo ra các lớp con cho từng kiểu kết quả tìm kiếm. Liệt kê 2 cho thấy kiểu kết quả tìm kiếm của Google.

Liệt kê 2. Lớp GoogleSearchResult
class GoogleSearchResult extends SearchResult
    constructor: (data) ->
        super data
        @content = @extras.content
    toHtml: ->
        "#{super} <div class='snippet'>#{@content}</div>"

Liệt kê 2 cho thấy việc lập trình hướng đối tượng bằng CoffeeScript dễ như thế nào. Lớp GoogleSearchResult là lớp mở rộng (kế thừa) từ lớp cơ sở SearchResultLiệt kê 1. Hàm tạo của nó gọi hàm tạo của lớp cha (superclass). Nếu bạn đã từng thực hiện sự thừa kế theo phong cách-lớp bằng JavaScript, thì bạn biết việc này khó khăn đến mức nào. Liệt kê 3 cho thấy mã JavaScript được tạo ra.

Liệt kê 3. Mã JavaScript của lớp GoogleSearchResult
GoogleSearchResult = (function() {
  __extends(GoogleSearchResult, SearchResult);
  function GoogleSearchResult(data) {
    GoogleSearchResult.__super__.constructor.call(this, data);
    this.content = this.extras.content;
  }
  GoogleSearchResult.prototype.toHtml = function() {
    return "" + GoogleSearchResult.__super__.toHtml.apply(this, arguments) 
+ " <div class='snippet'>" + this.content + "</div>";
  };
  return GoogleSearchResult;
})();

Để gọi hàm tạo của lớp cha, bạn phải giữ một cá thể của lớp cha trong biến __super__ (có thể dùng bất kỳ tên nào) và sau đó gọi hàm tạo một cách tường minh. Quay lại Liệt kê 2, bạn có thể thấy việc này dễ dàng hơn bao nhiêu trong CoffeeScript. Lưu ý rằng ví dụ này đã định nghĩa một biến cá thể mới được gọi là content (nội dung) trong lớp GoogleSearchResult. Về cơ bản, đây là một đoạn HTML từ trang web do kết quả tìm kiếm trỏ tới. Không có gì đáng ngạc nhiên là lớp GoogleSearchResult có đoạn này, nhưng lớp cơ sở lại không có. Cuối cùng, lưu ý việc nạp chồng (override) của phương thức toHtml. Ví dụ này sử dụng phương thức toHtml của lớp cha nhưng còn gắn thêm một div bổ sung có đoạn nội dung đó. Xem lại Liệt kê 3 để thấy bước gọi này đến phương thức toHtml của lớp cha được thực hiện như thế nào. Vì bạn có một lớp con GoogleSearchResult , nên bạn cũng cần một lớp con TwitterSearchResult, như thể hiện trong Liệt kê 4.

Liệt kê 4. Lớp TwitterSearchResult
class TwitterSearchResult extends SearchResult
    constructor: (data) ->
        super data
        @source = @extras.from_user
        @link = "http://twitter.com/#{@source}/status/#{@extras.id_str}"
        @title = @extras.text
    toHtml: ->
        "<a href='http://twitter.com/#{@source}'>@#{@source}</a>: #{super}"

Lớp TwitterSearchResult cũng giống như lớp GoogleSearchResult trong Liệt kê 2. Hàm tạo của nó cũng sử dụng hàm tạo của lớp cha. Cũng:

  • Định nghĩa biến thành viên riêng của mình có tên là source (nguồn).
  • Xây dựng biến thành viên link bằng cách sử dụng một khuôn mẫu chuỗi ký tự và các biến thành viên của nó.
  • Đặt lại giá trị biến thành viên title đến trường khác từ dữ liệu đầu vào.
  • Ghi chồng phương thức toHtml của lớp cha, gắn thêm một liên kết đến người dùng đã tạo ra tweet.

Một lần nữa, phép nội suy chuỗi của CoffeeScript làm cho việc sử dụng phương thức toHtml của lớp cha trở nên dễ dàng khi tạo một phương thức mới. Để gọi phương thức toHtml của lớp cha, bạn chỉ cần dùng từ khóa super. Bạn có thể dùng cách gọi super.toHtml, nhưng không cần phải làm thế và thực ra nếu gọi như vậy sẽ báo lỗi. Chỉ cần dùng như trên thì CoffeeScript đã hiểu rằng bạn muốn gọi chính phương thức tương tự từ lớp cha, điều này giúp bạn viết mã dễ dàng hơn một chút mà thôi.

Bây giờ bạn có các cấu trúc dữ liệu mà ứng dụng sẽ cần và bạn có thể bắt đầu viết một số xử lý ở phía máy khách. Sẽ dễ dàng hơn nhiều nếu thử nghiệm mã với một tầng làm việc bên dưới. Vì không chỉ có một tầng làm việc bên dưới, chúng ta hãy tiếp tục tìm hiểu phần thú vị nhất: dữ liệu giả định.


Sử dụng dữ liệu giả định

Khi xây dựng một ứng dụng máy chủ - máy khách như một ứng dụng web hiện đại, điều luôn có nghĩa là tạo giao diện dùng chung, nơi hai phần của ứng dụng gặp nhau và sau đó tạo ra dữ liệu giả định. Điều này cho phép bạn phát triển các phần ứng dụng phía máy khách và phía máy chủ song song với nhau. Cách tiếp cận này đặc biệt tốt với CoffeeScript vì bạn có thể sử dụng cùng một ngôn ngữ lập trình trên cả phía máy khách lẫn phía máy chủ. Liệt kê 5 cho thấy các kết quả tìm kiếm giả định từ Google.

Liệt kê 5. Các kết quả tìm kiếm giả định của Google
mockGoogleData = [
        GsearchResultClass:"GwebSearch",
        link:"http://jashkenas.github.com/coffee-script/",
        url:"http://jashkenas.github.com/coffee-script/",
        visibleUrl:"jashkenas.github.com",
        cacheUrl:"http://www.google.com/search?q\u003dcache:nuWrlCK4-v4J:jashkenas
.github.com",
        title:"\u003cb\u003eCoffeeScript\u003c/b\u003e",
        titleNoFormatting:"CoffeeScript",
        content:"\u003cb\u003eCoffeeScript\u003c/b\u003e is a little language that 
compiles into JavaScript. Underneath all of   those embarrassing braces and 
semicolons, JavaScript has always had a \u003cb\u003e...\u003c/b\u003e"
    ,
        GsearchResultClass:"GwebSearch",
        link:"http://en.wikipedia.org/wiki/CoffeeScript",
        url:"http://en.wikipedia.org/wiki/CoffeeScript",
        visibleUrl:"en.wikipedia.org",
        cacheUrl:"http://www.google.com/search?q\u003dcache:wshlXQEIrhIJ
:en.wikipedia.org",
        title:"\u003cb\u003eCoffeeScript\u003c/b\u003e - Wikipedia, the free 
encyclopedia",
        titleNoFormatting:"CoffeeScript - Wikipedia, the free encyclopedia",
        content:"\u003cb\u003eCoffeeScript\u003c/b\u003e is a programming language 
that transcompiles to JavaScript. The   language adds syntactic sugar inspired by 
Ruby, Python and Haskell to enhance \u003cb\u003e...\u003c/b\u003e"
    ,
        GsearchResultClass:"GwebSearch",
        link:"http://codelikebozo.com/why-im-switching-to-coffeescript",
        url:"http://codelikebozo.com/why-im-switching-to-coffeescript",
        visibleUrl:"codelikebozo.com",
        cacheUrl:"http://www.google.com/search?q\u003dcache:VDKirttkw30J:
codelikebozo.com",
        title:"Why I\u0026#39;m (Finally) Switching to \u003cb\u003eCoffeeScript
\u003c/b\u003e - Code Like Bozo",
        titleNoFormatting:"Why I\u0026#39;m (Finally) Switching to CoffeeScript - 
Code Like Bozo",
        content:"Sep 5, 2011 \u003cb\u003e...\u003c/b\u003e You may have already heard
about \u003cb\u003eCoffeeScript\u003c/b\u003e and some of the hype surrounding it
but you still have found several reasons to not make the \u003cb\u003e...
\u003c/b\u003e"   
]

Điều hiển nhiên là việc tạo dữ liệu giả định với cú pháp đơn giản của CoffeeScript còn dễ dàng hơn. Ví dụ này cho thấy các chữ hay số viết tắt (literals) của đối tượng trong CoffeeScript. Liệt kê 5 là một mảng. Sử dụng thụt đầu dòng thêm để chỉ báo một đối tượng và mỗi đặc tính của đối tượng lại được thụt vào nữa. Mã này rất tiện để so sánh với JSON. Khoảng trống thay cho các dấu ngoặc nhọn. Chúng giống như các chữ hay số viết tắt của JavaScript, ở đây các thuộc tính không nằm trong các dấu nháy. Với JSON các thuộc tính này cũng phải được đặt trong dấu nháy.

Liệt kê 6 cho thấy dữ liệu giả định tương tự với các kết quả tìm kiếm của Twitter.

Liệt kê 6. Các kết quả tìm kiếm giả định của Twitter
mockTwitterData = [
        created_at:"Wed, 09 Nov 2011 04:18:49 +0000",
        from_user:"jashkenas",
        from_user_id:123323498,
        from_user_id_str:"123323498",
        geo:null,
        id:134122748057370625,
        id_str:"134122748057370625",
        iso_language_code:"en",
        metadata:
            recent_retweets:4,
            result_type:"popular"
        profile_image_url:"http://a3.twimg.com/profile_images/1185870726/gravatar
_normal.jpg",
        source:"<a href="http://itunes.apple.com/us/app/twitter/id409789998?mt
=12&quot; rel="nofollow">Twitter for Mac</a>",
        text:""CoffeeScript [is] the closest I felt to the power I had twenty 
years ago in Smalltalk" - Ward Cunningham (http://t.co/2Wve2V4l) Nice.",
        to_user_id:null,
        to_user_id_str:null
]

Dữ liệu giả định trong Liệt kê 6 giống như dữ liệu giả định trong Liệt kê 5, nhưng với các trường đặc thù riêng của Twitter. Bây giờ bạn chỉ cần tạo ra một giao diện trả về dữ liệu giả định này. Liệt kê 7 cho thấy một lớp khác thực hiện chính điều đó.

Liệt kê 7. Một lớp của máy tìm kiếm giả định
class MockSearch
    search: (query, callback) ->
        results = 
            google: (new GoogleSearchResult obj for obj in mockGoogleData)
            twitter: (new TwitterSearchResult obj for obj in mockTwitterData)
        callback results

Lớp MockSearch có một phương thức đơn lẻ được gọi là search (tìm kiếm). Nó có hai tham số: query (truy vấn) để sử dụng cho việc tìm kiếm và một hàm callback (gọi lại). Lớp MockSearch trả về các kết quả nhanh chóng, nhưng một việc tìm kiếm thực sự sẽ cần truyền qua mạng để nói chuyện với các máy chủ. Để xử lý việc này trong JavaScript và để đảm bảo rằng bạn không làm cho UI bị treo, bạn thường sử dụng một hàm callback.

Bước tiếp theo tạo ra một đối tượng có tên là results (kết quả). Bạn sẽ sử dụng lại cú pháp chữ hay số viết tắt của đối tượng của CoffeeScript. Đối tượng results có hai trường: googletwitter. Các giá trị của mỗi trường được biểu thị bằng cách sử dụng một phép gồm mảng. Biểu thức sẽ tạo ra một mảng kiểu SearchResult thích hợp (GoogleSearchResult dùng cho Google và TwitterSearchResult dùng cho Twitter). Cuối cùng, hàm callback được gọi với đối tượng results được chuyển cho nó.

Việc tìm kiếm giả định sẽ làm việc, do đó, bạn cần sẵn sàng viết mã giao diện người dùng (UI) ở phía máy khách.


Mang tất cả vào trong trình duyệt

Trước khi liên kết giao diện người dùng với mã ứng dụng, đầu tiên hãy xem xét giao diện người dùng mà bạn sắp sử dụng. Liệt kê 8 cho thấy một trang web rất đơn giản.

Liệt kê 8. Trang web CoffeeSearch
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">

<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>CoffeeSearch</title>
    <script type="text/javascript" src="search.js"></script>
</head>
<body>
    <div>
        <label for="searchQuery">Keyword:</label>
        <input type="text" name="searchQuery" id="searchQuery"></input>
        <input type="button" value="Search" onclick="doSearch()"/>
    </div>
    <div class="goog" id="gr"/>
    <div class="twit" id="tr"/>
</body>
</html>

Trang web ở trên có một biểu mẫu cơ bản để nhập một từ khóa và chuyển nó đến một máy tìm kiếm. Nó đã định nghĩa hai phần và sẵn sàng dùng để thêm các kết quả tìm kiếm vào chúng. Trang web này đã không định nghĩa mã JavaScript nào trong đó. Thay vào đó, tất cả mã JavaScript đều ở trong một tệp có tên là doSearch, mà tệp này sẽ là phiên bản biên dịch của CoffeeScript. Lưu ý rằng khi nhấn vào nút tìm kiếm, một hàm có tên là doSearch được gọi ra. Hàm này phải nằm trong tệp search.js; đó là thứ duy nhất trong tệp search.js mà bạn vẫn chưa thấy. Liệt kê 9 cho thấy định nghĩa theo CoffeeScript.

Liệt kê 9. Hàm doSearch của trang web
@doSearch = ->
    $ = (id) -> document.getElementById(id)
    kw = $("searchQuery").value
    appender = (id, data) ->
        data.forEach (x) -> 
            $(id).innerHTML += "<p>#{x.toHtml()}</p>"
    ms = new MockSearch
    ms.search kw, (results) ->
        appender("gr", results.google)
        appender("tr", results.twitter)

Bạn có thể thấy rằng hàm này có một ký hiệu @ (một ký hiệu tắt của this) đặt trước nó. Khi được định nghĩa ở mức cao nhất của một kịch bản lệnh, this trở thành đối tượng chung (global). Trong trường hợp của một kịch bản lệnh trong một trang web, đối tượng chung là đối tượng cửa sổ, sẽ cho phép nó được tham chiếu trong trang web mà bạn thấy trong Liệt kê 8.

Hàm doSearch làm rất nhiều việc chỉ trong một vài dòng mã. Mã này:

  • Định nghĩa một hàm cục bộ có tên là $, về cơ bản đó chính là đại diện cho hàm document.getElementById. Sử dụng hàm này để nhận từ khóa đã nhập vào trong biểu mẫu tìm kiếm trong Liệt kê 8.
  • Định nghĩa một hàm cục bộ khác tên là appender, sẽ nhận ID của một phần tử trong DOM và một mảng. Sau đó nó sẽ duyệt qua mảng, tạo ra một chuỗi HTML và nối thêm chính phần tử có ID đã cho.
  • Tạo một đối tượng MockSearch và gọi phương thức search của nó.
  • Chuyển từ khoá từ biểu mẫu và tạo ra một hàm gọi lại.

    Hàm gọi lại sử dụng appender để nối thêm các kết quả tìm kiếm từ Google vào một div và các kết quả tìm kiếm từ Twitter vào một div khác.

Bây giờ bạn có thể chỉ cần biên dịch tất cả mã và chạy nó. Hình 1 cho thấy trang web với dữ liệu giả định.

Hình 1. Trang tìm kiếm với dữ liệu giả định
Trang tìm kiếm với dữ liệu giả định

Ví dụ này có thể trông không ấn tượng lắm, nhưng nó trình diễn tất cả các hàm cần thiết ở phía máy khách. Mặc dù bạn vẫn chưa viết mã phía máy chủ, nhưng bạn có thể hoàn toàn tin tưởng rằng ứng dụng này sẽ làm việc miễn là mã phía máy chủ tạo ra dữ liệu có cấu trúc giống như dữ liệu giả định.


Kết luận

Trong bài này, tôi đã đưa CoffeeScript ra khỏi phòng thí nghiệm và đã tạo một ứng dụng thực tế. Được kết hợp với node.js, CoffeeScript mang đến cho bạn cơ hội viết một ứng dụng hoàn chỉnh, cả phía máy khách lẫn phía máy chủ, khi sử dụng cùng một ngôn ngữ lập trình rõ ràng. Bạn có lợi thế tốt để sử dụng lại một số mã giống nhau khi xây dựng phía máy chủ của ứng dụng ở phần cuối cùng của loạt bài này. Hãy tiếp tục.


Tải về

Mô tảTênKích thước
Article source codecs3.zip5KB

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

Thảo luận

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Nguồn mở
ArticleID=853988
ArticleTitle="Tách café" CoffeeScript đầu tiên của bạn: Phần 3: Sử dụng CoffeeScript ở phía máy khách
publish-date=01042013