田小檬博客 https://lmwa.cn/ zh-CN 认真生活,积极分享,这里是记录我的学习和生活日常的小空间,世界上没有不成功的人,只有不努力的人 Fri, 26 May 2023 15:35:13 +0800 Fri, 26 May 2023 15:35:13 +0800 个人Blog,意义何在? http://lmwa.cn/archives/93.html http://lmwa.cn/archives/93.html Fri, 07 Jan 2022 09:57:00 +0800 田小檬 个人博客意义何在.jpeg

前言

从搭建第一个博客到现在也几年之久,但文章却只有寥寥几十篇,日IP也只有那屈指可数的几个。

有人说博客已经过时了,现在大多数人都用手机看朋友圈、微博等。是的,我一直在思考,在这个浮躁的时代,累死累活的搭建和维护一个个人博客的意义到底是什么?

我周围的同学有时也会质问我:“ 额,你做的这个..有什么用,能赚钱吗?

面对这个问题我竟吱吱呜呜答不上来, 因为赚不了钱反而倒贴钱,就不算运维成本,服务器域名CDN什么的都加起来一月也需要几十上百块 。最后,只能说一堆假大空的理想,哦不是,幻想敷衍了事并以此来博得他人的所谓的"认可"。

所以,我觉得也许有必要真正的静下来好好思考一下,我的博客的意义到底是什么,它的归宿又是何方.....

源由

现在有能力开独立博客的人,大多对技术有一定了解。然而这些人的目的非常简单:赚钱。这是很现实的目的,可问题是,抱着这样目的开博的人大多坚持不了半年就没兴趣了,因为钱挣不到。不可否认,开博客确实挣不到钱,但许多人忽略了它隐藏的价值,比如找工作、结识朋友

要研究一切事物的本质意义,就得先去搞清楚它的起源。就像科学家想要研究人类存在的意义的时候就必须知道人类的起源一样!

意义 去向

也许真正的意义就是自己喜欢就好
写博客是为了和过去以及未来的自己对话,个人博客从来不是写给别人看的,而是写给自己看的,我的想法可以畅言,我学习到的东西得以记录,我的生活值得回忆。
记录心情、生活、点点滴滴,个人博客并非赚钱的冰冷工具,有的人建立个人博客,完全是为了记录人生,抒发心情,与利益无关,这种博客往往能写出很多感人的故事。
个人博客是否还有存在的意义,关键在于个人博客能否体现其存在的价值。

关于我和关于本站的内容请前往关于我的查看

]]>
40 http://lmwa.cn/archives/93.html#comments http://lmwa.cn/feed/
uniapp的插件市场可以广告盈利 http://lmwa.cn/archives/880.html http://lmwa.cn/archives/880.html Fri, 26 May 2023 15:35:13 +0800 田小檬 前言

我记得在之前的时候下载uniapp的插件是不需要观看广告的,前段时间(应该挺长时间了)的时候发现他是有一些需要看广告了,大概阅读了一下官方的文档。是因为作者可以设置为观看广告后下载,同时作者也会获得收益,这样确实可以带动开发者的积极性,比较有钱(虽然不多,但是也比没有强)因此我也想试试。

发布插件

发布插件也很简单,打开uniapp的插件市场就可以看到发布插件的按钮了,进去直接就可以发布介绍写的也很清晰,大家可以自行尝试。

收益

不说什么直接看看我的吧,我刚刚开始发,也没几个插件,广告大概每次2毛钱左右吧,也还好。

收益

我的插件库

欢迎大家使用:

小檬的插件

]]>
0 http://lmwa.cn/archives/880.html#comments http://lmwa.cn/feed/
自由社 http://lmwa.cn/archives/874.html http://lmwa.cn/archives/874.html Sun, 21 May 2023 01:39:00 +0800 田小檬 自游社

说明

以后我的旅行文章博客内容将会转移到自游社 上进行发布
H5(开发版)
APP(安卓苹果通用下载地址-开发版)

微信小程(微信直接搜索或扫描下方小程序二维码)
微信小程序码

转移的目的是专项性,好吧博客东西太杂了也懒得整理了,干脆单独做个,还有如果你们也喜欢旅游和记录也欢迎加入我的“旅悦行”社区

旅悦行介绍

“自游社”是一款旅游社区APP,它的主要特色是集成了社区、动态、文章等功能,为用户提供了一个便利的平台来探索旅游灵感、分享旅行经验、获取目的地的详细信息以及丰富用户的旅行体验。
借助“自游社”APP的社区功能,用户可以联系旅游狂热者们分享旅行传播信息,探讨独特的旅游情感,或是寻求建议。
“自游社”APP的发现功能为用户提供许多旅游灵感,帮助用户发现世界上一些最迷人的景点、餐厅、酒店、商店、特价优惠等等。
最后,通过“自游社”APP的文章和动态功能,用户可以将自己的旅行心得和见闻写下来并发布在APP上,同时也可以浏览其他用户所分享的文章,对旅游目的地进行更全面、深入的了解,从多维度的角度去体验旅行的过程和旅游目的地的特色。

]]>
0 http://lmwa.cn/archives/874.html#comments http://lmwa.cn/feed/
黄花溪 http://lmwa.cn/archives/870.html http://lmwa.cn/archives/870.html Tue, 09 May 2023 09:46:00 +0800 田小檬 黄花溪全部图片:https://www.aliyundrive.com/s/mZZ7hZThmr2






]]>
3 http://lmwa.cn/archives/870.html#comments http://lmwa.cn/feed/
所以,旅途的意义到底是什么? http://lmwa.cn/archives/867.html http://lmwa.cn/archives/867.html Sun, 30 Apr 2023 13:44:00 +0800 田小檬 五一当节,全国旅游潮开始了,问自己一声,路途的意义到底是什么。在原神中我们刚刚踏入提瓦特大陆之时温迪曾告诉过我们:当你重新踏上旅途之后,一定要记得旅途本身的意义。
意义是什么呢,或许,它也是一种体验和生活方式。旅途中我们会遇到各种各样的人、事、物,这些都构成了旅途的价值和意义。或许是在旅途中我们可能会发现自己对某种风景或文化感兴趣,或许是发现自己有新的爱好或兴趣。这些都是旅途本身带来的意义和价值。

