2022寒假字节跳动前端训练营笔记

已经完成的部分

  • Day02 如何写好JS - 月影
  • Day04-2 前端动画实现 - 蒋翔
  • Day08-1 TypeScript入门 - 林皇
  • Day09-2 前端设计模式应用 - 吴立宁
  • Day04-1 响应式系统与React - 牛岳
  • Day06-2 Web开发的安全之旅 - 刘宇晨
  • Day01-1 前端与HTML - 韩广军
  • Day01-2 理解CSS - 韩广军
  • Day01-3 深入CSS - 韩广军
  • Day10-1 Web多媒体入门 - 刘立国
  • Day07 构建webpack知识体系 - 范文杰
  • Day05-1 HTTP实用指南 - 杨超男
  • Day03-2 前端调试知识 - 秃头披风侠
  • Day06-1 初识WebGL - 月影
  • Day05-2 Node.js与前端开发实战 - 欧阳亚东
  • Day09-1 小程序技术全解 - 张晓伟
  • Day08-2 小游戏开发 - ycaptain
  • Day03-1 Web标准与前端开发 - 李松峰
  • Day10-2 数据可视化基础 - 何菲菲

Day0 字节笔试

  • 不定项选择:JS基础, 事件冒泡等内容

  • 编程题

    判断输入的字符串是不是4的整数幂

    标准解法当然是位运算, 我比较懒, 读取数字, 求log(t)/log(4)是不是整数, 其实应该用algorithm库中更快速的的log_2(), 无奈在牛客的代码提示中没找到这个函数

    输入一个字符串, 输出全排列

    标准解法当时是搜索/康托, 我比较懒, 直接调std::next_permutation(begin, end)

Day1-1 前端与HTML - 韩广军

前端职责:图形界面下的人机交互问题

前端技术栈

  • HTML:结构
  • CSS:样式
  • JS:交互
  • http协议:传输

前端需要关注问题

  • 美观
  • 功能
  • 无障碍(不只是对于残障人士, 还包括一些困难的场景, 例如: 在抖动的环境下按一个小按钮)
  • 安全
  • 兼容性
  • 用户体验

前端可以实现的功能

  • 服务端(Node.JS)
  • 用户端(Election)
  • 3D(webGL)
  • 语音交互(webRTC)
  • 代码执行(WebAssembly)

HTML是什么

  • HyperText: 图片, 标签, 音视频
  • Markup Language: 标签, 属性键值

HTML语法

  • 不区分大小写(推荐小写)
  • 空标签可以不闭合或者加/闭合(<input>,<img />)
  • 属性值用双引号闭合
  • 部分标签属性值可以省略(required, readonly…)

浏览器拿到HTML后会将嵌套关系转化为一个DOM树

