如何優(yōu)化動(dòng)畫?
對于如何優(yōu)化動(dòng)畫,我們知道,一般情況下,動(dòng)畫需要頻繁的操作DOM,就就會(huì)導(dǎo)致頁面的性能問題,我們可以將動(dòng)畫的position屬性設(shè)置為absolute或者fixed,將動(dòng)畫脫離文檔流,這樣他的回流就不會(huì)影響到頁面了。
Vue 為什么要用 vm.$set() 解決對象新增屬性不能響應(yīng)的問題 ?你能說說如下代碼的實(shí)現(xiàn)原理么?
1)Vue為什么要用vm.$set() 解決對象新增屬性不能響應(yīng)的問題
- Vue使用了Object.defineProperty實(shí)現(xiàn)雙向數(shù)據(jù)綁定
- 在初始化實(shí)例時(shí)對屬性執(zhí)行 getter/setter 轉(zhuǎn)化
- 屬性必須在data對象上存在才能讓Vue將它轉(zhuǎn)換為響應(yīng)式的(這也就造成了Vue無法檢測到對象屬性的添加或刪除)
所以Vue提供了Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)
2)接下來我們看看框架本身是如何實(shí)現(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
}
// 對屬性進(jìn)行響應(yīng)式處理
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}
我們閱讀以上源碼可知,vm.$set 的實(shí)現(xiàn)原理是:
- 如果目標(biāo)是數(shù)組,直接使用數(shù)組的 splice 方法觸發(fā)相應(yīng)式;
- 如果目標(biāo)是對象,會(huì)先判讀屬性是否存在、對象是否是響應(yīng)式,
- 最終如果要對屬性進(jìn)行響應(yīng)式處理,則是通過調(diào)用 defineReactive 方法進(jìn)行響應(yīng)式處理
defineReactive 方法就是 Vue 在初始化對象時(shí),給對象屬性采用 Object.defineProperty 動(dòng)態(tài)添加 getter 和 setter 的功能所調(diào)用的方法
哪些情況會(huì)導(dǎo)致內(nèi)存泄漏
1、意外的全局變量:由于使用未聲明的變量,而意外的創(chuàng)建了一個(gè)全局變量,而使這個(gè)變量一直留在內(nèi)存中無法被回收
2、被遺忘的計(jì)時(shí)器或回調(diào)函數(shù):設(shè)置了 setInterval 定時(shí)器,而忘記取消它,如果循環(huán)函數(shù)有對外部變量的引用的話,那么這個(gè)變量會(huì)被一直留在內(nèi)存中,而無法被回收。
3、脫離 DOM 的引用:獲取一個(gè) DOM 元素的引用,而后面這個(gè)元素被刪除,由于一直保留了對這個(gè)元素的引用,所以它也無法被回收。
4、閉包:不合理的使用閉包,從而導(dǎo)致某些變量一直被留在內(nèi)存當(dāng)中。
對象創(chuàng)建的方式有哪些?
一般使用字面量的形式直接創(chuàng)建對象,但是這種創(chuàng)建方式對于創(chuàng)建大量相似對象的時(shí)候,會(huì)產(chǎn)生大量的重復(fù)代碼。但 js和一般的面向?qū)ο蟮恼Z言不同,在 ES6 之前它沒有類的概念。但是可以使用函數(shù)來進(jìn)行模擬,從而產(chǎn)生出可復(fù)用的對象創(chuàng)建方式,常見的有以下幾種:
(1)第一種是工廠模式,工廠模式的主要工作原理是用函數(shù)來封裝創(chuàng)建對象的細(xì)節(jié),從而通過調(diào)用函數(shù)來達(dá)到復(fù)用的目的。但是它有一個(gè)很大的問題就是創(chuàng)建出來的對象無法和某個(gè)類型聯(lián)系起來,它只是簡單的封裝了復(fù)用代碼,而沒有建立起對象和類型間的關(guān)系。
(2)第二種是構(gòu)造函數(shù)模式。js 中每一個(gè)函數(shù)都可以作為構(gòu)造函數(shù),只要一個(gè)函數(shù)是通過 new 來調(diào)用的,那么就可以把它稱為構(gòu)造函數(shù)。執(zhí)行構(gòu)造函數(shù)首先會(huì)創(chuàng)建一個(gè)對象,然后將對象的原型指向構(gòu)造函數(shù)的 prototype 屬性,然后將執(zhí)行上下文中的 this 指向這個(gè)對象,最后再執(zhí)行整個(gè)函數(shù),如果返回值不是對象,則返回新建的對象。因?yàn)?this 的值指向了新建的對象,因此可以使用 this 給對象賦值。構(gòu)造函數(shù)模式相對于工廠模式的優(yōu)點(diǎn)是,所創(chuàng)建的對象和構(gòu)造函數(shù)建立起了聯(lián)系,因此可以通過原型來識(shí)別對象的類型。但是構(gòu)造函數(shù)存在一個(gè)缺點(diǎn)就是,造成了不必要的函數(shù)對象的創(chuàng)建,因?yàn)樵?js 中函數(shù)也是一個(gè)對象,因此如果對象屬性中如果包含函數(shù)的話,那么每次都會(huì)新建一個(gè)函數(shù)對象,浪費(fèi)了不必要的內(nèi)存空間,因?yàn)楹瘮?shù)是所有的實(shí)例都可以通用的。
(3)第三種模式是原型模式,因?yàn)槊恳粋€(gè)函數(shù)都有一個(gè) prototype 屬性,這個(gè)屬性是一個(gè)對象,它包含了通過構(gòu)造函數(shù)創(chuàng)建的所有實(shí)例都能共享的屬性和方法。因此可以使用原型對象來添加公用屬性和方法,從而實(shí)現(xiàn)代碼的復(fù)用。這種方式相對于構(gòu)造函數(shù)模式來說,解決了函數(shù)對象的復(fù)用問題。但是這種模式也存在一些問題,一個(gè)是沒有辦法通過傳入?yún)?shù)來初始化值,另一個(gè)是如果存在一個(gè)引用類型如 Array 這樣的值,那么所有的實(shí)例將共享一個(gè)對象,一個(gè)實(shí)例對引用類型值的改變會(huì)影響所有的實(shí)例。
(4)第四種模式是組合使用構(gòu)造函數(shù)模式和原型模式,這是創(chuàng)建自定義類型的最常見方式。因?yàn)闃?gòu)造函數(shù)模式和原型模式分開使用都存在一些問題,因此可以組合使用這兩種模式,通過構(gòu)造函數(shù)來初始化對象的屬性,通過原型對象來實(shí)現(xiàn)函數(shù)方法的復(fù)用。這種方法很好的解決了兩種模式單獨(dú)使用時(shí)的缺點(diǎn),但是有一點(diǎn)不足的就是,因?yàn)槭褂昧藘煞N不同的模式,所以對于代碼的封裝性不夠好。
(5)第五種模式是動(dòng)態(tài)原型模式,這一種模式將原型方法賦值的創(chuàng)建過程移動(dòng)到了構(gòu)造函數(shù)的內(nèi)部,通過對屬性是否存在的判斷,可以實(shí)現(xiàn)僅在第一次調(diào)用函數(shù)時(shí)對原型對象賦值一次的效果。這一種方式很好地對上面的混合模式進(jìn)行了封裝。
(6)第六種模式是寄生構(gòu)造函數(shù)模式,這一種模式和工廠模式的實(shí)現(xiàn)基本相同,我對這個(gè)模式的理解是,它主要是基于一個(gè)已有的類型,在實(shí)例化時(shí)對實(shí)例化的對象進(jìn)行擴(kuò)展。這樣既不用修改原來的構(gòu)造函數(shù),也達(dá)到了擴(kuò)展對象的目的。它的一個(gè)缺點(diǎn)和工廠模式一樣,無法實(shí)現(xiàn)對象的識(shí)別。
對象繼承的方式有哪些?
(1)第一種是以原型鏈的方式來實(shí)現(xiàn)繼承,但是這種實(shí)現(xiàn)方式存在的缺點(diǎn)是,在包含有引用類型的數(shù)據(jù)時(shí),會(huì)被所有的實(shí)例對象所共享,容易造成修改的混亂。還有就是在創(chuàng)建子類型的時(shí)候不能向超類型傳遞參數(shù)。
(2)第二種方式是使用借用構(gòu)造函數(shù)的方式,這種方式是通過在子類型的函數(shù)中調(diào)用超類型的構(gòu)造函數(shù)來實(shí)現(xiàn)的,這一種方法解決了不能向超類型傳遞參數(shù)的缺點(diǎn),但是它存在的一個(gè)問題就是無法實(shí)現(xiàn)函數(shù)方法的復(fù)用,并且超類型原型定義的方法子類型也沒有辦法訪問到。
(3)第三種方式是組合繼承,組合繼承是將原型鏈和借用構(gòu)造函數(shù)組合起來使用的一種方式。通過借用構(gòu)造函數(shù)的方式來實(shí)現(xiàn)類型的屬性的繼承,通過將子類型的原型設(shè)置為超類型的實(shí)例來實(shí)現(xiàn)方法的繼承。這種方式解決了上面的兩種模式單獨(dú)使用時(shí)的問題,但是由于我們是以超類型的實(shí)例來作為子類型的原型,所以調(diào)用了兩次超類的構(gòu)造函數(shù),造成了子類型的原型中多了很多不必要的屬性。
(4)第四種方式是原型式繼承,原型式繼承的主要思路就是基于已有的對象來創(chuàng)建新的對象,實(shí)現(xiàn)的原理是,向函數(shù)中傳入一個(gè)對象,然后返回一個(gè)以這個(gè)對象為原型的對象。這種繼承的思路主要不是為了實(shí)現(xiàn)創(chuàng)造一種新的類型,只是對某個(gè)對象實(shí)現(xiàn)一種簡單繼承,ES5 中定義的 Object.create() 方法就是原型式繼承的實(shí)現(xiàn)。缺點(diǎn)與原型鏈方式相同。
(5)第五種方式是寄生式繼承,寄生式繼承的思路是創(chuàng)建一個(gè)用于封裝繼承過程的函數(shù),通過傳入一個(gè)對象,然后復(fù)制一個(gè)對象的副本,然后對象進(jìn)行擴(kuò)展,最后返回這個(gè)對象。這個(gè)擴(kuò)展的過程就可以理解是一種繼承。這種繼承的優(yōu)點(diǎn)就是對一個(gè)簡單對象實(shí)現(xiàn)繼承,如果這個(gè)對象不是自定義類型時(shí)。缺點(diǎn)是沒有辦法實(shí)現(xiàn)函數(shù)的復(fù)用。
(6)第六種方式是寄生式組合繼承,組合繼承的缺點(diǎn)就是使用超類型的實(shí)例做為子類型的原型,導(dǎo)致添加了不必要的原型屬性。寄生式組合繼承的方式是使用超類型的原型的副本來作為子類型的原型,這樣就避免了創(chuàng)建不必要的屬性。
DNS 記錄和報(bào)文
DNS 服務(wù)器中以資源記錄的形式存儲(chǔ)信息,每一個(gè) DNS 響應(yīng)報(bào)文一般包含多條資源記錄。一條資源記錄的具體的格式為
(Name,Value,Type,TTL)
其中 TTL 是資源記錄的生存時(shí)間,它定義了資源記錄能夠被其他的 DNS 服務(wù)器緩存多長時(shí)間。
常用的一共有四種 Type 的值,分別是 A、NS、CNAME 和 MX ,不同 Type 的值,對應(yīng)資源記錄代表的意義不同:
- 如果 Type = A,則 Name 是主機(jī)名,Value 是主機(jī)名對應(yīng)的 IP 地址。因此一條記錄為 A 的資源記錄,提供了標(biāo) 準(zhǔn)的主機(jī)名到 IP 地址的映射。
- 如果 Type = NS,則 Name 是個(gè)域名,Value 是負(fù)責(zé)該域名的 DNS 服務(wù)器的主機(jī)名。這個(gè)記錄主要用于 DNS 鏈?zhǔn)?查詢時(shí),返回下一級需要查詢的 DNS 服務(wù)器的信息。
- 如果 Type = CNAME,則 Name 為別名,Value 為該主機(jī)的規(guī)范主機(jī)名。該條記錄用于向查詢的主機(jī)返回一個(gè)主機(jī)名 對應(yīng)的規(guī)范主機(jī)名,從而告訴查詢主機(jī)去查詢這個(gè)主機(jī)名的 IP 地址。主機(jī)別名主要是為了通過給一些復(fù)雜的主機(jī)名提供 一個(gè)便于記憶的簡單的別名。
- 如果 Type = MX,則 Name 為一個(gè)郵件服務(wù)器的別名,Value 為郵件服務(wù)器的規(guī)范主機(jī)名。它的作用和 CNAME 是一 樣的,都是為了解決規(guī)范主機(jī)名不利于記憶的缺點(diǎn)。
什么是作用域?
ES5 中只存在兩種作用域:全局作用域和函數(shù)作用域。在 JavaScript 中,我們將作用域定義為一套規(guī)則,這套規(guī)則用來管理引擎如何在當(dāng)前作用域以及嵌套子作用域中根據(jù)標(biāo)識(shí)符名稱進(jìn)行變量(變量名或者函數(shù)名)查找
如果new一個(gè)箭頭函數(shù)的會(huì)怎么樣
箭頭函數(shù)是ES6中的提出來的,它沒有prototype,也沒有自己的this指向,更不可以使用arguments參數(shù),所以不能New一個(gè)箭頭函數(shù)。
new操作符的實(shí)現(xiàn)步驟如下:
- 創(chuàng)建一個(gè)對象
- 將構(gòu)造函數(shù)的作用域賦給新對象(也就是將對象的proto屬性指向構(gòu)造函數(shù)的prototype屬性)
- 指向構(gòu)造函數(shù)中的代碼,構(gòu)造函數(shù)中的this指向該對象(也就是為這個(gè)對象添加屬性和方法)
- 返回新的對象
所以,上面的第二、三步,箭頭函數(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)化各項(xiàng)資源,確保最大限度減小傳送大小。
優(yōu)化關(guān)鍵渲染路徑的常規(guī)步驟如下:
(1)對關(guān)鍵路徑進(jìn)行分析和特性描述:資源數(shù)、字節(jié)數(shù)、長度。
(2)最大限度減少關(guān)鍵資源的數(shù)量:刪除它們,延遲它們的下載,將它們標(biāo)記為異步等。
(3)優(yōu)化關(guān)鍵字節(jié)數(shù)以縮短下載時(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請求頭有哪些
強(qiáng)緩存:
- Expires
- Cache-Control
協(xié)商緩存:
- Etag、If-None-Match
- Last-Modified、If-Modified-Since
PWA使用過嗎?serviceWorker的使用原理是啥?
漸進(jìn)式網(wǎng)絡(luò)應(yīng)用(PWA)是谷歌在2015年底提出的概念。基本上算是web應(yīng)用程序,但在外觀和感覺上與原生app類似。支持PWA的網(wǎng)站可以提供脫機(jī)工作、推送通知和設(shè)備硬件訪問等功能。
Service Worker是瀏覽器在后臺(tái)獨(dú)立于網(wǎng)頁運(yùn)行的腳本,它打開了通向不需要網(wǎng)頁或用戶交互的功能的大門。 現(xiàn)在,它們已包括如推送通知和后臺(tái)同步等功能。 將來,Service Worker將會(huì)支持如定期同步或地理圍欄等其他功能。 本教程討論的核心功能是攔截和處理網(wǎng)絡(luò)請求,包括通過程序來管理緩存中的響應(yīng)。
常見的水平垂直方式有幾種?
//利用絕對定位,先將元素的左上角通過 top:50%和 left:50%定位到頁面的中心,然后再通過 translate 來調(diào)整元素的中心點(diǎn)到頁面的中心。該方法需要考慮瀏覽器兼容問題。
.parent {
position: relative;
}
.child {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
//利用絕對定位,設(shè)置四個(gè)方向的值都為 0,并將 margin 設(shè)置為 auto,由于寬高固定,因此對應(yīng)方向?qū)崿F(xiàn)平分,可以實(shí)現(xiàn)水平和垂直方向上的居中。該方法適用于盒子有寬高的情況:
.parent {
position: relative;
}
.child {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
//利用絕對定位,先將元素的左上角通過 top:50%和 left:50%定位到頁面的中心,然后再通過 margin 負(fù)值來調(diào)整元素的中心點(diǎn)到頁面的中心。該方法適用于盒子寬高已知的情況
.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è)置容器的垂直和水平方向上為居中對齊,然后它的子元素也可以實(shí)現(xiàn)垂直和水平的居中。該方法要**考慮兼容的問題**,該方法在移動(dòng)端用的較多:
.parent {
display: flex;
justify-content:center;
align-items:center;
}
//另外,如果父元素設(shè)置了flex布局,只需要給子元素加上`margin:auto;`就可以實(shí)現(xiàn)垂直居中布局
.parent{
display:flex;
}
.child{
margin: auto;
}
對 CSS 工程化的理解
CSS 工程化是為了解決以下問題:
- 宏觀設(shè)計(jì):CSS 代碼如何組織、如何拆分、模塊結(jié)構(gòu)怎樣設(shè)計(jì)?
- 編碼優(yōu)化:怎樣寫出更好的 CSS?
- 構(gòu)建:如何處理我的 CSS,才能讓它的打包結(jié)果最優(yōu)?
- 可維護(hù)性:代碼寫完了,如何最小化它后續(xù)的變更成本?如何確保任何一個(gè)同事都能輕松接手?
以下三個(gè)方向都是時(shí)下比較流行的、普適性非常好的 CSS 工程化實(shí)踐:
- 預(yù)處理器:Less、 Sass 等;
- 重要的工程化插件: PostCss;
- Webpack loader 等 。
基于這三個(gè)方向,可以衍生出一些具有典型意義的子問題,這里我們逐個(gè)來看:
(1)預(yù)處理器:為什么要用預(yù)處理器?它的出現(xiàn)是為了解決什么問題?
預(yù)處理器,其實(shí)就是 CSS 世界的“輪子”。預(yù)處理器支持我們寫一種類似 CSS、但實(shí)際并不是 CSS 的語言,然后把它編譯成 CSS 代碼: 那為什么寫 CSS 代碼寫得好好的,偏偏要轉(zhuǎn)去寫“類 CSS”呢?這就和本來用 JS 也可以實(shí)現(xiàn)所有功能,但最后卻寫 React 的 jsx 或者 Vue 的模板語法一樣——為了爽!要想知道有了預(yù)處理器有多爽,首先要知道的是傳統(tǒng) CSS 有多不爽。隨著前端業(yè)務(wù)復(fù)雜度的提高,前端工程中對 CSS 提出了以下的訴求:
- 宏觀設(shè)計(jì)上:我們希望能優(yōu)化 CSS 文件的目錄結(jié)構(gòu),對現(xiàn)有的 CSS 文件實(shí)現(xiàn)復(fù)用;
- 編碼優(yōu)化上:我們希望能寫出結(jié)構(gòu)清晰、簡明易懂的 CSS,需要它具有一目了然的嵌套層級關(guān)系,而不是無差別的一鋪到底寫法;我們希望它具有變量特征、計(jì)算能力、循環(huán)能力等等更強(qiáng)的可編程性,這樣我們可以少寫一些無用的代碼;
- 可維護(hù)性上:更強(qiáng)的可編程性意味著更優(yōu)質(zhì)的代碼結(jié)構(gòu),實(shí)現(xiàn)復(fù)用意味著更簡單的目錄結(jié)構(gòu)和更強(qiáng)的拓展能力,這兩點(diǎn)如果能做到,自然會(huì)帶來更強(qiáng)的可維護(hù)性。
這三點(diǎn)是傳統(tǒng) CSS 所做不到的,也正是預(yù)處理器所解決掉的問題。預(yù)處理器普遍會(huì)具備這樣的特性:
- 嵌套代碼的能力,通過嵌套來反映不同 css 屬性之間的層級關(guān)系 ;
- 支持定義 css 變量;
- 提供計(jì)算函數(shù);
- 允許對代碼片段進(jìn)行 extend 和 mixin;
- 支持循環(huán)語句的使用;
- 支持將 CSS 文件模塊化,實(shí)現(xiàn)復(fù)用。
(2)PostCss:PostCss 是如何工作的?我們在什么場景下會(huì)使用 PostCss?
它和預(yù)處理器的不同就在于,預(yù)處理器處理的是 類CSS,而 PostCss 處理的就是 CSS 本身。Babel 可以將高版本的 JS 代碼轉(zhuǎn)換為低版本的 JS 代碼。PostCss 做的是類似的事情:它可以編譯尚未被瀏覽器廣泛支持的先進(jìn)的 CSS 語法,還可以自動(dòng)為一些需要額外兼容的語法增加前綴。更強(qiáng)的是,由于 PostCss 有著強(qiáng)大的插件機(jī)制,支持各種各樣的擴(kuò)展,極大地強(qiáng)化了 CSS 的能力。
PostCss 在業(yè)務(wù)中的使用場景非常多:
- 提高 CSS 代碼的可讀性:PostCss 其實(shí)可以做類似預(yù)處理器能做的工作;
- 當(dāng)我們的 CSS 代碼需要適配低版本瀏覽器時(shí),PostCss 的 Autoprefixer 插件可以幫助我們自動(dòng)增加瀏覽器前綴;
- 允許我們編寫面向未來的 CSS:PostCss 能夠幫助我們編譯 CSS next 代碼;
(3)Webpack 能處理 CSS 嗎?如何實(shí)現(xiàn)? Webpack 能處理 CSS 嗎:
- Webpack 在裸奔的狀態(tài)下,是不能處理 CSS 的,Webpack 本身是一個(gè)面向 JavaScript 且只能處理 JavaScript 代碼的模塊化打包工具;
- Webpack 在 loader 的輔助下,是可以處理 CSS 的。
如何用 Webpack 實(shí)現(xiàn)對 CSS 的處理:
- Webpack 中操作 CSS 需要使用的兩個(gè)關(guān)鍵的 loader:css-loader 和 style-loader
- 注意,答出“用什么”有時(shí)候可能還不夠,面試官會(huì)懷疑你是不是在背答案,所以你還需要了解每個(gè) loader 都做了什么事情:
- css-loader:導(dǎo)入 CSS 模塊,對 CSS 代碼進(jìn)行編譯處理;
- style-loader:創(chuàng)建style標(biāo)簽,把 CSS 內(nèi)容寫入標(biāo)簽。
在實(shí)際使用中,css-loader 的執(zhí)行順序一定要安排在 style-loader 的前面。因?yàn)橹挥型瓿闪司幾g過程,才可以對 css 代碼進(jìn)行插入;若提前插入了未編譯的代碼,那么 webpack 是無法理解這坨東西的,它會(huì)無情報(bào)錯(cuò)。
代碼輸出結(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 都會(huì)被提升(變量提升),所以上面的代碼就相當(dāng)于:
var name = 'World!';
(function () {
var name;
if (typeof name === 'undefined') {
name = 'Jack';
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name);
}
})();
這樣,答案就一目了然了。
冒泡排序--時(shí)間復(fù)雜度 n^2
題目描述:實(shí)現(xiàn)一個(gè)冒泡排序
實(shí)現(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]];
}
}
}
// 返回?cái)?shù)組
return arr;
}
// console.log(bubbleSort([3, 6, 2, 4, 1]));
CSS 優(yōu)化和提高性能的方法有哪些?
加載性能:
(1)css壓縮:將寫好的css進(jìn)行打包壓縮,可以減小文件體積。
(2)css單一樣式:當(dāng)需要下邊距和左邊距的時(shí)候,很多時(shí)候會(huì)選擇使用 margin:top 0 bottom 0;但margin-bottom:bottom;margin-left:left;執(zhí)行效率會(huì)更高。
(3)減少使用@import,建議使用link,因?yàn)楹笳咴陧撁婕虞d時(shí)一起加載,前者是等待頁面加載完成之后再進(jìn)行加載。
選擇器性能:
(1)關(guān)鍵選擇器(key selector)。選擇器的最后面的部分為關(guān)鍵選擇器(即用來匹配目標(biāo)元素的部分)。CSS選擇符是從右到左進(jìn)行匹配的。當(dāng)使用后代選擇器的時(shí)候,瀏覽器會(huì)遍歷所有子元素來確定是否是指定的元素等等;
(2)如果規(guī)則擁有ID選擇器作為其關(guān)鍵選擇器,則不要為規(guī)則增加標(biāo)簽。過濾掉無關(guān)的規(guī)則(這樣樣式系統(tǒng)就不會(huì)浪費(fèi)時(shí)間去匹配它們了)。
(3)避免使用通配規(guī)則,如*{}計(jì)算次數(shù)驚人,只對需要用到的元素進(jìn)行選擇。
(4)盡量少的去對標(biāo)簽進(jìn)行選擇,而是用class。
(5)盡量少的去使用后代選擇器,降低選擇器的權(quán)重值。后代選擇器的開銷是最高的,盡量將選擇器的深度降到最低,最高不要超過三層,更多的使用類來關(guān)聯(lián)每一個(gè)標(biāo)簽元素。
(6)了解哪些屬性是可以通過繼承而來的,然后避免對這些屬性重復(fù)指定規(guī)則。
渲染性能:
(1)慎重使用高性能屬性:浮動(dòng)、定位。
(2)盡量減少頁面重排、重繪。
(3)去除空規(guī)則:{}。空規(guī)則的產(chǎn)生原因一般來說是為了預(yù)留樣式。去除這些空規(guī)則無疑能減少css文檔體積。
(4)屬性值為0時(shí),不加單位。
(5)屬性值為浮動(dòng)小數(shù)0.**,可以省略小數(shù)點(diǎn)之前的0。
(6)標(biāo)準(zhǔn)化各種瀏覽器前綴:帶瀏覽器前綴的在前。標(biāo)準(zhǔn)屬性在后。
(7)不使用@import前綴,它會(huì)影響css的加載速度。
(8)選擇器優(yōu)化嵌套,盡量避免層級過深。
(9)css雪碧圖,同一頁面相近部分的小圖標(biāo),方便使用,減少頁面的請求次數(shù),但是同時(shí)圖片本身會(huì)變大,使用時(shí),優(yōu)劣考慮清楚,再使用。
(10)正確使用display的屬性,由于display的作用,某些樣式組合會(huì)無效,徒增樣式體積的同時(shí)也影響解析性能。
(11)不濫用web字體。對于中文網(wǎng)站來說WebFonts可能很陌生,國外卻很流行。web fonts通常體積龐大,而且一些瀏覽器在下載web fonts時(shí)會(huì)阻塞頁面渲染損傷性能。
可維護(hù)性、健壯性:
(1)將具有相同屬性的樣式抽離出來,整合并通過class在頁面中進(jìn)行使用,提高css的可維護(hù)性。
(2)樣式與內(nèi)容分離:將css代碼定義到外部css中。
列表轉(zhuǎn)成樹形結(jié)構(gòu)
題目描述:
[
{
id: 1,
text: '節(jié)點(diǎn)1',
parentId: 0 //這里用0表示為頂級節(jié)點(diǎn)
},
{
id: 2,
text: '節(jié)點(diǎn)1_1',
parentId: 1 //通過這個(gè)字段來確定子父級
}
...
]
轉(zhuǎn)成
[
{
id: 1,
text: '節(jié)點(diǎn)1',
parentId: 0,
children: [
{
id:2,
text: '節(jié)點(diǎn)1_1',
parentId:1
}
]
}
]
實(shí)現(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;
}