使用 JavaScript 製作 Metro style app - 入門篇


微軟最新推出的 Metro style app 多了一個開發語言新選擇 JavaScript ,網頁程式工程師終於可以用最熟悉的語言和架構開發視窗程式。這篇文章利用一個簡單的範例「唐詩兩三首」,列出五位詩人的作品各三首,帶出使用 JavaScript 製作 Metro style app 的程式架構,也讓你發現與開發網頁是如此相似。

請先安裝 Windows 8 Consumer Preview ,再在 Windows 8 Consumer Preview 中安裝 Visual Studio 11 Express Beta for Windows 8 。目前 Windows 8 Consumer Preview 和 Visual Studio 11 Express Beta for Windows 8 皆還沒有繁體中文版,所以下面的示範使用英文版。

請在 Start 畫面中點選開啟 Visual Studio 11 Express Beta for Windows 8 。

開啟後點選 Start Page 中的 New Project 。

在跳出的 New Project 對話框左邊,展開 Installed、展開 Templates、展開 JavaScript、點下 Windows Metro style 。

點選中間的 Split Application 。

在下方的 Name 欄位打入「 TangPoem 」,再點選右下的 OK 。

稍待片刻,就會看到專案程式碼編輯畫面。我們直接點下編輯畫面上方的 Local Machine ,看看專案在本地端電腦上執行會長什麼樣子。

會看到這是一個兩頁的 App ,第一頁是格子狀排列的群組列表。

在第一頁點選任何一個群組,會進入第二頁,左邊是這個群組的資料項目列表,點選任何一個資料項目,會在右邊看到資料項目的內容。

按下 ALT + F4 終止 App 回到 Start 畫面。將 Start 畫面捲軸移到最右,會發現多了個專案產生的 App 「 TangPoem 」。

重新回到 Visual Studio 11 Express Beta for Windows 8 ,將 Solution Explorer 中的 css、html、images、js 資料夾皆展開。如果沒看到 Solution Explorer ,請點選左上角的 View 再點 Solution Explorer 。

html 資料夾中有兩個檔案 itemsPage.html 、splitPage.html ,分別就是 App 中的第一頁群組列表頁和第二頁群組資料項目列表頁。

css 資料夾中有 itemsPage.css、splitPage.css ,js 資料夾中有 itemsPage.js、splitPage.js ,分別就是 itemsPage.html、splitPage.html 使用的 CSS 和 JavaScript 檔案。

另外在 html 資料夾外還有一個 HTML 檔案 default.html ,必需花點功夫說明: Windows 使用 Internet Explorer 繪製 JavaScript 撰寫的 Metro style app ,但此時 Internet Explorer 的 Process 名稱是 WWAHost.exe 而非 iexplorer.exe 。繪製特定頁面,例如 itemsPage.html 時,並不是將整個 App 導往 itemsPage.html ,而是將 itemsPage.html 的 CSS 、 JavaScript 、 HTML 主區塊嵌入 default.html 中。所以整個 App 自始自終都會在同一個頁面 default.html ,也就是同一個 context 中,這點要特別注意。

images 資料夾中的圖片 logo.png、smalllogo.png、storelogo.png、splashscreen.png 分別是 App 的 Logo、小 Logo、Marketplace 中顯示的 Logo 與載入畫面圖示,你可以自行依其尺寸替換成你喜歡的圖片。

在這裡我們先將 App 中會使用到的詩人大頭照匯入。詩人大頭照請按這裡下載,請將 ZIP 檔解壓縮。

請在 Solution Explorer images 資料夾上點滑鼠右鍵,選 Add ,再點選 New Folder 。

將 New Folder 命名為 poets。

再在 poets 資料夾上點滑鼠右鍵,選 Add,再點選 Existing Item。

將剛剛解壓縮出的詩人大頭照選取加入。

整個 App 的資料都在 data.js 中。我們現在就來修改 data.js,將當中的範例資料取代為我們要顯示的詩人和詩。請點選 Solution Explorer js 資料夾下的 data.js,使 data.js 在左側的編輯窗格展開。

