3、操作系统|谷歌|百度|html标签|拼音data-|with业务端、http异步请求长连接socket同源跨域、ajax|rest业务逻辑|axios原理执行机制、custPromise、JS主要模块如seajs、编程范式、设计模式、git、gitLab、VSCode、jinja2语法、文字的水印与镂空、文字与盒子的阴影背景、线性渐变径向渐变、动画过渡转换(3100行)

一、操作系统、谷歌、百度、html标签、拼音、data-、with业务端参数
1、window操作系统的各版本发布时间
  (1)Windows1.0,1985年,首个图形界面版本,基于MS-DOS
  (2)Windows2.0,1987年,支持重叠窗口和快捷键
  (3)Windows3.0,1990年,首次大获成功,支持256色
  (4)Windows3.1,1992年,引入TrueType字体和多媒体支持
  (5)Windows95,1995年,革命性版本,开始菜单和任务栏首次出现
  (6)Windows98,1998年,集成IE浏览器,支持USB
  (7)Windows2000,2000年,面向企业的NT内核系统,稳定性提升
  (8)WindowsXP,2001年,最经典的版本之一,NT内核首次进入消费市场
  (9)Windows7,2009年,修复Vista问题,成为最受欢迎的系统之一
  (10)Windows8,2012年,取消开始菜单,主打触屏优化(口碑两极分化)
  (11)Windows8.1,2013年,部分回归传统设计,修复8代问题
  (12)Windows10,2015年,最后一个“版本号”系统,转为持续更新
  (13)Windows11,2021年,全新UI设计,要求TPM2.0等新硬件
2、mac操作系统的各版本发布时间
  (1)System1.0,1984年,首个Macintosh操作系统,图形化界面先驱
  (2)System7,1991年,支持彩色界面、虚拟内存等关键功能
  (3)MacOS9,1999年,最后一款“Classic Mac OS”,2001年后被OSX取代
  (4)MacOSX10.0,2001年,首个公开版,性能较差但奠定基础
  (5)MacOSX10.4,2005年,支持Intel处理器,Spotlight搜索首次亮相
  (6)MacOSX10.5,2007年,引入Time Machine备份、Boot Camp
  (7)MacOSX10.6,2009年,纯64位系统,性能优化
  (8)MacOSX10.7,2011年,通过Mac App Store分发,引入Launchpad
  (9)OSX10.9,2013年,首个免费升级版本,优化多屏支持
  (10)OSX10.10,2014年,全新扁平化UI(类似iOS7)
  (11)macOS10.12,2016年,引入Siri、Apple Pay
  (12)macOS10.13,2017年,升级APFS文件系统
  (13)macOS10.15,2019年,移除32位应用支持,拆分iTunes
  (14)macOS11.0,2020年,版本号跳至11,全新ARM架构支持(M1芯片)
  (15)macOS14.0,2023年,小组件互动、游戏模式
  (16)window和mac的最先发布时间比较 
    A、Mac操作系统(1984年)比Windows(1985年)早约1年发布,并率先定义了现代图形界面
    B、微软后来通过Windows3.1(1992)、Windows95(1995)等版本逐步追赶并反超
  (17)window比mac更普及,为什么
    A、硬件兼容性与开放性
      a、Windows采用开放的硬件生态,支持几乎所有品牌的(组装)电脑,
      b、mac仅限自家硬件
    B、软件兼容性与开放性
      a、Windows兼容主流办公软件、工业软件和银行系统
      b、Windows支持更广泛的开发工具和游戏生态
    C、价格因素
      a、Windows全价位段
      b、mac高价位段
3、谷歌的发展历程
  (1)1998年,Google公司正式成立,同年发布了Google搜索引擎
  (2)2000年,推出第一款关键词竞价广告系统AdWords
  (3)2004年,推出邮箱Gmail,在纳斯达克上市
  (4)2005年,收购安卓公司,推出了谷歌地图
  (5)2006年,收购视频网站YouTube
  (6)2008年,推出网页浏览器Google Chrome
  (7)2011年,推出社交网络服务Google+
  (8)2012年,推出提供云存储和在线办公软件的服务
  (9)2013年,宣布首个智能眼镜产品,开启了智能可穿戴设备的先河
  (10)2016年,推出首款自主设计和生产的智能手机
  (11)2023年,推出聊天机器人Bard,以应对ChatGPT的竞争
  (12)2024年,谷歌DeepMind团队发布全新诊断对话式AI
4、百度的发展历程
  (1)2000年,百度发布Baidu搜索引擎,比Google中文版迟了一年
  (2)2001年,在中国首创了竞价排名商业模式
  (3)2003年,百度新闻、百度图片、百度贴吧等多产品上线
  (4)2006年,百度空间上线
  (5)2008年,百度百科正式版发布
  (6)2010年,百度输入法正式上线发布
  (7)2015年,百度率先发布了世界上首个互联网NMT系统
  (8)2013年,百度在百度开发者页面上正式开放了“语音识别”技术
  (9)2018年,百度世界大会发布L4级无人驾驶乘用车
  (10)2023年,百度世界大会发布文心大模型
5、html的特殊标签 
  附、博客园改图标为wifi
    A、2024年3月21日,博客园改了图标,新图标状如wifi
    B、图标代码大致如下
      <head>
        <link rel="shortcut icon" href="/favicon.ico" />
      </head>  
  附、data-*属性,用来嵌入自定义数据,是HTML5新增的
    A、属性值是1个字符串
    B、示例
      <ul>
        <li data-animal-type="bird">Owl</li>
        <li data-animal-type="fish">Salmon</li>
        <li data-animal-type="spider">Tarantula</li>
      </ul>
  (1)object标签,外部资源的容器
    A、嵌入图像,<object data="tulip.jpg" width="300" height="300"></object>,最好使用<img>标签
    B、嵌入HTML页面,<object data="/index.html" width="500" height="300"></object>,最好使用<iframe>标签
    C、嵌入视频,<object data="shanghai.mp4" width="640" height="300"></object>,最好使用<video>和<audio>标签 
  (2)param标签,<object>元素的参数,来源,https://www.w3school.com.cn/tags/tag_param.asp
      <object data="song.mp3">
        <param name="autoplay" value="true"> //声音就会在页面加载后立即开始播放
      </object>
  (3)map标签,图像地图,来源,https://www.w3school.com.cn/tags/tag_map.asp
      <img src="life.png" alt="Life" usemap="#lifemap" width="650" height="451">
      <map name="lifemap">
        <area shape="rect" coords="10,208,155,338" alt="AirPods" href="airpods.html">
        <area shape="rect" coords="214,65,364,365" alt="iPhone" href="iphone.html">
        <area shape="circle" coords="570,291,75" alt="Coffee" href="coffee.html">
      </map>
  (4)拼音
    <!DOCTYPE html>
    <html lang="zh-CN">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>拼音+汉字展示</title>
        <style>
          .pinyin-flex {
            display: inline-flex;
            flex-direction: column;
            align-items: center;
            margin: 10px;
            font-size: 24px;
          }
          .pinyin-top {
            font-size: 14px;
            color: #666;
            margin-bottom: 2px;
          }
        </style>
      </head>
      <body>
        <div>
          <div>在搜狗输入法中文状态下,输入v8,会出现下拉菜单</div>
          <div>翻页下拉菜单,找到所需的头上有音调的字母,点击即可</div>
          <div class="pinyin-flex">
            <span class="pinyin-top">zhōng guó</span>
            <span>中国</span>
          </div>
          <div class="pinyin-flex">
            <span class="pinyin-top">huǎn yíng</span>
            <span>欢迎</span>
          </div>
          <div class="pinyin-flex">
            <span class="pinyin-top">nín</span>
            <span>您</span>
          </div>
        </div>
      </body>
    </html>
  (5)html5新标签,
    <!DOCTYPE html>
    <html>
      <head>
        <title>HTML5新增标签示例</title>
      </head>
      <body>
        <header>
          <h1>页面标题</h1>
        </header>
        <nav>
          <ul>
            <li><a href="#">首页</a></li>
            <li><a href="#">关于我们</a></li>
          </ul>
        </nav>
        <main>
          <p>这里是主要内容</p>
          <article>
            <time datetime="2023-01-01">2023年1月1日</time>
            <pre>
              header:页面或区段的头部;
              nav:导航链接;
              main:页面的主要内容区域
                article:独立的、完整的相关内容
                section:文档中的一个区段
                -------------------------------
                figure:媒体内容及其标题
                figcaption: figure 的标题
                -------------------------------
                details:展可扩展的详细内容
                summary: details 元素的标题
                -------------------------------
                time:日期或时间;datetime属性用于将时间转换为机器可读的格式
                mark:有标记的文本(通常是高亮显示)
              aside:内容之外的内容(如侧边栏)
              footer:页面或区段的底部
            </pre>
            <section>
              <figure data-testid="default-avatar">
                <figcaption>figure的标题</figcaption>
                <svg width="75" height="75" viewBox="0 0 75 75" fill="none" xmlns="http://www.w3.org/2000/svg" >
                  <mask id="mask0" x="0" y="0" mask-type="alpha">
                    <circle cx="37.5" cy="37.5" r="37.5" fill="#0042DA" />
                  </mask>
                  <g mask="url(#mask0)"><!-- 属性mask用来引用一个遮罩元素 -->
                    <rect x="-30" y="-43" width="131" height="154" fill="#0042DA" />
                    <rect x="2.50413" y="120.333" width="81.5597" height="86.4577" rx="2.5"
                      transform="rotate(-52.6423 2.50413 120.333)" stroke="#FED23D" stroke-width="5" />
                    <circle cx="76.5" cy="-1.5" r="29" stroke="#FF8E20" stroke-width="5" />
                    <path d="M-49.8224 22L-15.5 -40.7879L18.8224 22H-49.8224Z" stroke="#F7F8FF" stroke-width="5" />
                  </g>
                </svg>
                <img alt="图片1" src="logo1.png"/>
                <img alt="图片2" src="logo2.png"/>
              </figure>
              <details>
                <summary>标题是可见的</summary>
                <p>用户点击标题时,会显示出details</p>
              </details>
            </section>
          </article>
        </main>
        <aside>
          <p>这里是侧边栏内容,可以用于导航链接、关于作者的信息等。</p>
        </aside>
        <footer>
          <mark>编辑注释</mark>
          <p>版权所有 © 2023 我的网站</p>
        </footer>
      </body>
    </html>
  (6)Meta标签(元数据)
    来源,https://www.w3school.com.cn/tags/tag_meta.asp
    <html>
      <head>
        <meta charset="UTF-8">
        <meta name="description" content="免费的 Web 教程">
        <meta name="keywords" content="HTML,CSS,JavaScript">
        <meta name="author" content="YK Investment">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta name="my-name" content="name的等号后面是属性值,由开发者确定">
        <meta name="your-name" content="特定的属性值如keywords、viewport">
        <meta name="your-name" content="会被网络爬虫或浏览器识别!">
      </head>
    </html>
    <script>
      //name的等号后面是属性值,由开发者确定,特定的属性值如keywords、viewport,会被网络爬虫或浏览器识别!
      //js获取meta某name的content
      //方法1
      var metas = document.getElementsByTagName('meta');
      for (var i = 0; i < metas.length; i++) {
        if (metas[i].getAttribute('name') === 'my-name') {
          console.log(metas[i].getAttribute('content'));
        }
      }
      //方法2
      var names = document.getElementsByName('your-name');
      for (var i = 0; i < names.length; i++) {
        console.log(names[i].getAttribute('content'));
      }
    </script>
    //以下A单独使用;B、C选1个,与D组合使用
    A、name,元数据的名称,常用取值有application-name、author、description、generator、keywords、viewport
      a、application-name,规定页面代表的Web应用程序的名称
      b、author,规定文档作者的姓名。例如,
        <meta name="author" content="YK Investment">
      c、description,规定页面的描述。搜索引擎可以选择此描述来显示搜索结果。例如,
        <meta name="description" content="免费的 Web 教程">
      d、generator,规定用于生成文档的软件包之一(不用于手写页面)。例如,
        <meta name="generator" content="FrontPage 4.0">
      e、keywords,规定与页面相关的关键字列表,以逗号分隔。告知搜索引擎关于页面的内容。
        提示:请始终规定关键字(搜索引擎需要对页面进行分类)。例如:
        <meta name="keywords" content="HTML, meta tag, tag reference">
      f、viewport,控制视口(网页的用户可见区域)
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        width:视口的宽度,单位为像素。
        height:视口的高度,单位为像素(很少用)
        initial-scale:设置页面的初始缩放。
        minimum-scale:设置最小的缩小程度,它必须小于或等于maximum-scale设置。
        maximum-scale:设置最大的放大程度,它必须大于或等于minimum-scale设置。
        user-scalable:是否允许用户手动缩放,默认值为yes。
    B、http-equiv,http标头,常用取值有content-security-policy、content-type、default-style、refresh
      a、content-security-policy,规定文档的内容政策。例如:
        <meta http-equiv="content-security-policy" content="default-src 'self'">
      b、content-type,规定文档的字符编码。
        提示:这是另一种声明 HTML 页面所有字符编码的方法。例如:
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      c、default-style,规定要使用的首选样式表。例如:
        <meta http-equiv="default-style" content="the document's preferred stylesheet">
        注意:对应的 content 属性值应与同一文档中某个 style 元素或 link 元素的 title 属性值相同。
      d、refresh,文档刷新自身的时间间隔,以秒为单位。例如:
        <meta http-equiv="refresh" content="300">
        还可以另行指定一个url让浏览器载入:例如:
        <meta http-equiv="refresh" content="5; https://www.w3school.com.cn">
        注意:应谨慎使用"refresh"值,因为它会夺走用户对页面的控制权。使用"refresh"将导致W3C的Web内容可访问性指南失败。
    C、content,文本,name或http-equiv的注解
    D、charset,字符集,常用取值有UTF-8,规定HTML文档的字符编码
    附、有name属性的标签,来源,https://www.thinbug.com/q/27724898
      a、表单元素,<form>、<input>、<select>、<textarea>、
      b、常用元素,<a>、<button>、<frame>、<iframe>、<img>、<meta>
      c、其它元素,<applet>(H5弃用,小程序)、<map>、<object>、<param>
      d、frame,H5弃用,在HTML4中,用于在<frameset>中一个特定的窗口
      e、iframe,在当前HTML文档中嵌入另一个HTML文档的功能,<iframe src="https://www.example.com"></iframe>
  (7)img标签,img的标签
    // A、绝对路径,src="http://",src="https://",可以跨域
    // B、相对路径,基于当前页面的URL解析路径,src="assets/logo.png"
    // C、协议相对路径,继承当前页面的协议(HTTP或HTTPS),src="//example.com/image.png"
    // D、Base64编码的url,src="new FileReader().readAsDataURL(file.raw)",即src="data:image/png;base64,iVBOR"
    //   reader.readAsDataURL,将二进制图片读取为base64编码的url,该url直接编码数据到字符串,自动回收
    // E、Blob编码的URL,src="window.URL.createObjectURL(blob)"
    //   URL.createObjectURL,将二进制图片读取为Blob编码的url,该url只是一个指向内存中Blob的引用,用URL.revokeObjectURL(blobUrl)销毁
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>object-fit</title>
        <style>
          img.fill {
            width: 100px;
            height: 200px;
            object-fit: fill;
          }
          img.cover {
            width: 100px;
            height: 200px;
            object-fit: cover;
          }
          img.none {
            width: 100px;
            height: 200px;
            object-fit: none;
          }
          img.contain {
            width: 100px;
            height: 200px;
            object-fit: contain;
          }
          img.scaleDown {
            width: 100px;
            height: 200px;
            object-fit: scale-down;
          }
        </style>
      </head>
      <body>
        <pre>
          来源,https://www.runoob.com/cssref/pr-object-fit.html
          fill:默认,不保证保持原有的比例,内容拉伸填充整个内容容器
          cover:保持原有尺寸比例,但部分内容可能被剪切
          none:保留原有元素内容的长度和宽度,也就是说内容不会被重置
          contain:保持原有尺寸比例,内容被缩放
          scale-down:none 或 contain之间谁得到的对象尺寸会更小一些
        </pre>
        fill<img class="fill" src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg">
        cover<img class="cover" src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg">
        none<img class="none" src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg">
        contain<img class="contain" src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg">
        scale-down<img class="scaleDown" src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg">
        右侧原图<img src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg">
      </body>
    </html>