温迪:旅行者,当你重新踏上旅途之后,一定要记得旅途本身的意义。提瓦特的飞鸟、诗歌和城邦,女皇、愚人和怪物,都是你旅途的一部分。终点并不意味着一切,在抵达终点之前,用你的眼睛多多观察这个世界吧…

钟离:历史可以记录,但是历史并不可靠。时间拥有强大的力量,历史会在岁月中扭曲。你是远渡重天,跨越星海之人,把历史留在你的记忆里,也许会在未来某日,与你一同前往别的世界。身为旅行者的你,只要能够「记录」,那么提瓦特的时代与历史,就获得了一种「存在的备份」。

雷电将军:如果是你的话,一定可以拯救所有人的。如果是你的话,一定会拯救世界的。就像是我记得稻妻的一切一样,只要你记得我,我就能永远地活着。

纳西妲:旅行者,你是提瓦特的「第四降临者」。我对你未来的命运很感兴趣,那将是无法被观测,也不会被这个世界所记录的旅程。我曾说,命运是终极的知识,而你的未来则是终极的命运。

这些来自游戏《原神》中四个角色的话语,虽然都是在描述虚构世界中的旅行者,但它们的现实意义同样值得我们深思。

首先,从温迪的话语中可以看到,在旅途中接触不同的文化和经历不同的景色,可以让我们更加开放、宽容和有智慧。这种对多元性的认知能力在现实生活中同样非常重要,它可以帮助我们更好地理解和尊重不同文化之间的差异,并且更好地与他人沟通交流。

其次,钟离强调了记录的重要性。在现实生活中,无论是通过文字、照片、视频或其他方式记录经历,都可以帮助我们回顾过去、学习经验教训、分享感受和启发他人。我们可以将旅途中的经历与他人分享,甚至可能成为他人的启发和鼓励。

第三,雷电将军的话中传达出一种珍惜记忆和存在的态度。在现实生活中,我们也可以通过记忆保存那些让我们难以忘怀的经历和人物,这些记忆可以让我们感受到自己的存在意义,并且给生命赋予更多的意义和价值。

最后,纳西妲的话语则强调了未来的不确定性和挑战。在现实生活中,我们也会面临很多不确定的情况和挑战,但这些挑战也可能是一个机会,让我们学习、成长、进步并探索新的可能性。

总之,《原神》中这四个角色的话语虽然描述的是虚构世界的旅行者,但这些思考和洞见同样适用于我们在现实生活中的经历。因此,我们可以从这些话语中汲取灵感,帮助自己更好地理解、经历和欣赏这个世界。

生命的在“旅途”中要多多观察这个世界,感受和欣赏每一个瞬间。只有这样,我们才能从旅途中获得更多的收获,获取超乎预期的经验和知识,更深入地了解我们周围的世界。

]]>
9 http://lmwa.cn/archives/867.html#comments http://lmwa.cn/feed/
简洁封装-支付宝、微信支付、QQ钱包第三方PHP SDK http://lmwa.cn/archives/866.html http://lmwa.cn/archives/866.html Fri, 28 Apr 2023 16:16:00 +0800 田小檬 本文章中的SDK由彩虹(缤纷彩虹天地)开发

WeChatPay SDK for PHP

微信支付第三方 PHP SDK,基于官方最新版本,包含V2和V3两种接口。
下载:WeChatPay SDK for PHP

功能特点

  • 根据微信支付最新API开发,相比官方SDK,功能更完善,代码更简洁
  • 支持V2和V3两种接口,支持微信支付服务商模式与电商收付通模式
  • 支持Composer安装,无需加载多余组件,可应用于任何平台或框架
  • 符合PSR标准,你可以各种方便的与你的框架集成
  • 基本完善的PHPDoc,可以随心所欲添加本项目中没有的API接口

环境要求

PHP >= 7.1

使用方法

  1. Composer 安装。

    composer require cccyun/wechatpay-sdk
  2. 创建APIv2配置文件 config.php,或APIv3配置文件 config.php,填写微信支付商户信息。
  3. 引入配置文件,构造请求参数,调用PaymentService中的方法发起请求,APIv2参考 examples/qrpay.php,APIv3参考 examples/V3/qrpay.php
  4. 更多实例,请移步 examples 目录。
  5. 类功能说明

    类名说明
    PaymentService基础支付服务类,所有支付功能都用这个
    JsApiToolJSAPI支付工具类,用于公众号、小程序登录获取Openid
    TransferService微信支付商家转账功能
    ProfitsharingService微信支付分账功能
    ComplainService消费者投诉处理功能
    PartnerPaymentService服务商基础支付服务类,APIv3服务商调用支付功能使用
  6. 要对接的API在以上实现类中没有,可根据微信支付官方的文档,使用BaseService类中的execute方法直接调用接口,参考 examples/V3/other.php