請將變數 groupDescription、itemDescription、itemContent、lightGray、mediumGray、darkGray 去掉,我們使用不到。

再將陣列 sampleGroups 取代為下面的陣列 poets :

var poets = [ { key: "Poet 1", name: "李白", avatar: "images/poets/1-0.jpg" }, { key: "Poet 2", name: "杜甫", avatar: "images/poets/2-0.jpg" }, { key: "Poet 3", name: "李商隱", avatar: "images/poets/3-0.jpg" }, { key: "Poet 4", name: "白居易", avatar: "images/poets/4-0.jpg" }, { key: "Poet 5", name: "王維", avatar: "images/poets/5-0.jpg" } ];

請注意 sampleGroups 和 poets 陣列中元件的屬性並不相同。

也將陣列 sampleItems 取代為下面的陣列 poems :

var poems = [ { group: poets[0], title: "靜夜思", content: "<p>床前看月光,<br />疑是地上霜。</p><p>舉頭望山月,<br />低頭思故鄉。</p>", thumb: "images/poets/1-1.jpg" }, { group: poets[0], title: "黃鶴樓送孟浩然之廣陵", content: "<p>故人西辭黃鶴樓,<br />煙花三月下揚州。</p><p>孤帆遠影碧空盡,<br />唯見長江天際流。</p>", thumb: "images/poets/1-2.jpg" }, { group: poets[0], title: "早發白帝城", content: "<p>朝辭白帝彩雲間,<br />千里江陵一日還。</p><p>兩岸猿聲啼不住,<br />輕舟已過萬重山。</p>", thumb: "images/poets/1-3.jpg" }, { group: poets[1], title: "八陣圖", content: "<p>功蓋三分國,<br />名成八陣圖。</p><p>江流石不轉,<br />遺恨失吞吳。</p>", thumb: "images/poets/2-1.jpg" }, { group: poets[1], title: "春望", content: "<p>國破山河在,<br />城春草木深。</p><p>感時花濺淚,<br />恨別鳥驚心。</p><p>烽火連三月,<br />家書抵萬金。</p><p>白頭搔更短,<br />渾欲不勝簪。</p>", thumb: "images/poets/2-2.jpg" }, { group: poets[1], title: "贈衛八處士", content: "<p>人生不相見,<br />動如參與商。</p><p>今夕復何夕,<br />共此燈燭光。</p><p>少壯能幾時,<br />鬢髮各已蒼。</p><p>訪舊半為鬼,<br />驚呼熱中腸。</p><p>焉知二十載,<br />重上君子堂。</p><p>昔別君未婚,<br />男女忽成行。</p><p>怡然敬父執,<br />問我來何方。</p><p>問答乃未已,<br />驅兒羅酒漿。</p><p>夜雨剪春韭,<br />新炊間黃梁。</p><p>主稱會面難,<br />一舉累十觴。</p><p>十觴亦不醉,<br />感子故意長。</p><p>明日隔山岳,<br />世事兩茫茫。</p>", thumb: "images/poets/2-3.jpg" }, { group: poets[2], title: "樂遊原", content: "<p>向晚意不適,<br />驅車登古原。</p><p>夕陽無限好,<br />只是近黃昏。</p>", thumb: "images/poets/3-1.jpg" }, { group: poets[2], title: "無題", content: "<p>昨夜星辰昨夜風,<br />畫樓西畔桂堂東。</p><p>身無彩鳳雙飛翼,<br />心有靈犀一點通。</p><p>隔座送鉤春酒暖,<br />分曹射覆蠟燈紅。</p><p>嗟余聽鼓應官去,<br />走馬蘭台類轉蓬。</p>", thumb: "images/poets/3-2.jpg" }, { group: poets[2], title: "錦瑟", content: "<p>錦瑟無端五十弦,<br />一弦一柱思華年。</p><p>莊生曉夢迷蝴蝶,<br />望帝春心託杜鵑。</p><p>滄海月明珠有淚,<br />藍田日暖玉生煙。</p><p>此情可待成追憶,<br />只是當時已惘然。</p>", thumb: "images/poets/3-3.jpg" }, { group: poets[3], title: "賦得古原草送別", content: "<p>離離原上草,<br />一歲一枯榮。</p><p>野火燒不盡,<br />春風吹又生。</p><p>遠芳侵古道,<br />晴翠接荒城。</p><p>又送王孫去,<br />萋萋滿別情。</p>", thumb: "images/poets/4-1.jpg" }, { group: poets[3], title: "琵琶行", content: "<p>潯陽江頭夜送客,<br />楓葉荻花秋瑟瑟。</p><p>主人下馬客在船,<br />舉酒欲飲無管絃;</p><p>醉不成歡慘將別,<br />別時茫茫江浸月。</p><p>忽聞水上琵琶聲,<br />主人忘歸客不發。</p><p>尋聲暗問彈者誰?<br />琵琶聲停欲語遲。</p><p>移船相近邀相見,<br />添酒回燈重開宴。</p><p>千呼萬喚始出來,<br />猶抱琵琶半遮面。</p><p>轉軸撥絃三兩聲,<br />未成曲調先有情。</p><p>絃絃掩抑聲聲思,<br />似訴平生不得志。</p><p>低眉信手續續彈,<br />說盡心中無限事。</p><p>輕攏慢撚抹復挑,<br />初為霓裳後六么。</p><p>大絃嘈嘈如急雨,<br />小絃切切如私語;</p><p>嘈嘈切切錯雜彈,<br />大珠小珠落玉盤。</p><p>閒關鶯語花底滑,<br />幽咽泉流水下灘。</p><p>水泉冷澀絃凝絕,<br />凝絕不通聲漸歇。</p><p>別有幽愁暗恨生,<br />此時無聲勝有聲。</p><p>銀瓶乍破水漿迸,<br />鐵騎突出刀鎗鳴。</p><p>曲終收撥當心畫,<br />四絃一聲如裂帛。</p><p>東船西舫悄無言,<br />唯見江心秋月白。</p><p>沈吟放撥插絃中,<br />整頓衣裳起斂容。</p><p>自言本是京城女,<br />家在蝦蟆陵下住。</p><p>十三學得琵琶成,<br />名屬教坊第一部。</p><p>曲罷曾教善才服,<br />妝成每被秋娘妒。</p><p>五陵年少爭纏頭,<br />一曲紅綃不知數。</p><p>鈿頭銀篦擊節碎,<br />血色羅裙翻酒汙。</p><p>今年歡笑復明年,<br />秋月春風等閒度。</p><p>弟走從軍阿姨死,<br />暮去朝來顏色故;</p><p>門前冷落車馬稀,<br />老大嫁作商人婦。</p><p>商人重利輕別離,<br />前月浮梁買茶去,</p><p>去來江口守空船,<br />繞船月明江水寒。</p><p>夜深忽夢少年事,<br />夢啼妝淚紅闌干。</p><p>我聞琵琶已嘆息,<br />又聞此語重唧唧。</p><p>同是天涯淪落人,<br />相逢何必曾相識?</p><p>我從去年辭帝京,<br />謫居臥病潯陽城。</p><p>潯陽地僻無音樂,<br />終歲不聞絲竹聲。</p><p>住近湓城地低濕,<br />黃蘆苦竹繞宅生。</p><p>其間旦暮聞何物?<br />杜鵑啼血猿哀鳴。</p><p>春江花朝秋月夜,<br />往往取酒還獨傾。</p><p>豈無山歌與村笛?<br />嘔啞嘲哳難為聽。</p><p>今夜聞君琵琶語,<br />如聽仙樂耳暫明。</p><p>莫辭更坐彈一曲,<br />為君翻作琵琶行。</p><p>感我此言良久立,<br />卻坐促絃絃轉急。</p><p>淒淒不似向前聲,<br />滿座重聞皆掩泣。</p><p>座中泣下誰最多,<br />江州司馬青衫濕。</p>", thumb: "images/poets/4-2.jpg" }, { group: poets[3], title: "長恨歌", content: "<p>漢皇重色思傾國,<br />御宇多年求不得。</p><p>楊家有女初長成,<br />養在深閨人未識。</p><p>天生麗質難自棄,<br />一朝選在君王側。</p><p>回眸一笑百媚生,<br />六宮粉黛無顏色。</p><p>春寒賜浴華清池,<br />溫泉水滑洗凝脂;</p><p>侍兒扶起嬌無力,<br />始是新承恩澤時。</p><p>雲鬢花顏金步搖,<br />芙蓉帳暖度春宵;</p><p>春宵苦短日高起,<br />從此君王不早朝。</p><p>承歡侍宴無閑暇,<br />春從春遊夜專夜。</p><p>後宮佳麗三千人,<br />三千寵愛在一身。</p><p>金屋妝成嬌侍夜,<br />玉樓宴罷醉和春。</p><p>姊妹弟兄皆列土,<br />可憐光彩生門戶。</p><p>遂令天下父母心,<br />不重生男重生女。</p><p>驪宮高處入青雲,<br />仙樂風飄處處聞。</p><p>緩歌慢舞凝絲竹,<br />盡日君王看不足。</p><p>漁陽鼙鼓動地來,<br />驚破霓裳羽衣曲。</p><p>九重城闕煙塵生,<br />千乘萬騎西南行。</p><p>翠華搖搖行復止,<br />西出都門百餘里;</p><p>六軍不發無奈何?<br />宛轉蛾眉馬前死。</p><p>花鈿委地無人收,<br />翠翹金雀玉搔頭。</p><p>君王掩面救不得,<br />回看血淚相和流。</p><p>黃埃散漫風蕭索,<br />雲棧縈紆登劍閣。</p><p>峨嵋山下少人行,<br />旌旗無光日色薄。</p><p>蜀江水碧蜀山青,<br />聖主朝朝暮暮情。</p><p>行宮見月傷心色,<br />夜雨聞鈴腸斷聲。</p><p>天旋地轉迴龍馭,<br />到此躊躇不能去。</p><p>馬嵬坡下泥土中,<br />不見玉顏空死處。</p><p>君臣相顧盡霑衣,<br />東望都門信馬歸。</p><p>歸來池苑皆依舊,<br />太液芙蓉未央柳;</p><p>芙蓉如面柳如眉,<br />對此如何不淚垂?</p><p>春風桃李花開日,<br />秋雨梧桐葉落時。</p><p>西宮南內多秋草,<br />落葉滿階紅不掃。</p><p>梨園子弟白髮新,<br />椒房阿監青娥老。</p><p>夕殿螢飛思悄然,<br />孤燈挑盡未成眠。</p><p>遲遲鐘鼓初長夜,<br />耿耿星河欲曙天。</p><p>鴛鴦瓦冷霜華重,<br />翡翠衾寒誰與共?</p><p>悠悠生死別經年,<br />魂魄不曾來入夢。</p><p>臨邛道士鴻都客,<br />能以精誠致魂魄;</p><p>為感君王輾轉思,<br />遂教方士殷勤覓。</p><p>排空馭氣奔如電,<br />升天入地求之遍;</p><p>上窮碧落下黃泉,<br />兩處茫茫皆不見。</p><p>忽聞海上有仙山,<br />山在虛無縹緲間。</p><p>樓閣玲瓏五雲起,<br />其中綽約多仙子。</p><p>中有一人字太真,<br />雪膚花貌參差是。</p><p>金闕西廂叩玉扃,<br />轉教小玉報雙成。</p><p>聞道漢家天子使,<br />九華帳裡夢魂驚;</p><p>攬衣推枕起徘徊,<br />珠箔銀屏迤邐開。</p><p>雲鬢半偏新睡覺,<br />花冠不整下堂來。</p><p>風吹仙袂飄飄舉,<br />猶似霓裳羽衣舞。</p><p>玉容寂寞淚闌干,<br />梨花一枝春帶雨。</p><p>含情凝睇謝君王,<br />一別音容兩渺茫。</p><p>昭陽殿裡恩愛絕,<br />蓬萊宮中日月長。</p><p>回頭下望人寰處,<br />不見長安見塵霧。</p><p>唯將舊物表深情,<br />鈿合金釵寄將去。</p><p>釵留一股合一扇,<br />釵擘黃金合分鈿。</p><p>但教心似金鈿堅,<br />天上人間會相見。</p><p>臨別殷勤重寄詞,<br />詞中有誓兩心知,</p><p>七月七日長生殿,<br />夜半無人私語時。</p><p>在天願作比翼鳥,<br />在地願為連理枝。</p><p>天長地久有時盡,<br />此恨綿綿無絕期。</p>", thumb: "images/poets/4-3.jpg" }, { group: poets[4], title: "相思", content: "<p>紅豆生南國,<br />春來發幾枝?</p><p>勸君多採擷,<br />此物最相思!</p>", thumb: "images/poets/5-1.jpg" }, { group: poets[4], title: "雜詩", content: "<p>君自故鄉來,<br />應知故鄉事。</p><p>來日綺窗前,<br />寒梅著花未。</p>", thumb: "images/poets/5-2.jpg" }, { group: poets[4], title: "九月九日憶山東兄弟", content: "<p>獨在異鄉為異客,<br />每逢佳節倍思親;</p><p>遙知兄弟登高處,<br />遍插茱萸少一人。</p>", thumb: "images/poets/5-3.jpg" } ];