6、获取当前浏览器
  (1)操作系统
    var getOS = function () { 
      var os;
      if (navigator.userAgent.indexOf('Android') > -1 || navigator.userAgent.indexOf('Linux') > -1) {
          os = 'Android';
      } else if (navigator.userAgent.indexOf('iPhone') > -1) {
          os = 'iOS';
      } else if (navigator.userAgent.indexOf('WindowsPhone') > -1) {
          os = 'WP';
      } else {
          os = '不是移动端';
      }
      return os;
    }
    附、操作系统相关知识
      A、OS是Operating System(操作系统)的缩写
      B、桌面操作系统,Windows、macOS
      C、服务器操作系统,Linux
      D、移动操作系统,Android(基于Linux)、iOS(基于Unix)
  (2)名称
    var getExploreName = function (){ 
      var userAgent = navigator.userAgent;
      if(userAgent.indexOf("Opera") > -1 || userAgent.indexOf("OPR") > -1){
          return 'Opera';
      }else if(userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1){
          return 'IE';
      }else if(userAgent.indexOf("Edge") > -1){
          return 'Edge';
      }else if(userAgent.indexOf("Firefox") > -1){
          return 'Firefox';
      }else if(userAgent.indexOf("Safari") > -1 && userAgent.indexOf("Chrome") == -1){
          return 'Safari';
      }else if(userAgent.indexOf("Chrome") > -1 && userAgent.indexOf("Safari") > -1){
          return 'Chrome';
      }else if(!!window.ActiveXObject || "ActiveXObject" in window){
          return 'IE>=11';
      }else{
          return 'Unkonwn';
      }
    }
  (3)业务端参数并解析
    //业务端参数是指在软件开发或系统设计中,与具体业务逻辑相关的参数或配置
    <body>
      <div id="app">
      <div id="loader-wrapper">
        <div id="loader"></div>
        <div class="loader-section section-left"></div>
        <div class="loader-section section-right"></div>
        <div class="load_title">正在加载系统资源,请耐心等待</div>
      </div>
      </div>
      <script>
        /* with语句的作用是:将代码的作用域设置到一个特定的对象中,代码块内部可以直接访问该对象的属性,类似于解构赋值
          另外,后面with代码块内部可以直接使用上面所有层级with对象的属性。
        */
        with(document) 
        with(body)
        with(
          insertBefore(createElement("script"),firstChild) //with获取返回值script
        )
        setAttribute( //setAttribute,只能一次设置一个属性
          "exparams","is_track_single_page=true&show_log=true&url_type=url_type",//有效
          id="furtureTV-beacon-aplus",//有效
          src="/src/utils/sensors"//有效
        )
        //以下原代码辅助1
        document.getElementsByTagName('body')[0].children[0].setAttribute(
          "exparams","is_track_single_page=true&show_log=true&url_type=url_type",//有效
          id="furtureTV-beacon-aplus",//无效
          src="/src/utils/sensors"//无效
        )
        //以下原代码辅助2
        document.body.insertBefore("script", document.body.firstChild);//在body的第一个子标签前插入一个script标签
        //以下原代码辅助3
        var insertedNode = parentNode.insertBefore(newNode, referenceNode);
      </script>
      <script type="module" src="/src/main.js"></script>
    </body>
    //以下获取业务端参数并解析
    var getExtraParams = function (){ 
        var params = {};
        var jsTag = document.getElementById("furtureTV-beacon-aplus") || document.getElementById("beacon-aplus");
        if (jsTag) {
            var attr = jsTag.getAttribute("exparams").split('&');
            for(var i = 0; i <attr.length; i++){
                var item = attr[i].split('=');
                if(item[1] === 'false') item[1] = false;
                if(item[1] === 'true') item[1] = true;
                params[item[0]] = item[1];
            }
        }
        return params;
    }
  (4)with使用
    var foo = {
      bar: {
        data: {
          x: 0,
          y: 0,
          z: 0,
        }
      }
    }
    A、普通获取
      console.log( foo.bar.data.x, foo.bar.data.y, foo.bar.data.z );
    B、with获取
      with(foo.bar.data){
        console.log( x, y, z );
      }
      with(foo)with(bar)with(data){
        console.log( x, y, z );
      }
   
二、http异步请求与跨域
  附、远程控制与VPN
    A、远程控制:通过本机操控远程电脑,反应较慢,常用于1对1
    B、VPN:把本IP发出的请求,伪装成指定IP发出的请求,常用于公司局域网协作办公 
1、http异步请求-标签与属性
  (1)标签请求1,<video>、<audio>、<img>、<script>、<link>、<iframe>等标签的src属性
  (2)标签请求2,<object>和<embed>标签的data属性,加载外部资源(如PDF、Flash)
  (3)ping属性,HTML5引入了<a>标签的ping属性,可以在用户点击链接时发送一个POST请求
    A、示例<a href="https://example.com" ping="https://example.com/track">Click Me</a>
    B、请求的格式如下:
      a、请求方法:POST
      b、请求头:
        POST /track HTTP/1.1
        Host: example.com
        Content-Type: text/ping
        Ping-From: https://current-page.com
        Ping-To: https://example.com
      c、请求体:空
2、http异步请求-长连接
  (1)SSE:服务器向客户端推送数据,Server-Sent Events,服务器发送事件 
    A、请求头示例
      GET /events HTTP/1.1
      Host: example.com
      Accept: text/event-stream
      Cache-Control: no-cache
      Connection: keep-alive
    B、前端代码
      const eventSource = new EventSource('https://example.com/sse');// 创建 EventSource 对象,连接到服务器端点
      eventSource.onmessage = function(event) {// 监听服务器推送的消息
        console.log('Received message:', event.data);
      };
      eventSource.addEventListener('customEvent', function(event) {// 监听自定义事件
        console.log('Received custom event:', event.data);
      });
      eventSource.onopen = function() {// 监听连接打开事件
        console.log('Connection opened');
      };
      eventSource.onerror = function() {// 监听错误事件
        console.error('Connection error');
      }; 
    C、响应头示例
      HTTP/1.1 200 OK
      Content-Type: text/event-stream
      Cache-Control: no-cache
      Connection: keep-alive
      Access-Control-Allow-Origin: *   
    D、后端代码
      const express = require('express');
      const app = express();
      app.get('/sse', (req, res) => {// 设置响应头
        res.setHeader('Content-Type', 'text/event-stream');
        res.setHeader('Cache-Control', 'no-cache');
        res.setHeader('Connection', 'keep-alive');
        res.write('data: Connected\n\n');// 发送初始数据
        const interval = setInterval(() => {// 定时推送数据
          const data = JSON.stringify({ time: new Date().toISOString() });
          res.write(`data: ${data}\n\n`);
        }, 1000);
        req.on('close', () => {// 处理客户端断开连接
          clearInterval(interval);
          res.end();
        });
      });
      app.listen(3000, () => {
        console.log('Server is running on http://localhost:3000');
      });  
  (2)WebSocket:全双工通信协议,允许服务器和客户端之间进行实时、双向的数据交换 update
    A、请求头示例
      GET /chat HTTP/1.1
      Host: example.com
      Upgrade: websocket
      Connection: Upgrade
      Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
      Sec-WebSocket-Version: 13
    B、前端代码
      <!DOCTYPE html>
      <html>
        <head>
          <title><%= title %></title>
          <link rel='stylesheet' href='/stylesheets/style.css' />
        </head>
        <body>
          <h1><%= title %></h1>
          <p>Welcome to <%= title %></p>
          <div id="clock"></div> 
        </body> 
        <script>   
          let socket = new WebSocket('ws://localhost:8888')
          socket.onopen = function () {     
            console.log('1. 客户端连接上了服务器', new Date().getTime());     
            socket.send('3. 你好')   
          }   
          socket.onmessage = function (e) {     
            console.log('6',e.data);   
          }
          ws.onerror = function(event) {
            console.error('WebSocket 连接出现错误:', event);
          };
          ws.onclose = function() {
            console.log('WebSocket 连接已经关闭。');
          };
        </script>
      </html>
    C、响应头示例
      HTTP/1.1 101 Switching Protocols
      Connection: Upgrade
      Upgrade: websocket
      Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
    D、后端代码
      var app = express();
      let WebSocket = require('ws')
      let wss = new WebSocket.Server({port:8888})
      wss.on('connection', function(ws) { 
        console.log('2.服务器监听到了客户端的连接', new Date().getTime()); 
        ws.on('message',function(data){   
          console.log('4.客户端发来的消息',data);   
          ws.send('5.服务端说:你也好') 
        })
      })
      module.exports = app;
3、http异步请求-常用(超文本传输协议,HyperText Transfer Protocol)
  附、Connection: keep-alive,
    A、现代浏览器默认使用持久连接,允许在一个TCP连接上发送多个HTTP请求,减少连接建立和断开的开销‌,
    B、比如一个TCP连接发送多个HTTP请求,加载多个js文件 
  (1)action请求,基于浏览器的原生表单提交机制
    A、是一种特殊的HTTP请求,通常用于执行一些需要长时间运行的操作,比如文件上传、视频播放等
    B、不会立即得到响应,而是会在后台异步执行,执行完成后会通过回调函数或者事件来通知用户
    C、避免阻塞用户界面,提高用户体验
  (2)ajax实现方案-XMLHttpRequest
    A、1994年,美国公司网景成立
    B、1995年,美国人布兰登·艾克加入网景,并在10天内创造出JavaScript语言
    C、1999年,微软创建一个名XMLHttpRequest的ActiveX控件,用于在JS中发起http请求
    D、2005年,ajax这个词第一次正式提出,它是“Asynchronous JavaScript and XML”的缩写,意为“异步JavaScript和XML”
    E、2006年,W3C开始制定XMLHttpRequest的标准
    F、ajax相关
      a、定义,“不重新加载整个页面”的情况下,通过“异步请求”与服务器进行数据交互,并“动态更新页面的部分内容”
      b、影响,可能引发重绘(元素的外观改变)和重排(元素的尺寸改变)。另,不初始化本地数据
      c、以前,提交请求后,页面刷新,服务器返回新的html页面
    G、示例
      const xhr = new XMLHttpRequest();
      xhr.open('GET', 'https://api.example.com/data', true); //true表示异步请求,false表示同步请求
      xhr.onload = function() {
        if (xhr.status == 200) {
          console.log('Response:', xhr.responseText);
        }
      };
      xhr.send();
  (3)ajax实现方案-fetch,
    A、避免XMLHttpRequest的地域回调
    B、在2015年独立的标准化的浏览器原生API
    C、基于Promise
  (4)sendBeacon,基于浏览器自身的网络栈和任务调度机制,不等服务端响应。navigator.sendBeacon(url, data); //导航员.发送信标
    A、优点
      a、不受页面卸载过程的影响,确保数据可靠发送
      b、异步执行,不阻塞页面关闭或者跳转,优先级较低,在空闲时间发送请求
      c、能够发送跨域请求
    B、缺点
      a、只能发送Http POST请求
      b、只能传输少量数据(64KB以内)
      c、无法自定义请求头
      d、只能传输ArrayBuffer、ArrayBufferView、Blob、DOMString、FormData或URLSearchParams类型数据
      e、如果处于危险网络环境、开启了广告屏蔽插件 该请求将无效
    C、示例
      window.addEventListener('unload', function() {
        const data = JSON.stringify({
          event: 'page_unload',
          time: Date.now(),
          userId: '12345'
        });
        navigator.sendBeacon('/log', data);
      });
4、跨域
  (1)相关定义
    A、同源策略,浏览器的安全机制要求页面地址栏和页面ajax的url,在协议、域名、端口号上都必须相同!!!
    B、跨源资源共享,一个源使用另一个源的数据,形成跨域,CORS,Cross-Origin Resource Sharing
  (2)用标签的属性实现跨域
    A、href,
      跨域,<link href='xxxxxx'></link>,
      跳转,<a href="xxxxxx">不属于跨域</a>
    B、src,
      <img src='xxxxxx'/>,
      <script src='xxxxxx'></script>(可用于JSONP)
      <iframe src="https://www.w3school.com.cn" title="W3School 在线教程"></iframe>
  (3)用JSONP实现跨域
    A、前端代码
      <script>
        // 定义回调函数
        function handleResponse(data) {
          elementA.innerText = JSON.stringify(data, null, 2);
        }
        // 点击按钮时发起 JSONP 请求
        elementB.onclick = function() {
          const script = document.createElement('script');
          script.src = 'https://example.com/api?callback=handleResponse';//把定义的函数传给后端
          document.body.appendChild(script);
        };
      </script>
    B、后端代码
      const express = require('express');
      const app = express();
      app.get('/api', (req, res) => {
        const callback = req.query.callback; // 获取客户端传递的回调函数名
        const data = { message: 'Hello, JSONP!' };
        res.send(`${callback}(${JSON.stringify(data)})`);// 前端把返回值当js代码执行
      });
      app.listen(3000, () => {
        console.log('Server is running on http://localhost:3000');
      });
  (4)浏览器对ajax跨域的处理流程
    A、浏览器检测出跨域,在请求头中添加Origin字段,该字段不能手动修改
    B、跨域之预检请求!!!
      a、满足下列情形之一,浏览器发送预检请求-options
        请求方法为PUT、DELETE、PATCH,中的一个
        请求头包含Authorization字段
        请求头包含Content-Type且取值不是text/plain、application/x-www-form-urlencoded、multipart/form-data
      b、服务器响应预检请求
        如果同意跨域,在响应头中添加Access-Control-Allow-Origin字段,
        如果不同意跨域,浏览器会阻止实际请求,并抛出CORS错误
    C、浏览器发送实际请求,请求头仍会包含Origin字段
    D、服务器返回最终数据,其中响应头必须包含Access-Control-Allow-Origin字段
  (5)前端开发对跨域的处理流程
    A、前端代码//webpack.config.js
      const webpack = require('webpack');
      const path = require('path'); 
      module.exports = {
        devServer: {
          contentBase: path.join(__dirname, 'dist'),
          port: 3000,
          before: require('./mock/mock-server.js'),
          proxy: {
            "/app": { //key不同,对应的target不同,同一个项目,可以配置多个目标服务器
              // 跨域之绕过跨域的实现原理!!!
              // 浏览器先向本地代理服务器发送同源请求;本地代理服务器
              //   (1)根据target修改Origin字段
              //   (2)根据target、devServer.proxy的key、请求路径中第一个斜杠部分,修改请求路径
              // 将请求转发到目标服务器,目标服务器处理请求并返回响应,本地代理服务器将响应返回给浏览器
              // 注意:是本地代理服务器转发浏览器对目标服务器的访问;服务器之间直接访问,没有跨域问题
              // 请求处理方案的优先级从大往小为,Mock.js、devServer.proxy、直接发出(浏览器跨域)
              // 判断依据:相同请求,前者生效
              target: 'http://192.168.10.231:8080/', //目标服务器的域名
              changeOrigin: true, //true,代理服务器将请求头中的Origin字段修改为目标服务器的域名
              pathRewrite: {
                "^/app": "/" //重写接口
              }
            },
          },
        }
      };
    B、后端代码
      const http = require('http');
      const server = http.createServer((req, res) => { //跨域之设置跨域头!!!
        res.setHeader('Access-Control-Allow-Origin', 'https://your-website.com'); //必需此项
        res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); //非必需此项
        res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); //非必需此项
        res.setHeader('Access-Control-Allow-Credentials', 'true'); //非必需此项
        // 以下,不常用
        res.setHeader('Access-Control-Expose-Headers', 'X-Custom-Header'); //允许浏览器访问的自定义头
        res.setHeader('Access-Control-Max-Age', 86400); //指定预检请求结果的缓存时间(秒),24小时
        // 处理预检请求(OPTIONS)
        if (req.method === 'OPTIONS') {
          res.writeHead(200);
          res.end();
          return;
        }
        // 正常请求
        if (req.method === 'GET' && req.url === '/data') {
          res.writeHead(200, { 'Content-Type': 'application/json' });
          res.end(JSON.stringify({ message: '跨域请求成功!' }));
        } else {
          res.writeHead(404);
          res.end('Not Found');
        }
      });
      server.listen(3000, () => {
        console.log('服务器运行在 http://localhost:3000');
      });