HTML标签复习
  • <!doctype html>: 指明HTML版本(不写的话浏览器按照老版本标准(兼容模式)执行)

  • <html>根标签

  • <head>存放元数据

  • <body>需要呈现的内容

  • 标题: <h1/>-<h6/>默认样式从大到小

  • 列表:

    • 有序列表

      <ol>
        <li>1</li>
        <li>2</li>
        <li>3</li>
      </ol>
      
    • 无序列表

      <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
      </ul>
      
    • 定义列表(注意,支持多对多)

      <dl>
        <dt>导演</dt>
        <dd>陈凯歌</dd>
        <dt>主演</dt>
        <dd>张国荣</dd>
        <dd>张丰毅</dd>
        <dt>出品</dt>
        <dt>宣发</dt>
        <dd>巩俐</dd>
      </dl>
      
      导演
      陈凯歌
      主演
      张国荣
      张丰毅
      出品 宣发
      巩俐
  • 链接:<a href="" target="" />最重要的两个属性是hreftarget

  • 多媒体: <img/>,<audio></auduio>,<video></video>

    • alt: 不被加载的时候的替换内容(不被加载的情况包括: 加载失败, 用户开启省流模式)
    • 音视频自动播放属性:
      • autoplay: 如果用户之前访问过这个域名, 还手动播放该域名下的视频过才会自动播放
      • controls: 规定浏览器应该为视频提供播放控件
      • muted: 静音播放
    • 音视频自动播放属性:
      • 视频静音(通过加上 muted 属性)或音量设置为 0
      • 用户与网站进行了交互(通过点击, 敲击, 按键等)
      • 站点已被列入浏览器白名单;
      • 浏览器确定用户频繁使用媒体, 自动加入白名单或者通过首选项或其他用户界面功能手动发生
      • 自动播放功能策略授予了<iframe>及其文档自动播放支持.
      • 用户已将该站点添加到其移动设备的主屏幕或在桌面设备上安装了 PWA.
  • 输入:

    • <input placeholder="" type=""/>

      • type="range"

      • type= "number"

      • type= "date"

      • type= "checkbox" 通过name进行分组互斥, 通过<label for="">打标记

      • type= "radio" 通过name进行分组互斥, 通过<label for="">打标记

      • type= "select"下拉框

        Apple Orange Pineapple Banana
        <select>
            <option>Apple</option>
            <option>Orange</option>
            <option>Pineapple</option>
            <option>Banana</option>
        </select>
        
    • 可选下拉列表输入框(可以选择,也可以随便输入内容,如果输入的是选项的前缀会自动跳出提示)

      <input list="ice-cream-flavors" id="ice-cream-choice" name="ice-cream-choice" />
      
      <datalist id="ice-cream-flavors">
          <option value="Chocolate">
          <option value="Coconut">
          <option value="Mint">
          <option value="Strawberry">
          <option value="Vanilla">
      </datalist>
      
    • 多行输入框: <textarea></textarea>

  • 文本标签

    • 引用
      • 块级别引用: <blockquote cite=""></blockquote>
      • 短引用: <cite></cite>(一般写一些作品名)
      • 引用之前页面中的内容<q></q>
    • 代码
      • 单行代码<code></code>
      • 多行代码<pre><code></code></pre>
    • 强调
      • <strong></strong>表示语意上重要 紧急
      • <em></em>语气上的重读
  • 版块划分

    • 页头
      • header
      • nav
    • 主体
      • main
    • 与内容相关但不属于主体内容的(信息推荐, 广告): aside
    • 页尾: <footer></footer>
语义化的作用:
  • 开发者:方便修改与维护
  • 浏览器:展示页面
  • 搜索引擎:提取关键词, 排序
  • 屏幕阅读器
跨域问题

<script>, <a>是完全支持跨域的, 在Chrome97中https页面不能跨域引用来自http页面的图片等资源

自定义标签

webComponment是一套不同的技术, 允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的web应用中使用它们.

  • 目的: 实现复杂HTML组件的复用
  • 技术组成:
    • Custom elements(自定义元素)
    • Shadow DOM(影子DOM)
    • HTML templates(HTML模板)

shadow DOM

