# 疑难杂症 & 常见问题
# 1 移动端物理像素问题
设备像素比就是设备的物理像素与逻辑像素的比值,公式表达如下:dpr = window.devicePixelRatio。retina屏的手机,dpr值等于2或3,什么意思呢?就是css样式表里写的 1px 映射到物理像素上就有 2px 或 3px。
那么如何实现移动端的 1px
# 伪类 + transform + 媒体查询
不使用原来 border 设置边框, 改用其伪类来模拟 border, 通过媒体查询 和 transform 的 scale 大小来改变边框值。
.border-1px {
position: relative;
box-sizing: border-box;
width: 100%;
padding: 10px;
text-align: center;
}
.border-1px:after {
position: absolute;
top: 0;
left: 0;
content: '';
box-sizing: border-box;
border: 1px solid red;
border-radius: 10px;
transform-origin: 0 0;
}
@media (-webkit-min-device-pixel-ratio: 2) {
.border-1px::after {
width: 200%;
height: 200%;
transform: scale(0.5);
}
}
@media (-webkit-min-device-pixel-ratio: 3) {
.border-1px::after {
width: 300%;
height: 300%;
transform: scale(0.333);
}
}
- 优点是所有场景都能满足
- 缺点是兼容性
# 小数方案 + 媒体查询
通过媒体查询,给不同 dpr
设置不同单位边框值。
.border-1px {
text-align: center;
padding: 10px;
border-radius: 5px;
border: 1px solid red;
}
@media screen and (-webkit-min-device-pixel-ratio: 2) {
.border-1px {
border: 0.5px solid red;
}
}
@media screen and (-webkit-min-device-pixel-ratio: 3) {
.border-1px {
border: 0.333px solid red;
}
}
- IOS8+ 苹果系列都已经支持单位小数方案了
- IOS7及以下和 Android,小数方案会显示为 0px,可以通过
window.devicePixelRatio
来写 Hack - 优点就是简单,代码少
- 缺点就是兼容性差
# viewport + rem
通过 javascript 更改 viewport 的 rem 基准值,这样就可以继续写 px 了。
- 比如 dpr = 2 时,
- 比如 dpr = 3 时,
.border-1px {
box-sizing: border-box;
padding: 1rem;
text-align: center;
border: 1px solid red;
border-radius: 0.5rem;
}
const viewport = document.querySelector('meta[name=viewport]');
const dpr = window.devicePixelRatio || 1;
const scale = (1 / dpr).toFixed(3);
viewport.setAttribute('content', 'width=device-width,' + 'initial-scale=' + scale + ', maximum-scale=' + scale + ',minimum-scale=' + scale + ', user-scalable=no');
const docE1 = document.documentElement;
const fontsize = 10 * (docEl.clientWidth / 320) + "px";
docEl.style.fontSize = fontsize;
- 优点就是只适用于新项目,一套代码兼容所有布局
- 缺点就是老项目修改代价大
# 图片方式
不管通过 border-image
还是 background-image
, 修改边框颜色需要换图片,圆角还需单独处理且边缘模糊。
# 插件 postcss-write-svg
项目中使用 PostCss
, 然后安装这个插件。
@svg border-1px-base {
height: 2px;
@rect {
fill: var(--color, gray);
width: 100%;
height: 50%;
}
}
.border-1px {
border: 1px solid transparent;
border-image: svg(border-1px param(--color red)) 2 2 stretch;
}
# 2 vue 触发更新问题
1.怎么实现 this._test
改变而不是 this._data.test
改变触发更新呢?
答:其中这中间有一个 代理 的过程。
_proxy(options.data);
function _proxy(data) {
const that = this;
Object.keys(data).forEach((key) => {
Object.defineProperty(that, key, {
configurable: true,
enumerable: true,
get: function proxyGetter () {
return that._data[key];
},
set: function proxySetter (val) {
that._data[key] = val;
}
})
})
}
本质就是通过 Object.defineProperty
使在访问 this
上的某属性时从 this._data
中读取(写入)。
- 能不能将依赖收集中讲到的
dep.addSub(Dep.target)
改成dep.addSub(new Watcher())
呢?
为了便于读者理解这部分内容,我将代码做了简化,实际上一个 watcher
对象可能会在多个 Dep
中,并不是每次 addSub
都是一个新的 Watcher
对象,需依赖 Dep.target
进行收集(实际上 Dep.target
) 也是通过 Watcher
对象的 get
方法调用 pushTarget
将自身赋值给 Dep.target
)。