三、axios请求拦截封装
1、REST,软件架构风格,2000年提出
  附、REST,Representational State Transfer,表述性状态转移
  (1)目的,规范组件间的通信方式,规范分布式系统中组件(如客户端与服务器)之间的通信方式
  (2)应用,Web应用、移动应用、微服务间通信、第三方开放平台(如微信开放平台)
  (3)原则(约束)。前后端分离、无状态、可缓存、统一接口、分层系统、按需代码
    A、前后端(客户端-服务器)分离
    B、无状态,每个请求包含所有必要信息,服务器不保存会话状态!!!
    C、可缓存,响应应明确标识是否可缓存
    D、统一接口,核心原则(约束),包含4个子原则
      a、资源标识(URI)
      b、通过表述操作资源(如JSON/XML)
      c、自描述消息
      d、超媒体作为应用状态引擎(HATEOAS)
    E、分层系统,客户端不知道是否直接连接最终服务器
    F、按需代码,服务器可以临时扩展客户端功能
  (4)RESTful-API的定义,
    A、是一种基于REST架构风格设计的应用程序接口(API)
    B、主要用于不同系统之间的数据交互,尤其是客户端(如Web/移动端)与服务器之间的通信
    C、它的核心目标是标准化、简化网络通信,使系统更高效、可扩展和易于维护
  (5)组件/软件一般分为三个层次:表现层、业务逻辑层(接口)、数据层
    A、表现层:负责界面展示
    B、业务逻辑层:接收表现层的请求,向数据层请求数据,将数据返给表现层
      a、接口,包含url、method、参数、返回值
      b、业务,应用程序中的具体任务。前端程序员不仅要关注展示,还应关注业务,弄清不同业务之间的关系
      c、业务逻辑,应用程序中,实现具体任务的代码
    C、数据层:负责数据存储、读取   
  (6)有状态api与无状态api
    A、有状态api,认证方式:cookie + session
      a、有状态api--服务器保存会话数据--如session;
      b、用户登录,后端创建session,响应头含cookie;用户再请求,浏览器自动携带Cookie(session_id),后端用session_id查询session
    B、无状态api,认证方式:Authorization(API Key) + token (JWT)
      a、无状态api--客户端保存会话数据--如token
      b、用户登录,后端返回Token,前端存储到localStorage,后续在请求头Authorization中携带Token(如Bearer token)
      附、'Authorization': 'Bearer token', // '授权': '提单持有人 令牌'
      附、JWT,JSON Web Token,JSON网络令牌
2、ajax请求实例
  (1)请求发出前 
    var xhr = new XMLHttpRequest();
    xhr.open(method, url, boolean);
    // GET请求:参数通过?key=value拼接在URL中,send(data)无效
    // POST请求:参数需通过send(data)发送,URL中的?参数可能被忽略(取决于服务器)
    // boolean,默认为true,为异步;false为同步,阻塞页面,已废弃
    // #锚点:不会发送到服务器(浏览器本地定位)
    // 附、Axios行为:
    //   get:参数通过params拼接到URL
    //   post:参数通过data放在请求体
    //   delete:部分浏览器允许URL参数 + 请求体,但非所有服务器支持
    xhr.withCredentials = true; //默认false,
    // 附、跨域之不需要携带证书,不需要任何配置
    // 附、跨域之需要携带证书(Cookies、Authorization字段),必须满足以下2个条件!!!
    //   A、前端设置,xhr.withCredentials = true;//证书/凭证
    //   B、后端设置,Access-Control-Allow-Credentials: true;且,Access-Control-Allow-Origin不能为*,必须是请求来源的域名!
    xhr.timeout = 10 * 1000;
    // 指定10秒钟超时,如果该属性等于0,就表示没有时间限制
    xhr.overrideMimeType('text/plain');
    // MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型
    // MIME类型通常由两部分组成,格式为:type/subtype,主类型/子类型
    // 最初,是为了在电子邮件中支持非文本内容(如图片、音频、视频等)而设计的
    // 现在,指定浏览器用哪个应用程序打开文件,覆盖服务器返回的真正的MIME类型
    xhr.responseType = "blob"; // 优先级高于overrideMimeType
    // 作用:告诉XMLHttpRequest如何自动解析接收到的响应数据!!!
    // 注意:是一个纯客户端配置,不会作为HTTP请求的一部分发送给服务器
    // 1. 请求实例的“responseType”字段值为字符串类型,合法取值包括:
    //    "text"(纯文本)、"blob"(二进制大对象)、"json"(自动解析为JSON对象)、
    //    "arraybuffer"(二进制缓冲区)、"document"(解析为XML Document对象)
    // 2. 默认值为''(空字符串),效果等同于"text",响应数据会作为字符串处理
    xhr.setRequestHeader(key, value); 
    // 作用:设置HTTP请求头。当key为“Content-Type”时,告诉服务器当前请求体(Request Body)的数据格式!!!
    // 注意:若设置“Content-Type”请求头,其值必须是标准MIME类型,常见取值包括:
    //    application/x-www-form-urlencoded(表单默认格式)、application/json(JSON格式)、
    //    multipart/form-data(文件上传格式)、text/plain(纯文本请求体)
    // 设置请求头,修改请求头,下面2个设置与跨域之是否无关
    // xhr.setRequestHeader('Authorization', 'Bearer token'); 
    // xhr.setRequestHeader('Content-Type', 'application/json'); 
    xhr.ontimeout = function () {//超时触发,需结合timeout属性使用
      console.error('The request for ' + url + ' timed out.');
    };
    xhr.onabort = function () {//调用abort()后触发
      console.log('The request was aborted');
    };
    xhr.onload = function() {//请求成功完成时触发
      console.log();
    };
    xhr.loadend = function () {//请求结束,状态未知
      console.log();
    };
    xhr.upload.onprogress = function (event) {//仅上传进度,event对象有三个属性
      console.log(event.loaded);//返回已经传输的数据量
      console.log(event.total);//返回总的数据量
      console.log(event.lengthComputable);//返回一个布尔值,表示加载的进度是否可以计算
      if (event.lengthComputable) {
        var percentComplete = (event.loaded / event.total) * 100;
        console.log('Download progress: ' + percentComplete.toFixed(2) + '%');
      }
    };
    xhr.onprogress = function(event) {//仅下载进度,event对象有三个属性
      console.log(event.loaded);//返回已经传输的数据量
      console.log(event.total);//返回总的数据量
      console.log(event.lengthComputable);//返回一个布尔值,表示加载的进度是否可以计算
      if (event.lengthComputable) {
        var percentComplete = (event.loaded / event.total) * 100;
        console.log('Download progress: ' + percentComplete.toFixed(2) + '%');
      }
    };
    xhr.onreadystatechange = function () {//实例事件
      // xhr.readyState,表示XMLHttpRequest请求的当前状态,由浏览器自动更新以反映请求进度。改变会触发onreadystatechange事件
      //  A、调用open()方法前,xhr.readyState为0
      //  B、调用open()方法后,xhr.readyState为1时,代表请求已初始化
      //  C、调用send()方法后,建立TCP连接(三次握手)、发送HTTP请求
      //  D、xhr.readyState为2时,代表响应头已接收
      //  E、xhr.readyState为3时,代表响应体下载中
      //  F、xhr.readyState为4时,代表响应体已完全接收,请求已完成
      //  G、关闭TCP连接(四次挥手)
      // xhr.status,表示服务器响应的HTTP状态码,由服务器生成并返回
      //  200: OK,访问正常
      //  301: Moved Permanently,永久移动
      //  302: Move temporarily,暂时移动
      //  304: Not Modified,未修改
      //  307: Temporary Redirect,暂时重定向
      //  401: Unauthorized,未授权
      //  403: Forbidden,禁止访问
      //  404: Not Found,未发现指定网址
      //  500: Internal Server Error,服务器发生错误
      if (xhr.readyState==4 && xhr.status==200){
        console.log(xhr.response);//按照xhr.responseType纯客户端配置,返回解析后的数据!!!
      }
    };
    xhr.send();
    // GET请求,通常用send(null)或send()
    // POST请求,需传入数据(如send(JSON.stringify(data)))
  (2)请求发出后 
    xhr.abort();//执行后发生了以下事情 
      // readyState变为4,status变为0
      // 触发onabort事件
      // 服务器可能继续处理请求,但客户端不再接收响应
    xhr.getAllResponseHeaders();
      // 返回一个字符串,表示服务器发来的所有HTTP头信息,每个头信息之间使用(回车+换行)分隔
      // 跨域限制:默认仅能访问简单响应头(如 Cache-Control),需服务器通过 Access-Control-Expose-Headers 暴露其他头
    xhr.getResponseHeader("Last-Modified");//返回HTTP头信息指定字段的值
3、axios简介
  (1)请求方式
    A、get:参数通过params拼接到URL
    B、post:参数通过data放在请求体
    C、delete:部分浏览器允许URL参数 + 请求体,但非所有服务器支持
  (2)原理:axios包含Promise;Promise包含ajax;ajax.onload包含resolve。伪代码如下
    来源,https://blog.csdn.net/weixin_44952258/article/details/121896095
    function axios(URL) {
      //interceptors拦截请求
      return new Promise(function (resolve, reject) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', URL, true);
        xhr.responseType = 'json'; //可以是 'text', 'json', 'blob', 'arraybuffer', 'document' 等
        xhr.onload = function () {
          //interceptors拦截响应
          if (xhr.status === 200) {
            resolve(xhr.responseText);
          } else {
            reject(new Error(xhr.statusText));
          }
        };
        xhr.onerror = function () {
          reject(new Error(xhr.statusText));
        };
        xhr.send();
      });
    }
  (3)Promise执行机制(then、catch、finally)!!!
    A、执行Promise.resolve(value)或返回非Promise类型值,其后若是catch则跳过,是then则执行其第1参
    B、执行Promise.reject(error)或执行throw error,其后若是then独参则跳过,是then两参则执行其第2参,是catch则执行
    C、在不同的异步操作中主动封装不同的错误信息,通过外层catch统一捕获所有请求的错误,通过其参数判断来自于哪个请求
    D、在Promise链的末尾使用finally方法,统一执行清理操作,无论请求成功(then)还是失败(catch)
  (4)Error、throw、catch说明
    A、Error内置对象,其实例包含3个属性,其中message是错误信息(值是其构造函数的参数),name是错误类型,stack是错误堆栈
    B、throw语句,中断当前代码执行流程,并将一个任意类型的错误信息传递给错误处理机制,未被捕获的错误信息会显示在控制台
    C、catch函数,用于捕获try代码块中,throw、Promise.reject抛出的错误,用一个参数接收捕获到的错误信息
    D、Promise.reject(err)。err为普通对象与Error对象的区别,在错误信息的完整性和调试便利性上
  (5)httpRequest.js
    import axios from 'axios';
    import router from './router';
    import { loading } from './loading'; 
    const axiosInstance = axios.create({
      baseURL: 'https://api.example.com',
      timeout: 5000,
      headers: { 'Content-Type': 'application/json' },
    });
    //请求拦截器
    axiosInstance.interceptors.request.use(
      (config) => { //这里的config就是调用时传入的配置对象,即下面httpRequest的参数
        const token = localStorage.getItem('token');
        if (token) {
          config.headers.Authorization = `Bearer ${token}`;
        }
        loading.open(); //开启加载动画
        return config; //返回修改后的配置
      },
      (error) => {
        loading.close(); 
        const url = error.config?.url || '未知URL';
        const errorMsg = `请求未发出。url为:${url}`;
        return Promise.reject(new Error(errorMsg)); 
      }
    );
    //响应拦截器
    axiosInstance.interceptors.response.use(
      (response) => { //response.config就是经过请求拦截器处理后的配置对象
        loading.close();
        //处理二进制响应
        if (['blob', 'arraybuffer'].includes(response.request?.responseType)) {
          return response;
        }
        const { code, message } = response.data;
        const { url, method } = response.config;
        const path = url ? url.split('?')[0] : '未知路径';
        if (code === 200) {
          return response.data;
        } else if (code === 401) {
          const errorMsg = `登录已过期(接口:${path})`;
          router.push('/login');
          return Promise.reject(createBusinessError(errorMsg, code, message, url, path, method));
        } else {
          const errorMsg = `业务处理失败:${message || '未知错误'}(接口:${path})`;
          return Promise.reject(createBusinessError(errorMsg, code, message, url, path, method));
        }
      },
      (error) => {
        loading.close();
        let errorMsg = '响应出错';
        const status = error.response?.status;
        const path = error.response?.config?.url || '未知路径';
        //根据HTTP状态码提供更具体的错误信息
        if (status === 401) {
          errorMsg = '身份验证失败';
          router.push('/login');
        } else if (status === 403) {
          errorMsg = '权限不足';
        } else if (status === 404) {
          errorMsg = '请求资源不存在';
        } else if (status >= 500) {
          errorMsg = '服务器错误';
        } else if (error.code === 'ECONNABORTED') {
          errorMsg = '请求超时';
        } else if (!error.response) {
          errorMsg = '网络连接失败';
        }
        const err = new Error(errorMsg);
        err.status = status;
        err.path = path;
        return Promise.reject(err);
      }
    );
    //创建业务错误的辅助函数
    function createBusinessError(message, code, businessMessage, url, path, method) {
      const error = new Error(message);
      error.code = code; //业务状态码
      error.businessMessage = businessMessage; //业务错误信息
      error.url = url; //完整URL
      error.path = path; //接口路径
      error.method = method; //请求方法
      error.isBusinessError = true; //标记为业务错误
      return error;
    }
    export default axiosInstance; //axiosInstance就是下面的httpRequest
  (6)sendToServer.js
    import httpRequest from './httpRequest';
    const sendToServer1 = (params) => {
      return httpRequest({
        url: '/server1',
        method: 'post',
        data: params,
      });
    };
    const sendToServer2 = (params) => {
      return httpRequest({
        url: '/server2',
        method: 'post',
        data: params,
      });
    };
    const sendToServer3 = (params) => {
      return httpRequest({
        url: '/server3',
        method: 'post',
        data: params,
      });
    };
    export { sendToServer1, sendToServer2, sendToServer3 };
  (7)业务逻辑(getInfo.js)
    import { sendToServer1, sendToServer2, sendToServer3 } from './sendToServer';
    import { ElMessageBox, ElMessage } from 'element-plus';
    const getInfo = function (row) {
      ElMessageBox.confirm(
        '确认执行操作吗?',
        '提示',
        {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'info'
        }
      )
      .then(() => {
        return sendToServer1({ ids: [row.id] });
      })
      .then((res) => {
        return sendToServer2({ ids: [row.id] });
      })
      .then((res) => {
        return sendToServer3({ ids: [row.id] });
      })
      .then((res) => {
        console.log(res.data);
      })
      .catch((err) => {
        if (err.message && err.message.includes('cancel')) {
          console.log('用户取消操作');
          return;
        }
        return ElMessage.error(err.msg);
      });
    };