Alipay SDK for PHP

支付宝开放平台第三方 PHP SDK,基于官方最新版本,支持公钥和公钥证书2种模式。

下载:Alipay SDK for PHP

功能特点

  • 根据支付宝开放平台最新API开发,相比官方SDK,功能更完善,代码更简洁
  • 支持支付宝服务商模式与互联网平台直付通模式
  • 支持Composer安装,无需加载多余组件,可应用于任何平台或框架
  • 符合PSR标准,你可以各种方便的与你的框架集成
  • 基本完善的PHPDoc,可以随心所欲添加本项目中没有的API接口

环境要求

PHP >= 7.1

使用方法

  1. Composer 安装。

    composer require cccyun/alipay-sdk
  2. 创建配置文件 config.php,填写配置信息。
  3. 引入配置文件,构造请求参数,调用AlipayTradeService中的方法发起请求,参考 examples/qrpay.php
  4. 更多实例,请移步 examples 目录。
  5. AlipayService实现类功能说明

    类名说明
    AlipayTradeService支付宝交易功能,基本上所有支付产品都用这个
    AlipayOauthService支付宝快捷登录功能,用于JS支付快捷登录以及第三方应用授权
    AlipaySettleService支付宝分账功能
    AlipayTransferService支付宝转账功能
    AlipayComplainService支付宝交易投诉处理
    AlipayCertifyService支付宝身份认证
    AlipayCertdocService支付宝实名证件信息比对验证
    AlipayBillService支付宝账单功能
  6. 要对接的API在AlipayService实现类中没有,可根据支付宝官方的文档,使用AlipayService类中的aopExecute方法直接调用接口,参考 examples/other.php

QQPay SDK for PHP

QQ钱包支付第三方 PHP SDK,基于官方最新版本。

下载:QQPay SDK for PHP

功能特点

  • 根据QQ钱包支付最新API开发,相比官方SDK,功能更完善,代码更简洁
  • 支持Composer安装,无需加载多余组件,可应用于任何平台或框架
  • 符合PSR标准,你可以各种方便的与你的框架集成
  • 基本完善的PHPDoc,可以随心所欲添加本项目中没有的API接口

环境要求

PHP >= 7.1

使用方法

  1. Composer 安装。

    composer require cccyun/qqpay-sdk
  2. 创建配置文件 config.php,填写QQ钱包支付商户信息。
  3. 引入配置文件,构造请求参数,调用PaymentService中的方法发起请求,参考 examples/qrpay.php
  4. 更多实例,请移步 examples 目录。
  5. 类功能说明

    类名说明
    PaymentService基础支付服务类,所有支付功能都用这个
    TransferServiceQQ钱包企业付款功能
  6. 要对接的API在以上实现类中没有,可根据QQ钱包官方的文档,使用BaseService类中的execute方法直接调用接口。
]]>
1 http://lmwa.cn/archives/866.html#comments http://lmwa.cn/feed/
你不知道的 CSS 之包含块 http://lmwa.cn/archives/861.html http://lmwa.cn/archives/861.html Thu, 27 Apr 2023 13:35:28 +0800 田小檬 你不知道的 CSS 之包含块

一说到 CSS 盒模型,这是很多小伙伴耳熟能详的知识,甚至有的小伙伴还能说出 border-box 和 content-box 这两种盒模型的区别。

但是一说到 CSS 包含块,有的小伙伴就懵圈了,什么是包含块?好像从来没有听说过这玩意儿。

image-20220814222004395

好吧,如果你对包含块的知识一无所知,那么系好安全带,咱们准备出发了。

image-20220813140434032

包含块英语全称为containing block,实际上平时你在书写 CSS 时,大多数情况下你是感受不到它的存在,因此你不知道这个知识点也是一件很正常的事情。但是这玩意儿是确确实实存在的,在 CSS 规范中也是明确书写了的:

https://drafts.csswg.org/css2/#containing-block-details

image-20220814222458695

并且,如果你不了解它的运作机制,有时就会出现一些你认为的莫名其妙的现象。

那么,这个包含块究竟说了什么内容呢?

说起来也简单,就是元素的尺寸和位置,会受它的包含块所影响。对于一些属性,例如 width, height, padding, margin,绝对定位元素的偏移值(比如 position 被设置为 absolute 或 fixed),当我们对其赋予百分比值时,这些值的计算值,就是通过元素的包含块计算得来。

来吧,少年,让我们从最简单的 case 开始看。

image-20220814223152726

<body>
  <div class="container">
    <div class="item"></div>
  </div>
</body>
.container{
  width: 500px;
  height: 300px;
  background-color: skyblue;
}
.item{
  width: 50%;
  height: 50%;
  background-color: red;
}

请仔细阅读上面的代码,然后你认为 div.item 这个盒子的宽高是多少?

image-20220814223451349

相信你能够很自信的回答这个简单的问题,div.item 盒子的 width 为 250px,height 为 150px。

这个答案确实是没有问题的,但是如果我追问你是怎么得到这个答案的,我猜不了解包含块的你大概率会说,因为它的父元素 div.container 的 width 为 500px,50% 就是 250px,height 为 300px,因此 50% 就是 150px。

这个答案实际上是不准确的。正确的答案应该是,div.item 的宽高是根据它的包含块来计算的,而这里包含块的大小,正是这个元素最近的祖先块元素的内容区。

