🔍 搜尋結果:`

🔍 搜尋結果:`

切版示範-教你怎麼完成一個切板,完全新手導向,帶你從零到一!!

## 題目說明 ![](https://i.imgur.com/5O73qvg.png) 寫出這樣子的網頁切版 ### 示範效果 ![](https://i.imgur.com/s4H57CU.jpg) CODEPEN:https://codepen.io/flwrtykd-the-scripter/pen/VwGyOwa ## 代碼寫法 ### HTML ``` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="style.css" /> <title>貓咪網站</title> </head> <body> <div class="wrap"> <div class="hero"> <img src="https://wallpapers.com/images/featured/g9rdx9uk2425fip2.jpg" alt="" /> <div class="text"> <span class="red">有貓咪也有狗,</span> <br /> <span class="red">甚至是螳螂!</span> <br /> <span class="green">隨便讓你領養</span> </div> </div> <div class="content"> <div class="block"> <img src="https://i.imgur.com/9ej5zY3.jpg" alt="" referrerpolicy="no-referrer" /> <p> Lorem ipsum dolor sit amet consectetur adipisicing elit. Culpa commodi quo assumenda nam tempore sint quia neque voluptatum ab. </p> </div> <div class="block"> <img src="https://i.imgur.com/9ej5zY3.jpg" alt="" referrerpolicy="no-referrer" /> <p> Lorem ipsum dolor sit amet consectetur adipisicing elit. Culpa commodi quo assumenda nam tempore sint quia neque voluptatum ab. </p> </div> <div class="block"> <img src="https://i.imgur.com/9ej5zY3.jpg" alt="" referrerpolicy="no-referrer" /> <p> Lorem ipsum dolor sit amet consectetur adipisicing elit. Culpa commodi quo assumenda nam tempore sint quia neque voluptatum ab. </p> </div> </div> <div class="footer"> <p>@copy right 這邊就是一些文字</p> </div> </div> </body> </html> ``` ### CSS ``` body { background: url("https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1888&q=80"); overflow: hidden; } .wrap { width: 1000px; background-color: #e9edc9; margin: 0 auto; height: 100vh; margin-top: -10px; } .hero img { width: 100%; height: 250px; object-fit: cover; object-position: 0px 1px; } .hero span { font-size: 45px; } .hero .green { font-size: 35px; } .text { position: absolute; top: 35px; left: 250px; font-weight: 900; text-shadow: 2px 2px 5px white; border-left: 5px solid yellow; } .text > * { margin-left: 20px; } .content { display: flex; justify-content: space-around; margin-top: 40px; } .block { width: 250px; border: 1px solid black; } .block img { width: 100%; } .block p { padding: 5px 20px; } .block:nth-child(1) { margin-left: 25px; } .footer { background-color: black; color: white; text-align: center; height: 30px; } .footer p { transform: translateY(25%); } ``` ## 布局解釋 ### 背景以及格局 先設定一個背景 background img之後 使用wrap來製造一個效果是左右縮窄 只要設定width 以及margin auto 0就可以了 之後我使用三個分區 分別是上到下的四個div: hero 裡面有大圖片 text是標題文字 然後是content裡面放置三個block裝著小圖片跟內容文字 最底下是footer放著版權說明 ### hero & text hero就放圖片 width設定100%即可 text的話我裡面用三個span寫文字 設定class分別調整樣式 換行是使用br標籤 其中左邊的書籤效果(黃色長長那條) 是對text這個div使用border left完成的 ### content 這邊的話 先對content的div設定display flex達到水平排列的效果 其中可以用justify content讓它space around 平均分散開 再使用:nth-child(1) 去針對第一個block調整margin left 讓排版更精準 裡面的話 寫一個block 完成後再複製成三個 block放置img跟p即可 而它本身也要用一個border製造出邊線的效果 ### footer 我使用 transform: translateY(25%); 讓文字有點垂直置中的感覺 這都可以自己微調 ## 特別注意 在hero的img調整上我使用了 object-fit object-position 這兩個屬性去微調 這個是很好用的小技巧,可以特別留意 ## 心得 這種切板很常見 使用的伎倆其實差不多就那些 學習上 可以藉由先完成效果 再尋求優化的原則 以上 有相關問題都可以提出討論 或是歡迎投稿想要切版的圖片 我未來可以寫一系列的切版文來跟大家交流~~~

[作業問題]HTML&CSS 第6課 Loading Page

