JSON傳輸格式
json是一種傳輸數據的格式(以對象爲樣板,本質上即使對象,但是用途有區別,對象就是本地用的,json是用來傳輸的)
對象的屬性加不加雙引號都可以,但是json規定 json格式的屬性名必須加上雙引號
。
JSON是一個靜態類(類似於Math),不用去構造它,它自己就有這些方法。
-
JSON.stringifg(); json—>string
-
JSON.parse(); string—>json
var obj = {
"name" : "abc",
"age" : 123
}
var str = JSON.stringify(obj);
console.log(str);//"{"name":"abc","age":123}"
console.log(JSON.parse(str));//Object{name: "abc", age: 123}
前後端傳輸的時候傳的都是json格式的字符串
瀏覽器渲染頁面過程
第一步,首先是建立一個dom樹,它是解析所有的dom節點(深度遍歷的方法)
解析dom節點就是解析到哪個dom節點就把它掛到dom樹上
像碰到< img src = “XXX”/>這樣的還是隻把img節點掛到dom樹上,意思就是說dom樹的構建完成就是所有dom節點的解析完畢,並不代表所有dom節點加載完畢(dom的解析完畢一定在dom加載完畢之前)。
第二步生成cssTree
瀏覽器解析CSS代碼,計算出最終的樣式數據。解析CSS的時候會按照如下順序來定義優先級:瀏覽器默認設置,用戶設置,外聯樣式,內聯樣式,html中的style(嵌在標籤中的行間樣式)。
第三步生成randertree,也可以說是渲染樹
渲染樹中每一個節點都存儲着對應的CSS屬性。
因爲js可以操作dom,所以當渲染樹建立後,如果動態的操作了dom,機會導致頁面重新渲染,叫做reflow重排(重構)
reflow重排:dom節點的刪除,添加。dom節點的寬高變化,位置變化,displaynone–>block,offsetwidth,offsetLeft等一系列都會導致重排。
repaint重繪:js改變了節點 的css,會導致那一部分的重繪,這樣影響比較小。
第四步:當渲染樹創建完成之後,瀏覽器就可以根據渲染樹直接把頁面繪製到屏幕上。
DOM樹完全和html標籤一一對應,而渲染樹會忽略不需要渲染的元素(head、display:none的元素)。
JS異步加載
js加載的缺點
js加載本身是屬於同步加載的,加載js文件會阻塞文檔,比如有多個js文件,在加載到一個文件處有錯後面的就加載不了
或者一旦網速不好,那麼整個網站將等待js加載而不進行後續渲染等工作
但是有些工具方法需要按需加載,有一些工具js文件它是不會改變頁面的,用到再加載,不用不加載。
javascript異步加載的三種的方案
- . defer異步加載
defer異步加載,但要等到dom文檔全部解析完纔會被執行,也可以將代碼寫到內部。
defer 屬性是一個布爾屬性。
<script src="demo_defer.js" defer></script>
- async 異步加載
async 異步加載,加載完就執行,async 只能加載外部腳本(只有在使用 src 屬性時),不能把js寫在script標籤裏。
Internet Explorer 9 及之前的版本不支持 script 標籤的 async 屬性。
異步腳本一定會在頁面 load 事件前執行。
<script src="demo_async.js" async></script>
defer和async異步加載時不阻塞頁面,但是兼容性不好,不能控制加載的順序
3. 創建script,插入到DOM中,加載完畢後callBack
<script>
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "demo.js";//到了這一步之後就會異步的去下載demo.js文件
document.head.appendChild(script);//當把標籤插入到頁面的時候纔會去執行這個js腳本
</script>
接下來我麼創建一個名爲demo.js文件寫入test函數
function test(){
condole.log("a“);
}
執行他
<script>
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "demo.js";//到了這一步之後就會異步的去下載demo.js文件
document.head.appendChild(script);//當把標籤插入到頁面的時候纔會去執行這個js腳本
test();
</script>
執行到test()時候會報錯
Uncaught ReferenceError: test is not defined
這是因爲文件還沒有下載完,讀程序是以微秒計的。
有可能異步的下載demo.js文件還沒有下載完,程序就讀到test執行這了
還沒有下載完demo.js,當然沒有test函數了
接着我們 添加一個setTimeout() 方法,讓test()在1000毫秒後再執行時不時就不報錯了
<script>
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "demo.js";//到了這一步之後就會異步的去下載demo.js文件
document.head.appendChild(script);//當把標籤插入到頁面的時候纔會去執行這個js腳本
setTimeout(function () {
test();
}, 1000)
</script>
但是這樣並不能從根本上解決問題,我們需要知道程序什麼時候異步把外部js文件下載完成
下面介紹幾種方法讓我們知道什麼時候加載完成
- load事件監聽:
Safari,chrome,firefox,opera都可以用這個來監聽什麼時候這個文件異步下載完畢
<script>
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "demo.js";
script.onload = function(){
test();
}
document.head.appendChild(script);
</script>
當文件下載完之後就會執行onload方法,這樣就知道什麼時候demo.js加載完了。
- 如果是IE瀏覽器 就用·狀態碼的改變·來知道什麼時候完成文件的下載
<script>
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "demo.js";
script.onreadystatechange = function(){
if(script.readyState == "complete" || script.readyState == "loaded"){
test();
}
}
document.head.appendChild(script);
</script>
綜合以上兩鍾方法實現所有瀏覽器兼容
<script>
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "demo.js";//到了這一步之後就會異步的去下載demo.js文件
if(script.readyState){
script.onreadystatechange = function(){
if(script.readyState == "complete" || script.readyState == "loaded"){
test();
}
}
}else{
script.onload = function(){
test();
}
}
document.head.appendChild(script);
</script>
案例:封裝一個函數兼容性的異步加載js文件並且可以按需執行該文件裏面的函數
callback:回調函數
在事件裏面的綁定的事件處理函數就是回調函數,回調函數其實就是當滿足一定條件才調用的函數。
function loadScript (url, callback){
// url是異步下載的js文件
//callback是異步下載的js文件中的某一個函數
var script = document.createElement('script');
script.type = "text/javascript";
script.src = url;//異步下載的js文件
if(script.readyState){
script.onreadystatechange = function(){
if(script.readyState == "complete" || script.readyState == "loaded"){
callback();
}
}
}else{
script.onload = function(){
callback();
}
}
document.head.appendChild(script);
}
</script>
在IE上如果下載太快(比讀程序還快),IE的readystatechange 事件檢測狀態碼的時候,它早已經從loading變成complete或者loaded(以極快的速度加載完了文件,你還沒來得及檢測),
那你再檢測它就不會變了,它一直都是complete或者loaded,這個時候檢測也沒用了。
<script>
function loadScript (url, callback){
// url是異步下載的js文件
//callback是異步下載的js文件中的某一個函數
var script = document.createElement('script');
script.type = "text/javascript";
if(script.readyState){
script.onreadystatechange = function(){
if(script.readyState == "complete" || script.readyState == "loaded"){
callback();
}
}
}else{
script.onload = function(){
callback();
}
}
script.src = url;//開始異步下載的js文件
document.head.appendChild(script);
}
</script>
先綁定事件,然後再script.src = url開始異步下載js文件,那麼這個時候下載,肯定有一個狀態碼的轉換,這樣就解決了來不及檢測狀態碼的尷尬。
時間線
-
創建Documen對象開始解析web頁面。解析HTML元素和他們的文本內容後添加Element對象和Text節點到文檔中。這個階段document.readyState= ‘loading’ 。
-
遇到link外部css,創建線程加載,井繼續解析文檔。
-
遇到script外部js,並且沒有設置async,defer, 瀏覽器加載,並阻塞,等待js加載完成並執行該腳本,然後繼續解析文檔。
-
遇到script外部js.並且設置有async, defer,瀏覽器創建線程加載,並繼續解析文檔。對於async屬性的腳本,腳本加載完成後立即執行。(異步禁止使用document.write(),因爲它會清除文檔流)
-
遇到img等,先正常解析dom結構,然後瀏覽器異步加載src,並繼續解析文檔。
-
當文檔解析完成,document.readyState = ‘Interactive’。
-
文檔解析完成後,所有設置有defer的腳本會按照順序執行。(注意 async的不同,但同樣禁止使用document.wite()) ;
-
document對象觸發DOMContentLoaded事件,這也標誌着程序執行從同步腳本執行階段,轉化爲事件驅動階段。
-
當所有async的腳本加載完成井執行後。img等加載完成後,document.readyState = ‘complete’, window對象觸發load事件。
-
從此,以異步響應方式處理用戶輸入,網絡事件等。
document.write()會清除文檔流
<body>
<div style="width:100px;height:100px;background-color:red;"></div>
<script>
window.onload = function () {
document.write('a');
}
</script>
</body>
這個時候頁面就只會輸出a,div就被清除了
創建Documen對象開始解析web頁面,script標籤也是dom元素,所以它也要識別,這個階段document.readyState= ‘loading’ 。
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<script>
console.log(document.readyState);//loading
<script>
</body>
</html>
用狀態碼改變才能檢測文檔解析到哪個狀態了
<body>
<script>
console.log(document.readyState);//loading
document.onreadystatechange = function (){
console.log(document.readyState);// interactive complete
}
<script>
</body>
DOMContentLoaded事件只有用addEventListener綁定纔有效
document.addEventListener('DOMContentLoaded',function(){ console.log('a')},false);
正常的情況下,當文檔加載(解析文檔,就是構成dom樹,然後再加載dom元素)完畢後,才執行js代碼,
但是document對象觸發DOMContentLoaded事件可以當dom解析完了(dom樹構建完成,還沒加載完畢的時候)就去執行js代碼
/就像jquery裏面的
當dom解析完就執行的部分
$(document).ready(function (){
…
})
現在用document對象觸發DOMContentLoaded事件放在head裏面,照樣可以拿到div
因爲它是在dom樹構建完成之後就調用事件的那個處理函數,不用等到文檔全部加載完畢
而window.onload()就是要等到文檔全部加載完畢了(像img的src的圖片加載完成了),它纔會執行window.onload()裏面的js。
<!DOCTYPE html>
<html lang="en">
<head>
<script>
document.addEventListener('DOMContentLoaded',function(){
var div = document.getElementsByTagName('div')[0];
console.log(div);
},false);
</script>
</head>
<body>
<div style="width:100px;height:100px;background-color:red"></div>
</body>
</html>