因此正如我前面所说,很多时候你都感受不到包含块的存在。

包含块分为两种,一种是根元素(HTML 元素)所在的包含块,被称之为初始包含块(initial containing block)。对于浏览器而言,初始包含块的的大小等于视口 viewport 的大小,基点在画布的原点(视口左上角)。它是作为元素绝对定位和固定定位的参照物。

另外一种是对于非根元素,对于非根元素的包含块判定就有几种不同的情况了。大致可以分为如下几种:

  • 如果元素的 positiion 是 relative 或 static ,那么包含块由离它最近的块容器(block container)的内容区域(content area)的边缘建立。
  • 如果 position 属性是 fixed,那么包含块由视口建立。
  • 如果元素使用了 absolute 定位,则包含块由它的最近的 position 的值不是 static (也就是值为fixed、absolute、relative 或 sticky)的祖先元素的内边距区的边缘组成。

前面两条实际上都还比较好理解,第三条往往是初学者容易比较忽视的,我们来看一个示例:

<body>
    <div class="container">
      <div class="item">
        <div class="item2"></div>
      </div>
    </div>
  </body>
.container {
  width: 500px;
  height: 300px;
  background-color: skyblue;
  position: relative;
}
.item {
  width: 300px;
  height: 150px;
  border: 5px solid;
  margin-left: 100px;
}
.item2 {
  width: 100px;
  height: 100px;
  background-color: red;
  position: absolute;
  left: 10px;
  top: 10px;
}

首先阅读上面的代码,然后你能在脑海里面想出其大致的样子么?或者用笔和纸画一下也行。

公布正确答案:

image-20220814233548188

怎么样?有没有和你所想象的对上?

其实原因也非常简单,根据上面的第三条规则,对于 div.item2 来讲,它的包含块应该是 div.container,而非 div.item。

如果你能把上面非根元素的包含块判定规则掌握,那么关于包含块的知识你就已经掌握 80% 了。

实际上对于非根元素来讲,包含块还有一种可能,那就是如果 position 属性是 absolute 或 fixed,包含块也可能是由满足以下条件的最近父级元素的内边距区的边缘组成的:

  • transform 或 perspective 的值不是 none
  • will-change 的值是 transform 或 perspective
  • filter 的值不是 none 或 will-change 的值是 filter(只在 Firefox 下生效).
  • contain 的值是 paint (例如: contain: paint;)

我们还是来看一个示例:

<body>
  <div class="container">
    <div class="item">
      <div class="item2"></div>
    </div>
  </div>
</body>
.container {
  width: 500px;
  height: 300px;
  background-color: skyblue;
  position: relative;
}
.item {
  width: 300px;
  height: 150px;
  border: 5px solid;
  margin-left: 100px;
  transform: rotate(0deg); /* 新增代码 */
}
.item2 {
  width: 100px;
  height: 100px;
  background-color: red;
  position: absolute;
  left: 10px;
  top: 10px;
}

我们对于上面的代码只新增了一条声明,那就是 transform: rotate(0deg),此时的渲染效果却发生了改变,如下图所示:

image-20220814234347149

可以看到,此时对于 div.item2 来讲,包含块就变成了 div.item。

好了,到这里,关于包含块的知识就基本讲完了。

image-20220814234654914

我们再把 CSS 规范中所举的例子来看一下。

<html>
  <head>
    <title>Illustration of containing blocks</title>
  </head>
  <body id="body">
    <div id="div1">
      <p id="p1">This is text in the first paragraph...</p>
      <p id="p2">
        This is text
        <em id="em1">
          in the
          <strong id="strong1">second</strong>
          paragraph.
        </em>
      </p>
    </div>
  </body>
</html>

上面是一段简单的 HTML 代码,在没有添加任何 CSS 代码的情况下,你能说出各自的包含块么?

对应的结果如下:

元素包含块
htmlinitial C.B. (UA-dependent)
bodyhtml
div1body
p1div1
p2div1
em1p2
strong1p2

首先 HTML 作为根元素,对应的包含块就是前面我们所说的初始包含块,而对于 body 而言,这是一个 static 定位的元素,因此该元素的包含块参照第一条为 html,以此类推 div1、p1、p2 以及 em1 的包含块也都是它们的父元素。

不过 strong1 比较例外,它的包含块确实 p2,而非 em1。为什么会这样?建议你再把非根元素的第一条规则读一下:

  • 如果元素的 positiion 是 relative 或 static ,那么包含块由离它最近的块容器(block container)的内容区域(content area)的边缘建立。

没错,因为 em1 不是块容器,而包含块是离它最近的块容器的内容区域,所以是 p2。

接下来添加如下的 CSS:

#div1 { 
  position: absolute; 
  left: 50px; top: 50px 
}

上面的代码我们对 div1 进行了定位,那么此时的包含块会发生变化么?你可以先在自己思考一下。

答案如下:

元素包含块
htmlinitial C.B. (UA-dependent)
bodyhtml
div1initial C.B. (UA-dependent)
p1div1
p2div1
em1p2
strong1p2

可以看到,这里 div1 的包含块就发生了变化,变为了初始包含块。这里你可以参考前文中的这两句话:

  • 初始包含块(initial containing block)。对于浏览器而言,初始包含块的的大小等于视口 viewport 的大小,基点在画布的原点(视口左上角)。它是作为元素绝对定位和固定定位的参照物。
  • 如果元素使用了 absolute 定位,则包含块由它的最近的 position 的值不是 static (也就是值为fixed、absolute、relative 或 sticky)的祖先元素的内边距区的边缘组成。