shadow DOM允许在文档流中创建一些完全独立于其他DOM的子DOM树(不会使用页面的JS/CSS, 在DevTools中现实的也是#shadowDOM字样). 于是我们可以封装一个具有独立功能的组件, 并完全不会干扰其他DOM与被其他DOM干扰. 在shadow DOM内部我们可以定义元素的样式与行为

有这样的术语

  • Shadow host: Shadow DOM挂载到的DOM节点.
  • Shadow tree: Shadow DOM内部的DOM树.
  • Shadow boundary: Shadow DOM结束的地方, 也是常规 DOM开始的地方.
  • Shadow root: Shadow tree的根节点.

方法

  • Element.attachShadow(): 将一个 shadow root 附加到任何一个元素上. 它接受一个配置对象{mode: 'open'|'closed'}分别表示这个DOM是否可以被页面的JS抓取

HTML templates&slots

类似于响应式框架中的模板, 开发者通过模板来复用一些 HTML 代码段, 在 HTML5 标准下我们甚至不需要 javascript 框架就能轻松使用模板.

<template id="my-paragraph">
  <p>My paragraph</p>
</template>

在模板中创建 HTML 代码块和子 DOM 树, 使得我们可以用不同的物理文件来组织代码. 通过<link>标签来引入这些文件, link到的数据并不会展示到DOM, 需要手动添加

<head>
    <link rel="import" href="components/some.html" />
</head>
<body>
    <!--some DOM-->
    <my-paragraph id="my-paragraph"></my-paragraph>
    <!--some DOM-->
</body>

还可以通过slots增加灵活性: 也有默认插槽, 具名插槽等. 见MDN

Custom elements

我们声明一个语义化的自定义元素来引用组件, 用 javascript 建立自定义元素和模板, shadow DOM 之间的关联, 然后将自定义标签插入到页面上就能得到一个封装好的组件.

"自定义元素的名字必须包含一个破折号(-)所以<x-tags>, <my-element><my-awesome-app>都是正确的名字, 而<tabs><foo_bar>是不正确的. 这样的限制使得 HTML 解析器可以分辨那些是标准元素, 哪些是自定义元素. "

浏览器提供了两种类型对象HTMLUnknownElementHTMLElement

  • HTML标准元素在浏览器中会被解析为HTMLElement实例
  • 用户没有声明的自定义元素会被解析为HTMLUnknownElement
  • 一旦自定义元素中使用了破折号此时自定义元素就变为了HTMLElement

使用

// 定义一个 <my-element></my-element>
class MyElement extends HTMLElement {
   ...}
window.customElements.define('my-element', MyElement);

上面代码中, 原生的window.customElements对象的define方法用来定义 Custom Element. 该方法接受两个参数, 第一个参数是自定义元素的名字, 第二个参数是一个 ES6 的class.

这个class使用getset方法定义 Custom Element 的某个属性.

class MyElement extends HTMLElement {
   
 get content() {
   
   return this.getAttribute('content');
 }

 set content(val) {
   
   this.setAttribute('content', val);
 }
}

有了这个定义, 网页之中就可以插入<my-element>了.

<my-element content="Custom Element">
 Hello
</my-element>
使用div代替button

可以这么代替, 但是不仅要复刻样式, 还要加入属性role="button"方便浏览器赋予聚焦等属性与无障碍优化(Aria,Roles)

HTML5离线工作
  • 前端缓存方案: localStorage, sessionStorage
  • 使用Service Worker进行离线后行为管理与通信

Service workers 本质上充当 Web 应用程序, 浏览器与网络(可用时)之间的代理服务器. 这个 API 旨在创建有效的离线体验, 它会拦截网络请求并根据网络是否可用来采取适当的动作, 更新来自服务器的的资源. 它还提供入口以推送通知和访问后台同步 API. Service workers 是基于 HTTPS 的, 因为Service Worker中涉及到请求拦截, 所以必须使用HTTPS协议来保障安全.

Service workers生命周期

  • 注册: 要使用Service Worker, 首先需要注册一个sw, 通知浏览器为该页面分配一块内存, 然后sw就会进入安装阶段.

    (function() {
         
        if('serviceWorker' in navigator) {
         
            navigator.serviceWorker.register('./sw.js');
        }
    })()
    
    // 只针对部分页面进行注册
    navigator.serviceWorker.register('/topics/sw.js');
    // 另一种作用域限定
    navigator.serviceWorker.register('sw.js', {
          scope: './' });
    
  • 安装: sw开始缓存文件了, 检查所有文件的缓存状态, 如果都已经缓存了, 则安装成功, 进入下一阶段.

  • 激活: 如果是第一次加载sw, 在安装后, 会直接进入activated阶段, 而如果sw进行更新, 情况就会显得复杂一些(新版本安装, 老版本工作 - 新版本开始waiting, 等待老版本terminated - 新版本替换)

  • 空闲: 空闲状态一般是不可见的, 这种一般说明sw的事情都处理完毕了,然后处于闲置状态了,浏览器会周期性的轮询, 去释放处于idle的sw占用的资源

  • 终止: 终止状态一般触发条件由下面几种方式

    1. 关闭浏览器一段时间
    2. 手动清除serviceworker
    3. 在sw安装时直接跳过waiting阶段
    self.addEventListener('install', function(event) {
         
        //跳过等待过程
        self.skipWaiting();
    });
    
  • 拦截: sw最终要和关键阶段,主要用于拦截代理所有指定的请求,然后进行二次相应的处理操作

Day1-2 理解CSS - 韩广军

CSS: Cascading Style Sheets 层叠样式表, 用来定义页面元素的样式

使用CSS的三种方法: 外链,嵌入,内联(组件式开发看起来是新的模式)

CSS的加载过程
加载HTML
解析HTML
创建DOM树
加载CSS
解析CSS
渲染展示页面
选择器复习
  • 通配选择器*
  • 标签选择器tagName
  • id选择器#id
  • 类选择器.class
  • 属性选择器
    • 没有值只有键[disabled]
    • 完全匹配值[type="pwd"]
    • 开头[type^="pwd"]
    • 结尾[type$="pwd"]
  • 伪类选择器
    • 状态伪类:a有LVHA,在按下后有:force状态(LVFHA)
    • 结构伪类:first-child…
  • 选择器组合
    • AB同时满足AB
    • A B选择B如果BA的子孙
    • A>B选择B如果BA的子元素
    • A~B选择B如果BA后且与A同级
    • A+B选择B如果他紧随在A后面
颜色表示复习
  • RGB: 三原色混合, RGB值的变化不能直观表示颜色的变化, 不方面操作颜色
  • HSL: 更加直观方便操作
    • Hue: 色相, 表示颜色基本属性, 值为0-360表示色环上的色值
    • Saturation: 饱和度, 表示颜色的鲜艳程度, 值为0-100%, 越高越鲜艳, 越低越灰
    • Lightness: 亮度, 值为0-100%, 越高越白越低越灰(例如想要让btn变灰只需要调低L)
  • alpha透明度: 可以与rgb/hsl组成rgba/hsla, 值为0-1/映射到0-255后转Hex
字体使用复习

font-family: 指定使用的字体组合, 由于浏览器使用字体基于用户安装, 所以要多指定几组, 并且在最后使用字体类型兜底, 例如font-family:'Segoe UI', Tahoma, sans-serif

字体类型

  • serif: 衬线字体, 在字末尾有一定装饰(Georgia, 宋体)
  • sans-serif: 无衬线字体, 线条单一, 末尾没有装饰(Arial, Helvetica, 黑体)
  • cursive: 手写体(Caflisch Script, 楷体)
  • fantasy: 艺术体
  • monosapce: 等宽字体(Consolas, Courier, 中文字体)

在使用font-family的时候注意

  • 一般在font-family最后指定一个字体类型兜底
  • 先英文后中文, 原因是浏览器在渲染字符的时候会逐字符比对使用的字体, 一般中文字体中就包含了英文字体, 如果中文在前, 为英文字体准备的字体就不会匹配了

使用自定义字体

<style>
  @font-face {
     
    font-family: f1;
    src: url("//s2.ssl.qhimg.com/static/ff00cb8151eeecd2.woff2") format("woff2");
  }
</style>

<h1 style="font-family: f1, serif">落霞与孤鹜齐飞, 秋水共长天一色. </h1>

对于英文字符这这种方法可以快速引入字体, 但是对于中文字符来说, 中文字体包太大了, 一般需要裁剪字体包获得需要的字符

字体样式

  • font-size: 设置字体大小
    • 关键字: small medium large
    • 长度单位: px em
    • 百分数: 相对于父元素字体大小
  • font-style: 设置字体风格
    • normal: 正常
    • italic: 斜体
    • oblique: 倾斜
  • font-weight: 设置字重
    • 关键字: normal, bold…
    • 数值: 100-900(400=normal,700=bold), 如果系统中找不到字体则会就近替换
  • line-height: 设置行高(baseline之间的距离)
  • font: 字体设置简写([斜体] 粗细 大小[/行高] [字体族])

段落属性

  • text-align: 对齐方式 left|center|right|justify(分散对齐, 且仅对非最后一行有效, 如果文字只有一行那也不生效)
  • letter-spacing: Xpx 字符之间的距离
  • word-spacing: Xpx 字符之间的距离
  • text-indent: Xpx 首行缩进(支持负数)
  • text-decoration: underline|line-through|overline|none 下划线, 删除线, 上划线, 无
  • white-space: normal(默认)|nowrap(不换行)|pre(保留所有换行与空格)|pre-wrap(保留空格, 会换行)|pre-wrap(合并空格, 保留换行)
原子化CSS(Windi CSS)

原子化 CSS 是一种 CSS 的架构方式, 它倾向于小巧且用途单一的 class, 并且会以视觉效果进行命名

CSS原子化是一种写法, 其特点就是一个类名对应一个样式, 从而通过在标签上附加不同的类名来生成对应的效果, 可以有效的减少CSS的相关代码, 例如

.flex {
   
    display: flex;
}

坏处

  1. 需要定制一大堆工具类, 这些使用的话需要开发人员的熟悉.
  2. 虽然减少了CSS的代码, 但是使得HTML的代码变得更臃肿了.
  3. CSS 规则插入顺序仍然很重要.

好处

  1. 写法真的变简单了, 比如添加一个常用的规则时, 直接就加原子类
  2. 原子类是全局的, 所以当我们移动一段标签到其他页面时, 样式也就是有的
  3. 当我们使用原子类的时候, 我们修改页面的样式时往往不会再去修改CSS代码了, 而是直接修改原子类名

框架: tailwindcss

Tailwind CSS 是一个功能类优先的 CSS 框架, 它集成了诸如 flex, pt-4, text-centerrotate-90 这样的的类, 它们能直接在脚本标记语言中组合起来, 构建出任何设计.

是不是就是CSS原子类的写法了?当然, tailwindcss不止于此, 它还有很多强大的功能和配置

参考

优雅降级与渐进增强

CSS构建的两种观点

  • "优雅降级"观点
    “优雅降级"观点认为应该针对那些最高级, 最完善的浏览器来设计网站. 而将那些被认为"过时"或有功能缺失的浏览器下的测试工作安排在开发周期的最后阶段, 并把测试对象限定为主流浏览器(如 IE, Mozilla 等)的前一个版本. 在这种设计范例下, 旧版的浏览器被认为仅能提供"简陋却无妨 (poor, but passable)” 的浏览体验. 你可以做一些小的调整来适应某个特定的浏览器. 但由于它们并非我们所关注的焦点, 因此除了修复较大的错误之外, 其它的差异将被直接忽略.
  • "渐进增强"观点
    "渐进增强"观点则认为应关注于内容本身. 内容是我们建立网站的诱因. 有的网站展示它, 有的则收集它, 有的寻求, 有的操作, 还有的网站甚至会包含以上的种种, 但相同点是它们全都涉及到内容. 这使得"渐进增强"成为一种更为合理的设计范例. 这也是它立即被 Yahoo! 所采纳并用以构建其"分级式浏览器支持 (Graded Browser Support)"策略的原因所在.
.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;
}
CSSinJS

