瀑布流佈局的原理: 1) 瀑布流佈局要求要進行佈置的元素等寬, 而後計算元素的寬度, 與瀏覽器寬度之比,獲得須要佈置的列數; 2) 建立一個數組,長度爲列數, 裏面的值爲已佈置元素的總高度(最開始爲0); 3) 而後將未佈置的元素依次佈置到高度最小的那一列, 就獲得了瀑布流佈局; 4) 滾動加載, scroll事件獲得scrollTop, 與最後盒子的offsetTop對比, 符合條件就不斷滾動加載。 瀑布流佈局核心代碼: /** * 實現瀑布流的佈局 * @param {string}parentBox * @param {string}childBox */ function waterFull(parentBox, childBox) { // 1. 求出父盒子的寬度 // 1.1 獲取全部的子盒子 var allBox = $(parentBox). getElementsByClassName(childBox); // console.log(allBox); // 1.2 求出子盒子的寬度 var boxWidth = allBox[0].offsetWidth; // console.log(boxWidth); // 1.3 獲取窗口的寬度 var clientW = document. documentElement.clientWidth; // console.log(clientW); // 1.4 求出總列數 var cols = Math.floor(clientW / boxWidth); // console.log(cols); // 1.5 父盒子居中 $(parentBox).style.width = cols * boxWidth + 'px'; $(parentBox).style.margin = '0 auto'; // 2. 子盒子定位 // 2.1 定義變量 var heightArr = [], boxHeight = 0, minBoxHeight = 0, minBoxIndex = 0; // 2.2 遍歷全部的子盒子 for (var i = 0; i < allBox.length; i++) { // 2.2.1 求出每個子盒子的高度 boxHeight = allBox[i].offsetHeight; // console.log(boxHeight); // 2.2.2 取出第一行盒子的高度放入高度數組中 if (i < cols) { // 第一行 heightArr.push(boxHeight); } else { // 剩餘行的盒子 // 2.2.3 取出數組中最矮的高度 minBoxHeight = _.min(heightArr); // 2.2.4 求出最矮高度對應的索引 minBoxIndex = getMinBoxIndex(heightArr, minBoxHeight); // 2.2.5 盒子定位 allBox[i].style.position = 'absolute'; allBox[i].style.left = minBoxIndex * boxWidth + 'px'; allBox[i].style.top = minBoxHeight + 'px'; // 2.2.6 更新最矮的高度 heightArr[minBoxIndex] += boxHeight; } } } /** * 根據內容取出在數組中對應的索引 * @param {object}arr * @param {number}val * @returns {boolean} */ function getMinBoxIndex(arr, val) { for (var i = 0; i < arr.length; i++) { if (arr[i] === val) return i; } } /** * 判斷是否具有加載子盒子的條件 * @returns {boolean} */ function checkWillLoadImage() { // 1. 獲取最後一個盒子 var allBox = $('main').getElementsByClassName('box'); var lastBox = allBox[allBox.length - 1]; // 2. 求出高度 var lastBoxDis = lastBox.offsetHeight * 0.5 + lastBox.offsetTop; // 3. 求出窗口的高度 var clientH = document.documentElement.clientHeight; // 4. 求出頁面滾動產生的高度 var scrollTopH = scroll().top; // 5. 對比 return lastBoxDis <= clientH + scrollTopH; }
1) 經過url地址欄傳遞參數; 例如:點擊列表頁中的每一條數據, 咱們跳轉到同一個詳細頁面, 可是根據點擊的不同能夠看到 不一樣的內容,這樣的話咱們就能夠 在URL中傳遞不一樣的值來區分了; 2) 經過本地存儲 cookie、localeStorage、 sessionStroage...,例如:京東的登陸, 咱們在登陸頁登陸完成後, 把用戶信息存儲到本地, 而後在其它頁面中若是須要使用的話, 咱們直接從本地的存儲數據中拿 出來用便可; 3) 使用iframe在A頁面中嵌入B頁面, 這樣的話,在A中能夠經過一些屬性 和方法實現和B頁面的通訊; 4) 利用postMessage實現頁面間通訊, 父窗口往子窗口傳遞信息, 子窗口往父窗口傳遞信息。
此題主要考性能! 1) 能夠用JS中的createElement建立div, 每當建立一個就把它添加到div中, 但會形成引起迴流的次數太多; 2) 使用字符串拼接的方式, 把1000個div都拼接完成後, 統一的添加到頁面中, 但會對div原有的元素標籤產生影響: 原來標籤綁定的事件都消失了 3) 綜合1和2可使用文檔碎片方式來處理。 追問:若是是建立1000萬個呢? 可採用的方案: 數據分批異步加載 1) 首先把前兩屏幕的數據量 (例如:300條)先獲取到, 而後使用字符串拼接或者文檔碎片 的方式綁定到頁面中; 2) 當瀏覽器滾動到指定的區域的 時候在加載300條...以此類推。
1) 在控制檯加斷點, F10->逐過程 F11->逐語句; 2) 在代碼重點的位置加入 console.log輸出對應的值來進行調試; 3) debugger調試; 4) 代碼分割還原調試; 5) 異常捕獲機制, 記錄運行日誌; 6) 單元測試。
如今框架(vue, react,...)、構建工具(webpack, ...) 已經給咱們解決掉大部分的性能優化問題, 面試時, 能夠就你瞭解的框架來深刻剖析, 但此題應該是考原生JS的範疇, 參考答案以下: 1) 雅虎35條性能優化黃金定律; 2) JS代碼優化: a. 項目中的JS/CSS文件最好一個頁面只用一個, 須要把JS/CSS進行合併壓縮, 而且減小頁面中的垃圾冗餘代碼。 項目的資源文件在服務器上最好 作一下GZIP壓縮。 b. 解除文件緩存; 咱們修改代碼並上傳, 若是以前頁面訪問過該網站, 頗有可能不能當即見效; 咱們在引入CSS/JS文件的時候, 在文件名的後面加上版本號(加時間戳), 好比: <script src='itlike.com.js?_=202001...'></script>; 當咱們上傳新的文件後 把時間戳改一下就能夠清除緩存了。 c. 移動端儘可能少用圖片: icon能用svg畫的不用圖片; 靜態資源圖:作佈局的時候就能肯定下來的圖片, 好比: 1) css sprite圖片合併 (針對於小圖片) 2) 作圖片延遲加載 (針對於大圖片 頭部的長條圖片、背景大圖...), 開始給一張默認的小的圖片 (最好維持在10kb之內) 3) base64 (存在問題: 頁面的代碼太臃腫了,之後維護很差操做); 若是項目中因爲圖片太大實在解決不了, 改爲base64就解決了 d. 動態數據圖: 經過ajax從後臺讀取回來的圖片 , 圖片懶加載; e. 音視頻文件的優化: 加載頁面的時候,儘可能不要加載音視頻文件, 當頁面中的其餘資源加載完成後, 再開始加載音視頻文件; 目前移動端常常給音視頻作的優化是: 走直播流文件(音頻後綴名是m3u8格式); f. 減小頁面資源請求的次數: 若是當前只是一個宣傳頁, 或者是一個簡單的頁面, 使用的css和js能夠採用內嵌式開發; g. ajax數據請求分批請求, 例如:一次要請求10000條數據的話, 咱們每一次只請求100條,第一屏幕確定能看全了, 當頁面滾動到對應的其它屏幕的時候, 在加載下一個100條... h. 作數據的二次緩存, 能用CSS3作動畫的絕對不用JS, 能使用transform儘可能使用, 能用animation的進行不用transition... 儘可能減小同步操做,多用異步操做; 能使用原生JS本身編寫的, 絕對不用插件或者框架;
1) 封裝緩動動畫函數; 2) 點擊切換, 滾動切換, 聯動處理; 核心代碼以下: // 3. 監聽GPS上的li的點擊 for (var j = 0; j < olLis.length; j++) { (function (index) { var olLi = olLis[index]; olLi.onmousedown = function () { isClick = true; // 3.1 排他 for (var m = 0; m < olLis.length; m++) { olLis[m].className = '' } addClass(this, 'current'); // 3.2 讓樓層滾動起來 buffer( document.documentElement, {'scrollTop': index * client().height}, function () { isClick = false; }) } })(j) } // 4. 監聽文檔的滾動 window.onscroll = function (ev1) { // 4.1 沒有點擊產生的滾動 if (!isClick) { // 4.2 獲取頁面產出的頭部滾動的高度 var roll = Math.ceil(scroll().top); console.log(roll); // 4.3 遍歷 for (var i = 0; i < olLis.length; i++) { // 4.4 判斷 if (roll >= ulLis[i].offsetTop) { for (var m = 0; m < olLis.length; m++) { olLis[m].className = '' } addClass(olLis[i], 'current'); } } } } 緩動動畫函數: /** * 緩動動畫(撩課學院) * @param {object}obj * @param {object}json * @param {function}fn */ function buffer(obj, json, fn) { // 1.1 清除定時器 clearInterval(obj.timer); // 1.2 設置定時器 var begin = 0, target = 0, speed = 0; obj.timer = setInterval(function () { // 1.3.0 旗幟 var flag = true; for (var k in json) { // 1.3 獲取初始值 if ("opacity" === k) { // 透明度 begin = parseInt(parseFloat(getCSSAttrValue(obj, k)) * 100); target = parseInt(parseFloat(json[k]) * 100); } else if ("scrollTop" === k) { begin = Math.ceil(obj.scrollTop); target = parseInt(json[k]); } else { // 其餘狀況 begin = parseInt(getCSSAttrValue(obj, k)) || 0; target = parseInt(json[k]); } // 1.4 求出步長 speed = (target - begin) * 0.2; // 1.5 判斷是否向上取整 speed = (target > begin) ? Math.ceil(speed) : Math.floor(speed); // 1.6 動起來 if ("opacity" === k) { // 透明度 // w3c的瀏覽器 obj.style.opacity = (begin + speed) / 100; // ie 瀏覽器 obj.style.filter = 'alpha(opacity:' + (begin + speed) + ')'; } else if ("scrollTop" === k) { obj.scrollTop = begin + speed; } else if ("zIndex" === k) { obj.style[k] = json[k]; } else { obj.style[k] = begin + speed + "px"; } // 1.5 判斷 if (begin !== target) { flag = false; } } // 1.3 清除定時器 if (flag) { clearInterval(obj.timer); // 判斷有沒有回調函數 if (fn) { fn(); } } }, 20); }