為什么大爺大媽們總對排隊情有獨鐘,為什么平常不想要的東西一打折就買一堆……震驚!原來套路你的竟然都是自己的大腦!
如今想要成為一名合格的用戶體驗設計師,首要就是對目標用戶的理解和把控。而為了實現這一目標,掌握一些心理學知識就顯得尤為重要。當我們能在設計中合理利用心理學的時候,就離創造出讓用戶感慨「深得朕心」的體驗不遠了。
這次要介紹的是設計心理學中與我們日常生活密切相關的認知偏差 (Cognitive bias) ,了解它的基本定義之后再結合相關案例探討如何在設計中利用認知偏差來創造更好的用戶體驗。
網絡上的定義是這樣的:人們在知覺自身、他人或外部環境時,常因自身或情境的原因使得知覺結果出現失真的現象。
簡單來說,就是大腦創造了一些快捷方式,在處理信息時自然地去調用這些快捷方式,只是這種操作在快速的同時,也會對我們的決策過程產生危害,比如我們會選擇性忽略一些信息,或是自發地對信息進行腦補。這樣的認知模式導致我們產生了非理性的偏差。
△ 圖:大腦的操作
認知偏差種類豐富,已經被提出的就有幾百種,有很多尚未被完全驗證。下圖總結了現有的認知偏差,它們大概可以分為四類:信息過載,信息的意義不明確,大腦來不及認真作出反應以及大腦存不下所有的記憶。通過這張圖,我們可以更清晰地了解不同認知偏差背后的成因。
△ 圖:認知偏差寶典
那我們該怎么在設計中利用認知偏差呢?我根據日常工作中經常接觸的消費場景和學習場景總結了以下幾個方面:
△ 圖:章節概覽,也叫懶人速讀版
作為體驗設計師,我們需要為用戶的決策創造合適的環境,來引導其按照我們設計的方向去達成他們的目標。
錨定效應
錨定效應(Anchoring)是比較常見的一個被利用在消費場景中的認知偏差。它指的是人在做決定的時候,會在很大程度上依賴于其接觸到的信息。
比如商場里原價 2000 現價 500 的商品,原價的存在會讓人覺得這件商品的價值就是 2000,現在 500 簡直是撿了大便宜。
在體驗設計中可以借鑒這種做法,通過前后對比放大來優惠感知,進而促進用戶做出有利于我們的決策。
△ 圖:利用錨定效應推薦會員套餐,對比差價確實誘人
從眾效應
從眾效應(Bandwagon Effect)是另外一個常見的用于消費場景的認知偏差,指的是人們做決定時通常會和他人保持一致。
下圖是售前頁一般的設計技巧,通過展示購買人數和滾動播放購買信息來體現出該商品的熱門,讓正在猶豫的用戶「隨大流」下單購買。
基于此,我們在設計中可以營造出一種群體選擇的效果來吸引更多的用戶。
△ 圖:這么多人選擇,跟隨大家不會錯,買它
內群體偏差
前面提到的認知偏差之外,內群體偏差 (ingroup bias) 也一般被用來引導用戶決策,它是指人們會在認知上傾向于自己所屬的群體。
比如 Booking 在查看評論的區域加入了篩選評論語言這一按鈕,雖然設計的本意可能是為了方便用戶更好地理解評論內容,但是在真實的使用過程中可以發現,用戶更加信任自己所選標簽內的評論內容,因為同語言往往意味著來自相同的國家或者相近的文化背景,用戶通過這種方式找到一個小群體,然后被影響進而做出與群體內人們更為相似的決策。
△ 圖:同胞的評價更可靠
因此在體驗設計中利用內群體偏差的關鍵點在于打造群體歸屬感,借由小群體的力量影響用戶的決策。
引導用戶做出于我們有利的決策之外,同樣,我們可以利用認知偏差提高用戶粘性,增強用戶和產品之間的聯系,使他們對產品「愛不釋手」。
宜家效應
宜家效應 (The Ikea Effect) 是指消費者對于自己投入勞動、情感而創造的物品的價值產生高估的價值判斷偏差的現象,消費者對于一個物品付出的勞動(情感)越多,就越容易高估該物品的價值。利用宜家效應提高用戶粘性的核心是創造低投入、高回報、高貢獻價值的任務,保證用戶能夠完成任務的基礎上貢獻自己的價值。
在學習場景中我們可以利用宜家效應提高用戶粘性,將用戶留下來堅持學習。
首先需要保證用戶能夠完成任務。懂你英語 ®A? 產品中,我們會在用戶設置目標時描述該目標的表現,用戶以此選擇自己的目標。這種方式下的用戶對于任務結果的預見性提高,能清楚的知道如果完成任務會達到什么效果,更能被促使順利地完成任務。
△ 圖:目標展示與貢獻積累
其次,用戶對產品粘性增強需要能夠感知到自己所做出的投入,學習場景下這種投入感知更多體現在知識的累積上。在懂你英語 ®A? 課程中,我們將用戶與目標之間的距離設計為學習路徑,用戶每完成一個階段的學習便會在路徑上明顯前進,日積月累下來用戶能看到自己前進的印記,清晰感知到自身知識的累積,也就因此對產品有更高的價值感受。
負向偏見
由于學習的「反人性」,學習場景下的用戶在體驗流程中產生消極情緒的概率要大于其余場景,比如學習效果不好,難以堅持等。此類消極情緒對于學習產品影響很大,是因為負向偏見 (negativity bias) 的存在,人們對不好的事情的記憶比快樂的記憶更加清晰,更經?;貞?。
因此在學習場景下我們需要給用戶更多的正面積極的反饋來抵消掉負面體驗的影響。在懂你英語 ®A? 課程設計中,我們在學習結果頁根據用戶不同的學習表現給出不同的反饋,即使是偏低的成績,也依然會給出一個較為積極的反饋,以期鼓勵用戶堅持學習。
△ 圖:做得不好也不要灰心
除了簡單抵消掉用戶的負面偏見,我們甚至可以通過設計去完全扭轉局面,變困境為有趣的體驗。最經典例子便是谷歌斷網時的小恐龍游戲,不知道有多少人會故意關掉網絡來玩這個游戲。
△ 圖:谷歌的斷網小恐龍
作為設計師我們可以通過了解和利用認知偏差來創造既讓用戶滿意又平衡商業的雙贏體驗。但由于設計師本身也是人類,與用戶擁有同樣的思考機制,因此在日常的調研分析和設計的過程中也要警惕認知偏差的影響,不斷深入了解用戶以及使用科學的測試方法來完善自己的設計,持續迭代反思,不因為某個方案自己傾注了很多心血,就覺得它是最好的。用戶可能并不買賬呢。
最近設計項目中涉及到「瓷片區」,于是和一些設計伙伴請教了解了一下,在此記錄總結一下,也希望可以對大家有些小小的幫助。
聽說:美團內部將首頁的運營廣告位模塊稱為瓷片位,其可以根據需求變動靈活調整,就像瓷片一樣靈活適用,顧名思義,瓷片區也就被叫開來了。
根據下圖我們可以了解到,瓷片區在產品中的應用。
瓷片區作為與 Banner、金剛區并行的三大運營板塊,都負責著導流的功能。瓷片區較兩者更便于在頁面中進行布局,可以靈活復用。
在電商產品中,導流指的是通過某種形式,增加對商品/功能的曝光,使自己的用戶群去購買或了解感興趣的商品/功能。導流簡化流程:導流入口 → 落地頁 → 轉化率,設計師需要通過在這個流程中收集的數據,進行復盤反思優化設計。
瓷片區屬于運營區,在頁面中通常位于用戶容易點擊的區域。通常為圖文混排,常見的類型有:實物類、插畫類。
1. 實物類
應用場景:需要對商品/服務有高曝光度的產品類型,如外賣、電商、旅游類等,通過對自有商品/服務的直觀展示,達到吸引用戶的目的。
優點:識別度高、適配千人千面、元素更換靈活;
缺點:圖片質量要求較高;
2. 插畫類
應用場景:常見于金融類、虛擬類產品。
優點:高度概括主題的圖形,通過插畫增加產品的調性和趣味性;
缺點:針對性比較強,難復用,花費時間;
1. 排版
瓷片區常見排版方式:左右排版、上下排版和對角線排版。設計師可以將三種排版方式組合設計,使頁面更具有節奏感;
2. 圖片
對于電商或商城類產品來說,配圖的好壞是影響用戶點擊率最直觀的元素;設計瓷片區時需要考慮全局配圖和局部配圖的情況:
3. 文字
4. 背景
瓷片區背景常見類型:白色/淺色背景、漸變色背景;設計師根據產品調性及業務需求對背景進行靈活使用,但要遵守以下原則:
文中從四點對瓷片區進行了一個概括性的了解總結,設計要點的話在排版設計中都是需要注意的基礎知識所以未多加贅述,以此為自己在項目中遇到的知識點做一次沉淀。
文章來源:優設 作者:木子的小千世界
一轉眼,夏天就來了,設計中需要清新又抓人眼球的圖片,老板又要求使用各年齡段都喜歡的插畫風格,應用場景要廣,但是預算又沒有那么多,該怎么辦呢?這也不是熬夜就能熬出來的,不要煩惱,下面這個網站立馬解決你上面所有需求的同時,還能幫助提升你的工作效率,延緩你禿頭的時間。
這個創造性超強的插畫制作網站就是 BLUSH ,你可以根據自己的想法進行元素的替換,也可以直接使用 8 位網站合作設計師的現有作品,超清新的顏色和多樣的搭配空間,絕對會讓你十分滿意的,無論是做 Banner、海報還是用作文章中的插圖都十分合適。最重要的是,可以免費商用!接下來就跟著我一起,走進這個網站吧。
網站內擁有 12 大種類的插畫圖庫,包含人物、城市風景、植物、甜點、涂鴉等等。選擇你喜歡或者需要的一類,就可以開始你的創作。操作類似于換裝游戲,在各個你需要改變的地方更換你喜歡的元素就可以。如果你要摸清整個網站可以產生多少種搭配的話,可能需要花上你不止一天的時間哦。
從上圖可以看出,單在人物上,網站就提供了不同的風格,有像兒童形象的「Friendly ones」,也有極簡線條的「Big Shoes」,還有夸張超炫的「Power Moves」等等。接下來,為了更好的理解網站提供的服務和操作的方式,我選取實用多變的「Croods」來演示。
首先在欄目左側選擇「Croods」,就會看到下面這樣的界面。最上面是設計師的作品,下面則是可以由你自由創造作品的入口,造型可以選擇站著或者坐著,場景可以選擇談話、聚會、公園等等,整體的背景也有五大類供你選擇。
這里我選擇點擊「Peaceful Place」。網站出現的作品很符合現在多數人在家辦公的狀態,如果你不想自己動手,那直接選擇這張圖片或者點擊「Randomiza」再隨機生成一張圖片,自己滿意之后,點擊「Download」便可以獲取 PNG 格式的圖片,不過,高品質 PNG 和 SVG 格式則需要付費。
當然,你也可以通過改變圖片上的各個元素,生成你所需要的場景插畫。例如想要一個盤著腿坐在沙發上發消息,和朋友聊天聊的很開心的一個形象,就可以通過下方給予的元素進行個性化定制,把表情改成開心,電腦換成手機,右上方替換成聊天框,最后導出為下圖的樣子。如果想要多人的場景,在上一步選擇多人的場景進入就可以了。
同時,網站也支持下載插件,方便眾位設計師在 Figma 中更好的運用。在首頁點擊右上角的「Get Plugin」進入頁面,注冊賬號就可以順利安裝插件。大家可以根據自己的需要選擇下載插件或者直接在網站上在線制作。
最后,不得不提的一點是,網站圖庫在兼顧多樣性和平等性上做了很大的努力,人物的膚色可以隨你改變,殘疾人也有一席之地,可以選擇為人物匹配輪椅,或者給人物戴上假肢,涵蓋盡可能多的人物情況和場景。相信這樣的網站一定能滿足你豐富的設計需求,快去使用吧!
文字來源:優設 作者:山楂
在教程開始之前,我先說一下今天的教程我們會講哪些部分。首先我們會分析兒童風格的特點、然后在這些特點之上給大家演示繪制一些插畫小元素。
我們先來看一下這些海報里,是不是很容易就能分辨出,哪一個是關于兒童主題的畫面?
可能剛剛的會比較明顯,那這組是不是也同樣可以分辨出哪一個兒童感比較強的畫面?兒童感它既然能夠在這些風格里凸顯,它一定有一些特點。那么接下來我會通過幾個方面去分析這些特點。
我們要選用一些符合兒童風格的元素。植物是兒童畫里出現最常用的元素,動物作為自然界的一份子,當然也非常適合。一些兒童屬性的物品。比如書本、棒棒糖等。還有天空的云朵、房子這些兒童畫畫的時候經常出現的元素,都非常適合兒童畫面。
但是一些明顯不屬于兒童用品的元素就不合適了。比如說口紅,口紅不會讓人聯想到兒童,更多是成年女性。骷髏頭這種恐怖的物體也不能出現。
那我問一下,眼鏡適合嗎?如果是這種尖銳的眼鏡的話可能不太適合。
但是一旦它的外形變得圓潤可愛,就很合適。很多物品也像眼鏡一樣,只要把外形改變得更符合兒童的氣質,就不會顯得突兀。
假如某一些物體兒童化特征不明顯,而又想讓它有兒童感,那應該怎么做呢?加入表情,表情簡單可愛,就能表現兒童感的情緒。
每個物體加上表情就會變得可愛。但也正因為這些表情加在了本身沒有表情的物體上,所以風格里有一種搞怪感。
我把隨機下載的兒童海報拼接在一起,整體分析它們的特點。
兒童插畫的海報元素使用的色相比較多,這里隨機抽取的海報里,畫面都是使用了比較多的顏色,紅、黃、藍、綠基本都用上了。
在飽和度上都偏高,一般保持在 70% 以上。
明度方面,把畫面模糊再變黑白,我們可以看到整體畫面給我們的感覺是比較亮的,就連中間用了大色塊的海報的明度也在 50% 以上。而存在的一些深色也是作為畫面的點綴。
那么總結起來,一般是色相選取的顏色比較多、飽和度比較高、明度比較高。我們以這張圖為例。
當它的顏色種類變少的時候,畫面的活潑程度就會降低。
而飽和度變低后,不僅活潑度降低,這種顏色的風格會趨向于成人化。
畫面的顏色變深之后,畫面就沒有透氣感。有一個很合適的成語形容,叫「老氣橫秋」。就是缺乏朝氣的感覺。
那么兒童插畫的形體有什么特點?總結起來有兩個特點,簡單和隨意。首先兒童對世界的理解還不夠深,能力也不足,往往畫岀來的東西都是以概括的形式。
比如兒童畫一顆真實的樹的時候,去掉很多細節,把一個物體比較有特征的外形表現出來。比如兒童畫一顆樹可能是這樣畫的。
其次兒童的能力和經驗不夠,畫畫的時候邊緣都是比較不平滑的,看起來比較隨意。
而且就算本來在他眼中是對稱的物體,因為手繪的感覺而看起來歪歪扭扭。
當一個物體簡單成平滑的幾何形的時候,它是偏向于設計感和現代感的。當它越來越接近于隨意感覺的時候,它的兒童特征會更強,同時更親切。
就像這些作品,雖然是插畫師畫的,但是卻有很強的兒童感,就是這種簡單和隨意再加上插畫師的審美形成的畫面。
要注意的是簡單和隨意的把握,太過隨意,商業化的價值就不高了。
兒童海報里的插畫雖然每個元素比較簡單隨意,但是要保持畫面比較完整,是需要很多元素的拼湊的。
就像這個幼兒園品牌的元素都特別隨意,但是最后呈現的效果卻也不錯,除了在延伸上花了很多心思之外,還因為它的元素比較多,看起來不至于太單薄。
而這些元素的擺放可以不遵循嚴格的透視和邏輯,這種方式體現了兒童繪畫和成年人嚴格的繪畫原理的不同。
能滿足上面的特點的兒童風格有很多,在這里我打算用一種比較簡單的繪制風格,來進行操作的演示。
首先我們畫一顆簡單的樹,來看一下這個風格。外形不會追求非常的對稱。
就連樹干我也是用「畫筆工具」和鼠標畫的。營造一種隨意感。
在這里調整一下最后整體的外形后,我們會發現它的邊緣和線條都太順了。沒有手繪的自然感。
所以我們要用到「變形工具」,對邊緣進行推拉,這樣它的邊緣就不會特別光滑了。
最后調整一下尖銳的錨點,樹就這么簡單地完成了。
大家可能會覺得這個風格過于簡單了,我用畫動物,去講解一下其中的細節。首先還是用「鋼筆工具」,把形體輪廓和主要部位畫出來。 大家是不是覺得畫到這里就完成了?其實這個風格比較簡單,如果沒有一些裝飾感的細節的話就會顯得太過隨意了。所以在這只鳥里,要去想怎么添加一些細節。
首先可以加個腮紅。腮紅的顏色不一定是紅的。像鳥的身體色塊面積太大了,需要有裝飾,所以畫幾條羽毛。羽毛的形狀太過規矩了,還是需要轉曲用「變形工具」調整。
還有尾巴的部分也是需要用線的形式讓整體更有細節感。最后都是要用「變形工具」讓它的線條有種隨意感。
最后的細節在嘴巴上添加,直接畫一條直線。其實添加細節就是不要出現一整大塊的純色色塊。
對于物體的簡化不用太過于死板,比如我們現在看到的這個植物,它可以直接簡化成圓形和密集的點的組合。
一開始我是直接用「剪切蒙板」的,但是這樣邊緣過于光滑了,而且里面的點也被框住,不夠美觀。于是就把點的分布超出圓形一點。這樣看起來就比較有細節了。
而我們通過改變形體,就可以演變出很多種不一樣的形態。比如這個物體,我在做的時候想能不能把中間的圓縮小一點 。黑色的點占比再大一點。這樣就產生了另外一種植物。
甚至還可以直接去掉中間的圓,把點再放大作為主體。原本的點放大會變得太過隨意。所以只能通過畫圓形和「變形工具」調整。
這樣又可以得到不一樣的一顆植物了。這樣的演變可以根據畫面的需求選取不一樣的植物。
所以在這里我已經畫好一套常用元素的素材了。以植物動物為主,基本能滿足日常設計中的需求了。這套素材會分享給大家,在我們的訂閱號后臺回復「兒童節素材」就可以下載。這套素材是允許大家商用的。有了素材后,怎么用呢,接下來我將用這套素材演示它能怎么使用。
這些素材可以在畫面作為點綴營造氣氛。
直接使用這些素材,堆疊成完整的畫面。
也可以和圖片中的人物或者產品做穿插。
接下來用一個案例演示一下,我是怎么用素材的。這個是學而思的手機端海報的文案,主要有標題部分、課程內容、和二維碼等。
首先建立一個手機端尺寸。設置好版心的大小。在這里我做的是下方卡片式的結構。(白色色塊部分是海報)
可以先把文案編排進來。首先安排好大的元素,二維碼和活動價格。主講老師和上課時間的小標題,字號大小要保持一致??ㄆ锏男畔⒕途幣磐炅恕?
標題是畫面最大的字號放在上方,副標題和補充信息和大標題居中。接下來就是要在畫面中添加我們的素材了。那么怎么選擇呢?首先要定好你要選擇的元素范圍,不要同時出現太多種類的元素。以植物為主、再加上幾個元素點綴是一般不會出錯的選擇。
首先添加的是面積比較大、塊面化比較明顯的元素。這里就加了三棵簡單的樹。
接下來就是添加一些更有裝飾感的花花草草。
在添加元素的時候也要注意畫面點、線、面的結合。就比如現在我們添加的元素主要是以面和點為主。
雖然樹的樹干也有線,但是輪廓的線條太「順」了。
所以要添加「線」的特征更明顯一點的元素。
在畫面的上方加上適合存在畫面上方的元素、比如長頸鹿因為本身比較高,所以沒問題。把標題移到適合的位置,加蒲公英作為點綴。最后只剩下中間的部分怎么做出和主題相關聯的畫面了。
我們還有一個信息可以放在人物的手上。最后一個元素就從海報的主題出發。既然是學習的內容,可以畫一本書,放在背影上。元素的拼湊完成了,最后就是細節的添加。
現在畫面下方的信息不夠突出,是因為卡片和背景都是白色的,所以下方加上一個深色的色塊,讓信息更顯眼。
突出的信息可以變顏色。
背景變成黃白色,最后加上 logo?,F在畫面好像完成了,但是我最后還發現了一個問題,畫面上的顏色飽和度都很高,各種顏色之間碰撞,看起來有點透不過氣的感覺。
是因為畫面缺少淺色。所以我選了兩個花把它變成白色。
現在這個素材使用的案例就完成了。
最后要說明,這一套素材它只是一種風格,我們教程前面有說到的任何一個因素的改變都可以演變成無數的風格。
比如它的顏色搭配也可以變,變成一種更亮眼的顏色?;蛘哒f它變得更隨意,有筆觸的自然感。甚至還可以是畫畫技法風格變化。也可以為每一個元素都加上眼睛,一種搞怪感就出現了。所以是有無限的可能性的,最重要的是要學以致用。
做好畫面還不夠,我們畫面選擇的字體也是很重要的,那么我覺得這幾款字體做得都不錯。
文章來源:優設 作者:研習設
什么是圖片懶加載
當我們向下滾動的時候圖片資源才被請求到,這也就是我們本次要實現的效果,進入頁面的時候,只請求可視區域的圖片資源這也就是懶加載。
比如我們加載一個頁面,這個頁面很長很長,長到我們的瀏覽器可視區域裝不下,那么懶加載就是優先加載可視區域的內容,其他部分等進入了可視區域在加載。
這個功能非常常見,你打開淘寶的首頁,向下滾動,就會看到會有圖片不斷的加載;你在百度中搜索圖片,結果肯定成千上萬條,不可能所有的都一下子加載出來的,很重要的原因就是會有性能問題。你可以在Network中查看,在頁面滾動的時候,會看到圖片一張張加載出來。
lazyLoad
為什么要做圖片懶加載
懶加載是一種網頁性能優化的方式,它能極大的提升用戶體驗。就比如說圖片,圖片一直是影響網頁性能的主要元兇,現在一張圖片超過幾兆已經是很經常的事了。如果每次進入頁面就請求所有的圖片資源,那么可能等圖片加載出來用戶也早就走了。所以,我們需要懶加載,進入頁面的時候,只請求可視區域的圖片資源。
總結出來就兩個點:
1.全部加載的話會影響用戶體驗
2.浪費用戶的流量,有些用戶并不像全部看完,全部加載會耗費大量流量。
懶加載原理
圖片的標簽是 img標簽,圖片的來源主要是 src屬性,瀏覽器是否發起加載圖片的請求是根據是否有src屬性決定的。
所以可以從 img標簽的 src屬性入手,在沒進到可視區域的時候,就先不給 img 標簽的 src屬性賦值。
懶加載實現
實現效果圖:
imgLazyLoad
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
display: flex;
flex-direction: column;
}
img {
width: 100%;
height: 300px;
}
</style>
</head>
<body>
<div>
<img data-src="https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657907683.jpeg">
<img data-src="https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657913523.jpeg">
<img data-src="https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657925550.jpeg">
<img data-src="https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657930289.jpeg">
<img data-src="https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657934750.jpeg">
<img data-src="https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657918315.jpeg">
</div>
</body>
</html>
監聽 scroll 事件判斷元素是否進入視口
const imgs = [...document.getElementsByTagName('img')];
let n = 0;
lazyload();
function throttle(fn, wait) {
let timer = null;
return function(...args) {
if(!timer) {
timer = setTimeout(() => {
timer = null;
fn.apply(this, args)
}, wait)
}
}
}
function lazyload() {
var innerHeight = window.innerHeight;
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
for(let i = n; i < imgs.length; i++) {
if(imgs[i].offsetTop < innerHeight + scrollTop) {
imgs[i].src = imgs[i].getAttribute("data-src");
n = i + 1;
}
}
}
window.addEventListener('scroll', throttle(lazyload, 200));
可能會存在下面幾個問題:
每次滑動都要執行一次循環,如果有1000多個圖片,性能會很差
每次讀取 scrollTop 都會引起回流
scrollTop跟DOM的嵌套關系有關,應該根據getboundingclientrect獲取
滑到最后的時候刷新,會看到所有的圖片都加載了
IntersectionObserver
Intersection Observer API提供了一種異步觀察目標元素與祖先元素或文檔viewport的交集中的變化的方法。
創建一個 IntersectionObserver對象,并傳入相應參數和回調用函數,該回調函數將會在目標(target)元素和根(root)元素的交集大小超過閾值(threshold)規定的大小時候被執行。
var observer = new IntersectionObserver(callback, options);
IntersectionObserver是瀏覽器原生提供的構造函數,接受兩個參數:callback是可見性變化時的回調函數(即目標元素出現在root選項指定的元素中可見時,回調函數將會被執行),option是配置對象(該參數可選)。
返回的 observer是一個觀察器實例。實例的 observe 方法可以指定觀察哪個DOM節點。
具體的用法可以 查看 MDN文檔
const imgs = [...document.getElementsByTagName('img')];
// 當監聽的元素進入可視范圍內的會觸發回調
if(IntersectionObserver) {
// 創建一個 intersection observer
let lazyImageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach((entry, index) => {
let lazyImage = entry.target;
// 相交率,默認是相對于瀏覽器視窗
if(entry.intersectionRatio > 0) {
lazyImage.src = lazyImage.getAttribute('data-src');
// 當前圖片加載完之后需要去掉監聽
lazyImageObserver.unobserve(lazyImage);
}
})
})
for(let i = 0; i < imgs.length; i++) {
lazyImageObserver.observe(imgs[i]);
}
}
源碼地址-codePen點擊預覽
vue自定義指令-懶加載
Vue自定義指令
下面的api來自官網自定義指令:
鉤子函數
bind: 只調用一次,指令第一次綁定到元素時調用。在這里可以進行一次性的初始化設置。
inserted: 被綁定元素插入父節點時調用 (僅保證父節點存在,但不一定已被插入文檔中)。
update: 所在組件的 VNode 更新時調用,但是可能發生在其子 VNode 更新之前。指令的值可能發生了改變,也可能沒有。但是你可以通過比較更新前后的值來忽略不必要的模板更新
componentUpdated: 指令所在組件的 VNode 及其子 VNode 全部更新后調用。
unbind: 只調用一次,指令與元素解綁時調用。
鉤子函數參數
指令鉤子函數會被傳入以下參數:
el:指令所綁定的元素,可以用來直接操作 DOM。
binding:一個對象,包含以下 property:
name:指令名,不包括 v- 前綴。
value:指令的綁定值,例如:v-my-directive="1 + 1" 中,綁定值為 2。
oldValue:指令綁定的前一個值,僅在 update 和 componentUpdated 鉤子中可用。無論值是否改變都可用。
expression:字符串形式的指令表達式。例如 v-my-directive="1 + 1" 中,表達式為 "1 + 1"。
arg:傳給指令的參數,可選。例如 v-my-directive:foo 中,參數為 "foo"。
modifiers:一個包含修飾符的對象。例如:v-my-directive.foo.bar 中,修飾符對象為 { foo: true, bar: true }。
vnode:Vue 編譯生成的虛擬節點。
oldVnode:上一個虛擬節點,僅在 update 和 componentUpdated 鉤子中可用。
實現 v-lazyload 指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
img {
width: 100%;
height: 300px;
}
</style>
</head>
<body>
<div id="app">
<p v-for="item in imgs" :key="item">
<img v-lazyload="item">
</p>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
Vue.directive("lazyload", {
// 指令的定義
bind: function(el, binding) {
可視化是利用計算機圖形學和圖像處理技術,將數據轉換成圖形或圖像在屏幕上顯示出來,再進行交互處理的理論、方法和技術。面對醫療行業,如何將行業數據,轉化為視覺可視化中的點線面,是在這個項目中需要思考的問題。
本文將帶來設計師在醫療院感可視化項目中的設計過程及思考,講述如何在業務場景下對數據進行抽取表達。通過可視化打破傳統院感系統的表單呈現,使院感場景具備互動性、觀賞性,滿足不同角色的使用需求。同時院感醫生通過可視化的解決方案能清晰直觀的了解到院感發生分布、病例分析,從而控制院感發生和預防。
本項目以浙江省建德市第一人民醫院為案例,地理數據以建德醫院坐標為準。
項目背景
院感是什么?院感為醫院感染,入院48小時內都有可能感染到院感細菌。在醫院里有專門的院感醫生職位,對醫院感染進行有效的預防與控制。而傳統院感管理的工作流:醫護人員及院感醫生 > 院感系統分析疑似病例 > 得出結論預防或治療。這種偏人工的方式數據獲取方式,無法更的獲取院感發生的原因、定位、以及未來的院感預測。
P1 因此我們通過對醫院數據的整理,抽離出影響院感的數據,將院感發生、發展、管控、治療、預測全流程進行整合。
P2 通過醫院>樓層>人員三個層面,空間和時間兩個維度對院感展示。打破傳統院感系統的表單呈現,使院感場景具備互動性、觀賞性,滿足不同角色的使用需求:如院長的展示性需求。院感醫生通過可視化的解決方案能清晰直觀的了解到院感發生分布、病例分析,從而控制院感發生和預防。
P3 同時在這樣的設計場景下,可以覆蓋的醫療業務場景和數據單位也會更多元,具有一定的商業化價值。
P4 設計流程
整個項目的設計流程可以分為4個階段:信息收集、可視化、線上搭建、效果調試。在此項目實踐中,重點投入在前三大部分。
P5 Part1信息收集
我們基于項目背景,梳理要展現的數據指標,對整體業務場景進行故事腳本的規劃(即如何展現前期的數據收集,并把其串聯在整體業務場景中),設定動作攝像機語言,同時也需要了解最終呈現的硬件設備與使用環境。
P6 Part2可視化
1.交互信息框架
首先梳理院感的信息框架和交互方式。
整個大屏分為院樓層,呈現整體院感數據的統計;樓層屏,作為重點病區的監測預測;個人屏,分析病例回溯病程,從而預測院感。三屏之間相互跳轉, 交互演示方式從醫院的外部跳轉內部結構、再到患者的個人維度,三屏都分別展示相關的數據指標。
P7 2.視覺風格
在大屏視效風格探索上,期初的目標是希望可以打造不一樣行業視覺語言,所以選擇不同于以往的設計大屏風格,選擇白色的風格,符合大家對醫護行業的認知。但到中期發現,在硬件設備上展示發是過曝的。因此對設計風格進行調整改為X光片的風格,色系上偏冷綠的感覺。這是在這個項目中的試錯經驗之一。
P8 3.建模設計
在可視化部分中遇到的難點:建筑模型的高還原。下圖為建德第一人民醫院實拍圖。在大屏項目中,必須真實還原地理位置。而在此醫院沒有清晰的CAD圖紙提供的;在Google的衛星地圖下也沒有的建筑結構的,所以我在建模的過程中,是踩了坑的,先盲畫了一版,但是精細度不夠,過于粗糙。
P9 因此我反復看得到的資料,通過在確定地理氛圍內,去豐富場景。這樣的好處是使業務場景更加豐富,包括擴展到院外的車流數據,為業務場景提供更多可能性 當然后期也搖到了建筑內部的消防圖,根據消防圖繪制內部結構。
P10 4.數據面板
對可視化組件的組件進行設計:時間篩選、數據統計、占比關系、趨勢分析。設計之前也參考了很多概念版的可視化設計,希望在院感屏上可以呈現一種科技概念的感覺。
P11 Part3線上搭建
1.獲取地理數據
這部分是非常耗時的,datav是數據驅動的可視化產品,在搭建部分,是全程依靠datav平臺的。
首先需要明確地理數據,通過高德數據通過點線成面,可以作為場景定位,也就是物理模型的經緯度數據 后面還原數據效果,造虛擬數據,是非常依賴于這個坐標數據的。
119.291724 , 29.472365
這是建德醫院的坐標,醫院在地圖上的數據是很簡化的,顆粒度很大,具體位置無法顯示。
P12 因此我們需要建立與地理數據綁定的建模,先對位置。
P13 在這個過程中我發現,如果最開始沒有對準位置,也不用緊張,可以在DATAV平臺增加hook數據過濾器,解決地理數據與世界坐標無法對齊的問題。
2.線上場景還原
根據對確定過位置模型進行烘焙還原。這個過程中遇到了一些不知名的原因烘焙失敗,原因可能是命名有中文/位置數據錯誤/模型塊面復雜等,遇到這樣的問題就需要重新從頭檢查烘焙流程每一步。
P14 3.數據維度展示還原
這一步我們需要把前期做好的數據可視化效果,還原到線上模型中。在這一步我遇到的問題是因建德醫院內部具體結構的缺失,使一些可視化效果無法精準匹配到模型上。所以設計過程中只能依賴于在對的地理位置上豐富的場景內造數據,這個過程是比較吃力的。
P15 這個問題的解決辦法是通過開發工具和導出的結構俯視圖,對位置,然后轉化出LEGO的數據
P16 在數據效果還原的過程中,也發現我在前期設計的數據效果,不能全部實現,有些是依賴于開發的 。這時可以通過其他組件效果代替嘗試,比如熱力的效果用粒子放大,通過參數調節得到熱力 再比如局部房間的掃管,通過設計部分多次烘焙模型,不斷疊加掃光層,得到房間監測的效果
P17 設計小結
綜合以上的經驗,院感可視化從設計到落地,整體結構應該是這樣從物理基礎坐標的獲取、到場景搭建、再到數據展示的過程。在這個過程中會用到DATAV、C4D、數據庫、簡單的代碼等技術來實現。
P18 這個項目雖然這只是醫療行業中一個小的業務場景,但我們的業務數據提取及可視化設計思路,他不僅限于醫療行業,同時也可以成為場館類大屏解決方案的一部分,是具有一定商業化價值的。同時在這過程中沉淀下來的人體結構模型,和一些設計經驗,是可以復用到對應行業解決方案中,達到提效。
轉自:
火車車次
/^[GCDZTSPKXLY1-9]\d{1,4}$/
手機機身碼(IMEI)
/^\d{15,17}$/
必須帶端口號的網址(或ip)
/^((ht|f)tps?:\/\/)?[\w-]+(\.[\w-]+)+:\d{1,5}\/?$/
網址(url,支持端口和"?+參數"和"#+參數)
/^(((ht|f)tps?):\/\/)?[\w-]+(\.[\w-]+)+([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])?$/
統一社會信用代碼
/^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$/
迅雷鏈接
/^thunderx?:\/\/[a-zA-Z\d]+=$/
ed2k鏈接(寬松匹配)
/^ed2k:\/\/\|file\|.+\|\/$/
磁力鏈接(寬松匹配)
/^magnet:\?xt=urn:btih:[0-9a-fA-F]{40,}.*$/
子網掩碼
/^(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){3}$/
linux"隱藏文件"路徑
/^\/(?:[^\/]+\/)*\.[^\/]*/
linux文件夾路徑
/^\/(?:[^\/]+\/)*$/
linux文件路徑
/^\/(?:[^\/]+\/)*[^\/]+$/
window"文件夾"路徑
/^[a-zA-Z]:\\(?:\w+\\?)*$/
window下"文件"路徑
/^[a-zA-Z]:\\(?:\w+\\)*\w+\.\w+$/
股票代碼(A股)
/^(s[hz]|S[HZ])(000[\d]{3}|002[\d]{3}|300[\d]{3}|600[\d]{3}|60[\d]{4})$/
大于等于0, 小于等于150, 支持小數位出現5, 如145.5, 用于判斷考卷分數
/^150$|^(?:\d|[1-9]\d|1[0-4]\d)(?:.5)?$/
html注釋
/^<!--[\s\S]*?-->$/
md5格式(32位)
/^([a-f\d]{32}|[A-F\d]{32})$/
版本號(version)格式必須為X.Y.Z
/^\d+(?:\.\d+){2}$/
視頻(video)鏈接地址(視頻格式可按需增刪)
/^https?:\/\/(.+\/)+.+(\.(swf|avi|flv|mpg|rm|mov|wav|asf|3gp|mkv|rmvb|mp4))$/i
圖片(image)鏈接地址(圖片格式可按需增刪)
/^https?:\/\/(.+\/)+.+(\.(gif|png|jpg|jpeg|webp|svg|psd|bmp|tif))$/i
24小時制時間(HH:mm:ss)
/^(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d$/
12小時制時間(hh:mm:ss)
/^(?:1[0-2]|0?[1-9]):[0-5]\d:[0-5]\d$/
base64格式
/^\s*data:(?:[a-z]+\/[a-z0-9-+.]+(?:;[a-z-]+=[a-z0-9-]+)?)?(?:;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s]*?)\s*$/i
數字/貨幣金額(支持負數、千分位分隔符)
/^-?\d+(,\d{3})*(\.\d{1,2})?$/
數字/貨幣金額 (只支持正數、不支持校驗千分位分隔符)
/(?:^[1-9]([0-9]+)?(?:\.[0-9]{1,2})?$)|(?:^(?:0){1}$)|(?:^[0-9]\.[0-9](?:[0-9])?$)/
銀行卡號(10到30位, 覆蓋對公/私賬戶, 參考微信支付)
/^[1-9]\d{9,29}$/
中文姓名
/^(?:[\u4e00-\u9fa5·]{2,16})$/
英文姓名
/(^[a-zA-Z]{1}[a-zA-Z\s]{0,20}[a-zA-Z]{1}$)/
車牌號(新能源)
/[京津滬渝冀豫云遼黑湘皖魯新蘇浙贛鄂桂甘晉蒙陜吉閩貴粵青藏川寧瓊使領 A-Z]{1}[A-HJ-NP-Z]{1}(([0-9]{5}[DF])|([DF][A-HJ-NP-Z0-9][0-9]{4}))$/
車牌號(非新能源)
/^[京津滬渝冀豫云遼黑湘皖魯新蘇浙贛鄂桂甘晉蒙陜吉閩貴粵青藏川寧瓊使領 A-Z]{1}[A-HJ-NP-Z]{1}[A-Z0-9]{4}[A-Z0-9掛學警港澳]{1}$/
車牌號(新能源+非新能源)
/^(?:[京津滬渝冀豫云遼黑湘皖魯新蘇浙贛鄂桂甘晉蒙陜吉閩貴粵青藏川寧瓊使領 A-Z]{1}[A-HJ-NP-Z]{1}(?:(?:[0-9]{5}[DF])|(?:[DF](?:[A-HJ-NP-Z0-9])[0-9]{4})))|(?:[京津滬渝冀豫云遼黑湘皖魯新蘇浙贛鄂桂甘晉蒙陜吉閩貴粵青藏川寧瓊使領 A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9 掛學警港澳]{1})$/
手機號(mobile phone)中國(嚴謹), 根據工信部2019年公布的手機號段
/^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-7|9])|(?:5[0-3|5-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[1|8|9]))\d{8}$/
手機號(mobile phone)中國(寬松), 只要是13,14,15,16,17,18,19開頭即可
/^(?:(?:\+|00)86)?1[3-9]\d{9}$/
手機號(mobile phone)中國(最寬松), 只要是1開頭即可, 如果你的手機號是用來接收短信, 優先建議選擇這一條
/^(?:(?:\+|00)86)?1\d{10}$/
date(日期)
/^\d{4}(-)(1[0-2]|0?\d)\1([0-2]\d|\d|30|31)$/
email(郵箱)
/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
座機(tel phone)電話(國內),如: 0341-86091234
/^\d{3}-\d{8}$|^\d{4}-\d{7}$/
身份證號(1代,15位數字)
/^[1-9]\d{7}(?:0\d|10|11|12)(?:0[1-9]|[1-2][\d]|30|31)\d{3}$/
身份證號(2代,18位數字),最后一位是校驗位,可能為數字或字符X
/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0\d|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/
身份證號, 支持1/2代(15位/18位數字)
/(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0\d|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/
護照(包含香港、澳門)
/(^[EeKkGgDdSsPpHh]\d{8}$)|(^(([Ee][a-fA-F])|([DdSsPp][Ee])|([Kk][Jj])|([Mm][Aa])|(1[45]))\d{7}$)/
帳號是否合法(字母開頭,允許5-16字節,允許字母數字下劃線組合
/^[a-zA-Z]\w{4,15}$/
中文/漢字
/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])+$/
小數
/^\d+\.\d+$/
數字
/^\d{1,}$/
html標簽(寬松匹配)
/<(\w+)[^>]*>(.*?<\/\1>)?/
qq號格式正確
/^[1-9][0-9]{4,10}$/
數字和字母組成
/^[A-Za-z0-9]+$/
英文字母
/^[a-zA-Z]+$/
小寫英文字母組成
/^[a-z]+$/
大寫英文字母
/^[A-Z]+$/
密碼強度校驗,最少6位,包括至少1個大寫字母,1個小寫字母,1個數字,1個特殊字符
/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])(?=\S*[!@#$%^&*? ])\S*$/
用戶名校驗,4到16位(字母,數字,下劃線,減號)
/^[a-zA-Z0-9_-]{4,16}$/
ip-v4
/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
ip-v6
/^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i
16進制顏色
/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/
微信號(wx),6至20位,以字母開頭,字母,數字,減號,下劃線
/^[a-zA-Z][-_a-zA-Z0-9]{5,19}$/
郵政編碼(中國)
/^(0[1-7]|1[0-356]|2[0-7]|3[0-6]|4[0-7]|5[1-7]|6[1-7]|7[0-5]|8[013-6])\d{4}$/
中文和數字
/^((?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])|(\d))+$/
不能包含字母
/^[^A-Za-z]*$/
java包名
/^([a-zA-Z_][a-zA-Z0-9_]*)+([.][a-zA-Z_][a-zA-Z0-9_]*)+$/
mac地址
/^((([a-f0-9]{2}:){5})|(([a-f0-9]{2}-){5}))[a-f0-9]{2}$/i
使用 vue-router 的導航守衛鉤子函數,某些鉤子函數可以讓開發者根據業務邏輯,控制是否進行下一步,或者進入到指定的路由。
例如,后臺管理頁面,會在進入路由前,進行必要登錄、權限判斷,來決定去往哪個路由,以下是偽代碼:
// 全局導航守衛
router.beforEach((to, from, next) => {
if('no login'){
next('/login')
}else if('admin') {
next('/admin')
}else {
next()
}
})
// 路由配置鉤子函數
{
path: '',
component: component,
beforeEnter: (to, from, next) => {
next()
}
}
// 組件中配置鉤子函數
{
template: '',
beforeRouteEnter(to, from, next) {
next()
}
}
調用 next,意味著繼續進行下面的流程;不調用,則直接終止,導致路由中設置的組件無法渲染,會出現頁面一片空白的現象。
鉤子函數有不同的作用,例如 beforEach,afterEach,beforeEnter,beforeRouteEnter,beforeRouteUpdate,beforeRouteLeave,針對這些注冊的鉤子函數,要依次進行執行,并且在必要環節有控制權決定是否繼續進入到下一個鉤子函數中。
以下分析下源碼中實現的方式,而源碼中處理的邊界情況比較多,需要抓住核心點,去掉冗余代碼,精簡出便于理解的實現。
精簡源碼核心功能
總結下核心點:鉤子函數注冊的回調函數,能順序執行,同時會將控制權交給開發者。
先來一個能夠注冊回調函數的類:
class VueRouter {
constructor(){
this.beforeHooks = []
this.beforeEnterHooks = []
this.afterHooks = []
}
beforEach(callback){
return registerHook(this.beforeHooks, callback)
}
beforeEnter(callback){
return registerHook(this.beforeEnterHooks, callback)
}
afterEach(callback){
return registerHook(this.afterHooks, callback)
}
}
function registerHook (list, fn) {
list.push(fn)
return () => {
const i = list.indexOf(fn)
if (i > -1) list.splice(i, 1)
}
}
聲明的類,提供了 beforEach 、beforeEnter 和 afterEach 來注冊必要的回調函數。
抽象出一個 registerHook 公共方法,作用:
注冊回調函數
返回的函數,可以取消注冊的回調函數
使用一下:
const router = new VueRouter()
const beforEach = router.beforEach((to, from, next) => {
console.log('beforEach');
next()
})
// 取消注冊的函數
beforEach()
以上的回調函數會被取消,意味著不會執行了。
router.beforEach((to, from, next) => {
console.log('beforEach');
next()
})
router.beforeEnter((to, from, next) => {
console.log('beforeEnter');
next()
})
router.afterEach(() => {
console.log('afterEach');
})
以上注冊的鉤子函數會依次執行。beforEach 和 beforeEnter 的回調接收內部傳來的參數,同時通過調用 next 可繼續走下面的回調函數,如果不調用,則直接被終止了。
最后一個 afterEach 在上面的回調函數都執行后,才被執行,且不接收任何參數。
先來實現依次執行,這是最簡單的方式,在類中增加 run 方法,手動調用:
class VueRouter {
// ... 其他省略,增加 run 函數
run(){
// 把需要依次執行的回調存放在一個隊列中
let queue = [].concat(
this.beforeHooks,
this.afterHooks
)
for(let i = 0; i < queue.length; i++){
if(queue(i)) {
queue(i)('to', 'from', () => {})
}
}
}
}
// 手動調用
router.run()
打?。?
'beforEach'
'beforeEnter'
上面把要依次執行的回調函數聚合在一個隊列中執行,并傳入必要的參數,但這樣開發者不能控制是否進行下一步,即便不執行 next 函數,依然會依次執行完隊列的函數。
改進一下:
class VueRouter {
// ... 其他省略,增加 run 函數
run(){
// 把需要依次執行的回調存放在一個隊列中
let queue = [].concat(
this.beforeHooks,
this.afterHooks
)
queue[0]('to', 'from', () => {
queue[1]('to', 'from', () => {
console.log('調用結束');
})
})
}
}
router.beforEach((to, from, next) => {
console.log('beforEach');
// next()
})
router.beforeEnter((to, from, next) => {
console.log('beforeEnter');
next()
})
傳入的 next 函數會有調用下一個回調函數的行為,把控制權交給了開發者,調用了 next 函數會繼續執行下一個回調函數;不調用 next 函數,則終止了隊列的執行,所以打印結果是:
'beforEach'
上面實現有個弊端,代碼不夠靈活,手動一個個調用,在真實場景中無法確定注冊了多少個回調函數,所以需要繼續抽象成一個功能更強的方法:
function runQueue (queue, fn, cb) {
const step = index => {
// 隊列執行結束了
if (index >= queue.length) {
cb()
} else {
// 隊列有值
if (queue[index]) {
// 傳入隊列中回調,做一些必要的操作,第二個參數是為了進行下一個回調函數
fn(queue[index], () => {
step(index + 1)
})
} else {
step(index + 1)
}
}
}
// 初次調用,從第一個開始
step(0)
}
runQueue 就是執行隊列的通用方法。
第一個參數為回調函數隊列, 會依次取出來;
第二個參數是函數,它接受隊列中的函數,進行一些其他處理;并能進行下個回調函數的執行;
第三個參數是隊列執行結束后調用。
知道了這個函數的含義,來使用一下:
class VueRouter {
// ... 其他省略,增加 run 函數
run(){
// 把需要依次執行的回調存放在一個隊列中
let queue = [].concat(
this.beforeHooks,
this.beforeEnterHooks
)
// 接收回到函數,和進行下一個的執行函數
const iterator = (hook, next) => {
// 傳給回調函數的參數,第三個參數是函數,交給開發者調用,調用后進行下一個
hook('to', 'from', () => {
console.log('執行下一個回調時,處理一些相關信息');
next()
})
}
runQueue(queue, iterator, () => {
console.log('執行結束');
// 執行 afterEach 中的回調函數
this.afterHooks.forEach((fn) => {
fn()
})
})
}
}
// 注冊
router.beforEach((to, from, next) => {
console.log('beforEach');
next()
})
router.beforeEnter((to, from, next) => {
console.log('beforeEnter');
next()
})
router.afterEach(() => {
console.log('afterEach');
})
router.run();
從上面代碼可以看出來,每次把隊列 queue 中的回調函數傳給 iterator , 用 hook 接收,并調用。
傳給 hook 必要的參數,尤其是第三個參數,開發者在注冊的回調函數中調用,來控制進行下一步。
在隊列執行完畢后,依次執行 afterHooks 的回調函數,不傳入任何參數。
所以打印結果為:
beforEach
執行下一個回調時,處理一些相關信息
beforeEnter
執行下一個回調時,處理一些相關信息
執行結束
afterEach
以上實現的非常巧妙,再看 Vue-router 源碼這塊的實現方式,相信你會豁然開朗。
文章目錄
小白學VUE——快速入門
前言:什么是VUE?
環境準備:
vue的js文件
vscode
Vue入門程序
抽取代碼片段
vue標準語法:
什么是vue指令?
v-bind指令
事件單向綁定
v-model:事件雙向綁定
v-on事件監聽指令
v: on:submit.prevent指令
v-if 判斷指令
v-for 循環渲染指令
Vue.js(讀音 /vju?/, 類似于 view) 是一套構建用戶界面的漸進式框架。 Vue 只關注視圖層, 采用自底向上增量開發的設計。 Vue 的目標是通過盡可能簡單的 API 實現響應的數據綁定和組合的視圖組件。
環境準備:
vue的js文件
使用CDN外部導入方法
以下推薦國外比較穩定的兩個 CDN,把這些網址放進script標簽的src屬性下即可,國內還沒發現哪一家比較好,目前還是建議下載到本地。
Staticfile CDN(國內) : https://cdn.staticfile.org/vue/2.2.2/vue.min.js
unpkg:https://unpkg.com/vue/dist/vue.js, 會保持和 npm 發布的的版本一致。
cdnjs : https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.min.js
2.VSCODE軟件
(2).使用內部導入方法(自行下載js文件放進工作區js文件夾即可)
前往vscode官網下載對應版本的vscode
Vue入門程序
首先了解一下什么是插值
插值:數據綁定最常見的形式就是使用 **{{…}}(雙大括號)**的文本插值:
單獨抽出這段來看一下:
Vue即是vue內置的對象,el(element)指的是綁定的元素,可以用#id綁定元素,data指的是定義頁面中顯示的模型數據,還有未展示的methods,指的是方法
var app = new Vue({
el: "#app",//綁定VUE作用的范圍
data: {//定義頁面中顯示的模型數據
message: 'hello vue'
}
});
代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/vue.min.js"></script>
</head>
<body>
<!-- 插值表達式 獲取data里面定義的值 {{message}} -->
<div id="app">{{ message }}</div>
<script>
//創建一個VUE對象
var app = new Vue({
el: "#app",//綁定VUE作用的范圍
data: {//定義頁面中顯示的模型數據
message: 'hello vue'
}
});
</script>
</body>
</html>
步驟:文件-首選項-用戶片段
輸入片段名稱回車
{
"vh": {
"prefix": "vh", // 觸發的關鍵字 輸入vh按下tab鍵
"body": [
"<!DOCTYPE html>",
"<html lang=\"en\">",
"",
"<head>",
" <meta charset=\"UTF-8\">",
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">",
" <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">",
" <title>Document</title>",
" <script src=\"js/vue.min.js\"></script>",
"</head>",
"",
"<body>",
" <div id=\"app\"></div>",
" <script>",
" var vm=new Vue({",
" el:'#app',",
" data:{},",
" methods:{}",
" });",
" </script>",
"</body>",
"",
"</html>",
],
"description": "vh components"
}
}
此時,新建一個html文件,輸入vh在按下tab鍵即可快速填充內容
vue標準語法:
什么是vue指令?
在vue中提供了一些對于頁面 + 數據的更為方便的輸出,這些操作就叫做指令, 以v-xxx表示
類似于html頁面中的屬性 `
比如在angular中 以ng-xxx開頭的就叫做指令
在vue中 以v-xxx開頭的就叫做指令
指令中封裝了一些DOM行為, 結合屬性作為一個暗號, 暗號有對應的值,根據不同的值,框架會進行相關DOM操作的綁定
下面簡單介紹一下vue的幾個基礎指令: v-bind v-if v-for v-on等
v-bind指令
作用:
給元素的屬性賦值
可以給已經存在的屬性賦值 input value
也可以給自定義屬性賦值 mydata
語法
在元素上 v-bind:屬性名="常量||變量名"
簡寫形式 :屬性名="變量名"
例:
<div v-bind:原屬性名="變量"></div> <div :屬性名="變量"></div>
事件單向綁定,可以用 v-bind:屬性名="常量||變量名,綁定事件,用插值表達式取出值
<body>
————————————————
版權聲明:本文為CSDN博主「熱愛旅行的小李同學」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/m0_46275020/java/article/details/106055312
1. 訪問內部屬性
JavaScript 對象無法以常規方式訪問的內部屬性。內部屬性名由雙方括號[[]]包圍,在創建對象時可用。
內部屬性不能動態地添加到現有對象。
內部屬性可以在某些內置 JavaScript 對象中使用,它們存儲ECMAScript規范指定的內部狀態。
有兩種內部屬性,一種操作對象的方法,另一種是存儲數據的方法。例如:
[[Prototype]] — 對象的原型,可以為null或對象
[[Extensible]] — 表示是否允許在對象中動態添加新的屬性
[[PrivateFieldValues]] — 用于管理私有類字段
2. 屬性描述符對象
數據屬性包含了一個數據值的位置,在這個位置可以讀取和寫入值。也就是說,數據屬性可以通過 對象.屬性 訪問,就是我么平常接觸的用戶賦什么值,它們就返回什么,不會做額外的事情。
數據屬性有4個描述其行為的特性(為了表示內部值,把屬性放在兩對方括號中),稱為描述符對象。
屬性 解釋 默認值
[[Configurable]] 能否通過delete刪除屬性從而重新定義屬性;
能否修改屬性的特性;
能否把屬性修改為訪問器屬性 true
[[Enumerable]] 能否通過for-in循環返回屬性 true
[[Writable]] 能否修改屬性的值 true
[[Value]] 包含這個屬性的數據值 undefined
value 描述符是屬性的數據值,例如,我們有以下對象 :
let foo = {
a: 1
}
那么,a 的value屬性描述符為1。
writable是指該屬性的值是否可以更改。 默認值為true,表示屬性是可寫的。 但是,我們可以通過多種方式將其設置為不可寫。
configurable 的意思是可以刪除對象的屬性還是可以更改其屬性描述符。 默認值為true,這意味著它是可配置的。
enumerable 意味著它可以被for ... in循環遍歷。 默認值為true,說明能通過for-in循環返回屬性
將屬性鍵添加到返回的數組之前,Object.keys方法還檢查enumerable 描述符。 但是,Reflect.ownKeys方法不會檢查此屬性描述符,而是返回所有自己的屬性鍵。
Prototype描述符有其他方法,get和set分別用于獲取和設置值。
在創建新對象, 我們可以使用Object.defineProperty方法設置的描述符,如下所示:
let foo = {
a: 1
}
Object.defineProperty(foo, 'b', {
value: 2,
writable: true,
enumerable: true,
configurable: true,
});
這樣得到foo的新值是{a: 1, b: 2}。
我們還可以使用defineProperty更改現有屬性的描述符。 例如:
let foo = {
a: 1
}
Object.defineProperty(foo, 'a', {
value: 2,
writable: false,
enumerable: true,
configurable: true,
});
這樣當我們嘗試給 foo.a 賦值時,如:
foo.a = 2;
如果關閉了嚴格模式,瀏覽器將忽略,否則將拋出一個錯誤,因為我們將 writable 設置為 false, 表示該屬性不可寫。
我們還可以使用defineProperty將屬性轉換為getter,如下所示:
'use strict'
let foo = {
a: 1
}
Object.defineProperty(foo, 'b', {
get() {
return 1;
}
})
當我們這樣寫的時候:
foo.b = 2;
因為b屬性是getter屬性,所以當使用嚴格模式時,我們會得到一個錯誤:Getter 屬性不能重新賦值。
3.無法分配繼承的只讀屬性
繼承的只讀屬性不能再賦值。這是有道理的,因為我們這樣設置它,它是繼承的,所以它應該傳播到繼承屬性的對象。
我們可以使用Object.create創建一個從原型對象繼承屬性的對象,如下所示:
const proto = Object.defineProperties({}, {
a: {
value: 1,
writable: false
}
})
const foo = Object.create(proto)
在上面的代碼中,我們將proto.a的 writable 描述符設置為false,因此我們無法為其分配其他值。
如果我們這樣寫:
foo.a = 2;
在嚴格模式下,我們會收到錯誤消息。
總結
我們可以用 JavaScript 對象做很多我們可能不知道的事情。
首先,某些 JavaScript 對象(例如內置瀏覽器對象)具有內部屬性,這些屬性由雙方括號包圍,它們具有內部狀態,對象創建無法動態添加。
JavaScript對象屬性還具有屬性描述符,該屬性描述符使我們可以控制其值以及可以設置它們的值,還是可以更改其屬性描述符等。
我們可以使用defineProperty更改屬性的屬性描述符,它還用于添加新屬性及其屬性描述符。
最后,繼承的只讀屬性保持只讀狀態,這是有道理的,因為它是從父原型對象繼承而來的。
藍藍設計的小編 http://www.syprn.cn