CSS默认是全局生效的, 可以使用CSS in JS, scoped等方案实现, CSS in JS是一种CSS工程化方案

传统CSS问题

  • 全局污染 - CSS的选择器是全局生效的, 所以在class名称比较简单时, 容易引起全局选择器冲突, 导致样式互相影响.
  • 命名混乱 - 因为怕全局污染, 所以日常起class名称时会尽量加长, 这样不容易重复, 但当项目由多人维护时, 很容易导致命名风格不统一.
  • 样式重用困难 - 有时虽然知道项目上已有一些相似的样式, 但因为怕互相影响, 不敢重用.
  • 代码冗余 - 由于样式重用的困难性等问题, 导致代码冗余.

为解决问题而提出的规范

  • SASS, LESS - 提供了变量, 简单函数, 运算, 继承等, 扩展性, 重用性都有了很大的提升, 解决了一些样式重用冗余的问题, 但是对于命名混乱问题的效果不大.
  • BEM (.block__element–modifier) - 比较流行的class命名规则, 部分解决了命名混乱和全局污染的问题, 但class定义起来还是不太方便, 比较冗长, 而且和第三方库的命名还是有可能冲突.
  • CSS Modules - 模块化CSS, 将CSS文件import到JavaScript里并声明为字符串, 在使用的时候拼串导出. 基本上解决了全局污染, 命名混乱, 样式重用和冗余的问题, 但CSS有嵌套结构的限制(只能一层), 也无法方便的在CSS和JavaScript之间共享变量.