四、custPromise源码与验证!!!
1、custPromise源码,共40行
  function custPromise(callback) { //父级,callback常为异步函数
    this.value = null;
    this.status = "pending";
    this.cbArray = [];
    callback(this.resolve.bind(this));
  }
  custPromise.prototype.then = function (success) { //1、父级cbArray存储父级success
    var that = this;
    if (this.status == "pending") {
      return new custPromise(function (resolve) { //子级,callback必为同步函数
        that.cbArray.push(function () {//push参数callbackIn
          var result = success(that.value); //第1次父级success返回父参实例;第2次子级resolve返回undefined;
          if (result instanceof custPromise) {
            result.then(resolve); //3、父参cbArray存储子级resolve
          }else if(typeof result != "undefined"){
            resolve(result);
          }
        });
      });
    }
    if (this.status == "resolve") {
      return new custPromise(function (resolve) {
        var result = success(that.value);
        if (result instanceof custPromise) {
          result.then(resolve);
        }else if(typeof result != "undefined"){
          resolve(result);
        }
      });
    }
  };
  //2、父级resolve调用父级success;
  //4、父参resolve调用子级resolve,进而调用子级success
  custPromise.prototype.resolve = function (value) { 
    this.value = value;
    this.status = "resolve";
    if (this.cbArray.length > 0) {
      this.cbArray.forEach(function (callbackIn) {
        callbackIn(); //push参数callbackIn
      });
    }
  };
2、custPromise验证
  (1)全异步
    new custPromise(function (resolve) {
      setTimeout(function () {
        resolve(1);
      }, 2000);
    })
    .then(function (value) { 
      return new custPromise(function (resolve) {
        setTimeout(function () {
          console.log('此处1的value为:' + value)
          resolve(value + 1);
        }, 1000);
      });
    })
    .then(function (value) { 
      return new custPromise(function (resolve) {
        setTimeout(function () {
          console.log('此处2的value为:' + value)
          resolve(value + 1);
        }, 1000);
      });
    })
    .then(function (value) {
      return new custPromise(function (resolve) {
        setTimeout(function () {
          console.log('此处3的value为:' + value)
          resolve(value + 1);
        }, 1000);
      });
    })
    .then(function (value) {
      return new custPromise(function (resolve) {
        setTimeout(function () {
          console.log('此处4的value为:' + value)
          resolve(value + 1);
        }, 1000);
      });
    })
  (2)全同步,可以去掉return看效果
    new custPromise(function (resolve) {
      resolve(0);
    })
    .then(function (value) {
      console.log('此处1的value为:' + value)
      return value + 1;
    })
    .then(function (value) {
      console.log('此处2的value为:' + value)
      return value + 1;
    })
    .then(function (value) {
      console.log('此处3的value为:' + value)
      return value + 1;
    })
    .then(function (value) {
      console.log('此处4的value为:' + value)
      return value + 1;
    })
    
五、JS主要模块规范及加载器
 附、说明:
  A、CommonJS的同步加载特性仅适用于服务器端,如Node.js,若在浏览器端使用,会导致页面阻塞,需通过工具(如Browserify)转换
  B、AMD是提前执行(预执行),AMD中require声明的所有依赖模块会被提前并行加载,待所有依赖加载完成后,模块工厂函数立即执行
  C、CMD是延迟执行(按需执行),CMD中require声明的模块会被提前并行加载,但模块工厂函数仅在代码运行到具体调用该模块的位置时才会执行
1、common.js
  (1)2009年,首次出现common.js,名为ServerJS,为服务器端设计的同步模块规范,如Node.js
  (2)使用: 
    A、模块缓存机制:同一模块被require多次时,仅首次加载执行,后续直接返回缓存结果,避免重复执行
    B、文件
      //math.js,定义一个提供数学工具的模块
      exports.add = function(a, b) {
        return a + b;
      };
      exports.subtract = function(a, b) {
        return a - b;
      };
      //logger.js,定义一个日志工具模块
      const fs = require('fs'); //导入Node.js的fs模块用于文件操作
      exports.log = function(message) {
        console.log(message);
        //将日志写入文件
        fs.appendFile('app.log', message + '\n', (err) => {
          if (err) throw err;
        });
      };
      //main.js,主应用模块
      const math = require('./math');
      const logger = require('./logger');
      logger.log('应用已启动');
      const result = math.add(5, 3);
      logger.log('5 + 3 = ' + result);
    C、服务器
      //在Node.js中同步加载模块
      const http = require('http'); //同步加载Node.js的http模块
      const server = http.createServer((req, res) => {
        res.end('Hello World!');
      });
      server.listen(3000, () => {
        console.log('服务器运行在端口3000');
      });
2、require.js
  (1)2010年,首次出现require.js,实现了AMD规范 
  (2)使用:
    A、项目结构示例:
      // project/
      // ├── index.html  
      // ├── js/  
      // │   ├── app/       <-- 应用模块目录  
      // │   │   ├── math.js  
      // │   │   └── logger.js  
      // │   ├── lib/       <-- RequireJS 和第三方库目录  
      // │   │   ├── require.js  
      // │   │   └── other-lib.js  
      // │   └── main.js    <-- 主入口文件(补充)  
    B、子文件
      //math.js,定义一个提供数学工具的模块
      define(function() {
        return {
          add: function(a, b) {
            return a + b;
          },
          subtract: function(a, b) {
            return a - b;
          }
        };
      });
      //logger.js,定义一个日志工具模块
      define(['jquery'], function($) {
        return {
          log: function(message) {
            $('#log').append('<div>' + message + '</div>');
            console.log(message);
          }
        };
      });
    C、主文件
      //main.js,主应用模块
      require.config({ //配置模块加载
        baseUrl: 'js/lib', //设置模块的基础加载路径,当你加载未指定完整路径的模块,如'math'时,RequireJS会自动从js/lib目录下查找
        paths: {
          app: '../app', //有“../”,表示从baseUrl向上一级目录,再进入app目录;无“../”,表示从baseUrl直接进入app目录
          jquery: 'https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min', //模块名jquery直接映射到CDN地址
        }
      });
      require(['app/math', 'app/logger'], function(math, logger) {
        //模块加载完成后立即执行
        logger.log('应用已启动');
        const result = math.add(5, 3);
        logger.log('5 + 3 = ' + result);
      });
    D、在HTML页面中使用RequireJS加载这个应用的示例
      <!DOCTYPE html>
      <html>
        <head>
          <title>RequireJS AMD示例</title>
          <script data-main="js/main" src="js/lib/require.js"></script>
          <!-- data-main是RequireJS的一个特殊属性,用于指定应用的主入口模块 -->
        </head>
        <body>
          <div id="log"></div>
        </body>
      </html>
3、sea.js
  (1)2011年,首次出现sea.js,实现了CMD规范 
  (2)使用:
    A、项目结构示例:
      // project/  
      // ├── index.html  
      // ├── js/  
      // │   ├── app/   <-- 应用模块目录  
      // │   │   ├── math.js  
      // │   │   └── logger.js  
      // │   ├── lib/       <-- Sea.js 和第三方库目录  
      // │   │   ├── sea.js  
      // │   │   └── jquery.js  
      // │   └── main.js    <-- 主入口文件(补充)  
      // └── css/   <-- 样式文件 
    B、子文件
      //math.js,定义一个提供数学工具的模块  
      define(function(require, exports, module) {  
        //按需加载依赖(延迟执行)  
        const logger = require('./logger');
        exports.add = function(a, b) {  
          logger.log(`计算 ${a} + ${b}`);  
          return a + b;  
        };
        exports.subtract = function(a, b) {  
          return a - b;  
        };  
      }); 
      //logger.js,定义一个日志工具模块  
      define(function(require, exports, module) {  
        const $ = require('jquery'); //按需加载jQuery  
        
        exports.log = function(message) {  
          console.log('[Logger] ' + message);  
          if ($) $('#log').append(`<div>${message}</div>`);  
        };  
      }); 
    C、主文件
      //main.js,主应用模块  
      seajs.config({  
        base: '/js/',  //统一配置基础路径  
        alias: {  
          'jquery': 'lib/jquery-3.6.0.min.js', //映射本地或CDN资源  
        }  
      }); 
      seajs.use(['app/math', 'app/logger'], function(math, logger) {  
        logger.log('应用已启动'); //立即执行初始化日志  
        //动态加载(CMD优势):2秒后再加载并使用新模块  
        setTimeout(() => {  
          const asyncModule = require('./asyncModule'); //按需加载  
          asyncModule.init();  
        }, 2000);  
      }); 
    D、在HTML页面中使用Sea.js加载这个应用的示例
      <!DOCTYPE html>  
      <html>  
        <head>  
          <title>Sea.js CMD示例</title>  
          <script src="js/lib/sea.js"></script> <!-- 先加载Sea.js -->  
          <script> 
            seajs.use('js/main'); //加载主模块(路径与main.js的实际位置匹配)  
          </script>  
        </head>  
        <body>  
          <div id="log"></div>  
        </body>  
      </html> 

六、编程范式,7种
1、命令式编程 (Imperative Programming)
  (1)核心思想:通过明确的指令步骤("如何做")修改程序状态
  (2)特点:依赖变量、循环和条件语句
  (3)示例
    const numbers = [1, 2, 3, 4, 5];
    let sum = 0; // 初始化状态变量
    // 显式地通过循环逐步修改状态
    for (let i = 0; i < numbers.length; i++) {
      sum += numbers[i]; // 修改状态
    }
    console.log(sum); // 输出: 15
2、响应式编程 (Reactive Programming)
  (1)核心思想:基于 数据流 和 事件驱动,自动响应变化
  (2)典型库:RxJS、ReactiveX
  (3)示例(RxJS 监听点击事件):
    import { fromEvent } from 'rxjs';
    const button = document.querySelector('button');
    fromEvent(button, 'click')
      .subscribe(() => console.log('Button clicked!')); //自动响应事件
  (4)RxJS简介说明介绍
    A、是“Reactive Extensions for JavaScript”的缩写,
    B、它是一个基于“观察者模式”和“函数式编程”的库,
    C、用于处理异步数据流(如事件、HTTP 请求、定时任务等)
    D、CDN,https://www.bootcdn.cn/rxjs/
    E、中文文档,https://cn.rx.js.org/
  (5)RxJS版本发布历史
    A、RxJS1,2012年发布
    B、RxJS2,2014年发布
    C、RxJS3,2015年发布
    D、RxJS4,2016年发布,短暂存在,很快被RxJS5取代
    E、RxJS5,2016年发布,彻底重写(基于ES6+),API更规范,但兼容性差
    F、RxJS6,2018年发布
    G、RxJS7,2020年发布,大约7千行
    H、RxJS8,2023年发布
3、函数式编程 (FP)
  (1)核心思想:用 纯函数 和 不可变数据 构建程序,避免副作用
  (2)关键概念:高阶函数、柯里化、递归
  (3)语言:Haskell、Lisp、JavaScript(部分支持)
  (4)示例(JavaScript 高阶函数)
    // 纯函数:无副作用,固定输入 → 固定输出
    const add = (a, b) => a + b;
    // 高阶函数:接收函数作为参数
    const map = (arr, fn) => arr.map(fn);
    const numbers = [1, 2, 3];
    const squared = map(numbers, x => x * x);  // [1, 4, 9](原数组不变)
4、面向对象编程 (OOP)
  (1)核心思想:用 对象 封装数据和方法,通过继承和多态组织代码
  (2)四大支柱:封装、继承、多态、抽象
  (3)示例
    // 构造函数方式
    function Person(name, age) {
      // 属性
      this.name = name;
      this.age = age;
      // 方法
      this.greet = function() {
        console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
      };
    }
    // 创建实例
    const person1 = new Person('Alice', 25);
    const person2 = new Person('Bob', 30);
    person1.greet(); // 输出: Hello, my name is Alice and I'm 25 years old.
    person2.greet(); // 输出: Hello, my name is Bob and I'm 30 years old.
5、泛型编程 (Generic Programming)
  (1)核心思想:编写 与数据类型无关 的通用代码
  (2)实现方式:模板(C++)、泛型(Java/TypeScript)
  (3)示例(TypeScript 泛型函数)
    function identity<T>(arg: T): T {  // T 为类型参数
      return arg;
    }
    const num = identity<number>(42);  // 类型安全
    const str = identity<string>("Hello");
6、声明式编程 (Declarative Programming)
  (1)核心思想:描述 "做什么",而非具体步骤
  (2)子类:SQL、HTML、Prolog
  (3)示例(SQL 查询):
    -- 声明式:只描述需求,不关心如何遍历数据
    SELECT name, age FROM users WHERE age > 18 ORDER BY age DESC;
7、逻辑编程 (Logic Programming)
  (1)核心思想:通过 规则和事实 推导结果(如数学证明)
  (2)语言:Prolog
  (3)示例(Prolog 家族关系)
    father(john, bob).  % 事实:John 是 Bob 的父亲
    parent(X, Y) :- father(X, Y).  % 规则:父亲是父母的一种
 
七、设计模式,10种
  附、js常见的设计模式及其示例,deepseek
1、单例模式 (Singleton),确保一个类只有一个实例,并提供全局访问点
  附、单例模式、工厂模式、模块模式;策略模式、适配器模式、代理模式;装饰器模式、职责链模式;观察者模式、发布-订阅模式
  class Singleton {
    constructor() {
      if (!Singleton.instance) {
        Singleton.instance = this;
      }
      return Singleton.instance;
    }
    log(message) {
      console.log(`Logger: ${message}`);
    }
  }
  // 使用示例
  const logger1 = new Singleton();
  const logger2 = new Singleton();
  logger1.log("First message"); // Logger: First message
  logger2.log("Second message"); // Logger: Second message
  console.log(logger1 === logger2); // true
2、工厂模式 (Factory),创建对象而不指定具体类
  附、单例模式、工厂模式、模块模式;策略模式、适配器模式、代理模式;装饰器模式、职责链模式;观察者模式、发布-订阅模式
  class Car {
    constructor(options) {
      this.type = options.type || 'sedan';
      this.color = options.color || 'white';
    }
  }
  class Truck {
    constructor(options) {
      this.type = options.type || 'truck';
      this.color = options.color || 'blue';
    }
  }
  class VehicleFactory {
    createVehicle(options) {
      switch(options.vehicleType) {
        case 'car':
          return new Car(options);
        case 'truck':
          return new Truck(options);
        default:
          throw new Error('Unknown vehicle type');
      }
    }
  }
  // 使用示例
  const factory = new VehicleFactory();
  const myCar = factory.createVehicle({
    vehicleType: 'car',
    color: 'red'
  });
  const myTruck = factory.createVehicle({
    vehicleType: 'truck'
  });
  console.log(myCar); // Car { type: 'sedan', color: 'red' }
  console.log(myTruck); // Truck { type: 'truck', color: 'blue' }
3、模块模式 (Module),将相关的属性和方法封装在一个模块中,提供私有和公共的访问级别
  附、单例模式、工厂模式、模块模式;策略模式、适配器模式、代理模式;装饰器模式、职责链模式;观察者模式、发布-订阅模式
  const MyModule = (function() {
    // 私有变量
    let privateVar = 'I am private';
    // 私有函数
    function privateMethod() {
      console.log('Private method called');
    }
    // 公共接口
    return {
      publicVar: 'I am public',
      publicMethod: function() {
        console.log('Public method called');
        console.log('Accessing private var: ' + privateVar);
        privateMethod();
      },
      getPrivateVar: function() {
        return privateVar;
      },
      setPrivateVar: function(value) {
        privateVar = value;
      }
    };
  })();
  // 使用示例
  console.log(MyModule.publicVar); // I am public
  MyModule.publicMethod();
  // 输出:
  // Public method called
  // Accessing private var: I am private
  // Private method called
  console.log(MyModule.getPrivateVar()); // I am private
  MyModule.setPrivateVar('New private value');
  console.log(MyModule.getPrivateVar()); // New private value
