解答: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 个阶段建立前/后,载入前/后,更新前/后,销毁前/后。