是不是一下子就理解了。没错,因为我们对 div1 进行了定位,因此它会应用非根元素包含块计算规则的第三条规则,寻找离它最近的 position 的值不是 static 的祖先元素,不过显然 body 的定位方式为 static,因此 div1 的包含块最终就变成了初始包含块。

接下来我们继续修改我们的 CSS:

#div1 { 
  position: absolute; 
  left: 50px; 
  top: 50px 
}
#em1  { 
  position: absolute; 
  left: 100px; 
  top: 100px 
}

这里我们对 em1 同样进行了 absolute 绝对定位,你想一想会有什么样的变化?

没错,聪明的你大概应该知道,em1 的包含块不再是 p2,而变成了 div1,而 strong1 的包含块也不再是 p2 了,而是变成了 em1。

如下表所示:

元素包含块
htmlinitial C.B. (UA-dependent)
bodyhtml
div1initial C.B. (UA-dependent)
p1div1
p2div1
em1div1(因为定位了,参阅非根元素包含块确定规则的第三条)
strong1em1(因为 em1 变为了块容器,参阅非根元素包含块确定规则的第一条)

好了,这就是 CSS 规范中所举的例子。如果你全都能看明白,以后你还能跟别人说你是看过这一块知识对应的 CSS 规范的人。

image-20220815093518833

另外,关于包含块的知识,在 MDN 上除了解说了什么是包含块以外,也举出了很多简单易懂的示例。

具体你可以移步到:https://developer.mozilla.org/zh-CN/docs/Web/CSS/Containing_block

好了,这就是有关包含块的所有内容了,你学会了么?-)


-EOF-

]]>
0 http://lmwa.cn/archives/861.html#comments http://lmwa.cn/feed/
CSS 属性计算过程 http://lmwa.cn/archives/860.html http://lmwa.cn/archives/860.html Thu, 27 Apr 2023 13:35:10 +0800 田小檬 CSS 属性计算过程

你是否了解 CSS 的属性计算过程呢?

有的同学可能会讲,CSS属性我倒是知道,例如:

p{
  color : red;
}

上面的 CSS 代码中,p 是元素选择器,color 就是其中的一个 CSS 属性。

但是要说 CSS 属性的计算过程,还真的不是很清楚。

没关系,通过此篇文章,能够让你彻底明白什么是 CSS 属性的计算流程。

首先,不知道你有没有考虑过这样的一个问题,假设在 HTML 中有这么一段代码:

<body>
  <h1>这是一个h1标题</h1>
</body>

上面的代码也非常简单,就是在 body 中有一个 h1 标题而已,该 h1 标题呈现出来的外观是如下:

image-20220813140724136

目前我们没有设置该 h1 的任何样式,但是却能看到该 h1 有一定的默认样式,例如有默认的字体大小、默认的颜色。

那么问题来了,我们这个 h1 元素上面除了有默认字体大小、默认颜色等属性以外,究竟还有哪些属性呢?

image-20220815094215982

答案是该元素上面会有 CSS 所有的属性。你可以打开浏览器的开发者面板,选择【元素】,切换到【计算样式】,之后勾选【全部显示】,此时你就能看到在此 h1 上面所有 CSS 属性对应的值。

image-20220813141516153

换句话说,我们所书写的任何一个 HTML 元素,实际上都有完整的一整套 CSS 样式。这一点往往是让初学者比较意外的,因为我们平时在书写 CSS 样式时,往往只会书写必要的部分,例如前面的:

p{
  color : red;
}

这往往会给我们造成一种错觉,认为该 p 元素上面就只有 color 属性。而真实的情况确是,任何一个 HTML 元素,都有一套完整的 CSS 样式,只不过你没有书写的样式,大概率可能会使用其默认值。例如上图中 h1 一个样式都没有设置,全部都用的默认值。

但是注意,我这里强调的是“大概率可能”,难道还有我们“没有设置值,但是不使用默认值”的情况么?

image-20220815094458940

嗯,确实有的,所以我才强调你要了解“CSS 属性的计算过程”。

总的来讲,属性值的计算过程,分为如下这么 4 个步骤:

  • 确定声明值
  • 层叠冲突
  • 使用继承
  • 使用默认值

确定声明值

首先第一步,是确定声明值。所谓声明值就是作者自己所书写的 CSS 样式,例如前面的:

p{
  color : red;
}

这里我们声明了 p 元素为红色,那么就会应用此属性设置。

当然,除了作者样式表,一般浏览器还会存在“用户代理样式表”,简单来讲就是浏览器内置了一套样式表。

image-20220813143500066

在上面的示例中,作者样式表中设置了 color 属性,而用户代理样式表(浏览器提供的样式表)中设置了诸如 display、margin-block-start、margin-block-end、margin-inline-start、margin-inline-end 等属性对应的值。

这些值目前来讲也没有什么冲突,因此最终就会应用这些属性值。

层叠冲突

在确定声明值时,可能出现一种情况,那就是声明的样式规则发生了冲突。

此时会进入解决层叠冲突的流程。而这一步又可以细分为下面这三个步骤:

  • 比较源的重要性
  • 比较优先级
  • 比较次序

来来来,我们一步一步来看。

比较源的重要性

当不同的 CSS 样式来源拥有相同的声明时,此时就会根据样式表来源的重要性来确定应用哪一条样式规则。

那么问题来了,咱们的样式表的源究竟有几种呢?