4、策略模式 (Strategy),定义一系列算法,封装每个算法,并使它们可以互换
  附、单例模式、工厂模式、模块模式;策略模式、适配器模式、代理模式;装饰器模式、职责链模式;观察者模式、发布-订阅模式
  class PaymentStrategy {
    pay(amount) {
      throw new Error('This method must be implemented');
    }
  }
  class CreditCardPayment extends PaymentStrategy {
    pay(amount) {
      console.log(`Paying $${amount} using Credit Card`);
    }
  }
  class BankCardPayment extends PaymentStrategy {
    pay(amount) {
      console.log(`Paying $${amount} using BankCard`);
    }
  }
  class ShoppingCart {
    constructor() {
      this.amount = 0;
      this.strategy = null;
    }
    setPaymentStrategy(strategy) {
      this.strategy = strategy;
    }
    checkout() {
      if (!this.strategy) {
        throw new Error('Payment strategy not set');
      }
      this.strategy.pay(this.amount);
    }
  }
  // 使用示例
  const cart = new ShoppingCart();
  cart.amount = 100;
  cart.setPaymentStrategy(new CreditCardPayment());
  cart.checkout();
  cart.setPaymentStrategy(new BankCardPayment());
  cart.checkout();
5、适配器模式 (Adapter),将一个类的接口转换成客户希望的另一个接口
  附、单例模式、工厂模式、模块模式;策略模式、适配器模式、代理模式;装饰器模式、职责链模式;观察者模式、发布-订阅模式
  // 旧版打印机接口(不兼容)
  class LegacyPrinter {
    printDocument(text) {
      console.log(`Legacy Printing: ${text}`);
    }
  }
  // 新版打印机接口标准
  class ModernPrinter {
    print(text) {
      throw new Error("Method not implemented");
    }
  }
  // 适配器
  class PrinterAdapter extends ModernPrinter {
    constructor(legacyPrinter) {
      super();
      this.legacyPrinter = legacyPrinter;
    }
    print(text) {
      this.legacyPrinter.printDocument(text); // 接口转换
    }
  }
  // 使用
  const legacy = new LegacyPrinter();
  const adapter = new PrinterAdapter(legacy);
  adapter.print("Hello");
6、代理模式 (Proxy),为其他对象提供一种代理以控制对这个对象的访问
  附、单例模式、工厂模式、模块模式;策略模式、适配器模式、代理模式;装饰器模式、职责链模式;观察者模式、发布-订阅模式
  class RealImage {
    constructor(filename) {
      this.filename = filename;
      this.loadFromDisk();
    }
    loadFromDisk() {
      console.log(`Loading ${this.filename}`);
    }
    display() {
      console.log(`Displaying ${this.filename}`);
    }
  }
  class ProxyImage {
    constructor(filename) {
      this.filename = filename;
      this.realImage = null;
    }
    display() {
      if (!this.realImage) {
        this.realImage = new RealImage(this.filename);
      }
      this.realImage.display();
    }
  }
  // 使用示例
  const image = new ProxyImage('test.jpg');
  // 此时,图像还未加载
  image.display();
  // 此时,图像已经加载
  image.display();
7、装饰器模式 (Decorator),动态地给对象添加额外的职责
  附、单例模式、工厂模式、模块模式;策略模式、适配器模式、代理模式;装饰器模式、职责链模式;观察者模式、发布-订阅模式
  class Coffee {
    cost() {
      return 5;
    }
  }
  // 装饰器基类
  class CoffeeDecorator {
    constructor(coffee) {
      this.coffee = coffee;
    }
    cost() {
      return this.coffee.cost();
    }
  }
  class MilkDecorator extends CoffeeDecorator {
    cost() {
      return this.coffee.cost() + 2;
    }
  }
  class SugarDecorator extends CoffeeDecorator {
    cost() {
      return this.coffee.cost() + 1;
    }
  }
  // 使用示例
  let coffee = new Coffee();
  // coffee = { cost: fn1 }
  console.log(coffee.cost()); // 5
  // 以下,第1层装饰
  coffee = new MilkDecorator(coffee);
  // coffee = {
  //   coffee: { cost: fn1 },
  //   cost: fn2,//fn2() -> this.coffee.cost()+2 -> fn1()+2
  // }
  console.log(coffee.cost()); // 7
  // 以下,第2层装饰
  coffee = new SugarDecorator(coffee);
  // coffee = {
  //   coffee: {
  //     coffee: { cost: fn1 },
  //     cost: fn2
  //   },
  //   cost: fn3,//fn3() -> this.coffee.cost()+1 -> fn2()+1
  // }
  console.log(coffee.cost()); // 8
8、职责链模式 (Chain of Responsibility),将请求的发送者和接收者解耦,使多个对象都有机会处理这个请求
  附、单例模式、工厂模式、模块模式;策略模式、适配器模式、代理模式;装饰器模式、职责链模式;观察者模式、发布-订阅模式
  class Handler {
    constructor() {
      this.nextHandler = null;
    }
    setNext(handler) {
      this.nextHandler = handler;
      return handler;
    }
    handle(request) {
      if (this.nextHandler) {
        return this.nextHandler.handle(request);
      }
      return null;
    }
  }
  class ConcreteHandlerA extends Handler {
    handle(request) {
      if (request === 'A') {
        return `Handler A processed ${request}`;
      }
      return super.handle(request);//调用父类的handle方法,该方法里的this就是这里的this
    }
  }
  class ConcreteHandlerB extends Handler {
    handle(request) {
      if (request === 'B') {
        return `Handler B processed ${request}`;
      }
      return super.handle(request);
    }
  }
  class ConcreteHandlerC extends Handler {
    handle(request) {
      if (request === 'C') {
        return `Handler C processed ${request}`;
      }
      return super.handle(request);
    }
  }
  // 使用示例
  const handlerA = new ConcreteHandlerA();
  const handlerB = new ConcreteHandlerB();
  const handlerC = new ConcreteHandlerC();
  handlerA.setNext(handlerB).setNext(handlerC);
  console.log(handlerA.handle('A')); // Handler A processed A
  console.log(handlerA.handle('B')); // Handler B processed B
  console.log(handlerA.handle('C')); // Handler C processed C
  console.log(handlerA.handle('D')); // null
9、观察者模式 (Observer),定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会得到通知
  附、单例模式、工厂模式、模块模式;策略模式、适配器模式、代理模式;装饰器模式、职责链模式;观察者模式、发布-订阅模式
  附、观察者模式和发布-订阅模式,两种模式各有优劣,Vue的响应式系统同时采用了这两种模式的思想
  (1)特点:
    A、直接依赖:被观察者(Subject)直接持有观察者(Observer)的引用
    B、同步通知:状态变更时,被观察者主动调用观察者的更新方法
    C、强耦合:观察者和被观察者必须知道对方的存在
  (2)类比说明,像微信群:
    A、群主(Subject)发消息时,
    B、所有群成员(Observer)都会直接收到通知
  (3)如何选择,
    A、需要明确控制依赖关系
    B、如DOM事件监听、RxJS
  (4)示例
    class Subject {
      constructor() {
        this.observers = [];
      }
      subscribe(observer) { //主体订阅观察者
        this.observers.push(observer);
      }
      unsubscribe(observer) {
        this.observers = this.observers.filter(obs => obs !== observer);
      }
      notify(data) {
        this.observers.forEach(observer => observer.update(data));
      }
    }
    class Observer {
      update(data) {
        console.log(`Observer received: ${data}`);
      }
    }
    const subject = new Subject();
    const observer1 = new Observer();
    const observer2 = new Observer();
    subject.subscribe(observer1);
    subject.subscribe(observer2);
    subject.notify('Hello observers!');
    subject.unsubscribe(observer2);
    subject.notify('Observer 2 unsubscribed');
10、发布-订阅模式 (Pub/Sub),定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会得到通知
  附、单例模式、工厂模式、模块模式;策略模式、适配器模式、代理模式;装饰器模式、职责链模式;观察者模式、发布-订阅模式
  附、观察者模式和发布-订阅模式,两种模式各有优劣,Vue的响应式系统同时采用了这两种模式的思想
  (1)特点:
    A、间接通信:通过事件通道(Event Bus)解耦,发布者和订阅者不直接交互
    B、异步处理:消息的发布和订阅可以异步执行
    C、动态订阅:订阅者可以随时订阅或取消订阅事件
  (2)类比说明,像报纸订阅:
    A、报社(Publisher)发布报纸,
    B、邮局(Event Bus)负责分发,
    C、订阅者(Subscriber)只需从邮局取报,无需知道报社是谁
  (3)如何选择,
    A、需要低耦合和跨系统通信,
    B、如Vue的EventBus、Redux
  (4)示例
    class EventBus {
      constructor() {
        this.events = {};
      }
      subscribe(eventName, callback) {
        if (!this.events[eventName]) {
          this.events[eventName] = [];
        }
        this.events[eventName].push(callback);
      }
      publish(eventName, data) {
        if (!this.events[eventName]) return;
        this.events[eventName].forEach(callback => {
          callback(data);
        });
      }
      unsubscribe(eventName, callback) {
        if (!this.events[eventName]) return;
        this.events[eventName] = this.events[eventName].filter(
          cb => cb !== callback
        );
      }
    }
    const eventBus = new EventBus();
    const callback1 = data => console.log('Callback 1:', data);
    const callback2 = data => console.log('Callback 2:', data);
    eventBus.subscribe('message', callback1);
    eventBus.subscribe('message', callback2);
    eventBus.publish('message', 'Hello World!');
    eventBus.unsubscribe('message', callback1);
    eventBus.publish('message', 'Hello again!');
附、继承
  // 父类
  class Animal {
    constructor(name, age) {
      this.name = name;
      this.age = age;
    }
    speak() {
      console.log(`${this.name} makes a noise.`);
    }
  }
  // 子类(继承 Animal)
  class Dog extends Animal {
    constructor(name, age, breed) {
      // 必须调用 super() 来继承父类的 this
      super(name, age);  // 相当于 Animal.call(this, name, age)
      this.breed = breed; // 子类特有的属性
    }
    // 方法覆盖(override)
    speak() {
      console.log(`${this.name} barks!`);
    }
    // 子类特有方法
    fetch() {
      console.log(`${this.name} fetches the ball!`);
    }
  }
  // 使用
  const myDog = new Dog('Rex', 3, 'Golden Retriever');
  console.log(myDog.name);  // "Rex" (继承自 Animal)
  console.log(myDog.breed); // "Golden Retriever" (Dog 特有)
  myDog.speak();  // "Rex barks!" (覆盖了父类方法)
  myDog.fetch();  // "Rex fetches the ball!" (子类特有方法)
    
八、git
 附、Git常用命令大全http://cd.itheima.com/news/20211214/144207.html
1、github是什么
  (1)github是2008年出现的专业的代码托管分享平台,
  (2)创始人是Tom Preston-Werner(汤姆·普雷斯顿-维尔纳)、Chris Wanstrath(克里斯·万斯特拉斯),
  (3)2018年的时候被微软以75亿美元收购,在2020年github收购了NPM
2、一个简单的git流程
  (1)克隆并关联项目:git clone https://github.com/AAA/BBB.git
  (2)基于当前分支建立并切换分支git checkout -b branch_1
  (3)写代码
  (4)添加到暂存区:git add README.md(或用 git add . 添加所有修改)
  (5)提交到本地仓库:git commit -m "first branch_1"
  (6)推送到远程仓库:git push -u origin branch_1("-u" 关联远程分支,后续直接用 git push 即可)
3、项目首次运行的正确步骤。(2)(3)的顺序不能颠倒
  (1)拉取项目
  (2)在小乌龟上查看并切换到一个可运行的分支
  (3)安装依赖
  (4)运行该分支,如果出错,则查看node版本和pnpm版本是否达到最低要求
4、git常见命令
  (1)将远程Git仓库完整地复制到本地计算机,git clone https://github.com/AAA/BBB.git
  (2)为本地Git仓库添加一个远程仓库的关联,git remote add origin https://github.com/AAA/BBB.git
  (3)将本地Git分支branch_1推送到远程仓库origin,并通过-u建立本地分支与远程分支的关联,git push -u origin branch_1
    A、-u是--set-upstream的简写
    B、以后用git push即可
  (4)建立分支,git branch branch_1
  (5)删除分支,git branch -d branch_1
  (6)切换分支,git checkout branch_1
  (7)建立并切换分支,git checkout -b branch_1
5、查看提交
  (1)查看某文件-每次提交的-所有改变,git log -p src\views\content\content-item.vue;复制相对路径
  (2)查看某提交-改变文件的-所有改变,git show hash_id;用git log,获取hash_id,截取前面一小段即可
6、回退
  (1)git reset --hard HEAD^ 回退到上个版本
  (2)git reset --hard HEAD~3 回退到前3次提交之前
  (3)git reset --hard commit_id 退到指定的commit_id //git log,获取commit_id
7、查看有变化的文件
  (1)git status,查看当前工作目录的状态(哪些文件发生了变化)
  (2)git diff,查看尚未暂存的变更(即修改过但还没用git add添加的变更)
  (3)git diff --cached,查看已暂存的变更(即用git add添加过的变更,但还没有提交的变更)
  (4)git diff HEAD,查看所有变更(包括暂存区和工作目录中的)
  (5)git diff <file-path>,查看指定文件的变更:
  (6)git diff --cached <file-path>,查看暂存区和最后一次提交的变更(对于指定文件)
  (7)git diff HEAD <file-path>,查看工作目录与最后一次提交的变更(对于指定文件)
8、冲突
  (1)产生条件:多人在各自的分支上对同一内容做了不同的修改,后合并的人会报冲突
  (2)产生现象
<<<<<<< HEAD
本地向服务器提交的内容(我写的)
=======
服务器下载到本地的内容(人家写的)
>>>>>>>b0ef58d69851fedad4169878a62033c0ce16246c
9、git问题与解决
  (1)问题1
    A、内容,'common-module' already exists in the index
    B、解决,git rm --cached 'common-module' 
  (2)问题2,
    A、内容,dependency was not found:* @common-module/DateUtil
    B、解决,git submodule add http://10.71.59.14:30190/maxiaolin/common-module
  (3)问题3,
    A、内容,在高版本node环境下,在vscode的命令窗口执行npm run dev出错
    B、解决,在vscode的命令窗口执行一次$env:NODE_OPTIONS="--openssl-legacy-provider"即可
    C、这个命令的作用是,指定在运行Node.js时使用旧版的OpenSSL提供程序
    D、SSL(Secure Socket Layer)安全套接层
  (4)问题4
    A、内容,浏览器控制台出现非常慢
    B、解决,F12-设置-preferences-Language-“English(US)-English(US)”
   
九、gitLab的问题
1、通过Powershell从gitLib上拉取代码失败,解决SSL certificate problem
  方案一:在项目准备存放的地方运行 git config --global http.sslVerify false
  方案二:在项目准备存放的路径下运行 set GIT_SSL_NO_VERIFY=true git clone
  方案三:用公钥pub
  (1)运行ssh-keygen -t rsa -C “123456@qq.com”;cd ~/.ssh;ls(查看目录是否有id_rsa.pub文件);cat id_rsa.pub(查看公钥);复制公钥。
  (2)进入gitlab;点击右上方settings;点击左中间SSH Keys;在Key里粘贴公钥;点击Add key;
  (3)在项目准备存放的路径下运行 git clone gitlab地址(此过程可能与三次握手相关,另外还有四次挥手)
  (4)来源,https://blog.csdn.net/kawayiyy123/article/details/110920412
2、通过Powershell向gitLib上推送代码失败,
  方案一:git push -u origin fen_zhi_ming(从第2次开始,使用git push即可)
  方案二:git push --set-upstream origin fen_zhi_ming(从第2次开始,使用git push即可)