您好,請問幾個問題。 1.為什麼我使用position:relative,網頁會出現很大的空白,我該如何解決。 2.使用絕對位置,我該如何讓我的網頁隨著比例縮放。 問題1 : ![](https://i.imgur.com/wzXvqGk.jpg) 問題2: ![](https://i.imgur.com/4a7OA6u.jpg) ![](https://i.imgur.com/BzUIBdJ.jpg) 這是網頁全開的時候 ![](https://i.imgur.com/YGNQAfx.jpg) HTML: ``` <!DOCTYPE html> <html> <head> <meta charset = "UTF-8"> <title>佳加安全帽</title> <link rel = "stylesheet" href = "style.css"> </head> <body class = "page"> <div class = "navbar"> <div class="container"> <div class = "logo"> <a href = "#"><img src = "jiajia.jpg"/></a> </div> <div class="menu"> <ul class="choose"> <li><a href = "">所有商品</a></li> <li><a href = "">特價商品</a></li> <li><a href = "">安全帽</a></li> <li><a href = "">藍芽耳機</a></li> <li><a href = "">裝備衣物</a></li> <li><a href = "">配件</a></li> <li><a href = "">包包</a></li> </ul> </div> <div class="user"> <ul> <li><a href = "">Log in 登入</a></li> <li>|</li> <li><a href = "">Sign up 註冊</a></li> </ul> </div> </div> </div> <div class="background"> <div class="content"> <h1>在危險的世界中,給你多一份安全</h1> </br> </br> <p>留下您的email,獲得新商品及最新優惠資訊</p> </br> <input type= "email" name = "email" value = "Your email"> </br> <input class="submit" type = "submit" name= "submit" value = "訂閱"> </div> </div> </body> </html> ``` CSS: ``` *{ margin: 0px; padding: 0px; list-style-type: none; text-decoration: none; } .navbar{ background-color: #272727; height: 50px; width: 100%; } .logo img{ height: 50px; } .menu{ position: relative; top: -50px; line-height: 45px; left: 500px; } .menu li{ float: left; } .menu a{ color: #D9B300; display: block; padding: 0 10px; } .menu a:hover{ background-color: #D9B300; color: #272727; } .user{ position: relative; top: -50px; left: 800px; line-height: 45px; } .user li{ float: left; color: #D9B300; } .user a{ color: #D9B300; display: block; padding: 0 10px; } .user a:hover{ background-color: #D9B300; color: #272727; } h1{ color: white; } p{ color: white; } .background{ background-image: url("motor.jpg"); background-size: cover; background-repeat: no-repeat; height: 660px; text-align: center; } .content input{ width: 400px; height: 40px; } .content h1{ font-size: 40px; } .content p{ font-size: 20px; } .content{ position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .submit{ font-weight: bold; font-size: 20px; background-color: #D9B300; color: #272727; border-radius: 10px; border: 0px; } .submit:hover{ background-color: #272727; color: #D9B300; }

RE: 請教切板問題--切版觀念教學!實例code review!

## 前提 很棒的問題,很榮幸我也有辦法可以回答。 你好,稍微看了一下你的code,有些東西想要跟你討論、分享。 ## 解答 ### 修改完的效果 ![](https://i.imgur.com/3KBwL9M.png) ### 觀念方面 首先必須要稱讚你的野心,想要clone整個網頁,這是非常值得嘉獎的想法! 是我也會這樣練習,這種想法非常棒! 我覺得你這種學習態度非常好哦~~~ 針對code的問題, 我覺得你寫得有點亂,具體在於class與ID的名稱取名,我不知道你是否是直接copy人家的過來。 如果是這樣子,我覺得你可以試著自己重構, 如果不是的話,也建議你可以分區,使用註解來把哪一個區域是什麼標註起來, ![](https://i.imgur.com/hbMMJV8.png) 這對新手來說是個很好的習慣。 再來是你的分區我覺得滿亂的,加上似乎又有重複的div閉合標籤(? 不知道是你用copy的還是哪裡有問題,這部分要注意!! 做切版要先確定怎麼分割,你比較像是邊做邊分割,切的不是太好,導致排不太出來。 但這不是你的問題,這邊跟你分享他怎麼切,其實他這種板很簡單。 ### 邏輯方面 主要就是三個東西,wrap、container:main、side。 wrap的話就是設定寬度width以及margin:0 auto,製造一個居中效果。 container裡面有main與side就是你想要排的左跟右邊區。 只要container寫成dispaly:flex 與 justify-content: space around就可以了, 裡面的main寫flex: 0.66、然後side是flex:0.33 這樣會有平均分配的效果。 至於細節寬度就使用padding跟margin去調整。 ### HTML ``` <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="icon" href="https://glitch.com/favicon.ico" /> <title>Hello world!</title> <!-- import the webpage's stylesheet --> <link rel="stylesheet" href="style2.css" /> <!-- import the webpage's javascript file --> <script src="/script.js" defer></script> </head> <body> <div class="wrap"> <div id="logo"> <a href="https://www.storm.mg/"> <img id="homepage" src="https://www.storm.mg/images/logo.svg" /> </a> </div> <div id="nav_menu"> <nav> <a class="nav_btn" href="https://new7.storm.mg/" ><img id="img1" src="https://www.storm.mg/images/logo_new7.svg" /></a> <a class="nav_btn" href="https://onepercent.storm.mg/" ><img id="img2" src="https://www.storm.mg/images/1percentstyle.svg" /></a> <a class="nav_btn" href="https://www.storm.mg/premium" ><img id="img3" src="https://www.storm.mg/images/vip_logo.png" /></a> <a class="nav_btn" href="#">新聞</a> <a class="nav_btn" href="#">評論</a> <a class="nav_btn" href="#">財經</a> <a class="nav_btn" href="#">生活</a> <a class="nav_btn" href="#">下班經濟學</a> </nav> </div> <div id="tag_title"> <a class="tag_link" href="https://www.storm.mg/category/118">政治</a> <a class="tag_link" href="https://www.storm.mg/category/22168" >公共政策</a > </div> <h1>北捷研擬早晚鳥4折票救內湖交通 高嘉瑜列3原因打臉:根本劃錯</h1> <div id="article_info"> <div id="article_info_name"> <a id="name" href="https://www.storm.mg/authors/313137/%E9%99%B3%E5%AC%BF%E5%A9%B7" >陳嬿婷</a > </div> <div id="article_info_time"> <span id="article_time">2023-02-13 16:16</span> </div> </div> </div> <div class="container2 wrap"> <div class="main"> <img src="https://image.cache.storm.mg/styles/smg-800x533-fp-wm/s3/media/image/2023/01/18/20230118-104054_U18448_M824647_96f1.jpeg?itok=xwGSqPL9" /> <p id="imgintro"> 民進黨立委高嘉瑜(見圖)認為,北市府放錯重點,需2年內讓汐止民生線、東環段動工,才有望根治長年未解的內湖交通問題。(資料照,柯承惠攝) </p> <p class="content_text"> 台北市內湖區近年發展快速,交通壅塞的問題成為各界關注的焦點,為解決內科交通問題,北市副市長李四川與相關單位討論,初步研擬推出北捷早晚鳥票,提供悠遊卡4折優惠,吸引通勤族捨棄汽機車,改搭捷運通勤,並鼓勵民眾在離峰時間上、下班。對此,民進黨立委高嘉瑜認為,北市府放錯重點,需2年內讓汐止民生線、東環段動工,才有望根治長年未解的內湖交通問題。 </p> <a id="link1" href="https://www.storm.mg/line/notify/login" >[啟動LINE推播] 每日重大新聞通知</a > <p class="content_text"> 高嘉瑜於<a id="fb" href="https://www.facebook.com/ntufishfans/posts/pfbid02AhK796XpShFigJjCsMe8vgDsGn8nCrzwXYs8aQdWPcbp86u92sXPJqZUXnnVadoZl" >臉書</a >發文,指出內科塞車問題,主要肇因於在上下班時間同時湧入高達15萬人次的跨區、跨縣市通勤者,而內科地區員工上班時間又有高達82%都集中在上午9時。雖然北市交通局預計推出早晚鳥票,並搭配企業彈性上下班,意圖使內科員工上班能錯開通勤尖峰時段,但事實上,前台北市長柯文哲早在2016年就曾提出為期3個月的「綠色運輸試辦計畫」,其內容就包括捷運早鳥優惠,結果仍是失敗收場。 </p> <p class="content_text"> 高嘉瑜也提到,彈性分流上下班很好,但優惠票誘因不足,她認為,企業端該如何溝通鼓勵彈性上下班?市府端又該如何推動共乘、減少一人一車?才是短期能立竿見影的方法,而長期而言,內科交通最終仍得靠完善捷運路網來拯救,如何解決現有文湖線運量不足且妥善率差及路線轉乘次數高等問題,才是當務之急。 </p> <p class="content_text"> 高嘉瑜也批評,目前民生線遙遙無期,汐東段亦未延伸至舊宗,北市府仍一拖再拖。她表示,行政院在今年1月已核定捷運汐東線,她也多次質詢跟強調,台北端的民生線應接續推動,至少此次汐東線也應先同步延伸至內湖舊宗路的「SB07站」,進而在未來與東環段接軌,串聯內湖、南港、社后、樟樹灣、保長坑及北五堵新市鎮等產業區。 </p> <p class="content_text"> 高嘉瑜說,但遺憾的是,經她近日再度詢問北市府關於「捷運汐東線之規劃,評估可否延伸」,得到的答覆卻是,「因民生線與汐東捷運、基隆捷運所採用之系統不同,須待汐東捷運具體規劃方案核定後,才會開始評估。」至今仍未能有明確定案。 </p> <p class="content_text"> 高嘉瑜強調,台北市長蔣萬安選前承諾東環段2年內動工,務必說到做到。她指出,環狀線東環段可串聯捷運紅線(象山)、藍線(永春)、綠線(松山),為東台北縱向捷運路網的重要建設,並有望根治長年未解的內湖交通問題,蔣萬安也曾在選前承諾2年內動工。她表示,自己會持續關注東環段進度,希望台北市政府可以更積極推動,中央加速進度儘快核定,別讓民眾一等再等。(相關報導:<a id="link2" href="https://www.storm.mg/article/4724935" >幕後》和柯文哲不一樣 高虹安的「軍機處」走自己的路</a >|更多文章) </p> </div> <div id="sidebar"> <div id="video_area"> <p class="sidebar_video">現正熱映中</p> <iframe id="smg_live_video" class="smg_live_video embed-responsive-item" src="https://www.youtube.com/embed/eJ4RErMLLh4?autoplay=&mute=1&version=3&loop=1" height="200" frameborder="0" allowfullscreen=""></iframe> </div> <div id="recommend"> <p class="sidebar_news sidebar_video">熱門新聞</p> </div> </div> </body> </html> ``` ### CSS ``` * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } #logo { margin: 0 auto; text-align: center; } #homepage { width: 169px; height: auto; } #img1 { width: 79px; height: 25px; margin: -8px 0 -5px; } #img2 { width: 79px; height: 25px; margin: -8px 0 -5px; } #img3 { width: 79px; height: 25px; margin: -8px 0 -5px; } #nav_menu { width: 100%; height: auto; border-top: 1px solid black; border-bottom: 1px solid #cccccc; text-align: left; } .nav_btn { display: inline-block; padding: 13px 15px 14px; letter-spacing: 3px; font-size: 16px; text-decoration: none; color: black; } #tag_title { border-left-width: 10px; border-left-style: solid; border-left-color: rgb(255, 56, 56); margin: 10px; } .tag_link { padding: 10px; color: black; text-decoration: bold; } #container { background: #f5f5f5; } #intro { margin: 10px; color: grey; font-size: 10px; } h1 { font-size: 32px; color: black; } #content { background: lightyellow; width: 800px; padding: 5px 30px 20px; } .content_text { padding: 15px 0px; } #artice_info, #article_info_name, #article_info_time { display: inline-block; margin: 10px; } #name { color: red; font-size: 16px; padding: 5px; } #article_time { color: black; font-size: 16px; padding: 5px; } #sidebar { flex:0.33; width: 300px; margin-left: 20px; padding: 0px 30px; } .main{ flex: 0.66; } #video_area, #recommend { border-top-width: 2px; border-top-style: solid; border-top-color: rgb(0, 0, 0); } .sidebar_news{ border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: grey; padding: 10px 10px 10px 0px; } #img_area { min-height: 420px; position: relative; } #link1 { color: grey; padding: 0px 25px; } #link2 { color: grey; } #fb { color: red; } .container2{ display: flex; justify-content: space-around; } .wrap { width: 1250px; margin: 0 auto; padding: 0px 39px; } .sidebar_video{ margin-top: 10px; font-size: 20px; letter-spacing: 5px; color: #ff3838; margin-bottom: 10px; } .smg_live_video{ margin-bottom: 10px; } ``` ## 後記 以上,很不錯的練習,整體而言做得很好! 希望未來可以一起繼續進步哦~~ 另外也還是要感謝站長創立這個網站,我從加入的第一天到現在都還是很喜歡哦。 現在站長也有[線上諮詢](https://codelove.tw/coaching)的服務,歡迎各位有需求,投資自己,跟高手學習! 時間就是金錢,掌握關鍵的學習方法其實比胡搞瞎搞重要! shout out to my man 阿川。

還在用CSS搞?介紹 10 個優秀的 HTML 標籤,超牛逼低調神標籤!! (更:補MDN說明

來自這個: https://www.youtube.com/embed/HhkIstbkmpU fieldset ・legend ・datalist ・option ・progress ・abbr ・del ・ins ・mark ・marquee ## 摘要 fieldset ・legend 表單用 ・datalist ・option 資料選單用 ・progress 進度條用 ・abbr 縮寫用 ・del 刪除線用 ・ins 插入文字用 ・mark 螢光標籤用 ・marquee 過時的捲動元素 ## [fieldset](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/fieldset) + [legend](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/legend) ### fieldset 功能說明: 用於對表單中的控制元素進行分組 ![](https://i.imgur.com/oxy0MJC.png) ```htmlmixed= <form action="#"> <fieldset> <legend>Do you agree?</legend> <input type="checkbox" id="chbx" name="agree" value="Yes!" /> <label for="chbx">I agree</label> </fieldset> </form> ``` ### legend 功能說明: HTML `<legend> 元素用於表示其父元素 <fieldset> 的內容標題。` ![](https://i.imgur.com/a2v8wkM.png) ## [datalist](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist) + [option](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/option) ### datalist 功能說明: `HTML <datalist> 元素包含了一組 <option> 元素,這些元素表示其它表單控件可選值。` ![](https://i.imgur.com/D02kWpS.png) ### option 功能說明: `HTML 元素 <option> 用於定義在 <select>, <optgroup> 或 <datalist> 元素中包含的項。<option> 可以在彈出窗口和 HTML 文檔中的其他項目列表中表示菜單項。` ## [progress](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/progress) ### 功能說明: `HTML中的 <progress> 元素用來顯示一項任務的完成進度。雖然規範中沒有規定該元素具體如何顯示,瀏覽器開發商可以自己決定,但通常情況下,該元素都顯示為一個進度條形式。` ![](https://i.imgur.com/jQtHfE6.png) ## [abbr](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/abbr) ![](https://i.imgur.com/ODAFv8H.png) ### 功能說明: `HTML 縮寫元素(<abbr>)用於代表縮寫,並且可以通過可選的 title 屬性提供完整的描述。若使用 title 屬性,則它必須且僅可包含完整的描述內容。` ![](https://i.imgur.com/B6qRKeB.png) ## [del](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/del) + [ins](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/ins) ### del 功能說明: `HTML 的 <del> 標簽表示一些被從文檔中刪除的文字內容。比如可以在需要顯示修改記錄或者源代碼差異的情況使用這個標簽。<ins>標簽的作用恰恰於此相反:表示文檔中添加的內容。` ![](https://i.imgur.com/F0kX2GV.png) ### ins 功能說明: `HTML <ins> 元素定義已經被插入文檔中的文本。` ![](https://i.imgur.com/TcKaH3Q.png) ```htmlembedded= <p>“You're late!”</p> <del> <p>“I apologize for the delay.”</p> </del> <ins cite="../howtobeawizard.html" datetime="2018-05"> <p>“A wizard is never late …”</p> </ins> ``` ## [mark](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/mark) ### 功能說明: ``` HTML 標記文本元素 (< Mark >) 表示為引用或符號目的而標記或突出顯示的文本,這是由於標記的段落在封閉上下文中的相關性或重要性造成的。 這個 HTML mark 標簽代表突出顯示的文字,例如可以為了標記特定上下文中的文本而使用這個標簽**.** 舉個例子,它可以用來顯示搜索引擎搜索後關鍵詞 ``` ![](https://i.imgur.com/83IiPxw.png) ## [marquee](https://developer.mozilla.org/zh-TW/docs/Web/HTML/Element/marquee) ### 功能說明: 捲動元素(已過時) ![](https://i.imgur.com/VDYcTy2.png)

在 React 使用 useWorker 來跑多執行緒,大幅改善 UX 效能

使用 useWorker 在單獨的執行緒中,處理昂貴且阻塞 UI 的任務。 眾所周知,Javascript 是一種單線程語言。所以,做任何昂貴的任務,它都會阻塞 UI 互動。用戶需要等到它完成,才能執行剩餘的其他操作,這會帶來糟糕的用戶體驗。 為了克服和執行這些任務,Javascript 有一個名為 [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) 的解決方案,它允許在 web 中執行消耗效能的任務時,瀏覽器不會阻塞用戶界面,使用戶體驗非常流暢。 本篇文章簡單介紹這個 hook。 原文出處:https://dev.to/nilanth/multi-threaded-react-app-using-useworker-gf8 --- ## Web Workers Web Worker 是一個在後台執行而不影響用戶界面的腳本,因為它在一個單獨的線程而不是主線程中執行。所以它不會對用戶交互造成任何阻塞。 Web Worker 主要用於在 Web 瀏覽器中執行昂貴的任務,例如對大量資料進行排序、CSV 導出、圖像處理等。 ![工作線程](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p2ikpn8va49460arsuum.png) 參考上圖,我們可以看到一個昂貴的任務是由一個工作線程並行執行的,而不會阻塞主線程。當我們在主線程中執行相同的操作時,會導致 UI 阻塞。 ## React 中的 Concurrent mode 呢? React [並發模式](https://beta.reactjs.org/reference/react/startTransition) 不會並行執行任務。它將非緊急任務轉移,接著立即執行緊急任務。它使用相同的主線程來處理。 正如我們在下圖中看到的,緊急任務是使用上下文切換來處理的。例如,如果一個表正在呈現一個大型資料集,並且用戶試圖搜尋某些內容,React 會將任務切換為用戶搜尋,並首先處理它,如下所示。 ![任務切換](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7nc8978zlpi8gmrcxlnd.png) 當為同一任務使用 worker 時,表格渲染在一個單獨的線程中並行執行。檢查下圖。 ![反應工作者](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4tnoy22tqchmq8cuavdp.png) ## useWorker [useWorker](https://github.com/alewin/useWorker) 是一個通過 React Hooks 在簡單配置中使用 Web Worker API 的函式庫。它支持在不阻塞 UI 的情況下執行昂貴任務,支援 promise 而不是事件監聽器,還有一些值得注意的功能: 1. 結束超時 worker 的選項 2. 遠程依賴 3. Transferable 4. Worker status > useWorker 是一個 3KB 的庫 ## 為什麼不用 JavaScript 內置 web worker? 在使用 javascript web worker 時,我們需要加入一些複雜的配置,來設置多行程式碼的 worker。使用 useWorker,我們可以簡化 worker 的設置。讓我們在下面的程式碼中,看看兩者之間的區別。 ## 在 React App 中使用 JS Web Worker 時 ``` // webworker.js self.addEventListener("message", function(event) { var numbers = [...event.data] postMessage(numbers.sort()) }); ``` ``` //index.js var webworker = new Worker("./webworker.js"); webworker.postMessage([3,2,1]); webworker.addEventListener("message", function(event) { console.log("Message from worker:", event.data); // [1,2,3] }); ``` ## 在 React App 中使用 useWorker 時 ``` // index.js const sortNumbers = numbers => ([...numbers].sort()) const [sortWorker] = useWorker(sortNumbers); const result = await sortWorker([1,2,3]) ``` 正如我之前所說,與普通的 javascript worker 相比,`useWorker()` 簡化了配置。 讓我們與 React App 整合並執行高 CPU 密集型任務,來看看 useWorker() 的實際執行情況。 ## Quick Start 要將 useWorker() 加入到 React 專案,請使用以下命令 `yarn add @koale/useworker` 安裝套件後,導入 useWorker()。 `import { useWorker, WORKER_STATUS } from "@koale/useworker";` 我們從函式庫中導入 useWorker 和 WORKER_STATUS。 **useWorker()** 鉤子返回 workerFn 和 controller。 1. `workerFn` 是允許使用 web worker 執行函數的函數。 2.controller 由 status 和 kill 參數組成。 status 參數返回 worker 的狀態和用於終止當前執行的 worker 的 kill 函數。 讓我們用一個例子來看看 **useWorker()**。 使用 **useWorker()** 和主線程對大型陣列進行排序 首先建立一個SortingArray元件,加入如下程式碼 ``` //Sorting.js import React from "react"; import { useWorker, WORKER_STATUS } from "@koale/useworker"; import { useToasts } from "react-toast-notifications"; import bubleSort from "./algorithms/bublesort"; const numbers = [...Array(50000)].map(() => Math.floor(Math.random() * 1000000) ); function SortingArray() { const { addToast } = useToasts(); const [sortStatus, setSortStatus] = React.useState(false); const [sortWorker, { status: sortWorkerStatus }] = useWorker(bubleSort); console.log("WORKER:", sortWorkerStatus); const onSortClick = () => { setSortStatus(true); const result = bubleSort(numbers); setSortStatus(false); addToast("Finished: Sort", { appearance: "success" }); console.log("Buble Sort", result); }; const onWorkerSortClick = () => { sortWorker(numbers).then((result) => { console.log("Buble Sort useWorker()", result); addToast("Finished: Sort using useWorker.", { appearance: "success" }); }); }; return ( <div> <section className="App-section"> <button type="button" disabled={sortStatus} className="App-button" onClick={() => onSortClick()} > {sortStatus ? `Loading...` : `Buble Sort`} </button> <button type="button" disabled={sortWorkerStatus === WORKER_STATUS.RUNNING} className="App-button" onClick={() => onWorkerSortClick()} > {sortWorkerStatus === WORKER_STATUS.RUNNING ? `Loading...` : `Buble Sort useWorker()`} </button> </section> <section className="App-section"> <span style={{ color: "white" }}> Open DevTools console to see the results. </span> </section> </div> ); } export default SortingArray; ``` 這裡我們配置了 useWorker 並傳遞了 bubleSort 函數來使用 worker 執行昂貴的排序。 接下來,將以下程式碼加入到 App.js 元件,並導入 SortingArray 元件。 ``` //App.js import React from "react"; import { ToastProvider } from "react-toast-notifications"; import SortingArray from "./pages/SortingArray"; import logo from "./react.png"; import "./style.css"; let turn = 0; function infiniteLoop() { const lgoo = document.querySelector(".App-logo"); turn += 8; lgoo.style.transform = `rotate(${turn % 360}deg)`; } export default function App() { React.useEffect(() => { const loopInterval = setInterval(infiniteLoop, 100); return () => clearInterval(loopInterval); }, []); return ( <ToastProvider> <div className="App"> <h1 className="App-Title">useWorker</h1> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <ul> <li>Sorting Demo</li> </ul> </header> <hr /> </div> <div> <SortingArray /> </div> </ToastProvider> ); } ``` 我們已將 React 徽標加入到 **App.js** 元件,該組件每 **100ms** 旋轉一次,以直觀地表示執行昂貴任務時的阻塞和非阻塞 UI。 執行上面的程式碼時,我們可以看到兩個按鈕 Normal Sort 和 Sort using **useWorker()**。 接下來,單擊 Normal Sort 按鈕在主線程中對陣列進行排序。我們可以看到 React 徽標停止旋轉幾秒鐘。由於排序任務阻塞了UI渲染,所以排序完成後logo又開始旋轉了。這是因為兩個任務都在主線程中處理。檢查以下gif ![正常排序](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k74iuo6qd36qbvpr9iaz.gif) 讓我們使用 [chrome 性能記錄](https://developer.chrome.com/docs/devtools/performance/) 檢查其性能分析。 ![性能正常](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8eclh8a6o7g9xzta03ic.png) 我們可以看到 Event: click 任務用了 **3.86 秒** 來完成這個過程,它阻塞了主線程 3.86 秒。 接下來,讓我們嘗試使用 **useWorker()** 選項進行排序。點擊它的時候我們可以看到 react logo 還在不間斷的旋轉。由於 useWorker 在不阻塞 UI 的情況下在後台執行排序。這使得用戶體驗非常流暢。檢查以下gif ![工人排序](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xe5ci0p3hy5nca4321cg.gif) 您還可以使用 `sortWorkerStatus` 在控制台中看到 worker 狀態為 **RUNNING**、**SUCCESS**。 讓我們看看這種方法的性能分析結果 ![主線程](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bxsl696fld6u79dqpwra.png) ![工作線程](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fcnwe2lczguyxgfqftrj.png) 正如我們所看到的,第一張圖片表示主線程中沒有長時間執行的進程。在第二張圖片中,我們可以看到排序任務由一個單獨的工作線程處理。所以主線程沒有阻塞任務。 您可以在以下沙箱中試用整個範例。 https://codesandbox.io/embed/useworker-sorting-example-041qhc?fontsize=14&hidenavigation=1&theme=dark ## 何時使用worker 1.圖像處理 2. 排序或處理大資料集。 3. 大資料CSV或Excel導出。 4. 畫布繪圖 5. 任何 CPU 密集型任務。 ## useWorker 的限制 1. web worker 無權存取 window 物件和 document。 2. 當 worker 正在執行時,我們不能再次呼叫它,直到它完成或被中止。為了解決這個問題,我們可以建立兩個或多個 useWorker() 鉤子實例。 3. Web Worker 無法返回函數,因為響應是序列化的。 4. Web Workers 受限於最終用戶機器的可用 CPU 內核和內存。 ## 結論 Web Worker 允許在 React 應用程式中使用多線程來執行昂貴的任務而不會阻塞 UI。而 useWorker 允許在 React 應用程式中以簡化的掛鉤方法使用 Web Worker API。 Worker 不應該被過度使用,我們應該只在必要時使用它,否則會增加管理 Worker 的複雜性。

JavaScript 系列八:結語

恭喜你完成了一系列的專案開發挑戰 放眼市面上的新手教材,應該沒人會把作業設定到這麼困難! 但我認為時機差不多了,所以要求一些有挑戰性、有趣的專案來讓你做,應該難不倒你 而且這種只用基本功能,就完成複雜專案的經驗,有助於掌握框架的本質、熟練框架基礎 既然你通通順利完成了,代表你的學習狀況「相當不錯」...... 再次跟你說聲恭喜! --- 這些專案內容,已經相當接近實務上、職場上會遇到的開發需求 也就是說,你的開發經驗已經「大大提升」了不少 事實上,光是目前的技能與經驗,已經可以應付職場上絕大多數的功能要求 你已經離「進入職場工作」,又大大靠近了一步! 但是呢,要能夠與團隊共同開發中大型專案,還是需要學習框架進階觀念、生態系、以及 JavaScript 生態系各種工具 坊間補習班,通常會在課程很早期開始教上述這些內容。我認為這會令學習者非常眼花撩亂 所以我讓你僅僅學完 `Essentials` 的內容,就立刻動手開發複雜、好玩的應用程式 我認為這樣才能清楚認識框架的強大,以及知道「工具不是導入越多、搞得越複雜就越好」 而是要視情況,只導入必要的、大小適宜的技術工具,這樣才能收放自如 那麼更大型的專案,又該引入哪些進階觀念、生態系工具呢? 這些會在後續的課程提到! --- 消化、研究完本課程之後,關於 JavaScript 更多必學的基本觀念 請接著前往「自學網頁の嬰兒教材:JavaScript(九)」開始學習吧! https://codelove.tw/@howtomakeaturn/course/QxMYx5

JavaScript 系列八:第5課 ── 體重追蹤應用程式

## 課程目標 - 能夠寫出體重追蹤應用程式 ## 課程內容 找套件時,如果官方沒有提供特定框架的版本,通常會有開發者,自行包裝一個,分享給大家使用 搜尋 `how to use 套件名稱 in 框架名稱` 大概就找得到相關資源了 舉例來說,這款圖表套件,官方沒有提供框架版本 https://www.chartjs.org/ 但是社群就有人做好了 https://vue-chartjs.org/ --- 介紹一個網站 https://dribbble.com/ 業界設計師,經常在上面搜尋別人的成品、找靈感、分享靈感 除了在 google 搜尋關鍵字,也可以常常到 Dribbble 搜尋 --- 這一課來寫體重追蹤應用程式 關鍵字 `weight tracking` ## 課後作業 請使用 https://replit.com/ 來寫作業 這次作業的規格如下: - 這種程式應該要讓人註冊登入使用,需要後端配合,但我們簡化吧:免登入、把資料存在 local storage 就好 - 此程式能夠新增一筆體重資料,input 欄位有「體重」、「日期」 - 有清單列表顯示每一筆輸入的資料,按照日期排序。每筆資料旁邊有刪除按鈕 - 使用 Chart.js 將體重的走勢,用折線圖(Line Chart)呈現 - 請「不要」使用社群包裝的版本(vue-chartjs),學著自己在框架內導入套件 --- 提示:vue 跟 chart.js 整合的時候,有一個地雷!會導致 `RangeError: Maximum call stack size exceeded ` 要解決這個地雷,請參閱這邊的討論 https://codelove.tw/@birdie2019/post/Ja69K3 --- 做出以上功能,你就完成這次的課程目標了!

JavaScript 系列八:第4課 ── 行事曆應用程式

## 課程目標 - 能夠寫出行事曆應用程式 ## 課程內容 現在新推出的大部份 javascript 套件,都會提供 native(原生)、React、Vue 三種版本,方便使用 舉例來說,這款行事曆套件,就有多種版本 https://fullcalendar.io/ 也有易安裝的 CDN 版本 https://fullcalendar.io/docs/initialize-globals --- 所謂的 React、Vue 或是任何框架版本,其實就只是把套件,根據框架規格,封裝成元件而已 (元件的觀念,之後會教,先別擔心) 套件會大量跟 DOM 互動,框架也會跟 DOM 互動,兩者容易衝突,使用上通常會 bug 一大堆 所以官方會推出套件版本,改掉一些底層寫法,方便框架用戶使用 如果沒有,那就由開發者自己小心管理,通常也沒太大問題 就是把初始化相關放在 `mounted` 裡面,然後狀態更新時,找方法與套件狀態同步,就這樣而已 --- 這一課來寫行事曆應用程式 關鍵字 `calendar app ui` ## 課後作業 請使用 https://replit.com/ 來寫作業 這次作業的規格如下: - 這種程式應該要讓人註冊登入使用,需要後端配合,但我們簡化吧:免登入、把資料存在 local storage 就好 - 有一個「新增活動」按鈕,點擊可以輸入「活動名稱」、選擇「活動日期」與「活動時間」 - 不用做成「開始、結束」兩個時間點,就做成開始時間就好 - 日期時間的 UI,請使用 https://github.com/xdan/datetimepicker - 上述功能,可以做成 modal 視窗(輸入欄位放在視窗內),也可以直接把輸入欄位都放在畫面上 - 使用 FullCalendar 呈現行事曆,將活動顯示在對應日期 - 點擊行事曆內的活動,可以刪除活動。請使用 javascript 內建的 `confirm` 函式,確認「是否刪除此筆活動?」後刪除 - 請「不要」用官方 Vue 版本,請用 CDN 版本,學著自己在框架內導入套件 做出以上功能,你就完成這次的課程目標了!

JavaScript 系列八:第3課 ── 分帳應用程式

## 課程目標 - 能夠寫出分帳應用程式 ## 課程內容 實務上,在開發應用程式,難免需要用到一些數學計算 通常也不用到太進階,大概國中數學的內容就很好用了 我個人經驗是,其實國中數學、高中數學的內容,超級強大、好用 只是大家普遍是填鴨式教育出身,失去創意、不知如何運用了 --- 這一課來寫分帳應用程式 關鍵字 `bill split online` `分帳 app` `splitwise 網頁版` ## 課後作業 請使用 https://replit.com/ 來寫作業 這次作業的規格如下: - 這個程式的用途,是跟一群朋友出遊時,方便均分所有支出 - 可以隨時打開使用,所以資料要隨時存到 local storage - 可以清楚看到,誰目前還欠多少錢、誰目前先出了多少錢 - 旅遊結束時,可以根據上述資料,快速地互相結清帳款 UI 規格如下: - 頁面上有一個「分帳成員」清單,裡面列出目前參與分帳的成員 - 有一個「新增成員」按鈕,點擊可以輸入「成員姓名」後新增 - 有一個「新增支出款項」按鈕,點擊可以輸入「款項名稱」、「款項金額」,並且可以選擇「由誰付款」 - 上述兩個功能,可以做成 modal 視窗(輸入欄位放在視窗內),也可以直接把輸入欄位都放在畫面上 - 頁面上條列出所有帳款,顯示付款人、金額、款項名稱,旁邊有刪除按鈕 - 在「分帳成員」清單內,每個成員旁邊顯示「應付金額」或者「應收金額」,分別顯示為紅色、綠色(目前總支出,除以成員人數之後,與成員目前代墊支出的差額) - 頁面上有一個「旅程結束」按鈕,點擊後刪除所有成員、所有款項 做出以上功能,你就完成這次的課程目標了!

JavaScript 系列七:結語

恭喜你完成這份奇怪的 Vue 入門課程 如同我在這門課程中不斷強調的 任何的技術工具,其實都充滿了大量的過時觀念、多餘觀念、錯誤觀念 也就是很多小功能、小語法,要嘛是已經很少人在用了,要嘛是重複設計了,要嘛是根本設計失敗了。這些學了也用不到 偏偏你在補習班或者上網自學的時候,沒有人敢跟你這樣講。因為講的人自己也不太確定、沒有真正深入了解 所以我在這門課程只引導你使用最基本的語法、用最基本的功能 你已經完成了所有作業,代表這些基本功你都順利掌握了 不要小看這種只專注在基本功的學習方法 把基本功摸熟的人,在有必要學習任何進階觀念的時候,會比別人更快融會貫通 --- 偷偷跟你說一句,即便是業界工程師、業界講師,大多數人對於程式設計、大型工具的許多觀念,也都是一知半解的 這沒有什麼問題,我也總是跟你說:能解決問題就好 比較有問題的是:不確定的東西,要知道自己不懂、承認自己也不確定。在有必要的時候,要知道往哪個方向去深究 除此之外,既然自己也不太確定,就不要跟別人說 XXX 寫法一定比 YYY 寫法好,諸如此類的話 在程式開發這個圈子,絕大多數意見,都只是主觀意見,卻被講得好像是科學證明過的公認客觀意見、有對錯之分。這絕非事實 請積極學習,但也請保持懷疑&批判的態度。面對新工具、新觀念,不需要感到太困擾或者焦慮 --- 翻閱 Vue 官方文件,你會發現 `Essentials` 這個段落,你幾乎通通讀完了 這些就是 Vue 的基礎觀念,已足夠你開發出各種易於維護、架構彈性的中型應用程式了 那麼該如何開發大型應用程式呢?那就需要更進一步學習模組化、元件等等的觀念 這些會在後續的課程提到! --- 消化、研究完本課程之後,關於 JavaScript 更多必學的基本觀念 請接著前往「自學網頁の嬰兒教材:JavaScript(八)」開始學習吧! https://codelove.tw/@howtomakeaturn/course/jqe6xX

JavaScript 系列七:第7課 ── 認識 Template Refs

## 課程目標 - 認識 Template Refs ## 課程內容 來讀一下 Template Refs 的說明 - https://vuejs.org/guide/essentials/template-refs.html 簡單來說,在我們使用了任何前端框架之後,就已經把「所有 DOM 更新相關」的工作,委託給了框架處理 我們從此只要寫框架提供的語法就好了,不要直接去操作 DOM! 不然的話,框架已經接手了整個 DOM,你又在某些地方對 DOM 做出更新,會引起衝突,會造成很多 bug! 而且這種 bug 很難處理,因為你不知道框架實際上是怎麼更新 DOM 的,需要深入研究框架背後的機制才行 所以才會說,用了框架,就請避免操作 DOM 但是,很多時候,會需要用一些現成的第三方套件(例如你學過的一堆 jQuery 外掛套件),那些套件初始化設定時,又都需要提供一個 selector 這種情況下,Vue 提供了 ref 功能,讓你可以存取「vue 底下真正的 DOM」,可以拿來跟其他小套件整合 --- 看到這邊,你可能會心想: 「那我如果不使用 ref,我就是要直接使用 `document.querySelector()` 來抓取 DOM 元素,然後作為參數放進其他小套件執行,會怎樣?」 老實講,根據我個人經驗,在絕大多數情況下,其實也根本不會怎麼樣,程式還是可以順利運作的 所以,你真的要這樣做,也是可以的 但如果真的遇到很難解決的 bug,例如抓不到 DOM 元素、抓錯 DOM 元素時,記得往這方向研究一下就是了! ## 課後作業 承接上一課的作業 在輸入記事內容的地方,為了加強 UX,我們來加上 autosize 功能 請使用此款套件 https://github.com/jackmoore/autosize 將 textarea 加上 autosize 功能吧! 做出以上功能,你就完成這次的課程目標了!

JavaScript 系列七:第6課 ── 認識 Lifecycle Hooks 與 Watchers

## 課程目標 - 認識 Lifecycle Hooks 與 Watchers ## 課程內容 來認識一下 Lifecycle Hooks - https://vuejs.org/guide/essentials/lifecycle.html 簡單來說,在 vue 執行階段,又可分為 `mounted` `updated` `unmounted` 等等,將近十個時間點 根據應用程式的需要,可以在這十個時間點,進行必要的檢查&執行特定任務 但是,實務上,除了 `mounted` 經常用來跑一些初始化任務之外,其餘的時間點,幾乎不會用到 所謂 Lifecycle Hooks 是一個有點過時的觀念!稍微讀一下就好,相關功能大多被 Watchers 取代了 --- 接著就來認識一下 Watchers - https://vuejs.org/guide/essentials/watchers.html 簡單講,在 Vue 中,所謂的 Reactivity 已經很好用了: 「某些變數在更新之後,其他相關變數、以及畫面 UI,會跟著自動更新」 除此之外,還可以進一步使用更進階功能: 「隨時監控某些變數,如果發現它們更新,就執行一些相關的任務」 這些任務我們稱之為 `副作用(side effects)` 關於 side effects 怎麼解釋,是一個比較困難的主題 這邊先不詳談,目前有點困惑沒關係,作業中會讓你練習一下,先有點小感覺就好 ## 課後作業 承接上一課的作業 這一課要來做自動儲存功能 請使用 Watchers 去監聽 `notes` 狀態 每次 `notes` 有更新,就自動儲存到 Local Storage 裡面! 接著,在 `mounted` 裡面檢查 Local Storage 如果有資料,就把資料更新到 `notes` 裡面 如此一來,就完成了自動儲存功能,連儲存按鈕都不用做! --- 提示:普通的 watchers 不夠用,要使用 Deep Watchers 這部份有點難解釋,你先知道:普通的 data 變數型態,例如字串、數字、布林,可以用普通的 watcher 但如果是陣列或物件型態的 data,就要使用 Deep Watchers 先知道這樣就好 --- 做出以上功能,你就完成這次的課程目標了!

JavaScript 系列七:第5課 ── 學習 Vue 事件處理的寫法

## 課程目標 - 學習事件處理的寫法 ## 課程內容 來學一下事件處理的寫法 - https://vuejs.org/guide/essentials/event-handling.html 眼花撩亂沒關係,我已經多次告訴過你應該用什麼心態去面對 不用把這些東西看得太神奇,這些東西的背後,就只是你已經寫過的 html `onclick` 那些東西而已,只是多加一層「抽象化」,由 Vue 中介在中間管理 DOM 我們在寫 Vue 的時候,就是「完全不親自操作 DOM」了,通通交由 Vue 處理 我們負責專心管理 application state 就好,不用費心思去管理 DOM 用了框架,就不要再去直接操作 DOM,否則會跟框架起衝突、讓程式充滿 bug --- 有件事我必須說一下,就是 `@click` 後面可以放函式名稱,也可以直接執行函式 這個設計,實在很糟糕!我非常不喜歡! ``` <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> <div id="app"> <button @click="hello1"> hello 1 </button> <button @click="hello2('John')"> hello 2 </button> </div> <script> const { createApp } = Vue createApp({ methods: { hello1() { alert('hi'); }, hello2(name) { alert('hello ' + name) } } }).mount('#app') </script> ``` 以上範例,在 `@click` 裡面的內容,到底要不要加上小括號 `()`? 乍看之下,根本無法理解。要翻閱文件,才發現原來兩種寫法都可以 我認為這違反了程式碼風格應該要 `保持一致性(consistency)` 的原則 Vue 這種彈性設計,讓程式碼變得令人困惑、難以理解 --- 再提一件事,Vue 的 template 中,雙引號的內容,到底是 `變數` 還是 `字串`? ``` <button @click="greet">Greet</button> ``` 這個 greet 是函式名稱,是變數 ``` <div :class="{ active: isActive }"></div> ``` 這裡在雙引號內容,宣告 JSON 物件,所以是變數 ``` <template v-if="ok"> ``` 這裡是變數,變數型態大概是布林 ``` <div class="active text-danger"></div> ``` 這裡是字串 ``` <input :value="text" @input="event => text = event.target.value"> ``` 這邊的 `text` 到底是字串還是變數? ``` <input ref="input"> ``` 這邊的 `input` 到底是字串還是變數? 透過觀察可以發現,冒號開頭 `:`,或者小老鼠開頭 `@` 或者縮寫法 `v-` 開頭的,雙引號裡面是變數 不然的話,就跟普通 html attribute 一樣,雙引號裡面就只是字串而已 對於 Vue 這種彈性設計,我一樣「非常不喜歡」! 當然,這些都是我個人主觀看法,一定很多人覺得又好寫、又彈性 這對於只想快速開發、懶得追根究底的新手,可能很方便 但在深入思考、試著搞懂資料型別、試著像資深工程師一樣思考的時候,會造成很多額外的困惑 --- 我想說的是,遇到這類問題,你就多試一下、多翻官網文件 然後不要覺得自己很笨、被框架嚇傻了 要知道複雜的工具,本身背後就代表多種不同的主觀偏好,然後那些主流觀點,可能真的不怎麼樣,所以不是你的問題 隨著更有經驗之後,你會能夠做出各種主觀的判斷、能夠比較各種設計的優劣之處 目前階段,你就先想辦法讓程式能跑、能完成任務就好了! --- 再來學一下表單欄位內容的處理 - https://vuejs.org/guide/essentials/forms.html 在上個系列課程中,我們學過 data model + render function 的寫法 使用框架之後,則變成,就連表單欄位內容,也是 data model 的一部份 乍聽之下有點怪,其實,實際寫過之後,會發現還滿方便的! ``` <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> <div id="app"> <input v-model="username"> <button @click="greet"> Greet </button> <h1> {{ username }} </h1> </div> <script> const { createApp } = Vue createApp({ data() { return { username: "John Doe" } }, methods: { greet() { alert('Hello, ' + this.username); } } }).mount('#app') </script> ``` 正因為把 `欄位中目前輸入的值` 視為 `當前應用程式的狀態` 的一部份 所以可以隨時取得表單欄位內容,而不用像以前那樣,要透過 DOM selector 之類的,去找到元素、才拿到內容! 一樣,表單欄位內容的取得,寫法多種,就挑順眼、簡單的來用即可 ## 課後作業 承接上一課的作業 這次要來開發「新增」與「刪除」功能囉! 請在記事列表的上方: - 新增一個 `input` 來輸入「記事標題」 - 新增一個 `textarea` 來輸入「記事內容」 - 新增一個 `select` 來選擇顏色 - 新增一個「新增」按鈕,點擊之後,會新增記事 - 請將這些欄位的內容,放在 application state 裡面管理 然後,請在每則記事的裡面,新增一個「刪除」按鈕。點擊之後,會刪除記事 做出以上功能,你就完成這次的課程目標了!