外边距合并 Collapsing Margins

两个块级元素垂直外边距会合并的情况是W3C CSS2 规范定义之初就存在的,并不能说是 bug ,而是 future。

前端开发者往往不了解 CSS属性的由来和设计的本意,在实际使用中产生副作用。

下面几种常见的情况,会出现外边距合并:

相邻元素

相邻元素(包括空内容)之间外边距重叠是经常出现的情况,重叠时一般按照最大值为相邻距离。

迫不得已可以在它们之间加入 <br > 空元素来避免这种情况

嵌套元素

嵌套其中一个元素也会有这个问题,但仅仅发生在,嵌套元素没有固定的高度、没有 padding 和 border 的情况。

同样,创建 BFC 块格式化上下文 可以避免两个相邻的块级元素外边距合并的情况发生,但引入 BFC 会破坏原有 HTML 结构,并产生一些复杂的问题。

开发者应该避免这种情况发生,在整体布局上少用外边距。

参考

Everything You Need To Know About CSS Margins

What You Should Know About Collapsing Margins

W3C - 8.3.1 Collapsing margins

重翻译:css2.1规范 8.3.1节(margin边距折叠)

The Rules of Margin Collapse

块格式化上下文 - Web 开发者指南 | MDN

[CSS 外边距(margin)重叠及防止方法
边距重叠解决方案(BFC)](https://www.cnblogs.com/webdom/p/10643176.html)

往年目标

  • 100天慢跑 × 仅完成了 56天,有十几天没有带小米手环出门,工作上有加班的话整个月都懒下来没有出门跑步
  • 每月博文 × 只完成了 5篇,三篇总结了工作上的事件案例,React 编写小程序,和混合式应用 从理论到实践,尝试编写了表单应用。
  • 读完 5本书 × 只读完了3本,分别是《软件困局》《构建之法》《思维改变生活》
  • 增重到 110斤 × 没有做到!实在是有点难,应该分季度执行,每月增重1斤,再做后续计划
  • 参与一次计算机等级考试 √ 参加了软考中级,网络工程师,因为是裸考没有准备,考了个不及格
  • 规划一次国内旅行 √ 圣诞节跟钢琴妹妹到珠海长隆玩
  • 探索学习测试、后端技术 √。写了文章《小试牛刀 使用 Laravel 编写表单应用》

疫情

今年几乎有半年笼罩在疫情之下,春节假期(7天)从1月24日延长到了3月。 2月6日在疫情比较严重的时候回到广州,3月1日才陆续轮班上岗。

经历了一个月自己买菜煮饭的日子,每天想着吃啥也是挺痛苦,一开始方便面、麦当劳度日,后面吃腻了只能到菜市场买肉买菜,开始做饭。还不小心切到了手指,印象深刻。

工作

公司的多数同事奉行形式主义,任务导向,导致简单的事情变成非常复杂,真正伤害了实际用户,导致用户体验非常差。

唯一办法是多开展思想工作和分享会,从认知上改善这种行为。

读书

在年底的时候读到一本《思维改变生活》,真的让我大开眼界。

编程

更新 2017年编写的项目 filmy 相册,还有些文档没写完,仍未正式发布。

考试

  • 参加 《2020年下半年计算机技术与软件专业技术资格考试》 网络工程师(中级)

电影

由于疫情的关系,今年上映的电影很少

  • 拆弹专家2
  • 信条
  • 1917

新朋友

认识了四位新朋友,玉群、奶茶妹妹、欣怡、还有鸣鸣,大家聊了很多,很大程度上能认识到自己的优点和缺点,以及对方需要什么。

认识女孩子不要太急功近利,太着急只会让自己失望,一旦被冷落之后就会觉得自己一无是处,最好先收拾自信,认清优势。

还是要多认识些朋友,才能知道自己喜欢怎样的女孩子,希望在来年能让自己变得更好,不断努力向前。

存钱

到 12月,存到了计划 10万块的其中 9万,平时吃喝花的有点多,每周都有一两次奶茶,平均每月多花费 200块钱在奶茶上面,有时候还请同事喝。虽然说每月计划存 3500,总是会往存钱的卡扣钱来用,导致存得钱减少了。

7493块钱花在数码产品上

关于定制 2021年目标

2020年的目标完成度不算高。需要定制合适自己的目标, 免得打击积极性。

每季度应该检查目标完成情况,适度调整目标

  • 每月8次或者每周两次跑步
  • 每季度完成一篇较长的博文,或每月一篇工作技术总结
  • 每月增重1斤
  • 每季度一本书,建议两本技术,两本非技术
  • 参加计算机技术与软件专业技术资格考试
  • 规划国内旅行
  • 背前端面试题,参加一次面试
  • 存 3W 块钱

相关文章

道高一尺

疫情呆在家里,发现墙又高了不少, Clone Github 仓库时,速度极慢。

$ git clone [email protected]:docsifyjs/docsify.git
Cloning into 'docsify'...
remote: Enumerating objects: 8846, done.
Receiving objects:   1% (89/8846), 44.01 KiB | 1024 bytes/s
Receiving objects:   2% (177/8846), 68.01 KiB | 2.00 KiB/s

使用 SSH 方式安全性更高,不用输入帐户密码,只需配置 SSH keys 即可

魔高一丈

如果使用 HTTPS 的连接方式,可以很方便设置 http.proxy

git config --global http.proxy http://127.0.0.1:23333
git config --global --unset http.proxy

而 SSH 的连接需要配置 .ssh 目录下的 config 文件

Host github.com
  User git
  Port 22
  Hostname github.com
  IdentityFile "C:\Users\Mather\.ssh\id_rsa"
  ProxyCommand connect -S 127.0.0.1:23333 -a none %h %p
  TCPKeepAlive yes

Host ssh.github.com
  User git
  Port 443
  Hostname ssh.github.com
  IdentityFile "C:\Users\Mather\.ssh\id_rsa"
  ProxyCommand connect -S 127.0.0.1:23333 -a none %h %p
  TCPKeepAlive yes

看到速度已经起飞

$ git clone [email protected]:docsifyjs/docsify.git
Cloning into 'docsify'...
remote: Enumerating objects: 8846, done.
remote: Total 8846 (delta 0), reused 0 (delta 0), pack-reused 8846
Receiving objects: 100% (8846/8846), 16.68 MiB | 2.21 MiB/s, done.
Resolving deltas: 100% (5639/5639), done.

参考文档

SSH Proxy Command -- connect.c - wiki

Windows 下 Git SSH 连接方式配置 Socks 代理

git 设置和取消代理

macOS 给 Git(Github) 设置代理(HTTP/SSH

[toc]

混合式应用

如今手机应用的开发已经从客户端发展到跨平台,原生开发不再是一枝独秀。前端领域也尝试分一杯羹,更有后起的 Flutter 。

HybridApp 混合应用指的是,同时使用网页前端技术和原生技术开发的 App,通常由网页负责部分界面开发和业务逻辑,原生提供给前端调用的函数/接口,两者以 WebView 作为媒介建立通信,既拥有 Web 开发的速度又是,又能拥有强大的原生能力。

典型案例

是什么场景适合到混合式应用?

因为产品前期方案设计存在不足,没有合理地规范录入内容的结构,各种乱七八糟内容以 HTML 的形式保存到数据库中。久而久之,客户端难以解析这些结构,导致内容显示空白,甚至应用崩溃。

在内容管理系统中,文本和多媒体组成的内容,一般会以 HTML 代码的形式存储在数据库中。而客户端应用需要解析 HTML,转换成控件。

为了解决该问题,成本较低的方案是改用 Web 技术来展示这些文本和多媒体内容。

它能解决:

  • 客户端难以解析文本结构的问题。凭借 Web 前端天然的优势,浏览器可直接解析 HTML 代码
  • 发现问题或错误时,可随时修复更新,无需发布新版本客户端,减少因功能失效后的损失。

那么由 Web 实现主要的业务场景,也会存在技术难点:

  • 客户端与 Web 之间函数互相调用问题
  • Web 应用在客户端 WebView 组件的生命周期问题
  • 代码版本维护与发布更新问题
数据影响深远。数据的结构影响到了选取开发方案的结果。Editor.js 给提供了描述内容的方案,将内容描述成简洁的数据,易于清洗、扩展和集成的内容。

原生与 Web 相互调用

Webview 组件提供了功能上相似的 API。Android 平台拥有 JavascriptInterface API,而 iOS 则是 WKWebView API,但两端的调用方式不一致,必定导致一方的取舍。

引入 DSbridge 这个库来保证不同平台与 Web 相互调用功能的统一。以 Android 为例,原生调用前端 API的声明方式:

  • 在 Javascript 中定义 addValue API

    dsBridge.registerAsyn('addValue',function(l,r){
      return l+r;
    })
  • 在 Java 中调用 addValue API

    dwebView.callHandler("addValue",new Object[]{3,4},new OnReturnValue<Integer>(){
      @Override
      public void onValue(Integer retValue) {
          Log.d("jsbridge","call succeed,return value is "+retValue);
      }
    });

更多用例见 DSbridge 中文文档

握手与数据更新

从程序设计角度看,代码运行会有初始化、创建、执行、暂停、结束、销毁等生命周期。将对应的逻辑放在不同的生命周期中运行,才能表现出软件的有序性,易维护性,提高程序的运行效率。

在混合应用中,同样需要生命周期,保证了客户端与 Web 间的调用是有序的。双方传输数据前就必须建立连接,并要使每一方能够确知对方的存在

利用 DSbridge 定义名为 onWebViewCreated 的 API,确知双方都准备好进入下一生命周期。

定义 onWebViewCreated API

Android:

@JavascriptInterface
public void onWebViewCreated(Object msg, CompletionHandler<Boolean> handler) {
    handler.complete(true); //return true value to front end
}

Javascript:

const methodName = "onWebViewCreated";

dsBridge.call(methodName, (res) => {
    if(!!res) { 
        // get true from client
    }
});
定义 notifyWebViewUpdated API

在本文开头,混合式应用主要是解决展示富文本的业务需求。那么第一握手成功后,Web 应用即可主动调用相关 API,进入业务流程。

假设遇到用户信息更新,客户端应该通过事件通知的形式,告知 Web 应用主动获取最新的用户信息,更新内部数据和页面显示内容。

这种情况就像你收到一条包裹短信,其内容告知包裹所在的门店,你要赶在打样前到门店取回包裹

notifyWebViewUpdated 传入:

{
    "name": "user"
}

Android:

JSONObject updateObj = new JSONObject();
try { 
    updateObj.put("name", "user"); 
} catch (JSONException e) {
    e.printStackTrace();
}

dWebView.callHandler("notifyWebViewUpdated", new Object[]{ updateObj }, new OnReturnValue<Boolean>() {
    @Override
    public void onValue(Boolean retValue) { //callback from front end
        showToast(retValue); //retVlue is true
    }
});

Javascript:

dsBridge.register('notifyWebViewUpdated', (e) => {
    if(e.name === 'user') {
        //trigger get user API
    }
    return "true";
})

部署与维护

与多数 Web 应用一样,混合应用的 Web 项目同样需要部署在服务器或分发到 CDN。

版本管理

在混合项目中 Web 应用加载流程与浏览器是一致的,Webview 请求网页服务器,请求资源到本地,再完成渲染。

与传统项目不同的是,Web 应用和客户端版本之间是存在关系的,这种关系影响到部署。是因为混合应用方案未经过迭代,可能存在不明显的缺陷,避免在代码层兼容多种客户端,应采用版本一对一的方式进行部署。

某个Web 应用版本出现缺陷,针对具体版本进行修复并发布,避免了缺陷对其他客户端版本造成影响。这样做的好处是,保证版本正常运行同时,可迅速地对异常版本修复,将异常等级到最低

版本迭代

每个项目都有其独立的版本号,但在混合式项目中,客户端与 Web 应用项目版本号将产生关联,并记录在案,方便我们在项目迭代和方案复盘时,提供有力的数据。

客户端平台客户端版本Web 应用版本备注
Android7.0.01.0.0发布初始版本
iOS7.0.01.0.0-
Android7.0.01.0.1修复小问题,更新小版本号 1.0.1
iOS7.0.11.0.1-
Android7.0.11.1.0跟随客户端功能迭代,更新中版本号 1.1.0
iOS7.0.21.1.0-
iOS7.1.01.3.0跟随客户端功能迭代,更新中版本号 1.3.0

修复混合应用项目 ①

经过修复后的混合应用版本号从 1.0.0 更新至 1.0.1 ,产出的代码对应客户端 7.0.0 7.0.1 版本

更新小版本号时,根据数月内版本活跃量,筛选哪些版本适用本次升级迭代

发布新功能 ②

与客户端相关的功能,更新版本号, 如 1.1.0

并确定各平台客户端版本号 如: Android 7.0.1、 iOS 7.0.2 ,产出的代码对应这些版本即可

User-Agent

字符串 <应用名称>/<软件版本号> 会附在 User Agent 末尾,例如 7.0.0 版本 iPhone 客户端带有标识 iread/7.0.0

Mozilla/5.0 (iPhone; CPU iPhone OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) iread/7.0

浏览器包含平台(iPhone/iPad/Android)标识,因此我们不需要再添加平台信息。

发布与部署

版本管理和迭代章节指出,不同项目版本将会分开部署,意味着每个对应版本的应用都是独立部署的,我们可以将部署内容分散在不同文件夹中,这里使用一个简单的配置展示独立部署效果。

D:\WWW\DEV.WEBSITE     <----- root
├─1.1.x                <----- $path_dir
│      index.html      <----- project entry
│      
├─1.2.x                <----- $path_dir
│      index.html
│      
├─1.3.x                <----- $path_dir
│      index.html
│      
└─default              <----- default version for all
       index.html

配合使用 Nginx map 和虚拟主机的特性,获取 HTTP Header 中的 User-Agent 后将虚拟主机的根目录映射至不同目录。

map $http_user_agent $path_dir {
    ~*iread/7.0.0     "1.1.x";
    ~*iread/7.0.1     "1.1.x";

    ~*iread/7.1.0     "1.2.x";
    ~*iread/7.2.0     "1.3.x";

    default           "default";
}

server {
    listen 80;
    server_name dev.website;
    root "D:/www/dev.website/$path_dir/";

    location / {
        index index.html index.htm index.php;
    }
}

也可以加入平台标识的判断

起因是在 Windows 10 平台下使用 Docker 需要开启 Hyper-V,但需要虚拟化 Windows 7,选择了 VMware Workstation,他们的虚拟化技术是两种不同的类型,导致不兼容的问题。

要使用 VMware Workstation 虚拟化,必须关闭 Windows Hyper-V。要么使用 Hyper-V 来虚拟化 Windows 7

关闭 Hyper-V

  1. 在 Windows 功能中关闭 Hyper-V
  2. services.msc 服务中禁用和停止 Hyper-V 主机服务
  3. 管理员命令行中键入 bcdedit /set hypervisorlaunchtype off
  4. 重启

开启 Hyper-V

  1. 在主板 BIOS 管理面板中开启硬件虚拟化功能
  2. 在 Windows 功能中开启 Hyper-V
  3. 管理员身份打开 PowerShell Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All
  4. 重启

虚拟化技术的不同点

Host OS 操作系统虚拟化和 Bare-Metal “裸机”虚拟化

Host OS 的 Hypervisor 化实现在传统的操作系统中,属于系统虚拟化技术。典型的应用有 VMware Workstation 和 VirtualBox。虚拟化的系统通过 Hypervisor 层级来共享资源。

Bare-Metal 类型的 Hypervisor 化实现无需操作系统,属于全虚拟化和准虚拟化技术。将 Hypervisor 直接部署在硬件上。常见的有 Microsoft 的 Hyper-V。

Hyper-V

Hyper-V 是 Windows 为企业级用户提供的的虚拟化功能,主要解决数据中心和云服务的需求。

VMware

在个人电脑上可以很方便地使用 VMware Workstation,它使用的是运行在操作系统之上的虚拟化方案(Host OS)。

由于它依赖于操作系统,当宿主系统崩溃或出现安全问题时,被虚拟出来的系统也会遭到影响。

无论哪种虚拟化技术,它都需要独占硬件虚拟化,意味着你不能在一台硬件上启用两种不同的虚拟化技术。

Intel VT/AMD-V 是硬件厂商 Intel 和 AMD 在其处理器中加入的硬件辅助虚拟化功能。

参考

在 Windows 10 上安装 Hyper-V

Win10系统下提示VMware与Device/Credential Guard不兼容如何解决

Hyper-V vs. VMware: Which Is Best?

Hypervisor虚拟化概述