「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战」
简介
这篇文章主要是总结下移动端开发常见问题,帮助大家一起避坑。如果已经看过这篇文章了的话也可以看看笔者写的
HTML方面
调用系统功能
<!-- 拨打电话 -->
<a href="tel:10010">拨打电话给10010</a>
<!-- 发送短信 -->
<a href="sms:10010">发送短信给10010</a>
<!-- 发送邮件 -->
<a href="mailto:randy@qq.com">发送邮件给randy</a>
<!-- 选择照片或拍摄照片 -->
<input type="file" accept="image/*" />
<!-- 选择视频或拍摄视频 -->
<input type="file" accept="video/*" />
<!-- 选择文件 -->
<input type="file" />
<!-- 多选文件 -->
<input type="file" multiple />
忽略自动识别
有时电话、邮箱的自动识别会很烦,我们可以关闭。
<!-- 忽略自动识别电话 -->
<meta name="format-detection" content="telephone=no" />
<!-- 忽略自动识别邮箱 -->
<meta name="format-detection" content="email=no" />
<!-- 忽略自动识别电话和邮箱 -->
<meta name="format-detection" content="telephone=no, email=no" />
弹出数字键盘
这里需要注意,系统需要使用的是原生键盘才会生效,不能使用搜狗等输入法,不然只会出现相应输入法的数字键盘。
<!-- 纯数字带#和* -->
<input type="tel">
<!-- 纯数字 -->
<input type="number" pattern="\d*">
优先使用最新版本 IE 和 Chrome
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
禁止页面缓存
<meta http-equiv="Cache-Control" content="no-cache" />
关闭首字母大写
当我们使用键盘在输入框输入英文字符的时候,一般首字母会自动大写,如果不需要可以关闭。
<input type="text" autocapitalize="off" />
关闭输入自动修正
和英文输入默认自动首字母大写那样,IOS还做了一个功能,默认输入法会开启自动修正输入内容,这样的话,用户经常要操作两次。如果不希望开启此功能,我们可以通过input标签属性来关闭掉。
<input type="text" autocorrect="off" />
禁止页面缩放
不想让页面缩放我们可以把viewport的user-scalable值设置为no,这个在上篇文章介绍viewport的时候有说到。
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, minimum-scale=1, maximum-scale=1" />
长按识别二维码
有时候我们会接到需求,就是长按二维码要自动识别出二维码,其实这个是最简单的,就是把我们的图片使用img
标签展示出来就可以了。记住不要使用背景图,不然会无效。
CSS方面
1px问题
1px问题在Retina 高清屏上才会出现,由于高清屏用多个物理像素显示一个css像素,比如iphone6,由于dpr为2,所以1css像素会用2个物理像素显示,所以看起来1px的线条会特别宽。
解决1px问题的方案有很多,有背景图、阴影、缩放等等,笔者在这里只介绍笔者曾经使用过得方案,就是缩放。原理就是边框固定,把元素整体先放大然后再缩小。
.ele {
position: relative;
width: 100px;
height: 80px;
&::after {
content: "";
position: absolute;
left: 0;
top: 0;
border: 1px solid red;
width: 200%;
height: 200%;
transform: scale(0.5);
transform-origin: left top;
}
}
如果不想给每个元素都这样设置的话,我们可以采用笔者前面写的移动端H5网页开发必备知识里面介绍的lib-flexible
方案,根据dpr将网页整体进行缩放。
横屏和竖屏
在某些特定横屏或者竖屏的情况下的样式我们可以通过如下@media查询进行设置。
/* 竖屏 */
@media all and (orientation: portrait) {
/* 自定义样式 */
}
/* 横屏 */
@media all and (orientation: landscape) {
/* 自定义样式 */
}
让苹果手机滚动更流畅
在苹果手机上滚动的时候有时候可能会不流畅,我们可以给元素添加如下属性。
.elem {
-webkit-overflow-scrolling: touch;
}
让苹果手机页面里的字体变清晰
.elem {
-webkit-font-smoothing:antialiased;
}
-webkit-font-smoothing
在window
系统下没有起作用,但是在IOS
设备上是起作用的,灰度平滑。
禁止选择
有时候我们的图片不想让别人长按保存等操作,我们可以使用如下样式,让用户长按图片无效。
#img1 {
pointer-events: none; /* 微信浏览器还需附加该属性才有效 */
user-select: none;
-webkit-touch-callout: none;
}
原生表单美化
我们使用原生表单的时候样式会很丑,特别还有一层灰色的遮罩,我们可以使用如下样式把遮罩进行消除。小伙伴们是不是感觉清爽了很多呢。
button,
input,
select,
textarea {
-webkit-appearance: none;
}
元素被点击时产生的半透明灰色遮罩怎么去掉
当使用原生表单的时候,点击的时候会有个灰色点击特效,如果不需要可以使用如下样式去掉。
a,button,input,textarea,select {
-webkit-tap-highlight-color: transparent;
}
美化滚动条
滚动条样式太丑希望自定义,我们可以使用如下样式属性进行自定义样式。这个是pc端和移动端通用的样式。
- ::-webkit-scrollbar:滚动条整体部分
- ::-webkit-scrollbar-track:滚动条轨道部分
- ::-webkit-scrollbar-thumb:滚动条滑块部分
::-webkit-scrollbar {
width: 6px;
height: 6px;
background-color: transparent;
}
::-webkit-scrollbar-track {
background-color: transparent;
}
::-webkit-scrollbar-thumb {
border-radius: 3px;
background-image: linear-gradient(90deg, #09f, #3c9);
}
隐藏滚动条
::-webkit-scrollbar {
display: none; /* Chrome Safari */
}
美化占位符
这个是pc端和移动端通用的样式,当我们需要修改占位符placeholder的颜色我们可以使用如下样式。
input::placeholder {
color: red;
}
输入框文字居中对齐
有时候我们就算设置了line-height的值等于height,文字看起来也不居中,我们可以使用如下样式。
input {
line-height: normal;
}
修改input光标颜色
这种方法不会影响字体颜色,只会修改光标颜色。
input {
caret-color: red;
}
当然你也可以可以设置字体颜色来修改光标颜色。但是这种方式会将字体颜色也一起修改。
input {
color: red;
}
删除 type="number"
末尾的箭头
.no-arrow::-webkit-outer-spin-button,
.no-arrow::-webkit-inner-spin-button {
-webkit-appearance: none;
}
input自动填充时出现背景颜色
比如,如果保存了账号密码,自动填充会有一个背景颜色。
解决就是给它设置一个足够大的白色内阴影。
:-webkit-autofill {
box-shadow: 0 0 0px 1000px white inset !important;
}
这样设置后背景色会没有,但是自动填充的内容文本默认是黑色的。
如果也想让字体颜色自定义的话还需要加上如下样式。比如笔者这里设置的蓝色。
:-webkit-autofill {
-webkit-text-fill-color: #4c7fd2 !important;
}
这样就完美了。
怎么解决img底部空白间隙问题
- 将
img
元素设置为display: block
- 将
img
元素设置为vertical-align: bottom
- 设置父元素
font-size
设为0
全网页置灰
html {
filter: grayscale(100%);
}
如果需要具体到某元素置灰,把该样式应用到某具体元素即可。
文本溢出省略号
当我们需要将多余文本用省略号显示的时候,我们可以用到如下样式。这个是pc端和移动端通用的样式。
多行省略没有这个限制。
单行省略
使用这个样式需要注意,元素必须是块级元素,或者是具有指定宽度(设置width
或max-width
)的行内块级元素。
/* 单行出现省略号*/
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
并且元素不能嵌套,也就是下面的这种写法是无效的
<div class="ellipsis">
<div>
多层div嵌套也会不起作用。
当我们需要将多于文本用省略号显示的时候,我们可以用到如下样式。其实这个是pc端和移动端通用的样式。
使用这个样式需要注意,元素必须是块级元素,或者是具有指定宽度的行内块级元素(就是display:
inline-block)。
</div>
</div>
多行省略
多行省略相对限制没那么多,可以是块级元素也可以是行内元素,并且还支持嵌套。
/* 多行省略号,就是想在多少行后再出现省略号就使用如下样式*/
.ellipsis-n {
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 2; /*这个值可以随便设置,比如这里的2就是最多显示两行,两行后出现省略号*/
-webkit-box-orient: vertical;
}
注意,包裹文字的元素不能有固定高度或者下外边距,否则可能会有文字漏出。
图片模糊问题
这个类似于1px问题,在Retina 高清屏上才会出现,由于高清屏用多个物理像素显示一个css像素,比如iphone6(750个物理像素375个css像素),由于dpr为2,所以1css像素会用2个物理像素显示,所以1像素的图片用2个物理像素显示出来就会模糊。
解决图片模糊根本原因就是根据不同dpr使用不同倍数的图片。
@media查询
.avatar{
background-image: url(randy_1x.png);
}
@media only screen and (-webkit-min-device-pixel-ratio:2){
.avatar{
background-image: url(randy_2x.png);
}
}
@media only screen and (-webkit-min-device-pixel-ratio:3){
.avatar{
background-image: url(randy_3x.png);
}
}
image-set
.avatar {
background-image: -webkit-image-set( "randy_1x.png" 1x, "randy_2x.png" 2x );
}
srcset
这里需要注意,这里的图片不能被webpack处理,就是不能被转成base64,不然会失效。
<img src="randy_1x.png" srcset="randy_2x.png 2x, randy_3x.png 3x" />
js处理
const dpr = window.devicePixelRatio;
const images = document.querySelectorAll('img');
images.forEach((img)=>{
img.src.replace(".", `${dpr}x.`);
})
svg
当然,我们还可以使用不失帧的svg图片,这样就不需要管是什么屏幕了,svg图片会自动拉伸并且不会失帧。
<img src="randy.svg" />
JS方面
点击延迟
在移动端浏览器里点击操作会存在300ms
延迟。
07年,苹果公司发布首款Iphone前夕,遇到一个问题:当时的网站都是为大屏设计,手机屏幕太小无法正常浏览,于是苹果工程师做了一些约定解决此类问题。 这些约定当中,最为有名的是双击缩放(double tap to zoom),这是产生300ms延迟的根源。 用手指在屏幕上快速点击两次,iOS 自带的 Safari 浏览器会将网页缩放至原始比例。如果用户在 iOS Safari 里边点击了一个链接。由于用户可以进行双击缩放或者双击滚动的操作,当用户一次点击屏幕之后,浏览器并不能立刻判断用户是确实要打开这个链接,还是想要进行双击操作。因此,iOS Safari 就等待 300 毫秒,以判断用户是否再次点击了屏幕。 鉴于iPhone的成功,其他移动浏览器都复制了 iPhone Safari 浏览器的多数约定,包括双击缩放,几乎现在所有的移动端浏览器都有这个功能。 由此产生了300ms延迟问题。
解决点击延迟的方案有很多,下面笔者着重介绍四种。
禁止页面缩放
我们知道点击延迟的根源就是因为页面缩放,所以我们可以把页面禁止缩放,就能从源头上解决该问题了,但是该方案的缺点也很明显,就是页面不能缩放了。
<!-- 1.禁用缩放 user-scalable=no -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
更改默认的视口宽度
<meta name="viewport" content="width=device-width" />
因为双击缩放主要是用来改善桌面站点在移动端浏览体验的,而随着响应式设计的普及,很多站点都已经对移动端坐过适配和优化了,这个时候就不需要双击缩放了,如果能够识别出一个网站是响应式的网站,那么移动端浏览器就可以自动禁掉默认的双击缩放行为并且去掉300ms的点击延迟。如果设置了上述meta
标签,那浏览器就可以认为该网站已经对移动端做过了适配和优化,就无需双击缩放操作了。
这个方案相比方案一的好处在于,它没有完全禁用缩放,而只是禁用了浏览器默认的双击缩放行为,但用户仍然可以通过双指缩放操作来缩放页面。
使用zepto
在前端领域里最早解决点击延迟的是jQuery时代
的zepto
,估计现在大部分同学都未使用过zepto
,其实它就是移动端版本的jquery
。zepto
封装tap事件
能有效地解决点击穿透,我们知道移动端点击事件触发的顺序为 touchstart —> touchmove —> touchend —> click
,通过监听document
上的touch事件
(因为click事件在移动端有300ms延迟但是touch事件在移动端没延迟)完成tap事件
的模拟,并将tap事件
冒泡到document
上触发模拟移动端的click事件。
虽然zepto能解决点击延迟问题,但是也会带来点击穿透问题,就是如果某元素下面真实有绑定click的元素,我们知道事件具有传播性,就会在tap事件后会继续触发click,这样就会带来点击穿透问题。
说道这里,细心的小伙伴会发现了,为什么不在touchend
事件里面使用e.preventDefault()
来阻止后续事件触发呢?其实这跟zepto的tap事件实现有关,因为zepto的tap事件是全局监听touchend
事件的,所以如果使用了e.preventDefault()
会影响到真正的touchend
事件。
使用fastclick
fastclick 是 FT Labs 专门为解决移动端浏览器 300 毫秒点击延迟问题所开发的一个轻量级的库。fastclick的实现原理是在检测到touchend事件的时候,会通过DOM自定义事件立即出发模拟一个click事件,并把浏览器在300ms之后的click事件阻止掉。
fastclick不但能解决点击延迟而且不会带来点击穿透问题,是点击延迟的最优解。具体使用方法可以查看官方文档。
时间问题
在苹果系统上解析YYYY-MM-DD HH:mm:ss
这种日期格式会报错,但在安卓系统上解析这种日期格式完全无问题。但是YYY/MM/DD HH:mm:ss
这种日期格式在苹果和安卓上都能正常运行,所以我们只需要统一使用这种方案就可以了。
const date = "2010-12-31 12:30:00";
new Date(date.replace(/\-/g, "/"));
滚动到指定位置
这个技巧在pc端和移动端都适用,使用Element.scrollIntoView()方法,我们再也不需要使用scrollBy()
和scrollTo()
来实现滚动到指定位置了。
element.scrollIntoView(); // 等同于element.scrollIntoView(true)
element.scrollIntoView(alignToTop); // Boolean型参数
element.scrollIntoView(scrollIntoViewOptions); // Object型参数
alignToTop
可选,一个Boolean
值:
-
如果为
true
,元素的顶端将和其所在滚动区的可视区域的顶端对齐。相应的scrollIntoViewOptions: {block: "start", inline: "nearest"}
。这是这个参数的默认值。 -
如果为
false
,元素的底端将和其所在滚动区的可视区域的底端对齐。相应的scrollIntoViewOptions: {block: "end", inline: "nearest"}
。
scrollIntoViewOptions
可选,一个包含下列属性的对象:
-
behavior
可选,定义动画过渡效果,"auto"
或"smooth"
之一。默认为"auto"
。 -
block
可选,定义垂直方向的对齐,"start"
,"center"
,"end"
, 或"nearest 就近对齐"
之一。默认为"start"
。 -
inline
可选,定义水平方向的对齐,"start"
,"center"
,"end"
, 或"nearest 就近对齐"
之一。默认为"nearest 就近对齐"
。
横屏和竖屏
js中监控横竖屏方法有两种
这种方法现在已不推荐使用,了解即可。
window.addEventListener("resize", ()=>{
if (window.orientation === 180 || window.orientation === 0) {
// 正常方向或屏幕旋转180度
console.log('竖屏');
};
if (window.orientation === 90 || window.orientation === -90 ){
// 屏幕顺时钟旋转90度或屏幕逆时针旋转90度
console.log('横屏');
}
});
我们重点来看看第二种,这种方式功能非常强大,不仅仅是横竖屏检测,只要是合法的媒体查询他都支持。后面我们说主题色监听也是使用的这种方式。
// 竖屏检测
const mediaQuery = window.matchMedia("(orientation: portrait)");
// const mediaQuery = window.matchMedia("(orientation: landscape)"); 这是横屏
const darkModeHandler = () => {
if (mediaQuery.matches) {
console.log("竖屏");
} else {
console.log("横屏");
}
};
// 3种 监听模式变化
mediaQuery.addListener(darkModeHandler);
// mediaQuery.addEventListener("change", darkModeHandler);
// mediaQuery.onchange = darkModeHandler;
禁止网页复制
const html = document.querySelector('html');
html.oncopy = () => false;
iPhoneX适配
viewport-fit
viewport-fit
是专门为了适配iPhoneX
而诞生的一个属性,它用于限制网页如何在安全区域内进行展示。
-
contain
: 可视窗口完全包含网页内容 -
cover
:网页内容完全覆盖可视窗口
默认情况下或者设置为auto
和contain
效果相同。
<meta name="viewport" content="viewport-fit=cover" />
env、constant
我们需要将顶部和底部合理的摆放在安全区域内,iOS11
新增了两个CSS
函数env、constant
,用于设定安全区域与边界的距离。
constant
在iOS < 11.2
的版本中生效,env
在iOS >= 11.2
的版本中生效,这意味着我们往往要同时设置他们,将页面限制在安全区域内:
函数内部可以是四个常量:
safe-area-inset-left
:安全区域距离左边边界距离safe-area-inset-right
:安全区域距离右边边界距离safe-area-inset-top
:安全区域距离顶部边界距离safe-area-inset-bottom
:安全区域距离底部边界距离
注意:我们必须指定viewport-fit=cover
后才能使用这两个函数。
body {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
深色主题适配方案
随着 iOS 13 的发布,深色模式(Dark Mode)越来越多地出现在大众的视野中,支持深色模式已经成为现代移动应用和网站的一个潮流,前段时间更是因为微信的适配再度引起热议。深色模式不仅可以大幅减少电量的消耗,减弱强光对比,还能提供更好的可视性和沉浸感。
如何切换深色模式
- iOS:设置->显示与亮度->外观->选择深色。
- Android:系统设置->显示->深色模式。
适配方案
如果系统设置了深色模式,H5页面不做相应的处理,会出现背景色冲突、深色文字显示异常,深色图标显示异常等一些显示上的问题。
声明 color-scheme
在head中声明<meta name="color-scheme" content="light dark">
,声明当前页面支持 light 和 dark 两种模式,系统切换到深色模式时,浏览器默认样式也会切换到深色;具体细节可以查看 color-scheme 文档。
或者使用css申明,也能达到同样的效果。
:root {
color-scheme: light dark;
}
设置完color-scheme
后,浏览器就会帮我们干一件事,就是将背景颜色调节一下,深色主题就将背景色设置为黑色,浅色主题就将背景色设置为白色。这显然不符合我们的要求,所以我们的系统如果需要支持主题色的话,还得做更多的处理。
prefers-color-scheme
prefers-color-scheme CSS 媒体特性用于检测用户是否有将系统的主题色设置为亮色或者暗色。我们可以通过监听用户所设置的主题来进行一些特殊样式的处理。
- no-preference
表示系统未得知用户在这方面的选项。在布尔值上下文中,其执行结果为 false。
- light
表示用户已告知系统他们选择使用浅色主题的界面。
- dark
表示用户已告知系统他们选择使用暗色主题的界面。
:root {
color-scheme: light dark;
color: black;
}
@media (prefers-color-scheme: dark) {
:root {
color: white;
}
}
JS中的处理
除了CSS的媒体查询能获取用户主题,在JS中我们也能获取得到用户的主题。
matchMedia
Window 的matchMedia() 方法返回一个新的MediaQueryList 对象,表示指定的媒体查询 (en-US)字符串解析后的结果。返回的MediaQueryList 可被用于判定Document是否匹配媒体查询,或者监控一个document 来判定它匹配了或者停止匹配了此媒体查询。
const mqList = window.matchMedia(mediaQueryString)
addListener()
MediaQueryList接口的addListener()方法向MediaQueryListener添加一个侦听器,该侦听器将运行自定义回调函数以响应媒体查询状态的更改。
MediaQueryList.addListener(func)
在JS中需要这两者配合使用
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
function darkModeHandler() {
if (mediaQuery.matches) {
console.log('现在是深色模式')
} else {
console.log('现在是浅色模式')
}
}
// 3种 监听模式变化
mediaQuery.addListener(darkModeHandler);
// mediaQuery.addEventListener("change", darkModeHandler);
// mediaQuery.onchange = darkModeHandler;
禁止微信浏览器放大字体
微信内置了调整字体大小的功能,用户可以根据实际情况进行调节。但是这也会导致字体大小改变以后,出现页面布局错乱的情况。
IOS解决方案
IOS
的解决方案是覆盖掉微信的样式
body {
/* IOS禁止微信调整字体大小 */
-webkit-text-size-adjust: 100% !important;
-moz-text-size-adjust: 100% !important;
text-size-adjust: 100% !important;
}
安卓解决方案
安卓比较麻烦,需要用js
来处理。安卓的解决方案是通过 WeixinJSBridge
对象将网页的字体大小设置为默认大小,并且重写设置字体大小的方法,让用户不能在该网页下设置字体大小
(function () {
if (
typeof WeixinJSBridge == "object" &&
typeof WeixinJSBridge.invoke == "function"
) {
handleFontSize();
} else {
if (document.addEventListener) {
document.addEventListener("WeixinJSBridgeReady", handleFontSize, false);
} else if (document.attachEvent) {
document.attachEvent("WeixinJSBridgeReady", handleFontSize);
document.attachEvent("onWeixinJSBridgeReady", handleFontSize);
}
}
function handleFontSize() {
// 设置网页字体为默认大小
WeixinJSBridge.invoke("setFontSizeCallback", { fontSize: 0 });
// 重写设置网页字体大小的事件
WeixinJSBridge.on("menu:setfont", function () {
WeixinJSBridge.invoke("setFontSizeCallback", { fontSize: 0 });
});
}
})();
这个问题可以看看移动端字体放大问题的研究这篇文章,写的很好。
ios 微信浏览器点击自带返回页面不刷新
在ios
微信浏览器上,从a
系统点击进入b
系统的,然后点击微信自带的返回按钮不会触发a
系统页面的重新加载,也就是不会进入onmounted
方法,所以页面不会重新加载最新数据。
解决得使用pageshow
事件监听。
const pageshow = (e) => {
// e.persisted 为true代表从缓存加载该页面
console.log(e.persisted)
// 自定义逻辑,比如重新加载数据
}
onMounted(async () => {
// 添加监听
window.addEventListener("pageshow", pageshow);
});
// ios跳转外部链接,不会触发该方法 但是会触发pagehide监听
// 系统内部的跳转会进入该销毁方法,移除监听,避免重复添加监听
onUnmounted(() => {
// 移除监听
window.removeEventListener("pageshow", pageshow);
});
再补充说明下
pageshow
在页面首次加载会触发,pagehide
在页面跳转至其它系统时触发,比如从a
系统跳转到b
系统。
visibilitychange
是在页面显示或隐藏时触发,比如切换浏览器tab、缩小窗口到后台。
load
在页面首次加载会触发,beforeunload、unload
在ios
上跳转外部链接不会触发
pageshow
事件类似于 load
事件,load
事件在页面第一次加载时触发, pageshow
事件在每次加载页面时触发,(注意 由于vue、react是单页面应用所以系统内页面跳转不会触发,只在第一次加载触发)。而 load
事件在页面从浏览器缓存中读取时不触发。
为了查看页面是直接从服务器上载入还是从缓存中读取,你可以使用 PageTransitionEvent
对象的 persisted
属性来判断。 如果页面从浏览器的缓存中读取该属性返回 ture
,否则返回 false
扩展
判断是否在微信浏览器
export const isWechatBrowser = () => {
const ua = navigator.userAgent.toLowerCase();
return ua.indexOf('micromessenger') != -1;
}
检查是否在苹果设备上
const isAppleDevice = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
console.log(isAppleDevice);
检查设备是移动设备还是电脑
const detectDeviceType = () =>
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|OperaMini/i.test(navigator.userAgent)
? 'Mobile'
: 'Desktop';
console.log(detectDeviceType()); // "Mobile" or "Desktop"
系列文章
参考文章
中高级前端必须注意的40条移动端H5坑位指南 | 网易三年实践
后记
感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!