也請注意 sampleItems 和 poems 陣列中元件的屬性並不相同。

最後將 data.js 下方的 sampleItems.forEach 修改為 poems.forEach 。

到這裡 data.js 修改完畢。

因應 data.js 陣列 poets 取代陣列 sampleGroups 時,陣列中元件屬性的更改,我們要來修改splitPage.js 。

請用 Solution Explorer 打開 js 資料夾下的 splitPage.js 。

找到:

element.querySelector("header[role=banner] .pagetitle").textContent = this.group.title;

將 this.group.title 更改為 this.group.name :

element.querySelector("header[role=banner] .pagetitle").textContent = this.group.name;

JavaScript 到此修改完畢。

再來我們修改 HTML 。

請用 Solution Explorer 打開 html 資料夾下的 itemsPage.html 。

找到 <h1> 中的:

<span class="pagetitle">TangPoem</span>

將 TangPoem 取代為「唐詩兩三首」:

<span class="pagetitle">唐詩兩三首</span>

再找到片段:

<div class="itemtemplate" data-win-control="WinJS.Binding.Template"> <img class="item-image" src="#" data-win-bind="src: backgroundImage; alt: title" /> <div class="item-overlay"> <h4 class="item-title" data-win-bind="textContent: title"></h4> <h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: subtitle"></h6> </div> </div>