image-20220823180047075

整体来讲有三种来源:

  • 浏览器会有一个基本的样式表来给任何网页设置默认样式。这些样式统称用户代理样式
  • 网页的作者可以定义文档的样式,这是最常见的样式表,称之为页面作者样式
  • 浏览器的用户,可以使用自定义样式表定制使用体验,称之为用户样式

对应的重要性顺序依次为:页面作者样式 > 用户样式 > 用户代理样式

更详细的来源重要性比较,可以参阅 MDNhttps://developer.mozilla.org/zh-CN/docs/Web/CSS/Cascade

我们来看一个示例。

例如现在有页面作者样式表用户代理样式表中存在属性的冲突,那么会以作者样式表优先。

p{
  color : red;
  display: inline-block;
}

image-20220813144222152

可以明显的看到,作者样式表和用户代理样式表中同时存在的 display 属性的设置,最终作者样式表干掉了用户代理样式表中冲突的属性。这就是第一步,根据不同源的重要性来决定应用哪一个源的样式。

比较优先级

那么接下来,如果是在在同一个源中有样式声明冲突怎么办呢?此时就会进行样式声明的优先级比较。

例如:

<div class="test">
  <h1>test</h1>
</div>
.test h1{
  font-size: 50px;
}

h1 {
  font-size: 20px;
}

在上面的代码中,同属于页面作者样式,源的重要性是相同的,此时会以选择器的权重来比较重要性。

很明显,上面的选择器的权重要大于下面的选择器,因此最终标题呈现为 50px

image-20210916151546500

可以看到,落败的作者样式在 Elements>Styles 中会被划掉。

有关选择器权重的计算方式,不清楚的同学,可以进入此传送门:https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity

比较次序

经历了上面两个步骤,大多数的样式声明能够被确定下来。但是还剩下最后一种情况,那就是样式声明既是同源,权重也相同。

此时就会进入第三个步骤,比较样式声明的次序。

举个例子:

h1 {
  font-size: 50px;
}

h1 {
  font-size: 20px;
}

在上面的代码中,同样都是页面作者样式选择器的权重也相同,此时位于下面的样式声明会层叠掉上面的那一条样式声明,最终会应用 20px 这一条属性值。

image-20220823183928330

至此,样式声明中存在冲突的所有情况,就全部被解决了。

使用继承

层叠冲突这一步完成后,解决了相同元素被声明了多条样式规则究竟应用哪一条样式规则的问题。

那么如果没有声明的属性呢?此时就使用默认值么?

No、No、No,别急,此时还有第三个步骤,那就是使用继承而来的值。

例如:

<div>
  <p>Lorem ipsum dolor sit amet.</p>
</div>
div {
  color: red;
}

在上面的代码中,我们针对 div 设置了 color 属性值为红色,而针对 p 元素我们没有声明任何的属性,但是由于 color 是可以继承的,因此 p 元素从最近的 div 身上继承到了 color 属性的值。

image-20220813145102293

这里有两个点需要同学们注意一下。

首先第一个是我强调了是最近的 div 元素,看下面的例子:

<div class="test">
  <div>
    <p>Lorem ipsum dolor sit amet.</p>
  </div>
</div>
div {
  color: red;
}
.test{
  color: blue;
}

image-20220813145652726

因为这里并不涉及到选中 p 元素声明 color 值,而是从父元素上面继承到 color 对应的值,因此这里是谁近就听谁的,初学者往往会产生混淆,又去比较权重,但是这里根本不会涉及到权重比较,因为压根儿就没有选中到 p 元素。

第二个就是哪些属性能够继承?

关于这一点的话,大家可以在 MDN 上面很轻松的查阅到。例如我们以 text-align 为例,如下图所示:

image-20220813150147885

使用默认值

好了,目前走到这一步,如果属性值都还不能确定下来,那么就只能是使用默认值了。

如下图所示:

image-20220813150824752

前面我们也说过,一个 HTML 元素要在浏览器中渲染出来,必须具备所有的 CSS 属性值,但是绝大部分我们是不会去设置的,用户代理样式表里面也不会去设置,也无法从继承拿到,因此最终都是用默认值。

好了,这就是关于 CSS 属性计算过程的所有知识了。

image-20220814234654914

一道面试题

好了,学习了今天的内容,让我来用一道面试题测试测试大家的理解程度。

下面的代码,最终渲染出来的效果,a 元素是什么颜色?p 元素又是什么颜色?

<div>
  <a href="">test</a>
  <p>test</p>
</div>
div {
  color: red;
}

大家能说出为什么会呈现这样的结果么?

解答如下:

image-20220813151941113

实际上原因很简单,因为 a 元素在用户代理样式表中已经设置了 color 属性对应的值,因此会应用此声明值。而在 p 元素中无论是作者样式表还是用户代理样式表,都没有对此属性进行声明,然而由于 color 属性是可以继承的,因此最终 p 元素的 color 属性值通过继承来自于父元素。

你答对了么?-)


-EOF-

]]>
0 http://lmwa.cn/archives/860.html#comments http://lmwa.cn/feed/
浏览器事件循环 http://lmwa.cn/archives/850.html http://lmwa.cn/archives/850.html Thu, 27 Apr 2023 13:34:25 +0800 田小檬 事件循环

浏览器的进程模型

何为进程?

程序运行需要有它自己专属的内存空间,可以把这块内存空间简单的理解为进程

每个应用至少有一个进程,进程之间相互独立,即使要通信,也需要双方同意。