CSS in JS就是将应用的CSS样式写在JavaScript文件里面, 可以利用标签的style拼串, 也可以利用ele.style实现

见此

一些不知道的伪元素
  • ::first-letter: 首字母
  • ::first-line: 首行
  • ::cue (:cue): 用于选中VTT视频中的怪东西
  • ::selection: 选中被用户高亮的/选中的内容
  • ::slotted(): 选中HTML模板元素

MDN

Day1-3 深入CSS - 韩广军

CSS选择器的特异程度: ID选择器数目_(伪)类选择器数目_标签选择器数目拼串

继承

部分属性可以自动继承其来自父元素的计算值, 除非显式指定

  • 可以继承的样式: 与文字相关的
  • 不可以继承的属性: 宽度, 高度, 盒子模型, 尺寸相关, **对于不可继承的属性可以通过key:inherit**继承
  • 如果元素想要继承的值父元素们都没有, 那么其会自动使用默认值. 可以使用key:initial显式使用默认值
  • 还可以使用key:unset, 若该CSS属性可继承, 则从父级继承对应属性值, 若该CSS性不可继承, 则将其重置为初始值
CSS的求值过程
声明值
层叠值
指定值
计算值
使用值
DOM树
filtering
样式规则
cascading
defauting
resolving
formatting
constraining
实际值
  1. filtering: 找到可以匹配到元素的规则
  2. cascading: 根据选择器特异性找到优先级最高的属性值
  3. defauting: 对于没有匹配到的属性值, 要么继承父类, 要么使用默认值
  4. resolving: 将相对值(red, rm)转化为浏览器下的值(#f00, px), 这一部分不转换需要部署才知道的值(em可以转为px, width:60%需要布局后才知道是多少, 在这一步不转化), 此处得到的结果叫计算值, 子元素样式为inherit的时候就是参考了父元素的计算值
  5. formatting: 将相对值进一步转化(例如width:60%)
  6. constraining: 将小数像素转化为整数像素, 并根据特殊规则进行调整(min-width, chrome中的font-size:10px会转化为12px)最终应用在页面上
布局

布局相关技术

  • 常规流(文档流)
    • 行级
    • 块级
    • 表格布局
    • FlexBox
    • Grid布局
  • 浮动
  • 绝对定位

盒子模型

当padding设置为百分比的时候padding参考的是父元素的宽度

据此实现一个1:1的盒子

<style>
  .wap{
     
    width: 200px;
    height: 300px;
    background-color: #bfa;
  }
  .cont{
     
    width: 100%;
    height: 0;
    padding-bottom: 100%;
    background-color: red;
  }
</style>

<body>
  <div class="wap">
    <div class="cont"></div>
  </div>
</body>

当margin设置为百分比的时候padding参考的是父元素的宽度

垂直方向margin会合并折叠

块级元素与行内元素

  • 块级元素不能并排摆放, 适用所有盒模型
  • 行内元素可以与其他行内元素拜访, 但是盒模型width, height不适用
  • inline-block本身是行内元素, 但是被放在了盒子中, 适用盒模型width, height

Flex Box

  • flex-dirextion: row|row-reverse|column|column-reverse: 摆放流向

  • justify-content: flex-start|flex-end|center|space-between|space-around|space-evenly: 主轴对齐方式

  • align-items: flex-start|flex-end|center|stretch|baseline侧轴对齐

  • align-self: 为特定元素设置侧轴对齐方式

  • order: 手动指定顺序

  • flex-grow: 当容器有剩余空间的时候的伸展能力

  • flex-shrink: 当容器剩余空间不足的时候的压缩能力(默认是1)

  • flex-base: 容器的自然长度

  • flex: 缩写

    • 单值语法: 值必须为以下其中之一:
      • 一个无单位数(<number>): 它会被当作flex:<number> 1 0; <flex-shrink>的值被假定为1, 然后<flex-basis> 的值被假定为0.
      • 一个有效的宽度(width)值: 它会被当作 <flex-basis>的值.
      • 关键字none, auto或initial.
    • 双值语法: 第一个值必须为一个无单位数, 并且它会被当作 <flex-grow> 的值. 第二个值必须为以下之一:
      • 一个无单位数:它会被当作 <flex-shrink> 的值.
      • 一个有效的宽度值: 它会被当作 <flex-basis> 的值.
    • 三值语法:
      • 第一个值必须为一个无单位数, 并且它会被当作 <flex-grow> 的值.
      • 第二个值必须为一个无单位数, 并且它会被当作 <flex-shrink> 的值.
      • 第三个值必须为一个有效的宽度值, 并且它会被当作 <flex-basis> 的值.
    /* 一个值, 无单位数字: flex-grow */
    flex: 2;
    
    /* 一个值, width/height: flex-basis */
    flex: 10em;
    flex: 30px;
    flex: min-content;
    
    /* 两个值: flex-grow | flex-basis */
    flex: 1 30px;
    
    /* 两个值: flex-grow | flex-shrink */
    flex: 2 2;
    
    /* 三个值: flex-grow | flex-shrink | flex-basis */
    flex: 2 2 10%;
    
    /*全局属性值 */
    flex: inherit;
    flex: initial;
    flex: unset;
    

Grid 布局

  • 使用display: grid创建网格容器
  • 使用grid-template-X: 将容器划分为网格
  • 设置每一个子元素占用那些行列
  • 使用grid-area: X/X/X/Xgrid-column/row-start/end指定元素所占位置, 允许重叠

Float 布局

本质是做图文环绕的, 在没有先进布局的时候还用来做各种布局, 现在已经不需要float做布局了, 除了图文环绕, 其他不需要用float做

Position

  • static: 默认
  • relative: 相对元素本身定位
  • absolute: 相对非static祖先定位
  • fixed: 相对于视口定位

Day2 如何写好JS - 月影

原则-各司其责

我们希望让HTML, CSS, JS分别去控制结构, 表现, 功能. 各司其职并不是让他们物理上分类(写成三个文件), 而是让他们功能上分离(要不然现在组件化开发岂不是完全违反了原则), 例如不要让JS去直接操作CSS样式

举例: 请实现一个静态页面的深夜模式

  • 最基础的版本

    <header>
        <button id="modeBtn">🌞</button>
        <h1>深夜食堂</h1>
    </header>
    
    const btn = document.getElementById('modeBtn');
    btn.addEventListener('click', (e) => {
         
      const body = document.body;
      if(e.target.innerHTML === '🌞') {
         
        body.style.backgroundColor = 'black';
        body.style.color = 'white';
        e.target.innerHTML = '🌜';
      } else {
         
        body.style.backgroundColor = 'white';
        body.style.color = 'black';
        e.target.innerHTML = '🌞';
      }
    });
    

    有问题: 通过文字判断状态, 使用JS直接操作了一个CSS样式

  • 改一改

    body.night {
         
      background-color: black;
      color: white;
      transition: all 1s;
    }
    
    #modeBtn::after {
         
      content: '🌞';
    }
    body.night #modeBtn::after {
         
      content: '🌜';
    }
    
    const btn = document.getElementById('modeBtn');
    btn.addEventListener('click', (e) => {
         
      const body = document.body;
      if(body.className !== 'night') {
         
        body.className = 'night';
      } else {
         
        body.className = '';
      }
    });
    

    定义了该night样式, 不用通过文字判断状态了. 要是让我写我最多改成classList.toggle()

  • 回头想想: 我们在做什么事情? 我们在做一个纯视觉效果展示, 样式应该是CSS控制的! JS是控制行为的, 我们可以尝试纯CSS+HTML实现

    <input id="modeCheckBox" type="checkbox">
    <div class="content">
    	<header>
    		<label id="modeBtn" for="modeCheckBox"></label>
    		<h1>深夜食堂</h1>
    	</header>
    </div>
    
    #modeCheckBox {
         
      display: none;
    }
    
    #modeCheckBox:checked + .content {
         
      background-color: black;
      color: white;
      transition: all 1s;
    }
    
    #modeBtn {
         
      font-size: 2rem;
      float: right;
    }
    
    #modeBtn::after {
         
      content: '🌞';
    }
    
    #modeCheckBox:checked + .content #modeBtn::after {
         
      content: '🌜';
    }
    

    直接使用checkbox用于点击, 通过选择checkboxchecked伪类来定义夜间模式

    要是之和我这么一说, 我估计要这么写

    - <input id="modeCheckBox" type="checkbox">
    <div class="content">
    	<header>
    +	    <input id="modeCheckBox" type="checkbox">
    	    <label id="modeBtn" for="modeCheckBox"></label>
    		<h1>深夜食堂</h1>
    	</header>
    </div>
    
    #modeCheckBox {
    - display: none;
    + outline: none;
    + width: 0;
    + height: 0;
    }
    
    // ...
    

    很自然的, 我把inputlabel放一起了, 然后把在关掉checkbox的样式, 但是这不符合语义化要求啊!!!, 回头想想, 为啥要专门有个label标签呢? 为的是让他们永远在一起吗? 当然是可以让这两个标签分开啊

    人家还可以把这个checkbox给删掉, 太他妈牛了. 这才是语义化, 样式与表现各司其职