因應 data.js 陣列 poets 取代陣列 sampleGroups 時,陣列中元件屬性的更改,將 <img> 的屬性 data-win-bind="src: backgroundImage; alt: title" 改為 data-win-bind="src: avatar; alt: name" ,<h4> 的屬性data-win-bind="textContent: title" 改為 data-win-bind="textContent: name" ,再將 <h6> 去掉:

<div class="itemtemplate" data-win-control="WinJS.Binding.Template"> <img class="item-image" src="#" data-win-bind="src: avatar; alt: name" /> <div class="item-overlay"> <h4 class="item-title" data-win-bind="textContent: name"></h4> </div> </div>

再使用 Solution Explorer 打開 html 資料夾下的 splitPage.html 。

找到片段:

<div class="itemtemplate" data-win-control="WinJS.Binding.Template"> <img class="item-image" src="#" data-win-bind="src: backgroundImage; alt: title" /> <div class="item-info"> <h3 class="item-title win-type-ellipsis" data-win-bind="textContent: title"></h3> <h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: subtitle"></h6> <h4 class="item-description" data-win-bind="textContent: description"></h4> </div> </div>

因應 data.js 陣列 poems 取代陣列 sampleItems 時,陣列中元件屬性的更改,將 <img> 的屬性 data-win-bind="src: backgroundImage; alt: title" 修改為 data-win-bind="src: thumb; alt: title",再將 <h6> 和 <h4> 去掉:

<div class="itemtemplate" data-win-control="WinJS.Binding.Template"> <img class="item-image" src="#" data-win-bind="src: thumb; alt: title" /> <div class="item-info"> <h3 class="item-title win-type-ellipsis" data-win-bind="textContent: title"></h3> </div> </div>

再找到片段:

<section class="articlesection" aria-atomic="true" aria-label="Item detail" aria-live="assertive"> <header class="header"> <div class="text"> <h2 class="article-title win-type-ellipsis" data-win-bind="textContent: title"></h2> <h4 class="article-subtitle" data-win-bind="textContent: subtitle"></h4> </div> <img class="article-image" src="#" data-win-bind="src: backgroundImage; alt: title" /> </header> <article class="article-content" data-win-bind="innerHTML: content"></article> </section>

將 <h4> 去掉,<img> 的屬性 data-win-bind="src: backgroundImage; alt: title" 改為 data-win-bind="src: thumb; alt: title" :

<section class="articlesection" aria-atomic="true" aria-label="Item detail" aria-live="assertive"> <header class="header"> <div class="text"> <h2 class="article-title win-type-ellipsis" data-win-bind="textContent: title"></h2> </div> <img class="article-image" src="#" data-win-bind="src: thumb; alt: title" /> </header> <article class="article-content" data-win-bind="innerHTML: content"></article> </section>

