如何優(yōu)化動畫逼肯?
對于如何優(yōu)化動畫耸黑,我們知道,一般情況下篮幢,動畫需要頻繁的操作DOM大刊,就就會導(dǎo)致頁面的性能問題,我們可以將動畫的position
屬性設(shè)置為absolute
或者fixed
三椿,將動畫脫離文檔流奈揍,這樣他的回流就不會影響到頁面了。
Vue 為什么要用 vm.$set() 解決對象新增屬性不能響應(yīng)的問題 赋续?你能說說如下代碼的實現(xiàn)原理么?
1)Vue為什么要用vm.$set() 解決對象新增屬性不能響應(yīng)的問題
- Vue使用了Object.defineProperty實現(xiàn)雙向數(shù)據(jù)綁定
- 在初始化實例時對屬性執(zhí)行 getter/setter 轉(zhuǎn)化
- 屬性必須在data對象上存在才能讓Vue將它轉(zhuǎn)換為響應(yīng)式的(這也就造成了Vue無法檢測到對象屬性的添加或刪除)
所以Vue提供了Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)
2)接下來我們看看框架本身是如何實現(xiàn)的呢?
Vue 源碼位置:vue/src/core/instance/index.js
export function set (target: Array<any> | Object, key: any, val: any): any {
// target 為數(shù)組
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 修改數(shù)組的長度, 避免索引>數(shù)組長度導(dǎo)致splcie()執(zhí)行有誤
target.length = Math.max(target.length, key)
// 利用數(shù)組的splice變異方法觸發(fā)響應(yīng)式
target.splice(key, 1, val)
return val
}
// key 已經(jīng)存在另患,直接修改屬性值
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
const ob = (target: any).__ob__
// target 本身就不是響應(yīng)式數(shù)據(jù), 直接賦值
if (!ob) {
target[key] = val
return val
}
// 對屬性進行響應(yīng)式處理
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}
我們閱讀以上源碼可知纽乱,vm.$set 的實現(xiàn)原理是:
- 如果目標是數(shù)組,直接使用數(shù)組的 splice 方法觸發(fā)相應(yīng)式昆箕;
- 如果目標是對象鸦列,會先判讀屬性是否存在、對象是否是響應(yīng)式鹏倘,
- 最終如果要對屬性進行響應(yīng)式處理薯嗤,則是通過調(diào)用 defineReactive 方法進行響應(yīng)式處理
defineReactive 方法就是 Vue 在初始化對象時,給對象屬性采用 Object.defineProperty 動態(tài)添加 getter 和 setter 的功能所調(diào)用的方法
哪些情況會導(dǎo)致內(nèi)存泄漏
1纤泵、意外的全局變量:由于使用未聲明的變量,而意外的創(chuàng)建了一個全局變量,而使這個變量一直留在內(nèi)存中無法被回收
2骆姐、被遺忘的計時器或回調(diào)函數(shù):設(shè)置了 setInterval 定時器,而忘記取消它捏题,如果循環(huán)函數(shù)有對外部變量的引用的話玻褪,那么這個變量會被一直留在內(nèi)存中,而無法被回收公荧。
3带射、脫離 DOM 的引用:獲取一個 DOM 元素的引用,而后面這個元素被刪除循狰,由于一直保留了對這個元素的引用窟社,所以它也無法被回收。
4绪钥、閉包:不合理的使用閉包灿里,從而導(dǎo)致某些變量一直被留在內(nèi)存當中。
對象創(chuàng)建的方式有哪些程腹?
一般使用字面量的形式直接創(chuàng)建對象钠四,但是這種創(chuàng)建方式對于創(chuàng)建大量相似對象的時候,會產(chǎn)生大量的重復(fù)代碼。但 js和一般的面向?qū)ο蟮恼Z言不同缀去,在 ES6 之前它沒有類的概念侣灶。但是可以使用函數(shù)來進行模擬,從而產(chǎn)生出可復(fù)用的對象創(chuàng)建方式缕碎,常見的有以下幾種:
(1)第一種是工廠模式褥影,工廠模式的主要工作原理是用函數(shù)來封裝創(chuàng)建對象的細節(jié),從而通過調(diào)用函數(shù)來達到復(fù)用的目的咏雌。但是它有一個很大的問題就是創(chuàng)建出來的對象無法和某個類型聯(lián)系起來凡怎,它只是簡單的封裝了復(fù)用代碼,而沒有建立起對象和類型間的關(guān)系赊抖。
(2)第二種是構(gòu)造函數(shù)模式统倒。js 中每一個函數(shù)都可以作為構(gòu)造函數(shù),只要一個函數(shù)是通過 new 來調(diào)用的氛雪,那么就可以把它稱為構(gòu)造函數(shù)房匆。執(zhí)行構(gòu)造函數(shù)首先會創(chuàng)建一個對象,然后將對象的原型指向構(gòu)造函數(shù)的 prototype 屬性报亩,然后將執(zhí)行上下文中的 this 指向這個對象浴鸿,最后再執(zhí)行整個函數(shù),如果返回值不是對象弦追,則返回新建的對象岳链。因為 this 的值指向了新建的對象,因此可以使用 this 給對象賦值劲件。構(gòu)造函數(shù)模式相對于工廠模式的優(yōu)點是掸哑,所創(chuàng)建的對象和構(gòu)造函數(shù)建立起了聯(lián)系,因此可以通過原型來識別對象的類型零远。但是構(gòu)造函數(shù)存在一個缺點就是举户,造成了不必要的函數(shù)對象的創(chuàng)建,因為在 js 中函數(shù)也是一個對象遍烦,因此如果對象屬性中如果包含函數(shù)的話俭嘁,那么每次都會新建一個函數(shù)對象,浪費了不必要的內(nèi)存空間服猪,因為函數(shù)是所有的實例都可以通用的供填。
(3)第三種模式是原型模式,因為每一個函數(shù)都有一個 prototype 屬性罢猪,這個屬性是一個對象近她,它包含了通過構(gòu)造函數(shù)創(chuàng)建的所有實例都能共享的屬性和方法。因此可以使用原型對象來添加公用屬性和方法膳帕,從而實現(xiàn)代碼的復(fù)用粘捎。這種方式相對于構(gòu)造函數(shù)模式來說薇缅,解決了函數(shù)對象的復(fù)用問題。但是這種模式也存在一些問題攒磨,一個是沒有辦法通過傳入?yún)?shù)來初始化值泳桦,另一個是如果存在一個引用類型如 Array 這樣的值,那么所有的實例將共享一個對象娩缰,一個實例對引用類型值的改變會影響所有的實例灸撰。
(4)第四種模式是組合使用構(gòu)造函數(shù)模式和原型模式,這是創(chuàng)建自定義類型的最常見方式拼坎。因為構(gòu)造函數(shù)模式和原型模式分開使用都存在一些問題浮毯,因此可以組合使用這兩種模式,通過構(gòu)造函數(shù)來初始化對象的屬性泰鸡,通過原型對象來實現(xiàn)函數(shù)方法的復(fù)用刽宪。這種方法很好的解決了兩種模式單獨使用時的缺點睬隶,但是有一點不足的就是说搅,因為使用了兩種不同的模式埠褪,所以對于代碼的封裝性不夠好串述。
(5)第五種模式是動態(tài)原型模式画畅,這一種模式將原型方法賦值的創(chuàng)建過程移動到了構(gòu)造函數(shù)的內(nèi)部谬盐,通過對屬性是否存在的判斷炕置,可以實現(xiàn)僅在第一次調(diào)用函數(shù)時對原型對象賦值一次的效果兆沙。這一種方式很好地對上面的混合模式進行了封裝欧芽。
(6)第六種模式是寄生構(gòu)造函數(shù)模式,這一種模式和工廠模式的實現(xiàn)基本相同葛圃,我對這個模式的理解是千扔,它主要是基于一個已有的類型,在實例化時對實例化的對象進行擴展库正。這樣既不用修改原來的構(gòu)造函數(shù)曲楚,也達到了擴展對象的目的。它的一個缺點和工廠模式一樣褥符,無法實現(xiàn)對象的識別龙誊。
對象繼承的方式有哪些?
(1)第一種是以原型鏈的方式來實現(xiàn)繼承喷楣,但是這種實現(xiàn)方式存在的缺點是趟大,在包含有引用類型的數(shù)據(jù)時,會被所有的實例對象所共享铣焊,容易造成修改的混亂逊朽。還有就是在創(chuàng)建子類型的時候不能向超類型傳遞參數(shù)。
(2)第二種方式是使用借用構(gòu)造函數(shù)的方式曲伊,這種方式是通過在子類型的函數(shù)中調(diào)用超類型的構(gòu)造函數(shù)來實現(xiàn)的叽讳,這一種方法解決了不能向超類型傳遞參數(shù)的缺點,但是它存在的一個問題就是無法實現(xiàn)函數(shù)方法的復(fù)用,并且超類型原型定義的方法子類型也沒有辦法訪問到岛蚤。
(3)第三種方式是組合繼承邑狸,組合繼承是將原型鏈和借用構(gòu)造函數(shù)組合起來使用的一種方式。通過借用構(gòu)造函數(shù)的方式來實現(xiàn)類型的屬性的繼承灭美,通過將子類型的原型設(shè)置為超類型的實例來實現(xiàn)方法的繼承推溃。這種方式解決了上面的兩種模式單獨使用時的問題,但是由于我們是以超類型的實例來作為子類型的原型届腐,所以調(diào)用了兩次超類的構(gòu)造函數(shù)铁坎,造成了子類型的原型中多了很多不必要的屬性。
(4)第四種方式是原型式繼承犁苏,原型式繼承的主要思路就是基于已有的對象來創(chuàng)建新的對象硬萍,實現(xiàn)的原理是,向函數(shù)中傳入一個對象围详,然后返回一個以這個對象為原型的對象朴乖。這種繼承的思路主要不是為了實現(xiàn)創(chuàng)造一種新的類型,只是對某個對象實現(xiàn)一種簡單繼承助赞,ES5 中定義的 Object.create() 方法就是原型式繼承的實現(xiàn)买羞。缺點與原型鏈方式相同。
(5)第五種方式是寄生式繼承雹食,寄生式繼承的思路是創(chuàng)建一個用于封裝繼承過程的函數(shù)畜普,通過傳入一個對象,然后復(fù)制一個對象的副本群叶,然后對象進行擴展吃挑,最后返回這個對象。這個擴展的過程就可以理解是一種繼承街立。這種繼承的優(yōu)點就是對一個簡單對象實現(xiàn)繼承舶衬,如果這個對象不是自定義類型時。缺點是沒有辦法實現(xiàn)函數(shù)的復(fù)用赎离。
(6)第六種方式是寄生式組合繼承逛犹,組合繼承的缺點就是使用超類型的實例做為子類型的原型,導(dǎo)致添加了不必要的原型屬性梁剔。寄生式組合繼承的方式是使用超類型的原型的副本來作為子類型的原型圾浅,這樣就避免了創(chuàng)建不必要的屬性。
DNS 記錄和報文
DNS 服務(wù)器中以資源記錄的形式存儲信息憾朴,每一個 DNS 響應(yīng)報文一般包含多條資源記錄狸捕。一條資源記錄的具體的格式為
(Name,Value众雷,Type灸拍,TTL)
其中 TTL 是資源記錄的生存時間做祝,它定義了資源記錄能夠被其他的 DNS 服務(wù)器緩存多長時間。
常用的一共有四種 Type 的值鸡岗,分別是 A混槐、NS、CNAME 和 MX 轩性,不同 Type 的值声登,對應(yīng)資源記錄代表的意義不同:
- 如果 Type = A,則 Name 是主機名揣苏,Value 是主機名對應(yīng)的 IP 地址悯嗓。因此一條記錄為 A 的資源記錄,提供了標 準的主機名到 IP 地址的映射卸察。
- 如果 Type = NS脯厨,則 Name 是個域名,Value 是負責該域名的 DNS 服務(wù)器的主機名坑质。這個記錄主要用于 DNS 鏈式 查詢時合武,返回下一級需要查詢的 DNS 服務(wù)器的信息。
- 如果 Type = CNAME涡扼,則 Name 為別名稼跳,Value 為該主機的規(guī)范主機名。該條記錄用于向查詢的主機返回一個主機名 對應(yīng)的規(guī)范主機名吃沪,從而告訴查詢主機去查詢這個主機名的 IP 地址汤善。主機別名主要是為了通過給一些復(fù)雜的主機名提供 一個便于記憶的簡單的別名。
- 如果 Type = MX巷波,則 Name 為一個郵件服務(wù)器的別名萎津,Value 為郵件服務(wù)器的規(guī)范主機名卸伞。它的作用和 CNAME 是一 樣的抹镊,都是為了解決規(guī)范主機名不利于記憶的缺點。
什么是作用域荤傲?
ES5 中只存在兩種作用域:全局作用域和函數(shù)作用域垮耳。在 JavaScript 中,我們將作用域定義為一套規(guī)則遂黍,這套規(guī)則用來管理引擎如何在當前作用域以及嵌套子作用域中根據(jù)標識符名稱進行變量(變量名或者函數(shù)名)查找
如果new一個箭頭函數(shù)的會怎么樣
箭頭函數(shù)是ES6中的提出來的终佛,它沒有prototype,也沒有自己的this指向雾家,更不可以使用arguments參數(shù)铃彰,所以不能New一個箭頭函數(shù)。
new操作符的實現(xiàn)步驟如下:
- 創(chuàng)建一個對象
- 將構(gòu)造函數(shù)的作用域賦給新對象(也就是將對象的proto屬性指向構(gòu)造函數(shù)的prototype屬性)
- 指向構(gòu)造函數(shù)中的代碼芯咧,構(gòu)造函數(shù)中的this指向該對象(也就是為這個對象添加屬性和方法)
- 返回新的對象
所以牙捉,上面的第二竹揍、三步,箭頭函數(shù)都是沒有辦法執(zhí)行的邪铲。
如何優(yōu)化關(guān)鍵渲染路徑芬位?
為盡快完成首次渲染,我們需要最大限度減小以下三種可變因素:
(1)關(guān)鍵資源的數(shù)量带到。
(2)關(guān)鍵路徑長度昧碉。
(3)關(guān)鍵字節(jié)的數(shù)量。
關(guān)鍵資源是可能阻止網(wǎng)頁首次渲染的資源揽惹。這些資源越少被饿,瀏覽器的工作量就越小,對 CPU 以及其他資源的占用也就越少永丝。同樣锹漱,關(guān)鍵路徑長度受所有關(guān)鍵資源與其字節(jié)大小之間依賴關(guān)系圖的影響:某些資源只能在上一資源處理完畢之后才能開始下載,并且資源越大慕嚷,下載所需的往返次數(shù)就越多哥牍。最后,瀏覽器需要下載的關(guān)鍵字節(jié)越少喝检,處理內(nèi)容并讓其出現(xiàn)在屏幕上的速度就越快嗅辣。要減少字節(jié)數(shù),我們可以減少資源數(shù)(將它們刪除或設(shè)為非關(guān)鍵資源)挠说,此外還要壓縮和優(yōu)化各項資源澡谭,確保最大限度減小傳送大小。
優(yōu)化關(guān)鍵渲染路徑的常規(guī)步驟如下:
(1)對關(guān)鍵路徑進行分析和特性描述:資源數(shù)损俭、字節(jié)數(shù)蛙奖、長度。
(2)最大限度減少關(guān)鍵資源的數(shù)量:刪除它們杆兵,延遲它們的下載雁仲,將它們標記為異步等。
(3)優(yōu)化關(guān)鍵字節(jié)數(shù)以縮短下載時間(往返次數(shù))琐脏。
(4)優(yōu)化其余關(guān)鍵資源的加載順序:您需要盡早下載所有關(guān)鍵資產(chǎn)攒砖,以縮短關(guān)鍵路徑長度
手寫發(fā)布訂閱
class EventListener {
listeners = {};
on(name, fn) {
(this.listeners[name] || (this.listeners[name] = [])).push(fn)
}
once(name, fn) {
let tem = (...args) => {
this.removeListener(name, fn)
fn(...args)
}
fn.fn = tem
this.on(name, tem)
}
removeListener(name, fn) {
if (this.listeners[name]) {
this.listeners[name] = this.listeners[name].filter(listener => (listener != fn && listener != fn.fn))
}
}
removeAllListeners(name) {
if (name && this.listeners[name]) delete this.listeners[name]
this.listeners = {}
}
emit(name, ...args) {
if (this.listeners[name]) {
this.listeners[name].forEach(fn => fn.call(this, ...args))
}
}
}
與緩存相關(guān)的HTTP請求頭有哪些
強緩存:
- Expires
- Cache-Control
協(xié)商緩存:
- Etag、If-None-Match
- Last-Modified日裙、If-Modified-Since
PWA使用過嗎吹艇?serviceWorker的使用原理是啥?
漸進式網(wǎng)絡(luò)應(yīng)用(PWA)
是谷歌在2015年底提出的概念昂拂∈苌瘢基本上算是web應(yīng)用程序,但在外觀和感覺上與原生app
類似格侯。支持PWA
的網(wǎng)站可以提供脫機工作鼻听、推送通知和設(shè)備硬件訪問等功能樟结。
Service Worker
是瀏覽器在后臺獨立于網(wǎng)頁運行的腳本,它打開了通向不需要網(wǎng)頁或用戶交互的功能的大門精算。 現(xiàn)在瓢宦,它們已包括如推送通知和后臺同步等功能。 將來灰羽,Service Worker
將會支持如定期同步或地理圍欄等其他功能驮履。 本教程討論的核心功能是攔截和處理網(wǎng)絡(luò)請求,包括通過程序來管理緩存中的響應(yīng)廉嚼。
常見的水平垂直方式有幾種?
//利用絕對定位玫镐,先將元素的左上角通過 top:50%和 left:50%定位到頁面的中心,然后再通過 translate 來調(diào)整元素的中心點到頁面的中心怠噪。該方法需要考慮瀏覽器兼容問題恐似。
.parent {
position: relative;
}
.child {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
//利用絕對定位,設(shè)置四個方向的值都為 0傍念,并將 margin 設(shè)置為 auto矫夷,由于寬高固定,因此對應(yīng)方向?qū)崿F(xiàn)平分憋槐,可以實現(xiàn)水平和垂直方向上的居中双藕。該方法適用于盒子有寬高的情況:
.parent {
position: relative;
}
.child {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
//利用絕對定位,先將元素的左上角通過 top:50%和 left:50%定位到頁面的中心阳仔,然后再通過 margin 負值來調(diào)整元素的中心點到頁面的中心忧陪。該方法適用于盒子寬高已知的情況
.parent {
position: relative;
}
.child {
position: absolute;
top: 50%;
left: 50%;
margin-top: -50px; /* 自身 height 的一半 */
margin-left: -50px; /* 自身 width 的一半 */
}
//使用 flex 布局,通過 align-items:center 和 justify-content:center 設(shè)置容器的垂直和水平方向上為居中對齊近范,然后它的子元素也可以實現(xiàn)垂直和水平的居中嘶摊。該方法要**考慮兼容的問題**,該方法在移動端用的較多:
.parent {
display: flex;
justify-content:center;
align-items:center;
}
//另外评矩,如果父元素設(shè)置了flex布局叶堆,只需要給子元素加上`margin:auto;`就可以實現(xiàn)垂直居中布局
.parent{
display:flex;
}
.child{
margin: auto;
}
對 CSS 工程化的理解
CSS 工程化是為了解決以下問題:
- 宏觀設(shè)計:CSS 代碼如何組織、如何拆分稚照、模塊結(jié)構(gòu)怎樣設(shè)計蹂空?
- 編碼優(yōu)化:怎樣寫出更好的 CSS俯萌?
- 構(gòu)建:如何處理我的 CSS果录,才能讓它的打包結(jié)果最優(yōu)?
- 可維護性:代碼寫完了咐熙,如何最小化它后續(xù)的變更成本弱恒?如何確保任何一個同事都能輕松接手?
以下三個方向都是時下比較流行的棋恼、普適性非常好的 CSS 工程化實踐:
- 預(yù)處理器:Less返弹、 Sass 等锈玉;
- 重要的工程化插件: PostCss;
- Webpack loader 等 义起。
基于這三個方向拉背,可以衍生出一些具有典型意義的子問題,這里我們逐個來看:
(1)預(yù)處理器:為什么要用預(yù)處理器默终?它的出現(xiàn)是為了解決什么問題椅棺?
預(yù)處理器,其實就是 CSS 世界的“輪子”齐蔽。預(yù)處理器支持我們寫一種類似 CSS两疚、但實際并不是 CSS 的語言,然后把它編譯成 CSS 代碼: 那為什么寫 CSS 代碼寫得好好的含滴,偏偏要轉(zhuǎn)去寫“類 CSS”呢诱渤?這就和本來用 JS 也可以實現(xiàn)所有功能,但最后卻寫 React 的 jsx 或者 Vue 的模板語法一樣——為了爽谈况!要想知道有了預(yù)處理器有多爽勺美,首先要知道的是傳統(tǒng) CSS 有多不爽。隨著前端業(yè)務(wù)復(fù)雜度的提高碑韵,前端工程中對 CSS 提出了以下的訴求:
- 宏觀設(shè)計上:我們希望能優(yōu)化 CSS 文件的目錄結(jié)構(gòu)励烦,對現(xiàn)有的 CSS 文件實現(xiàn)復(fù)用;
- 編碼優(yōu)化上:我們希望能寫出結(jié)構(gòu)清晰泼诱、簡明易懂的 CSS坛掠,需要它具有一目了然的嵌套層級關(guān)系,而不是無差別的一鋪到底寫法治筒;我們希望它具有變量特征屉栓、計算能力、循環(huán)能力等等更強的可編程性耸袜,這樣我們可以少寫一些無用的代碼友多;
- 可維護性上:更強的可編程性意味著更優(yōu)質(zhì)的代碼結(jié)構(gòu),實現(xiàn)復(fù)用意味著更簡單的目錄結(jié)構(gòu)和更強的拓展能力堤框,這兩點如果能做到域滥,自然會帶來更強的可維護性。
這三點是傳統(tǒng) CSS 所做不到的蜈抓,也正是預(yù)處理器所解決掉的問題启绰。預(yù)處理器普遍會具備這樣的特性:
- 嵌套代碼的能力,通過嵌套來反映不同 css 屬性之間的層級關(guān)系 沟使;
- 支持定義 css 變量委可;
- 提供計算函數(shù);
- 允許對代碼片段進行 extend 和 mixin腊嗡;
- 支持循環(huán)語句的使用着倾;
- 支持將 CSS 文件模塊化拾酝,實現(xiàn)復(fù)用。
(2)PostCss:PostCss 是如何工作的卡者?我們在什么場景下會使用 PostCss蒿囤?
它和預(yù)處理器的不同就在于,預(yù)處理器處理的是 類CSS崇决,而 PostCss 處理的就是 CSS 本身蟋软。Babel 可以將高版本的 JS 代碼轉(zhuǎn)換為低版本的 JS 代碼。PostCss 做的是類似的事情:它可以編譯尚未被瀏覽器廣泛支持的先進的 CSS 語法嗽桩,還可以自動為一些需要額外兼容的語法增加前綴岳守。更強的是,由于 PostCss 有著強大的插件機制碌冶,支持各種各樣的擴展湿痢,極大地強化了 CSS 的能力。
PostCss 在業(yè)務(wù)中的使用場景非常多:
- 提高 CSS 代碼的可讀性:PostCss 其實可以做類似預(yù)處理器能做的工作扑庞;
- 當我們的 CSS 代碼需要適配低版本瀏覽器時譬重,PostCss 的 Autoprefixer 插件可以幫助我們自動增加瀏覽器前綴;
- 允許我們編寫面向未來的 CSS:PostCss 能夠幫助我們編譯 CSS next 代碼罐氨;
(3)Webpack 能處理 CSS 嗎臀规?如何實現(xiàn)? Webpack 能處理 CSS 嗎:
- Webpack 在裸奔的狀態(tài)下栅隐,是不能處理 CSS 的塔嬉,Webpack 本身是一個面向 JavaScript 且只能處理 JavaScript 代碼的模塊化打包工具;
- Webpack 在 loader 的輔助下租悄,是可以處理 CSS 的谨究。
如何用 Webpack 實現(xiàn)對 CSS 的處理:
- Webpack 中操作 CSS 需要使用的兩個關(guān)鍵的 loader:css-loader 和 style-loader
- 注意,答出“用什么”有時候可能還不夠泣棋,面試官會懷疑你是不是在背答案胶哲,所以你還需要了解每個 loader 都做了什么事情:
- css-loader:導(dǎo)入 CSS 模塊,對 CSS 代碼進行編譯處理潭辈;
- style-loader:創(chuàng)建style標簽鸯屿,把 CSS 內(nèi)容寫入標簽。
在實際使用中把敢,css-loader 的執(zhí)行順序一定要安排在 style-loader 的前面寄摆。因為只有完成了編譯過程,才可以對 css 代碼進行插入技竟;若提前插入了未編譯的代碼冰肴,那么 webpack 是無法理解這坨東西的屈藐,它會無情報錯榔组。
代碼輸出結(jié)果
var friendName = 'World';
(function() {
if (typeof friendName === 'undefined') {
var friendName = 'Jack';
console.log('Goodbye ' + friendName);
} else {
console.log('Hello ' + friendName);
}
})();
輸出結(jié)果:Goodbye Jack
我們知道熙尉,在 JavaScript中, Function 和 var 都會被提升(變量提升)搓扯,所以上面的代碼就相當于:
var name = 'World!';
(function () {
var name;
if (typeof name === 'undefined') {
name = 'Jack';
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name);
}
})();
這樣检痰,答案就一目了然了。
冒泡排序--時間復(fù)雜度 n^2
題目描述:實現(xiàn)一個冒泡排序
實現(xiàn)代碼如下:
function bubbleSort(arr) {
// 緩存數(shù)組長度
const len = arr.length;
// 外層循環(huán)用于控制從頭到尾的比較+交換到底有多少輪
for (let i = 0; i < len; i++) {
// 內(nèi)層循環(huán)用于完成每一輪遍歷過程中的重復(fù)比較+交換
for (let j = 0; j < len - 1; j++) {
// 若相鄰元素前面的數(shù)比后面的大
if (arr[j] > arr[j + 1]) {
// 交換兩者
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
}
// 返回數(shù)組
return arr;
}
// console.log(bubbleSort([3, 6, 2, 4, 1]));
CSS 優(yōu)化和提高性能的方法有哪些锨推?
加載性能:
(1)css壓縮:將寫好的css進行打包壓縮铅歼,可以減小文件體積。
(2)css單一樣式:當需要下邊距和左邊距的時候换可,很多時候會選擇使用 margin:top 0 bottom 0椎椰;但margin-bottom:bottom;margin-left:left;執(zhí)行效率會更高。
(3)減少使用@import沾鳄,建議使用link慨飘,因為后者在頁面加載時一起加載,前者是等待頁面加載完成之后再進行加載译荞。
選擇器性能:
(1)關(guān)鍵選擇器(key selector)瓤的。選擇器的最后面的部分為關(guān)鍵選擇器(即用來匹配目標元素的部分)。CSS選擇符是從右到左進行匹配的吞歼。當使用后代選擇器的時候圈膏,瀏覽器會遍歷所有子元素來確定是否是指定的元素等等;
(2)如果規(guī)則擁有ID選擇器作為其關(guān)鍵選擇器篙骡,則不要為規(guī)則增加標簽稽坤。過濾掉無關(guān)的規(guī)則(這樣樣式系統(tǒng)就不會浪費時間去匹配它們了)。
(3)避免使用通配規(guī)則糯俗,如*{}計算次數(shù)驚人慎皱,只對需要用到的元素進行選擇。
(4)盡量少的去對標簽進行選擇叶骨,而是用class茫多。
(5)盡量少的去使用后代選擇器,降低選擇器的權(quán)重值忽刽。后代選擇器的開銷是最高的天揖,盡量將選擇器的深度降到最低,最高不要超過三層跪帝,更多的使用類來關(guān)聯(lián)每一個標簽元素今膊。
(6)了解哪些屬性是可以通過繼承而來的,然后避免對這些屬性重復(fù)指定規(guī)則伞剑。
渲染性能:
(1)慎重使用高性能屬性:浮動斑唬、定位。
(2)盡量減少頁面重排、重繪恕刘。
(3)去除空規(guī)則:{}缤谎。空規(guī)則的產(chǎn)生原因一般來說是為了預(yù)留樣式褐着。去除這些空規(guī)則無疑能減少css文檔體積坷澡。
(4)屬性值為0時,不加單位含蓉。
(5)屬性值為浮動小數(shù)0.**频敛,可以省略小數(shù)點之前的0。
(6)標準化各種瀏覽器前綴:帶瀏覽器前綴的在前馅扣。標準屬性在后斟赚。
(7)不使用@import前綴,它會影響css的加載速度差油。
(8)選擇器優(yōu)化嵌套汁展,盡量避免層級過深。
(9)css雪碧圖厌殉,同一頁面相近部分的小圖標食绿,方便使用,減少頁面的請求次數(shù)公罕,但是同時圖片本身會變大器紧,使用時,優(yōu)劣考慮清楚楼眷,再使用铲汪。
(10)正確使用display的屬性,由于display的作用罐柳,某些樣式組合會無效掌腰,徒增樣式體積的同時也影響解析性能。
(11)不濫用web字體张吉。對于中文網(wǎng)站來說WebFonts可能很陌生齿梁,國外卻很流行。web fonts通常體積龐大肮蛹,而且一些瀏覽器在下載web fonts時會阻塞頁面渲染損傷性能勺择。
可維護性、健壯性:
(1)將具有相同屬性的樣式抽離出來伦忠,整合并通過class在頁面中進行使用省核,提高css的可維護性。
(2)樣式與內(nèi)容分離:將css代碼定義到外部css中昆码。
列表轉(zhuǎn)成樹形結(jié)構(gòu)
題目描述:
[
{
id: 1,
text: '節(jié)點1',
parentId: 0 //這里用0表示為頂級節(jié)點
},
{
id: 2,
text: '節(jié)點1_1',
parentId: 1 //通過這個字段來確定子父級
}
...
]
轉(zhuǎn)成
[
{
id: 1,
text: '節(jié)點1',
parentId: 0,
children: [
{
id:2,
text: '節(jié)點1_1',
parentId:1
}
]
}
]
實現(xiàn)代碼如下:
function listToTree(data) {
let temp = {};
let treeData = [];
for (let i = 0; i < data.length; i++) {
temp[data[i].id] = data[i];
}
for (let i in temp) {
if (+temp[i].parentId != 0) {
if (!temp[temp[i].parentId].children) {
temp[temp[i].parentId].children = [];
}
temp[temp[i].parentId].children.push(temp[i]);
} else {
treeData.push(temp[i]);
}
}
return treeData;
}