我们学到了

  • 什么是HTML, CSS, JS各司其职
  • 不要让JS直接干预Style
  • 用class表示状态
  • 对于样式的展示尽量寻求零JS方案
原则-组件封装

我们希望从Web页面中抽象出一个一个包含HTML, CSS, JS的组件出来, 使得组件具备良好的封装性, 正确性, 可扩展性, 复用性. 我们将通过封装一个轮播图组件来了解组件封装的过程

  1. 结构设计:

    轮播图显然是一个列表结构, 使用ul即可

  2. 样式设计:

    使用CSS绝对定位将图片重叠在一起, 使用修饰符定义active元素, 使用transition切换动画

  3. 行为设计-API设计(功能设计):

    应该保证API是: 原子操作, 职责单一, 满足灵活性

    我们设计一个Slide

    class Slider{
         
     constructor(id){
         			
       this.container = document.getElementById(id);
       this.items = this.container
       .querySelectorAll('.slider-list__item, .slider-list__item--selected');
     }
     getSelectedItem(){
         		// 活动元素
       const selected = this.container
         .querySelector('.slider-list__item--selected');
       return selected
     }
     getSelectedItemIndex(){
         	// 活动元素index
       return Array.from(this.items).indexOf(this.getSelectedItem());
     }
     slideTo(idx){
         				// 滚到
       const selected = this.getSelectedItem();
       if(selected){
          
         selected.className = 'slider-list__item';
       }
       const item = this.items[idx];
       if(item){
         
         item.className = 'slider-list__item--selected';
       }
     }
     slideNext(){
         				// 下一个
       const currentIdx = this.getSelectedItemIndex();
       const nextIdx = (currentIdx + 1) % this.items.length;
       this.slideTo(nextIdx);
     }
     slidePrevious(){
         			// 上一个
       const currentIdx = this.getSelectedItemIndex();
       const previousIdx = (this.items.length + currentIdx - 1)
         % this.items.length;
       this.slideTo(previousIdx);  
     }
    }
    
    const slider = new Slider('my-slider');
    slider.slideTo(3);
    
  4. 行为设计-控制流

    组件应该是在DOM上可控制的(总不能让用户输入slider.slideNext切换吧), 我们需要完成DOM状态与API行为的耦合

    我们可以通过自定义事件进行解耦(尽量让DOM状态的改变与我们的代码之间独立)

    Slide类加入startstop实现控制, 在构造函数中加入控制器的时间绑定

    class Slider{
         
        
      constructor(id, cycle = 3000){
         
        this.container = document.getElementById(id);
        this.items = this.container.querySelectorAll('.slider-list__item, .slider-list__item--selected');
        this.cycle = cycle;
    
        const controller = this.container.querySelector('.slide-list__control');
        if(controller){
         
          const buttons = controller.querySelectorAll('.slide-list__control-buttons, .slide-list__control-buttons--selected');
          controller.addEventListener('mouseover', evt=>{
         
            const idx = Array.from(buttons).indexOf(evt.target);
            if(idx >= 0){
         
              this.slideTo(idx);
              this.stop();
            }
          });
          
          controller.addEventListener('mouseout', evt=>{
         
            this.start();
          });
          
          this.container.addEventListener('slide', evt => {
         
            const idx = evt.detail.index
            const selected = controller.querySelector('.slide-list__control-buttons--selected');
            if(selected) selected.className = 'slide-list__control-buttons';
            buttons[idx].className = 'slide-list__control-buttons--selected';
          })
        }
        
        const previous = this.container.querySelector('.slide-list__previous');
        if(previous){
         
          previous.addEventListener('click', evt => {
         
            this.stop();
            this.slidePrevious();
            this.start();
            evt.preventDefault();
          });
        }
        
        const next = this.container.querySelector('.slide-list__next');
        if(next){
         
          next.addEventListener('click', evt => {
         
            this.stop();
            this.slideNext();
            this.start();
            evt.preventDefault();
          });
        }
      }
        
      getSelectedItem(){
         /*...*/}
      getSelectedItemIndex(){
         /*...*/}
      slideNext(){
         
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Liukairui

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值