何为线程?

有了进程后,就可以运行程序的代码了。

运行代码的「人」称之为「线程」。

一个进程至少有一个线程,所以在进程开启后会自动创建一个线程来运行代码,该线程称之为主线程。

如果程序需要同时执行多块代码,主线程就会启动更多的线程来执行代码,所以一个进程中可以包含多个线程。

image-20220809210859457

浏览器有哪些进程和线程?

浏览器是一个多进程多线程的应用程序

浏览器内部工作极其复杂。

为了避免相互影响,为了减少连环崩溃的几率,当启动浏览器后,它会自动启动多个进程。

image-20220809213152371

可以在浏览器的任务管理器中查看当前的所有进程

其中,最主要的进程有:

  1. 浏览器进程

    主要负责界面显示、用户交互、子进程管理等。浏览器进程内部会启动多个线程处理不同的任务。

  2. 网络进程

    负责加载网络资源。网络进程内部会启动多个线程来处理不同的网络任务。

  3. 渲染进程(本节课重点讲解的进程)

    渲染进程启动后,会开启一个渲染主线程,主线程负责执行 HTML、CSS、JS 代码。

    默认情况下,浏览器会为每个标签页开启一个新的渲染进程,以保证不同的标签页之间不相互影响。

    将来该默认模式可能会有所改变,有兴趣的同学可参见chrome官方说明文档

渲染主线程是如何工作的?

渲染主线程是浏览器中最繁忙的线程,需要它处理的任务包括但不限于:

  • 解析 HTML
  • 解析 CSS
  • 计算样式
  • 布局
  • 处理图层
  • 每秒把页面画 60 次
  • 执行全局 JS 代码
  • 执行事件处理函数
  • 执行计时器的回调函数
  • ......
思考题:为什么渲染进程不适用多个线程来处理这些事情?

要处理这么多的任务,主线程遇到了一个前所未有的难题:如何调度任务?

比如:

  • 我正在执行一个 JS 函数,执行到一半的时候用户点击了按钮,我该立即去执行点击事件的处理函数吗?
  • 我正在执行一个 JS 函数,执行到一半的时候某个计时器到达了时间,我该立即去执行它的回调吗?
  • 浏览器进程通知我“用户点击了按钮”,与此同时,某个计时器也到达了时间,我应该处理哪一个呢?
  • ......

渲染主线程想出了一个绝妙的主意来处理这个问题:排队

image-20220809223027806

  1. 在最开始的时候,渲染主线程会进入一个无限循环
  2. 每一次循环会检查消息队列中是否有任务存在。如果有,就取出第一个任务执行,执行完一个后进入下一次循环;如果没有,则进入休眠状态。
  3. 其他所有线程(包括其他进程的线程)可以随时向消息队列添加任务。新任务会加到消息队列的末尾。在添加新任务时,如果主线程是休眠状态,则会将其唤醒以继续循环拿取任务

这样一来,就可以让每个任务有条不紊的、持续的进行下去了。

整个过程,被称之为事件循环(消息循环)

若干解释

何为异步?

代码在执行过程中,会遇到一些无法立即处理的任务,比如:

  • 计时完成后需要执行的任务 —— setTimeoutsetInterval
  • 网络通信完成后需要执行的任务 -- XHRFetch
  • 用户操作后需要执行的任务 -- addEventListener

如果让渲染主线程等待这些任务的时机达到,就会导致主线程长期处于「阻塞」的状态,从而导致浏览器「卡死」

image-20220810104344296

渲染主线程承担着极其重要的工作,无论如何都不能阻塞!

因此,浏览器选择异步来解决这个问题

image-20220810104858857

使用异步的方式,渲染主线程永不阻塞

面试题:如何理解 JS 的异步?

参考答案:

JS是一门单线程的语言,这是因为它运行在浏览器的渲染主线程中,而渲染主线程只有一个。

而渲染主线程承担着诸多的工作,渲染页面、执行 JS 都在其中运行。

如果使用同步的方式,就极有可能导致主线程产生阻塞,从而导致消息队列中的很多其他任务无法得到执行。这样一来,一方面会导致繁忙的主线程白白的消耗时间,另一方面导致页面无法及时更新,给用户造成卡死现象。

所以浏览器采用异步的方式来避免。具体做法是当某些任务发生时,比如计时器、网络、事件监听,主线程将任务交给其他线程去处理,自身立即结束任务的执行,转而执行后续代码。当其他线程完成时,将事先传递的回调函数包装成任务,加入到消息队列的末尾排队,等待主线程调度执行。

在这种异步模式下,浏览器永不阻塞,从而最大限度的保证了单线程的流畅运行。

JS为何会阻碍渲染?

先看代码

<h1>Mr.Yuan is awesome!</h1>
<button>change</button>
<script>
  var h1 = document.querySelector('h1');
  var btn = document.querySelector('button');

  // 死循环指定的时间
  function delay(duration) {
    var start = Date.now();
    while (Date.now() - start < duration) {}
  }

  btn.onclick = function () {
    h1.textContent = '袁老师很帅!';
    delay(3000);
  };
</script>

点击按钮后,会发生什么呢?

<见具体演示>

任务有优先级吗?

任务没有优先级,在消息队列中先进先出

消息队列是有优先级的

根据 W3C 的最新解释:

随着浏览器的复杂度急剧提升,W3C 不再使用宏队列的说法