3、通过TortoiseGit从gitLib上拉取、推送代码的步骤
  步骤一:获取并添加公钥
  (1)运行ssh-keygen -t rsa -C “123456@qq.com”;cd ~/.ssh;ls(查看目录是否有id_rsa.pub文件);cat id_rsa.pub(查看公钥);复制公钥。
  (2)进入gitlab;点击右上方settings;点击左中间SSH Keys;在Key里粘贴公钥;点击Add key;
  步骤二:获取并添加私钥
  (1)在命令行输入PuTTYgen,按Enter,点击load
  (2)选择文件类型All Files,点击id_rsa,点击打开
  (3)点击 save private key,点击是,自动保存私钥到C:\Users\Haier\.ssh文件夹下,名称是id_rsa.ppk,如果没有该文件,则复制该文件夹下的文件id_rsa,并改名为id_rsa.ppk
  (4)在项目处,右键,点击Git克隆,填写URL:git@...、加载Putty密钥(K):C:\Users\Haier\.ssh\id_rsa.ppk,点击确定。
  注意:此后拉取、推送就很顺畅了
4、通过TortoiseGit向gitLib上推送代码失败,一直提示输入密码,用私钥ppk
  (1)执行3、步骤二:(1)(2)(3)
  (2)在项目文件下,右键,移动鼠标到TortoiseGit上,点击Settings,点击Remote,填写Remote:origin、URL:git@...、Putty Key:C:\Users\Haier\.ssh\id_rsa.ppk,点击确定。
5、gitLab的提交地址由http改为https后,下拉代码不成功,解决方案如下:
  过程:audit-html文件夹下空白处--右键单击---鼠标置于TortoiseGit(t)上---单击设置---单击确定--单击“编辑本地.git/config(L)”---把第9行代码中的http改成https,即可。
6、gitHub与gitLab
  (1)都通过git命令管理代码
  (2)通过TortoiseGit([ˈtɔːtəs],乌龟,中间人),把git命令操作改成点击操作
  (3)通过TortoiseGit-LanguagePack,把TortoiseGit的英文界面改为中文界面
7、SSH:Secure Shell(安全壳),一项创建在应用层和传输层基础上的安全协议,为计算机上的Shell提供安全的传输和使用环境。从客户端来看,SSH基于密匙的安全验证过程为:
  (1)需要依靠密匙,也就是你必须为自己创建一对密匙,并把公用密匙放在需要访问的服务器上。
  (2)如果你要连接到SSH服务器上,客户端软件就会向服务器发出请求,请求用你的密匙进行安全验证。(此过程可能与三次握手相关,另外还有四次挥手)
  (3)服务器收到请求之后,先在该服务器上你的主目录下寻找你的公用密匙,然后把它和你发送过来的公用密匙进行比较。
  (4)如果两个密匙一致,服务器就用公用密匙加密“质询”(challenge)并把它发送给客户端软件。
  (5)客户端软件收到“质询”之后就可以用你的私人密匙解密再把它发送给服务器。
8、CMD与Windows PowerShell
  (1)前者增强后为后者
  (2)前者命令少,后者命令多
  (3)前者在固定位置打开,后者在当前位置打开
  (4)覆盖写入文件,echo 1111111 > b.js
  (5)追加写入文件,echo 1111111 >> b.js
  (6)读取文件,cat b.js
9、gitLab删除仓库:进入仓库→点击(左下方)settings→可以不点击general→点击(Adanced)后面的expand→点击下面的Remove project→输入仓库名→点击Confirm
 
十、VSCode
 附、代码智能补全:CodeGeeX
 附、提交流程
   A、保存修改
   B、点击树杈号
   C、点击“更改”文件(夹)右侧加号,暂存单个(所有)文件
   D、在输入框中填写本次提交的描述
   E、点击“✓提交”,或,执行Ctrl+Enter
