在vue項目下的 config/index.js 文件裏面配置代理proxyTable:javascript
var path = require('path') module.exports = { build: { env: require('./prod.env'), index: path.resolve(__dirname, '../dist/index.html'), assetsRoot: path.resolve(__dirname, '../dist'), assetsSubDirectory: 'static', assetsPublicPath: '/', productionSourceMap: true, // Gzip off by default as many popular static hosts such as // Surge or Netlify already gzip all static assets for you. // Before setting to `true`, make sure to: // npm install --save-dev compression-webpack-plugin productionGzip: false, productionGzipExtensions: ['js', 'css'], // Run the build command with an extra argument to // View the bundle analyzer report after build finishes: // `npm run build --report` // Set to `true` or `false` to always turn it on or off bundleAnalyzerReport: process.env.npm_config_report }, dev: { env: require('./dev.env'), port: 8080, autoOpenBrowser: true, assetsSubDirectory: 'static', assetsPublicPath: '/', proxyTable: { '/login': { target: 'http://192.168.0.240:8888/yammiicn/v1/login', changeOrigin: true, pathRewrite: { '^/login':'' } } }, // CSS Sourcemaps off by default because relative paths are "buggy" // with this option, according to the CSS-Loader README // (https://github.com/webpack/css-loader#sourcemaps) // In our experience, they generally work as expected, // just be aware of this issue when enabling this option. cssSourceMap: false } }
使用php
Vue.http.options.xhr = { withCredentials: true } //或是 this.$http.jsonp('...', { credentials: true }) //例子 this.$http.jsonp("https://sug.so.360.cn/suggest",{ params:{ word:'a' } }).then(resp=>{ console.log(resp.data.s); },response => { console.log("發送失敗"+response.status+","+response.statusText); });
JS自適應高度,其實就是設置iframe的高度,使其等於內嵌網頁的高度,從而看不出來滾動條和嵌套痕跡。對於用戶體驗和網站美觀起着重要做用。若是內容是固定的,那麼咱們能夠經過CSS來給它直接定義一個高度,一樣能夠實現上面的需求。當內容是未知或者是變化的時候。這個時候又有幾種狀況了。iframe內容未知,高度可預測這個時候,咱們能夠給它添加一個默認的CSS的min-height值,而後同時使用JavaScript改變高度。經常使用的兼容代碼有:`html
// document.domain = "caibaojian.com"; function setIframeHeight(iframe) { if (iframe) { var iframeWin = iframe.contentWindow || iframe.contentDocument.parentWindow; if (iframeWin.document.body) { iframe.height = iframeWin.document.documentElement.scrollHeight || iframeWin.document.body.scrollHeight; } } }; window.onload = function () { setIframeHeight(document.getElementById('external-frame')); };
演示地址·演示一(若是在同個頂級域名下,不一樣子域名之間互通訊息,設置document.domain=」caibaojian.com」只要修改以上的iframe的ID便可了。或者你能夠直接在iframe裏面寫代碼,咱們通常爲了避免污染HTML代碼,建議使用上面的代碼。前端
<iframe src="backtop.html" frameborder="0" scrolling="no" id="external-frame" onload="setIframeHeight(this)"></iframe>
演示二多個iframe的狀況下`vue
<script language="javascript"> //輸入你但願根據頁面高度自動調整高度的iframe的名稱的列表 //用逗號把每一個iframe的ID分隔. 例如: ["myframe1", "myframe2"],能夠只有一個窗體,則不用逗號。 //定義iframe的ID var iframeids=["test"]; //若是用戶的瀏覽器不支持iframe是否將iframe隱藏 yes 表示隱藏,no表示不隱藏 var iframehide="yes"; function dyniframesize() { var dyniframe=new Array() for (i=0; i<iframeids.length; i++) { if (document.getElementById) { //自動調整iframe高度 dyniframe[dyniframe.length] = document.getElementById(iframeids[i]); if (dyniframe[i] && !window.opera) { dyniframe[i].style.display="block"; if (dyniframe[i].contentDocument && dyniframe[i].contentDocument.body.offsetHeight) //若是用戶的瀏覽器是NetScape dyniframe[i].height = dyniframe[i].contentDocument.body.offsetHeight; else if (dyniframe[i].Document && dyniframe[i].Document.body.scrollHeight) //若是用戶的瀏覽器是IE dyniframe[i].height = dyniframe[i].Document.body.scrollHeight; } } //根據設定的參數來處理不支持iframe的瀏覽器的顯示問題 if ((document.all || document.getElementById) && iframehide=="no") { var tempobj=document.all? document.all[iframeids[i]] : document.getElementById(iframeids[i]); tempobj.style.display="block"; } } } if (window.addEventListener) window.addEventListener("load", dyniframesize, false); else if (window.attachEvent) window.attachEvent("onload", dyniframesize); else window.onload=dyniframesize; </script>
演示三針對知道的iframe的ID調用html5
function iframeAutoFit(iframeObj){ setTimeout(function(){if(!iframeObj) return;iframeObj.height=(iframeObj.Document?iframeObj.Document.body.scrollHeight:iframeObj.contentDocument.body.offsetHeight);},200) }
演示四內容寬度變化的iframe高度自適應java
<iframe src="backtop.html" frameborder="0" scrolling="no" id="test" onload="this.height=100"></iframe> <script type="text/javascript"> function reinitIframe(){ var iframe = document.getElementById("test"); try{ var bHeight = iframe.contentWindow.document.body.scrollHeight; var dHeight = iframe.contentWindow.document.documentElement.scrollHeight; var height = Math.max(bHeight, dHeight); iframe.height = height; console.log(height); }catch (ex){} } window.setInterval("reinitIframe()", 200);
演示五打開調試運行窗口能夠看到運行。python
跨域下的iframe自適應高度跨域的時候,因爲js的同源策略,父頁面內的js不能獲取到iframe頁面的高度。須要一個頁面來作代理。
方法以下:假設www.a.com下的一個頁面a.html要包含www.b.com下的一個頁面c.html。
咱們使用www.a.com下的另外一個頁面agent.html來作代理,經過它獲取iframe頁面的高度,並設定iframe元素的高度。jquery
a.html中包含iframe:
<iframe src="http://www.b.com/c.html" id="Iframe" frameborder="0" scrolling="no" style="border:0px;"></iframe>
在c.html中加入以下代碼:
<iframe id="c_iframe" height="0" width="0" src="http://www.a.com/agent.html" style="display:none" ></iframe> <script type="text/javascript"> (function autoHeight(){ var b_width = Math.max(document.body.scrollWidth,document.body.clientWidth); var b_height = Math.max(document.body.scrollHeight,document.body.clientHeight); var c_iframe = document.getElementById("c_iframe"); c_iframe.src = c_iframe.src + "#" + b_width + "|" + b_height; // 這裏經過hash傳遞b.htm的寬高 })(); </script>
最後,agent.html中放入一段js:
<script type="text/javascript"> var b_iframe = window.parent.parent.document.getElementById("Iframe"); var hash_url = window.location.hash; if(hash_url.indexOf("#")>=0){ var hash_width = hash_url.split("#")[1].split("|")[0]+"px"; var hash_height = hash_url.split("#")[1].split("|")[1]+"px"; b_iframe.style.width = hash_width; b_iframe.style.height = hash_height; } </script>
agent.html從URL中得到寬度值和高度值,並設置iframe的高度和寬度(由於agent.html在www.a.com下,因此操做a.html時不受JavaScript的同源限制) avascript中獲取dom元素高度和寬度的方法以下: 網頁可見區域寬: document.body.clientWidth 網頁可見區域高: document.body.clientHeight 網頁可見區域寬: document.body.offsetWidth (包括邊線的寬) 網頁可見區域高: document.body.offsetHeight (包括邊線的高) 網頁正文全文寬: document.body.scrollWidth 網頁正文全文高: document.body.scrollHeight 網頁被捲去的高: document.body.scrollTop 網頁被捲去的左: document.body.scrollLeft 對應的dom元素的寬高有如下幾個經常使用的: 元素的實際高度:document.getElementById("div").offsetHeight 元素的實際寬度:document.getElementById("div").offsetWidth 元素的實際距離左邊界的距離:document.getElementById("div").offsetLeft 元素的實際距離上邊界的距離:document.getElementById("div").offsetTop
data(){
return { clientHeight: '600px', }, }, mounted() { // 動態設置背景圖的高度爲瀏覽器可視區域高度 // 首先在Virtual DOM渲染數據時,設置下背景圖的高度. this.clientHeight.height = `${document.documentElement.clientHeight}px`; // 而後監聽window的resize事件.在瀏覽器窗口變化時再設置下背景圖高度. const that = this; window.onresize = function temp() { that.clientHeight = `${document.documentElement.clientHeight}px`; }; },
[https://github.com/pagekit/vue-resource/blob/develop/docs/http.md]:
用戶訪問未使用CDN緩存網站的過程爲:
1)、用戶向瀏覽器提供要訪問的域名;
2)、瀏覽器調用域名解析函數庫對域名進行解析,以獲得此域名對應的IP地址;
3)、瀏覽器使用所獲得的IP地址,向域名的服務主機發出數據訪問請求;
4)、瀏覽器根據域名主機返回的數據顯示網頁的內容。
經過以上四個步驟,瀏覽器完成從用戶處接收用戶要訪問的域名到從域名服務主機處獲取數據的整個過程。CDN網絡是在用戶和服務器之間增長Cache層,如何將用戶的請求引導到Cache上得到源服務器的數據,主要是經過接管DNS實現,下面讓咱們看看訪問使用CDN緩存後的網站的過程:
經過上圖,咱們能夠了解到,使用了CDN緩存後的網站的訪問過程變爲:
1)、用戶向瀏覽器提供要訪問的域名;
2)、瀏覽器調用域名解析庫對域名進行解析,因爲CDN對域名解析過程進行了調整,因此解析函數庫通常獲得的是該域名對應的CNAME記錄,爲了獲得實際IP地址,瀏覽器須要再次對得到的CNAME域名進行解析以獲得實際的IP地址;在此過程當中,使用的全局負載均衡DNS解析,如根據地理位置信息解析對應的IP地址,使得用戶能就近訪問。
3)、這次解析獲得CDN緩存服務器的IP地址,瀏覽器在獲得實際的IP地址之後,向緩存服務器發出訪問請求;
4)、緩存服務器根據瀏覽器提供的要訪問的域名,經過Cache內部專用DNS解析獲得此域名的實際IP地址,再由緩存服務器向此實際IP地址提交訪問請求;
5)、緩存服務器從實際IP地址得獲得內容之後,一方面在本地進行保存,以備之後使用,另外一方面把獲取的數據返回給客戶端,完成數據服務過程;
6)、客戶端獲得由緩存服務器返回的數據之後顯示出來並完成整個瀏覽的數據請求過程。
經過以上的分析咱們能夠獲得,爲了實現既要對普通用戶透明(即加入緩存之後用戶客戶端無需進行任何設置,直接使用被加速網站原有的域名便可訪問,又要在爲指定的網站提供加速服務的同時下降對ICP的影響,只要修改整個訪問過程當中的域名解析部分,以實現透明的加速服務,下面是CDN網絡實現的具體操做過程。
1)、做爲ICP,只須要把域名解釋權交給CDN運營商,其餘方面不須要進行任何的修改;操做時,ICP修改本身域名的解析記錄,通常用cname方式指向CDN網絡Cache服務器的地址。
2)、做爲CDN運營商,首先須要爲ICP的域名提供公開的解析,爲了實現sortlist,通常是把ICP的域名解釋結果指向一個CNAME記錄;
3)、當須要進行sortlist時,CDN運營商能夠利用DNS對CNAME指向的域名解析過程進行特殊處理,使DNS服務器在接收到客戶端請求時能夠根據客戶端的IP地址,返回相同域名的不一樣IP地址;
4)、因爲從cname得到的IP地址,而且帶有hostname信息,請求到達Cache以後,Cache必須知道源服務器的IP地址,因此在CDN運營商內部維護一個內部DNS服務器,用於解釋用戶所訪問的域名的真實IP地址;
5)、在維護內部DNS服務器時,還須要維護一臺受權服務器,控制哪些域名能夠進行緩存,而哪些又不進行緩存,以避免發生開放代理的狀況。
1.用戶向瀏覽器輸入www.web.com這個域名,瀏覽器第一次發現本地沒有dns緩存,則向網站的DNS服務器請求;
2.網站的DNS域名解析器設置了CNAME,指向了www.web.51cdn.com,請求指向了CDN網絡中的智能DNS負載均衡系統;
3.智能DNS負載均衡系統解析域名,把對用戶響應速度最快的IP節點返回給用戶;
4.用戶向該IP節點(CDN服務器)發出請求;
5.因爲是第一次訪問,CDN服務器會向原web站點請求,並緩存內容;
6.請求結果發給用戶。
合理規劃站點結構(一、扁平化結構 二、輔助導航、麪包屑導航、次導航)
內容頁結構設置(最新文章、推薦文章、熱門文章、增長相關性、方便自助根據連接抓取更多內容)
較快的加載速度
簡潔的頁面結構
Robot.txt
次導航
404頁面設置、301重定向
網站地圖
圖片Alt、title標籤
標題
關鍵詞
描述
關鍵字密度
個別關鍵字密度
H1H2H3中的關鍵字
關鍵字強調
外鏈最好nofollow
爲頁面添加元標記meta
豐富網頁摘要(微數據、微格式和RDFa)
html網站地圖(一、爲搜索引擎創建一個良好的導航結構 二、橫向和縱向地圖:01橫向爲頻道、欄目、專題/02縱向主要針對關鍵詞 三、每頁都有指向網站地圖的連接)
XML網站地圖(sitemap.xml提交給百度、google)
挑選關鍵詞的步驟(一、肯定目標關鍵詞 二、目標關鍵詞定義上的擴展 三、模擬用戶的思惟設計關鍵詞 四、研究競爭者的關鍵詞)
頁面關鍵詞優化前後順序(一、最終頁>專題>欄目>頻道>首頁 二、最終頁:長尾關鍵詞 三、專題頁:【a、熱門關鍵詞 b、爲熱點關鍵詞製做專題 c、關鍵詞相關信息的聚合 d、輔以文章內鏈導入連接】 四、欄目頁:固定關鍵詞 五、頻道頁:目標關鍵詞 六、首頁:作行業一到兩個頂級關鍵詞,或者網站名稱)
關鍵詞部署建議(一、不要把關鍵詞堆積在首頁 二、每一個頁面承載關鍵詞合理數目爲3-5個 三、系統規劃)
而後,咱們的內容編輯人員要對網站進行內容建設,怎樣合理的作到網站內部優化的功效?這裏主要有五個方面:
原創內容或僞原創內容
編輯撰稿或UGC
掃描書籍、報刊、雜誌
標題寫法、關鍵詞、描述設置
文章摘要規範
URL標準化
次導航
內頁增長錨文本以及第一次出現關鍵詞進行加粗
長尾關鍵詞記錄單
圖片Alt、titile標籤
外鏈最好nofollow
百度站長工具、google管理員工具的使用
創建反向連接
挑選關鍵詞的步驟(一、肯定目標關鍵詞 二、目標關鍵詞定義上的擴展 三、模擬用戶的思惟設計關鍵詞 四、研究競爭者的關鍵詞)
頁面關鍵詞優化前後順序(一、最終頁>專題>欄目>頻道>首頁 二、最終頁:長尾關鍵詞 三、專題頁:【a、熱門關鍵詞 b、爲熱點關鍵詞製做專題 c、關鍵詞相關信息的聚合 d、輔以文章內鏈導入連接】 四、欄目頁:固定關鍵詞 五、頻道頁:目標關鍵詞 六、首頁:作行業一到兩個頂級關鍵詞,或者網站名稱)
關鍵詞部署建議(一、不要把關鍵詞堆積在首頁 二、每一個頁面承載關鍵詞合理數目爲3-5個 三、系統規劃)
控制文章內部連接數量
連接對象的相關性要高
給重要網頁更多的關注
使用絕對路徑
須要改進的地方
不要大量採集
有節奏的更新
編輯發佈文章的時候要作好錨文本
作好長尾關鍵詞記錄單
接下來,咱們的推廣人員就要對網站進行站外優化了,這裏主要包括兩個大的方面:
友情連接
軟文
目錄提交
獨立博客
論壇簽名
黃頁網站
提交收藏
分類信息
微博推廣
sns推廣
舉辦活動,帶上相關連接,引導網友大規模轉播
最後,咱們的數據分析人員就要對網站進行天天的數據分析,總結流量來源,這裏主要從五個方面分析:
根據統計(百度統計工具,CNZZ統計工具等等),分析用戶進入的關鍵詞,模擬用戶思路,思考長尾關鍵詞
百度權重、PR值
快照
反鏈
內鏈
收錄
網站歷史
品牌關鍵詞
長尾關鍵詞
網站結構
目標關鍵詞
品牌關鍵詞
熱門關鍵詞
長尾關鍵詞
目標型長尾(目標型指的是網站的產品或者服務延伸的長尾關鍵詞,每每優化長尾的時候都是先以目標型長尾爲主,由於這些長尾能夠真實給咱們帶來目標客戶和目標量)
營銷型長尾(營銷型長尾是指與行業站服務相關的長尾,可讓咱們進行二次轉化成咱們的目標用戶)
百度指數工具
百度知道
百度及其餘SE的相關搜索及下拉框
百度站長工具、google關鍵詞分析工具
至此,一個完整的網站SEO優化方案已經完成
域名解析就是國際域名或者國內域名以及中文域名等域名申請後作的到IP地址的轉換過程。IP地址是網路上標識您站點的數字地址,爲了簡單好記,採用域名來代替ip地址標識站點地址。域名的解析工做由DNS服務器完成。
A (Address) 記錄是用來指定主機名(或域名)對應的IP地址記錄。用戶能夠將該域名下的網站服務器指向到本身的web server上。同時也能夠設置您域名的二級域名。
即:別名記錄。這種記錄容許您將多個名字映射到另一個域名。一般用於同時提供WWW和MAIL服務的計算機。例如,有一臺計算機名爲「host.mydomain.com」(A記錄)。它同時提供WWW和MAIL服務,爲了便於用戶訪問服務。能夠爲該計算機設置兩個別名(CNAME):WWW和MAIL。這兩個別名的全稱就 http://www.mydomain.com/和「mail.mydomain.com」。實際上他們都指向「host.mydomain.com」。
A記錄就是把一個域名解析到一個IP地址(Address,特製數字IP地址),而CNAME記錄就是把域名解析到另一個域名。其功能是差很少,CNAME將幾個主機名指向一個別名,其實跟指向IP地址是同樣的,由於這個別名也要作一個A記錄的。可是使用CNAME記錄能夠很方便地變動IP地址。若是一臺服務器有100個網站,他們都作了別名,該臺服務器變動IP時,只須要變動別名的A記錄就能夠了。
域名解析CNAME記錄A記錄哪種比較好?若是論對網站的影響,就沒有多大區別。可是:CNAME有一個好處就是穩定,就好像一個IP與一個域名的區別。服務商從方便維護的角度,通常也建議用戶使用CNAME記錄綁定域名的。若是主機使用了雙線IP,顯然使用CNAME也要方便一些。
A記錄也有一些好處,例如能夠在輸入域名時不用輸入WWW.來訪問網站哦!從SEO優化角度來看,一些搜索引擎如alex或一些搜索查詢工具網站等等則默認是自動去掉WWW.來辨別網站,CNAME記錄是必須有如:WWW(別名)前綴的域名,有時候會遇到這樣的麻煩,前綴去掉了默認網站沒法訪問。
有人認爲,在SEO優化網站的時候,因爲搜索引擎找不到去掉WWW.的域名時,對網站權重也會有些影響。由於有些網民客戶也是不喜歡多寫三個W來訪問網站的,網站沒法訪問有少許網民客戶會放棄繼續嘗試加WWW.訪問域名了,所以網站訪問瀏覽量也會減小一些。
也有人認爲同一個域名加WWW.和不加WWW.訪問網站也會使網站權重分散,這也是個問題。可是能夠使用301跳轉把不加WWW.跳轉到加WWW.的域名,問題就解決了。
從上面這個圖中,咱們能夠看到那麼幾個事:
1)瀏覽器會解析三個東西:
2)解析完成後,瀏覽器引擎會經過DOM Tree 和 CSS Rule Tree 來構造 Rendering Tree。注意:
3)最後經過調用操做系統Native GUI的API繪製。
HTML的DOM Tree解析以下:
<html> <head> <title>Web page parsing</title> </head> <body> <div> <h1>Web page parsing</h1> <p>This is an example Web page.</p> </div> </body></html>
上面這段HTML會解析成這樣:
下面是另外一個有SVG標籤的狀況。
CSS的解析大概是下面這個樣子(下面主要說的是Gecko也就是Firefox的玩法),假設咱們有下面的HTML文檔:
<doc><title>A few quotes</title><para> Franklin said that <quote>"A penny saved is a penny earned."</quote></para><para> FDR said <quote>"We have nothing to fear but <span>fear itself.</span>"</quote></para></doc>
因而DOM Tree是這個樣子:
而後咱們的CSS文檔是這樣的:
/* rule 1 */ doc { display: block; text-indent: 1em; }/* rule 2 */ title { display: block; font-size: 3em; }/* rule 3 */ para { display: block; }/* rule 4 */ [class="emph"] { font-style: italic; }
因而咱們的CSS Rule Tree會是這個樣子:
注意,圖中的第4條規則出現了兩次,一次是獨立的,一次是在規則3的子結點。因此,咱們能夠知道,創建CSS Rule Tree是須要比照着DOM Tree來的。CSS匹配DOM Tree主要是從右到左解析CSS的Selector,好多人覺得這個事會比較快,其實並不必定。關鍵還看咱們的CSS的Selector怎麼寫了。
注意:CSS匹配HTML元素是一個至關複雜和有性能問題的事情。因此,你就會在N多地方看到不少人都告訴你,DOM樹要小,CSS儘可能用id和class,千萬不要過渡層疊下去,……
經過這兩個樹,咱們能夠獲得一個叫Style Context Tree,也就是下面這樣(把CSS Rule結點Attach到DOM Tree上):
因此,Firefox基本上來講是經過CSS 解析 生成 CSS Rule Tree,而後,經過比對DOM生成Style Context Tree,而後Firefox經過把Style Context Tree和其Render Tree(Frame Tree)關聯上,就完成了。注意:Render Tree會把一些不可見的結點去除掉。而Firefox中所謂的Frame就是一個DOM結點,不要被其名字所迷惑了。
注:Webkit不像Firefox要用兩個樹來幹這個,Webkit也有Style對象,它直接把這個Style對象存在了相應的DOM結點上了。
渲染的流程基本上以下(黃色的四個步驟):
注意:上圖流程中有不少鏈接線,這表示了Javascript動態修改了DOM屬性或是CSS屬會致使從新Layout,有些改變不會,就是那些指到天上的箭頭,好比,修改後的CSS rule沒有被匹配到,等。
reflow(迴流),與repaint區別就是他會影響到dom的結構渲染,同時他會觸發repaint,他會改變他自己與全部父輩元素(祖先),這種開銷是很是昂貴的,致使性能降低是必然的,頁面元素越多效果越明顯。
repaint(重繪) ,repaint發生更改時,元素的外觀被改變,且在沒有改變佈局的狀況下發生,如改變outline,visibility,background color,不會影響到dom結構渲染。
若是想設定元素的樣式,經過改變元素的 class 名 (儘量在 DOM 樹的最末端)
避免設置多項內聯樣式
應用元素的動畫,使用 position 屬性的 fixed 值或 absolute 值
權衡平滑和速度
避免使用table佈局
避免使用CSS的JavaScript表達式 (僅 IE 瀏覽器)
迴流能夠自上而下,或自下而上的迴流的信息傳遞給周圍的節點。迴流是不可避免的,但能夠減小其影響。儘量在DOM樹的裏面改變class,能夠限制了迴流的範圍,使其影響儘量少的節點。例如,你應該避免經過改變對包裝元素類去影響子節點的顯示。面向對象的CSS始終嘗試得到它們影響的類對象(DOM節點或節點),但在這種狀況下,它已儘量的減小了迴流的影響,增長性能優點。
咱們都知道與DOM交互很慢。咱們嘗試在一種無形的DOM樹片斷組進行更改,而後整個改變應用到DOM上時僅致使了一個迴流。一樣,經過style屬性設置樣式致使迴流。避免設置多級內聯樣式,由於每一個都會形成迴流,樣式應該合併在一個外部類,這樣當該元素的class屬性可被操控時僅會產生一個reflow。
動畫效果應用到position屬性爲absolute或fixed的元素上,它們不影響其餘元素的佈局,所它他們只會致使從新繪製,而不是一個完整迴流。這樣消耗會更低。
Opera還建議咱們犧牲平滑度換取速度,其意思是指您可能想每次1像素移動一個動畫,可是若是此動畫及隨後的迴流使用了100%的CPU,動畫就會看上去是跳動的,由於瀏覽器正在與更新迴流作鬥爭。動畫元素每次移動3像素可能在很是快的機器上看起來平滑度低了,但它不會致使CPU在較慢的機器和移動設備中抖動。
避免使用table佈局。可能您須要其它些避免使用table的理由,在佈局徹底創建以前,table常常須要多個關口,由於table是個和罕見的能夠影響在它們以前已經進入的DOM元素的顯示的元素。想象一下,由於表格最後一個單元格的內容過寬而致使縱列大小徹底改變。這就是爲何全部的瀏覽器都逐步地不支持table表格的渲染(感謝Bill Scott提供)。然而有另一個緣由爲何表格佈局時很糟糕的主意,根據Mozilla,即便一些小的變化將致使表格(table)中的全部其餘節點回流。
這項規則較過期,但確實是個好的主意。主要的緣由,這些表現是如此昂貴,是由於他們每次從新計算文檔,或部分文檔、迴流。正如咱們從全部的不少事情看到的:引起迴流,它能夠每秒產生成千上萬次。小心!
在Web應用領域,Web緩存大體能夠分爲如下幾種類型:
Web應用,特別是社交網絡服務類型的應用,每每關係比較複雜,數據庫表繁多,若是頻繁進行數據庫查詢,很容易致使數據庫不堪重荷。爲了提供查詢的性能,會將查詢後的數據放到內存中進行緩存,下次查詢時,直接從內存緩存直接返回,提供響應效率。好比經常使用的緩存方案有memcached,redis等。
- 代理服務器緩存
代理服務器是瀏覽器和源服務器之間的中間服務器,瀏覽器先向這個中間服務器發起Web請求,通過處理後(好比權限驗證,緩存匹配等),再將請求轉發到源服務器。代理服務器緩存的運做原理跟瀏覽器的運做原理差很少,只是規模更大。能夠把它理解爲一個共享緩存,不僅爲一個用戶服務,通常爲大量用戶提供服務,所以在減小相應時間和帶寬使用方面頗有效,同一個副本會被重用屢次。常見代理服務器緩存解決方案有Squid,Nginx,Apache等。
- CDN緩存
CDN(Content delivery networks)緩存,也叫網關緩存、反向代理緩存。CDN緩存通常是由網站管理員本身部署,爲了讓他們的網站更容易擴展並得到更好的性能。瀏覽器先向CDN網關發起Web請求,網關服務器後面對應着一臺或多臺負載均衡源服務器,會根據它們的負載請求,動態將請求轉發到合適的源服務器上。雖然這種架構負載均衡源服務器之間的緩存無法共享,但卻擁有更好的處擴展性。從瀏覽器角度來看,整個CDN就是一個源服務器,瀏覽器和服務器之間的緩存機制,在這種架構下一樣適用。
瀏覽器緩存根據一套與服務器約定的規則進行工做,在同一個會話過程當中會檢查一次並肯定緩存的副本足夠新。若是你瀏覽過程當中,好比前進或後退,訪問到同一個圖片,這些圖片能夠從瀏覽器緩存中調出而即時顯現。
應用層緩存指的是從代碼層面上,經過代碼邏輯和緩存策略,實現對數據,頁面,圖片等資源的緩存,能夠根據實際狀況選擇將數據存在文件系統或者內存中,減小數據庫查詢或者讀寫瓶頸,提升響應效率。
使用Web緩存的做用實際上是很是顯而易見的:
減小網絡帶寬消耗
不管對於網站運營者或者用戶,帶寬都表明着金錢,過多的帶寬消耗,只會便宜了網絡運營商。當Web緩存副本被使用時,只會產生極小的網絡流量,能夠有效的下降運營成本。
下降服務器壓力
給網絡資源設定有效期以後,用戶能夠重複使用本地的緩存,減小對源服務器的請求,間接下降服務器的壓力。同時,搜索引擎的爬蟲機器人也能根據過時機制下降爬取的頻率,也能有效下降服務器的壓力。
減小網絡延遲,加快頁面打開速度
帶寬對於我的網站運營者來講是十分重要,而對於大型的互聯網公司來講,可能有時由於錢多而真的不在意。那Web緩存還有做用嗎?答案是確定的,對於最終用戶,緩存的使用可以明顯加快頁面打開速度,達到更好的體驗。
如今的大型網站,隨便一個頁面都是一兩百個請求,天天 pv 都是億級別,若是沒有緩存,用戶體驗會急劇降低、同時服務器壓力和網絡帶寬都面臨嚴重的考驗。 緩存和重用之前獲取的資源的是優化網頁性能很重要的一個方面。
缺點也是有的:
緩存沒有清理機制:這些緩存的文件會永久性地保存在機器上,在特定的時間內,這些文件多是幫了你大忙,可是時間一長,咱們已經再也不須要瀏覽以前的這些網頁,這些文件就成了無效或者無用的文件,它們存儲在用戶硬盤中只會佔用空間而沒有任何用處,若是要緩存的東西很是多,那就會撐暴整個硬盤空間。
給開發帶來的困擾:明明修改了樣式文件、圖片、視頻或腳本,刷新頁面或部署到站點以後看不到修改以後的效果。
因此在產品開發的時候咱們老是想辦法避免緩存產生,而在產品發佈之時又在想策略管理緩存提高網頁的訪問速度。瞭解瀏覽器的緩存命中原理和清除方法,對咱們大有裨益。
一、非HTTP協議定義的緩存機制
瀏覽器緩存機制,其實主要就是HTTP協議定義的緩存機制(如: Expires; Cache-control等)。可是也有非HTTP協議定義的緩存機制,如使用HTML Meta 標籤,Web開發者能夠在HTML頁面的節點中加入標籤,代碼以下:
一、Expires(期限)
說明:能夠用於設定網頁的到期時間。一旦網頁過時,必須到服務器上從新傳輸。
用法:
Html代碼
<meta http-equiv="expires" content="Wed, 20 Jun 2007 22:33:00 GMT">
注意:必須使用GMT的時間格式。
二、Pragma(cache模式)
說明:是用於設定禁止瀏覽器從本地機的緩存中調閱頁面內容,設定後一旦離開網頁就沒法從Cache中再調出
用法:
Html代碼
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
注意:這樣設定,訪問者將沒法脫機瀏覽。
更多設置:參看Meta http-equiv屬性詳解
二、HTTP協議定義的緩存機制。
瀏覽器緩存機制圖
瀏覽器緩存機制策略
三、其餘主流瀏覽器緩存機制
它分爲強緩存和協商緩存:
1)瀏覽器在加載資源時,先根據這個資源的一些http header判斷它是否命中強緩存,強緩存若是命中,瀏覽器直接從本身的緩存中讀取資源,不會發請求到服務器。好比某個css文件,若是瀏覽器在加載它所在的網頁時,這個css文件的緩存配置命中了強緩存,瀏覽器就直接從緩存中加載這個css,連請求都不會發送到網頁所在服務器;
2)當強緩存沒有命中的時候,瀏覽器必定會發送一個請求到服務器,經過服務器端依據資源的另一些http header驗證這個資源是否命中協商緩存,若是協商緩存命中,服務器會將這個請求返回,可是不會返回這個資源的數據,而是告訴客戶端能夠直接從緩存中加載這個資源,因而瀏覽器就又會從本身的緩存中去加載這個資源;
3)強緩存與協商緩存的共同點是:若是命中,都是從客戶端緩存中加載資源,而不是從服務器加載資源數據;區別是:強緩存不發請求到服務器,協商緩存會發請求到服務器。
4)當協商緩存也沒有命中的時候,瀏覽器直接從服務器加載資源數據。
當瀏覽器對某個資源的請求命中了強緩存時,返回的http狀態爲200,在chrome的開發者工具的network裏面size會顯示爲from cache
強緩存是利用Expires或者Cache-Control這兩個http response header實現的,它們都用來表示資源在客戶端緩存的有效期。
Expires是http1.0提出的一個表示資源過時時間的header,它描述的是一個絕對時間,由服務器返回,用GMT格式的字符串表示,如:Expires:Thu, 31 Dec 2017 23:55:55 GMT,它的緩存原理是:
1)瀏覽器第一次跟服務器請求一個資源,服務器在返回這個資源的同時,在respone的header加上Expires的header,
2)瀏覽器在接收到這個資源後,會把這個資源連同全部response header一塊兒緩存下來(因此緩存命中的請求返回的header並非來自服務器,而是來自以前緩存的header);
3)瀏覽器再請求這個資源時,先從緩存中尋找,找到這個資源後,拿出它的Expires跟當前的請求時間比較,若是請求時間在Expires指定的時間以前,就能命中緩存,不然就不行。
4)若是緩存沒有命中,瀏覽器直接從服務器加載資源時,Expires Header在從新加載的時候會被更新。
Expires是較老的強緩存管理header,因爲它是服務器返回的一個絕對時間,在服務器時間與客戶端時間相差較大時,緩存管理容易出現問題,好比隨意修改下客戶端時間,就能影響緩存命中的結果。
因此在http1.1的時候,提出了一個新的header,就是Cache-Control,這是一個相對時間,在配置緩存的時候,以秒爲單位,用數值表示,如:Cache-Control:max-age=315360000,它的緩存原理是:
1)瀏覽器第一次跟服務器請求一個資源,服務器在返回這個資源的同時,在respone的header加上Cache-Control的header
2)瀏覽器在接收到這個資源後,會把這個資源連同全部response header一塊兒緩存下來;
3)瀏覽器再請求這個資源時,先從緩存中尋找,找到這個資源後,根據它第一次的請求時間和Cache-Control設定的有效期,計算出一個資源過時時間,再拿這個過時時間跟當前的請求時間比較,若是請求時間在過時時間以前,就能命中緩存,不然就不行。
4)若是緩存沒有命中,瀏覽器直接從服務器加載資源時,Cache-Control Header在從新加載的時候會被更新。
Cache-Control描述的是一個相對時間,在進行緩存命中的時候,都是利用客戶端時間進行判斷,因此相比較Expires,Cache-Control的緩存管理更有效,安全一些。
這兩個header能夠只啓用一個,也能夠同時啓用,當response header中,Expires和Cache-Control同時存在時,Cache-Control優先級高於Expires:
在實際應用中咱們會碰到須要強緩存的場景和不須要強緩存的場景,一般有2種方式來設置是否啓用強緩存:
1)經過代碼的方式,在web服務器返回的響應中添加Expires和Cache-Control Header;
java.util.Date date = new java.util.Date(); response.setDateHeader("Expires",date.getTime()+20000); //Expires:過期期限值 response.setHeader("Cache-Control", "public"); //Cache-Control來控制頁面的緩存與否,public:瀏覽器和緩存服務器均可以緩存頁面信息; response.setHeader("Pragma", "Pragma"); //Pragma:設置頁面是否緩存,爲Pragma則緩存,no-cache則不緩存
設置不緩存:
response.setHeader( "Pragma", "no-cache" ); response.setDateHeader("Expires", 0); response.addHeader( "Cache-Control", "no-cache" );//瀏覽器和緩存服務器都不該該緩存頁面信息
2)經過配置web服務器的方式,讓web服務器在響應資源的時候統一添加Expires和Cache-Control Header。
tomcat還提供了一個ExpiresFilter專門用來配置強緩存,具體使用的方式可參考tomcat的官方文檔:http://tomcat.apache.org/tomcat-7.0-doc/config/filter.html#Expires_Filter
好比在javaweb裏面,咱們能夠使用相似下面的代碼設置強緩存:
強緩存是前端性能優化最有力的工具,沒有之一,對於有大量靜態資源的網頁,必定要利用強緩存,提升響應速度。一般的作法是,爲這些靜態資源所有配置一個超時時間超長的Expires或Cache-Control,這樣用戶在訪問網頁時,只會在第一次加載時從服務器請求靜態資源,其它時候只要緩存沒有失效而且用戶沒有強制刷新的條件下都會從本身的緩存中加載,好比前面提到過的京東首頁緩存的資源,它的緩存過時時間都設置到了2027年
然而這種緩存配置方式會帶來一個新的問題,就是發佈時資源更新的問題,好比某一張圖片,在用戶訪問第一個版本的時候已經緩存到了用戶的電腦上,當網站發佈新版本,替換了這個圖片時,已經訪問過第一個版本的用戶因爲緩存的設置,致使在默認的狀況下不會請求服務器最新的圖片資源,除非他清掉或禁用緩存或者強制刷新,不然就看不到最新的圖片效果
當瀏覽器對某個資源的請求沒有命中強緩存,就會發一個請求到服務器,驗證協商緩存是否命中,若是協商緩存命中,請求響應返回的http狀態爲304而且會顯示一個Not Modified的字符串。
協商緩存是利用的是【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】這兩對Header來管理的。
Last-Modified,If-Modified-Since的控制緩存的原理是:
1)瀏覽器第一次跟服務器請求一個資源,服務器在返回這個資源的同時,在respone的header加上Last-Modified的header,這個header表示這個資源在服務器上的最後修改時間
2)瀏覽器再次跟服務器請求這個資源時,在request的header上加上If-Modified-Since的header,這個header的值就是上一次請求時返回的Last-Modified的值:
3)服務器再次收到資源請求時,根據瀏覽器傳過來If-Modified-Since和資源在服務器上的最後修改時間判斷資源是否有變化,若是沒有變化則返回304 Not Modified,可是不會返回資源內容;若是有變化,就正常返回資源內容。當服務器返回304 Not Modified的響應時,response header中不會再添加Last-Modified的header,由於既然資源沒有變化,那麼Last-Modified也就不會改變
4)瀏覽器收到304的響應後,就會從緩存中加載資源。
5)若是協商緩存沒有命中,瀏覽器直接從服務器加載資源時,Last-Modified Header在從新加載的時候會被更新,下次請求時,If-Modified-Since會啓用上次返回的Last-Modified值。
Last-Modified -> ETag
【Last-Modified,If-Modified-Since】都是根據服務器時間返回的header,通常來講,在沒有調整服務器時間和篡改客戶端緩存的狀況下,這兩個header配合起來管理協商緩存是很是可靠的,可是有時候也會服務器上資源其實有變化,可是最後修改時間卻沒有變化的狀況,而這種問題又很不容易被定位出來,而當這種狀況出現的時候,就會影響協商緩存的可靠性。因此就有了另一對header來管理協商緩存,這對header就是【ETag、If-None-Match】。
ETag、If-None-Match的緩存管理的方式是:
1)瀏覽器第一次跟服務器請求一個資源,服務器在返回這個資源的同時,在respone的header加上ETag的header,這個header是服務器根據當前請求的資源生成的一個惟一標識,這個惟一標識是一個字符串,只要資源有變化這個串就不一樣,跟最後修改時間沒有關係,因此能很好的補充Last-Modified的問題
2)瀏覽器再次跟服務器請求這個資源時,在request的header上加上If-None-Match的header,這個header的值就是上一次請求時返回的ETag的值
3)服務器再次收到資源請求時,根據瀏覽器傳過來If-None-Match和而後再根據資源生成一個新的ETag,若是這兩個值相同就說明資源沒有變化,不然就是有變化;若是沒有變化則返回304 Not Modified,可是不會返回資源內容;若是有變化,就正常返回資源內容。與Last-Modified不同的是,當服務器返回304 Not Modified的響應時,因爲ETag從新生成過,response header中還會把這個ETag返回,即便這個ETag跟以前的沒有變化
4)瀏覽器收到304的響應後,就會從緩存中加載資源。
若是沒有協商緩存,每一個到服務器的請求,就都得返回資源內容,這樣服務器的性能會極差。
【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】通常都是同時啓用,這是爲了處理Last-Modified不可靠的狀況。有一種場景須要注意:
分佈式系統裏多臺機器間文件的Last-Modified必須保持一致,以避免負載均衡到不一樣機器致使比對失敗;
分佈式系統儘可能關閉掉ETag(每臺機器生成的ETag都會不同);
協商緩存須要配合強緩存使用,你看前面這個截圖中,除了Last-Modified這個header,還有強緩存的相關header,由於若是不啓用強緩存的話,協商緩存根本沒有意義。
meta方法
//不緩存
<META HTTP-EQUIV="pragma" CONTENT="no-cache"> <META HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate"> <META HTTP-EQUIV="expires" CONTENT="0">
jquery ajax清除瀏覽器緩存
方式一:用ajax請求服務器最新文件,並加上請求頭If-Modified-Since和Cache-Control,以下:
$.ajax({
url:'www.jb51.net', dataType:'json', data:{}, beforeSend :function(xmlHttp){ xmlHttp.setRequestHeader("If-Modified-Since","0"); xmlHttp.setRequestHeader("Cache-Control","no-cache"); }, success:function(response){ //操做 } async:false }); 方法二,直接用cache:false, $.ajax({ url:'www.jb51.net', dataType:'json', data:{}, cache:false, ifModified :true , success:function(response){ //操做 } async:false });
ajax 跨域請求設置headers :
http://blog.csdn.net/WRian_Ban/article/details/70257261
方法三:用隨機數,隨機數也是避免緩存的一種很不錯的方法
URL 參數後加上 「?ran=」 + Math.random(); //固然這裏參數 ran能夠任意取了
方法四:用隨機時間,和隨機數同樣。
在 URL 參數後加上 「?timestamp=」 + new Date().getTime();
在HTML5中,一個新增的很是重要的功能就是客戶端本地保存數據的功能,咱們知道在過去本地存儲數據基本都是使用cookies保存一些簡單的數據,可是經過長期的實際使用下來,我們不難發現使用cookies存儲永久數據存在一下幾個問題:
一、cookies的大小被限制在4KB;
二、cookies是隨HTTP事務一塊兒發送的,所以會浪費一部分發送cookies時所使用的帶寬;
三、cookies操做繁瑣複雜;
Web Storage是什麼?
Web Storage功能,顧名思義,就是在Web上針對客戶端本地儲存數據的功能,具體來講Web Storage分爲兩種;
sessionStorage:
將數據保存在session對象中,所謂session是指用戶在瀏覽某個網站時,從進入網站到瀏覽器關閉所通過的這段時間,也就是用戶瀏覽這個網站所花費的時間。session對象能夠用來保存在這段時間內所要求保存的任何數據。
localStorage:
將數據保存在客戶端本地的硬件設備(一般指硬盤,固然能夠是其餘的硬件設備)中,便是瀏覽器被關閉了,該數據仍然存在,下次打開瀏覽器訪問網站時,仍然能夠繼續使用。
localStorage自帶的方法:
名稱 | 說明 |
---|---|
clear | 清空localStorage上存儲的數據 |
getItem | 讀取數據 |
hasOwnProperty | 檢查localStorage上是否保存了變量x,須要傳入x |
key | 讀取第i個數據的名字或稱爲鍵值(從0開始計數) |
length | localStorage存儲變量的個數 |
propertyIsEnumerable | 用來檢測屬性是否屬於某個對象的 |
removeItem | 刪除某個具體變量 |
setItem | 存儲數據 |
toLocaleString | 將(數組)轉爲本地字符串 |
valueOf | 獲取全部存儲的數據 |
清空localStorage
localStorage.clear() // undefined localStorage // Storage {length: 0}
存儲數據
localStorage.setItem("name","caibin") //存儲名字爲name值爲caibin的變量 localStorage.name = "caibin"; // 等價於上面的命令 localStorage // Storage {name: "caibin", length: 1}
讀取數據
localStorage.getItem("name") //caibin,讀取保存在localStorage對象里名爲name的變量的值 localStorage.name // "caibin" localStorage.valueOf() //讀取存儲在localStorage上的全部數據 localStorage.key(0) // 讀取第一條數據的變量名(鍵值) //遍歷並輸出localStorage裏存儲的名字和值 for(var i=0; i<localStorage.length;i++){ console.log('localStorage裏存儲的第'+i+'條數據的名字爲:'+localStorage.key(i)+',值爲:'+localStorage.getItem(localStorage.key(i))); }
刪除某個變量
localStorage.removeItem("name"); //undefined localStorage // Storage {length: 0} 能夠看到以前保存的name變量已經從localStorage裏刪除了
檢查localStorage裏是否保存某個變量
// 這些數據都是測試的,是在我當下環境裏的,只是demo哦~ localStorage.hasOwnProperty('name') // truelocalStorage.hasOwnProperty('sex') // false
將數組轉爲本地字符串
var arr = ['aa','bb','cc']; // ["aa","bb","cc"] localStorage.arr = arr //["aa","bb","cc"] localStorage.arr.toLocaleString(); // "aa,bb,cc"
將JSON存儲到localStorage裏
var students = { xiaomin: { name: "xiaoming", grade: 1 }, teemo: { name: "teemo", grade: 3 } } students = JSON.stringify(students); //將JSON轉爲字符串存到變量裏 console.log(students); localStorage.setItem("students",students);//將變量存到localStorage裏 var newStudents = localStorage.getItem("students"); newStudents = JSON.parse(students); //轉爲JSON console.log(newStudents); // 打印出原先對象
設置localStorage的過時時間
<script type="text/javascript"> //封裝過時控制代碼 function set(key,value){ var curTime = new Date().getTime(); localStorage.setItem(key,JSON.stringify({data:value,time:curTime})); } function get(key,exp){ var data = localStorage.getItem(key); var dataObj = JSON.parse(data); if (new Date().getTime() - dataObj.time>exp) { console.log('信息已過時'); //alert("信息已過時") }else{ //console.log("data="+dataObj.data); //console.log(JSON.parse(dataObj.data)); var dataObjDatatoJson = JSON.parse(dataObj.data) return dataObjDatatoJson; } } </script>
SessionStorage:
將數據保存在session對象中,所謂session是指用戶在瀏覽某個網站時,從進入網站到瀏覽器關閉所通過的這段時間會話,也就是用戶瀏覽這個網站所花費的時間就是session的生命週期。session對象能夠用來保存在這段時間內所要求保存的任何數據。
localStorage和sessionStorage區別
https://www.test.com (不一樣源,由於協議不一樣)
http://my.test.com (不一樣源,由於主機名不一樣)
http://www.test.com:8080 (不一樣源,由於端口不一樣)
localStorage.setItem("key","value");//以「key」爲名稱存儲一個值「value」 localStorage.getItem("key");//獲取名稱爲「key」的值 枚舉localStorage的方法: for(var i=0;i<localStorage.length;i++){ var name = localStorage.key(i); var value = localStorage.getItem(name); } 刪除localStorage中存儲信息的方法: localStorage.removeItem("key");//刪除名稱爲「key」的信息。 localStorage.clear();//清空localStorage中全部信息
示例:
滾動時保存滾動位置
var scrollTop=document.body.scrollTop sessionStorage.setItem("offsetTop", scrollTop);//保存滾動位置 document.body.scrollTop= sessionStorage.getItem("offsetTop")
一、cookie的做用:
咱們在瀏覽器中,常常涉及到數據的交換,好比你登陸郵箱,登陸一個頁面。咱們常常會在此時設置30天內記住我,或者自動登陸選項。那麼它們是怎麼記錄信息的呢,答案就是今天的主角cookie了,Cookie是由HTTP服務器設置的,保存在瀏覽器中,但HTTP協議是一種無狀態協議,在數據交換完畢後,服務器端和客戶端的連接就會關閉,每次交換數據都須要創建新的連接。就像咱們去超市買東西,沒有積分卡的狀況下,咱們買完東西以後,超市沒有咱們的任何消費信息,但咱們辦了積分卡以後,超市就有了咱們的消費信息。cookie就像是積分卡,能夠保存積分,商品就是咱們的信息,超市的系統就像服務器後臺,http協議就是交易的過程。
二、機制的區別:
session機制採用的是在服務器端保持狀態的方案,而cookie機制則是在客戶端保持狀態的方案,cookie又叫會話跟蹤機制。打開一次瀏覽器到關閉瀏覽器算是一次會話。說到這裏,講下HTTP協議,前面提到,HTTP協議是一種無狀態協議,在數據交換完畢後,服務器端和客戶端的連接就會關閉,每次交換數據都須要創建新的連接。此時,服務器沒法從連接上跟蹤會話。cookie能夠跟蹤會話,彌補HTTP無狀態協議的不足。
三、cookie的分類:
cookie分爲會話cookie和持久cookie,會話cookie是指在不設定它的生命週期expires時的狀態,前面說了,瀏覽器的開啓到關閉就是一次會話,當關閉瀏覽器時,會話cookie就會跟隨瀏覽器而銷燬。當關閉一個頁面時,不影響會話cookie的銷燬。會話cookie就像咱們沒有辦理積分卡時,單一的買賣過程,離開以後,信息則銷燬。
持久cookie則是設定了它的生命週期expires,此時,cookie像商品同樣,有個保質期,關閉瀏覽器以後,它不會銷燬,直到設定的過時時間。對於持久cookie,能夠在同一個瀏覽器中傳遞數據,好比,你在打開一個淘寶頁面登錄後,你在點開一個商品頁面,依然是登陸狀態,即使你關閉了瀏覽器,再次開啓瀏覽器,依然會是登陸狀態。這就是由於cookie自動將數據傳送到服務器端,在反饋回來的結果。持久cookie就像是咱們辦理了一張積分卡,即使離開,信息一直保留,直到時間到期,信息銷燬。
一、應用場景
離線訪問對基於網絡的應用而言愈來愈重要。雖然全部瀏覽器都有緩存機制,但它們並不可靠,也不必定總能起到預期的做用。HTML5 使用ApplicationCache 接口解決了由離線帶來的部分難題。前提是你須要訪問的web頁面至少被在線訪問過一次。
二、使用緩存接口可爲您的應用帶來如下三個優點:
三、離線本地存儲和傳統的瀏覽器緩存有什麼不一樣呢?
離線存儲爲整個web提供服務,瀏覽器緩存只緩存單個頁面;
離線存儲能夠指定須要緩存的文件和哪些文件只能在線瀏覽,瀏覽器緩存沒法指定;
離線存儲能夠動態通知用戶進行更新。
四、如何實現
實現:
http://blog.csdn.net/fwwdn/article/details/8082433
機制:
http://www.cnblogs.com/yexiaochai/p/4271834.html
更新:
http://www.oschina.net/question/28_44911?sort=time
http://blog.csdn.net/wjb820728252/article/details/71246522?locationNum=6&fps=1
http://blog.csdn.net/wjb820728252/article/details/71246522?locationNum=6&fps=1
參考地址:
http://www.zhihu.com/question/20790576
歸納一下:
大公司的靜態資源優化方案,基本上要實現這麼幾個東西:
這篇文章提到的「大公司裏怎樣開發和部署前端代碼」的幾個解決方案的進階關係:
問題1:對於大公司來講,那些變態的訪問量和性能指標,將會讓前端一點也不「好玩」
答案1:強制瀏覽器使用本地緩存(cache-control/expires)200(from cache),不要和服務器通訊。好了,請求方面的優化已經達到變態級別,
問題2:那問題來了:你都不讓瀏覽器發資源請求了,這緩存咋更新?
答案2:經過更新頁面中引用的資源路徑,讓瀏覽器主動放棄緩存,加載新資源相似於:
<script src="${context_path}/js/imgSlide.js?v=1.0.1" type="text/javascript"></script>
下次上線,把連接地址改爲新的版本,就更新資源了,那麼新問題來了
問題3:頁面引用了3個css,而某次上線只改了其中的a.css,若是全部連接都更新版本,就會致使b.css,c.css的緩存也失效,那豈不是又有浪費了?
答案3:要解決這種問題,必須讓url的修改與文件內容關聯,也就是說,只有文件內容變化,纔會致使相應url的變動,從而實現文件級別的精確緩存控制。這回再有文件修改,就只更新那個文件對應的url了,想到這裏貌似很完美了。你以爲這就夠了麼?
問題4:當我要更新靜態資源的時候,同時也會更新html中的引用,若是同時改了頁面結構和樣式,也更新了靜態資源對應的url地址,如今要發佈代碼上線,那麼我們是先上線頁面,仍是先上線靜態資源?
先部署誰都不成!都會致使部署過程當中發生頁面錯亂的問題。
答案4:解決它也好辦,就是實現 非覆蓋式發佈。用文件的摘要信息來對資源文件進行重命名,把摘要信息放到資源文件發佈路徑中,這樣,內容有修改的資源就變成了一個新的文件發佈到線上,不會覆蓋已有的資源文件。上線過程當中,先全量部署靜態資源,再灰度部署頁面,整個問題就比較完美的解決了
函數是JavaScript應用程序的基礎。 它幫助你實現抽象層,模擬類,信息隱藏和模塊。在TypeScript裏,雖然已經支持類,命名空間和模塊,但函數仍然是主要的定義 行爲的地方。 TypeScript爲JavaScript函數添加了額外的功能,讓咱們能夠更容易地使用。
function max(x: number, y: number): number { return x > y ? x : y; }
function max(x: number, y?: number): number { if (y) { return x > y ? x : y; } else { return x; } } let result1 = max(2); //ok let result2 = max(2, 4, 7); //報錯 let result3 = max(2, 4); //ok
可選參數必須放在必選參數的後面
function max(x: number, y: number = 8): number { if (y) { return x > y ? x : y; } else { return x; } } let result1 = max(5); //ok let result2 = max(5, undefined); //ok let result3 = max(5, 3); //ok
帶默認值的參數沒必要放在必選參數的後面,但若是默認參數放到了必選參數的前,用戶必須顯式地傳入undefined
function max(x: number = 3, y: number): number { if (y) { return x > y ? x : y; } else { return x; } } let result1 = max(5); //報錯 let result2 = max(undefined, 5); //ok let result3 = max(5, 7); //ok
function sum(result: number = 0, ...restNum: number[]): number { restNum.forEach((item) => result += item); return result; } let result1 = sum(10, 1, 2, 3, 4); //ok let a: number[] = [1, 2, 3]; let result2 = sum(undefined, ...a); //ok let result3 = sum(); //ok
剩餘參數能夠理解爲個數不限的可選參數,個數能夠爲零到任意個
編譯器會根據參數類型來判斷該調用哪一個函數。TypeScript的函數重載經過查找重載列表來實現匹配,根據定義的優先順序來依次匹配。 所以,在定義重載的時候,建議把最精確的定義放在最前面。
function test(input: string); function test(input: number); function test(input: any) { if (typeof input === 'string') { return `input is string type, ${input}`; } else if (typeof input === 'number') { return `input is number type, ${input}`; } } let result1 = test(7);
function test(input: any)並非重載列表的一部分,所以這裏只有兩個重載:一個是接收字符串另外一個接收數字。 以其它參數調用test會產生錯誤。
傳統的JavaScript程序使用函數和基於原型的繼承來建立可重用的組件,但對於熟悉使用面向對象方式的程序員來說就有些棘手,由於他們用的是基於類的繼承而且對象是由類構建出來的。 從ECMAScript 2015,也就是ECMAScript 6開始,JavaScript程序員將可以使用基於類的面向對象的方式。
class Car { engine: string; constructor(engine: string) { this.engine = engine; } drive(distance: number = 100) { console.log(`A ${this.engine} car runs ${distance}m`); } } let myCar = new Car('petrol'); myCar.drive();
使用extends關鍵字便可方便地實現繼承
class MotoCar extends Car { constructor(engine: string) { super(engine); } } class Jeep extends Car { constructor(engine: string) { super(engine); } drive(distance: number) { console.log(`Jeep...`); super.drive(distance); } } let tesla = new MotoCar("electricity"); let landRover: Car = new Jeep("petrol"); tesla.drive(); //調用父類的drive方法 landRover.drive(200); //調用子類的的drive方法
派生類構造函數必須調用super(),它會執行父類的構造方法。
在類中的修飾符能夠分爲公共(public)、私有(private)、受保護(protected)三種類型,每一個成員默認爲public,能夠被自由地訪問。
class Car { public constructor(public engine: string) {} public drive(distance: number = 100) { console.log(`A ${this.engine} car runs ${distance}m`); } } let myCar = new Car('petrol'); myCar.drive();
private成員只能在類內部訪問
class Car { constructor(private engine: string) {} } let myCar = new Car('petrol'); console.log(myCar.engine); //報錯
protected成員能在派生類中和類內部訪問
class Car { constructor(protected engine: string) {} drive(distance: number = 0) { console.log(`A ${this.engine} car runs ${distance}m`); } } class MotoCar extends Car { constructor(engine: string) { super(engine); } drive(distance: number = 50) { super.drive(distance); } } let tesla = new MotoCar("electricity"); console.log(tesla.engine); //報錯 tesla.drive();
使用readonly關鍵字將屬性設置爲只讀的。只讀屬性只能在聲明時或構造函數裏被初始化。
class Car { constructor(readonly engine: string) {} drive(distance: number = 0) { console.log(`A ${this.engine} car runs ${distance}m`); } } let myCar = new Car("petrol"); myCar.engine = "diesel"; //報錯 myCar.drive();
最簡單判斷該用readonly仍是const的方法是看要把它作爲變量使用仍是作爲一個屬性。 作爲變量使用的話用 const,若作爲屬性則使用readonly。
使用static關鍵字來定義類的靜態成員。類的靜態成員存在於類自己而不是類的實例上,如同在實例屬性上使用this.前綴來訪問屬性同樣,咱們使用類名.來訪問靜態屬性。
class Grid { static origin = {x: 0, y: 0}; calculateDistance(point: {x: number; y: number;}) { let xDist = (point.x - Grid.origin.x); let yDist = (point.y - Grid.origin.y); return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale; } constructor (public scale: number) {} } let grid1 = new Grid(1.0); let grid2 = new Grid(5.0); console.log(grid1.calculateDistance({x: 10, y: 10})); console.log(grid2.calculateDistance({x: 10, y: 10}));
抽像類做爲其餘派生類的基類使用,通常不會直接被實例化。使用abstract關鍵字定義抽象類和抽象類內部定義抽象方法。抽象類中的抽象方法不包含具體實現而且必須在派生類中實現。與接口方法類似,二者都是定義方法簽名但不包含方法體。
abstract class Person { abstract speak(): void; //必須在派生類中實現 walking(): void { console.log('Walking on the road'); } } class Male extends Person { speak(): void { console.log('How are you?'); } } let person: Person; //建立一個抽象類引用 person = new Person(); //報錯 person = new Male(); //ok person.speak(); person.walking();
TypeScript的核心原則之一是對值所具備的結構進行類型檢查。 它有時被稱作「鴨式辨型法」或「結構性子類型化」。 在TypeScript裏,接口的做用就是爲這些類型命名和爲你的代碼或第三方代碼定義契約。
接口可以描述JavaScript中對象擁有的各類各樣的類型:
interface FullName {
firstName: string; secondName: string; } function printName(name: FullName) { console.log(`${name.firstName} ${name.secondName}`); } let myObj = {sex: "Male", firstName: "Jim", secondName: "Lee"}; printName(myObj);
類型檢查器不會去檢查屬性的順序,只要相應的屬性存在而且類型也是對的就能夠。
interface FullName {
firstName: string; secondName?: string; } function printName(name: FullName) { console.log(`${name.firstName} ${name.secondName}`); } let myObj = {firstName: "Jim"}; printName(myObj);
interface Point { readonly x: number; readonly y: number; } let p1: Point = { x: 10, y: 20 }; //只能在對象建立時修改其值 p1.x = 5; //報錯
let arr: ReadonlyArray<number> = [1, 2, 3, 4]; arr[0] = 12; //報錯 arr.push(5); //報錯 arr.length = 100; //報錯
除了描述帶有屬性的普通對象外,接口也能夠描述函數類型。定義函數類型接口時,須要明肯定義函數的參數列表和返回值類型,且參數列表的每一個參數都要有參數名和類型。
let arr: ReadonlyArray<number> = [1, 2, 3, 4]; arr[0] = 12; //報錯 arr.push(5); //報錯 arr.length = 100; //報錯
可索引類型接口用來描述可以「經過索引獲得」的類型,好比a[10]或objMap[「daniel」]。 它包含一個索引簽名,表示用來索引的類型和返回值類型,即經過特定的索引來獲得指定類型的返回值。
索引簽名支持字符串和數字兩種數據類型。
interface StringArray { [index: number]: string; } interface StringObject { [index: string]: string; } let myArray: StringArray; myArray = ["Bob", "Fred"]; console.log(myArray[0]); let myObject: StringObject; myObject = {"name": "David"}; console.log(myObject["name"]);
類類型接口用來規範一個類的內容。
interface ClockInterface { currentTime: Date; setTime(d: Date); } class Clock implements ClockInterface { currentTime: Date; setTime(d: Date) { this.currentTime = d; } showTime() { console.log(this.currentTime); } } let clock = new Clock(); clock.setTime(new Date(2017, 9, 1)); clock.showTime();
和類同樣,接口也能夠相互繼承。 這讓咱們可以從一個接口裏複製成員到另外一個接口裏,能夠更靈活地將接口分割到可重用的模塊裏。
一個接口能夠繼承多個接口,建立出多個接口的合成接口。
interface Shape { color: string; } interface PenStroke { penWidth: number; } interface Square extends Shape, PenStroke { sideLength: number; } let square: Square; square = {color: "blue", sideLength: 10, penWidth: 5.0}; console.log(square);
軟件工程中,有時須要支持不特定的數據類型,而泛型就是用來實現這樣的效果。
在像C#和Java這樣的語言中,能夠使用泛型來建立可重用的組件,一個組件能夠支持多種類型的數據。 這樣用戶就能夠以本身的數據類型來使用組件。
interface Icalc<T> { add(): T; } class Calc implements Icalc<number> { constructor(public x: number, public y: number) { } add() { return this.x + this.y; } } let myAdd = new Calc(5, 8); let result = myAdd.add();
TypeScript是一種由微軟開發的自由和開源的編程語言,由C#語言之父安德斯•海爾斯伯格主導開發的,2012年微軟發佈了首個公開版本。TypeScript是JavaScript的超集,它擴展了JavaScript的語法,遵循ES6的規範,所以現有的JavaScript代碼無需作任何修改即可與TypeScript一塊兒使用,要運行TypeScript程序需先編譯成瀏覽器能識別的JavaScript代碼,編譯後能夠在任何瀏覽器、任何操做系統、任何計算機運行。它添加了可選的靜態類型,提供了類、接口和模塊來幫助構建組件,可以幫助開發者編寫出更出色的JavaScript代碼。
1.一、類型批註
TypeScript是靜態類型語言,須要編譯,擁有編譯時類型檢查的特性,在編譯過程當中,到達運行時間前能夠捕獲全部類型的錯誤。
TypeScript的類型是設計爲批註,而不是定義,因此和C#的類型寫在前面不一樣,類型是以可選的形式寫在後面的。
let foo: string;foo = true; //報錯
1.二、IDE支持
好比VisualStudio,編寫TypeScript文件時,就比編寫JavaScript要聰明的多,方便開發更智能的自動完成功能,實際上TypeScript的各類開發工具都作得很不錯。這就是靜態類型帶來的好處。
因爲模塊、命名空間和強大的面向對象編程支持,使構建大型複雜應用程序的代碼庫更加容易。
利用TypeScript的關鍵詞module,能夠達到相似於命名空間的效果,而export能夠控制是否被外部訪問。
一、經過npm安裝
npm install -g typescript
二、構建一個TypeScript文件,文件擴展名爲.ts
三、編譯代碼
在命令行上,運行TypeScript編譯器
tsc abc.ts
此時會在同個目錄下生成一個同名js文件abc.js,該文件中的代碼是基於ES5標準的,能夠直接在瀏覽器中運行。
http://www.typescriptlang.org/play/index.html
英文:http://www.typescriptlang.org/
中文:https://tslang.cn/
<!-- src/components/Hello.vue --> <template> <div> <div class="greeting">Hello {{name}}{{exclamationMarks}}</div> <button @click="decrement">-</button> <button @click="increment">+</button> </div> </template> <script lang="ts"> import Vue from "vue"; export default Vue.extend({ props: ['name', 'initialEnthusiasm'], data() { return { enthusiasm: this.initialEnthusiasm, } }, methods: { increment() { this.enthusiasm++; }, decrement() { if (this.enthusiasm > 1) { this.enthusiasm--; } }, }, computed: { exclamationMarks(): string { return Array(this.enthusiasm + 1).join('!'); } } }); </script> <style> .greeting { font-size: 20px; } </style>
完整示例參考: https://github.com/Barry215/Gobang
在TypeScript中,提供瞭如下基礎數據類型:
https://tslang.cn/docs/handbook/basic-types.html
WebSocket是Html5一系列新的API,或者說新規範,新技術中的一部分。
WebSocket協議是基於TCP的一種新的網絡協議。它實現了瀏覽器與服務器全雙工(full-duplex)通訊——容許服務器主動發送信息給客戶端。
WebSocket通訊協議於2011年被IETF定爲標準RFC 6455。
- WebSocket是HTML5出的東西(協議),也就是說HTTP協議沒有變化
首先HTTP有1.1和1.0之說,也就是所謂的keep-alive,把多個HTTP請求合併爲一個,可是Websocket實際上是一個新協議,跟HTTP協議基本沒有關係,只是爲了兼容現有瀏覽器的握手規範而已,也就是說它是HTTP協議上的一種補充。
WebSocket 本質上跟 HTTP 徹底不同,只不過爲了兼容性,WebSocket 的握手是以 HTTP 的形式發起的,若是服務器或者代理不支持 WebSocket,它們會把這當作一個不認識的 HTTP 請求從而優雅地拒絕掉。
簡單的說,WebSocket協議以前,雙工通訊和長鏈接都是經過某種手段模擬實現的。
爲了在半雙工HTTP的基礎上模擬全雙工通訊,目前的許多解決方案都是使用了兩個鏈接:一個用於下行數據流,另外一個用於上行數據流。HTTP 協議中所謂的 keep-alive connection,就是指在一次 TCP 鏈接中完成多個 HTTP 請求,可是對每一個請求仍然要單獨發 header,這致使了效率低下。
實現實時信息傳遞的長鏈接,通常會用long poll 和 ajax輪詢。一般會形成HTTP輪詢的濫用: 客戶端(通常就是瀏覽器)不斷主動的向服務器發 HTTP 請求查詢是否有新數據。
ajax輪詢
ajax輪詢 的原理很是簡單,讓瀏覽器隔個幾秒就發送一次請求,詢問服務器是否有新信息。
場景再現:
客戶端:啦啦啦,有沒有新信息(Request)
服務端:沒有(Response)
客戶端:啦啦啦,有沒有新信息(Request)
服務端:沒有。。(Response)
客戶端:啦啦啦,有沒有新信息(Request)
服務端:你好煩啊,沒有啊。。(Response)
客戶端:啦啦啦,有沒有新消息(Request)
服務端:好啦好啦,有啦給你。(Response)
客戶端:啦啦啦,有沒有新消息(Request)
服務端:。。。。。沒。。。。沒。。。沒有(Response)—— loop
long poll
long poll 其實原理跟 ajax輪詢 差很少,都是採用輪詢的方式,不過採起的是阻塞模型(一直打電話,沒收到就不掛電話),也就是說,客戶端發起鏈接後,若是沒消息,就一直不返回Response給客戶端。直到有消息才返回,返回完以後,客戶端再次創建鏈接,周而復始。
場景再現
客戶端:啦啦啦,有沒有新信息,沒有的話就等有了才返回給我吧(Request)
服務端:額。。 等待到有消息的時候。。來 給你(Response)
客戶端:啦啦啦,有沒有新信息,沒有的話就等有了才返回給我吧(Request) -loop
上面這兩種都是很是消耗資源的。由於HTTP是非狀態性的,服務器和客戶端要不斷地創建、關閉HTTP協議(HTTP協議的被動性),服務端不能主動聯繫客戶端,只能有客戶端發起。除了真正的數據部分外,每次都要在 HTTP header中從新傳輸identity info(鑑別信息),信息交換效率很低。
一個更簡單的解決方案是使用單個TCP鏈接雙向通訊。這就是WebSocket協議所提供的功能。結合WebSocket API ,WebSocket協議提供了一個用來替代HTTP輪詢實現網頁到遠程主機的雙向通訊的方法。
WebSocket 解決的第一個問題是,經過第一個 HTTP request 創建了 TCP 鏈接以後,以後的交換數據都不須要再發 HTTP request了,使得這個長鏈接變成了一個真.長鏈接。他解決了HTTP的被動性,當服務器完成協議升級後(HTTP->Websocket),服務端就能夠主動推送信息給客戶端啦。
使用WebSockets,上面的情景能夠作以下修改。
客戶端:啦啦啦,我要創建Websocket協議,須要的服務:chat,Websocket協議版本:17(HTTP Request)
服務端:ok,確認,已升級爲Websocket協議(HTTP Protocols Switched)
客戶端:麻煩你有信息的時候推送給我噢。。
服務端:ok,有的時候會告訴你的。
服務端:balabalabalabala
服務端:balabalabalabala
服務端:哈哈哈哈哈啊哈哈哈哈
服務端:笑死我了哈哈哈哈哈哈哈
就變成了這樣,只須要通過一次HTTP請求,就能夠作到源源不斷的信息傳送了。這樣的協議解決了上面同步有延遲,並且還很是消耗資源的這種狀況。
WebSocket 仍是一個雙通道的鏈接,在同一個 TCP 鏈接上既能夠發也能夠收信息。
傳統HTTP客戶端與服務器請求響應模式以下圖所示:
WebSocket模式客戶端與服務器請求響應模式以下圖:
一旦WebSocket鏈接創建後,後續數據都以幀序列的形式傳輸。在客戶端斷開WebSocket鏈接或Server端中斷鏈接前,不須要客戶端和服務端從新發起鏈接請求。在海量併發及客戶端與服務器交互負載流量大的狀況下,極大的節省了網絡帶寬資源的消耗,有明顯的性能優點,且客戶端發送和接受消息是在同一個持久鏈接上發起,實時性優點明顯。
Websocket是基於HTTP協議的,或者說借用了HTTP的協議來完成一部分握手,也就是說,在握手階段是同樣的。
首先咱們來看個典型的Websocket握手。
Request Headers
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 Origin: http://example.com
這段相似HTTP協議的握手請求中,多了幾個東西。
Upgrade: websocket Connection: Upgrade
這個就是Websocket的核心了,告訴Apache、Nginx等服務器:注意啦,窩發起的是Websocket協議,快點幫我找到對應的助理處理~不是那個老土的HTTP。
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13
而後服務器會返回下列東西,表示已經接受到請求, 成功創建Websocket啦!
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat
基本API語句以下:
建立對象
var ws = new WebSocket(url,name);
url爲WebSocket服務器的地址,name爲發起握手的協議名稱,爲可選擇項。
發送文本消息
ws.send(msg);
msg爲文本消息,對於其餘類型的能夠經過二進制形式發送。
接收消息
ws.onmessage = (function(){…})();
錯誤處理
ws.onerror = (function(){…})();
關閉鏈接
ws.close();
全部主流瀏覽器都支持RFC6455。可是具體的WebSocket版本有區別。websocket api在瀏覽器端的普遍實現彷佛只是一個時間問題了。
C C++ Node.js php jetty netty ruby Kaazing nginx python Tomcat Django erlang netty .net等語言都可以用來實現支持WebSocket的服務器。對於JAVA開發人員Tomcat是最熟悉的,在Tomcat8中已經實現了WebSocket API 1.0。 值得注意的是服務器端沒有標準的api, 各個實現都有本身的一套api, 而且tcp也沒有相似的提案, 因此使用websocket開發服務器端有必定的風險.可能會被鎖定在某個平臺上或者未來被迫升級。
許多語言、框架和服務器都提供了 WebSocket 支持,例如:
JavaScript一種直譯式腳本語言,是一種動態類型、弱類型、基於原型的語言,內置支持類型。它的解釋器被稱爲JavaScript引擎,爲瀏覽器的一部分,普遍用於客戶端的腳本語言,最先是在HTML(標準通用標記語言下的一個應用)網頁上使用,用來給HTML網頁增長動態功能。
在1995年時,由Netscape公司的Brendan Eich,在網景導航者瀏覽器上首次設計實現而成。由於Netscape與Sun合做,Netscape管理層但願它外觀看起來像Java,所以取名爲JavaScript。但實際上它的語法風格與Self及Scheme較爲接近。爲了取得技術優點,微軟推出了JScript,CEnvi推出ScriptEase,與JavaScript一樣可在瀏覽器上運行。爲了統一規格,由於JavaScript兼容於ECMA標準,所以也稱爲ECMAScript。
一個常見的問題是,ECMAScript 和 JavaScript 究竟是什麼關係?
要講清楚這個問題,須要回顧歷史。1996年11月,JavaScript 的創造者 Netscape 公司,決定將 JavaScript 提交給國際標準化組織ECMA,但願這種語言可以成爲國際標準。次年,ECMA 發佈262號標準文件(ECMA-262)的初版,規定了瀏覽器腳本語言的標準,並將這種語言稱爲 ECMAScript,這個版本就是1.0版。
該標準從一開始就是針對 JavaScript 語言制定的,可是之因此不叫 JavaScript,有兩個緣由。一是商標,Java 是 Sun 公司的商標,根據受權協議,只有 Netscape 公司能夠合法地使用 JavaScript 這個名字,且 JavaScript 自己也已經被 Netscape 公司註冊爲商標。二是想體現這門語言的制定者是 ECMA,不是 Netscape,這樣有利於保證這門語言的開放性和中立性。
所以,ECMAScript 和 JavaScript 的關係是,前者是後者的規格,後者是前者的一種實現(另外的 ECMAScript 方言還有 Jscript 和 ActionScript)。平常場合,這兩個詞是能夠互換的。(以上摘自阮一峯《ES6入門》)
ECMAScript 2015(簡稱 ES2015)這個詞,也是常常能夠看到的。它與 ES6 是什麼關係呢?
2011年,ECMAScript 5.1版發佈後,就開始制定6.0版了。所以,ES6 這個詞的原意,就是指 JavaScript 語言的下一個版本。
標準委員會最終決定,標準在每一年的6月份正式發佈一次,做爲當年的正式版本。接下來的時間,就在這個版本的基礎上作改動,直到下一年的6月份,草案就天然變成了新一年的版本。這樣一來,就不須要之前的版本號了,只要用年份標記就能夠了。
ES6 的第一個版本,就這樣在2015年6月發佈了,正式名稱就是《ECMAScript 2015標準》(簡稱 ES2015)。2016年6月,小幅修訂的《ECMAScript 2016標準》(簡稱 ES2016)如期發佈,這個版本能夠看做是 ES6.1 版,由於二者的差別很是小(只新增了數組實例的includes方法和指數運算符),基本上是同一個標準。根據計劃,2017年6月發佈 ES2017 標準。
所以,ES6 既是一個歷史名詞,也是一個泛指,含義是5.1版之後的 JavaScript 的下一代標準,涵蓋了ES201五、ES201六、ES2017等等,而ES2015 則是正式名稱,特指該年發佈的正式版本的語言標準。本書中提到 ES6 的地方,通常是指 ES2015 標準,但有時也是泛指「下一代 JavaScript 語言」。(以上摘自阮一峯《ES6入門》)
這個組織的目標是評估,開發和承認電信和計算機標準。你們決定把ECMA的總部設在日內瓦是由於這樣可以讓它與其它與之協同工做的標準制定組織更接近一些,比方說國際標準化組織(ISO)和國際電子技術協會(IEC)。ECMA是「European Computer Manufactures Association」的縮寫,中文稱歐洲計算機制造聯合會。是1961年成立的旨在創建統一的電腦操做格式標準—包括程序語言和輸入輸出的組織。
ECMAScript6入門:http://es6.ruanyifeng.com/#README
ES5傳送門:http://www.w3school.com.cn/js/index.asp
MDN火狐開發者網絡中心:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript
ES6新增:
let 和 const 命令
Symbol
Set 和 Map 數據結構
變量的解構賦值
Decorator
Class 的基本語法
Reflect
Proxy
Promise 對象
Generator 函數的語法
Iterator 和 for…of 循環
ES6各類擴展:
字符串的擴展:模板字符串
正則的擴展:RegExp 構造函數
數值的擴展:Number.parseInt(), Number.parseFloat()
數組的擴展:擴展運算符,Arrary.from(),includes()
函數的擴展:箭頭函數
對象的擴展:Object.assign()
與js語言相關:
模塊化與Module 的加載實現
1.js的模塊化,服務器端首先實現
2.瀏覽器端實現模塊化:requirejs,seajs
3.CMD,AMD,CommonJS與ES6三個模塊化規範的差別(主要是commonjs):
a.CommonJS 模塊是運行時加載,ES6 模塊是編譯時輸出接口
b.CommonJS 模塊輸出的是一個值的拷貝,ES6 模塊輸出的是值的引用。
4.ES6模塊化:
編程風格
讀懂規格
二進制數組
SIMD
推薦:你所不知道的js
介紹異步以前,回顧一下,所謂同步編程,就是計算機一行一行按順序依次執行代碼,當前代碼任務耗時執行會阻塞後續代碼的執行。
同步編程,便是一種典型的請求-響應模型,當請求調用一個函數或方法後,需等待其響應返回,而後執行後續代碼。
通常狀況下,同步編程,代碼按序依次執行,能很好的保證程序的執行,可是在某些場景下,好比讀取文件內容,或請求服務器接口數據,須要根據返回的數據內容執行後續操做,讀取文件和請求接口直到數據返回這一過程是須要時間的,網絡越差,耗費時間越長,若是按照同步編程方式實現,在等待數據返回這段時間,JavaScript是不能處理其餘任務的,此時頁面的交互,滾動等任何操做也都會被阻塞,這顯然是及其不友好,不可接受的,而這正是須要異步編程大顯身手的場景,以下圖,耗時任務A會阻塞任務B的執行,等到任務A執行完才能繼續執行B:
異步編程,不一樣於同步編程的請求-響應模式,其是一種事件驅動編程,請求調用函數或方法後,無需當即等待響應,能夠繼續執行其餘任務,而以前任務響應返回後能夠經過狀態、通知和回調來通知調用者。
JavaScript執行異步任務時,不須要等待響應返回,能夠繼續執行其餘任務,而在響應返回時,會獲得通知,執行回調或事件處理程序
1:首要要知道JavaScript語言是單線程的:
JavaScript語言的一大特色就是單線程,也就是說,同一個時間只能作一件事。那麼,爲何JavaScript不能有多個線程呢?這樣能提升效率啊。
JavaScript的單線程,與它的用途有關。做爲瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操做DOM。這決定了它只能是單線程,不然會帶來很複雜的同步問題。好比,假定JavaScript同時有兩個線程,一個線程在某個DOM節點上添加內容,另外一個線程刪除了這個節點,這時瀏覽器應該以哪一個線程爲準?
因此,爲了不復雜性,從一誕生,JavaScript就是單線程,這已經成了這門語言的核心特徵,未來也不會改變。
爲了利用多核CPU的計算能力,HTML5提出Web Worker標準,容許JavaScript腳本建立多個線程,可是子線程徹底受主線程控制,且不得操做DOM。因此,這個新標準並無改變JavaScript單線程的本質。
2:事件和回調函數
「任務隊列」是一個事件的隊列(也能夠理解成消息的隊列),IO設備完成一項任務,就在」任務隊列」中添加一個事件,表示相關的異步任務能夠進入」執行棧」了。主線程讀取」任務隊列」,就是讀取裏面有哪些事件。
「任務隊列」中的事件,除了IO設備的事件之外,還包括一些用戶產生的事件(好比鼠標點擊、頁面滾動等等)。只要指定過回調函數,這些事件發生時就會進入」任務隊列」,等待主線程讀取。
所謂」回調函數」(callback),就是那些會被主線程掛起來的代碼。異步任務必須指定回調函數,當主線程開始執行異步任務,就是執行對應的回調函數。
3:事件循環(Event Loop)
如下是event-loop的簡單介紹 http://www.ruanyifeng.com/blog/2013/10/event_loop.html
(1)全部同步任務都在主線程上執行,造成一個執行棧
(2)主線程以外,還存在一個」任務隊列」。只要異步任務有了運行結果,就在」任務隊列」之中放置一個事件。
(3)一旦」執行棧」中的全部同步任務執行完畢,系統就會讀取」任務隊列」,看看裏面有哪些事件。那些對應的異步任務,因而結束等待狀態,進入執行棧,開始執行。
(4)主線程不斷重複上面的第三步。
/*示例*/ //如下是在引入jq環境下運行的腳本 console.log(new Date()) //打印當前電腦時間 console.time("計數") //計數開始 console.log("通知請求線程進行加載,請求成功將把回調push到消息隊列中") $.ajax({ type: "get", async: false, url: "https://biaozhunshijian.51240.com/web_system/51240_com_www/system/file/biaozhunshijian/time.js/?v=1501641667339", dataType: "jsonp", jsonp: "callback", jsonpCallback:"baidu_time",//自定義的jsonp回調函數名稱,默認爲jQuery自動生成的隨機函數名,也能夠寫"?",jQuery會自動爲你處理數據 success: function(json){ console.timeEnd("計數") console.log(new Date(json.time)) }, error: function(){ alert('fail'); } }); for(var i=0;i<90000;i++){ //當前js線程一直在作循環 只有當js線程空餘下來 纔會去讀取消息隊列中的回調函數 console.log("執行") }
4:常見的異步場景
1:http請求
2:DOM事件(事件綁定)
3:定時器
消息隊列中除了放置異步任務的事件,」任務隊列」還能夠放置定時事件,即指定某些代碼在多少時間以後執行。這叫作」定時器」(timer)功能,也就是定時執行的代碼。
定時器功能主要由setTimeout()和setInterval()這兩個函數來完成,它們的內部運行機制徹底同樣,區別在於前者指定的代碼是一次性執行,後者則爲反覆執行。
瀏覽器是多線程的
Javascript是單線程的
javascript引擎線程是瀏覽器多個線程中的一個,它自己是單線程的。瀏覽器還包括不少其餘線程,如GUI渲染線程,瀏覽器事件觸發線程,Http請求線程等。
js能夠操做DOM元素,進而會影響到GUI的渲染結果,所以JS引擎線程與GUI渲染線程是互斥的。也就是說當JS引擎線程處於運行狀態時,GUI渲染線程將處於凍結狀態。
1:官方解釋
Promise 是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大。它由社區最先提出和實現,ES6 將其寫進了語言標準,統一了用法,原生提供了Promise對象。
new Promise(function(resolve,reject){ console.log('Pending') //此時的promise的狀態是正在執行 Pending resolve(); //表明狀態變成了 ‘resolve’ 將執行resolve的對應函數 //reject(); //表明狀態變成了 是 ‘reject’ 將執行reject的對應函數 }).then(function(){ //直接在實例後點then(); 分別對應resolve 和reject函數 console.log('resolve') },function(){ console.log('reject') }) //promise的狀態一旦發生改變 就不可逆
Promise實例生成之後,能夠用then方法分別指定Resolved狀態和Rejected狀態的回調函數。
2:ES5.0實現Promise
原址:::::https://www.codetd.com/article/1832350function MyPromise(fn) { //聲明一個構造函數 this.value; this.status = 'pending'; //默認狀態是pending狀態 this.resolveFunc = function() {}; this.rejectFunc = function() {}; fn(this.resolve.bind(this), this.reject.bind(this)); //執行傳進來的函數 分別給與二個函數名提供調用 } MyPromise.prototype.resolve = function(val) { //在原型上增長resolve方法 var self = this; if (this.status == 'pending') { this.status = 'resolved'; this.value=val; setTimeout(function() { self.resolveFunc(self.value); }, 0); } } MyPromise.prototype.reject = function(val) { //在原型上增長reject方法 var self = this; if (this.status == 'pending') { this.status = 'rejected'; this.value=val; setTimeout(function() { self.rejectFunc(self.value); }, 0); } } MyPromise.prototype.then = function(resolveFunc, rejectFunc) { //在原型上增長then方法 this.resolveFunc = resolveFunc; this.rejectFunc = rejectFunc; }