在目前 chrome 的实现中,至少包含了下面的队列:

  • 延时队列:用于存放计时器到达后的回调任务,优先级「中」
  • 交互队列:用于存放用户操作后产生的事件处理任务,优先级「高」
  • 微队列:用户存放需要最快执行的任务,优先级「最高」

添加任务到微队列的主要方式主要是使用 Promise、MutationObserver

例如:

// 立即把一个函数添加到微队列
Promise.resolve().then(函数)

浏览器还有很多其他的队列,由于和我们开发关系不大,不作考虑

面试题:阐述一下 JS 的事件循环

参考答案:

事件循环又叫做消息循环,是浏览器渲染主线程的工作方式。

在 Chrome 的源码中,它开启一个不会结束的 for 循环,每次循环从消息队列中取出第一个任务执行,而其他线程只需要在合适的时候将任务加入到队列末尾即可。

过去把消息队列简单分为宏队列和微队列,这种说法目前已无法满足复杂的浏览器环境,取而代之的是一种更加灵活多变的处理方式。

根据 W3C 官方的解释,每个任务有不同的类型,同类型的任务必须在同一个队列,不同的任务可以属于不同的队列。不同任务队列有不同的优先级,在一次事件循环中,由浏览器自行决定取哪一个队列的任务。但浏览器必须有一个微队列,微队列的任务一定具有最高的优先级,必须优先调度执行。

面试题:JS 中的计时器能做到精确计时吗?为什么?

参考答案:

不行,因为:

  1. 计算机硬件没有原子钟,无法做到精确计时
  2. 操作系统的计时函数本身就有少量偏差,由于 JS 的计时器最终调用的是操作系统的函数,也就携带了这些偏差
  3. 按照 W3C 的标准,浏览器实现计时器时,如果嵌套层级超过 5 层,则会带有 4 毫秒的最少时间,这样在计时时间少于 4 毫秒时又带来了偏差
  4. 受事件循环的影响,计时器的回调函数只能在主线程空闲时运行,因此又带来了偏差
]]>
0 http://lmwa.cn/archives/850.html#comments http://lmwa.cn/feed/
域名购买解析说明 http://lmwa.cn/archives/842.html http://lmwa.cn/archives/842.html Fri, 07 Apr 2023 22:42:00 +0800 田小檬 介绍

接上篇文章来教域名的购买和解析(解析应该在购买服务器后进行,因为此处在一个平台操作所以一次性录制)

视频

[vplayer url="https://lmwa.cn/usr/uploads/2023/04/2655994611.mp4" pic="" /]

平台

可选如:阿里云、腾讯云、百度云....

本次选取阿里云

阿里云

进入阿里云官网搜索域名,选择并进入

选取域名

注册域名首先需要想好域名,例如:田小檬-tianxiaomeng.com

与产品或个人有相关性更容易记住

如果没有想要的可以换一下,这里购买 xiaoword.top 作为演示

选择后进行付款购买即可,这里因为我已经有域名,我就不做购买继续做后续的操作。大家购买后会跳转到域名控制台。

域名解析

点击解析设置进行域名解析,添加解析记录,如果指向IP就使用默认即可,指向域名就切换为CNAME就可以了。

主机记录值,在阿里云有个小问号会有相应的提示,我这里简单说一下。

主机记录值:例如我的域名是lmwa.cn。记录值填写www,那么代表我可以通过www.lmwa.cn进行访问。同理我解析blog那么可以是使用blog.lmwa.cn

记录值填写IP地址(或者填写指向目标域名-注意不是自己的域名)即可

至此阿里云域名购买及解析教程已经结束

]]>
1 http://lmwa.cn/archives/842.html#comments http://lmwa.cn/feed/
宝塔自动检测状态,并重启PHP、redis、MySQL、Nginx服务 http://lmwa.cn/archives/835.html http://lmwa.cn/archives/835.html Thu, 09 Feb 2023 21:09:00 +0800 田小檬 定时任务

使用宝塔定时任务执行下方代码即可实现定时检测

实例

PHP监控(PHP56举例)

#!/bin/bash
# Linux监控PHP服务,关闭就自动重启
pgrep -x php-fpm &> /dev/null
if [ $? -ne 0 ];then
/etc/init.d/php-fpm-56 restart
echo "监控到php56已停止,已执行重启计划,时间: `date "+%Y-%m-%d %H:%M:%S"` " >> /www/php_jiankong.log  
fi

redis监控

#!/bin/bash
# Linux监控redis服务,关闭就自动重启
pgrep -x redis &> /dev/null
if [ $? -ne 0 ];then
/etc/init.d/redis start
fi

MySQL监控

#!/bin/bash
# Linux监控MySQL服务,关闭就自动重启
pgrep -x mysqld &> /dev/null
if [ $? -ne 0 ];then
bash /www/server/panel/script/rememory.sh   
/etc/init.d/mysqld start    
echo "监控到MySQL已停止,已执行重启计划,时间: `date "+%Y-%m-%d %H:%M:%S"` " >> /www/mysql_jiankong.log  
fi

Nginx监控

#!/bin/bash
# Linux监控Nginx服务,关闭就自动重启
nginx_procnum=`ps -ef|grep "nginx"|grep -v grep|wc -l`
 
if [ $nginx_procnum -eq 0 ]
then
    echo $(date) "Success,Nginx重启成功!" >> /var/log/nginxmonitor.log
    /etc/init.d/nginx start
else 
    sleep 5
    echo "Nginx正常运行中..."
fi
]]>
0 http://lmwa.cn/archives/835.html#comments http://lmwa.cn/feed/