1、设置
  (1)找到设置setting
    A、左下角齿轮----设置setting
    B、文件file----首选项preference----设置setting
  (2)常用设置
    A、自动折行换行,设置setting----on(editor:word wrap)
    B、修改字体,设置setting----Font Family
    C、修改字体大小,设置setting----Font Zize
    D、设置颜色主题,设置settinge----Color Theme
    E、代码自动保存,设置setting----搜索auto save
    F、关闭右侧预览,设置setting----搜索"editor.minimap.enabled"
    G、多页签并存,设置setting----搜索"Workbench>Editor:Enable Preview"
    H、滚轮改变字体大小,设置setting----搜索"mouseWheelZoom"(单词之间没有空格)----勾选
    I、缩进2个空格,设置setting----Editor:Tab Size----2
    J、html标签生成:在html中输入英文叹号!--按下tab或enter键。
    K、js代码运行:安装插件code runner、安装node环境、F5
    L、代码语法错误提示:插件,ESlint
    M、代码处显示提交记录:插件,“GitLens — Git supercharged”
    N、代码比较:单击文件1->选择以进行比较,单击文件2->与已选择项目进行比较
    O、恢复默认设置:C:\Users\张三\AppData\Roaming\Code\User\settings.json,删除即可
  (3)VSCode设置成中文语言环境
    A、Ctrl+Shift+p
    B、configure display language
    C、确定=>“locale”:“zh-CN”
    D、重启vscode工具
    E、商店(最左侧最下方图标)
    F、Chinese(Simplied) Lang
    G、安装
  (4)修改窗口高度和字体大小
    A、找到workbench.main.css文件
      途径一、C:\Program Files\Microsoft VS Code\resources\app\out\vs\workbench
      途径二、C:\Users\登录名\AppData\Local\Programs\Microsoft VS Code\resources\app\out\vs\workbench
    B、修改左侧窗口字体大小,找到“.part>.content{font-size:”
    C、修改选项卡字体大小,找到“.title .title-label a{text-decoration:none;font-size:”进行修改
    D、修改选项卡高度,找到
        .title .editor-actions{cursor:default;flex:initial;padding-left:4px;height:
        .tab{position:relative;display:flex;white-space:nowrap;cursor:pointer;height:
        .title .tabs-container{display:flex;height:
        进行修改
  (5)vscode如何用谷歌浏览器预览html文件
    A、安装“view in browser”
    B、安装“open in browser”
    C、文件---首选项---设置---输入"open-in-browser.default"---“Chrome”
    D、右键“x.html”---“Open in Default Browsers”
  (6)运行结果释义,上面浏览器,下面浏览器控制
    --------------------------------------
    Local: http://localhost:8900
    External: http://172.18.10.109:8900
    --------------------------------------
    UI: http://localhost:3001
    UI External: http://172.18.10.109:3001
    --------------------------------------
  (7)window快捷键
    A、window + e: 快速打开我的电脑
    B、window + l: 快速锁屏
    C、window + d: 快速回到桌面
    D、shift + delete: 快速删除
  附、代码检查工具(也适用于vscode)
    A、代码出现红黄下划线时,将鼠标置其上,百度悬浮内容,查找解决方案
    B、ESLint“代码检查工具”,可在根目录下添加.eslintrc.js文件
      module.exports = {
        "env": {
          "browser": true,
          "node": true
        },
        "globals": {//不在这里配置而使用下列变量,则会报错
          "angular": true,
        }
      }
    C、JSHint“代码检查工具”,可在根目录下添加.jshintrc.js文件,也可以
      设置setting->检索“jshint:options”->点击“在settings.json中设置”->在"jshint.options":{}中加入
      "asi": true,//无视没有加分号的行尾
      "esversion": 8//使用ES8版的规则进行检测
2、快捷键
  来源:https://www.cnblogs.com/weihe-xunwu/p/6687000.html
  (1)注释:
    A、单行注释:[ctrl+k,ctrl+c] 或 ctrl+/
    B、取消单行注释:[ctrl+k,ctrl+u] (按下ctrl不放,再按k + u)
    C、多行注释:[alt+shift+A]
    D、多行注释:ctrl+/ (双斜线)
  (2)移动行:alt+up/down
  (3)显示/隐藏左侧目录栏 ctrl + b
  (4)复制当前行:shift + alt +up/down
  (5)删除当前行:shift + ctrl + k
  (6)控制台终端显示与隐藏:ctrl + ~
  (7)查找文件/安装vs code 插件地址:ctrl + p
  (8)代码格式化:shift + alt + f
  (9)新建一个窗口: ctrl + shift + n
  (10)行增加缩进: ctrl + [
  (11)行减少缩进: ctrl + ]
  (12)裁剪尾随空格(去掉一行的末尾那些没用的空格): ctrl + shift + x
  (13)字体放大/缩小: ctrl + ( + 或 - )
  (14)拆分编辑器: ctrl + 1/2/3
  (15)切换窗口: ctrl + shift + left/right
  (16)关闭编辑器窗口: ctrl + w
  (17)关闭所有窗口: ctrl + k + w
  (18)切换全屏: F11
  (19)自动换行: alt + z
  (20)显示git: ctrl + shift + g
  (21)全局查找文件:ctrl + shift + f
  (22)显示相关插件的命令(如:git log):ctrl + shift + p
  (23)选中文字:shift + left / right / up / down
  (24)折叠代码: ctrl + k + 0-9 (0是完全折叠)
  (25)展开代码: ctrl + k + j (完全展开代码)
  (26)删除行: ctrl + shift + k
  (27)快速切换主题:ctrl + k / ctrl + t
  (28)快速回到顶部: ctrl + home
  (29)快速回到底部: ctrl + end
  (30)格式化选定代码:ctrl + k / ctrl +f
  (31)选中代码: shift + 鼠标左键
  (32)多行同时添加内容(光标):ctrl + alt + up/down
  (33)全局替换:ctrl + shift + h
  (34)当前文件替换:ctrl + h
  (35)打开最近打开的文件:ctrl + r
  (36)打开新的命令窗:ctrl + shift + c
  (37)函数代码块的注释生成方法:安装插件“Document This”;将光标放置于function上面,快捷键Ctrl+Alt+D;
  (38)代码正则替换:在Ctrl+H后,会出现两行搜索框,上行搜索框的右侧有雪花号,点击雪花号,正则替换可以使用了
  (39)代码正则替换示例1-删除所有空行-步骤:
    A、ctrl+h;
    B、点击雪花号;
    C、^\s*$,11111111111111111,替换;
    D、上一行尾部+11111111111111111,空,替换
  (40)代码正则替换示例2-给index=1;index=25;index=330;的数字后面加空格-步骤:
    A、ctrl+h;
    B、点击雪花号;
    C、index=(\d+);
    D、index=$1  ;
3、加代码块
  打开vscode>文件>首选项>配置用户代码片段>
  (1)html
    {
      "jq": {
        "prefix": "jq",
        "body": [
          "<!DOCTYPE html>",
          "<html lang='en'>",
          "<head>",
          "  <meta charset='UTF-8'>",
          "  <title>jq</title>",
          "  <script src='https://cdn.bootcss.com/jquery/1.9.1/jquery.js'></script>",
          "</head>",
          "<body>",
          "  <div>",
          "  $1",
          "  </div>",
          "</body>",
          "</html>",
          "<script type='text/javascript'>",
          "  ",
          "</script>"
        ],
        "description": "jq"
      },
      "echarts": {
        "prefix": "echarts",
        "body": [
          "<!DOCTYPE html>",
          "<html lang='en'>",
          "<head>",
          "  <meta charset='UTF-8'>",
          "  <title></title>",
          "  <script src='https://cdn.bootcss.com/echarts/3.7.1/echarts.js'></script>",
          "</head>",
          "<body>",
          "  <div id='myEchartsId' style='height:300px;'></div>",
          "</body>",
          "</html>",
          "<script type='text/javascript'>",
          "  var myEcharts = echarts.init(document.getElementById('myEchartsId'));",
          "  var myEchartsOption = { ",
          "  };",
          "  myEcharts.setOption(myEchartsOption);",
          "</script>",
        ],
        "description": "echarts"
      },
      "ng": {
        "prefix": "ng",
        "body": [
          "<!DOCTYPE html>",
          "<html lang='en' ng-app='myModel'>",
          "<head>",
          "  <meta charset='UTF-8'>",
          "  <title>ng</title>",
          "  <script src='https://cdn.bootcss.com/angular.js/1.6.2/angular.js'></script>",
          "</head>",
          "<body>",
          "  <div ng-controller='firstCtrl'>",
          "  $1",
          "  </div>",
          "</body>",
          "</html>",
          "<script type='text/javascript'>",
          "  var app = angular.module('myModel', []);",
          "  app.controller('firstCtrl', function ($scope){",
          "  ",
          "  });",
          "</script>"
        ],
        "description": "ng"
      },
      "bt": {
        "prefix": "bt",
        "body": [
          "<!DOCTYPE html>",
          "<html lang='en'>",
          "<head>",
          "  <title>两列高度自适应</title>",
          "  <link rel='stylesheet' href='https: //cdn.staticfile.org/twitter-bootstrap/4.1.1/css/bootstrap.min.css'>",
          "  <style>",
          "    .row>div {",
          "      background: #cccccc;",
          "    }",
          "  </style>",
          "</head>",
          "<body>",
          "  <div class='container'>",
          "    <div class='row'>",
          "      <div class='col-xl-6'>col-xl-6;col-xl-6;col-xl-6;</div>",
          "      <div class='col-xl-5'>col-xl-6</div>",
          "    </div>",
          "  </div>",
          "</body>",
          "</html>",
        ],
        "description": "bt"
      },
    }
  (2)js
    {
       
      "ccc": {
        "prefix": "ccc",
        "body": ["console.log($0)"],
        "description": "console.log($0)"
      },
      "c1": {
        "prefix": "c1",
        "body": ["console.log(   $0   )"],
        "description": "console.log($0)"
      },
      "c2": {
        "prefix": "c2",
        "body": ["console.log('$0')"],
        "description": "console.log('$0')"
      },
      "fn": {
        "prefix": "fn",
        "body": ["function $0(){", "  ", "}"],
        "description": "function $0()"
      },
      "ng": {
        "prefix": "ng",
        "body": [
          "(function () {",
          "  angular",
          "    .module('$0')",
          "    .controller('', function ($scope) {",
          "  ",
          "    })",
          "})()"
        ],
        "description": "ng"
      },
      "sss": {
        "prefix": "sss",
        "body": ["$$scope.$1"],
        "description": "$scope.'$1'"
      },
      "ppp": {
        "prefix": "ppp",
        "body": [
          "/*方法说明",
          " *@method 方法名",
          " *@param{参数类型}参数名 参数说明",
          " *@return {返回值类型} 返回值说明",
          "*/"
        ],
        "description": "ng"
      }
    }
  (3)jQuery
    <!DOCTYPE html>
    <html lang='en'>
      <head>
        <meta charset='UTF-8'>
        <title>jq</title>
        <script src='https://cdn.bootcss.com/jquery/1.9.1/jquery.js'></script>
      </head>
      <body>
        <div>
 
        </div>
      </body>
    </html>
    <script type='text/javascript'>
 
    </script>
  (4)echarts
    <!DOCTYPE html>
    <html lang='en'>
      <head>
        <meta charset='UTF-8'>
        <title></title>
        <script src='https://cdn.bootcss.com/echarts/3.7.1/echarts.js'></script>
      </head>
      <body>
        <div id='myEchartsId' style='height:300px;'></div>
      </body>
    </html>
    <script type='text/javascript'>
      var myEcharts = echarts.init(document.getElementById('myEchartsId'));
      var myEchartsOption = {
      };
      myEcharts.setOption(myEchartsOption);
    </script>
  (5)angular1
    <!DOCTYPE html>
    <html lang='en' ng-app='myModel'>
      <head>
        <meta charset='UTF-8'>
        <title>ng</title>
        <script src='https://cdn.bootcss.com/angular.js/1.6.2/angular.js'></script>
      </head>
      <body>
        <div ng-controller='firstCtrl'>
 
        </div>
      </body>
    </html>
    <script type='text/javascript'>
      var app = angular.module('myModel', []);
      app.controller('firstCtrl', function ($scope){
 
      });
    </script>
  (6)bootstrap
    <!DOCTYPE html>
    <html lang='en'>
      <head>
        <title>两列高度自适应</title>
        <link rel='stylesheet' href='https: //cdn.staticfile.org/twitter-bootstrap/4.1.1/css/bootstrap.min.css'>
        <style>
          .row>div {
            background: #cccccc;
          }
        </style>
      </head>
      <body>
        <div class='container'>
          <div class='row'>
            <div class='col-xl-6'>col-xl-6;col-xl-6;col-xl-6;</div>
            <div class='col-xl-5'>col-xl-6</div>
          </div>
        </div>
      </body>
    </html>
 
十一、jinja2语法
1、行内条件
  <div>{{ '' if system_event.peak_date == None else system_event.peak_date }}</div>
2、set
  {% set dev_info = data["info"]["dev_info"] %}
    <div>{{ dev_info.dev_name }}</div>
3、跨行条件
  (1)跨行条件1
    {% if device.on_line %}
      <div>预备测试</div>
    {% endif %}
  (2)跨行条件2
    {% if device.on_line %}
      <div>预备测试</div>
    {% else %}
      <div>正式设备</div>
    {% endif %}
4、for
  (1)for之1
    {% for dep_num_dict  in data.info.dep_num_dict.keys() %}
      <div>
        <div>{{dep_num_dict}}</div>
        <div>{{data.info.dep_num_dict[dep_num_dict].on_line}}</div>
      </div>
    {% endfor %}
  (2)for之2
    <div class="mytable">
      {% for log_list in data.info.black_event %}
      <div class="table-row">
        <div>{{loop.index}}</div>
        <div>{{ log_list.threat_name }}</div>
        <div style="text-align:left;width:900px">
          {% for log_list_in in log_list.suggest.split('<br>') %}//把字符串切分成数组,然后遍历
            {{ log_list_in }}<br/>
          {% endfor %}
        </div>
      </div>
      {% endfor %}
    </div>
       
十二、文字的水印与镂空、文字与盒子的阴影,背景,线性渐变(边框渐变、文字渐变),径向渐变,转换、过渡、动画
1、文字的水印与镂空、文字与盒子的阴影
  附、颜色的优先级-从小到大
    background-color < border-color、color < -webkit-text-stroke(文字轮廓色)、-webkit-text-fill-color(文字填充色)
  (1)文字的水印与镂空
    <!DOCTYPE html>
    <html>
      <head lang="en">
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            .text-watermark{
                z-index: 1;
                position: absolute;
                opacity: 0.2;
                transform: rotate(-45deg) translate(30px);
                user-select: none;
            }
            .text-uesd{
                z-index: 2;
                position: relative;
            }
            .text-stroke{
                font-size: 24px;
                font-weight: 900;
                color: transparent;
                -webkit-text-stroke: 1px gray;/* 文本轮廓的线宽、颜色;stroke,笔画 */
            }
        </style>
      </head>
      <body>
        <pre style="padding-left: 50px;">
          1/2、文字水印
          <pre class="text-watermark">
            文字水印样式
            .text-watermark{
                z-index: 1;
                position: absolute;
                opacity: 0.2;
                transform: rotate(-45deg) translate(30px);
                user-select: none;
            }
          </pre>
          <pre class="text-uesd">
            文字主体样式
            .text-uesd{
                z-index: 2;
                position: relative;
            }
          </pre>
        </pre>
        <pre class="text-stroke">
          2/2、文字镂空
          .text-stroke{
              font-size: 24px;
              font-weight: 900;
              color: transparent;
              -webkit-text-stroke: 1px gray;/* 文本轮廓的线宽、颜色;stroke,笔画 */
          }
        </pre>
      </body>
    </html>
  (2)文字与盒子的阴影
    <!DOCTYPE html>
    <html>
      <head lang="en">
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            .text-shadow{
                font-size: 24px;
                font-weight: 900;
                color: #000;
                text-shadow: -5px -5px 3px gray, 5px 5px 3px #ccc;
            }
            .box-shadow{
                width: 1000px;
                margin: 30px 30px 30px 130px ;
                padding-top: 15px;
                font-size: 15px;
                border-radius: 10px;
                box-shadow: 0 0 10px 20px gray, 0 0 10px 20px #ccc inset;
            }
        </style>
      </head>
      <body>
        <pre class="text-shadow">
          1/2、文字阴影
          .text-shadow{
              font-size: 20px;
              font-weight: 900;
              color: #000;
              text-shadow: -5px -5px 3px gray <左上阴影>, 5px 5px 3px #ccc <右下阴影>;
          }
          以下text-shadow的取值说明,
          来源,https://www.runoob.com/cssref/css3-pr-box-shadow.html
          text-shadow: h-shadow v-shadow blur color;
              h-shadow,必需,水平阴影的位置;负值左阴影,0底阴影,正值右阴影
              v-shadow,必需,垂直阴影的位置;负值上阴影,0底阴影,正值下阴影
              blur,可选,模糊的距离
              color,可选,阴影的颜色
        </pre>
        <pre class="box-shadow">
          2/2、盒子阴影
          .box-shadow{
              box-shadow: 0 0 10px 10px gray;
              box-shadow: 0 0 10px 10px gray <外阴影>, 0 0 10px 10px #ccc inset <内阴影>;
          }
          以下box-shadow的取值说明
              box-shadow: h-shadow v-shadow blur spread color inset;
                  h-shadow,必需,水平阴影的位置;负值左阴影,0左右阴影,正值右阴影
                  v-shadow,必需,垂直阴影的位置;负值上阴影,0上下阴影,正值下阴影
                  blur,可选,模糊距离
                  spread,可选,阴影的大小
                  color,可选,阴影的颜色;在CSS颜色值寻找颜色值的完整列表
                  inset,可选,从外层的阴影(开始时)改变阴影内侧阴影
        </pre>
      </body>
    </html>
2、背景
  <!DOCTYPE html>
  <html lang="en">
    <style>
      .bg{
        width: 1100px;
        height: 200px;
        border-radius: 20px;
        padding: 20px;
        margin: 20px;
        border: 20px dotted black;
        /* 以下,单背景颜色,覆盖范围为content+padding+border */
        background-color: gray;
        /* 以下,多背景图像,从后向前渲染;如用渐变,则把url换成
        linear-gradient、radial-gradient、repeating-linear-gradient或repeating-radial-gradient */
        background-image:
          url("https://img-blog.csdnimg.cn/violation-del.png"),
          url("https://img-blog.csdnimg.cn/violation-del.png");
        background-repeat: no-repeat, no-repeat;
        /* 以下4个,先定位放置、后裁切显示,优先级逐渐降低;
          border-box border-box; padding-box padding-box; content-box content-box; */
        background-origin: content-box, border-box;
        background-position: left center, right center;
        background-size: 35%, contain;
        /*  background-clip: content-box, content-box; 注掉background-clip,可能露出背景色 */
      }
      .pre{
        border: 2px solid black;
        width: 1200px;
        margin: 30px;
        border-radius: 10px;
        padding: 30px 0;
      }
      .span{
        font-size: 20px;
        font-weight: 900;
      }
    </style>
    <head>
      <meta charset="UTF-8">
      <title>背景</title>
    </head>
    <body>
      <div class="bg"></div>
      <pre class="pre">
        用法来源,https://www.runoob.com/cssref/css3-pr-background.html
        background:汇总
          background-color background-image background-repeat background-attachment;
          background-position background-size background-origin background-clip;
        background:取值说明
          (1)background-attachment,设置背景图像是否固定或者随着页面的其余部分滚动,默认值:initial
            A、scroll,背景图片随着页面的滚动而滚动,这是默认的
            B、fixed,背景图片不会随着页面的滚动而滚动
            C、local,背景图片会随着元素内容的滚动而滚动
            D、initial,设置该属性的默认值,https://www.runoob.com/cssref/css-initial.html
          (2)background-color,指定要使用的一个背景颜色,默认值:transparent
            覆盖范围为content+padding+border,
          (3)background-image,指定要使用的一或多个背景图像,默认值:none
            覆盖范围由(5)(6)(7)(8)配置决定,
          (4)background-repeat,指定如何重复背景图像,默认值:repeat
            A、repeat,背景图像将向垂直和水平方向重复
            B、repeat-x,只有水平位置会重复背景图像
            C、repeat-y,只有垂直位置会重复背景图像
            D、no-repeat,不重复
            //以下4个,先定位放置、后裁切显示,优先级逐渐降低
          (5)background-origin,指定背景图像的定位区域,默认值:padding-box
            A、padding-box,背景图像填充框的相对位置
            B、border-box,背景图像边界框的相对位置
            C、content-box,背景图像的相对位置的内容框
          (6)background-position,指定背景图像的位置,默认值:0% 0%
            //以下,top,bottom,left,right的覆盖范围为content+padding,
            A、x% y%,指定一个,另一个是50%
            B、xpx ypx,指定一个,另一个是50%
            C、center top,指定一个,另一个是"center"。top/bottom/left/right
          (7)background-size,指定背景图片的大小,默认值:auto。范围决定者:自身宽、高、padding,子级撑开
            A、关键字取值
              a、auto(默认值):保持图片原始尺寸,不进行缩放!!!
              b、contain:按比例缩放图片,使图片完全显示在容器内(可能存在留白)
              c、cover:按比例缩放图片,使图片完全覆盖容器(可能会裁剪图片)
            B、长度值
              a、单值:宽度固定,高度默认为auto,按宽高比例进行缩放
              b、双值:分别指定宽和高
            C、百分比值
              a、单值:宽度为容器的百分比宽,高度默认为auto,按宽高比例进行缩放
              b、双值:宽高为容器宽高的百分比
            D、透明背景
              a、设置为107%。实现:让透明阴影溢出
              d、设置为100% auto和margin-bottom:-15px。实现:原图不变形且与下图间距变小
          (8)background-clip,指定背景图像的绘制区域(显示区域),默认值:border-box
            A、border-box,背景绘制在边框方框内(剪切成边框方框)
            B、padding-box,背景绘制在衬距方框内(剪切成衬距方框)
            C、content-box,背景绘制在内容方框内(剪切成内容方框)
            D、-webkit-background-clip:text;背景绘制在文字上
      </pre>
    </body>
  </html>
3、线性渐变
  (1)文字与边框渐变
    <!DOCTYPE html>
    <html lang="en">
      <style>
        .div{
          width: 1200px;
          margin: 30px;
          border-radius: 10px;
        }
        .gradientBorder{
          background-image: linear-gradient(#fff,#fff),linear-gradient(10deg,#624AFF,#1CF7CC,#1697F8);
          background-clip: content-box,border-box;
          /* 以下方案一 */
          border: 2px solid transparent;
          padding: 0;
          /* 以下方案二 */
          /* padding: 2px; */
        }
        .padding{
          padding: 10px;
        }
        .gradientText{
          background-image: -webkit-linear-gradient(right,#624AFF,#f71c27,#1697F8);
          color: transparent;/* -webkit-text-fill-color: transparent; */
          -webkit-background-clip: text;
        }
      </style>
      <head>
        <meta charset="UTF-8">
        <title>文字渐变、边框渐变</title>
      </head>
      <body>
        <pre>
          background-image,用法来源,https://www.runoob.com/cssref/pr-background-image.html
        </pre>
        <div class="div gradientBorder">
          <div class="padding">示例:文字渐变、边框渐变</div>
          <pre class="gradientText padding">
            .gradientBorder{
              background-image: linear-gradient(#fff,#fff),linear-gradient(98deg,#624AFF,#1CF7CC,#1697F8);
              background-clip: content-box,border-box;
              /* 以下方案一 */
              border: 2px solid transparent;
              padding: 0;
              /* 以下方案二 */
              /* padding: 2px; */
            }
            .gradientText{
              background-image: -webkit-linear-gradient(right,#624AFF,#1CF7CC,#1697F8);
              color: transparent;/* -webkit-text-fill-color: transparent; */
              -webkit-background-clip: text;
            }
          </pre>
        </div>
      </body>
    </html>
  (2)线性渐变
    <!DOCTYPE html>
    <html lang="en">
      <style>
        .div{
          width: 1200px;
          margin: 30px;
          border-radius: 10px;
        }
        .blackBorder{
          border: 2px solid black;
        }
        .padding{
          padding: 10px;
        }
        .linearGradientYes0{
          background-image: linear-gradient(to right, red 0, yellow 600px, green 1200px);
        }
        .linearGradientNo0{
          background-image: linear-gradient(to right, red 20%, yellow 50%, green 80%);
        }
        .repeatingLinearGradientYes0{
          background-image: repeating-linear-gradient(to right, red 0, yellow 150px, green 300px);
        }
        .repeatingLinearGradientNo0{
          background-image: repeating-linear-gradient(to right, red 8%, yellow 20%, green 30%);
        }
      </style>
      <head>
        <meta charset="UTF-8">
        <title>(重复)线性渐变</title>
      </head>
      <body>
        <pre>
          background-image,用法来源,https://www.runoob.com/cssref/pr-background-image.html
        </pre>
        <div class="div blackBorder linearGradientYes0">
          <div class="padding">1、线性渐变-以0为起点</div>
          <pre>
            linear-gradient(direction, color-stopA, color-stopB, ...);
         a、direction 用角度值指定渐变的方向(或角度);从上到下(默认)
         b、color-stopA, color-stopB, ... , color-stopN color-stopA/N用于指定“整个渐变”开始/结束时的颜色和位置
              C、示例,background-image: linear-gradient(to right, red 0, yellow 600px, green 1200px);
              D、从red过渡到yellow,再过渡到green;
          </pre>
        </div>
        <div class="div blackBorder linearGradientNo0">
          <div class="padding">2、线性渐变-不以0为起点</div>
          <pre>
            linear-gradient(direction, color-stopA, color-stopB, ...);
         a、direction 用角度值指定渐变的方向(或角度);从上到下(默认)
         b、color-stopA, color-stopB, ... , color-stopN color-stopA/N用于指定“整个渐变”开始/结束时的颜色和位置
              C、示例,background-image:linear-gradient(to right, red 20%, yellow 50%, green 80%);
              D、0到20%为red,80%到100%为green;20%到80%,从red过渡到yellow,再过渡到green;
          </pre>
        </div>
        <div class="div blackBorder repeatingLinearGradientYes0">
          <div class="padding">3、重复线性渐变-以0为起点</div>
          <pre>
            repeating-linear-gradient(direction, color-stopA, color-stopB, ...);
         a、direction 用角度值指定渐变的方向(或角度);从上到下(默认)
         b、colorA A%, colorB B%, ... , colorN N%,A%、N%用于指定第1、2个渐变的结束位置,colorA-colorN,用于指定1个完整渐变的颜色构成
              C、示例,background-image: repeating-linear-gradient(to right, red 0, yellow 150px, green 300px);
              D、上例中,数据释义如下
                0,第1个渐变的结束位置
                300px,第2个渐变的结束位置
                300px,300px-0px,1个完整渐变的占比,颜色与位置对应为red 0, yellow 150, green 300
                900px,3*300px,3个完整渐变的占比
                900px,最后1个渐变的开始位置
          </pre>
        </div>
        <div class="div blackBorder repeatingLinearGradientNo0">
          <div class="padding">4、重复线性渐变-不以0为起点</div>
          <pre>
            repeating-linear-gradient(direction, color-stopA, color-stopB, ...);
         a、direction 用角度值指定渐变的方向(或角度);从上到下(默认)
         b、colorA A%, colorB B%, ... , colorN N%,A%、N%用于指定第1、2个渐变的结束位置,colorA-colorN,用于指定1个完整渐变的颜色构成
              C、示例,background-image: repeating-linear-gradient(to right, red 8%, yellow 20%, green 30%);
              D、上例中,数据释义如下
                8%,第1个渐变的结束位置
                30%,第2个渐变的结束位置
                22%,30%-8%,1个完整渐变的占比,颜色与位置对应为red 0, yellow 12, green 22
                88%,4*22%,4个完整渐变的占比
                96%,8%+88%,最后1个渐变的开始位置
          </pre>
        </div>
      </body>
    </html>
4、径向渐变
  <!DOCTYPE html>
  <html>
    <head lang="en">
      <meta charset="UTF-8">
      <title>(重复)径向渐变</title>
      <style type="text/css">
          .common{
              width: 1200px;
              font-size: 15px;
              font-weight: 900;
              color: black;
              border-radius: 10px;
              margin: 50px;
              padding: 0px;
          }
          .params{
            border: 2px solid black;
            width: 1200px;
            margin: 30px;
            border-radius: 10px;
            padding: 30px 0;
          }
          .paddingLeft{
            padding-left: 10px;
          }
          .backgroundSize{
              background-size:370px 220px;
          }
          /* .backgroundRepeat{
              background-repeat: no-repeat;
          } */
          .radialNumber{
              height: 400px;
              background-image: radial-gradient(250px 35% at center,red 60px,yellow 90px,green 130px);
          }
          .radialRate{
              height: 400px;
              background-image: radial-gradient(250px 35% at center,red 20%, yellow 30%, green 45%);
          }
          .repeatNumber{
              height: 400px;
              background-image: repeating-radial-gradient(250px 35% at center,red 60px,yellow 90px,green 130px);
          }
          .repeatRate{
              height: 400px;
              background-image: repeating-radial-gradient(250px 35% at center,red 20%, yellow 30%, green 45%);
          }
          .money{
              background-image:repeating-radial-gradient(
                circle,
                gray 0,gray 5px,transparent 5px,transparent 20px,
                gray 20px,gray 25px,transparent 25px,transparent 50px
              );
              background-size:90px 90px;
              display: flex;
              justify-content: flex-end;
              align-items: flex-end;
          }
      </style>
    </head>
    <body>
      <pre class="params">
        <div class="paddingLeft">径向渐变-参数说明</div>
        <div>
          background-image: radial-gradient(shape size at position, start-color, ..., last-color);
          1、shape,径向渐变的形状,默认为ellipse
            circle,指定圆形的径向渐变
              background-image: radial-gradient(circle, red, yellow, green);
            ellipse,指定椭圆形的径向渐变
              background-image: radial-gradient(ellipse closest-corner, red, yellow, green);
            特别说明,当指定size为“数字”时,“指定形状”,必须根据数字的个数,否则无渐变效果,因此“指定形状”是多余的
              background-image: radial-gradient(circle 400px, red, yellow, green);
              background-image: radial-gradient(ellipse 400px 40%, red, yellow, green);
            特别说明,当指定size为“关键字”时,“指定形状”可以没有、可以任意,“指定形状”不是多余的
              background-image: radial-gradient(farthest-side, red, yellow, green);
              background-image: radial-gradient(circle farthest-side, red, yellow, green);
          2、size,径向渐变的宽高,默认为farthest-corner,可以用“省略”或“一个关键字”或“一、两个数字”
            farthest-corner,指定径向渐变的半径长度为从圆心到离圆心最远的角
            closest-side,指定径向渐变的半径长度为从圆心到离圆心最近的边
            closest-corner,指定径向渐变的半径长度为从圆心到离圆心最近的角
            farthest-side,指定径向渐变的半径长度为从圆心到离圆心最远的边
          3、position,定义渐变的位置,默认为center,可以用“省略”或“一个关键字”或“两个数字”
            center,设置中间为径向渐变圆心的纵坐标值
            top,设置顶部为径向渐变圆心的纵坐标值
            bottom,设置底部为径向渐变圆心的纵坐标值
            left,设置左侧为径向渐变圆心的横坐标值
            right,设置右侧为径向渐变圆心的横坐标值
              background-image: radial-gradient(circle, red, yellow, yellow);
              background-image: radial-gradient(circle at center, red, yellow, green);
              background-image: radial-gradient(400px 40% at 156px 60%, red, yellow, green);
          4、start-color, ..., last-color,用于指定“整个渐变”开始/结束时的颜色和位置
        </div>
        <div class="paddingLeft">径向渐变-案例解析</div>
        <div>
          1、案例解析1
            例1、background-image: radial-gradient(250px-A处 35%-B处 at center,red 60px,yellow 90px,green 130px);
            例2、background-image: repeating-radial-gradient(250px-A处 35%-B处 at center,red 60px,yellow 90px,green 130px);
            (1)例1中,数据释义如下
                60px,径向渐变开始位置,如果大于0,那么0到开始位置的颜色为开始位置的颜色
                130px,径向渐变结束位置,如果小于100%,那么结束位置到100%的颜色为结束位置的颜色
            (2)例2中,数据释义如下
                60px,重复径向渐变第1层椭圆的结束位置,即该层椭圆的X轴半径是60px
                130px,重复径向渐变第2层椭圆的结束位置,即该层椭圆的X轴半径是130px
          2、案例解析2
            例1、background-image: radial-gradient(250px-A处 35%-B处 at center, red 20%, yellow 30%, green 45%);
            例2、background-image: repeating-radial-gradient(250px-A处 35%-B处 at center, red 20%, yellow 30%, green 45%);
            (1)例1中,数据释义如下
                20%,径向渐变开始位置,即250px的20%,如果大于0,那么0到开始位置的颜色为开始位置的颜色
                45%,径向渐变结束位置,即250px的45%,如果小于100%,那么结束位置到100%的颜色为结束位置的颜色
            (2)例2中,数据释义如下
                20%,重复径向渐变第1层椭圆的结束位置,即该层椭圆的X轴半径是250px的20%
                45%,重复径向渐变第2层椭圆的结束位置,即该层椭圆的X轴半径是250px的45%
          3、上面案例中,
            (1)如果A处或B处为百分数,那么先通过盒子的width或height计算出对应值,进而计算出椭圆的X轴(先)或Y轴(后)半径
            (2)center,将上面的椭圆放到盒子的正中间,当center改为10px 30px时,将上面的椭圆中心放到盒子左边右侧10px,上边下方30px的地方,
            (3)重复径向渐变,各个色带(层距)从外向里填充颜色,当中心椭圆的半径小于层距时,椭圆中心不会出现色带内侧的颜色
          4、盒子的width或height只含content,但background-image默认是从border开始覆盖的
        </div>
      </pre>
      <pre class="common radialNumber backgroundRepeat">
        <div class="paddingLeft">1、径向渐变-像素</div>
        <div>
          .radialNumber{
              height: 400px;
              background-image: radial-gradient(250px 35% at center,red 60px,yellow 90px,green 130px);
          }
          上例中,径向渐变数据释义如下
              椭圆的X轴半径是130
              椭圆的Y轴半径是72.8,(400*35%)/250*130,y中心/x中心*x半径
        </div>
      </pre>
      <pre class="common radialNumber backgroundRepeat backgroundSize">
        <div class="paddingLeft">2、径向渐变-像素,含backgroundSize</div>
        <div>
          .radialNumber{
              height: 400px;
              background-image: radial-gradient(250px 35% at center,red 60px,yellow 90px,green 130px);
          }
          .backgroundSize{
              background-size:370px 220px;
          }
          上例中,径向渐变数据释义如下
              椭圆的X轴半径是130
              椭圆的Y轴半径是40.04,(220*35%)/250*130,y中心/x中心*x半径
        </div>
      </pre>
      <pre class="common radialRate backgroundRepeat">
        <div class="paddingLeft">3、径向渐变-百分比</div>
        <div>
          .radialRate{
              height: 400px;
              background-image: radial-gradient(250px 35% at center,red 20%, yellow 30%, green 45%);
          }
          上例中,径向渐变数据释义如下
              椭圆的X轴半径是112.5,250*45%
              椭圆的Y轴半径是63,(400*35%)/250*112.5,y中心/x中心*x半径
        </div>
      </pre>
      <pre class="common radialRate backgroundRepeat backgroundSize">
        <div class="paddingLeft">4、径向渐变-百分比,含backgroundSize</div>
        <div>
          .radialRate{
              height: 400px;
              background-image: radial-gradient(250px 35% at center,red 20%, yellow 30%, green 45%);
          }
          .backgroundSize{
              background-size:370px 220px;
          }
          上例中,径向渐变数据释义如下
              椭圆的X轴半径是112.5,250*45%
              椭圆的Y轴半径是34.65,(220*35%)/250*112.5,y中心/x中心*x半径
        </div>
      </pre>
      <pre class="common repeatNumber backgroundRepeat">
        <div class="paddingLeft">5、重复径向渐变-像素</div>
        <div>
          .repeatNumber{
              height: 400px;
              background-image: repeating-radial-gradient(250px 35% at center,red 60px,yellow 90px,green 130px);
          }
          上例中,重复径向渐变数据释义如下
              第1层椭圆的X轴半径是60
              第2层椭圆的X轴半径130
              X轴层距70,130-60
              第1层椭圆的Y轴半径33.6,(400*35%)/250*60,y中心/x中心*1层x半径
              第2层椭圆的Y轴半径72.8,(400*35%)/250*130,y中心/x中心*2层x半径
              Y轴层距39.2,72.8-33.6
        </div>
      </pre>
      <pre class="common repeatNumber backgroundRepeat backgroundSize">
        <div class="paddingLeft">6、重复径向渐变-像素,含backgroundSize</div>
        <div>
          .repeatNumber{
              height: 400px;
              background-image: repeating-radial-gradient(250px 35% at center,red 60px,yellow 90px,green 130px);
          }
          .backgroundSize{
              background-size:370px 220px;
          }
          上例中,重复径向渐变数据释义如下
              第1层椭圆的X轴半径是60
              第2层椭圆的X轴半径130
              X轴层距70,130-60
              第1层椭圆的Y轴半径18.48,(220*35%)/250*60,y中心/x中心*1层x半径
              第2层椭圆的Y轴半径40.04,(220*35%)/250*130,y中心/x中心*2层x半径
              Y轴层距21.56,40.04-18.48
        </div>
      </pre>
      <pre class="common repeatRate backgroundRepeat">
        <div class="paddingLeft">7、重复径向渐变-百分比</div>
        <div>
          .repeatRate{
              height: 400px;
              background-image: repeating-radial-gradient(250px 35% at center,red 20%, yellow 30%, green 45%);
          }
          上例中,重复径向渐变数据释义如下
              第1层椭圆的X轴半径是50,250*20%
              第2层椭圆的X轴半径112.5,250*45%
              X轴层距62.5,112.5-50
              第1层椭圆的Y轴半径28,(400*35%)/250*50,y中心/x中心*1层x半径
              第2层椭圆的Y轴半径63,(400*35%)/250*112.5,y中心/x中心*2层x半径
              Y轴层距35,63-28
        </div>
      </pre>
      <pre class="common repeatRate backgroundRepeat backgroundSize">
        <div class="paddingLeft">8、重复径向渐变-百分比,含backgroundSize</div>
        <div>
          .repeatRate{
              height: 400px;
              background-image: repeating-radial-gradient(250px 35% at center,red 20%, yellow 30%, green 45%);
          }
          .backgroundSize{
              background-size:370px 220px;
          }
          上例中,重复径向渐变数据释义如下
              第1层椭圆的X轴半径是50,250*20%
              第2层椭圆的X轴半径112.5,250*45%
              X轴层距62.5,112.5-50
              第1层椭圆的Y轴半径15.4,(220*35%)/250*50,y中心/x中心*1层x半径
              第2层椭圆的Y轴半径34.65,(220*35%)/250*112.5,y中心/x中心*2层x半径
              Y轴层距19.25,34.65-15.4
        </div>
      </pre>
      <pre class="common money">
        <div>
          附、重复径向渐变-铜钱效果
            .money{
              background-image:repeating-radial-gradient(circle,
                gray 0,gray 5px,transparent 5px,transparent 20px,
                gray 20px,gray 25px,transparent 25px,transparent 50px
              );
              background-size:90px 90px;
              display: flex;
              justify-content: flex-end;
              align-items: flex-end;
            }
        </div>
      </pre>
    </body>
  </html>     
5、过渡示例   
  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8">
      <title>过渡</title>
      <style>
        .div{
          background: rgb(95, 95, 97);
          padding: 10px;
          margin-top: 200px;
          margin-left: 150px;
          width: 700px;
          height: 220px;
          font-size: 14px;
          transform: rotate(0) skew(0) scale(1) translate(0);
          transition: 2s;/* 有整体配置 */
          /* transition:
              transform 2s ease-in 0,// 属性名 持续时间 速度曲线 开始前的延迟时间
              width 2s ease-in 0s,
              height 2s ease-in 0s,
              font-size 2s ease-in 0s;
          */
        }
        /* 以下过渡参目标 */
        .div:hover{
          width: 1100px;
          height: 300px;
          font-size: 20px;
          transform/*转换*/: translate(120px)/*平移*/ rotate(7deg)/*旋转*/ scale(1.4)/*缩放*/ skew(10deg)/*倾斜*/;
        }
      </style>
    </head>
    <body>
      <div class="div">
        <pre>
          附、过渡与动画的区别
     a、transition(过渡),用于实现简单的动画效果
            初始值,多个属性,每个属性对应多个配置;有整体配置
            目标值,定义在hover里;不应与初始值一致
     b、animation(动画),用于实现复杂的动画效果
            初始值,多个帧,每帧对应多个配置;无整体配置
            目标值,定义在帧里;应与初始值一致
          另、转换、过渡、动画,都是CSS的属性;它们仨的属性值比较复杂 
        </pre>
      </div>
    </body>
  </html>
6、动画示例
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>动画</title>
      <style>
        .divSize{
          margin: 30px auto;
          color: #ffffff;
          display: flex;
          justify-content: center;
          align-items: center;
          /* 以下参与动画 */
          width: 1000px;
          background: #999999;
          height: 720px;
          font-size: 16px;
          animation: widthMove 2s;/* 无整体配置 */
          /*
          animation:
            widthMove 2s ease 0.1s infinite normal,
            heightMove 2s ease 0.1s infinite normal;
            动画说明:动画名 单次循环的持续时间 速度曲线 开始前的延迟时间 重复次数 播放方向
          */
        }
        .divIn{
          position: relative;
          left: -50px;
          top: 10px;
        }
        @keyframes widthMove {
          from {
            width: 0px;
            background: #ffffff;
            height: 0px;
            font-size: 12px;
          }
          to {
            width: 1000px;
            background: #999999;
            height: 720px;
            font-size: 16px;
          }
        }
      </style>
    </head>
    <body>
      <div class="divSize divDown">
        <div class="divIn">
          <pre>
            1、animation悬停触发,有可能闪烁,原因如下
              (1)在初始位置,面积为100%,hover触发动画
              (2)from面积为0,失去hover
              (3)回到初始位置,面积为100%,hover触发动画
            2、animation部分配置取值
              速度曲线
                ease,默认,动画以低速开始,然后加快,在结束前变慢
                ease-in,动画以低速开始
                ease-out,动画以低速结束
                ease-in-out,动画以低速开始和结束
                linear,动画从头到尾的速度是相同的
              重复次数
                number,一个数字,定义应该播放多少次动画
                infinite,指定动画应该播放无限次(永远)
              播放方向
                normal,正常播放,默认值
                reverse,反向播放
                alternate,奇正向播放,动画在奇数次(1、3、5...)正向播放,在偶数次(2、4、6...)反向播放
                alternate-reverse,奇反向播放,动画在奇数次(1、3、5...)反向播放,在偶数次(2、4、6...)正向播放
          </pre>
        </div>
      </div>
    </body>
  </html> 
  

  

posted @ 2019-06-03 13:55  WEB前端工程师_钱成  阅读(3760)  评论(0)    收藏  举报