HTML 到此修改完畢。

修改 HTML 時,移除了不少東西,空出了不少空間,所以我們要來修改 CSS ,將詩人的姓名、詩的標題和內文字體放大。

請用 Solution Explorer 打開 css 資料夾下的 itemsPage.css 。

找到 .itemspage .itemslist .win-item .item-overlay .item-title :

.itemspage .itemslist .win-item .item-overlay .item-title { -ms-grid-row: 1; overflow: hidden; width: 220px; }

加上 font-size: 21pt; :

.itemspage .itemslist .win-item .item-overlay .item-title { -ms-grid-row: 1; overflow: hidden; width: 220px; font-size: 21pt; }

再用 Solution Explorer 打開 css 資料夾下的 splitPage.css 。

找到 .splitpage .itemlistsection .itemlist .win-item .item-info .item-title:

.splitpage .itemlistsection .itemlist .win-item .item-info .item-title { margin-top: 4px; }

加上 font-size : 17pt; 。

.splitpage .itemlistsection .itemlist .win-item .item-info .item-title { margin-top: 4px; font-size: 17pt; }

找到 .splitpage .articlesection .article-content:

.splitpage .articlesection .article-content { overflow-y: auto; }

加上 font-size : 15pt;:

.splitpage .articlesection .article-content { overflow-y: auto; font-size: 15pt; }

CSS 修改到此完畢。

最後我們要修改 App 在 Start 畫面顯示的名稱。

請使用 Solution Explorer 打開 package.appxmainfest ,將 Display name 修改為「唐詩兩三首」。

到此整個 App 就完成囉!請按下上方的 Local Machine 就可以看到 App 在本地端電腦執行的樣子。

最後介紹一點點開發的小技巧。

上方 Local Machine 的右邊有一個向下的小箭頭。點下選擇 Simulator ,再在 Simulator 上按一下,可以將 App 在模擬器中執行。

模擬器的右方,有一排按鈕,可以模擬各種觸控手勢、解析度、App 執行位置、直握橫握等各種 Local Machine 可能無法測試的狀態。

如果你喜歡 Internet Explorer 的開發者工具、Firefox 的 Firebug、或 Chrome 的開發人員工具,那你應該會很喜歡下面兩個東西。

在模擬器執行的狀態下,回到 Visual Studio 11 Express Beta for Windows 8,點上方的 Debug,選 Windows,會看到兩個選項,JavaScript Console 和 DOM Explorer 。

有 JavaScript Console 當然也就可以在 JavaScript 中使用 console.log debug 。

DOM Explorer 也和 Internet Explorer 開發者工具相同,左上有個 Select Element 按鈕。點下後,再到模擬器中點 App 的任一 DOM 元件,DOM Explorer 就會自動展開到所點元件。

本文章的範例專案檔請點這裡下載