解答:css
(1)緣由:html
css中的1px並不等於移動設備的1px,這些因爲不一樣的手機有不一樣的像素密度。在window對象中有一個devicePixelRatio屬性,他能夠反應css中的像素與設備的像素比。前端
devicePixelRatio的官方的定義爲:設備物理像素和設備獨立像素的比例,也就是 devicePixelRatio = 物理像素 / 獨立像素。vue
關於devicePixelRatio的詳細介紹能夠參考張鑫旭的這篇博文,http://www.zhangxinxu.com/wordpress/2012/08/window-devicepixelratio/webpack
(2)解決方法:nginx
IOS8下已經支持帶小數的px值, media query 對應 devicePixelRatio 有個查詢值 -webkit-min-device-pixel-ratio, css能夠寫成這樣
經過-webkit-min-device-pixel-ratio設置。web
.border { border: 1px solid #999 } @media screen and (-webkit-min-device-pixel-ratio: 2) { .border { border: 0.5px solid #999 } } @media screen and (-webkit-min-device-pixel-ratio: 3) { .border { border: 0.333333px solid #999 } }
若是使用less/sass的話只是加了1句mixin
缺點: 安卓與低版本IOS不適用, 這個或許是將來的標準寫法, 如今不作期望vue-router
同時經過設置對應viewport的rem基準值,這種方式就能夠像之前同樣輕鬆愉快的寫1px了。
在devicePixelRatio = 2 時,輸出viewport:vue-cli
<meta name="viewport" content="initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">
<meta name="viewport" content="initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no">
準備一張符合你要求的border-image:npm
.border-bottom-1px { border-width: 0 0 1px 0; -webkit-border-image: url(linenew.png) 0 0 2 0 stretch; border-image: url(linenew.png) 0 0 2 0 stretch; }
上文是把border設置在邊框的底部,因此使用的圖片是2px高,上部的1px顏色爲透明,下部的1px使用視覺規定的border的顏色。若是邊框底部和頂部同時須要border,可使用下面的border-image:
.border-image-1px { border-width: 1px 0; -webkit-border-image: url(linenew.png) 2 0 stretch; border-image: url(linenew.png) 2 0 stretch; }
background-image 跟border-image的方法同樣,你要先準備一張符合你要求的圖片。而後將邊框模擬在背景上。
樣式設置:
.background-image-1px { background: url(../img/line.png) repeat-x left bottom; -webkit-background-size: 100% 1px; background-size: 100% 1px; }
利用css 對陰影處理的方式實現0.5px的效果
.box-shadow-1px { box-shadow: inset 0px -1px 1px -1px #c8c7cc; }
對於老項目,有沒有什麼辦法能兼容1px的尷尬問題了,我的認爲僞類+transform是比較完美的方法了。
原理是把原先元素的 border 去掉,而後利用 :before 或者 :after 重作 border ,並 transform 的 scale 縮小一半,原先的元素相對定位,新作的 border 絕對定位。
單條border樣式設置:
.scale-1px{ position: relative; border:none; } .scale-1px:after{ content: ''; position: absolute; bottom: 0; background: #000; width: 100%; height: 1px; -webkit-transform: scaleY(0.5); transform: scaleY(0.5); -webkit-transform-origin: 0 0; transform-origin: 0 0; }
四條boder樣式設置:
.scale-1px{ position: relative; margin-bottom: 20px; border:none; } .scale-1px:after{ content: ''; position: absolute; top: 0; left: 0; border: 1px solid #000; -webkit-box-sizing: border-box; box-sizing: border-box; width: 200%; height: 200%; -webkit-transform: scale(0.5); transform: scale(0.5); -webkit-transform-origin: left top; transform-origin: left top; }
最好在使用前也判斷一下,結合 JS 代碼,判斷是否 Retina 屏:
if(window.devicePixelRatio && devicePixelRatio >= 2){ document.querySelector('ul').className = 'scale-1px'; }
利用 <script> 標籤沒有跨域限制的漏洞,網頁能夠獲得從其餘來源動態產生的 JSON 數據。JSONP 請求必定須要對方的服務器作支持才能夠。
JSONP 優勢是簡單兼容性好,可用於解決主流瀏覽器的跨域數據訪問的問題。缺點是僅支持 get 方法具備侷限性,不安全可能會遭受 XSS 攻擊。
CORS 須要瀏覽器和後端同時支持。IE 8 和 9 須要經過 XDomainRequest 來實現。
瀏覽器會自動進行 CORS 通訊,實現 CORS 通訊的關鍵是後端。只要後端實現了 CORS,就實現了跨域。
服務端設置 Access-Control-Allow-Origin 就能夠開啓 CORS。 該屬性表示哪些域名能夠訪問資源,若是設置通配符則表示全部網站均可以訪問資源。
雖然設置 CORS 和前端沒什麼關係,可是經過這種方式解決跨域問題的話,會在發送請求時出現兩種狀況,分別爲簡單請求和複雜請求。
postMessage 是 HTML5 XMLHttpRequest Level 2 中的 API,且是爲數很少能夠跨域操做的 window 屬性之一,它可用於解決如下方面的問題:
postMessage()方法容許來自不一樣源的腳本採用異步方式進行有限的通訊,能夠實現跨文本檔、多窗口、跨域消息傳遞。
otherWindow.postMessage(message, targetOrigin, [transfer]);
Websocket 是 HTML5 的一個持久化的協議,它實現了瀏覽器與服務器的全雙工通訊,同時也是跨域的一種解決方案。WebSocket 和 HTTP 都是應用層協議,都基於 TCP 協議。可是 WebSocket 是一種雙向通訊協議,在創建鏈接以後,WebSocket 的 server 與 client 都能主動向對方發送或接收數據。同時,WebSocket 在創建鏈接時須要藉助 HTTP 協議,鏈接創建好了以後 client 與 server 之間的雙向通訊就與 HTTP 無關了。
原生 WebSocket API 使用起來不太方便,咱們使用Socket.io
,它很好地封裝了 WebSocket 接口,提供了更簡單、靈活的接口,也對不支持 WebSocket 的瀏覽器提供了向下兼容。
實現原理:同源策略是瀏覽器須要遵循的標準,而若是是服務器向服務器請求就無需遵循同源策略。
代理服務器,須要作如下幾個步驟:
實現原理相似於 Node 中間件代理,須要你搭建一箇中轉 nginx 服務器,用於轉發請求。
使用 nginx 反向代理實現跨域,是最簡單的跨域方式。只須要修改 nginx 的配置便可解決跨域問題,支持全部瀏覽器,支持 session,不須要修改任何代碼,而且不會影響服務器性能。
實現思路:經過 nginx 配置一個代理服務器(域名與 domain1 相同,端口不一樣)作跳板機,反向代理訪問 domain2 接口,而且能夠順便修改 cookie 中 domain 信息,方便當前域 cookie 寫入,實現跨域登陸。
window.name屬性的獨特之處:name 值在不一樣的頁面(甚至不一樣域名)加載後依舊存在,而且能夠支持很是長的 name 值(2MB)。
經過 iframe 的 src 屬性由外域轉向本地域,跨域數據即由 iframe 的 window.name從外域傳遞到本地域。這個就巧妙地繞過了瀏覽器的跨域訪問限制,但同時它又是安全操做。
實現原理: a.html 欲與 c.html 跨域相互通訊,經過中間頁 b.html 來實現。 三個頁面,不一樣域之間利用 iframe 的 location.hash 傳值,相同域之間直接 js 訪問來通訊。
具體實現步驟:一開始 a.html 給 c.html 傳一個 hash 值,而後 c.html 收到 hash 值後,再把 hash 值傳遞給 b.html,最後 b.html 將結果放到 a.html 的 hash 值中。
該方式只能用於二級域名相同的狀況下,好比 a.test.com
和 b.test.com
適用於該方式。
只須要給頁面添加 document.domain ='test.com'
表示二級域名都相同就能夠實現跨域。
實現原理:兩個頁面都經過 js 強制設置 document.domain 爲基礎主域,就實現了同域。
在Vue項目中,通常使用vue-cli構建項目後,咱們會在Router文件夾下面的index.js裏面引入相關的路由組件,如:
import Hello from '@/components/Hello' import Boy from '@/components/Boy' import Girl from '@/components/Girl'
這樣作的結果就是webpack在npm run build的時候會打包成一個整個的js文件,若是頁面一多,會致使這個文件很是大,加載緩慢,爲了解決這個問題,須要將他分紅多個小文件,並且還要實現異步按需加載,即用到了再加載,而不用一股腦所有加載。
1.webpack提供的require.ensure(),這樣能夠實現按需加載,而且你能夠將多個相同類的組件打包成一個文件,只要給他們指定相同的chunkName便可,如示例中的demo將會打包成一個文件。
{
path:
'/promisedemo'
,
name:
'PromiseDemo'
,
component: r => require.ensure([], () => r(require(
'../components/PromiseDemo'
)),
'demo'
)
},
{
path:
'/hello'
,
name:
'Hello'
,
// component: Hello
component: r => require.ensure([], () => r(require(
'../components/Hello'
)),
'demo'
)
}
|
2.Vue的異步組件技術,這種方法能夠實現按需加載,而且一個組件會打包成一個js文件
{
path:
'/promisedemo'
,
name:
'PromiseDemo'
,
component: resolve => require([
'../components/PromiseDemo'
], resolve)
}
|
3.es提案的import(),也是我推薦的方法
首先,能夠將異步組件定義爲返回一個 Promise 的工廠函數 (該函數返回的 Promise 應該 resolve 組件自己):
const Foo = () => Promise.resolve({
/* 組件定義對象 */
})
|
第二,在 Webpack 2 中,咱們可使用動態 import語法來定義代碼分塊點 (split point):
import(
'./Foo.vue'
)
// 返回 Promise
|
注意:若是您使用的是 Babel,你將須要添加 syntax-dynamic-import
插件,才能使 Babel 能夠正確地解析語法。
結合這二者,這就是如何定義一個可以被 Webpack 自動代碼分割的異步組件。
const Foo = () => import(
'./Foo.vue'
)
|
const Foo = () => import(
/* webpackChunkName: "group-foo" */
'./Foo.vue'
)
|
/*
this的使用:在預編譯的過程this指向是window
在全局做用域裏this指向是window
call/apply 改變this的指向
obj.function();function()裏面的this指向的是obj
*/
var
obj = {
a:
function
() {
console.log(
this
.name)
},
name:
'123'
}
obj.a();
//誰調用這個方法this指向誰,沒用調用就是預編譯
var
foo = 123;
function
print() {
this
.foo = 234;
console.log(foo);
}
/*print();*/
//234
new
print();
//123
/*
arguments.callee:指向函數的引用,也就是它自己
*/
var
num = (
function
(n) {
if
(n == 1) {
return
1;
}
else
{
return
n * arguments.callee(n - 1);
}
}(10))
/*深層拷貝*/
/*
遍歷對象 for (var prop in obj)
1:判斷是否是原始值 typeof()
2:判斷是數組仍是對象
3:判斷相應的數組和對象
*/
var
obj = {
name:
'tom'
,
age: 23,
son: {},
wife: [
'sss'
,
'ddd'
]
}
function
deepClone(origin, target) {
//容錯
var
target = target || {},
toStr = Object.prototype.toString(),
arrStr =
"[Object Array]"
;
for
(
var
prop
in
origin){
if
(origin.hasOwnProperty(prop)){
if
(origin[prop] !==
'null'
&&
typeof
(origin[prop]) ==
'object'
){
if
( toStr.call(origin[prop]) == arrStr){
target[prop]= [];
}
else
{
target[prop] ={};
}
deepClone(origin[prop],target[prop]);
}
else
{
target[prop] == origin[prop];
}
}
}
}
|
優雅降級:
Web站點在全部新式瀏覽器中都能正常工做,若是用戶使用的是老式瀏覽器,則代碼會檢查以確認它們是否能正常工做。因爲IE獨特的盒模型佈局問題,針對不一樣版本的IE的hack實踐過優雅降級了,爲那些沒法支持功能的瀏覽器增長候選方案,使之在舊式瀏覽器上以某種形式降級體驗卻不至於徹底失效。
漸進加強:
從被全部瀏覽器支持的基本功能開始,逐步地添加那些只有新式瀏覽器才支持的功能,向頁面增長無害於基礎瀏覽器的額外樣式和功能的。當瀏覽器支持時,它們會自動地呈現出來併發揮做用。
.transition {
/*漸進加強寫法*/
-webkit-transition: all .5s;
-moz-transition: all .5s;
-o-transition: all .5s;
transition: all .5s;
}
.transition {
/*優雅降級寫法*/
transition: all .5s;
-o-transition: all .5s;
-moz-transition: all .5s;
-webkit-transition: all .5s;
}
|
前綴CSS3(-webkit- / -moz- / -o-*)和正常的 CSS3 在瀏覽器中的支持狀況是這樣的:
注意: css中須要知道的是 - 若是屬性不可用,則不發揮任何做用,無影響;若是屬性是相同的做用,則後者會覆蓋前者。
漸進加強的寫法,優先考慮老版本瀏覽器的可用性,最後才考慮新版本瀏覽器的可用性。
而優雅降級的寫法,優先考慮新版本瀏覽器的可用性,最後才考慮瀏覽器的可用性。
就CSS3來講,咱們更加推薦漸進加強的寫法。
觸發高頻事件後n秒內函數只會執行一次,若是n秒內高頻事件再次被觸發,則從新計算時間
思路:
每次觸發事件時都取消以前的延時調用方法
function
debounce(fn) {
let timeout =
null
;
// 建立一個標記用來存放定時器的返回值
return
function
() {
clearTimeout(timeout);
// 每當用戶輸入的時候把前一個 setTimeout clear 掉
timeout = setTimeout(() => {
// 而後又建立一個新的 setTimeout, 這樣就能保證輸入字符後的 interval 間隔內若是還有字符輸入的話,就不會執行 fn 函數
fn.apply(
this
, arguments);
}, 500);
};
}
function
sayHi() {
console.log(
'防抖成功'
);
}
var
inp = document.getElementById(
'inp'
);
inp.addEventListener(
'input'
, debounce(sayHi));
// 防抖
|
高頻事件觸發,但在n秒內只會執行一次,因此節流會稀釋函數的執行頻率
思路:
每次觸發事件時都判斷當前是否有等待執行的延時函數
function
throttle(fn) {
let canRun =
true
;
// 經過閉包保存一個標記
return
function
() {
if
(!canRun)
return
;
// 在函數開頭判斷標記是否爲true,不爲true則return
canRun =
false
;
// 當即設置爲false
setTimeout(() => {
// 將外部傳入的函數的執行放在setTimeout中
fn.apply(
this
, arguments);
// 最後在setTimeout執行完畢後再把標記設置爲true(關鍵)表示能夠執行下一次循環了。當定時器沒有執行的時候標記永遠是false,在開頭被return掉
canRun =
true
;
}, 500);
};
}
function
sayHi(e) {
console.log(e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener(
'resize'
, throttle(sayHi));
|
vue.js 是採用數據劫持結合發佈者-訂閱者模式的方式,經過 Object.defineProperty()來劫持各個屬性的 setter,getter,在數據變更時發佈消息給訂閱者,觸發相應的監聽回調。
具體步驟:
第一步:須要 observe 的數據對象進行遞歸遍歷,包括子屬性對象的屬性,都加上 setter 和 getter 這樣的話,給這個對象的某個值賦值,就會觸發 setter,那麼就能監聽到了數據變化
第二步:compile 解析模板指令,將模板中的變量替換成數據,而後初始化渲染頁面視圖,並將每一個指令對應的節點綁定更新函數,添加監聽數據的訂閱者,一旦數據有變更,收到通知,更新視圖
第三步:Watcher 訂閱者是 Observer 和 Compile 之間通訊的橋樑,主要作的事情是:
第四步:MVVM 做爲數據綁定的入口,整合 Observer、Compile 和 Watcher 三者,經過 Observer 來監聽本身的 model 數據變化,經過 Compile 來解析編譯模板指令,最終利用 Watcher 搭起 Observer 和 Compile 之間的通訊橋樑,達到數據變化 -> 視圖更新;視圖交互變化(input) -> 數據 model 變動的雙向綁定效果。
總共分爲 8 個階段建立前/後,載入前/後,更新前/後,銷燬前/後。