<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>个人学习心得体会</title>
  
  <subtitle>Luwang&#39;s Blog</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://wallleap.cn/"/>
  <updated>2025-01-24T09:22:15.353Z</updated>
  <id>https://wallleap.cn/</id>
  
  <author>
    <name>Luwang</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>PS学习案例之蒙版</title>
    <link href="https://wallleap.cn/2021/03/04/ps-anli1/"/>
    <id>https://wallleap.cn/2021/03/04/ps-anli1/</id>
    <published>2021-03-04T14:16:01.000Z</published>
    <updated>2025-01-24T09:22:15.353Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2><span id="一-简介">一、简介</span></h2><ul><li><p>在PS中的蒙版可以分为<strong>图层蒙版</strong>、<strong>矢量蒙版</strong>、<strong>剪切蒙版</strong>、<strong>快速蒙版</strong>四类。</p></li><li><p>蒙版可以理解为浮在图层上面的一块玻璃，它本身不包含图像数据，只是对图层的部分起到遮挡的作用，当我们对图层进行操作时，被挡的数据是不会受到影响的(这样可以不需要直接在原图层上修改，达到保护图片的作用)。</p></li><li><p>蒙板是将不同灰度色值转化为不同的透明度值，并作用到它所在的图层，让图层不同地方透明度发生相应的变化。纯黑色表示完全透明，纯白色表示完全不透明。</p></li><li><p>PS中蒙版有两种，一种是白蒙版，另一种是黑蒙版(反向蒙版)，添加蒙版之后一般用颜色相反的画笔进行涂抹，并且画笔的不透明度一般设置在20%-25%，画笔笔尖采用柔笔。</p></li><li><p>白蒙版直接新建蒙版，黑蒙版<kbd>Alt</kbd>+创建蒙版：</p></li></ul><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210309152659135.png" alt="image-20210309152659135"></p><h2><span id="二-案例">二、案例</span></h2><h3><span id="1-剪切蒙版">1、剪切蒙版</span></h3><p>步骤1、将形状图层放下面；</p><p>步骤2、将需要剪贴的图层放上面(主体物在上方——第一视觉看到的)；</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304111427253.png" alt="image-20210304111427253"></p><p>步骤3、点击创建蒙版按钮；</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304111504959.png" alt="image-20210304111504959"></p><p>步骤4、按住<kbd>Alt</kbd>+点击两个图层的中间即可创建剪贴蒙版(以后也可以用这个方式把图层样式、图层放到某个图层里/下)。</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304111829545.png" alt="image-20210304111829545"></p><h3><span id="2-图层蒙版案例">2、图层蒙版案例</span></h3><h4><span id="1-鸡身鸭蹼">(1) 鸡身鸭蹼</span></h4><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304131348101.png" alt="image-20210304131348101"></p><p>1、将鸡、鸭拖入PS，鸡图层在上面，鸭图层在下面</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304132101200.png" alt="image-20210304132101200"></p><p>2、把鸡图层透明度调小一点</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304132205094.png" alt="image-20210304132205094"></p><p>3、移动鸭图层，让鸭的脚蹼到鸡的合适的位置（选中鸭图层，将自动选择前复选框的√去掉）</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304132413074.png" alt="image-20210304132413074"></p><p>4、点击鸡图层，将不透明度改为100%，并且新建一个蒙版</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304132640380.png" alt="image-20210304132640380"></p><p>5、点击画笔工具，对椭圆部位进行涂抹</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304132600987.png" alt="image-20210304132600987"></p><p>6、涂抹完成</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304142035401.png" alt="image-20210304142035401"></p><h4><span id="2-人物森林">(2) 人物森林</span></h4><p>1、拖入风景、人物两张图片，人物图层放在下方，风景图层放在上方</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304113216340.png" alt="image-20210304113216340"></p><p>2、将背景图层的锁取消，点击上面的图层的眼睛，把它隐藏掉，使用快速选择工具选出人物</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304113509514.png" alt="image-20210304113509514"></p><p>3、回到风景图层，点击左边的眼睛，让该图层显示出来，点击创建蒙版按钮</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304113601309.png" alt="image-20210304113601309"></p><p>4、点击画笔工具，画笔笔尖形状用【柔角30】，硬度为【0%】，画笔模式为【正常】，不透明度【28%】</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304113703527.png" alt="image-20210304113703527"></p><p>5、把人脸涂抹出来</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304113743938.png" alt="image-20210304113743938"></p><h4><span id="3-桥和湖面">(3) 桥和湖面</span></h4><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/40.jpg" alt="40"></p><p>1、将两张图片拖到PS中，蓝色湖面的图层在下面，桥的图层在上面</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210309154334518.png" alt="image-20210309154334518"></p><p>2、给桥的图层添加一个蒙版</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210309154609069.png" alt="image-20210309154609069"></p><p>3、在工具组中选择<code>渐变工具</code></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210309154729153.png" alt="image-20210309154729153"></p><p>4、在上面设置中把渐变颜色改为【从前景色到背景色渐变】，【线性渐变】，并且把前景色调为白背景色调黑(如果正好相反，可以用快捷键<kbd>X</kbd>调换前背景色，或者勾选上方渐变设置中的反向)</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210309154917995.png" alt="image-20210309154917995"></p><p>5、之后在合适的位置拉一条渐变</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210309155450740.png" alt="image-20210309155450740"></p><p>6、这样蓝色湖面就出来了，接下来就需要把倒影弄出来</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210309155606605.png" alt="image-20210309155606605"></p><p>7、再把桥的图片拖进来并进行垂直翻转</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210309155827140.png" alt="image-20210309155827140"></p><p>8、把这个图层移到蒙版的下面，并且把透明度调低</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210309160105389.png" alt="image-20210309160105389"></p><p>9、选中移动工具，把自动选择前的勾去掉，把图片移到合适的位置</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210309160239538.png" alt="image-20210309160239538"></p><hr><h3><span id="3-利用蒙版进行人物精修">3、利用蒙版进行人物精修</span></h3><h4><span id="1-方法一">(1) 方法一</span></h4><p>1、拖入图片，<kbd>Ctrl</kbd>+<kbd>J</kbd>复制图层</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304142349456.png" alt="image-20210304142349456"></p><p>2、滤镜-模糊-<strong>高斯模糊</strong>，半径数值调整到图片上看不到斑点为止(调的数值决定了能够淡斑的最终程度)</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304142659034.png" alt="image-20210304142659034"></p><p>3、<kbd>Alt</kbd>+创建蒙版创建反向(黑色)蒙版</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304142756605.png" alt="image-20210304142756605"></p><p>4、使用画笔进行涂抹（不透明度在20%-25%）</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/design@latest/img/image-20210304142918909.png" alt="image-20210304142918909"></p><h4><span id="2-方法二">(2) 方法二</span></h4><p>1、拖入图片，<kbd>Ctrl</kbd>+<kbd>J</kbd>复制图层</p><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="设计" scheme="https://wallleap.cn/categories/%E8%AE%BE%E8%AE%A1/"/>
    
      <category term="PS" scheme="https://wallleap.cn/categories/%E8%AE%BE%E8%AE%A1/PS/"/>
    
    
      <category term="设计" scheme="https://wallleap.cn/tags/%E8%AE%BE%E8%AE%A1/"/>
    
  </entry>
  
  <entry>
    <title>Vue学习笔记</title>
    <link href="https://wallleap.cn/2020/08/28/VUE/"/>
    <id>https://wallleap.cn/2020/08/28/VUE/</id>
    <published>2020-08-28T04:33:14.000Z</published>
    <updated>2025-01-24T09:22:15.350Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2><span id="一-vue概述">一、Vue概述</span></h2><h3><span id="11-前言">1.1 前言</span></h3><h4><span id="111-前端开发模式的发展">1.1.1 前端开发模式的发展</span></h4><ol><li>静态页面</li></ol><ul><li>最初的网页以HTML为主，是纯静态的网页。网页是只读的，信息流只能从服务端到客户端单向流通。开发人员也只关心页面的样式和内容。</li></ul><ol start="2"><li>异步刷新，操作DOM</li></ol><ul><li>1995年，网景工程师Brendan Eich花了10天时间设计了JavaScript语言。<ul><li>随着JavaScript的诞生，我们可以操作页面的DOM元素及样式，页面有了一些动态地效果，但是依然是以静态为主。</li></ul></li><li>ajax盛行：<ul><li>2005年开始，ajax逐渐被前端开发人员所重视，因为不用刷新页面就可以更新页面的数据和渲染效果。</li><li>此时的开发人员不仅需要编写HTML样式，还要动ajax与后端交互，然后通过js操作DOM元素来实现页面动态效果。比较流行的框架如jQuery就是典型代表。</li></ul></li></ul><ol start="3"><li>MVVM，关注模型和视图</li></ol><ul><li>2008年，Google的Chrome发布，随后就以极快的速度占领市场，超过IE称为浏览器市场的主导者。</li><li>2009年，Ryan Dahl在谷歌的Chrome V8引擎基础上，打造了基于事件循环的异步IO框架：Node.js。<ul><li>基于事件循环的异步IO</li><li>单线程运行，避免多线程的变量同步问题</li><li>js可以编写后台diamante，前后台统一编程语言</li></ul></li><li>Node.js的伟大之处不在于让js迈向了后端开发，而是构建了一个庞大的生态系统。</li><li>2010年，NPM作为node.js的包管理系统首次发布，开发人员可以遵循CommonJS规范来编写Node.js模块，然后发布到NPM上供其他开发人员使用。目前已经是世界上最大的包模块管理系统。</li><li>随后，在Node的基础上，涌现了一大批的前端框架：</li></ul><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustrtion/20200827092819.png" alt="image-20200827092403465"></p><h4><span id="112-mvvm模式">1.1.2 MVVM模式</span></h4><p>M：Model，模型，包括数据和一些基本操作</p><p>V：View，视图，页面渲染结果</p><p>VM：View-Model，模型与视图间的双向操作(无需开发人员干涉)</p><p>在MVVM之前，开发人员从后端获取需要的数据模型，然后要通过DOM操作Model渲染到View中。而后当用户操作视图，我们还需要通过DOM获取View中的数据，然后同步到Model中。</p><p>而MVVM中的VM要做的事情就是把DOM操作完全封装起来，开发人员不用再关心Model和View之间是如何相互影响的：</p><ul><li>只要Model发生了改变，View上自然就会表现出来。</li><li>当用户修改了View，Model中的数据也会跟着改变。</li></ul><p>把开发人员从繁琐的DOM操作中解放出来，把关注点放在如何操作Model上。</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustrtion/20200827093743.png" alt></p><p>MVVM通过<strong>视图</strong>与<strong>模型</strong>的<strong>双向绑定</strong>，简化前端操作。</p><p>Vue就是一款MVVM模式的框架</p><h3><span id="12-认识vue">1.2 认识Vue</span></h3><blockquote><p>Vue (读音 /vjuː/，类似于 <strong>view</strong>) 是一套用于构建用户界面的<strong>渐进式框架</strong>。与其它大型框架不同的是，Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层，不仅易于上手，还便于与第三方库或既有项目整合。另一方面，当与<a href="https://cn.vuejs.org/v2/guide/single-file-components.html" target="_blank" rel="noopener">现代化的工具链</a>以及各种<a href="https://github.com/vuejs/awesome-vue#libraries--plugins" target="_blank" rel="noopener">支持类库</a>结合使用时，Vue 也完全能够为复杂的单页应用提供驱动。</p></blockquote><ul><li>前端框架三巨头：Vue.js、React.js、Angular.js，Vue.js以其轻量易用著称，Vue.js和React.js发展速度最快。</li><li>遵循 MVVM 模式  </li><li>渐进式 JavaScript 框架  <ul><li>渐进式：可以<strong>选择性</strong>地使用该框架的一个或一些组件，这些组件的使用也不需要将框架全部组件都应用，而且用了这些组件也不要求你的系统全部都使用该框架。</li></ul></li><li>中文官网：<a href="https://cn.vuejs.org/" target="_blank" rel="noopener">https://cn.vuejs.org/</a></li><li>英文官网：<a href="https://vuejs.org/" target="_blank" rel="noopener">https://vuejs.org/</a></li><li>参考：<a href="https://cn.vuejs.org/v2/guide/" target="_blank" rel="noopener">https://cn.vuejs.org/v2/guide/</a></li><li>作用: 动态构建用户界面  </li><li>特点：<ul><li>易用</li><li>灵活</li><li>高效</li></ul></li><li>编码简洁, 体积小, 运行效率高, 适合移动/PC 端开发  </li><li>它本身只关注 UI, 可以轻松引入 vue 插件或其它第三库开发项目  </li><li>GitHub地址：<a href="https://github.com/vuejs" target="_blank" rel="noopener">https://github.com/vuejs</a></li><li>作者：<a href="https://github.com/yyx990803" target="_blank" rel="noopener">尤雨溪</a>(一位华裔前 Google 工程师)，Vue.js创作者，Vue Technology创始人，致力于Vue的研究开发。</li></ul><h3><span id="13-与其它前端js框架的关联">1.3 与其它前端JS框架的关联</span></h3><ul><li>借鉴 angular 的<strong>模板</strong>和<strong>数据绑定</strong>技术</li><li>借鉴 react 的<strong>组件化</strong>和<strong>虚拟 DOM</strong> 技术  </li></ul><h3><span id="14-vue扩展插件">1.4 Vue扩展插件</span></h3><ul><li>vue-cli: vue 脚手架</li><li>vue-resource(axios): ajax 请求</li><li>vue-router: 路由</li><li>vuex: 状态管理</li><li>vue-lazyload: 图片懒加载</li><li>vue-scroller: 页面滑动相关</li><li>mint-ui: 基于 vue 的 UI 组件库(移动端)</li><li>element-ui: 基于 vue 的 UI 组件库(PC 端)  </li></ul><h2><span id="二-快速入门">二、快速入门</span></h2><p>搭建示例工程</p><h3><span id="21创建工程">2.1创建工程</span></h3><p>创建一个目录，例如：<code>testVue</code></p><h3><span id="22-引入vue">2.2 引入Vue</span></h3><ul><li><p>使用CDN(使用jsdelivr，也可以到bootcdn中找)</p><ul><li>开发版(包含了有帮助的命令行警告)：<a href="https://cdn.jsdelivr.net/npm/vue/dist/vue.js" target="_blank" rel="noopener">https://cdn.jsdelivr.net/npm/vue/dist/vue.js</a></li><li>生产版(压缩了，优化了尺寸和速度)：<a href="https://cdn.jsdelivr.net/npm/vue" target="_blank" rel="noopener">https://cdn.jsdelivr.net/npm/vue</a></li></ul><pre><code class="html">&lt;!-- 引入 --&gt;&lt;script src=&quot;https://……&quot;&gt;&lt;/script&gt;</code></pre></li></ul><ul><li><p>下载到本地</p><ul><li>下载地址：<a href="https://github.com/vuejs/vue" target="_blank" rel="noopener">https://github.com/vuejs/vue</a></li><li>下载release版：<a href="https://github.com/vuejs/vue/archive/v2.6.11.zip，解压之后在`dist`目录中可以看到`vue.js`文件" target="_blank" rel="noopener">https://github.com/vuejs/vue/archive/v2.6.11.zip，解压之后在`dist`目录中可以看到`vue.js`文件</a></li></ul><pre><code class="html">&lt;!-- 引入 --&gt;&lt;script src=&quot;vue.js&quot;&gt;&lt;/script&gt;</code></pre></li></ul><ul><li><p><strong>npm安装</strong>(推荐)</p><ul><li>进入工程目录：<code>cd testVue</code></li><li>初始化项目：<code>npm init -y</code></li><li>下载安装Vue：<code>npm install vue --save</code></li></ul></li></ul><h3><span id="23-演示双向绑定与事件处理">2.3、演示双向绑定与事件处理</span></h3><p>需求：创建testVue.html页面并初始化Vue实例，通过console修改Vue数据实现双向绑定效果和创建按钮实现点击自增效果。</p><ul><li>创建页面，初始化Vue</li><li><code>{ {} }</code>获取显示数据</li><li><code>v-model</code>实现<strong>双向绑定</strong></li><li><code>v-on</code>演示<strong>事件处理</strong></li></ul><p>在刚刚<code>testVue</code>目录中新建<code>testVue.html</code>文件，书写代码，测试初始化Vue实例、<code>{ {} }</code>使用</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;!-- 引入vue.js        1. cdn        2. 下载后本地引入        3. npm安装后从node_modules中引入(项目中import引入)    --&gt;    &lt;script src=&quot;node_modules/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;h2&gt;{{name}}&lt;/h2&gt;  &lt;!-- 插值 --&gt;        &lt;p&gt;渐进式JavaScript框架&lt;/p&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        /* 创建Vue实例，在里面可以指定一些vue的参数 */        var app = new Vue({ // 键值对            el: &quot;#app&quot;, // 指定需要渲染的元素            data:{ // 指定数据                name: &quot;Vue.js&quot;            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p>在控制台输入命令：</p><pre><code class="javascript">app.name=&quot;Vue&quot;</code></pre><p>Vue.js也会变成Vue</p><p>测试<code>v-model</code></p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;input type=&quot;text&quot; v-model=&quot;num&quot;&gt; &lt;!-- 模型 --&gt;        &lt;h2&gt;{{name}} 创建 {{num}} 年了&lt;/h2&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var app = new Vue({            el: &quot;#app&quot;,            data:{                name: &quot;Vue.js&quot;,                num: 5            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p>修改文本框中的值，下面的xx年也会变(模型改变，视图也改变；视图改变也会影响对应的模型)，控制台中也能修改</p><p>上面的演示的是双向绑定，下面接着演示事件处理</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;input type=&quot;text&quot; v-model=&quot;num&quot;&gt; &lt;!-- 模型 --&gt;        &lt;h2&gt;{{name}} 创建 {{num}} 年了&lt;/h2&gt;        &lt;button v-on:click=&quot;num++&quot;&gt;num+1&lt;/button&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var app = new Vue({             el: &quot;#app&quot;,            data:{                name: &quot;Vue.js&quot;,                num: 5            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p>点击按钮，num会自增</p><h2><span id="三-vue实例">三、Vue实例</span></h2><p>在创建Vue实例的时候可以指定模板id、数据和方法；而如果要在实例化、模板渲染的过程中需要执行一些其他操作的话，那么可以使用生命周期钩子函数</p><h3><span id="31-创建vue实例">3.1 创建Vue实例</span></h3><p>每个Vue应用都是通过用<code>Vue</code>函数创建一个新的<strong>Vue实例</strong>开始的(常用变量vm——ViewModel接收)</p><pre><code class="javascript">var vm = new Vue({ // 传入的参数——对象  // 选项})</code></pre><p>在构造函数中传入一个对象，并且在对象中声明各种Vue需要的数据和方法，包括：</p><ul><li><p>el：指定一个页面元素，受Vue实例的管理，只有被Vue实例管理的元素内部才能使用Vue的语法。</p></li><li><p>data：定义Vue实例中使用到的数据，本身就是一个对象，里面的键值对可以随意写。使用时可以直接<code>vm.msg</code>使用这个数据(不需要<code>vm.data.msg</code>)，在声明周期钩子函数中使用<code>this.msg</code>。</p></li><li><p>methods：定义Vue实例的一些函数</p></li><li><p>computed：计算属性，可以将一些属性数据经过方法处理之后返回。</p></li><li><p>watch：监控属性，可以指定一些方法，监控指定的值的变化。</p><ul><li><p>监控简单数据：定义一个和监控的变量名称一致的函数即可，函数的参数为新值和旧值。例如，要监控data中的message，在watch中<code>message(newValue,oldValue){}</code></p></li><li><p>监控对象中的数据——深度监控：定义一个和监控的对象名称一致的属性，值是一个对象。内部设置deep属性为true代表深度监控开启，回调函数为handler，会传过来新的对象，例如：</p><pre><code class="javascript">watch:{  person: {    /* 开启深度监控，监控对象中的属性值变化 */    deep: true,    // 可以获取到最新的对象属性数据    handler(obj){     console.log(&quot;姓名：&quot; + obj.name + &quot;,年龄：&quot; + obj.age)  }}</code></pre></li></ul></li></ul><ul><li><p>template</p></li><li><p>声明周期钩子函数名(ES6语法<code>created: function(){}</code>简化为<code>created(){}</code>)，本质仍是键值对</p></li><li><p>…</p></li></ul><h3><span id="32-模板或元素">3.2 模板或元素</span></h3><p>每个Vue实例都需要关联一段HTML模板，Vue会基于此模板进行视图渲染；可以通过el属性来指定。</p><p>例如一段HTML模板：</p><pre><code class="html">&lt;div id=&quot;app&quot;&gt;&lt;/div&gt;</code></pre><p>然后创建Vue实例，关联这个div</p><pre><code class="javascript">var vm = new Vue({  el: &quot;#app&quot;})</code></pre><p>这样，Vue就可以基于id为app的div元素作为模板进行渲染了，在这个div范围以外的部分是无法使用vue特性的。</p><h3><span id="33-数据">3.3 数据</span></h3><p>当Vue实例被创建时，它会尝试获取在data中定义的所有属性，用于视图的渲染，并且<strong>监视</strong>data中的属性变化，当data发生改变，所有相关的视图都将重新渲染，这就是“响应式”系统。</p><p>例如在HTML中模型指定name</p><pre><code class="html">&lt;div id=&quot;app&quot;&gt;  &lt;input type=&quot;text&quot; v-model=&quot;name&quot; /&gt;&lt;/div&gt;</code></pre><p>js中vue的data属性设置一个name</p><pre><code class="javascript">var vm = new Vue({  el: &quot;#app&quot;,  data:{    name: &quot;Vue.js&quot;  }})</code></pre><ul><li>name的变化会影响到input的值</li><li>input中输入的值，也会导致vm中的name发生改变</li></ul><h3><span id="34-方法">3.4 方法</span></h3><p>Vue实例中除了可以定义data属性，也可以定义方法，并且在Vue的作用范围内使用。</p><p>例如HTML模板中指定v-on事件click之后执行的代码(可以是一个语句也可以是个函数)</p><pre><code class="html">&lt;div id=&quot;app&quot;&gt;  &lt;button v-on:click=&quot;add&quot;&gt;add&lt;/button&gt;&lt;/div&gt;</code></pre><p>在创建实例时到methods属性中定义add方法</p><pre><code class="javascript">var vm = new Vue({  el: &quot;#app&quot;,  data:{},  methods:{    add: function(){      console.log(&quot;add被点击了&quot;)    }  }})</code></pre><h3><span id="35-声明周期及钩子函数">3.5 声明周期及钩子函数</span></h3><h4><span id="351-声明周期">3.5.1 声明周期</span></h4><blockquote><p>每个Vue实例在被创建时都要经过一系列的初始化过程——创建实例、装载模板、渲染模板等，Vue为生命周期中的每个状态都设置了钩子函数(监听函数)，每当Vue实例处于不同的声明周期时，对应的函数就会被触发调用。</p></blockquote><p>所有的生命周期钩子自动绑定<code>this</code>上下文到实例中，因此你可以访问数据，对属性和方法进行运算。这意味着你<strong>不能使用箭头函数来定义一个声明周期方法</strong>(比如 <code>created: () =&gt; console.log(this.a)</code> 或 <code>vm.$watch(&#39;a&#39;, newValue =&gt; this.myMethod())</code>)，因为箭头函数的 <code>this</code>与你期待的Vue实例不同，<code>this</code> 会作为变量一直向上级词法作用域查找，直至找到为止，经常导致 <code>Uncaught TypeError: Cannot read property of undefined</code> 或 <code>Uncaught TypeError: this.myMethod is not a function</code> 之类的错误。</p><p>声明周期：</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustrtion/20200827121350.png" alt></p><p>实例化——&gt;挂载——&gt;销毁    (如果使用构建步骤，模板编译会提前执行，例如单文件组件)</p><p>钩子函数会在Vue实例的各个声明周期阶段自动调用，具体有：</p><p>(1) 初始化显示：</p><ul><li><code>beforeCreate</code>：在实例初始化之后，数据观测(data observer)和event/watcher事件配置之前被调用。</li><li><strong><code>created</code></strong>：在实例创建完成后被立即调用。在这一步，实例已完成以下的配置：数据观测(data observer)，属性和方法的运算，watch/event事件回调。然而，挂载阶段还没开始，$el属性目前不可见。</li><li><code>beforeMount</code>：在挂载开始之前被调用。相关的render函数首次被调用。</li><li><strong><code>mounted</code></strong>：el被新创建的<code>vm.$el</code>替换，并挂载到实例上去之后调用该钩子。如果root实例挂载了一个文档内元素。当mounted被调用时<code>vm.$el</code>也在文档内。[vm.$el：Vue实例使用的根DOM元素]</li></ul><p>(2) 更新状态：(<code>this.xxx=value</code>)</p><ul><li><code>beforeUpdate</code>：数据更新时调用，发生在虚拟DOM打补丁之前，这里适合在更新之前访问现有的DOM，比如手动移除已添加的事件监听器。</li><li><code>updated</code>：由于数据更改导致的虚拟DOM重新渲染和打补丁，在这之后会调用该钩子。当这个 钩子被调用时，组件DOM已经更新，所以此时可以执行依赖于DOM的操作。然而在大多数情况下，应该避免在此期间更改状态，如果要相应状态改变，通常最好使用计算属性或watcher取而代之。</li></ul><p>(3) 销毁Vue实例：(<code>vm.$destory()</code>)</p><ul><li><strong><code>beforeDestroy</code></strong>：实例在销毁之前调用。在这一步，实例仍然完全可用。</li><li><code>destroyed</code>：Vue实例销毁后调用。调用后，Vue实例指示的所有东西都会解绑定，所有的事件监听器会被移除，所有的子实例也会被销毁。</li></ul><p>[vm.$root：当前组件树的根Vue实例，如果当前实例没有父实例，此实例将会是其自己]</p><h4><span id="352-钩子函数">3.5.2 钩子函数</span></h4><p>例如：created代表在Vue实例创建后</p><p>可以在Vue中定义一个created函数，代表这个时期的构造函数：</p><p>创建页面<code>vueLifCycle.html</code></p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js声明周期钩子created测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;h2&gt;{{msg}}&lt;/h2&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var vm = new Vue({            el: &quot;#app&quot;,            data:{                msg: &quot;&quot;            },            // 钩子函数            created(){                this.msg = &quot;Hello Vue.js created&quot;                console.log(this)            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p>created常用于<strong>数据的初始化</strong>，即发送ajax，获取后台数据，给data属性中的数据进行赋值，接着在模板中应用数据，视图也会发生改变，从而实现数据的异步加载。</p><p>mounted发送 ajax 请求, 启动定时器等异步任务  </p><p>beforeDestory做收尾工作, 如: 清除定时器  </p><h4><span id="353-this">3.5.3 this</span></h4><p>打印的this(这个Vue实例对象)如下：</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustrtion/20200827155040.png" alt="image-20200827155038198"></p><h2><span id="四-指令">四、指令</span></h2><p>指令(Directives)是带有<code>v-</code>前缀的特殊属性。例如在入门案例中的v-model，代表双向绑定。</p><h3><span id="41-插值表达式">4.1 插值表达式</span></h3><h4><span id="411-大括号">4.1.1 大括号</span></h4><ul><li><p>格式：<code></code></p></li><li><p>说明：</p><ul><li>该表达式支持<strong>JS语法</strong>，可以调用js内置函数(必须有<strong>返回值</strong>)</li><li>表达式必须有返回结果。例如<code>1+1</code>，没有结果的表达式不允许使用，如<code>var a = 1 + 1</code></li><li>可以直接获取Vue实例data中定义的<strong>数据或函数</strong></li><li>可以直接写<code>&quot;&quot;</code></li></ul></li><li><p>示例：</p></li></ul><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js插值表达式测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;h2&gt;{{msg}}&lt;/h2&gt;    &lt;h2&gt;{{"说的没错"}}&lt;/h2&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var vm = new Vue({            el: &quot;#app&quot;,            data:{                msg: &quot;Vue.js 轻便好用&quot;            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><h4><span id="412-插值闪烁">4.1.2 <del>插值闪烁</del></span></h4><p>使用<code>{ {} }</code>方式在网速较慢的时候会出现问题，在数据未加载完成时，页面会显示出原始的<code></code>，加载完毕后才显示正确数据，这种情况称为插值闪烁(在最新的Vue中几乎没有这个问题)。</p><p>在以前版本中，出现插值闪烁，可以使用v-text、v-html解决。</p><h4><span id="413-v-text和v-html">4.1.3 v-text和v-html</span></h4><ul><li><p>使用v-text和v-html指令来代替<code>{ {} }</code></p></li><li><p>说明：</p><ul><li><code>v-text</code>：将数据输出到元素内部，如果输出的数据有HTML代码，会作为普通文本输出</li><li><code>v-html</code>：将数据输出到元素内部，如果输出的数据有HTML代码，会被渲染</li></ul></li><li><p>示例：</p></li></ul><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js v-html/v-text测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;h2 v-text=&quot;msg&quot;&gt;&lt;/h2&gt;        &lt;h2 v-html=&quot;msg&quot;&gt;&lt;/h2&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var vm = new Vue({            el: &quot;#app&quot;,            data:{                msg: &quot;&lt;span style=&#39;color: red;&#39;&gt;Vue.js 很好用&lt;/span&gt;&quot;            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustrtion/20200827164955.png" alt="image-20200827164954008"></p><p><strong>插值</strong>可以使用在需要显示Vue实例数据的地方，可以在插值表达式中调用实例的属性和函数。</p><p>v-text和v-html的作用：可以将数据在模板中进行显示；区别：<strong>v-html</strong>会对内容中出现的HTML标签进行渲染，而<strong>v-text</strong>会将内容当作普通文本输出到元素里面。(有点像js中的innerHTML和innerText)</p><h3><span id="42-v-model">4.2 v-model</span></h3><p>刚才的v-text和v-html可以看做是<strong>单向绑定</strong>，数据影响了视图渲染，但是反过来就不行。接下来的v-model就是<strong>双向绑定</strong>的了，视图(View)和模型(Model)之间会相互影响。</p><p>既然是双向绑定，一定是在视图中可以修改数据，这样就限定了视图的元素类型。目前v-model可以使用的元素有：</p><ul><li>input</li><li>select</li><li>textarea</li><li>checkbox</li><li>radio</li><li>components(Vue中的自定义组件)</li></ul><p>基本上除了最后一项，其它都是表单的输入项。</p><p>示例：</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js v-model测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;label&gt;&lt;input type=&quot;checkbox&quot; value=&quot;C/C++&quot; v-model=&quot;language&quot; /&gt;C/C++ &lt;/label&gt;&lt;br&gt;        &lt;label&gt;&lt;input type=&quot;checkbox&quot; value=&quot;Java&quot; v-model=&quot;language&quot; /&gt;Java &lt;/label&gt;&lt;br&gt;        &lt;label&gt;&lt;input type=&quot;checkbox&quot; value=&quot;PHP&quot; v-model=&quot;language&quot; /&gt;PHP &lt;/label&gt;&lt;br&gt;        &lt;h2&gt;你选择了：{{language.join(", ")}}&lt;/h2&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var vm = new Vue({            el: &quot;#app&quot;,            data:{                language: []            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustrtion/20200827182301.gif" alt="demo"></p><ul><li>多个<code>checkbox</code>对应一个model时，model的类型是一个<strong>数组</strong>，单个checkbox值是<strong>boolean类型</strong></li><li><code>radio</code>对应的值是input的<strong>value值</strong></li><li><code>input</code>和<code>textarea</code>默认对应的model是<strong>字符串</strong></li><li><code>select</code>单选对应<strong>字符串</strong>，多选对应也是<strong>数组</strong></li></ul><p>补充：插件Vue.js devtools</p><p><a href="https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?utm_source=chrome-ntp-icon" target="_blank" rel="noopener">https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?utm_source=chrome-ntp-icon</a></p><h3><span id="43-v-on">4.3 v-on</span></h3><h4><span id="431-基本用法">4.3.1 基本用法</span></h4><p>在没有使用Vue之前，页面标签可以通过设置onXXX响应事件；在Vue中可以通过v-on指令响应事件(<strong>给页面元素绑定事件</strong>)。</p><p>语法：<code>v-on:事件名=&quot;js片段或函数名&quot;</code></p><p>简写语法：<code>@事件名=&quot;js片段或函数名&quot;</code></p><p>例如<code>v-on:click=&quot;add&quot;</code>可以简写为<code>@click=&quot;add&quot;</code></p><p>示例：</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js v-on测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;button v-on:click=&quot;num++&quot;&gt;增加&lt;/button&gt;        &lt;button @click=&quot;decrement&quot;&gt;减少&lt;/button&gt;        &lt;h2&gt;num的数值为 {{num}}&lt;/h2&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var vm = new Vue({            el: &quot;#app&quot;,            data:{                num: 1            },            methods: {                decrement: function(){                    this.num--                }            },        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustrtion/20200827184155.gif" alt="on"></p><p>默认事件形参: <code>event</code><br>隐含属性对象: <code>$event</code></p><h4><span id="432-事件修饰符">4.3.2 事件修饰符</span></h4><p>在事件处理程序中调用<code>event.preventDefault()</code>或<code>event.stopPropagation()</code>是非常常见的需求(阻止默认事件)。尽管我们可以在方法中轻松实现这点，但方法只有纯粹的数据逻辑，而不是去处理DOM事件细节。</p><p>为了解决这个问题，Vue.js为<code>v-on</code>提供了<strong>事件修饰符</strong>。修饰符是由点开头的指令后缀来表示的。</p><p>语法：<code>v-on:事件名.修饰符=&quot;js片段或函数名&quot;</code> 或 <code>@事件名.修饰符=&quot;js片段或函数名&quot;</code></p><p>常用修饰符：</p><ul><li><strong><code>.stop</code></strong>：阻止事件冒泡<ul><li>事件冒泡：默认情况下，在某个页面元素上触发的事件，在当前元素处理完之后会自动传递给祖先元素，祖先的相同事件也会执行</li></ul></li><li><strong><code>.prevent</code></strong>：阻止默认事件发生<ul><li>浏览器默认的一些事件行为，例如：<ul><li>获取焦点事件会把光标放入输入框</li><li>表单提交事件会提交数据到action指定的url</li><li>点击a标签会跳转到href指定的地址</li></ul></li></ul></li><li><code>.capture</code>：使用事件捕获模式<ul><li>相当于和冒泡相反，父元素先于子元素获取事件</li></ul></li><li><code>.self</code>：只有元素自身触发事件才执行(冒泡或捕获的都不执行)</li><li><code>.once</code>：只执行一次</li></ul><p>冒泡测试：</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;h2&gt;事件冒泡测试&lt;/h2&gt;        &lt;div v-on:click=&quot;print(&#39;点击了div&#39;)&quot; style=&quot;background: skyblue; width: 400px; height: 400px;&quot;&gt;            &lt;button @click=&quot;print(&#39;点击了button&#39;)&quot;&gt;按钮&lt;/button&gt;        &lt;/div&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var vm = new Vue({            el: &quot;#app&quot;,            data:{                num: 1            },            methods: {                print(str){                    console.log(str)                }            },        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustrtion/20200827190036.gif" alt="bubble"></p><p>阻止冒泡：</p><pre><code class="html">&lt;div id=&quot;app&quot;&gt;    &lt;h2&gt;阻止事件冒泡测试&lt;/h2&gt;    &lt;div v-on:click=&quot;print(&#39;点击了div&#39;)&quot; style=&quot;background: skyblue; width: 400px; height: 400px;&quot;&gt;        &lt;button @click.stop=&quot;print(&#39;点击了button&#39;)&quot;&gt;按钮&lt;/button&gt;    &lt;/div&gt;&lt;/div&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustrtion/20200827190024.gif" alt="stopbubble"></p><p>阻止默认事件：点击超链接不会跳转</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;h2&gt;阻止默认事件&lt;/h2&gt;        &lt;a href=&quot;https://www.baidu.com&quot; @click.prevent=&quot;print(&#39;点击了a&#39;)&quot;&gt;百度&lt;/a&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var vm = new Vue({            el: &quot;#app&quot;,            data:{                num: 1            },            methods: {                print(str){                    console.log(str)                }            },        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><h4><span id="433-按键修饰符">4.3.3 按键修饰符</span></h4><ul><li><code>.keycode</code> : 操作的是某个 keycode 值的键</li><li><code>.keyName</code> : 操作的某个按键名的键(少部分)  </li></ul><pre><code class="html">&lt;h2&gt;3. 按键修饰符&lt;/h2&gt;&lt;input @keyup.8=&quot;test&quot;&gt; &lt;!-- 按键8 --&gt;&lt;input @keyup.enter=&quot;test&quot;&gt; &lt;!-- 按键enter --&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/vue.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;new Vue({  el: &#39;#example&#39;,  data: {      name: &#39;Vue.js&#39;  },  methods: {    test(event) {          alert(event.keyCode + &#39;---&#39; + event.target.value)      }  }})&lt;/script&gt;</code></pre><h3><span id="44-v-for">4.4 v-for</span></h3><p>可以在Vue实例化的时候指定要遍历的数据，然后通过v-for指令在模板中遍历显示数据。一般情况下，要遍历的数据可以通过钩子函数created发送异步请求获取数据。</p><h4><span id="441-遍历数组">4.4.1 遍历数组</span></h4><ul><li>语法：<code>v-for=&quot;item in items&quot;</code><ul><li>items：要遍历的数组名或对象名，需要在Vue的data中定义好。</li><li>item：循环遍历</li></ul></li><li>示例：</li></ul><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;ul&gt;            &lt;li v-for=&quot;user in users&quot;&gt;                我是{{user.name}}，今年{{user.age}}岁了，性别{{user.gender}}            &lt;/li&gt;        &lt;/ul&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var vm = new Vue({            el: &quot;#app&quot;,            data:{                users:[                    {&quot;name&quot;:&quot;唐僧&quot;,&quot;age&quot;:24,&quot;gender&quot;:&quot;男&quot;},                    {&quot;name&quot;:&quot;孙悟空&quot;,&quot;age&quot;:26,&quot;gender&quot;:&quot;男&quot;},                    {&quot;name&quot;:&quot;猪八戒&quot;,&quot;age&quot;:30,&quot;gender&quot;:&quot;男&quot;},                    {&quot;name&quot;:&quot;沙和尚&quot;,&quot;age&quot;:32,&quot;gender&quot;:&quot;男&quot;},                    {&quot;name&quot;:&quot;蜘蛛精&quot;,&quot;age&quot;:18,&quot;gender&quot;:&quot;女&quot;}                ]            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustrtion/20200827200312.png" alt="image-20200827200311325"></p><h4><span id="442-数组角标">4.4.2 数组角标</span></h4><p>在遍历的过程中，如果需要知道数组角标/索引号，可以指定第二个参数</p><ul><li>语法：<code>v-for=&quot;(item,index) in items&quot;</code><ul><li>items：要遍历的数组</li><li>item：遍历得到的数组元素别名</li><li>index：遍历到的当前元素索引，从0开始</li></ul></li><li>示例：</li></ul><pre><code class="html">&lt;div id=&quot;app&quot;&gt;    &lt;ul&gt;        &lt;li v-for=&quot;(user, index) in users&quot;&gt;            {{index}}：我是{{user.name}}，今年{{user.age}}岁了，性别{{user.gender}}        &lt;/li&gt;    &lt;/ul&gt;&lt;/div&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustrtion/20200827200728.png" alt="image-20200827200727402"></p><h4><span id="443-遍历对象">4.4.3 遍历对象</span></h4><p>v-for除了可以迭代数组，也可以迭代对象，语法基本类似</p><p>语法：</p><pre><code>v-for=&quot;value in object&quot;v-for=&quot;(value,key) in object&quot;v-for=&quot;(value,key,index) in object&quot;</code></pre><ul><li>1个参数时，得到的是对象的值</li><li>2个参数时，第一个是值，第二个是键</li><li>3个参数时，第三个是索引，从0开始</li></ul><p>示例：</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;ul&gt;            &lt;li v-for=&quot;(value, key, index) in person&quot;&gt;                {{index}}——{{key}}——{{value}}            &lt;/li&gt;        &lt;/ul&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var vm = new Vue({            el: &quot;#app&quot;,            data:{                person:{&quot;name&quot;:&quot;孙悟空&quot;,&quot;age&quot;:26,&quot;gender&quot;:&quot;男&quot;,&quot;address&quot;:&quot;花果山&quot;}            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustrtion/20200827201526.png" alt="image-20200827201525393"></p><h4><span id="444-key">4.4.4 key</span></h4><p>当Vue.js用<code>v-for</code>正在更新已渲染过的元素列表时，它默认用”就地复用”策略。如果数据项的顺序被改变,，Vue 将不会移动DOM元素来匹配数据项的顺序，而是简单复用此处每个元，并且确保它在特定索引下显示已被渲染过的每个元素。<br>如果使用key这个功能可以有效的提高渲染的效率; key一般使用在遍历完后，还要增、减集合元素的时候更有意义。<br>但是要实现这个功能，你需要给Vue-些提示，以便它能跟踪每个节点的身份，从而重用和重新排序现有元素，你需要为每项提供一个唯一key 属性。理想的<code>key</code>值是每项都有的且唯一的id。也就是key是该项的唯一标识。</p><p>示例：</p><pre><code class="html">    &lt;ul&gt;        &lt;li v-for=&quot;(user, index) in users&quot; :key=&quot;index&quot;&gt;            {{index}}：我是{{user.name}}，今年{{user.age}}岁了，性别{{user.gender}}        &lt;/li&gt;    &lt;/ul&gt;</code></pre><p>这里使用了一个特殊的语法：<code>:key=&quot;&quot;</code>，它可以读取Vue中的属性，并赋值给key属性</p><p>这里绑定的key是数组的索引，是唯一的(以后可以加其他的唯一的数据，例如user.id)</p><h3><span id="45-v-if和v-show">4.5 v-if和v-show</span></h3><h4><span id="451-基本使用">4.5.1 基本使用</span></h4><p>v-if，条件判断，当得到的结果为true时，所在的元素才会被渲染。</p><p>语法：<code>v-if=&quot;布尔表达式&quot;</code></p><p>示例：</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;button @click=&quot;show=!show&quot;&gt;按钮&lt;/button&gt;        &lt;h2 v-if=&quot;show&quot;&gt;Hello Vue.js&lt;/h2&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var vm = new Vue({            el: &quot;#app&quot;,            data:{                show: true            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><h4><span id="452-与v-for结合">4.5.2 与v-for结合</span></h4><p>当v-if和v-for一起出现的时候，v-for优先级更高，即会先遍历，再判断条件。</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;h2&gt;女性人物&lt;/h2&gt;        &lt;ul&gt;            &lt;li v-for=&quot;(user, index) in users&quot; v-if=&quot;user.gender==&#39;女&#39;&quot;&gt;                {{index}}：我是{{user.name}}，今年{{user.age}}岁了，性别{{user.gender}}            &lt;/li&gt;        &lt;/ul&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var vm = new Vue({            el: &quot;#app&quot;,            data:{                users:[                    {&quot;name&quot;:&quot;唐僧&quot;,&quot;age&quot;:24,&quot;gender&quot;:&quot;男&quot;},                    {&quot;name&quot;:&quot;孙悟空&quot;,&quot;age&quot;:26,&quot;gender&quot;:&quot;男&quot;},                    {&quot;name&quot;:&quot;猪八戒&quot;,&quot;age&quot;:30,&quot;gender&quot;:&quot;男&quot;},                    {&quot;name&quot;:&quot;沙和尚&quot;,&quot;age&quot;:32,&quot;gender&quot;:&quot;男&quot;},                    {&quot;name&quot;:&quot;蜘蛛精&quot;,&quot;age&quot;:18,&quot;gender&quot;:&quot;女&quot;}                ]            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><h4><span id="453-v-else">4.5.3 v-else</span></h4><p>可以使用<code>v-else</code>指令来表示<code>v-if</code>的“else块”，需要注意，v-else元素必须紧跟在带有v-if或v-else-if的元素的后面，否则它将不会被识别(两者之间不能插入其他元素)</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;h2&gt;西游人物&lt;/h2&gt;        &lt;ul&gt;            &lt;li v-for=&quot;(user, index) in users&quot; v-if=&quot;user.gender==&#39;女&#39;&quot; style=&quot;color:pink;&quot;&gt;                {{index+1}}：我是{{user.name}}，今年{{user.age}}岁了，性别{{user.gender}}            &lt;/li&gt;            &lt;li v-else style=&quot;color:skyblue;&quot;&gt;                {{index+1}}：我是{{user.name}}，今年{{user.age}}岁了，性别{{user.gender}}            &lt;/li&gt;        &lt;/ul&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var vm = new Vue({            el: &quot;#app&quot;,            data:{                users:[                    {&quot;name&quot;:&quot;唐僧&quot;,&quot;age&quot;:24,&quot;gender&quot;:&quot;男&quot;},                    {&quot;name&quot;:&quot;孙悟空&quot;,&quot;age&quot;:26,&quot;gender&quot;:&quot;男&quot;},                    {&quot;name&quot;:&quot;猪八戒&quot;,&quot;age&quot;:30,&quot;gender&quot;:&quot;男&quot;},                    {&quot;name&quot;:&quot;沙和尚&quot;,&quot;age&quot;:32,&quot;gender&quot;:&quot;男&quot;},                    {&quot;name&quot;:&quot;蜘蛛精&quot;,&quot;age&quot;:18,&quot;gender&quot;:&quot;女&quot;}                ]            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p><code>v-else-if</code>，充当<code>v-if</code>的”else-if块“，可以连续使用(v-else-if也必须紧跟在带v-if或v-else-if的元素后)</p><pre><code class="html">&lt;div id=&quot;app&quot;&gt;    &lt;h2&gt;西游人物&lt;/h2&gt;    &lt;ul&gt;        &lt;li v-for=&quot;(user, index) in users&quot; v-if=&quot;user.age&lt;20&quot; style=&quot;color:yellowgreen;&quot;&gt;            {{index+1}}：我是{{user.name}}，今年{{user.age}}岁了，性别{{user.gender}}        &lt;/li&gt;        &lt;li v-else-if=&quot;user.age&lt;30&quot; style=&quot;color:skyblue;&quot;&gt;            {{index+1}}：我是{{user.name}}，今年{{user.age}}岁了，性别{{user.gender}}        &lt;/li&gt;        &lt;li v-else style=&quot;color:yellow;&quot;&gt;            {{index+1}}：我是{{user.name}}，今年{{user.age}}岁了，性别{{user.gender}}        &lt;/li&gt;    &lt;/ul&gt;&lt;/div&gt;</code></pre><h4><span id="454-v-show">4.5.4 v-show</span></h4><p><code>v-show</code>也可以根据条件是否展示元素，例如：</p><pre><code class="html">&lt;h1 v-show=&quot;ok&quot;&gt;Hello Vue.js.&lt;/h1&gt;</code></pre><p>但是，带有<code>v-show</code>的元素始终会被渲染并保留在DOM中，<code>v-show</code>只是简单地切换元素的CSS属性<code>display</code>的值。</p><p>示例：</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;button @click=&quot;show=!show&quot;&gt;切换&lt;/button&gt;        &lt;h2 v-if=&quot;show&quot;&gt;Vue.js&lt;/h2&gt;        &lt;h2 v-show=&quot;show&quot;&gt;Vue.js&lt;/h2&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var vm = new Vue({            el: &quot;#app&quot;,            data:{                show: true            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustrtion/20200828082251.png" alt="image-20200828082250681"></p><p><code>v-if</code>在条件不满足的时候元素会消失；<code>v-show</code>条件不满足的时候只是<code>display:none</code>了</p><h3><span id="46-v-bind">4.6 v-bind</span></h3><h4><span id="461-属性上使用vue数据">4.6.1 属性上使用vue数据</span></h4><p>插值表达式不能用在属性中，会报错<code>&lt;div id=&quot;box&quot; class=&quot;&quot;&gt;点击按钮改变背景颜色&lt;/div&gt;</code></p><p>v-bind作用：可以对所有元素的属性值设置为vue中data的数据</p><p>语法：在属性名之前加上<code>v-bind:</code>(<code>v-bind:属性名=&#39;Vue中的变量&#39;</code>)，简写为<code>:属性名=&#39;属性值&#39;</code></p><p><code>&lt;img src=&quot;&quot; height=&quot;&quot; /&gt;</code>其中src和height的值如果不想写死，而是想获取Vue实例中的数据属性值的话，那么可以通过使用v-bind实现</p><pre><code class="html">&lt;img v-bind:src=&quot;vue实例中的数据属性名&quot; :height=&quot;vue实例中的数据属性名&quot; /&gt;</code></pre><p>利用v-bind实现点击切换背景颜色</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;    &lt;style&gt;        #box{            width: 150px;            height: 150px;            color: white;        }        .red{            background-color: red;        }        .green{            background-color: green;        }    &lt;/style&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;button @click=&quot;bgcolor=&#39;red&#39;&quot;&gt;切换红色&lt;/button&gt;        &lt;button @click=&quot;bgcolor=&#39;green&#39;&quot;&gt;切换绿色&lt;/button&gt;        &lt;br&gt;&lt;br&gt;        &lt;div id=&quot;box&quot; v-bind:class=&quot;bgcolor&quot;&gt;点击按钮改变背景颜色&lt;/div&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var vm = new Vue({            el: &quot;#app&quot;,            data:{                bgcolor: &quot;red&quot;            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><h4><span id="462-class属性的特殊用法">4.6.2 class属性的特殊用法</span></h4><p>上面虽然实现了颜色切换，但是比较麻烦。</p><p>Vue对class属性进行了特殊处理，可接受数组或对象格式</p><p>对象语法：</p><p>可以传给<code>:class</code>一个对象，用于动态切换class：</p><pre><code class="html">&lt;div :class=&quot;{red: true, green: false}&quot;&gt;&lt;/div&gt;</code></pre><ul><li>对象中，key是已经定义的class样式的名称，比如上面的<code>red</code>、<code>green</code></li><li>对象中，value是一个布尔值，如果为true，则这个样式会生效，如果为false，则不生效。</li></ul><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;    &lt;style&gt;        #box{            width: 150px;            height: 150px;            color: white;        }        .red{            background-color: red;        }        .green{            background-color: green;        }    &lt;/style&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;button @click=&quot;bool=!bool&quot;&gt;切换颜色&lt;/button&gt;        &lt;br&gt;&lt;br&gt;        &lt;div id=&quot;box&quot; v-bind:class=&quot;{red: bool, green: !bool}&quot;&gt;点击按钮改变背景颜色&lt;/div&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var vm = new Vue({            el: &quot;#app&quot;,            data:{                bgcolor: &quot;red&quot;,                bool: true            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><h3><span id="47-计算属性">4.7 计算属性</span></h3><p>在插值表达式中使用js表达式是非常方便的，而且也经常被用到。</p><p>但是如果表达式的内容很长，就会显得不够优雅，而且后期维护起来也不方便。</p><p>例如，将一个日期的毫秒值显示转为格式化的yyyy-MM-dd：</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;h2&gt;日期：{{new Date(date).getFullYear()}}-{{new Date(date).getMonth()+1}}-{{new Date(date).getDate()}}&lt;/h2&gt; &lt;!-- 日期：2020-8-28 --&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var vm = new Vue({            el: &quot;#app&quot;,            data:{                date: 1598580451457 // 毫秒值            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p>这样利用js的方法能够实现需求，但是很麻烦。</p><p>Vue中提供了计算属性，来替代复杂的表达式：</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;h2&gt;日期：{{new Date(date).getFullYear()}}-{{new Date(date).getMonth()+1}}-{{new Date(date).getDate()}}&lt;/h2&gt;        &lt;hr&gt;        &lt;h2&gt;computed,日期：{{getDay}}&lt;/h2&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var vm = new Vue({            el: &quot;#app&quot;,            data:{                date: 1598580451457 // 毫秒值            },            computed: {                getDay(){                    const date = new Date(this.date)                    return date.getFullYear() + &quot;-&quot; + (date.getMonth()+1) + &quot;-&quot; + date.getDate()                }            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p>computed计算属性可以应用在插值或者指令表达式复杂的时候，它可以将一些属性数据经过方法处理之后返回。</p><h3><span id="48-watch">4.8 watch</span></h3><h4><span id="481-监控">4.8.1 监控</span></h4><p>在vue实例中，数据属性因为在页面中修改而产生了变化，可以通过watch监控获取其改变前后的值。</p><p>watch使用场景：可以监控视图中的数据变化从而做出相应的反应，例如，下拉列表中，如果选择了对应的下拉列表选项之后，要根据最新的值去加载一些其他数据。</p><h4><span id="482-深度监控">4.8.2 深度监控</span></h4><p>如果是修改的对象数据属性，可以开启深度监控获取修改后最新的对象数据。</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;input type=&quot;text&quot; v-model=&quot;message&quot;&gt;        &lt;br&gt;&lt;hr&gt;        &lt;input type=&quot;text&quot; v-model=&quot;person.name&quot;&gt;        &lt;input type=&quot;text&quot; v-model=&quot;person.age&quot;&gt;        &lt;button @click=&quot;person.age++&quot;&gt;年龄+1&lt;/button&gt;        &lt;h2&gt;姓名：{{person.name}}，年龄：{{person.age}}&lt;/h2&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        var vm = new Vue({            el: &quot;#app&quot;,            data:{                message: &quot;你好啊！&quot;,                person:{                    name: &quot;张三&quot;,                    age: 21                }            },            watch: {                message(newValue, oldValue){                    console.log(&quot;新值：&quot;+ newValue +&quot;，旧值：&quot; + oldValue)                },                person: {                    /* 开启深度监控，监控对象中的属性值变化 */                    deep: true,                    // 可以获取到最新的对象属性数据                    handler(obj){                        console.log(&quot;姓名：&quot; + obj.name + &quot;,年龄：&quot; + obj.age)                    }                }            },        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><h2><span id="五-组件化">五、组件化</span></h2><p>在大型应用开发的时候，页面可以划分成很多部分。</p><p>但是如果每个页面都独自开发，无疑会增加开发的成本，因此会把页面的不同部分拆分成独立的组件，然后再不同的页面共享这些组件，避免重复开发。</p><h3><span id="51-定义全局组件">5.1 定义全局组件</span></h3><p>通过Vue的component方法来定义一个全局组件。</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;!-- 引入组件(使用) --&gt;        &lt;counter /&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        // 定义组件        const counter = {            // el 组件不需要el绑定一个具体的元素            template: &quot;&lt;button @click=&#39;num++&#39;&gt;你点击了{{num}}次&lt;/button&gt;&quot;,            data() {                return {                    num: 0                }            } // data只能是一个函数，并且有返回            /* data: {                num: 0            } */        }        // 全局注册组件：在所有的vue实例中都可以使用组件        Vue.component(&quot;counter&quot;, counter) // 参数1：组件内名称，参数2：具体的组件        var vm = new Vue({            el: &quot;#app&quot;        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><ul><li><p>组件其实也是一个Vue实例，因此它在定义时也会接收data、methods、生命周期函数等</p></li><li><p>不同的是，组件不会与页面的元素绑定，否则就无法复用了，因此没有el属性</p></li><li><p>但是组件渲染需要hmtl模板，所以添加了template属性，值就是HTML模板</p></li><li><p>全局组件定义完毕，任何Vue实例都可以直接在HTML中通过组件名称来使用该组件</p></li><li><p>data的定义方式比较特殊，必须是一个函数</p></li></ul><h3><span id="52-组件的复用">5.2 组件的复用</span></h3><p>定义好的组件，可以任意复用多次：</p><pre><code class="html">&lt;div id=&quot;app&quot;&gt;    &lt;!-- 引入组件(使用) --&gt;    &lt;counter&gt;&lt;/counter&gt;    &lt;counter&gt;&lt;/counter&gt;    &lt;counter&gt;&lt;/counter&gt;&lt;/div&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustrtion/20200828121653.png" alt="image-20200828121651862"></p><p>每个组件互不干扰，有自己的num值，这是因为</p><p><strong>组件的data属性必须是函数</strong></p><p>当定义这个组件的时候，它的data并不是像这样直接提供一个对象：</p><pre><code class="javascript">data:{    num: 0}</code></pre><p>而必须是一个函数，因此每个实例可以维护一分被返回对象的独立的拷贝</p><pre><code class="javascript">data: function() {  return {    num: 0  }}</code></pre><p>如果Vue没有这条规则，点击一个按钮就会影响到其他所有实例</p><h3><span id="53-局部注册">5.3 局部注册</span></h3><p>一旦全局注册，就意味着即便以后不再使用这个组件，它依然会随着Vue的加载而加载。因此，对于一些使用并不频繁的组件，会采用局部注册。</p><p>先在外部定义一个对象，结构与创建组件时传递的第二个参数一致，然后再Vue中使用它：</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;!-- 引入组件(使用) --&gt;        &lt;counter&gt;&lt;/counter&gt;        &lt;counter&gt;&lt;/counter&gt;        &lt;counter&gt;&lt;/counter&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        // 定义组件        const counter = {            // el 组件不需要el绑定一个具体的元素            template: &quot;&lt;button @click=&#39;num++&#39;&gt;你点击了{{num}}次&lt;/button&gt;&quot;,            data() {                return {                    num: 0                }            } // data只能是一个函数，并且有返回        }        // 全局注册组件：在所有的vue实例中都可以使用组件        /* Vue.component(&quot;counter&quot;, counter) */ // 参数1：组件内名称，参数2：具体的组件        var vm = new Vue({            el: &quot;#app&quot;,            // 局部注册组件            components:{                counter: counter // 组件名: 具体的某个组件            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><ul><li>components就是当前vue对象子组件集合<ul><li>其key就是子组件名称</li><li>其值就是组件对象的属性</li></ul></li><li>效果与全局注册时一样的，但是这个局部注册的counter组件只能在当前的Vue实例中使用</li></ul><p>组件使用场景：在项目需要重用某个模块(头部、尾部、内容……)的时候，可以将模块抽取成组件，其他页面中注册组件并引用。</p><p>全局注册：在任何Vue实例中都可以引用，如：网站的头部导航菜单</p><p>局部注册：可以在有需要的页面引入组件，如：商城网站首页页面中各种活动模块</p><h3><span id="54-组件通信">5.4 组件通信</span></h3><p>通常一个单页面应用会以一颗嵌套的组件树的形式来组织：</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustrtion/20200828155751.png" alt="img"></p><ul><li>页面首先分为了顶部导航、左侧内容区、右侧边栏三个部分</li><li>左侧内容区又分为上下两个组件</li><li>右侧边栏中包含了3个子组件</li></ul><p>各个组件之间以嵌套的关系组合在一起，那么这个时候不可避免地会有组件间通信的需求。</p><h4><span id="541-父向子传递-props">5.4.1 父向子传递 props</span></h4><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;introduce :title=&quot;msg&quot;&gt;&lt;/introduce&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        const introduce = {            template:&quot;&lt;h2&gt;{{title}}&lt;/h2&gt;&quot;,            // 定义接收父组件的属性            props:[&quot;title&quot;]        }        Vue.component(&quot;introduce&quot;, introduce)        var vm = new Vue({            el: &quot;#app&quot;,            data:{                msg:&quot;父组件的msg属性数据内容&quot;            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p>introduce这个子组件中要使用title属性渲染页面，但是自己并没有title属性。通过props来接收父组件属性，名为title。父组件使用子组件，同时传递title属性。</p><h4><span id="542-传递复杂数据">5.4.2 传递复杂数据</span></h4><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        &lt;my-list :items=&quot;lessons&quot;&gt;&lt;/my-list&gt; &lt;!-- 这里就不能用驼峰命名法了 --&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        const myList = {            template:`                &lt;ul&gt;                    &lt;li v-for=&quot;item in items&quot; :key=&quot;item.id&quot;&gt;{{item.id}}——{{item.name}}&lt;/li&gt;                &lt;/ul&gt;            `, // 这里用的是模板字符串，也可以用双引号、单引号            props:{ // 通过props来接收父组件传递来的属性                items:{ // 这里定义items属性                    // 数据类型，如果是数组则是Array，如果是对象则是Object                    type:Array,                    // 默认值(如果父组件没有传值，那么就是一个空数组)                    default:[]                }            }        }        var vm = new Vue({            el: &quot;#app&quot;,            data:{                msg:&quot;父组件的msg属性数据内容&quot;,                lessons:[                    {&quot;id&quot;:1, &quot;name&quot;:&quot;语文&quot;},                    {&quot;id&quot;:2, &quot;name&quot;:&quot;数学&quot;},                    {&quot;id&quot;:3, &quot;name&quot;:&quot;英语&quot;},                    {&quot;id&quot;:4, &quot;name&quot;:&quot;物理&quot;},                    {&quot;id&quot;:5, &quot;name&quot;:&quot;化学&quot;},                    {&quot;id&quot;:6, &quot;name&quot;:&quot;生物&quot;}                ]            },            components:{                myList // ES6语法            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><ul><li><p>这个子组件可以对items进行迭代，并输出到页面</p></li><li><p>但是组件中并没有定义items属性</p></li><li><p>可以通过props来定义需要从父组件中接收的属性</p><ul><li><p>items：要接收的属性名称</p><ul><li><p>type：限定父组件传递来的必须是数组，否则报错[type的值可以是Array或者Object，传递对象的时候使用]</p></li><li><p>default：默认值，如果是对象则需要写成方法的方式返回默认值，如：</p><pre><code>default(){    return {&quot;xxx&quot;:&quot;默认值&quot;}}</code></pre></li></ul></li></ul></li></ul><h4><span id="543-子向父的通信">5.4.3 子向父的通信</span></h4><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;title&gt;Vue.js测试&lt;/title&gt;    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;    &lt;div id=&quot;app&quot;&gt;        num = {{num}}        &lt;counter @plus=&quot;numPlus&quot; @reduce=&quot;numReduce()&quot; :snum=&quot;num&quot;&gt;&lt;/counter&gt;    &lt;/div&gt;    &lt;script type=&quot;text/javascript&quot;&gt;        const counter = {            template:`                &lt;div&gt;                     &lt;button @click=&#39;incrNum&#39;&gt;增加&lt;/button&gt;                    &lt;button @click=&#39;decrNum&#39;&gt;减少&lt;/button&gt;                &lt;/div&gt;            `,/* 只能是一个容器包裹多个元素 */            props:[&quot;snum&quot;],            methods: {                incrNum(){                    // 调用到父组件中的方法                    return this.$emit(&quot;plus&quot;)                },                decrNum(){                    // 调用到父组件中的方法                    return this.$emit(&quot;reduce&quot;)                }            },        }        Vue.component(&quot;counter&quot;, counter)        var vm = new Vue({            el: &quot;#app&quot;,            data:{                num: 0            },            methods: {                numPlus(){                    this.num++                },                numReduce(){                    this.num--                }            }        })    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p>实现了在子组件中点击对应按钮，父组件中属性数据的改变</p><p>子组件绑定自定义事件，在子组件中通过<code>$emit</code>触发这个事件，去调用执行外界父组件传递过来的函数操作，函数调用时可以传递参数，从而间接地传递数据给父组件。(父组件给子组件传递一个函数，然后子组件调用此函数，调用时可以传参，函数执行时，实际执行的是父组件中的逻辑，从而可以拿到子组件传参过来的数据)</p><p><code>$emit</code>可以传值，<code>this.$emit(&quot;自定义事件名&quot;,要传的数据)</code>，接着会在父组件中以方法的参数传过去</p><h2><span id="六-vuejs-ajax">六、Vuejs ajax</span></h2><p>Vuejs并没有直接处理ajax的组件，但可以使用axios或vue-resource组件实现对异步请求的操作。</p><h3><span id="61-vue-resource">6.1 vue-resource</span></h3><p>vue-resource是Vue.js的插件，提供了使用XMLHttpRequest或JSONP进行web请求和处理响应的服务。当Vue更新到2.0之后，作者就宣告不再对vue-resource更新，而是推荐使用axios。</p><p>GitHub地址：<a href="https://github.com/pagekit/vue-resource" target="_blank" rel="noopener">https://github.com/pagekit/vue-resource</a></p><h3><span id="62-axios简介">6.2 axios简介</span></h3><p>axios是一个基于promise的HTTP库，可以用在浏览器和Node.js中。</p><p>GitHub地址：<a href="https://github.com/axios/axios" target="_blank" rel="noopener">https://github.com/axios/axios</a></p><pre><code class="sh"># npm 安装npm install axios</code></pre><p>也可以直接使用cdn服务：</p><pre><code class="html">&lt;script src=&quot;https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js&quot;&gt;&lt;/script&gt;</code></pre><h3><span id="63-axios应用">6.3 axios应用</span></h3><h4><span id="631-方法说明">6.3.1 方法说明</span></h4><p>axios可以使用的方法有：</p><ul><li><code>axios(config)</code></li><li><code>axios.get(url[,config])</code></li><li><code>axios.delete(url[,config])</code></li><li><code>axios.head(url[,config])</code></li><li><code>axios.post(url[,data[,config]])</code></li><li><code>axios.put(url[,data[,config]])</code></li><li><code>axios.patch(url[,data[,config]])</code></li></ul><p>1、config请求配置</p><p>这些是创建请求时可以用的配置选项。只有<code>url</code>是必须的，如果没有指定<code>method</code>，请求将默认使用<code>get</code>方法。</p><pre><code class="javascript">{  // url是用于请求的服务器URL  url: &#39;/user&#39;,  // method是创建请求时使用的方法  method: &#39;get&#39;, // 默认是get方式  // baseURL将自动加在url的前面，除非url是一个绝对URL。  // 它可以通过设置一个baseURL，便于为axios实例的方法传递相对URL。  baseURL: &#39;https://some-domain.com/api/&#39;,  // transformRequest允许在将请求数据发送到服务器之前对数据进行修改  // 只能用在&#39;PUT&#39;、&#39;POST&#39;、&#39;PATCH&#39; and &#39;DELETE&#39;这些请求方法  // 数组中的最后一个函数必须返回字符串或Buffer，ArrayBuffer，FormData或Stream的实例  // You may modify the headers object.  transformRequest: [function (data, headers) {    // 对data进行任意转换处理    return data;  }],  // transformResponse在传递给then/catch前，运行修改响应数据  transformResponse: [function (data) {    // 对data进行任意转换处理    return data;  }],  // headers是即将被发送的自定义请求头  headers: {    &#39;X-Requested-With&#39;: &#39;XMLHttpRequest&#39;,    &#39;Content-Type&#39;: &#39;application/json&#39;  },  // params是即将与请求一起发送的URL参数  // 必须是一个无格式对象(plain object)或URLSearchParams对象  params: {    ID: 12345  },  // paramsSerializer是用于序列化params的可选功能(e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)  paramsSerializer: function (params) {    return Qs.stringify(params, {arrayFormat: &#39;brackets&#39;})  },  // data是作为请求主体被发送的数据  // 只适用于&#39;PUT&#39;、&#39;POST&#39;、&#39;DELETE&#39;和&#39;PATCH&#39;请求方法  // 在没有设置transformRequest时, 必须是一下类型之一:  // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams  // - 浏览器专属: FormData, File, Blob  // - Node专属: Stream, Buffer  data: {    firstName: &#39;Fred&#39;  },  // syntax alternative to send data into the body  // method post  // only the value is sent, not the key  data: &#39;Country=Brasil&amp;City=Belo Horizonte&#39;,  // timeout指定请求超时的毫秒数(0表示无超时时间)  // 如果请求花费超过timeout的时间, 请求将被中断  timeout: 1000, // 默认是`0` (no timeout)  // withCredentials表示跨域请求时是否需要凭证  withCredentials: false, // 默认是false  // adapter` allows custom handling of requests which makes testing easier.  // Return a promise and supply a valid response (see lib/adapters/README.md).  adapter: function (config) {    /* ... */  },  // `auth` indicates that HTTP Basic auth should be used, and supplies credentials.  // This will set an `Authorization` header, overwriting any existing  // `Authorization` custom headers you have set using `headers`.  // Please note that only HTTP Basic auth is configurable through this parameter.  // For Bearer tokens and such, use `Authorization` custom headers instead.  auth: {    username: &#39;janedoe&#39;,    password: &#39;s00pers3cret&#39;  },  // responseType表示服务器响应的数据类型，可以是&#39;arraybuffer&#39;, &#39;document&#39;, &#39;json&#39;, &#39;text&#39;, &#39;stream&#39;  // 浏览器专属: &#39;blob&#39;  responseType: &#39;json&#39;, // 默认是json  // responseEncoding` indicates encoding to use for decoding responses (Node.js only)  // Note: Ignored for `responseType` of &#39;stream&#39; or client-side requests  responseEncoding: &#39;utf8&#39;, // default  // `xsrfCookieName` is the name of the cookie to use as a value for xsrf token  xsrfCookieName: &#39;XSRF-TOKEN&#39;, // default  // `xsrfHeaderName` is the name of the http header that carries the xsrf token value  xsrfHeaderName: &#39;X-XSRF-TOKEN&#39;, // default  // `onUploadProgress` allows handling of progress events for uploads  // browser only  onUploadProgress: function (progressEvent) {    // Do whatever you want with the native progress event  },  // `onDownloadProgress` allows handling of progress events for downloads  // browser only  onDownloadProgress: function (progressEvent) {    // Do whatever you want with the native progress event  },  // maxContentLength定义运行的响应内容的最大尺寸  maxContentLength: 2000,  // `maxBodyLength` (Node only option) defines the max size of the http request content in bytes allowed  maxBodyLength: 2000,  // validateStatus` defines whether to resolve or reject the promise for a given HTTP response status code. 如果validateStatus返回true(或者设置为null或undefined), promise将被resolved; 否则promise将被rejected.  validateStatus: function (status) {    return status &gt;= 200 &amp;&amp; status &lt; 300; // 默认是200~300  },  // maxRedirects定义在node.js中执行重定向的最大数目  // 如果设置为0，将不执行任何重定向。  maxRedirects: 5, // 默认是5  // `socketPath` defines a UNIX Socket to be used in node.js.  // e.g. &#39;/var/run/docker.sock&#39; to send requests to the docker daemon.  // Only either `socketPath` or `proxy` can be specified.  // If both are specified, `socketPath` is used.  socketPath: null, // default  // `httpAgent` and `httpsAgent` define a custom agent to be used when performing http  // and https requests, respectively, in node.js. This allows options to be added like  // `keepAlive` that are not enabled by default.  httpAgent: new http.Agent({ keepAlive: true }),  httpsAgent: new https.Agent({ keepAlive: true }),  // `proxy` defines the hostname and port of the proxy server.  // You can also define your proxy using the conventional `http_proxy` and  // `https_proxy` environment variables. If you are using environment variables  // for your proxy configuration, you can also define a `no_proxy` environment  // variable as a comma-separated list of domains that should not be proxied.  // Use `false` to disable proxies, ignoring environment variables.  // `auth` indicates that HTTP Basic auth should be used to connect to the proxy, and  // supplies credentials.  // This will set an `Proxy-Authorization` header, overwriting any existing  // `Proxy-Authorization` custom headers you have set using `headers`.  proxy: {    host: &#39;127.0.0.1&#39;,    port: 9000,    auth: {      username: &#39;mikeymike&#39;,      password: &#39;rapunz3l&#39;    }  },  // `cancelToken` specifies a cancel token that can be used to cancel the request  // (see Cancellation section below for details)  cancelToken: new CancelToken(function (cancel) {  }),  // `decompress` indicates whether or not the response body should be decompressed   // automatically. If set to `true` will also remove the &#39;content-encoding&#39; header   // from the responses objects of all decompressed responses  // - Node only (XHR cannot turn off decompression)  decompress: true // default}</code></pre><p>2、响应结构</p><pre><code class="javascript">{  // data是由服务器提供的响应数据  data: {},  // status是来自服务器响应的HTTP状态码  status: 200,  // statusText服务器响应的HTTP状态信息  statusText: &#39;OK&#39;,  // headers是服务器响应的头，服务器使用所有标头名称响应的HTTP标头均使用小写字母，并且可以使用方括号表示法进行访问。  // 例如: `response.headers[&#39;content-type&#39;]`  headers: {},  // config是为请求提供给axios的配置  config: {},  // `request` is the request that generated this response  // It is the last ClientRequest instance in node.js (in redirects)  // and an XMLHttpRequest instance in the browser  request: {}}</code></pre><p>使用<code>then</code>时，将会收到如下响应：</p><pre><code class="javascript">axios.get(&#39;/user/12345&#39;)  .then(function (response) {    console.log(response.data);    console.log(response.status);    console.log(response.statusText);    console.log(response.headers);    console.log(response.config);  });</code></pre><p>当使用<code>catch</code>或将<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then" target="_blank" rel="noopener">拒绝回调</a>作为<code>then</code>的第二个参数传递时，响应将通过<code>error</code>对象提供，如“<a href="https://github.com/axios/axios#handling-errors" target="_blank" rel="noopener">处理错误</a>”部分所述。</p><h4><span id="632-axios方法示例">6.3.2 axios方法示例</span></h4><h4><span id="633-get方法示例">6.3.3 get方法示例</span></h4><h4><span id="634-post方法示例">6.3.4 post方法示例</span></h4><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="笔记" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="前端" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="前端" scheme="https://wallleap.cn/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="web" scheme="https://wallleap.cn/tags/web/"/>
    
      <category term="JavaScript" scheme="https://wallleap.cn/tags/JavaScript/"/>
    
      <category term="Vue" scheme="https://wallleap.cn/tags/Vue/"/>
    
  </entry>
  
  <entry>
    <title>Ajax学习笔记</title>
    <link href="https://wallleap.cn/2020/08/20/AJAX/"/>
    <id>https://wallleap.cn/2020/08/20/AJAX/</id>
    <published>2020-08-20T04:33:14.000Z</published>
    <updated>2025-01-24T09:22:15.347Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2><span id="一-概述">一、概述</span></h2><blockquote><p>Web程序的最初的目的就是将信息(数据)放到公共的服务器，让所有的网络用户都可以通过浏览器访问</p></blockquote><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815183452.png" alt="image-20200815183450778"></p><p>在此之前，我们可以通过以下几种方式让浏览器发出服务端的请求，获得服务端的数据：</p><ul><li><p>地址栏输入地址，回车，刷新</p></li><li><p>特定元素的href或src属性</p></li><li><p>表单提交</p></li></ul><p>这些方案都是我们无法通过或者很难通过代码的方式进行编程(对服务器发出请求并且接收服务端返回的响应)，如果我们可以通过JavaScript直接发送网络请求，那么web的可能就会更多，随之能够实现的功能也会更多，至少不再是“单机游戏”。</p><p>1、AJAX(Asynchronous JavaScript and XML,异步的JS和XML)，最早出现在2005年的Google Suggest，是在浏览器端进行网络编程(发送请求，接收响应)的技术方案，它使我们可以<strong>通过JavaScript直接获取服务端最新的内容而不必重新加载页面</strong>，让web更能接近桌面应用的用户体验。</p><p>说白了，AJAX就是<strong>浏览器提供的一套API</strong>，可以通过JavaScript调用，从而实现代码控制请求与响应，实现网络编程。(AJAX不是新的编程语言，而是一种将现有标准组合在一起使用的新的方式)</p><blockquote><p>能力不够API凑。</p></blockquote><blockquote><p>对xxx进行编程指的就是用代码的方式操作它</p></blockquote><p>2、XML</p><p>可扩展标记语言，被设计用来传输和存储数据，和HTML有点像，但是HTML中都是预定义标签，而XML中没有预定义标签，全都是自定义标签，用来表示一些数据。</p><p>现在已经被json取代了。</p><p>3、AJAX的特点</p><p>(1) 优点</p><p>可以无需刷新页面而与服务器端进行通信。</p><p>允许你根据用户事件来更新部分页面内容。</p><p>(2) 缺点</p><p>没有浏览历史，不能回退</p><p>存在跨域问题(同源)</p><p>SEO不友好</p><p>补充：搭建环境</p><p><strong>使用Express搭建后台</strong></p><p><a href="https://www.expressjs.com.cn/" target="_blank" rel="noopener">Express中文网</a></p><p>1、安装</p><p>需要有node，没有的可以先<a href="https://nodejs.org/en/" target="_blank" rel="noopener">下载</a></p><p>进入一个文件夹，输入命令创建node应用</p><pre><code class="bash">npm init --yes</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200810090417637.png" alt="node初始化命令"></p><p>接着输入命令，安装express</p><pre><code class="bash">npm install express</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200810090617412.png" alt="express安装"></p><p>2、基本使用</p><p>(1) 编写代码</p><pre><code class="javascript">// 1、引入expressconst express = require(&#39;express&#39;)const { response } = require(&#39;express&#39;)// 2、创建应用对象const app = express()// 3、创建路由规则 // reques是对请求报文的封装；response是对响应报文的封装app.get(&#39;/&#39;, (request, response) =&gt; { // 前端访问http://localhost:8000/  // 设置响应  response.send(&#39;Hello Express&#39;)})// 4、监听端口启动服务app.listen(8000, () =&gt; {  console.log(&#39;服务已经启动，8000端口监听中，请访问http://localhost:8000/&#39;)})</code></pre><p>(2) 运行代码</p><p>在该目录下输入命令</p><pre><code class="bash">node 文件名.js</code></pre><p>eg:</p><pre><code class="bash">node expressTest.js</code></pre><p>接着访问地址<a href="http://localhost:8000/即可" target="_blank" rel="noopener">http://localhost:8000/即可</a></p><p>3、准备服务端代码</p><p><code>server.js</code></p><pre><code class="javascript">const express = require(&#39;express&#39;)const { response } = require(&#39;express&#39;)const app = express()// 这里设置为get则请求方式为GET、&#39;/server&#39;则请求url需要加上这个app.get(&#39;/server&#39;, (request, response) =&gt; {  // 设置响应头  response.setHeader(&#39;Access-Control-Allow-origin&#39;,&#39;*&#39;) // 设置允许跨域  // 设置响应体  response.send(&#39;Hello AJAX&#39;)})// 在这里接着加上接口app.listen(8000, () =&gt; {  console.log(&#39;服务已经启动，8000端口监听中，请访问http://localhost:8000/&#39;)})</code></pre><p>关闭上面那个，将这个启动：</p><pre><code class="bash">node server.js</code></pre><p>reload包——nodemon</p><p>自动检测js代码变化，restart服务</p><p>1、安装</p><pre><code class="sh">npm install -g nodemon</code></pre><p>2、利用nodemon执行文件</p><pre><code class="sh">nodemon 文件名.js</code></pre><p>例如：</p><pre><code class="sh">nodemon server.js</code></pre><h2><span id="二-快速上手">二、快速上手</span></h2><h3><span id="1-ajax基础">1、AJAX基础</span></h3><p>(一个构造函数、两个方法、一个事件)</p><p>发送请求</p><pre><code class="html">&lt;style&gt;#result{    width:200px;    height:100px;    border:1px solid skyblue;}&lt;/style&gt;&lt;button id=&quot;btn&quot;&gt;点击发送请求&lt;/button&gt;&lt;div id=&quot;result&quot;&gt;&lt;/div&gt;&lt;script&gt;btn.onclick = function(){  // 涉及到AJAX操作的页面不能使用文件协议访问（文件的方式访问）  // AJAX是一套API，核心提供的类型：XMLHttpRequest XML--&gt;JSON(现在使用的是JSON格式的了)  // 1、安装浏览器（用户代理）——创建对象  var xhr = new XMLHttpRequest() // xhr就类似于浏览器的作用(发送请求接收响应)  // 2、打开浏览器 输入网址——初始化 设置请求方式和url  xhr.open(&#39;GET&#39;, &#39;http://127.0.0.1:8000/server&#39;)  // 这一步只是在搭桥铺路  // 3、敲回车键 开始请求——发送  xhr.send()  // send才是开始请求  /* 上面三个是ajax核心代码 */}&lt;/script&gt;</code></pre><p>接收响应</p><pre><code class="html">&lt;script&gt;  var xhr = new XMLHttpRequest()  xhr.open(&#39;GET&#39;, &#39;http://127.0.0.1:8000/server&#39;) // 方式，url  xhr.send()  /* 因为响应需要时间，所以无法通过返回值的方式返回响应 */  // var response = xhr.send()  // console.log(response) // undefine  // 4、等待响应——事件绑定 处理服务端返回的结果  /* 如果需要捕获状态的变化，需要注意代码的执行顺序的问题(不要出现来不及的情况)  * 因为客户端永远不知道服务端何时才能返回我们需要的响应，所以AJAX API采用事件的机制(通知的感觉)  */  xhr.onreadystatechange = function(){ // 建议事件使用addEventListener方式    // 这个事件并不是只在响应时触发，XHR状态改变就触发    console.log(this.readyState)    if(this.readyState!==4) return    // 所以下面就是为4的情况    // 5、看结果    console.log(this.responseText) // 获取响应内容(响应体)    result.innerHTML = xhr.response  }&lt;/script&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815183735.png" alt="ajax基础操作"></p><h3><span id="2-理解readystate">2、理解readyState</span></h3><p><code>onreadystatechange</code>是XHR状态改变时触发的</p><pre><code class="html">&lt;script&gt;  var xhr = new XMLHttpRequest()  console.log(xhr.readyState) // 0  xhr.open(&#39;GET&#39;, &#39;./time.php&#39;)  console.log(xhr.readyState) // 1  xhr.send()  // console.log(xhr.readyState) // 1 取上面那个  xhr.addEventListener(&#39;readystatechange&#39;, function(){    // if(this.readyState !== 4) return    // console.log(this.responseText)    console.log(this.readyState) // 2 3 4  })&lt;/script&gt;</code></pre><p>readyState：</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815183844.png" alt="readyState代码"></p><p>new XMLHttpRequest –&gt; 0 初始化 <strong>请求代理对象</strong></p><p>1–-&gt; open方法已经调用，<strong>建立</strong>一个与服务端特定端口的<strong>连接</strong></p><p>2 –-&gt; 已经<strong>接收</strong>到了响应报文的<strong>响应头</strong> <code>console.log(this.getAllResponseHeaders())</code> 可以拿到响应头，拿不到响应体</p><p>拆分：<code>console.log(this.getAllResponseHeaders().splite(‘\n’).splite(‘:’))</code></p><p>获取指定键：<code>console.log(this.getAllResponseHeaders(‘data’))</code></p><p>3 –-&gt; <strong>正在下载响应报文的响应体</strong>，可能响应体为空或不完整</p><p>4 –-&gt; 一切OK，<strong>整个响应报文已经下载下来</strong>了 <code>console.log(this.responseText)</code></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815183915.png" alt="readyState"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815183935.png" alt="image-20200815183934889"></p><p>所以应该<strong>在readyState的值为4</strong>时，才去处理后续的逻辑</p><p>可以用<code>xhr.onload</code>替代</p><pre><code class="html">&lt;script&gt;  var xhr = new XMLHttpRequest()  xhr.open(&#39;GET&#39;, &#39;time.php&#39;)  xhr.send(null) // send可以传请求体，传null代表没有请求体  xhr.onload = function (){ // 加载完成 H5中提供的XMLHttpRequest version 2.0定义的    // 相当于readyState为4之后的    console.log(this.responseText)  }&lt;/script&gt;</code></pre><p>ps: console.log(this) 显示readyState是2、3、4可展开来全都是4，这个是console.log的机制问题，展开的时候只会显示此时的状态</p><p>例如：</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815184045.png" alt="log机制测试"></p><p>在浏览器上看，不展开没问题显示123，展开的一瞬间都是456</p><h3><span id="3-ajax遵循http协议">3、AJAX遵循HTTP协议</span></h3><p>HTTP协议(Hypertext Transport Protocol，超文本传输协议)详细规定了浏览器和万维网服务器之间互相通信的规则。</p><p>本质上XMLHttpRequest就是JavaScript在web平台中发送HTTP请求的手段，所以我们发送出去的请求仍然是HTTP请求，同样符合HTTP约定的格式</p><p>请求报文：</p><ul><li>请求行 <code>POST /s?ie=utf-8 HTTP/1.1</code></li></ul><p>​    <code>GET /s?ie=utf-8 HTTP/1.1</code></p><ul><li>请求头 <code>Host: atguigu.com</code></li></ul><p>​    <code>Cookie: name=guigu</code></p><p>​    <code>Content-Type: application/x-www-form-urlencoded</code></p><p>​    <code>User-Agent: chrome 83</code></p><ul><li><p>空行(必须得有)</p></li><li><p>请求体 (GET请求这里为空，POST可不为空) <code>username=admin&amp;password=admin</code></p></li></ul><p>响应报文：</p><ul><li>行  <code>HTTP/1.1 200 OK</code></li></ul><p>​       404</p><p>​       403</p><p>​       401</p><p>​       500</p><ul><li><p>头  <code>Content-Type: text/html;charset=utf-8</code></p><p> <code>Content-length: 2048</code></p><p> <code>Content-encoding: gzip</code></p></li><li><p>空行 </p></li><li><p>体  </p></li></ul><pre><code>&lt;html&gt;  &lt;head&gt;  &lt;/head&gt;  &lt;body&gt;    &lt;h1&gt;wallleap&lt;/h1&gt;  &lt;/body&gt;&lt;/html&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815184354.png" alt="HTTP响应报文信息"></p><pre><code class="html">&lt;script&gt;  // 1、创建对象  var xhr = new XMLHttpRequest()  // 2、初始化 设置请求方法和urlxhr.open(&#39;POST&#39;, &#39;add.php&#39;) // 设置请求行  xhr.setRequestHeader(&#39;Foo&#39;, &#39;Bar&#39;) // 设置一个请求头  // 一旦请求体是urlencoded格式的内容，一定要设置请求头中的Content-Type为下面这个  xhr.setRequestHeader(&#39;Content-Type&#39;, &#39;application/x-www-form-urlencoded&#39;) // 设置第二请求头  xhr.send(&#39;key1=value1&amp;key2=value2&#39;) // 以urlencoded格式设置请求体  // xhr.send(&#39;{&quot;foo&quot;: &quot;123&quot;}&#39;) // 以json格式设置请求体，上面设置application/json  xhr.onload = function (){    // 响应行    console.log(this.status) // 获取响应状态码    console.log(this.statusText) // 获取响应状态描述    // 获取响应头信息    console.log(this.getResponseHeader(&#39;Content-Type&#39;)) // 指定响应头    console.log(this.getAllResponseHeaders()) // 全部响应头    // 获取响应体    console.log(this.responseText) // 文本形式    console.log(this.responseXML) // XML形式，了解即可  }&lt;/script&gt;</code></pre><p>补充：</p><pre><code class="javascript">xhr.addEventListener(&#39;readystatechange&#39;, function(){    if(this.readyState === 4 &amp;&amp; this.status === 200){      console.log(this)    }})</code></pre><p>可能有的人会同时判断状态码200，事实上没有必要，状态码404也需要处理,可以到里面嵌套，例如：</p><pre><code class="javascript">xhr.onreadystatechange=function(){  if(this.readyState === 4){    if(this.status &gt;= 200  &amp;&amp; xhr.status &lt; 300){      // 处理结果    }else{      ...    }  }})</code></pre><p>Chrome打开开发者模式</p><p>点击Network能够看到传输的文件</p><p>点击XHR查看</p><p>Request Headers——请求头</p><p>点击view source可以看到请求行</p><p>Response Header——响应头</p><p>点击view source可以看到响应行</p><p>Response——响应体</p><p>Preview——预览，对响应体解析之后的页面</p><h2><span id="三-具体用法">三、具体用法</span></h2><h3><span id="1-数据接口的概念">1、数据接口的概念</span></h3><p>服务器端返回的响应就是一个JSON内容（返回的就是数据）</p><p>对于返回数据的地址一般我们称之为接口（形式上是web形式）</p><p><a href="http://api.douban.com/v2/movie/top250" target="_blank" rel="noopener">http://api.douban.com/v2/movie/top250</a></p><p>提供一定的能力，有输入有输出就可以称为接口</p><h3><span id="2-ajax发送get请求并传递参数">2、AJAX发送GET请求并传递参数</span></h3><pre><code class="html">&lt;script&gt;  var xhr = new XMLHttpRequest()  // GET请求传递参数通常使用URL中的问号传递数据  xhr.open(&#39;GET&#39;, &#39;http://127.0.0.1:8000/server?a=100&amp;b=200&amp;c=300&#39;)  // 一般在GET请求时无需设置响应体，可以传null或者干脆不传  xhr.send(null)  xhr.onreadystatechange = function (){    if(this.readyState !== 4) return    console.log(this.responseText)  }&lt;/script&gt;&lt;!-- 一般情况下URL传递的都是参数性质的数据，而POST一般都是业务数据 --&gt;</code></pre><p>例子：将得到的四个用户名称{}放到ul&gt;li中，点击li能够获取到该用户的年龄</p><pre><code class="html">&lt;ul id=&quot;list&quot;&gt;&lt;/ul&gt;&lt;script&gt;  var listElement = document.getElementById(&#39;list&#39;)  /* 发送请求获取到列表数据，呈现在页面上 */  var xhr = new XMLHttpRequest()  xhr.open(&#39;POST&#39;, &#39;user.php?id=2&#39;)  xhr.send(null)  xhr.onreadystatechange = function (){    if(this.readyState !== 4) return    var data = JSON.parse(this.responseText)    // console.log(data)    for(var i = 0; i &lt; data.length; i++){      // console.log(data[i])      var liElement = document.createElement(&#39;li&#39;)      liElement.innerHTML = data[i].name      liElement.id = data[i].id      listElement.appendChild(liElement)      /* 给每一个li注册点击事件 */      // 由于li是动态创建的，因此需要移到创建li的时候      listElement.addEventListener(&#39;click&#39;, function (){          // TODO: 通过AJAX操作获取服务端对应数据的信息          // 获取当前被点击元素对应数据的id          // console.log(this.id)          var xhr1 = new XMLHttpRequest()          xhr1.open(&#39;GET&#39;, &#39;users.php?id=&#39; + this.id)          xhr1.send()          xhr1.onreadystatechange = function (){            if(this.readyState !== 4) return            var obj = JSON.parse(this.responseText)            alert(obj.age)          }      })    }  }&lt;/script&gt;</code></pre><h3><span id="3-post请求">3、POST请求</span></h3><p>POST请求过程中，都是采用请求体承载需要提交的数据</p><pre><code class="javascript">// 1.创建对象const xhr = new XMLHttpRequest()// 2.初始化 设置类型与URL(open的第一个参数的作用就是设置请求的method)xhr.open(&#39;POST&#39;,&#39;http://127.0.0.1:8000/server&#39;)// 设置请求头信息xhr.setRequestHeader(&#39;Content-Type&#39;,&#39;application/x-www-form-urlencoded&#39;) // 设置请求头中的Content-Type为application/x-www-form-urlencoded——标识这次请求得请求体格式为urlencoded以便于服务端接收数据// xhr.setRequestHeader(&#39;name&#39;,&#39;wallleap&#39;) // 也可以自定义// 3.发送 POST方式需要提交到服务端的数据可以通过send方法的参数传递，格式：key1=value1&amp;key2=value2xhr.send(&quot;key1=value1&amp;key2=value2&quot;)// xhr.send(&quot;key1:value1&amp;key2:value2&quot;)// xhr.send(&quot;value1&quot;)  // ---&gt;可以随意写，但是需要按格式，方便后台处理// 4.事件绑定xhr.onreadystatechange = function(){  // 判断  if(xhr.readyState===4){    if(xhr.status &gt;= 200 &amp;&amp; xhr.status &lt; 300){      // 处理服务端返回的结果      result.innerHTML = xhr.response    }  }}</code></pre><p>由于<code>server.js</code>中只设置了get的允许跨域，因此需要在文件中加入允许post跨域的代码</p><pre><code class="javascript">// app.get(&#39;/server&#39;, (request, response) =&gt; {app.post(&#39;/server&#39;, (request, response) =&gt; {  // 设置响应头  设置允许跨域  response.setHeader(&#39;Access-Control-Allow-Origin&#39;, &#39;*&#39;);  // 设置允许所有头信息，就比如上面设置的自定义响应头会报错，就需要加上这个  // response.setHeader(&#39;Access-Control-Allow-Headers&#39;, &#39;*&#39;);  // 设置响应体  response.send(&#39;HELLO AJAX POST&#39;);});</code></pre><p>可以改为all</p><pre><code class="javascript">//可以接收任意类型的请求(get/post/options/...)app.all(&#39;/server&#39;, (request, response) =&gt; {  //设置响应头  设置允许跨域  response.setHeader(&#39;Access-Control-Allow-Origin&#39;, &#39;*&#39;);  //响应头  response.setHeader(&#39;Access-Control-Allow-Headers&#39;, &#39;*&#39;);  //设置响应体  response.send(&#39;HELLO AJAX POST&#39;);});</code></pre><p>测试</p><pre><code class="html">&lt;style&gt;#result{  width:200px;  height:100px;  border:solid 1px #903;}&lt;/style&gt;&lt;/head&gt;&lt;body&gt;&lt;div id=&quot;result&quot;&gt;&lt;/div&gt;&lt;script&gt;  //获取元素对象  const result = document.getElementById(&quot;result&quot;);  //绑定事件  result.addEventListener(&quot;mouseover&quot;, function(){    //1. 创建对象    const xhr = new XMLHttpRequest();    //2. 初始化 设置类型与 URL    xhr.open(&#39;POST&#39;, &#39;http://127.0.0.1:8000/server&#39;);    //设置请求头    xhr.setRequestHeader(&#39;Content-Type&#39;,&#39;application/x-www-form-urlencoded&#39;);    xhr.setRequestHeader(&#39;name&#39;,&#39;wallleap&#39;);    //3. 发送    xhr.send(&#39;a=100&amp;b=200&amp;c=300&#39;);    // xhr.send(&#39;a:100&amp;b:200&amp;c:300&#39;);    // xhr.send(&#39;1233211234567&#39;);    //4. 事件绑定    xhr.onreadystatechange = function(){      //判断      if(xhr.readyState === 4){        if(xhr.status &gt;= 200 &amp;&amp; xhr.status &lt; 300){          //处理服务端返回的结果          result.innerHTML = xhr.response;        }      }    }  });&lt;/script&gt;</code></pre><p>例子：点击登录按钮不刷新页面将数据传到后台</p><pre><code class="html">&lt;style&gt;.loading{  display: none;  position: fixed;  top: 0;  left: 0;  right: 0;  bottom: 0;  background-color: #555;  text-align: center;  padding-top: 200px;  opacity: .5;}.loading::after{  content: &#39;加载中……&#39;;  font-size: 60px;  color: #fff;}&lt;/style&gt;&lt;div class=&quot;loading&quot;&gt;&lt;/div&gt;&lt;table border=&quot;1&quot;&gt;  &lt;tr&gt;    &lt;td&gt;用户名&lt;/td&gt;    &lt;td&gt;&lt;input type=&quot;text&quot; name=&quot;&quot; id=&quot;username&quot;&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;密码&lt;/td&gt;    &lt;td&gt;&lt;input type=&quot;password&quot; name=&quot;&quot; id=&quot;password&quot;&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;&lt;/td&gt;    &lt;td&gt;&lt;button id=&quot;btn&quot;&gt;登录&lt;/button&gt;&lt;/td&gt;  &lt;/tr&gt;&lt;/table&gt;&lt;script&gt;  // 找一个合适的时机，做一件合适的事情(时间、内容)  // 1、获取界面上的元素 value  var textUsername = document.getElementById(&#39;username&#39;)  var textPassword = document.getElementById(&#39;password&#39;)  var btn = document.getElementById(&#39;btn&#39;)  var loading = document.querySelector(&#39;.loading&#39;)  btn.onclick = function (){    loading.style.display = &#39;block&#39;    var username = textUsername.value    var password = textPassword.value    // 2、通过XHR发送一个POST请求    var xhr = new XMLHttpRequest    xhr.open(&#39;POST&#39;, &#39;login.php&#39;)    // 一定注意：如果请求体是urlencoded格式，必须设置这个请求头！    xhr.setRequestHeader(&#39;Content-Type&#39;, &#39;application/x-www-form-urlencoded&#39;)    // xhr.send(&#39;username=&#39; + username + &#39;&amp;password=&#39; + password)    xhr.send(`username=${username}&amp;password=${password}`)    // 3、根据服务端的反馈，作出界面提示    xhr.onreadystatechange = function (){      if(this.readyState !== 4) return      console.log(this.responseText)      loading.style.display = &#39;none&#39;    }  }&lt;/script&gt;</code></pre><h3><span id="4-同步和异步">4、同步和异步</span></h3><p>生活中：</p><p>同步：一个人在同一个时刻只能做一件事情，在执行一些耗时的操作(不需要看管)不去做别的事情，只是等待</p><p>异步：在执行一些耗时的操作(不需要看管)去做别的事，而不是等待</p><p>xhr.open()第三个参数(async)要求传入的是一个bool值，其作用就是设置此次请求是否采用异步方式执行，默认为true，如果需要同步执行可以通过传递false实现</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815184832.png" alt="image-20200815184831019"></p><p><code>console.time(‘标识’)</code> 启动一个秒表</p><p>中间写代码</p><p><code>console.timeEnd(‘标识’)</code> 结束这个秒表</p><p>这样就能知道用了多长时间(标识名称得相同)</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815184906.png" alt="image-20200815184905369"></p><p>如果采用同步方式执行，则代码会卡死在xhr.send()这一步</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815184921.png" alt="image-20200815184920339"></p><p>send方法会不会出现等待情况(区分同异步)</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815184939.png" alt="image-20200815184938765"></p><p>知道同步模式即可(已被遗弃)</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815184956.png" alt="image-20200815184955646"></p><p>同步模式注册时间时机问题</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815185018.png" alt="image-20200815185017871"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815185029.png" alt="image-20200815185028276"></p><h3><span id="5-响应数据格式">5、响应数据格式</span></h3><p>如果希望服务器返回一个复杂数据，该如何处理：</p><p> 服务器发出何种格式的数据，这个格式如何在客户端用JavaScript解析</p><p>5.1 XML</p><p>一种数据描述手段</p><p>老掉牙的东西，现在项目中基本不使用</p><p>淘汰的原因：数据冗余太多</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815185058.png" alt="image-20200815185057257"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815185108.png" alt="image-20200815185106691"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815185115.png" alt="image-20200815185114823"></p><p><strong>5.2 JSON</strong></p><p>也是一种数据描述手段，类似于JavaScript字面量方式</p><p>服务端采用JSON格式返回数据，客户端按照JSON格式解析数据</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815185141.png" alt="image-20200815185140660"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815185148.png" alt="image-20200815185147671"></p><p>来测试一下json的</p><p><code>server.js</code>中添加</p><pre><code class="javascript">//JSON 响应app.all(&#39;/json-server&#39;, (request, response) =&gt; {  //设置响应头  设置允许跨域  response.setHeader(&#39;Access-Control-Allow-Origin&#39;, &#39;*&#39;);  //响应头  response.setHeader(&#39;Access-Control-Allow-Headers&#39;, &#39;*&#39;);  //响应一个数据  const data = {    name: &#39;wallleap&#39;  };  //对对象进行字符串转换  let str = JSON.stringify(data);  //设置响应体  response.send(str);});</code></pre><p>测试</p><pre><code class="html">&lt;style&gt;  #result{    width:200px;    height:100px;    border:solid 1px #89b;  }&lt;/style&gt;&lt;div id=&quot;result&quot;&gt;&lt;/div&gt;&lt;script&gt;  //绑定键盘按下事件  window.onkeydown = function(){    //发送请求    const xhr = new XMLHttpRequest();    //设置响应体数据的类型    xhr.responseType = &#39;json&#39;; // 自动转换    //初始化    xhr.open(&#39;GET&#39;,&#39;http://127.0.0.1:8000/json-server&#39;);    //发送    xhr.send();    //事件绑定    xhr.onreadystatechange = function(){      if(xhr.readyState === 4){        if(xhr.status &gt;= 200 &amp;&amp; xhr.status &lt; 300){          // console.log(xhr.response);          // result.innerHTML = xhr.response;          // 1. 手动对数据转化          // let data = JSON.parse(xhr.response);          // console.log(data);          // result.innerHTML = data.name;          // 2. 自动转换          console.log(xhr.response);          result.innerHTML = xhr.response.name;        }      }    }  }&lt;/script&gt;</code></pre><p>注意：</p><p>不论是JSON，还是XML，只是在AJAX请求过程中用到，并不代表它们之间有必然的联系，它们只是数据协议罢了</p><p>不管服务器使用XML还是JSON本质上都是将数据返回给客户端</p><p>服务端应该设置一个合理的Content-Type</p><h3><span id="6-处理服务器端响应的数据">6、处理服务器端响应的数据</span></h3><p>动态渲染数据到表格中</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815185218.png" alt="image-20200815185217284"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815185226.png" alt="image-20200815185225225"></p><p>现在一般都不会这样操作，太繁琐了</p><p>模板引擎</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815185252.png" alt="image-20200815185251189"></p><p>常见模板引擎列表：<a href="https://github.com/tj/consolidate.js#supported-template-engines" target="_blank" rel="noopener">https://github.com/tj/consolidate.js#supported-template-engines</a></p><p>artTemplate： <a href="https://aui.github.io/art-template" target="_blank" rel="noopener">https://aui.github.io/art-template</a></p><p>模板引擎实际上就是一个API，模板引擎有很多种，使用方式大同小异，目的为了可以更容易地将数据渲染到HTML中</p><pre><code class="html">&lt;table id=&quot;comment&quot; border=&quot;1&quot;&gt;&lt;/table&gt;&lt;!-- // 1.选择一个模板引擎 https://github.com/tj/consolidate.js#supported-template-engines// 2、下载模板引擎JS库// 3、引入到页面中 --&gt;&lt;script src=&quot;https://cdn.bootcdn.net/ajax/libs/art-template/4.13.2/lib/template-web.min.js&quot;&gt;&lt;/script&gt;&lt;!--   script标签的特点：  1、innerHTML永远不会显示在界面上(display: none;)  2、如果type属性不是text/javascript，内部的内容不会作为JavaScript执行 --&gt;&lt;!-- // 4、准备一个模板 --&gt;&lt;!--   JavaScript中用变量保存(维护不方便、不能换行)  ——&gt; HTML中放到一个div中通过样式隐藏(添加误用元素、多余样式)  ——&gt; script标签type修改之后不会显示在页面中，建议text/x-开头--&gt;&lt;script id=&quot;tmpl&quot; type=&quot;text/x-art-template&quot;&gt;{{each comments}}  &lt;tr&gt;  &lt;td&gt;{{$index+1}}&lt;/td&gt;  &lt;td&gt;{{$value.author}}&lt;/td&gt;  &lt;td&gt;{{$value.content}}&lt;/td&gt;  &lt;td&gt;{{$value.created}}&lt;/td&gt;&lt;/tr&gt;{{/each}}&lt;/script&gt;&lt;script&gt;var xhr = new XMLHttpRequest()xhr.open(&#39;GET&#39;, &#39;test.php&#39;)xhr.send()xhr.onreadystatechange = function (){  if(this.readyState !== 4) return  var res = JSON.parse(this.responseText)  // // 5、准备一个数据  var context = {comments: res.data}  // 上面的关键词就是comments  console.log(context)  var html = template(&#39;tmpl&#39;, context)  console.log(html)  document.getElementById(&#39;comment&#39;).innerHTML = html  // 6、通过模板引擎的JS提供一个函数将模板和数据整合得到渲染结果HTML  // 7、将渲染结果的HTML设置到某个元素的innerHTML中}&lt;/script&gt;</code></pre><h3><span id="7-兼容方案">7、兼容方案</span></h3><p>XMLHttpRequest在老板浏览器(IE5/6)中有兼容问题，可以通过另一种方式代替</p><pre><code class="javascript">var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject(&#39;Microsoft.XMLHTTP&#39;)</code></pre><h3><span id="8-补充">8、补充</span></h3><p>(1) response、responseText</p><p>都是获取的响应</p><p>response: 获取到的结果或根据this.responseType的变化而变化(可以表述二进制数据)</p><p>responseText: 永远获取的是字符串形式的响应体</p><pre><code class="javascript">var xhr = new XMLHttpRequestxhr.open(&#39;GET&#39;, &#39;test.php&#39;)xhr.send()xhr.responseType = &#39;json&#39;  // 通过代码告诉请求代理对象，服务端响应给我们的是JSONxhr.onreadystatechange = function (){  if(this.readyState !== 4) return  console.log(this)  console.log(this.response)  console.log(this.responseText)  // 由于设置了json，因此不存在}</code></pre><p>(2) IE缓存问题：IE浏览器会将ajax返回结果缓存起来，再次发送请求时，显示的是本地缓存，而不是最新的请求到的数据</p><p>在服务端添加一个：</p><pre><code class="javascript">//针对 IE 缓存app.get(&#39;/ie&#39;, (request, response) =&gt; {  //设置响应头  设置允许跨域  response.setHeader(&#39;Access-Control-Allow-Origin&#39;, &#39;*&#39;);  //设置响应体  response.send(&#39;HELLO AJAX IE&#39;);});</code></pre><p>解决</p><pre><code class="html">&lt;style&gt;  #result{    width:200px;    height:100px;    border:solid 1px #258;  }&lt;/style&gt;&lt;button&gt;点击发送请求&lt;/button&gt;&lt;div id=&quot;result&quot;&gt;&lt;/div&gt;&lt;script&gt;  const btn = document.getElementsByTagName(&#39;button&#39;)[0];  const result = document.querySelector(&#39;#result&#39;);  btn.addEventListener(&#39;click&#39;, function(){    const xhr = new XMLHttpRequest();    xhr.open(&quot;GET&quot;,&#39;http://127.0.0.1:8000/ie?t=&#39;+Date.now()); // 解决方案：加上参数，这样浏览器认为每次请求url都不一样    xhr.send();    xhr.onreadystatechange = function(){      if(xhr.readyState === 4){        if(xhr.status &gt;= 200 &amp;&amp; xhr.status&lt; 300){          result.innerHTML = xhr.response;        }      }    }  })&lt;/script&gt;</code></pre><p>(3) 请求超时和网络异常</p><p><code>server.js</code>中添加</p><pre><code class="javascript">//延时响应app.all(&#39;/delay&#39;, (request, response) =&gt; {  //设置响应头  设置允许跨域  response.setHeader(&#39;Access-Control-Allow-Origin&#39;, &#39;*&#39;);  response.setHeader(&#39;Access-Control-Allow-Headers&#39;, &#39;*&#39;);  // 手动设置一个延时效果  setTimeout(() =&gt; {    //设置响应体    response.send(&#39;延时响应&#39;);  }, 1000) // 3000});</code></pre><p>进行处理，在2s内还没有响应则取消</p><pre><code class="html">&lt;style&gt;  #result{    width:200px;    height:100px;    border:solid 1px #90b;  }&lt;/style&gt;&lt;button&gt;点击发送请求&lt;/button&gt;&lt;div id=&quot;result&quot;&gt;&lt;/div&gt;&lt;script&gt;  const btn = document.getElementsByTagName(&#39;button&#39;)[0];  const result = document.querySelector(&#39;#result&#39;);  btn.addEventListener(&#39;click&#39;, function(){    const xhr = new XMLHttpRequest();    //超时设置 2s 设置    xhr.timeout = 2000;    //超时回调    xhr.ontimeout = function(){      alert(&quot;网络异常, 请稍后重试!!&quot;);    }    //网络异常回调    xhr.onerror = function(){      alert(&quot;你的网络似乎出了一些问题!&quot;);    }    xhr.open(&quot;GET&quot;,&#39;http://127.0.0.1:8000/delay&#39;);    xhr.send();    xhr.onreadystatechange = function(){      if(xhr.readyState === 4){        if(xhr.status &gt;= 200 &amp;&amp; xhr.status&lt; 300){          result.innerHTML = xhr.response;        }      }    }  })&lt;/script&gt;</code></pre><p>网络异常可以利用浏览器调试中Network一栏，设置为Offline</p><p>(3) 取消请求</p><p>利用abort()方法取消请求</p><pre><code class="html">&lt;button&gt;点击发送&lt;/button&gt;&lt;button&gt;点击取消&lt;/button&gt;&lt;script&gt;  //获取元素对象  const btns = document.querySelectorAll(&#39;button&#39;);  let x = null; // 由于第二个按钮也需要用到  btns[0].onclick = function(){    x = new XMLHttpRequest();    x.open(&quot;GET&quot;,&#39;http://127.0.0.1:8000/delay&#39;);    x.send();  }  // abort  btns[1].onclick = function(){    x.abort();  }&lt;/script&gt;</code></pre><p>(3) ajax重复发送请求问题：用户频繁发送请求，对服务器压力很大</p><p>请求时，可以判断，如果前面有一条这样的请求，那么将前面的请求取消掉</p><pre><code class="html">&lt;button&gt;点击发送&lt;/button&gt;&lt;script&gt;  //获取元素对象  const btns = document.querySelectorAll(&#39;button&#39;);  let x = null;  //标识变量  let isSending = false; // 是否正在发送AJAX请求  btns[0].onclick = function(){    //判断标识变量    if(isSending) x.abort();// 如果正在发送, 则取消该请求, 创建一个新的请求    x = new XMLHttpRequest();    //修改 标识变量的值    isSending = true;    x.open(&quot;GET&quot;,&#39;http://127.0.0.1:8000/delay&#39;);    x.send();    x.onreadystatechange = function(){      if(x.readyState === 4){        //修改标识变量        isSending = false;      }    }  }&lt;/script&gt;</code></pre><h2><span id="四-封装">四、封装</span></h2><h3><span id="1-ajax请求封装">1、AJAX请求封装</span></h3><p>封装的套路：</p><p>(1)  写一个相对比较完善的用例</p><pre><code class="javascript">var xhr = new XMLHttpRequestxhr.open(&#39;GET&#39;, &#39;test.php&#39;, true)xhr.send(null)xhr.addEventListener(&#39;readystatechange&#39;, function(){  if(this.readyState !== 4) return  console.log(this.responseText)})</code></pre><p>(2)  写一个空函数，没有形参，将刚刚的用例直接作为函数的函数体</p><pre><code class="javascript">function ajax(){  var xhr = new XMLHttpRequest  xhr.open(&#39;GET&#39;, &#39;test.php&#39;, true)  xhr.send(null)  xhr.addEventListener(&#39;readystatechange&#39;, function(){    if(this.readyState !== 4) return    console.log(this.responseText)  })}</code></pre><p>(3)  根据使用过程中的需求抽象参数</p><pre><code class="javascript">/*Ajax请求 version1method: 请求方式 GET/POSTurl: 请求地址 &#39;http://xxx.com/api&#39;*/function ajax(method, url){  var xhr = new XMLHttpRequest  xhr.open(method, url, true)  xhr.send(data)  xhr.addEventListener(&#39;readystatechange&#39;, function(){    if(this.readyState !== 4) return    console.log(this.responseText)  })}ajax(&#39;GET&#39;, &#39;test.php&#39;)</code></pre><p>send需要传参</p><pre><code class="javascript">/*Ajax请求 version2method: 请求方式 GET/POSTurl: 请求地址 &#39;http://xxx.com/api&#39;params: 键值对字符串*/function ajax(method, url, params){  var xhr = new XMLHttpRequest  var data = null  if(method === &#39;GET&#39;){    url += &#39;?&#39; + params  }  xhr.open(method, url, true)  if(method === &#39;POST&#39;){    xhr.setRequestHeader(&#39;Content-Type&#39;, &#39;application/x-www-form-urlencoded&#39;)    data = params || null  }  xhr.send(data)  xhr.addEventListener(&#39;readystatechange&#39;, function(){    if(this.readyState !== 4) return    console.log(this.responseText)  })}ajax(&#39;GET&#39;, &#39;test.php&#39;)ajax(&#39;POST&#39;, &#39;post.php&#39;, &#39;key1=value1&amp;key2=value2&#39;)</code></pre><p>send实参可以传对象</p><pre><code class="javascript">/*Ajax请求 version3method: 请求方式 GET/POSTurl: 请求地址 &#39;http://xxx.com/api&#39;params: 对象*/function ajax(method, url, params){  var xhr = new XMLHttpRequest  // 将Object类型的参数转换为 key1=value1&amp;key2=value2的形式  if(typeof params === &#39;object&#39;){    var tempArr = []    for (var key in params){      var value = params[key]      tempArr.push(key + &#39;=&#39; + params[key])      // tempArr =&gt; [&#39;key1=value1&#39;, &#39;key2=value2&#39;]    }    params = tempArr.join(&#39;&amp;&#39;)    // tempArr =&gt; [&#39;key1=value1&amp;key2=value2&#39;]  }  if(method === &#39;GET&#39;){    params ? url += &#39;?&#39; + params : url  }  xhr.open(method, url, true)  var data = null  if(method === &#39;POST&#39;){    xhr.setRequestHeader(&#39;Content-Type&#39;, &#39;application/x-www-form-urlencoded&#39;)    data = params || null  }  xhr.send(data)  xhr.addEventListener(&#39;readystatechange&#39;, function(){    if(this.readyState !== 4) return    console.log(this.responseText)  })}ajax(&#39;GET&#39;, &#39;test.php&#39;)ajax(&#39;GET&#39;, &#39;time.php&#39;, {id: 1})ajax(&#39;POST&#39;, &#39;post.php&#39;, {key1: &#39;value1&#39;, key2: &#39;value2&#39;, key3: &#39;value3&#39;})ajax(&#39;POST&#39;, &#39;post.php&#39;, &#39;key1=value1&amp;key2=value2&#39;)</code></pre><p>不应该在封装的函数中主观地处理响应结果</p><pre><code class="javascript">/*Ajax请求 version4method: 请求方式 GET/POST/get/posturl: 请求地址 &#39;http://xxx.com/api&#39;params: 对象*/function ajax(method, url, params){  var res = null  method= method.toUpperCase()  var xhr = new XMLHttpRequest  // 将Object类型的参数转换为 key1=value1&amp;key2=value2的形式  if(typeof params === &#39;object&#39;){    var tempArr = []    for (var key in params){      var value = params[key]      tempArr.push(key + &#39;=&#39; + params[key])      // tempArr =&gt; [&#39;key1=value1&#39;, &#39;key2=value2&#39;]    }    params = tempArr.join(&#39;&amp;&#39;)    // tempArr =&gt; [&#39;key1=value1&amp;key2=value2&#39;]  }  if(method === &#39;GET&#39;){    params ? url += &#39;?&#39; + params : url  }  xhr.open(method, url, false) // 改为同步了  var data = null  if(method === &#39;POST&#39;){    xhr.setRequestHeader(&#39;Content-Type&#39;, &#39;application/x-www-form-urlencoded&#39;)    data = params || null  }  xhr.addEventListener(&#39;readystatechange&#39;, function(){    if(this.readyState !== 4) return    // console.log(this.responseText)    // return xhr.responseText // 无法再给外部函数返回结果，利用闭包、原型链    res = this.responseText  })  xhr.send(data)  // 由于是异步，会先执行(用同步，事件放到send前x不推荐)  return res}console.log(ajax(&#39;post&#39;, &#39;post.php&#39;, {key1: &#39;value1&#39;, key2: &#39;value2&#39;, key3: &#39;value3&#39;}))</code></pre><p>不能使用同步模式</p><p>补充一个概念：</p><p>委托(或回调)</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815185828.png" alt="image-20200815185827212"></p><p>函数可以理解为一个想要做的事情，函数体中约定了这件事情做的过程，直到调用时才开始工作。</p><p>将函数作为参数传递就像是将一件事情交给别人，这就是委托。</p><p>异步编程中回调/委托使用频率很高。（由于是异步的，你先执行，我告诉你做什么，我就不等了——等不及，你执行就行了）</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815185846.png" alt="image-20200815185845589"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815185858.png" alt="image-20200815185856765"></p><p>回调地狱/黑洞：死循环</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815185914.png" alt="image-20200815185913313"></p><p><font color="red"><strong>最终版本</strong></font></p><pre><code class="javascript">/*发送一个Ajax请求 version5@param (String) method  请求方式 GET/POST/get/post@param (String) url     请求地址 &#39;http://xxx.com/api&#39;@param (Object) params  请求参数@param (Function) done  请求完成过后需要做的事情(委托/回调)*/function ajax(method, url, params, done){  var res = null  method= method.toUpperCase()  var xhr = new XMLHttpRequest  // 将Object类型的参数转换为 key1=value1&amp;key2=value2的形式  if(typeof params === &#39;object&#39;){    var tempArr = []    for (var key in params){      var value = params[key]      tempArr.push(key + &#39;=&#39; + params[key])    }    params = tempArr.join(&#39;&amp;&#39;)  }  if(method === &#39;GET&#39;){    params ? url += &#39;?&#39; + params : url  }  xhr.open(method, url, true)  var data = null  if(method === &#39;POST&#39;){    xhr.setRequestHeader(&#39;Content-Type&#39;, &#39;application/x-www-form-urlencoded&#39;)    data = params || null  }  xhr.send(data)  xhr.addEventListener(&#39;readystatechange&#39;, function(){    if(this.readyState !== 4) return    res = this.responseText    done(res)  })}var ondone = function (res){  console.log(&#39;准备执行&#39;)  // console.log(res)  alert(res)  console.log(&#39;执行完成了&#39;)}ajax(&#39;post&#39;, &#39;post.php&#39;, {key1: &#39;value1&#39;, key2: &#39;value2&#39;}, ondone)ajax(&#39;get&#39;, &#39;time.php&#39;, {}, function(res){  console.log(res)})</code></pre><h3><span id="2-jquery中的ajax">2、jQuery中的Ajax</span></h3><p>jQuery中有一套专门针对AJAX的封装，功能十分完善，经常使用，需要注意。</p><p><a href="https://www.jquery123.com/category/ajax/" target="_blank" rel="noopener">https://www.jquery123.com/category/ajax/</a></p><p>(1)通用方法$.ajax</p><p>底层接口(其他接口依赖于这个)</p><pre><code class="html">&lt;script src=&quot;https://cdn.bootcdn.net/ajax/libs/jquery/1.12.4/jquery.min.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;// 1、最基础的调用$.ajax(&#39;./time.php&#39;, { // url:   type: &#39;POST&#39;,  // method: 请求方法  success: function(res){    // res =&gt; 拿到的只是响应体    console.log(res)  }})// 可以把url写到里面去$.ajax({   url: &#39;./time.php&#39;,// url:   type: &#39;POST&#39;,  // method: 请求方法  data: {id: 1, name: &#39;张三&#39;}, // 用于提交到服务端的参数  /*    get方式通过url传递    post方式按照请求体传递  */  success: function(res){    // res =&gt; 拿到的只是响应体    console.log(res)  }})// res返回格式$.ajax({   url: &#39;./json.php&#39;,// url: 请求地址  type: &#39;get&#39;,  // method: 请求方法  success: function(res){    // res会根据服务器响应的Content-Type自动转换为对象    // 这是jQuery内部实现的    console.log(res)  }})// 指定响应体类型$.ajax({   url: &#39;./json.php&#39;,// url:   type: &#39;get&#39;,  // method: 请求方法  // data: {id: 1, name: &#39;张三&#39;}, // 设置请求参数  dataType: &#39;json&#39;, // 用于设置响应体的类型(与data参数没关系)  success: function(res){    // 一旦设置了dataType选项，就不再关心服务端响应的Content-Type了    // 客户端会主观地认为服务端返回的就是json格式    console.log(res)  }})&lt;/script&gt;</code></pre><p>原生操作中不论请求状态码是多少都会触发回调</p><p>jQuery中ajax的回调</p><pre><code class="html">&lt;script src=&quot;https://cdn.bootcdn.net/ajax/libs/jquery/1.12.4/jquery.min.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;// jQuery中ajax的回调$.ajax({   url: &#39;./time.php&#39;, // url  data: {key1:value1, key2:value2}, // 参数  type: &#39;POST&#39;,  // method: 请求方法  dataType: &#39;json&#39;, // 响应体结果  beforeSend: function(xhr){    // 在所有发送请求得操作(open、send)之前执行    console.log(&#39;beforeSend&#39;, xhr)  }  success: function(res){ // 成功的回调    // 只有请求成功(状态码为200)才会执行这个函数    // res =&gt; 拿到的只是响应体    console.log(res)  },  // timeout: 2000, // 超时时间  error: function(xhr){ // 失败的回调    // 只有请求不正常才会执行(状态码不为200)    console.log(&#39;error&#39;,xhr)  },  complete: function(xhr){    // 不管成功还是失败都是完成，都会执行这个complete函数    console.log(&#39;complete&#39;, xhr)  }})&lt;/script&gt;</code></pre><p>(2) 高级封装</p><pre><code class="html">&lt;script src=&quot;https://cdn.bootcdn.net/ajax/libs/jquery/1.12.4/jquery.min.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;// 2、jQuery中ajax的快捷方法$.get(&#39;time.php&#39;, function(res){  console.log(res)})$.post(&#39;time.php&#39;, function(res){  console.log(res)})// 服务端设置了json能转换对象，没设置不行$.get(&#39;time.php&#39;, {id: 1}, function(res){  console.log(res)})$.post(&#39;time.php&#39;, {id: 1}, function(res){  console.log(res)})// 无视服务端Content-Type，视作JSON格式$.getJSON(&#39;json.php&#39;, {id: 1}, function(res){  console.log(res)})$.postJSON(&#39;json.php&#39;, {id: 1}, function(res){  console.log(res)})明确请求的方式，根据方式选择快捷方法&lt;/script&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815190207.png" alt="image-20200815190206531"></p><pre><code class="html">&lt;script src=&quot;https://cdn.bootcdn.net/ajax/libs/jquery/1.12.4/jquery.min.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;// 跳转其他页面，会白屏，但是有些页面只是部分不相同，可以局部刷新$(function($){  // 有一个独立的作用域，顺便确保页面加载完成执行  $(&#39;a.item&#39;).on(&#39;click&#39;, function(){    var url = $(this).attr(&#39;href&#39;)    $(&#39;#main&#39;).load(url + &#39; #main&gt;*&#39;) // 元素.load(链接+空格+选择器)    // 阻止a的默认行为    return false  })})&lt;/script&gt;</code></pre><p>将jQuery的几种方式汇总一下：</p><p><code>server.js</code>中：</p><pre><code class="javascript">//jQuery 服务app.all(&#39;/jquery-server&#39;, (request, response) =&gt; {    //设置响应头  设置允许跨域    response.setHeader(&#39;Access-Control-Allow-Origin&#39;, &#39;*&#39;);    response.setHeader(&#39;Access-Control-Allow-Headers&#39;, &#39;*&#39;);    // response.send(&#39;Hello jQuery AJAX&#39;);    const data = {name:&#39;wallleap&#39;};    response.send(JSON.stringify(data));});</code></pre><p>前端</p><pre><code class="html">&lt;link crossorigin=&quot;anonymous&quot; href=&quot;https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css&quot; rel=&quot;stylesheet&quot;&gt;&lt;script crossorigin=&quot;anonymous&quot; src=&quot;https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js&quot;&gt;&lt;/script&gt; &lt;!-- crossorigin=&quot;anonymous&quot;：跨域源请求设置，加上之后向这个资源发送请求时不会携带当前域名下的cookie --&gt;&lt;div class=&quot;container&quot;&gt;    &lt;h2 class=&quot;page-header&quot;&gt;jQuery发送AJAX请求 &lt;/h2&gt;    &lt;button class=&quot;btn btn-primary&quot;&gt;GET&lt;/button&gt;    &lt;button class=&quot;btn btn-danger&quot;&gt;POST&lt;/button&gt;    &lt;button class=&quot;btn btn-info&quot;&gt;通用型方法ajax&lt;/button&gt;&lt;/div&gt;&lt;script&gt;  $(&#39;button&#39;).eq(0).click(function(){    $.get(&#39;http://127.0.0.1:8000/jquery-server&#39;, {a:100, b:200}, function(data){      console.log(data);    },&#39;json&#39;);  });  $(&#39;button&#39;).eq(1).click(function(){    $.post(&#39;http://127.0.0.1:8000/jquery-server&#39;, {a:100, b:200}, function(data){      console.log(data);    });  });  $(&#39;button&#39;).eq(2).click(function(){    $.ajax({      //url      url: &#39;http://127.0.0.1:8000/jquery-server&#39;,      //参数      data: {a:100, b:200},      //请求类型      type: &#39;GET&#39;,      //响应体结果      dataType: &#39;json&#39;,      //成功的回调      success: function(data){        console.log(data);      },      //超时时间      timeout: 2000,      //失败的回调      error: function(){        console.log(&#39;出错啦!!&#39;);      },      //头信息      headers: {        c:300,        d:400      }    });  });&lt;/script&gt;</code></pre><p>(3) 全局事件及配置NProgress显示加载进度</p><pre><code class="html">&lt;style&gt;  .loading{    display: none;    position: fixed;    left: 0;    right: 0;    top: 0;    bottom: 0;    background-color: rgba(85, 85, 85, .5);    text-align: center;    padding-top: 200px;    color: #fff;    font-size: 50px;  }&lt;/style&gt;&lt;div class=&quot;loading&quot;&gt;正在玩命加载中……&lt;/div&gt;&lt;script src=&quot;https://cdn.bootcdn.net/ajax/libs/jquery/1.12.4/jquery.min.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;// 3、jQuery全局事件处理函数/*$.ajax({  url: &#39;time.php&#39;,  beforeSend: function(xhr){    // 显示加载提示    $(&#39;.loading&#39;).fadeIn()    console.log(&#39;即将开始请求&#39;)  },  complete: function(xhr){    // 结束提示    $(&#39;.loading&#39;).fadeOut()    console.log(&#39;请求结束了&#39;)  }})*/// 所有的ajax请求开始和结束$(document).ajaxStart(function(){  // 只要有ajax请求发生就会执行  // 显示加载提示  $(&#39;.loading&#39;).fadeIn()  console.log(&#39;即将开始请求&#39;)})$(document).ajaxStop(function(){  // 只要有ajax请求发生就会执行  // 显示加载提示  $(&#39;.loading&#39;).fadeOut()  console.log(&#39;请求结束了&#39;)})$(&#39;body&#39;).on(&#39;click&#39;, function(){  // $.ajax({  //   url: &#39;time.php&#39;  // })  $.get(&#39;time.php&#39;)})&lt;/script&gt;</code></pre><p>可以写成链式的：</p><pre><code class="javascript">$(document)  .ajaxStart(function(){    ……  })  .ajaxStop(function(){    ……  })</code></pre><p>搭配NProgress</p><pre><code class="html">&lt;link rel=&quot;stylesheet&quot; href=&quot;https://unpkg.com/nprogress@0.2.0/nprogress.css&quot;&gt;&lt;script src=&quot;https://unpkg.com/nprogress@0.2.0/nprogress.js&quot;&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;&lt;div style=&quot;margin-top: 100px;text-align: center;&quot;&gt;点击加载&lt;/div&gt;&lt;script src=&quot;https://cdn.bootcdn.net/ajax/libs/jquery/1.12.4/jquery.min.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;// 3、jQuery全局事件处理函数搭配nprogress// 所有的ajax请求开始和结束$(document)  .ajaxStart(function(){    // 只要有ajax请求发生就会执行    NProgress.start()  })  .ajaxStop(function(){    // 只要有ajax请求发生就会执行    NProgress.done()  })$(&#39;body&#39;).on(&#39;click&#39;, function(){  $.get(&#39;time.php&#39;)})&lt;/script&gt;&lt;/body&gt;</code></pre><h3><span id="3-axios">3、axios</span></h3><p>(1) 使用教程：<a href="http://www.axios-js.com/zh-cn/docs/" target="_blank" rel="noopener">http://www.axios-js.com/zh-cn/docs/</a></p><p>Axios 是一个基于 promise 的 HTTP 库，可以用在浏览器和 node.js 中。</p><p>(2) 特性</p><ul><li>从浏览器中创建 <a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest" target="_blank" rel="noopener">XMLHttpRequests</a></li><li>从 node.js 创建 <a href="http://nodejs.org/api/http.html" target="_blank" rel="noopener">http</a> 请求</li><li>支持 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise" target="_blank" rel="noopener">Promise</a> API</li><li>拦截请求和响应</li><li>转换请求数据和响应数据</li><li>取消请求</li><li>自动转换 JSON 数据</li><li>客户端支持防御 <a href="http://en.wikipedia.org/wiki/Cross-site_request_forgery" target="_blank" rel="noopener">XSRF</a></li></ul><p>(3) 安装</p><p>使用 npm:</p><pre><code class="sh">$ npm install axios</code></pre><p>使用 bower:</p><pre><code class="bash">$ bower install axios</code></pre><p>使用 cdn:</p><pre><code class="html">&lt;script src=&quot;https://unpkg.com/axios/dist/axios.min.js&quot;&gt;&lt;/script&gt;</code></pre><p>(4) 案例</p><p><code>server.js</code></p><pre><code class="javascript">//axios 服务app.all(&#39;/axios-server&#39;, (request, response) =&gt; {    //设置响应头  设置允许跨域    response.setHeader(&#39;Access-Control-Allow-Origin&#39;, &#39;*&#39;);    response.setHeader(&#39;Access-Control-Allow-Headers&#39;, &#39;*&#39;);    // response.send(&#39;Hello jQuery AJAX&#39;);    const data = {name:&#39;wallleap&#39;};    response.send(JSON.stringify(data));});</code></pre><p>axios发送Ajax请求</p><pre><code class="html">&lt;script crossorigin=&quot;anonymous&quot; src=&quot;https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.js&quot;&gt;&lt;/script&gt;&lt;button&gt;GET&lt;/button&gt;&lt;button&gt;POST&lt;/button&gt;&lt;button&gt;AJAX&lt;/button&gt;&lt;script&gt;  // https://github.com/axios/axios  const btns = document.querySelectorAll(&#39;button&#39;);  //配置 baseURL  axios.defaults.baseURL = &#39;http://127.0.0.1:8000&#39;;  btns[0].onclick = function () {    //GET 请求 get(url, 其它配置)    // axios.get(&#39;http://127.0.0.1:8000/axios-server&#39;, {    axios.get(&#39;/axios-server&#39;, { // 配置了baseURL      //url 参数——&gt;id=100&amp;vip=7      params: {        id: 100,        vip: 7      },      //请求头信息      headers: {        name: &#39;wallleap&#39;,        age: 20      }    }).then(value =&gt; { // 基于promise 处理返回结果 value.config、data、headers、request、status、statusText      console.log(value);    });  }  btns[1].onclick = function () {    // POST请求 post(url, 请求体, 其它配置)    axios.post(&#39;/axios-server&#39;, { // 请求体      username: &#39;admin&#39;,      password: &#39;password&#39;    }, {      //url       params: {        id: 200,        vip: 9      },      //请求头参数      headers: {        height: 180,        weight: 180,      }    }).then(response=&gt;{      // 配置      console.log(response.config);      // XMLHttpRequest      console.log(response.request);      //响应状态码      console.log(response.status);      //响应状态字符串      console.log(response.statusText);      //响应头信息      console.log(response.headers);      //响应体      console.log(response.data);    })  }  btns[2].onclick = function(){    // 通用方式 axios(对象) --&gt; {method, url, 参数, 头信息, 请求体参数}    axios({      //请求方法      method : &#39;POST&#39;,      //url      url: &#39;/axios-server&#39;,      //url参数      params: {        vip:10,        level:30      },      //头信息      headers: {        a:100,        b:200      },      //请求体参数      data: {        username: &#39;admin&#39;,        password: &#39;password&#39;      }    }).then(response=&gt;{      //响应状态码      console.log(response.status);      //响应状态字符串      console.log(response.statusText);      //响应头信息      console.log(response.headers);      //响应体      console.log(response.data);    }).catch(function (error) {      console.log(error);    })  }&lt;/script&gt;</code></pre><h2><span id="4-fetch">4、fetch</span></h2><p>fetch使用：<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch</a></p><p>fetch发送AJAX请求</p><p><code>server.js</code></p><pre><code class="javascript">//fetch 服务app.all(&#39;/fetch-server&#39;, (request, response) =&gt; {    //设置响应头  设置允许跨域    response.setHeader(&#39;Access-Control-Allow-Origin&#39;, &#39;*&#39;);    response.setHeader(&#39;Access-Control-Allow-Headers&#39;, &#39;*&#39;);    // response.send(&#39;Hello jQuery AJAX&#39;);    const data = {name:&#39;wallleap&#39;};    response.send(JSON.stringify(data));});</code></pre><pre><code class="html">&lt;button&gt;AJAX请求&lt;/button&gt;&lt;script&gt;  //文档地址  //https://developer.mozilla.org/zh-CN/docs/Web/API/WindowOrWorkerGlobalScope/fetch  const btn = document.querySelector(&#39;button&#39;);  btn.onclick = function(){    fetch(&#39;http://127.0.0.1:8000/fetch-server?vip=10&#39;, {      //请求方法      method: &#39;POST&#39;,      //请求头      headers: {        name:&#39;atguigu&#39;      },      //请求体      body: &#39;username=admin&amp;password=admin&#39;    }).then(response =&gt; {      // return response.text();      return response.json();    }).then(response=&gt;{      console.log(response);    });  }&lt;/script&gt;</code></pre><h2><span id="五-跨域">五、跨域</span></h2><h3><span id="1-概念">1、概念</span></h3><p>(1) 同源策略(Same-Origin Policy)：最早由Netscape公司提出，是浏览器的一种安全策略，所谓同源是指<strong>协议、域名、端口</strong>完全相同，只有同源的地址才可以相互通过AJAX的方式请求。</p><p>下面来一个同源的案例：</p><p>重写一个<code>server.js</code></p><pre><code class="javascript">const express = require(&#39;express&#39;);const app = express();app.get(&#39;/home&#39;, (request, response)=&gt;{    //响应一个页面    response.sendFile(__dirname + &#39;/index.html&#39;);});app.get(&#39;/data&#39;, (request, response)=&gt;{    response.send(&#39;用户数据&#39;);});app.listen(9000, ()=&gt;{    console.log(&quot;服务已经启动...&quot;);});</code></pre><p>运行起来</p><pre><code class="sh">nodemon server.js</code></pre><p>在这个目录下新建<code>index.html</code></p><pre><code class="html">&lt;h1&gt;wallleap&lt;/h1&gt;&lt;button&gt;点击获取用户数据&lt;/button&gt;&lt;script&gt;  const btn = document.querySelector(&#39;button&#39;);  btn.onclick = function(){    const x = new XMLHttpRequest();    //这里因为是满足同源策略的, 所以 url 可以简写    x.open(&quot;GET&quot;,&#39;/data&#39;);    //发送    x.send();    //    x.onreadystatechange = function(){      if(x.readyState === 4){        if(x.status &gt;= 200 &amp;&amp; x.status &lt; 300){          console.log(x.response);        }      }    }  }&lt;/script&gt;</code></pre><p>访问<a href="http://127.0.0.1:9000/home，可以访问这个index.html，点击按钮可以获取到数据" target="_blank" rel="noopener">http://127.0.0.1:9000/home，可以访问这个index.html，点击按钮可以获取到数据</a></p><p>(2) 同源或者不同源说的是两个地址之间的关系，不同源地址之间请求我们称之为跨域请求。</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815190430.png" alt="image-20200815190429165"></p><p>跨域的案例：</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815190440.png" alt="image-20200815190439633"></p><p>跨域会报错：</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815190456.png" alt="image-20200815190455302"></p><h3><span id="2-解决方案">2、解决方案</span></h3><p>不同源地址之间如果需要相互请求，必须服务端和客户端配合才能完成</p><p>尝试找到一种可以发送不同源请求的方式</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815190529.png" alt="可能可以解决跨域的方法"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815190556.png" alt="正常图片标签"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815190621.png" alt="尝试使用img标签解决跨域"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815190650.png" alt="正常link标签"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815190712.png" alt="尝试使用link标签解决跨域"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815190738.png" alt="尝试使用script标签解决跨域"></p><p>初级跨域解决方案</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815190812.png" alt="服务器端将json用函数包裹返回"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815190858.png" alt="客户端使用该函数拿到数据"></p><p>(1) <strong>JSONP</strong></p><p>JSON with Padding，是一种借助于<code>script</code>标签发送跨域请求的技巧。这个是非官方的跨域解决方案，是程序员们机智地想出来的，只支持get请求。</p><p>其原理就是在客户端借助<code>script</code>标签请求服务端的一个动态网页(php等)，服务端的这个动态网页返回一段带有函数调用的JavaScript全局函数调用的脚本，将原本需要返回给客户端的数据传递进去。</p><p>以后绝大多数情况都是采用JSONP的手段完成不同源地址之间的跨域请求。</p><ul><li>原理演示：</li></ul><p>当前目录下，新建<code>js/app.js</code></p><pre><code class="javascript">const data = {    name: &#39;测试jsonp&#39;};/* 把这个挪走，到测试的html文件中//处理数据    function handle(data) {        //获取 result 元素        const result = document.getElementById(&#39;result&#39;);        result.innerHTML = data.name;    }*/handle(data);</code></pre><p>html</p><pre><code class="html">&lt;style&gt;  #result {    width: 300px;    height: 100px;    border: solid 1px #78a;  }&lt;/style&gt;&lt;div id=&quot;result&quot;&gt;&lt;/div&gt;&lt;script&gt;    //处理数据    function handle(data) {        //获取 result 元素        const result = document.getElementById(&#39;result&#39;);        result.innerHTML = data.name;    }&lt;/script&gt;&lt;!-- &lt;script src=&quot;http://127.0.0.1:5500/jsonp/js/app.js&quot;&gt;&lt;/script&gt; --&gt; &lt;!-- 利用file://方式访问这个html页面 --&gt;&lt;!-- 进阶版 --&gt;&lt;script src=&quot;http://127.0.0.1:8000/jsonp-server&quot;&gt;&lt;/script&gt;</code></pre><p><code>server.js</code></p><pre><code class="javascript">//jsonp服务app.all(&#39;/jsonp-server&#39;,(request, response) =&gt; {    // response.send(&#39;console.log(&quot;hello jsonp&quot;)&#39;); // 前端拿到js代码    const data = {        name: &#39;测试jsonp&#39;    };    //将数据转化为字符串    let str = JSON.stringify(data);    //返回结果    response.end(`handle(${str})`); // 会返回一个函数调用，实参是想返回给前端的数据(前端需要先声明这个函数)});</code></pre><ul><li>用php演示一下</li></ul><p><code>server.php</code></p><pre><code class="php">&lt;?php$conn = mysqli_connect(&#39;localhost&#39;, &#39;root&#39;, &#39;123456&#39;, &#39;demo&#39;);$query = mysqli_query($conn, &#39;select * from users&#39;);while ($row = mysqli_fetch_assoc($query)) {  $data[] = $row;}if (empty($_GET[&#39;callback&#39;])) {  header(&#39;Content-Type: application/json&#39;);  echo json_encode($data);  exit();}// 如果客户端采用的是 script 标记对我发送的请求// 一定要返回一段 JavaScriptheader(&#39;Content-Type: application/javascript&#39;);$result = json_encode($data);$callback_name = $_GET[&#39;callback&#39;];echo &quot;typeof {$callback_name} === &#39;function&#39; &amp;&amp; {$callback_name}({$result})&quot;;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815191014.png" alt="image-20200815191013064"></p><ul><li>原生方式实践jsonp</li></ul><p><code>server.js</code></p><pre><code class="javascript">//用户名检测是否存在app.all(&#39;/check-username&#39;,(request, response) =&gt; {    // response.send(&#39;console.log(&quot;hello jsonp&quot;)&#39;);    const data = {        exist: 1,        msg: &#39;用户名已经存在&#39;    };    //将数据转化为字符串    let str = JSON.stringify(data);    //返回结果    response.end(`handle(${str})`);});</code></pre><p>前端代码：</p><pre><code class="html">用户名: &lt;input type=&quot;text&quot; id=&quot;username&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;script&gt;  //获取 input 元素  const input = document.querySelector(&#39;input&#39;);  const p = document.querySelector(&#39;p&#39;);  //声明 handle 函数  function handle(data){    input.style.border = &quot;solid 1px #f00&quot;;    //修改 p 标签的提示文本    p.innerHTML = data.msg;  }  //绑定事件  input.onblur = function(){    //获取用户的输入值    let username = this.value;    //向服务器端发送请求 检测用户名是否存在    //1. 创建 script 标签    const script = document.createElement(&#39;script&#39;);    //2. 设置标签的 src 属性    script.src = &#39;http://127.0.0.1:8000/check-username&#39;;    //3. 将 script 插入到文档中    document.body.appendChild(script);  }&lt;/script&gt;</code></pre><ul><li>封装成一个函数</li></ul><pre><code class="javascript">function jsonp (url, params, callback) {  var funcName = &#39;jsonp_&#39; + Date.now() + Math.random().toString().substr(2, 5)  if (typeof params === &#39;object&#39;) {    var tempArr = []    for (var key in params) {      var value = params[key]      tempArr.push(key + &#39;=&#39; + value)    }    params = tempArr.join(&#39;&amp;&#39;)  }  var script = document.createElement(&#39;script&#39;)  script.src = url + &#39;?&#39; + params + &#39;&amp;callback=&#39; + funcName  document.body.appendChild(script)  window[funcName] = function (data) {    callback(data)    delete window[funcName]    document.body.removeChild(script)  }}jsonp(&#39;http://localhost/jsonp/server.php&#39;, { id: 123 }, function (res) {  console.log(res)})jsonp(&#39;http://localhost/jsonp/server.php&#39;, { id: 123 }, function (res) {  console.log(res)})</code></pre><ul><li>jQuery方式实践jsonp</li></ul><p><code>server.js</code></p><pre><code class="javascript">// jQuery jsonpapp.all(&#39;/jquery-jsonp-server&#39;,(request, response) =&gt; {  // response.send(&#39;console.log(&quot;hello jsonp&quot;)&#39;);  const data = {    name:&#39;尚硅谷&#39;,    city: [&#39;北京&#39;,&#39;上海&#39;,&#39;深圳&#39;]  };  //将数据转化为字符串  let str = JSON.stringify(data);  //接收 callback 参数  let cb = request.query.callback; // 函数名从前端获取  //返回结果  response.end(`${cb}(${str})`);});</code></pre><p>前端代码：</p><pre><code class="html">&lt;style&gt;  #result{    width:300px;    height:100px;    border:solid 1px #089;  }&lt;/style&gt;&lt;script crossorigin=&quot;anonymous&quot; src=&#39;https://cdn.bootcss.com/jquery/3.5.0/jquery.min.js&#39;&gt;&lt;/script&gt;&lt;button&gt;点击发送 jsonp 请求&lt;/button&gt;&lt;div id=&quot;result&quot;&gt;&lt;/div&gt;&lt;script&gt;  $(&#39;button&#39;).eq(0).click(function(){    $.getJSON(&#39;http://127.0.0.1:8000/jquery-jsonp-server?callback=?&#39;, function(data){      $(&#39;#result&#39;).html(`                名称: ${data.name}&lt;br&gt;                校区: ${data.city}            `)    });  });&lt;/script&gt;</code></pre><p>(2)CORS</p><p>HTTP访问控制（CORS）<a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS</a></p><p>Cross Origin Resource Share，跨域资源共享。CORS是官方的跨域解决方案，它的特点是不需要再客户端做任何特殊的操作，完全在服务器中进行处理，支持get和post请求(其他也支持)。跨域资源共享标准新增了一组HTTP首部字段，允许服务器声明哪些源站通过浏览器有权限访问哪些资源。</p><p>CORS是通过设置一个响应头来告诉浏览器，这个请求允许跨域，浏览器收到该响应以后就会对响应放行。</p><p><code>server.js</code></p><pre><code class="javascript">app.all(&#39;/cors-server&#39;, (request, response)=&gt;{  //设置响应头  response.setHeader(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;); // 允许所有源站发送请求  response.setHeader(&quot;Access-Control-Allow-Headers&quot;, &#39;*&#39;); // 允许携带的响应头  response.setHeader(&quot;Access-Control-Allow-Method&quot;, &#39;*&#39;); // 允许请求方法  // response.setHeader(&quot;Access-Control-Allow-Origin&quot;, &quot;http://127.0.0.1:5500&quot;); // 允许单个  response.send(&#39;hello CORS&#39;);});</code></pre><p>测试：</p><pre><code class="html">&lt;style&gt;    #result{        width:200px;        height:100px;        border:solid 1px #90b;    }&lt;/style&gt;&lt;button&gt;发送请求&lt;/button&gt;&lt;div id=&quot;result&quot;&gt;&lt;/div&gt;&lt;script&gt;  const btn = document.querySelector(&#39;button&#39;);  btn.onclick = function(){    //1. 创建对象    const x = new XMLHttpRequest();    //2. 初始化设置    x.open(&quot;GET&quot;, &quot;http://127.0.0.1:8000/cors-server&quot;);    //3. 发送    x.send();    //4. 绑定事件    x.onreadystatechange = function(){      if(x.readyState === 4){        if(x.status &gt;= 200 &amp;&amp; x.status &lt; 300){          //输出响应体          console.log(x.response);        }      }    }  }&lt;/script&gt;</code></pre><pre><code class="php">// 允许远端访问(甚至直接打开文件的方式也可以file://……)header(&#39;Access-Control-Allow-Origin: *&#39;) // 允许所有header(&#39;Access-Control-Allow-Origin: http://localhost/index.html&#39;) // 允许单个</code></pre><p>eg:</p><pre><code class="php">&lt;?php$conn = mysqli_connect(&#39;localhost&#39;, &#39;root&#39;, &#39;123456&#39;, &#39;demo&#39;);$query = mysqli_query($conn, &#39;select * from users&#39;);while ($row = mysqli_fetch_assoc($query)) {  $data[] = $row;}// 一行代码搞定// 允许跨域请求header(&#39;Access-Control-Allow-Origin: *&#39;);header(&#39;Content-Type: application/json&#39;);echo json_encode($data);</code></pre><p>客户端</p><pre><code class="html">&lt;script src=&quot;jquery.js&quot;&gt;&lt;/script&gt;&lt;script&gt;  $.get(&#39;http://localhost/cors.php&#39;, {}, function (res) {    console.log(res)  })&lt;/script&gt;</code></pre><p>这种方案无序客户端作出任何变化(不用改代码)，只是在被请求的服务端响应的时候<strong>添加一个<code>Access-Control-Allow-Origin</code>的响应头</strong>，表示这个资源是否允许指定域请求。</p><h2><span id="六-xmlhttprequest20">六、XMLHttpRequest2.0</span></h2><blockquote><p>暂作了解，无需着重看待</p></blockquote><p>更易用，更强大。</p><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="笔记" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="前端" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="前端" scheme="https://wallleap.cn/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="web" scheme="https://wallleap.cn/tags/web/"/>
    
      <category term="Ajax" scheme="https://wallleap.cn/tags/Ajax/"/>
    
  </entry>
  
  <entry>
    <title>React全家桶(技术栈)学习笔记</title>
    <link href="https://wallleap.cn/2020/07/12/React/"/>
    <id>https://wallleap.cn/2020/07/12/React/</id>
    <published>2020-07-12T08:54:50.000Z</published>
    <updated>2025-01-24T09:22:15.349Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1><span id="第1章-react入门">第1章 React入门</span></h1><h2><span id="11-react的基本认识">1.1 React的基本认识</span></h2><h3><span id="111-官网">1.1.1 官网</span></h3><p>1)     英文官网:<a href="https://reactjs.org/" target="_blank" rel="noopener"> https://reactjs.org/</a></p><p>2)     中文官网: <a href="https://doc.react-china.org/" target="_blank" rel="noopener">https://doc.react-china.org/</a></p><h3><span id="112-介绍描述">1.1.2 介绍描述</span></h3><p>1)     用于构建用户界面的 JavaScript 库(只关注于View)</p><ul><li>JS库：<ul><li>jQuery——函数库(方法、函数包装DOM操作)</li><li>React 基本上不操作DOM——JS框架</li></ul></li><li>构建用户界面：把数据展现出来</li></ul><p>2)     由<strong>Facebook</strong>开源</p><h3><span id="113-react的特点">1.1.3 React的特点</span></h3><p>1)     Declarative(声明式编码)</p><p>（申请一块内存，只需要声明一个变量即可）不需要亲自操作DOM，只需要告诉它，我要更新，就会帮你更新，只需要更新数据，界面不需要手动更新（以前需要更新DOM）</p><p>2)     Component-Based(组件化编码)</p><p>简化特别复杂的功能，可以拆分为多个简单的部分（一个小的界面功能就是一个组件），维护也方便</p><p>3)     Learn Once, Write Anywhere(支持客户端与服务器渲染)</p><p>一次学习，随处编写：不仅能写web应用，还能写React Native打包为Android、IOS应用</p><p>4)     高效</p><p>5)     单向数据流</p><h3><span id="114-react高效的原因区域-次数更新界面效率提高">1.1.4 React高效的原因(区域、次数——更新界面效率提高)</span></h3><p>1)     <strong>虚拟(virtual)DOM</strong>,，不总是直接操作DOM</p><ul><li>虚拟DOM：对象——与组件对应，修改映射到真实的DOM上(批量修改、界面重绘<strong>次数少</strong>)</li></ul><p>2)     <strong>DOM Diff算法</strong>,，最小化页面重绘</p><ul><li>界面中组件是否更新(更新<strong>区域小</strong>)</li></ul><h2><span id="12-react的基本使用">1.2 React的基本使用</span></h2><p>注意: 此时只是测试语法使用, 并不是真实项目开发使用</p><h3><span id="121-效果">1.2.1 效果</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/react1592556203130.png" alt="React基本使用"></p><p>将h1标签利用react放到test中</p><h3><span id="123-相关js库">1.2.3 相关js库</span></h3><p>可以到bootcdn引用地址，访问链接<kbd>Ctrl</kbd>+<kbd>S</kbd>保存到本地</p><p>1)     <code>react.js</code>: React的核心库</p><ul><li>development.js：开发版，开发编写的时候使用</li><li>production.min.js：生产版，上线的时候使用，压缩过的</li></ul><p>2)     <code>react-dom.js</code>: 提供操作DOM的react扩展库</p><p>3)     <code>babel.min.js</code>: 解析JSX语法代码转为纯JS语法代码的库,这里不是ES6转ES5(jsx是js扩展语法)</p><h3><span id="124-在页面中导入js">1.2.4 在页面中导入js</span></h3><pre><code class="html">&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react-dom.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/babel.min.js&quot;&gt;&lt;/script&gt;</code></pre><h3><span id="125-编码">1.2.5 编码</span></h3><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;head&gt;  &lt;meta charset=&quot;UTF-8&quot;&gt;  &lt;title&gt;01_HelloWorld&lt;/title&gt;&lt;/head&gt;&lt;body&gt;  &lt;div id=&quot;test&quot;&gt;&lt;/div&gt;  &lt;script type=&quot;text/javascript&quot; src=&quot;../js/react.development.js&quot;&gt;&lt;/script&gt;  &lt;script type=&quot;text/javascript&quot; src=&quot;../js/react-dom.development.js&quot;&gt;&lt;/script&gt;  &lt;script type=&quot;text/javascript&quot; src=&quot;../js/babel.min.js&quot;&gt;&lt;/script&gt;  &lt;script type=&quot;text/babel&quot;&gt;/*告诉babel.js解析里面的jsx的代码*/    // 1. 创建虚拟DOM元素对象    var vDom = &lt;h1&gt;Hello React!&lt;/h1&gt;   // jsx，不是字符串，不能加引号    // 2. 将虚拟DOM渲染到页面真实DOM容器中    ReactDOM.render(vDom, document.getElementById(&#39;test&#39;))  // react-dom.js提供的  render——渲染   将vDom加入到#test中  &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><h3><span id="126-使用react开发者工具调试">1.2.6 使用React开发者工具调试</span></h3><p>​          <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/react2.gif" alt="React开发调试插件">  </p><p>React Developer Tool.crx</p><h2><span id="13-react-jsx">1.3 React JSX</span></h2><h3><span id="131-效果">1.3.1 效果</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/react1592556978982.png" alt="ReactJSX"></p><p>两个#test分别加入相应内容</p><p>代码：</p><pre><code class="html">&lt;div id=&quot;test1&quot;&gt;&lt;/div&gt;&lt;div id=&quot;test2&quot;&gt;&lt;/div&gt;&lt;script src=&quot;../js/react.development.js&quot;&gt;&lt;/script&gt;&lt;script src=&quot;../js/react-dom.development.js&quot;&gt;&lt;/script&gt;&lt;script src=&quot;../js/babel.min.js&quot;&gt;&lt;/script&gt;&lt;script&gt; // 还没用到jsx语法，不需要babel  const msg = &#39;I Like You!&#39;  const myId = &#39;Atguigu&#39;  // 1.创建虚拟DOM  // var element = React.createElement(&#39;h1&#39;, {id:&#39;myTitle&#39;},&#39;hello&#39;)  const vDom1 = React.createElement(&#39;h2&#39;, {id:myId.toLowerCase()},msg.toUpperCase())  // 2.渲染虚拟DOM  ReactDOM.render(vDom1, document.getElementById(&#39;test1&#39;))&lt;/script&gt;&lt;script type=&quot;text/babel&quot;&gt;  // 1.创建虚拟DOM  const vDom2 = &lt;h3 id={myId.toUpperCase()}&gt;{msg.toLowerCase()}&lt;/h3&gt;  // 变量用{}括起来  // 2.渲染虚拟DOM  ReactDOM.render(vDom2, document.getElementById(&#39;test2&#39;))&lt;/script&gt;</code></pre><h3><span id="132-虚拟dom">1.3.2 虚拟DOM</span></h3><p>1)     React提供了一些API来创建一种 <strong>特别</strong> 的一般js对象</p><p>a.     <code>var element = React.createElement(&#39;h1&#39;, {id:&#39;myTitle&#39;},&#39;hello&#39;)</code></p><p>b.     上面创建的就是一个简单的虚拟DOM对象，babel将会把jsx语法转为上述的形式</p><p>2)     虚拟DOM对象最终都会被React<strong>转换为</strong>真实的DOM（虚拟DOM中的对应真实DOM中的标签元素）</p><p>3)     我们编码时基本只需要操作react的虚拟DOM相关数据, react会转换为真实DOM变化而更新界面</p><p>补充知识：<code>debugger</code>可以在某条js代码处添加断点</p><p>虚拟DOM——轻对象，更新虚拟DOM页面不重绘</p><p>真实DOM——重对象，更新真实DOM页面会发生变化（页面重绘）</p><h3><span id="133-jsx">1.3.3 JSX</span></h3><p>1)     全称: JavaScript XML</p><p>2)     react定义的一种类似于XML的<strong>JS扩展语法</strong>: XML+JS</p><p>3)     作用: 用来创建react虚拟DOM(元素)对象</p><ul><li><p><code>var ele = &lt;h1&gt;Hello JSX!&lt;/h1&gt;</code></p></li><li><p>注意1: 它不是字符串, 也不是HTML/XML标签</p></li><li><p>注意2: 它最终产生的就是一个JS对象</p></li></ul><p>4)     <strong>标签名任意</strong>: HTML标签或其它标签</p><p>5)     标签属性任意: HTML标签属性或其它</p><p>6)     基本语法规则</p><ul><li><p><strong>遇到 &lt;开头</strong>的代码, 以<strong>标签</strong>的语法解析: html同名标签转换为html同名元素, 其它标签需要特别解析</p></li><li><p><strong>遇到以 { 开头</strong>的代码，以<strong>JS语法</strong>解析: 标签中的js代码必须用{ }包含</p></li></ul><p>7)     babel.js的作用</p><ul><li><p>浏览器不能直接解析JSX代码, 需要<strong>babel转译为纯JS的代码</strong>才能运行</p></li><li><p>只要用了JSX，都要加上<code>type=&quot;text/babel&quot;</code>, 声明需要babel来处理</p></li></ul><h3><span id="134-渲染虚拟dom元素">1.3.4 渲染虚拟DOM(元素)</span></h3><p>1)     语法: <code>ReactDOM.render(virtualDOM, containerDOM)</code></p><p>2)     作用: 将虚拟DOM元素渲染到页面中的真实容器DOM中显示</p><p>3)     参数说明</p><ul><li><p>参数一: 纯js或jsx创建的虚拟dom对象</p></li><li><p>参数二: 用来包含虚拟DOM元素的真实dom元素对象(一般是一个div)</p></li></ul><h3><span id="135-建虚拟dom的2种方式">1.3.5 建虚拟DOM的2种方式</span></h3><p>1)     纯JS(一般不用)</p><p><code>React.createElement(&#39;h1&#39;, {id:&#39;myTitle&#39;}, title)</code></p><p>2)     <strong>JSX</strong>:</p><p><code>&lt;h1 id=&#39;myTitle&#39;&gt;{title}&lt;/h1&gt;</code></p><h3><span id="136-jsx练习">1.3.6 JSX练习</span></h3><p>需求: 动态展示列表数据</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/react1592558986188.png" alt="JSX"></p><p>代码：</p><pre><code class="html">&lt;h2&gt;前端JS框架列表&lt;/h2&gt;&lt;div id=&quot;example1&quot;&gt;&lt;/div&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react-dom.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/babel.min.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/babel&quot;&gt;  /*    功能: 动态展示列表数据    - 如何将一个数据的数组，转换为一个标签的数组      使用数组的map()方法  */  // 数据：名称、类型，数组存放  const names = [&#39;jQuery&#39;, &#39;zepto&#39;, &#39;angular&#39;, &#39;react&#39;, &#39;vue&#39;]  // 1.创建虚拟DOM，有嵌套结构，最好用小括号括起来  const ul = (    &lt;ul&gt;      {        names.map((name, index) =&gt; &lt;li key={index}&gt;{name}&lt;/li&gt;)      }    &lt;/ul&gt;  )  // 2.渲染虚拟DOM  ReactDOM.render(ul, document.getElementById(&#39;example1&#39;))&lt;/script&gt;</code></pre><h2><span id="14-模块与组件和模块化与组件化的理解">1.4 模块与组件和模块化与组件化的理解</span></h2><h3><span id="141-模块">1.4.1 模块</span></h3><p>1)     理解: 向外<strong>提供特定功能的</strong>js程序, 一般就是一个<strong>js文件</strong></p><p>2)     为什么: js代码更多更复杂</p><p>3)     作用: 复用js, 简化js的编写, 提高js运行效率</p><p>有特定功能的js文件，内部有数据及对数据的操作</p><ul><li><p>数据：变量</p></li><li><p>操作：函数</p></li></ul><p>私有的函数向外暴露</p><ul><li>暴露一个函数：暴露函数本身</li><li>暴露多个函数：以对象形式暴露</li></ul><h3><span id="142-组件">1.4.2 组件</span></h3><p>1)     理解: 用来<strong>实现特定(局部)功能效果</strong>的<strong>代码</strong>集合(html/css/js)</p><p>2)     为什么: 一个界面的功能更复杂</p><p>3)     作用: 复用编码, 简化项目编码, 提高运行效率</p><h3><span id="143-模块化">1.4.3 模块化</span></h3><p>当应用的<strong>js都以模块来编写</strong>的, 这个应用就是一个模块化的应用</p><p>形容项目或编码</p><h3><span id="144-组件化">1.4.4 组件化</span></h3><p>当应用是以多组件的方式实现, 这个应用就是一个组件化的应用</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/react1592560823655.png" alt="component"></p><h1><span id="第2章-react面向组件编程">第2章 React面向组件编程</span></h1><p>面向对象——面向模块——面向组件</p><h2><span id="21-基本理解和使用">2.1 基本理解和使用</span></h2><h3><span id="211-效果">2.1.1 效果</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/react1592561100045.png" alt="组件"></p><p>组件标签：可以随便取的标签，首字母大写，与HTML标签区分开</p><h3><span id="212-自定义组件component">2.1.2 自定义组件(Component) :</span></h3><p>1)     定义组件(2种方式)</p><p>  方式1: <strong>工厂函数组件(简单组件：没有状态的组件)</strong>——效率高，不需要创建对象</p><pre><code>function MyComponent(){    return &lt;h2&gt;工厂函数组件(简单组件)&lt;/h2&gt;}</code></pre><p>方式2: <strong>ES6类组件(复杂组件)</strong>——需要创建对象，有了状态只能使用这种方式</p><pre><code class="javascript">class MyComponent2 extends React.Component{  render(){    console.log(this)  // 组件类对象 MyComponent2{...}    return &lt;h2&gt;ES6类组件(复杂组件)&lt;/h2&gt;  }}</code></pre><p>2)     渲染组件标签</p><pre><code class="JavaScript">ReactDOM.render(&lt;MyComponent  /&gt;, document.getElementById(&#39;example1&#39;))ReactDOM.render(&lt;MyComponent2  /&gt;, document.getElementById(&#39;example2&#39;))</code></pre><h3><span id="213-注意">2.1.3 注意</span></h3><p>1)     组件名必须首字母大写</p><p>2)     虚拟DOM元素只能有一个根元素</p><p>3)     虚拟DOM元素必须有结束标签</p><h3><span id="214-render渲染组件标签的基本流程">2.1.4 render()渲染组件标签的基本流程</span></h3><p>1)     React内部会创建组件实例对象</p><p>2)     得到包含的虚拟DOM并解析为真实DOM</p><p>3)     插入到指定的页面元素内部</p><h2><span id="22-组件三大属性1-state">2.2 组件三大属性1: state</span></h2><h3><span id="221-效果">2.2.1 效果</span></h3><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/Like%E7%BB%84%E4%BB%B6.gif" alt="state"></p><h3><span id="222-理解">2.2.2 理解</span></h3><p>1)     state是组件对象最重要的属性, 值是对象(可以包含多个数据)</p><p>2)     组件被称为”状态机”, 通过更新组件的state来更新对应的页面显示(重新渲染组件)</p><h3><span id="223-编码操作">2.2.3 编码操作</span></h3><p>1)     初始化状态:</p><pre><code class="javascript">constructor (props) {  super(props)  this.state = {   stateProp1 : value1,   stateProp2 : value2  }}</code></pre><p>2)     读取某个状态值</p><pre><code class="javascript">this.state.statePropertyName</code></pre><p>3)     更新状态—-&gt;组件界面更新</p><pre><code class="javascript">this.setState({  stateProp1 : value1,  stateProp2 : value2})</code></pre><h3><span id="224-代码">2.2.4 代码</span></h3><pre><code class="html">&lt;div id=&quot;example&quot;&gt;&lt;/div&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react-dom.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/babel.min.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/babel&quot;&gt;  /*  需求: 自定义组件, 功能说明如下    1. 显示h2标题, 初始文本为: 你喜欢我    2. 点击标题更新为: 我喜欢你  */  // 1.定义组件  class Like extends React.Component {    constructor(props){      super(props)      // 初始化状态      this.state = {        isLikeMe: false      }      // 将新增方法中的this强制绑定为组件对象      this.handleClick = this.handleClick.bind(this) // 也可以不在这里绑定    }    handleClick(){      // console.log(this) // handleClick是新添加方法，内部this默认不是组件对象，而是undefined  render是重写组件类的方法 到上面或下面绑定this      // 得到状态，并取反      const isLikeMe = !this.state.isLikeMe      // 更新状态      this.setState({isLikeMe}) // isLikeMe: isLikeMe    }    render() {      // 读取状态      // const isLikeMe = this.state.isLikeMe      const {isLikeMe} = this.state // 解构赋值      return &lt;h2 onClick={this.handleClick}&gt;{isLikeMe?&#39;你喜欢我&#39;:&#39;我喜欢你&#39;}&lt;/h2&gt; // this——组件对象 // this.handleClick.bind(this) —— 在这里绑定也可以    }  }  // 2.渲染组件标签  ReactDOM.render(&lt;Like /&gt;, document.getElementById(&#39;example&#39;))&lt;/script&gt;</code></pre><h2><span id="23-组件三大属性2-props">2.3 组件三大属性2: props</span></h2><h3><span id="231-效果">2.3.1 效果</span></h3><pre><code>需求: 自定义用来显示一个人员信息的组件  1). 姓名必须指定  2). 如果性别没有指定, 默认为男  3). 如果年龄没有指定, 默认为18</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/react-4.png" alt="props"></p><h3><span id="232-理解">2.3.2 理解</span></h3><p>1)     每个组件对象都会有props(properties的简写)属性</p><p>2)     组件标签的所有属性都保存在props中</p><h3><span id="233-作用">2.3.3 作用</span></h3><p>1)     通过标签属性从组件外向组件内传递变化的数据</p><p>2)     注意: 组件内部不要修改props数据</p><h3><span id="234-编码操作">2.3.4 编码操作</span></h3><p>1)     内部读取某个属性值</p><p><code>this.props.propertyName</code></p><p>2)     对props中的属性值进行类型限制和必要性限制</p><blockquote><p>注意：</p><p>自 React v15.5 起，<code>React.PropTypes</code> 已移入另一个包中。请使用 <a href="https://www.npmjs.com/package/prop-types" target="_blank" rel="noopener"><code>prop-types</code> 库</a> 代替。需要先引入该库。</p><p>我们提供了一个 <a href="https://react.docschina.org/blog/2017/04/07/react-v15.5.0.html#migrating-from-reactproptypes" target="_blank" rel="noopener">codemod 脚本</a>来做自动转换。</p></blockquote><pre><code class="javascript">Person.propTypes = {    name: React.PropTypes.string.isRequired,    age: React.PropTypes.number.isRequired}</code></pre><p>3)     扩展属性: 将对象的所有属性通过props传递</p><p><code>&lt;Person {...person}/&gt;</code></p><p>4)     默认属性值</p><pre><code class="javascript">Person.defaultProps = {    name: &#39;Mary&#39;}</code></pre><p>5)     组件类的构造函数</p><pre><code class="javascript">constructor (props) {    super(props)    console.log(props) // 查看所有属性}</code></pre><h3><span id="235-代码">2.3.5 代码</span></h3><pre><code class="html">&lt;div id=&quot;example1&quot;&gt;&lt;/div&gt;&lt;div id=&quot;example2&quot;&gt;&lt;/div&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react-dom.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/prop-types.js&quot;&gt;&lt;/script&gt; &lt;!-- 验证类型和必要性 --&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/babel.min.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/babel&quot;&gt;  /*需求: 自定义用来显示一个人员信息的组件, 效果如页面. 说明  1). 如果性别没有指定, 默认为男  2). 如果年龄没有指定, 默认为18  */  // 1、定义组件  /*function Person(props){    return (      &lt;ul&gt;        &lt;li&gt;姓名：{props.name}&lt;/li&gt;        &lt;li&gt;性别：{props.sex}&lt;/li&gt;        &lt;li&gt;年龄：{props.age}&lt;/li&gt;      &lt;/ul&gt;    )  }*/  class Person extends React.Component{    render(){      return (        &lt;ul&gt;          &lt;li&gt;姓名：{this.props.name}&lt;/li&gt;  // this——组件对象          &lt;li&gt;性别：{this.props.sex}&lt;/li&gt;          &lt;li&gt;年龄：{this.props.age}&lt;/li&gt;        &lt;/ul&gt;      )    }  }  // 指定属性默认值  Person.defaultProps = {    sex: &#39;男&#39;,    age: 18  }  // 指定属性值的类型和必要性  Person.propTypes = {    name: PropTypes.string.isRequired,    age: PropTypes.number  }  // 2、渲染组件标签  const p1 = {    name: &#39;Tom&#39;,    sex: &#39;女&#39;,    age: 18  }  // ReactDOM.render(&lt;Person name={p1.name} sex={p1.sex} age={p1.age}/&gt;, document.getElementById(&#39;example1&#39;))  ReactDOM.render(&lt;Person {...p1}/&gt;, document.getElementById(&#39;example1&#39;)) // ...作用：1.打包：function fn(...as){} fun(1,2,3) 2.解包：const arr1=[1,2,3] const arr2=[6,...arr1,9] 这里也是在解包  const p2 = {    name: &#39;JACK&#39;,    age: 17  }  ReactDOM.render(&lt;Person name={p2.name} age={p2.age}/&gt;, document.getElementById(&#39;example2&#39;))&lt;/script&gt;</code></pre><h3><span id="236-面试题">2.3.6 面试题</span></h3><p>问题: 请区别一下组件的props和state属性</p><p>1)     state: <strong>组件自身内部可变化的数据</strong></p><p><em>2)</em>    props: 从组件外部向组件内部<strong>传递数据</strong>，组件内部只读不修改</p><h2><span id="24-组件三大属性3-refs与事件处理">2.4 组件三大属性3: refs与事件处理</span></h2><h3><span id="241-效果">2.4.1 效果</span></h3><p>需求: 自定义组件, 功能说明如下:</p><ol start="2"><li>点击按钮, 提示第一个输入框中的值</li><li>当第2个输入框失去焦点时, 提示这个输入框中的值</li></ol><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/props_event.gif" alt="props_event"></p><h3><span id="242-组件的3大属性之二-refs属性">2.4.2 组件的3大属性之二: refs属性</span></h3><p>1)     组件内的标签都可以定义ref属性来标识自己</p><p>a.     &lt;input type=”text” ref={input =&gt; this.msgInput = input}/&gt;</p><p>b.     回调函数在组件初始化渲染完或卸载时自动调用</p><p>2)     在组件中可以通过this.msgInput来得到对应的真实DOM元素</p><p>3)     作用: 通过ref获取组件内容特定标签对象, 进行读取其相关数据</p><h3><span id="243-事件处理">2.4.3 事件处理</span></h3><p>1)     通过onXxx属性指定组件的事件处理函数(注意大小写)</p><p>a.     React使用的是自定义(合成)事件, 而不是使用的原生DOM事件</p><p>b.     React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)</p><p>2)     通过event.target得到发生事件的DOM元素对象</p><pre><code>handleFocus(event) { event.target // 返回input对象}</code></pre><h3><span id="244-强烈注意">2.4.4 强烈注意</span></h3><p>1)     组件内置的方法中的this为组件对象</p><p>2)     在组件类中自定义的方法中this为null</p><p>a.     强制绑定this: 通过函数对象的bind()</p><p>b.     箭头函数(ES6模块化编码时才能使用)</p><h3><span id="245-代码">2.4.5 代码</span></h3><pre><code class="html">&lt;div id=&quot;example&quot;&gt;&lt;/div&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react-dom.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/babel.min.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/babel&quot;&gt;  /*  需求: 自定义组件, 功能说明如下:    1. 界面如果页面所示    2. 点击按钮, 提示第一个输入框中的值    3. 当第2个输入框失去焦点时, 提示这个输入框中的值 */  // 1、定义组件 // React中必须要结束标签  class MyComponent extends React.Component{    constructor(props){      super(props)      this.showInput = this.showInput.bind(this)      this.handleBlur = this.handleBlur.bind(this)    }    showInput(){      const input = this.refs.content      // alert(input.value)      alert(this.input.value)    }    handleBlur(event){      alert(event.target.value)    }    render(){      return(        &lt;div&gt;          &lt;input type=&quot;text&quot; ref=&quot;content&quot;/&gt;&amp;nbsp;&amp;nbsp;          &lt;input type=&quot;text&quot; ref={input =&gt; this.input = input}/&gt;&amp;nbsp;&amp;nbsp;          &lt;button onClick={this.showInput}&gt;提示输入&lt;/button&gt;&amp;nbsp;&amp;nbsp;          &lt;input type=&quot;text&quot; placeholder=&quot;失去焦点提示内容&quot; onBlur={this.handleBlur}/&gt;        &lt;/div&gt;      )    }  }  // 2、渲染组件标签  ReactDOM.render(&lt;MyComponent/&gt;, document.getElementById(&#39;example&#39;))&lt;/script&gt;</code></pre><h3><span id="246-区别">2.4.6 区别</span></h3><ol><li>state</li><li>props</li><li>refs</li></ol><h2><span id="25-组件的组合">2.5 组件的组合</span></h2><h3><span id="251-效果">2.5.1 效果</span></h3><pre><code>功能: 组件化实现此功能  1. 显示所有todo列表  2. 输入文本, 点击按钮显示到列表的首位, 并清除输入的文本</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/component%E7%BB%84%E5%90%88%E4%BD%BF%E7%94%A8.gif" alt="component组合使用"></p><h3><span id="252-功能界面的组件化编码流程无比重要">2.5.2 功能界面的组件化编码流程(无比重要)</span></h3><p>1)     拆分组件: 拆分界面,抽取组件</p><p>2)     实现静态组件: 使用组件实现静态页面效果(只有静态界面，没有动态数据和交互)</p><p>3)     实现动态组件</p><ul><li><p>实现初始化数据动态显示</p></li><li><p>实现交互功能(从绑定事件监听开始)</p></li></ul><h3><span id="253-代码">2.5.3 代码</span></h3><pre><code class="html">&lt;div id=&quot;example&quot;&gt;&lt;/div&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react-dom.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/prop-types.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/babel.min.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/babel&quot;&gt;  // 静态组件——&gt;动态组件  /*  * -名称、类型：todos、数组，Add需要、List需要  * -数据保存在哪个组件内？  *   放到App  *   看数据是某个组件需要(给这个)，还是某些组件需要(给共同的父组件)  * -需要在子组件中改变父组件的状态  *    子组件中不能直接改变父组件的状态  *    状态在哪个组件，更新状态的行为就应该定义在哪个组件，由子组件来调用(通过组件属性传递)  *    父组件定义函数，传递给子组件，子组件调用  */  class App extends React.Component{    constructor(props){      super(props)      // 初始化状态      this.state = {        todos: [&#39;吃饭&#39;, &#39;睡觉&#39;, &#39;敲代码&#39;, &#39;打游戏&#39;]      }      this.addTodo = this.addTodo.bind(this)  // 没定义加上bind    }    addTodo(todo){      // this.state.todos.unshift(todo) // 不能这样做      const {todos} = this.state      todos.unshift(todo)      // 更新状态      this.setState({todos})    }    render(){      const {todos} = this.state      return(        &lt;div&gt;          &lt;h1&gt;Simple TODO List&lt;/h1&gt;          &lt;Add count={todos.length} addTodo={this.addTodo}/&gt;          &lt;List todos={todos}/&gt;        &lt;/div&gt;      )    //  &lt;List todos={this.state.todos}/&gt; 前面赋值了    }  }  class Add extends React.Component{    constructor(props){      super(props)      this.add = this.add.bind(this)    }    add(){      // 1、读取输入的数据      const todo = this.todoInput.value.trim()      // 2、检查合法性      if(!todo){        return      }      // 3、添加      this.props.addTodo(todo)      // 4、清除输入      this.todoInput.value = &#39;&#39;    }    render(){      return(        &lt;div&gt;          &lt;input type=&quot;text&quot; ref={input =&gt; this.todoInput=input}/&gt;          &lt;button onClick={this.add}&gt;add #{this.props.count+1}&lt;/button&gt;        &lt;/div&gt;      )    }  }  Add.propTypes = {    count: PropTypes.number.isRequired,    addTodo: PropTypes.func.isRequired  }  class List extends React.Component{    render(){      return(        &lt;ul&gt;          {            this.props.todos.map((todo, index) =&gt; &lt;li key={index}&gt;{todo}&lt;/li&gt;)          }        &lt;/ul&gt;      )      /*this.props.todos.map((todo, index) =&gt; {return &lt;li key={index}&gt;{todo}&lt;/li&gt;})*/      /* 加了大括号需要加return */    }  }  List.protoTypes = {    todos: PropTypes.array.isRequired  }  ReactDOM.render(&lt;App /&gt;,document.getElementById(&#39;example&#39;))&lt;/script&gt;</code></pre><h2><span id="26-收集表单数据">2.6 收集表单数据</span></h2><h3><span id="261-效果">2.6.1 效果</span></h3><pre><code>需求: 自定义包含表单的组件  1. 输入用户名密码后, 点击登陆提示输入信息  3. 不提交表单</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/component%E8%A1%A8%E5%8D%95.gif" alt="component表单 (2)"></p><h3><span id="262-理解">2.6.2 理解</span></h3><p>1)     问题: 在react应用中, 如何收集表单输入数据</p><p>2)     包含表单的组件分类</p><p>a.     受控组件: 表单项输入数据能自动收集成状态</p><p>b.     非受控组件: 需要时才手动读取表单输入框中的数据</p><h3><span id="263-代码">2.6.3 代码</span></h3><pre><code class="html">&lt;div id=&quot;example&quot;&gt;&lt;/div&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react-dom.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/babel.min.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/babel&quot;&gt;  /*  需求: 自定义包含表单的组件    1. 界面如下所示    2. 输入用户名密码后, 点击登陆提示输入信息    3. 不提交表单  */  class LoginForm extends React.Component{    constructor(props){      super(props)      // 初始化状态      this.state  = {        pwd: &#39;&#39;      }      this.handleSubmit = this.handleSubmit.bind(this)      this.handleChange = this.handleChange.bind(this)    }    handleSubmit(event){      const name = this.nameInput.value // 非受控组件      const {pwd} = this.state // 受控组件      alert(`准备提交的用户名为${name}，密码为${pwd}`)      // 阻止事件的默认行为      event.preventDefault()    }    // 当事件和标签是同一个时，使用event更方便    handleChange(event){      // 读取输入的值      const pwd = event.target.value      // 更新pwd的状态      this.setState({pwd})    }    render(){      return(        &lt;form action=&quot;/test&quot; onSubmit={this.handleSubmit}&gt;          用户名：&lt;input type=&quot;text&quot; ref={input =&gt; this.nameInput = input}/&gt;          密码：&lt;input type=&quot;password&quot; value={this.state.pwd} onChange={this.handleChange}/&gt;          &lt;input type=&quot;submit&quot; value=&quot;登录&quot;/&gt;        &lt;/form&gt;      )    }    // 原生jsonChange事件是在失去焦点时触发，react中是输入则触发  }  ReactDOM.render(&lt;LoginForm /&gt;, document.getElementById(&#39;example&#39;))&lt;/script&gt;</code></pre><h2><span id="27-组件生命周期">2.7 组件生命周期</span></h2><h3><span id="271-效果">2.7.1 效果</span></h3><pre><code>需求: 自定义组件  1. 让指定的文本做显示/隐藏的渐变动画  2. 切换持续时间为2S  3. 点击按钮从界面中移除组件界面</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/component%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.gif" alt="component生命周期"></p><h3><span id="272-理解">2.7.2 理解</span></h3><p>1)     组件对象从创建到死亡它会经历特定的生命周期阶段</p><p>2)     React组件对象包含一系列的勾子函数(生命周期回调函数), 在生命周期特定时刻回调</p><p>3)     我们在定义组件时, 可以重写特定的生命周期回调函数, 做特定的工作</p><h3><span id="273-生命周期流程图">2.7.3 生命周期流程图</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/reactlife.png" alt="生命周期流程图"></p><p>Mount：挂载，将虚拟标签放到容器(页面)中</p><p>render()渲染</p><p>左边初始化过程，这些方法称为声明周期回调函数，或称为生命周期的勾子，这些方法在特定的时刻调用</p><p>(回调函数：你定义的，你没有调用，但是最终执行了；声明式编程,流程设定好,、命令式编程jQuery,每一步自己操作)</p><p>will将、did完成</p><h3><span id="274-生命周期详述">2.7.4 生命周期详述</span></h3><p>1)     组件的三个生命周期状态:</p><ul><li><p>Mount：插入真实 DOM</p></li><li><p>Update：被重新渲染</p></li><li><p>Unmount：被移出真实 DOM</p></li></ul><p>2)     React 为每个状态都提供了勾子(hook)函数，可重写</p><ul><li><p><code>componentWillMount()</code></p></li><li><p><code>componentDidMount()</code></p></li><li><p><code>componentWillUpdate()</code></p></li><li><p><code>componentDidUpdate()</code></p></li><li><p><code>componentWillUnmount()</code></p></li></ul><p>3)    生命周期流程:</p><p>a.     第一次初始化渲染显示: ReactDOM.render()</p><ul><li><p>constructor(): 创建对象初始化state</p></li><li><p>componentWillMount() : 将要插入回调</p></li><li><p>render() : 用于插入虚拟DOM回调</p></li><li><p>componentDidMount() : 已经插入回调</p></li></ul><p>b.     每次更新state: this.setSate()</p><ul><li><p>componentWillUpdate() : 将要更新回调</p></li><li><p>render() : 更新(重新渲染)</p></li><li><p>componentDidUpdate() : 已经更新回调</p></li></ul><p>c.     移除组件: ReactDOM.unmountComponentAtNode(containerDom)</p><ul><li>componentWillUnmount() : 组件将要被移除回调</li></ul><p>三个阶段，可以都打印一下，看下方法执行的过程(与写的顺序无关)</p><h3><span id="275-重要的勾子">2.7.5 重要的勾子</span></h3><p>1)     render(): 初始化渲染或更新渲染调用</p><p>2)     componentDidMount(): 开启监听, 发送ajax请求</p><p>3)     componentWillUnmount(): 做一些收尾工作, 如: 清理定时器</p><p>4)     componentWillReceiveProps(): 后面需要时讲</p><h3><span id="276-代码">2.7.6 代码</span></h3><pre><code class="html">&lt;div id=&quot;example&quot;&gt;&lt;/div&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react-dom.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/babel.min.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/babel&quot;&gt;  /*  需求: 自定义组件    1. 让指定的文本做显示/隐藏的动画    2. 切换时间为2S    3. 点击按钮从界面中移除组件界面   */  class Life extends React.Component{    constructor(props){      super(props)      // 初始化状态      this.state = {        opacity: 1      }      this.distroyComponent = this.distroyComponent.bind(this)    }    distroyComponent(){      ReactDOM.unmountComponentAtNode(document.getElementById(&#39;example&#39;))    }    // 重写方法    componentDidMount(){      // 启动循环定时器      this.intervalId = setInterval(function () { // 两个函数需要同一个变量，放到上一层共同组件上        console.log(&#39;定时器执行……&#39;)        let {opacity} = this.state        opacity -= 0.1        if(opacity&lt;=0){          opacity = 1        }        // 更新状态        this.setState({opacity})      }.bind(this), 200) // componentDidMount的this    }    componentWillUnmount(){      // 清理定时器      clearInterval(this.intervalId)    }    render(){ // 一旦改变，就会重新调用;永远写在其他下方，构造器在最上方      const {opacity} = this.state      return(        &lt;div&gt;          &lt;h2 style={{opacity: opacity}}&gt;{this.props.msg}&lt;/h2&gt;          &lt;button onClick={this.distroyComponent}&gt;不活了&lt;/button&gt;        &lt;/div&gt;      )    }// style中两个大括号，外面的代表写的是js代码，里面的是对象（样式名：值，也可以写ES6）  }  ReactDOM.render(&lt;Life msg=&quot;react太难了&quot;/&gt;, document.getElementById(&#39;example&#39;))&lt;/script&gt;</code></pre><h2><span id="28-虚拟dom与dom-diff算法">2.8 虚拟DOM与DOM Diff算法</span></h2><p>虚拟DOM：减少操作真实DOM的次数，更新界面次数变少</p><p>DOM Diff算法：计算哪里需要更新，哪里不需要更新，减少更新界面的区域</p><p>共同提高更新界面的效率</p><h3><span id="281-效果">2.8.1 效果</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/vDOM.gif" alt="component虚拟DOM"></p><pre><code class="html">&lt;div id=&quot;example&quot;&gt;&lt;/div&gt;&lt;br&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react-dom.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/babel.min.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/babel&quot;&gt;  /*  验证:  虚拟DOM+DOM Diff算法: 最小化页面重绘  */  class HelloWorld extends React.Component {    constructor(props) {      super(props)      this.state = {          date: new Date()      }    }    componentDidMount () {      setInterval(() =&gt; {        this.setState({            date: new Date()        })      }, 1000)    }    render () {      console.log(&#39;render()&#39;)      return (        &lt;p&gt;          Hello, &lt;input type=&quot;text&quot; placeholder=&quot;Your name here&quot;/&gt;!&amp;nbsp;          &lt;span&gt;It is {this.state.date.toTimeString()}&lt;/span&gt;        &lt;/p&gt;      )    }  }  ReactDOM.render(    &lt;HelloWorld/&gt;,    document.getElementById(&#39;example&#39;)  )&lt;/script&gt;</code></pre><p> 只有时间更新，其他不更新</p><h3><span id="282-基本原理图">2.8.2 基本原理图</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/react-5.png" alt="基本原理"></p><ul><li><p>初始化：虚拟DOM树(div&gt;p&gt;span……)，更新虚拟DOM界面不会变——&gt;更新真实DOM界面才会变化(更新状态)</p></li><li><p>更新(关键)：调用setState()更新状态(会进行对比)——&gt;根据差异更新真实DOM、重绘页面变化的区域</p></li></ul><h1><span id="第3章-react应用基于react脚手架">第3章 react应用(基于react脚手架)</span></h1><h2><span id="31-使用create-react-app创建react应用">3.1 使用create-react-app创建react应用</span></h2><h3><span id="311-react脚手架">3.1.1 react脚手架</span></h3><p>1)     xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目</p><ul><li><p>包含了所有需要的配置</p></li><li><p>指定好了所有的依赖</p></li><li><p>可以直接安装/编译/运行一个简单效果</p></li></ul><p>2)     react提供了一个用于创建react项目的脚手架库: create-react-app</p><p>3)     项目的整体技术架构为: react + webpack + es6 + eslint</p><p>4)     使用脚手架开发的项目的特点: 模块化、组件化、工程化</p><h3><span id="312-创建项目并启动">3.1.2 创建项目并启动</span></h3><p><code>npm install -g create-react-app</code> 全局下载</p><p><code>create-react-app hello-react</code></p><p><code>cd hello-react</code></p><p><code>npm start</code></p><p>浏览器访问<a href="http://localhost:3000" target="_blank" rel="noopener">http://localhost:3000</a></p><p>注：</p><p><code>npm root -g</code> 查看全局下载目录<br>C:\Users\Shinelon\AppData\Roaming\npm\node_modules</p><h3><span id="313-react脚手架项目结构">3.1.3 react脚手架项目结构</span></h3><pre><code>ReactNews |--node_modules---第三方依赖模块文件夹 |--public   |-- index.html-----------------主页面 |--scripts   |-- build.js-------------------build打包引用配置   |-- start.js-------------------start运行引用配置 |--src------------源码文件夹   |--components-----------------react组件      |-- app.jsx   |--index.css   |--index.js-------------------应用入口js(main.js) |--.gitignore------git版本管制忽略的配置 |--package.json----应用包配置文件  |--README.md-------应用描述说明的readme文件</code></pre><p> <code>package.json</code></p><ul><li><code>&quot;dependencies&quot;</code>：运行时依赖</li><li><code>&quot;devDependencies&quot;</code>：开发时依赖，编译打包时需要，开发时不需要，编译打包时工具包</li></ul><p><code>public/index.html</code>主界面</p><ul><li>只有一个<code>&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;</code>，依靠组件</li></ul><p><code>src/index.js</code>应用入口</p><ul><li>引入包(<code>import * from &quot;*&quot;</code>)、CSS(<code>import &quot;*.css&quot;</code>)</li><li>渲染组件</li></ul><p><code>README.md</code>对项目的说明文件</p><p>SPA(Single Page Application)：单应用</p><ul><li>index.html</li></ul><pre><code class="html">&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;</code></pre><ul><li><code>src/components/app.jsx</code></li></ul><pre><code class="jsx">import React, {Component} from &#39;react&#39;import logo from &#39;../logo.svg&#39;export default class App extends Component {  render() {    return(      &lt;div&gt;        &lt;img className=&#39;logo&#39; src={logo} alt=&quot;logo&quot;/&gt;        &lt;p className=&quot;title&quot;&gt;react组件&lt;/p&gt;      &lt;/div&gt;    )  }}</code></pre><ul><li><code>src/index.js</code></li></ul><pre><code class="javascript">import React from &#39;react&#39;import ReactDOM from &#39;react-dom&#39;import App from &#39;./components/app&#39;import &#39;./index.css&#39;ReactDOM.render(&lt;App /&gt;, document.getElementById(&#39;root&#39;))</code></pre><ul><li><code>src/index.css</code></li></ul><pre><code class="css">.logo{  width: 200px;  height: 200px;}.title{  color: red;  font-size: 25px;}</code></pre><p><code>cd react_app</code><br><code>npm start</code>或<code>npm run start</code></p><h2><span id="32-demo-评论管理">3.2 demo: 评论管理</span></h2><h3><span id="321-效果">3.2.1 效果</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/demo_comment.gif" alt="demo_comment"></p><h3><span id="322-拆分组件">3.2.2 拆分组件</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200708170522553.png" alt="image-20200708170522553"></p><p>应用组件: App</p><ul><li>state: comments/array</li></ul><p>添加评论组件: CommentAdd</p><ul><li><p>state: username/string, content/string</p></li><li><p>props: add/func</p></li></ul><p>评论列表组件: CommentList</p><ul><li>props: comment/object, delete/func, index/number</li></ul><p>评论项组件: CommentItem</p><ul><li>props: comments/array, delete/func</li></ul><h3><span id="323-实现静态组件">3.2.3 实现静态组件</span></h3><p>render(){return}中的内容</p><ul><li><code>src/index.js</code></li></ul><pre><code class="javascript">import React from &#39;react&#39;import ReactDOM from &#39;react-dom&#39;import App from &#39;./components/app/app&#39;ReactDOM.render(&lt;App/&gt;, document.getElementById(&#39;root&#39;))</code></pre><ul><li><code>src/components/app/app.jsx</code></li></ul><pre><code class="jsx">import React from &#39;react&#39;import CommentAdd from &#39;../comment-add/comment-add&#39;import CommentList from &#39;../comment-list/comment-list&#39;export default class App extends React.Component {  constructor (props) {    super(props)    this.state = {      comments: []    }    this.delete = this.delete.bind(this)  }  componentDidMount () {    //模拟异步获取数据    setTimeout(() =&gt; {      const comments = [        {          username: &quot;Tom&quot;,          content: &quot;ReactJS好难啊!&quot;,          id: Date.now()        },        {          username: &quot;JACK&quot;,          content: &quot;ReactJS还不错!&quot;,          id: Date.now() + 1        }      ]      this.setState({        comments      })    }, 1000)  }  add = (comment) =&gt; {    let comments = this.state.comments    comments.unshift(comment)    this.setState({ comments })  }  delete (index) {    let comments = this.state.comments    comments.splice(index, 1)    this.setState({ comments })  }  render () {    return (      &lt;div&gt;        &lt;header className=&quot;site-header jumbotron&quot;&gt;          &lt;div className=&quot;container&quot;&gt;            &lt;div className=&quot;row&quot;&gt;              &lt;div className=&quot;col-xs-12&quot;&gt;                &lt;h1&gt;请发表对React的评论&lt;/h1&gt;              &lt;/div&gt;            &lt;/div&gt;          &lt;/div&gt;        &lt;/header&gt;        &lt;div className=&quot;container&quot;&gt;          &lt;CommentAdd add={this.add}/&gt;          &lt;CommentList comments={this.state.comments} delete={this.delete}/&gt;        &lt;/div&gt;      &lt;/div&gt;    )  }}// export default App 可以写到上面</code></pre><ul><li><code>src/components/comment-add/comment-add.jsx</code></li></ul><pre><code class="jsx">import React from &#39;react&#39;import PropTypes from &#39;prop-types&#39;class CommentAdd extends React.Component {  constructor (props) {    super(props)    this.state = {      username: &#39;&#39;,      content: &#39;&#39;    }    this.addComment = this.addComment.bind(this)    this.changeUsername = this.changeUsername.bind(this)    this.changeContent = this.changeContent.bind(this)  }  addComment () {    // 根据输入的数据创建评论对象    let { username, content } = this.state    let comment = { username, content }    // 添加到comments中, 更新state    this.props.add(comment)    // 清除输入的数据    this.setState({      username: &#39;&#39;,      content: &#39;&#39;    })  }  changeUsername (event) {    this.setState({      username: event.target.value    })  }  changeContent (event) {    this.setState({      content: event.target.value    })  }  render () {    return (      &lt;div className=&quot;col-md-4&quot;&gt;        &lt;form className=&quot;form-horizontal&quot;&gt;          &lt;div className=&quot;form-group&quot;&gt;            &lt;label&gt;用户名&lt;/label&gt;            &lt;input type=&quot;text&quot; className=&quot;form-control&quot; placeholder=&quot;用户名&quot;                   value={this.state.username} onChange={this.changeUsername}/&gt;          &lt;/div&gt;          &lt;div className=&quot;form-group&quot;&gt;            &lt;label&gt;评论内容&lt;/label&gt;            &lt;textarea className=&quot;form-control&quot; rows=&quot;6&quot; placeholder=&quot;评论内容&quot;                      value={this.state.content} onChange={this.changeContent}&gt;&lt;/textarea&gt;          &lt;/div&gt;          &lt;div className=&quot;form-group&quot;&gt;            &lt;div className=&quot;col-sm-offset-2 col-sm-10&quot;&gt;              &lt;button type=&quot;button&quot; className=&quot;btn btn-default pull-right&quot; onClick={this.addComment}&gt;提交&lt;/button&gt;            &lt;/div&gt;          &lt;/div&gt;        &lt;/form&gt;      &lt;/div&gt;    )  }}CommentAdd.propTypes = {  add: PropTypes.func.isRequired}export default CommentAdd</code></pre><ul><li><code>src/components/comment-list/comment-list.css</code></li></ul><pre><code class="css">.reply {  margin-top: 0px;}</code></pre><ul><li><code>src/components/comment-list/comment-list.jsx</code></li></ul><pre><code class="jsx">import React from &#39;react&#39;import PropTypes from &#39;prop-types&#39;import CommentItem from &#39;../comment-item/comment-item&#39;import &#39;./commentList.css&#39;class CommentList extends React.Component {  constructor (props) {    super(props)  }  render () {    let comments = this.props.comments    let display = comments.length &gt; 0 ? &#39;none&#39; : &#39;block&#39;</code></pre><p>下面h2中由于用了双大括号，一直报错，因此请直接到github看源码</p><pre><code class="jsx">    return (      &lt;div className=&quot;col-md-8&quot;&gt;        &lt;h3 className=&quot;reply&quot;&gt;评论回复：&lt;/h3&gt;        &lt;h2 style=双大括号 display: display 双大括号&gt;暂无评论，点击左侧添加评论&lt;/h2&gt;        &lt;ul className=&quot;list-group&quot;&gt;          {            comments.map((comment, index) =&gt; {              console.log(comment)              return &lt;CommentItem comment={comment} key={index} index={index} delete={this.props.delete}/&gt;            })          }        &lt;/ul&gt;      &lt;/div&gt;    )    }}CommentList.propTypes = {  comments: PropTypes.array.isRequired,  delete: PropTypes.func.isRequired}export default CommentList</code></pre><ul><li><code>src/components/comment-item/comment-item.css</code></li></ul><pre><code class="css">li {  transition: .5s;  overflow: hidden;}.handle {  width: 40px;  border: 1px solid #ccc;  background: #fff;  position: absolute;  right: 10px;  top: 1px;  text-align: center;}.handle a {  display: block;  text-decoration: none;}.list-group-item .centence {  padding: 0px 50px;}.user {  font-size: 22px;}</code></pre><ul><li><code>src/components/comment-item/comment-item.jsx</code></li></ul><pre><code class="jsx">import React from &#39;react&#39;import PropTypes from &#39;prop-types&#39;import &#39;./commentItem.css&#39;class CommentItem extends React.Component {  constructor (props) {    super(props)    this.deleteComment = this.deleteComment.bind(this)  }  deleteComment () {    let username = this.props.comment.username    if (window.confirm(`确定删除${username}的评论吗?`)) {      this.props.delete(this.props.index)    }  }  render () {    let comment = this.props.comment    return (      &lt;li className=&quot;list-group-item&quot;&gt;        &lt;div className=&quot;handle&quot;&gt;          &lt;a href=&quot;javascript:&quot; onClick={this.deleteComment}&gt;删除&lt;/a&gt;        &lt;/div&gt;        &lt;p className=&quot;user&quot;&gt;&lt;span &gt;{comment.username}&lt;/span&gt;&lt;span&gt;说:&lt;/span&gt;&lt;/p&gt;        &lt;p className=&quot;centence&quot;&gt;{comment.content}&lt;/p&gt;      &lt;/li&gt;    )  }}CommentItem.propTypes = {  comment: PropTypes.object.isRequired,  index: PropTypes.number.isRequired,  delete: PropTypes.func.isRequired}export default CommentItem</code></pre><h3><span id="324-实现动态组件">3.2.4 实现动态组件</span></h3><p>动态展示初始化数据</p><ul><li><p>初始化状态数据</p></li><li><p>传递属性数据</p></li></ul><p>响应用户操作, 更新组件界面</p><ul><li><p>绑定事件监听, 并处理</p></li><li><p>更新state</p></li></ul><h1><span id="第4章-react-ajax">第4章 react ajax</span></h1><h2><span id="41-理解">4.1 理解</span></h2><h3><span id="411-前置说明">4.1.1 前置说明</span></h3><p>1)     React本身只关注于界面, 并不包含发送ajax请求的代码</p><p>2)     前端应用需要通过ajax请求与后台进行交互(json数据)</p><p>3)     react应用中需要集成第三方ajax库(或自己封装)</p><h3><span id="412-常用的ajax请求库">4.1.2 常用的ajax请求库</span></h3><p>1)     jQuery: 比较重, 如果需要另外引入不建议使用</p><p>2)     <strong>axios</strong>: 轻量级, 建议使用</p><p>​    a.     封装XmlHttpRequest对象的ajax</p><p>​    b.     promise风格</p><p>​    c.     可以用在浏览器端和node服务器端</p><p>3)     fetch: 原生函数, 但老版本浏览器不支持</p><p>​    a.     不再使用XmlHttpRequest对象提交ajax请求</p><p>​    b.     为了兼容低版本的浏览器, 可以引入兼容库fetch.js</p><h3><span id="413-效果">4.1.3 效果</span></h3><pre><code>需求:  1. 界面效果如下  2. 根据指定的关键字在github上搜索匹配的最受关注的库  3. 显示库名, 点击链接查看库4. 测试接口: https://api.github.com/search/repositories?q=r&amp;sort=stars</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/ajax.gif" alt="ajax"></p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;head&gt;  &lt;meta charset=&quot;UTF-8&quot;&gt;  &lt;title&gt;11_ajax&lt;/title&gt;  &lt;style&gt;    h2{      text-align: center;      margin-top: 200px;    }  &lt;/style&gt;&lt;/head&gt;&lt;body&gt;&lt;div id=&quot;example&quot;&gt;&lt;/div&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/react-dom.development.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;../js/babel.min.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/babel&quot;&gt;  /*  需求:    1. 界面效果如下    2. 根据指定的关键字在github上搜索匹配的最受关注的库    3. 显示库名, 点击链接查看库    4. 测试接口: https://api.github.com/search/repositories?q=r&amp;sort=stars  */  class MostStarRepo extends React.Component{    state = {      repoName: &#39;&#39;,      repoUrl: &#39;&#39;    }    componentDidMount(){      // 使用axios发送异步的ajax请求      const url = `https://api.github.com/search/repositories?q=re&amp;sort=stars`      axios.get(url)          .then(response =&gt; {            const result = response.data            // console.log(response)            // 得到数据            const {name, html_url} = result.items[0]            // 更新状态            this.setState({repoName:name, repoUrl: html_url})          })          .catch((error) =&gt; {            alert(error.message)          })      // 使用fetch发送异步的ajax请求      /*fetch(url)          .then(response =&gt; {            return response.json()          })          .then(data =&gt; {            // 得到数据            const {name, html_url} = data.items[0]            // 更新状态            this.setState({repoName:name, repoUrl: html_url})          })      */    }    render(){      const {repoName, repoUrl} = this.state      if(!repoName){        return(          &lt;h2&gt;LOADING...&lt;/h2&gt;        )      }else{        return(          &lt;h2&gt;Most star repo is &lt;a href={repoUrl}&gt;{repoName}&lt;/a&gt;&lt;/h2&gt;        )      }    }  }  ReactDOM.render(&lt;MostStarRepo /&gt;, document.getElementById(&#39;example&#39;))&lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><h2><span id="42-axios">4.2 axios</span></h2><h3><span id="421-文档">4.2.1 文档</span></h3><p><a href="https://github.com/axios/axios" target="_blank" rel="noopener">https://github.com/axios/axios</a></p><h3><span id="422-相关api">4.2.2 相关API</span></h3><p>1)     GET请求</p><pre><code class="javascript">axios.get(&#39;/user?ID=12345&#39;)  .then(function (response) {    console.log(response);  })  .catch(function (error) {    console.log(error);  });axios.get(&#39;/user&#39;, {    params: {      ID: 12345    }  })  .then(function (response) {    console.log(response);  })  .catch(function (error) {    console.log(error);  });</code></pre><p>2)     POST请求</p><pre><code class="javascript">axios.post(&#39;/user&#39;, {    firstName: &#39;Fred&#39;,axios.post(&#39;/user&#39;, {    firstName: &#39;Fred&#39;,    lastName: &#39;Flintstone&#39;}).then(function (response) {  console.log(response);}).catch(function (error) {  console.log(error);});</code></pre><h2><span id="43-fetch">4.3 Fetch</span></h2><h3><span id="431-文档">4.3.1 文档</span></h3><p>1)     <a href="https://github.github.io/fetch/" target="_blank" rel="noopener">https://github.github.io/fetch/</a></p><p>2)     <a href="https://segmentfault.com/a/1190000003810652" target="_blank" rel="noopener">https://segmentfault.com/a/1190000003810652</a></p><h3><span id="432-相关api">4.3.2 相关API</span></h3><p>1)     GET请求</p><pre><code class="javascript">fetch(url).then(function(response) {  return response.json()}).then(function(data) {  console.log(data)}).catch(function(e) {  console.log(e)});</code></pre><p>2)     POST请求</p><pre><code class="javascript">fetch(url, {  method: &quot;POST&quot;,  body: JSON.stringify(data),}).then(function(data) {  console.log(data)}).catch(function(e) {  console.log(e)})</code></pre><h2><span id="44-demo-github-users">4.4 demo: github users</span></h2><h3><span id="441-效果">4.4.1 效果</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/demo_users.gif" alt="demo_users"></p><h3><span id="442-拆分组件">4.4.2 拆分组件</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200711091558440.png" alt="image-20200711091558440"></p><p>App</p><p>​       * state: searchName/string</p><p>  Search</p><p>​      * props: setSearchName/func</p><p>  List</p><p>​      * props: searchName/string</p><p>​      * state: firstView/bool, loading/bool, users/array, errMsg/string</p><h3><span id="443-编写静态组件">4.4.3 编写静态组件</span></h3><h3><span id="444-编写动态组件">4.4.4 编写动态组件</span></h3><p>componentWillReceiveProps(nextProps): 监视接收到新的props, 发送ajax</p><p>​     使用axios库发送ajax请求</p><ul><li><code>public/index.html</code></li></ul><pre><code class="html">&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;</code></pre><ul><li><code>src/index.js</code></li></ul><pre><code class="javascript">import React from &#39;react&#39;import { render } from &#39;react-dom&#39;import App from &#39;./components/app&#39;import &#39;./index.css&#39;render(&lt;App /&gt;, document.getElementById(&#39;root&#39;))</code></pre><ul><li><code>src/index.css</code></li></ul><pre><code class="css">.album {  min-height: 50rem; /* Can be removed; just added for demo purposes */  padding-top: 3rem;  padding-bottom: 3rem;  background-color: #f7f7f7;}.card {  float: left;  width: 33.333%;  padding: .75rem;  margin-bottom: 2rem;  border: 1px solid #efefef;  text-align: center;}.card &gt; img {  margin-bottom: .75rem;  border-radius: 100px;}.card-text {  font-size: 85%;}</code></pre><ul><li><code>src/componets/app.jsx</code></li></ul><pre><code class="jsx">import React from &#39;react&#39;import Search from &#39;./search&#39;import UserList from &#39;./user-list&#39;export default class App extends React.Component {  state = {    searchName: &#39;&#39;  }  refreshName = (searchName) =&gt; this.setState({searchName})  render() {    return (      &lt;div className=&quot;container&quot;&gt;        &lt;section className=&quot;jumbotron&quot;&gt;          &lt;h3 className=&quot;jumbotron-heading&quot;&gt;Search Github Users&lt;/h3&gt;          &lt;Search refreshName={this.refreshName}/&gt;        &lt;/section&gt;        &lt;UserList searchName={this.state.searchName}/&gt;      &lt;/div&gt;    )  }}</code></pre><ul><li><code>src/componets/search.jsx</code></li></ul><pre><code class="jsx">/** * 上部的搜索模块 */import React, {Component} from &#39;react&#39;import PropTypes from &#39;prop-types&#39;class Search extends Component {  static propTypes = {    refreshName: PropTypes.func.isRequired  }  search = () =&gt; {    var name = this.nameInput.value    this.props.refreshName(name)  }  render() {    return (      &lt;div&gt;        &lt;input type=&quot;text&quot; placeholder=&quot;enter the name you search&quot;               ref={(input =&gt; this.nameInput = input)}/&gt;        &lt;button onClick={this.search}&gt;Search&lt;/button&gt;      &lt;/div&gt;    )  }}export default Search</code></pre><ul><li><code>src/componets/user-list.jsx</code></li></ul><pre><code class="jsx">/** * 下部的用户列表模块 */import React from &#39;react&#39;import PropTypes from &#39;prop-types&#39;import axios from &#39;axios&#39;// npm install axios --save class UserList extends React.Component {  static propTypes = {    searchName: PropTypes.string.isRequired  }  state = {    firstView: true,    loading: false,    users: null,    error: null  }  async componentWillReceiveProps(nextProps)  {    let searchName = nextProps.searchName    console.log(&#39;发送ajax请求&#39;, searchName)    const url = `https://api.github.com/search/users?q=${searchName}`    this.setState({ firstView: false, loading: true })    // 使用axios库    axios.get(url)      .then((response) =&gt; {        console.log(response)        this.setState({ loading: false, users: response.data.items })      })      .catch((error)=&gt;{        // debugger        console.log(&#39;error&#39;, error.response.data.message, error.message)        this.setState({ loading: false, error: error.message })      })    try {      const result = await axios.get(url)      this.setState({ loading: false, users: result.data.items })    } catch(err) {      // debugger      console.log(&#39;----&#39;, err.message)    }  }  render () {    if (this.state.firstView) {      return &lt;h2&gt;Enter name to search&lt;/h2&gt;    } else if (this.state.loading) {      return &lt;h2&gt;Loading result...&lt;/h2&gt;    } else if (this.state.error) {      return &lt;h2&gt;{this.state.error}&lt;/h2&gt;    } else {      return (        &lt;div className=&quot;row&quot;&gt;          {            this.state.users.map((user) =&gt; (              &lt;div className=&quot;card&quot; key={user.html_url}&gt;                &lt;a href={user.html_url} target=&quot;_blank&quot;&gt;                  &lt;img src={user.avatar_url} style={{width: '100px'}} alt=&#39;user&#39;/&gt;                &lt;/a&gt;                &lt;p className=&quot;card-text&quot;&gt;{user.login}&lt;/p&gt;              &lt;/div&gt;            ))          }        &lt;/div&gt;      )    }  }}export default UserList</code></pre><h1><span id="第5章-几个重要技术总结">第5章 几个重要技术总结</span></h1><h2><span id="51-组件间通信">5.1 组件间通信</span></h2><h3><span id="511-方式一-通过props传递">5.1.1 方式一: 通过props传递</span></h3><p>1)     共同的数据放在父组件上, 特有的数据放在自己组件内部(state)</p><p>2)     通过props可以传递一般数据和函数数据, 只能一层一层传递</p><p>3)     一般数据–&gt;父组件传递数据给子组件–&gt;子组件读取数据</p><p>4)     函数数据–&gt;子组件传递数据给父组件–&gt;子组件调用函数</p><p>父组件传到孙组件、兄弟组件之间不能直接通信，经过子组件、服务器传递</p><h3><span id="512-方式二-使用消息订阅subscribe-发布publish机制">5.1.2 方式二: 使用消息订阅(subscribe)-发布(publish)机制</span></h3><p>1)     工具库: PubSubJS</p><p>2)     下载: <code>npm install pubsub-js --save</code></p><p>3)     使用: </p><pre><code class="javascript">import PubSub from &#39;pubsub-js&#39; //引入PubSub.publish(&#39;delete&#39;, data) //发布消息// 消息名，消息PubSub.subscribe(&#39;delete&#39;, function(msg, data){ }); //订阅// 消息名，回调函数</code></pre><p>以上一个demo为例(用户搜索的)，search和userlist(main)之间需要通信，它们是兄弟组件</p><p>在这里是通过父组件，利用props进行通信</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200714161016458.png" alt="props-search"></p><p>这是以前的app.ejs</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200714161302225.png" alt="app.ejs"></p><p>现在不通过父组件来通信</p><p><code>src/components/app.ejs</code></p><pre><code class="ejs">import React from &#39;react&#39;import Search from &#39;./search&#39;import UserList from &#39;./user-list&#39;export default class App extends React.Component {  render() {    return (      &lt;div className=&quot;container&quot;&gt;        &lt;section className=&quot;jumbotron&quot;&gt;          &lt;h3 className=&quot;jumbotron-heading&quot;&gt;Search Github Users&lt;/h3&gt;          &lt;Search/&gt;        &lt;/section&gt;        &lt;UserList/&gt;      &lt;/div&gt;    )  }}</code></pre><p><code>src/components/search.ejs</code></p><pre><code class="ejs">/** * 上部的搜索模块 */import React, {Component} from &#39;react&#39;import PropTypes from &#39;prop-types&#39;import PubSub from &#39;pubsub-js&#39; //引入class Search extends Component {  search = () =&gt; {    var searchName = this.nameInput.value    if(searchName){      // 搜索      // 发布消息 searchPubSub.publish(&#39;search&#39;, searchName)    }  }  render() {    return (      &lt;div&gt;        &lt;input type=&quot;text&quot; placeholder=&quot;enter the name you search&quot;               ref={(input =&gt; this.nameInput = input)}/&gt;        &lt;input type=&quot;submit&quot; value=&quot;Search&quot; onClick={this.search} /&gt;      &lt;/div&gt;    )  }}export default Search</code></pre><p><code>src/components/user-list.ejs</code></p><pre><code class="ejs">/** * 下部的用户列表模块 */import React from &#39;react&#39;import PropTypes from &#39;prop-types&#39;import axios from &#39;axios&#39;import PubSub from &#39;pubsub-js&#39; //引入class UserList extends React.Component {  static propTypes = {    searchName: PropTypes.string.isRequired  }  state = {    firstView: true,    loading: false,    users: null,    error: null  }  componentDidMount(){    // 订阅消息 search   PubSub.subscribe(&#39;search&#39;, (msg, searchName) =&gt; { // 指定了新的name，需要请求          this.setState({ firstView: false, loading: true })      // 使用axios库      const url = `https://api.github.com/search/users?q=${searchName}`      axios.get(url)        .then((response) =&gt; {    console.log(response)          this.setState({ loading: false, users: response.data.items })        })        .catch((error)=&gt;{          // debugger       console.log(&#39;error&#39;, error.response.data.message, error.message)          this.setState({ loading: false, error: error.message })        })      try {        const result = axios.get(url)        this.setState({ loading: false, users: result.data.items })      } catch(err) {        // debugger        console.log(&#39;----&#39;, err.message)      }    })  }  render () {    if (this.state.firstView) {      return &lt;h2&gt;Enter name to search&lt;/h2&gt;    } else if (this.state.loading) {      return &lt;h2&gt;Loading result...&lt;/h2&gt;    } else if (this.state.error) {      return &lt;h2&gt;{this.state.error}&lt;/h2&gt;    } else {      return (        &lt;div className=&quot;row&quot;&gt;          {         this.state.users.map((user) =&gt; (              &lt;div className=&quot;card&quot; key={user.html_url}&gt;                &lt;a href={user.html_url} target=&quot;_blank&quot;&gt;                  &lt;img src={user.avatar_url} style={{width: '100px'}} alt=&#39;user&#39;/&gt;                &lt;/a&gt;                &lt;p className=&quot;card-text&quot;&gt;{user.login}&lt;/p&gt;              &lt;/div&gt;            ))          }        &lt;/div&gt;      )    }  }}export default UserList</code></pre><p>再以之前的评论的为例</p><p>App.ejs中有个删除评论的函数，传给了List组件(并没有用到)，接着传给Item</p><pre><code class="jsx">delete={this.delete}</code></pre><h3><span id="513-方式三-redux">5.1.3 方式三: redux</span></h3><p>后面专门讲解</p><h2><span id="52-事件监听理解">5.2 事件监听理解</span></h2><h3><span id="521-原生dom事件">5.2.1 原生DOM事件</span></h3><p>1)     绑定事件监听</p><p>a.     事件名(类型): 只有有限的几个, 不能随便写</p><p>b.     回调函数</p><p>2)         触发事件</p><p>a.     用户操作界面</p><p>b.     事件名(类型)</p><p>c.     数据</p><h3><span id="522-自定义事件消息机制">5.2.2 自定义事件(消息机制)</span></h3><p>1)     绑定事件监听</p><p>a.     事件名(类型): 任意</p><p>b.     回调函数: 通过形参接收数据, 在函数体处理事件</p><p>2)     触发事件(编码)</p><p>a.     事件名(类型): 与绑定的事件监听的事件名一致</p><p>b.     数据: 会自动传递给回调函数</p><h2><span id="53-es6常用新语法">5.3 ES6常用新语法</span></h2><p>1)     定义常量/变量: <code>const/let</code></p><p>2)     解构赋值:<code>let {a, b} = this.props</code>  <code>import {aa} from &#39;xxx&#39;</code></p><p>3)     对象的简洁表达: <code>{a, b}</code></p><p>4)     箭头函数: </p><p>a.     常用场景</p><ul><li><p>组件的自定义方法: <code>xxx = () =&gt; {}</code></p></li><li><p>参数匿名函数</p></li></ul><p>b.     优点:</p><p>​          * 简洁</p><p>​          * 没有自己的this,使用引用this查找的是外部this</p><p>5)     扩展(三点)运算符: 拆解对象<code>(const MyProps = {}, &lt;Xxx {...MyProps}&gt;)</code></p><p>6)     类: <code>class/extends/constructor/super</code></p><p>7)     ES6模块化: <code>export default | import</code></p><h1><span id="第6章-react-router4">第6章 react-router4</span></h1><h2><span id="61-相关理解">6.1 相关理解</span></h2><h3><span id="611-react-router的理解">6.1.1 react-router的理解</span></h3><p>1)     react的一个插件库(依赖/基于React)</p><p>2)     专门用来实现一个SPA应用</p><p>3)     基于react的项目基本都会用到此库</p><h3><span id="612-spa的理解">6.1.2 SPA的理解</span></h3><p>1)     单页Web应用（single page web application，SPA）</p><p>2)     整个应用只有一个完整的页面</p><p>3)     点击页面中的链接不会刷新页面, 本身也不会向服务器发请求</p><p>4)     当点击路由链接时, 只会做页面的<strong>局部更新</strong></p><p>5)     数据都需要通过<strong>ajax请求</strong>获取, 并在前端<strong>异步展现</strong></p><h3><span id="613-路由的理解">6.1.3 路由的理解</span></h3><p>1)     什么是路由?</p><p>a.     一个路由就是一个<strong>映射关系(key:value)</strong></p><p>b.     key为<strong>路由路径(path)</strong>, value可能是function(后台路由)/<strong>component(前台路由)</strong></p><p>2)     路由分类</p><p>a.     后台路由: node服务器端路由, value是function, 用来处理客户端提交的请求并返回一个响应数据</p><p>b.     前台路由: 浏览器端路由, value是component, 当请求的是路由path时, 浏览器端前没有发送http请求, 但界面会更新显示对应的组件 </p><p>3)     后台路由</p><p>a.     注册路由: <code>router.get(path, function(req, res))</code></p><p>b.     当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据</p><p>4)         前端路由</p><p>a.     注册路由: <code>&lt;Route path=&quot;/about&quot; component={About}&gt;</code></p><p>b.     当浏览器的<strong>hash</strong>变为<code>#about</code>时, 当前路由组件就会变为About组件</p><h3><span id="614-前端路由的实现">6.1.4 前端路由的实现</span></h3><p>(底层实现)</p><p>1)     history库</p><p>a.     网址: <a href="https://github.com/ReactTraining/history" target="_blank" rel="noopener">https://github.com/ReactTraining/history</a></p><p>b.     管理浏览器会话历史(history)的工具库</p><p>c.     包装的是原生BOM中<code>window.history</code>和<code>window.location.hash</code></p><p>2)     history API</p><p>a.     <code>History.createBrowserHistory()</code>: 得到封装window.history的管理对象</p><p>b.     <code>History.createHashHistory()</code>: 得到封装window.location.hash的管理对象</p><p>c.     <code>history.push()</code>: 添加一个新的历史记录</p><p>d.     <code>history.replace()</code>: 用一个新的历史记录替换当前的记录</p><p>e.     <code>history.goBack()</code>: 回退到上一个历史记录</p><p>f.     <code>history.goForword()</code>: 前进到下一个历史记录</p><p>g.     <code>history.listen(function(location){})</code>: 监视历史记录的变化</p><p>3)     测试</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/history-%E6%96%B9%E5%BC%8F1.gif" alt="history-方式1"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/history-%E6%96%B9%E5%BC%8F2.gif" alt="history-方式2"></p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;  &lt;meta charset=&quot;UTF-8&quot;&gt;  &lt;title&gt;history test&lt;/title&gt;&lt;/head&gt;&lt;body&gt;  &lt;p&gt;&lt;input type=&quot;text&quot;&gt;&lt;/p&gt;  &lt;a href=&quot;/test1&quot; onclick=&quot;return push(&#39;/test1&#39;)&quot;&gt;test1&lt;/a&gt;&lt;br&gt;&lt;br&gt;  &lt;button onClick=&quot;push(&#39;/test2&#39;)&quot;&gt;push test2&lt;/button&gt;&lt;br&gt;&lt;br&gt;  &lt;button onClick=&quot;back()&quot;&gt;回退&lt;/button&gt;&lt;br&gt;&lt;br&gt;  &lt;button onClick=&quot;forword()&quot;&gt;前进&lt;/button&gt;&lt;br&gt;&lt;br&gt;  &lt;button onClick=&quot;replace(&#39;/test3&#39;)&quot;&gt;replace test3&lt;/button&gt;&lt;br&gt;&lt;br&gt;  &lt;script type=&quot;text/javascript&quot; src=&quot;https://cdn.bootcss.com/history/4.7.2/history.js&quot;&gt;&lt;/script&gt;  &lt;script type=&quot;text/javascript&quot;&gt;    let history = History.createBrowserHistory() // 方式一    history = History.createHashHistory() // 方式二    // console.log(history)    function push (to) {      history.push(to)      return false    } // 可以返回    function back() {      history.goBack()    }    function forword() {      history.goForward()    }    function replace (to) {      history.replace(to)    } // 不能返回    history.listen((location) =&gt; {      console.log(&#39;请求路由路径变化了&#39;, location)    })  &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><h2><span id="62-react-router相关api">6.2 react-router相关API</span></h2><p><a href="https://reactrouter.com/" target="_blank" rel="noopener">react_router</a></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200714181151269.png" alt="react-router"></p><p><a href="https://reactrouter.com/web/guides/quick-start" target="_blank" rel="noopener">web</a></p><h3><span id="621-组件">6.2.1 组件</span></h3><p>1)     <code>&lt;BrowserRouter&gt;</code>：BrowserRouter是react路由的容器</p><p>2)     <code>&lt;HashRouter&gt;</code>：这个是用来兼容老浏览器的</p><p>3)     <code>&lt;Route&gt;</code>：Route的作用就是用来渲染路由匹配的组件。路由渲染有三种方式，每一种方式都可以传递match,location,history对象</p><p>4)     <code>&lt;Redirect&gt;</code>：路由重定向</p><p>5)     <code>&lt;Link&gt;</code>：Link的作用和a标签类似</p><p>6)     <code>&lt;NavLink&gt;</code>：NavLink和Link一样最终都是渲染成a标签，NavLink可以给这个a标签添加额外的属性</p><p>7)     <code>&lt;Switch&gt;</code>：Switch组件内部可以是Route或者Redirect，只会渲染第一个匹配的元素</p><h3><span id="622-其它">6.2.2 其它</span></h3><p>1)     <strong><code>history</code>对象</strong>：这里的history对象是使用history插件生成的，<a href="http://www.cnblogs.com/ye-hcj/p/7741742.html已经详细讲过了" target="_blank" rel="noopener">http://www.cnblogs.com/ye-hcj/p/7741742.html已经详细讲过了</a><br>记住一点，再使用location做对比的使用，通过history访问的location是动态变化的，最好通过Route访问location</p><p>2)     <code>match</code>对象：match对象表示当前的路由地址是怎么跳转过来的</p><p>3)     <code>withRouter</code>函数：当一个非路由组件也想访问到当前路由的match,location,history对象，那么withRouter将是一个非常好的选择</p><h2><span id="63-基本路由使用">6.3 基本路由使用</span></h2><h3><span id="631-效果">6.3.1 效果</span></h3><p>![react-router demo1 (3)](<a href="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/react-router" target="_blank" rel="noopener">https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/react-router</a> demo1 (3).gif)</p><p>并没有刷新页面</p><h3><span id="632-准备">6.3.2 准备</span></h3><p>1)     下载react-router: <code>npm install --save react-router@4</code></p><p>我们只需要web版本：<code>npm install --save react-router-dom</code></p><p>2)     由于使用到了BootStrap，因此在index.html中引入bootstrap.css: <code>&lt;link rel=&quot;stylesheet&quot; href=&quot;/css/bootstrap.css&quot;&gt;</code></p><p><code>public/index.html</code></p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;  &lt;head&gt;    &lt;meta charset=&quot;utf-8&quot;&gt;    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;    &lt;meta name=&quot;theme-color&quot; content=&quot;#000000&quot;&gt;    &lt;link rel=&quot;manifest&quot; href=&quot;%PUBLIC_URL%/manifest.json&quot;&gt;    &lt;link rel=&quot;shortcut icon&quot; href=&quot;%PUBLIC_URL%/favicon.ico&quot;&gt;    &lt;title&gt;React-router&lt;/title&gt;    &lt;link rel=&quot;stylesheet&quot; href=&quot;/css/bootstrap.css&quot;&gt;  &lt;/head&gt;  &lt;body&gt;    &lt;noscript&gt;      You need to enable JavaScript to run this app.    &lt;/noscript&gt;    &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;  &lt;/body&gt;&lt;/html&gt;</code></pre><p>一般会将路由组件和非路由组件分开写</p><p>pages/views存放路由组件</p><p>components存放其他组件</p><h3><span id="633-路由组件-viewsaboutjsx">6.3.3 路由组件: <code>views/about.jsx</code></span></h3><pre><code class="jsx">import React,{Component} from &#39;react&#39;export default class About extends Component{  render(){    return(        &lt;div&gt;About组件内容&lt;/div&gt;      )  }}</code></pre><h3><span id="634-路由组件-viewshomejsx">6.3.4 路由组件: <code>views/home.jsx</code></span></h3><pre><code class="jsx">import React, {Component} from &#39;react&#39;export default class Home extends Component{  render(){    return (        &lt;div&gt;Home组件内容&lt;/div&gt;      )  }}</code></pre><h3><span id="635-包装navlink组件-componentsmy-nav-linkjsx">6.3.5 包装NavLink组件: <code>components/my-nav-link.jsx</code></span></h3><p>由于每个NavLink都需要自定义active样式(加入属性activeClassName)，因此提出来</p><pre><code class="jsx">import React, {Component} from &#39;react&#39;import {NavLink} from &#39;react-router-dom&#39;export default class MyNavLink extends Component {  render(){    // 利用this.props三点运算符接受所有的属性    return(      &lt;NavLink {...this.props} activeClassName=&#39;activeClass&#39;/&gt;    )  }}</code></pre><h3><span id="636-应用组件-componentsappjsx">6.3.6 应用组件: <code>components/app.jsx</code></span></h3><pre><code class="jsx">import React from &#39;react&#39;import {NavLink, Route, Switch, Redirect} from &#39;react-router-dom&#39;import MyNavLink from &#39;./my-nav-link&#39;import About from &#39;../views/about&#39;import Home from &#39;../views/home&#39;export default class App extends React.Component {  render() {    return (      &lt;div&gt;        &lt;div className=&quot;row&quot;&gt;          &lt;div className=&quot;col-xs-offset-2 col-xs-8&quot;&gt;            &lt;div className=&quot;page-header&quot;&gt;              &lt;h2&gt;React Router Demo&lt;/h2&gt;            &lt;/div&gt;          &lt;/div&gt;        &lt;/div&gt;        &lt;div className=&quot;row&quot;&gt;          &lt;div className=&quot;col-xs-2 col-xs-offset-2&quot;&gt;            &lt;div className=&quot;list-group&quot;&gt;              {/*导航路由链接，不能使用a标签，to指向的path*/}              &lt;MyNavLink className=&quot;list-group-item&quot; to=&#39;/about&#39;&gt;About&lt;/MyNavLink&gt;              &lt;MyNavLink className=&quot;list-group-item&quot; to=&#39;/home&#39;&gt;Home&lt;/MyNavLink&gt;            &lt;/div&gt;          &lt;/div&gt;          &lt;div className=&quot;col-xs-6&quot;&gt;            &lt;div className=&quot;panel&quot;&gt;              &lt;div className=&quot;panel-body&quot;&gt;                {/*可切换的路由组件，使用switch只有匹配才显示，route的path对应上方的to、component对应路由组件，Redirect自动重定向到about、默认到about组件*/}                &lt;Switch&gt;                  &lt;Route path=&#39;/about&#39; component={About}/&gt;                  &lt;Route path=&#39;/home&#39; component={Home}/&gt;                  &lt;Redirect to=&#39;/about&#39;/&gt;                &lt;/Switch&gt;              &lt;/div&gt;            &lt;/div&gt;          &lt;/div&gt;        &lt;/div&gt;      &lt;/div&gt;    )  }}</code></pre><h3><span id="637-自定义样式-indexcss">6.3.7 自定义样式: <code>index.css</code></span></h3><pre><code class="css">activeClass{  color: red !important;}</code></pre><h3><span id="638-入口js-indexjs">6.3.8 入口JS: <code>index.js</code></span></h3><pre><code class="javascript">import React from &#39;react&#39;//import ReactDOM from &#39;react-dom&#39;import {render} from &#39;react-dom&#39;import {BrowserRouter, HashRouter} from &#39;react-router-dom&#39;import App from &#39;./components/app&#39;import &#39;./index.css&#39;// ReactDOM.render(render(  (    &lt;BrowserRouter&gt;      &lt;App/&gt;    &lt;/BrowserRouter&gt;    /* 组件需要用路由器组件包含起来，两者任选一个 */    /*&lt;HashRouter&gt;      &lt;App /&gt;    &lt;/HashRouter&gt;*/  ),  document.getElementById(&#39;root&#39;))</code></pre><p>总结：如何编写路由效果？</p><ol><li>编写路由组件</li><li>在父路由组件中指定<ul><li>路由连接：<code>&lt;NavLink&gt;&lt;/NavLink&gt;</code></li><li>路由：<code>&lt;Route&gt;&lt;/Route&gt;</code></li></ul></li></ol><h2><span id="64-嵌套路由使用">6.4 嵌套路由使用</span></h2><p>嵌套路由——路由组件中的路由</p><h3><span id="641-效果">6.4.1 效果</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/react-router-demo2.gif" alt="react-router demo2"></p><h3><span id="642-二级路由组件-viewsnewsjsx">6.4.2 二级路由组件: <code>views/news.jsx</code></span></h3><pre><code class="jsx">import React from &#39;react&#39;export default class News extends React.Component {  state = {    newsArr: [&#39;news001&#39;, &#39;news002&#39;, &#39;news003&#39;]  }  render () {    return (      &lt;div&gt;        &lt;ul&gt;          {            this.state.newsArr.map((news, index) =&gt; &lt;li key={index}&gt;{news}&lt;/li&gt;)          }        &lt;/ul&gt;      &lt;/div&gt;    )  }}</code></pre><h3><span id="643-二级路由组件-viewsmessagejsx">6.4.3 二级路由组件: <code>views/message.jsx</code></span></h3><pre><code class="jsx">import React from &#39;react&#39;import {Link, Route} from &#39;react-router-dom&#39;export default class Message extends React.Component {  state = {    messages: []  }  componentDidMount () {    // 模拟发送ajax请求    setTimeout(() =&gt; {      const data = [        {id: 1, title: &#39;Message001&#39;},        {id: 3, title: &#39;Message003&#39;},        {id: 6, title: &#39;Message006&#39;},      ]      this.setState({        messages: data      })    }, 1000)  }  render () {    const path = this.props.match.path    return (      &lt;div&gt;        &lt;ul&gt;          {            this.state.messages.map((m, index) =&gt; {              return (                &lt;li key={index}&gt;                   &lt;a href=&#39;???&#39;&gt;{m.title}&lt;/a&gt;                &lt;/li&gt;              )            })          }        &lt;/ul&gt;      &lt;/div&gt;    )  }}</code></pre><h3><span id="644-一级路由组件-viewshomejsx">6.4.4 一级路由组件: <code>views/home.jsx</code></span></h3><pre><code class="jsx">import React, {Component} from &#39;react&#39;import {Switch, Route, Redirect} from &#39;react-router-dom&#39;import MyNavLink from &#39;../components/my-nav-link&#39;import News from &#39;./news&#39;import Message from &#39;./message&#39;export default class Home extends Component{  render(){    return (      &lt;div&gt;        &lt;h2&gt;Home组件内容&lt;/h2&gt;        &lt;div&gt;          &lt;ul className=&quot;nav nav-tabs&quot;&gt;            &lt;li&gt;              &lt;MyNavLink to=&#39;/home/news&#39;&gt;News&lt;/MyNavLink&gt;            &lt;/li&gt;            &lt;li&gt;              &lt;MyNavLink to=&quot;/home/message&quot;&gt;Message&lt;/MyNavLink&gt;            &lt;/li&gt;          &lt;/ul&gt;          &lt;Switch&gt;            &lt;Route path=&#39;/home/news&#39; component={News} /&gt;            &lt;Route path=&#39;/home/message&#39; component={Message} /&gt;            &lt;Redirect to=&#39;/home/news&#39;/&gt;          &lt;/Switch&gt;        &lt;/div&gt;      &lt;/div&gt;    )  }}</code></pre><h2><span id="65-向路由组件传递参数数据">6.5 向路由组件传递参数数据</span></h2><p>传递的是id值</p><h3><span id="651-效果">6.5.1 效果</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/react-router-demo3.gif" alt="react-router demo3 (2)"></p><h3><span id="652-三级路由组件-viewsmessage-detailjsx">6.5.2 三级路由组件: <code>views/message-detail.jsx</code></span></h3><pre><code class="jsx">import React from &#39;react&#39;const messageDetails = [  {id: 1, title: &#39;Message001&#39;, content: &#39;中国，你是最棒的&#39;},  {id: 3, title: &#39;Message003&#39;, content: &#39;对的，没错，非常赞成楼上&#39;},  {id: 6, title: &#39;Message006&#39;, content: &#39;我也赞成&#39;},]// 函数的组件export default function MessageDetail(props) {  const id = props.match.params.id  const md = messageDetails.find(md =&gt; md.id===id*1)  return (    &lt;ul&gt;      &lt;li&gt;ID: {md.id}&lt;/li&gt;      &lt;li&gt;TITLE: {md.title}&lt;/li&gt;      &lt;li&gt;CONTENT: {md.content}&lt;/li&gt;    &lt;/ul&gt;  )}</code></pre><h3><span id="653-二级路由组件-viewsmessagejsx">6.5.3 二级路由组件: <code>views/message.jsx</code></span></h3><pre><code class="jsx">import React from &#39;react&#39;import {Link, Route} from &#39;react-router-dom&#39;import MessageDetail from &quot;./message-detail&quot;export default class Message extends React.Component {  state = {    messages: []  }  componentDidMount () {    // 模拟发送ajax请求    setTimeout(() =&gt; {      const data = [        {id: 1, title: &#39;Message001&#39;},        {id: 3, title: &#39;Message003&#39;},        {id: 6, title: &#39;Message006&#39;},      ]      this.setState({        messages: data      })    }, 1000)  }  render () {    const path = this.props.match.path    return (      &lt;div&gt;        &lt;ul&gt;          {            this.state.messages.map((m, index) =&gt; {              return (                &lt;li key={index}&gt;                  &lt;Link to={`${path}/${m.id}`}&gt;{m.title}&lt;/Link&gt;                &lt;/li&gt;              )            })          }        &lt;/ul&gt;        &lt;Route path={`${path}/:id`} component={MessageDetail}&gt;&lt;/Route&gt;      &lt;/div&gt;    )  }}</code></pre><p>路由链接与非路由链接：是否发了请求(路由连接不发)</p><p><code>&lt;NavLink to=&#39;&#39;&gt;&lt;/NavLink&gt;`` &lt;Link to=&#39;&#39;&gt;&lt;/Link&gt;</code></p><p><code>&lt;a href=&quot;&quot;&gt;&lt;/a&gt;</code></p><h2><span id="66-多种路由跳转方式">6.6 多种路由跳转方式</span></h2><p>前面讲的路由切换都是通过点击链接的方式切换的，不是链接也能够</p><h3><span id="661-效果">6.6.1 效果</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/react-router-demo4.gif" alt="react-router demo4"></p><h3><span id="662-二级路由-viewsmessagejsx">6.6.2 二级路由: views/message.jsx</span></h3><pre><code class="jsx">import React from &#39;react&#39;import {Link, Route} from &#39;react-router-dom&#39;import MessageDetail from &quot;./message-detail&quot;export default class Message extends React.Component {  state = {    messages: []  }  componentDidMount () {    // 模拟发送ajax请求    setTimeout(() =&gt; {      const data = [        {id: 1, title: &#39;Message001&#39;},        {id: 3, title: &#39;Message003&#39;},        {id: 6, title: &#39;Message006&#39;},      ]      this.setState({        messages: data      })    }, 1000)  }  // props中有history属性，它由push等方法  ShowDetail = (id) =&gt; {    this.props.history.push(`/home/message/${id}`)  }  ShowDetail2 = (id) =&gt; {    this.props.history.replace(`/home/message/${id}`)  }  back = () =&gt; {    this.props.history.goBack()  }  forward = () =&gt; {    this.props.history.goForward()  }  render () {    const path = this.props.match.path    return (      &lt;div&gt;        &lt;ul&gt;          {            this.state.messages.map((m, index) =&gt; {              return (                &lt;li key={index}&gt;                  &lt;Link to={`${path}/${m.id}`}&gt;{m.title}&lt;/Link&gt;                  &amp;nbsp;&amp;nbsp;&amp;nbsp;                  &lt;button onClick={() =&gt; this.ShowDetail(m.id)}&gt;查看详情(push)&lt;/button&gt;&amp;nbsp;                  &lt;button onClick={() =&gt; this.ShowDetail2(m.id)}&gt;查看详情(replace)&lt;/button&gt;                &lt;/li&gt;              )            })          }        &lt;/ul&gt;        &lt;p&gt;          &lt;button onClick={this.back}&gt;返回&lt;/button&gt;&amp;nbsp;          &lt;button onClick={this.forward}&gt;前进&lt;/button&gt;&amp;nbsp;        &lt;/p&gt;        &lt;hr/&gt;        &lt;Route path={`${path}/:id`} component={MessageDetail}&gt;&lt;/Route&gt;        {/*&lt;Route path={`home/message/meassagedetail/:id`} component={MessageDetail}&gt;&lt;/Route&gt;*/}      &lt;/div&gt;    )  }}</code></pre><p> 总结：</p><ul><li>路由器标签<ul><li><code>&lt;BrowserRouter&gt;</code>：BrowserRouter是react路由的容器</li><li><code>&lt;HashRouter&gt;</code>：多了一个#号</li></ul></li><li>路由<ul><li><code>&lt;Route&gt;</code>：Route的作用就是用来渲染路由匹配的组件。路由渲染有三种方式，每一种方式都可以传递match,location,history对象</li></ul></li><li><code>&lt;Redirect&gt;</code>：路由重定向</li><li>链接<ul><li><code>&lt;Link&gt;</code>：Link的作用和a标签类似</li><li><code>&lt;NavLink&gt;</code>：可以添加其他属性，例如activeClassName</li></ul></li><li><ul><li><code>&lt;Switch&gt;</code>：Switch组件内部可以是Route或者Redirect，只会渲染第一个匹配的元素</li></ul></li></ul><ul><li>this.props.<ul><li>match<ul><li>params</li></ul></li><li>history<ul><li>push()</li><li>replace()</li><li>goback()</li><li>goforward()</li></ul></li></ul></li></ul><h1><span id="第7章-react-ui">第7章 react-ui</span></h1><h2><span id="71-最流行的开源react-ui组件库">7.1 最流行的开源React UI组件库</span></h2><h3><span id="711-material-ui国外">7.1.1 material-ui(国外)</span></h3><p>1)     官网: <a href="http://www.material-ui.com/#/" target="_blank" rel="noopener">http://www.material-ui.com/#/</a></p><p>2)     github: <a href="https://github.com/callemall/material-ui" target="_blank" rel="noopener">https://github.com/callemall/material-ui</a></p><h3><span id="712-ant-design国内蚂蚁金服">7.1.2 ant-design(国内蚂蚁金服)</span></h3><p>1)     PC官网: <a href="https://ant.design/index-cn" target="_blank" rel="noopener">https://ant.design/index-cn</a></p><p>2)     移动官网: <a href="https://mobile.ant.design/index-cn" target="_blank" rel="noopener">https://mobile.ant.design/index-cn</a></p><p>3)     Github: <a href="https://github.com/ant-design/ant-design/" target="_blank" rel="noopener">https://github.com/ant-design/ant-design/</a></p><p>4)     Github: <a href="https://github.com/ant-design/ant-design-mobile/" target="_blank" rel="noopener">https://github.com/ant-design/ant-design-mobile/</a></p><h2><span id="72-ant-design-mobile使用入门">7.2 ant-design-mobile使用入门</span></h2><h3><span id="721-效果">7.2.1 效果</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/antd-mobile.gif" alt="antd-mobile"></p><h3><span id="722-使用create-react-app创建react应用">7.2.2 使用create-react-app创建react应用</span></h3><pre><code class="bash">npm install create-react-app -gcreate-react-app antm-democd antm-demonpm start</code></pre><h3><span id="723-搭建antd-mobile的基本开发环境">7.2.3 搭建antd-mobile的基本开发环境</span></h3><p>1)     下载</p><p><code>npm install antd-mobile --save</code></p><p>2)     <code>src/components/App.jsx</code></p><pre><code class="jsx">import React, {Component} from &#39;react&#39;import {Button, Toast} from &#39;antd-mobile&#39;export default class App extends Component {  handleClick = () =&gt; {    Toast.info(&#39;提交成功&#39;, 2)  }  render() {    return (        &lt;div&gt;          &lt;Button type=&quot;primary&quot; onClick={this.handleClick}&gt;提交&lt;/Button&gt;        &lt;/div&gt;         )  }  /* type值不同样式不同 */}</code></pre><p>3)     <code>src/index.js</code></p><pre><code class="javascript">import React from &#39;react&#39;;import {render} from &#39;react-dom&#39;import App from &quot;./components/App&quot;// 引入整体cssimport &#39;antd-mobile/dist/antd-mobile.css&#39;render(&lt;App /&gt;, document.getElementById(&#39;root&#39;))</code></pre><p>4)     <code>index.html</code></p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;  &lt;head&gt;    &lt;meta charset=&quot;utf-8&quot;&gt;    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no&quot; /&gt;    &lt;meta name=&quot;theme-color&quot; content=&quot;#000000&quot;&gt;    &lt;link rel=&quot;manifest&quot; href=&quot;%PUBLIC_URL%/manifest.json&quot;&gt;    &lt;link rel=&quot;shortcut icon&quot; href=&quot;%PUBLIC_URL%/favicon.ico&quot;&gt;    &lt;title&gt;React ant design mobile&lt;/title&gt;    &lt;script src=&quot;https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js&quot;&gt;&lt;/script&gt;    &lt;script&gt;      if (&#39;addEventListener&#39; in document) {        document.addEventListener(&#39;DOMContentLoaded&#39;, function() {          FastClick.attach(document.body);        }, false);      }      if(!window.Promise) {        document.writeln(&#39;&lt;script src=&quot;https://as.alipayobjects.com/g/component/es6-promise/3.2.2/es6-promise.min.js&quot;&#39;+&#39;&gt;&#39;+&#39;&lt;&#39;+&#39;/&#39;+&#39;script&gt;&#39;);      }    &lt;/script&gt;  &lt;/head&gt;  &lt;body&gt;    &lt;noscript&gt;      You need to enable JavaScript to run this app.    &lt;/noscript&gt;    &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;  &lt;/body&gt;&lt;/html&gt;</code></pre><h3><span id="724-实现按需打包组件jscss">7.2.4 实现按需打包(组件js/css)</span></h3><p>1)     下载依赖包</p><pre><code class="bash">yarn add react-app-rewired --devyarn add babel-plugin-import --dev</code></pre><p>2)     修改默认配置: </p><p>l package.json</p><pre><code class="json">{  &quot;name&quot;: &quot;react_ui&quot;,  &quot;version&quot;: &quot;0.1.0&quot;,  &quot;private&quot;: true,  &quot;dependencies&quot;: {    &quot;antd-mobile&quot;: &quot;^2.1.3&quot;,    &quot;react&quot;: &quot;^16.2.0&quot;,    &quot;react-dom&quot;: &quot;^16.2.0&quot;  },  &quot;devDependencies&quot;: {    &quot;babel-plugin-import&quot;: &quot;^1.6.3&quot;,    &quot;react-app-rewired&quot;: &quot;^1.4.0&quot;,    &quot;react-scripts&quot;: &quot;1.0.17&quot;  },  &quot;scripts&quot;: {    &quot;start&quot;: &quot;react-app-rewired start&quot;,    &quot;build&quot;: &quot;react-app-rewired build&quot;,    &quot;test&quot;: &quot;react-app-rewired test --env=jsdom&quot;  }}</code></pre><p>l config-overrides.js</p><pre><code class="javascript">const {injectBabelPlugin} = require(&#39;react-app-rewired&#39;);module.exports = function override(config, env) {  config = injectBabelPlugin([&#39;import&#39;, {libraryName: &#39;antd-mobile&#39;, style: &#39;css&#39;}], config);  return config;};</code></pre><p>3)     编码</p><pre><code class="jsx">// import &#39;antd-mobile/dist/antd-mobile.css&#39;// import Button from &#39;antd-mobile/lib/button&#39;// import Toast from &#39;antd-mobile/lib/toast&#39; import {Button, Toast} from &#39;antd-mobile&#39;</code></pre><h1><span id="第8章-redux">第8章 redux</span></h1><h2><span id="81-redux理解">8.1 redux理解</span></h2><h3><span id="811-学习文档">8.1.1 学习文档</span></h3><p>1)     英文文档: <a href="https://redux.js.org/" target="_blank" rel="noopener">https://redux.js.org/</a></p><p>2)     中文文档: <a href="http://www.redux.org.cn/" target="_blank" rel="noopener">http://www.redux.org.cn/</a></p><p>3)     Github: <a href="https://github.com/reactjs/redux" target="_blank" rel="noopener">https://github.com/reactjs/redux</a></p><h3><span id="812-redux是什么">8.1.2 redux是什么?</span></h3><p>1)     redux是一个独立专门用于做<strong>状态管理</strong>的<strong>JS库</strong>(不是react插件库)</p><p>2)     它可以用在react, angular, vue等项目中, 但基本与react配合使用</p><p>3)     作用: <strong>集中式管理</strong>react应用中多个组件共享的状态</p><h3><span id="813-redux工作流程">8.1.3 redux工作流程</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200715104120743.png" alt="redux"></p><h3><span id="814-什么情况下需要使用redux">8.1.4 什么情况下需要使用redux</span></h3><p>1)     总体原则: 能不用就不用, 如果不用比较吃力才考虑使用</p><p>2)     某个组件的状态，需要共享</p><p>3)     某个状态需要在任何地方都可以拿到</p><p>4)     一个组件需要改变全局状态</p><p>5)     一个组件需要改变另一个组件的状态</p><h2><span id="82-redux的核心api">8.2 redux的核心API</span></h2><h3><span id="821-createstore">8.2.1 <code>createStore()</code></span></h3><p>1)     作用:  创建包含指定reducer的store对象</p><p>2)     编码:</p><pre><code>import {createStore} from &#39;redux&#39;import counter from &#39;./reducers/counter&#39;const store = createStore(counter)</code></pre><h3><span id="822-store对象">8.2.2 <code>store</code>对象</span></h3><p>1)     作用: </p><p>redux库最核心的管理对象</p><p>2)     它内部维护着:</p><ul><li><p><code>state</code></p></li><li><p><code>reducer</code></p></li></ul><p>3)     核心方法:</p><ul><li><p><code>getState()</code></p></li><li><p><code>dispatch(action)</code></p></li><li><p><code>subscribe(listener)</code></p></li></ul><p>4)     编码:</p><pre><code>store.getState()store.dispatch({type:&#39;INCREMENT&#39;, number})store.subscribe(render)</code></pre><h3><span id="823-applymiddleware">8.2.3 <code>applyMiddleware()</code></span></h3><p>1)     作用:</p><p>应用上基于redux的中间件(插件库)</p><p>2)     编码:</p><pre><code>import {createStore, applyMiddleware} from &#39;redux&#39;import thunk from &#39;redux-thunk&#39; // redux异步中间件const store = createStore( counter, applyMiddleware(thunk) // 应用上异步中间件)</code></pre><h3><span id="824-combinereducers">8.2.4 <code>combineReducers()</code></span></h3><p>1)     作用:</p><p>合并多个reducer函数</p><p>2)     编码:</p><pre><code>export default combineReducers({ user, chatUser, chat})</code></pre><h2><span id="83-redux的三个核心概念">8.3 redux的三个核心概念</span></h2><h3><span id="831-action">8.3.1 <code>action</code></span></h3><p>1)     标识要执行行为的对象</p><p>2)     包含2个方面的属性</p><p>a.     type: 标识属性, 值为字符串, 唯一, 必要属性</p><p>b.     xxx: 数据属性, 值类型任意, 可选属性</p><p>3)     例子:</p><pre><code>const action = {    type: &#39;INCREMENT&#39;,    data: 2}</code></pre><p>4)     Action Creator(创建Action的工厂函数)</p><pre><code>const increment = (number) =&gt; ({type: &#39;INCREMENT&#39;, data: number})</code></pre><h3><span id="832-reducer">8.3.2 <code>reducer</code></span></h3><p>1)     根据老的state和action, 产生新的state的纯函数</p><p>2)     样例</p><pre><code>export default function counter(state = 0, action) {  switch (action.type) {     case &#39;INCREMENT&#39;:        return state + action.data     case &#39;DECREMENT&#39;:        return state - action.data     default:        return state  }}</code></pre><p>3)     注意</p><p>a.     返回一个新的状态</p><p>b.     不要修改原来的状态</p><h3><span id="833-store">8.3.3 <code>store</code></span></h3><p>1)     将state,action与reducer联系在一起的对象</p><p>2)     如何得到此对象?</p><pre><code>import {createStore} from &#39;redux&#39;import reducer from &#39;./reducers&#39;const store = createStore(reducer)</code></pre><p>3)     此对象的功能?</p><ul><li><p>getState(): 得到state</p></li><li><p>dispatch(action): 分发action, 触发reducer调用, 产生新的state</p></li><li><p>subscribe(listener): 注册监听, 当产生了新的state时, 自动调用</p></li></ul><h2><span id="84-使用redux编写应用">8.4 使用redux编写应用</span></h2><h3><span id="841-效果">8.4.1 效果</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/redux.gif" alt="redux"></p><p>使用react实现</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200715112226383.png" alt="app.jsx"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200715112313127.png" alt="index.js"></p><h3><span id="842-下载依赖包">8.4.2 下载依赖包</span></h3><p><code>npm install --save redux</code></p><h3><span id="843-srcreduxaction-typesjs">8.4.3 <code>src/redux/action-types.js</code></span></h3><pre><code class="javascript">/*Action对象的type常量名称模块 */export const INCREMENT = &#39;increment&#39;export const DECREMENT = &#39;decrement&#39;</code></pre><h3><span id="844-srcreduxactionsjs">8.4.4 <code>src/redux/actions.js</code></span></h3><pre><code class="javascript">/*action creator模块 */import {INCREMENT, DECREMENT} from &#39;./action-types&#39;export const increment = number =&gt; ({type: INCREMENT, number})export const decrement = number =&gt; ({type: DECREMENT, number})</code></pre><h3><span id="845-srcreduxreducersjs">8.4.5 <code>src/redux/reducers.js</code></span></h3><pre><code class="javascript">/*包含n个reducer函数的模块根据老的state和指定action, 处理返回一个新的state */import {INCREMENT, DECREMENT} from &#39;./action-types&#39;export function counter(state = 0, action) {  console.log(&#39;counter&#39;, state, action)  switch (action.type) {    case INCREMENT:      return state + action.number    case DECREMENT:      return state - action.number    default:      return state  }}</code></pre><h3><span id="846-srccomponentsappjsx">8.4.6 <code>src/components/app.jsx</code></span></h3><pre><code class="jsx">/*应用组件 */import React, {Component} from &#39;react&#39;import PropTypes from &#39;prop-types&#39;import * as actions from &#39;../redux/actions&#39;import { INCREMENT } from &#39;../redux/action-types&#39;export default class App extends Component {  static propTypes = {    store: PropTypes.object.isRequired,  }  increment = () =&gt; {    // 1.得到选择增加数量    const number = this.refs.numSelect.value * 1    // 2.调用store的方法更新状态    this.props.store.dispatch(actions.increment(number))  }  decrement = () =&gt; {    // 1.得到选择减小数量    const number = this.refs.numSelect.value * 1    // 2.调用store的方法更新状态    this.props.store.dispatch(actions.decrement(number))  }  incrementIfOdd = () =&gt; {    // 1.得到选择增加数量    const number = this.refs.numSelect.value * 1    // 2.得到原本的count状态    let count = this.props.store.getState()    // 判断，满足条件猜更新状态    if (count % 2 === 1) {      // 调用store的方法更新状态      this.props.store.dispatch(actions.increment(number))    }  }  incrementAsync = () =&gt; {    // 1.得到选择增加数量    const number = this.refs.numSelect.value * 1    // 2.启动延时定时器，模拟异步    setTimeout(() =&gt; {      // 3.调用store的方法更新状态      this.props.store.dispatch(actions.increment(number))    }, 1000)  }  render() {    const count = this.props.store.getState()    return (      &lt;div&gt;        &lt;p&gt;          click {count} times {&#39; &#39;}        &lt;/p&gt;        &lt;select ref=&quot;numSelect&quot;&gt;          &lt;option value=&quot;1&quot;&gt;1&lt;/option&gt;          &lt;option value=&quot;2&quot;&gt;2&lt;/option&gt;          &lt;option value=&quot;3&quot;&gt;3&lt;/option&gt;        &lt;/select&gt;{&#39; &#39;}        &lt;button onClick={this.increment}&gt;+&lt;/button&gt;        {&#39; &#39;}        &lt;button onClick={this.decrement}&gt;-&lt;/button&gt;        {&#39; &#39;}        &lt;button onClick={this.incrementIfOdd}&gt;increment if odd&lt;/button&gt;        {&#39; &#39;}        &lt;button onClick={this.incrementAsync}&gt;increment async&lt;/button&gt;      &lt;/div&gt;    )  }}</code></pre><h3><span id="847-srcindexjs">8.4.7 <code>src/index.js</code></span></h3><pre><code class="javascript">import React from &#39;react&#39;import ReactDOM from &#39;react-dom&#39;import {createStore} from &#39;redux&#39;import App from &#39;./components/app&#39;import {counter} from &#39;./redux/reducers&#39;// 根据counter函数创建store对象const store = createStore(counter) // 内部会第一次调用reduer函数，得到初始state// 定义渲染根组件标签的函数const render = () =&gt; {  ReactDOM.render(    &lt;App store={store}/&gt;,    document.getElementById(&#39;root&#39;)  )}// 初始化渲染render()// 注册(订阅)监听, 一旦状态发生改变, 自动重新渲染store.subscribe(render)</code></pre><p>也可以把index.js中store提取出来</p><ul><li><code>src/redux/store.js</code></li></ul><pre><code class="javascript">import {createStore} from &#39;redux&#39;import {counter} from &#39;./reducers&#39;// 根据counter函数创建store对象const store = createStore(counter)  // // 内部会第一次调用reduer函数，得到初始stateexport default store</code></pre><ul><li><code>src/index.js</code></li></ul><pre><code class="javascript">import React from &#39;react&#39;import ReactDOM from &#39;react-dom&#39;import App from &#39;./components/app&#39;import store from &#39;./redux/store&#39;// 定义渲染根组件标签的函数const render = () =&gt; {  ReactDOM.render(    &lt;App store={store}/&gt;,    document.getElementById(&#39;root&#39;)  )}// 初始化渲染render()// 注册(订阅)监听, 一旦状态发生改变, 自动重新渲染store.subscribe(render)</code></pre><h3><span id="848-问题">8.4.8 问题</span></h3><p>1)     redux与react组件的代码耦合度太高(大多数地方用到store)</p><p>2)     编码不够简洁</p><h2><span id="85-react-redux">8.5 react-redux</span></h2><h3><span id="851-理解">8.5.1 理解</span></h3><p>1)     一个<strong>react插件库</strong></p><p>2)     专门用来简化react应用中使用redux</p><h3><span id="852-react-redux将所有组件分成两大类">8.5.2 React-Redux将所有组件分成两大类</span></h3><p>1)     UI组件</p><p>​    a.     只负责 UI 的呈现，不带有任何业务逻辑</p><p>​    b.     通过props接收数据(一般数据和函数)</p><p>​    c.     不使用任何 Redux 的 API</p><p>​    d.     一般保存在<code>components</code>文件夹下</p><p>2)     容器组件</p><p>​    a.     负责管理数据和业务逻辑，不负责UI的呈现</p><p>​    b.     使用 Redux 的 API</p><p>​    c.     一般保存在<code>containers</code>文件夹下</p><h3><span id="853-相关api">8.5.3 相关API</span></h3><p>1)     <code>Provider</code></p><p>让所有组件都可以得到state数据</p><pre><code>&lt;Provider store={store}&gt;   &lt;App /&gt;&lt;/Provider&gt;</code></pre><p>2)     <code>connect()</code></p><p>用于包装 UI 组件生成容器组件</p><pre><code>import { connect } from &#39;react-redux&#39;connect(   mapStateToprops,   mapDispatchToProps)(Counter)</code></pre><p>3)     <code>mapStateToprops()</code></p><p>将外部的数据（即state对象）转换为UI组件的标签属性</p><pre><code>const mapStateToprops = function (state) {  return {   value: state  }}</code></pre><p>4)     <code>mapDispatchToProps()</code></p><p>将分发action的函数转换为UI组件的标签属性</p><p>简洁语法可以直接指定为actions对象或包含多个action方法的对象</p><h3><span id="854-使用react-redux">8.5.4 使用react-redux</span></h3><p>1)     下载依赖包</p><p><code>npm install --save react-redux</code></p><p>2)     <code>redux/action-types.js</code></p><p>不变</p><p>3)     <code>redux/actions.js</code></p><p>不变</p><p>4)     <code>redux/reducers.js</code></p><p>不变</p><p>5)     <code>components/counter.jsx</code></p><pre><code class="jsx">/*UI组件: 不包含任何redux API */import React from &#39;react&#39;import PropTypes from &#39;prop-types&#39;export default class Counter extends React.Component {  static propTypes = {    count: PropTypes.number.isRequired,    increment: PropTypes.func.isRequired,    decrement: PropTypes.func.isRequired  }  increment = () =&gt; {    const number = this.refs.numSelect.value * 1    this.props.increment(number)  }  decrement = () =&gt; {    const number = this.refs.numSelect.value * 1    this.props.decrement(number)  }  incrementIfOdd = () =&gt; {    const number = this.refs.numSelect.value * 1    let count = this.props.count    if (count % 2 === 1) {      this.props.increment(number)    }  }  incrementAsync = () =&gt; {    const number = this.refs.numSelect.value * 1    setTimeout(() =&gt; {      this.props.increment(number)    }, 1000)  }  render() {    return (      &lt;div&gt;        &lt;p&gt;          click {this.props.count} times {&#39; &#39;}        &lt;/p&gt;        &lt;select ref=&quot;numSelect&quot;&gt;          &lt;option value=&quot;1&quot;&gt;1&lt;/option&gt;          &lt;option value=&quot;2&quot;&gt;2&lt;/option&gt;          &lt;option value=&quot;3&quot;&gt;3&lt;/option&gt;        &lt;/select&gt;{&#39; &#39;}        &lt;button onClick={this.increment}&gt;+&lt;/button&gt;        {&#39; &#39;}        &lt;button onClick={this.decrement}&gt;-&lt;/button&gt;        {&#39; &#39;}        &lt;button onClick={this.incrementIfOdd}&gt;increment if odd&lt;/button&gt;        {&#39; &#39;}        &lt;button onClick={this.incrementAsync}&gt;increment async&lt;/button&gt;      &lt;/div&gt;    )  }}</code></pre><p>6)     <code>containters/app.jsx</code></p><pre><code class="jsx">/*包含Counter组件的容器组件 */import React from &#39;react&#39;// 引入连接函数import {connect} from &#39;react-redux&#39;// 引入action函数import {increment, decrement} from &#39;../redux/actions&#39;import Counter from &#39;../components/counter&#39;// 向外暴露连接App组件的包装组件export default connect(  state =&gt; ({count: state}),  {increment, decrement})(Counter)</code></pre><p>7)     <code>store.js</code></p><p>不变</p><p>8)     <code>index.js</code></p><pre><code class="javascript">import React from &#39;react&#39;import ReactDOM from &#39;react-dom&#39;import {Provider} from &#39;react-redux&#39;import App from &#39;./containers/app&#39;import store from &#39;./redux/store&#39;// 定义渲染根组件标签的函数ReactDOM.render(  (    &lt;Provider store={store}&gt;      &lt;App/&gt;    &lt;/Provider&gt;  ),  document.getElementById(&#39;root&#39;))</code></pre><h3><span id="855-问题">8.5.5 问题</span></h3><p>1)    redux默认是不能进行异步处理的, （前面的都是react实现的）</p><p>2)    应用中又需要在redux中执行异步任务(ajax, 定时器)</p><h2><span id="86-redux异步编程">8.6 redux异步编程</span></h2><h3><span id="861-下载redux插件异步中间件">8.6.1 下载redux插件(异步中间件)</span></h3><p><code>npm install --save redux-thunk</code></p><h3><span id="862-reduxstorejs">8.6.2 <code>redux/store.js</code></span></h3><pre><code class="javascript">import React from &#39;react&#39;import {createStore, applyMiddleware} from &#39;redux&#39;import thunk from &#39;redux-thunk&#39;import {composeWithDevTools} from &#39;redux-devtools-extension&#39;import reducers from &#39;./reducers&#39;// 根据counter函数创建store对象export default createStore(  reducers,  applyMiddleware(thunk) // 应用上异步中间件)</code></pre><h3><span id="863-indexjs">8.6.3 <code>index.js</code></span></h3><pre><code class="javascript">import React from &#39;react&#39;import ReactDOM from &#39;react-dom&#39;import {Provider} from &#39;react-redux&#39;import App from &#39;./containers/app&#39;import store from &#39;./redux/store&#39;// 定义渲染根组件标签的函数ReactDOM.render(  (    &lt;Provider store={store}&gt;      &lt;App/&gt;    &lt;/Provider&gt;  ),  document.getElementById(&#39;root&#39;))</code></pre><h3><span id="864-reduxactionsjs">8.6.4 <code>redux/actions.js</code></span></h3><pre><code class="jsx">/*action creator模块 */import {  INCREMENT,  DECREMENT} from &#39;./action-types&#39;export const increment = number =&gt; ({type: INCREMENT, number})export const decrement = number =&gt; ({type: DECREMENT, number})// 异步action creator(返回一个函数)export const incrementAsync = number =&gt; {  return dispatch =&gt; { // 要在这返回一个函数，得在store中应用上异步中间件    // 异步的代码    setTimeout(() =&gt; {      // 1s之后才分发一个增加的action      dispatch(increment(number))    }, 1000)  }}</code></pre><p> 同步的action都返回一个对象</p><p>异步的action返回的是一个函数</p><h3><span id="865-componentscounterjsx">8.6.5 <code>components/counter.jsx</code></span></h3><pre><code class="jsx">/*包含Counter组件的容器组件 */import React from &#39;react&#39;import PropTypes from &#39;prop-types&#39;export default class Counter extends React.Component {  static propTypes = {    count: PropTypes.number.isRequired,    increment: PropTypes.func.isRequired,    decrement: PropTypes.func.isRequired  }  increment = () =&gt; {    const number = this.refs.numSelect.value*1    this.props.increment(number)  }  decrement = () =&gt; {    const number = this.refs.numSelect.value*1    this.props.decrement(number)  }  incrementIfOdd = () =&gt; {    const number = this.refs.numSelect.value*1    let count = this.props.count    if(count%2===1) {      this.props.increment(number)    }  }  // Async  incrementAsync = () =&gt; {    const number = this.refs.numSelect.value*1    this.props.incrementAsync(number)  }  render () {    return (      &lt;div&gt;        &lt;p&gt;          click {this.props.count} times {&#39; &#39;}        &lt;/p&gt;        &lt;select ref=&quot;numSelect&quot;&gt;          &lt;option value=&quot;1&quot;&gt;1&lt;/option&gt;          &lt;option value=&quot;2&quot;&gt;2&lt;/option&gt;          &lt;option value=&quot;3&quot;&gt;3&lt;/option&gt;        &lt;/select&gt;{&#39; &#39;}        &lt;button onClick={this.increment}&gt;+&lt;/button&gt;{&#39; &#39;}        &lt;button onClick={this.decrement}&gt;-&lt;/button&gt;{&#39; &#39;}        &lt;button onClick={this.incrementIfOdd}&gt;increment if odd&lt;/button&gt;{&#39; &#39;}        &lt;button onClick={this.incrementAsync}&gt;increment async&lt;/button&gt;      &lt;/div&gt;    )  }}</code></pre><h3><span id="866-containersappjsx">8.6.6 <code>containers/app.jsx</code></span></h3><pre><code class="jsx">/*包含Counter组件的容器组件 */import React from &#39;react&#39;// 引入连接函数import {connect} from &#39;react-redux&#39;// 引入action函数import {increment, decrement, incrementAsync} from &#39;../redux/actions&#39;import Counter from &#39;../components/counter&#39;// 向外暴露连接App组件的包装组件export default connect(  state =&gt; ({count: state.counter}),  {increment, decrement, incrementAsync})(Counter)</code></pre><p><code>redux/action-types.js</code></p><pre><code class="javascript">export const INCREMENT = &#39;increment&#39;export const DECREMENT = &#39;decrement&#39;</code></pre><h2><span id="87-使用上redux调试工具">8.7 使用上redux调试工具</span></h2><h3><span id="871-安装chrome浏览器插件">8.7.1 安装chrome浏览器插件</span></h3><p><a href="https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?utm_source=chrome-ntp-icon" target="_blank" rel="noopener">Redux DevTools</a></p><h3><span id="872-下载工具依赖包">8.7.2 下载工具依赖包</span></h3><pre><code>npm install --save-dev redux-devtools-extension</code></pre><h3><span id="873-编码">8.7.3 编码</span></h3><pre><code class="javascript">import { composeWithDevTools } from &#39;redux-devtools-extension&#39;const store = createStore(  counter,  composeWithDevTools(applyMiddleware(thunk)))</code></pre><h2><span id="88-redux版评论">8.8 Redux版评论</span></h2><h3><span id="881-需求">8.8.1 需求</span></h3><p>使用react_redux和中间件实现异步评论功能</p><h3><span id="882-安装">8.8.2 安装</span></h3><pre><code class="bash">……npm install --save react-reduxnpm install --save redux-thunknpm install --save-dev redux-devtools-extension</code></pre><h3><span id="883-目录结构">8.8.3 目录结构</span></h3><blockquote><ul><li>REACT_REDUX<ul><li>public<ul><li>css<ul><li>bootstrap.css</li></ul></li><li>index.html</li></ul></li><li>src<ul><li>components<ul><li>app<ul><li>app.jsx</li></ul></li><li>comment-add<ul><li>comment-add.jsx</li></ul></li><li>comment-item<ul><li>comment-item.jsx</li><li>comment-item.css</li></ul></li><li>comment-list<ul><li>comment-list.jsx</li><li>comment-list.css</li></ul></li></ul></li><li>redux<ul><li>action-types.js</li><li>actions.js</li><li>reducers.js</li><li>store.js</li></ul></li><li>index.js</li></ul></li></ul></li></ul></blockquote><h3><span id="884-文件内容">8.8.4 文件内容</span></h3><ul><li><code>public/index.html</code></li></ul><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;  &lt;head&gt;    &lt;meta charset=&quot;utf-8&quot;&gt;    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;    &lt;meta name=&quot;theme-color&quot; content=&quot;#000000&quot;&gt;    &lt;link rel=&quot;manifest&quot; href=&quot;%PUBLIC_URL%/manifest.json&quot;&gt;    &lt;link rel=&quot;shortcut icon&quot; href=&quot;%PUBLIC_URL%/favicon.ico&quot;&gt;    &lt;title&gt;React App&lt;/title&gt;    &lt;link rel=&quot;stylesheet&quot; href=&quot;/css/bootstrap.css&quot;&gt;  &lt;/head&gt;  &lt;body&gt;    &lt;noscript&gt;      You need to enable JavaScript to run this app.    &lt;/noscript&gt;    &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;  &lt;/body&gt;&lt;/html&gt;</code></pre><ul><li><code>src/index.js</code></li></ul><pre><code class="javascript">import React from &#39;react&#39;import ReactDOM from &#39;react-dom&#39;import {Provider} from &#39;react-redux&#39;import App from &#39;./components/app/app&#39;import store from &#39;./redux/store&#39;// 定义渲染根组件标签的函数ReactDOM.render(  (    &lt;Provider store={store}&gt;      &lt;App/&gt;    &lt;/Provider&gt;  ),  document.getElementById(&#39;root&#39;))</code></pre><ul><li><code>src/componets/app/app.jsx</code></li></ul><pre><code class="jsx">import React from &#39;react&#39;import {connect} from &#39;react-redux&#39;import CommentAdd from &#39;../comment-add/comment-add&#39;import CommentList from &#39;../comment-list/comment-list&#39;import {getComments} from &#39;../../redux/actions&#39;class App extends React.Component {  componentDidMount() {    //模拟异步获取数据    this.props.getComments()  }  render() {    return (      &lt;div&gt;        &lt;header className=&quot;site-header jumbotron&quot;&gt;          &lt;div className=&quot;container&quot;&gt;            &lt;div className=&quot;row&quot;&gt;              &lt;div className=&quot;col-xs-12&quot;&gt;                &lt;h1&gt;请发表对React的评论&lt;/h1&gt;              &lt;/div&gt;            &lt;/div&gt;          &lt;/div&gt;        &lt;/header&gt;        &lt;div className=&quot;container&quot;&gt;          &lt;CommentAdd/&gt;          &lt;CommentList/&gt;        &lt;/div&gt;      &lt;/div&gt;    )  }}export default connect(  null,  {getComments})(App)</code></pre><ul><li><code>src/componets/comment-add/comment-add.jsx</code><pre><code class="jsx">import React from &#39;react&#39;import PropTypes from &#39;prop-types&#39;import {connect} from &#39;react-redux&#39;import {addComment} from &#39;../../redux/actions&#39;class CommentAdd extends React.Component {constructor (props) {  super(props)  this.state = {    username: &#39;&#39;,    content: &#39;&#39;  }  this.addComment = this.addComment.bind(this)  this.changeUsername = this.changeUsername.bind(this)  this.changeContent = this.changeContent.bind(this)}addComment () {  // 根据输入的数据创建评论对象  let { username, content } = this.state  let comment = { username, content }  // 添加到comments中, 更新state  this.props.addComment(comment)  // 清除输入的数据  this.setState({    username: &#39;&#39;,    content: &#39;&#39;  })}changeUsername (event) {  this.setState({    username: event.target.value  })}changeContent (event) {  this.setState({    content: event.target.value  })}render () {  return (    &lt;div className=&quot;col-md-4&quot;&gt;      &lt;form className=&quot;form-horizontal&quot;&gt;        &lt;div className=&quot;form-group&quot;&gt;          &lt;label&gt;用户名&lt;/label&gt;          &lt;input type=&quot;text&quot; className=&quot;form-control&quot; placeholder=&quot;用户名&quot;                 value={this.state.username} onChange={this.changeUsername}/&gt;        &lt;/div&gt;        &lt;div className=&quot;form-group&quot;&gt;          &lt;label&gt;评论内容&lt;/label&gt;          &lt;textarea className=&quot;form-control&quot; rows=&quot;6&quot; placeholder=&quot;评论内容&quot;                    value={this.state.content} onChange={this.changeContent}&gt;&lt;/textarea&gt;        &lt;/div&gt;        &lt;div className=&quot;form-group&quot;&gt;          &lt;div className=&quot;col-sm-offset-2 col-sm-10&quot;&gt;            &lt;button type=&quot;button&quot; className=&quot;btn btn-default pull-right&quot; onClick={this.addComment}&gt;提交&lt;/button&gt;          &lt;/div&gt;        &lt;/div&gt;      &lt;/form&gt;    &lt;/div&gt;  )}}CommentAdd.propTypes = {addComment: PropTypes.func.isRequired}export default connect(null,{addComment})(CommentAdd)</code></pre></li></ul><ul><li><code>src/componets/comment-item/comment-item.jsx</code><pre><code class="jsx">import React from &#39;react&#39;import PropTypes from &#39;prop-types&#39;import {connect} from &#39;react-redux&#39;import &#39;./commentItem.css&#39;import {deleteComment} from &#39;../../redux/actions&#39;class CommentItem extends React.Component {constructor(props) {  super(props)}deleteComment = () =&gt; {  let username = this.props.comment.username  if (window.confirm(`确定删除${username}的评论吗?`)) {    this.props.deleteComment(this.props.index)  }}render() {  let comment = this.props.comment  return (    &lt;li className=&quot;list-group-item&quot;&gt;      &lt;div className=&quot;handle&quot;&gt;        &lt;a href=&quot;javascript:&quot; onClick={this.deleteComment}&gt;删除&lt;/a&gt;      &lt;/div&gt;      &lt;p className=&quot;user&quot;&gt;&lt;span&gt;{comment.username}&lt;/span&gt;&lt;span&gt;说:&lt;/span&gt;&lt;/p&gt;      &lt;p className=&quot;centence&quot;&gt;{comment.content}&lt;/p&gt;    &lt;/li&gt;  )}}CommentItem.propTypes = {comment: PropTypes.object.isRequired,index: PropTypes.number.isRequired,deleteComment: PropTypes.func.isRequired}export default connect(null,{deleteComment})(CommentItem)</code></pre></li></ul><pre><code>- `src/componets/comment-item/comment-item.css````cssli {  transition: .5s;  overflow: hidden;}.handle {  width: 40px;  border: 1px solid #ccc;  background: #fff;  position: absolute;  right: 10px;  top: 1px;  text-align: center;}.handle a {  display: block;  text-decoration: none;}.list-group-item .centence {  padding: 0px 50px;}.user {  font-size: 22px;}</code></pre><ul><li><code>src/componets/comment-list/comment-list.jsx</code><pre><code class="jsx">import React from &#39;react&#39;import PropTypes from &#39;prop-types&#39;import {connect} from &#39;react-redux&#39;import CommentItem from &#39;../comment-item/comment-item&#39;import &#39;./commentList.css&#39;class CommentList extends React.Component {render () {  let comments = this.props.comments  let display = comments.length &gt; 0 ? &#39;none&#39; : &#39;block&#39;  return (    &lt;div className=&quot;col-md-8&quot;&gt;      &lt;h3 className=&quot;reply&quot;&gt;评论回复：&lt;/h3&gt;      &lt;h2 style=双大括号 display: display 双大括号&gt;暂无评论，点击左侧添加评论！！！&lt;/h2&gt;      &lt;ul className=&quot;list-group&quot;&gt;        {          comments.map((comment, index) =&gt; {            console.log(comment)            return &lt;CommentItem comment={comment} key={index} index={index}/&gt;          })        }      &lt;/ul&gt;    &lt;/div&gt;  )}}CommentList.propTypes = {comments: PropTypes.array.isRequired,}export default connect(state =&gt; ({comments: state.comments}))(CommentList)</code></pre></li></ul><ul><li><code>src/componets/comment-list/comment-list.css</code><pre><code class="css">.reply {margin-top: 0px;}</code></pre></li></ul><ul><li><code>redux/action-types.js</code><pre><code class="javascript">export const ADD_COMMENT = &#39;ADD_COMMENT&#39;export const DELETE_COMMENT = &#39;DELETE_COMMENT&#39;export const RECEIVE_COMMENTS = &#39;RECEIVE_COMMENTS&#39;</code></pre></li></ul><ul><li><code>redux/actions.js</code><pre><code class="javascript">import {ADD_COMMENT,DELETE_COMMENT,RECEIVE_COMMENTS} from &#39;./action-types&#39;export const addComment = (comment) =&gt; ({type: ADD_COMMENT, data: comment})export const deleteComment = (index) =&gt; ({type: DELETE_COMMENT, data: index})const receiveComments = (comments) =&gt; ({type: RECEIVE_COMMENTS, data: comments})export const getComments = () =&gt; {return dispatch =&gt; {  setTimeout(() =&gt; {    const comments = [      {        username: &quot;Tom&quot;,        content: &quot;ReactJS好难啊!&quot;,        id: Date.now()      },      {        username: &quot;JACK&quot;,        content: &quot;ReactJS还不错!&quot;,        id: Date.now() + 1      }    ]    dispatch(receiveComments(comments))  }, 1000)}}</code></pre></li></ul><ul><li><code>redux/reducers.js</code></li></ul><pre><code class="javascript">import {combineReducers} from &#39;redux&#39;import {  ADD_COMMENT,  DELETE_COMMENT,  RECEIVE_COMMENTS} from &#39;./action-types&#39;const initComments = []function comments(state = initComments, action) {  switch (action.type) {    case ADD_COMMENT:      return [...state, action.data]    case DELETE_COMMENT:      return state.filter((c, index) =&gt; index !== action.data)    case RECEIVE_COMMENTS:      return action.data    default:      return state  }}export default combineReducers({  comments})</code></pre><ul><li><code>redux/store.js</code></li></ul><pre><code class="javascript">import React from &#39;react&#39;import {createStore, applyMiddleware} from &#39;redux&#39;import thunk from &#39;redux-thunk&#39;import {composeWithDevTools} from &#39;redux-devtools-extension&#39;import reducers from &#39;./reducers&#39;// 根据counter函数创建store对象export default createStore(  reducers,  composeWithDevTools(applyMiddleware(thunk)) // 应用上异步中间件)</code></pre><h2><span id="89-相关重要知识-纯函数和高阶函数">8.9 相关重要知识: 纯函数和高阶函数</span></h2><h3><span id="891-纯函数">8.9.1 纯函数</span></h3><p>1)     一类特别的函数: 只要是同样的输入，必定得到同样的输出</p><p>2)    必须遵守以下一些约束 </p><p>a.    不得改写参数</p><p>b.    不能调用系统 I/O 的API</p><p>c.    能调用Date.now()或者Math.random()等不纯的方法 </p><p>3)     reducer函数必须是一个纯函数</p><h3><span id="892-高阶函数">8.9.2 高阶函数</span></h3><p>4)     理解: 一类特别的函数</p><p>a.     情况1: 参数是函数</p><p>b.     情况2: 返回是函数</p><p>5)         常见的高阶函数: </p><p>a.     定时器设置函数</p><p>b.     数组的map()/filter()/reduce()/find()/bind()</p><p>c.     react-redux中的connect函数</p><p>6)         作用: </p><p>​     能实现更加动态, 更加可扩展的功能</p><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="笔记" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="前端" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="前端" scheme="https://wallleap.cn/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="框架" scheme="https://wallleap.cn/tags/%E6%A1%86%E6%9E%B6/"/>
    
      <category term="React" scheme="https://wallleap.cn/tags/React/"/>
    
  </entry>
  
  <entry>
    <title>Git学习笔记</title>
    <link href="https://wallleap.cn/2020/07/04/Git/"/>
    <id>https://wallleap.cn/2020/07/04/Git/</id>
    <published>2020-07-04T15:33:49.000Z</published>
    <updated>2025-01-24T09:22:15.348Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2><span id="版本控制">版本控制</span></h2><h3><span id="集中式svn">集中式(svn)</span></h3><p>svn因为每次存的都是差异 需要的硬盘空间会相对的小一点  可是回滚的速度会很慢</p><ul><li>优点: <pre><code>代码存放在单一的服务器上 便于项目的管理</code></pre></li><li>缺点: <pre><code>服务器宕机: 员工写的代码得不到保障服务器炸了: 整个项目的历史记录都会丢失</code></pre></li></ul><h3><span id="分布式git">分布式(git)</span></h3><p>git每次存的都是项目的完整快照 需要的硬盘空间会相对大一点<br>    (Git团队对代码做了极致的压缩 最终需要的实际空间比svn多不了太多 可是Git的回滚速度极快)</p><ul><li>优点:<pre><code>完全的分布式</code></pre></li><li>缺点:    <pre><code>学习起来比SVN陡峭</code></pre></li></ul><h2><span id="git底层命令">Git底层命令</span></h2><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/git.webp" alt="git"></p><h3><span id="底层命令">底层命令</span></h3><pre><code>git对象    git hash-object -w fileUrl : 生成一个key(hash值):val(压缩后的文件内容)键值对存到.git/objectstree对象    git update-index --add --cacheinfo 100644 hash test.txt : 往暂存区添加一条记录(让git对象 对应 上文件名)存到.git/index    git write-tree : 生成树对象存到.git/objectscommit对象    echo &#39;first commit&#39; | git commit-tree treehash : 生成一个提交对象存到.git/objects对以上对象的查询    git cat-file -p hash       : 拿对应对象的内容    git cat-file -t hash       : 拿对应对象的类型</code></pre><h3><span id="查看暂存区">查看暂存区</span></h3><pre><code>git ls-files -s        </code></pre><p>​    </p><h2><span id="git高层命令">Git高层命令</span></h2><h3><span id="安装">安装</span></h3><pre><code>git --version</code></pre><h3><span id="初始化配置">初始化配置</span></h3><pre><code>git config --global user.name &quot;damu&quot;git config --global user.email damu@example.com    git config --list</code></pre><h3><span id="初始化仓库">初始化仓库</span></h3><pre><code>git init</code></pre><h3><span id="c新增">C(新增)</span></h3><pre><code>在工作目录中新增文件git statusgit add ./git commit -m &quot;msg&quot;    </code></pre><h3><span id="u修改">U(修改)</span></h3><pre><code>在工作目录中修改文件git statusgit add ./git commit -m &quot;msg&quot;     </code></pre><h3><span id="d删除-amp-重命名">D(删除 &amp; 重命名)</span></h3><p>   git rm 要删除的文件     git mv 老文件 新文件<br>   git  status             git  status<br>   git commit -m “msg”     git commit -m “msg”</p><h3><span id="r查询">R(查询)</span></h3><p>   git  status   :  查看工作目录中文件的状态(已跟踪(已提交 已暂存 已修改) 未跟踪)<br>   git  diff     :  查看未暂存的修改<br>   git  diff –cache : 查看未提交的暂存<br>   git  log –oneline : 查看提交记录</p><h3><span id="分支">分支</span></h3><pre><code>分支的本质其实就是一个提交对象!!!HEAD:     是一个指针 它默认指向master分支 切换分支时其实就是让HEAD指向不同的分支    每次有新的提交时 HEAD都会带着当前指向的分支 一起往前移动git  log --oneline --decorate --graph --all : 查看整个项目的分支图  git branch : 查看分支列表git branch -v: 查看分支指向的最新的提交git branch name : 在当前提交对象上创建新的分支git branch name commithash: 在指定的提交对象上创建新的分支git checkout name :     切换分支git branch -d name : 删除空的分支 删除已经被合并的分支git branch -D name : 强制删除分支 </code></pre><p>​       </p><h2><span id="git分支">Git分支</span></h2><h3><span id="git分支本质">git分支本质</span></h3><pre><code>分支本质是一个提交对象,所有的分支都会有机会被HEAD所引用(HEAD一个时刻只会指向一个分支)当我们有新的提交的时候 HEAD会携带当前持有的分支往前移动</code></pre><h3><span id="git分支命令">git分支命令</span></h3><pre><code>创建分支            : git branch branchname切换分支           : git checkout  branchname创建&amp;切换分支     : git checkout -b branchname版本穿梭(时光机) :  git branch branchname commitHash  普通删除分支      : git  branch -d branchname强制删除分支      : git  branch -D branchname合并分支         : git merge branchname    快进合并 --&gt; 不会产生冲突    典型合并 --&gt; 有机会产生冲突    解决冲突 --&gt; 打开冲突的文件 进行修改 add commit </code></pre><p>​    </p><pre><code>查看分支列表 : git branch查看合并到当前分支的分支列表: git branch --merged    一旦出现在这个列表中 就应该删除查看没有合并到当前分支的分支列表: git branch --no-merged    一旦出现在这个列表中 就应该观察一下是否需要合并</code></pre><h3><span id="git分支的注意点">git分支的注意点</span></h3><pre><code>在切换的时候 一定要保证当前分支是干净的!!!    允许切换分支:         分支上所有的内容处于 已提交状态            (避免)分支上的内容是初始化创建 处于未跟踪状态        (避免)分支上的内容是初始化创建 第一次处于已暂存状态    不允许切分支:         分支上所有的内容处于 已修改状态  或 第二次以后的已暂存状态  在分支上的工作做到一半时 如果有切换分支的需求, 我们应该将现有的工作存储起来    git stash : 会将当前分支上的工作推到一个栈中    分支切换  进行其他工作 完成其他工作后 切回原分支    git stash apply : 将栈顶的工作内容还原 但不让任何内容出栈     git stash drop  : 取出栈顶的工作内容后 就应该将其删除(出栈)    git stash pop   :      git stash apply +  git stash drop     git stash list : 查看存储</code></pre><h3><span id="后悔药">后悔药</span></h3><pre><code>撤销工作目录的修改   :  git checkout -- filename撤销暂存区的修改     :  git reset HEAD  filename撤销提交             :  git commit --amend</code></pre><h3><span id="reset三部曲">reset三部曲</span></h3><pre><code>git reset --soft commithash    ---&gt; 用commithash的内容重置HEAD内容git reset [--mixed] commithash ---&gt; 用commithash的内容重置HEAD内容 重置暂存区git reset --hard commithash    ---&gt; 用commithash的内容重置HEAD内容 重置暂存区 重置工作目录</code></pre><h3><span id="路径reset">路径reset</span></h3><pre><code>所有的路径reset都要省略第一步!!!    第一步是重置HEAD内容  我们知道HEAD本质指向一个分支 分支的本质是一个提交对象     提交对象 指向一个树对象 树对象又很有可能指向多个git对象 一个git对象代表一个文件!!!    HEAD可以代表一系列文件的状态!!!!git reset [--mixed] commithash filename       用commithash中filename的内容重置暂存区</code></pre><h3><span id="checkout深入理解">checkout深入理解</span></h3><pre><code>git   checkout brancname  跟   git reset --hard commithash特别像    共同点        都需要重置 HEAD   暂存区   工作目录    区别         checkout对工作目录是安全的    reset --hard是强制覆盖         checkout动HEAD时不会带着分支走而是切换分支         reset --hard时是带着分支走checkout + 路径      git checkout commithash  filename              重置暂存区           重置工作目录      git checkout -- filename            重置工作目录  </code></pre><h2><span id="eslint">eslint</span></h2><h3><span id="eslint">eslint</span></h3><pre><code>js代码的检查工具下载: npm i eslint -D使用:    生成配置文件 npx eslint --init    检查js文件   npx eslint 目录名    命中的规则:        字符串必须使用单引号        语句结尾不能有分号        文件的最后必须要有换行</code></pre><h3><span id="eslint结合git">eslint结合git</span></h3><pre><code>husky: 哈士奇, 为Git仓库设置钩子程序使用    在仓库初始化完毕之后 再去安装哈士奇    在package.json文件写配置        &quot;husky&quot;: {            &quot;hooks&quot;: {              &quot;pre-commit&quot;: &quot;npm run lint&quot;                 //在git commit之前一定要通过npm run lint的检查              // 只有npm run lint不报错时 commit才能真正的运行            }          }           </code></pre><h2><span id="远程协作">远程协作</span></h2><h3><span id="三个必须懂得概念">三个必须懂得概念</span></h3><pre><code>本地分支远程跟踪分支(remote/分支名)远程分支</code></pre><h3><span id="远程协作的基本流程">远程协作的基本流程</span></h3><pre><code>第一步: 项目经理创建一个空的远程仓库第二步: 项目经理创建一个待推送的本地仓库第三步: 为远程仓库配别名  配完用户名 邮箱第四步: 在本地仓库中初始化代码 提交代码第五步: 推送第六步: 邀请成员第七步: 成员克隆远程仓库第八步: 成员做出修改第九步: 成员推送自己的修改第十步: 项目经理拉取成员的修改</code></pre><h3><span id="做跟踪">做跟踪</span></h3><pre><code>克隆才仓库时 会自动为master做跟踪本地没有分支    git checkout --track 远程跟踪分支(remote/分支名)本地已经创建了分支    git branch -u 远程跟踪分支(remote/分支名)</code></pre><h3><span id="推送">推送</span></h3><pre><code>git push</code></pre><h3><span id="拉取">拉取</span></h3><pre><code>git pull</code></pre><h3><span id="pull-request">pull request</span></h3><pre><code>让第三方人员参与到项目中 fork</code></pre><h3><span id="使用频率最高的五个命令">使用频率最高的五个命令</span></h3><pre><code>git statusgit addgit commitgit pushgit pull</code></pre><p>​</p><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="笔记" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="前端" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="前端" scheme="https://wallleap.cn/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="Git" scheme="https://wallleap.cn/tags/Git/"/>
    
  </entry>
  
  <entry>
    <title>NodeJs学习笔记</title>
    <link href="https://wallleap.cn/2020/06/14/nodejs/"/>
    <id>https://wallleap.cn/2020/06/14/nodejs/</id>
    <published>2020-06-14T03:33:49.000Z</published>
    <updated>2025-01-24T09:22:15.353Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2><span id="一-基本知识">一、基本知识</span></h2><h3><span id="1-命令行窗口">1、命令行窗口</span></h3><ul><li><p>又称cmd窗口、终端、shell</p></li><li><p>打开方式：</p><ul><li><p>win：<kbd>win</kbd>+<kbd>R</kbd>，输入<code>cmd</code>，打开</p></li><li><p>mac\linux: <code>terminal</code></p></li></ul></li><li><p>前面显示的为目前所在的目录</p></li><li><p>常用的命令</p><ul><li>dir 列出当前目录下所有文件</li><li>cd  目录名 进入到指定的目录</li><li>目录<ul><li>. 表示当前目录</li><li>.. 表示上一级目录</li></ul></li><li>md 目录名 创建一个文件夹</li><li>rd 目录名 删除一个文件夹</li><li>输入当前目录下的一个文件名(包括后缀)可以打开这个文件</li></ul></li><li><p>环境变量(Windows系统中的变量)</p><ul><li>path  </li><li>当我们在命令行窗口打开一个文件，或调用一个程序时，系统会首先在当前目录下寻找文件程序，如果找到了则直接打开，如果没有找到则会依次到环境变量path的路径中寻找，知道找到为止，如果没找到则报错</li><li>所以我们可以将一些经常需要访问的程序和文件的路径添加到paht中，这样我们就可以在任意位置来访问这些文件和程序了</li></ul></li></ul><h3><span id="2-进程和线程">2、进程和线程</span></h3><h4><span id="进程">进程</span></h4><ul><li>进程负责为程序的运行提供必备的环境</li><li>进程就相当于工厂中的车间</li></ul><h4><span id="线程">线程</span></h4><ul><li>线程是计算机中的最小的计算单位，线程负责执行进程中的程序</li><li>线程就相当于工厂中的工人</li><li><ul><li>单线程<ul><li>js是单线程</li></ul></li><li>多线程</li></ul></li></ul><h2><span id="二-nodejs简介">二、Node.js简介</span></h2><ul><li>nodejs是一个能够在服务器端运行js的开放源代码、跨平台的js运行环境</li><li>node采用google开发的v8引擎运行js代码，使用</li></ul><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/1588819260571.png" alt="1588819260571"></p><h2><span id="三-node使用">三、node使用</span></h2><pre><code class="shell">node&gt; var i = 0&gt; console.log(i)</code></pre><pre><code class="shell">node hello.js</code></pre><h2><span id="四-commonjs规范">四、COMMONJS规范</span></h2><h3><span id="1-ecmascript标准的缺陷">1、ECMAScript标准的缺陷</span></h3><ul><li>没有模块系统</li><li>标准库较少</li><li>没有标准接口</li><li>缺乏管理系统</li></ul><h3><span id="2-模块化-modulejs">2、模块化 module.js</span></h3><ul><li><p>在node中，一个js就是一个模块</p></li><li><p>在Node中，每一个js文件中的js代码都是独立运行在一个函数中，而不是全局作用域，所以一个模块中的变量和函数在其他模块中无法访问</p></li><li><p>我们可以通过exports来向外部暴露变量和方法，只需要将要暴露给外部的变量或方法设置为exports的属性即可</p><pre><code class="javascript">exports.x=&quot;我是modeule.js中的x&quot;exports.fn = function(){}</code></pre></li><li><p>在Node中有一个全局对象global，它的作用和网页中window类似，在全局中创建的变量都会作为global的属性保存，在全局中创建的函数都会作为global的方法保存</p><pre><code class="javascript">var a = 10;console.log(global.a);</code></pre></li><li><p>arguments</p><ul><li>arguments.callee 这个属性保存的是当前执行的函数对象</li></ul></li><li><p>在node执行模块中的代码时，它首先会在代码的最顶部添加代码<code>function(exports, require, module, __filename, __dirname){</code>，在代码的最底部，添加代码<code>}</code>.实际上模块中的代码都是包装在一个函数中执行的，并且在函数执行时，同时传递进了5个实参(<code>arguments.length</code>)。</p><ul><li><p>exports 该对象用来将变量或函数暴露到外部</p></li><li><p>require 函数，用来引入外部的模块</p></li><li><p>module </p><ul><li><p>module代表的是当前模块本身</p></li><li><p>exports就是module的属性</p></li><li><p>既可以使用exports导出，也可以使用modeule.exports导出 <code>module.exports==exports</code>，<code>module.exports.a  exports.a</code></p></li><li><p>通过exports只能使用.的方式来向外暴露内部变量，而modele.exports既可以通过.的形式，也可以直接复制</p><pre><code class="javascript">exports.xxx = xxxmodule.exports.xxx = xxxmodule.exports = {    name = &quot;swk&quot;    age = 15000    sayName = function{        console.log(name)    }}</code></pre></li></ul></li></ul></li></ul><ul><li><p>__filename 当前模块的完整路径</p></li><li><p>__dirname 当前模块所在文件夹的完整路径</p></li></ul><ul><li>在node中通过require()函数来引入外部的模块，require()可以传递一个文件的路径作为参数，node将会自动根据该路径来引入外部模块，这里的路径如果使用相对路径，必须以.或..开头。使用require引入模块后，该函数会返回一个对象，这个对象代表的是引入的模块</li></ul><pre><code class="javascript">var md = require(&quot;./modele.js&quot;);console.log(md)</code></pre><ul><li><p>模块分为两大类</p><ul><li><p>核心模块</p><ul><li>由node引擎提供的模块</li></ul></li><li><p>文件模块</p><ul><li>由用户自己创建的模块</li></ul></li></ul></li></ul><h3><span id="3-commonjs规范">3、CommonJS规范</span></h3><ul><li>CommonJS规范的提出，主要是为了弥补当前JavaScript没有标准的缺陷。</li><li>CommonJS规范为js制定了一个美好的愿景，希望js能够在任何地方运行。</li><li>CommonJS规范对模块的定义十分简单<ul><li>模块引用</li><li>模块定义</li><li>模块标识<ul><li>我们使用require()引入外部模块时，使用的就是模块标识，我们可以通过模块标识来找到指定的模块</li><li>核心模块的标识就是模块的名字</li><li>文件模块的标识就是文件的路径(绝对、相对)，相对路径使用.或..开头</li></ul></li></ul></li></ul><h2><span id="五-包-package">五、包 Package</span></h2><h3><span id="1-简介">1、简介</span></h3><ul><li>CommonJS的包规范允许我们将一组相关的<strong>模块组合到一起</strong>，形成一组完整的工具。</li><li>CommonJS的包规范由包结构和包描述文件两个部分组成</li><li>包结构：用于组织包中的各种文件</li><li>包描述文件：描述包的相关信息，以供外部读取分析</li></ul><h3><span id="2-包结构">2、包结构</span></h3><p>包实际上就是一个压缩文件，解压以后还原为目录。</p><p>符合规范的目录，应该包含如下文件：</p><ul><li><p><strong>package.json 描述文件</strong></p><pre><code class="json">{    &quot;dependecies&quot;: {}, // 依赖    &quot;description&quot;: &quot;描述&quot;,    &quot;devDependencies&quot;: { // 开发依赖        ...    }    ...    &quot;version&quot;: &quot;v1.0.0&quot;}</code></pre></li></ul><ul><li><p>bin 可执行二进制文件</p></li><li><p>lib js代码</p></li><li><p>doc 文档</p></li><li><p>test 单元测试</p></li></ul><h3><span id="3-包描述文件">3、包描述文件</span></h3><p>包描述文件用于表达非代码相关的信息，它是一个JSON格式的文件——package.json，位于包的根目录下，是包的重要组成部分</p><p>package.json中的字段(不能写注释)</p><ul><li><code>name</code></li><li><code>description</code></li><li><code>version</code></li><li><code>keywords</code></li><li><code>maintainers</code></li><li><code>contributors</code></li><li><code>bugs</code></li><li><code>licenses</code></li><li><code>repositories</code></li><li><code>dependencies</code></li><li><code>homepage</code></li><li><code>os</code></li><li><code>cpu</code></li><li><code>engine</code></li><li><code>builtin</code></li><li><code>directories</code></li><li><code>implements</code></li><li><code>scripts</code></li><li><code>author</code></li><li><code>bin</code></li><li><code>main</code></li><li><code>devDependencies</code></li></ul><ol><li>标识</li><li>依赖</li><li>运行/打包</li></ol><p>通过<code>npm run</code></p><h2><span id="六-npmnode-package-manager">六、NPM(Node Package Manager)</span></h2><h3><span id="1-简介">1、简介</span></h3><ul><li><p>CommonJS包规范是理论，NPM是其中一种实践</p></li><li><p>对于Node而言，NPM帮助其完成了第三方模块的发布、安装和依赖等。借助NPM，Node与第三方模块之间形成了很好的一个生态系统。</p></li></ul><h3><span id="2-npm命令">2、NPM命令</span></h3><ul><li><code>npm -v</code> 查看npm版本</li><li><code>npm version</code> 查看所有模块版本</li><li><code>npm</code> 帮助说明</li><li><code>npm init</code> 在当前目录下创建package.json文件，驼峰命名改为_连接</li><li><code>npm search 包名</code>   搜索包</li><li><code>npm install/i 包名</code>  安装包，将会在当前目录的<code>node_modules</code>下，直接通过包名引入即可，<code>var math = require(&quot;math&quot;)</code></li><li><code>npm install 包名 -g</code>  全局安装包，一般都是一些工具</li><li><code>npm remove/r 包名</code>  删除一个模块</li><li><code>npm install 包名 --save</code>  <strong>安装包并将其添加到依赖中</strong>，一般不传node_modules，太大、不能保证是最新的，有了依赖，传package.json直接<code>npm install</code>可以下载当前项目所依赖的包</li><li><code>npm uninstall 包名</code>  卸载包</li><li><code>npm install 文件路径</code>  从本地安装</li><li><code>npm install 包名 -registry=地址</code>  从镜像源安装包</li><li><code>npm config set registry 地址</code>  设置镜像源</li></ul><p>淘宝镜像：<a href="https://npm.taobao.org" target="_blank" rel="noopener">https://npm.taobao.org</a></p><p>一般不直接设置官方原版npm替换为其他源，可以使用cnpm</p><ul><li><code>npm install -g cnpm --registry=https://registry.npm.taobao.org</code>  </li></ul><p>注：</p><ul><li>Node在使用模块名字来引入模块时，它会首先在当前目录的node_modules中寻找是否含有该模块，如果有则直接使用，如果没有则去上一级目录的node_modules中寻找，如果有则直接使用，如果没有则再去上一级目录寻找，知道找到为止，如果找到磁盘根目录仍没有，则报错。</li></ul><h2><span id="七-buffer缓冲区">七、Buffer(缓冲区)</span></h2><ul><li><p>Buffer的就结构和数组很像，操作的方法也和数组类似</p></li><li><p>数组中不能存储二进制的文件，而buffer就是专门用来存储<strong>二进制</strong>数据的</p></li><li><p>使用buffer不需要引入模块，直接使用即可</p><pre><code class="javascript">var str = &quot;hello&quot;// 将一个字符串保存到buffer中var buf = Buffer.from(str)console.log(buf)console.log(buf.length,str.length)  // 占用内存的大小,字符串的长度</code></pre></li><li><p>在buffer中存储的都是二进制数据，但是在显示的时候都是以16进制的形式显示的，buffer中每一个元素的范围是从00到ff(0<del>255) 00000000</del>11111111 8位(bit) = 1字节(byte)，buffer中的一个元素占用内存的一个字节     ps：一个汉字3字节，一个英文字母1字节</p></li><li><p>buffer的大小一旦确定，则不能修改，Buffer实际上是对底层内存的直接操作</p><ul><li><code>Buffer.from(str)</code>将一个字符串转为buffer</li><li><code>Buffer.alloc(size)</code>创建一个指定大小的buffer</li><li><code>Buffer.allocUnsafe(size)</code>创建一个指定的大小的buffer，但是可能包含敏感数据</li><li><code>buf.toString()</code>将buffer里的数据转为字符串</li></ul><pre><code class="javascript">// 创建一个指定大小的buffervar buf = new Buffer(10) // 10个字节的buffer// buffer构造函数都是不推荐使用的var buf2 = Buffer.alloc(10)   // 全都是00buf2[0] = 88buf2[1] = 255buf2[2] = 0xaabuf2[3] = 556// 1000101100截取后八位buf2[10] = 15  // 没改变// 只要数字在控制台或页面中输出一定是10进制console.log(buf2[2])console.log(buf2[2].toString(16))  // 可以这样转为16进制显示var buf3 = Buffer.allocUnsafe(10)  // 创建一个指定大小的buffer，但是buffer中可能含有敏感数据，不全为00</code></pre></li></ul><p><a href="https://nodejs.cn" target="_blank" rel="noopener">https://nodejs.cn</a></p><h2><span id="八-文件系统file-system">八、文件系统(File System)</span></h2><h3><span id="1-简介">1、简介</span></h3><ul><li><p>文件系统简单来说就是通过Nodejs来操作系统中的文件</p></li><li><p>在Node中，与文件系统的交互是非常重要的，服务器的本质就是将本地的文件发送给远程的客户端</p></li><li><p>Node通过fs模块来和文件系统进行交互</p></li><li><p>fs模块提供了一些标准文件访问API来打开、读取、写入文件，以及与其交互</p></li><li><p>要使用文件系统，需要先引入fs模块，fs是核心模块，直接引入不需要下载；要使用fs模块，首先需要对其进行加载<code>const fs = require(&quot;fs&quot;)</code></p></li></ul><h3><span id="2-同步和异步调用">2、同步和异步调用</span></h3><ul><li><p>fs模块中所有的操作都有两种形式可供选择，同步(fs.xxx)和异步(fs.xxxSync)</p></li><li><p>同步文件系统会阻塞程序的执行，也就是除非操作完毕，否则不会向下执行代码</p></li><li><p>异步文件系统不会阻塞程序的执行，而是在操作完成时，通过回调函数将结果返回</p></li></ul><h3><span id="3-同步-异步文件写入">3、同步、异步文件写入</span></h3><ul><li><p>同步文件的写入</p><ul><li><p>操作步骤</p><ol><li><p>打开文件</p><ul><li><p><code>fs.openSync(path, flags[, mode])</code></p></li><li><p>path 要打开文件的路径</p></li><li><p>flags 打开文件要做的操作的类型：r只读的、w可写的</p></li><li><p>mode 设置文件的操作权限，一般不传</p></li><li><p>返回值： 返回一个文件的描述符，可以通过该描述符来对文件进行各种操作</p><pre><code class="javascript">var fs = require(&quot;fs&quot;);var fd = fs.openSync(&quot;hello.txt&quot;, &quot;w&quot;);// console.log(fd)</code></pre></li></ul></li><li><p>向文件中写入内容</p><ul><li><p><code>fs.writeSync(fd, string[, position[, encoding]])</code></p></li><li><p>fd 文件的描述符，需要传递要写入的文件的描述符</p></li><li><p>string 要写入的内容</p></li><li><p>position 写入的起始位置</p></li><li><p>encoding 写入的编码，默认utf-8</p><pre><code class="javascript">fs.writeSync(fd, &quot;这是写入的内容&quot;)</code></pre></li></ul></li><li><p>保存并关闭文件</p><ul><li><code>fs.closeSync(fd)</code></li><li>fd 要关闭的文件的描述符</li></ul></li></ol></li></ul></li><li><p>异步文件写入</p><ol><li><p><code>fs.open(path, flags[, mode], callback)</code></p><ul><li><p>用来打开一个文件</p></li><li><p>异步方法没有返回值，有返回值的都是同步方法。异步调用的方法，结果都是通过回调函数参数返回的。</p></li><li><p>callback 回调函数两个参数(arguements):</p><ul><li><p>err 错误对象，如果没有错误则为null</p></li><li><p>fd 文件的描述符</p></li></ul></li></ul><pre><code class="javascript">var fs = require(&quot;fs&quot;)var ffs.open(&quot;hello.txt&quot;,&quot;w&quot;,function(err, fd){    // console.log(&#39;回调函数中的代码&#39;)  // callback中的代码会在读取完毕之后执行    if(!err){        f = fd    }else{        console.log(err)    }})console.log(&quot;open下的代码&quot;)    // 能比上面的更早执行</code></pre><pre><code></code></pre></li></ol></li></ul><ol start="2"><li><p><code>fs.write(fd, string[, position[, encoding]], callback)</code></p><ul><li>用来异步写入一个文件</li></ul><ol start="3"><li><p><code>fs.close(fd, callback)</code></p><ul><li><p>用来关闭文件</p><pre><code class="javascript">var fs = require(&quot;fs&quot;)fs.open(&quot;hello.txt&quot;,&quot;w&quot;,function(err, fd){   if(!err){       fs.write(fd, &quot;这是异步写入的内容&quot;,function(err)          {           if(!err){               console.log(&#39;写入成功&#39;)           }           fs.close(fd, function(err){               if(!err){                   console.log(&#39;文件已关闭&#39;)               }           })       })   }else{       console.log(err)   }})</code></pre></li></ul></li></ol></li></ol><h3><span id="4-简单文件写入">4、简单文件写入</span></h3><ul><li><p><code>fs.writeFile(file, data[, options], callback)</code></p><ul><li><p>file 要操作的文件的路径</p></li><li><p>data 要写入的数据</p></li><li><p>options 选项，可以对写入进行一些设置，是一个对象{encoding, mode, flag}</p><ul><li>encoding: ‘utf8’</li><li>mode: ‘0o666’</li><li>flag: ‘w’   一般用r(只读)、w(可写)、a(追加)</li></ul></li><li><p>callback 当写入完成以后执行的函数</p><pre><code class="javascript">var fs = require(&#39;fs&#39;)// 路径也可以C:/Users/Shinlo/Desktop/hello.txtfs.writeFile(&quot;C:\\Users\\Shinlon\\Desktop\\hello.txt&quot;, &quot;这是通过writeFile写入的内容&quot;, {flag: &quot;a&quot;}, function(err){    if(!err){        console.log(&#39;写入成功&#39;)    }else{        console.log(err)    }})</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/1588844894085.png" alt="1588844894085"></p></li></ul></li><li><p><code>fs.writeFileSync(file, data[, options])</code></p><ul><li>同步简单写入</li></ul></li></ul><h3><span id="5-流式文件写入">5、流式文件写入</span></h3><ul><li><p>同步、异步、简单文件的写入都不适合大文件的写入，性能较差，容易导致内存溢出</p></li><li><p>流式文件写入</p><ul><li><p><code>fs.createWriteStream(path[, options])</code></p><ul><li>可以用来创建一个可写流</li><li>path 文件路径</li><li>options 配置的参数</li></ul><pre><code class="javascript">var fs = require(&quot;fs&quot;)var ws = fs.createWriteStream(&quot;hello.txt&quot;)// 可以通过监听流的open和close事件来监听流的打开和关闭// ws.on(&quot;open&quot;, function{  // on绑定一个长期有效的事件ws.once(&quot;open&quot;, function{   // once绑定一次性的事件，在触发一次之后事件自动失效    console.log(&quot;流打开了&quot;)})ws.once(&quot;close&quot;, function{    console.log(&quot;流关闭了&quot;)})// 通过ws向文件中输出内容ws.write(&quot;通过可写流写入文件的内容1&quot;)ws.write(&quot;通过可写流写入文件的内容2&quot;)ws.write(&quot;通过可写流写入文件的内容3&quot;)// 只要流还存在就可以接着写入// 关闭流// ws.close()    // 这个在传入的方向断开流，文件没到管子里ws.end()        // 在传出的这一方断开流，数据已经在管子里了</code></pre></li></ul></li></ul><h3><span id="6-文件的读取">6、文件的读取</span></h3><ul><li><p>同步文件读取</p></li><li><p>异步文件读取</p></li><li><p>简单文件读取</p><ul><li><p><code>fs.readFile(path[, options], callback)</code></p></li><li><p><code>fs.readFileSync(path[, options])</code></p><ul><li>path 要读取的文件的路径</li><li>options 读取的选项</li><li>callback 回调函数，通过回调函数将读取到的内容返回<ul><li>err 错误对象</li><li>data 读取到的数据，会返回一个Buffer</li></ul></li></ul><pre><code class="javascript">var fs = require(&quot;fs&quot;)var path=&quot;C:/Users/Shinlon/a.mp3&quot;fs.readFile(&quot;hello.txt&quot;, function(err, data){    if(!err){        console.log(data)  // buffer通用性更高        // console.log(data.toString())文本可以，其他不行        fswriteFile(&quot;hello.mp3&quot;, data, function(err){            if(!err){                console.log(&quot;文件写入成功&quot;)            }        })    }})</code></pre></li></ul></li><li><p>流式文件读取</p><ul><li>流式文件读取也适用于一些比较大的文件，可以分多次将文件读取到内存中</li><li><code>fs.createReadStream(path[, options])</code></li></ul><pre><code class="javascript">var fs = require(&quot;fs&quot;)// 创建一个可读流var rs = fs.createReadStream(&quot;an.jpg&quot;)// 监听流的开启和关闭rs.once(&quot;open&quot;, function(){    console.log(&quot;可读流打开了&quot;)})rs.once(&quot;close&quot;, function(){    console.log(&quot;可读流关闭了&quot;)})// 读取一个可读流中的数据，必须要为可读流绑定一个data事件，data事件绑定完毕，它会自动开始读取数据rs.on(&quot;data&quot;, function(data){    console.log(data) // 参数就是数据 data.length最大65536字节})</code></pre></li></ul><p>可读流、可写流复制一个大文件</p><pre><code class="javascript">var fs = require(&quot;fs&quot;)var rs = fs.createReadStream(&quot;an.jpg&quot;)var ws = fs.createWriteStream(&quot;an.jpg&quot;)rs.once(&quot;open&quot;, function(){    console.log(&quot;可读流打开了&quot;)})rs.once(&quot;close&quot;, function(){    console.log(&quot;可读流关闭了&quot;)    // 数据读取完毕，关闭可写流    ws.end()})ws.once(&quot;open&quot;, function(){    console.log(&quot;可写流打开了&quot;)})ws.once(&quot;close&quot;, function(){    console.log(&quot;可写流关闭了&quot;)})rs.on(&quot;data&quot;, function(data){    ws.write(data)})</code></pre><p>简单的方式</p><pre><code class="javascript">var fs = require(&quot;fs&quot;)var rs = fs.createReadStream(&quot;an.jpg&quot;)var ws = fs.createWriteStream(&quot;an.jpg&quot;)rs.once(&quot;open&quot;, function(){    console.log(&quot;可读流打开了&quot;)})rs.once(&quot;close&quot;, function(){    console.log(&quot;可读流关闭了&quot;)})ws.once(&quot;open&quot;, function(){    console.log(&quot;可写流打开了&quot;)})ws.once(&quot;close&quot;, function(){    console.log(&quot;可写流关闭了&quot;)})// pipe()可以将可读流中的内容直接输出到可写流中rs.pipe(ws)</code></pre><h3><span id="7-fs的其他方法">7、fs的其他方法</span></h3><ul><li><p>验证路径是否存在</p><ul><li><del>fs.exists(path, callback)</del></li><li><code>fs.exitsSync(path)</code></li></ul><pre><code>var fs = require(&quot;fs&quot;)var isExists = fs.exitsSync(&quot;a.mp3&quot;)// console.log(isExists)</code></pre></li><li><p>获取文件信息</p><ul><li><code>fs.stat(path, callback)</code>获取文件的状态，会返回一个对象，这个对象中保存了当前对象状态的相关信息</li><li><code>fs.statSync(path)</code></li></ul><pre><code>fs.stat(&quot;a.mp3&quot;, function(err, stat){    console.log(stat)})</code></pre><ul><li>stat参数的一些属性、方法<ul><li>size 大小</li><li>……</li></ul></li></ul></li><li><p>删除文件</p><ul><li><code>fs.unlink(path, callback)</code></li><li><code>fs.unlinkSync(path)</code></li></ul><pre><code>fs.unlinkSync(&quot;hello.txt&quot;)</code></pre></li><li><p>列出文件</p><ul><li><code>fs.readdir(path[, options], callback)</code>读取一个目录的目录结构</li><li><code>fs.readdirSync(path[, options])</code><ul><li>files是一个字符串数组，每一个元素就是一个文件夹或文件的名字</li></ul></li></ul><pre><code>fs.readdir(&quot;.&quot;, function(err, files){    if(!err){        console.log(files)    }})</code></pre></li><li><p>截断文件</p><ul><li><code>fs.truncate(path, len, callback)</code> 将文件修改为指定的大小</li><li><code>fs.truncateSync(path, len)</code></li></ul><pre><code>fs.truncateSync(&quot;hello.txt&quot;, 3)</code></pre></li><li><p>建立目录</p><ul><li><code>fs.mkdir(path[,mode], callback)</code></li><li><code>fs.mkdirSync(path[, mode])</code></li></ul><pre><code>fs.mkdirSync(&quot;hello&quot;)</code></pre></li><li><p>删除目录</p><ul><li><code>fs.rmdir(path, callback)</code></li><li><code>fs.rmdirSync(path)</code></li></ul><pre><code>fs.rmdirSync(&quot;hello&quot;)</code></pre></li><li><p>重命名文件和目录</p><ul><li><code>fs.rename(oldPath, newPath, callback)</code></li><li><code>fs.renameSync(oldPath, newPath)</code><ul><li>oldPath 旧的路径</li><li>newPath 新的路径</li><li>callback 回调函数</li></ul></li></ul><pre><code>fs.rename(&quot;a.mp3&quot;, &quot;new.mp3&quot;, function(err){    if(!err){        console.log(&quot;succece&quot;)    }})</code></pre></li><li><p>监视文件更改写入</p><ul><li><code>fs.watchFile(filename[, options], listener)</code><ul><li>filename 要监视的文件的名字</li><li>options 配置选项</li><li>listener 回调函数，当文件发生变化时，回调函数会执行<ul><li>curr 当前文件的状态</li><li>prev 修改前文件的状态</li><li>这两个对象都是stats对象</li></ul></li></ul></li></ul><pre><code>fs.watchFile(&quot;hello.txt&quot;, function{    console.log(prev.size)    console.log(curr.size)})</code></pre><ul><li>时间间隔，配置选项中</li></ul><pre><code>fs.watchFile(&quot;hello.txt&quot;, {interval: 1000}, function{    console.log(prev.size)    console.log(curr.size)})</code></pre></li></ul><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="笔记" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="后端" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/%E5%90%8E%E7%AB%AF/"/>
    
    
      <category term="后端" scheme="https://wallleap.cn/tags/%E5%90%8E%E7%AB%AF/"/>
    
      <category term="NodeJS" scheme="https://wallleap.cn/tags/NodeJS/"/>
    
  </entry>
  
  <entry>
    <title>MongoDB学习笔记</title>
    <link href="https://wallleap.cn/2020/06/14/MongoDB/"/>
    <id>https://wallleap.cn/2020/06/14/MongoDB/</id>
    <published>2020-06-14T03:22:00.000Z</published>
    <updated>2025-01-24T09:22:15.349Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1><span id="mongodb">MongoDB</span></h1><h2><span id="一-数据库简介">一、数据库简介</span></h2><ul><li>数据库是按照数据结构来组织、存储和管理数据的仓库</li><li>我们的程序都是在内存中运行的，一旦程序运行结束或者计算机断电，程序运行的数据都会丢失</li><li>所以我们就需要将一些程序运行的数据持久化到硬盘之中，以确保数据的安全性</li><li>说白了，数据库就是存储数据的仓库</li><li>数据库分类<ul><li>关系型数据库(RDBMS)<ul><li>MySQL、Oracle、DB2、SQL Server……</li><li>关系数据库中都是表</li></ul></li><li>非关系型数据库(No SQL  Not Only SQL)<ul><li>MongoDB、Redis……</li><li>键值对数据库</li><li>文档数据库MongoDB</li></ul></li></ul></li></ul><h2><span id="二-mongodb简介">二、MongoDB简介</span></h2><ul><li><p>MongoDB是为快速开发互联网Web应用而设计的数据库系统</p></li><li><p>MongoDB的设计目标是极简、灵活、作为Web应用栈的一部分</p></li><li><p>MongoDB的数据模型是面向文档的，所谓文档是一种类似于JSON的结构，简单理解MongoDB这个数据库中存的是各种各样的JSON（BSON）</p></li><li><p>三个概念</p><ul><li>数据库（database）：数据库是一个仓库，在仓库中可以存放集合</li><li>集合（collection）：集合类似于数组，在集合中可以存放文档</li><li>文档（document）：文档数据库中的最小单位，我们存储和操作的内容都是文档</li></ul></li><li><p>下载MongoDB</p><ul><li>下载地址：<a href="https://www.mongodb.org/dl/win32/" target="_blank" rel="noopener">https://www.mongodb.org/dl/win32/</a></li><li>MongoDB的版本偶数版本为稳定版，奇数版本为开发版</li><li>MongoDB对于32位系统支持不佳，所以3.2版本以后没有再对32位系统的支持</li></ul></li><li><p>安装MongoDB</p><ul><li><p>安装</p></li><li><p>配置环境变量 D:\Program Files\MongoDB\Server\4.2\bin</p></li><li><p>在C盘根目录下创建data/db文件夹</p></li><li><p>打开cmd命令行窗口</p><ul><li>输入<code>mongod</code>启动MongoDB服务器</li><li>32位的初次启动需要输入<code>mongod --storageEngine=mmapv1</code></li></ul></li><li><p>再打开一个cmd窗口</p><ul><li>输入<code>mongo</code>连接mongodb，出现<code>&gt;</code></li></ul></li><li><p>其他配置命令<code>mongod --dbpath &quot;D:\Program Files\MongoDB\Server\4.2\data\db --port 123&quot;</code></p></li><li><p>数据库</p><ul><li><p>服务器用来保存数据，<code>mongod</code>启动</p></li><li><p>客户端用来操作服务器，对数据进行增删改查的操作，<code>mongo</code>来启动</p></li><li><p>需要打开两个窗口，我们可以将MongoDB设置为系统服务，可以自动在后台启动，不需要每次都手动启动</p><ul><li><p>C://data/创建db、log目录</p></li><li><p>在D:\Program Files\MongoDB\Server\4.2下添加一个配置文件mongod.cfg，配置文件中添加内容</p><pre><code>systemLog:    destination: file    path: c:\data\log\mongod.logstorage:    dbPath: c:\data\db</code></pre></li><li><p>以管理员身份打开命令行窗口，输入命令<code>sc.exe create MongoDB binPath=&quot;\&quot;D:\Program Files\MongoDB\Server\4.2\bin\mongod.exe\&quot; --service --config=\&quot;D:\Program Files\MongoDB\Server\4.2\mongod.cfg\&quot;&quot; DisplayName= &quot;MongoDB&quot; start= &quot;auto&quot;</code></p></li><li><p>启动MongoDB服务</p></li><li><p>如果启动失败，则输入<code>sc delete MongoDB</code>删除服务之后，重新开始配置</p></li></ul></li></ul></li></ul></li></ul><h2><span id="三-mongodb的基本操作">三、MongoDB的基本操作</span></h2><p>在MongoDB中，数据库和集合都不需要我们手动创建，当我们创建文档时，如果文档所在的集合或数据库不存在则会自动创建数据库和集合</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/1588908280549.png" alt="MongoDB"></p><h3><span id="1-基本命令">1、基本命令</span></h3><ul><li>显示当前的所有数据库<ul><li><code>show dbs</code></li><li><code>show databases</code></li></ul></li><li>进入到指定的数据库中<ul><li><code>use 数据库名</code></li></ul></li><li>db表示的是当前所处的数据库<ul><li><code>db</code></li></ul></li><li>显示数据库中所有的集合<ul><li><code>show collections</code></li></ul></li></ul><h3><span id="2-数据库的crud操作">2、数据库的CRUD操作</span></h3><p>安装图形化工具：<code>MongodbManagerFree</code>或<code>Studio-3T</code></p><ul><li><p>向数据库中插入文档</p><ul><li><code>db.&lt;collection&gt;.insert(doc)</code> 向集合中插入一个或多个文档。当我们向集合中插入文档时，如果没有给文档指定<code>_id</code>属性，则数据库会自动为文档添加<code>_id</code>，该属性用来作为文档的唯一标识，也可以自己指定，如果我们指定了，数据库就不会添加了，如果自己指定_id也必须确保它的唯一性</li></ul><pre><code>// 向test数据库中的stus集合中插入一个新的学生对象{name:&quot;swk&quot;,age:18,gender:&quot;man&quot;}use testdb.stus.insert({name:&quot;swk&quot;,age:18,gender:&quot;man&quot;})db.stus.insert([    {name:&quot;shs&quot;,age:38,gender:&quot;man&quot;},    {name:&quot;bgj&quot;,age:18,gender:&quot;female&quot;},    {name:&quot;zbj&quot;,age:28,gender:&quot;man&quot;}])ObjectId()  // 根据时间戳、机器码db.stus.insert({_id:&quot;ts&quot;,name:&quot;ts&quot;,age:18,gender:&quot;man&quot;})   // 建议不要自己指定</code></pre><ul><li><code>db.&lt;collection&gt;.insertOne(doc)</code>  插入一个文档对象</li><li><code>db.&lt;collection&gt;.insertMany(doc)</code>  插入多个文档对象</li><li>这两个和上面的差不多，就是写开了更容易理解</li></ul><p>注：MongoDB中的文档的属性值也可以是一个文档，当一个文档的属性值是文档时，我们称这为内嵌文档。</p><p>MongoDB支持直接通过内嵌文档的属性进行查询，如果要查询内嵌文档，可以通过<code>.</code>的形式来匹配。属性名必须用引号括起来</p></li><li><p>查询</p><ul><li><code>db.&lt;collectin&gt;.find()</code> 查询当前集合中的所有的文档<ul><li>find()用来查询集合中所有符合条件的文档。在开发时，绝对不会执行不带条件的查询</li><li>find()可以接受一个对象作为条件参数<ul><li>{}表示查询集合中所有的文档</li><li>{字段名:值}查询属性是指定值的文档<ul><li>值还可以添加一个选项,多个条件之间使用<code>,</code>连接</li><li><code>$eq</code> 等于</li><li><code>$gt</code>  <code>$gte</code> 大于  大于等于</li><li><code>$in</code></li><li><code>$lt</code> <code>$lte</code></li><li><code>$ne</code></li><li><code>$nin</code></li><li><code>$or</code> 或</li></ul></li></ul></li><li>find()返回的是一个数组</li><li><code>db.stus.find({}).count()</code>返回对象个数，一般用这个；<code>db.stus.find({}).length()</code></li><li><code>db.stus.find().limit(10)</code> 设置显示数据的上限，这里表示只显示前十条数据</li><li><code>db.numbers.find().skip(10).limit(10)</code> skip()用于跳过指定数量的数据，这里会显示第11~20条数据。MongoDB会自动调整skip和limit的位置，因此顺序随便</li></ul></li><li><code>db.&lt;collectin&gt;.findOne()</code> 查询当前集合中符合条件的第一个文档<ul><li>findOne()返回的是一个文档对象</li></ul></li></ul><pre><code>db.stus.find();db.stus.find({});db.stus.find({}).count();db.stus.find({}).length();db.stus.find({_id:&quot;ts&quot;});db.stus.find({age: 28,name:&quot;zbj&quot;});db.stus.find({age: 28})[1];db.stus.find({age:{$gt:20}})</code></pre></li><li><p>修改</p><ul><li><code>db.&lt;collectin&gt;.update(查询条件,新对象[,配置选项])</code> <ul><li>update()默认情况下回使用新对象来替换旧的对象</li><li>如果需要修改指定的属性，而不是替换，需要使用“修改操作符”来完成修改<ul><li><code>$set</code> 可以用来修改文档中的指定属性</li><li><code>$push</code>用于向数据中添加一个新的元素</li><li><code>$addT0Set</code>向数据中添加一个新元素，如果数组中已经存在该元素，则不会添加</li><li><code>$unset</code> 可以用来删除文档的指定属性</li></ul></li><li>update()默认只会修改一个</li></ul></li><li><code>db.&lt;collectin&gt;.updateMany(查询条件,新对象)</code>  同时修改多个符合条件的文档</li><li><code>db.&lt;collectin&gt;.updateOne(查询条件,新对象)</code>  修改一个符合条件的文档</li><li><code>db.&lt;collectin&gt;.replace(查询条件,新对象)</code>   替换一个文档</li><li>这三个也是将第一个分开了</li></ul><pre><code>db.stus.upadate({name:&#39;shs&#39;},{age:29}) // 替换db.stus.upadate({name:&#39;shs&#39;},{$set{age:29， address:&quot;lsh&quot;}) // 修改，默认值修改一个db.stus.upadate({name:&#39;shs&#39;},{$set{address:&quot;lsh&quot;})db.stus.upadate({name:&#39;shs&#39;},{$unset{age:29}) // 删除db.stus.upadate({name:&#39;shs&#39;},{$set{age:29， address:&quot;lsh&quot;}) // 添加配置项也可以修改多个</code></pre></li></ul><ul><li><p>删除</p><ul><li><code>db.&lt;collectin&gt;.remove()</code> <ul><li>remove()可以根据条件来删除文档，传递的条件的方式和find()一样</li><li>删除符合条件的所有的文档，默认会删除多个</li><li>如果remove()第二个参数传递一个true，则只会删除一个</li><li>如果只传递一个空对象，则会删除所有的</li></ul></li><li><code>db.&lt;collectin&gt;.deleteOne()</code></li><li><code>db.&lt;collectin&gt;.deleteMany()</code>  </li><li><code>db.collection.drop()</code> 删除集合</li><li><code>db.dropDatabase()</code>删除数据库</li><li>一般数据库中的数据都不会删除，所以删除的方法很少调用，一般在数据中添加一个字段，来表示数据是否被删除</li></ul><pre><code>db.stus.remove()db.stus.remove({})   // 清空集合（性能略差）db.stus.drop()     // 删除集合db.database()       // 删除数据库// 一般删除一个文档都是用这种方式db.stus.inert([    {        name: &quot;ts&quot;,        isDel: 0    },    {        name: &quot;zbj&quot;,        isDel: 0    },    {        name:&quot;shs&quot;,        isDel: 0    }])db.stus.updateOne({name:&quot;ts&quot;},{$et:{isDel:1}})db.stus.find({isDel:0})</code></pre></li></ul><h2><span id="四-文档之间的关系">四、文档之间的关系</span></h2><ul><li><p>一对一(One to One)</p><ul><li>夫妻（一个丈夫对应一个妻子）</li><li>在MongoDB中，可以通过内嵌文档的形式来体现出一对一的关系</li></ul><pre><code>db.wifeAndHusband.insert([    {        name:&quot;hr&quot;,        husband:{            name:&quot;gj&quot;        }    },{        name:&quot;pjl&quot;        husband:{            name:&quot;wdl&quot;        }    }])</code></pre></li></ul><ul><li><p>一对多(One to Many)/多对一(Many to One)</p><ul><li>父母——孩子、用户——订单、文章——评论</li><li>也可以通过内嵌文档来映射一对多的关系，属性变为数组</li></ul><pre><code>db.users.insert([    {        username:&quot;swk&quot;    },{        username:&quot;zbj&quot;    }])db.order.insert({    list:[&quot;pg&quot;,&quot;xj&quot;,&quot;dyl&quot;],    user_id:ObjectId(&quot;swk_Id&quot;)})var user_idd = db.users.findOne({username:&quot;swk&quot;})._iddb.order.find({user_id:user_idd})</code></pre></li></ul><ul><li><p>多对多(Many to Many)</p><ul><li>分类——商品、老师——学生</li><li>学生属于多个老师、老师教多个学生，学生中有一个老师的id，id多个用数组</li></ul><pre><code>db.teachers.insert([    {        name: &#39;hqg&#39;,    },    {        name: &#39;hys&#39;,    },    {        name: &quot;gxr&quot;    }])db.stus.insert([    {        name:&quot;gj&quot;,        tech_ids:[            ObjectID(&#39;hqgID&#39;),            ObjectID(&quot;hysID&quot;)        ]    },    {        name:&quot;swk&quot;,        tech_ids: [            ObjectID(&#39;hqgID&#39;),            ObjectID(&quot;hysID&quot;),            ObjectID(&quot;gxr&quot;)        ]    }])db.teachers.find()db.stus.find()</code></pre></li></ul><h2><span id="五-sort和投影">五、sort和投影</span></h2><ul><li><code>db.emp.find()</code> 查询文档时，默认情况是按照_id的值进行排列(升序)</li><li><code>db.emp.find({}).sort({sal:1})</code> sort()可以用来指定文档的排序的规则，sort()需要传递一个队形来指定排序的规则<ul><li>1 表示升序</li><li>-1 表示降序</li><li><code>db.emp.find({}).sort(sal:1, empno:-1)</code> 先按照sal升序排列，如果sal相同，按照empno降序排列</li></ul></li><li>注：limit、skip、sort书写顺序没有要求</li><li><code>db.emp.find({}, {ename:1})</code>在查询时，可以在第二个参数的位置来设置查询结果的投影   <code>db.emp.find({}, {ename: 1, _id: 0, sal: 1})</code></li></ul><h2><span id="六-mongoose简介">六、MongoOse简介</span></h2><h3><span id="1-简介">1、简介</span></h3><ul><li>之前我们都是通过shell来完成对数据库的各种操作的，在开发中大部分的时候我们都需要通过程序来完成对数据库的操作</li><li>Mongoose就是一个让我们可以通过Node来操作MongoDB的模块</li><li>Mongoose是一个对象文档模型(ODM)库，它对Node原生的MongoDB模块进行了进一步的优化封装，并提供了很多的功能</li><li>在大多数情况下，它被用来把结构化的模式应用到一个MongoDB集合，并提供了验证和类型转换等好处</li></ul><h3><span id="2-mongoose好处">2、Mongoose好处</span></h3><ul><li>可以为文档创建一个模式结构（Schema）   ——约束</li><li>可以对模型中的对象/文档进行验证</li><li>数据可以通过类型转换转为对象模型</li><li>可以使用中间件来应用业务逻辑挂钩</li><li>比Node原生的MongoDB驱动更容易</li></ul><h3><span id="3-新的对象">3、新的对象</span></h3><ul><li>Schema(模式对象)<ul><li>定义约束了数据库中的文档结构</li></ul></li><li>Model<ul><li>Model对象作为集合中所有文档的表示，相当于MongoDB数据库中的集合</li></ul></li><li>Document<ul><li>Document表示集合中的具体文档，相当于集合中的一个具体的文档</li></ul></li></ul><h2><span id="七-连接mongodb数据库">七、连接MongoDB数据库</span></h2><ol><li>下载安装Mongoose   <code>npm i mongoose --save</code></li><li>在项目中引入Mongoose   <code>var mongoose = require(&quot;mongoose&quot;)</code></li><li>连接数据库   <code>mongoose.connect(&#39;mongodb://数据库IP地址:端口号/数据库名&#39;);</code><ul><li>端口号如果是默认端口号(27017)，则可以省略不写</li></ul></li><li>[可选] 监听MongoDB数据库的连接状态<ul><li>在Mongoose对象中，有一个属性叫做connection，该对象表示的就是数据库连接通过监视该对象的状态，可以来监听数据库的连接与断开<ul><li><code>mongoose.connection.once(&quot;open&quot;,function(){})</code> 数据库连接成功的事件</li><li><code>mongoose.connection.once(&quot;close&quot;,function(){})</code> 数据库断开的事件</li></ul></li></ul></li><li>断开数据库连接(一般不需要调用) <code>mongoose.disconnect()</code><ul><li>MongoDB数据库，一般情况下只需要连接一次，连接一次以后，除非项目停止、服务器关闭，否接连接一般不会断开</li></ul></li></ol><pre><code>// 例子var mongoose = require(&quot;mongoose&quot;)mongoose.connect(&quot;mongodb://127.0.0.1/mongoose_test&quot;)mongoose.connection.once(&quot;open&quot;,function(){    console.log(&quot;数据库已连接&quot;)})mongoose.connection.once(&quot;close&quot;,function(){    console.log(&quot;数据库已断开&quot;)})mongoose.disconnect()</code></pre><h2><span id="八-schema-model和document">八、Schema、Model和Document</span></h2><p>有了Model，就可以对数据库进行增删改查的操作了</p><ul><li><code>Model.crate(doc(s), [callback])</code><ul><li>用来创建一个文档并添加到数据库中</li><li>doc(s)  可以是一个文档对象，也可以是一个文档对象的数组</li><li>callback  当操作完成之后调用的回调函数</li></ul></li><li>查询<ul><li><code>Model.find(conditions, [projection], [options], [callback])</code> 查询所有符合条件的文档 总会返回一个数组</li><li><code>Model.findById(id, [projection], options), [callback]</code> 根据文档的id属性查询文档</li><li><code>Model.findOne([conditions], [projection], [options], [callback])</code> 查询符合条件的第一个文档  总会返回一个具体的对象<ul><li>conditions 查询的条件</li><li>projection 投影  需要获取到的字段 两种方式 <ul><li><code>{}</code>  对象，要的字段为1，不要的为0</li><li><code>&quot;&quot;</code> 字段名、不要的设为-</li></ul></li><li>options 查询选项（skip limit）</li><li>callback 回调函数，查询结果会通过回调函数返回，毁掉函数必须传，如果不传回调函数，压根不会查询</li></ul></li><li>通过find()查询的结果，返回的对象，就是Document，文档对象</li></ul></li><li>修改<ul><li><code>Model.update(conditions, doc, [options], [callback])</code></li><li><code>Model.updateMany(conditions, doc, [options], [callback])</code></li><li><code>Model.updateOne(conditions, doc, [options], [callback])</code><ul><li>用来修改一个或多个文档</li><li>conditions 查询条件</li><li>doc 修改后的对象</li><li>options 配置参数</li><li>callback 回调函数</li></ul></li><li><code>Model.replaceOne(conditions, doc, [options], [callback])</code> 替换</li></ul></li><li>删除(不用这个)<ul><li><code>Model.remove(conditions, [callback])</code></li><li><code>Model.deleteOne(conditions, [callback])</code></li><li><code>Model.deleteMany(conditions, [callback])</code></li></ul></li><li><code>Model.count(conditions, [callback])</code><ul><li>统计文档的数量</li></ul></li></ul><p>Document和集合中的文档一一对应，Document是Model的实例，通过Model查询到结果都是Document</p><p>Document的方法</p><ul><li><code>Model#save([options], [options.safe], [options.validateBeforeSave], [fn])</code></li><li><code>update(update, [options], [callback])</code><ul><li>修改对象</li></ul></li><li><code>remove([callback])</code><ul><li>删除对象(拒绝用这个)</li></ul></li><li><code>get(name)</code><ul><li>获取文档中的指定属性值</li></ul></li><li><code>set(name, value)</code><ul><li>设置文档的指定的属性值</li></ul></li><li><code>id</code><ul><li>获取文档的_id属性值</li></ul></li><li><code>toJSON()</code><ul><li>转换为一个JSON对象</li></ul></li><li><code>toObject()</code><ul><li>将Document对象转换为普通的js对象</li><li>转换为普通的js对象以后，所有的Document对象的方法或属性都不能使用了</li></ul></li><li>其他 <code>equals(doc)</code>  <code>isNew</code>  <code>isInit(path)</code> </li></ul><pre><code class="javascript">var mongoose = require(&quot;mongoose&quot;)mongoose.connect(&quot;mongodb://127.0.0.1/mongoose_test&quot;, {useMongoClient:true})// Schema对数据进行约束// 将mongoose.Schema赋值给一个变量var Schema = mongoose.Schema// 创建Schema(模式)对象var stuSchema = new Schema({    name: String,    age: Number,    gender: {        type: String,        default: &quot;male&quot;    },    address: String})// Model代表的是数据库中的集合，通过Model才能对数据库进行操作// 通过Schema来创建Model// mogoose.model(&quot;要映射的集合名&quot;, 刚创建的Schema对象) Mongoose会自动将集合名变为复数var StuModel = mogoose.model(&quot;student&quot;, stuSchema)// 向数据库中出入一个文档 model.create(文档, 回调函数)stuModel.create({    name: &#39;swk&#39;,    age: 18,    gender: &#39;male&#39;,    address: &quot;hgs&quot;}, function(err){    if(!err){        console.log(&quot;插入成功&quot;)    }})stuModel.create([{    name: &#39;shs&#39;,    age: 28,    gender: &#39;male&#39;,    address: &quot;lsh&quot;},{    name: &quot;bgj&quot;,    age: 17,    gender: &quot;female&quot;,    address: &quot;bgd&quot;}], function(err){    if(!err){        console.log(&quot;插入成功&quot;)    }})// 查询StuModel.find({name:&quot;shs&quot;},function(err, docs){    if(!err){        console.log(docs)        console.log(docs[0].name)    }})/* StuModel.find({name:&quot;shs&quot;},{name:1, _id:0},function(err, docs){    if(!err){        console.log(docs)    }}) */StuModel.find({name:&quot;shs&quot;},&quot;name -_id&quot;,function(err, docs){    if(!err){        console.log(docs)    }})StuModel.find({name:&quot;shs&quot;},&quot;name -_id&quot;,{skip:3, limit:1},function(err, docs){    if(!err){        console.log(docs)    }})StuModel.findOne({}, function(err, doc){    if(!err){        console.log(doc.name)    }})StuModel.findById(&quot;...&quot;,function(err,doc){    if(!err){        console.log(doc)    }})// Document对象是Model的实例/* StuModel.findById(&quot;...&quot;,function(err,doc){    if(!err){        console.log(doc instantceof StuModel)    }}) */StuModel.updateOne({name: &#39;ts&#39;}, {$set:{age:20}},function(err){    if(!err){        console.log(&quot;修改成功&quot;)    }})StuModel.remove({name:&quot;bgj&quot;}, function(err){    if(!err){        console.log(&quot;删除成功&quot;)    }})StuModel.count({}, function(err, count){    if(!err){        console.log(count)    }})// 创建一个Documentvar stu = new StuModel({    name: &quot;blm&quot;,    age: 14,    gender: &quot;male&quot;,    address: &quot;bbt&quot;})stu.save(function(err){    if(!err){        console.log(&quot;保存成功&quot;)    }})StuModel.findOne({}, function(err, doc){    if(!err){        /* doc.update({$set:{age: 28}}, function(err){            if(!err){                console.log(&quot;修改成功&quot;)            }        }) */        doc.age = 28        save()        /* doc.remove(function(err){            if(!err){                console.log(&quot;删除成功&quot;)            }        }) */        // console.log(doc.get(&quot;age&quot;))        console.log(doc.age)        // doc.set(&quot;name&quot;, &quot;zhu&quot;)        doc.name = &quot;zhu&quot;        // console.log(doc.id)        console.log(doc._id)        console.log(doc.toJSON())        doc = doc.toObject()        delete doc.address        console.log(doc)    }})</code></pre><h2><span id="九-mongoose的模块化">九、Mongoose的模块化</span></h2><p>定义一个模块，用来连接MongoDB数据库</p><pre><code class="javascript">// conn_mongo.jsvar mongoose = require(&quot;mongoose&quot;)mongoose.connect(&quot;mongodb://127.0.0.1/mongoose_test&quot;)</code></pre><p>定义一个student的模型，几个模型就可以创建几个js文件</p><pre><code class="javascript">// student.jsvar mongoose = require(&quot;mongoose&quot;)var Schema = mongoose.Schemavar stuSchema = new Schema({    name: String,    age: Number,    gender: {        type: String,        default: &quot;male&quot;    },    address: String})var StuModel = mongoose.model(&quot;student&quot;, stuSchema)// exports.model = StuModelmodule.exports = StuModel</code></pre><p>其他地方引入</p><pre><code class="javascript">require(&quot;./conn_mongo&quot;)// var Student = require(&quot;./models/student&quot;).modelvar Student = require(&quot;./models/student&quot;)Student.find({}, function(err, docs){    if(!err){        console.log(docs)    }})</code></pre><h2><span id="练习">练习</span></h2><pre><code>//1.进入my_test数据库use my_test//2.向数据库的user集合中插入一个文档  db.users.insert({    username:&quot;sunwukong&quot;});//3.查询user集合中的文档db.users.find();//4.向数据库的user集合中插入一个文档   db.users.insert({    username:&quot;zhubajie&quot;});//5.查询数据库user集合中的文档db.users.find();//6.统计数据库user集合中的文档数量db.users.find().count();//7.查询数据库user集合中username为sunwukong的文档db.users.find({username:&quot;sunwukong&quot;});//8.向数据库user集合中的username为sunwukong的文档，添加一个address属性，属性值为huaguoshandb.users.update({username:&quot;sunwukong&quot;},{$set:{address:&quot;huaguoshan&quot;}});//9.使用{username:&quot;tangseng&quot;} 替换 username 为 zhubajie的文档db.users.replaceOne({username:&quot;zhubajie&quot;},{username:&quot;tangseng&quot;});    //10.删除username为sunwukong的文档的address属性db.users.update({username:&quot;sunwukong&quot;},{$unset:{address:1}});//11.向username为sunwukong的文档中，添加一个hobby:{cities:[&quot;beijing&quot;,&quot;shanghai&quot;,&quot;shenzhen&quot;] , movies:[&quot;sanguo&quot;,&quot;hero&quot;]}//MongoDB的文档的属性值也可以是一个文档，当一个文档的属性值是一个文档时，我们称这个文档叫做 内嵌文档db.users.update({username:&quot;sunwukong&quot;},{$set:{hobby:{cities:[&quot;beijing&quot;,&quot;shanghai&quot;,&quot;shenzhen&quot;] , movies:[&quot;sanguo&quot;,&quot;hero&quot;]}}});db.users.find();//12.向username为tangseng的文档中，添加一个hobby:{movies:[&quot;A Chinese Odyssey&quot;,&quot;King of comedy&quot;]}db.users.update({username:&quot;tangseng&quot;},{$set:{hobby:{movies:[&quot;A Chinese Odyssey&quot;,&quot;King of comedy&quot;]}}})//13.查询喜欢电影hero的文档//MongoDB支持直接通过内嵌文档的属性进行查询，如果要查询内嵌文档则可以通过.的形式来匹配//如果要通过内嵌文档来对文档进行查询，此时属性名必须使用引号 db.users.find({&#39;hobby.movies&#39;:&quot;hero&quot;});//14.向tangseng中添加一个新的电影Interstellar//$push 用于向数组中添加一个新的元素//$addToSet 向数组中添加一个新元素 ， 如果数组中已经存在了该元素，则不会添加db.users.update({username:&quot;tangseng&quot;},{$push:{&quot;hobby.movies&quot;:&quot;Interstellar&quot;}});db.users.update({username:&quot;tangseng&quot;},{$addToSet:{&quot;hobby.movies&quot;:&quot;Interstellar&quot;}});db.users.find();//15.删除喜欢beijing的用户db.users.remove({&quot;hobby.cities&quot;:&quot;beijing&quot;});//16.删除user集合db.users.remove({});db.users.drop();show dbs;//17.向numbers中插入20000条数据 7.2sfor(var i=1 ; i&lt;=20000 ; i++){    db.numbers.insert({num:i});}db.numbers.find()db.numbers.remove({});//0.4svar arr = [];for(var i=1 ; i&lt;=20000 ; i++){    arr.push({num:i});}db.numbers.insert(arr);//18.查询numbers中num为500的文档db.numbers.find({num:500})//19.查询numbers中num大于5000的文档db.numbers.find({num:{$gt:500}});db.numbers.find({num:{$eq:500}});//20.查询numbers中num小于30的文档db.numbers.find({num:{$lt:30}});//21.查询numbers中num大于40小于50的文档db.numbers.find({num:{$gt:40 , $lt:50}});//22.查询numbers中num大于19996的文档db.numbers.find({num:{$gt:19996}});//23.查看numbers集合中的前10条数据db.numbers.find({num:{$lte:10}});//limit()设置显示数据的上限db.numbers.find().limit(10);//在开发时，我们绝对不会执行不带条件的查询db.numbers.find();//24.查看numbers集合中的第11条到20条数据/*    分页 每页显示10条        1-10     0        11-20    10        21-30    20        。。。        skip((页码-1) * 每页显示的条数).limit(每页显示的条数);    skip()用于跳过指定数量的数据        MongoDB会自动调整skip和limit的位置*/db.numbers.find().skip(10).limit(10);//25.查看numbers集合中的第21条到30条数据db.numbers.find().skip(20).limit(10);db.numbers.find().limit(10).skip(10);//26.将dept和emp集合导入到数据库中db.dept.find()db.emp.find()//27.查询工资小于2000的员工db.emp.find({sal:{$lt:2000}});//28.查询工资在1000-2000之间的员工db.emp.find({sal:{$lt:2000 , $gt:1000}});//29.查询工资小于1000或大于2500的员工db.emp.find({$or:[{sal:{$lt:1000}} , {sal:{$gt:2500}}]});//30.查询财务部的所有员工//(depno)var depno = db.dept.findOne({dname:&quot;财务部&quot;}).deptno;db.emp.find({depno:depno});//31.查询销售部的所有员工var depno = db.dept.findOne({dname:&quot;销售部&quot;}).deptno;db.emp.find({depno:depno});//32.查询所有mgr为7698的所有员工db.emp.find({mgr:7698})//33.为所有薪资低于1000的员工增加工资400元db.emp.updateMany({sal:{$lte:1000}} , {$inc:{sal:400}});db.emp.find()</code></pre><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="笔记" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="后端" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/%E5%90%8E%E7%AB%AF/"/>
    
    
      <category term="后端" scheme="https://wallleap.cn/tags/%E5%90%8E%E7%AB%AF/"/>
    
      <category term="MongoDB" scheme="https://wallleap.cn/tags/MongoDB/"/>
    
  </entry>
  
  <entry>
    <title>AngularJs学习笔记</title>
    <link href="https://wallleap.cn/2020/06/04/AngularJs/"/>
    <id>https://wallleap.cn/2020/06/04/AngularJs/</id>
    <published>2020-06-04T15:33:49.000Z</published>
    <updated>2025-01-24T09:22:15.347Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2><span id="1-angular介绍">1、Angular介绍</span></h2><ul><li>官网： <a href="https://angularjs.org" target="_blank" rel="noopener">https://angularjs.org</a></li><li>Angular是Google开源的前端JS结构化框架</li><li>AngularJS特性和优点<ul><li>双向数据绑定</li><li>声明式依赖注入</li><li>解耦应用逻辑，数据模型和视图<ul><li>耦合度：两者之间关系密切度</li><li>降低耦合度</li></ul></li><li>完善的页面指令</li><li>定制表单验证</li><li>Ajax封装</li></ul></li><li>与jQuery比较<ul><li>jQuery<ul><li>JS函数库</li><li>封装简化DOM操作</li></ul></li><li>Angular<ul><li>JS结构化框架</li><li>主体不再是DOM，而是页面中的动态数据</li></ul></li></ul></li><li>AngularJS能做什么项目<ul><li>构建单页面(SPA)Web应用或Web APP应用<ul><li>单页面应用（SPA）Simple Page Application特点：<ul><li>将所有的活动局限于一个页面</li><li>当页面中有部分数据发生了变化不会去刷新整个页面，而是局部刷新</li><li>利用的Ajax技术、路由</li></ul></li></ul></li><li>应用<ul><li><del>饿了么 <a href="https://www.ele.me/home/" target="_blank" rel="noopener">https://www.ele.me/home/</a></del></li><li>微信网页版 <a href="https://wx.qq.com" target="_blank" rel="noopener">https://wx.qq.com</a></li><li>知乎周报 <a href="https://zhuanlan.zhihu.com/Weekly" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/Weekly</a></li><li>后台管理应用：阿里云、土豆后台、唯品会……</li></ul></li></ul></li><li>版本学习<ul><li>1.0 JavaScript </li><li><del>2.0 3.0 4.0 TypeScript</del></li></ul></li></ul><h3><span id="2-第一个angular程序">2、第一个Angular程序</span></h3><p>使用jQuery实现</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;  &lt;meta charset=&quot;UTF-8&quot;&gt;  &lt;title&gt;jQuery实现&lt;/title&gt;&lt;/head&gt;&lt;body&gt;  &lt;input type=&quot;text&quot;&gt;  &lt;p&gt;您输入的内容是：&lt;span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;script src=&quot;jquery.js&quot;&gt;&lt;/script&gt;  &lt;script&gt;    $(function(){  //document.ready 文档(页面结构)加载完毕 window.onload:整个页面加载完毕，包括图片等资源      $(&#39;input&#39;).keyup(function(){        var value = this.value   // $(this).val()        $(&#39;span&#39;).html(value)      })    })  &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p>使用AngularJS实现</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;  &lt;meta charset=&quot;UTF-8&quot;&gt;  &lt;title&gt;AngularJS实现&lt;/title&gt;&lt;/head&gt;&lt;body ng-app&gt;  &lt;input type=&quot;text&quot; ng-model=&quot;username&quot;&gt;  &lt;p&gt;您输入的内容是：&lt;span&gt;{{username}}&lt;/span&gt;&lt;/p&gt;  &lt;script src=&quot;https://cdn.bootcdn.net/ajax/libs/angular.js/1.2.29/angular.js&quot;&gt;&lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p>Chrome插件：</p><blockquote><p>ng-inspector for AngularJS</p></blockquote><ul><li><code>ng-app</code>(指令)：告诉angular核心，它管理当前标签所包含的整个区域，并且会自动创建$rootScope根作用域对象（通常放在body标签）</li><li><code>ng-model</code>：将当前输入框的值与谁关联(属性名:属性值)，并作为当前作用域对象($rootScope)的属性</li></ul><pre><code>{{}}(表达式)：显示数据，从当前作用域对象的指定属性名上取</code></pre><ul><li><p>表达式：通常有一个返回值，可以放在任何需要值的地方，比如函数调用的参数、一个变量名、一个运算等</p></li><li><p>语句：通常表示一个完整的执行单位，一段完整的js可执行代码，有的语句也可以用表达式来执行，称为表达式语句</p></li><li><p>区别：语句用分号结尾，有些语句我们没有加分号，例如console.log虽然没加分号，但也是语句，因为js引擎会自动在解析的时候加上分号</p></li><li><p>特例：if语句，就不用加分号，也可以是完整的语句</p></li></ul><h2><span id="二-四个重要概念">二、四个重要概念</span></h2><h3><span id="1-双向数据绑定">1、双向数据绑定</span></h3><ul><li><p>数据绑定：数据从一个地方A转移(传递)到另一个地方B，而且这个操作由框架来完成</p></li><li><p>双向数据绑定：数据可以从View(视图层)流向Model(模型)，也可以从Model流向View</p><ul><li><p>视图(View)：也就是我们的页面（主要是Angular指令和表达式）</p></li><li><p>模型(Model)：作用域对象(当前为$rootScope)，它可以包含一些属性或方法</p></li><li><p>当改变View中的数据，Model对象的对应属性也会随之改变：ng-model指令 数据从View到Model</p></li><li><p>当Model域对象的属性发生改变时，页面对应数据随之更新：{{}}表达式  数据从Model到View</p></li><li><p>ng-model是双向数据绑定，而{{}}是单向数据绑定</p><p>(View——页面   Model——内存)</p></li></ul></li><li><p>ng-init：用来初始化当前作用域变量  （View–&gt;Model–&gt;View)</p></li></ul><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;  &lt;meta charset=&quot;UTF-8&quot;&gt;  &lt;title&gt;AngularJS实现&lt;/title&gt;&lt;/head&gt;&lt;body ng-app&gt;    &lt;!-- ng-app=&quot;&quot;除了接管区域，还会自动生成根作用域($rootScope) --&gt;  &lt;!--  &lt;input type=&quot;text&quot; ng-model=&quot;name&quot;&gt;   先传递到Model，再从Model到三个View中的相应位置  &lt;p&gt;姓名1：{{name}}&lt;/p&gt;  &lt;input type=&quot;text&quot; ng-model=&quot;name&quot;&gt;  &lt;p&gt;姓名2：{{name}}&lt;/p&gt;  --&gt;  &lt;script src=&quot;https://cdn.bootcdn.net/ajax/libs/angular.js/1.2.29/angular.js&quot;&gt;&lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p>显示结果</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/1589612197548.png" alt="测试"></p><p>过程</p><pre><code class="html">&lt;body ng-app&gt;</code></pre><p>ng-app=””除了接管区域，还会自动生成根作用域($rootScope)</p><pre><code class="html">&lt;input type=&quot;text&quot; ng-model=&quot;name&quot;&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/1589611921666.png" alt="过程1"></p><pre><code class="html">&lt;p&gt;姓名1：{{name}}&lt;/p&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/1589612077497.png" alt="过程2"></p><ul><li>初始化数据</li></ul><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;  &lt;meta charset=&quot;UTF-8&quot;&gt;  &lt;title&gt;AngularJS实现&lt;/title&gt;&lt;/head&gt;&lt;body ng-app ng-init=&quot;name=&#39;Tom&#39;&quot;&gt; &lt;!-- ng-init初始化数据 --&gt;  &lt;input type=&quot;text&quot; ng-model=&quot;name&quot;&gt;  &lt;p&gt;姓名1：{{name}}&lt;/p&gt;  &lt;input type=&quot;text&quot; ng-model=&quot;name&quot;&gt;  &lt;p&gt;姓名2：{{name}}&lt;/p&gt;  &lt;script src=&quot;https://cdn.bootcdn.net/ajax/libs/angular.js/1.2.29/angular.js&quot;&gt;&lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/1589612507494.png" alt="过程3"></p><h3><span id="2-依赖注入">2、依赖注入</span></h3><ul><li>依赖对象：完成某个特定的功能需要某个对象才能实现，这个对象就是依赖对象。</li><li>依赖注入：依赖的对象以形参的形式被注入进来使用，这种方式就是依赖注入。</li><li>Angular的<code>$scope</code>对象就是依赖对象，并且是依赖注入的形式进行使用。</li><li>回调函数的event的就是依赖对象</li><li>回调函数有形参就是依赖注入</li></ul><p>补：</p><p>开发的两种方式：</p><ul><li>命令式<ul><li>更加注重的是执行的过程</li><li>更像考试的解答题</li></ul></li><li>声明式<ul><li>更加注重的是执行的结果</li><li>声明式是对命令的局部包装</li><li>更像选择题或填空题</li></ul></li></ul><p>eg：数组中每一项加10</p><pre><code class="javascript">var arr = [1,2,3,4,5]var newArr1 = []// 命令式for(var i = 0; i &lt; arr.length; i++){    var value = arr[i] + 10    newArr1.push(value)}console.log(newArr1)// 声明式var newArr2 = arr.map(function(item, index){    return item + 10})console.log(newArr2)</code></pre><h2><span id="三-三个重要对象">三、三个重要对象</span></h2><h3><span id="1-作用域与控制器">1、作用域与控制器</span></h3><ul><li>作用域对象<ul><li>一个JS实例对象，ng-app指令默认会创建一个根作用域对象($rootScope)</li><li>它的属性和方法与页面中的指令或表达式是关联的</li></ul></li><li>控制器<ul><li>用来控制AngularJS应用数据的实例对象</li><li>ng-controller：指定控制器构造函数，Angular会自动new此函数创建控制器对象</li><li>同时Angular还有创建一个新的域对象$scope，它是$rootScope的子对象</li><li>在控制器函数中声明<code>$scope</code>形参，Angular会自动将<code>$scope</code>传入</li></ul></li></ul><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;  &lt;meta charset=&quot;UTF-8&quot;&gt;  &lt;title&gt;AngularJS实现&lt;/title&gt;&lt;/head&gt;&lt;body ng-app ng-init=&quot;age=12&quot;&gt;  &lt;div ng-controller=&quot;MyController&quot;&gt;    &lt;input type=&quot;text&quot; placeholder=&quot;姓&quot; ng-model=&quot;firstName&quot;&gt;    &lt;input type=&quot;text&quot; placeholder=&quot;名&quot; ng-model=&quot;lastName&quot;&gt;    &lt;p&gt;姓名1：{{firstName+'-'+lastName}}&lt;/p&gt;    &lt;p&gt;姓名2：{{getName()}}&lt;/p&gt;    {{age}}  &lt;/div&gt;  &lt;div&gt;    {{firstName}}   &lt;!-- 不能显示 --&gt;  &lt;/div&gt;  &lt;script src=&quot;https://cdn.bootcdn.net/ajax/libs/angular.js/1.2.29/angular.js&quot;&gt;&lt;/script&gt;  &lt;script&gt;    function MyController($scope){ // 形参必须是$scope      // console.log($scope)      // console.log(this instanceof MyController)      $scope.firstName = &#39;lu&#39;      $scope.lastName = &#39;wang&#39;      $scope.getName = function(){        return $scope.firstName + &#39; &#39; + $scope.lastName        // return this.firstName + &#39; &#39; + this.lastName      }    }  &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/1589613477087.png" alt="作用域"></p><h3><span id="2-模块和控制器">2、模块和控制器</span></h3><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;  &lt;meta charset=&quot;UTF-8&quot;&gt;  &lt;title&gt;AngularJS实现&lt;/title&gt;&lt;/head&gt;&lt;body ng-app=&quot;myApp&quot;&gt;   &lt;!--指向模块对象的名字--&gt;  &lt;div ng-controller=&quot;MyController&quot;&gt;    &lt;input type=&quot;text&quot; ng-model=&#39;empName&#39;&gt;    &lt;p&gt;员工名字1：{{empName}}&lt;/p&gt;  &lt;/div&gt;  &lt;div ng-controller=&quot;MyController2&quot;&gt;    &lt;input type=&quot;text&quot; ng-model=&#39;empName&#39;&gt;    &lt;p&gt;员工名字2：{{empName}}&lt;/p&gt;  &lt;/div&gt;  &lt;script src=&quot;https://cdn.bootcdn.net/ajax/libs/angular.js/1.5.5/angular.js&quot;&gt;&lt;/script&gt;  &lt;script&gt;    // console.log(angular)    /* // 创建模块对象    var myModule = angular.module(&quot;myApp&quot;, [])    // 生成作用域对象    myModule.controller(&#39;MyController&#39;, function($scope){      $scope.empName = &#39;Tom&#39;    })    myModule.controller(&#39;MyController2&#39;, function($scope){      $scope.empName = &#39;Jack&#39;    }) */    // 优化 链式调用    /* &lt;angular.module(&quot;myApp&quot;, [])          .controller(&#39;MyController&#39;, function($scope){  // 返回值是模块对象            $scope.empName = &#39;Tom&#39;          })          .controller(&#39;MyController2&#39;, function($scope){ // 隐式声明依赖注入            $scope.empName = &#39;Jack&#39;          }) */      // 但是，由于js代码压缩之后形参会用其他字母abcd代替，会造成angular解析不了，解决方案：      &lt;angular.module(&quot;myApp&quot;, [])          .controller(&#39;MyController&#39;, [&#39;$scope&#39;, function($scope){ // 显式声明依赖注入            $scope.empName = &#39;Tom&#39;          }])          .controller(&#39;MyController2&#39;, [&#39;$scope&#39;, function($scope){            $scope.empName = &#39;Jack&#39;          }])  &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><h2><span id="四-两个页面语法">四、两个页面语法</span></h2><h3><span id="1-表达式">1、表达式</span></h3><ul><li>使用Angular表达式：<ul><li>语法：</li><li>作用：显示表达式的结果数据</li><li>注意：表达式中引用的变量必须是当前域对象有的属性(包括其原型属性)</li></ul></li><li>操作的数据<ul><li>基本类型数据：Number/String/Boolean</li><li>undefined, Infinity,NaN,null解析为空串””，不显示任何效果</li><li>对象的属性或方法</li><li>数组</li></ul></li></ul><h3><span id="2-常用指令">2、常用指令</span></h3><ul><li>Angular指令<ul><li>Angular为HTML页面扩展的：自定义标签属性或标签</li><li>与Angular的作用域对象(scope)交互，扩展页面的动态表现力</li></ul></li><li>常用指令<ul><li>ng-app：</li><li>ng-model</li><li>ng-init</li><li>ng-click</li><li>ng-controller</li><li>ng-bind</li><li>ng-repeat</li><li>ng-show</li><li>ng-hide</li><li>ng-class</li><li>ng-style</li><li>ng-mouseenter</li><li>ng-mouseleave</li></ul></li></ul><h2><span id="练习">练习</span></h2><h3><span id="1-我的笔记">1、我的笔记</span></h3><p>功能：在文本输入区域写文本，下方显示可输入剩余字符量；保存能够将文本保存，这样点击读取的时候文本能接着在文本输入区域显示，点删除删除</p><h3><span id="2-我的备忘录">2、我的备忘录</span></h3><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="笔记" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="前端" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="前端" scheme="https://wallleap.cn/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="AngularJS" scheme="https://wallleap.cn/tags/AngularJS/"/>
    
  </entry>
  
  <entry>
    <title>ES和模块化学习笔记</title>
    <link href="https://wallleap.cn/2020/06/04/ES/"/>
    <id>https://wallleap.cn/2020/06/04/ES/</id>
    <published>2020-06-04T15:33:49.000Z</published>
    <updated>2025-01-24T09:22:15.348Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2><span id="一-理解es">一、理解ES</span></h2><ol><li><p>全称: ECMAScript</p><ul><li>它是一种由ECMA组织(前身为欧洲计算机制造商协会)制定和发布的脚本语言规范</li><li>我们学习的JavaScript就是ECMA的实现，但属于ECMAScript和JavaScript平时表达同一个意思</li></ul></li><li><p>JS包含三个部分：</p><ul><li><p>ECMAScript(js基础、核心)</p></li><li><p>扩展–&gt;浏览器端</p><ul><li>BOM(浏览器对象模型)</li><li>DOM(文档对象模型)</li></ul></li><li><p>扩展–&gt;服务器端</p><ul><li>Node.js</li></ul></li></ul></li><li><p>ES的几个重要版本</p><ul><li>ES5：09年发布</li><li><strong>ES6(ES2015)</strong>:15年发布，也称为ECMA2015——重点</li><li>ES7(ES2016):16年发布，也称为ECMA2016（变化不大）</li></ul></li></ol><h2><span id="二-es5">二、ES5</span></h2><h3><span id="1-严格模式">1、严格模式</span></h3><ul><li><p>理解</p><ul><li>运行模式: 正常(混杂)模式与严格模式</li><li>这种模式使得JavaScript在更严格的语法条件下运行</li></ul></li><li><p>目的/作用: </p><ul><li>使得Javascript在更严格的条件下运行</li><li>消除Javascript语法的一些不合理、不严谨之处，减少一些怪异行为</li><li>消除代码运行的一些不安全之处，保证代码运行的安全</li></ul></li></ul><ul><li><p>使用</p><ul><li>在全局或函数的第一条语句定义为：<code>use strict</code></li><li>如果浏览器不支持，只解析为一条简单的语句，没有任何副作用</li></ul></li><li><p>语法和行为改变</p><ul><li>声明定义变量必须用var</li><li>禁止自定义的函数中的this关键字指向全局对象(window)</li><li>创建eval作用域, 更安全</li><li>对象不能有重名的属性</li></ul><pre><code class="html">&lt;script&gt;  &#39;use strict&#39;  var username = &#39;luwang&#39;  // name = &#39;luwang&#39; 在严格模式下不用var声明变量会报错  console.log(username)  function Person(name, age){    this.name = name    this.age = age  }  new Person(&#39;luwang&#39;, 23)  // Person(&#39;luwang&#39;, 23) //没有new会报错  var str = &#39;web&#39;  eval(&#39;var str = &quot;HTML&quot;; alert(str)&#39;) // HTML  alert(str) // web 即开启严格模式之后不会污染全局作用域  var obj = {    username: &#39;luwang&#39;,    username: &#39;luwang&#39;  // 定义重名了  }&lt;/script&gt;</code></pre></li></ul><h3><span id="2-json对象">2、JSON对象</span></h3><ul><li>作用: 用于在json对象/数组与js对象/数组相互转换</li><li><code>JSON.stringify(obj/arr)</code> js对象(数组)转换为json对象(数组)</li><li><code>JSON.parse(json)</code> json对象(数组)转换为js对象(数组)<pre><code class="html">&lt;script&gt;var obj = {username: &#39;wallleap&#39;}obj = JSON.stringify(obj)console.log(typeof obj)obj = JSON.parse(obj)console.log(typeof obj)&lt;/script&gt;</code></pre></li></ul><h3><span id="3-object扩展">3、Object扩展</span></h3><p>ES5给Object扩展了一些静态方法，常用的两个：</p><ul><li><p><code>Object.create(prototype[, descriptors])</code>: 创建一个新的对象</p><ul><li><p>作用：以指定对象为原型创建新的对象</p></li><li><p>为新的对象指定新的属性, 并对属性进行描述</p><ul><li><code>value</code>: 指定值</li><li><code>writable</code> : 标识当前属性值是否是可修改的, 默认为<code>false</code></li><li><code>configurable</code>：标识当前属性是否可以被删除，默认为<code>false</code></li><li><code>enumerable</code>：标识当前属性是否能用for in枚举，默认为<code>false</code></li></ul><pre><code class="html">&lt;script&gt;  var obj = {username: &#39;luwang&#39;, age:23}  var obj1 = {}  obj1 = Object.create(obj, {  // obj的属性为obj1的原型    sex: {      value: &#39;男&#39;,      writable: true,  // 默认false      configurable: true,      enumerable: true    }  })  console.log(obj1.sex)  obj1.sex = &#39;nan&#39;  console.log(obj1.sex)  delete obj1.sex  console.log(obj1)  for(var i in obj1){    console.log(i)  }&lt;/script&gt;</code></pre></li></ul></li></ul><ul><li><p><code>Object.defineProperties(object, descriptors)</code>: 为指定对象定义扩展多个属性</p><ul><li><strong>get方法</strong> : 用来得到当前属性值的回调函数</li><li><strong>set方法</strong> : 用来监视当前属性值变化的回调函数</li></ul><pre><code class="html">&lt;script&gt;  var obj = {username: &#39;luwang&#39;, age:23}  var obj1 = {}  var obj2 = {firstName: &#39;lu&#39;, lastName: &#39;wang&#39;}  Object.defineProperties(obj2, {    fullName: { // 此方法在原型中      get: function(){ // 获取扩展属性的值        console.log(&#39;get方法被调用&#39;)        return this.firstName + &#39; &#39; + this.lastName      },      set: function(data){ // 监听扩展属性，当扩展属性发生变化的时候会自动调用，自动调用后会讲变化的值作为实参注入到set函数        console.log(&#39;set方法被调用，&#39;, data)        var names = data.split(&#39; &#39;) // 根据空格拆分为数组        this.firstName = names[0]        this.lastName = names[1]      }    }  })  console.log(obj2.fullName) // get会自动调用   obj2.fullName = &#39;lu wang&#39;    console.log(obj2.fullName)&lt;/script&gt;</code></pre><p>console.log(obj2)   fullName</p><p>惰性求值：点击才给值(什么时候要什么时候给)，会再次调用get方法</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/1589786198022.png" alt="惰性求值"></p><ul><li>什么时候调用：<ul><li>get方法：获取扩展属性值的时候get方法自动调用</li><li>set方法：监听</li></ul></li><li>存储器属性：setter，getter一个用来存值，一个用来取值</li></ul></li></ul><p>对象本身也有两个方法</p><ul><li>get propertyName(){}</li><li>set propertyName(){}</li></ul><pre><code class="html">&lt;script&gt;  var obj = {    firstName: &#39;lu&#39;,     lastName: &#39;wang&#39;,    get fullName(){      return this.firstName + &#39; &#39; + this.lastName    },    set fullName(data){      var names = data.split(&#39; &#39;)      this.firstName = names[0]      this.lastName = names[1]    }  }  console.log(obj)  obj.fullName = &#39;lu wang&#39;  console.log(obj.fullName)&lt;/script&gt;</code></pre><h3><span id="4-array扩展">4、Array扩展</span></h3><ul><li><p>Array.prototype.indexOf(value) : 得到值在数组中的第一个下标</p></li><li><p>Array.prototype.lastIndexOf(value) : 得到值在数组中的最后一个下标</p></li><li><p><strong>Array.prototype.forEach(function(item, index){}) : 遍历数组</strong></p></li><li><p><strong>Array.prototype.map(function(item, index){}) : 遍历数组返回一个新的数组</strong></p></li><li><p><strong>Array.prototype.filter(function(item, index){}) : 遍历过滤出一个子数组，返回条件为true的值</strong></p><pre><code class="javascript">var arr = [2,4,5,1,6,7,4,3,9]console.log(arr.indexOf(4))console.log(arr.lastIndexOf(4))arr.forEach(function(item, index){    console.log(item, index)})var arr1 = arr.map(function(item, index){    return item + 10})console.log(arr, arr1)arr.filter(fuction(item, index){    return item &gt; 3})</code></pre></li></ul><h3><span id="5-function扩展">5、Function扩展</span></h3><p>this，强制绑定使用call和bind</p><p>eg:</p><pre><code class="javascript">var obj = {username: &#39;luwang&#39;}function foo(){    console.log(this)   }foo() // this--&gt;Window 全局// call和apply不传参的时候是一样的foo.call(obj) // this--&gt;{username: &#39;luwang&#39;} obj对象foo.apply(obj) // this--&gt;{username: &#39;luwang&#39;} obj对象// bind的特点： 绑定完this不会立即调用当前的函数，而是将函数返回// var bar = foo.bind(obj)// bar()foo.bind(obj)()// 传入参数的形式var obj1 = {age: 23}function fun(data){    console.log(this, data)}fun(22) // Window  22// call直接从第二个参数开始，依次传入fun.call(obj1, 21) // {age: 23} 21// 第二参数必须是数组，传入放在数组里fun.apply(obj1, [20]) // {age: 23} 20// bind传参的方式通call一样fun.bind(obj1, 18)()</code></pre><ul><li><p><code>Function.prototype.bind(obj)</code></p><ul><li>将函数内的this绑定为obj, 并将函数返回</li></ul></li><li><p>面试题: 区别bind()与call()和apply()?</p><ul><li><p><code>fn.bind(obj)</code> : 指定函数中的this, 并返回函数(不会立即调用)，一般用在回调函数绑定其他对象的this</p><pre><code class="javascript">var obj = {username: &#39;luwang&#39;}setTimeout(function(){    console.log(this) // Window}, 1000)setTimeout(function(){    console.log(this) // Window}.bind(obj), 1000)</code></pre></li><li><p><code>fn.call(obj)</code>: 指定函数中的this,并调用函数</p></li><li><p><code>fn.apply(obj)</code></p></li></ul></li></ul><h3><span id="6-date扩展">6、Date扩展</span></h3><ul><li>Date.now() : 得到当前时间值</li></ul><h2><span id="三-es6">三、ES6</span></h2><h3><span id="1-2个新的关键字">1、2个新的关键字</span></h3><ul><li>块作用域：ES5中没有(只有全局和函数作用域)，ES6有</li></ul><ol><li><p><strong>let</strong></p><ul><li><p>作用：与var相似，用于声明一个变量</p></li><li><p>特点</p><ul><li>在块作用域内有效</li><li>不能重复声明</li><li>不会预处理，不存在变量提升</li></ul></li><li><p>应用</p><ul><li><p>循环遍历加监听</p><pre><code class="html">&lt;br/&gt;&lt;button&gt;按钮1&lt;/button&gt;&lt;br/&gt;&lt;br/&gt;&lt;button&gt;按钮2&lt;/button&gt;&lt;br/&gt;&lt;br/&gt;&lt;button&gt;按钮3&lt;/button&gt;&lt;script&gt;    var btns = document.getElementsByTagName(&#39;button&#39;)    for(var i = 0; i &lt; btns.length; i++){        var btn = btns[i]        btn.onclick = function(){            alert(i)        }    }    /*     * 一直会显示3     * 点击事件对应的是回调函数，回调函数又称勾子函数，回调函数会被放到事件队列中，等主线程上的代码执行完毕之后再通过钩子一样的形式，勾出来执行     * 以前的方式是通过闭包，立即执行函数(自己的作用域)    */    for(var i = 0; i &lt; btns.length; i++){      var btn = btns[i]      ;(function(i){  // 声明的形参        btn.onclick = function(){        alert(i)      }      })(i)   // 传的实参    }    /*    * 闭包利用的是函数作用域的特点    * 因此可以直接使用let    */    for(let i = 0; i &lt; btns.length; i++){ // let，在块作用域内有效      var btn = btns[i]      btn.onclick = function(){        alert(i)      }    }&lt;/script&gt;</code></pre></li><li><p>使用let代替var是趋势</p></li></ul></li></ul></li><li><p><strong>const</strong></p><ul><li><p>作用：定义一个常量</p></li><li><p>特点</p><ul><li>不能修改</li><li>其他特点同let</li></ul></li><li><p>应用</p><ul><li>保存不用改变的数据</li></ul><pre><code class="javas">const PI = 3.1415926</code></pre></li></ul></li></ol><h3><span id="2-变量对象的解构赋值">2、变量(对象)的解构赋值</span></h3><ul><li><p>理解：从对象或数组中提取数据，并赋值给多个变量</p></li><li><p>将包含多个数据的对象(数组)一次赋值给多个变量</p></li><li><p>数据源: 对象/数组</p></li><li><p>目标: {a, b}/[a, b]</p></li><li><p>对象的解构赋值：<code>let {n, a} = {n:&#39;tom&#39;, a:12}</code> 把对象中的值赋值出来(根据属性名key)</p></li><li><p>数组的解构赋值：<code>let[a, b] = [1, &#39;luwang&#39;]</code> (根据下标)</p></li><li><p>用途：给多个形参赋值</p><pre><code class="javascript">let obj = {    username: &#39;luwang&#39;,    age: 23}// let username = obj.username// let age = obj.age// console.log(username, age)// let {username, age} = obj // 对象，因此需要以对象的形式来接收 只需要一个就写一个，不需要按顺序// console.log(username, age)let {age} = objconsole.log(age)let arr = [1, 3, 5,&#39;abc&#39;, true]// let [a, b, c, d, e] = arr// console.log(a, b, c, d, e)// let [a, b] = arr// console.log(a, b)let [,,a, b] = arrconsole.log(a, b)function foo({username, age}){ // {username, age} = obj    console.log(username, age)}foo(obj)</code></pre></li></ul><h3><span id="3-各种数据类型的扩展">3、各种数据类型的扩展</span></h3><ul><li><p>字符串</p><ul><li><strong>模板字符串</strong> <ul><li>作用: 简化字符串的拼接</li><li>模板字符串必须用``，波浪线那个</li><li>变化的部分使用${xxx}定义</li></ul></li></ul><pre><code class="javascript">let obj = {username: &#39;luwang&#39;, age: 23}/** 之前的写法：简单拼串* 缺点：可能会拼错，效率低。比如，url携带10个参数，动态拼起来*/let str = &#39;My name is &#39; + obj.username + &#39;, age is &#39;+ obj.ageconsole.log(str)/** ES6提供的模板字符串*/str = `My name is ${obj.username} age is ${obj.age}`</code></pre><ul><li><code>includes(str)</code> : 判断是否包含指定的字符串</li><li><code>startsWith(str)</code>: 判断是否以指定字符串开头</li><li><code>endsWith(str)</code> : 判断是否以指定字符串结尾</li><li><code>repeat(count)</code>: 重复指定次数</li></ul><pre><code class="javascript">let str = &#39;asdfghjkklqwrtyuiopzxcvbnm123467890&#39;console.log(str.includes(&#39;t&#39;)) // trueconsole.log(str.includes(&#39;abc&#39;)) // falseconsole.log(str.startsWith(&#39;a&#39;)) // trueconsole.log(str.endsWith(&#39;0&#39;)) // trueconsole.log(str.repeat(2)) // asdfghjkklqwrtyuiopzxcvbnm123467890asdfghjkklqwrtyuiopzxcvbnm123467890</code></pre></li></ul><ul><li><p>数值扩展</p><ul><li>二进制与八进制表示法：二进制用0b，八进制用0o</li><li><code>Number.isFinite(i)</code>：判断是否是有限大的数字</li><li><code>Number.isNaN(i)</code>：判断是否是NaN</li><li><code>Number.isInteger(i)</code>：判断是否是整数</li><li><code>Number.parseInt(str)</code>：将字符串转换为对应的数值</li><li><code>Math.trunc(i)</code>：直接去除小数部分</li></ul><pre><code class="javascript">console.log(0b1010)console.log(0o12)console.log(Number.isFinite(Infinity))console.log(Number.isNaN(NaN))console.log(Number.isInteger(123.1))console.log(Number.isInteger(123.0))console.log(Number.parseInt(&#39;123abc123&#39;)) // 123console.log(Number.parseInt(&#39;a123abc123&#39;)) // NNconsole.log(Math.trunc(123.123)) // 123</code></pre></li></ul><ul><li><p>对象</p><ul><li><p><strong>简化的对象写法</strong></p><ul><li>省略同名的属性值</li><li>省略方法的function</li></ul><pre><code class="JavaScript">let name = &#39;Tom&#39;;let age = 12;/* 正常情况 */let obj = {    name: name,    age: age，    getName: function(){        retrun this.name    }}console.log(obj)/* key和value相同，可以省略 */let person = {    name,  // 同名的属性可以不写    age,    setName (name) { // 可以省略函数的function        this.name = name    }}</code></pre></li><li><p><code>Object.assign(target, source1, source2..)</code>: 将源对象的属性复制到目标对象上</p></li></ul><pre><code class="javascript">let obj = {}let obj1 = {username:&#39;a&#39;, age: 20}let obj2 = {sex: &#39;男&#39;}// Object.assign(obj, obj1)// console.log(obj) // {username: &quot;a&quot;, age: 20}Object.assign(obj, obj1, obj2)console.log(obj) // {username: &quot;a&quot;, age: 20, sex: &quot;男&quot;}</code></pre><ul><li><code>Object.is(v1, v2)</code> : 判断2个数据是否完全相等</li></ul><pre><code class="javascript">console.log(0 == -0) // trueconsole.log(NaN == NaN) //falseconsole.log(Object.is(0, -0)) // falseconsole.log(Object.is(NaN, NaN)) // true</code></pre><ul><li><strong>proto</strong>属性 : 隐式原型属性.ES6中能直接操作<code>__proto__</code>属性</li></ul><pre><code class="javascript">let obj = {}let obj1 = {salary: 5000000}obj.__proto__ = obj1console.log(obj)console.log(obj.salary)</code></pre></li></ul><ul><li><p>数组</p><ul><li><code>Array.from(v)</code> : 将伪数组对象或可遍历对象转换为真数组</li><li><code>Array.of(v1, v2, v3)</code> : 将一系列值转换成数组</li><li><code>find(function(value, index, arr){return true})</code> : 找出第一个满足条件返回true的元素</li><li><code>findIndex(function(value, index, arr){return true})</code>: 找出第一个满足条件返回true的元素下标</li></ul><pre><code class="html">&lt;button&gt;測試1&lt;/button&gt;&lt;br&gt;&lt;button&gt;測試2&lt;/button&gt;&lt;br&gt;&lt;button&gt;測試3&lt;/button&gt;&lt;script&gt;  let btns = document.getElementsByTagName(&#39;button&#39;)  // 偽數組 不能使用forEach(數組的方法)  Array.from(btns).forEach(function(item, index){    console.log(item)  })  let arr = Array.of(1, 4, &#39;abc&#39;, true)  console.log(arr)  let arr2 = [2,3,4,2,5,7,3,6]  console.log(arr2.find(function(item, index){    return item &gt; 4  }))  console.log(arr2.findIndex(function(item, index){    return item &gt; 4  }))&lt;/script&gt;</code></pre></li></ul><ul><li><p>函数</p><ul><li><strong>箭头函数</strong><ul><li>用来定义匿名函数</li><li>基本语法:<ul><li>没有参数: () =&gt; console.log(‘xxxx’)   箭头前的()不能省略</li><li>一个参数: i =&gt; i+2  可以省略</li><li>大于一个参数: (i,j) =&gt; i+j  ()不能省略</li><li>函数体不用大括号: 默认返回结果</li><li>函数体如果有多个语句, 需要用{}包围</li></ul></li><li>使用场景: 多用来定义回调函数</li><li>特点：<ul><li>简洁</li><li>箭头函数没有自己的this，箭头函数的this不是调用的时候决定的，而是在定义的时候所处的对象就是它的this</li><li>扩展理解：箭头函数的this看外层是否有函数<ul><li>箭头外层有函数，this是外层函数的this</li><li>箭头外层无函数，this是window</li></ul></li></ul></li></ul></li></ul><pre><code class="javascript">let fun = function(){console.log(&#39;fun&#39;)}fun()// 1、没有形参let fun1 = () =&gt; console.log(&#39;fun1&#39;)fun1()// 2、只有一个形参let fun2 = (a) =&gt; console.log(a)// 可省略() let fun2 = a =&gt; console.log(a)fun2(&#39;aaa&#39;)// 3、两个及两个以上的形参let fun3 = (x,y) =&gt; console.log(x, y)fun3(1, 2)// I、函数体只有一条语句或表达式，{}可以省略--&gt;会自动返回语句执行的结果或表达式的结果let foo = (x, y) =&gt; x + y// let foo = (x, y) =&gt; {return x + y}console.log(foo(1, 3))// II、函数体不止一条语句或者表达式， {}不可以省略let foo2 = (x, y) =&gt; {    console.log(x, y)    return x + y}console.log(foo2(3, 5))// 箭头函数的this&lt;br/&gt;&lt;button id=&quot;btn1&quot;&gt;按钮1&lt;/button&gt;&lt;br/&gt;&lt;br/&gt;&lt;button id=&quot;btn2&quot;&gt;按钮2&lt;/button&gt;&lt;br/&gt;&lt;br/&gt;&lt;button id=&quot;btn3&quot;&gt;按钮3&lt;/button&gt;&lt;script&gt;    let btn1 = document.getElementById(&#39;btn1&#39;)    let btn2 = document.getElementById(&#39;btn2&#39;)    let btn3 = document.getElementById(&#39;btn3&#39;)    btn1.onclick = function(){      console.log(this) // &lt;button id=&quot;btn1&quot;&gt;按钮1&lt;/button&gt;    }    btn2.onclick = () =&gt; {      console.log(this)  // Window    }    let obj = {      name: &#39;箭头函数&#39;,      getName: function(){        btn3.onclick = () =&gt; {          console.log(this) // {name: &quot;箭头函数&quot;, getName: ƒ}        }      }    }    obj.getName()    let obj1 = {      name: &#39;箭头函数&#39;,      getName: () =&gt; {        btn3.onclick = () =&gt; {          console.log(this) // Window        }      }    }    obj.getName()&lt;/script&gt;</code></pre></li></ul><ul><li><p><strong>3点运算符/点点点运算符</strong></p><ul><li>rest(可变)参数<ul><li>通过形参左侧的…来表达, 取代arguments的使用</li><li>比arguments灵活，只能是最后部分形参参数<ul><li>arguments是伪数组，有length，但是没有数组的一般方法，不能使用forEach遍历</li><li>callee是arguments的一个属性，等于函数本身，递归的时候可以写为：<code>arguments.callee()</code></li></ul></li></ul></li></ul><pre><code class="javascript">// argumentsfunction foo(a, b){  console.log(arguments)  // arguments.callee() 调用自身，相当于foo(参数)  /* arguments.forEach(function(item, index){ // 会报错，伪数组并没有数组的一般方法      console.log(item, index)  }) */}foo(2,5)</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/1590476357564.png" alt="参数"></p><pre><code class="javascript">// 点点点运算符function foo(...value){  console.log(arguments)  console.log(value) // 就是一个正常的数组  value.forEach(function(item, index){    console.log(item, index)  })}foo(2,5)function foo(a, ...value){// ...value只能放在最后面  console.log(arguments)  // arguments.callee()  console.log(value)    // 使用的时候不用加...  value.forEach(function(item, index){    console.log(item, index)  })}foo(2, 3, 5, 7) // 最前面的就是a，value就不包括它了</code></pre></li></ul><ul><li><p>第二种用法——扩展运算符</p><ul><li>可以分解出数组或对象中的数据</li></ul><pre><code class="javascript">let arr = [1, 6]let arr1 = [2, 3, 4, 5]arr = [1, ...arr1, 6]console.log(arr)    // (6) [1, 2, 3, 4, 5, 6]  数组console.log(...arr) // 1 2 3 4 5 6  每项值</code></pre></li></ul><ul><li><p><strong>形参的默认值</strong></p><ul><li>定义形参时指定其默认的值</li><li>当不传入参数的时候默认使用形参里的默认值</li></ul><pre><code class="javascript">// 定义一个点的坐标的构造函数function Point(x, y){  this.x = x  this.y = y}let point = new Point(50, 20)console.log(point) // Point {x: 50, y: 20}// 忘记传参let point1 = new Point()console.log(point1) // Point {x: undefined, y: undefined}/* * 因此会有需求，在忘记传参的时候使用默认值* 在形参的位置赋默认值*/function Point(x = 0, y = 0){  this.x = x  this.y = y}let point = new Point(50, 20)console.log(point) // Point {x: 50, y: 20}// 忘记传参，使用默认值let point1 = new Point()console.log(point1) // Point {x: 0, y: 0}</code></pre></li></ul><h3><span id="4-深度克隆">4、深度克隆</span></h3><ul><li><p>拷贝数据：</p><ul><li>基本数据类型<ul><li>拷贝后会生成一份新的数据</li><li>修改拷贝以后的数据不会影响原数据</li></ul></li><li>引用数据类型<ul><li>拷贝后不会生成新的数据，而是拷贝的引用</li><li>修改拷贝以后的数据会影响原来的数据</li></ul></li></ul><pre><code class="javascript">// 基本數據類型let str = &#39;abcd&#39;let str2 = strconsole.log(str, str2)str2 = &#39;&#39;console.log(str, str2)// 引用數據類型let obj = {username: &#39;kobe&#39;, age:39}let obj1 = objconsole.log(obj, obj1)obj1.username = &#39;wade&#39;console.log(obj, obj1)let arr = [1,4,{username:&#39;kobe&#39;,age:39}]let arr2 = arrarr2[0] = &#39;abcd&#39;console.log(arr, arr2)</code></pre></li></ul><ul><li><p>拷贝数据的方法</p><ul><li>直接赋值给一个变量    // 浅拷贝(浅克隆)——能影响</li><li><code>Object.assign()</code>     // 浅拷贝</li></ul><pre><code class="javascript">let obj = {username: &#39;kobe&#39;}let obj2 = Object.assign(obj)console.log(obj, obj2)obj2.username = &#39;wade&#39;console.log(obj, obj2)</code></pre><ul><li><code>Array.prototype.concat()</code>   //浅拷贝</li></ul><pre><code class="javascript">let arr = [1, 4, {username: &#39;kobe&#39;}]let testArr = [&#39;ce&#39;, &#39;shi&#39;]// let arr2 = arr.concat(testArr)let arr2 = arr.concat()console.log(arr, arr2)arr2[1] = &#39;a&#39;console.log(arr, arr2)arr2[2].username = &#39;wade&#39;console.log(arr, arr2)</code></pre><ul><li><code>Array.prototype.slice()</code>   // 浅拷贝</li></ul><pre><code class="javascript">let arr = [1, 4, {username: &#39;kobe&#39;}]let arr2 = arr.slice()arr2[2].username = &#39;wade&#39;console.log(arr, arr2)</code></pre><ul><li><code>JSON.parse(JSON.stringify())</code>   // 深拷贝(深度克隆)——修改不影响引用类型的原数据。<ul><li>拷贝的数据里不能有函数(处理不了)，先是将数据转为了JSON格式，字符串对应js中的只有对象和数组，没有函数</li></ul></li></ul><pre><code class="javascript">let arr = [1, 4, {username: &#39;kobe&#39;}]let arr2 = JSON.stringify(arr)arr2 = JSON.parse(arr2)arr2[2].username = &#39;wade&#39;console.log(arr, arr2)</code></pre></li><li><p>浅拷贝</p><ul><li>特点：拷贝的是引用，修改拷贝以后的数据会影响原来的数据，使得原数据不安全</li></ul></li><li><p>深拷贝</p><ul><li>特点：拷贝的时候生成新数据，修改拷贝以后的数据不会影响原数据</li></ul></li><li><p>这两个都是针对对象/数组来说的</p></li><li><p>思考：如何实现深度拷贝？</p><ul><li><p>拷贝的数据里有对象/数组，即使有对象/数组可以继续遍历对象、数组，拿到里边的每一项值，直到拿到的是基本数据类型，然后再去复制(拷贝的数据里不能有对象/数组)</p></li><li><p>知识点储备</p><ul><li>如何判断数据类型<ol><li><code>typeof</code>返回的数据类型： String、Number、boolean、undefined、Object、Function</li><li><code>Object.prototype.toString.call(obj)</code>—&gt;<code>Object.prototype.toString.call(data).slice(8, -1)</code></li></ol></li></ul><pre><code class="javascript">let result = &#39;abc&#39; // [object String]result = null // [object Null]result = [1, &#39;a&#39;] // test-demo.html:17 [object Array]// console.log(Object.prototype.toString.call(result))// console.log(typeof Object.prototype.toString.call(result)) // 返回的是string// 因此可以用下面这种方式显示数据类型console.log(Object.prototype.toString.call(result).slice(8, -1))</code></pre><ul><li><p>for in 循环</p><ul><li>用于循环对象，枚举出来的是属性名</li></ul><pre><code class="javascript">let obj = {username: &#39;kobe&#39;, age: 39}for(let i in obj){  console.log(i) // username age}</code></pre><ul><li>循环数组时，枚举的是下标</li></ul><pre><code class="javascript">let arr = [1,3,&#39;abc&#39;]for(let i in arr){  console.log(i) // 0 1 2}</code></pre></li></ul></li><li><p>实现深度克隆</p></li></ul><pre><code class="javascript">// 定义检测数据类型的功能函数function checkType(target){  return Object.prototype.toString.call(target).slice(8, -1)}console.log(checkType([1,2,&#39;a&#39;]))// 实现深度克隆--&gt;对象/数组function clone(target){  // 1.判断拷贝的数据类型  let result, targetType = checkType(target)  if(targetType === &#39;Object&#39;){    // 2.初始化数据，对象/数组/其他类型仍不改变    result = {}  }else if(targetType === &#39;Array&#39;){    result = []  }else{    return target  }  // 3.遍历目标数据  for(let i in target){    // 遍历数据结构的每一项值    let value = target[i] // key、下标都可以用[]    // 判断目标结构里的每一值是否存在数组/对象    if(checkType(value) === &#39;Object&#39; || checkType(value) === &#39;Array&#39;){// 对象、数组中还嵌套了对象数组      // 继续遍历获取到的value值      result[i] = clone(value)    }else{ // 获取到的value值是基本数据类型或函数      result[i] = value    }  }  return result}let arr = [1,2,{username: &#39;kobe&#39;}]let arr2 = clone(arr)console.log(arr, arr2)arr2[2].username = &#39;abdc&#39;console.log(arr, arr2)let obj = {username: &#39;luwang&#39;, age: 23}let obj2 = clone(obj)console.log(obj, obj2)obj2.username = &#39;LUWANG&#39;console.log(obj, obj2)</code></pre></li></ul><h3><span id="7-class类">7、class类</span></h3><ul><li><p>通过class定义类，实现类的继承</p><ul><li><p>回顾：原型、构造函数、构造函数+原型——继承</p><pre><code class="javascript">function Person(name, age){  this.name = name  this.age = age}let person = new Person(&#39;kobe&#39;, 39)console.log(person)</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/1591769869983.png" alt="继承"></p></li></ul></li><li><p>在类中通过 constructor() 定义构造方法(相当于构造函数)</p><pre><code class="javascript">// 定義一個人物的類class Person{ // 类的构造方法  constructor(name, age){    this.name = name    this.age = age  }  // 类的一般方法  showMe(){    console.log(this.name)  }}let person = new Person(&#39;kobe&#39;, 39)console.log(person)person.showName()</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/1591770059566.png" alt="继承"></p></li><li><p>一般方法: xxx () {}</p></li><li><p>用extends来定义子类（实现累的继承）</p></li><li><p>用super()来调用父类的构造方法</p></li><li><p>子类方法自定义: 将从父类中继承来的方法重新实现一遍（重写从父类继承的一般方法）</p><pre><code class="javascript">// 定義一個人物的類class Person{  constructor(name, age){    this.name = name    this.age = age  }  showMe(){    console.log(&#39;調用父類的方法&#39;)    console.log(this.name, this.age)  }}let person = new Person(&#39;kobe&#39;, 39)person.showMe()// 子類class StarPerson extends Person{  constructor(name, age, salary){    super(name, age) // 調用父類的構造方法    this.salary = salary  }  showMe(){    console.log(&#39;子類重寫的方法&#39;)    console.log(this.name, this.age, this.salary)  }}let p1 = new StarPerson(&#39;wade&#39;, 36, 10000000)console.log(p1)p1.showMe()</code></pre></li><li><p>js中没有方法重载(方法名相同, 但参数不同)的语法</p></li></ul><h3><span id="4-setmap容器结构">4、set/Map容器结构</span></h3><ul><li><p>容器: 能保存多个数据的对象, 同时必须具备操作内部数据的方法</p></li><li><p>任意对象都可以作为容器使用, 但有的对象不太适合作为容器使用(如函数)</p></li><li><p><strong>Set的特点</strong>: 保存多个value, value是不重复 ====&gt;数组元素去重</p></li><li><p><strong>Map的特点</strong>: 保存多个key–value, key是不重复, value是可以重复的</p></li><li><p>API</p><ul><li>Set容器：无序不可重复的多个value的集合体<ul><li><code>Set()</code></li><li><code>Set(arr)</code> //arr是一维数组</li><li><code>add(value)</code></li><li><code>delete(value)</code></li><li><code>clear()</code></li><li><code>has(value)</code></li><li><code>size</code></li><li>Map容器：无序的key、不重复的多个key-value的集合体</li></ul></li><li><code>Map()</code></li><li><code>Map(arr)</code>//arr是二维数组</li><li><code>set(key, value)</code></li><li><code>get(key)</code></li><li><code>delete(key)</code></li><li><code>clear()</code></li><li><code>has(key)</code></li><li><code>size</code></li></ul><pre><code class="javascript">// let set = new Set()let set = new Set([1,2,4,5,2,3,6]) // 重复的会去除console.log(set)set.add(7)console.log(set.size, set)console.log(set.has(8))console.log(set.has(7))set.delete(7)console.log(set.size, set)set.clear()console.log(set.size, set)// let map = new Map()let map = new Map([[&#39;username&#39;, &#39;aaa&#39;], [&#39;age&#39;, 35], [&#39;sex&#39;, &#39;female&#39;]]) // 二维数组，且只能有两值(一个是key，一个是value)map.set(&#39;other&#39;, &#39;shuoming&#39;)console.log(map.size, map)map.delete(&#39;other&#39;)console.log(map)console.log(map.has(&#39;username&#39;))map.clear()console.log(map)</code></pre></li></ul><h3><span id="5-forof循环">5、for–of循环</span></h3><ul><li>可以遍历任何容器（Set、Map）</li></ul><pre><code class="javascript">let set = new Set([1, 2, 4, 3, 4, 5]) for(let i of set){  console.log(i)}// 可以用Set给数组去重let arr = [1,2,4,5,5,6,2]let arr1 = arrarr = [] // 保留数组类型let set = new Set(arr1)for(let i of set){  arr.push(i)}console.log(arr)</code></pre><ul><li>数组</li><li>对象</li><li>伪/类对象</li><li>字符串</li><li>可迭代的对象</li></ul><h3><span id="6-promise对象">6、Promise对象</span></h3><ul><li><p>理解：</p><ul><li>Promise对象代表了某个将要发生的事件（通常是一个异步操作）</li><li>ES6的Promise是一个构造函数，用来生成promise实例</li></ul><ul><li><p>解决<code>回调地狱</code>(回调函数的层层嵌套, 编码是不断向右扩展, 阅读性很差)；有了promise对象，可以将异步操作以同步的流程表达出来，避免了层层嵌套的回调函数（回调地狱）</p></li><li><p>能以同步编码的方式实现异步调用</p></li><li><p>在es6之前原生的js中是没这种实现的, 一些第三方框架(jQuery)实现了promise</p></li><li><p>promise对象的3个状态：</p><ul><li>pending：初始化状态</li><li>fullfilled：成功状态</li><li>rejected：失败状态</li></ul></li><li><p>应用：</p><ul><li>使用promise实现超时处理</li><li>使用promise封装处理Ajax请求</li></ul><pre><code>let request = new XMLHttpRequest()request.responseType = &#39;json&#39;request.open(&quot;GET&quot;, url)request.send()</code></pre></li></ul></li></ul><ul><li><p>ES6中定义实现API(使用Promise基本步骤): </p><pre><code>// 1. 创建promise对象let promise = new Promise((resolve, reject) =&gt; {       // 初始化promise状态为pending  // 执行异步操作   if(异步操作成功) { // 调用成功的回调    resolve(result);     // 修改promise状态为fullfilled  } else { // 调用失败的回调    reject(errorMsg);   // 修改promise的状态为rejected  } }) // 2. 调用promise对象的then()promise.then(function(  result =&gt; console.log(result),   errorMsg =&gt; alert(errorMsg)))</code></pre><p>例子：</p><pre><code class="javascript">// 1、创建promise对象let promise = new Promise((resolve, reject) =&gt; {  // 初始化promise状态  pending： 初始化  console.log(&#39;11111111&#39;)  // 执行异步操作，通常是发送Ajax请求，开启定时器  setTimeout(() =&gt; {    console.log(&#39;3333333&#39;)    // 根据异步任务的返回结果去修改promise的状态    // 异步任务执行成功    // resolve(&#39;哈哈，&#39;) // 修改promise的状态为 fullfilled：成功    // 异步任务执行失败    reject(&#39;555, &#39;) // 修改promise的状态为 rejsected： 失败  }, 2000)})console.log(&#39;222222222&#39;)// 2. 调用promise对象的then()promise  .then((data) =&gt; { // 成功的回调    console.log(data, &#39;成功了~~~&#39;)  }, (error) =&gt; { // 失败的回调    console.log(error, &#39;失败了……&#39;)})</code></pre><p>实例：新闻、新闻的评论：只发新闻的内容；在接着根据新闻的id拿取这个新闻下的评论</p><p>1、打开ES5_6_7中code目录中的es_server</p><p>2、输入命令<code>node bin/www</code></p><p>3、浏览器中访问 <a href="http://localhost:3000/news" target="_blank" rel="noopener">http://localhost:3000/news</a> ，能够获取数据</p><pre><code class="javascript">// 定义获取新闻的功能函数function getNews(url){  let promise = new Promise((resolve, reject) =&gt; {    // 状态：初始化    // 执行异步任务    let xmlHttp = new XMLHttpRequest()    // 绑定监听readyState    /*xmlHttp.onreadystatechange = function(){      if(xmlHttp.readyState === 4 &amp;&amp; xmlHttp.status == 200){        // 请求成功        console.log(xmlHttp.responseText)        // 修改状态        resolve(xmlHttp.responseText) // 修改promise的状态为成功      }else{        // 请求失败        reject(&#39;暂时没有新闻内容&#39;)      }    } --&gt; 逻辑有问题*/    xmlHttp.onreadystatechange = function(){      if(xmlHttp.readyState === 4){        if(xmlHttp.status == 200){          // 请求成功          // console.log(xmlHttp.responseText)          // 修改状态          resolve(xmlHttp.responseText) // 修改promise的状态为成功        }else{          // 请求失败          reject(&#39;暂时没有新闻内容&#39;)        }      }    }    // open 设置请求得方式以及url    xmlHttp.open(&#39;GET&#39;, url)    // 发送    xmlHttp.send()  })  return promise}getNews(&#39;http://localhost:3000/news?id=2&#39;)  .then((data) =&gt; {    console.log(data)    // 发送请求获取评论内容准备url    let commentsUrl = JSON.parse(data).commentsUrl    let url = &#39;http://localhost:3000&#39; + commentsUrl    // 发送请求    return getNews(url)  },(error) =&gt; {    console.log(error)  })  .then((data) =&gt; {    console.log(data)  }, () =&gt; {})</code></pre></li></ul><h3><span id="8-symbol属性">8、Symbol属性</span></h3><ul><li>前言：ES5中对象的属性名都是字符串，容易造成重名，污染环境</li><li>概念：ES6中添加了一种<strong>原始数据类型symbol</strong>(已有的数据类型：String、Number、boolean、null、undefined、对象)</li><li>特点<ul><li>Symbol属性对应的值是<strong>唯一</strong>的，解决命名冲突问题</li><li>Symbol值<strong>不能</strong>与<strong>其他数据</strong>进行<strong>计算</strong>，包括与字符串拼串</li><li>for in、for of遍历时不会遍历symbol属性</li></ul></li><li>使用<ul><li>调用Symbol函数得到symbol值</li><li>传参标识</li><li>内置Symbol值<ul><li>除了定义自己使用的Symbol值以外，ES6还提供了11个内置的Symbol值（查看官方文档）</li><li>对象的Symbol.iterator属性，指向该对象的默认遍历器方法</li></ul></li></ul></li></ul><pre><code class="javascript">// 创建symbol属性值let symbol = Symbol()console.log(symbol)  // Symbol()let obj = {username:&#39;kobe&#39;, age:39}// 可以添加symbol属性——但是得用另一种方式obj.gender = &#39;男&#39;obj[symbol] = &#39;hello&#39;console.log(obj)  // {username: &quot;kobe&quot;, age: 39, gender: &quot;男&quot;, Symbol(): &quot;hello&quot;}//let symbol2 = Symbol()//let symbol3 = Symbol()// 并不相同，值是唯一的//console.log(symbol2, symbol3, symbol2 == symbol3)  // Symbol() Symbol() false// 可以传参，这样就能很明显看出不同了let symbol2 = Symbol(&#39;one&#39;)let symbol3 = Symbol(&#39;two&#39;)console.log(symbol2, symbol3, symbol2 == symbol3)  // Symbol(one) Symbol(two) false// 可以用来定义常量const Person_key = Symbol(&#39;person_key&#39;)console.log(Person_key)  // Symbol(person_key)// 等同于在指定的数据结构上部署了Iterator接口// 当使用for of去遍历某一个数据结构的时候，首先去找Symbol.itearator，找到了就去遍历，没有找到就不能遍历let targetData = {  [Symbol.iterator]: function(){    let nextIndex = 0    return{      next: function(){        return nextIndex &lt; this.length ? {value: this[nextIndex++], done: false} : {value: undefined, done: true}      }    }  }}// 使用三点运算符、解构赋值，默认会去调用Iterator接口let arr2 = [1,6]let arr3 = [2,3,4,5]arr2 = [1,...arr3,6]console.log(arr2)let [a,b] = arr2console.log(a,b)</code></pre><h3><span id="9-iterator遍历器">9、Iterator遍历器</span></h3><ul><li><p>概念：iterator是一种接口机制，为各种不同的数据结构提供统一的访问机制</p></li><li><p>作用：</p><ul><li>为各种数据结构，提供一个统一的、简便的访问接口</li><li>使得数据机构的成员能够按照某种次序排列</li><li>ES6创造了一种新的遍历命令for…of循环，Iterator接口主要供for…of消费</li></ul></li><li><p>工作原理</p><ul><li>创建一个指针对象(遍历器对象)，指向数据结构的起始位置</li><li>第一次调用next方法，指针自动指向数据结构的第一个成员</li><li>接下来不断调用next方法，指针会一直往后移动，直到指向最后一个成员</li><li>没调用next方法返回的是一个包含value和done的对象<code>{value: 当前成员的值, done: 布尔值}</code><ul><li>value表示当前成员的值，done对应的布尔值表示当前的数据的结构是否遍历结束</li><li>当遍历结束的时候返回的value值是undefined，done值为false</li></ul></li></ul></li><li><p>原生具备Iterator接口的数据(可用for…of遍历)</p></li><li><p>扩展理解</p><ul><li>当数据结构上部署了Symbol.iterator接口，该数据就是可以用for of遍历</li><li>当使用for of去遍历目标数据的时候，该数据会自动去找Symbol.iterator属性（Symbol.iterator属性指向对象的默认遍历器方法）<ul><li>Array</li><li>arguments</li><li>set容器</li><li>map容器</li><li>String</li><li>……</li></ul></li></ul><pre><code class="javascript">// 模拟指针对象(遍历器对象)function myIterator(arr){// Iterator接口let nextIndex = 0 // 记录指针的位置  return{    next: function(){// 遍历器对象      return nextIndex &lt; arr.length ? {value: arr[nextIndex++], done: false} : {value: undefined, done: true}    }  }}// 准备一个数据let arr =[1,4,65,&#39;abc&#39;]let iteratorObj = myIterator(arr)console.log(iteratorObj.next()) // {value: 1, done: false}console.log(iteratorObj.next()) // {value: 4, done: false}console.log(iteratorObj.next()) // {value: 65, done: false}console.log(iteratorObj.next()) // {value: &quot;abc&quot;, done: false}console.log(iteratorObj.next()) // {value: undefined, done: true}// 将iterator接口部署到指定的数据类型上，可以使用for of去循环遍历// 数组、字符串、argument、set容器、map容器for(let i of arr){  console.log(i)}// 1 4 65 abclet str = &#39;abcdefg&#39;for(let i of str){  console.log(i)}// a b c d e f gfunction fun(){  for(let i of arguments){    console.log(i)  }}fun(1,4,5,&#39;abc&#39;) // 1 4 5 abc// let obj = {username:&#39;kobe&#39;, age: 39}// for(let i of obj){//   console.log(i)// }// Uncaught TypeError: obj is not iterable 不可迭代</code></pre></li></ul><h3><span id="10-generator函数">10、Generator函数</span></h3><ul><li><p>概念</p><ul><li>ES6提供的解决异步编程的方案之一</li><li>Generator函数是一个状态机，内部封装了不同状态的数据</li><li>用来生成遍历器对象</li><li>可暂停函数(惰性求值)，yield可暂停，next方法可启动。每次返回的是yield后的表达式结果</li></ul></li><li><p>特点</p><ul><li><p>function 与函数名之间有一个星号</p></li><li><p>内部用yield表达式来定义不同的状态</p><p>例如：</p><pre><code class="javascript">function* generatorExample(){    let result = yield &#39;hello&#39;  // 状态值为hello    yield &#39;generator&#39;  // 状态值为generator}</code></pre></li><li><p>generator函数返回的是指针对象，而不会执行函数内部逻辑</p><pre><code class="javascript">function* generatorExample(){    console.log(&#39;开始执行&#39;)    let result = yield &#39;hello&#39;  // 状态值为hello    yield &#39;generator&#39;  // 状态值为generator}generatorExample() // 调用并不会执行函数内部逻辑</code></pre></li><li><p>调用next方法函数，内部逻辑开始执行，遇到yield表达式停止，返回<code>{value: yield后的表达式结果/return后的返回结果(如果没写，返回undefined),done: boolean值(后面还有返回false，没有返回true)}</code></p><pre><code class="javascript">function* generatorExample(){    console.log(&#39;开始执行&#39;)    let result = yield &#39;hello&#39;  // 状态值为hello，会执行，停止 测试yield console.log(&#39;会执行&#39;)    console.log(&#39;下次调用next执行&#39;)    yield &#39;generator&#39;  // 状态值为generator    console.log(&#39;下次调用next执行&#39;)    return &#39;返回的结果&#39;}let MG = generatorExample() // 返回的是指针对象console.log(MG.next()) // 执行，遇到yield停止console.log(MG.next(&#39;可以拿到这个值&#39;)) // 再次调用next，往下执行，可以传参console.log(MG.next()) // 再次调用next，往下执行，返回true</code></pre></li><li><p>再次调用next方法会从上一次停止时的yield处开始，直到最后</p></li><li><p>yield语句返回结果通常为undefined，当调用next方法时传参内容会作为启动时yield语句的返回值</p></li><li><p>补充</p><ul><li>对象的Symbol.iterator属性，指向遍历器对象</li></ul><pre><code class="javascript">let obj = {username:&#39;kobe&#39;, age: 39}obj[Symbol.iterator] = function* myTest(){    yield 1    yield 2    yield 3}for(let i of obj){  console.log(i)}</code></pre><ul><li>案例<ul><li>需求<ul><li>发送ajax请求获取新闻内容</li><li>新闻内容获取成功后再次发送请求，获取对应的新闻评论内容</li><li>新闻内容获取失败则不需要再次发送请求</li></ul></li><li>启动服务器——进入es_server目录，cmd输入命令<code>node bin/www</code></li></ul></li></ul><pre><code class="javascript">// 要比使用Promise更好function getNews(url){  $.get(url, function(data){ // 前面引入了jQuery    console.log(data)    let url = &#39;http://localhost:3000&#39; + data.commentsUrl    SX.next(url) // 放在这里也可以往下移，并且这里参数传输更方便  })}function* sendXml(){  let url = yield getNews(&#39;http://localhost:3000/news?id=3&#39;) // 如果这里出错，后面评论也不会再执行了  yield getNews(url)}// 获取遍历器对象let SX = sendXml()SX.next()</code></pre></li></ul></li></ul><p>​        </p><h2><span id="四-模块化">四、模块化</span></h2><h3><span id="1-理解">1、理解</span></h3><ul><li><p>什么是模块?</p><ul><li>将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起</li><li>块的内部数据/实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信</li></ul></li><li><p>一个模块的组成</p><ul><li>数据—&gt;内部的属性</li><li>操作数据的行为—&gt;内部的函数</li></ul></li><li><p>模块化</p><ul><li>编码时是按照模块一个一个编码的, 整个项目就是一个模块化的项目</li></ul></li><li><p>模块化的进化过程</p><ul><li><p>全局function模式 : </p><ul><li>编码: 全局变量/函数</li><li>问题: 污染全局命名空间, 容易引起命名冲突/数据不安全</li></ul></li></ul></li></ul><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/1591870404488.png" alt="全局变量"></p><ul><li><p>namespace模式 : </p><ul><li>编码: 将数据/行为封装到对象中</li><li>解决: 命名冲突(减少了全局变量)</li><li>问题: 数据不安全(外部可以直接修改模块内部的数据)</li></ul></li></ul><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/1591870416611.png" alt="Namespace"></p><ul><li><p>IIFE模式/增强</p><ul><li><p>IIFE : 立即调用函数表达式—&gt;匿名函数自调用</p></li><li><p>编码: 将数据和行为封装到一个函数内部, 通过给window添加属性来向外暴露接口</p></li></ul></li></ul><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/1591870442007.png" alt="IIFE"></p><ul><li><p>引入依赖: 通过函数形参来引入依赖模块</p><pre><code>(function(window, module2){  var data = &#39;atguigu.com&#39;  function foo() {     module2.xxx()     console.log(&#39;foo()&#39;+data)  }  function bar() {     console.log(&#39;bar()&#39;+data)  }  window.module = {foo}})(window, module2)</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/1591870456267.png" alt="IIFE增强"></p></li><li><p>模块化好处</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/1591870477012.png" alt="模块化好处"></p><ul><li>避免命名冲突(减少命名空间污染)</li><li>更好的分离, 按需加载</li><li>更高复用性</li><li>高可维护性</li></ul></li><li><p>页面引入加载script</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/1591871174402.png" alt="引入js"></p><ul><li>问题<ul><li>请求过多</li><li>依赖模糊</li><li>难以维护</li></ul></li></ul></li><li><p>模块化规范</p><ul><li><p><strong>CommonJS</strong></p><ul><li><p>Node.js : 服务器端</p></li><li><p>Browserify : 浏览器端    也称为js的打包工具</p></li><li><p>基本语法:</p><ul><li><p>定义暴露模块 : exports</p><pre><code>exports.xxx = valuemodule.exports = value</code></pre><p>引入模块 : require</p><pre><code>var module = require(&#39;模块名/模块相对路径&#39;)</code></pre></li></ul></li><li><p>每个文件都可以当作一个模块</p></li><li><p>引入模块发生在什么时候?</p><ul><li>Node : 运行时, 动态同步引入</li><li>Browserify : 在运行前对模块进行编译/转译/打包的处理(已经将依赖的模块包含进来了), <pre><code>运行的是打包生成的js, 运行时不存在需要再从远程引入依赖模块</code></pre></li></ul></li></ul></li><li><p>*<em>AMD *</em>: 浏览器端，模块的加载时异步的</p><ul><li><p>require.js</p></li><li><p>基本语法</p><ul><li><p>定义暴露模块: define([依赖模块名], function(){return 模块对象})</p></li><li><p>引入模块: require([‘模块1’, ‘模块2’, ‘模块3’], function(m1, m2){//使用模块对象})</p></li><li><p>配置: </p><pre><code>require.config({  //基本路径  baseUrl : &#39;js/&#39;,  //标识名称与路径的映射  paths : {    &#39;模块1&#39; : &#39;modules/模块1&#39;,    &#39;模块2&#39; : &#39;modules/模块2&#39;,    &#39;angular&#39; : &#39;libs/angular&#39;,    &#39;angular-messages&#39; : &#39;libs/angular-messages&#39;  },  //非AMD的模块  shim : {    &#39;angular&#39; : {        exports : &#39;angular&#39;    },    &#39;angular-messages&#39; : {        exports : &#39;angular-messages&#39;,        deps : [&#39;angular&#39;]    }  }})</code></pre></li></ul></li></ul></li><li><p>CMD(通用模块定义) : 浏览器端，模块加载是异步的；模块使用时才会加载执行</p><ul><li><p>sea.js</p></li><li><p>基本语法</p><ul><li><p>定义暴露模块: </p><pre><code>define(function(require, module, exports){  通过require引入依赖模块  通过module/exports来暴露模块  exports.xxx = value})</code></pre></li><li><p>使用模块seajs.use([‘模块1’, ‘模块2’])</p></li></ul></li></ul></li><li><p><strong>ES6</strong></p><ul><li><p>ES6内置了模块化的实现，依赖模块需要编译打包处理</p></li><li><p>基本语法</p><ul><li><p>定义暴露模块 : export</p><ul><li><p>暴露一个对象: </p><pre><code>export default 对象</code></pre></li><li><p>暴露多个: </p><pre><code>export var xxx = value1export let yyy = value2var xxx = value1let yyy = value2export {xxx, yyy}</code></pre></li></ul></li><li><p>引入使用模块 : import</p><ul><li><p>default模块:</p><pre><code>import xxx  from &#39;模块路径/模块名&#39;</code></pre></li><li><p>其它模块</p><pre><code>import {xxx, yyy} from &#39;模块路径/模块名&#39;import * as module1 from &#39;模块路径/模块名&#39;</code></pre></li></ul></li></ul></li><li><p>问题: 所有浏览器还不能直接识别ES6模块化的语法  </p></li><li><p>解决:</p><ul><li>使用Babel将ES6—&gt;ES5(使用了CommonJS) —-浏览器还不能直接支行</li><li>使用Browserify—&gt;打包处理—-浏览器可以运行</li></ul></li></ul></li></ul></li></ul><h3><span id="2-commonjs基于服务器端nodejs">2、CommonJS基于服务器端（Node.js）</span></h3><ul><li><p>下载安装Node.js</p></li><li><p>创建目录结构</p><ul><li><p>Modules</p><ul><li>module1.js</li><li>module2.js</li><li>module3.js</li></ul></li><li><p>app.js</p></li><li><p>package.json（命令创建,<code>npm init</code>,再输入包名）</p><pre><code class="json">{    &quot;name&quot;: &quot;conmmonjs_node&quot;,    &quot;version&quot;: &quot;1.0.0&quot;}</code></pre></li></ul></li><li><p>下载第三方模块<code>npm install uniq --save</code></p><ul><li><p>package.json</p><pre><code class="json">{    &quot;name&quot;: &quot;conmmonjs_node&quot;,    &quot;version&quot;: &quot;1.0.0&quot;,    &quot;dependencies&quot;:{        &quot;uniq&quot;: &quot;^1.0.1&quot;    }}</code></pre></li></ul></li><li><p>模块化编码</p><ul><li><p>module1.js</p><pre><code class="javascript">// module.exports = value 暴露一个对象module.exports = {    msg: &#39;module1&#39;,    foo(){        console.log(this.msg)    }}</code></pre></li><li><p>module2.js</p><pre><code class="javascript">// 暴露一个函数 module.exports = function(){}module.exports = fuction(){    console.log(&quot;module2&quot;)}</code></pre></li><li><p>module3.js</p><pre><code class="javascript">// exports.xxx = value 分别暴露exports.foo = function(){    console.log(&#39;foo() module3&#39;)}exports.bar = function(){    console.log(&#39;bar() module3&#39;)}exports.arr = [2, 4, 5, 2, 3, 5, 1]</code></pre></li><li><p>app.js</p><pre><code class="javascript">// 将其他的模块汇集到主模块let uniq = require(&#39;uniq&#39;)let module1 = require(&#39;./modules/module1&#39;)let module2 = require(&#39;./modules/module2&#39;)let module3 = require(&#39;./modules/module3&#39;)module1.foo()module2()module3.foo()module3.bar()let result = uniq(module3.arr)console.log(result)</code></pre></li></ul></li><li><p>通过node运行app.js——<code>node app.js</code></p></li></ul><h3><span id="3-commonjs基于浏览器端应用browserify">3、CommonJS基于浏览器端应用（Browserify）</span></h3><ul><li><p>创建项目结构</p><ul><li><p>js</p><ul><li>dist //打包生成文件的目录</li><li>src //源码所在的目录<ul><li>module1.js</li><li>module2.js</li><li>module3.js</li><li>app.js //应用主源文件</li></ul></li></ul></li><li><p>index.html</p></li><li><p>package.json</p><pre><code class="json">{    &quot;name&quot;: &quot;browserify-test&quot;,    &quot;version&quot;: &quot;1.0.0&quot;}</code></pre></li></ul></li><li><p>下载browserify</p><ul><li><p>全局：<code>npm install browserify -g</code></p></li><li><p>局部：<code>npm install browserify --save-dev</code></p><pre><code class="json">{    &quot;name&quot;: &quot;browserify-test&quot;,    &quot;version&quot;: &quot;1.0.0&quot;,    &quot;devDependencies&quot;:{        &quot;browserify&quot;: &quot;^14.5.0&quot;    }}</code></pre></li></ul></li><li><p>下载第三方模块<code>npm install uniq --save</code></p><ul><li><p>package.json</p><pre><code class="json">{    &quot;name&quot;: &quot;browserify-test&quot;,    &quot;version&quot;: &quot;1.0.0&quot;,    &quot;devDependencies&quot;:{        &quot;browserify&quot;: &quot;^14.5.0&quot;    },    &quot;dependencies&quot;:{        &quot;uniq&quot;: &quot;^1.0.1&quot;    }}</code></pre></li></ul></li><li><p>定义模块代码</p><ul><li><p>module1.js</p><pre><code class="javascript">module.exports = {  foo() {    console.log(&#39;moudle1 foo()&#39;)  }}</code></pre></li><li><p>module2.js</p><pre><code class="javascript">module.exports = function () {  console.log(&#39;module2()&#39;)}</code></pre></li><li><p>module3.js</p><pre><code class="javascript">exports.foo = function () {  console.log(&#39;module3 foo()&#39;)}exports.bar = function () {  console.log(&#39;module3 bar()&#39;)}</code></pre></li><li><p>app.js</p><pre><code class="javascript">//引用模块let module1 = require(&#39;./module1&#39;)let module2 = require(&#39;./module2&#39;)let module3 = require(&#39;./module3&#39;)let uniq = require(&#39;uniq&#39;)//使用模块module1.foo()module2()module3.foo()module3.bar()console.log(uniq([1, 3, 1, 4, 3]))</code></pre></li></ul></li><li><p>打包处理js：<code>browserify js/src/app.js -o js/dist/bundle.js</code></p></li><li><p>页面使用引入</p><ul><li><p>index.html</p><pre><code class="html">&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;  &lt;meta charset=&quot;UTF-8&quot;&gt;  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;  &lt;title&gt;broserify测试&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&lt;script src=&quot;js/dist/bundle.js&quot;&gt;&lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre></li></ul></li></ul><h3><span id="4-amd规范">4、AMD规范</span></h3><h4><span id="1noamd">（1）NoAMD</span></h4><ul><li><p>项目结构</p><ul><li>js<ul><li>alerter.js</li><li>dataService.js</li></ul></li><li>app.js</li><li>test1.html</li></ul></li><li><p>文件内容</p><ul><li><p>dataService.js</p><pre><code class="javascript">// 定义一个没有依赖的模块(function(win){ // 形参可以和实参的名字相同，这里方便区分    let name = &#39;dataservice.js&#39;    function getName(){        return name    }    win.dataService = {getName}})(window) // 实参</code></pre></li><li><p>alerter.js</p><pre><code class="javascript">// 定义一个有依赖的模块(function(window, dataService){    let msg = &#39;alerter.js&#39;    function showMsg(){        alert(msg + &#39;,&#39; + dataService.getName())    }    window.alerter = {showMsg}})(window, dataService) // 依赖dataService</code></pre></li><li><p>app.js</p><pre><code class="javascript">(function (alerter){    alerter.showMsg()})(alerter)</code></pre></li><li><p>test1.html</p><pre><code class="html">&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;  &lt;meta charset=&quot;UTF-8&quot;&gt;  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;  &lt;title&gt;Modular Demo: 未使用AMD(require.js)&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&lt;script src=&quot;./js/dataService.js&quot;&gt;&lt;/script&gt; &lt;script src=&quot;./js/alerter.js&quot;&gt;&lt;/script&gt; &lt;script src=&quot;./app.js&quot;&gt;&lt;/script&gt; // 下面的分别依赖上面的模块&lt;/body&gt;&lt;/html&gt;</code></pre></li></ul></li></ul><h4><span id="2amdrequirejs">（2）AMD（require.js）</span></h4><ol><li><p>下载require.js, 并引入</p><ul><li>官网: <a href="http://www.requirejs.cn/" target="_blank" rel="noopener">http://www.requirejs.cn/</a></li><li>github : <a href="https://github.com/requirejs/requirejs" target="_blank" rel="noopener">https://github.com/requirejs/requirejs</a></li><li>将require.js导入项目: js/libs/require.js </li></ul></li><li><p>创建项目结构</p></li></ol><ul><li>js<ul><li>libs<ul><li>require.js</li></ul></li><li>modules<ul><li>alerter.js</li><li>dataService.js</li></ul></li><li>main.js</li></ul></li><li>index.html</li></ul><ol start="3"><li><p>定义require.js的模块代码</p><ul><li><p>dataService.js</p><pre><code class="javascript">// 定义没有依赖的模块define(function () { let msg = &#39;dataService.js&#39; function getMsg() {   return msg.toUpperCase() } // 暴露模块 return {getMsg}})</code></pre></li><li><p>alerter.js</p><pre><code class="javascript">// 定义有依赖的模块define([&#39;dataService&#39;, &#39;jquery&#39;], function (dataService, $) { let msg = &#39;alerter.js&#39; function showMsg() {   $(&#39;body&#39;).css(&#39;background&#39;, &#39;gray&#39;)   alert(dataService.getMsg() + &#39;, &#39; + msg) } return {showMsg}})</code></pre></li></ul></li><li><p>应用主(入口)js：main.js</p><pre><code class="javascript">(function () {    //配置    require.config({        //基本路径 出发点在根目录下        baseUrl: &quot;js/&quot;,        //模块标识名与模块路径映射        paths: {            &quot;alerter&quot;: &quot;modules/alerter&quot;,            &quot;dataService&quot;: &quot;modules/dataService&quot;,        }    })    //引入使用模块    require( [&#39;alerter&#39;], function(alerter) {        alerter.showMsg()    })})()</code></pre></li><li><p>页面使用模块：index.html</p></li></ol><pre><code class="html">&lt;script data-main=&quot;js/main&quot; src=&quot;js/libs/require.js&quot;&gt;&lt;/script&gt;</code></pre><hr><ol start="6"><li><p>使用第三方基于require.js的框架(jquery)</p><ul><li><p>将jquery的库文件导入到项目: </p><ul><li>js/libs/jquery-1.10.1.js</li></ul></li><li><p>在main.js中配置jquery路径</p><pre><code class="javascript">paths: {   &#39;jquery&#39;: &#39;libs/jquery-1.10.1&#39;  // jQuery定义的返回AMD模块名为小写}</code></pre></li><li><p>在alerter.js中使用jquery</p><pre><code class="javascript">define([&#39;dataService&#39;, &#39;jquery&#39;], function (dataService, $) {   var name = &#39;xfzhang&#39;   function showMsg() {       $(&#39;body&#39;).css({background : &#39;red&#39;})       alert(name + &#39; &#39;+dataService.getMsg())   }   return {showMsg}})</code></pre></li></ul></li></ol><hr><ol start="7"><li><p>使用第三方不基于require.js的框架(angular/angular-messages)</p><ul><li><p>将angular.js和angular-messages.js导入项目</p><ul><li>js/libs/angular.js</li><li>js/libs/angular-messages.js</li></ul></li><li><p>在main.js中配置</p><pre><code class="javascript">(function () { require.config({   //基本路径   baseUrl: &quot;js/&quot;,   //模块标识名与模块路径映射   paths: {     //第三方库     &#39;jquery&#39; : &#39;libs/jquery-1.10.1&#39;,     &#39;angular&#39; : &#39;libs/angular&#39;,     &#39;angular-messages&#39; : &#39;libs/angular-messages&#39;,     //自定义模块     &quot;alerter&quot;: &quot;modules/alerter&quot;,     &quot;dataService&quot;: &quot;modules/dataService&quot;   },   /*    配置不兼容AMD的模块    exports : 指定导出的模块名    deps  : 指定所有依赖的模块的数组    */   shim: {     &#39;angular&#39; : {       exports : &#39;angular&#39;     },     &#39;angular-messages&#39; : {       exports : &#39;angular-messages&#39;,       deps : [&#39;angular&#39;]     }   } }) //引入使用模块 require( [&#39;alerter&#39;, &#39;angular&#39;, &#39;angular-messages&#39;], function(alerter, angular) {   alerter.showMsg()   angular.module(&#39;myApp&#39;, [&#39;ngMessages&#39;])   angular.bootstrap(document,[&quot;myApp&quot;]) })})()</code></pre></li><li><p>页面:</p><pre><code class="html">&lt;form name=&quot;myForm&quot;&gt; 用户名: &lt;input type=&quot;text&quot; name=&quot;username&quot; ng-model=&quot;username&quot; ng-required=&quot;true&quot;&gt; &lt;div style=&quot;color: red;&quot; ng-show=&quot;myForm.username.$dirty&amp;&amp;myForm.username.$invalid&quot;&gt;用户名是必须的&lt;/div&gt;&lt;/form&gt;</code></pre></li></ul></li></ol><h3><span id="5-cmd规范"><font color="lightgray">5、CMD规范</font></span></h3><ol><li><p>下载sea.js, 并引入</p><ul><li>官网: <a href="http://seajs.org/" target="_blank" rel="noopener">http://seajs.org/</a></li><li>github : <a href="https://github.com/seajs/seajs" target="_blank" rel="noopener">https://github.com/seajs/seajs</a></li><li>将sea.js导入项目: js/libs/sea.js </li></ul></li><li><p>创建项目结构</p><pre><code>|-js |-libs   |-sea.js |-modules   |-module1.js   |-module2.js   |-module3.js   |-module4.js   |-main.js|-index.html</code></pre></li><li><p>定义sea.js的模块代码</p><ul><li><p>module1.js</p><pre><code class="javascript">// 没有依赖的模块define(function (require, exports, module) { //内部变量数据 var data = &#39;atguigu.com&#39; //内部函数 function show() {   console.log(&#39;module1 show() &#39; + data) } //向外暴露 exports.show = show})</code></pre></li><li><p>module2.js</p><pre><code class="javascript">define(function (require, exports, module) { let msg = &#39;module2&#39; function bar(){     console.log(msg) }   module.exports = bar /* module.exports = {   msg: &#39;I Will Back&#39; } */})</code></pre></li><li><p>module3.js</p><pre><code class="javascript">define(function(require, exports, module){   let data = &#39;module3&#39;   function fun(){       console.log(data)   }   exports.module3 = {fun}})/* define(function (require, exports, module) { const API_KEY = &#39;abc123&#39; exports.API_KEY = API_KEY}) */</code></pre></li><li><p>module4.js</p><pre><code class="javascript">define(function(require, exports, module){   let msg = &#39;module4&#39;   // 同步引入   let module2 = require(&#39;./module2&#39;)   module2()   // 异步引入   require.async(&#39;./module3&#39;, function(module3){       module3.module3.fun()   })   function fun2(){       console.log(msg)   }   exports.fun2 = fun2})/*define(function (require, exports, module) { //引入依赖模块(同步) var module2 = require(&#39;./module2&#39;) function show() {   console.log(&#39;module4 show() &#39; + module2.msg) } exports.show = show //引入依赖模块(异步) require.async(&#39;./module3&#39;, function (m3) {   console.log(&#39;异步引入依赖模块3  &#39; + m3.API_KEY) })})*/</code></pre></li><li><p>main.js : 主(入口)模块</p><pre><code class="javascript">define(function (require) { let module1 = require(&#39;./module1&#39;) console.log(module1.foo()) let module4 = require(&#39;./module4&#39;) module4.fun2()})/*define(function (require) { var m1 = require(&#39;./module1&#39;) var m4 = require(&#39;./module4&#39;) m1.show() m4.show()})*/</code></pre></li></ul></li><li><p>index.html:</p><pre><code>&lt;!--使用seajs: 1. 引入sea.js库 2. 如何定义导出模块 :   define()   exports   module.exports 3. 如何依赖模块:   require() 4. 如何使用模块:   seajs.use()--&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;js/libs/sea.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt; seajs.use(&#39;./js/modules/main&#39;)&lt;/script&gt;</code></pre></li></ol><p>​       </p><h3><span id="6-es6-babel-browserify使用教程"><font color="red">6、ES6-Babel-Browserify使用教程</font></span></h3><ol><li><p>定义package.json文件</p><pre><code class="json">{ &quot;name&quot; : &quot;es6-babel-browserify&quot;, &quot;version&quot; : &quot;1.0.0&quot;}</code></pre></li><li><p>安装babel-cli, babel-preset-es2015和browserify // cli——command line interface</p><ul><li><code>npm install babel-cli browserify -g</code></li></ul></li></ol><ul><li><code>npm install babel-preset-es2015 --save-dev</code></li><li>preset 预设（将es6转换成es5的所有插件打包）</li></ul><ol start="3"><li><p>定义.babelrc文件（babel run control）</p><pre><code class="bash">{ &quot;presets&quot;: [&quot;es2015&quot;]}</code></pre></li><li><p>编码</p><ul><li><p>js/src/module1.js</p><pre><code class="JavaScript">// 暴露模块 分别暴露export function foo() { console.log(&#39;module1 foo()&#39;);}export let bar = function () { console.log(&#39;module1 bar()&#39;);}export const DATA_ARR = [1, 3, 5, 1]</code></pre></li><li><p>js/src/module2.js</p><pre><code class="JavaScript">// 统一暴露——常规暴露let data = &#39;module2 data&#39;function fun1() { console.log(&#39;module2 fun1() &#39; + data);}function fun2() { console.log(&#39;module2 fun2() &#39; + data);}export {fun1, fun2}</code></pre></li><li><p>js/src/module3.js</p><pre><code class="JavaScript">// 默认暴露：可以暴露任意数据类型，暴露什么数据类型接收到的就是什么数据// export default valueexport default() =&gt;{   console.log(&#39;我是默认暴露的箭头函数&#39;)} // 只能暴露一个default，因此暴露对象/* export default { name: &#39;Tom&#39;, setName: function (name) {   this.name = name }} */</code></pre></li><li><p>js/src/app.js</p><pre><code class="JavaScript">// 引入其他的模块// 语法： import xxx from &#39;路径&#39;import {foo, bar} from &#39;./module1&#39;import {DATA_ARR} from &#39;./module1&#39;import {fun1, fun2} from &#39;./module2&#39;import module3 from &#39;./module3&#39;/* import person from &#39;./module3&#39; */// 安装 npm install jquery@1import $ from &#39;jquery&#39;$(&#39;body&#39;).css(&#39;background&#39;, &#39;red&#39;)foo()bar()console.log(DATA_ARR);fun1()fun2()module3()/* person.setName(&#39;JACK&#39;)console.log(person.name); */</code></pre></li></ul></li><li><p>编译</p><ul><li>使用Babel将ES6编译为ES5代码(但包含CommonJS语法) : <code>babel js/src -d js/lib</code></li><li>使用Browserify编译js : <code>browserify js/lib/app.js -o js/lib/bundle.js</code></li></ul></li><li><p>页面中引入测试</p><pre><code class="html">&lt;script type=&quot;text/javascript&quot; src=&quot;js/lib/bundle.js&quot;&gt;&lt;/script&gt;</code></pre></li><li><p>引入第三方模块(jQuery)<br>1). 下载jQuery模块: </p><ul><li><code>npm install jquery@1 --save</code> // @后的是版本，1代表<code>1.x.x</code>中的最新版本</li></ul><p>2). 在app.js中引入并使用</p><pre><code>import $ from &#39;jquery&#39;$(&#39;body&#39;).css(&#39;background&#39;, &#39;red&#39;)</code></pre></li></ol><h2><span id="五-es7">五、ES7</span></h2><h3><span id="1-async函数源自es2017">1、async函数(源自ES2017)</span></h3><ul><li><p>概念：真正意义上去解决异步回调的问题，同步流程表达异步操作</p></li><li><p>本质：Generator的语法</p></li><li><p>语法：</p><pre><code class="javascript">async function foo(){    await 异步操作;    await 异步操作;}</code></pre></li><li><p>特点：</p><ul><li>不需要像Generator去调用next方法，遇到await等待，当前的异步操作完成就往下执行</li><li>返回的总是Promise对象，可以用then方法进行下一步操作</li><li>async取代Generator函数的<code>*</code>，await取代Generator的yield</li><li>语义上更为明确，使用简单，暂时没有任何副作用</li></ul><pre><code class="javascript">// async基本使用async function foo(){  return new Promise(resolve =&gt; {    // setTimeout(function(){    //   resolve()    // }, 2000)    // 可以写成下方这种    setTimeout(resolve, 2000)  })}async function test(){  console.log(&#39;开始执行&#39;, new Date().toTimeString())  await foo()  console.log(&#39;执行完毕&#39;, new Date().toTimeString())}test()</code></pre></li></ul><p>  // async里await返回值<br>  function test2(){<br>    return ‘xxx’<br>  }<br>  async function asyncPrint(){<br>    /* let result = await test2()<br>    console.log(result) // 普通函数没有返回值<br>    <em>/<br>    /</em><br>    let result = await Promise.resolve()<br>    console.log(result) // Promise对象成功状态返回undefined<br>    */<br>    let result = await Promise.resolve(‘promise’)<br>    console.log(result) // Promise对象成功状态传参返回参数 promise<br>    result = await Promise.reject(‘失败了……’)<br>    console.log(result) // 失败状态，返回出错，且能将参数返回 Uncaught (in promise) 失败了……</p><p>  }<br>  asyncPrint()</p><pre><code>  仍是获取新闻内容案例  ```javascript  // async比generator又更简单  async function getNews(url){    return new Promise((resolve, reject) =&gt; {      $.ajax({ // 前面已经引入jQuery        method: &#39;GET&#39;,        url,  // 这是ES6中简写        /* success: function(data){          resolve()        },        error: function(error){          reject()         }*/        // 简写        success: data =&gt; resolve(data),        error: error =&gt; reject(error)      })    })  }  async function sendXml(){    let result = await getNews(&#39;http://localhost:3000/news?id=7&#39;)    console.log(result) // {id: &quot;7&quot;, title: &quot;news title1...&quot;, content: &quot;news content1...&quot;, commentsUrl: &quot;/comments?newsId=7&quot;}    result = await getNews(&#39;http://localhost:3000&#39; + result.commentsUrl)    console.log(result)  }  sendXml()</code></pre><p>  改进一下，由于这种写法error并不会显示错误信息</p><pre><code class="html">  &lt;script src=&quot;./jquery-3.1.0.min.js&quot;&gt;&lt;/script&gt;  &lt;script&gt;    // async比generator又更简单    async function getNews(url){      return new Promise((resolve, reject) =&gt; {        $.ajax({          method: &#39;GET&#39;,          url,  // 这是ES6中简写          /* success: function(data){            resolve()          },          error: function(error){            reject()           }*/          // 简写          success: data =&gt; resolve(data),          // error: error =&gt; reject(error)          error: error =&gt; resolve(false) // 不用reject，而是返回false        })      })    }    async function sendXml(){      let result = await getNews(&#39;http://localhost:30010/news?id=7&#39;)      console.log(result) // {id: &quot;7&quot;, title: &quot;news title1...&quot;, content: &quot;news content1...&quot;, commentsUrl: &quot;/comments?newsId=7&quot;}      if(!result){ // 出错就弹窗        alert(&#39;暂时没有新闻……&#39;)      }      result = await getNews(&#39;http://localhost:3000&#39; + result.commentsUrl)      console.log(result)    }    sendXml()  &lt;/script&gt;</code></pre><h3><span id="2-指数运算符幂">2、指数运算符(幂): <code>**</code></span></h3><p><code>console.log(3 ** 3)</code></p><h3><span id="3-arrayprototypeincludesvalue-判断数组中是否包含指定value">3、<code>Array.prototype.includes(value)</code>: 判断数组中是否包含指定value</span></h3><pre><code class="javascript">let arr = [1,2,3,&#39;abc&#39;]console.log(arr.includes(&#39;a&#39;))</code></pre><ul><li><strong>区别方法的2种称谓</strong><ul><li>静态(工具)方法<ul><li>Fun.xxx = function(){}</li></ul></li><li>实例方法<ul><li>所有实例对象 : Fun.prototype.xxx = function(){} //xxx针对Fun的所有实例对象</li><li>某个实例对象 : fun.xxx = function(){} //xxx只是针对fun对象</li></ul></li></ul></li></ul><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="笔记" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="前端" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="前端" scheme="https://wallleap.cn/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="ES" scheme="https://wallleap.cn/tags/ES/"/>
    
      <category term="模块化" scheme="https://wallleap.cn/tags/%E6%A8%A1%E5%9D%97%E5%8C%96/"/>
    
  </entry>
  
  <entry>
    <title>jQuery学习笔记</title>
    <link href="https://wallleap.cn/2020/04/26/jQuery/"/>
    <id>https://wallleap.cn/2020/04/26/jQuery/</id>
    <published>2020-04-26T12:33:14.000Z</published>
    <updated>2025-01-24T09:22:15.352Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><ul><li>初识jQuery</li><li>jQuery的两把利器</li><li>使用jQuery核心函数</li><li>使用jQuery对象</li><li>jQuery对象</li><li>练习</li></ul><h2><span id="一-初识jquery">一、初识jQuery</span></h2><h3><span id="1-what">1、what</span></h3><ul><li><a href="http://jquery.com/" target="_blank" rel="noopener">http://jquery.com/</a></li><li>一个优秀的JS函数库</li><li>使用了jQuery的网站超过90%中大型WEB项目开发首选</li><li>Write Less, Do More!!!</li></ul><h3><span id="2-why">2、why</span></h3><ul><li>HTML元素选取(选择器)</li><li>HTML元素操作</li><li>CSS操作</li><li>HTML事件处理</li><li>JS动画效果</li><li>链式调用</li><li>读写合一</li><li>浏览器兼容</li><li>易扩展插件</li><li>ajax封装<br>……</li></ul><blockquote><p>封装简化DOM操作(CRUD) / Ajax</p><p>强大选择器：方便快速查找DOM元素</p><p>隐式遍历(迭代)：一次操作多个元素</p><p>读写合一：读数据/写数据用的是一个函数</p><p>事件处理、链式调用、DOM操作(CUD)、样式操作……</p></blockquote><h3><span id="3-how">3、how</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815145128.png" alt="image-20200815145126124"></p><p>(1) 引入库：本地引入、CDN远程引入，测试版、生产版(压缩版)</p><p>(2) 使用库：函数(<code>$</code>/<code>jQuery</code>)、对象(<code>$xxx</code>)</p><pre><code class="html">&lt;!--需求: 点击&quot;确定&quot;按钮, 提示输入的值--&gt;用户名: &lt;input type=&quot;text&quot; id=&quot;username&quot;&gt;&lt;button id=&quot;btn1&quot;&gt;确定(原生版)&lt;/button&gt;&lt;button id=&quot;btn2&quot;&gt;确定(jQuery版)&lt;/button&gt;&lt;!--使用原生DOM--&gt;&lt;script type=&quot;text/javascript&quot;&gt;  window.onload = function () {    var btn1 = document.getElementById(&#39;btn1&#39;)    btn1.onclick = function () {      var username = document.getElementById(&#39;username&#39;).value      alert(username)    }  }&lt;/script&gt;&lt;!--使用jQuery实现--&gt;  &lt;!--本地引入--&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;js/jquery-1.12.3.js&quot;&gt;&lt;/script&gt;  &lt;!--远程引入--&gt;&lt;!--&lt;script src=&quot;https://cdn.bootcss.com/jquery/1.12.4/jquery.js&quot;&gt;&lt;/script&gt;--&gt;&lt;script type=&quot;text/javascript&quot;&gt;  //绑定文档加载完成的监听  jQuery(function () {    var $btn2 = $(&#39;#btn2&#39;)    $btn2.click(function () { // 给btn2绑定点击监听      var username = $(&#39;#username&#39;).val()      alert(username)    })  })  /*  1. 使用jQuery核心函数: $/jQuery  2. 使用jQuery核心对象: 执行$()返回的对象    */  //新的注释&lt;/script&gt;</code></pre><h2><span id="二-jquery的2把利器">二、jQuery的2把利器</span></h2><ul><li>jQuery函数: <code>$</code>/<code>jQuery</code><ul><li>jQuery向外暴露的就是jQuery函数, 可以直接使用</li><li>当成一般函数使用: <code>$(param)</code><ul><li>param是function: 相当于<code>window.onload = function</code>(文档加载完成的监听)</li><li>param是选择器字符串: 查找所有匹配的DOM元素, 返回包含所有DOM元素的jQuery对象</li><li>param是DOM元素: 将DOM元素对象包装为jQuery对象返回  <code>$(this)</code></li><li>param是标签字符串: 创建标签DOM元素对象并包装为jQuery对象返回</li></ul></li><li>当成对象使用: <code>$.xxx</code><ul><li><code>each(obj/arr, function(key, value){})</code></li><li><code>trim(str)</code></li></ul></li></ul></li><li>jQuery对象<ul><li>包含所有匹配的n个DOM元素的伪数组对象</li><li>执行<code>$()</code>返回的就是jQuery对象</li><li>基本行为:<ul><li><code>length</code>/<code>size()</code>: 得到dom元素的个数</li><li><code>[index]</code>: 得到指定下标对应的dom元素</li><li><code>each(function(index, domEle){})</code>: 遍历所有dom元素</li><li><code>index()</code>: 得到当前dom元素在所有兄弟中的下标</li></ul></li></ul></li></ul><h3><span id="1-jquery核心函数">1、jQuery核心函数</span></h3><ul><li><p>简称: jQuery函数(<code>$</code>/<code>jQuery</code>)</p></li><li><p>jQuery库向外直接暴露的就是<code>$</code>/<code>jQuery</code></p></li><li><p>引入jQuery库后, 直接使用<code>$</code>即可</p><ul><li>当函数用: <code>$(xxx)</code></li><li>当对象用: <code>$.xxx()</code></li></ul></li></ul><pre><code class="html">&lt;script type=&quot;text/javascript&quot; src=&quot;js/jquery-1.12.3.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  //1.  jQuery函数: 直接可用  console.log($, typeof $) // f(){}  console.log(jQuery===$) // true  /*  (function (window) {    var jQuery = function () {      return new xxx()    }    window.$ = window.jQuery = jQuery  })(window)  */&lt;/script&gt;</code></pre><p>理解</p><ul><li>即<code>$</code> 或<code>jQuery</code></li><li>jQuery定义了这个全局的函数供我们调用</li><li>它既可作为一般函数调用, 且传递的参数类型不同/格式不同功能就完全不同</li><li>也可作为对象调用其定义好的方法, 此时<code>$</code>就是一个工具对象</li></ul><p>作为函数调用</p><ul><li>参数为函数 <code>$(fun)</code></li><li>参数为选择器(selector)字符串 <code>$(&quot;#div1&quot;)</code></li><li>参数为DOM对象 <code>$(div1Ele)</code></li><li>参数为html标签字符串<code>$(&quot;&lt;div&gt;&quot;)</code></li></ul><p>作为对象使用</p><ul><li>发送ajax请求的方法<br><code>$.ajax()</code><br><code>$.get()</code><br><code>$.post()</code><br>……</li><li>其它工具方法<br><code>$.each()</code><br><code>$.trim()</code><br><code>$.parseJSON()</code><br>……</li></ul><pre><code class="html">&lt;div&gt;  &lt;button id=&quot;btn&quot;&gt;测试&lt;/button&gt;  &lt;br/&gt;  &lt;input type=&quot;text&quot; name=&quot;msg1&quot;/&gt;&lt;br/&gt;  &lt;input type=&quot;text&quot; name=&quot;msg2&quot;/&gt;&lt;br/&gt;&lt;/div&gt;&lt;!--1. 作为一般函数调用: $(param)    1). 参数为函数 : 当DOM加载完成后，执行此回调函数    2). 参数为选择器字符串: 查找所有匹配的标签, 并将它们封装成jQuery对象    3). 参数为DOM对象: 将dom对象封装成jQuery对象    4). 参数为html标签字符串 (用得少): 创建标签对象并封装成jQuery对象2. 作为对象使用: $.xxx()    1). $.each() : 隐式遍历数组    2). $.trim() : 去除两端的空格--&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  /*   需求1. 点击按钮: 显示按钮的文本, 显示一个新的输入框   需求2. 遍历输出数组中所有元素值   需求3. 去掉&quot;  my atguigu  &quot;两端的空格   */  /*需求1. 点击按钮: 显示按钮的文本, 显示一个新的输入框*/  //1.1). 参数为函数 : 当DOM加载完成后，执行此回调函数  $(function () { // 绑定文档加载完成的监听    // 1.2). 参数为选择器字符串: 查找所有匹配的标签, 并将它们封装成jQuery对象    $(&#39;#btn&#39;).click(function () { // 绑定点击事件监听      // this是什么? 发生事件的dom元素(&lt;button&gt;)      // alert(this.innerHTML)      // 1.3). 参数为DOM对象: 将dom对象封装成jQuery对象      alert($(this).html())      // 1.4). 参数为html标签字符串 (用得少): 创建标签对象并封装成jQuery对象      $(&#39;&lt;input type=&quot;text&quot; name=&quot;msg3&quot;/&gt;&lt;br/&gt;&#39;).appendTo(&#39;div&#39;)    })  })  /*需求2. 遍历输出数组中所有元素值*/  var arr = [2, 4, 7]  // 1). $.each() : 隐式遍历数组  $.each(arr, function (index, item) {    console.log(index, item)  })  // 2). $.trim() : 去除两端的空格  var str = &#39; my atguigu  &#39;  // console.log(&#39;---&#39;+str.trim()+&#39;---&#39;)  console.log(&#39;---&#39;+$.trim(str)+&#39;---&#39;)&lt;/script&gt;</code></pre><h3><span id="2-jquery核心对象">2、jQuery核心对象</span></h3><ul><li><p>简称: jQuery对象</p></li><li><p>得到jQuery对象: 执行jQuery函数返回的就是jQuery对象</p></li><li><p>使用jQuery对象: <code>$obj.xxx()</code></p></li></ul><pre><code class="html">&lt;script type=&quot;text/javascript&quot; src=&quot;js/jquery-1.12.3.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  //2. jQuery对象: 执行jQuery函数得到它  console.log($() instanceof Object) // true&lt;/script&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815155511.png" alt="image-20200815155509580"></p><p>理解</p><ul><li>即执行jQuery核心函数返回的对象</li><li>jQuery对象内部包含的是dom元素对象的伪数组(可能只有一个元素)</li><li>jQuery对象拥有很多有用的属性和方法, 让程序员能方便的操作dom</li></ul><p>属性/方法</p><ul><li><p>基本行为<br><code>size()</code>/<code>length</code><br><code>[index]</code>/<code>get(index)</code><br><code>each()</code><br><code>index()</code></p></li><li><p>属性</p><pre><code>    操作内部标签的属性或值</code></pre></li><li><p>CSS</p><pre><code>    操作标签的样式</code></pre></li><li><p>文档</p><pre><code>    对标签进行增删改操作</code></pre></li><li><p>筛选</p><pre><code>    根据指定的规则过滤内部的标签</code></pre></li><li><p>事件</p><pre><code>    处理事件监听相关</code></pre></li><li><p>效果</p><pre><code>    实现一些动画效果</code></pre></li></ul><pre><code class="html">&lt;button&gt;测试一&lt;/button&gt;&lt;button&gt;测试二&lt;/button&gt;&lt;button id=&quot;btn3&quot;&gt;测试三&lt;/button&gt;&lt;button&gt;测试四&lt;/button&gt;&lt;!--1. jQuery对象是一个包含所有匹配的任意多个dom元素的伪数组对象2. 基本行为  * size()/length: 包含的DOM元素个数  * [index]/get(index): 得到对应位置的DOM元素  * each(): 遍历包含的所有DOM元素  * index(): 得到在所在兄弟元素中的下标--&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  /*   需求:   需求1. 统计一共有多少个按钮   需求2. 取出第2个button的文本   需求3. 输出所有button标签的文本   需求4. 输出&#39;测试三&#39;按钮是所有按钮中的第几个   */  //需求1. 统计一共有多少个按钮  var $buttons = $(&#39;button&#39;)  /*size()/length: 包含的DOM元素个数*/  console.log($buttons.size(), $buttons.length)  //需求2. 取出第2个button的文本  /*[index]/get(index): 得到对应位置的DOM元素*/  console.log($buttons[1].innerHTML, $buttons.get(1).innerHTML)  //需求3. 输出所有button标签的文本  /*each(): 遍历包含的所有DOM元素*/  /*$buttons.each(function (index, domEle) {    console.log(index, domEle.innerHTML, this)  })*/  $buttons.each(function () {    console.log(this.innerHTML)  })  //需求4. 输出&#39;测试三&#39;按钮是所有按钮中的第几个  /*index(): 得到在所在兄弟元素中的下标*/  console.log($(&#39;#btn3&#39;).index())  //2  /*  1. 伪数组    * Object对象    * length属性    * 数值下标属性    * 没有数组特别的方法: forEach(), push(), pop(), splice()   */  console.log($buttons instanceof Array) // false  // 自定义一个伪数组  var weiArr = {}  weiArr.length = 0  weiArr[0] = &#39;atguigu&#39;  weiArr.length = 1  weiArr[1] = 123  weiArr.length = 2  for (var i = 0; i &lt; weiArr.length; i++) {    var obj = weiArr[i]    console.log(i, obj)  }  console.log(weiArr.forEach, $buttons.forEach) //undefined, undefined&lt;/script&gt;</code></pre><h2><span id="三-使用jquery核心函数">三、使用jQuery核心函数</span></h2><h3><span id="1-选择器">1、选择器</span></h3><p>(1) 说明</p><ul><li>选择器本身只是一个有特定语法规则的字符串, 没有实质用处</li><li>它的基本语法规则使用的就是CSS的选择器语法, 并对基进行了扩展</li><li>只有调用<code>$()</code>, 并将选择器作为参数传入才能起作用</li><li><code>$(selector)</code>作用 : 根据选择器规则在整个文档中查找所有匹配的标签的数组, 并封装成jQuery对象返回(用来查找特定页面元素)</li></ul><p>(2) 分类</p><ul><li><p>基本选择器(最基本最常用的选择器)</p><ul><li><code>#id</code>：id选择器</li><li><code>element</code>：元素选择器</li><li><code>.class</code>：属性选择器</li><li><code>*</code>：任意标签</li><li><code>selector1,selector2,selectorN</code>：取多个选择器的并集(组合选择器)</li><li><code>selector1selector2selectorN</code>：取多个选择器的交集(相交选择器)</li></ul><pre><code class="html">&lt;div id=&quot;div1&quot; class=&quot;box&quot;&gt;div1(class=&quot;box&quot;)&lt;/div&gt;&lt;div id=&quot;div2&quot; class=&quot;box&quot;&gt;div2(class=&quot;box&quot;)&lt;/div&gt;&lt;div id=&quot;div3&quot;&gt;div3&lt;/div&gt;&lt;span class=&quot;box&quot;&gt;span(class=&quot;box&quot;)&lt;/span&gt;&lt;br&gt;&lt;ul&gt;  &lt;li&gt;AAAAA&lt;/li&gt;  &lt;li title=&quot;hello&quot;&gt;BBBBB(title=&quot;hello&quot;)&lt;/li&gt;  &lt;li class=&quot;box&quot;&gt;CCCCC(class=&quot;box&quot;)&lt;/li&gt;  &lt;li title=&quot;hello&quot;&gt;DDDDDD(title=&quot;hello&quot;)&lt;/li&gt;&lt;/ul&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  /*   需求:   1. 选择id为div1的元素   2. 选择所有的div元素   3. 选择所有class属性为box的元素   4. 选择所有的div和span元素   5. 选择所有class属性为box的div元素   */  //1. 选择id为div1的元素  // $(&#39;#div1&#39;).css(&#39;background&#39;, &#39;red&#39;)  //2. 选择所有的div元素  // $(&#39;div&#39;).css(&#39;background&#39;, &#39;red&#39;)  //3. 选择所有class属性为box的元素  //$(&#39;.box&#39;).css(&#39;background&#39;, &#39;red&#39;)  //4. 选择所有的div和span元素  // $(&#39;div,span&#39;).css(&#39;background&#39;, &#39;red&#39;)  //5. 选择所有class属性为box的div元素  //$(&#39;div.box&#39;).css(&#39;background&#39;, &#39;red&#39;)  //$(&#39;*&#39;).css(&#39;background&#39;, &#39;red&#39;)&lt;/script&gt;</code></pre></li></ul><ul><li><p>层次选择器(查找子元素, 后代元素, 兄弟元素的选择器)</p><ul><li><code>ancestor descendant</code>：在给定的祖先元素下的后代元素中匹配元素</li><li><code>parent &gt; child</code>：在给定的父元素下的子元素中匹配元素</li><li><code>prev + next</code>：匹配所有紧接在prev元素后的next元素</li><li><code>prev ~ siblings</code>：匹配prev元素之后的所有siblings元素</li></ul><pre><code class="html">&lt;ul&gt;  &lt;li&gt;AAAAA&lt;/li&gt;  &lt;li class=&quot;box&quot;&gt;CCCCC&lt;/li&gt;  &lt;li title=&quot;hello&quot;&gt;&lt;span&gt;BBBBB&lt;/span&gt;&lt;/li&gt;  &lt;li title=&quot;hello&quot;&gt;&lt;span class=&quot;box&quot;&gt;DDDD&lt;/span&gt;&lt;/li&gt;  &lt;span&gt;EEEEE&lt;/span&gt;&lt;/ul&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  /*   需求:   1. 选中ul下所有的的span   2. 选中ul下所有的子元素span   3. 选中class为box的下一个li   4. 选中ul下的class为box的元素后面的所有兄弟元素   */  //1. 选中ul下所有的的span  // $(&#39;ul span&#39;).css(&#39;background&#39;, &#39;yellow&#39;)  //2. 选中ul下所有的子元素span  // $(&#39;ul&gt;span&#39;).css(&#39;background&#39;, &#39;yellow&#39;)  //3. 选中class为box的下一个li  // $(&#39;.box+li&#39;).css(&#39;background&#39;, &#39;yellow&#39;)  //4. 选中ul下的class为box的元素后面的所有兄弟元素  $(&#39;ul .box~*&#39;).css(&#39;background&#39;, &#39;yellow&#39;)&lt;/script&gt;</code></pre></li></ul><ul><li><p>过滤选择器(在原有选择器匹配的元素中进一步进行过滤的选择器)</p><ul><li>基本<ul><li><code>:first</code></li><li><code>:last</code></li><li><code>:eq(index)</code></li><li><code>:lt</code></li><li><code>:gt</code></li><li><code>:odd</code></li><li><code>:even</code></li><li><code>:not(selector)</code></li><li><code>header</code></li><li><code>animated</code></li><li><code>focus</code></li></ul></li><li>内容<ul><li><code>:contains(text)</code></li><li><code>empty</code></li><li><code>has(selector)</code></li><li><code>parent</code></li></ul></li><li>可见性<ul><li><code>:hidden</code></li><li><code>:visible</code></li></ul></li><li>属性<ul><li><code>[attrbute]</code></li><li><code>[attrName=value]</code></li><li><code>[attribute!=value]</code></li><li><code>[attribute^=value]</code></li><li><code>[attribute$=value]</code></li><li><code>[attribute*=value]</code></li><li><code>[attrSel1][attrSel2][attrSelN]</code></li></ul></li></ul><pre><code class="html">&lt;div id=&quot;div1&quot; class=&quot;box&quot;&gt;class为box的div1&lt;/div&gt;&lt;div id=&quot;div2&quot; class=&quot;box&quot;&gt;class为box的div2&lt;/div&gt;&lt;div id=&quot;div3&quot;&gt;div3&lt;/div&gt;&lt;span class=&quot;box&quot;&gt;class为box的span&lt;/span&gt;&lt;br/&gt;&lt;ul&gt;  &lt;li&gt;AAAAA&lt;/li&gt;  &lt;li title=&quot;hello&quot;&gt;BBBBB&lt;/li&gt;  &lt;li class=&quot;box&quot;&gt;CCCCC&lt;/li&gt;  &lt;li title=&quot;hello&quot;&gt;DDDDDD&lt;/li&gt;  &lt;li title=&quot;two&quot;&gt;BBBBB&lt;/li&gt;  &lt;li style=&quot;display:none&quot;&gt;我本来是隐藏的&lt;/li&gt;&lt;/ul&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  /*   需求:   1. 选择第一个div   2. 选择最后一个class为box的元素   3. 选择所有class属性不为box的div   4. 选择第二个和第三个li元素   5. 选择内容为BBBBB的li   6. 选择隐藏的li   7. 选择有title属性的li元素   8. 选择所有属性title为hello的li元素   */  //1. 选择第一个div  // $(&#39;div:first&#39;).css(&#39;background&#39;, &#39;red&#39;)  //2. 选择最后一个class为box的元素  //$(&#39;.box:last&#39;).css(&#39;background&#39;, &#39;red&#39;)  //3. 选择所有class属性不为box的div  // $(&#39;div:not(.box)&#39;).css(&#39;background&#39;, &#39;red&#39;)  //没有class属性也可以  //4. 选择第二个和第三个li元素  // $(&#39;li:gt(0):lt(2)&#39;).css(&#39;background&#39;, &#39;red&#39;) // 多个过滤选择器不是同时执行, 而是依次  //$(&#39;li:lt(3):gt(0)&#39;).css(&#39;background&#39;, &#39;red&#39;)  //5. 选择内容为BBBBB的li  // $(&#39;li:contains(&quot;BBBBB&quot;)&#39;).css(&#39;background&#39;, &#39;red&#39;)  //6. 选择隐藏的li  // console.log($(&#39;li:hidden&#39;).length, $(&#39;li:hidden&#39;)[0])  //7. 选择有title属性的li元素  // $(&#39;li[title]&#39;).css(&#39;background&#39;, &#39;red&#39;)  //8. 选择所有属性title为hello的li元素  $(&#39;li[title=&quot;hello&quot;]&#39;).css(&#39;background&#39;, &#39;red&#39;)&lt;/script&gt;</code></pre></li></ul><p>练习： 表格隔行变色</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815163734.png" alt="image-20200815163732938"></p><pre><code class="html">&lt;style&gt;div, span, p {  width: 140px;  height: 140px;  margin: 5px;  background: #aaa;  border: #000 1px solid;  float: left;  font-size: 17px;  font-family: Verdana;}div.mini {  width: 55px;  height: 55px;  background-color: #aaa;  font-size: 12px;}div.hide {  display: none;}#data {  width: 600px;}#data, td, th {  border-collapse: collapse;  border: 1px solid #aaaaaa;}th, td {  height: 28px;}#data thead {  background-color: #333399;  color: #ffffff;}.odd {  background-color: #ccccff;}&lt;/style&gt;&lt;table id=&quot;data&quot;&gt;  &lt;thead&gt;    &lt;tr&gt;      &lt;th&gt;姓名&lt;/th&gt;      &lt;th&gt;工资&lt;/th&gt;      &lt;th&gt;入职时间&lt;/th&gt;      &lt;th&gt;操作&lt;/th&gt;    &lt;/tr&gt;  &lt;/thead&gt;  &lt;tbody&gt;  &lt;tr&gt;    &lt;td&gt;Tom&lt;/td&gt;    &lt;td&gt;$3500&lt;/td&gt;    &lt;td&gt;2010-10-25&lt;/td&gt;    &lt;td&gt;&lt;a href=&quot;javascript:void(0)&quot;&gt;删除&lt;/a&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;Mary&lt;/td&gt;    &lt;td&gt;$3400&lt;/td&gt;    &lt;td&gt;2010-12-1&lt;/td&gt;    &lt;td&gt;&lt;a href=&quot;javascript:void(0)&quot;&gt;删除&lt;/a&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;King&lt;/td&gt;    &lt;td&gt;$5900&lt;/td&gt;    &lt;td&gt;2009-08-17&lt;/td&gt;    &lt;td&gt;&lt;a href=&quot;javascript:void(0)&quot;&gt;删除&lt;/a&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;Scott&lt;/td&gt;    &lt;td&gt;$3800&lt;/td&gt;    &lt;td&gt;2012-11-17&lt;/td&gt;    &lt;td&gt;&lt;a href=&quot;javascript:void(0)&quot;&gt;删除&lt;/a&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;Smith&lt;/td&gt;    &lt;td&gt;$3100&lt;/td&gt;    &lt;td&gt;2014-01-27&lt;/td&gt;    &lt;td&gt;&lt;a href=&quot;javascript:void(0)&quot;&gt;删除&lt;/a&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;Allen&lt;/td&gt;    &lt;td&gt;$3700&lt;/td&gt;    &lt;td&gt;2011-12-05&lt;/td&gt;    &lt;td&gt;&lt;a href=&quot;javascript:void(0)&quot;&gt;删除&lt;/a&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;/tbody&gt;&lt;/table&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;jquery-1.10.1.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  $(&#39;#data&gt;tbody&gt;tr:odd&#39;).css(&#39;background&#39;, &#39;#ccccff&#39;)&lt;/script&gt;</code></pre><ul><li><p>表单选择器</p><ul><li>表单<ul><li><code>:input</code></li><li><code>:text</code></li><li><code>:checkbox</code></li><li><code>:radio</code></li></ul></li><li>表单对象属性<ul><li><code>:checked</code>: 选中的</li></ul></li></ul><pre><code class="html">&lt;form&gt;  用户名: &lt;input type=&quot;text&quot;/&gt;&lt;br&gt;  密 码: &lt;input type=&quot;password&quot;/&gt;&lt;br&gt;  爱 好:  &lt;input type=&quot;checkbox&quot; checked=&quot;checked&quot;/&gt;篮球  &lt;input type=&quot;checkbox&quot;/&gt;足球  &lt;input type=&quot;checkbox&quot; checked=&quot;checked&quot;/&gt;羽毛球 &lt;br&gt;  性 别:  &lt;input type=&quot;radio&quot; name=&quot;sex&quot; value=&#39;male&#39;/&gt;男  &lt;input type=&quot;radio&quot; name=&quot;sex&quot; value=&#39;female&#39;/&gt;女&lt;br&gt;  邮 箱: &lt;input type=&quot;text&quot; name=&quot;email&quot; disabled=&quot;disabled&quot;/&gt;&lt;br&gt;  所在地:  &lt;select&gt;    &lt;option value=&quot;1&quot;&gt;北京&lt;/option&gt;    &lt;option value=&quot;2&quot; selected=&quot;selected&quot;&gt;天津&lt;/option&gt;    &lt;option value=&quot;3&quot;&gt;河北&lt;/option&gt;  &lt;/select&gt;&lt;br&gt;  &lt;input type=&quot;submit&quot; value=&quot;提交&quot;/&gt;&lt;/form&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  /*   需求:   1. 选择不可用的文本输入框   2. 显示选择爱好 的个数   3. 显示选择的城市名称   */  //1. 选择不可用的文本输入框  // $(&#39;:text:disabled&#39;).css(&#39;background&#39;, &#39;red&#39;)  //2. 显示选择爱好 的个数  console.log($(&#39;:checkbox:checked&#39;).length)  //3. 显示选择的城市名称  $(&#39;:submit&#39;).click(function () {    var city = $(&#39;select&gt;option:selected&#39;).html() // 选择的option的标签体文本    city = $(&#39;select&#39;).val()  // 选择的option的value属性值    alert(city)  })&lt;/script&gt;</code></pre></li></ul><h3><span id="2-工具方法">2、<code>$</code>工具方法</span></h3><ol><li><code>$.each()</code>: 遍历数组或对象中的数据</li><li><code>$.trim()</code>: 去除字符串两边的空格</li><li><code>$.type(obj)</code>: 得到数据的类型</li><li><code>$.isArray(obj)</code>: 判断是否是数组</li><li><code>$.isFunction(obj)</code>: 判断是否是函数</li><li><code>$.parseJSON(json)</code>: 解析json字符串转换为js对象/数组</li></ol><pre><code class="html">&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  //1. $.each(): 遍历数组或对象中的数据  var obj = {    name: &#39;Tom&#39;,    setName: function (name) {      this.name = name    }  }  $.each(obj, function (key, value) {    console.log(key, value)  })  //2. $.trim(): 去除字符串两边的空格  //3. $.type(obj): 得到数据的类型  console.log($.type($)) // &#39;function&#39;  //4. $.isArray(obj): 判断是否是数组  console.log($.isArray($(&#39;body&#39;)), $.isArray([])) // false true  //5. $.isFunction(obj): 判断是否是函数  console.log($.isFunction($)) // true  //6. $.parseJSON(json) : 解析json字符串转换为js对象/数组  var json = &#39;{&quot;name&quot;:&quot;Tom&quot;, &quot;age&quot;:12}&#39;  // json对象: {}  // json对象===&gt;JS对象  console.log($.parseJSON(json))  json = &#39;[{&quot;name&quot;:&quot;Tom&quot;, &quot;age&quot;:12}, {&quot;name&quot;:&quot;JACK&quot;, &quot;age&quot;:13}]&#39; // json数组: []  // json数组===&gt;JS数组  console.log($.parseJSON(json))  /*  JSON.parse(jsonString)   json字符串---&gt;js对象/数组  JSON.stringify(jsObj/jsArr)  js对象/数组---&gt;json字符串  */&lt;/script&gt;</code></pre><p>练习：多Tab点击切换</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815163919.png" alt="image-20200815163918240"></p><pre><code class="html">&lt;style&gt;*{margin:0;padding:0}#tab li{float:left;list-style:none;width:80px;height:40px;line-height:40px;cursor:pointer;text-align:center}#container{position:relative}#content1,#content2,#content3{width:300px;height:100px;padding:30px;position:absolute;top:40px;left:0}#tab1,#content1{background-color:#fc0}#tab2,#content2{background-color:#f0c}#tab3,#content3{background-color:#0cf}&lt;/style&gt;&lt;h2&gt;多Tab点击切换&lt;/h2&gt;&lt;ul id=&quot;tab&quot;&gt;  &lt;li id=&quot;tab1&quot; value=&quot;1&quot;&gt;10元套餐&lt;/li&gt;  &lt;li id=&quot;tab2&quot; value=&quot;2&quot;&gt;30元套餐&lt;/li&gt;  &lt;li id=&quot;tab3&quot; value=&quot;3&quot;&gt;50元包月&lt;/li&gt;&lt;/ul&gt;&lt;div id=&quot;container&quot;&gt;  &lt;div id=&quot;content1&quot;&gt;    10元套餐详情：&lt;br/&gt;&amp;nbsp;每月套餐内拨打100分钟，超出部分2毛/分钟  &lt;/div&gt;  &lt;div id=&quot;content2&quot; style=&quot;display: none&quot;&gt;    30元套餐详情：&lt;br/&gt;&amp;nbsp;每月套餐内拨打300分钟，超出部分1.5毛/分钟  &lt;/div&gt;  &lt;div id=&quot;content3&quot; style=&quot;display: none&quot;&gt;    50元包月详情：&lt;br/&gt;&amp;nbsp;每月无限量随心打  &lt;/div&gt;&lt;/div&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;jquery-1.10.1.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  var $contents = $(&#39;#container&gt;div&#39;)  // 给3个li加监听  /*$(&#39;#tab&gt;li&#39;).click(function () { // 隐式遍历    //alert(&#39;----&#39;)    // 隐隐藏所有内容div    $contents.css(&#39;display&#39;, &#39;none&#39;)    // 显示对应的内容div    // 得到当前点击的li在兄弟中下标    var index = $(this).index()    // 找到对应的内容div, 并显示    $contents[index].style.display = &#39;block&#39;    // $($contents[index]).css(&#39;display&#39;, &#39;block&#39;)  })*/  // 改进——第二三个div默认none，不需要全部设置隐藏  var currIndex = 0 //当前显示的内容div的下标  $(&#39;#tab&gt;li&#39;).click(function () { // 隐式遍历    //alert(&#39;----&#39;)    // 隐藏当前已经显示的内容div    $contents[currIndex].style.display = &#39;none&#39;    // 显示对应的内容div      // 得到当前点击的li在兄弟中下标    var index = $(this).index()      // 找到对应的内容div, 并显示    $contents[index].style.display = &#39;block&#39;    // 更新下标    currIndex = index  })&lt;/script&gt;</code></pre><h3><span id="3-ajax">3、ajax</span></h3><ul><li><p><code>ajax()</code></p></li><li><p><code>get()</code></p></li><li><p><code>post</code></p><p>……</p></li></ul><h2><span id="四-使用jquery对象">四、使用jQuery对象</span></h2><h3><span id="1-属性文本">1、属性/文本</span></h3><p>操作标签的属性, 标签体文本</p><p>操作任意属性</p><ul><li><code>attr(name)</code> /<code>attr(name, value)</code>: 读写非布尔值的标签属性</li><li><code>prop(name)</code> / <code>prop(name, value)</code>: 读写布尔值的标签属性</li><li><code>removeAttr(name)</code>/<code>removeProp(name)</code>: 删除属性</li></ul><p>操作class属性</p><ul><li><code>addClass(classValue)</code>: 添加class</li><li><code>removeClass(classValue)</code>: 移除指定class</li></ul><p>操作HTML代码/文本/值</p><ul><li><code>val()</code> / <code>val(value)</code>: 读写标签的value</li><li><code>html()</code> / <code>html(htmlString)</code>: 读写标签体文本</li></ul><pre><code class="html">&lt;div id=&quot;div1&quot; class=&quot;box&quot; title=&quot;one&quot;&gt;class为box的div1&lt;/div&gt;&lt;div id=&quot;div2&quot; class=&quot;box&quot; title=&quot;two&quot;&gt;class为box的div2&lt;/div&gt;&lt;div id=&quot;div3&quot;&gt;div3&lt;/div&gt;&lt;span class=&quot;box&quot;&gt;class为box的span&lt;/span&gt;&lt;br/&gt;&lt;ul&gt;  &lt;li&gt;AAAAA&lt;/li&gt;  &lt;li title=&quot;hello&quot; class=&quot;box2&quot;&gt;BBBBB&lt;/li&gt;  &lt;li class=&quot;box&quot;&gt;CCCCC&lt;/li&gt;  &lt;li title=&quot;hello&quot;&gt;DDDDDD&lt;/li&gt;  &lt;li title=&quot;two&quot;&gt;&lt;span&gt;BBBBB&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;input type=&quot;text&quot; name=&quot;username&quot; value=&quot;guiguClass&quot;/&gt;&lt;br&gt;&lt;input type=&quot;checkbox&quot;&gt;&lt;input type=&quot;checkbox&quot;&gt;&lt;br&gt;&lt;button&gt;选中&lt;/button&gt;&lt;button&gt;不选中&lt;/button&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  //1. 读取第一个div的title属性  // console.log($(&#39;div:first&#39;).attr(&#39;title&#39;)) // one  //2. 给所有的div设置name属性(value为atguigu)  // $(&#39;div&#39;).attr(&#39;name&#39;, &#39;atguigu&#39;)  //3. 移除所有div的title属性  // $(&#39;div&#39;).removeAttr(&#39;title&#39;)  //4. 给所有的div设置class=&#39;guiguClass&#39;  //$(&#39;div&#39;).attr(&#39;class&#39;, &#39;guiguClass&#39;)  //5. 给所有的div添加class=&#39;abc&#39;  //$(&#39;div&#39;).addClass(&#39;abc&#39;)  //6. 移除所有div的guiguClass的class  //$(&#39;div&#39;).removeClass(&#39;guiguClass&#39;)  //7. 得到最后一个li的标签体文本  //console.log($(&#39;li:last&#39;).html())  //8. 设置第一个li的标签体为&quot;&lt;h1&gt;mmmmmmmmm&lt;/h1&gt;&quot;  //$(&#39;li:first&#39;).html(&#39;&lt;h1&gt;mmmmmmmmm&lt;/h1&gt;&#39;)  //9. 得到输入框中的value值  //console.log($(&#39;:text&#39;).val()) // 读取  //10. 将输入框的值设置为atguigu  //$(&#39;:text&#39;).val(&#39;atguigu&#39;) // 设置      读写合一  //11. 点击&#39;全选&#39;按钮实现全选    // attr(): 操作属性值为非布尔值的属性    // prop(): 专门操作属性值为布尔值的属性  var $checkboxs = $(&#39;:checkbox&#39;)  $(&#39;button:first&#39;).click(function () {    $checkboxs.prop(&#39;checked&#39;, true)  })  //12. 点击&#39;全不选&#39;按钮实现全不选  $(&#39;button:last&#39;).click(function () {    $checkboxs.prop(&#39;checked&#39;, false)  })&lt;/script&gt;</code></pre><p>练习表格隔行变色改进—&gt;odd样式类</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815164644.png" alt="image-20200815164643571"></p><h3><span id="2-css">2、CSS</span></h3><p>(1) style样式(设置CSS样式/读取CSS值)</p><ul><li><code>css(styleName)</code>: 根据样式名得到对应的值</li><li><code>css(styleName, value)</code>: 设置一个样式</li><li><code>css({多个样式对})</code>: 设置多个样式</li></ul><pre><code class="html">&lt;p style=&quot;color: blue;&quot;&gt;HTML&lt;/p&gt;&lt;p style=&quot;color: green;&quot;&gt;CSS&lt;/p&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  //1. 得到第一个p标签的颜色  //console.log($(&#39;p:first&#39;).css(&#39;color&#39;))  //2. 设置所有p标签的文本颜色为red  //$(&#39;p&#39;).css(&#39;color&#39;, &#39;red&#39;)  //3. 设置第2个p的字体颜色(#ff0011),背景(blue),宽(300px), 高(30px)  $(&#39;p:eq(1)&#39;).css({    color: &#39;#ff0011&#39;,    background: &#39;blue&#39;,    width: 300,    height: 30  })&lt;/script&gt;</code></pre><p>(2) 位置坐标(获取/设置标签的位置数据)</p><ul><li><code>offset()</code>: 读/写当前元素坐标(原点是页面左上角)</li><li><code>position()</code>: 读当前元素坐标(原点是父元素左上角)</li><li><code>scrollTop()</code>/<code>scrollLeft()</code>: 读/写元素/页面的滚动条坐标</li></ul><pre><code class="html">&lt;style type=&quot;text/css&quot;&gt;  * {    margin: 0px;  }  .div1 {    position: absolute;    width: 200px;    height: 200px;    top: 20px;    left: 10px;    background: blue;  }  .div2 {    position: absolute;    width: 100px;    height: 100px;    top: 50px;    background: red;  }  .div3 {    position: absolute;    top: 250px;  }&lt;/style&gt;&lt;body style=&quot;height: 2000px;&quot;&gt;&lt;div class=&quot;div1&quot;&gt;  &lt;div class=&quot;div2&quot;&gt;测试offset&lt;/div&gt;&lt;/div&gt;&lt;div class=&#39;div3&#39;&gt;  &lt;button id=&quot;btn1&quot;&gt;读取offset和position&lt;/button&gt;  &lt;button id=&quot;btn2&quot;&gt;设置offset&lt;/button&gt;&lt;/div&gt;&lt;!--获取/设置标签的位置数据  * offset(): 相对页面左上角的坐标  * position(): 相对于父元素左上角的坐标--&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  $(&#39;#btn1&#39;).click(function () {        // 打印 div1 相对于页面左上角的位置    var offset = $(&#39;.div1&#39;).offset()    console.log(offset.left, offset.top) // 10 20        // 打印 div2 相对于页面左上角的位置    offset = $(&#39;.div2&#39;).offset()    console.log(offset.left, offset.top) // 10 70        // 打印 div1 相对于父元素左上角的位置    var position = $(&#39;.div1&#39;).position()    console.log(position.left, position.top) // 10 20        // 打印 div2 相对于父元素左上角的位置    position = $(&#39;.div2&#39;).position()    console.log(position.left, position.top) // 0 50  })  $(&#39;#btn2&#39;).click(function () {    // 设置 div2 相对于页面的左上角的位置    $(&#39;.div2&#39;).offset({      left: 50,      top: 100    })  })&lt;/script&gt;</code></pre><ol><li><p>scrollTop(): 读取/设置滚动条的Y坐标</p></li><li><p><code>$(document.body).scrollTop()+$(document.documentElement).scrollTop()</code>: 读取页面滚动条的Y坐标(兼容chrome和IE)</p></li><li><p><code>$(&#39;body,html&#39;).scrollTop(60);</code>滚动到指定位置(兼容chrome和IE)</p></li></ol><pre><code class="html">&lt;body style=&quot;height: 2000px;&quot;&gt;&lt;div style=&quot;border:1px solid black;width:100px;height:150px;overflow:auto&quot;&gt;  This is some text. This is some text. This is some text. This is some text.  This is some text. This is some text. This is some text. This is some text.  This is some text. This is some text. This is some text. This is some text.  This is some text. This is some text. This is some text. This is some text.  This is some text. This is some text. This is some text. This is some text.  This is some text. This is some text. This is some text. This is some text.  his is some text.&lt;/div&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;button id=&quot;btn1&quot;&gt;得到scrollTop&lt;/button&gt;&lt;button id=&quot;btn2&quot;&gt;设置scrollTop&lt;/button&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot;&gt;&lt;/script&gt;&lt;script&gt;  //1. 得到div或页面滚动条的坐标  $(&#39;#btn1&#39;).click(function () {    console.log($(&#39;div&#39;).scrollTop())    // console.log($(&#39;html&#39;).scrollTop()+$(&#39;body&#39;).scrollTop())    console.log($(document.documentElement).scrollTop()+$(document.body).scrollTop()) // 兼容IE/Chrome  })  //2. 让div或页面的滚动条滚动到指定位置  $(&#39;#btn2&#39;).click(function () {    $(&#39;div&#39;).scrollTop(200)    $(&#39;html,body&#39;).scrollTop(300)  })&lt;/script&gt;&lt;/body&gt;</code></pre><p>(3) 尺寸(获取/设置标签的尺寸数据)</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815171021.png" alt="image-20200815171020176"></p><ul><li><p>内容尺寸</p><ul><li><code>height()</code>: height</li><li><code>width()</code>: width</li></ul></li><li><p>内部尺寸</p><ul><li><code>innerHeight()</code>: height+padding</li><li><code>innerWidth()</code>: width+padding</li></ul></li><li><p>外部尺寸</p><ul><li>outerHeight(false/true): height+padding+border 如果是true, 加上margin</li><li>outerWidth(false/true): width+padding+border 如果是true, 加上margin</li></ul></li></ul><pre><code class="html">div {  width: 100px;  height: 150px;  background: red;  padding: 10px;  border: 10px #fbd850 solid;  margin: 10px;}&lt;div&gt;div&lt;/div&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script&gt;  var $div = $(&#39;div&#39;)  // 1. 内容尺寸  console.log($div.width(), $div.height())  // 100 150  // 2. 内部尺寸  console.log($div.innerWidth(), $div.innerHeight()) //120 170  // 3. 外部尺寸  console.log($div.outerWidth(), $div.outerHeight()) //140 190  console.log($div.outerWidth(true), $div.outerHeight(true)) //160 210&lt;/script&gt;</code></pre><h3><span id="3-筛选">3、筛选</span></h3><p>(1) 过滤</p><p>在jQuery对象中的元素对象数组中过滤出一部分元素来</p><ol><li><p>first()</p></li><li><p>last()</p></li><li><p>eq(index|-index)</p></li><li><p>filter(selector)：对当前元素提要求</p></li><li><p>not(selector)：对当前元素提要求, 并取反</p></li><li><p>has(selector)：对子孙元素提要求</p></li></ol><pre><code class="html">&lt;ul&gt;  &lt;li&gt;AAAAA&lt;/li&gt;  &lt;li title=&quot;hello&quot; class=&quot;box2&quot;&gt;BBBBB&lt;/li&gt;  &lt;li class=&quot;box&quot;&gt;CCCCC&lt;/li&gt;  &lt;li title=&quot;hello&quot;&gt;DDDDDD&lt;/li&gt;  &lt;li title=&quot;two&quot;&gt;&lt;span&gt;BBBBB&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;eeeee&lt;/li&gt;&lt;li&gt;EEEEE&lt;/li&gt;&lt;br&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  var $lis = $(&#39;ul&gt;li&#39;)  //1. ul下li标签第一个  // $lis.first().css(&#39;background&#39;, &#39;red&#39;)  // $lis[0].style.background = &#39;red&#39;  //2. ul下li标签的最后一个  // $lis.last().css(&#39;background&#39;, &#39;red&#39;)  //3. ul下li标签的第二个  // $lis.eq(1).css(&#39;background&#39;, &#39;red&#39;)  //4. ul下li标签中title属性为hello的  // $lis.filter(&#39;[title=hello]&#39;).css(&#39;background&#39;, &#39;red&#39;)  //5. ul下li标签中title属性不为hello的  // $lis.not(&#39;[title=hello]&#39;).css(&#39;background&#39;, &#39;red&#39;)  // $lis.filter(&#39;[title!=hello]&#39;).filter(&#39;[title]&#39;).css(&#39;background&#39;, &#39;red&#39;)  //6. ul下li标签中有span子标签的  $lis.has(&#39;span&#39;).css(&#39;background&#39;, &#39;red&#39;)&lt;/script&gt;</code></pre><p>(2) 查找</p><p>在已经匹配出的元素集合中根据选择器查找孩子/父母/兄弟标签</p><ol><li><p>children(): 子标签中找</p></li><li><p>find() : 后代标签中找</p></li><li><p>parent() : 父标签</p></li><li><p>prevAll() : 前面所有的兄弟标签</p></li><li><p>nextAll() : 后面所有的兄弟标签</p></li><li><p>siblings() : 前后所有的兄弟标签</p></li></ol><pre><code class="html">&lt;div id=&quot;div1&quot; class=&quot;box&quot; title=&quot;one&quot;&gt;class为box的div1&lt;/div&gt;&lt;div id=&quot;div2&quot; class=&quot;box&quot;&gt;class为box的div2&lt;/div&gt;&lt;div id=&quot;div3&quot;&gt;div3&lt;/div&gt;&lt;span class=&quot;box&quot;&gt;class为box的span&lt;/span&gt;&lt;br/&gt;&lt;div&gt;  &lt;ul&gt;    &lt;span&gt;span文本1&lt;/span&gt;    &lt;li&gt;AAAAA&lt;/li&gt;    &lt;li title=&quot;hello&quot; class=&quot;box2&quot;&gt;BBBBB&lt;/li&gt;    &lt;li class=&quot;box&quot; id=&#39;cc&#39;&gt;CCCCC&lt;/li&gt;    &lt;li title=&quot;hello&quot;&gt;DDDDDD&lt;/li&gt;    &lt;li title=&quot;two&quot;&gt;&lt;span&gt;span文本2&lt;/span&gt;&lt;/li&gt;    &lt;span&gt;span文本3&lt;/span&gt;  &lt;/ul&gt;  &lt;span&gt;span文本444&lt;/span&gt;&lt;br&gt;  &lt;li&gt;eeeee&lt;/li&gt;  &lt;li&gt;EEEEE&lt;/li&gt;  &lt;br&gt;&lt;/div&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  var $ul = $(&#39;ul&#39;)  //1. ul标签的第2个span子标签  //$ul.children(&#39;span:eq(1)&#39;).css(&#39;background&#39;, &#39;red&#39;)  //2. ul标签的第2个span后代标签  // $ul.find(&#39;span:eq(1)&#39;).css(&#39;background&#39;, &#39;red&#39;)  //3. ul标签的父标签  // $ul.parent().css(&#39;background&#39;, &#39;red&#39;)  //4. id为cc的li标签的前面的所有li标签  var $li = $(&#39;#cc&#39;)  // $li.prevAll(&#39;li&#39;).css(&#39;background&#39;, &#39;red&#39;)  //5. id为cc的li标签的所有兄弟li标签  $li.siblings(&#39;li&#39;).css(&#39;background&#39;, &#39;red&#39;)&lt;/script&gt;</code></pre><h3><span id="4-文档处理cud">4、文档处理(CUD)</span></h3><ul><li>增加<ul><li>append() / appendTo(): 向当前匹配的所有元素内部的最后插入指定内容</li><li>preppend() / preppendTo(): 向当前匹配的所有元素内部的最前面插入指定内容</li><li>before(): 将指定内容插入到当前所有匹配元素的前面</li><li>after(): 将指定内容插入到当前所有匹配元素的后面替换节点</li></ul></li><li>删除<ul><li>remove(): 将自己及内部的孩子都删除</li><li>empty(): 掏空(自己还在)</li></ul></li><li>更新<ul><li>replaceWith()：用指定内容替换所有匹配的标签删除节点</li></ul></li></ul><pre><code class="html">&lt;style type=&quot;text/css&quot;&gt;  * {    margin: 0px;  }  .div1 {    position: absolute;    width: 200px;    height: 200px;    top: 20px;    left: 10px;    background: blue;  }  .div2 {    position: absolute;    width: 100px;    height: 100px;    /*top: 50px;*/    background: red;  }  .div3 {    position: absolute;    top: 250px;  }&lt;/style&gt;&lt;ul id=&quot;ul1&quot;&gt;  &lt;li&gt;AAAAA&lt;/li&gt;  &lt;li title=&quot;hello&quot;&gt;BBBBB&lt;/li&gt;  &lt;li class=&quot;box&quot;&gt;CCCCC&lt;/li&gt;  &lt;li title=&quot;hello&quot;&gt;DDDDDD&lt;/li&gt;  &lt;li title=&quot;two&quot;&gt;EEEEE&lt;/li&gt;  &lt;li&gt;FFFFF&lt;/li&gt;&lt;/ul&gt;&lt;br&gt;&lt;br&gt;&lt;ul id=&quot;ul2&quot;&gt;  &lt;li&gt;aaa&lt;/li&gt;  &lt;li title=&quot;hello&quot;&gt;bbb&lt;/li&gt;  &lt;li class=&quot;box&quot;&gt;ccc&lt;/li&gt;  &lt;li title=&quot;hello&quot;&gt;ddd&lt;/li&gt;  &lt;li title=&quot;two&quot;&gt;eee&lt;/li&gt;&lt;/ul&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  //1. 向id为ul1的ul下添加一个span(最后)  var $ul1 = $(&#39;#ul1&#39;)  // $ul1.append(&#39;&lt;span&gt;append()添加的span&lt;/span&gt;&#39;)  $(&#39;&lt;span&gt;appendTo()添加的span&lt;/span&gt;&#39;).appendTo($ul1)  //2. 向id为ul1的ul下添加一个span(最前)  // $ul1.prepend(&#39;&lt;span&gt;prepend()添加的span&lt;/span&gt;&#39;)  $(&#39;&lt;span&gt;prependTo()添加的span&lt;/span&gt;&#39;).prependTo($ul1)  //3. 在id为ul1的ul下的li(title为hello)的前面添加span  $ul1.children(&#39;li[title=hello]&#39;).before(&#39;&lt;span&gt;before()添加的span&lt;/span&gt;&#39;)  //4. 在id为ul1的ul下的li(title为hello)的后面添加span  $ul1.children(&#39;li[title=hello]&#39;).after(&#39;&lt;span&gt;after()添加的span&lt;/span&gt;&#39;)  //5. 将在id为ul2的ul下的li(title为hello)全部替换为p  $(&#39;#ul2&gt;li[title=hello]&#39;).replaceWith(&#39;&lt;p&gt;replaceAll()替换的p&lt;/p&gt;&#39;)  //6. 移除id为ul2的ul下的所有li  // $(&#39;#ul2&#39;).empty()  // &lt;p&gt;也会删除  $(&#39;#ul2&gt;li&#39;).remove()&lt;/script&gt;</code></pre><h3><span id="5-事件">5、事件</span></h3><p>(1) 事件处理</p><ul><li><p>绑定事件</p><ul><li><p>eventName(function(){})：绑定对应事件名的监听, 例如：<code>$(&#39;#div&#39;).click(function(){});</code></p></li><li><p>on(‘eventName’, function(){})： 通用的绑定事件监听, 例如：<code>$(&#39;#div&#39;).on(&#39;click&#39;, function(){})</code></p><ul><li>常用: <code>click</code>, <code>mouseenter</code>/<code>mouseleave</code> <code>mouseover</code>/<code>mouseout</code> <code>focus</code>/<code>blur</code></li></ul><p>优缺点:</p><p>eventName: 编码方便, 但只能加一个监听, 且有的事件监听不支持</p><p>on: 编码不方便, 可以添加多个监听, 且更通用</p></li></ul></li><li><p>解绑事件</p><ul><li>off(‘eventName’)</li></ul></li></ul><pre><code class="html">&lt;style type=&quot;text/css&quot;&gt;  * {    margin: 0px;  }  .out {    position: absolute;    width: 200px;    height: 200px;    top: 20px;    left: 10px;    background: blue;  }  .inner {    position: absolute;    width: 100px;    height: 100px;    top: 50px;    background: red;  }  .divBtn {    position: absolute;    top: 250px;  }&lt;/style&gt;&lt;body style=&quot;height: 2000px;&quot;&gt;&lt;div class=&quot;out&quot;&gt;  外部DIV  &lt;div class=&quot;inner&quot;&gt;内部div&lt;/div&gt;&lt;/div&gt;&lt;div class=&#39;divBtn&#39;&gt;  &lt;button id=&quot;btn1&quot;&gt;取消绑定所有事件&lt;/button&gt;  &lt;button id=&quot;btn2&quot;&gt;取消绑定mouseover事件&lt;/button&gt;  &lt;button id=&quot;btn3&quot;&gt;测试事件坐标&lt;/button&gt;  &lt;a href=&quot;http://www.baidu.com&quot; id=&quot;test4&quot;&gt;百度一下&lt;/a&gt;&lt;/div&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  //1. 给.out绑定点击监听(用两种方法绑定)  /*$(&#39;.out&#39;).click(function () {   console.log(&#39;click out&#39;)   })*/  $(&#39;.out&#39;).on(&#39;click&#39;, function () {    console.log(&#39;on click out&#39;)  })  //2. 给.inner绑定鼠标移入和移出的事件监听(用3种方法绑定)  /*   $(&#39;.inner&#39;)   .mouseenter(function () { // 进入    console.log(&#39;进入&#39;)   })   .mouseleave(function () { // 离开   console.log(&#39;离开&#39;)   })   */  /*   $(&#39;.inner&#39;)   .on(&#39;mouseenter&#39;, function () {   console.log(&#39;进入2&#39;)   })   .on(&#39;mouseleave&#39;, function () {   console.log(&#39;离开2&#39;)   })   */  $(&#39;.inner&#39;).hover(function () {    console.log(&#39;进入3&#39;)  }, function () {    console.log(&#39;离开3&#39;)  })  //3. 点击btn1解除.inner上的所有事件监听  $(&#39;#btn1&#39;).click(function () {    $(&#39;.inner&#39;).off()  })  //4. 点击btn2解除.inner上的mouseenter事件  $(&#39;#btn2&#39;).click(function () {    $(&#39;.inner&#39;).off(&#39;mouseenter&#39;)  })  //5. 点击btn3得到事件坐标  $(&#39;#btn3&#39;).click(function (event) { // event事件对象    console.log(event.offsetX, event.offsetY) // 原点为事件元素的左上角    console.log(event.clientX, event.clientY) // 原点为窗口的左上角    console.log(event.pageX, event.pageY) // 原点为页面的左上角  })  //6. 点击.inner区域, 外部点击监听不响应  $(&#39;.inner&#39;).click(function (event) {    console.log(&#39;click inner&#39;)    //停止事件冒泡    event.stopPropagation()  })  //7. 点击链接, 如果当前时间是偶数不跳转  $(&#39;#test4&#39;).click(function (event) {    if(Date.now()%2===0) {      event.preventDefault()    }  })&lt;/script&gt;</code></pre><p>(2) 事件切换</p><p>hover(function(){}, function(){}) 同时绑定鼠标移入和移出监听</p><p>区别mouseover与mouseenter?</p><ul><li><p>mouseover: 在移入子元素时也会触发, 对应mouseout</p></li><li><p>mouseenter: 只在移入当前元素时才触发, 对应mouseleave</p></li></ul><p>​        hover()使用的就是mouseenter()和mouseleave()</p><p>区别on(‘eventName’, fun)与eventName(fun)</p><ul><li><p>on(‘eventName’, fun): 通用, 但编码麻烦</p></li><li><p>eventName(fun): 编码简单, 但有的事件没有对应的方法</p></li></ul><pre><code class="html">&lt;style type=&quot;text/css&quot;&gt;    * {        margin: 0px;    }    .div1 {        position: absolute;        width: 200px;        height: 200px;        top: 50px;        left: 10px;        background: olive;    }    .div2 {        position: absolute;        width: 100px;        height: 100px;        top: 50px;        background: red;    }    .div3 {        position: absolute;        width: 200px;        height: 200px;        top: 50px;        left: 230px;        background: olive;    }    .div4 {        position: absolute;        width: 100px;        height: 100px;        top: 50px;        background: yellow;    }    .divText{        position: absolute;        top: 330px;        left: 10px;    }&lt;/style&gt;&lt;div class=&quot;divText&quot;&gt;    区分鼠标的事件&lt;/div&gt;&lt;div class=&quot;div1&quot;&gt;    div1.....    &lt;div class=&quot;div2&quot;&gt;div2....&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;div3&quot;&gt;    div3.....    &lt;div class=&quot;div4&quot;&gt;div4....&lt;/div&gt;&lt;/div&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;    $(&#39;.div1&#39;)        .mouseover(function () {            console.log(&#39;mouseover 进入&#39;)        })        .mouseout(function () {            console.log(&#39;mouseout 离开&#39;)        })    $(&#39;.div3&#39;)        .mouseenter(function () {            console.log(&#39;mouseenter 进入&#39;)        })        .mouseleave(function () {            console.log(&#39;mouseleave 离开&#39;)        })&lt;/script&gt;</code></pre><p>(3) 事件委托</p><ul><li>理解: 将子元素的事件委托给父辈元素处理<ul><li>事件监听绑定在父元素上, 但事件发生在子元素上</li><li>　事件会冒泡到父元素</li><li>但最终调用的事件回调函数的是子元素: event.target</li></ul></li><li>好处<ul><li>新增的元素没有事件监听</li><li>减少监听的数量(n==&gt;1)</li></ul></li><li>编码<ul><li>delegate(selector, ‘eventName’, function(event){}) // 回调函数中的this是子元素</li><li>undelegate(‘eventName’)</li></ul></li></ul><pre><code class="html">&lt;ul&gt;  &lt;li&gt;11111&lt;/li&gt;  &lt;li&gt;1111111&lt;/li&gt;  &lt;li&gt;111111111&lt;/li&gt;  &lt;li&gt;11111111111&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;22222&lt;/li&gt;&lt;br&gt;&lt;button id=&quot;btn&quot;&gt;添加新的li&lt;/button&gt;&lt;br&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot;&gt;&lt;/script&gt;&lt;script&gt;  /*   需求：   1. 点击 li 背景就会变为红色   2. 点击 btn 就添加一个 li  */  $(&#39;ul&gt;li&#39;).click(function () {    this.style.background = &#39;red&#39;  })  $(&#39;#btn&#39;).click(function () {    $(&#39;ul&#39;).append(&#39;&lt;li&gt;新增的li....&lt;/li&gt;&#39;)  })&lt;/script&gt;</code></pre><p>引入：绑定事件监听的问题: 新加的元素没有监听</p><ol><li>事件委托(委派/代理):</li></ol><ul><li><p>将多个子元素(li)的事件监听委托给父辈元素(ul)处理</p></li><li><p>监听回调是加在了父辈元素上</p></li><li><p>当操作任何一个子元素(li)时, 事件会冒泡到父辈元素(ul)</p></li><li><p>父辈元素不会直接处理事件, 而是根据event.target得到发生事件的子元素(li), 通过这个子元素调用事件回调函数</p></li></ul><ol start="2"><li>事件委托的2方:</li></ol><ul><li><p>委托方: 业主 li</p></li><li><p>被委托方: 中介 ul</p></li></ul><ol start="3"><li>使用事件委托的好处</li></ol><ul><li><p>添加新的子元素, 自动有事件响应处理</p></li><li><p>减少事件监听的数量: n==&gt;1</p></li></ul><ol start="4"><li>jQuery的事件委托API</li></ol><ul><li><p>设置事件委托: <code>$(parentSelector).delegate(childrenSelector, eventName, callback)</code></p></li><li><p>移除事件委托: <code>$(parentSelector).undelegate(eventName)</code></p></li></ul><pre><code class="html">&lt;ul&gt;  &lt;li&gt;1111&lt;/li&gt;  &lt;li&gt;2222&lt;/li&gt;  &lt;li&gt;3333&lt;/li&gt;  &lt;li&gt;4444&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;22222&lt;/li&gt;&lt;br&gt;&lt;button id=&quot;btn1&quot;&gt;添加新的li&lt;/button&gt;&lt;button id=&quot;btn2&quot;&gt;删除ul上的事件委托的监听器&lt;/button&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot;&gt;&lt;/script&gt;&lt;script&gt;  // 设置事件委托  $(&#39;ul&#39;).delegate(&#39;li&#39;, &#39;click&#39;, function () {    // console.log(this)    this.style.background = &#39;red&#39;  })  $(&#39;#btn1&#39;).click(function () {    $(&#39;ul&#39;).append(&#39;&lt;li&gt;新增的li....&lt;/li&gt;&#39;)  })  $(&#39;#btn2&#39;).click(function () {    // 移除事件委托    $(&#39;ul&#39;).undelegate(&#39;click&#39;)  })&lt;/script&gt;</code></pre><ul><li><p>事件坐标</p><ul><li><p><code>event.offsetX</code>/<code>event.offsetY</code>: 原点是当前元素左上角(相对于事件元素左上角)</p></li><li><p><code>event.clientX</code>/<code>event.clientY</code>: 原点是窗口左上角(相对于视口的左上角)</p></li><li><p><code>event.pageX</code>/<code>event.pageY</code>: 原点是页面左上角(相对于页面的左上角)</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815173853.png" alt="image-20200815173852046"></p></li></ul></li><li><p>事件相关处理</p><ul><li>停止事件冒泡: <code>event.stopPropagation()</code></li><li>阻止事件的默认行为: <code>event.preventDefault()</code></li></ul></li></ul><h3><span id="6-动画效果">6、动画效果</span></h3><p>在一定的时间内, 不断改变元素样式</p><p>(1) 滑动动画(不断改变元素的高度来实现的)</p><ul><li>slideDown()：带动画的展开</li><li>slideUp()：带动画的收缩</li><li>slideToggle()：带动画的切换展开/收缩</li></ul><pre><code class="html">&lt;style type=&quot;text/css&quot;&gt;  * {    margin: 0px;  }  .div1 {    position: absolute;    width: 200px;    height: 200px;    top: 50px;    left: 10px;    background: red;  }&lt;/style&gt;&lt;body&gt;&lt;button id=&quot;btn1&quot;&gt;慢慢收缩&lt;/button&gt;&lt;button id=&quot;btn2&quot;&gt;慢慢展开&lt;/button&gt;&lt;button id=&quot;btn3&quot;&gt;收缩/展开切换&lt;/button&gt;&lt;div class=&quot;div1&quot;&gt;&lt;/div&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  /*   需求：   1. 点击btn1, 向上滑动   2. 点击btn2, 向下滑动   3. 点击btn3, 向上/向下切换   */  var $div1 = $(&#39;.div1&#39;)  // 1. 点击btn1, 向上滑动  $(&#39;#btn1&#39;).click(function () {    $div1.slideUp(3000)  })  // 2. 点击btn2, 向下滑动  $(&#39;#btn2&#39;).click(function () {    $div1.slideDown()  })  // 3. 点击btn3, 向上/向下切换  $(&#39;#btn3&#39;).click(function () {    $div1.slideToggle()  })&lt;/script&gt;</code></pre><p>(2) 淡入淡出动画(不断改变元素的透明度来实现的)</p><ul><li>fadeIn()：带动画的显示</li><li>fadeOut()：带动画隐藏</li><li>fadeToggle()：带动画切换显示/隐藏</li></ul><pre><code class="html">&lt;style type=&quot;text/css&quot;&gt;  * {    margin: 0px;  }  .div1 {    position: absolute;    width: 200px;    height: 200px;    top: 50px;    left: 10px;    background: red;  }&lt;/style&gt;&lt;body&gt;&lt;button id=&quot;btn1&quot;&gt;慢慢淡出&lt;/button&gt;&lt;button id=&quot;btn2&quot;&gt;慢慢淡入&lt;/button&gt;&lt;button id=&quot;btn3&quot;&gt;淡出/淡入切换&lt;/button&gt;&lt;div class=&quot;div1&quot;&gt;&lt;/div&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  /*   需求：   1. 点击btn1, 慢慢淡出     * 无参     * 有参       * 字符串参数       * 数字参数   2. 点击btn3, 慢慢淡入   3. 点击btn3, 淡出/淡入切换，动画结束时提示“动画结束了”   */  var $div1 = $(&#39;.div1&#39;)  $(&#39;#btn1&#39;).click(function () {    // $div1.fadeOut()    // $div1.fadeOut(&#39;slow&#39;)    $div1.fadeOut(1000, function () {      alert(&#39;动画完成了!!!&#39;)    })  })  $(&#39;#btn2&#39;).click(function () {    $div1.fadeIn()  })  $(&#39;#btn3&#39;).click(function () {    $div1.fadeToggle()  })&lt;/script&gt;</code></pre><p>(3) 显示/隐藏动画(不断改变元素的尺寸和透明度来实现)</p><ul><li>show()：(不)带动画的显示</li><li>hide()：(不)带动画的隐藏</li><li>toggle()：(不)带动画的切换显示/隐藏</li></ul><pre><code class="html">&lt;style type=&quot;text/css&quot;&gt;  * {    margin: 0px;  }  .div1 {    position: absolute;    width: 200px;    height: 200px;    top: 50px;    left: 10px;    background: red;    display: none;  }&lt;/style&gt;&lt;body&gt;&lt;button id=&quot;btn1&quot;&gt;瞬间显示&lt;/button&gt;&lt;button id=&quot;btn2&quot;&gt;慢慢显示&lt;/button&gt;&lt;button id=&quot;btn3&quot;&gt;慢慢隐藏&lt;/button&gt;&lt;button id=&quot;btn4&quot;&gt;显示隐藏切换&lt;/button&gt;&lt;div class=&quot;div1&quot;&gt;&lt;/div&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  /*  需求:  1. 点击btn1, 立即显示  2. 点击btn2, 慢慢显示  3. 点击btn3, 慢慢隐藏  4. 点击btn4, 切换显示/隐藏   */  var $div1 = $(&#39;.div1&#39;)  //1. 点击btn1, 立即显示  $(&#39;#btn1&#39;).click(function () {    $div1.show()  })  //2. 点击btn2, 慢慢显示  $(&#39;#btn2&#39;).click(function () {    $div1.show(1000)  })  //3. 点击btn3, 慢慢隐藏  $(&#39;#btn3&#39;).click(function () {    $div1.hide(1000)  })  //4. 点击btn4, 切换显示/隐藏  $(&#39;#btn4&#39;).click(function () {    $div1.toggle(1000)  })&lt;/script&gt;</code></pre><p>(4) 自定义动画</p><ul><li>animate({结束时的样式}, time, fun)：自定义动画效果的动画</li><li>stop()：停止动画</li></ul><pre><code class="html">&lt;style type=&quot;text/css&quot;&gt;  * {    margin: 0px;  }  .div1 {    position: absolute;    width: 100px;    height: 100px;    top: 50px;    left: 300px;    background: red;  }&lt;/style&gt;&lt;/head&gt;&lt;body&gt;&lt;button id=&quot;btn1&quot;&gt;逐渐扩大&lt;/button&gt;&lt;button id=&quot;btn2&quot;&gt;移动到指定位置&lt;/button&gt;&lt;button id=&quot;btn3&quot;&gt;移动指定距离&lt;/button&gt;&lt;button id=&quot;btn4&quot;&gt;停止动画&lt;/button&gt;&lt;div class=&quot;div1&quot;&gt;  花满田间，月照人影&lt;/div&gt;&lt;!--jQuery动画本质 : 在指定时间内不断改变元素样式值来实现的1. animate(): 自定义动画效果的动画2. stop(): 停止动画--&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  /*   需求：    1. 逐渐扩大      1). 宽/高都扩为200px      2). 宽先扩为200px, 高后扩为200px    2. 移动到指定位置      1).移动到(500, 100)处      2).移动到(100, 20)处    3.移动指定的距离      1). 移动距离为(100, 50)      2). 移动距离为(-100, -20)    4. 停止动画   */  var $div1 = $(&#39;.div1&#39;)  /*   1. 逐渐扩大     1). 宽/高都扩为200px     2). 宽先扩为200px, 高后扩为200px   */  $(&#39;#btn1&#39;).click(function () {    /*    $div1.animate({      width: 200,      height: 200    }, 1000)    */    $div1      .animate({        width: 200      }, 1000)      .animate({        height: 200      }, 1000)  })  /*   2. 移动到指定位置     1).移动到(500, 100)处     2).移动到(100, 20)处   */  $(&#39;#btn2&#39;).click(function () {    // 1).移动到(500, 100)处    /*    $div1.animate({ // 向右下移动      left: 500,      top: 100    }, 1000)    */    // 2).移动到(100, 20)处    $div1.animate({ // 向左上移动      left: 100,  // 300      top: 20  // 50    }, 1000)  })  /*   3.移动指定的距离     1). 移动距离为(100, 50)     2). 移动距离为(-100, -20)   */  $(&#39;#btn3&#39;).click(function () {    // 1). 移动距离为(100, 50)    /*$div1.animate({      left: &#39;+=100&#39;,      top: &#39;+=50&#39;    }, 1000)*/    // 2). 移动距离为(-100, -20)    $div1.animate({      left: &#39;-=100&#39;,      top: &#39;-=20&#39;    }, 3000)  })  $(&#39;#btn4&#39;).click(function () {    $div1.stop()  })&lt;/script&gt;</code></pre><p>jQuery对象使用特点</p><ul><li>链式调用：调用jQuery对象的任何方法后返回的还是当前jQuery对象</li></ul><pre><code class="javascript">$(&#39;.div1&#39;)        .mouseover(function () {            console.log(&#39;mouseover 进入&#39;)        })        .mouseout(function () {            console.log(&#39;mouseout 离开&#39;)        })</code></pre><ul><li>读写合一<ul><li>读: 内部第一个dom元素</li><li>写: 内部所有的dom元素</li></ul></li></ul><h2><span id="五-jquery插件">五、jQuery插件</span></h2><h3><span id="1-扩展插件">1、扩展插件</span></h3><ul><li>扩展jQuery的工具方法</li></ul><pre><code class="javascript">$.extend({  xxx: fuction () {} // this是$})$.xxx()</code></pre><ul><li>扩展jQuery对象的方法</li></ul><pre><code class="javascript">$.fn.extend({  xxx: function(){}  // this是jQuery对象})$obj.xxx()</code></pre><pre><code class="html">&lt;style type=&quot;text/css&quot;&gt;  * {    margin: 0px;  }  .div1 {    position: absolute;    width: 100px;    height: 100px;    top: 50px;    left: 10px;    background: red;  }&lt;/style&gt;&lt;input type=&quot;checkbox&quot; name=&quot;items&quot; value=&quot;足球&quot;/&gt;足球&lt;input type=&quot;checkbox&quot; name=&quot;items&quot; value=&quot;篮球&quot;/&gt;篮球&lt;input type=&quot;checkbox&quot; name=&quot;items&quot; value=&quot;羽毛球&quot;/&gt;羽毛球&lt;input type=&quot;checkbox&quot; name=&quot;items&quot; value=&quot;乒乓球&quot;/&gt;乒乓球&lt;br/&gt;&lt;input type=&quot;button&quot; id=&quot;checkedAllBtn&quot; value=&quot;全　选&quot;/&gt;&lt;input type=&quot;button&quot; id=&quot;checkedNoBtn&quot; value=&quot;全不选&quot;/&gt;&lt;input type=&quot;button&quot; id=&quot;reverseCheckedBtn&quot; value=&quot;反选&quot;/&gt;&lt;!--1. 扩展jQuery的工具方法  $.extend(object)2. 扩展jQuery对象的方法  $.fn.extend(object)--&gt;&lt;script src=&quot;js/jquery-1.10.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;js/my_jQuery-plugin.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  /*   需求：   1. 给 $ 添加4个工具方法:     * min(a, b) : 返回较小的值     * max(c, d) : 返回较大的值     * leftTrim() : 去掉字符串左边的空格     * rightTrim() : 去掉字符串右边的空格   2. 给jQuery对象 添加3个功能方法:     * checkAll() : 全选     * unCheckAll() : 全不选     * reverseCheck() : 全反选   */  console.log($.min(3, 5), $.max(3, 5))  var string = &#39;   my atguigu    &#39;  console.log(&#39;-----&#39; + $.leftTrim(string) + &#39;-----&#39;)  console.log(&#39;-----&#39; + $.rightTrim(string) + &#39;-----&#39;)  var $items = $(&#39;:checkbox[name=items]&#39;)  $(&#39;#checkedAllBtn&#39;).click(function () {    $items.checkAll()  })  $(&#39;#checkedNoBtn&#39;).click(function () {    $items.unCheckAll()  })  $(&#39;#reverseCheckedBtn&#39;).click(function () {    $items.reverseCheck()  })&lt;/script&gt;</code></pre><h3><span id="2-jquery插件">2、jQuery插件</span></h3><ul><li><p>理解</p><ul><li>基于jQuery编写的扩展库</li><li><a href="http://plugins.jquery.com/" target="_blank" rel="noopener">http://plugins.jquery.com/</a></li></ul></li><li><p>jquery-validation</p><ul><li>表单验证插件</li><li>参考”菜鸟教程”学习 <a href="https://www.runoob.com/jquery/jquery-plugin-validate.html" target="_blank" rel="noopener">https://www.runoob.com/jquery/jquery-plugin-validate.html</a></li><li>使用<ul><li>下载</li><li>引入js<ul><li>jquery-1.11.1.js</li><li>jquery.validate.js</li><li>messages_zh.js</li></ul></li><li>定义验证<ul><li>直接在标签中指定</li><li>js编码指定</li></ul></li></ul></li></ul></li><li><p>jquery UI  <a href="http://jqueryui.com/" target="_blank" rel="noopener">http://jqueryui.com/</a></p></li><li><p>laydate   <a href="http://www.layui.com/laydate/" target="_blank" rel="noopener">http://www.layui.com/laydate/</a></p></li></ul><h2><span id="六-其他">六、其他</span></h2><h3><span id="1-多库共存">1、多库共存</span></h3><pre><code class="html">&lt;style type=&quot;text/css&quot;&gt;  * {    margin: 0px;  }  .div1 {    position: absolute;    width: 100px;    height: 100px;    top: 50px;    left: 10px;    background: red;  }&lt;/style&gt;&lt;/head&gt;&lt;body&gt;&lt;!--问题 : 如果有2个库都有$, 就存在冲突解决 : jQuery库可以释放$的使用权, 让另一个库可以正常使用, 此时jQuery库只能使用jQuery了API : jQuery.noConflict()--&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;js/myLib.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;js/jquery-1.10.1.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  // 释放$的使用权  jQuery.noConflict()  // 调用myLib中的$  $()  // 要想使用jQuery的功能, 只能使用jQuery  jQuery(function () {    console.log(&#39;文档加载完成&#39;)  })&lt;/script&gt;</code></pre><h3><span id="2-jquery中的function">2、jQuery中的<code>$(function(){})</code></span></h3><pre><code class="html">&lt;h1&gt;测试window.onload与$(document).ready()&lt;/h1&gt;&lt;img id=&quot;logo&quot; src=&quot;https://gss0.bdstatic.com/5bVWsj_p_tVS5dKfpU_Y_D3/res/r/image/2017-05-19/6fec71d56242b74eb24b4ac80b817eac.png&quot;&gt;&lt;!--区别: window.onload与 $(document).ready()  * window.onload    * 包括页面的图片加载完后才会回调(晚)    * 只能有一个监听回调  * $(document).ready()    * 等同于: $(function(){})    * 页面加载完就回调(早)    * 可以有多个监听回调--&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;js/jquery-1.10.1.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  /*   需求：   1. 直接打印img的宽度，观察其值   2. 在 $(function(){}) 中 打印 img 的宽度   3. 在 window.onload 中打印宽度   4. 在 img 加载完成后打印宽度   */  // 1. 直接打印img的宽度，观察其值  console.log(&#39;直接&#39;, $(&#39;#logo&#39;).width())  window.onload = function () {    console.log(&#39;onload&#39;, $(&#39;#logo&#39;).width())  }  window.onload = function () {    console.log(&#39;onload2&#39;, $(&#39;#logo&#39;).width())  }  $(function () {    console.log(&#39;ready&#39;, $(&#39;#logo&#39;).width())  })  $(function () {    console.log(&#39;ready2&#39;, $(&#39;#logo&#39;).width())  })  $(&#39;#logo&#39;).on(&#39;load&#39;, function () {    console.log(&#39;img load&#39;, $(this).width())  })  /*$(document).ready(function () {  })*/&lt;/script&gt;</code></pre><h3><span id="3-练习">3、练习</span></h3><p>以前用原生js实现过的用jQuery来一遍</p><p>(1) 爱好选择器</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815180658.png" alt="image-20200815180657634"></p><pre><code class="html">&lt;form&gt;  你爱好的运动是？&lt;input type=&quot;checkbox&quot; id=&quot;checkedAllBox&quot;/&gt;全选/全不选  &lt;br/&gt;  &lt;input type=&quot;checkbox&quot; name=&quot;items&quot; value=&quot;足球&quot;/&gt;足球  &lt;input type=&quot;checkbox&quot; name=&quot;items&quot; value=&quot;篮球&quot;/&gt;篮球  &lt;input type=&quot;checkbox&quot; name=&quot;items&quot; value=&quot;羽毛球&quot;/&gt;羽毛球  &lt;input type=&quot;checkbox&quot; name=&quot;items&quot; value=&quot;乒乓球&quot;/&gt;乒乓球  &lt;br/&gt;  &lt;input type=&quot;button&quot; id=&quot;checkedAllBtn&quot; value=&quot;全　选&quot;/&gt;  &lt;input type=&quot;button&quot; id=&quot;checkedNoBtn&quot; value=&quot;全不选&quot;/&gt;  &lt;input type=&quot;button&quot; id=&quot;checkedRevBtn&quot; value=&quot;反　选&quot;/&gt;  &lt;input type=&quot;button&quot; id=&quot;sendBtn&quot; value=&quot;提　交&quot;/&gt;&lt;/form&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;jquery-1.10.1.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  /*   功能说明:   1. 点击&#39;全选&#39;: 选中所有爱好   2. 点击&#39;全不选&#39;: 所有爱好都不勾选   3. 点击&#39;反选&#39;: 改变所有爱好的勾选状态   4. 点击&#39;提交&#39;: 提示所有勾选的爱好   5. 点击&#39;全选/全不选&#39;: 选中所有爱好, 或者全不选中   6. 点击某个爱好时, 必要时更新&#39;全选/全不选&#39;的选中状态   */  var $checkedAllBox = $(&#39;#checkedAllBox&#39;)  var $items = $(&#39;:checkbox[name=items]&#39;)  // 1. 点击&#39;全选&#39;: 选中所有爱好  $(&#39;#checkedAllBtn&#39;).click(function () {    $items.prop(&#39;checked&#39;, true)    $checkedAllBox.prop(&#39;checked&#39;, true)  })  // 2. 点击&#39;全不选&#39;: 所有爱好都不勾选  $(&#39;#checkedNoBtn&#39;).click(function () {    $items.prop(&#39;checked&#39;, false)    $checkedAllBox.prop(&#39;checked&#39;, false)  })  // 3. 点击&#39;反选&#39;: 改变所有爱好的勾选状态  $(&#39;#checkedRevBtn&#39;).click(function () {    $items.each(function () {      this.checked = !this.checked    })    $checkedAllBox.prop(&#39;checked&#39;, $items.filter(&#39;:not(:checked)&#39;).length===0)  })  //4. 点击&#39;提交&#39;: 提示所有勾选的爱好  $(&#39;#sendBtn&#39;).click(function () {    $items.filter(&#39;:checked&#39;).each(function () {      alert(this.value)    })  })  // 5. 点击&#39;全选/全不选&#39;: 选中所有爱好, 或者全不选中  $checkedAllBox.click(function () {    $items.prop(&#39;checked&#39;, this.checked)  })  // 6. 点击某个爱好时, 必要时更新&#39;全选/全不选&#39;的选中状态  $items.click(function () {    $checkedAllBox.prop(&#39;checked&#39;, $items.filter(&#39;:not(:checked)&#39;).length===0)  })&lt;/script&gt;</code></pre><p>(2) 增删员工记录</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815180844.png" alt="image-20200815180841962"></p><pre><code class="html">&lt;style&gt;#total{width:450px;margin-left:auto;margin-right:auto}ul{list-style-type:none}li{border-style:solid;border-width:1px;padding:5px;margin:5px;background-color:#9f9;float:left}.inner{width:400px;border-style:solid;border-width:1px;margin:10px;padding:10px;float:left}#employeeTable{border-spacing:1px;background-color:black;margin:80px auto 10px auto}th,td{background-color:white}#formDiv{width:250px;border-style:solid;border-width:1px;margin:50px auto 10px auto;padding:10px}#formDiv input{width:100%}.word{width:40px}.inp{width:200px}#employeeTable,#employeeTable th,#employeeTable td{border:1px solid;border-spacing:0}&lt;/style&gt;&lt;table id=&quot;employeeTable&quot;&gt;  &lt;tr&gt;    &lt;th&gt;Name&lt;/th&gt;    &lt;th&gt;Email&lt;/th&gt;    &lt;th&gt;Salary&lt;/th&gt;    &lt;th&gt;&amp;nbsp;&lt;/th&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;Tom&lt;/td&gt;    &lt;td&gt;tom@tom.com&lt;/td&gt;    &lt;td&gt;5000&lt;/td&gt;    &lt;td&gt;&lt;a href=&quot;deleteEmp?id=001&quot;&gt;Delete&lt;/a&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;Jerry&lt;/td&gt;    &lt;td&gt;jerry@sohu.com&lt;/td&gt;    &lt;td&gt;8000&lt;/td&gt;    &lt;td&gt;&lt;a href=&quot;deleteEmp?id=002&quot;&gt;Delete&lt;/a&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;Bob&lt;/td&gt;    &lt;td&gt;bob@tom.com&lt;/td&gt;    &lt;td&gt;10000&lt;/td&gt;    &lt;td&gt;&lt;a href=&quot;deleteEmp?id=003&quot;&gt;Delete&lt;/a&gt;&lt;/td&gt;  &lt;/tr&gt;&lt;/table&gt;&lt;div id=&quot;formDiv&quot;&gt;  &lt;h4&gt;添加新员工&lt;/h4&gt;  &lt;table&gt;    &lt;tr&gt;      &lt;td class=&quot;word&quot;&gt;name:&lt;/td&gt;      &lt;td class=&quot;inp&quot;&gt;        &lt;input type=&quot;text&quot; name=&quot;empName&quot; id=&quot;empName&quot;/&gt;      &lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;td class=&quot;word&quot;&gt;email:&lt;/td&gt;      &lt;td class=&quot;inp&quot;&gt;        &lt;input type=&quot;text&quot; name=&quot;email&quot; id=&quot;email&quot;/&gt;      &lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;td class=&quot;word&quot;&gt;salary:&lt;/td&gt;      &lt;td class=&quot;inp&quot;&gt;        &lt;input type=&quot;text&quot; name=&quot;salary&quot; id=&quot;salary&quot;/&gt;      &lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;td colspan=&quot;2&quot; align=&quot;center&quot;&gt;        &lt;button id=&quot;addEmpButton&quot; value=&quot;abc&quot;&gt;          Submit        &lt;/button&gt;      &lt;/td&gt;    &lt;/tr&gt;  &lt;/table&gt;&lt;/div&gt;</code></pre><p>初级版本</p><pre><code class="html">&lt;script type=&quot;text/javascript&quot; src=&quot;jquery-1.10.1.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  /*  1. 添加  2. 删除   */  $(&#39;#addEmpButton&#39;).click(function () {    //1. 收集输入的数据    var $empName = $(&#39;#empName&#39;)    var $email = $(&#39;#email&#39;)    var $salary = $(&#39;#salary&#39;)    var empName = $empName.val()    var email = $email.val()    var salary = $salary.val()    //2. 生成对应的&lt;tr&gt;标签结构, 并插入#employeeTable的tbody中    /*     &lt;tr&gt;       &lt;td&gt;Bob&lt;/td&gt;       &lt;td&gt;bob@tom.com&lt;/td&gt;       &lt;td&gt;10000&lt;/td&gt;       &lt;td&gt;&lt;a href=&quot;deleteEmp?id=003&quot;&gt;Delete&lt;/a&gt;&lt;/td&gt;     &lt;/tr&gt;     */    var $xxx = $(&#39;&lt;tr&gt;&lt;/tr&gt;&#39;)      .append(&#39;&lt;td&gt;&#39;+empName+&#39;&lt;/td&gt;&#39;) // 拼串      .append(&#39;&lt;td&gt;&#39;+email+&#39;&lt;/td&gt;&#39;)      .append(&#39;&lt;td&gt;&#39;+salary+&#39;&lt;/td&gt;&#39;)      .append(&#39;&lt;td&gt;&lt;a href=&quot;deleteEmp?id=&quot;&#39;+Date.now()+&#39;&gt;Delete&lt;/a&gt;&lt;/td&gt;&#39;)      .appendTo(&#39;#employeeTable&gt;tbody&#39;)      .find(&#39;a&#39;)      .click(clickDelete)    //3. 清除输入    $empName.val(&#39;&#39;)    $email.val(&#39;&#39;)    $salary.val(&#39;&#39;)  })  // 给所有删除链接绑定点击监听  $(&#39;#employeeTable a&#39;).click(clickDelete)  /*  点击删除的回调函数   */  function clickDelete () {    var $tr = $(this).parent().parent()    var name = $tr.children(&#39;:first&#39;).html()    if(confirm(&#39;确定删除&#39;+name+&#39;吗?&#39;)) {      $tr.remove()    }    return false  }&lt;/script&gt;</code></pre><p>进阶版本</p><pre><code class="html">&lt;script type=&quot;text/javascript&quot; src=&quot;jquery-1.10.1.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  /*  1. 添加  2. 删除   */  $(&#39;#addEmpButton&#39;).click(function () {    //1. 收集输入的数据    var $empName = $(&#39;#empName&#39;)    var $email = $(&#39;#email&#39;)    var $salary = $(&#39;#salary&#39;)    var empName = $empName.val()    var email = $email.val()    var salary = $salary.val()    //2. 生成对应的&lt;tr&gt;标签结构, 并插入#employeeTable的tbody中    /*     &lt;tr&gt;       &lt;td&gt;Bob&lt;/td&gt;       &lt;td&gt;bob@tom.com&lt;/td&gt;       &lt;td&gt;10000&lt;/td&gt;       &lt;td&gt;&lt;a href=&quot;deleteEmp?id=003&quot;&gt;Delete&lt;/a&gt;&lt;/td&gt;     &lt;/tr&gt;     */    var $xxx = $(&#39;&lt;tr&gt;&lt;/tr&gt;&#39;)      .append(&#39;&lt;td&gt;&#39;+empName+&#39;&lt;/td&gt;&#39;) // 拼串      .append(&#39;&lt;td&gt;&#39;+email+&#39;&lt;/td&gt;&#39;)      .append(&#39;&lt;td&gt;&#39;+salary+&#39;&lt;/td&gt;&#39;)      .append(&#39;&lt;td&gt;&lt;a href=&quot;deleteEmp?id=&quot;&#39;+Date.now()+&#39;&gt;Delete&lt;/a&gt;&lt;/td&gt;&#39;)      .appendTo(&#39;#employeeTable&gt;tbody&#39;)    //3. 清除输入    $empName.val(&#39;&#39;)    $email.val(&#39;&#39;)    $salary.val(&#39;&#39;)  })  // 通过table实现对所有a的click事件委托  $(&#39;#employeeTable&#39;).delegate(&#39;a&#39;, &#39;click&#39;, clickDelete)  /*  点击删除的回调函数   */  function clickDelete () {    var $tr = $(this).parent().parent()    var name = $tr.children(&#39;:first&#39;).html()    if(confirm(&#39;确定删除&#39;+name+&#39;吗?&#39;)) {      $tr.remove()    }    return false  }&lt;/script&gt;</code></pre><p>(3) 轮播图</p><pre><code class="html">&lt;style type=&quot;text/css&quot;&gt;/*去除内边距,没有链接下划线*/* {  margin: 0;  padding: 0;  text-decoration: none;}/*让&lt;body有20px的内边距*/body {  padding: 20px;}/*最外围的div*/#container {width: 600px;height: 400px;overflow: hidden;position: relative; /*相对定位*/margin: 0 auto;}/*包含所有图片的&lt;div&gt;*/#list {width: 4200px; /*7张图片的宽: 7*600 */height: 400px;position: absolute; /*绝对定位*/z-index: 1;}/*所有的图片&lt;img&gt;*/#list img {float: left; /*浮在左侧*/}/*包含所有圆点按钮的&lt;div&gt;*/#pointsDiv {position: absolute;height: 10px;width: 100px;z-index: 2;bottom: 20px;left: 250px;}/*所有的圆点&lt;span&gt;*/#pointsDiv span {cursor: pointer;float: left;border: 1px solid #fff;width: 10px;height: 10px;border-radius: 50%;background: #333;margin-right: 5px;}/*第一个&lt;span&gt;*/#pointsDiv .on {background: orangered;}/*切换图标&lt;a&gt;*/.arrow {  cursor: pointer;  display: none;  line-height: 39px;  text-align: center;  font-size: 36px;  font-weight: bold;  width: 40px;  height: 40px;  position: absolute;  z-index: 2;  top: 180px;  background-color: RGBA(0, 0, 0, 0.3);  color: #fff;}/*鼠标移到切换图标上时*/.arrow:hover {  background-color: RGBA(0, 0, 0, 0.7);}/*鼠标移到整个div区域时*/#container:hover .arrow {display: block; /*显示*/}/*上一个切换图标的左外边距*/#prev {left: 20px;}/*下一个切换图标的右外边距*/#next {right: 20px;}&lt;/style&gt;&lt;div id=&quot;container&quot;&gt;  &lt;div id=&quot;list&quot; style=&quot;left: -600px;&quot;&gt;    &lt;img src=&quot;img/5.jpg&quot; alt=&quot;5&quot;/&gt;    &lt;img src=&quot;img/1.jpg&quot; alt=&quot;1&quot;/&gt;    &lt;img src=&quot;img/2.jpg&quot; alt=&quot;2&quot;/&gt;    &lt;img src=&quot;img/3.jpg&quot; alt=&quot;3&quot;/&gt;    &lt;img src=&quot;img/4.jpg&quot; alt=&quot;4&quot;/&gt;    &lt;img src=&quot;img/5.jpg&quot; alt=&quot;5&quot;/&gt;    &lt;img src=&quot;img/1.jpg&quot; alt=&quot;1&quot;/&gt;  &lt;/div&gt;  &lt;div id=&quot;pointsDiv&quot;&gt;    &lt;span index=&quot;1&quot; class=&quot;on&quot;&gt;&lt;/span&gt;    &lt;span index=&quot;2&quot;&gt;&lt;/span&gt;    &lt;span index=&quot;3&quot;&gt;&lt;/span&gt;    &lt;span index=&quot;4&quot;&gt;&lt;/span&gt;    &lt;span index=&quot;5&quot;&gt;&lt;/span&gt;  &lt;/div&gt;  &lt;a href=&quot;javascript:;&quot; id=&quot;prev&quot; class=&quot;arrow&quot;&gt;&amp;lt;&lt;/a&gt;  &lt;a href=&quot;javascript:;&quot; id=&quot;next&quot; class=&quot;arrow&quot;&gt;&amp;gt;&lt;/a&gt;&lt;/div&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;jquery-1.10.1.js&quot;&gt;&lt;/script&gt;&lt;script&gt;  /* 功能说明: 1. 点击向右(左)的图标, 平滑切换到下(上)一页 2. 无限循环切换: 第一页的上一页为最后页, 最后一页的下一页是第一页 3. 每隔3s自动滑动到下一页 4. 当鼠标进入图片区域时, 自动切换停止, 当鼠标离开后,又开始自动切换 5. 切换页面时, 下面的圆点也同步更新 6. 点击圆点图标切换到对应的页 bug: 快速点击时, 翻页不正常 */$(function () {  var $container = $(&#39;#container&#39;)  var $list = $(&#39;#list&#39;)  var $points = $(&#39;#pointsDiv&gt;span&#39;)  var $prev = $(&#39;#prev&#39;)  var $next = $(&#39;#next&#39;)  var PAGE_WIDTH = 600 //一页的宽度  var TIME = 400 // 翻页的持续时间  var ITEM_TIME = 20 // 单元移动的间隔时间  var imgCount = $points.length  var index = 0 //当前下标  var moving = false // 标识是否正在翻页(默认没有)  // 1. 点击向右(左)的图标, 平滑切换到下(上)一页  $next.click(function () {    // 平滑翻到下一页    nextPage(true)  })  $prev.click(function () {    // 平滑翻到上一页    nextPage(false)  })  // 3. 每隔3s自动滑动到下一页  var intervalId = setInterval(function () {    nextPage(true)  }, 1000)  // 4. 当鼠标进入图片区域时, 自动切换停止, 当鼠标离开后,又开始自动切换  $container.hover(function () {    // 清除定时器    clearInterval(intervalId)  }, function () {    intervalId = setInterval(function () {      nextPage(true)    }, 1000)  })  // 6. 点击圆点图标切换到对应的页  $points.click(function () {    // 目标页的下标    var targetIndex = $(this).index()    // 只有当点击的不是当前页的圆点时才翻页    if(targetIndex!=index) {      nextPage(targetIndex)    }  })  /**   * 平滑翻页   * @param next   * true: 下一页   * false: 上一页   * 数值: 指定下标页   */  function nextPage (next) {    /*      总的时间: TIME=400      单元移动的间隔时间: ITEM_TIME = 20      总的偏移量: offset      单元移动的偏移量: itemOffset = offset/(TIME/ITEM_TIME)      启动循环定时器不断更新$list的left, 到达目标处停止停止定时器     */    //如果正在翻页, 直接结束    if(moving) { //已经正在翻页中      return    }    moving = true // 标识正在翻页    // 总的偏移量: offset    var offset = 0    // 计算offset    if(typeof next===&#39;boolean&#39;) {      offset = next ? -PAGE_WIDTH : PAGE_WIDTH    } else {      offset = -(next-index)* PAGE_WIDTH    }    // 计算单元移动的偏移量: itemOffset    var itemOffset = offset/(TIME/ITEM_TIME)    // 得到当前的left值    var currLeft = $list.position().left    // 计算出目标处的left值    var targetLeft = currLeft + offset    // 启动循环定时器不断更新$list的left, 到达目标处停止停止定时器    var intervalId = setInterval(function () {      // 计算出最新的currLeft      currLeft += itemOffset      if(currLeft===targetLeft) { // 到达目标位置        // 清除定时器        clearInterval(intervalId)        // 标识翻页停止        moving = false        // 如果到达了最右边的图片(1.jpg), 跳转到最左边的第2张图片(1.jpg)        if(currLeft===-(imgCount+1) * PAGE_WIDTH) {          currLeft = -PAGE_WIDTH        } else if(currLeft===0){          // 如果到达了最左边的图片(5.jpg), 跳转到最右边的第2张图片(5.jpg)          currLeft = -imgCount * PAGE_WIDTH        }      }      // 设置left      $list.css(&#39;left&#39;, currLeft)    }, ITEM_TIME)    // 更新圆点    updatePoints(next)  }  /**   * 更新圆点   * @param next   */  function updatePoints (next) {    // 计算出目标圆点的下标targetIndex    var targetIndex = 0    if(typeof next === &#39;boolean&#39;) {      if(next) {        targetIndex = index + 1   // [0, imgCount-1]        if(targetIndex===imgCount) {// 此时看到的是1.jpg--&gt;第1个圆点          targetIndex = 0        }      } else {        targetIndex = index - 1        if(targetIndex===-1) { // 此时看到的是5.jpg--&gt;第5个圆点          targetIndex = imgCount-1        }      }    } else {      targetIndex = next    }    // 将当前index的&lt;span&gt;的class移除    // $points.eq(index).removeClass(&#39;on&#39;)    $points[index].className = &#39;&#39;    // 给目标圆点添加class=&#39;on&#39;    // $points.eq(targetIndex).addClass(&#39;on&#39;)    $points[targetIndex].className = &#39;on&#39;    // 将index更新为targetIndex    index = targetIndex  }})&lt;/script&gt;</code></pre><p>jQuery文档的结构图</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815175515.png" alt="jQuery文档结构图"></p><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="笔记" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="前端" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="前端" scheme="https://wallleap.cn/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="web" scheme="https://wallleap.cn/tags/web/"/>
    
      <category term="JavaScript" scheme="https://wallleap.cn/tags/JavaScript/"/>
    
      <category term="jQuery" scheme="https://wallleap.cn/tags/jQuery/"/>
    
  </entry>
  
  <entry>
    <title>JavaScript高级学习笔记</title>
    <link href="https://wallleap.cn/2020/04/15/JavaScriptAdvance/"/>
    <id>https://wallleap.cn/2020/04/15/JavaScriptAdvance/</id>
    <published>2020-04-15T11:33:14.000Z</published>
    <updated>2025-01-24T09:22:15.348Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><ul><li><p>基础总结</p></li><li><p>函数高级</p></li><li><p>面向对象高级</p></li><li><p>线程机制与事件机制</p></li></ul><h2><span id="一-基础总结">一、基础总结</span></h2><h3><span id="1-数据类型">1、数据类型</span></h3><p><strong>分类</strong></p><ul><li><p>基本（值）类型</p><ul><li>String：任意字符串</li><li>Number：任意的数字</li><li>boolean：true/false</li><li>undefined：undefined</li><li>null：null</li></ul></li><li><p>对象（引用）类型</p><ul><li>Object：任意对象</li><li>Function：一种特殊的对象（可以执行）</li><li>Array：一种特殊的对象（数值下标，内部数据是有序的）</li></ul></li></ul><pre><code class="javascript">var obj = {  name: &#39;TOM&#39;,  age: 12}fuction test(){  var a = 3}var arr = [3, &#39;abc&#39;]arr[1]</code></pre><p><strong>判断</strong></p><ul><li><p><code>typeof</code></p><ul><li>可以判断：undefined/ 数值 /字符串/布尔值/function</li><li>不能判断：null与Object Object与array</li></ul></li><li><p><code>instanceof</code>：判断对象的具体类型</p></li><li><p><code>===</code>可以判断：undefined/null/function</p></li><li><p><code>isNaN</code> 判断NaN ……</p></li></ul><pre><code class="javascript">//1. 基本// typeof返回数据类型的字符串表达var a// undefinedconsole.log(a, typeof a, typeof a===&#39;undefined&#39;,a===undefined )  // undefined &#39;undefined&#39; true trueconsole.log(undefined===&#39;undefined&#39;) // false// Number Sting Booleana = 4console.log(typeof a===&#39;number&#39;) // truea = &quot;abc123&quot;console.log(isNaN(a)) // truea = &#39;string iiii&#39;console.log(typeof a===&#39;string&#39;) // truea = trueconsole.log(typeof a===&#39;boolean&#39;) // true// nulla = nullconsole.log(typeof a, a===null) // &#39;object&#39; true//2. 对象var b1 = {    b2: [1, &#39;abc&#39;, console.log],    b3: function () {        console.log(&#39;b3&#39;) // b3        return function () {            return &#39;funb1&#39;        }    }}// Object Array Functionconsole.log(b1 instanceof Object, b1 instanceof Array) // true  falseconsole.log(b1.b2 instanceof Array, b1.b2 instanceof Object) // true trueconsole.log(b1.b3 instanceof Function, b1.b3 instanceof Object) // true trueconsole.log(typeof b1.b2) // &#39;object&#39;// Functionconsole.log(typeof b1.b3===&#39;function&#39;) // trueconsole.log(typeof b1.b2[2]===&#39;function&#39;) // true// 细节问题b1.b2[2](4) // 4console.log(b1.b3()()) // funb1/*b1.b3() ---&gt; b3当前的functionb1.b3()() ---&gt; return的function*/</code></pre><p><strong>实例</strong></p><p>实例：实例对象</p><p>类型：类型对象</p><pre><code class="javascript">function Person(name, age){ // 构造函数 类型    this.name = name  this.age = age} var p = new Person(&#39;Tom&#39;, 12) // 根据类型创建的实例对象</code></pre><p><strong>其他问题</strong></p><ol><li><p>undefined与null的区别?</p><ul><li>undefined代表定义未赋值</li><li>nulll定义并赋值了, 只是值为null</li></ul><pre><code class="javascript">var aconsole.log(a)  // undefineda = nullconsole.log(a) // null</code></pre></li></ol><ol start="2"><li><p>什么时候给变量赋值为null呢?</p><ul><li>初始赋值, 表明将要赋值为对象</li><li>结束前, 让对象成为垃圾对象(被垃圾回收器回收)</li></ul><pre><code class="javascript">//起始var b = null  // 初始赋值为null, 表明将要赋值为对象//确定对象就赋值b = [&#39;atguigu&#39;, 12]//最后b = null // 让b指向的对象成为垃圾对象(被垃圾回收器回收)// b = 2</code></pre></li></ol><ol start="3"><li><p>严格区别变量类型与数据类型?</p><ul><li><p>数据的类型</p><ul><li>基本类型 </li><li>对象类型 </li></ul></li><li><p>变量的类型(变量内存值的类型)</p><ul><li>基本类型: 保存就是基本类型的数据</li><li>引用类型: 保存的是地址值</li></ul><pre><code class="javascript">var c = function () {}console.log(typeof c) // &#39;function&#39;</code></pre></li></ul></li></ol><h3><span id="2-数据-变量与内存">2、数据、变量与内存</span></h3><ol><li>什么是数据?</li></ol><ul><li><p>存储在内存中代表特定信息的’东东’, 本质上是0101…</p></li><li><p>数据的特点: 可传递, 可运算</p></li><li><p>一切皆数据</p></li><li><p>内存中所有操作的目标: 数据</p><ul><li>算术运算</li><li>逻辑运算</li><li>赋值</li><li>运行函数</li></ul></li></ul><ol start="2"><li>什么是内存?</li></ol><ul><li><p>内存条通电后产生的可储存数据的空间(临时的)</p></li><li><p>内存产生和死亡: 内存条(电路版)—&gt;通电—&gt;产生内存空间—&gt;存储数据—&gt;处理数据—&gt;断电—&gt;内存空间和数据都消失</p></li><li><p>一块小内存的2个数据</p><ul><li>内部存储的数据</li><li>地址值</li></ul></li><li><p>内存分类</p><ul><li>栈: 全局变量/局部变量</li><li>堆: 对象</li></ul></li></ul><ol start="3"><li>什么是变量?</li></ol><ul><li>可变化的量, 由变量名和变量值组成</li></ul><ul><li>每个变量都对应的一块小内存, 变量名用来查找对应的内存, 变量值就是内存中保存的数据</li></ul><ol start="4"><li>内存,数据, 变量三者之间的关系</li></ol><ul><li><p>内存用来存储数据的空间</p></li><li><p>变量是内存的标识</p></li></ul><pre><code class="javascript">var age = 18console.log(age)var obj = {name: &#39;Tom&#39;}console.log(obj.name)function fn () {  var obj = {name: &#39;Tom&#39;}}var a = 3var b = a + 2</code></pre><ol start="5"><li>关于赋值与内存的问题</li></ol><p>问题: var a = xxx, a内存中到底保存的是什么?</p><ul><li><p>xxx是<strong>基本数据</strong>, 保存的就是这个<strong>数据</strong></p></li><li><p>xxx是<strong>对象</strong>, 保存的是对象的<strong>地址值</strong></p></li><li><p>xxx是一个<strong>变量</strong>, 保存的xxx的<strong>内存内容(可能是基本数据, 也可能是地址值)</strong></p></li></ul><pre><code class="javascript">var a = 3a = function () {}var b = &#39;abc&#39;a = bb = {}a = b</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815100154.png" alt="image-20200815100153040"></p><ol start="6"><li>关于引用变量赋值问题</li></ol><ul><li><p>2个引用变量指向同一个对象, 通过一个变量修改对象内部数据, 另一个变量看到的是修改之后的数据</p></li><li><p>2个引用变量指向同一个对象, 让其中一个引用变量指向另一个对象, 另一引用变量依然指向前一个对象</p></li></ul><pre><code class="javascript">var obj1 = {name: &#39;Tom&#39;}var obj2 = obj1obj2.age = 12console.log(obj1.age)  // 12function fn (obj) {  obj.name = &#39;A&#39;}fn(obj1)console.log(obj2.name) //Avar a = {age: 12}var b = aa = {name: &#39;BOB&#39;, age: 13}b.age = 14console.log(b.age, a.name, a.age) // 14 Bob 13function fn2 (obj) {  obj = {age: 15}}fn2(a)console.log(a.age) // 13</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815101835.png" alt="image-20200815101834022"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815102623.png" alt="image-20200815102622137"></p><ol start="7"><li>关于数据传递问题</li></ol><p>问题: 在js调用函数时传递变量参数时, 是值传递还是引用传递</p><ul><li><p>理解1: 都是值(基本/地址值)传递</p></li><li><p>理解2: 可能是值传递, 也可能是引用传递(地址值)</p></li></ul><pre><code class="javascript">var a = 3function fn (a) {  a = a +1}fn(a)console.log(a)  // 3function fn2 (obj) {  console.log(obj.name)}var obj = {name: &#39;Tom&#39;}fn2(obj)  // Tom</code></pre><ol start="8"><li>JS引擎如何管理内存</li></ol><p>问题: JS引擎如何管理内存?</p><ul><li>内存生命周期<ul><li>分配小内存空间, 得到它的使用权</li><li>存储数据, 可以反复进行操作</li><li>释放小内存空间</li></ul></li></ul><ul><li>释放内存<ul><li>局部变量: 函数执行完自动释放</li><li>对象: 成为垃圾对象==&gt;垃圾回收器回收</li></ul></li></ul><pre><code class="javascript">var a = 3var obj = {}obj = undefinedfunction fn () {  var b = {}}fn() // b是自动释放, b所指向的对象是在后面的某个时刻由垃圾回收器回收</code></pre><h3><span id="3-对象">3、对象</span></h3><ol><li>什么是对象?</li></ol><ul><li><p>多个数据的封装体</p></li><li><p>用来保存多个数据的容器</p></li><li><p>一个对象代表现实中的一个事物</p></li></ul><ol start="2"><li>为什么要用对象?</li></ol><ul><li>统一管理多个数据</li></ul><ol start="3"><li>对象的组成</li></ol><ul><li><p>属性: 属性名(字符串)和属性值(任意)组成</p></li><li><p>方法: 一种特别的属性(属性值是函数)</p></li></ul><ol start="4"><li>如何访问对象内部数据?</li></ol><ul><li><p><code>.属性名</code>: 编码简单, 有时不能用</p></li><li><p><code>[&#39;属性名&#39;]</code>: 编码麻烦, 能通用</p></li></ul><pre><code class="javascript">var p = {  name: &#39;Tom&#39;,  age: 12,  setName: function (name) {    this.name = name  },  setAge: function (age) {    this.age = age  }}p.setName(&#39;Bob&#39;)p[&#39;setAge&#39;](23)console.log(p.name, p[&#39;age&#39;])</code></pre><p>问题: 什么时候必须使用[‘属性名’]的方式?</p><ol><li><p>属性名包含特殊字符: <code>-</code>空格</p></li><li><p>属性名不确定</p></li></ol><pre><code class="javascript">var p = {}//1. 给p对象添加一个属性: content type: text/json// p.content-type = &#39;text/json&#39; //不能用p[&#39;content-type&#39;] = &#39;text/json&#39;console.log(p[&#39;content-type&#39;])//2. 属性名不确定var propName = &#39;myAge&#39;var value = 18// p.propName = value //不能用p[propName] = valueconsole.log(p[propName])</code></pre><h3><span id="4-函数">4、函数</span></h3><ol><li>什么是函数?</li></ol><ul><li>实现特定功能的n条语句的封装体</li><li>只有函数是可以执行的, 其它类型的数据不能执行</li></ul><ol start="2"><li>为什么要用函数?</li></ol><ul><li>提高代码复用</li><li>便于阅读交流</li></ul><ol start="3"><li>如何定义函数?</li></ol><ul><li>函数声明</li><li>表达式</li></ul><ol start="4"><li>如何调用(执行)函数?</li></ol><ul><li>test(): <strong>直接调用</strong></li><li>obj.test(): <strong>通过对象调用</strong></li><li>new test(): <strong>new调用</strong></li><li>test.call/apply(obj): <strong>临时让test成为obj的方法进行调用</strong></li></ul><pre><code class="javascript">/*编写程序实现以下功能需求:  1. 根据年龄输出对应的信息  2. 如果小于18, 输出: 未成年, 再等等!  3. 如果大于60, 输出: 算了吧!  4. 其它, 输出: 刚好!*/function showInfo (age) {  if(age&lt;18) {    console.log(&#39;未成年, 再等等!&#39;)  } else if(age&gt;60) {    console.log(&#39;算了吧!&#39;)  } else {    console.log(&#39;刚好!&#39;)  }}showInfo(17)showInfo(20)showInfo(65)function fn1 () { //函数声明  console.log(&#39;fn1()&#39;)}var fn2 = function () { //表达式  console.log(&#39;fn2()&#39;)}fn1()fn2()var obj = {}function test2 () {  this.xxx = &#39;atguigu&#39;}// obj.test2()  不能直接, 根本就没有test2.call(obj) // obj.test2()   // 可以让一个函数成为指定任意对象的方法进行调用console.log(obj.xxx)</code></pre><h3><span id="5-回调函数">5、回调函数</span></h3><ol><li><p>什么函数才是回调函数?<br> 1). 你定义的<br> 2). 你没有调<br> 3). 但最终它执行了(在某个时刻或某个条件下)</p></li><li><p>常见的回调函数?</p><ul><li><p>dom事件回调函数 ==&gt;发生事件的dom元素</p></li><li><p>定时器回调函数 ===&gt;window</p></li><li><p>ajax请求回调函数(后面讲)</p></li><li><p>生命周期回调函数(后面讲)</p></li></ul></li></ol><pre><code class="html">&lt;button id=&quot;btn&quot;&gt;测试点击事件&lt;/button&gt;&lt;script type=&quot;text/javascript&quot;&gt;  document.getElementById(&#39;btn&#39;).onclick = function () { // dom事件回调函数    alert(this.innerHTML)  }  //定时器    // 超时定时器    // 循环定时器  setTimeout(function () { // 定时器回调函数    alert(&#39;到点了&#39;+this)  }, 2000)  /*var a = 3  alert(window.a)  window.b = 4  alert(b)*/&lt;/script&gt;</code></pre><h3><span id="6-iife">6、IIFE</span></h3><ol><li>理解<ul><li>全称: Immediately-Invoked Function Expression（立即执行函数）</li></ul></li><li>作用<ul><li>隐藏实现</li><li>不会污染外部(全局)命名空间</li><li>用它来编码js模块</li></ul></li></ol><pre><code class="javascript">(function () { //匿名函数自调用  var a = 3  console.log(a + 3)})()var a = 4console.log(a);(function () {  var a = 1  function test () {    console.log(++a)  }  window.$ = function () { // 向外暴露一个全局函数    return {      test: test    }  }})()$().test() // 1.</code></pre><h3><span id="7-函数中的this">7、函数中的this</span></h3><ol><li>this是什么?<ul><li>任何函数本质上都是通过某个对象来调用的,如果没有直接指定就是window</li><li>所有函数内部都有一个变量this</li><li>它的值是调用函数的当前对象</li></ul></li><li>如何确定this的值?<ul><li>test(): window</li><li>p.test(): p</li><li>new test(): 新创建的对象</li><li>p.call(obj): obj</li></ul></li></ol><pre><code class="javascript">function Person(color) {  console.log(this)  this.color = color;  this.getColor = function () {    console.log(this)    return this.color;  };  this.setColor = function (color) {    console.log(this)    this.color = color;  };}Person(&quot;red&quot;); //this是谁? windowvar p = new Person(&quot;yello&quot;); //this是谁? pp.getColor(); //this是谁? pvar obj = {};p.setColor.call(obj, &quot;black&quot;); //this是谁? objvar test = p.setColor;test(); //this是谁? windowfunction fun1() {  function fun2() {    console.log(this);  }  fun2(); //this是谁? window}fun1();</code></pre><h2><span id="二-函数高级">二、函数高级</span></h2><p>超级重点，两大神兽：==原型和闭包==</p><h3><span id="1-原型和原型链">1、原型和原型链</span></h3><p>(1) 原型</p><ol><li>函数的prototype属性(图)<ul><li>每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)</li><li>原型对象中有一个属性constructor, 它指向函数对象</li></ul></li></ol><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815113737.png" alt="image-20200815113735029"></p><ol start="2"><li>给原型对象添加属性(一般都是方法)<ul><li>作用: 函数的所有实例对象自动拥有原型中的属性(方法)</li></ul></li></ol><pre><code class="javascript">// 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)console.log(Date.prototype, typeof Date.prototype)function Fun () {//alt + shift +r(重命名rename)}console.log(Fun.prototype)  // 默认指向一个Object空对象(没有我们的属性)// 原型对象中有一个属性constructor, 它指向函数对象console.log(Date.prototype.constructor===Date)console.log(Fun.prototype.constructor===Fun)//给原型对象添加属性(一般是方法) ===&gt;实例对象可以访问Fun.prototype.test = function () {  console.log(&#39;test()&#39;)}var fun = new Fun()fun.test()</code></pre><p>(2) 显式原型与隐式原型</p><ol><li>每个函数function都有一个prototype，即显式原型(属性)</li><li>每个实例对象都有一个<strong>proto</strong>，可称为隐式原型(属性)</li><li>对象的隐式原型的值为其对应构造函数的显式原型的值</li><li>内存结构(图)</li><li>总结:<ul><li>函数的<code>prototype</code>属性: 在定义函数时自动添加的, 默认值是一个空Object对象</li><li>对象的<code>__proto__</code>属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值</li><li>程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)</li></ul></li></ol><pre><code class="javascript">//定义构造函数function Fn() {   // 内部语句: this.prototype = {}}// 1. 每个函数function都有一个prototype，即显式原型属性, 默认指向一个空的Object对象console.log(Fn.prototype)// 2. 每个实例对象都有一个__proto__，可称为隐式原型//创建实例对象var fn = new Fn()  // 内部语句: this.__proto__ = Fn.prototypeconsole.log(fn.__proto__)// 3. 对象的隐式原型的值为其对应构造函数的显式原型的值console.log(Fn.prototype===fn.__proto__) // true//给原型添加方法Fn.prototype.test = function () {  console.log(&#39;test()&#39;)}//通过实例调用原型的方法fn.test()</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815115018.png" alt="image-20200815115016550"></p><p>(3) 原型链</p><ol><li>原型链(图解)<ul><li>访问一个对象的属性时，<ul><li>先在自身属性中查找，找到返回</li><li>如果没有, 再沿着<code>__proto__</code>这条链向上查找, 找到返回</li><li>如果最终没找到, 返回undefined</li></ul></li><li>别名: 隐式原型链</li><li>作用: 查找对象的属性(方法)</li></ul></li></ol><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815115136.png" alt="image-20200815115135158"></p><ol start="2"><li>构造函数/原型/实体对象的关系(图解)</li></ol><pre><code class="javascript">var o1 = new Object();var o2 = {};</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815115415.png" alt="image-20200815115413214"></p><ol start="3"><li>构造函数/原型/实体对象的关系2(图解)</li></ol><pre><code class="javascript">function Foo(){  }// var Foo = new Function()// Function = new Function()// 所有函数的__proto__都是一样的</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815115508.png" alt="image-20200815115505581"></p><pre><code class="javascript">// console.log(Object)//console.log(Object.prototype)console.log(Object.prototype.__proto__)function Fn() {  this.test1 = function () {    console.log(&#39;test1()&#39;)  }}console.log(Fn.prototype)Fn.prototype.test2 = function () {  console.log(&#39;test2()&#39;)}var fn = new Fn()fn.test1()fn.test2()console.log(fn.toString())console.log(fn.test3)// fn.test3()/*1. 函数的显示原型指向的对象默认是空Object实例对象(但Object不满足)  */console.log(Fn.prototype instanceof Object) // trueconsole.log(Object.prototype instanceof Object) // falseconsole.log(Function.prototype instanceof Object) // true/*2. 所有函数都是Function的实例(包含Function)*/console.log(Function.__proto__===Function.prototype)/*3. Object的原型对象是原型链尽头  */console.log(Object.prototype.__proto__) // null</code></pre><ol start="4"><li>原型继承</li></ol><ul><li>构造函数的实例对象自动拥有构造函数原型对象的属性(方法)</li><li>利用的就是原型链</li></ul><ol start="5"><li>原型属性问题</li></ol><ul><li>读取对象的属性值时: 会自动到原型链中查找</li><li>设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值</li><li>方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上</li></ul><pre><code class="javascript">function Fn() {}Fn.prototype.a = &#39;xxx&#39;var fn1 = new Fn()console.log(fn1.a, fn1)var fn2 = new Fn()fn2.a = &#39;yyy&#39;console.log(fn1.a, fn2.a, fn2)function Person(name, age) {  this.name = name  this.age = age}Person.prototype.setName = function (name) {  this.name = name}var p1 = new Person(&#39;Tom&#39;, 12)p1.setName(&#39;Bob&#39;)console.log(p1)var p2 = new Person(&#39;Jack&#39;, 12)p2.setName(&#39;Cat&#39;)console.log(p2)console.log(p1.__proto__===p2.__proto__) // true</code></pre><p>(4) 探索instanceof</p><ol><li>instanceof是如何判断的?<ul><li>表达式: A instanceof B</li><li>如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false</li></ul></li><li>Function是通过new自己产生的实例</li></ol><p><em>案例1</em></p><pre><code class="javascript">function Foo() {  }var f1 = new Foo()console.log(f1 instanceof Foo) // trueconsole.log(f1 instanceof Object) // true</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815120818.png" alt="image-20200815120816214"></p><p><em>案例2</em></p><pre><code class="javascript">console.log(Object instanceof Function) // trueconsole.log(Object instanceof Object) // trueconsole.log(Function instanceof Function) // trueconsole.log(Function instanceof Object) // truefunction Foo() {}console.log(Object instanceof  Foo) // false</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815120915.png" alt="image-20200815120913828"></p><p>(5) 面试题</p><pre><code class="javascript">/*测试题1  */function A () {}A.prototype.n = 1var b = new A()A.prototype = {  n: 2,  m: 3}var c = new A()console.log(b.n, b.m, c.n, c.m) // 1 undefined 2 3</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815122322.png" alt="image-20200815122319966"></p><pre><code class="javascript">/*  测试题2  */function F (){}Object.prototype.a = function(){  console.log(&#39;a()&#39;)}Function.prototype.b = function(){  console.log(&#39;b()&#39;)}var f = new F()f.a() // a()// f.b() // Uncaught TypeError: f.b is not a functionF.a() // a()F.b() // b()console.log(f) // F {}console.log(Object.prototype) // {a: ƒ, constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, …}console.log(Function.prototype) // ƒ () { [native code] }</code></pre><h3><span id="2-执行上下文与执行上下文栈">2、执行上下文与执行上下文栈</span></h3><p>(1) 变量提升与函数提升</p><ol><li>变量声明提升<ul><li>通过<strong>var定义(声明)的变量</strong>, 在定义语句之前就可以访问到</li><li>值: undefined</li></ul></li><li>函数声明提升<ul><li>通过<strong>function声明的函数</strong>, 在之前就可以直接调用</li><li>值: 函数定义(对象)</li></ul></li><li>问题: 变量提升和函数提升是如何产生的?<ul><li>在js中js引擎会优先解析var变量和function定义！在预解析完成后从上到下逐步进行！</li><li>解析var变量时，会把值存储在“执行环境”中，而不会去赋值，值是存储作用！例如:<br>alert（a）; var a = 2; 这时会输出undifiend,意思是没有被初始化没有被赋值!<br>这并不是没有被定义、错误了的意思！</li><li>在解析function时会把函数整体定义，这也就解释了为什么在function定义函数时为什么可以先调用后声明了！其实表面上看是先调用了，其实在内部机制中第一步实行的是把以function方式定义的函数先声明了（预处理）</li></ul></li></ol><pre><code class="javascript">/*面试题 : 输出 undefined  */var a = 3function fn () {  console.log(a)  var a = 4}fn() // undefinedconsole.log(b) //undefined  变量提升fn2() //可调用  函数提升 fn2()// fn3() //不能  变量提升 Uncaught TypeError: fn3 is not a functionvar b = 3function fn2() {  console.log(&#39;fn2()&#39;)}var fn3 = function () {  console.log(&#39;fn3()&#39;)}</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815122405.png" alt="image-20200815122404360"></p><p>(2) 执行上下文</p><ol><li>代码分类(位置)<ul><li>全局代码</li><li>函数(局部)代码</li></ul></li><li>全局执行上下文<ul><li>在执行全局代码前将window确定为全局执行上下文</li><li>对全局数据进行预处理<ul><li>var定义的全局变量==&gt;undefined, 添加为window的属性</li><li>function声明的全局函数==&gt;赋值(fun), 添加为window的方法</li><li>this==&gt;赋值(window)</li></ul></li><li>开始执行全局代码<pre><code class="javascript">// 全局声明的变量和函数都会在window中：window.a1、window.a2()console.log(a1, window.a1) // undefined undefinedwindow.a2() // a2() console.log(this) // Window</code></pre></li></ul></li></ol><p>var a1 = 3 // 在这声明，事实上调到上面声明了，这里赋值(因此上面能访问)<br>function a2() { // 在这里声明，能够在上面调用<br>  console.log(‘a2()’)<br>}<br>console.log(a1) // 3</p><pre><code>3. 函数执行上下文  * 在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象(虚拟的, 存在于栈中)  * 对局部数据进行预处理    * 形参变量——&gt;赋值(实参)——&gt;添加为执行上下文的属性    * arguments==&gt;赋值(实参列表), 添加为执行上下文的属性    * var定义的局部变量==&gt;undefined, 添加为执行上下文的属性    * function声明的函数 ==&gt;赋值(fun), 添加为执行上下文的方法    * this==&gt;赋值(调用函数的对象)  * 开始执行函数体代码![image-20200815123117687](https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815123118.png)(3) 执行上下文栈1. 在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象2. 在全局执行上下文(window)确定后, 将其添加到栈中(压栈)3. 在函数执行上下文创建后, 将其添加到栈中(压栈)4. 在当前函数执行完后,将栈顶的对象移除(出栈)5. 当所有的代码执行完后, 栈中只剩下window```javascriptvar a = 10var bar = function (x) {  var b = 5  foo(x + b)}var foo = function (y) {  var c = 5  console.log(a + c + y)}bar(10)// bar(10)</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815123328.png" alt="image-20200815123327077"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815123402.png" alt="image-20200815123401244"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815123416.png" alt="image-20200815123414448"></p><pre><code class="javascript">console.log(&#39;gb: &#39;+ i)var i = 1foo(1)function foo(i) {  if (i == 4) {    return  }  console.log(&#39;fb:&#39; + i)  foo(i + 1) //递归调用: 在函数内部调用自己  console.log(&#39;fe:&#39; + i)}console.log(&#39;ge: &#39; + i)/*1. 依次输出什么?  gb: undefined  fb: 1  fb: 2  fb: 3  fe: 3  fe: 2  fe: 1  ge: 12. 整个过程中产生了几个执行上下文?  5   */</code></pre><p>(4) 面试题</p><pre><code class="javascript">/*  测试题1:  先执行变量提升, 再执行函数提升(先找var和function xxx(){})  */function a() {}var aconsole.log(typeof a) // function</code></pre><pre><code class="javascript">/*  测试题2: 先提出去，window中有b，且未赋值  */if (!(b in window)) {  var b = 1}console.log(b) // undefined</code></pre><pre><code class="javascript">/*  测试题3: 针对变量名同名或函数名同名的情况：如果声明了同名的函数其定义会被后者覆盖，声明了同名的变量其值也会被后者覆盖  */var c = 1function c(c) {  console.log(c)  var c = 3}console.log(c) // 1c(2) // 报错 Uncaught TypeError: c is not a function再看一个//声明阶段function x(){//函数声明    //console.log(5)此句会被下句代码覆盖    console.log(3)}var x;//变量声明，因为x已经声明过了，此处不进行声明（忽略）//执行阶段console.log(x) // ƒ x(){//函数声明//console.log(5);此句会被下句代码覆盖console.log(3);}console.log(x()) // 3x=1 x=100 //x的值被覆盖console.log(x) // 100console.log(x()) // Uncaught TypeError: x is not a function</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815125003.png" alt="image-20200815125002631"></p><h3><span id="3-作用域与作用域链">3、作用域与作用域链</span></h3><p>(1) 作用域</p><ol><li>理解<ul><li>就是一块”地盘”, 一个代码段所在的区域</li><li>它是静态的(相对于上下文对象), 在编写代码时就确定了</li></ul></li><li>分类<ul><li>全局作用域</li><li>函数作用域</li><li>没有块作用域(ES6有了)<pre><code class="javascript">/*  //没块作用域if(true) {var c = 3}console.log(c) // 3 有块作用域则报错*/</code></pre></li></ul></li></ol><ol start="3"><li>作用<ul><li>隔离变量，不同作用域下同名变量不会有冲突</li></ul></li></ol><p>例如把如下代码分割</p><pre><code class="javascript">var a = 10,  b = 20function fn(x) {  var a = 100,    c = 300;  console.log(&#39;fn()&#39;, a, b, c, x)   function bar(x) {    var a = 1000,      d = 400    console.log(&#39;bar()&#39;, a, b, c, d, x)  }  bar(100) // bar() 1000 20 300 400 100  bar(200) // bar() 1000 20 300 400 200}fn(10) // fn() 100 20 300 10</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815125424.png" alt="image-20200815125422438"></p><p>(2) 作用域与执行上下文</p><ol><li>区别1<ul><li>全局作用域之外，每个函数都会创建自己的作用域，作用域在函数定义时就已经确定了。而不是在函数调用时</li><li>全局执行上下文环境是在全局作用域确定之后, js代码马上执行之前创建</li><li>函数执行上下文是在调用函数时, 函数体代码执行之前创建</li></ul></li><li>区别2<ul><li>作用域是静态的, 只要函数定义好了就一直存在, 且不会再变化</li><li>执行上下文是动态的, 调用函数时创建, 函数调用结束时就会自动释放</li></ul></li><li>联系<ul><li>执行上下文(对象)是从属于所在的作用域</li><li>全局上下文环境==&gt;全局作用域</li><li>函数上下文环境==&gt;对应的函数使用域</li></ul></li></ol><p>还是上面那串代码</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815125637.png" alt="image-20200815125635951"></p><p>(3) 作用域链</p><ol><li>理解<ul><li>多个上下级关系的作用域形成的链, 它的方向是从下向上的(从内到外)</li><li>查找变量时就是沿着作用域链来查找的</li></ul></li><li>查找一个变量的查找规则<ul><li>在当前作用域下的执行上下文中查找对应的属性, 如果有直接返回, 否则进入2</li><li>在上一级作用域的执行上下文中查找对应的属性, 如果有直接返回, 否则进入3</li><li>再次执行2的相同操作, 直到全局作用域, 如果还找不到就抛出找不到的异常</li></ul></li></ol><pre><code class="javascript">var a = 1function fn1() {  var b = 2  function fn2() {    var c = 3    console.log(c) // 3    console.log(b) // 2    console.log(a) // 1    console.log(d) // Uncaught ReferenceError: d is not defined  }  fn2()}fn1()</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815125756.png" alt="image-20200815125755108"></p><p>(4) 面试题</p><pre><code class="javascript">var x = 10;function fn() {  console.log(x);}function show(f) {  var x = 20;  f();}show(fn); // 10</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815125904.png" alt="image-20200815125902910"></p><pre><code class="javascript">var fn = function () {  console.log(fn)}fn() // f(){console.log(fn)}var obj = {  fn2: function () {    console.log(fn2)    //console.log(this.fn2) // ƒ () {// console.log(fn2) console.log(this.fn2)}  }}obj.fn2() // Uncaught ReferenceError: fn2 is not defined</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815125936.png" alt="image-20200815125935489"></p><h3><span id="4-闭包">4、闭包</span></h3><p>(1) 引入实例</p><pre><code class="html">&lt;button&gt;测试1&lt;/button&gt;&lt;button&gt;测试2&lt;/button&gt;&lt;button&gt;测试3&lt;/button&gt;&lt;!--需求: 点击某个按钮, 提示&quot;点击的是第n个按钮&quot;--&gt;&lt;script type=&quot;text/javascript&quot;&gt;  var btns = document.getElementsByTagName(&#39;button&#39;)  //遍历加监听  /*// 无论点哪个都是 第4个 ——点击的时候循环已经执行完了  for (var i = 0,length=btns.length; i &lt; length; i++) {    var btn = btns[i]    btn.onclick = function () {      alert(&#39;第&#39;+(i+1)+&#39;个&#39;)    }  }*/  /*  for (var i = 0,length=btns.length; i &lt; length; i++) {    var btn = btns[i]    //将btn所对应的下标保存在btn上，利用这中方式可以实现    btn.index = i    btn.onclick = function () {      alert(&#39;第&#39;+(this.index+1)+&#39;个&#39;)    }  }*/  //利用闭包  for (var i = 0,length=btns.length; i &lt; length; i++) {    (function (j) {      var btn = btns[j]      btn.onclick = function () {        alert(&#39;第&#39;+(j+1)+&#39;个&#39;)      }    })(i)  }&lt;/script&gt;</code></pre><p>(2) 理解闭包</p><ol><li>如何产生闭包?<ul><li>当一个<strong>嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)</strong>时, 就产生了闭包</li></ul></li><li>闭包到底是什么?<ul><li>使用chrome调试查看</li><li>理解一: 闭包是嵌套的内部函数(绝大部分人)</li><li>理解二: 包含被引用变量(函数)的对象(极少数人)</li><li>注意: 闭包存在于嵌套的内部函数中</li></ul></li><li><strong><em>产生闭包的条件?</em></strong><ul><li>函数嵌套</li><li>内部函数引用了外部函数的数据(变量/函数)</li></ul></li></ol><pre><code class="javascript">function fn1 () {  var a = 2  var b = &#39;abc&#39;  function fn2 () { //执行函数定义就会产生闭包(不用调用内部函数)    console.log(a)  }  // fn2()}fn1()function fun1() {  var a = 3  var fun2 = function () { // 声明变量形式定义函数，不会产生闭包    console.log(a)  }}fun1()</code></pre><p>(3) 常见的闭包</p><ol><li>将函数作为另一个函数的返回值</li><li>将函数作为实参传递给另一个函数调用</li></ol><pre><code class="javascript">// 1. 将函数作为另一个函数的返回值function fn1() {  var a = 2  function fn2() {    a++    console.log(a)  }  return fn2}var f = fn1()f() // 3f() // 4/* f调用的是内部函数，fn1外部函数执行了一次，产生1个闭包 */// 2. 将函数作为实参传递给另一个函数调用function showDelay(msg, time) {  setTimeout(function () {    alert(msg)  }, time)}showDelay(&#39;test&#39;, 2000)</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815131913.png" alt="image-20200815131912058"></p><p>(4) 闭包的作用</p><ol><li>使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)</li><li>让函数外部可以操作(读写)到函数内部的数据(变量/函数)</li></ol><p><em>问题:</em></p><ul><li><p>函数执行完后, 函数内部声明的局部变量是否还存在? </p><p>一般是不存在, 存在于闭中的变量才可能存在</p></li><li><p>在函数外部能直接访问函数内部的局部变量吗? </p><p>不能, 但我们可以通过闭包让外部操作它</p></li></ul><pre><code class="javascript">function fn1() {  var a = 2  function fn2() {    a++    console.log(a)    // return a  }  function fn3() {    a--    console.log(a)  }  return fn3}var f = fn1()f() // 1f() // 0</code></pre><p>(5) 闭包的生命周期</p><ol><li>产生: 在嵌套内部函数<strong>定义</strong>执行完时就产生了(不是在调用)</li><li>死亡: 在嵌套的内部函数成为垃圾对象时</li></ol><pre><code>function fn1() {  //此时闭包就已经产生了(函数提升, 内部函数对象已经创建了)  var a = 2  function fn2 () {    a++    console.log(a)  }  return fn2}var f = fn1()f() // 3f() // 4f = null //闭包死亡(包含闭包的函数对象成为垃圾对象)</code></pre><p>(6) 闭包的应用: 自定义JS模块</p><p>模块：</p><ul><li>具有特定功能的js文件<ul><li>将所有的数据和功能都封装在一个函数内部(私有的)</li><li>只向外暴露一个包括n个方法的对象或函数</li><li>模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能</li></ul></li></ul><p>例如：</p><p>文件<code>myModule1.js</code>就是一个模块</p><pre><code class="javascript">function myModule() {  //私有数据  var msg = &#39;Hello This is Module1&#39;  //操作数据的函数  function doSomething() {    console.log(&#39;doSomething() &#39;+msg.toUpperCase())  }  function doOtherthing () {    console.log(&#39;doOtherthing() &#39;+msg.toLowerCase())  }  //向外暴露对象(给外部使用的方法)  return {    doSomething: doSomething,    doOtherthing: doOtherthing  }}</code></pre><p>在其他文件中引入</p><pre><code class="html">&lt;script type=&quot;text/javascript&quot; src=&quot;myModule.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  var module = myModule()  module.doSomething()  module.doOtherthing()&lt;/script&gt;</code></pre><p>下面这种更好用一点，可以不需要先执行那个函数（IIFE）</p><p><code>myModule2.js</code></p><pre><code class="javascript">(function () {  //私有数据  var msg = &#39;Hello This is Module1&#39;  //操作数据的函数  function doSomething() {    console.log(&#39;doSomething() &#39;+msg.toUpperCase())  }  function doOtherthing () {    console.log(&#39;doOtherthing() &#39;+msg.toLowerCase())  }  //向外暴露对象(给外部使用的方法)  window.myModule2 = {    doSomething: doSomething,    doOtherthing: doOtherthing  }})()</code></pre><p>引入使用</p><pre><code class="html">&lt;script type=&quot;text/javascript&quot; src=&quot;myModule2.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  myModule2.doSomething()  myModule2.doOtherthing()&lt;/script&gt;</code></pre><p>压缩代码时，会把变量改为a、b、c这种的，因此最好这样做</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815132854.png" alt="image-20200815132852872"></p><p>(7) 闭包的缺点及解决</p><ol><li>缺点<ul><li>函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长</li><li>容易造成内存泄露</li></ul></li><li>解决<ul><li>能不用闭包就不用</li><li>及时释放</li></ul></li></ol><pre><code class="javascript">function fn1() {  var arr = new Array[100000]  function fn2() {    console.log(arr.length)  }  return fn2}var f = fn1()f()f = null //让内部函数成为垃圾对象--&gt;回收闭包</code></pre><p>补充：内存溢出与内存泄露</p><ul><li>内存溢出<ul><li>一种程序运行出现的错误</li><li>当程序运行需要的内存超过了剩余的内存时，就会抛出内存溢出的错误</li></ul></li><li>内存泄露<ul><li>占用的内存没有及时释放</li><li>内存泄露积累多了就容易导致内存溢出</li><li>常见的内存泄露：<ul><li>意外的全局变量</li><li>没有及时清理的计时器或回调函数</li><li>闭包</li></ul></li></ul></li></ul><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815133732.png" alt="image-20200815133731332"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815133741.png" alt="image-20200815133739782"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815133756.png" alt="image-20200815133755479"></p><p>(8) 面试题</p><pre><code class="javascript">//代码片段一var name = &quot;The Window&quot;;var object = {  name : &quot;My Object&quot;,  getNameFunc : function(){    return function(){      return this.name;    };  }};alert(object.getNameFunc()());  // the window//代码片段二var name2 = &quot;The Window&quot;;var object2 = {  name2 : &quot;My Object&quot;,  getNameFunc : function(){    var that = this;    return function(){      return that.name2;    };  }};alert(object2.getNameFunc()()); // my object// 三function fun(n,o) {  console.log(o)  return {    fun:function(m){      return fun(m,n)    }  }}var a = fun(0)a.fun(1)a.fun(2)a.fun(3)//undefined,0,0,0var b = fun(0).fun(1).fun(2).fun(3)//undefined,0,1,2var c = fun(0).fun(1)c.fun(2)c.fun(3)//undefined,0,1,1</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815133831.png" alt="image-20200815133830697"></p><h2><span id="三-面向对象高级">三、面向对象高级</span></h2><h3><span id="1-对象创建模式">1、对象创建模式</span></h3><p>有如下五种方式：</p><p>(1) Object构造函数模式</p><ul><li>套路: 先创建空Object对象, 再动态添加属性/方法</li><li>适用场景: 起始时不确定对象内部数据</li><li>问题: 语句太多</li></ul><pre><code class="javascript">/*一个人: name:&quot;Tom&quot;, age: 12  */// 先创建空Object对象var p = new Object()p = {} //此时内部数据是不确定的// 再动态添加属性/方法p.name = &#39;Tom&#39;p.age = 12p.setName = function (name) {  this.name = name}//测试console.log(p.name, p.age)p.setName(&#39;Bob&#39;)console.log(p.name, p.age)</code></pre><p>(2) 对象字面量模式</p><ul><li>套路: 使用{}创建对象, 同时指定属性/方法<ul><li>适用场景: 起始时对象内部数据是确定的</li><li>问题: 如果创建多个对象, 有重复代码</li></ul></li></ul><pre><code class="javascript">var p = {  name: &#39;Tom&#39;,  age: 12,  setName: function (name) {    this.name = name  }}//测试console.log(p.name, p.age)p.setName(&#39;JACK&#39;)console.log(p.name, p.age)var p2 = {  //如果创建多个对象代码很重复  name: &#39;Bob&#39;,  age: 13,  setName: function (name) {    this.name = name  }}</code></pre><p>(3) <del>工厂模式</del></p><ul><li>套路: 通过工厂函数动态创建对象并返回<ul><li>适用场景: 需要创建多个对象</li><li>问题: 对象没有一个具体的类型, 都是Object类型</li></ul></li></ul><pre><code class="javascript">function createPerson(name, age) { //返回一个对象的函数===&gt;工厂函数  var obj = {    name: name,    age: age,    setName: function (name) {      this.name = name    }  }  return obj}// 创建2个人var p1 = createPerson(&#39;Tom&#39;, 12)var p2 = createPerson(&#39;Bob&#39;, 13)// p1/p2是Object类型function createStudent(name, price) {  var obj = {    name: name,    price: price  }  return obj}var s = createStudent(&#39;张三&#39;, 12000)// s也是Object</code></pre><p>(4) 自定义构造函数模式</p><ul><li>套路: 自定义构造函数, 通过new创建对象</li><li>适用场景: 需要创建多个类型确定的对象</li><li>问题: 每个对象都有相同的数据, 浪费内存</li></ul><pre><code class="javascript">//定义类型function Person(name, age) {  this.name = name  this.age = age  this.setName = function (name) {    this.name = name  }}var p1 = new Person(&#39;Tom&#39;, 12)p1.setName(&#39;Jack&#39;)console.log(p1.name, p1.age)console.log(p1 instanceof Person)function Student (name, price) {  this.name = name  this.price = price}var s = new Student(&#39;Bob&#39;, 13000)console.log(s instanceof Student)var p2 = new Person(&#39;JACK&#39;, 23)console.log(p1, p2)</code></pre><p>方法两者都有，且相同，不需要单独拥有，可放到原型中(方法一般放到原型中)——&gt;</p><p>(5) 构造函数+原型的组合模式</p><ul><li>套路: 自定义构造函数, 属性在函数中初始化, 方法添加到原型上</li><li>适用场景: 需要创建多个类型确定的对象</li></ul><pre><code class="javascript">function Person(name, age) { //在构造函数中只初始化一般函数  this.name = name  this.age = age}Person.prototype.setName = function (name) {  this.name = name}var p1 = new Person(&#39;Tom&#39;, 23)var p2 = new Person(&#39;Jack&#39;, 24)console.log(p1, p2)</code></pre><h3><span id="2-继承模式">2、继承模式</span></h3><p>继承，在js中有三种方式：</p><p>(1) 原型链继承</p><ol><li>套路<ul><li>定义父类型构造函数</li><li>给父类型的原型添加方法</li><li>定义子类型的构造函数</li><li>创建父类型的对象赋值给子类型的原型</li><li>将子类型原型的构造属性设置为子类型</li><li>给子类型原型添加方法</li><li>创建子类型的对象: 可以调用父类型的方法</li></ul></li><li>关键<ul><li>子类型的原型为父类型的一个实例对象</li></ul></li></ol><pre><code class="javascript">//父类型function Supper() {  this.supProp = &#39;Supper property&#39;}Supper.prototype.showSupperProp = function () {  console.log(this.supProp)}//子类型function Sub() {  this.subProp = &#39;Sub property&#39;}// 子类型的原型为父类型的一个实例对象Sub.prototype = new Supper()// 让子类型的原型的constructor指向子类型Sub.prototype.constructor = SubSub.prototype.showSubProp = function () {  console.log(this.subProp)}var sub = new Sub()sub.showSupperProp()// sub.toString()sub.showSubProp()console.log(sub)  // Sub</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815135207.png" alt="image-20200815135206553"></p><p>(2) 借用构造函数继承</p><ol><li>套路:<ul><li>定义父类型构造函数</li><li>定义子类型构造函数</li><li>在子类型构造函数中调用父类型构造</li></ul></li><li>关键:<ul><li>在子类型构造函数中通用call()调用父类型构造函数</li></ul></li></ol><pre><code class="javascript">function Person(name, age) {  this.name = name  this.age = age}function Student(name, age, price) {  Person.call(this, name, age)  // 相当于: this.Person(name, age)  /*this.name = name  this.age = age*/  this.price = price}var s = new Student(&#39;Tom&#39;, 20, 14000)console.log(s.name, s.age, s.price)</code></pre><p>(3) 组合继承</p><ol><li><p>利用原型链实现对父类型对象的方法继承</p></li><li><p>利用super()借用父类型构建函数初始化相同属性</p></li></ol><pre><code class="javascript">function Person(name, age) {  this.name = name  this.age = age}Person.prototype.setName = function (name) {  this.name = name}function Student(name, age, price) {  Person.call(this, name, age)  // 为了得到属性  this.price = price}Student.prototype = new Person() // 为了能看到父类型的方法Student.prototype.constructor = Student //修正constructor属性Student.prototype.setPrice = function (price) {  this.price = price}var s = new Student(&#39;Tom&#39;, 24, 15000)s.setName(&#39;Bob&#39;)s.setPrice(16000)console.log(s.name, s.age, s.price)</code></pre><h2><span id="四-线程机制与事件机制">四、线程机制与事件机制</span></h2><h3><span id="1-进程与线程">1、进程与线程</span></h3><p>(1) 进程(process)</p><ul><li>程序的一次执行, 它占有一片独有的内存空间</li><li>可以通过windows任务管理器查看进程</li></ul><p>(2) 线程(thread)</p><ul><li>是进程内的一个独立执行单元</li><li>是程序执行的一个完整流程</li><li>是CPU的最小的调度单元</li></ul><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815135743.png" alt="线程与进程"></p><p>应用程序必须运行在某个进程的某个线程上<br>一个进程中至少有一个运行的线程: 主线程,  进程启动后自动创建<br>一个进程中也可以同时运行多个线程, 我们会说程序是多线程运行的<br>一个进程内的数据可以供其中的多个线程直接共享<br>多个进程之间的数据是不能直接共享的<br>线程池(thread pool): 保存多个线程对象的容器, 实现线程对象的反复利用</p><p>(3) 相关问题</p><ol><li>何为多进程与多线程?</li></ol><ul><li>多进程运行: 一应用程序可以同时启动多个实例运行</li><li>多线程: 在一个进程内, 同时有多个线程运行</li></ul><ol start="2"><li>比较单线程与多线程?</li></ol><ul><li>多线程<ul><li>优点<ul><li>能有效提升CPU的利用率</li></ul></li><li>缺点<ul><li>创建多线程开销</li><li>线程间切换开销</li><li>死锁与状态同步问题</li></ul></li></ul></li><li>单线程<ul><li>优点<ul><li>顺序编程简单易懂</li></ul></li><li>缺点<ul><li>效率低</li></ul></li></ul></li></ul><ol start="3"><li>JS是单线程还是多线程?</li></ol><ul><li>js是单线程运行的，但使用H5中的 Web Workers可以多线程运行</li></ul><ol start="4"><li>浏览器运行是单线程还是多线程?</li></ol><ul><li>都是多线程运行的</li></ul><ol start="5"><li>浏览器运行是单进程还是多进程?</li></ol><ul><li>有的是单进程<ul><li>firefox</li><li>老版IE</li></ul></li><li>有的是多进程<ul><li>chrome</li><li>新版IE</li></ul></li><li>如何查看浏览器是否是多进程运行的呢?<ul><li>任务管理器–&gt;进程</li></ul></li></ul><h3><span id="2-浏览器内核">2、浏览器内核</span></h3><ul><li>支撑浏览器运行的最核心的程序</li><li>不同的浏览器可能不一样<ul><li>Chrome, Safari : webkit</li><li>firefox : Gecko</li><li>IE    : Trident</li><li>360,搜狗等国内浏览器: Trident + webkit</li></ul></li><li>内核由很多模块组成<ul><li>主线程<ul><li>js引擎模块 : 负责js程序的编译与运行</li><li>html,css文档解析模块 : 负责页面文本的解析</li><li>DOM/CSS模块 : 负责dom/css在内存中的相关处理 </li><li>布局和渲染模块 : 负责页面的布局和效果的绘制(内存中的对象)<pre><code>......</code></pre></li></ul></li><li>​    分线程<ul><li>定时器模块 : 负责定时器的管理</li><li>DOM事件响应模块 : 负责事件的管理</li><li>网络请求模块 : 负责ajax请求</li></ul></li></ul></li></ul><h3><span id="3-定时器引发的思考">3、定时器引发的思考</span></h3><ol><li>定时器真是定时执行的吗?</li></ol><ul><li><p>定时器并不能保证真正定时执行</p></li><li><p>一般会延迟一丁点(可以接受), 也有可能延迟很长时间(不能接受)</p></li></ul><ol start="2"><li>定时器回调函数是在分线程执行的吗?</li></ol><ul><li>在主线程执行的, js是单线程的</li></ul><ol start="3"><li>定时器是如何实现的?</li></ol><ul><li>事件循环模型(后面讲)</li></ul><pre><code class="javascript">document.getElementById(&#39;btn&#39;).onclick = function () {  var start = Date.now()  console.log(&#39;启动定时器前...&#39;)  setTimeout(function () {    console.log(&#39;定时器执行了&#39;, Date.now()-start)  }, 200)  console.log(&#39;启动定时器后...&#39;)  // 做一个长时间的工作  for (var i = 0; i &lt; 1000000000; i++) {  }}</code></pre><h3><span id="4-js是单线程执行的">4、JS是单线程执行的</span></h3><ol><li><p>如何证明js执行是单线程的?</p><ul><li>setTimeout()的回调函数是在主线程执行的</li><li>定时器回调函数只有在运行栈中的代码全部执行完后才有可能执行</li></ul></li><li><p>为什么js要用单线程模式, 而不用多线程模式?</p><ul><li>JavaScript的单线程，与它的用途有关。</li><li>作为浏览器脚本语言，JavaScript的主要用途是与用户互动，以及操作DOM。</li><li>这决定了它只能是单线程，否则会带来很复杂的同步问题</li></ul></li><li><p>代码的分类:</p><ul><li>初始化代码</li><li>回调代码</li></ul></li><li><p>js引擎执行代码的基本流程</p><ul><li>先执行初始化代码: 包含一些特别的代码   回调函数(异步执行)<ul><li>设置定时器</li><li>绑定事件监听</li><li>发送ajax请求</li></ul></li><li>后面在某个时刻才会执行回调代码(回调函数包含的代码)</li></ul></li></ol><pre><code class="javascript">setTimeout(function () {  console.log(&#39;timeout 2222&#39;)  alert(&#39;22222222&#39;)}, 2000)setTimeout(function () {  console.log(&#39;timeout 1111&#39;)  alert(&#39;1111111&#39;)}, 1000)setTimeout(function () {  console.log(&#39;timeout() 00000&#39;)}, 0)function fn() {  console.log(&#39;fn()&#39;)}fn()console.log(&#39;alert()之前&#39;)alert(&#39;------&#39;) //暂停当前主线程的执行, 同时暂停计时, 点击确定后, 恢复程序执行和计时console.log(&#39;alert()之后&#39;)</code></pre><h3><span id="5-浏览器的事件循环轮询模型">5、浏览器的事件循环(轮询)模型</span></h3><p>(1) 模型原理图</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815141019.png" alt="image-20200815141017614"></p><ol><li>所有代码分类<ul><li>初始化执行代码(同步代码): 包含绑定dom事件监听, 设置定时器, 发送ajax请求的代码</li><li>回调执行代码(异步代码): 处理回调逻辑</li></ul></li><li>js引擎执行代码的基本流程:<ul><li>初始化代码===&gt;回调代码</li></ul></li><li>模型的2个重要组成部分:<ul><li>事件(定时器/DOM事件/Ajax)管理模块</li><li>回调队列</li></ul></li><li>模型的运转流程<ul><li>执行初始化代码, 将事件回调函数交给对应模块管理</li><li>当事件发生时, 管理模块会将回调函数及其数据添加到回调列队中</li><li>只有当初始化代码执行完后(可能要一定时间), 才会遍历读取回调队列中的回调函数执行</li></ul></li></ol><pre><code class="javascript">function fn1() {  console.log(&#39;fn1()&#39;)}fn1()document.getElementById(&#39;btn&#39;).onclick = function () {  console.log(&#39;点击了btn&#39;)}setTimeout(function () {  console.log(&#39;定时器执行了&#39;)}, 2000)function fn2() {  console.log(&#39;fn2()&#39;)}fn2()</code></pre><p>(2) 相关重要概念</p><ol><li>执行栈 execution stack</li></ol><ul><li>所有的代码都是在此空间中执行的</li></ul><ol start="2"><li><p>浏览器内核 browser core</p><ul><li><p>js引擎模块(在主线程处理)</p></li><li><p>其它模块(在主/分线程处理)</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815141526.png" alt="运行原理图"></p></li></ul></li></ol><ol start="3"><li><p>任务队列 task queue</p></li><li><p>消息队列 message queue</p></li><li><p>事件队列 event queue</p><p>上面这三个在同一个 callback queue </p></li></ol><ol start="6"><li><p>事件轮询 event loop<br> 从任务队列中循环取出回调函数放入执行栈中处理(一个接一个)</p></li><li><p>事件驱动模型 event-driven interaction model</p></li><li><p>请求响应模型 request-response model</p></li></ol><h3><span id="6-h5-web-workers多线程">6、H5 Web Workers(多线程)</span></h3><p>(1) 介绍</p><ul><li>Web Workers 是 HTML5 提供的一个javascript多线程解决方案</li><li>我们可以将一些大计算量的代码交由web Worker运行而不冻结用户界面</li><li>但是子线程完全受主线程控制，且不得操作DOM。所以，这个新标准并没有改变JavaScript单线程的本质</li></ul><p>(2) 相关API</p><ul><li>Worker: 构造函数, 加载分线程执行的js文件</li><li>Worker.prototype.onmessage: 用于接收另一个线程的回调函数</li><li>Worker.prototype.postMessage: 向另一个线程发送消息</li></ul><p>(3) 使用</p><ul><li>创建在分线程执行的js文件</li></ul><pre><code class="javascript">var onmessage =function (event){ //不能用函数声明    console.log(&#39;onMessage()22&#39;);    var upper = event.data.toUpperCase();//通过event.data获得发送来的数据    postMessage( upper );//将获取到的数据发送会主线程}</code></pre><ul><li>在主线程中的js中发消息并设置回调</li></ul><pre><code class="javascript">//创建一个Worker对象并向它传递将在新线程中执行的脚本的URLvar worker = new Worker(&quot;worker.js&quot;);  //接收worker传过来的数据函数worker.onmessage = function (event) {         console.log(event.data);             };//向worker发送数据worker.postMessage(&quot;hello world&quot;);    </code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815143823.png" alt="image-20200815143821391"></p><p>(4) 应用练习</p><ul><li>直接在主线程</li></ul><pre><code class="javascript">&lt;input type=&quot;text&quot; placeholder=&quot;数值&quot; id=&quot;number&quot;&gt;&lt;button id=&quot;btn&quot;&gt;计算&lt;/button&gt;&lt;script type=&quot;text/javascript&quot;&gt;// 1 1 2 3 5 8    f(n) = f(n-1) + f(n-2)function fibonacci(n) {  return n&lt;=2 ? 1 : fibonacci(n-1) + fibonacci(n-2)  //递归调用}// console.log(fibonacci(7))var input = document.getElementById(&#39;number&#39;)document.getElementById(&#39;btn&#39;).onclick = function () {  var number = input.value  var result = fibonacci(number)  alert(result)}&lt;/script&gt;</code></pre><ul><li><p>使用Worker在分线程</p><ul><li>主线程</li></ul><pre><code class="javascript">var input = document.getElementById(&#39;number&#39;)document.getElementById(&#39;btn&#39;).onclick = function () {  var number = input.value  //创建一个Worker对象  var worker = new Worker(&#39;worker.js&#39;)  // 绑定接收消息的监听  worker.onmessage = function (event) {    console.log(&#39;主线程接收分线程返回的数据: &#39;+event.data)    alert(event.data)  }  // 向分线程发送消息  worker.postMessage(number)  console.log(&#39;主线程向分线程发送数据: &#39;+number)}// console.log(this) // window</code></pre></li></ul><ul><li><p>分线程 worker.js</p><pre><code class="javascript">function fibonacci(n) {return n&lt;=2 ? 1 : fibonacci(n-1) + fibonacci(n-2)  //递归调用}console.log(this)this.onmessage = function (event) {var number = event.dataconsole.log(&#39;分线程接收到主线程发送的数据: &#39;+number)//计算var result = fibonacci(number)postMessage(result)console.log(&#39;分线程向主线程返回数据: &#39;+result)// alert(result)  alert是window的方法, 在分线程不能调用// 分线程中的全局对象不再是window, 所以在分线程中不可能更新界面}</code></pre></li></ul><p>(5) 不足</p><ul><li>worker内代码不能操作DOM(更新UI)</li><li>不能跨域加载JS</li><li>不是每个浏览器都支持这个新特性</li></ul><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="笔记" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="前端" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="前端" scheme="https://wallleap.cn/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="web" scheme="https://wallleap.cn/tags/web/"/>
    
      <category term="JavaScript" scheme="https://wallleap.cn/tags/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title>JavaScript基础学习笔记</title>
    <link href="https://wallleap.cn/2020/04/05/JavaScriptBase/"/>
    <id>https://wallleap.cn/2020/04/05/JavaScriptBase/</id>
    <published>2020-04-05T04:33:14.000Z</published>
    <updated>2025-01-24T09:22:15.349Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2><span id="一-javascript简介">一、JavaScript简介</span></h2><h3><span id="1-简介">1、简介</span></h3><p>什么是语言</p><ul><li>计算机就是一个由人来控制的机器，人让它干什么，它就得干什么。</li><li>我们要学习的语言就是人和计算机交流的工具，人类通过语言来控制、操作计算机。</li><li>编程语言和我们说的中文、英文本质上没有区别，只是语法比较特殊。</li><li>语言的发展<ul><li>纸带机：机器语言</li><li>汇编语言：符号语言</li><li>现代语言：高级语言</li></ul></li></ul><p>JavaScript起源</p><ul><li>JavaScript诞生于1995年，它的出现除妖是用于处理网页中的前端验证</li><li>所谓的前端验证，就是指检查用户输入的内容是否符合一定的规则</li><li>比如：用户名的长度，密码的长度，邮箱的格式等</li></ul><p>JavaScript简史</p><ul><li>JavaScript是由<strong>网景</strong>公司发明，起初命名为LiveScript，后来由于SUN公司的介入更名为了JavaScript</li><li>1996年微软公司在其最新的IE3浏览器中引入了自己对JavaScript的实现JScript</li><li>于是在市面上存在两个版本的JavaScript，一个网景公司的JavaScript和微软的JScript</li><li>为了确保不同的浏览器上运行的JavaScript标准一致，所以几个公司共同定制了JS的标准名命名为<font color="red">ECMAScript</font></li></ul><p>时间表</p><table><thead><tr><th>年份</th><th>事件</th></tr></thead><tbody><tr><td>1995年</td><td>网景公司开发了JavaScript</td></tr><tr><td>1996年</td><td>微软发布了和JavaScript兼容的JScript</td></tr><tr><td>1997年</td><td>ECMAScript第1版(ECMA-262)</td></tr><tr><td>1998年</td><td>ECMAScript第2版</td></tr><tr><td>1998年</td><td>DOM Level1的制定</td></tr><tr><td>1998年</td><td>新型语言DHTML登场</td></tr><tr><td>1999年</td><td>ECMASript第3版</td></tr><tr><td>2000年</td><td>DOM Level2 的制定</td></tr><tr><td>2002年</td><td>ISO/IEC 16262:2002的确立</td></tr><tr><td>2004年</td><td>DOM Level3的制定</td></tr><tr><td>2005年</td><td>AJAX登场</td></tr><tr><td>2009年</td><td>ECMAScrpt第5版</td></tr><tr><td>2009年</td><td>新型语言HTML5登场</td></tr></tbody></table><p>实现</p><ul><li>ECMAScript是一个标准，而这个标准需要由各个厂商去实现。</li><li>不同的浏览器厂商对该标准会有不同的实现。</li></ul><table><thead><tr><th>浏览器</th><th>JavaScript实现方式</th></tr></thead><tbody><tr><td>FireFox</td><td>SpiderMonkey</td></tr><tr><td>Internet Explorer</td><td>JScript/Chakra</td></tr><tr><td>Safari</td><td>JavaScriptCore</td></tr><tr><td>Chrome</td><td>v8</td></tr><tr><td>Carakan</td><td>Carakan</td></tr></tbody></table><ul><li>我们已经知道ECMAScript是JavaScript标准，所以一般情况下这两个词我们认为是一个意思。</li><li>但是实际上JavaScript的含义却要更大一些。</li><li>一个完整的JavaScript实现应该由以下三个部分构成:</li></ul><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200812081314.png" alt="image-20200812081312883"></p><p>学习内容</p><ul><li>我们已经知道了一 个完整的JavaScript实现包含了三个部分：ECMAScript、DOM和BOM。</li><li>由此我们也知道了我们所要学习的内容就是这三部分：<ul><li>ECMAScript</li><li>DOM</li><li>BOM</li></ul></li></ul><p>JS的特点</p><ul><li>解释型语言</li><li>类似于C和Java的语法结构</li><li>动态语言</li><li>基于原型的面向对象</li></ul><h3><span id="2-写法">2、写法</span></h3><p>(1) JS代码需要写到script标签中</p><p>例如：</p><pre><code class="html">&lt;script type=”text/javascript”&gt;   alert(“Hello”); // 警告框   document.write(“hahhh”); // 向body中输出一个内容   console.log(“OK”); // 向控制台输出一个内容&lt;/script&gt;</code></pre><p>自上而下顺序执行</p><p>(2) JS代码编写位置</p><ul><li>写到标签属性中</li></ul><p>可以将js代码编写到便签的onclick属性中，当我们点击按钮时，js代码才会执行</p><pre><code class="html">&lt;button onclick=&quot;alert(&#39;点我干嘛~&#39;)&quot;&gt;点我一下&lt;/button&gt;</code></pre><p>可以将js代码写在超链接的href属性中，这样当点击超链接时，会执行js代码</p><pre><code class="html">&lt;a href=&quot;javascript:alert(&#39;让你点你就点？？&#39;);&quot;&gt;点我一下&lt;/a&gt;&lt;a href=&quot;javascript:;&quot;&gt;点我一下&lt;/a&gt;</code></pre><p>虽然可以写在标签的属性中，但是他们属于结构和行为耦合，不方便维护，不推荐使用。</p><ul><li>写到script标签中</li></ul><pre><code class="html">&lt;script type=&quot;text/javascript&quot;&gt;  alert(&#39;我是script标签中的代码&#39;)&lt;/script&gt;</code></pre><ul><li>写到js文件中，再引入</li></ul><p>可以将js代码编写到外部js文件中，然后通过script标签引入，写到外部文件中可以在不同的页面中同时引用，也可以利用感到浏览器的缓存机制。——推荐使用的方式</p><pre><code class="html">&lt;script src=&quot;js/script.js&quot;&gt;&lt;/script&gt;</code></pre><p>script标签一旦用于引入外部文件了，就不能再编写代码的，即使编写了浏览器也会忽略，如果需要则可以再创建一个新的script标签用于编写内部代码</p><h2><span id="二-ecmascript">二、ECMAScript</span></h2><h3><span id="1-注释">1、注释</span></h3><pre><code class="javascript">// 单行注释/*  多行注释，注释中的内容不会被执行，但是可以在源代码中查看*/</code></pre><p>要养成良好的编写注释的习惯，也可以通过注释来对代码进行一些简单的调试</p><h3><span id="2-说明">2、说明</span></h3><ul><li><p>js代码中严格区分大小写</p></li><li><p>js中每一条语句以分号(;)结尾</p><p>如果不写分号，浏览器会自动添加，但是会消耗一些系统资源(也不会很大)，而且有些时候，浏览器会加错分号，所以在开发中分号必须写</p></li></ul><p>关于是否应该写分号可以看下这篇文章：……查找中……</p><p>引用下尤雨溪大佬讲的：</p><blockquote><p>没有应该不应该，只有你自己喜欢不喜欢。</p><p>至于什么时候加分号。真正会导致上下行解析出问题的 token 有 5 个：括号，方括号，正则开头的斜杠，加号，减号。我还从没见过实际代码中用正则、加号、减号作为行首的情况，所以总结下来就是一句话：</p><p>一行开头是括号或者方括号的时候加上分号就可以了，其他时候全部不需要。</p></blockquote><p>我以前也都是每行必加的，但就像某位老师讲的，用过了才知道，“不加分号是真的爽啊”</p><ul><li>js中回忽略多个空格和换行，所以我们可以利用空格和换行对代码进行格式化</li></ul><h3><span id="3-字面量和变量">3、字面量和变量</span></h3><ul><li><p>字面量/常量，都是一些不可改变的值，比如：1、2、3、4、5</p><p>字面量都是可以直接使用的，但是我们一般都不会直接使用字面量</p></li><li><p>变量 变量可以用来保存字面量，而且变量的值是可以任意改变的，变量更方便我们使用，所以在开发中都是通过变量去保存一个字面量，而很少直接使用字面量</p></li></ul><p>在js中使用var关键字来声明一个变量，为变量赋值</p><pre><code class="javascript">var a; a=123; console.log(a);</code></pre><p>声明和赋值同时进行</p><pre><code class="javascript">var b=789;</code></pre><p>也可以通过变量对字面量进行描述</p><pre><code class="javascript">var age = 23;</code></pre><h3><span id="4-标识符">4、标识符</span></h3><p>在js中所有的可以由我们自主命名的都可以称为标识符</p><p>如：变量名、函数名、属性名</p><p>命名一个标识符时需要遵守如下的规则：</p><ul><li><p>标识符中可以含有字母、数字、<code>_</code>、<code>$</code></p></li><li><p>标识符不能以数字开头</p></li><li><p>标识符不能是ES中的关键字或保留字</p></li></ul><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200812084204.png" alt="image-20200812084202495"></p><ul><li>其他不建议使用的标识符</li></ul><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200812084252.png" alt="image-20200812084250806"></p><ul><li><p>标识符一般都采用驼峰命名法 helloWorld</p></li><li><p>js底层保存标识符时，实际上是采用Unicode编码，所以理论上讲，所有的utf-8中含有的内容都可以作为标识符</p></li></ul><h3><span id="5-数据类型">5、数据类型</span></h3><p>数据类型指的就是字面量的类型</p><p>在js中一共有六种数据类型</p><ul><li><p><code>String</code> 字符串</p></li><li><p><code>Number</code> 数值</p></li><li><p><code>Boolean</code> 布尔值</p></li><li><p><code>Null</code> 空值</p></li><li><p><code>Undefined</code> 未定义</p></li><li><p><code>Object</code> 对象</p></li><li><p>前五种属于<strong>基本数据类型</strong>，<code>Object</code>属于<strong>引用数据类型</strong></p></li></ul><p>(1) String</p><ul><li><p>在js中字符串需要使用引号引一起 <code>var str=”hello”;</code></p></li><li><p>使用双引号或单引号都可以，但是不要混着用</p></li><li><p>引号不能嵌套，双引号中不能放双引号，单引号中不能放单引号，但是单/双引号中可以放双/单引号</p></li><li><p>在字符串中，可以使用<code>\</code>作为转义字符，当表示一些特殊符号时，可以使用\进行转义 <code>\”</code>  <code>\’</code>  <code>\n</code>  <code>\t</code>  <code>\\</code></p></li></ul><p>(2) Number</p><ul><li><p>在js中所有的数值都是Number类型</p></li><li><p>包括整数和浮点数（小数）</p></li><li><p>可以使用运算符<code>typeof</code>来检查一个变量的类型</p><pre><code class="javascript">console.log(typeof a);console.log(Number.MAX_VALUE);</code></pre></li></ul><ul><li><p><code>Number.MAX_VALUE</code> 数字的最大值，超过了这个值回返回Infinity，表示正无穷 -Infinity 负无穷</p></li><li><p><code>NAN</code>，表示Not A Number，是一个特殊的数字</p></li><li><p><code>Number.MIN_VALUE</code> 大于0的最小值</p></li><li><p>在js中整数的运算基本可以保证精确</p></li><li><p><font color="red">浮点数计算，可能得到一个不精确的值</font>，不要进行对精确度要求高的计算</p></li></ul><p>(3) Boolean</p><p>布尔值只有两个，主要用来做逻辑判断</p><ul><li><p>true真</p></li><li><p>false假</p></li></ul><p>(4) Null</p><p><code>null</code>专门用来表示一个为空的对象</p><p>(5) undefined</p><p>声明了一个变量但是没有赋值时，它的值就是<code>undefined</code></p><h3><span id="6-强制类型转换">6、强制类型转换</span></h3><p>强制类型转换：将一个数据类型转换成其他的数据类型</p><p>String Number Boolean</p><p>(1) 调用被转换数据类型的<code>toString()</code>方法</p><pre><code class="javascript">var a = 123;a = a.toString();</code></pre><ul><li><p>该方法不会影响到原变量，它会将转换的结果返回，所以可以直接用该变量接收</p></li><li><p>但是注意：<code>null</code>和<code>undefined</code>这两个值没有<code>toString()</code>方法，如果调用它们的方法，会报错</p></li></ul><p>(2) 调用<code>String()</code>函数，并将被转换的数值作为参数传递给函数 </p><pre><code class="javascript">var a = truea=String(a);</code></pre><ul><li>对于<code>Number</code>和<code>Boolean</code>实际上就是调用<code>toString()</code>方法</li><li>但是对于<code>null</code>和<code>undefined</code>，就不会调用<code>toString()方</code>法，它会将<code>null</code>直接转换成<code>&quot;null&quot;</code></li></ul><p>(3) 使用<code>Number()</code>函数 </p><pre><code class="javascript">var a = truea = Number(a);</code></pre><ul><li>字符串—&gt;数字<ul><li>如果是纯数字的字符串，则直接将其转换为数字</li><li>如果字符串中有非数字的内容，则转换为NaN</li><li>如果字符串是一个空串或者是一个全是空格的字符，转换为0</li></ul></li><li>布尔值—&gt;数字<ul><li>true 转成 1</li><li>false 转成 0</li></ul></li><li><code>Null</code>—&gt;数字 0</li><li><code>undefined</code>—&gt;数字 NaN</li></ul><p>(4) 对字符串，<code>parseInt()</code>把一个字符串转换为一个整数，可取整</p><p> 可以将字符串中的有效的整数内容取出来，然后转换为<code>Number</code></p><pre><code class="javascript">console.log(parseInt(&#39;123a123&#39;)) // 123console.log(parseInt(&#39;a123&#39;)) // NaN</code></pre><p>(5) <code>parseFloat()</code>把一个字符串转换为小数 </p><pre><code class="javascript">var a = parseFloat(&#39;123.5px&#39;); console.log(a) // 123.5</code></pre><p> 16进制 0x10  8进制 070 2进制 0b10</p><pre><code class="javascript">/* 像&#39;070&#39;这种字符串，有些浏览器会当成八进制解析，有些会当成十进制 */var a = &#39;070&#39;/* 可以在parseInt()中传递第二个参数，来指定数字的进制 */console.log(parseInt(a, 8)) // 56console.log(parseInt(a, 10)) // 70</code></pre><p>(6) 使用<code>Boolean()</code>函数将其他数据类型转换为Boolean</p><ul><li>数字 —&gt; 布尔值 除了0和NaN，其他的都是true</li><li>字符串 —&gt; 布尔值 除了空串，其他都是true</li><li>null和undefined都会转换为false</li></ul><h3><span id="7-运算符">7、运算符</span></h3><p>运算符(操作符) 通过运算符可以对一个或多个值进行运算</p><p>比如：<code>typeof</code>就是一个运算符，可以来获得一个值的类型，它会将该值的类型以字符串的形式返回</p><p>(1) 算术运算符：</p><ul><li>当对非Number类型的值进行运算时，会将这些值转换为Number然后再运算</li><li>任何值和NaN做运算都得NaN</li><li><code>+</code>可以对两个值进行加法运算，并将结果返回。如果对两个字符串进行加法运算，则会做拼串，将两个字符串拼接成一个字符串，并返回。任何的值和字符串做加法运算，都会先转换为字符串，然后再和字符串做拼串的操作，我们可以让任意的数据类型<code>+””</code>使其转换为字符串(隐式转换)。</li><li><code>-</code>可以对两个值进行减法运算，并将结果返回</li><li><code>*</code> 可以对两个值进行乘法运算</li><li><code>/</code>可以对两个值进行除法运算</li><li><code>%</code> 取模运算（取余数）</li></ul><p>(2) 一元运算符 只需要一个操作数</p><ul><li><p><code>+</code> 正号 正号不会对数字产生任何影响</p></li><li><p><code>-</code>负号 负号可以对数字进行负号的取反</p></li></ul><p>对于非Number类型的值，它会先转换为Number，然后再运算，可以对一个其它的数据类型使用<code>+</code>，将其转换为Number，它的原理和Number()函数一样</p><p>(3) 自增和自减</p><p>自增<code>++</code> 可以使变量在自身的基础上增加1，原变量的值改变 </p><p>后++(a++) 前++(++a) 都会立即使<strong>原变量的值自增1</strong>，但是两个值不同，后++为原值，前++为自增后的值，都改变了a的值，但是自身的看情况。自减<code>--</code>类似，只是变为了减一。</p><pre><code class="javascript">var a = 5console.log(a, a++, a, ++a, a) // 5 5 6 7 7console.log(a, a--, a, --a, a) // 7 7 6 5 5</code></pre><p>(4) 逻辑运算符 逻辑判断</p><ul><li><code>!</code>  <strong>非</strong> 对一个值进行非运算，对布尔值进行取反操作 <ul><li>取反 true变false、false变true</li><li>如果对一个值进行两次取反，它不会变化</li><li>如果对非布尔值进行运算，则会将其转换为布尔值，然后再取反，所以我们可以利用这个特点，来讲一个其他的数据类型转换为布尔值（可以为一个任意数据类型取两次反，来将其转换为布尔值），原理和Boolean()函数一样</li></ul></li></ul><pre><code class="javascript">var a = truea=!aconsole.log(a) // false</code></pre><ul><li><p><code>&amp;&amp;</code> <strong>与</strong> 对符号两侧的值进行与运算并返回结果</p><p>运算规则</p><ul><li><p>两个值只要有一个为false就返回false，只有两个值都为true时才会返回true</p></li><li><p>js中的与属于<strong>短路的与</strong>，如果第一个值为false，就不会再看第二个值了</p></li></ul></li></ul><pre><code class="javascript">// 如果两个值都是true则返回truevar result = true &amp;&amp; trueconsole.log(result) // true// 只要有一个false，就返回falseresult = true &amp;&amp; falseconsole.log(result) // falseconsole.log(false &amp;&amp; true) // falseconsole.log(false &amp;&amp; false) // false// 第一个值为true，会检查第二个值true &amp;&amp; alert(&#39;我会弹出来哦&#39;)// 第一个值为false，不会检查第二个值false &amp;&amp; alert(&#39;不会弹出来&#39;)</code></pre><ul><li><p><code>||</code>  <strong>或</strong> 可以对符号两侧的值进行或运算并返回结果</p><p>运算规则</p><ul><li><p>两个值中只要有一个true就返回true，如果两个值都为false，才返回false</p></li><li><p>js中的或属于<strong>短路的或</strong>，如果第一个值为true，就不会看第二个值</p></li></ul></li></ul><pre><code class="javascript">// 两个都是false，则返回falseconsole.log(false || false)// 只要有一个true，就返回trueconsole.log(true || false)console.log(false || true)console.log(true || true)// 第一个值为false，会检查第二个值false || alert(&#39;123&#39;)// 第一个值为true，则不会检查第二个值true || alert(&#39;123&#39;)</code></pre><ul><li><p><code>&amp;&amp;</code> <code>||</code> 的非布尔值情况</p><ul><li>对于非布尔值进行与或运算时，会先将其转换为布尔值，再运算，并且返回原值</li><li>与运算：如果第一个值为true，则必然返回第二个值。 如果第一个值为false，则直接返回第一个值</li></ul><pre><code class="javascript">// true &amp;&amp; true// 与运算：如果两个值都为true，则返回后边的var result = 5 &amp;&amp; 6console.log(result) // 6// 与运算：如果两个值中有false，则返回靠前的false// false &amp;&amp; trueresult = 0 &amp;&amp; 2console.log(result) // 0result = 2 &amp;&amp; 0console.log(result) // 0// false &amp;&amp; falseresult = NaN &amp;&amp; 0result = 0 &amp;&amp; NaNconsole.log(result) // 0</code></pre><ul><li>或运算：如果第一个值为true，则直接返回回一个值。 如果第一个值为false，则返回第二个值</li></ul><pre><code class="javascript">// true || true , true || false// 如果第一个值为true，则直接返回第一个值console.log(2 || 1) // 2console.log(2 || NaN) // 2console.log(2 || 0) // 2// 如果第一个值为false，则直接返回第二个值console.log(NaN || 1) // 1console.log(NaN || 0) // 0console.log(0 || NaN) // NaN</code></pre></li></ul><p>(5) 赋值运算符<code>=</code></p><p>可以将符号右侧的值赋值给符号左侧的变量</p><p>复合运算<code>+=</code>、<code>-=</code>、<code>*=</code>、<code>/=</code>、<code>%=</code></p><pre><code class="javascript">var a = 10a += 5 // a = a + 5a -= 5 // a = a - 5a *= 2 // a = a * 2a /= 2 // a = a / 2a %= 3 // a = a % 3</code></pre><p>(6) 关系运算符 </p><p>可以比较两个值之间的大小关系</p><p>如果关系成立它会返回true，如果关系不成立则返回false</p><ul><li><p><code>&gt;</code> 大于号</p><ul><li>判断符号左侧的值是否大于右侧的</li><li>如果关系成立则返回true，如果关系不成立则返回false</li></ul><pre><code class="javascript">var result = 5 &gt; 10 // falseresult = 5 &gt; 4 // trueresult = 5 &gt; 5 // false</code></pre></li><li><p><code>&gt;=</code> 大于等于 是否大于或等于</p></li><li><p><code>&lt;</code> 小于号 </p></li><li><p><code>&lt;=</code> 小于等于</p></li></ul><p>非数值的情况</p><ul><li>对于非数值进行比较时，会将其转换为数字然后再比较</li></ul><pre><code class="javascript">console.log(1 &gt; true) // falseconsole.log(1 &gt;= true) // trueconsole.log(1 &gt; &#39;0&#39;) // trueconsole.log(10 &gt; null) // true// 任何值和NaN做任何比较都是falseconsole.log(10 &lt;= &quot;hello&quot;) // falseconsole.log(1000000000 &gt; NaN) // falseconsole.log(true &gt; false) // true</code></pre><ul><li>如果符号两侧的值都是字符串时，不会将其转换为数字进行比较，而是会分别比较字符串中字符的Unicode编码</li></ul><pre><code class="javascript">// 比较练歌字符串时，比较的是字符串的字符编码console.log(&#39;a&#39; &lt; &#39;b&#39;) // true// 比较字符编码时是一位一位进行比较的，如果两位一样则比较下一位，所以可以借用它对英文进行排序console.log(&#39;abc&#39; &lt; &#39;bcd&#39;) // true// 比较中文时没有意义console.log(&quot;我&quot; &gt; &quot;你&quot;) // true// 如果比较两个字符串型的数字，可能会得到不可预测的结果console.log(&#39;1222222222&#39; &lt; &#39;5&#39;) // true// 因此在比较练歌字符串型的数字时，一定要转型console.log(&#39;1222222222&#39; &lt; +&#39;5&#39;) // false</code></pre><p>(7) 相等运算符</p><ul><li>使用<code>==</code>来做相等运算</li></ul><p>相等运算符用来比较两个值是否相等，如果相等会返回<code>true</code>，否则返回<code>false</code></p><p>如果两个进行比较的值类型不同，则会自动进行类型转换，将其转换为相同的类型，然后再比较</p><pre><code class="javascript">console.log(1 == 1); //truevar a=10;console.log(a == 4); //falseconsole.log(&quot;1&quot; == 1); //trueconsole.log(true == &quot;1&quot;); //trueconsole.log(null == 0); //false</code></pre><p>由于undefined衍生自null，所以这两个值做相等判断时，会返回ture</p><pre><code class="javascript">console.log(undefined == null) // true</code></pre><p>NaN不和任何值相等，包括它本身</p><pre><code class="javascript">console.log(NaN == &quot;1&quot;) // falseconsole.log(NaN == NaN) // false</code></pre><p>因此需要通过其他方式判断：isNaN()函数就是用来判断一个值是否是NaN的</p><pre><code>var a = &#39;abc&#39;a = Number(a)console.log(a, isNaN(a)) // NaN, trueconsole.log(isNaN(NaN)) // true</code></pre><ul><li>使用<code>!=</code>来做不相等运算</li></ul><p>不相等运算符用来判断两个值是否不相等，如果不相等会返回<code>true</code>，否则返回<code>false</code></p><p>不相等运算符也会对变量进行自动类型转换，如果转换后相等它也会返回false</p><ul><li>使用<code>===</code>来做全等运算</li></ul><p>用来判断两个值是否全等，它和相等类似，不同的是他<strong>不会做自动类型转换</strong>，如果两个值的类型不同，直接返回false</p><ul><li>使用<code>!==</code>来做不全等运算</li></ul><p>用来判断两个值是否不全等，和不等类似，不同的是它不会做自动类型转换</p><p>(8) 条件运算符，也叫三元运算符</p><p><code>条件表达式?语句1:语句2</code></p><p>执行的流程：</p><p>首先对条件表达式进行求值，如果该值为true，则执行语句1，并返回执行结果；如果该值为false，则执行语句2，并返回执行结果。</p><p>非布尔值会先转换为布尔值</p><pre><code class="javascript">var a = 30var b = 20var c = 10a &gt; b ? alert(&#39;a大&#39;) : alert(&quot;b大&quot;)// 获取a和b中的较大值var max = a &gt; b ? a : b// 获取a、b、c中的最大值max = max &gt; c ? max : c// 也可以放到一起，但是不推荐使用，不方便阅读var max = a &gt; b ? (a &gt; c ? a : c) : (b &gt; c ? b :c)</code></pre><p>(9) 逗号运算符</p><p>使用<code>,</code>可以分割多个语句，一般可以在声明多个变量时使用</p><pre><code class="javascript">// 使用逗号运算符同时声明多个变量var a, b, c// 可以同时声明多个变量并赋值var a = 1, b = 2, c = 3alert(b)</code></pre><p>和数学中一样，在js中运算符也有优先级，例如先乘除后加减</p><p>在JS中有一个运算符优先的表，在表中越靠上优先级越高，优先级越高越优先运算，如果优先级一样，则从左往右计算，可以利用<code>()</code>改变优先级</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200812113320.png" alt="运算符的优先级"></p><h3><span id="8-编码">8、编码</span></h3><p>在字符串中使用转义字符输入Unicode编码：<code>\u四位编码</code></p><pre><code>console.log(&#39;\u2620&#39;)</code></pre><p>在网页中使用Unicode编码：<code>&amp;#编码;</code>这里的编码需要的是十进制</p><pre><code class="html">&lt;h1 style=&quot;font-size: 200px;&quot;&gt;&amp;#9760&lt;/h1&gt;</code></pre><h3><span id="9-语句">9、语句</span></h3><ul><li>前面我们说的表达式、运算符等内容可以理解成是一门语言中的单词、短语</li><li>而<strong>语句(statement)</strong>就是我们这个语言中一句一句完整的话了</li><li>语句是一个程序的基本单位，js的程序就是由一条一条语句构成的，每一条语句使用<code>;</code>结尾</li><li>js中的语句默认是由上至下顺序执行的，但是我们也可以通过一些流程控制语句来控制语句的执行顺序</li><li>在js中可以使用<code>{}</code>来为语句进行分组，同一个<code>{}</code>中的语句我们称为是一组语句，它们要么都执行，要么都不执行，一个<code>{}</code>中的语句我们也称为一个代码块，在代码块的后边就不用再编写<code>;</code>了</li><li>js中的代码块，只具有分组的作用，没有其他的用途，代码块中的内容，在外部是完全可见的</li></ul><pre><code class="javascript">{    alert(&#39;hello&#39;);  console.log(&#39;你好&#39;);  document.write(&quot;语句&quot;);}</code></pre><p>流程控制语句</p><ul><li>JS中的程序是从上到下一行一行执行的。</li><li>通过流程控制语句可以控制程序执行流程，使程序可以根据一定的条件来选择执行。</li><li>语句的分类:<ol><li>条件判断语句</li><li>条件分支语句</li><li>循环语句</li></ol></li></ul><p>(1) 条件判断语句</p><p>使用条件判断语句可以在执行某个语句之前进行判断，如果条件成立才会执行语句，条件不成立则语句不执行。</p><p>if语句</p><p>语法一：</p><pre><code>if(条件表达式){    语句……}</code></pre><p>if语句在执行时，会先对条件表达式进行求值判断，如果条件表达式的值为true，则执行if后的语句，如果条件表达式的值为false，则不会执行if后的语句。</p><p>if语句只能控制紧随其后的那个语句，如果希望if语句可以控制多条语句，可以将这些语句统一放到代码块中。</p><p>语法二：</p><pre><code>if(条件表达式){    语句……}else{    语句……}</code></pre><p>if…else…语句</p><p>当该语句执行时，会先对if后的条件表达式进行求值判断如果该值为true，则执行if后的语句，如果该值为false，则执行else后的语句。</p><p>语法三：</p><pre><code>if(条件表达式){    语句……}else if(条件表达式){    语句……}else if(条件表达式){    语句……}else{    语句……}</code></pre><p>if…else if…else…</p><p>当该语句执行时，会从上到下依次对条件表达式进行求值判断，如果值为true，则执行当前语句，如果值为false，则继续向下判断。</p><p>补充一个知识点：</p><p><code>prompt()</code>可以弹出一个提示框。该提示框中会带有一个文本框，用户可以在文本恒中输入一段内容，该函数需要一个字符串作为参数，该字符串将会作为提示框的提示文字。用户输入的内容将会作为函数的返回值返回，可以定义一 个变量来按收该内容。</p><pre><code class="javascript">var score = prompt(&quot;请输入成绩：&quot;)// 判断是否合法if(score &gt; 100 || score &lt; 0 || isNaN(socre)){  alert(&quot;请输入0~100的整数&quot;)}</code></pre><p>(2) 条件分支语句</p><pre><code>switch(条件表达式){    case 表达式:        语句……        break;  case 表达式:        语句……        break;    default:        语句……        break;}</code></pre><p>switch…case..语句<br>在执行时会值次将case后的表达式的值和switch后的条件表达式的值进行全等比较。<br>如果比较结果为true，则从当前case处开始执行代码。当前case后的所有的代码都会执行，我们可以在case的语句后加上一个break关键字，这样可以确保只会执行当前case后的语句，而不会执行其他的case。<br>如果比较结果为false，则继续向下比较。<br>如果所有的比较结果都为false，则只执行default后的语句。</p><p>switch语句和If语句的功能实际上有重复的，使用switch可以在现if的功能，同样使用if也可以实现switch的功能， 所以我们使用时，可以根据自己的习惯选择。</p><pre><code class="javascript">/*switch(parseInt(score/10)){    case 10:    case 9:    case 8:        console.log(&quot;优秀&quot;);        break;    case 7:    case 6:        console.log(&quot;合格&quot;);        break;    default:        console.log(&quot;不合格&quot;);        break;}*/switch(true){  case score &gt;= 60:    console.log(&quot;合格&quot;);    break;  default:    console.log(&quot;不合格&quot;);    break;}</code></pre><p>(3) 循环语句</p><p>通过循环语句可以反复地执行一段代码多次</p><p>while循环</p><ul><li>语法：</li></ul><pre><code class="javascript">while(条件表达式){  语句……}</code></pre><ul><li>while语句在执行时，先对条件表达式进行求值判断，如果值为true，则执行循环体，循环体执行完毕以后，继续对表达式进行判断，如果为true，则继续执行循环体，以此类推，如果值为false则终止循环</li></ul><pre><code class="JavaScript">// 像这种条件表达式写死为true的循环，叫做死循环。该循环不会停止，除非浏览器关闭，死循环在开发时慎用。可以使用break来终止循环。while(true){  alert(n++)  if(n == 10){    break  }}// 创建一个循环，往往需要三个步骤：// 1、初始化一个变量var i = 1// 2、在循环中设置一个条件表达式while(i &lt; 300){  // 3、定义一个更新表达式，每次更新初始化变量  document.write(i++ + &quot;&lt;br /&gt;&quot;)}</code></pre><p>do…while循环</p><ul><li>语法：</li></ul><pre><code>do{    语句……}while(条件表达式)</code></pre><ul><li>执行流程：</li></ul><p>do…while语句在执行时，会先执行循环体，循环体执行完毕以后，再对while后的条件表达式进行判断，如果结果为true，则连续执行循环体，执行完毕后继续判断以此类推，如果结果为false则终止循环</p><p>实际上while和do…while这两个语句功能相似，不同的是while是先判断后执行，而do…while可以保证循环体至少执行一次，而while不能</p><pre><code class="javascript">do{  document.write(i++ + &#39;&lt;br /&gt;&#39;)}while(i &lt;= 10)while(true){  var score = prompt(&#39;请输入成绩：&#39;)  if(score &gt;= 0 &amp;&amp; score &lt;= 100){    alert(&#39;成绩合法&#39;)    break    }  alert(&#39;请输入有效的分数！&#39;)}</code></pre><p>for循环</p><p>for语句，也是一个循环语句，也称为for循环</p><p>在for循环中，提供了专门的位置用来放三个表达式：</p><ol><li>初始化表达式</li><li>条件表达式</li><li>更新表达式</li></ol><ul><li>语法：</li></ul><pre><code>for(初始化表达式; 条件表达式; 更新表达式){    语句……}</code></pre><ul><li>执行流程：</li></ul><p>①执行初始化表达式，初始化变量(初始化表达式只会执行一次)</p><p>②执行条件表达式，判断是否执行循环。</p><p>​    如果为true，则执行循环 ③</p><p>​    如果为false，终止循环</p><p>④执行更新表达式，更新表达式执行完毕继续重复 ③</p><pre><code class="javascript">// for循环中的三个部分都可以省略，也可以写在外部var i = 0for(; i&lt;10;){  alert(i++)}// 如果在for循环中不写任何的表达式，只写两个分号，即for(;;)，此时是一个死循环for(var i=100; i&lt;1000; i++){    var th = parseInt(i/100)   var te = parseInt((i-th*100)/10)  var on = i%10  // 判断i是否是水仙花数  if(th*th*th + te*te*te + on*on*on == i)    console.log(i)}</code></pre><p>补充：break和continue</p><ul><li>break关键字可以用来退出switch或循环语句，不能在if语句中使用break和continue，break关键字会立即终止离他最近的那阿哥循环语句</li></ul><p>可以为循环语句创建一个label，用来标识当前的循环<code>label: 循环语句</code>。使用break时，可以在break后跟着一个label，这样break将会结束指定的循环，而不是最近的。</p><pre><code class="javascript">outer:for(var i=0; i&lt;5; i++){  console.log(&quot;@外层循环&quot;+i)  for(var j=0; j&lt;5; j++){    break outer // 终止外层循环(外层循环都没执行，内层循环自然也执行不了了)    console.log(&quot;@内层循环&quot;+j)  }}</code></pre><ul><li>continue关键字可以用来跳过本次循环，通用continue也是默认只会对离他最近的循环起作用</li></ul><pre><code class="javascript">for(var i=0; i&lt;5; i++){  if(i==2){    continue;  }  console.log(i) // 0 1 3 4}// 当然，也可以用来跳过指定的outer: for(var i=0; i&lt;5; i++){  console.log(&quot;@外层循环&quot;+i)  for(var j=0; j&lt;5; j++){    if(j==1){      // continue      continue outer    }    console.log(&quot;@内层循环&quot;+j)  }}</code></pre><pre><code class="javascript">/* 测试某段代码的性能：* 在代码执行前，开始计时。console.time(&quot;计时器的名字&quot;)可以用来开启一个计时器，它需要一个字符串作为参数，这个字符串将会作为计时器的标识* 在代码结束后终止计时器，console.timeEnd(&quot;计时器的名字&quot;)用来停止一个计时器，需要一个计时器的名字作为参数*/console.time(&#39;test&#39;)for(var i=2; i&lt;=10000; i++){  var flag = true  for(var j=2; j&lt;i; j++){    if(i%j == 0){      flag = false      // break    }    if(flag){      // console.log(i)    }  }}console.timeEnd(&#39;test&#39;)</code></pre><p>可以通过<code>Math.sqrt()</code>对一个数进行开方：</p><pre><code class="javascript">var result = Math.sqrt(4)</code></pre><h3><span id="10-对象">10、对象</span></h3><p>五种基本数据类型：</p><p><code>String</code>字符串、<code>Number</code>数值、<code>Boolean</code>布尔值、<code>Null</code>空值、<code>Undefined</code>未定义</p><p>我们以后看到的值只要不是这五种，全都是对象(<code>Object</code>)</p><p>基本数据类型都是单一的值”helloe”、123、true，值和值之间没有任何的联系</p><p>在js中表示一个人的信息(name gender age)：</p><pre><code>var name = &quot;张三&quot;var gender = &quot;男&quot;var age = 18</code></pre><p>如果使用基本数据类型的数据，我们所创建的变量都是独立的，不能成为一个整体</p><p><strong>对象</strong>属于一种复合的数据类型，在对象中可以保存多个不同数据类型的属性。</p><p>对象的分类：</p><ol><li>内建对象<ul><li>由ES标准中定义的对象，在任何的ES的实现中都可以使用</li><li>比如：Math、String、Number、Boolean、Function、Object……</li></ul></li><li>宿主对象<ul><li>由JS运行环境提供的对象，目前来讲主要指由浏览器提供的对象</li><li>比如BOM、DOM</li></ul></li><li>自定义对象<ul><li>由开发人员自己创建的对象</li></ul></li></ol><ul><li>创建对象</li></ul><p>使用<code>new</code>关键字调用的函数，是构造函数constructor，构造函数是专门用来创建对象的函数。使用typeof检查一个对象时，会返回object。</p><pre><code class="javascript">var obj = new Object()console.log(obj, typeof(obj))</code></pre><ul><li>添加属性</li></ul><p>在对象中保存的值称为属性</p><p>向对象添加属性，语法：<code>对象.属性名=属性值;</code></p><pre><code class="javascript">// 向obj中添加一个name属性obj.name = &quot;张三&quot;// 向obj中添加一个gender属性obj.gender = &quot;男&quot;// 向obj中添加一个age属性obj.age = 18console.log(obj)</code></pre><ul><li>读取属性</li></ul><p>读取对象中的属性，语法：<code>对象.属性名</code></p><p>如果读取对象中没有的属性，不会报错，而是会返回<code>undefined</code></p><pre><code class="javascript">console.log(obj.gender)console.log(obj.hello)</code></pre><ul><li>修改属性值</li></ul><p>修改对象的属性值，语法：<code>对象.属性名=新值</code></p><pre><code class="javascript">obj.name = &quot;tom&quot;console.log(obj.name)</code></pre><ul><li>删除属性</li></ul><p>删除对象的属性，语法：<code>delete 对象.属性名</code></p><pre><code class="javascript">delete obj.nameconsole.log(obj.name)</code></pre><p>属性名</p><ul><li>对象的属性名不强制要求遵守标识符的规范，什么乱七八糟的名字都可以取</li><li>但是我们使用的时候还是尽量按照标识符的规范去做</li><li>如果想要使用特殊的属性名，不能采用<code>.</code>的方式来操作，需要使用另一种方式，语法：<code>对象[&quot;属性名&quot;]=属性值</code>，读取时也需要采用这种方式<code>对象.[&quot;属性名&quot;]</code>。使用<code>[]</code>这种形式去操作属性，更加地灵活，在<code>[]</code>中可以直接传递一个变量，这样变量值是多少就会读取那个属性(变量的时候也只能采用这种方式)。</li></ul><pre><code class="javas">obj[&quot;nihao&quot;] = &quot;你好&quot;var n = &quot;nihao&quot;console.log(obj[n])</code></pre><p>属性值</p><ul><li>js对象的属性值，可以是任意的数据类型，甚至也可以是一个对象</li></ul><pre><code class="javascript">var obj1 = new Object()obj1.name = &quot;对象&quot;obj1.obj2 = new Object()obj1[&quot;2&quot;] = nullobj1[&quot;num&quot;] = 12345var a = uobj1[a] = undefined</code></pre><p><code>in</code>运算符</p><ul><li>通过该运算符可以检查一个对象中时候含有指定的属性，如果有则返回true，没有则返回false</li><li>语法：<code>&quot;属性名&quot; in 对象</code></li></ul><pre><code class="javascript">console.log(&quot;name&quot; in obj1)console.log(&quot;test&quot; in obj1)</code></pre><p>基本数据类型和引用数据类型</p><p>基本数据类型：<code>String</code>、<code>Number</code>、<code>Boolean</code>、<code>Null</code>、<code>Undefined</code></p><p>引用数据类型：<code>Object</code></p><p>js中的变量都是保存到栈内存中的</p><ul><li>基本数据类型的值直接在栈内存中存储，值与值之间是独立存在，修改一个变量不会影响其他的变量。</li></ul><pre><code class="javascript">var a = 123var b = aa++</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200812163823.png" alt="image-20200812163822217"></p><ul><li>对象是保存到堆内存中的，每创建一个新的对象，就会在堆内存中开辟出一个新的空间，而变量保存的是对象的内存地址(对象的引用)，如果两个变量保存的是同一个对象引用，当修改其中一个的变量属性时，另一个也会受到影响。</li></ul><pre><code class="javascript">var obj = new Object()obj.name = &quot;张三&quot;var obj2 = objobj.name = &quot;李四&quot;console.log(obj.name)console.log(obj2.name)</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200812164416.png" alt="image-20200812164415240"></p><p><code>new</code>会在堆内存中开辟空间</p><p><code>obj2=null</code>，这样obj2就没有指向0x123这个地址了</p><p>当比较两个基本数据类型的值时，就是比较值。</p><p>而比较两个引用数据类型时，它是比较的对象的内存地址，如果两个对象是一模一样的，但是地址不同，它也会返回false</p><pre><code class="javascript">var c = 10var d = 10console.log(c == d)var obj3 = new Object()var obj4 = new Object()obj3.name = &quot;张三&quot;obj4.name = &quot;张三&quot;console.log(obj3, obj4, obj3==obj4)</code></pre><p>对象字面量</p><pre><code class="javascript">/* 创建一个对象 */// var obj = new Object()/* 使用对象字面量来创建一个对象 */var obj = {}console.log(typeof obj)obj.name = &quot;张三&quot;console.log(obj.name)</code></pre><p>使用对象字面量，可以在创建对象时，直接指定对象中的属性，语法：<code>{属性名:属性值,属性名:属性值……}</code></p><pre><code class="javascript">var obj2 = {name:&quot;张三&quot;, age: 18, gender: &quot;男&quot;}console.log(obj2)var obj3 = {  name: &quot;李四&quot;,  age: 23,  gender: &quot;男&quot;}</code></pre><p>对象字面量的属性名可以加引号也可以不加，建议不加，如果要使用一些特殊的名字，则必须加引号。</p><p>属性名和属性值是一组一组的名值对结构，名和值之间使用<code>:</code>连接，多个名值对之间使用<code>,</code>隔开，如果一个属性之后没有其他的属性了，就不要写<code>,</code>。</p><h3><span id="11-函数">11、函数</span></h3><p>(1) 函数function</p><ul><li>函数也是一个对象</li><li>函数中可以封装一些功能(代码)，在需要时可以执行这些功能(代码)</li><li>函数中可以保存一些代码在需要的时候再调用</li><li>使用typeof检查一个函数对象时，会返回<code>function</code></li></ul><pre><code class="javascript">// 创建一个函数对象var fun = new Function()console.log(typeof fun)</code></pre><p>(2) 创建函数和调用函数</p><ul><li>在创建一个函数对象的时候，可以将要封装的代码以字符串的形式传递给构造函数</li><li>封装到函数的代码不会立即执行</li><li>函数中的代码会在函数调用的时候执行</li><li>调用函数语法：<code>函数对象()</code></li><li>当调用函数时，函数中封装的代码会按照顺序执行</li><li>调用几次就会执行几次</li></ul><pre><code class="javascript">var fun = new Function(&quot;console.log(&#39;这是一个函数哦&#39;)&quot;)fun()fun()</code></pre><p>我们在实际开发中很少使用构造函数来创建一个函数对象，而是使用其他方式，例如以下两种(事实上学完ES6后这两种方式也很少使用了)</p><p>使用函数声明来创建一个函数</p><p>语法：</p><pre><code>function 函数名([形参1, 形参2...形参N]){    语句……}</code></pre><p>其中<code>[]</code>代表可选</p><pre><code class="javascript">function fun2(arg){  console.log(&quot;调用了这个&quot; + arg + &quot;函数了~&quot;)}// console.log(fun2)fun2(&quot;fun2&quot;)</code></pre><p>使用函数表达式来创建一个函数</p><p>语法：</p><pre><code>var 函数名 = ffunction([形参1, 形参2...形参N]){    语句……}</code></pre><pre><code class="javascript">var fun3 = function(){  console.log(&#39;fun3&#39;)}fun3()</code></pre><p>(3) 函数的参数</p><ul><li><p>可以在定义函数的时候，在函数的<code>()</code>中指定一个或多个形参(形式参数)，多个形参之间使用<code>,</code>隔开，声明形参就相当于在函数内部声明了对应的变量，但是并没有赋值。</p></li><li><p>在调用函数的时候，可以在<code>()</code>中指定实参(实际参数)，实参将会赋值给函数中对应的形参。</p><ul><li>调用函数时解析器不会检查实参的类型，所以要注意，是否有可能会接收到非法的参数，如果有可能则需要对参数进行类型的检查(函数的实参可以是任意的数据类型)。</li><li>调用函数时，解析器也不会检查实参的数量，多余的实参不会被赋值，如果实参的数量少于形参的数量，则没有对应实参的形参将是undefined</li></ul></li></ul><pre><code class="javascript">/* * 定义一个用来求两个数和的函数*/function sum(a, b){  console.log(a+b)}sum(1,2)sum(123,456)</code></pre><p>(4) 返回值</p><p>可以使用return来设置函数的返回值</p><p>语法：<code>return 值</code></p><p>return后的值将会作为函数的执行结果返回，可以定义一个变量来接收该结果</p><pre><code class="javascript">function sum(a, b, c){  var d = a + b + c  return d}var result = sum(4,7,8)</code></pre><p>在函数中return后的语句都不会执行</p><p>如果return语句后不跟任何值就相当于返回一个undefined，如果函数中不写return，也是返回undefined</p><p>return后可以跟任意类型的值(返回值可以是任意的类型)，可以是一个对象，可以是一个函数……</p><p>在函数内部可以再声明一个函数，可以这样调用——<code>fun()()</code></p><p>总结：使用break可以退出当前循环，使用continue跳过当次循环，使用return可以结束整个函数。</p><p>(5) 立即执行函数(匿名函数)——只想调用一次</p><p>函数定义完，立即被调用。</p><pre><code class="javascript">(function(){  alert(&quot;我是一个匿名函数~~~&quot;);})();</code></pre><p>如果不喜欢写分号的，记得上面说过的规则，开头是<code>()</code>、<code>[]</code>需要加上分号：</p><pre><code class="javascript">;(function(){  alert(&quot;我是一个匿名函数~~~&quot;)})()</code></pre><p>(6) 函数与方法</p><p>函数也可以成为对象的属性，如果一个函数作为一个对象的属性保存，那么我们称这个函数时这个对象的<strong>方法(method)</strong>，调用这个函数就说调用对象的方法。<br>但是它只是名称上的区别没有其他的区别。</p><pre><code class="javascript">var obj = new Object()obj.name = &quot;张三&quot;obj.age = 21obj.sayName = function(){  console.log(obj.name)}// console.log(obj.sayName)obj.sayName()</code></pre><p>调用方法：<code>obj.sayName()</code></p><p>调用函数：<code>fun()</code></p><pre><code class="javascript">var obj2 = {  name: &quot;李四&quot;,  age: 18,  sayName: fuction(){        console.log(obj2.name)        }}obj.sayName()</code></pre><h3><span id="12-forin">12、<font color="red">for…in</font></span></h3><p>使用for…in语句可以枚举对象中的属性</p><p>语法：</p><pre><code>for(var 变量 in 对象){}</code></pre><p>for…in语句，对象中有几个属性，循环体就会执行几次，每次执行时，会将对象中的恶一个属性的名字赋值给变量</p><pre><code class="javascript">for(var index in obj){    console.log(&quot;属性名：&quot;+index)  console.log(&quot;属性值：&quot;+obj[index])}</code></pre><h3><span id="13-作用域scope">13、作用域scope</span></h3><p>作用域指一个变量的作用的范围</p><p>在js中一共有两种作用域：全局作用域和函数作用域</p><ul><li><p>全局作用域</p><ul><li>直接编写在script标签中的js代码，都在全局作用域</li><li>全局作用域在页面打开时创建，页面关闭时销毁。</li><li>在全局作用域中有一个<strong>全局对象window</strong>，代表的是一个浏览器的窗口，它由浏览器创建，我们可以直接使用</li><li>在全局作用域中，创建的变量都会作为window对象的属性保存；创建的函数都会作为window对象的方法保存</li></ul><pre><code class="javascript">var a = 10console.log(window.a)function fun(){  console.log(&quot;fun()&quot;)}window.fun()</code></pre><ul><li>全局作用域中的变量都是全局变量，在页面的任意的部分都是可以访问到的。</li></ul></li></ul><ul><li>函数作用域<ul><li>调用函数时创建函数作用域，函数执行完毕以后，函数作用域销毁</li><li>每调用一次函数就会创建一个新的函数作用域，它们之间是相互独立的</li><li>在函数作用域中可以访问到全局作用域的变量，在全局作用域中无法访问到函数作用域的变量</li><li>当前函数作用域操作一个变量时，它会现在自身作用域中寻找，如果有就直接使用；如果没有就往上一级作用域中去找，知道找到全局作用域；如果全局作用域中依然没有找到，则会报错ReferenceError</li><li>在函数中要访问全局变量可以使用window对象</li><li>在函数作用域中也有声明提前的特性，使用var关键字声明的变量，会在函数中所有的代码执行之前声明。函数声明也会在函数中所有的代码执行之前声明</li><li>在函数中，不使用var声明的变量都会成为全局变量</li><li>定义形参就相当于在函数作用域中声明了变量</li></ul></li></ul><h3><span id="14-声明提前">14、声明提前</span></h3><p>变量的声明提前</p><ul><li>使用var关键字声明的变量，会在所有的代码执行之前被声明（即使放在使用的之后）但提前声明并不会同时赋值</li></ul><pre><code class="javascript">// 没有var a时console.log(a) // 报错Uncaught ReferenceError: a is not defined--------------------------------------------------------------// 提前声明，相当于在这里 var aconsole.log(a) // undefinedvar a = 1     // 这里a=1console.log(a) // 1</code></pre><ul><li>但是如果声明变量时不使用var关键字，则变量不会被声明提前</li></ul><p>函数的声明提前</p><ul><li>使用函数声明形式创建的函数<code>function 函数(){}</code>，它会在所有的代码执行之前就被创建</li></ul><pre><code class="javascript">fun() // 声明提前，输出fun()function fun(){    console.log(&quot;fun()&quot;)}</code></pre><ul><li>使用函数表达式创建的函数，不会被声明提前，所以不能在声明前调用</li></ul><pre><code class="javascript">var fun2 = function(){  console.log(&quot;我不会被提前声明&quot;)}</code></pre><h3><span id="15-this">15、this</span></h3><p>解析器在调用函数时每次都会向函数内部传递一个隐含的参数</p><p>这个隐含的参数就是this，this指向的是一个对象，这个对象我们称为函数执行的上下文对象，根据函数的调用方式 的不同，this会指向不同的对象。</p><ol><li>以函数的形式调用，this永远都是window</li><li>以方法的形式调用，this就是调用方法的那个对象</li><li>当以构造函数的形式调用时，this就是新创建的那个对象</li></ol><p>在后面的学习中将慢慢补全this是什么</p><pre><code class="javascript">function fun(){    console.log(this.name)}</code></pre><h3><span id="16-使用工厂方法创建对象">16、使用工厂方法创建对象</span></h3><p> 通过该方法可以大批量的创建对象</p><pre><code class="javascript">function createPerson(name, age, gender){  var obj = new Object()  obj.name = name  obj.age = age  obj.gender = gender  obj.sayName = function(){    alert(this.name)  }  return obj}var zbj = createPerson(&quot;猪八戒&quot;,1030,&quot;男&quot;)var lcg = createPerson(&quot;李长庚&quot;,11030,&quot;男&quot;)var ce = createPerson(&quot;嫦娥&quot;,18,&quot;女&quot;)ce.sayName()</code></pre><p>使用工厂方法创建的对象，使用的构造函数都是Object，所以创建的对象都是Object这个类型，这就导致我们无法区分出多种不同类型的对象</p><h3><span id="17-构造函数">17、<font color="red">构造函数</font></span></h3><p> 构造函数就是一个普通的函数，创建方式和普通函数没有区别，不同的是构造函数习惯上首字母大写。</p><p> 构造函数和普通函数的区别就是调用方法的不同，普通函数时直接调用，而构造函数需要使用new关键字调用</p><pre><code class="javascript">function Person(){}var per = new Person()console.log(per)</code></pre><p>构造函数的执行流程：</p><ul><li><p>立刻创建一个新的对象</p></li><li><p>将新建的对象设置为函数中的this，在构造函数中可以使用this来引用新建的对象</p></li><li><p>逐行执行函数中的代码</p></li><li><p>将新建的对象作为返回值返回</p></li></ul><pre><code class="javascript">function Person(name, age, gender){  this.name = name  this.age = age  this.gender = gender  this.sayName = function(){    alert(this.name)  }}var zbj = new Person(&quot;猪八戒&quot;,1030,&quot;男&quot;)var lcg = new Person(&quot;李长庚&quot;,11030,&quot;男&quot;)var ce = new Person(&quot;嫦娥&quot;,18,&quot;女&quot;)console.log(zbj)console.log(lcg)console.log(ce)</code></pre><p>使用同一个构造函数创建的对象，我们称为一类对象，也将一个构造函数称为一个类。我们将通过一个构造函数创建的对象，称为是该类的实例</p><h3><span id="18-instanceof">18、instanceof</span></h3><p>使用instanceof可以检查一个对象是否是一个类的实例</p><p>语法：<code>对象 instanceof 构造函数</code></p><p>如果是，则返回true，否则返回false</p><pre><code class="javascript">console.log(lcg instanceof Person) // trueconsole.log(dog instanceof Person) // false</code></pre><p>注意：</p><p>所有的对象都是Object的后代，所以任何对象和Object做instanceof检查时都会返回true</p><pre><code class="javascript">console.log(dog instanceof Object)</code></pre><p>缺陷</p><p>创建一个Person构造函数，在Person构造函数中， 为每一个对象都得加了一个sayName方法。<br>目前我们的方法是在构造固数内部创建的，也就是构造函数每执行一次就会创建一个新的sayName方法，也就是说实例的sayName都是唯一的。<br>这样就导致了构造函数执行一-次就会创建一个新的方法，执行10000次就会创建10000个新的方法，面10000个方法都是一 模一样的，这是完全没有必要，完全可以使所有的对象共享同一个方法。</p><pre><code class="javascript">function Person(name, age, gender){  this.name = name  this.age = age  this.gender = gender  // 向对象中添加一个方法  this.sayName = fun}// 将sayName()方法在全局作用域中定义function fun(){    alert(this.name)}var per = new Person(&quot;张三&quot;, 21, &quot;男&quot;)var per2 = new Person(&quot;李四&quot;, 18, &quot;男&quot;)</code></pre><p>将函数定义在全局作用域中，污染了全局作用域的命名空间，而且也不安全</p><h3><span id="19-原型prototype">19、原型prototype</span></h3><p>我们所创建的每一个函数，解析器都会向函数中添加一个属性prototype，这个属性对应着一个对象，这个对象就是我们所谓的原型对象</p><p>如果函数作为普通函数调用，prototype没有任何作用</p><p>当函数以构造函数的形式调用时，它所创建的对象中都会有一个隐含的属性，指向该构造函数的原型对象，我们可以通过<code>__proto__</code>来访问该属性</p><p>原型对象就相当于一个公共的区域，所有同一个类的实例都可以访问到这个原型对象，我们可以将对象中共有的内容，统一设置到原型对象中</p><p>当我们访问对象的一个属性或方法时，它会先在对象自身中寻找，如果有则直接使用，如果没有则会去原型对象中寻找，如果找到则直接使用</p><pre><code class="javascript">// 向MyClass的原型中添加属性aMyClass.prototype.a = 123// 向mc中添加a属性mc.a = &quot;我是mc中的a&quot;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200812192851.png" alt="image-20200812192849700"></p><p>mc、mc2、mc3是通过MyClass创建的对象，完整代码如下</p><pre><code class="javascript">function MyClass(){}MyClass.prototype.a = 123var mc = new MyClass()var mc2 = new MyClass()var mc3 = new MyClass()mc.a = &quot;我是mc中的a&quot;console.log(mc.a) // 由于mc中自己有a，因此输出自己的 我是mc中的aconsole.log(mc2.a)console.log(mc3.a) // 这两个自己没有a，因此会到原型中去找，找到了则输出 123</code></pre><p>建议：以后我们创建构造函数时，可以将这些对象共有的属性和方法。 统一添加加到构造函教的原型对象中，这样不用分别为每一个对像添加，也不会影响到到全局作用城，就可以使每个对象都具有这些属性和方法了</p><pre><code class="javascript">function Person(name, age, gender){  this.name = name  this.age = age  this.gender = gender}// 向原型中添加sayName方法Person.prototype.sayName = function(){    alert(this.name)}</code></pre><p>使用in检查对象中是否含有某个属性是，如果对象中没有，但是原型中有，也会返回true</p><pre><code class="javascript">console.log(&quot;a&quot; in mc2) // true</code></pre><p>可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性，该方法只有当对象自身中含有属性时，才会返回true</p><pre><code class="javascript">console.log(mc.hasOwnProperty(&quot;age&quot;)) // falseconsole.log(mc.hasOwnProperty(&quot;a&quot;)) // trueconsole.log(mc.__proto__.__proto__.hasOwnProperty(&quot;hasOwnProperty&quot;)) // true</code></pre><p>原型对象也是对象，所以它也有原型，当我们使用一个对象的属性或方法时，会先在自身中寻找，自身中如果有，则直接使用；如果没有则去原型对象中寻找，如果原型对象中有，则使用；如果没有则去原型的原型中寻找，直到找到0bject对象的原型，Object对象的原型没有原型，如果在Object原型中仍然没有找到，则返回undefined。</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200812195201.png" alt="image-20200812195159809"></p><p>当我们直接在页面中打印一个对象时，实际上输出的是对象的toString()方法的返回值</p><pre><code class="javascript">function Person(name, age, gender){  this.name = name  this.age = age  this.gender = gender}var per = new Person(&quot;张三&quot;, 21, &quot;男&quot;)var result = per.toString()console.log(&quot;result=&quot;+result) // result=[object Object]console.log(per.__proto__.__proto__.hasOwnProperty(&quot;toString&quot;)) // true</code></pre><p>如果我么希望在输出对象时不输出[object Object]，可以为对象添加一个toString方法</p><pre><code class="javascript">Person.prototype.toString = function(){  return &quot;Person[name=&quot;+this.name+&quot;,age=&quot;+this.age+&quot;,gender=&quot;+this.gender+&quot;]&quot;}</code></pre><h3><span id="20-垃圾回收gc">20、垃圾回收(GC)</span></h3><ul><li>就像人生活的时间长了会产生垃圾一样，程序运行过程中也会产生垃圾，这些垃圾积攒过多以后，会导致程序运行的速度过慢，所以我们需要一个垃圾回收的机制，来处理程序运行过程中产生的垃圾</li><li>当一个对象没有任何的变量或属性对它进行引用，此时我们将永远无法操作该对象，此时这种对象就是一个垃圾，这种对象过多会占用大量的内存空间，导致程序运行变慢，所以这种垃圾必须进行清理</li><li>在js中拥有自动的垃圾回收机制，会自动将这些垃圾对象从内存中销毁，我们不需要也不能进行垃圾回收的操作</li></ul><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200812201505.png" alt="image-20200812201503886"></p><ul><li>因此，我们需要做的只是将不再使用的对象设置为null即可</li></ul><h3><span id="21-数组">21、数组</span></h3><ul><li>数组(Array)也是一个对象</li><li>它和普通对象功能类似，也是用来存储一些值的</li><li>不同的是普通对象是使用字符串作为属性名的，而数组是使用数字来作为索引操作元素的</li><li>索引：从0开始的整数</li><li>数组的存储性能比普通对象要好，在开发中我们经常使用数组来存储一些数据</li></ul><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200812202048.png" alt="image-20200812202046775"></p><pre><code class="javascript">/* 创建数组对象 */var arr = new Array()console.log(typeof arr) // object// 使用构造函数创建数组时，可以同时添加元素，将要添加的元素作为构造函数的参数传递，元素之间用逗号隔开var arr1 = new Array(10,20,30)console.log(arr1)/* 向数组中添加元素：数组[索引]=值 */arr[0] = 10arr[1] = 20console.log(arr) // [10, 20]/* 读取数组中的元素：数组[索引] * 如果读取不存在的索引，不会报错，而是返回undefined*/console.log(arr[0]) // 10/* 获取数组的长度，使用length属性来获取 * 语法：数组.length * 对于连续的数组，使用length可以获取到数组的长度(元素的个数) * 对于非连续的数组，使用length会获取到数组的最大索引+1  尽量不要创建非连续的数组*/console.log(arr.length) // 2/* 修改length * 如果修改的length大于原长度，则多出的部分会空出来 * 如果修改的length小于原长度，则多出的元素会被删除*/arr.length = 10/* 向数组的最后一个位置添加元素 * 语法：数组[数组.length] = 值*/arr[arr.length] = 30arr[arr.length] = 40arr[arr.length] = 50</code></pre><p>使用字面量来创建数组</p><pre><code class="javascript">/* 使用字面量来创建数组 * 语法：[]*/var arr = []console.log(typeof arr, arr instanceof Array) // object true/* 使用字面量床架数组时，可以在创建时就指定数组中的元素 */var arr1 = [1,2,3,4,5,6]console.log(arr[3])// 数组中的元素可以是任意的数据类型arr = [&quot;hello&quot;,1,true,null,undefined]console.log(arr)// 也可以是对象var obj = {name:&quot;张三&quot;} arr[arr.length] = obj// 可以是函数arr = [function(){alert(1)}, function(){alert(2)}]arr[0]()// 数组中也可以放数组，如下这种数组称为二维数组arr = [[1,2,3], [3,4,5], [5,6,7]]console.log(arr[1])</code></pre><h3><span id="22-数组的方法">22、数组的方法</span></h3><table><thead><tr><th align="left">方法</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_concat_array.asp" target="_blank" rel="noopener">concat()</a></td><td align="left">连接两个或更多的数组，并返回结果。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_join.asp" target="_blank" rel="noopener">join()</a></td><td align="left">把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_pop.asp" target="_blank" rel="noopener">pop()</a></td><td align="left">删除并返回数组的最后一个元素</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_push.asp" target="_blank" rel="noopener">push()</a></td><td align="left">向数组的末尾添加一个或更多元素，并返回新的长度。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_reverse.asp" target="_blank" rel="noopener">reverse()</a></td><td align="left">颠倒数组中元素的顺序。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_shift.asp" target="_blank" rel="noopener">shift()</a></td><td align="left">删除并返回数组的第一个元素</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_slice_array.asp" target="_blank" rel="noopener">slice()</a></td><td align="left">从某个已有的数组返回选定的元素</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_sort.asp" target="_blank" rel="noopener">sort()</a></td><td align="left">对数组的元素进行排序</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_splice.asp" target="_blank" rel="noopener">splice()</a></td><td align="left">删除元素，并向数组添加新元素。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_tosource_array.asp" target="_blank" rel="noopener">toSource()</a></td><td align="left">返回该对象的源代码。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_toString_array.asp" target="_blank" rel="noopener">toString()</a></td><td align="left">把数组转换为字符串，并返回结果。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_toLocaleString_array.asp" target="_blank" rel="noopener">toLocaleString()</a></td><td align="left">把数组转换为本地数组，并返回结果。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_unshift.asp" target="_blank" rel="noopener">unshift()</a></td><td align="left">向数组的开头添加一个或更多元素，并返回新的长度。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_valueof_array.asp" target="_blank" rel="noopener">valueOf()</a></td><td align="left">返回数组对象的原始值</td></tr></tbody></table><p>push()</p><ul><li>该方法可以向数组的末尾添加一个或多个元素，并返回数组的新的长度</li><li>可以将要添加的元素作为方法的参数传递，这样这些元素将会自动添加到数组的末尾</li><li>该方法会将数组新的长度作为返回值返回</li></ul><pre><code class="javascript">var result = arr.push(&quot;HTML&quot;, &quot;CSS&quot;, &quot;JavaScript&quot;)console.log(arr, result)</code></pre><p>pop()</p><ul><li>该方法可以删除数组的最后一个元素，并将被删除的元素作为返回值返回</li></ul><pre><code class="javascript">result = arr.pop()console.log(arr, result)</code></pre><p>unshift()</p><ul><li>向数组开头添加一个或多个元素，并返回新的数组长度</li><li>向前边插入元素以后，其他的元素索引会依次调整</li></ul><pre><code class="javascript">console.log(arr)arr.unshift(&quot;HTTP&quot;)console.log(arr)</code></pre><p>shift()</p><ul><li>可以删除数组的第一个元素，并将被删除的元素作为返回值返回</li></ul><pre><code class="javascript">result = arr.shift()console.log(arr, result)</code></pre><p>数组的遍历</p><p>for循环</p><pre><code class="javascript">/* 所谓的遍历数组，就是将数组中的元素都取出来 * arr[0] * arr[1] * ... * arr[lenght-1]*/for(var i=0; i&lt;arr.length; i++){  console.log(arr[i])}</code></pre><p>forEach</p><p>一般我们都是使用for循环去遍历数组，js中还为我们提供了一个方法，用来遍历数组forEach()</p><p>这个方法只支持IE8以上的浏览器，IE8及以下的浏览器均不支持该方法，还是使用for</p><p>forEach( )方法需要一个函数作为参数</p><ul><li><p>像这种函数，由我们创建但是不由我们调用但最后执行了的，我们称为<strong>回调函数</strong></p></li><li><p>数组中有几个元素函数就会执行几次，每次执行时，浏览器会将遍历到的元素<br>以实参的形式传递进来， 我们可以来定义形参，来读取这些内容</p></li><li><p>浏览器会在回调函数中传递三个参数:</p><ul><li>第一个参数，就是当前正在遍历的元素</li><li>第二个参数，就是当前正在遍历的元素的索引</li><li>第三个参数，就是正在遍历的数组</li></ul><pre><code class="javascript">arr.forEach(function(value, index, obj){  console.log(value, index)})</code></pre></li></ul><p>slice()</p><ul><li>可以用来从数组提取指定元素</li><li>该方法不会改变元素数组，而是将截取到的元素封装到一个新数组中返回</li><li>参数，<ol><li>数取开始的位置的索引，包含开始索引</li><li>截取結東的位置的索引,不包含结束索引<ul><li>第二个参数可以省略不写,此时会截取从开始索引往后的所有元素</li><li>索引可以传递一个负值， 如果传递一个负值，则从后往前计算，-1——倒数第一个</li></ul></li></ol></li></ul><pre><code class="javascript">var result = arr.slice(1, 4)result = arr.slice(3)result = arr.slice(1, -2)</code></pre><p>splice()</p><ul><li>可以用于删除数组中的指定元素</li><li>使用splice()会影响到原数组，会将指定元素从原数组中删除，并将被删除的元素作为返回值返回</li><li>参数，<ul><li>第一个，表示开始位置的索引</li><li>第二个，表示删除的数量</li><li>第三个及以后，可以传递一些新的元素，这些元素将会自动插入到开始位置索引前边</li></ul></li></ul><pre><code class="javascript">var arr = [&quot;HTTP&quot;,&quot;HTML&quot;,&quot;CSS&quot;,&quot;JavaScript&quot;,&quot;jQuery&quot;,&quot;Less&quot;]var result = arr.splice(3,0,&quot;BootStrap&quot;)</code></pre><p>去除数组中重复的数字</p><pre><code class="javascript">for(var i=0; i&lt;arr.length; i++){  // console.log(i)  for(var j=i+1; j&lt;arr.length; j++){    if(arr[i] == arr[j]){      arr.splice(j,1)      j--  }}</code></pre><p>concat()</p><ul><li>可以连接两个或多个数组，并将新的数组返回</li><li>该方法不会对原数组产生影响</li></ul><pre><code class="javascript">var result = arr.concat(arr2)console.log(result)result = arr.concat(arr2,arr3,&quot;ES6&quot;,&quot;模块化&quot;,&quot;WebPack&quot;,&quot;AngularJS&quot;,&quot;React&quot;,&quot;VUE&quot;)</code></pre><p>join()</p><ul><li>可以将数组转换为一个字符串</li><li>该方法不会对原数组产生影响，而是将转换后的字符串作为结果返回</li><li>在join()中可以指定一个字符串作为参数，这个字符串将会成为数组中元素的连接符</li></ul><pre><code>result = arr.join() // 不传，默认用逗号分隔result = arr.join(&quot;&quot;) // 想让它们直接连接需要传空串(不是空格)result = arr.join(&quot;-&quot;) // 传什么就用什么连接</code></pre><p>reverse()</p><ul><li>用来反转数组(前面的元素到后面，后面的元素到前面)</li><li>该方法会直接修改原数组</li></ul><pre><code class="javascript">arr = [&quot;a&quot;,&quot;b&quot;,&quot;c&quot;,&quot;d&quot;]arr.reverse()</code></pre><p>sort()</p><ul><li>可以用来对数组中的元素进行排序</li><li>也会影响原数组，默认会按照Unicode编码进行排序</li></ul><pre><code class="javascript">arr.sort() // 排序arr.reverse() // 搭配起来进行反向排序</code></pre><ul><li>即使对于纯数字的数组，使用sort( )排序时，也会按照Unicode编码来排序，所以对数字进排序时，可能会得到错误的结果。</li><li>我们可以自己来指定排序的规则：我们可以在sort( )添加一个回调函数，来指定排序规则，回调函数中需要定义两个形参，浏览器将会分别使用数组中的元素作为实参去调用回调函数，使用哪个元素调用不确定，但是肯定的是在数组中a一定在b前边</li><li>浏览器会根据回调函数的返回值来决定元素的顺序，<ul><li>如果返回一个大于0的值，则元素会交换位置</li><li>如果返回一个小于0的值，则元素位置不变</li><li>如果返回一个0，则认为两个元素相等，也不交换位置</li></ul></li><li>如果需要升序排列，则返回a-b，如果需要降序排列，则返回b-a</li></ul><pre><code class="javascript">arr.sort(function(a,b){  /* if(a&gt;b){    return 1  }else if(a&lt;b){    return -1  }else{    return 0  } */  return a-b})</code></pre><h3><span id="23-函数的方法">23、函数的方法</span></h3><p>call()和apply()</p><ul><li>这两个方法都是函数对象的方法，需要通过函数对象来调用</li><li>当对函数调用这两个方法时，都会调用函数执行</li></ul><pre><code class="javascript">function fun(){  alert(&quot;fun()&quot;)}fun.apply()fun.call()fun()</code></pre><ul><li>在调用call()和apply()可以将一个对象指定为第一个参数，此时这个对象将会成为函数执行时的this</li></ul><pre><code class="javascript">function fun(){    alert(this.name)}var obj = {  name: &quot;obj&quot;,  sayName:function(){    alert(this.name)  }}// fun.call(obj)fun.apply(obj)</code></pre><ul><li>传递单个参数时没什么不同，但是多个参数：<ul><li>call()方法可以将实参在对象之后依次传递</li><li>apply()方法需要将实参封装到一个数组中统一传递</li></ul></li></ul><pre><code class="javascript">fun.call(obj,2,3)fun.apply(obj,[2,3])</code></pre><p>这里this又多了一种情况：</p><ol start="4"><li>使用call()和apply()调用时，this是指定的那个对象</li></ol><h3><span id="24-arguments">24、arguments</span></h3><p>在调用函数时，浏览器每次都会传递进两个隐含的参数：</p><ol><li><p>函数的上下文对象 this</p></li><li><p>封装实参的对象arguments</p><ul><li>arguments是一个类数组对象，它可以通过索引来操作数据，也可以获取长度</li></ul><pre><code class="javascript">function fun(){  // console.log(arguments instanceof Array) // false  console.log(Array.isArray(arguments)) // false}fun()</code></pre><ul><li>在调用函数时，我们所传递的实参都会在arguments中保存</li><li>arguments.length可以用来获取实参的长度</li><li>我们即使不定义形参，也可以通过arguments来使用实参，只不过比较麻烦<ul><li>arguments[0] 表示第一个实参</li><li>arguments[1] 表示第二个实参……</li></ul></li><li>它里边有一个属性叫做callee，这个属性对应一个函数对象，就是当前正在指向的函数的对象</li></ul><pre><code class="javascript">console.log(arguments.callee === fun) // true</code></pre><ul><li>由于是个伪数组(类数组)，因此不支持数组的方法，比如forEach</li></ul></li></ol><pre><code class="javascript"> /*函数参数可选，可以利用arguments来判断 arr：数组 length：长度，可选参数 callback：回调函数*/function(){  var arr = arguments[0]  if(arguments.length==2){    var fun = arguments[1]    fun()  }  if(arguments.length==3){    var length = arguments[1]    var fun = arguments[2]    fun()  }}</code></pre><h3><span id="25-date对象">25、Date()对象</span></h3><ul><li>在js中使用Date对象来表示一个时间</li></ul><pre><code class="javascript">// 创建一个Date对象var d = new Date()console.log(d) // Mon Apr 13 2020 09:09:04 GMT+0800 (中国标准时间)</code></pre><p>如果直接使用构造函数创建一个Date对象，则会封装为当前代码执行的时间</p><p>创建一个指定的时间对象，需要在构造函数中传递一个表示时间的字符串作为参数</p><p>日期的格式： <code>月份/日/年 时:分:秒</code></p><pre><code class="javascript">var d2 = new Date(&quot;4/16/2020 09:09:04&quot;) console.log(d2) // Thu Apr 16 2020 09:09:04 GMT+0800 (中国标准时间)</code></pre><table><thead><tr><th align="left">方法</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_Date.asp" target="_blank" rel="noopener">Date()</a></td><td align="left">返回当日的日期和时间。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getDate.asp" target="_blank" rel="noopener">getDate()</a></td><td align="left">从 Date 对象返回一个月中的某一天 (1 ~ 31)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getDay.asp" target="_blank" rel="noopener">getDay()</a></td><td align="left">从 Date 对象返回一周中的某一天 (0 ~ 6)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getMonth.asp" target="_blank" rel="noopener">getMonth()</a></td><td align="left">从 Date 对象返回月份 (0 ~ 11)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getFullYear.asp" target="_blank" rel="noopener">getFullYear()</a></td><td align="left">从 Date 对象以四位数字返回年份。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getYear.asp" target="_blank" rel="noopener">getYear()</a></td><td align="left">请使用 getFullYear() 方法代替。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getHours.asp" target="_blank" rel="noopener">getHours()</a></td><td align="left">返回 Date 对象的小时 (0 ~ 23)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getMinutes.asp" target="_blank" rel="noopener">getMinutes()</a></td><td align="left">返回 Date 对象的分钟 (0 ~ 59)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getSeconds.asp" target="_blank" rel="noopener">getSeconds()</a></td><td align="left">返回 Date 对象的秒数 (0 ~ 59)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getMilliseconds.asp" target="_blank" rel="noopener">getMilliseconds()</a></td><td align="left">返回 Date 对象的毫秒(0 ~ 999)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getTime.asp" target="_blank" rel="noopener">getTime()</a></td><td align="left">返回 1970 年 1 月 1 日至今的毫秒数。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getTimezoneOffset.asp" target="_blank" rel="noopener">getTimezoneOffset()</a></td><td align="left">返回本地时间与格林威治标准时间 (GMT) 的分钟差。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getUTCDate.asp" target="_blank" rel="noopener">getUTCDate()</a></td><td align="left">根据世界时从 Date 对象返回月中的一天 (1 ~ 31)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getUTCDay.asp" target="_blank" rel="noopener">getUTCDay()</a></td><td align="left">根据世界时从 Date 对象返回周中的一天 (0 ~ 6)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getUTCMonth.asp" target="_blank" rel="noopener">getUTCMonth()</a></td><td align="left">根据世界时从 Date 对象返回月份 (0 ~ 11)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getUTCFullYear.asp" target="_blank" rel="noopener">getUTCFullYear()</a></td><td align="left">根据世界时从 Date 对象返回四位数的年份。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getUTCHours.asp" target="_blank" rel="noopener">getUTCHours()</a></td><td align="left">根据世界时返回 Date 对象的小时 (0 ~ 23)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getUTCMinutes.asp" target="_blank" rel="noopener">getUTCMinutes()</a></td><td align="left">根据世界时返回 Date 对象的分钟 (0 ~ 59)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getUTCSeconds.asp" target="_blank" rel="noopener">getUTCSeconds()</a></td><td align="left">根据世界时返回 Date 对象的秒钟 (0 ~ 59)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_getUTCMilliseconds.asp" target="_blank" rel="noopener">getUTCMilliseconds()</a></td><td align="left">根据世界时返回 Date 对象的毫秒(0 ~ 999)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_parse.asp" target="_blank" rel="noopener">parse()</a></td><td align="left">返回1970年1月1日午夜到指定日期（字符串）的毫秒数。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_setDate.asp" target="_blank" rel="noopener">setDate()</a></td><td align="left">设置 Date 对象中月的某一天 (1 ~ 31)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_setMonth.asp" target="_blank" rel="noopener">setMonth()</a></td><td align="left">设置 Date 对象中月份 (0 ~ 11)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_setFullYear.asp" target="_blank" rel="noopener">setFullYear()</a></td><td align="left">设置 Date 对象中的年份（四位数字）。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_setYear.asp" target="_blank" rel="noopener">setYear()</a></td><td align="left">请使用 setFullYear() 方法代替。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_setHours.asp" target="_blank" rel="noopener">setHours()</a></td><td align="left">设置 Date 对象中的小时 (0 ~ 23)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_setMinutes.asp" target="_blank" rel="noopener">setMinutes()</a></td><td align="left">设置 Date 对象中的分钟 (0 ~ 59)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_setSeconds.asp" target="_blank" rel="noopener">setSeconds()</a></td><td align="left">设置 Date 对象中的秒钟 (0 ~ 59)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_setMilliseconds.asp" target="_blank" rel="noopener">setMilliseconds()</a></td><td align="left">设置 Date 对象中的毫秒 (0 ~ 999)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_setTime.asp" target="_blank" rel="noopener">setTime()</a></td><td align="left">以毫秒设置 Date 对象。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_setUTCDate.asp" target="_blank" rel="noopener">setUTCDate()</a></td><td align="left">根据世界时设置 Date 对象中月份的一天 (1 ~ 31)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_setUTCMonth.asp" target="_blank" rel="noopener">setUTCMonth()</a></td><td align="left">根据世界时设置 Date 对象中的月份 (0 ~ 11)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_setUTCFullYear.asp" target="_blank" rel="noopener">setUTCFullYear()</a></td><td align="left">根据世界时设置 Date 对象中的年份（四位数字）。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_setutchours.asp" target="_blank" rel="noopener">setUTCHours()</a></td><td align="left">根据世界时设置 Date 对象中的小时 (0 ~ 23)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_setUTCMinutes.asp" target="_blank" rel="noopener">setUTCMinutes()</a></td><td align="left">根据世界时设置 Date 对象中的分钟 (0 ~ 59)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_setUTCSeconds.asp" target="_blank" rel="noopener">setUTCSeconds()</a></td><td align="left">根据世界时设置 Date 对象中的秒钟 (0 ~ 59)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_setUTCMilliseconds.asp" target="_blank" rel="noopener">setUTCMilliseconds()</a></td><td align="left">根据世界时设置 Date 对象中的毫秒 (0 ~ 999)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_tosource_boolean.asp" target="_blank" rel="noopener">toSource()</a></td><td align="left">返回该对象的源代码。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_toString_date.asp" target="_blank" rel="noopener">toString()</a></td><td align="left">把 Date 对象转换为字符串。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_toTimeString.asp" target="_blank" rel="noopener">toTimeString()</a></td><td align="left">把 Date 对象的时间部分转换为字符串。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_toDateString.asp" target="_blank" rel="noopener">toDateString()</a></td><td align="left">把 Date 对象的日期部分转换为字符串。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_toGMTString.asp" target="_blank" rel="noopener">toGMTString()</a></td><td align="left">请使用 toUTCString() 方法代替。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_toUTCString.asp" target="_blank" rel="noopener">toUTCString()</a></td><td align="left">根据世界时，把 Date 对象转换为字符串。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_toLocaleString.asp" target="_blank" rel="noopener">toLocaleString()</a></td><td align="left">根据本地时间格式，把 Date 对象转换为字符串。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_toLocaleTimeString.asp" target="_blank" rel="noopener">toLocaleTimeString()</a></td><td align="left">根据本地时间格式，把 Date 对象的时间部分转换为字符串。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_toLocaleDateString.asp" target="_blank" rel="noopener">toLocaleDateString()</a></td><td align="left">根据本地时间格式，把 Date 对象的日期部分转换为字符串。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_utc.asp" target="_blank" rel="noopener">UTC()</a></td><td align="left">根据世界时返回 1970 年 1 月 1 日 到指定日期的毫秒数。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_valueOf_date.asp" target="_blank" rel="noopener">valueOf()</a></td><td align="left">返回 Date 对象的原始值。</td></tr></tbody></table><p>getTime()</p><ul><li>获取当前日期对象的时间戳</li><li>时间戳，指的是从格林威治标准时间的1970年1月1日 0时0分0秒到当前日期所花费的毫秒数(1秒=1000毫秒)</li></ul><pre><code class="javascript">var time = d2.getTime()console.log(time) // 1586999344000</code></pre><ul><li>计算机底层在保存时间时使用的是时间戳，可以利用时间关系进行换算</li></ul><pre><code class="javascript">console.log(time/1000/60/60/25/365)</code></pre><ul><li>以前利用过console.time()、console.timeEnd()测试代码的执行性能，现在可以利用Date的函数实现</li></ul><pre><code class="javascript">// 获取当前的时间戳var start  = Date.now()for(var i=0; i&lt;100; i++){  console.log(i)}var end = Date.now()console.log(&quot;执行了：&quot;+(end-start)+&quot;毫秒&quot;)</code></pre><h3><span id="26-math对象">26、Math对象</span></h3><p>Math 对象用于执行数学任务。</p><p>Math 对象并不像 Date 和 String 那样是对象的类，因此没有构造函数 Math()，像 Math.sin() 这样的函数只是函数，不是某个对象的方法。它属于一个工具类，无需创建它，通过把 Math 作为对象使用就可以调用其所有属性和方法。</p><p>Math 对象属性</p><table><thead><tr><th align="left">属性</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_e.asp" target="_blank" rel="noopener">E</a></td><td align="left">返回算术常量 e，即自然对数的底数（约等于2.718）。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_ln2.asp" target="_blank" rel="noopener">LN2</a></td><td align="left">返回 2 的自然对数（约等于0.693）。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_ln10.asp" target="_blank" rel="noopener">LN10</a></td><td align="left">返回 10 的自然对数（约等于2.302）。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_log2e.asp" target="_blank" rel="noopener">LOG2E</a></td><td align="left">返回以 2 为底的 e 的对数（约等于 1.414）。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_log10e.asp" target="_blank" rel="noopener">LOG10E</a></td><td align="left">返回以 10 为底的 e 的对数（约等于0.434）。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_pi.asp" target="_blank" rel="noopener">PI</a></td><td align="left">返回圆周率（约等于3.14159）。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_sqrt1_2.asp" target="_blank" rel="noopener">SQRT1_2</a></td><td align="left">返回返回 2 的平方根的倒数（约等于 0.707）。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_sqrt2.asp" target="_blank" rel="noopener">SQRT2</a></td><td align="left">返回 2 的平方根（约等于 1.414）。</td></tr></tbody></table><p>Math 对象方法</p><table><thead><tr><th align="left">方法</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_abs.asp" target="_blank" rel="noopener">abs(x)</a></td><td align="left">返回数的绝对值。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_acos.asp" target="_blank" rel="noopener">acos(x)</a></td><td align="left">返回数的反余弦值。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_asin.asp" target="_blank" rel="noopener">asin(x)</a></td><td align="left">返回数的反正弦值。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_atan.asp" target="_blank" rel="noopener">atan(x)</a></td><td align="left">以介于 -PI/2 与 PI/2 弧度之间的数值来返回 x 的反正切值。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_atan2.asp" target="_blank" rel="noopener">atan2(y,x)</a></td><td align="left">返回从 x 轴到点 (x,y) 的角度（介于 -PI/2 与 PI/2 弧度之间）。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_ceil.asp" target="_blank" rel="noopener">ceil(x)</a></td><td align="left">对数进行上舍入。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_cos.asp" target="_blank" rel="noopener">cos(x)</a></td><td align="left">返回数的余弦。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_exp.asp" target="_blank" rel="noopener">exp(x)</a></td><td align="left">返回 e 的指数。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_floor.asp" target="_blank" rel="noopener">floor(x)</a></td><td align="left">对数进行下舍入。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_log.asp" target="_blank" rel="noopener">log(x)</a></td><td align="left">返回数的自然对数（底为e）。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_max.asp" target="_blank" rel="noopener">max(x,y)</a></td><td align="left">返回 x 和 y 中的最高值。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_min.asp" target="_blank" rel="noopener">min(x,y)</a></td><td align="left">返回 x 和 y 中的最低值。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_pow.asp" target="_blank" rel="noopener">pow(x,y)</a></td><td align="left">返回 x 的 y 次幂。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_random.asp" target="_blank" rel="noopener">random()</a></td><td align="left">返回 0 ~ 1 之间的随机数。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_round.asp" target="_blank" rel="noopener">round(x)</a></td><td align="left">把数四舍五入为最接近的整数。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_sin.asp" target="_blank" rel="noopener">sin(x)</a></td><td align="left">返回数的正弦。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_sqrt.asp" target="_blank" rel="noopener">sqrt(x)</a></td><td align="left">返回数的平方根。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_tan.asp" target="_blank" rel="noopener">tan(x)</a></td><td align="left">返回角的正切。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_tosource_math.asp" target="_blank" rel="noopener">toSource()</a></td><td align="left">返回该对象的源代码。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/jsref_valueof_math.asp" target="_blank" rel="noopener">valueOf()</a></td><td align="left">返回 Math 对象的原始值。</td></tr></tbody></table><p>Math.random()</p><ul><li>可以用来生成一个0~1之间的随机数</li></ul><p><code>console.log(Math.random())</code></p><ul><li>生成一个0~10的随机数</li></ul><p><code>Math.random()*10</code> –&gt; 整数 <code>Math.round(Math.random()*10)</code></p><ul><li>生成一个0~x之间的随机整数</li></ul><p><code>Math.round(Math.random()*x)</code></p><ul><li>生成一个x~y之间的随机整数</li></ul><p><code>Math.round(Math.random()*(y-x)+x)</code></p><p>Math.max() 可以获取多个数中的最大值</p><p>Math.min() 可以获取多个数中的最小值</p><pre><code class="javascript">var max = Math.max(10,45,5,30,100,50)var min = Math.min(10,45,5,30,100,50)console.log(max,min)</code></pre><p>Math.pow(x,y) 返回x的y次幂</p><p>Math.sqrt(x) 返回x的平方根</p><pre><code class="javascript">console.log(Math.pow(2,3)) // 8console.log(Math.sqrt(2)) // 1.4142135623730951</code></pre><h3><span id="27-包装类">27、包装类</span></h3><p>基本数据类型：<code>String</code> <code>Number</code> <code>Boolean</code> <code>Null</code> <code>Undefined</code></p><p>引用数据类型：<code>Object</code></p><p>在js中为我们提供了三个包装类，通过这三个包装类可以将基本数据类型的数据转换为对象</p><ul><li>String() 可以将基本数据类型字符串转换为String对象</li><li>Number() 可以将基本数据类型的数字转换为Number对象</li><li>Boolean() 可以将基本数据类型的布尔值转换为Boolean对象</li></ul><pre><code class="javascript">var num = new Number(3)console.log(typeof num) // object</code></pre><p>但是往意，我们在实际应用中不会使用基本数据类型的对象，如果使用基本数据类型的对象， 在做一些比较时可能会带来一些不 可预期的结果</p><p>方法和属性只能添加给对象，不能添加给基本数据类型</p><p>当我们对一些基本数据类型的值去调用属性和方法时，浏览器会临时使用包装类将其转换为对象，然后在调用对象的属性和方法，调用完以后，再将其转换为基本数据类型</p><h3><span id="28-字符串相关">28、字符串相关</span></h3><p>创建一个字符串</p><pre><code class="javascript">var str = &quot;Hello JavaScript&quot;</code></pre><p>在底层字符串是以字符数组的形式保存的 <code>[&#39;H&#39;,&quot;e&quot;,&quot;l&quot;,&quot;l&quot;,&quot;o&quot;.&quot; &quot;,&quot;J&quot;,&quot;a&quot;]</code></p><pre><code class="javascript">console.log(str[1])</code></pre><p>length属性 </p><ul><li>可以用来获取字符串的长度</li></ul><pre><code class="javascript">console.log(str.length)</code></pre><p>charAt()</p><ul><li>可以返回字符串中指定位置的字符</li><li>根据索引获取指定的字符</li></ul><pre><code class="javascript">var result = str.charAt(6)console.log(result) // J</code></pre><p>charCodeAt()</p><ul><li>获取指定位置字符的字符编码(Unicode编码)</li></ul><pre><code class="javascript">console.log(str.charCodeAt(0)) // 72</code></pre><p>String.formCharCode()</p><ul><li>根据字符编码去获取字符</li></ul><pre><code class="javascript">result = String.fromCharCode(20045) // 乍</code></pre><p>concat()</p><ul><li>可以用来连接两个或多个字符串</li><li>作用和<code>+</code>一样</li></ul><pre><code class="javascript">result = str.concat(&quot;hello&quot;,&quot;javascript&quot;)</code></pre><p>indexof()</p><ul><li>该方法可以检索一个字符串中是否含有指定内容</li><li>如果字符串中含有该内容，则会返回其第一次出现的索引，如果没有找到指定的内容，则会返回-1</li><li>可以指定第二个参数，表示开始查找的位置</li></ul><pre><code class="javascript">result = str.indexOf(&quot;h&quot;,1)</code></pre><p>lastIndexOf()</p><ul><li>该方法的用法和indexOf()一样，不同的是indexOf是从前往后找，而lastIndexOf是从后往前找</li><li>也可以指定开始查找的位置</li></ul><p>slice()</p><ul><li>可以从字符串中截取指定的内容</li><li>不会影响原字符串，而是将截取到的内容返回</li><li>参数<ul><li>第一个，开始位置的索引(包括开始位置)</li><li>第二个，结束位置的索引(不包括结束位置)<ul><li>如果省略第二个参数，则会截取到后边所有的</li><li>也可以传递一个负数作为参数，负数的话将会从后边计算</li></ul></li></ul></li></ul><pre><code class="javascript">str = &quot;abcdefghijklmn&quot;result = str.slice(1)result = str.slice(1,3)result = str.slice(3,-3)</code></pre><p>substring()</p><ul><li>可以用来截取一个字符串，和slice()类似</li><li>参数：<ul><li>第一个，开始截取位置的索引(包括开始位置)</li><li>第二个：结束位置的索引(不包括结束位置)</li><li>不同的是这个方法不能接受负值作为参数。如果传递了一个负值，则默认使用0</li><li>而且他还会自动调整參数的位置，如果第二个参数小于第-一个， 则自动交换</li></ul></li></ul><pre><code class="javascript">result = str.substring(0,1);</code></pre><p>substr()</p><ul><li>用来截取字符串</li><li>参数<ul><li>截取开始位置的索引</li><li>截取的长度</li></ul></li></ul><pre><code class="javascript">result = str.substr(3,2)</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200813100827.png" alt="image-20200813100825993"></p><p>split()</p><ul><li>可以将一个字符串拆分为一个数组</li><li>参数<ul><li>需要一个字符串作为参数，将会根据这个字符串去拆分为数组</li><li>如果传递一个空串作为参数，则会将每个字符都拆分为数组中的一个元素</li></ul></li></ul><pre><code class="javascript">str = &quot;string&quot;result = str.split(&quot;&quot;) // [&quot;s&quot;, &quot;t&quot;, &quot;r&quot;, &quot;i&quot;, &quot;n&quot;, &quot;g&quot;]str = &quot;abc,bcd,efg,hij&quot;result = str.split(&quot;,&quot;) // [&quot;abc&quot;, &quot;bcd&quot;, &quot;efg&quot;, &quot;hij&quot;]console.log(Array.isArray(result)) // true</code></pre><p>toUpperCase()</p><ul><li>将一个字符串转换为大写并返回</li></ul><pre><code class="javascript">str = &quot;aBcdefg&quot;result = str.toUpperCase() // ABCDEFG</code></pre><p>toLowerCase()</p><ul><li>将一个字符串转换为小写并返回</li></ul><pre><code class="javascript">result = result.toLowerCase() // abcdefg</code></pre><p>字符串和正则相关方法(正则表达式看下一节)</p><p>splite()</p><ul><li>可以将一个字符串拆分为一个数组</li><li>方法中可以传递一个正则表达式作为参数，这样方法将会根据正则表达式去拆分字符串</li></ul><pre><code class="javascript">// 根据任意字母来将字符串财富var result = str.split(/[A-z]/)</code></pre><ul><li>这个方法即使不指定全局匹配，也会全部拆分</li></ul><p>search()</p><ul><li>可以搜索字符串中是否含有指定内容</li><li>如果搜索到指定内容，则会返回第一次出现的索引，如果没有搜索到则返回-1</li><li>它可以接受一个正则表达式作为参数，然后会根据正则表达式去检索字符串</li></ul><pre><code class="javascript">str = &quot;hello hello aec afc&quot;// 搜索字符串中是否含有abc或aec或afcresult = str.search(/a[bef]c/)</code></pre><ul><li>search()只会查找第一个，即使设置全局匹配也没用</li></ul><p>match()</p><ul><li>可以根据正则表打死，从一个字符串中将符合条件的内容提取出来</li><li>默认情况下match只会找到第一个符合要求的内容，找到以后就停止检索，我们可以设置正则表达式为全局匹配模式，这样就会匹配到所有的内容了</li></ul><pre><code class="javascript">str = &quot;1a2b3c4d5e6f7g8h9i&quot;result = str.match(/[A-z]/g)</code></pre><ul><li>可以为一个正则表达式设置多个匹配模式，且顺序无所谓</li></ul><pre><code class="javascript">result = str.match(/[A-z]/ig)</code></pre><ul><li>match()会将匹配到的内容封装到一个数组中返回，即使只查询到一个结果</li></ul><p>replace()</p><ul><li>可以将字符串中指定内容替换为新的内容</li><li>参数<ol><li>被替换的内容，可以接受一个正则表达式作为参数</li><li>新的内容</li></ol></li><li>默认只会替换一个</li></ul><pre><code class="javascript">result = str.replace(/a/gi &quot;@_@&quot;)</code></pre><h3><span id="29-正则表达式">29、正则表达式</span></h3><p>GitHub上有个很好的学习教程<a href="https://github.com/ziishaned/learn-regex/blob/master/translations/README-cn.md" target="_blank" rel="noopener">learn-regex</a>，<a href="https://github.com/cdoco/learn-regex-zh" target="_blank" rel="noopener">翻译版</a></p><ul><li><p>正则表达式用于定义一些字符串的规则，计算机可以根据正则表达式，来检查一个字符串是否符合规则，将字符串中符合规则的内容提取出来</p></li><li><p>创建正则表达式的对象</p><ul><li>语法： <code>var 变量  = new RegExp(&quot;正则表达式&quot;,&quot;匹配模式&quot;)</code></li><li>使用typeof检查正则对象，会返回object</li></ul><pre><code class="javascript">var reg = new RegExp(&quot;a&quot;)var str = &quot;a&quot;console.log(typeof reg)</code></pre></li><li><p>正则表达式的方法 test()</p><ul><li>使用这个方法可以检查一个字符串是否符合正则表达式的规则，如果符合则返回true，否则返回false</li></ul><pre><code class="javascript">var result = reg.test(str)console.log(result)console.log(reg.test(&quot;Abcbc&quot;))</code></pre></li><li><p>在构造函数中可以传递一个匹配模式作为第二个参数，可以是：</p><ul><li>i 忽略大小写</li><li>g 全局匹配模式</li></ul></li><li><p>使用字面量来创建正则表达式，语法：<code>var 变量 = /正则表达式/匹配模式</code></p></li></ul><pre><code class="javascript">// var reg = new RegExp(&quot;a&quot;,&quot;i&quot;)reg = /a/iconsole.log(typeof reg)console.log(reg.test(&quot;abc&quot;))</code></pre><p>使用字面量的方式创建更加简单，使用构造函数创建更加灵活</p><ul><li>使用<code>|</code>表示或者的意思</li></ul><pre><code class="javascript">// 创建一个正则表达式，检查一个字符串中是否含有a或breg = /a|b/</code></pre><ul><li><code>[]</code>里的内容也是或的关系 <code>[ab]==a|b</code><ul><li><code>[a-z]</code> 任意小写字母</li><li><code>[A-Z]</code> 任意大写字母</li><li><code>[A-z]</code> 任意字母</li><li><code>[0-9]</code> 任意数字</li></ul></li></ul><pre><code class="javascript">reg = /[ab]/// 检查一个字符串中是否含有abc或adc或aecreg = /a[bde]c/</code></pre><ul><li><code>[^ ]</code>除了</li></ul><pre><code class="javascript">reg = /[^ab]/console.log(reg.test(&quot;abc&quot;))</code></pre><ul><li>量词<ul><li>通过量词可以设置一个内容出现的次数</li><li>量词只对她前边的一个内容起作用</li></ul></li><li><code>{n}</code> 正好出现n次</li><li><code>{n,m}</code> 出现m~n次</li><li><code>{n,}</code> 出现m次以上</li></ul><pre><code class="javascript">reg = /a{3}/console.log(reg.test(&quot;aaabc&quot;))reg = /(ab){3}/reg = /ab{1,3}c/reg = /a{2,}/</code></pre><ul><li><code>+</code> 至少一个，相当于<code>{1,}</code></li><li><code>*</code> 0个或多个，相当于<code>{0,}</code></li><li><code>?</code> 0个或1个，相当于<code>{0,1}</code></li></ul><pre><code class="javascript">reg = /ab+c/reg = /ab*c/reg = /ab?c/</code></pre><ul><li><code>^</code> 表示开头</li><li><code>$</code> 表示结尾</li></ul><pre><code class="javascript">reg = /^a/reg = /a$/</code></pre><ul><li>在表达式中同时使用<code>^</code>、<code>$</code>则要求字符串必须完全符合正则表达式</li></ul><pre><code class="javascript">reg = /^a$/console.log(reg.test(&quot;a&quot;))reg = /^a|a$/</code></pre><ul><li><code>.</code>表示任意字符</li></ul><pre><code class="javascript">// 在正则表达式中使用\作为转义字符 \.表示.var reg = /\./console.log(reg.test(&quot;b.&quot;))reg = /\\/console.log(reg.test(&quot;b.\\&quot;))// 使用构造函数时，由于它的参数是一个字符串，而\是字符串中转义字符，如果要使用\则需要使用\\来代替reg = new RegExp(&quot;\\.&quot;)reg = new RegExp(&quot;\\\\&quot;)</code></pre><ul><li><code>\w</code> 任意字母、数字、下划线(<code>_</code>)     <code>[A-z0-9_]</code></li><li><code>\W</code> 除了字母、数字、下划线(<code>_</code>)     <code>[^A-z0-9_]</code></li><li><code>\d</code> 任意的数字</li><li><code>\D</code> 除了数字</li><li><code>\s</code> 空格</li><li><code>\S</code> 除了空格</li><li><code>\b</code> 单词边界</li><li><code>\B</code> 除了单词边界</li></ul><pre><code class="javascript">reg = /\bchild\b/console.log(reg.test(&quot;child ren&quot;))</code></pre><ul><li>创建一个正则表达式，用来检查一个字符串是否是合法手机号</li></ul><pre><code class="javascript">/*手机号的规则： 1 3 511111111 1.以1开头2.第二位3-9任意数字3.三位以后任意数字9个^1 [3-9] [0-9]{9}$*/var phoneStr = &quot;13511111111&quot;var phoneReg = /^1[3-9][0-9]{9}$/</code></pre><ul><li>去除首尾的空格 </li></ul><pre><code class="javascript">// 开头str = str.replace(/^\s*/, &quot;&quot;)// 结尾str = str.replace(/\s*$/, &quot;&quot;)// 开头和结尾 /^\s*|\s*$/gstr = str.replace(/^\s*|\s*$/g, &quot;&quot;)</code></pre><ul><li>合法电子邮件</li></ul><pre><code class="javascript">/*hello  .host  @  abc .com.cn任意字母数字下划线  .任意字母数字下划线 @  任意字母数字 .任意字母(2~5位)  .任意字母(2~5位) \w{3,}  (\.\w+)*  @  [A-z0-9]+  (\.[A-z]{2,5}){1,2}*/var emailReg = /^\w{3,}(\.\w+)*@[A-z0-9]+(\.[A-z]{2,5}){1,2}$/var email = &quot;abc.hello@163.com&quot;console.log(emailReg.test(email))</code></pre><h2><span id="三-dom">三、DOM</span></h2><h3><span id="1-什么是dom">1、什么是DOM</span></h3><ul><li><p>全称Document Object Model 文档对象模型</p></li><li><p>JS中通过DOM来<strong>对HTML文档进行操作</strong>。只要了解了DOM就可以随心所欲地操作WEB页面。</p></li><li><p>文档：表示的就是整个HTML网页文档</p></li><li><p>对象：表示将网页中的每一个部分都转换为了一个对象</p></li><li><p>模型：使用模型来表示对象之间的关系，这样方便我们获取对象</p></li><li><p>文档对象模型</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200813113821.png" alt="image-20200813113819936"></p></li></ul><h3><span id="2-节点">2、节点</span></h3><ul><li>节点Node，是构成我么网页的最基本的组成部分，网页中的每一个部分都可以称为是一个节点。比如：html标签、属性、文本、注释、整个文档等都是一个个节点。</li><li>虽然都是节点，但实际上他们的具体类型是不同的。比如：标签称为元素节点、属性称为属性节点、文本称为文本节点、文档称为文档节点</li></ul><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200813114231.png" alt="image-20200813114229455"></p><ul><li>节点的类型不同，属性和方法也都不尽相同</li></ul><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200813115721.png" alt="image-20200813115713878"></p><h3><span id="3-节点分类和dom查询">3、节点分类和DOM查询</span></h3><p>(1) 节点分类</p><ul><li><p>文档节点(document)</p><ul><li>文档节点document ,代表的是整个HTML文档，网页中的所有节点都是它的子节点。</li><li>document对象作为window对象的属性存在的，我们不用获取可以直接使用。</li><li>通过该对象我们可以在整个文档访问内查找节对象，并可以通过该对象创建各种节点对象。</li></ul></li><li><p>元素节点(Element)</p><ul><li>HTML中的各种标签都是元素节点，这也是我们最常用的一个节点。</li><li>浏览器会将页面中所有的标签都转换为一个元素节点，我们可以通过document的方法来获取元素节点。</li><li>比如: <code>document.getElementById)</code>根据id属性值获取-个元素节点对象。</li></ul></li><li><p>文本节点(Text)</p><ul><li>文本节点表示的是HTML标签以外的文本内容，任意非HTML的文本都是文本节点。</li><li>它包括可以字面解释的纯文本内容。</li><li>文本节点一般是作为元素节点的子节点存在的。</li><li>获取文本节点时, 一般先要获取元素节点，再通过元素节点获取文本<br>节点。</li><li>例如: <code>元素节点.firstChild;</code> 获取元素节点的第一个子节点，一般为文本节点</li></ul></li><li><p>属性节点(Attr)</p><ul><li>属性节点表示的是标签中的一个一个的属性，这里要注意的是属性节点并非是元素节点的子节点，而是元素节点的一部分。</li><li>可以通过元素节点来获取指定的属性节点。</li><li>例如: <code>元素节点.getAttributeNode(&quot;属性名&quot;);</code></li><li>注意：我们一般不使用属性节点。</li></ul></li></ul><p>(2) 获取元素节点</p><p>通过document对象调用</p><ul><li><code>getElementById()</code>  通过id属性获取一个元素节点对象</li><li><code>getElementsByTagName()</code>  通过标签名获取一组元素节点对象</li><li><code>getElementsByName()</code>  通过<code>name</code>属性获取一组元素节点对象</li></ul><pre><code class="javascript">var btn = document.getElementById(&quot;btn&quot;)var lis = document.getElementsTagName(&quot;li&quot;)var gender = document.getElementsByName(&quot;gender&quot;) // &lt;input name=&quot;gender&quot; type=&quot;radio&quot; value=&quot;男&quot; /&gt;</code></pre><p>(3) 获取元素节点的子节点</p><p>通过具体的元素节点调用</p><ul><li><code>getElementsByTagName()</code>  方法，返回当前节点的指定标签名后代节点</li></ul><pre><code class="javascript">// 查找#city下的所有li节点var list = city.getElementsByTagName(&quot;li&quot;)</code></pre><ul><li><code>childNodes</code>  属性，表示当前节点的所有子节点</li></ul><p>childNodes属性会获取包括文本节点在内的所有节点，包括DOM标签与标签间空白也会当成文本节点</p><pre><code class="javascript">// 返回#city的所有子节点var cns = city.childNodes</code></pre><p><code>children</code>属性可以获取当前元素的所有子元素</p><pre><code class="javascript">var cns2 = city.children</code></pre><ul><li><code>firstChild</code>  属性，表示当前节点的第一个子节点(包括空白文本节点)</li></ul><pre><code class="javascript">var phone = document.getElementById(&quot;phone&quot;)// 返回#phone的第一个子节点phone.childNodes[0]var fir = phone.firstChild</code></pre><p><code>firstElementChild</code>获取当前元素的第一个子元素，但是不支持IE8及以下的浏览器</p><pre><code class="javascript">fir = phone.firestElementChild</code></pre><ul><li><code>lastChild</code>  属性，表示当前节点的最后一个子节点</li></ul><p>(4) 获取父节点和兄弟节点</p><p>通过具体的节点调用</p><ul><li><code>parentNode</code>  属性，表示当前节点的父节点</li><li><code>previousSibling</code> 属性，表示当前节点的前一个兄弟节点</li><li><code>nextSiblig</code>  属性，表示当前节点的后一个兄弟节点</li></ul><pre><code class="javascript">&lt;button id=&quot;btn&quot;&gt;我是一个按钮&lt;/button&gt;&lt;script type=&quot;text/javascript&quot;&gt;  // 浏览器已经为我们提供文档节点对象，这个对象是window属性，可以在页面中直接使用，文档节点代表的是整个网页  // console.log(document)  var btn = document.getElementById(&#39;btn&#39;)  // 修改按钮的文字  btn.innerHTML = &quot;I&#39;m Button&quot;&lt;/script&gt;</code></pre><p>(5) DOM查询的其他方法</p><ul><li><p><code>querySelector()</code>  该方法需要一个选择器作为参数，可以根据一个CSS选择器来查询一个元素节点对象，IE8中也可以使用，总是会返回唯一的一个元素，如果满足条件的元素有多个，那么它只会返回第一个</p></li><li><p><code>querySelectorAll()</code>  该方法和querySelector()用法类似，不同的是它会将符合条件的元素封装到一个数组中返回，即使符合条件的元素之后一个，它也会返回数组</p></li><li><p>获取body标签 <code>document.getElementsByTagName(&quot;body&quot;)[0]</code> ，其实在document中有一个属性，它保存的就是body的引用<code>document.body</code></p></li><li><p><code>document.documentElement</code>保存的是html根标签</p></li><li><p><code>document.all</code>代表页面中所有元素，<code>document.getElementsByTagName(&quot;*&quot;)</code></p></li><li><p><code>getElementsByClassName()</code>可以根据元素的class属性值查询一组元素节点对象，但是该方法不支持IE8及以下的浏览器</p></li></ul><pre><code class="javascript">var btn = document.querySelector(&quot;#btn&quot;)var body = document.bodyvar html = document.documentElementvar all = document.allvar box = document.getElementsByClassName(&quot;box&quot;)[0]</code></pre><h3><span id="3-事件">3、事件</span></h3><ul><li><p>事件，就是文档或浏览器窗口中发生的一些特定的交互瞬间/交互行为。</p></li><li><p>JavaScript与HTML之间的交互是通过事件实现的。</p></li><li><p>对于Web应用来说,有下面这些代表性的事件：点击某个元素、将鼠标移动至某个<br>元素上方、按下键盘上某个键、关闭窗口等等。</p></li><li><p>可以在事件对应的属性中设置一些js代码，这样当事件被触发时，这些代码将会执行。</p></li></ul><table><thead><tr><th align="left">事件句柄</th><th align="left">此事件发生在何时…</th></tr></thead><tbody><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onabort.asp" target="_blank" rel="noopener">onabort</a></td><td align="left">图像的加载被中断。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onblur.asp" target="_blank" rel="noopener">onblur</a></td><td align="left">元素失去焦点。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onchange.asp" target="_blank" rel="noopener">onchange</a></td><td align="left">域的内容被改变。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onclick.asp" target="_blank" rel="noopener">onclick</a></td><td align="left">当用户点击某个对象时调用的事件句柄。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_ondblclick.asp" target="_blank" rel="noopener">ondblclick</a></td><td align="left">当用户双击某个对象时调用的事件句柄。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onerror.asp" target="_blank" rel="noopener">onerror</a></td><td align="left">在加载文档或图像时发生错误。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onfocus.asp" target="_blank" rel="noopener">onfocus</a></td><td align="left">元素获得焦点。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onkeydown.asp" target="_blank" rel="noopener">onkeydown</a></td><td align="left">某个键盘按键被按下。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onkeypress.asp" target="_blank" rel="noopener">onkeypress</a></td><td align="left">某个键盘按键被按下并松开。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onkeyup.asp" target="_blank" rel="noopener">onkeyup</a></td><td align="left">某个键盘按键被松开。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onload.asp" target="_blank" rel="noopener">onload</a></td><td align="left">一张页面或一幅图像完成加载。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onmousedown.asp" target="_blank" rel="noopener">onmousedown</a></td><td align="left">鼠标按钮被按下。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onmousemove.asp" target="_blank" rel="noopener">onmousemove</a></td><td align="left">鼠标被移动。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onmouseout.asp" target="_blank" rel="noopener">onmouseout</a></td><td align="left">鼠标从某元素移开。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onmouseover.asp" target="_blank" rel="noopener">onmouseover</a></td><td align="left">鼠标移到某元素之上。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onmouseup.asp" target="_blank" rel="noopener">onmouseup</a></td><td align="left">鼠标按键被松开。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onreset.asp" target="_blank" rel="noopener">onreset</a></td><td align="left">重置按钮被点击。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onresize.asp" target="_blank" rel="noopener">onresize</a></td><td align="left">窗口或框架被重新调整大小。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onselect.asp" target="_blank" rel="noopener">onselect</a></td><td align="left">文本被选中。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onsubmit.asp" target="_blank" rel="noopener">onsubmit</a></td><td align="left">确认按钮被点击。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/event_onunload.asp" target="_blank" rel="noopener">onunload</a></td><td align="left">用户退出页面。</td></tr></tbody></table><pre><code class="html">&lt;button id=&quot;btn&quot; onclick=&quot;alert(&#39;点我干嘛&#39;)&quot;&gt;我是一个按钮&lt;/button&gt;</code></pre><p>这种写法结构和行为耦合，不方便维护，不推荐使用</p><h3><span id="4-onclick">4、onclick</span></h3><p>可以为按钮的对应事件绑定处理函数的形式来响应事件，这样当事件被触发时，其对应的函数将会被调用</p><pre><code class="html">&lt;button id=&quot;btn&quot;&gt;我是一个按钮&lt;/button&gt;&lt;script&gt;  // 获取按钮对象  var btn = document.getElementById(&#39;btn&#39;)  // 绑定一个单击事件  btn.onclick = function(){    alert(&#39;你还点&#39;)  }&lt;/script&gt;</code></pre><p>像这种单击事件绑定的函数，我们称为单击响应函数</p><h3><span id="4-文档加载-onload">4、文档加载 onload</span></h3><p>浏览器在加载一个页面时，是按照自上向下的顺序加载的，读取到一行就运行一行，如果将script标签写到页面的上边，在代码执行时，页面还没有加载，页面没有加载DOM对象也没有加载，会导致无法获取到DOM对象</p><p>将js代码编写到页面的下方就是为了可以在页面加载完毕以后再执行js代码</p><p>但是，一定要放在上面呢，js也提供了一个事件</p><p>onload事件会在整个页面加载完成之后才触发</p><p>为window绑定一个onload事件，该事件对应的响应函数将会在页面加载完成之后执行，这样可以确保我们的代码执行时，所有的DOM对象已经加载完毕了</p><pre><code class="javascript">window.onload = function(){  var btn = document.getElementById(&quot;btn&quot;)  btn.onclick = function(){    alert(&quot;hello&quot;)  }}</code></pre><p>在这里来做几个练习呗</p><ol><li>城市选择</li></ol><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200814082611.png" alt="image-20200814082609725"></p><p>要求完成右边按钮的功能</p><pre><code class="javascript">// 查找#bj节点btn01.onclick = function(){  var bj = document.getElementById(&quot;bj&quot;)}// 查找所有li节点btn02.onclick = function(){  var lis = document.getElementsByTagName(&quot;li&quot;)}// 查找name为gender的所有节点btn03.onclick = function(){  var gNodes = document.getElementsByName(&quot;gender&quot;)}// 查找#city下所有li节点btn04.onclick = function(){  var cNodes = document.getElementById(&quot;city&quot;).getElementsByTagName(&quot;li&quot;)}// 返回#city的所有子节点btn05.onclick = function(){  var cAllNodes = document.getElementById(&quot;city&quot;).childNodes}// 返回#phone的第一个子节点btn06.onclick = function(){  var firstPNode = document.getElementById(&quot;#phone&quot;).firstChild}// 返回#bj的父节点btn07.onclick = function(){  var bParentNode = document.getElementById(&quot;bj&quot;).parentNode}// 返回#android的前一个兄弟节点btn08.onclick = function(){  var androidBro = document.getElementById(&quot;android&quot;).previousSibling}// 返回#username的value属性值btn09.onclick = function(){  var userNameValue = document.getElementById(&quot;username&quot;).value}// 设置Eusername的value属性值btn10.onclick = function(){  document.getElementById(&#39;username&#39;).value = &quot;用户名&quot;}// 返回#bj的文本值btn11.onclick = function(){  var bjText = document.getElementById(&quot;bj&quot;).innerText}</code></pre><p>它们的功能相似，可以封装为一个函数(只是提醒一下，这里不一定更方便)</p><pre><code class="javascript">function myClick(element, callback){  document.getElementById(element).onclick = callback}myClick(&quot;btn04&quot;,function(){  var cNodes = document.getElementById(&quot;city&quot;).getElementsByTagName(&quot;li&quot;)  console.log(cNodes)})</code></pre><ol start="2"><li>图片切换</li></ol><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200814095505.png" alt="image-20200814095504361"></p><pre><code class="html">&lt;style&gt;  *{    margin: 0;    padding: 0;    box-sizing: border-box;  }  img{    width: 533px;    height: 300px;  }  #wrapper{    position: relative;    margin: 50px auto;    width: 533px;    height: 300px;  }  #wrapper button{    position: absolute;    top: 0;    bottom: 0;    margin: auto 0;    width: 60px;    height: 30px;    text-align: center;    line-height: 30px;    background-color: rgba(0, 0, 0, .3);    color: white;    border: none;    outline: none;    cursor: pointer;  }  #wrapper #prev{    left: 0;  }  #wrapper #next{    right: 0;  }  #wrapper button:hover{    background-color: rgba(0, 0, 0, .8);  }  #wrapper #info{    width: 533px;    background-color: rgba(0, 0, 0, .3);    color: whitesmoke;    text-align: center;    position: absolute;    top: 0;  }&lt;/style&gt;&lt;div id=&quot;wrapper&quot;&gt;  &lt;img src=&quot;./img/1.jpg&quot; alt=&quot;图片&quot;&gt;  &lt;button id=&quot;prev&quot;&gt;上一张&lt;/button&gt;  &lt;button id=&quot;next&quot;&gt;下一张&lt;/button&gt;  &lt;p id=&quot;info&quot;&gt;&lt;/p&gt;&lt;/div&gt;&lt;script type=&quot;text/javascript&quot;&gt;  window.onload = function(){    /*     * 点击按钮切换图片     *  1、找到按钮     *  2、给按钮添加单击响应事件     *  3、在单击函数中修改img的src    */    var prev = document.getElementById(&quot;prev&quot;)    var next = document.getElementById(&quot;next&quot;)    var img = document.getElementsByTagName(&quot;img&quot;)[0]    var info = document.getElementById(&quot;info&quot;)    var imgArr = [&quot;img/1.jpg&quot;, &quot;img/2.jpg&quot;, &quot;img/3.jpg&quot;, &quot;img/4.jpg&quot;, &quot;img/5.jpg&quot;]    var index = 0    info.innerText = &quot;一共 &quot; +imgArr.length+&quot; 张图片，现在显示的是第 &quot; +(index+1)+&quot; 张&quot;    // console.log(img)    prev.onclick = function(){      // alert(&quot;上一张&quot;)      index--      if(index &lt; 0){        index = imgArr.length-1      }      img.src = imgArr[index]      // console.log(imgArr[index]);      info.innerText = &quot;一共 &quot; +imgArr.length+&quot; 张图片，现在显示的是第 &quot; +(index+1)+&quot; 张&quot;    }    next.onclick = function(){      // alert(&quot;下一张&quot;)      index++      if(index &gt; imgArr.length-1){        index = 0      }      img.src = imgArr[index]      // console.log(imgArr[index]);      info.innerText = &quot;一共 &quot; +imgArr.length+&quot; 张图片，现在显示的是第 &quot; +(index+1)+&quot; 张&quot;    }  }&lt;/script&gt;</code></pre><ol start="3"><li>全选</li></ol><pre><code class="html">&lt;form action=&quot;&quot; method=&quot;post&quot;&gt;  你喜欢的运动是？&lt;label id=&quot;control&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;checkAllBox&quot;&gt;全选/全不选&lt;/label&gt;&lt;br&gt;  &lt;input type=&quot;checkbox&quot; name=&quot;items&quot; value=&quot;足球&quot;&gt;足球  &lt;input type=&quot;checkbox&quot; name=&quot;items&quot; value=&quot;篮球&quot;&gt;篮球  &lt;input type=&quot;checkbox&quot; name=&quot;items&quot; value=&quot;羽毛球&quot;&gt;羽毛球  &lt;input type=&quot;checkbox&quot; name=&quot;items&quot; value=&quot;乒乓球&quot;&gt;乒乓球&lt;br&gt;  &lt;input type=&quot;button&quot; id=&quot;checkedAllBtn&quot; value=&quot;全选&quot;&gt;  &lt;input type=&quot;button&quot; id=&quot;checkedNoBtn&quot; value=&quot;全不选&quot;&gt;  &lt;input type=&quot;button&quot; id=&quot;checkedRevBtn&quot; value=&quot;反选&quot;&gt;  &lt;input type=&quot;button&quot; id=&quot;sendBtn&quot; value=&quot;提交&quot;&gt;&lt;/form&gt;&lt;script&gt;  window.onload = function(){    var items = document.getElementsByName(&quot;items&quot;)    // 在js中可以直接使用id名    /* 1、全选 */    checkedAllBtn.onclick = function(){      // alert(&#39;全选&#39;)      // 设置四个多选框变成选中状态      for(var i=0; i&lt;items.length; i++){        // 多选框的checked属性        items[i].checked = true      }      // checkAllBox状态      checkAllBox.checked = true    }    // 2、全不选    checkedNoBtn.onclick = function(){      for(var i=0; i&lt;items.length; i++){        items[i].checked = false      }      // checkAllBox状态      checkAllBox.checked = false    }    // 3、反选    checkedRevBtn.onclick = function(){      for(var i=0; i&lt;items.length; i++){        items[i].checked = !items[i].checked      }      // checkAllBox状态      checkAllBox.checked = true      for(var j=0; j&lt;items.length; j++){        // 只要有一个不是选中状态，则不是全选        if(!items[j].checked){          checkAllBox.checked = false          // 进入判断就说明已经false了，不需要再执行          break        }      }    }    // 4、发送    sendBtn.onclick = function(){      for(var i=0; i&lt;items.length; i++){        if(items[i].checked === true){          alert(items[i].value)        }      }    }    // 5、全选/全不选    checkAllBox.onclick = function(){      for(var i=0; i&lt;items.length; i++){        // items[i].checked = checkAllBox.checked        items[i].checked = this.checked      }    }    // checkAllBox状态    for(var i=0; i&lt;items.length; i++){      items[i].onclick = function(){        checkAllBox.checked = true        for(var j=0; j&lt;items.length; j++){          // 只要有一个不是选中状态，则不是全选          if(!items[j].checked){            checkAllBox.checked = false            // 进入判断就说明已经false了，不需要再执行            break          }        }      }    }  }&lt;/script&gt;</code></pre><p>反选可以修改下代码，减少一个循环</p><pre><code class="javascript">// 3、反选checkedRevBtn.onclick = function(){  //将checkedAllBox设置为选中状态  checkAllBox.checked = true;  for(var i=0; i&lt;items.length ; i++){    // 反选    items[i].checked = !items[i].checked;    // checkBox状态    if(!items[i].checked){      checkAllBox.checked = false;    }  }}</code></pre><p>上面又用到了this：</p><p>在事件的响应函数中，响应函数是给谁绑定的this就是谁</p><h3><span id="5-dom对象的其他属性-方法">5、DOM对象的其他属性、方法</span></h3><p>下面的属性和方法可用于所有 HTML 元素上：</p><table><thead><tr><th align="left">属性 / 方法</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_html_accesskey.asp" target="_blank" rel="noopener">element.accessKey</a></td><td align="left">设置或返回元素的快捷键。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_node_appendchild.asp" target="_blank" rel="noopener">element.appendChild()</a></td><td align="left">向元素添加新的子节点，作为最后一个子节点。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_node_attributes.asp" target="_blank" rel="noopener">element.attributes</a></td><td align="left">返回元素属性的 NamedNodeMap。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_node_childnodes.asp" target="_blank" rel="noopener">element.childNodes</a></td><td align="left">返回元素子节点的 NodeList。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_html_classname.asp" target="_blank" rel="noopener">element.className</a></td><td align="left">设置或返回元素的 class 属性。</td></tr><tr><td align="left">element.clientHeight</td><td align="left">返回元素的可见高度。</td></tr><tr><td align="left">element.clientWidth</td><td align="left">返回元素的可见宽度。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_node_clonenode.asp" target="_blank" rel="noopener">element.cloneNode()</a></td><td align="left">克隆元素。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_node_comparedocumentposition.asp" target="_blank" rel="noopener">element.compareDocumentPosition()</a></td><td align="left">比较两个元素的文档位置。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_html_contenteditable.asp" target="_blank" rel="noopener">element.contentEditable</a></td><td align="left">设置或返回元素的文本方向。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_html_dir.asp" target="_blank" rel="noopener">element.dir</a></td><td align="left">设置或返回元素的内容是否可编辑。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_node_firstchild.asp" target="_blank" rel="noopener">element.firstChild</a></td><td align="left">返回元素的首个子。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_element_getattribute.asp" target="_blank" rel="noopener">element.getAttribute()</a></td><td align="left">返回元素节点的指定属性值。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_element_getattributenode.asp" target="_blank" rel="noopener">element.getAttributeNode()</a></td><td align="left">返回指定的属性节点。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_element_getelementsbytagname.asp" target="_blank" rel="noopener">element.getElementsByTagName()</a></td><td align="left">返回拥有指定标签名的所有子元素的集合。</td></tr><tr><td align="left">element.getFeature()</td><td align="left">返回实现了指定特性的 API 的某个对象。</td></tr><tr><td align="left">element.getUserData()</td><td align="left">返回关联元素上键的对象。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_element_hasattribute.asp" target="_blank" rel="noopener">element.hasAttribute()</a></td><td align="left">如果元素拥有指定属性，则返回true，否则返回 false。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_node_hasattributes.asp" target="_blank" rel="noopener">element.hasAttributes()</a></td><td align="left">如果元素拥有属性，则返回 true，否则返回 false。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_node_haschildnodes.asp" target="_blank" rel="noopener">element.hasChildNodes()</a></td><td align="left">如果元素拥有子节点，则返回 true，否则 false。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_html_id.asp" target="_blank" rel="noopener">element.id</a></td><td align="left">设置或返回元素的 id。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_html_innerhtml.asp" target="_blank" rel="noopener">element.innerHTML</a></td><td align="left">设置或返回元素的内容。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_node_insertbefore.asp" target="_blank" rel="noopener">element.insertBefore()</a></td><td align="left">在指定的已有的子节点之前插入新节点。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_html_iscontenteditable.asp" target="_blank" rel="noopener">element.isContentEditable</a></td><td align="left">设置或返回元素的内容。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_node_isdefaultnamespace.asp" target="_blank" rel="noopener">element.isDefaultNamespace()</a></td><td align="left">如果指定的 namespaceURI 是默认的，则返回 true，否则返回 false。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_node_isequalnode.asp" target="_blank" rel="noopener">element.isEqualNode()</a></td><td align="left">检查两个元素是否相等。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_node_issamenode.asp" target="_blank" rel="noopener">element.isSameNode()</a></td><td align="left">检查两个元素是否是相同的节点。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_node_issupported.asp" target="_blank" rel="noopener">element.isSupported()</a></td><td align="left">如果元素支持指定特性，则返回 true。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_html_lang.asp" target="_blank" rel="noopener">element.lang</a></td><td align="left">设置或返回元素的语言代码。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_node_lastchild.asp" target="_blank" rel="noopener">element.lastChild</a></td><td align="left">返回元素的最后一个子元素。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_node_namespaceuri.asp" target="_blank" rel="noopener">element.namespaceURI</a></td><td align="left">返回元素的 namespace URI。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_node_nextsibling.asp" target="_blank" rel="noopener">element.nextSibling</a></td><td align="left">返回位于相同节点树层级的下一个节点。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_node_nodename.asp" target="_blank" rel="noopener">element.nodeName</a></td><td align="left">返回元素的名称。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_node_nodetype.asp" target="_blank" rel="noopener">element.nodeType</a></td><td align="left">返回元素的节点类型。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_node_nodevalue.asp" target="_blank" rel="noopener">element.nodeValue</a></td><td align="left">设置或返回元素值。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_node_normalize.asp" target="_blank" rel="noopener">element.normalize()</a></td><td align="left">合并元素中相邻的文本节点，并移除空的文本节点。</td></tr><tr><td align="left">element.offsetHeight</td><td align="left">返回元素的高度。</td></tr><tr><td align="left">element.offsetWidth</td><td align="left">返回元素的宽度。</td></tr><tr><td align="left">element.offsetLeft</td><td align="left">返回元素的水平偏移位置。</td></tr><tr><td align="left">element.offsetParent</td><td align="left">返回元素的偏移容器。</td></tr><tr><td align="left">element.offsetTop</td><td align="left">返回元素的垂直偏移位置。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_node_ownerdocument.asp" target="_blank" rel="noopener">element.ownerDocument</a></td><td align="left">返回元素的根元素（文档对象）。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_node_parentnode.asp" target="_blank" rel="noopener">element.parentNode</a></td><td align="left">返回元素的父节点。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_node_previoussibling.asp" target="_blank" rel="noopener">element.previousSibling</a></td><td align="left">返回位于相同节点树层级的前一个元素。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_element_removeattribute.asp" target="_blank" rel="noopener">element.removeAttribute()</a></td><td align="left">从元素中移除指定属性。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_element_removeattributenode.asp" target="_blank" rel="noopener">element.removeAttributeNode()</a></td><td align="left">移除指定的属性节点，并返回被移除的节点。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_node_removechild.asp" target="_blank" rel="noopener">element.removeChild()</a></td><td align="left">从元素中移除子节点。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_node_replacechild.asp" target="_blank" rel="noopener">element.replaceChild()</a></td><td align="left">替换元素中的子节点。</td></tr><tr><td align="left">element.scrollHeight</td><td align="left">返回元素的整体高度。</td></tr><tr><td align="left">element.scrollLeft</td><td align="left">返回元素左边缘与视图之间的距离。</td></tr><tr><td align="left">element.scrollTop</td><td align="left">返回元素上边缘与视图之间的距离。</td></tr><tr><td align="left">element.scrollWidth</td><td align="left">返回元素的整体宽度。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_element_setattribute.asp" target="_blank" rel="noopener">element.setAttribute()</a></td><td align="left">把指定属性设置或更改为指定值。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_element_setattributenode.asp" target="_blank" rel="noopener">element.setAttributeNode()</a></td><td align="left">设置或更改指定属性节点。</td></tr><tr><td align="left">element.setIdAttribute()</td><td align="left"></td></tr><tr><td align="left">element.setIdAttributeNode()</td><td align="left"></td></tr><tr><td align="left">element.setUserData()</td><td align="left">把对象关联到元素上的键。</td></tr><tr><td align="left">element.style</td><td align="left">设置或返回元素的 style 属性。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_html_tabindex.asp" target="_blank" rel="noopener">element.tabIndex</a></td><td align="left">设置或返回元素的 tab 键控制次序。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_element_tagname.asp" target="_blank" rel="noopener">element.tagName</a></td><td align="left">返回元素的标签名。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_node_textcontent.asp" target="_blank" rel="noopener">element.textContent</a></td><td align="left">设置或返回节点及其后代的文本内容。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_html_title.asp" target="_blank" rel="noopener">element.title</a></td><td align="left">设置或返回元素的 title 属性。</td></tr><tr><td align="left">element.toString()</td><td align="left">把元素转换为字符串。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_nodelist_item.asp" target="_blank" rel="noopener">nodelist.item()</a></td><td align="left">返回 NodeList 中位于指定下标的节点。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_nodelist_length.asp" target="_blank" rel="noopener">nodelist.length</a></td><td align="left">返回 NodeList 中的节点数。</td></tr></tbody></table><p>DOM查询学完了，接着就可以操作对象了(增删改)</p><p>增：</p><p><code>apperndChild()</code> 向父节点添加一个新的子节点，<code>父元素.appendChild(子节点)</code></p><p><code>insertBefore()</code> 在指定的子节点前插入新的子节点，<code>父节点.inserBefore(&quot;要插入的子节点&quot;,&quot;指定的子节点&quot;)</code></p><p><code>createElement()</code> 创建元素节点，需要一个标签名作为参数，将会根据该标签名创建元素节点对象，并将创建好的对象作为返回值返回</p><p><code>createTextNode()</code> 创建文本节点，需要一个文本内容作为参数，将会根据该内容创建文本节点，并将新的节点返回</p><p><code>createAttribute()</code> 创建属性节点</p><p>删：</p><p><code>removeChild()</code> 删除子节点，<code>父节点.removeChild(子节点)</code>—&gt;<strong><code>子节点.parentNode.removeChild(子节点)</code></strong></p><p>改：</p><p><code>replaceChild()</code> 使用指定的子节点替换已有子节点，<code>父节点.replaceChild(新节点,旧节点)</code></p><p><code>setAttribute()</code> 把指定属性设置或修改为指定的值</p><p><code>innerHTML</code> 用于获取元素内部的HMTL代码，对于自结束标签，这个属性没有意义</p><p>如果要读取元素节点属性，可以直接使用元素.属性名，例如 元素.id、元素.name、元素.value，注意 class属性不能采用这种方式，读取class属性时需要使用 元素.className</p><p><code>innerTex</code> t这个属性可以获取到元素内部的文本内容，和innerHTML类似，不同的是它会自动将html去除</p><p>可以使用<code>innerHTML</code>完成DOM的增删改操作</p><p>练习：</p><ol><li>城市节点修改</li></ol><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200814113432.png" alt="image-20200814113430625"></p><pre><code class="html">&lt;style&gt;  *{    margin: 0;    padding: 0;    box-sizing: border-box;  }  body {    width: 800px;    margin-left: auto;    margin-right: auto;  }  button {    width: 300px;    margin-bottom: 10px;  }  #btnList {    float:left;  }  #total{    width: 450px;    float:left;  }  ul{    list-style-type: none;    margin: 0px;    padding: 0px;  }  .inner li{    border-style: solid;    border-width: 1px;    padding: 5px;    margin: 5px;    background-color: #99ff99;    float:left;  }  .inner{    width:400px;    border-style: solid;    border-width: 1px;    margin-bottom: 10px;    padding: 10px;    float: left;  }&lt;/style&gt;&lt;div id=&quot;total&quot;&gt;  &lt;div class=&quot;inner&quot;&gt;    &lt;p&gt;      你喜欢哪个城市?    &lt;/p&gt;    &lt;ul id=&quot;city&quot;&gt;      &lt;li id=&quot;bj&quot;&gt;北京&lt;/li&gt;      &lt;li&gt;上海&lt;/li&gt;      &lt;li&gt;东京&lt;/li&gt;      &lt;li&gt;首尔&lt;/li&gt;    &lt;/ul&gt;  &lt;/div&gt;&lt;/div&gt;&lt;div id=&quot;btnList&quot;&gt;  &lt;div&gt;&lt;button id=&quot;btn01&quot;&gt;创建一个&quot;广州&quot;节点,添加到#city下&lt;/button&gt;&lt;/div&gt;  &lt;div&gt;&lt;button id=&quot;btn02&quot;&gt;将&quot;广州&quot;节点插入到#bj前面&lt;/button&gt;&lt;/div&gt;  &lt;div&gt;&lt;button id=&quot;btn03&quot;&gt;使用&quot;广州&quot;节点替换#bj节点&lt;/button&gt;&lt;/div&gt;  &lt;div&gt;&lt;button id=&quot;btn04&quot;&gt;删除#bj节点&lt;/button&gt;&lt;/div&gt;  &lt;div&gt;&lt;button id=&quot;btn05&quot;&gt;读取#city内的HTML代码&lt;/button&gt;&lt;/div&gt;  &lt;div&gt;&lt;button id=&quot;btn06&quot;&gt;设置#bj内的HTML代码&lt;/button&gt;&lt;/div&gt;  &lt;div&gt;&lt;button id=&quot;btn07&quot;&gt;将HTML代码&quot;广州&quot;节点,添加到#city下&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;script&gt;  function myClick(element, fun){    var btn = document.getElementById(element)    btn.onclick = fun  }  // 创建一个&quot;广州&quot;节点,添加到#city下  myClick(&quot;btn01&quot;, function(){    // 创建    var li = document.createElement(&quot;li&quot;)    var gzText = document.createTextNode(&quot;广州&quot;)    // 插入    // li.appendChild(gzText)    li.innerHTML = &quot;广州&quot;    city.appendChild(li)  })  // 将&quot;广州&quot;节点插入到#bj前面  myClick(&quot;btn02&quot;, function(){    // 创建    var li = document.createElement(&quot;li&quot;)    var gzText = document.createTextNode(&quot;广州&quot;)    // 插入    li.appendChild(gzText)    city.insertBefore(li,bj)  })  // 使用&quot;广州&quot;节点替换#bj节点  myClick(&quot;btn03&quot;, function(){    // 创建    var li = document.createElement(&quot;li&quot;)    var gzText = document.createTextNode(&quot;广州&quot;)    // 插入    li.appendChild(gzText)    city.replaceChild(li,bj)  })  // 删除#bj节点  myClick(&quot;btn04&quot;, function(){    // city.removeChild(bj)    bj.parentNode.removeChild(bj)  })  // 读取#city内的HTML代码  myClick(&quot;btn05&quot;, function(){    alert(city.innerHTML)  })  // 设置#bj内的HTML代码  myClick(&quot;btn06&quot;, function(){    bj.innerHTML = &quot;北平&quot;  })  // 创建一个&quot;广州&quot;节点,添加到#city下  myClick(&quot;btn07&quot;, function(){    city.innerHTML += &quot;&lt;li&gt;广州&lt;/&gt;&quot;  })&lt;/script&gt;</code></pre><ol start="2"><li>员工记录</li></ol><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200814112400.png" alt="image-20200814112357578"></p><p>HTML和CSS部分</p><pre><code class="html">&lt;style&gt;  #total {    width: 450px;    margin-left: auto;    margin-right: auto;  }  ul {    list-style-type: none;  }  li {    border-style: solid;    border-width: 1px;    padding: 5px;    margin: 5px;    background-color: #99ff99;    float: left;  }  .inner {    width: 400px;    border-style: solid;    border-width: 1px;    margin: 10px;    padding: 10px;    float: left;  }  #employeeTable {    border-spacing: 1px;    background-color: black;    margin: 80px auto 10px auto;  }  th,td {    background-color: white;  }  #formDiv {    width: 250px;    border-style: solid;    border-width: 1px;    margin: 50px auto 10px auto;    padding: 10px;  }  #formDiv input {    width: 100%;  }  .word {    width: 40px;  }  .inp {    width: 200px;  }&lt;/style&gt;&lt;table id=&quot;employeeTable&quot;&gt;  &lt;tr&gt;    &lt;th&gt;Name&lt;/th&gt;    &lt;th&gt;Email&lt;/th&gt;    &lt;th&gt;Salary&lt;/th&gt;    &lt;th&gt;&amp;nbsp;&lt;/th&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;Tom&lt;/td&gt;    &lt;td&gt;tom@tom.com&lt;/td&gt;    &lt;td&gt;5000&lt;/td&gt;    &lt;!-- &lt;td&gt;&lt;a href=&quot;javascript:;&quot;&gt;Delete&lt;/a&gt;&lt;/td&gt; --&gt;    &lt;td&gt;&lt;a href=&quot;deleteEmp?id=001&quot;&gt;Delete&lt;/a&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;Jerry&lt;/td&gt;    &lt;td&gt;jerry@sohu.com&lt;/td&gt;    &lt;td&gt;8000&lt;/td&gt;    &lt;td&gt;&lt;a href=&quot;deleteEmp?id=002&quot;&gt;Delete&lt;/a&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;Bob&lt;/td&gt;    &lt;td&gt;bob@tom.com&lt;/td&gt;    &lt;td&gt;10000&lt;/td&gt;    &lt;td&gt;&lt;a href=&quot;deleteEmp?id=003&quot;&gt;Delete&lt;/a&gt;&lt;/td&gt;  &lt;/tr&gt;&lt;/table&gt;&lt;div id=&quot;formDiv&quot;&gt;  &lt;h4&gt;添加新员工&lt;/h4&gt;  &lt;table&gt;    &lt;tr&gt;      &lt;td class=&quot;word&quot;&gt;name: &lt;/td&gt;      &lt;td class=&quot;inp&quot;&gt;        &lt;input type=&quot;text&quot; name=&quot;empName&quot; id=&quot;empName&quot; /&gt;      &lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;td class=&quot;word&quot;&gt;email: &lt;/td&gt;      &lt;td class=&quot;inp&quot;&gt;        &lt;input type=&quot;text&quot; name=&quot;email&quot; id=&quot;email&quot; /&gt;      &lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;td class=&quot;word&quot;&gt;salary: &lt;/td&gt;      &lt;td class=&quot;inp&quot;&gt;        &lt;input type=&quot;text&quot; name=&quot;salary&quot; id=&quot;salary&quot; /&gt;      &lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;td colspan=&quot;2&quot; align=&quot;center&quot;&gt;        &lt;button id=&quot;addEmpButton&quot; value=&quot;abc&quot;&gt;          Submit        &lt;/button&gt;      &lt;/td&gt;    &lt;/tr&gt;  &lt;/table&gt;&lt;/div&gt;</code></pre><p>取消a的默认行为</p><pre><code class="javascript">&lt;a href=&quot;javascript:;&quot;&gt;&lt;/a&gt;a.onclick=function(){  return false}</code></pre><p>for循环会在页面加载完成之后立即执行难，而响应函数会在超链接被点击时才执行，等它执行，循环已经结束了</p><pre><code class="javascript">var allA = document.getElementsByTagName(&quot;a&quot;)for(var i=0; i&lt;allA.length; i++){  allA[i].onclick = function(){    alert(i) // 一直是3    return false  }}</code></pre><p>可以用this来解决，添加之后删除，由于绑定删除是之前的，因此需要单独绑定</p><p>删除和添加员工</p><pre><code class="html">&lt;script&gt;  /* 删除员工 */  var allA = document.getElementsByTagName(&quot;a&quot;)  for(var i=0; i&lt;allA.length; i++){    allA[i].onclick = function(){      var tr = this.parentNode.parentNode      // var name = tr.getElementsByTagName(&quot;td&quot;)[0].innerText      var name = tr.children[0].innerHTML      // alert(&quot;确认删除吗？&quot;)      /* window对象中的方法，找到confirm，这是用于弹出一个带有确认和取消按钮的提示框，字符串(提示文字)作为参数，点击确认返回true，点击取消返回false */      var flag = confirm(&quot;确认删除&quot; +name+ &quot;吗？&quot;) // firstChild会找第一个节点，空格也是      if(flag){        tr.parentNode.removeChild(tr)      }      // 取消默认行为      return false    }  }  /* 添加员工 */  addEmpButton.onclick = function(){    var name = document.getElementById(&quot;empName&quot;).value    var email = document.getElementById(&quot;email&quot;).value    var salary = document.getElementById(&quot;salary&quot;).value    // console.log(name,email,salary)    /*    &lt;tr&gt;      &lt;td&gt;Bob&lt;/td&gt;      &lt;td&gt;bob@tom.com&lt;/td&gt;      &lt;td&gt;10000&lt;/td&gt;      &lt;td&gt;&lt;a href=&quot;deleteEmp?id=003&quot;&gt;Delete&lt;/a&gt;&lt;/td&gt;    &lt;/tr&gt;    */    var tr = document.createElement(&quot;tr&quot;)    var nameTd = document.createElement(&quot;td&quot;)    var emailTd = document.createElement(&quot;td&quot;)    var salaryTd = document.createElement(&quot;td&quot;)    var aTd = document.createElement(&quot;td&quot;)    var a = document.createElement(&quot;a&quot;)    // 文本节点    var nameText = document.createTextNode(name)    var emailText = document.createTextNode(email)    var salaryText = document.createTextNode(salary)    var delText = document.createTextNode(&quot;Delete&quot;)    // 将文本插入到td中    nameTd.appendChild(nameText)    emailTd.appendChild(emailText)    salaryTd.appendChild(salaryText)    a.appendChild(delText)    a.href = &quot;javascript:;&quot;    a.onclick = function(){      var tr = this.parentNode.parentNode      var name = tr.children[0].innerHTML      var flag = confirm(&quot;确认删除&quot; +name+ &quot;吗？&quot;)      if(flag){        tr.parentNode.removeChild(tr)      }      return false    }    aTd.appendChild(a)    // 将td添加到tr中    tr.appendChild(nameTd)    tr.appendChild(emailTd)    tr.appendChild(salaryTd)    tr.appendChild(aTd)    // 插入到table    // employeeTable.appendChild(tr) // 事实上tr在tbody中    employeeTable.getElementsByTagName(&quot;tbody&quot;)[0].appendChild(tr)  }&lt;/script&gt;</code></pre><p>由于删除的有相同代码，可以提出来</p><pre><code class="javascript">function delA(){  var tr = this.parentNode.parentNode  // var name = tr.getElementsByTagName(&quot;td&quot;)[0].innerText  var name = tr.children[0].innerHTML  // alert(&quot;确认删除吗？&quot;)  /* window对象中的方法，找到confirm，这是用于弹出一个带有确认和取消按钮的提示框，字符串(提示文字)作为参数，点击确认返回true，点击取消返回false */  var flag = confirm(&quot;确认删除&quot; +name+ &quot;吗？&quot;) // firstChild会找第一个节点，空格也是  if(flag){    tr.parentNode.removeChild(tr)  }  // 取消默认行为  return false}删除员工中allA[i].onclick = delA添加员工中a.onclick = delA</code></pre><p>增加的使用appendChild和innerHTML配合更简单一点</p><pre><code class="javascript">/* 添加员工 */addEmpButton.onclick = function(){  var name = document.getElementById(&quot;empName&quot;).value  var email = document.getElementById(&quot;email&quot;).value  var salary = document.getElementById(&quot;salary&quot;).value  var tr = document.createElement(&quot;tr&quot;)  tr.innerHTML = &quot;&lt;td&gt;&quot;+name+&quot;&lt;/td&gt;&quot;+                &quot;&lt;td&gt;&quot;+email+&quot;&lt;/td&gt;&quot;+                &quot;&lt;td&gt;&quot;+salary+&quot;&lt;/td&gt;&quot;+                &quot;&lt;td&gt;&lt;a href=&#39;javascript:;&#39;&gt;Delete&lt;/a&gt;&lt;/td&gt;&quot;  var a = tr.getElementsByTagName(&quot;a&quot;)[0]  a.onclick = delA  var tbody = employeeTable.getElementsByTagName(&quot;tbody&quot;)[0]  tbody.appendChild(tr)  // tbody.innerHTML += tr // 这样就不太合适了，会影响前面已有的}</code></pre><h3><span id="6-dom操作css">6、DOM操作CSS</span></h3><p>通过js修改元素的样式</p><ul><li><p>语法：<code>元素.style.样式名=样式值</code></p></li><li><p>注意：如果CSS的样式名中含有<code>-</code>，这种名称在js中是不合法的，需要将这种样式名修改为驼峰命名，去掉<code>-</code>，然后将<code>-</code>后的字母大写</p></li></ul><pre><code class="css">var box1 = document.getElementById(&quot;box1&quot;)var btn01 = document.getElementById(&quot;btn01&quot;)btn01.onclick = function(){  // 修改box1的宽度  box1.style.width = &quot;300px&quot;  // 修改背景颜色  box1.style.backgroundColor = &quot;yellow&quot;}</code></pre><ul><li>通过style属性设置的样式都是内联样式，而内联样式有较高的优先级，所以通过js修改的样式往往会立即显示；但如果在样式中写了<code>!important</code>，则此时样式会有最高的优先级，即使通过js也不能覆盖该样式，此时将会导致js修改样式失败，所以在写CSS样式的时候尽量不要加<code>!important</code></li></ul><p>js读取样式</p><ul><li>语法：<code>元素.style.样式名</code></li></ul><pre><code class="javascript">var btn02 = document.getElementById(&quot;btn02&quot;)btn02.onclick = function(){  // 读取box1的样式  alert(box1.style.height, box1.style.width)}</code></pre><ul><li>通过style属性设置和读取的都是内联样式，无法读取样式表中的样式</li></ul><p>js获取当前显示的样式</p><ul><li>语法：<code>元素.currentStyle.样式</code></li><li>它可以用来读取当前元素正在显示的样式，如果当前元素没有设置该样式，则获取它的默认值</li></ul><pre><code class="javascript">window.onload = function(){  btn01.onclick = function(){    alert(box1.currentStyle.width)    alert(box1.currentStyle.backgroundColor)  }}</code></pre><ul><li>currentStyle只有IE浏览器支持，其他的浏览器都不支持</li></ul><p>在其他浏览器中可以使用</p><p><code>getComputedStyle()</code>这个方法来获取元素当前的样式，这个方法是window的方法，可以直接使用</p><p>需要两个参数</p><p>  第一个：要获取样式的元素</p><p>  第二个：可以传递一个伪元素，一般都传null</p><p>这个方法会返回一个对象，对象中封装了当前元素对应的样式</p><p>可以通过<code>对象.样式名</code>来读取样式，如果获取的样式没有设置，则会获取到真实的值，而不是默认值。比如：没有设置width，它不会获取到auto，而是一个长度</p><p>这个方法不支持IE8及以下的浏览器</p><pre><code class="javascript">// var obj = getComputedStyle(box1, null)alert(getComputedStyle(box1,null).backgroundColor)</code></pre><p>通过currentStyle和getComputedStyle()读取到的样式都是只读的，不能修改，如果要修改必须通过style属性</p><pre><code class="javascript">/* 定义一个函数，用来获取指定元素的当前的样式* 参数：*  obj 要获取样式的元素*  name 要获取的样式名*//* function getStyle(obj, name){    if(window.getComputedStyle){    // 正常浏览器的方式，具有getComputedStyle()方法    return getComputedStyle(obj, null)[name]  }else{    // IE8的方式，没有getComputedStyle()方法    return obj.currentStyle[name]  }} *//* // 不建议用下面这个 IE11会优先使用current，虽然都能用function getStyle(obj, name){    if(obj.currentStyle){    return obj.currentStyle[name]  }else{    return getComputedStyle(obj, null)[name]  }}*/function getStyle(obj, name){    return window.getComputedStyle ? getComputedStyle(obj, null)[name] : obj.currentStyle[name]}</code></pre><p>clientWidth、clientHeight</p><p>这两个属性可以获取元素的可见宽度和高度</p><p>这些属性都是不带px的，返回都是一个数字，可以直接进行计算</p><p>会获取元素宽度和高度，包括内容区和内边距</p><p>这些属性都是只读的，不能修改</p><pre><code class="javascript">btn01.onclick = function(){  alert(box1.clientWidth)}</code></pre><p>offsetWidth、offsetHeight</p><p>获取元素的整个的宽度和高度，包括内容区、内边距和边框</p><pre><code class="javascript">alert(box1.offsetWidth)</code></pre><p>offsetParent</p><p>可以用来获取当前元素的定位父元素</p><p>会获取到离当前元素最近的开启了定位的祖先元素，如果所有的祖先元素都没开启定位，则返回body</p><pre><code class="javascript">var op = box1.offsetParentalert(op.id)</code></pre><p>offsetLeft</p><p>当前元素相对于其定位父元素的水平偏移量</p><p>offsetTop</p><p>当前元素相对于其定位父元素的垂直偏移量</p><p>设置了<code>overflow:auto;</code>，高度和长度都太大，会出现滚动条</p><p>scrollWidth、scrollHeight</p><p>可以获取元素整个滚动区域的宽、高度</p><pre><code class="javascript">alert(box4.clientHeight, box4.scrollHeight)</code></pre><p>scrollLeft</p><p>可以获取水平滚动条滚动的距离</p><p>scrollTop</p><p>可以获取垂直滚动条滚动的距离</p><pre><code class="javascript">alert(box4.scrollLeft)alert(box4.scrollTop)</code></pre><h3><span id="7-其他事件">7、其他事件</span></h3><p>onscroll </p><p>该事件会在元素的滚动条滚动时触发</p><p>阅读协议：</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200814143326.png" alt="image-20200814143324153"></p><pre><code class="html">&lt;style type=&quot;text/css&quot;&gt;  #info{    width: 300px;    height: 500px;    background-color: #bfa;    overflow: auto;  }&lt;/style&gt;&lt;h3&gt;欢迎亲爱的用户注册&lt;/h3&gt;&lt;p id=&quot;info&quot;&gt;  Lorem ipsum, dolor sit amet consectetur adipisicing elit. Amet, doloremque. Ea tempora consequuntur mollitia corrupti. Commodi iusto ducimus deleniti, ratione quam, harum fugit autem omnis nihil dolorem fuga, sequi eligendi excepturi expedita tenetur accusamus! In eaque voluptate inventore commodi a? Unde provident tempore, dolorem deserunt numquam reiciendis eum vero. Aut ipsum, dolorem doloribus nemo molestiae exercitationem deserunt amet quod voluptatibus expedita ex architecto, cum culpa fuga dolore. Ratione perspiciatis soluta, facilis temporibus, error laudantium nostrum facere doloremque natus sint cum esse incidunt officiis eum officia velit ab illo perferendis distinctio enim voluptas tempore? Reiciendis, itaque cupiditate? Totam mollitia tenetur labore neque possimus vero porro modi iure atque reiciendis fugit iste, omnis, magnam rem animi adipisci tempore repellendus iusto fugiat voluptatum consequuntur excepturi. Similique vel praesentium quo quam at. Veniam, neque nostrum, minima ex assumenda perferendis aliquam cum, molestiae culpa repellat explicabo? Sapiente, placeat eligendi voluptas velit beatae accusantium minus culpa reprehenderit similique, corporis eum neque tempore expedita iure, adipisci molestias. Quidem, sapiente temporibus deserunt nobis architecto incidunt ipsam. Provident distinctio accusamus quaerat inventore ex, omnis excepturi temporibus quod alias possimus maxime nesciunt odio aperiam error quia? Neque rem eos a perferendis repellat repudiandae reprehenderit reiciendis molestiae ullam! Nihil, illum odio!&lt;/p&gt;&lt;!-- 如果为表单项添加disabled=&quot;disabled&quot; 则表单项将变成不可用的状态 --&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;disabled&quot; /&gt;我已仔细阅读协议，一定遵守&lt;input type=&quot;submit&quot; value=&quot;注册&quot; disabled=&quot;disabled&quot; /&gt;&lt;script type=&quot;text/javascript&quot;&gt;  window.onload = function(){    /*      * 当垂直滚动条滚动到底时使表单项可用      */    //获取id为info的p元素    var info = document.getElementById(&quot;info&quot;)    //获取两个表单项    var inputs = document.getElementsByTagName(&quot;input&quot;)    //为info绑定一个滚动条滚动的事件    info.onscroll = function(){      //检查垂直滚动条是否滚动到底      if(info.scrollHeight - info.scrollTop == info.clientHeight){        //滚动条滚动到底，使表单项可用        /*          * disabled属性可以设置一个元素是否禁用，          *     如果设置为true，则元素禁用          *     如果设置为false，则元素可用          */        inputs[0].disabled = false        inputs[0].onclick = function(){ // 只有到底了才能能点复选框，点了复选框才能点按钮          if(inputs[0].checked){            inputs[1].disabled = false          }        }      }    }  }&lt;/script&gt;</code></pre><p>onmousemove</p><p>该事件会在鼠标在元素中移动时被触发</p><p>事件对象：当事件的响应函数被触发时，浏览器每次都会将一个事件对象作为实参传递进响应函数，在事件对象中封装了当前事件相关的一切信息。比如，鼠标坐标、键盘哪个按键被按下、鼠标滚轮滚动的方向……</p><pre><code class="html">&lt;style&gt;  #areaDiv{    position: relative;    width: 100vw;    height: 100vh;    background-color: rgba(0,0,0,.2);  }  #showMsg{    position: absolute;    width: 160px;    height: 20px;    line-height: 20px;    text-align: left;    background-color: rgba(0,0,0,.5);    left: 0;    top: 0;  }&lt;/style&gt;&lt;div id=&quot;areaDiv&quot;&gt;&lt;/div&gt;&lt;div id=&quot;showMsg&quot;&gt;&lt;/div&gt;&lt;script&gt;  /* 当鼠标在areaDiv中移动时，在showMsg中来显示鼠标的坐标 */  areaDiv.onmousemove = function(event){    /*     * clientX可以获取鼠标指针的水平坐标     * clientY可以获取鼠标指针的垂直坐标    */    var x = event.clientX    var y = event.clientY    showMsg.innerHTML = &quot;(x = &quot; + x + &quot;, y = &quot; + y +&quot;)&quot;    showMsg.style.left = x + 10 + &quot;px&quot;    showMsg.style.top = y + 10 + &quot;px&quot;  }&lt;/script&gt;</code></pre><p>在IE8中，响应函数被触发时，浏览器不会传递事件对象，在IE8及以下的浏览器中，是将事件对象作为window对象的属性保存的</p><pre><code class="javascript">/*var x = window.event.clientXvar y = window.event.clientY*//*if(!event){  event = window.event}*/envent = event || window.event</code></pre><p>上面也涉及到了跟随鼠标的，下面来详细说一下</p><pre><code class="html">&lt;style type=&quot;text/css&quot;&gt;  #box1{    width: 100px;    height: 100px;    background-color: red;    position: absolute;  }&lt;/style&gt;&lt;script type=&quot;text/javascript&quot;&gt;  window.onload = function(){    /*     * 使div可以跟随鼠标移动     */    //绑定鼠标移动事件    document.onmousemove = function(event){        //解决兼容问题      event = event || window.event      //获取滚动条滚动的距离      /*       * chrome认为浏览器的滚动条是body的，可以通过body.scrollTop来获取       * 火狐等浏览器认为浏览器的滚动条是html的，       */      var st = document.body.scrollTop || document.documentElement.scrollTop      var sl = document.body.scrollLeft || document.documentElement.scrollLeft      //var st = document.documentElement.scrollTop;      //获取到鼠标的坐标      /*       * clientX和clientY       *     用于获取鼠标在当前的可见窗口的坐标       * div的偏移量，是相对于整个页面的       *        * pageX和pageY可以获取鼠标相对于当前页面的坐标       *     但是这个两个属性在IE8中不支持，所以如果需要兼容IE8，则不要使用       */      var left = event.clientX      var　top = event.clientY      //设置div的偏移量      box1.style.left = left + sl + &quot;px&quot;      box1.style.top = top + st + &quot;px&quot;    };      };&lt;/script&gt;&lt;body style=&quot;height: 1000px;width: 2000px;&quot;&gt;  &lt;div id=&quot;box1&quot;&gt;&lt;/div&gt;&lt;/body&gt;</code></pre><h3><span id="8-事件的冒泡bubble">8、事件的冒泡(Bubble)</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200814152101.png" alt="image-20200814152059578"></p><p>所谓的冒泡指的就是事件的向上传导，当后代元素上的事件被触发时，其祖先元素的相同事件也会被触发</p><pre><code class="javascript">// body&gt;#box1&gt;span#s1s1.onclick = function(){  alert(&quot;我是span的单击响应函数&quot;)}box1.onclick = function(){  alert(&quot;我是div的单击响应函数&quot;)}document.body.onclick = function(){  alert(&quot;我是body的单击响应函数&quot;)}</code></pre><p>点一下span，span、div、body的单击响应函数都会被触发</p><p>在开发中大部分情况冒泡都是有用的，如果不希望发生事件冒泡可以通过事件对象来取消冒泡</p><pre><code class="javascript">s1.onclick = function(e){  event = e || window.event  alert(&quot;我是span的单击响应函数&quot;)  // 取消冒泡，可以将事件对象的cancelBubble设置为true，即可取消冒泡  event.cancelBubble = true}box1.onclick = function(e){  e = e || window.event  alert(&quot;我是div的单击响应函数&quot;)  e.cancelBubble = true}</code></pre><h3><span id="9-事件的委派">9、事件的委派</span></h3><pre><code class="html">&lt;script type=&quot;text/javascript&quot;&gt;  window.onload = function(){    var u1 = document.getElementById(&quot;u1&quot;)    //点击按钮以后添加超链接    var btn01 = document.getElementById(&quot;btn01&quot;)    btn01.onclick = function(){      //创建一个li      var li = document.createElement(&quot;li&quot;)      li.innerHTML = &quot;&lt;a href=&#39;javascript:;&#39; class=&#39;link&#39;&gt;新建的超链接&lt;/a&gt;&quot;;      //将li添加到ul中      u1.appendChild(li)    }    /*      * 为每一个超链接都绑定一个单击响应函数      * 这里我们为每一个超链接都绑定了一个单击响应函数，这种操作比较麻烦，而且这些操作只能为已有的超链接设置事件，而新添加的超链接必须重新绑定      */    //获取所有的a    var allA = document.getElementsByTagName(&quot;a&quot;)      //遍历      for(var i=0 ; i&lt;allA.length ; i++){        allA[i].onclick = function(){          alert(&quot;我是a的单击响应函数！！！&quot;)      }    }  }&lt;/script&gt;&lt;button id=&quot;btn01&quot;&gt;添加超链接&lt;/button&gt;&lt;ul id=&quot;u1&quot; style=&quot;background-color: #bfa;&quot;&gt;  &lt;li&gt;    &lt;p&gt;我是p元素&lt;/p&gt;  &lt;/li&gt;  &lt;li&gt;&lt;a href=&quot;javascript:;&quot; class=&quot;link&quot;&gt;超链接一&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;&lt;a href=&quot;javascript:;&quot; class=&quot;link&quot;&gt;超链接二&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;&lt;a href=&quot;javascript:;&quot; class=&quot;link&quot;&gt;超链接三&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</code></pre><p>事件的委派<br>指将事件统一绑定给元素的共同的祖先元素，这样当后代元素上的事件触发时，会一直冒泡到祖先元素，从而通过祖先元素的响应函数来处理事件。</p><p>事件委派是利用了冒泡，通过委派可以减少事件绑定的次数，提高程序的性能</p><pre><code class="javascript">/** 我们希望，只绑定一次事件，即可应用到多个的元素上，即使元素是后添加的* 我们可以尝试将其绑定给元素的共同的祖先元素*///为ul绑定一个单击响应函数u1.onclick = function(event){  event = event || window.event  /*   * target   *     - event中的target表示的触发事件的对象  */  //alert(event.target);  //如果触发事件的对象是我们期望的元素，则执行否则不执行  if(event.target.className == &quot;link&quot;){    alert(&quot;我是ul的单击响应函数&quot;)  }  }</code></pre><h3><span id="10-事件的绑定">10、事件的绑定</span></h3><p>使用 <code>对象.事件 = 函数</code> 的形式绑定响应函数，它只能同时为一个元素的一个事件绑定一个响应函数，能绑定多个，如果绑定了多个，则后边会覆盖掉前边的</p><pre><code class="javascript">/** 点击按钮以后弹出一个内容*///获取按钮对象var btn01 = document.getElementById(&quot;btn01&quot;)//为btn01绑定一个单击响应函数btn01.onclick = function(){  alert(1)}//为btn01绑定第二个响应函数btn01.onclick = function(){  alert(2)}</code></pre><p><strong><code>addEventListener()</code></strong></p><ul><li><p>通过这个方法也可以为元素绑定响应函数</p></li><li><p>参数：</p></li></ul><ol><li><p>事件的字符串，不要on</p></li><li><p>回调函数，当事件触发时该函数会被调用</p></li><li><p>是否在捕获阶段触发事件，需要一个布尔值，一般都传false</p></li></ol><p>使用addEventListener()可以同时为一个元素的相同事件同时绑定多个响应函数，这样当事件被触发时，响应函数将会按照函数的绑定顺序执行</p><p>这个方法不支持IE8及以下的浏览器</p><pre><code class="javascript">btn01.addEventListener(&quot;click&quot;,function(){  alert(1);},false);btn01.addEventListener(&quot;click&quot;,function(){  alert(2);},false);btn01.addEventListener(&quot;click&quot;,function(){  alert(3);},false);</code></pre><p> <code>attachEvent()</code></p><ul><li><p>在IE8中可以使用attachEvent()来绑定事件</p></li><li><p>参数：</p><ol><li>事件的字符串，要on</li><li>回调函数</li></ol></li><li><p>这个方法也可以同时为一个事件绑定多个处理函数，不同的是它是后绑定先执行，执行顺序和addEventListener()相反(顺序实在有要求可以反着写)</p></li></ul><pre><code class="javascript">btn01.attachEvent(&quot;onclick&quot;,function(){  alert(1)})btn01.attachEvent(&quot;onclick&quot;,function(){  alert(2)})btn01.attachEvent(&quot;onclick&quot;,function(){  alert(3)})btn01.addEventListener(&quot;click&quot;,function(){  alert(this)},false)btn01.attachEvent(&quot;onclick&quot;,function(){  alert(this)})</code></pre><p>定义一个函数，用来为指定元素绑定响应函数</p><ul><li><p>addEventListener()中的this，是绑定事件的对象</p></li><li><p>attachEvent()中的this，是window</p></li><li><p>需要统一两个方法this</p></li></ul><pre><code class="javascript"> /*  ** 参数：  **  obj 要绑定事件的对象  **  eventStr 事件的字符串(不要on)  **  callback 回调函数  **/function bind(obj , eventStr , callback){  if(obj.addEventListener){    //大部分浏览器兼容的方式    obj.addEventListener(eventStr , callback , false)  }else{    /*      * this是谁由调用方式决定      * callback.call(obj)      */    //IE8及以下    obj.attachEvent(&quot;on&quot;+eventStr , function(){      //在匿名函数中调用回调函数      callback.call(obj)    })  }}// 借助function由浏览器调用变成自己调用，就能使用call// 传入实参bind(btn01 , &quot;click&quot; , function(){  alert(this);})</code></pre><h3><span id="11-事件的传播">11、事件的传播</span></h3><p>关于事件的传播网景公司和微软公司有不同的理解</p><ul><li>微软公司认为事件应该是由内向外传播，也就是当事件触发时，应该先触发当前元素上的事件，然后再向元素的祖先元素上传播，也就是说事件应该在冒泡阶段执行</li><li>网景公司认为事件应该是由外向内传播的，也就是当前事件触发时，应该先触发当前元素的最外层的祖先元素的事件，然后再向内传播给后代元素</li></ul><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200814155622.png" alt="事件传播"></p><p>W3C综合了两个公司的方案，将事件传播分为了三个阶段：</p><ol><li>捕获阶段——在捕获阶段时从最外层的祖先元素，向目标元素进行事件的捕获，但是默认此时不会触发事件</li><li>目标阶段——事件捕获到目标元素，捕获结果开始在目标元素上触发事件</li><li>冒泡阶段——事件从目标元素向它的祖先元素传递，一次触发祖先元素上的事件</li></ol><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200814160425.png" alt="W3C事件传播"></p><p>如果希望在捕获阶段就触发事件，可以在addEventListener()的第三个参数设置为true，一般情况下我们不会希望在捕获阶段触发事件，所以这个参数一般都是false</p><pre><code class="javascript">obj.addEventListener(eventStr,callback,true)</code></pre><p>IE8及以下的浏览器中没有捕获阶段</p><h3><span id="12-鼠标事件">12、鼠标事件</span></h3><p>onmousedown、onmousemove、onmouseup</p><p>拖拽</p><p>还记得H5新添加的那个拖拽API嘛，那个非常好用的对吧，可是在H5还没出来的时候咋实现的呢</p><pre><code class="html">&lt;style&gt;  #box1{    width: 100px;    height: 100px;    background-color: red;    position: absolute;  }  #box2{    width: 100px;    height: 100px;    background-color: yellow;    position: absolute;    left: 200px;    top: 200px;  }&lt;/style&gt;&lt;div id=&quot;box1&quot;&gt;&lt;/div&gt;&lt;div id=&quot;box2&quot;&gt;&lt;/div&gt;&lt;script type=&quot;text/javascript&quot;&gt;  window.onload = function(){    /*    * 拖拽box1元素    *  - 拖拽的流程    *         1.当鼠标在被拖拽元素上按下时，开始拖拽  onmousedown    *         2.当鼠标移动时被拖拽元素跟随鼠标移动 onmousemove    *         3.当鼠标松开时，被拖拽元素固定在当前位置    onmouseup    */    //获取box1    var box1 = document.getElementById(&quot;box1&quot;)    //为box1绑定一个鼠标按下事件    //当鼠标在被拖拽元素上按下时，开始拖拽  onmousedown    box1.onmousedown = function(event){      event = event || window.event      //div的偏移量 鼠标.clentX - 元素.offsetLeft      //div的偏移量 鼠标.clentY - 元素.offsetTop      var ol = event.clientX - box1.offsetLeft      var ot = event.clientY - box1.offsetTop      //为document绑定一个onmousemove事件      document.onmousemove = function(event){        event = event || window.event        //当鼠标移动时被拖拽元素跟随鼠标移动 onmousemove        //获取鼠标的坐标        var left = event.clientX - ol        var top = event.clientY - ot        //修改box1的位置        box1.style.left = left+&quot;px&quot;        box1.style.top = top+&quot;px&quot;      }      //为document绑定一个鼠标松开事件      document.onmouseup = function(){        //当鼠标松开时，被拖拽元素固定在当前位置    onmouseup        //取消document的onmousemove事件(移到同样是`position: absolute`的另一个盒子上时，会被另一个覆盖，松开鼠标会触发另一个盒子的onmouseup，因此需要给document绑定)        document.onmousemove = null        //取消document的onmouseup事件        document.onmouseup = null      }    }  }&lt;/script&gt;</code></pre><p>onmouseup使用过了直接就不需要了，可以直接取消，这样onmouseup就变成一次性事件了。</p><p>点击box光标会变到鼠标左上角，需要求出box的偏移量，在按下时求出，移动时减去这个偏移量就是鼠标所处的位置了。(如果有滚动条还要进行处理)</p><pre><code class="javascript">box1.onmousedown = function(event){    /* 设置box1捕获所有鼠标按下的事件     * setCapture()     *  - 当调用一个元素的setCapture()方法后，这个元素将会把下一次所有的鼠标按下相关的事件捕获到自己身上     *     - 只有IE支持，但是在火狐中调用时不会报错，     *         而如果使用chrome调用，会报错     */  // 只需要设置一次，不需要一直捕获，在up中释放  // 做判断，有就用(IE)，没有就不用    /*if(box1.setCapture){        box1.setCapture()    }*/    box1.setCapture &amp;&amp; box1.setCapture()    event = event || window.event    //div的偏移量 鼠标.clentX - 元素.offsetLeft    //div的偏移量 鼠标.clentY - 元素.offsetTop    var ol = event.clientX - box1.offsetLeft    var ot = event.clientY - box1.offsetTop    //为document绑定一个onmousemove事件    document.onmousemove = function(event){        event = event || window.event        //当鼠标移动时被拖拽元素跟随鼠标移动 onmousemove        //获取鼠标的坐标        var left = event.clientX - ol        var top = event.clientY - ot        //修改box1的位置        box1.style.left = left+&quot;px&quot;        box1.style.top = top+&quot;px&quot;    }    //为document绑定一个鼠标松开事件    document.onmouseup = function(){        //当鼠标松开时，被拖拽元素固定在当前位置    onmouseup        //取消document的onmousemove事件        document.onmousemove = null        //取消document的onmouseup事件        document.onmouseup = null        //当鼠标松开时，取消对事件的捕获        box1.releaseCapture &amp;&amp; box1.releaseCapture()    }    /*     * 当我们拖拽一个网页中的内容时，浏览器会默认去搜索引擎中搜索内容，     *     此时会导致拖拽功能的异常，这个是浏览器提供的默认行为，     *     如果不希望发生这个行为，则可以通过return false来取消默认行为     * 但是这招对IE8不起作用(IE8用capture，其他用这个)     */    return false}</code></pre><p>封装</p><pre><code class="javascript">/** 提取一个专门用来设置拖拽的函数* 参数：开启拖拽的元素*/function drag(obj){//当鼠标在被拖拽元素上按下时，开始拖拽  onmousedownobj.onmousedown = function(event){    obj.setCapture &amp;&amp; obj.setCapture()    event = event || window.event    var ol = event.clientX - obj.offsetLeft    var ot = event.clientY - obj.offsetTop    //为document绑定一个onmousemove事件    document.onmousemove = function(event){        event = event || window.event        var left = event.clientX - ol        var top = event.clientY - ot        obj.style.left = left+&quot;px&quot;        obj.style.top = top+&quot;px&quot;    }    //为document绑定一个鼠标松开事件    document.onmouseup = function(){        //当鼠标松开时，被拖拽元素固定在当前位置    onmouseup        //取消document的onmousemove事件        document.onmousemove = null        //取消document的onmouseup事件        document.onmouseup = null        //当鼠标松开时，取消对事件的捕获        obj.releaseCapture &amp;&amp; obj.releaseCapture()    }    return false}var box1 = document.getElementById(&quot;box1&quot;)var box2 = document.getElementById(&quot;box2&quot;)var img1 = document.getElementById(&quot;img1&quot;)//开启box1的拖拽drag(box1)//开启box2的drag(box2)drag(img1)  // &lt;img src=&quot;img/an.jpg&quot; id=&quot;img1&quot; style=&quot;position: absolute;&quot;/&gt;</code></pre><p>鼠标滚轮事件</p><p>onmousewheel(火狐用DOMMouseScroll)</p><pre><code class="javascript">&lt;style type=&quot;text/css&quot;&gt;  #box1{    width: 100px;    height: 100px;    background-color: red;  }&lt;/style&gt;&lt;script type=&quot;text/javascript&quot;&gt;  window.onload = function(){    //获取id为box1的div    var box1 = document.getElementById(&quot;box1&quot;)    //为box1绑定一个鼠标滚轮滚动的事件    /*      * onmousewheel鼠标滚轮滚动的事件，会在滚轮滚动时触发，      *     但是火狐不支持该属性      * 在火狐中需要使用 DOMMouseScroll 来绑定滚动事件      *     注意该事件需要通过addEventListener()函数来绑定      */    box1.onmousewheel = function(event){      event = event || window.event;      //event.wheelDelta 可以获取鼠标滚轮滚动的方向      //向上滚 120   向下滚 -120      //wheelDelta这个值我们不看大小，只看正负      //alert(event.wheelDelta);       //wheelDelta这个属性火狐中不支持      //在火狐中使用event.detail来获取滚动的方向      //向上滚 -3  向下滚 3      //alert(event.detail);      /*        * 当鼠标滚轮向下滚动时，box1变长        *     当滚轮向上滚动时，box1变短        */      //判断鼠标滚轮滚动的方向      if(event.wheelDelta &gt; 0 || event.detail &lt; 0){        //向上滚，box1变短        box1.style.height = box1.clientHeight - 10 + &quot;px&quot;      }else{        //向下滚，box1变长        box1.style.height = box1.clientHeight + 10 + &quot;px&quot;      /*        * 使用addEventListener()方法绑定响应函数，取消默认行为时不能使用return false        * 需要使用event来取消默认行为event.preventDefault();        * 但是IE8不支持event.preventDefault();这个玩意，如果直接调用会报错        */      event.preventDefault &amp;&amp; event.preventDefault()      /*        * 当滚轮滚动时，如果浏览器有滚动条，滚动条会随之滚动，        * 这是浏览器的默认行为，如果不希望发生，则可以取消默认行为        */      return false    }    //为火狐绑定滚轮事件    bind(box1,&quot;DOMMouseScroll&quot;,box1.onmousewheel)  }  function bind(obj , eventStr , callback){    if(obj.addEventListener){      //大部分浏览器兼容的方式      obj.addEventListener(eventStr , callback , false);    }else{      /*        * this是谁由调用方式决定        * callback.call(obj)        */      //IE8及以下      obj.attachEvent(&quot;on&quot;+eventStr , function(){        //在匿名函数中调用回调函数        callback.call(obj)      })    }  }&lt;/script&gt;&lt;body style=&quot;height: 2000px;&quot;&gt;&lt;div id=&quot;box1&quot;&gt;&lt;/div&gt;&lt;/body&gt;</code></pre><h3><span id="13-键盘事件">13、键盘事件</span></h3><p>onkeydown</p><ul><li><p>按键被按下</p></li><li><p>对于onkeydown来说如果一直按着某个按键不松手，则事件会一直触发</p></li><li><p>当onkeydown连续触发时，第一次和第二次之间会间隔稍微长一点，其他的会非常的快，这种设计是为了防止误操作的发生。</p></li></ul><p>  onkeyup</p><ul><li>按键被松开<ul><li>键盘事件一般都会绑定给一些可以获取到焦点的对象或者是document</li></ul></li></ul><p>可以通过keyCode来获取按键的编码<br>通过它可以判断哪个按键被按下<br>除了keyCode，事件对象中还提供了几个属性<br><code>altKey</code> <code>ctrlKey</code> <code>shiftKey</code><br> 这个三个用来判断alt ctrl 和 shift是否被按下<br>如果按下则返回true，否则返回false</p><pre><code class="javascript">window.onload = function(){  document.onkeydown = function(event){    event = event || window.event    /*        * 可以通过keyCode来获取按键的编码，通过它可以判断哪个按键被按下        * 除了keyCode，事件对象中还提供了几个属性 altKey、    ctrlKey、shiftKey        */    //console.log(event.keyCode)    //判断一个y是否被按下    //判断y和ctrl是否同时被按下    if(event.keyCode === 89 &amp;&amp; event.ctrlKey){      console.log(&quot;ctrl和y都被按下了&quot;)    }  }  /*document.onkeyup = function(){            console.log(&quot;按键松开了&quot;)    }*/  //获取input  var input = document.getElementsByTagName(&quot;input&quot;)[0] // &lt;input type=&quot;text&quot; /&gt;  input.onkeydown = function(event){            event = event || window.event    //console.log(event.keyCode)    //数字 48 - 57    //使文本框中不能输入数字    if(event.keyCode &gt;= 48 &amp;&amp; event.keyCode &lt;= 57){      //在文本框中输入内容，属于onkeydown的默认行为      //如果在onkeydown中取消了默认行为，则输入的内容，不会出现在文本框中      return false    }  }}</code></pre><p>通过按键移动盒子</p><pre><code class="html">&lt;style type=&quot;text/css&quot;&gt;    #box1{        width: 100px;        height: 100px;        background-color: red;        position: absolute;    }&lt;/style&gt;&lt;script type=&quot;text/javascript&quot;&gt;    //使div可以根据不同的方向键向不同的方向移动    /*     * 按左键，div向左移     * 按右键，div向右移     * ……     */    window.onload = function(){        //为document绑定一个按键按下的事件        document.onkeydown = function(event){            event = event || window.event            //定义一个变量，来表示移动的速度            var speed = 10            //当用户按了ctrl以后，速度加快            if(event.ctrlKey){                speed = 500            }            /*             * 37 左             * 38 上             * 39 右             * 40 下             */            switch(event.keyCode){                case 37                    //alert(&quot;向左&quot;); left值减小                    box1.style.left = box1.offsetLeft - speed + &quot;px&quot;                    break                case 39:                    //alert(&quot;向右&quot;);                    box1.style.left = box1.offsetLeft + speed + &quot;px&quot;                    break                case 38:                    //alert(&quot;向上&quot;);                    box1.style.top = box1.offsetTop - speed + &quot;px&quot;                    break                case 40:                    //alert(&quot;向下&quot;);                    box1.style.top = box1.offsetTop + speed + &quot;px&quot;                    break            }        }    }&lt;/script&gt;&lt;div id=&quot;box1&quot;&gt;&lt;/div&gt;</code></pre><p>还存在问题：会有卡顿</p><h2><span id="四-bom">四、BOM</span></h2><p>BOM(浏览器对象模型)可以使我们通过js来操作浏览器</p><p>在BOM中为我们提供了一组对象，用来完成对浏览器的操作</p><p>BOM对象：</p><ul><li>window——代表的是整个浏览器窗口，同时也是网页中的全局对象</li><li>navigator——代表的是当前浏览器的信息，通过该对象可以识别不同的浏览器</li><li>location——代表当前浏览器的地址栏信息，通过Location可以获取地址栏信息，或者操作浏览器跳转网页</li><li>history——代表浏览器的历史记录，可以通过该对象来操作浏览器的而历史记录(由于隐私原因，该对象不能获取到具体的历史记录，只能操作浏览器向前或向后翻页，而且该操作只在当次访问时有效)</li><li>screen——代表用户的屏幕信息，通过该对象可以获取到用户的显示器的相关信息</li></ul><p>这些BOM对象在浏览器中都是作为window对象的属性保存的，可以通过window对象来使用，也可以直接使用</p><pre><code class="javascript">console.log(window)// console.log(navigator)console.log(window.navigator)console.log(location)console.log(screen)</code></pre><h3><span id="1-navigator">1、Navigator</span></h3><ul><li>代表的当前浏览器的信息，通过该对象可以来识别不同的浏览器</li><li>由于历史原因，Navigator对象中的大部分属性已经不能帮助我们识别浏览器了</li></ul><table><thead><tr><th align="left">属性</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_nav_appcodename.asp" target="_blank" rel="noopener">appCodeName</a></td><td align="left">返回浏览器的代码名。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_nav_appminorversion.asp" target="_blank" rel="noopener">appMinorVersion</a></td><td align="left">返回浏览器的次级版本。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_nav_appname.asp" target="_blank" rel="noopener">appName</a></td><td align="left">返回浏览器的名称。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_nav_appversion.asp" target="_blank" rel="noopener">appVersion</a></td><td align="left">返回浏览器的平台和版本信息。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_nav_browserlanguage.asp" target="_blank" rel="noopener">browserLanguage</a></td><td align="left">返回当前浏览器的语言。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_nav_cookieenabled.asp" target="_blank" rel="noopener">cookieEnabled</a></td><td align="left">返回指明浏览器中是否启用 cookie 的布尔值。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_nav_cpuclass.asp" target="_blank" rel="noopener">cpuClass</a></td><td align="left">返回浏览器系统的 CPU 等级。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_nav_online.asp" target="_blank" rel="noopener">onLine</a></td><td align="left">返回指明系统是否处于脱机模式的布尔值。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_nav_platform.asp" target="_blank" rel="noopener">platform</a></td><td align="left">返回运行浏览器的操作系统平台。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_nav_systemlanguage.asp" target="_blank" rel="noopener">systemLanguage</a></td><td align="left">返回 OS 使用的默认语言。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_nav_useragent.asp" target="_blank" rel="noopener">userAgent</a></td><td align="left">返回由客户机发送服务器的 user-agent 头部的值。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_nav_userlanguage.asp" target="_blank" rel="noopener">userLanguage</a></td><td align="left">返回 OS 的自然语言设置。</td></tr></tbody></table><table><thead><tr><th align="left">方法</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_nav_javaenabled.asp" target="_blank" rel="noopener">javaEnabled()</a></td><td align="left">规定浏览器是否启用 Java。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_nav_taintenabled.asp" target="_blank" rel="noopener">taintEnabled()</a></td><td align="left">规定浏览器是否启用数据污点 (data tainting)。</td></tr></tbody></table><pre><code class="javascript">alert(navigator.appName)</code></pre><ul><li>一般我们只会使用userAgent来判断浏览器的信息，userAgent是一个字符串，这个字符串中包含有用来描述浏览器信息的内容，不同的浏览器会有不同的userAgent</li></ul><pre><code class="javascript">console.log(navigator.userAgent)火狐的userAgent：&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0&quot;Chrome的userAgent：&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36&quot;IE11&quot;Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Tablet PC 2.0; rv:11.0) like Gecko&quot;IE10&quot;Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Tablet PC 2.0)&quot;IE9&quot;Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Tablet PC 2.0)&quot;IE8&quot;Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Tablet PC 2.0)&quot;</code></pre><p>根据UA来判断</p><pre><code class="javascript">var ua = navigator.userAgentconsole.log(ua)if(/firefox/i.test(ua)){  alert(&quot;你是火狐&quot;)}else if(/chrome/i.test(ua)){  alert(&quot;你是Chrome&quot;)}else if(/msie/i.test(ua)){  alert(&quot;woc，你是IE&quot;)}</code></pre><p>如果通过userAgent不能判断，还可以通过一些浏览器中特有的对象，来判断浏览器的信息</p><p>比如：<code>ActiveXObject</code></p><pre><code class="javascript">if(&quot;ActiveXObject&quot; in window){    alert(&quot;你是IE，我已经抓住你了~~~&quot;);}else{    alert(&quot;你不是IE~~~&quot;);}/*alert(&quot;ActiveXObject&quot; in window);*/if(/firefox/i.test(ua)){  alert(&quot;你是火狐&quot;)}else if(/chrome/i.test(ua)){  alert(&quot;你是Chrome&quot;)}else if(/msie/i.test(ua)){  alert(&quot;woc，你是IE&quot;)} else if(&quot;ActiveXObject&quot; in window){    alert(&quot;你是IE11，还好&quot;);}</code></pre><h3><span id="2-history">2、History</span></h3><ul><li>History对象可以用来操作浏览器向前或向后翻页</li></ul><table><thead><tr><th align="left">属性</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_his_length.asp" target="_blank" rel="noopener">length</a></td><td align="left">返回浏览器历史列表中的 URL 数量。</td></tr></tbody></table><table><thead><tr><th align="left">方法</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_his_back.asp" target="_blank" rel="noopener">back()</a></td><td align="left">加载 history 列表中的前一个 URL。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_his_forward.asp" target="_blank" rel="noopener">forward()</a></td><td align="left">加载 history 列表中的下一个 URL。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_his_go.asp" target="_blank" rel="noopener">go()</a></td><td align="left">加载 history 列表中的某个具体页面。</td></tr></tbody></table><pre><code class="javascript">// length属性可以获取到当前访问的链接数量alert(history.length)// back()可以用来回退到上一个页面，作用和浏览器的回退按钮一样history.back()// forward()可以跳转到下一个页面，作用和浏览器的前进按钮一样history.forward()/* go()可以用来跳转到指定的页面，需要一个整数作为参数 * 1：表示向前跳转一个页面，相当于forward() * 2：表示向前跳转两个页面 * -1：表示向后跳转一个页面 * -2：表示向后跳转 两个页面*/history.go(-2)</code></pre><h3><span id="3-location">3、Location</span></h3><p>如果直接打印location，则可以获取到地址栏中的信息(当前页面的完整路径)</p><pre><code>alert(location)</code></pre><p>如果直接将location属性修改为一个完整的路径或相对路径，则我们的页面会自动跳转到该路径，并且会生成响应的历史记录</p><pre><code>location = &quot;http://www.baidu.com&quot;location = &quot;./index.html&quot;</code></pre><table><thead><tr><th align="left">属性</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_loc_hash.asp" target="_blank" rel="noopener">hash</a></td><td align="left">设置或返回从井号 (#) 开始的 URL（锚）。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_loc_host.asp" target="_blank" rel="noopener">host</a></td><td align="left">设置或返回主机名和当前 URL 的端口号。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_loc_hostname.asp" target="_blank" rel="noopener">hostname</a></td><td align="left">设置或返回当前 URL 的主机名。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_loc_href.asp" target="_blank" rel="noopener">href</a></td><td align="left">设置或返回完整的 URL。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_loc_pathname.asp" target="_blank" rel="noopener">pathname</a></td><td align="left">设置或返回当前 URL 的路径部分。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_loc_port.asp" target="_blank" rel="noopener">port</a></td><td align="left">设置或返回当前 URL 的端口号。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_loc_protocol.asp" target="_blank" rel="noopener">protocol</a></td><td align="left">设置或返回当前 URL 的协议。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_loc_search.asp" target="_blank" rel="noopener">search</a></td><td align="left">设置或返回从问号 (?) 开始的 URL（查询部分）。</td></tr></tbody></table><table><thead><tr><th align="left">属性</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_loc_assign.asp" target="_blank" rel="noopener">assign()</a></td><td align="left">加载新的文档。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_loc_reload.asp" target="_blank" rel="noopener">reload()</a></td><td align="left">重新加载当前文档。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_loc_replace.asp" target="_blank" rel="noopener">replace()</a></td><td align="left">用新的文档替换当前文档。</td></tr></tbody></table><pre><code class="javascript">// assign()用来跳转到其他的页面，作用和直接修改location一样location.assign(&quot;http://www.baidu.com&quot;);/* * reload() *     - 用于重新加载当前页面，作用和刷新按钮一样 *     - 如果在方法中传递一个true，作为参数，则会强制清空缓存刷新页面 */location.reload(true);/* * replace() *     - 可以使用一个新的页面替换当前页面，调用完毕也会跳转页面 *         不会生成历史记录，不能使用回退按钮回退 */location.replace(&quot;index.html&quot;);</code></pre><h3><span id="4-screen">4、Screen</span></h3><table><thead><tr><th align="left">属性</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_screen_availheight.asp" target="_blank" rel="noopener">availHeight</a></td><td align="left">返回显示屏幕的高度 (除 Windows 任务栏之外)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_screen_availwidth.asp" target="_blank" rel="noopener">availWidth</a></td><td align="left">返回显示屏幕的宽度 (除 Windows 任务栏之外)。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_screen_bufferdepth.asp" target="_blank" rel="noopener">bufferDepth</a></td><td align="left">设置或返回调色板的比特深度。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_screen_colordepth.asp" target="_blank" rel="noopener">colorDepth</a></td><td align="left">返回目标设备或缓冲器上的调色板的比特深度。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_screen_devicexdpi.asp" target="_blank" rel="noopener">deviceXDPI</a></td><td align="left">返回显示屏幕的每英寸水平点数。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_screen_deviceydpi.asp" target="_blank" rel="noopener">deviceYDPI</a></td><td align="left">返回显示屏幕的每英寸垂直点数。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_screen_fontsmoothingenabled.asp" target="_blank" rel="noopener">fontSmoothingEnabled</a></td><td align="left">返回用户是否在显示控制面板中启用了字体平滑。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_screen_height.asp" target="_blank" rel="noopener">height</a></td><td align="left">返回显示屏幕的高度。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_screen_logicalxdpi.asp" target="_blank" rel="noopener">logicalXDPI</a></td><td align="left">返回显示屏幕每英寸的水平方向的常规点数。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_screen_logicalydpi.asp" target="_blank" rel="noopener">logicalYDPI</a></td><td align="left">返回显示屏幕每英寸的垂直方向的常规点数。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_screen_pixeldepth.asp" target="_blank" rel="noopener">pixelDepth</a></td><td align="left">返回显示屏幕的颜色分辨率（比特每像素）。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_screen_updateinterval.asp" target="_blank" rel="noopener">updateInterval</a></td><td align="left">设置或返回屏幕的刷新率。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_screen_width.asp" target="_blank" rel="noopener">width</a></td><td align="left">返回显示器屏幕的宽度。</td></tr></tbody></table><h3><span id="5-window">5、Window</span></h3><table><thead><tr><th align="left">属性</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_win_closed.asp" target="_blank" rel="noopener">closed</a></td><td align="left">返回窗口是否已被关闭。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_win_defaultstatus.asp" target="_blank" rel="noopener">defaultStatus</a></td><td align="left">设置或返回窗口状态栏中的默认文本。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/dom_obj_document.asp" target="_blank" rel="noopener">document</a></td><td align="left">对 Document 对象的只读引用。请参阅 <a href="https://www.w3school.com.cn/jsref/dom_obj_document.asp" target="_blank" rel="noopener">Document 对象</a>。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/dom_obj_history.asp" target="_blank" rel="noopener">history</a></td><td align="left">对 History 对象的只读引用。请参数 <a href="https://www.w3school.com.cn/jsref/dom_obj_history.asp" target="_blank" rel="noopener">History 对象</a>。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_win_innerheight_innerwidth.asp" target="_blank" rel="noopener">innerheight</a></td><td align="left">返回窗口的文档显示区的高度。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_win_innerheight_innerwidth.asp" target="_blank" rel="noopener">innerwidth</a></td><td align="left">返回窗口的文档显示区的宽度。</td></tr><tr><td align="left">length</td><td align="left">设置或返回窗口中的框架数量。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/dom_obj_location.asp" target="_blank" rel="noopener">location</a></td><td align="left">用于窗口或框架的 Location 对象。请参阅 <a href="https://www.w3school.com.cn/jsref/dom_obj_location.asp" target="_blank" rel="noopener">Location 对象</a>。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_win_name.asp" target="_blank" rel="noopener">name</a></td><td align="left">设置或返回窗口的名称。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/dom_obj_navigator.asp" target="_blank" rel="noopener">Navigator</a></td><td align="left">对 Navigator 对象的只读引用。请参数 <a href="https://www.w3school.com.cn/jsref/dom_obj_navigator.asp" target="_blank" rel="noopener">Navigator 对象</a>。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_win_opener.asp" target="_blank" rel="noopener">opener</a></td><td align="left">返回对创建此窗口的窗口的引用。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_win_outerheight.asp" target="_blank" rel="noopener">outerheight</a></td><td align="left">返回窗口的外部高度。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_win_outerwidth.asp" target="_blank" rel="noopener">outerwidth</a></td><td align="left">返回窗口的外部宽度。</td></tr><tr><td align="left">pageXOffset</td><td align="left">设置或返回当前页面相对于窗口显示区左上角的 X 位置。</td></tr><tr><td align="left">pageYOffset</td><td align="left">设置或返回当前页面相对于窗口显示区左上角的 Y 位置。</td></tr><tr><td align="left">parent</td><td align="left">返回父窗口。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/dom_obj_screen.asp" target="_blank" rel="noopener">Screen</a></td><td align="left">对 Screen 对象的只读引用。请参数 <a href="https://www.w3school.com.cn/jsref/dom_obj_screen.asp" target="_blank" rel="noopener">Screen 对象</a>。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_win_self.asp" target="_blank" rel="noopener">self</a></td><td align="left">返回对当前窗口的引用。等价于 Window 属性。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_win_status.asp" target="_blank" rel="noopener">status</a></td><td align="left">设置窗口状态栏的文本。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/prop_win_top.asp" target="_blank" rel="noopener">top</a></td><td align="left">返回最顶层的先辈窗口。</td></tr><tr><td align="left">window</td><td align="left">window 属性等价于 self 属性，它包含了对窗口自身的引用。</td></tr><tr><td align="left">screenLef/tscreenTop/screenX/screenY</td><td align="left">只读整数。声明了窗口的左上角在屏幕上的的 x 坐标和 y 坐标。IE、Safari 和 Opera 支持 screenLeft 和 screenTop，而 Firefox 和 Safari 支持 screenX 和 screenY。</td></tr></tbody></table><table><thead><tr><th align="left">方法</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_alert.asp" target="_blank" rel="noopener">alert()</a></td><td align="left">显示带有一段消息和一个确认按钮的警告框。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_blur.asp" target="_blank" rel="noopener">blur()</a></td><td align="left">把键盘焦点从顶层窗口移开。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_clearinterval.asp" target="_blank" rel="noopener">clearInterval()</a></td><td align="left">取消由 setInterval() 设置的 timeout。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_cleartimeout.asp" target="_blank" rel="noopener">clearTimeout()</a></td><td align="left">取消由 setTimeout() 方法设置的 timeout。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_close.asp" target="_blank" rel="noopener">close()</a></td><td align="left">关闭浏览器窗口。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_confirm.asp" target="_blank" rel="noopener">confirm()</a></td><td align="left">显示带有一段消息以及确认按钮和取消按钮的对话框。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_createpopup.asp" target="_blank" rel="noopener">createPopup()</a></td><td align="left">创建一个 pop-up 窗口。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_focus.asp" target="_blank" rel="noopener">focus()</a></td><td align="left">把键盘焦点给予一个窗口。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_moveby.asp" target="_blank" rel="noopener">moveBy()</a></td><td align="left">可相对窗口的当前坐标把它移动指定的像素。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_moveto.asp" target="_blank" rel="noopener">moveTo()</a></td><td align="left">把窗口的左上角移动到一个指定的坐标。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_open.asp" target="_blank" rel="noopener">open()</a></td><td align="left">打开一个新的浏览器窗口或查找一个已命名的窗口。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_print.asp" target="_blank" rel="noopener">print()</a></td><td align="left">打印当前窗口的内容。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_prompt.asp" target="_blank" rel="noopener">prompt()</a></td><td align="left">显示可提示用户输入的对话框。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_resizeby.asp" target="_blank" rel="noopener">resizeBy()</a></td><td align="left">按照指定的像素调整窗口的大小。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_resizeto.asp" target="_blank" rel="noopener">resizeTo()</a></td><td align="left">把窗口的大小调整到指定的宽度和高度。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_scrollby.asp" target="_blank" rel="noopener">scrollBy()</a></td><td align="left">按照指定的像素值来滚动内容。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_scrollto.asp" target="_blank" rel="noopener">scrollTo()</a></td><td align="left">把内容滚动到指定的坐标。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_setinterval.asp" target="_blank" rel="noopener">setInterval()</a></td><td align="left">按照指定的周期（以毫秒计）来调用函数或计算表达式。</td></tr><tr><td align="left"><a href="https://www.w3school.com.cn/jsref/met_win_settimeout.asp" target="_blank" rel="noopener">setTimeout()</a></td><td align="left">在指定的毫秒数后调用函数或计算表达式。</td></tr></tbody></table><p>定时器：</p><ul><li>循环定时器</li></ul><p>js的程序执行速度是非常快的，如果洗完过一段程序，可以每隔一段事件执行一次，可以使用定时调用</p><p><code>setInterval()</code>定时调用</p><p>可以将一个函数，每隔一段事件执行一次</p><p>参数有两个：</p><p>第一个是回调函数，该函数会每隔一段时间调用一次</p><p>第二个是每次调用间隔的时间，单位是毫秒</p><p>返回值：</p><p>返回一个Number类型的数据，这个数字用来作为定时器的唯一标识</p><p><code>clearInterval()</code>可以用来关闭一个定时器，方法中需要一个定时器的标识作为参数，这样将关闭标识对应的定时器</p><ul><li>延时定时器</li></ul><p><code>setTimeout()</code>延时调用，延时调用一个函数不马上执行，而是隔一段时间以后在执行，而且只会执行一次</p><p>延时调用和定时调用的区别，定时调用会执行多次，而延时调用只会执行一次</p><p>延时调用和定时调用实际上是可以互相代替的，在开发中可以根据自己需要去选择</p><ol><li>count自动增加</li></ol><pre><code class="html">&lt;h1 id=&quot;count&quot;&gt;&lt;/h1&gt;&lt;script&gt;    /*// 执行太快  for(var i=0 ; i&lt;10000 ; i++){      count.innerHTML = i      alert(&quot;hello&quot;)  }*/  var num = 1  var timer = setInterval(function(){    count.innerHTML = num++        if(num == 11){            //关闭定时器            clearInterval(timer)        }                },1000)            //console.log(timer)&lt;/script&gt;</code></pre><ol start="2"><li>自动切换图片</li></ol><pre><code class="html">&lt;script type=&quot;text/javascript&quot;&gt;  window.onload = function(){    /*      * 使图片可以自动切换      */    //获取img标签    var img = document.getElementById(&quot;img&quot;)    //创建一个数组来保存图片的路径    var imgArr = [&quot;img/1.jpg&quot;,&quot;img/2.jpg&quot;,&quot;img/3.jpg&quot;,&quot;img/4.jpg&quot;,&quot;img/5.jpg&quot;]    //创建一个变量，用来保存当前图片的索引    var index = 0    //定义一个变量，用来保存定时器的标识    var timer    //为btn01绑定一个单击响应函数    var startBtn = document.getElementById(&quot;startBtn&quot;)    startBtn.onclick = function(){      /*        * 目前，我们每点击一次按钮，就会开启一个定时器，        *     点击多次就会开启多个定时器，这就导致图片的切换速度过快，        *     并且我们只能关闭最后一次开启的定时器        */      //在开启定时器之前，需要将当前元素上的其他定时器关闭      clearInterval(timer)      /*        * 开启一个定时器，来自动切换图片        */      timer = setInterval(function(){        //使索引自增        index++        //判断索引是否超过最大索引        /*if(index &gt;= imgArr.length){          //则将index设置为0          index = 0;        }*/        index %= imgArr.length        //修改img的src属性        img.src = imgArr[index]      },1000)    }    //为stopBtn绑定一个单击响应函数    var stopBtn = document.getElementById(&quot;stopBtn&quot;)    stopBtn.onclick = function(){      //点击按钮以后，停止图片的自动切换，关闭定时器      /*        * clearInterval()可以接收任意参数，        *     如果参数是一个有效的定时器的标识，则停止对应的定时器        *     如果参数不是一个有效的标识，则什么也不做        */      clearInterval(timer)    }  }&lt;/script&gt;&lt;img id=&quot;img&quot; src=&quot;img/1.jpg&quot;/&gt;&lt;br /&gt;&lt;br /&gt;&lt;button id=&quot;startBtn&quot;&gt;开始&lt;/button&gt;&lt;button id=&quot;stopBtn&quot;&gt;停止&lt;/button&gt;</code></pre><ol start="3"><li>解决按键移动box卡顿问题</li></ol><pre><code class="html">&lt;style type=&quot;text/css&quot;&gt;  #box1{    width: 100px;    height: 100px;    background-color: red;    position: absolute;  }&lt;/style&gt;&lt;script type=&quot;text/javascript&quot;&gt;  //使div可以根据不同的方向键向不同的方向移动  /*    * 按左键，div向左移    * 按右键，div向右移    * 。。。    */  window.onload = function(){     //定义一个变量，来表示移动的速度    var speed = 10    //创建一个变量表示方向    //通过修改dir来影响移动的方向    var dir = 0    //开启一个定时器，来控制div的移动    setInterval(function(){      /*        * 37 左        * 38 上        * 39 右        * 40 下        */      switch(dir){        case 37:          //alert(&quot;向左&quot;); left值减小          box1.style.left = box1.offsetLeft - speed + &quot;px&quot;          break        case 39:          //alert(&quot;向右&quot;);          box1.style.left = box1.offsetLeft + speed + &quot;px&quot;          break        case 38:          //alert(&quot;向上&quot;);          box1.style.top = box1.offsetTop - speed + &quot;px&quot;          break        case 40:          //alert(&quot;向下&quot;);          box1.style.top = box1.offsetTop + speed + &quot;px&quot;          break      }    },30)    //为document绑定一个按键按下的事件    document.onkeydown = function(event){      event = event || window.event      //当用户按了ctrl以后，速度加快      if(event.ctrlKey){        speed = 500      }else{        speed = 10      }      //使dir等于按键的值      dir = event.keyCode    };    //当按键松开时，div不再移动    document.onkeyup = function(){      //设置方向为0      dir = 0    }    }&lt;/script&gt;&lt;div id=&quot;box1&quot;&gt;&lt;/div&gt;</code></pre><p>延时调用</p><pre><code class="javascript">/*setInterval(function(){    console.log(num++);},3000);*/var timer = setTimeout(function(){    console.log(num++)},3000)//使用clearTimeout()来关闭一个延时调用clearTimeout(timer);</code></pre><ol start="4"><li>盒子移动</li></ol><pre><code class="html">&lt;style type=&quot;text/css&quot;&gt;   *{    margin: 0;    padding: 0;  }   #box1{    width: 100px;    height: 100px;    margin-top: 50px;    background-color: red;    position: absolute;    left: 0;  }&lt;/style&gt;&lt;script type=&quot;text/javascript&quot;&gt;  window.onload = function(){    //获取box1    var box1 = document.getElementById(&quot;box1&quot;)    //获取btn01    var btn01 = document.getElementById(&quot;btn01&quot;)    //定义一个变量，用来保存定时器的标识    var timer    //点击按钮以后，使box1向右移动（left值增大）    btn01.onclick = function(){      //关闭上一个定时器      clearInterval(timer)      //开启一个定时器，用来执行动画效果      timer = setInterval(function(){        //获取box1的原来的left值        var oldValue = parseInt(getStyle(box1,&quot;left&quot;))        //在旧值的基础上增加        var newValue = oldValue + 1        //判断newValue是否大于800        if(newValue &gt; 800){          newValue = 800        }        //将新值设置给box1        box1.style.left = newValue + &quot;px&quot;        //当元素移动到800px时，使其停止执行动画        if(newValue == 800){          //达到目标，关闭定时器          clearInterval(timer)        }      },30)    }  }  /*    * 定义一个函数，用来获取指定元素的当前的样式    * 参数：    *         obj 要获取样式的元素    *         name 要获取的样式名    */  function getStyle(obj , name){    if(window.getComputedStyle){      //正常浏览器的方式，具有getComputedStyle()方法      return getComputedStyle(obj , null)[name]    }else{      //IE8的方式，没有getComputedStyle()方法      return obj.currentStyle[name]    }  }&lt;/script&gt;&lt;button id=&quot;btn01&quot;&gt;点击按钮以后box1向右移动&lt;/button&gt;&lt;div id=&quot;box1&quot;&gt;&lt;/div&gt;&lt;div style=&quot;width: 0; height: 1000px; border-left:1px black solid; position: absolute; left: 800px;top:0;&quot;&gt;&lt;/div&gt;</code></pre><ol start="5"><li>封装一个移动函数</li></ol><pre><code class="html">&lt;script type=&quot;text/javascript&quot;&gt;  window.onload = function(){    //获取box1    var box1 = document.getElementById(&quot;box1&quot;)    //获取btn01    var btn01 = document.getElementById(&quot;btn01&quot;)    //获取btn02    var btn02 = document.getElementById(&quot;btn02&quot;)    //点击按钮以后，使box1向右移动（left值增大）    btn01.onclick = function(){      move(box1 , 800 , 10)    }    //点击按钮以后，使box1向左移动（left值减小）    btn02.onclick = function(){      move(box1 , 0 , 10)    }  }  //定义一个变量，用来保存定时器的标识  var timer  //尝试创建一个可以执行简单动画的函数  /*    * 参数：    *     obj:要执行动画的对象    *     target:执行动画的目标位置    *     speed:移动的速度(正数向右移动，负数向左移动)    */  function move(obj , target ,speed){    //关闭上一个定时器    clearInterval(timer)    //获取元素目前的位置    var current = parseInt(getStyle(obj,&quot;left&quot;))    //判断速度的正负值    //如果从0 向 800移动，则speed为正    //如果从800向0移动，则speed为负    if(current &gt; target){      //此时速度应为负值      speed = -speed    }    //开启一个定时器，用来执行动画效果    timer = setInterval(function(){      //获取box1的原来的left值      var oldValue = parseInt(getStyle(obj,&quot;left&quot;))      //在旧值的基础上增加      var newValue = oldValue + speed      //判断newValue是否大于800      //从800 向 0移动      //向左移动时，需要判断newValue是否小于target      //向右移动时，需要判断newValue是否大于target      if((speed &lt; 0 &amp;&amp; newValue &lt; target) || (speed &gt; 0 &amp;&amp; newValue &gt; target)){        newValue = target      }      //将新值设置给box1      obj.style.left = newValue + &quot;px&quot;      //当元素移动到0px时，使其停止执行动画      if(newValue == target){        //达到目标，关闭定时器        clearInterval(timer)      }    },30)  }  /*    * 定义一个函数，用来获取指定元素的当前的样式    * 参数：    *         obj 要获取样式的元素    *         name 要获取的样式名    */  function getStyle(obj , name){    if(window.getComputedStyle){      //正常浏览器的方式，具有getComputedStyle()方法      return getComputedStyle(obj , null)[name]    }else{      //IE8的方式，没有getComputedStyle()方法      return obj.currentStyle[name]    }  }&lt;/script&gt;&lt;button id=&quot;btn01&quot;&gt;点击按钮以后box1向右移动&lt;/button&gt;&lt;button id=&quot;btn02&quot;&gt;点击按钮以后box1向左移动&lt;/button&gt;&lt;div id=&quot;box1&quot;&gt;&lt;/div&gt;&lt;div style=&quot;width: 0; height: 1000px; border-left:1px black solid; position: absolute; left: 800px;top:0;&quot;&gt;&lt;/div&gt;</code></pre><p>封装move.js</p><pre><code class="javascript">//尝试创建一个可以执行简单动画的函数/* * 参数： *     obj:要执行动画的对象 *     attr:要执行动画的样式，比如：left top width height *     target:执行动画的目标位置 *     speed:移动的速度(正数向右移动，负数向左移动) *  callback:回调函数，这个函数将会在动画执行完毕以后执行 */function move(obj, attr, target, speed, callback) {    //关闭上一个定时器    clearInterval(obj.timer)    //获取元素目前的位置    var current = parseInt(getStyle(obj, attr))    //判断速度的正负值    //如果从0 向 800移动，则speed为正    //如果从800向0移动，则speed为负    if(current &gt; target) {        //此时速度应为负值        speed = -speed    }    //开启一个定时器，用来执行动画效果    //向执行动画的对象中添加一个timer属性，用来保存它自己的定时器的标识    obj.timer = setInterval(function() {        //获取box1的原来的left值        var oldValue = parseInt(getStyle(obj, attr))        //在旧值的基础上增加        var newValue = oldValue + speed        //判断newValue是否大于800        //从800 向 0移动        //向左移动时，需要判断newValue是否小于target        //向右移动时，需要判断newValue是否大于target        if((speed &lt; 0 &amp;&amp; newValue &lt; target) || (speed &gt; 0 &amp;&amp; newValue &gt; target)) {            newValue = target        }        //将新值设置给box1        obj.style[attr] = newValue + &quot;px&quot;        //当元素移动到0px时，使其停止执行动画        if(newValue == target) {            //达到目标，关闭定时器            clearInterval(obj.timer)            //动画执行完毕，调用回调函数            callback &amp;&amp; callback()        }    }, 30)}/* * 定义一个函数，用来获取指定元素的当前的样式 * 参数： *         obj 要获取样式的元素 *         name 要获取的样式名 */function getStyle(obj, name) {    if(window.getComputedStyle) {        //正常浏览器的方式，具有getComputedStyle()方法        return getComputedStyle(obj, null)[name]    } else {        //IE8的方式，没有getComputedStyle()方法        return obj.currentStyle[name]    }}</code></pre><p>调用</p><pre><code class="html">&lt;style type=&quot;text/css&quot;&gt;  *{    margin: 0;    padding: 0;  }    #box1{    margin-top: 50px;    width: 100px;    height: 100px;    background-color: red;    position: absolute;    left: 0;  }  #box2{    width: 100px;    height: 100px;    background-color: yellow;    position: absolute;    left: 0;    top: 200px;  }&lt;/style&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;js/move.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;  window.onload = function(){    //点击按钮以后，使box1向右移动（left值增大）    btn01.onclick = function(){      move(box1 ,&quot;left&quot;, 800 , 20)    }    //点击按钮以后，使box1向左移动（left值减小）    btn02.onclick = function(){      move(box1 ,&quot;left&quot;, 0 , 10)    }    //获取btn03    var btn03 = document.getElementById(&quot;btn03&quot;)    btn03.onclick = function(){      move(box2 , &quot;left&quot;,800 , 10)    }    //测试按钮    var btn04 = document.getElementById(&quot;btn04&quot;);    btn04.onclick = function(){      //move(box2 ,&quot;width&quot;, 800 , 10);      //move(box2 ,&quot;top&quot;, 800 , 10);      //move(box2 ,&quot;height&quot;, 800 , 10);      move(box2 , &quot;width&quot; , 800 , 10 , function(){        move(box2 , &quot;height&quot; , 400 , 10 , function(){          move(box2 , &quot;top&quot; , 0 , 10 , function(){            move(box2 , &quot;width&quot; , 100 , 10 , function(){            })          })        })      })    }  }&lt;/script&gt;&lt;button id=&quot;btn01&quot;&gt;点击按钮以后box1向右移动&lt;/button&gt;&lt;button id=&quot;btn02&quot;&gt;点击按钮以后box1向左移动&lt;/button&gt;&lt;button id=&quot;btn03&quot;&gt;点击按钮以后box2向右移动&lt;/button&gt;&lt;button id=&quot;btn04&quot;&gt;测试按钮&lt;/button&gt;&lt;div id=&quot;box1&quot;&gt;&lt;/div&gt;&lt;div id=&quot;box2&quot;&gt;&lt;/div&gt;&lt;div style=&quot;width: 0; height: 1000px; border-left:1px black solid; position: absolute; left: 800px;top:0;&quot;&gt;&lt;/div&gt;</code></pre><ol start="6"><li>轮播图</li></ol><pre><code class="html">&lt;style&gt;*{    margin: 0;    padding: 0;}/* * 设置outer的样式 */#outer{    /*设置宽和高*/    width: 520px;    height: 333px;    /*居中*/    margin: 50px auto;    /*设置背景颜色*/    background-color: greenyellow;    /*设置padding*/    padding: 10px 0;    /*开启相对定位*/    position: relative;    /*裁剪溢出的内容*/    overflow: hidden;}/*设置imgList*/#imgList{    /*去除项目符号*/    list-style: none;    /*设置ul的宽度*/    /*width: 2600px;*/    /*开启绝对定位*/    position: absolute;    /*设置偏移量*/    /*     * 每向左移动520px，就会显示到下一张图片     */    left: 0px;}/*设置图片中的li*/#imgList li{    /*设置浮动*/    float: left;    /*设置左右外边距*/    margin: 0 10px;}/*设置导航按钮*/#navDiv{    /*开启绝对定位*/    position: absolute;    /*设置位置*/    bottom: 15px;    /*设置left值         outer宽度  520         navDiv宽度 25*5 = 125             520 - 125 = 395/2 = 197.5     * */    /*left: 197px;*/}#navDiv a{    /*设置超链接浮动*/    float: left;    /*设置超链接的宽和高*/    width: 15px;    height: 15px;    /*设置背景颜色*/    background-color: red;    /*设置左右外边距*/    margin: 0 5px;    /*设置透明*/    opacity: 0.5;    /*兼容IE8透明*/    filter: alpha(opacity=50);}/*设置鼠标移入的效果*/#navDiv a:hover{    background-color: black;}&lt;/style&gt;&lt;!-- 创建一个外部的div，来作为大的容器 --&gt;&lt;div id=&quot;outer&quot;&gt;    &lt;!-- 创建一个ul，用于放置图片 --&gt;    &lt;ul id=&quot;imgList&quot;&gt;        &lt;li&gt;&lt;img src=&quot;img/1.jpg&quot;/&gt;&lt;/li&gt;        &lt;li&gt;&lt;img src=&quot;img/2.jpg&quot;/&gt;&lt;/li&gt;        &lt;li&gt;&lt;img src=&quot;img/3.jpg&quot;/&gt;&lt;/li&gt;        &lt;li&gt;&lt;img src=&quot;img/4.jpg&quot;/&gt;&lt;/li&gt;        &lt;li&gt;&lt;img src=&quot;img/5.jpg&quot;/&gt;&lt;/li&gt;    &lt;!-- 由于需要流畅衔接，故在最后加一张 --&gt;    &lt;li&gt;&lt;img src=&quot;img/1.jpg&quot;/&gt;&lt;/li&gt;    &lt;/ul&gt;    &lt;!--创建导航按钮--&gt;    &lt;div id=&quot;navDiv&quot;&gt;        &lt;a href=&quot;javascript:;&quot;&gt;&lt;/a&gt;        &lt;a href=&quot;javascript:;&quot;&gt;&lt;/a&gt;        &lt;a href=&quot;javascript:;&quot;&gt;&lt;/a&gt;        &lt;a href=&quot;javascript:;&quot;&gt;&lt;/a&gt;        &lt;a href=&quot;javascript:;&quot;&gt;&lt;/a&gt;    &lt;/div&gt;&lt;/div&gt;</code></pre><p>js</p><pre><code class="html">        &lt;!--引用工具--&gt;        &lt;script type=&quot;text/javascript&quot; src=&quot;js/move.js&quot;&gt;&lt;/script&gt;        &lt;script type=&quot;text/javascript&quot;&gt;            window.onload = function(){                //获取imgList                var imgList = document.getElementById(&quot;imgList&quot;);                //获取页面中所有的img标签                var imgArr = document.getElementsByTagName(&quot;img&quot;);                //设置imgList的宽度                imgList.style.width = 520*imgArr.length+&quot;px&quot;;                /*设置导航按钮居中*/                //获取navDiv                var navDiv = document.getElementById(&quot;navDiv&quot;);                //获取outer                var outer = document.getElementById(&quot;outer&quot;);                //设置navDiv的left值                navDiv.style.left = (outer.offsetWidth - navDiv.offsetWidth)/2 + &quot;px&quot;;                //默认显示图片的索引                var index = 0;                //获取所有的a                var allA = document.getElementsByTagName(&quot;a&quot;);                //设置默认选中的效果                allA[index].style.backgroundColor = &quot;black&quot;;                /*                     点击超链接切换到指定的图片                         点击第一个超链接，显示第一个图片                         点击第二个超链接，显示第二个图片                 * */                //为所有的超链接都绑定单击响应函数                for(var i=0; i&lt;allA.length ; i++){                    //为每一个超链接都添加一个num属性                    allA[i].num = i;                    //为超链接绑定单击响应函数                    allA[i].onclick = function(){                        //关闭自动切换的定时器                        clearInterval(timer);                        //获取点击超链接的索引,并将其设置为index                        index = this.num;                        //切换图片                        /*                         * 第一张  0 0                         * 第二张  1 -520                         * 第三张  2 -1040                         */                        //imgList.style.left = -520*index + &quot;px&quot;;                        //设置选中的a                        setA();                        //使用move函数来切换图片                        move(imgList , &quot;left&quot; , -520*index , 20 , function(){                            //动画执行完毕，开启自动切换                            autoChange();                        });                    };                }                //开启自动切换图片                autoChange();                //创建一个方法用来设置选中的a                function setA(){                    //判断当前索引是否是最后一张图片                    if(index &gt;= imgArr.length - 1){                        //则将index设置为0                        index = 0;                        //此时显示的最后一张图片，而最后一张图片和第一张是一摸一样                        //通过CSS将最后一张切换成第一张                        imgList.style.left = 0;                    }                    //遍历所有a，并将它们的背景颜色设置为红色                    for(var i=0 ; i&lt;allA.length ; i++){                        allA[i].style.backgroundColor = &quot;&quot;;                    }                    //将选中的a设置为黑色                    allA[index].style.backgroundColor = &quot;black&quot;;                };                //定义一个自动切换的定时器的标识                var timer;                //创建一个函数，用来开启自动切换图片                function autoChange(){                    //开启一个定时器，用来定时去切换图片                    timer = setInterval(function(){                        //使索引自增                        index++;                        //判断index的值                        index %= imgArr.length;                        //执行动画，切换图片                        move(imgList , &quot;left&quot; , -520*index , 20 , function(){                            //修改导航按钮                            setA();                        });                    },3000);                }            };        &lt;/script&gt;</code></pre><p>类的操作实现样式修改</p><p>我们可以通过修改元素的class属性来间接地修改样式，这样一来，我们只需要修改一次，即可同时修改多个样式，浏览器只需要重新渲染页面一次，性能比较好，并且这种方式，可以使表现和行为进一步地分离</p><pre><code class="javascript">box.className = &quot;b2&quot; // 修改类名为b2box.className += &quot;b3&quot;  // 在原有类的基础上加</code></pre><p>定义函数，用来对一个元素中class进行操作</p><pre><code class="javascript">/* 添加添加指定的class属性值参数：obj: 要添加class属性的元素cn: 要添加的class值*//*function addClass(obj, cn){  obj.className += &quot; &quot;+cn}addClass(box, &quot;b2&quot;)*/// 有了就不需要添加了function addClass(obj, cn){  if(!hasClass(obj, cn)){    obj.className += &quot; &quot;+cn  }}/*判断一个元素中是否含有指定的class属性值，如果有则返回true，没有则返回false*/function hasClass(obj, cn){    var reg = new RegExp(&quot;\\b&quot; +cn+ &quot;\\b&quot;)  return reg.test(obj.className)}/*删除一个元素中的指定的class属性*/function removeClass(obj, cn){  var reg = new RegExp(&quot;\\b&quot; +cn+ &quot;\\b&quot;)  obj.className = obj.className.replace(reg, &quot;&quot;)}/*toggleClass用来切换一个类，如果有，则删除；没有则添加*/function toggleClass(obj, cn){  if(hasClass(obj, cn)){    removeClass(obj, cn)  }else{    addClass(obj, cn)  }}</code></pre><p>二级菜单</p><pre><code class="html">&lt;style type=&quot;text/css&quot;&gt;    * {        margin: 0;        padding: 0;        list-style-type: none;    }    a,img {        border: 0;        text-decoration: none;    }    body {        font: 12px/180% Arial, Helvetica, sans-serif, &quot;新宋体&quot;;    }    div.sdmenu {        width: 150px;        margin: 0 auto;        font-family: Arial, sans-serif;        font-size: 12px;        padding-bottom: 10px;        background: url(bottom.gif) no-repeat right bottom;        color: #fff;    }    div.sdmenu div {        background: url(title.gif) repeat-x;        overflow: hidden;    }    div.sdmenu div:first-child {        background: url(toptitle.gif) no-repeat;    }    div.sdmenu div.collapsed {        height: 25px;    }    div.sdmenu div span {        display: block;        height: 15px;        line-height: 15px;        overflow: hidden;        padding: 5px 25px;        font-weight: bold;        color: white;        background: url(expanded.gif) no-repeat 10px center;        cursor: pointer;        border-bottom: 1px solid #ddd;    }    div.sdmenu div.collapsed span {        background-image: url(collapsed.gif);    }    div.sdmenu div a {        padding: 5px 10px;        background: #eee;        display: block;        border-bottom: 1px solid #ddd;        color: #066;    }    div.sdmenu div a.current {        background: #ccc;    }    div.sdmenu div a:hover {        background: #066 url(linkarrow.gif) no-repeat right center;        color: #fff;        text-decoration: none;    }&lt;/style&gt;&lt;div id=&quot;my_menu&quot; class=&quot;sdmenu&quot;&gt;    &lt;div&gt;        &lt;span class=&quot;menuSpan&quot;&gt;在线工具&lt;/span&gt;        &lt;a href=&quot;javascript:;&quot;&gt;图像优化&lt;/a&gt;        &lt;a href=&quot;javascript:;&quot;&gt;收藏夹图标生成器&lt;/a&gt;        &lt;a href=&quot;javascript:;&quot;&gt;邮件&lt;/a&gt;        &lt;a href=&quot;javascript:;&quot;&gt;htaccess密码&lt;/a&gt;        &lt;a href=&quot;javascript:;&quot;&gt;梯度图像&lt;/a&gt;        &lt;a href=&quot;javascript:;&quot;&gt;按钮生成器&lt;/a&gt;    &lt;/div&gt;    &lt;div class=&quot;collapsed&quot;&gt;        &lt;span class=&quot;menuSpan&quot;&gt;支持我们&lt;/span&gt;        &lt;a href=&quot;javascript:;&quot;&gt;推荐我们&lt;/a&gt;        &lt;a href=&quot;javascript:;&quot;&gt;链接我们&lt;/a&gt;        &lt;a href=&quot;javascript:;&quot;&gt;网络资源&lt;/a&gt;    &lt;/div&gt;    &lt;div class=&quot;collapsed&quot;&gt;        &lt;span class=&quot;menuSpan&quot;&gt;合作伙伴&lt;/span&gt;        &lt;a href=&quot;javascript:;&quot;&gt;JavaScript工具包&lt;/a&gt;        &lt;a href=&quot;javascript:;&quot;&gt;CSS驱动&lt;/a&gt;        &lt;a href=&quot;javascript:;&quot;&gt;CodingForums&lt;/a&gt;        &lt;a href=&quot;javascript:;&quot;&gt;CSS例子&lt;/a&gt;    &lt;/div&gt;    &lt;div class=&quot;collapsed&quot;&gt;        &lt;span class=&quot;menuSpan&quot;&gt;测试电流&lt;/span&gt;        &lt;a href=&quot;javascript:;&quot;&gt;Test&lt;/a&gt;        &lt;a href=&quot;javascript:;&quot;&gt;2&lt;/a&gt;        &lt;a href=&quot;javascript:;&quot;&gt;Lorem&lt;/a&gt;        &lt;a href=&quot;javascript:;&quot;&gt;Lorem ipsum dolor sit amet&lt;/a&gt;    &lt;/div&gt;&lt;/div&gt;</code></pre><p>使这个菜单在点击时能够把二级菜单展示出来，点击另一个span，关掉该项打开点击的项</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200815084147.png" alt="image-20200815084145224"></p><pre><code class="html">&lt;script type=&quot;text/javascript&quot; src=&quot;js/move.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;js/class.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;    window.onload = function(){        /*         * 我们的每一个菜单都是一个div         *     当div具有collapsed这个类时，div就是折叠的状态         *     当div没有这个类是，div就是展开的状态         */        /*         * 点击菜单，切换菜单的显示状态         */        //获取所有的class为menuSpan的元素        var menuSpan = document.querySelectorAll(&quot;.menuSpan&quot;);        //定义一个变量，来保存当前打开的菜单        var openDiv = menuSpan[0].parentNode        //为span绑定单击响应函数        for(var i=0 ; i&lt;menuSpan.length ; i++){            menuSpan[i].onclick = function(){                //this代表我当前点击的span                //获取当前span的父元素                var parentDiv = this.parentNode                //切换菜单的显示状态                toggleMenu(parentDiv)                //判断openDiv和parentDiv是否相同                if(openDiv != parentDiv  &amp;&amp; !hasClass(openDiv , &quot;collapsed&quot;)){                    //打开菜单以后，应该关闭之前打开的菜单                    //为了可以统一处理动画过渡效果，我们希望在这将addClass改为toggleClass                    //addClass(openDiv , &quot;collapsed&quot;)                    //此处toggleClass()不需要有移除的功能                    //toggleClass(openDiv , &quot;collapsed&quot;)                    //切换菜单的显示状态                    toggleMenu(openDiv)                }                //修改openDiv为当前打开的菜单                openDiv = parentDiv            }        }        /*         * 用来切换菜单折叠和显示状态         */        function toggleMenu(obj){            //在切换类之前，获取元素的高度            var begin = obj.offsetHeight            //切换parentDiv的显示            toggleClass(obj , &quot;collapsed&quot;)            //在切换类之后获取一个高度            var end = obj.offsetHeight            //动画效果就是将高度从begin向end过渡            //将元素的高度重置为begin            obj.style.height = begin + &quot;px&quot;            //执行动画，从bengin向end过渡            move(obj,&quot;height&quot;,end,10,function(){                //动画执行完毕，内联样式已经没有存在的意义了，删除之                obj.style.height = &quot;&quot;            })        }    }&lt;/script&gt;</code></pre><h2><span id="五-json">五、JSON</span></h2><p>JSON</p><ul><li>JS中的对象只有JS自己认识，其他的语言都不认识</li><li>JSON就是一个特殊格式的字符串，这个字符串可以被任意的语言所识别，并且可以转换为任意语言中的对象，JSON在开发中主要用来数据的交互</li><li>JavaScript Object Notation JS对象表示法</li><li>JSON和JS对象的格式一样，只不过JSON字符串中的属性名必须加双引号，其他的和JS语法一致</li><li>JSON分类：<ol><li>对象 <code>{}</code></li><li>数组 <code>[]</code></li></ol></li><li>JSON中允许的值：<ol><li>字符串</li><li>数值</li><li>布尔值</li><li>null</li><li>对象</li><li>数组</li></ol></li></ul><pre><code class="javascript">//创建一个对象var arr = &#39;[1,2,3,&quot;hello&quot;,true,null]&#39;var obj2 = &#39;{&quot;arr&quot;:[1,2,3]}&#39;var arr2 =&#39;[{&quot;name&quot;:&quot;孙悟空&quot;,&quot;age&quot;:18,&quot;gender&quot;:&quot;男&quot;},{&quot;name&quot;:&quot;孙悟空&quot;,&quot;age&quot;:18,&quot;gender&quot;:&quot;男&quot;}]&#39;</code></pre><p>将JSON字符串转换为JS中的对象</p><ul><li>在JS中，为我们提供了一个工具类，就叫JSON</li><li>这个对象可以帮助我们将一个JSON转换为JS对象，也可以将一个JS对象转换为JSON</li></ul><p>json –&gt; js对象</p><ul><li><code>JSON.parse()</code></li><li>可以将以JSON字符串转换为js对象</li><li>它需要一个JSON字符串作为参数，会将该字符串转换为JS对象并返回</li></ul><pre><code class="javascript">var json = &#39;{&quot;name&quot;:&quot;孙悟空&quot;,&quot;age&quot;:18,&quot;gender&quot;:&quot;男&quot;}&#39;var o = JSON.parse(json)var o2 = JSON.parse(arr)console.log(o.gender)console.log(o2[1])</code></pre><p> JS对象 —&gt; JSON</p><ul><li><code>JSON.stringify()</code></li><li>可以将一个JS对象转换为JSON字符串</li><li>需要一个js对象作为参数，会返回一个JSON字符串</li></ul><pre><code class="javascript">var obj3 = {name:&quot;猪八戒&quot; , age:28 , gender:&quot;男&quot;}var str = JSON.stringify(obj3)console.log(str)</code></pre><p>JSON这个对象在IE7及以下的浏览器中不支持，所以在这些浏览器中调用时会报错</p><p>如果需要兼容IE7及以下的JSON操作，则可以通过引入一个外部的js文件来处理</p><pre><code class="html">&lt;script src=&quot;https://cdn.bootcdn.net/ajax/libs/json2/20110223/json2.js&quot;&gt;&lt;/script&gt;</code></pre><p><code>eval()</code></p><ul><li>这个函数可以用来执行一段字符串形式的JS代码，并将执行结果返回</li><li>如果使用eval()执行的字符串中含有{},它会将{}当成是代码块</li><li>如果不希望将其当成代码块解析，则需要在字符串前后各加一个()</li><li>eval()这个函数的功能很强大，可以直接执行一个字符串中的js代码，</li><li>但是在开发中尽量不要使用，首先它的执行性能比较差，然后它还具有安全隐患</li></ul><pre><code class="javascript">var str = &#39;{&quot;name&quot;:&quot;孙悟空&quot;,&quot;age&quot;:18,&quot;gender&quot;:&quot;男&quot;}&#39;var obj = eval(&quot;(&quot;+str+&quot;)&quot;)console.log(obj)</code></pre><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="笔记" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="前端" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="前端" scheme="https://wallleap.cn/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="web" scheme="https://wallleap.cn/tags/web/"/>
    
      <category term="JavaScript" scheme="https://wallleap.cn/tags/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title>CSS学习笔记</title>
    <link href="https://wallleap.cn/2020/03/30/css/"/>
    <id>https://wallleap.cn/2020/03/30/css/</id>
    <published>2020-03-30T04:33:14.000Z</published>
    <updated>2025-01-24T09:22:15.351Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2><span id="一-css简介">一、CSS简介</span></h2><h3><span id="1-层叠样式表cascading-style-sheeets">1、层叠样式表(Cascading Style Sheeets)</span></h3><ul><li><p>CSS可以用来为网页创建样式表，通过样式表可以对网页进行装饰</p></li><li><p>可以将网页想象成是一层一层的结构，层次高的将会覆盖层次低的。</p></li><li><p>CSS可以分别为网页的各个层次设置样式</p></li></ul><h3><span id="2-使用">2、使用</span></h3><ul><li>内联样式：将css样式编写到元素的style属性当中</li></ul><pre><code class="css">&lt;p style=”color:red;font-size:13px;”&gt;&lt;/p&gt;</code></pre><p>内联样式只对当前的元素中的内容起作用，不方便复用，结构与表现耦合，不方便后期维护，不推荐使用(可在测试样式时使用)</p><ul><li>将CSS样式编写到head中的style标签中,通过CSS选择器选中指定元素，可以同时为这些元素一起设置样式，这样可以是样式进一步的复用</li></ul><pre><code class="css">&lt;style type=”text/css”&gt;p {  color:red;  Font-size:13px;}&lt;/style&gt;</code></pre><p>将样式编写到style标签，可以是表现和结构进一步分离，推荐使用</p><ul><li>将样式编写到外部的css文件中，然后通过link标签将外部的css文件引入到当前页面中，这样外部文件中的css样式表将会应用到当前页面中</li></ul><pre><code class="html">&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;style.css&quot;&gt;</code></pre><p>完全使结构和表现分离，可以让样式表在不同的页面中使用，最大限度的使样式可以进行复用，将样式统一写在样式表中，然后通过link标签引入，可以利用浏览器的缓存加快用户访问的速度，提高用户体验，最推荐使用</p><h3><span id="3-注释">3、注释</span></h3><pre><code class="css">// 单行注释/* 注释内容 */</code></pre><p>注：style中的不是html内容了</p><h3><span id="4-语法">4、语法</span></h3><p><code>选择器 声明块</code></p><ul><li><p>选择器：通过选择器可以选中页面中指定的元素，并且将声明块中的样式应用到选择器对应的元素上</p></li><li><p>声明块：声明块紧跟在选择器的后边，使用一对<code>{}</code>括起来，声明块中实际上就是一组一组的名值对结构，这一组一组额名值对我们称为声明，在一个声明块中可以写多个声明，多个声明之间使用<code>;</code>隔开，声明的样式名和样式值之间使用<code>:</code>连接</p><p>例如：</p></li></ul><pre><code class="css">div{  width: 200px;  height: 200px;}</code></pre><h3><span id="5-块-内联元素">5、块、内联元素</span></h3><ul><li>块元素：独占一行的元素</li></ul><pre><code class="html">&lt;div&gt;   I’m div!&lt;/div&gt;</code></pre><p>无论内容有多少，都会占一整行</p><p>div、p、h1、br…</p><p>div标签没有任何语义，就是一个纯粹的块元素，并且不会为它里面的元素设置任何的默认样式，div元素主要用来对页面进行布局的</p><ul><li>内联元素(行内元素)：只占自身大小的元素，不会占用一行</li></ul><pre><code class="html">&lt;span&gt;  I’m span.&lt;/span&gt;</code></pre><p>a、img、iframe、span</p><p>span没有任何的语义，span标签用来选中文字，然后为文字设置样式</p><p>Ps:块元素主要用来做页面中的布局，内联元素主要用来选中文本设置样式</p><p>一般情况下只使用块元素去包含内联元素，而不会使用内联元素去包含块元素，a元素可以包含除它本身之外的任意元素，p元素不可以包含任何的块元素</p><h3><span id="6-元素之间的关系">6、元素之间的关系</span></h3><ul><li><p>父元素：直接包含子元素的元素</p></li><li><p>子元素：直接被父元素包含的元素</p></li><li><p>祖先元素：直接或间接包含后代元素的元素，父元素也是祖先元素</p></li><li><p>后代元素：直接或间接被祖先元素包含的元素，子元素也是后代元素</p></li><li><p>兄弟元素：拥有相同父元素的元素</p></li></ul><h2><span id="二-选择器">二、选择器</span></h2><p>由于选择器使用是基本的，因此就不和CSS3分开讲了</p><h3><span id="1-元素选择器可以选中页面中的所有指定元素">1、元素选择器:可以选中页面中的所有指定元素</span></h3><p>语法：<code>标签名{}</code></p><pre><code class="css">P{  Color: red;}h1{  Font-size:25px;}</code></pre><h3><span id="2-id选择器通过元素的id属性值选中唯一的一个元素">2、id选择器:通过元素的id属性值选中唯一的一个元素</span></h3><p>语法：<code>#id属性值{}</code></p><pre><code class="html">&lt;p id=”p1”&gt;hello&lt;/p&gt;&lt;style&gt;  #p {     Font-size:16px;  }&lt;/style&gt;</code></pre><h3><span id="3-类选择器通过元素的class属性值选中一组元素">3、类选择器:通过元素的class属性值选中一组元素</span></h3><p>语法：<code>.class属性值{}</code></p><pre><code class="html">&lt;p class=”p2”&gt;111&lt;/p&gt;&lt;p class=”p2”&gt;222&lt;/p&gt;&lt;style&gt;    .p{      font-size:16px;    }&lt;/style&gt;</code></pre><p>可以同时为一个元素设置多个class属性值，多个值之间使用空格隔开</p><pre><code class="html">&lt;p class=”p2 111”&gt;111&lt;/p&gt;&lt;style&gt;    .111{      font-size:20px;    }&lt;/style&gt;</code></pre><h3><span id="4-选择器分组并集选择器同时选中多个选择器对应的元素">4、选择器分组(并集选择器)：同时选中多个选择器对应的元素</span></h3><p>语法：<code>选择器1,选择器2,选择器n{}</code></p><pre><code class="css">#p1,.p2,h1{  background-color:yellow;}</code></pre><h3><span id="5-通配选择器选中页面中的所有元素">5、通配选择器:选中页面中的所有元素</span></h3><p>语法：<code>*{}</code></p><pre><code class="css">*{     margin:0;     padding:0;}</code></pre><h3><span id="6-复合选择器交集选择器选中同时满足多个选择器的元素">6、复合选择器(交集选择器):选中同时满足多个选择器的元素</span></h3><p>语法：选择器1选择器2选择器n{}</p><pre><code class="html">&lt;span class=&quot;p3&quot;&gt;&lt;/span&gt;&lt;style&gt;    span.p3{      background-color:yellow;    }&lt;/style&gt;</code></pre><p>Id一般不使用</p><h3><span id="7-后代元素选择器选中指定元素的指定后代元素">7、后代元素选择器：选中指定元素的指定后代元素</span></h3><p>语法：<code>祖先元素 后代元素{}</code></p><pre><code class="html">&lt;div&gt;  &lt;p&gt;      &lt;span&gt;hello&lt;/span&gt;      &lt;span&gt;nihao&lt;/span&gt;  &lt;/p&gt;  &lt;span&gt;hello&lt;/span&gt;&lt;/div&gt;&lt;style&gt;    div span{color:greenyellow;}    div p span{font-size:50px;}&lt;/style&gt;</code></pre><h3><span id="8-子元素选择器选中指定父元素的指定子元素">8、子元素选择器：选中指定父元素的指定子元素</span></h3><p>语法：<code>父元素&gt;子元素{}</code></p><pre><code class="css">div&gt;span{background-color:red;}</code></pre><p>IE6及以下的不兼容</p><h3><span id="9-伪类选择器">9、伪类选择器</span></h3><p>伪类专门用来表示元素的一种的特殊的状态，例如访问过的超链接、获取焦点的文本框</p><p>当需要为处在这些特殊状态的元素设置样式时，就可以使用伪类</p><p>eg：</p><ul><li><p>正常(未访问过的) <strong><code>a:link</code></strong></p></li><li><p>访问过的 <strong><code>a:visited</code></strong> –浏览器通过历史记录来判断一个连接是否访问过。由于涉及到用户的隐私问题，故使用这个伪类只能设置字体的颜色(IE6能设置背景颜色)。</p></li><li><p>鼠标滑过 <strong><code>a:hover</code></strong></p></li><li><p>正在点击 <strong><code>a:active</code></strong> (点击不松手)</p></li></ul><p>后两个伪类其他元素也能设置(<em>IE6不支持除a之外的元素设置</em>)</p><ul><li>获取焦点 <strong><code>:focus</code></strong> </li></ul><pre><code class="html">&lt;input type=&quot;text&quot; /&gt;&lt;style&gt;    input:focus{background-color: yellow;}&lt;/style&gt;</code></pre><ul><li>指定元素前 <strong><code>:before</code></strong> 指定元素最前边的部分 一般需要结合属性content一起使用，添加内容</li></ul><pre><code class="css">p:before{content:”最前边加的”;}</code></pre><ul><li><p>指定元素后 <strong><code>:after</code></strong>  结束元素的最后的部分(结束标签前)</p></li><li><p>选中的元素 <strong><code>::selection</code></strong> 例如选中文字 </p></li></ul><p>火狐中可<code>p::-moz-selection{background-color: orange;}</code></p><h3><span id="10-伪元素选择特殊的位置">10、伪元素(选择特殊的位置)</span></h3><p>段落定义</p><ul><li>文本的第一个字母或字 <code>::first-letter</code></li></ul><pre><code class="css">/* 首字下沉 */p::first-letter{  font-size: 20px;  float: left;  /* 文本环绕 */}</code></pre><ul><li>文本第一行<code>::first-line</code></li></ul><p>如果同时设置了first-letter，则无法影响这个字/字母</p><ul><li><code>::selection</code> 可改变选中文本的样式，只能设置显示的样式，不能改变内容大小</li></ul><ul><li><code>::before</code>和<code>::after</code>伪元素</li></ul><p>i.   是一个行内元素，如果想要设置宽高需要转换为块(<code>display:block</code> <code>float:**</code> <code>position:**</code>)</p><p>ii.  必须添加content，即使没有内容，也要加上<code>content: “”</code></p><p>iii. <code>E:before</code>、<code>E:after</code>在旧版本里是伪类，在新版本中是伪元素，新版本中单冒号的会被自动识别为<code>E::befor</code>、<code>E::after</code>，按伪元素来对待，这样做的目的是兼容性处理</p><p>iv. <code>E::before</code>：定义在一个元素的内容之前插入content属性定义的内容与样式</p><p>v. <code>E::after</code>：定义在一个元素的内容之后插入content属性定义的内容与样式</p><p>vi. 注意：</p><p>a)  IE6、IE7与IE8(怪异模式Quirks Mode)不支持此伪元素</p><p>b)  CSS2中E<code>:before</code>、<code>E:after</code>属于伪类，并且没有伪元素的概念，CSS3中提出伪元素的概念<code>E::before</code>、<code>E::after</code>，并且归属到了伪元素当中，伪类中就不再存在<code>E:before</code>、<code>E:after</code>伪类</p><p>c)  功能完全等同于一个DOM元素，但是不会在DOM树中生成</p><h3><span id="11-属性选择器">11、属性选择器</span></h3><p><code>title属性</code>可以给任何标签指定，光标移动到元素上时，元素中的<code>title属性</code>的值将会作为提示文字显示。</p><p>作用：可以根据元素中的属性或属性值来选取指定元素</p><p>语法：元素(可以是元素选择器、类选择器、id选择器等)后跟</p><ul><li><p><code>[属性名]</code> 选取含有指定属性的元素</p><p><code>E[attr]</code>: 查找拥有attr属性的E标签</p></li><li><p><code>[属性名=”属性值”]</code> 选取含有指定属性值的元素</p><p><code>E[attr=value]</code>: 查找拥有attr属性且属性值为value的E标签(严格匹配)</p></li><li><p><code>[属性名^=”属性值”]</code> 选取属性值以指定内容开头的元素</p><p><code>E[attr^=value]</code>: 查找拥有attr属性且属性值是以value开头的E标签</p></li><li><p><code>[属性名*=”属性值”]</code> 选取属性值包含指定内容的元素</p><p><code>E[attr*=value]</code>: 查找拥有attr属性且属性值中包含value(可以在任何位置)的E标签</p></li><li><p><code>[属性名$=”属性值”]</code> 选取属性值以指定内容结尾的元素</p><p><code>E[attr$=value]</code>: 查找拥有attr属性且属性值是以value结尾的E标签</p></li></ul><p>eg:</p><pre><code class="css">p[title] {background-color:green;}p[title=hello] {background-color:yellow;} // 也可给title值加上双引号p[title^=”ab”] {background-color:purple;} // 以ab开头的p[title$=”c”] {background-color:pink;} // 以c结尾的p[title*=”c”] {background-color:purple;} // 包含c的</code></pre><h3><span id="12-子元素选择器">12、子元素选择器</span></h3><ul><li><code>父元素&gt;子元素</code> : 比如<code>div&gt;a</code>代表选择<code>div</code>下的子元素<code>a</code></li></ul><p>其他子元素选择器(相对于父元素的结构伪类)</p><ul><li><code>:first-child</code>：选择该元素父标签下第一个子标签</li></ul><p><code>E:first-child</code>: 查找E元素的父元素下的第一个E元素</p><ul><li><code>:last-child</code>：选择该元素父标签下最后一个子标签</li></ul><p><code>E:last-child</code>: 查找E元素中最后一个指定类型的子元素</p><ul><li><code>:nth-child(n)</code>：选择指定位置的子元素，里面的n可以传<ul><li>数字：从1开始的数字，选择该元素父标签下的第几个子标签</li><li>n的表达式：<code>n</code>代表所有的该标签，<code>2n</code>代表父标签下偶数个子标签，<code>3n</code>代表选择第三个……，<code>3n-1</code></li><li><code>even</code>/<code>odd</code>：偶数/奇数</li></ul></li></ul><p><code>E:nth-child(n)</code>: 指定索引位置，n是从1开始的数字 也可以是偶数：even 奇数：odd</p><p>上面这三个是既需要满足最后一个，又得是这个标签，例如：</p><pre><code class="css">// p*3+ul&gt;(span{span1}+li{$}*6+span{span2})li:first-child{font-size: 30px;}  // 选择不到，因为li的父元素ul下第一个子元素是span，不满足lispan:last-child{color: green;} // 可以选择到，因为span的父元素ul下最后一个子元素是spanli:nth-child(2){background-color: red;} // 1的话选择不到，2能选择到&lt;li&gt;1&lt;/li&gt;(在父元素下第二个，且为li标签)body &gt; p:first-child{color: red;} // body子元素p如果p是在第一个元素则选中，若p前还有其他类型的元素，则选不到了(所有元素中排)P:last-child{color: green;}  // 如果最后一个p后面还有其他元素则选择不到P:nth-child(2){background-color:yellow;} // 任意位置，传值，可以设置数字、even(偶数)、odd(奇数)</code></pre><ul><li><code>:first-of-type</code></li><li><code>:last-of-type</code></li><li><code>:nth-of-tyle(n)</code> 这三个分别表示同类型中的第一个、最后一个、第几个</li></ul><p><code>E:nth-of-type(n)</code>: (限定类型) n也可以是数字、even、odd、还可以是n(代表默认取值范围为0~子元素的长度)、还可以利用n取前几个(-n+5就是代表前5个，当n&lt;=0时无效)</p><p>这三个选择指定类型的子元素，推荐使用这个</p><ul><li><code>:nth-last-of-tyle(n)</code></li></ul><p><code>E:nth-last-of-type(n)</code>: 同上(-n+5代表最后5个) （n——索引、关键字、表达式）</p><pre><code class="css">p:first-of-type{font-size: 20px;} // 所有p中的第一个(同一类型中排)</code></pre><ul><li><p><code>E:empty</code>: 选中没有任何子节点的E元素(空格也算子元素)</p></li><li><p><code>E:target</code>: 可以为锚点目标元素添加样式，当目标元素被触发为当前锚链接的目标时，调用此伪类样式  </p></li></ul><pre><code class="html">&lt;style&gt;  h2:target{    color: red;  }&lt;/style&gt;&lt;a href=&quot;#title1&quot;&gt;跳转到title1&lt;/a&gt;&lt;a href=&quot;#title2&quot;&gt;跳转到title2&lt;/a&gt;&lt;h2 id=&quot;title1&quot;&gt;title1&lt;/h2&gt;&lt;p&gt;……&lt;/p&gt;&lt;h2 id=&quot;title2&quot;&gt;title2&lt;/h2&gt;&lt;p&gt;……&lt;/p&gt;</code></pre><h3><span id="13-兄弟元素选择器兄弟伪类">13、兄弟元素选择器(兄弟伪类)</span></h3><p>(1) <strong>后一个</strong>兄弟选择器</p><p> 作用：选中一个元素后紧跟着的指定的兄弟元素</p><p> 语法：<code>前一个+后一个</code></p><pre><code class="css">span+p{} // span后紧挨着的p</code></pre><p>(2) 选中<strong>后边的所有兄弟</strong>元素</p><p> 语法：<code>前一个~后边所有</code></p><pre><code class="css">span~p{}</code></pre><h3><span id="14-否定伪类">14、否定伪类</span></h3><p> 作用：可以从已选中的元素中剔除出某些元素</p><p> 语法： :<code>not(选择器)</code></p><pre><code class="css">p:not(.hello){} // 选择所有不带hello类的p标签</code></pre><h2><span id="三-样式的继承">三、样式的继承</span></h2><p>和儿子继承父亲的遗产一样，在CSS中，祖先元素上的样式，也会被他的后代元素所继承。eg：</p><pre><code class="html">&lt;p&gt;    这个是p中文字  &lt;span&gt;p中的span&lt;/span&gt;&lt;/p&gt;</code></pre><p>利用继承，可以将一些基本的样式设置给祖先元素，这样所有的后代元素将会自动继承这些样式。但是并不是所有的样式都会被子元素所继承，例如<strong>背景相关</strong>、<strong>边框相关</strong>、<strong>定位相关</strong>的样式都<strong>不会被继承</strong>。</p><h2><span id="四-选择器的优先级">四、选择器的优先级</span></h2><p>当使用不同的选择器，选中同一个元素并且设置相同的样式时，这时样式之间产生了冲突，最终到底采用哪个选择器定义歌样式，由选择器的优先级(权重)决定，优先级高的优先显示。</p><h3><span id="1-优先级的规则">1、优先级的规则</span></h3><p> 内联样式，优先级 1000</p><p> id选择器，优先级 100</p><p> 类和伪类，优先级 10</p><p> 元素选择器，优先级 1</p><p> 通配*，优先级 0</p><p>继承的样式，没有优先级</p><p>当选择器中包含多种选择器时，需要将多种选择器的优先级相加然后再比较，但是注意，选择器优先级计算不会超过他的最大的数量级。<strong>如果选择器优先级一样，则使用靠后的样式(会覆盖前一个)。</strong></p><p>并集选择器的优先级是单独计算的。</p><p>可以在样式的最后，添加一个<code>!important</code>，则此时该样式将会获得一个最高的优先级。</p><pre><code class="css">p{  color: skyblue !important;}</code></pre><h3><span id="2-伪类的顺序">2、伪类的顺序</span></h3><p>涉及到a的伪类一共有四个</p><p>这四个选择器的优先级是一样的</p><p>active应写在hover后面，前两个要写在后两个前面，即下面的顺序：</p><p><strong><code>a:link{}</code></strong></p><p><strong><code>a:visited{}</code></strong> </p><p><strong><code>a:hover{}</code></strong></p><p><strong><code>a:active{}</code></strong> </p><h2><span id="五-html中一些标签">五、HTML中一些标签</span></h2><p>HTML中有些标签需要拿出来讲解一下</p><h3><span id="1-文本标签">1、文本标签</span></h3><p>下面两个强调</p><ul><li><p><code>&lt;strong&gt;&lt;/strong&gt;</code>内容上的强调 显示为粗体</p></li><li><p><code>&lt;em&gt;&lt;/em&gt;</code>   语气上的强调 显示为斜体</p></li></ul><p>下面两个无语义，单纯加粗、斜体</p><ul><li><code>&lt;b&gt;&lt;/b&gt;</code>   内容以粗体显示</li><li><code>&lt;i&gt;&lt;/i&gt;</code>    内容以斜体显示</li></ul><p>大小</p><ul><li><p><code>&lt;small&gt;&lt;/small&gt;</code> <code>small</code>标签中的内容会比他的父元素中的文字要小一些，在H5中表示一些细则一类的内容，比如：合同中的小字、网站的版权声明等</p></li><li><p><code>big</code>无语义，淘汰</p><p>引用</p></li><li><p><code>&lt;cite&gt;&lt;/cite&gt;</code>网页中所有的加书名号的内容，表示参考的内容</p></li><li><p><code>q</code>标签表示短的引用(行内引用) 自动加双引号</p></li><li><p><code>blockquote</code>标签表示一个长引用(块级引用)</p><p>上下标</p></li><li><p><code>sup</code> 设置上标</p></li><li><p><code>sub</code> 设置下标</p></li></ul><p>文字上的线</p><ul><li><p><code>del</code> 表示删除的内容 自动添加删除线</p></li><li><p><code>ins</code>表示一个插入的内容 自动添加下划线</p><p>代码片段</p></li><li><p><code>pre</code>标签是一个预格式标签，将会将代码中的格式保存，不会忽略多个空格</p></li><li><p><code>code</code>专门用来表示代码，一般结合<code>pre</code>和<code>code</code>来表示一段代码</p></li></ul><h3><span id="2-列表">2、列表</span></h3><p>(1) 无序列表</p><p><code>ul</code>标签来创建一个无序列表</p><p>使用<code>li</code>在<code>ul</code>中创建一个一个的列表项，一个<code>li</code>就是一个列表项</p><pre><code class="html">&lt;ul type=”disc”&gt;  &lt;li&gt;北京&lt;/li&gt;  &lt;li&gt;上海&lt;/li&gt;&lt;/ul&gt;</code></pre><p>通过<code>type</code>属性可以修改无序列表的项目符号。默认的项目符号一般都不使用！非要加项目符号，采用为<code>li</code>设置背景方式实现。</p><ul><li><p><code>disc</code> 默认，实心的原点</p></li><li><p><code>square</code> 实心方块</p></li><li><p><code>circle</code> 空心圆球</p></li></ul><p>去掉项目符号：</p><pre><code class="css">ul{  list-style: none;}</code></pre><p>导航栏中的一般都是用的无序列表</p><p><code>ul</code>、<code>li</code>都是块元素</p><p>(2) 有序列表</p><p><code>ol</code>来创建一个有序列表</p><pre><code class="html">&lt;ol&gt;  &lt;li&gt;结构&lt;/li&gt;     &lt;li&gt;表现&lt;/li&gt;     &lt;li&gt;行为&lt;/li&gt;&lt;/ol&gt;</code></pre><p>有序列表使用有序的序号作为项目符号。</p><p><code>type</code>属性可以指定序号的类型</p><ul><li><p><code>默认值</code>，使用阿拉伯数字</p></li><li><p><code>a</code>/<code>A</code>，小/大写</p></li><li><p><code>i</code>/<code>I</code>，罗马数字</p></li></ul><p>列表之间都是可以相互嵌套的，可以在无序列表中放有序列表，也可以在有序中放无序。当然嵌套的<code>ul</code>、<code>ol</code>都是放在<code>li</code>中。</p><p>(3) 定义列表</p><p>用来对一些词汇或内容进行定义</p><p>使用<code>dl</code>创建</p><p><code>dl</code>中有两个子标签</p><p><code>dt</code>：被定义的内容 <code>dd</code>：对定义的内容进行描述</p><pre><code class="html">&lt;dl&gt; &lt;dt&gt;胡萝卜&lt;dt&gt; &lt;dd&gt;蔬菜&lt;/dd&gt; &lt;dd&gt;好吃&lt;/dd&gt; &lt;dd&gt;含维生素E&lt;/dd&gt; &lt;dt&gt;西瓜&lt;/dt&gt; &lt;dd&gt;水果&lt;/dd&gt;&lt;/dl&gt;</code></pre><p>制作下拉菜单的时候可以用</p><h3><span id="3-表格">3、表格</span></h3><p>表格在日常生活中使用的非常多，比如excel就是专门用来创建表格的工具，表格就是用来表示一些格式化的数据的，比如：课程表、银行对账单等</p><p>在网页中也可以来创建出不同的表格</p><p>在HTML中使用<code>table</code>标签来创建一个表格</p><p>在<code>table</code>标签中使用<code>tr</code>来表示表格中的一行，有几行就写几个<code>tr</code></p><p>在<code>tr</code>中需要使用<code>td</code>来创建一个单元格，有几个单元格就写几个<code>td</code></p><p>可以使用<code>th</code>标签来表示表头中的内容，它的用法和<code>td</code>一样，不同的是它会有一些默认效果</p><p>使用<code>&lt;td colspan=&quot;2&quot;&gt;&lt;/td&gt;</code>横向合并单元格</p><p>使用<code>&lt;td rowspan=&quot;2&quot;&gt;&lt;/td&gt;</code>纵向合并单元格</p><p>直接用案例来看表格样式：</p><pre><code class="html">&lt;style&gt;  table{    width: 500px;    margin: 30px auto;    /* 设置边框 */    border: 1px solid silver;    /*      * table和td边框之间默认有一个距离，通过border-spacing属性可以设置这个距离     */     border-spacing: 10px;     /*       * border-collapse可以用来设置表格的边框合并，如果设置了边框合并，则border-spacing自动失效      */     border-collapse: collapse;  }  /* 设置隔行变色 */  tr:nth-child(even){    background-color: skyblue;  }  /* 鼠标移入tr后，改变颜色 */  tr:hover{    background-color: #ff0;  }&lt;/style&gt;&lt;table&gt;  &lt;tr&gt;    &lt;th&gt;姓名&lt;/th&gt;    &lt;th&gt;性别&lt;/th&gt;    &lt;th&gt;年龄&lt;/th&gt;    &lt;th&gt;地址&lt;/th&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;孙悟空&lt;/td&gt;    &lt;td rowspan=&quot;2&quot;&gt;男&lt;/td&gt;    &lt;td&gt;未知&lt;/td&gt;    &lt;td&gt;花果山&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;猪八戒&lt;/td&gt;    &lt;td&gt;未知&lt;/td&gt;    &lt;td&gt;高老庄&lt;/td&gt;  &lt;/tr&gt;&lt;/table&gt;</code></pre><p>长表格</p><p>有一些情况下表格是非常长的，这时就需要将表格分为三个部分，表头、表格的主体、表格底部，分别用三个标签<code>thead</code>、<code>tbody</code>、<code>tfoot</code>表示</p><p>这三个标签的作用就是区分表格的不同部分，它们都是table的子标签，都需要直接写到table中，tr需要写在这些标签当中</p><p>标签<code>thead</code>、<code>tbody</code>、<code>tfoot</code>中的内容，永远会显示在表格的头部、中间、底部</p><p>如果表格中没有写tbody，浏览器会自动在表格中添加tbody，并且将所有的tr都放到tbody中，所以注意tr并不是table的子元素，而是tbody的子元素，通过<code>table&gt;tr</code>无法选中行，需要通过<code>tbody&gt;tr</code></p><p>表格的列数由td最多的那行决定</p><p>表格是可以嵌套的，可以在td中再嵌套一个表格</p><p>使用表格布局–了解即可</p><p>以前表格很多情况实际上是用来对页面进行布局的，但是这种方式早已被CSS所淘汰了</p><p>演示一个吧：</p><pre><code class="html">&lt;style&gt;  *{    margin: 0;    padding: 0;  }  table{    width: 1200px;    margin: 0 auto;    border: none;    border-spacing: 0;  }  .header, .footer{    width: 1200px;    height: 200px;    background-color: yellow;  }  .nav{    width: 200px;    height: 516px;    background-color: green;  }  .content{    width: 780px;    height: 516px;    margin: 10px;    background-color: skyblue;  }  .aside{    width: 200px;    height: 516px;    background-color: pink;  }&lt;/style&gt;&lt;table&gt;  &lt;tr&gt;    &lt;td colspan=&quot;3&quot;&gt;&lt;div class=&quot;header&quot;&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;&lt;div class=&quot;nav&quot;&gt;&lt;/div&gt;&lt;/td&gt;    &lt;td&gt;&lt;div class=&quot;content&quot;&gt;&lt;/div&gt;&lt;/td&gt;    &lt;td&gt;&lt;div class=&quot;aside&quot;&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td colspan=&quot;3&quot;&gt;&lt;div class=&quot;footer&quot;&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;&lt;/table&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811172343.png" alt="image-20200811172342416"></p><p>写起来是真的很方便啊，但是改起来会想哭的</p><h3><span id="4-表单">4、表单</span></h3><p>表单的作用就是用来将用户信息提交给服务器的，比如：百度搜索框、登录这些操作都需要填写表单</p><p>使用<code>form</code>标签创建表单，<code>form</code>标签中必须制定一个<code>action</code>属性，该属性指向的是一个服务器的地址，当我们提交表单时将会提交到<code>action</code>属性对应的地址</p><p>使用<code>form</code>创建的仅仅是一个空白的表单，我们还需要向<code>form</code>中添加不同的表单项</p><ul><li>使用<code>input</code>来创建一个文本框，它的<code>type</code>属性是<code>text</code>，如果希望表单项中的数据提交到服务器中，还需要给表单项指定一个<code>name</code>属性，<code>name</code>表示提交内容的名字。用户填写的信息会负载url地址的后面以查询字符串的形式发送给服务器。在文本框中指定<code>value</code>属性值，将会作为默认值</li></ul><p><code>url地址?查询字符串</code></p><p><code>属性名=属性值&amp;属性名=属性值</code></p><ul><li>使用<code>input</code>创建密码框，它的<code>type</code>值为<code>password</code></li></ul><ul><li>使用<code>input</code>来创建单选按钮，它的<code>type</code>属性使用<code>radio</code>。单选按钮通过<code>name</code>属性进行分组，<code>name</code>属性值相同的是一组按钮。像这种需要用户选择，但是不需要用户直接填写内容的表单项，还必须指定一个<code>value</code>属性，这样被选中的表单项的<code>value</code>属性值将会最终提交给服务器</li></ul><ul><li>使用<code>input</code>创建一个多选框，它的<code>type</code>属性使用<code>checkbox</code></li></ul><ul><li>使用<code>select</code>创建一个下拉列表，在下拉列表中使用<code>option</code>标签来创建一个个列表项.下拉列表的<code>name</code>属性指定给<code>select</code>，<code>value</code>属性指定给<code>option</code></li></ul><p><code>multiple=”multiple”</code>可以选中多个</p><p>在<code>select</code>中可以使用<code>optgroup</code>对选项进行分组，同一个<code>optgroup</code>中的选项是一组，通过<code>label</code>属性来指定分组的名字</p><p>如果希望在单选按钮或者是多选框中指定默认选中的选项，可以在想选中的项中，添加<code>checked=”checked”</code>属性</p><p>下拉列表可以通过在option中添加selected=”selected”将选项设置为默认选中</p><ul><li>使用<code>textarea</code>创建一个文本域</li></ul><ul><li>提交按钮可以将表单中的信息提交给服务器，使用<code>input</code>创建一个提交按钮，它的<code>type</code>属性是<code>submit</code>，在提交按钮中，可以通过<code>value</code>属性来指定按钮上的文字</li></ul><p><code>&lt;input type=&quot;reset&quot; /&gt;</code>重置按钮，点击之后表单中的内容都会恢复为默认值</p><p><code>&lt;input type=&quot;button&quot; value=&quot;按钮&quot;&gt;</code>单纯的按钮，这个按钮没有任何功能，只能被点击，一般js设置功能</p><p><code>&lt;button type=&quot;submit&quot;&gt;提交&lt;/button&gt;</code></p><p><code>&lt;button type=&quot;reset&quot;&gt;重置&lt;/button&gt;</code></p><p><code>&lt;button type=&quot;button&quot;&gt;按钮&lt;/button&gt;</code></p><p>这种方式和使用input类似，但是更灵活</p><ul><li>在HTML中提供了一个标签，专门用来选中表单中的提示文字的<code>label</code>标签，该标签可以指定一个<code>for</code>属性，该属性的值需要指定一个表单项的<code>id值</code></li></ul><ul><li><p>可以使用<code>fieldset</code>对表单项进行分组，在<code>fieldset</code>中使用<code>legend</code>子标签来指定组名</p><pre><code class="html">&lt;form action=”target.html”&gt;&lt;fieldset&gt;  &lt;legend&gt;用户信息&lt;/legend&gt;  &lt;label for=&quot;usrname&quot;&gt;用户名：&lt;/label&gt;&lt;input type=&quot;text&quot; name=&quot;username&quot; id=&quot;usrname&quot; /&gt;&lt;br /&gt;  &lt;label&gt;联系电话：&lt;input type=&quot;text&quot; name=&quot;num&quot; /&gt; &lt;/label&gt;&lt;br /&gt;  密码：&lt;input type=&quot;password&quot; name=&quot;pwd&quot; /&gt;&lt;br /&gt;  性别：&lt;input type=&quot;radio&quot; name=&quot;gender&quot; value=&quot;male&quot; /&gt;  &lt;input type=&quot;radio&quot; name=&quot;gender&quot; value=&quot;female&quot; checked=&quot;checked&quot; /&gt;女&lt;br /&gt;&lt;/fieldset&gt;&lt;fieldset&gt;  &lt;legend&gt;爱好&lt;/legend&gt;  爱好：&lt;input type=&quot;checkbox&quot; name=&quot;hobby&quot; value=&quot;lq&quot; /&gt;篮球  &lt;input type=&quot;checkbox&quot; name=&quot;hobby&quot; value=&quot;zq&quot; /&gt;足球  &lt;input type=&quot;checkbox&quot; name=&quot;hobby&quot; value=&quot;pq&quot; checked=&quot;checked&quot; /&gt;排球  &lt;input type=&quot;checkbox&quot; name=&quot;hobby&quot; value=&quot;ymq&quot; checked=&quot;checked&quot; /&gt;羽毛球  喜欢的明星：&lt;select name=&quot;star&quot;&gt;    &lt;optgroup lable=&quot;女明星&quot;&gt;      &lt;option value=&quot;lxr&quot;&gt;林心如&lt;/option&gt;      &lt;option value=&quot;ym&quot; selected=&quot;selected&quot;&gt;杨幂&lt;/option&gt;      &lt;option value=&quot;fbb&quot;&gt;范冰冰&lt;/option&gt;    &lt;/optgroup&gt;    &lt;optgroup label=&quot;男明星&quot;&gt;      &lt;option value=&quot;lxr&quot;&gt;林心如&lt;/option&gt;      &lt;option value=&quot;ym&quot; selected=&quot;selected&quot;&gt;杨幂&lt;/option&gt;      &lt;option value=&quot;fbb&quot;&gt;范冰冰&lt;/option&gt;    &lt;/optgroup&gt;  &lt;/select&gt;&lt;/fieldset&gt;自我介绍：&lt;textarea name=&quot;info&quot;&gt;&lt;/textarea&gt;&lt;input type=&quot;submit&quot; value=&quot;提交&quot; /&gt;&lt;input type=&quot;reset&quot; /&gt;&lt;input type=&quot;button&quot; value=&quot;按钮&quot;&gt;&lt;/form&gt;</code></pre></li></ul><h2><span id="六-文本格式化">六、文本格式化</span></h2><h3><span id="1-单位">1、单位</span></h3><ul><li>px 像素</li></ul><p>一个像素就相当于我们屏幕中的一个小点，我们的屏幕实际上就是由这些像素点构成的。但是这些像素点，是不能直接看见。不同显示器一个像素的大小也不相同，显示效果越好越清晰，像素就越小，反之像素越大</p><ul><li>百分比</li></ul><p>根据其父元素的样式计算该值。</p><p>自适应页面常使用百分比作为单位。</p><ul><li>em </li></ul><p>em相对当前元素的字体大小来计算的</p><p>1em=1font-size</p><p>当设置字体相关的样式时，经常会使用em</p><h3><span id="2-颜色">2、颜色</span></h3><p>CSS3中的颜色也到这里讲</p><ul><li><p>预设常量值：</p><p>颜色单词 red、blue、green、orange……</p></li><li><p>RGB </p><p>各颜色浓度 </p><p>rgb(红, 绿, 蓝)，取值在0~255之间，还可以按百分比取</p><p>rgb(255,0,0) rgb(200,0,0) rgb(0%,100%,0%)</p></li><li><p>HEX </p><p>和上面类似，但以十六进制显示 ，前两位表示红，中间两位表示绿，最后两位表示蓝(00~ff)</p><p>#FFFFFF 重复的两位可简写 #ff00aa #f0a</p></li><li><p>HSL</p><p>H：Hue(色调、色相)，0或360表示红色、120表示绿色、240表示蓝色。0~360，过渡也是红橙黄绿青蓝紫</p><p>S：Saturation(饱和度)。0.0%~100.0%</p><p>L：Lightness(亮度)。0.0%~100.0%，50%是平衡值 brightness</p><p>rgb、hsl、HEX表示颜色，都是按<font color="red">红</font>橙黄<font color="green">绿</font>青<font color="blue">蓝</font>紫顺序过去的，因此改变该颜色值，可以调到相邻的另一种颜色，比如rgb(255, 0, 0)表示红色，那么取一半</p></li><li><p>rgba rgba(128,128,0, .5)</p></li><li><p>hsla hsla(120, 1%, 50%, .5)</p></li></ul><p>这两个只是多了一个透明通道alpha，都是在最后一个参数，取值0~1，这样设置的透明度不会影响子元素</p><p>后面讲的<code>opacity</code>：透明度 0~1 设置父容器，则父容器中的所有子元素也会透明</p><p>单词透明<code>transparent</code>不可调节透明度，始终完全透明</p><h3><span id="3-字体">3、字体</span></h3><p><strong>(1) 字体颜色</strong> </p><p>格式：<code>color: 颜色值;</code></p><p>例如：<code>color: red;</code>，<code>color: rgb(255, 0, 0);</code>，<code>color: #f00;</code></p><p><strong>(2) 字体大小</strong> </p><p>格式：<code>font-size: 大小;</code></p><p>基本上填数值，而不使用large、small等属性值</p><p><code>font-size: 20px;</code></p><p>默认大小16px 并不是文字本身的大小 在页面中，每个文字都是处在一个看不见的框中 设置的实际上是格的高度，一般文字都要比这个格要小一些，有时会比格大 根据字体不同，显示效果也不同</p><p><strong>(3) 字体</strong></p><p>格式：<code>font-family: 字体;</code></p><p> <code>font-family: &quot;微软雅黑&quot;;</code> </p><p>如果浏览器支持该字体，则以该字体显示，不支持则用默认字体 可指定多个，<code>font-family: arial, 宋体 ;</code>优先使用前面字体，没有则尝试下一个 浏览器使用的字体默认使用计算机中字体 在开发中，字体用的少，尽量不要使用 。字体在后面还会讲到。</p><p>字体分类：(浏览器会自动选择大分类的一种字体)</p><ul><li>serif(衬线字体)：有粗细变化</li><li>sans-serif(非衬线字体)：无粗细变化</li><li>monospace(等宽字体)：上下同宽</li><li>cursive 草书字体</li><li>fantasy 虚幻字体</li></ul><p>指定字体类型：<code>font-family: sans-serif;</code></p><p>一般会将字体的大分类，指定为font-family中的最后一个字体。</p><p><code>font-family: arial， 微软雅黑, 华文彩云, serif;</code> (前面字体找不到</p><p>找后面的)</p><p><strong>(4) 字体样式</strong> </p><p>格式：<code>font-style: 样式;</code></p><p>可选值：</p><ul><li><code>normal</code>正常字体</li><li><code>italic</code>斜体</li><li><code>oblique</code>斜体</li><li><code>inherit</code>继承父元素样式</li><li><code>initial</code>默认</li><li><code>unset</code>不设置(正常)</li></ul><p><strong>(5)文本粗细</strong></p><p>格式：<code>font-weight: 样式值;</code></p><p>可选值：</p><ul><li>normal 正常</li><li>bold 加粗</li><li>lighter 定义更细的字符</li><li>border 更粗一点</li><li>100、200、300……900 定义由粗到细的字符。400 等同于 normal，而 700 等同于 bold。</li></ul><p><strong>(6) 小型大写字母</strong> </p><p>格式：<code>font-variant: 样式值;</code></p><p>可选值：</p><ul><li>normal 正常</li><li>small-caps 设置为小型大写字母字体</li></ul><p><strong>(7) font简写</strong></p><p><code>font: italic small-caps bold 60px “微软雅黑”;</code></p><p>使用这个font属性最后两个属性值有顺序(位置必须倒一、倒二)，且必须有这两个属性值。</p><p><strong>(8) 行间距</strong> </p><p>通过设置行高 行高越大、行间距越大</p><p> <code>line-height: 40px;</code></p><p>行高类似于单线本，两线之间的高度就是行高</p><p>网页中的文字实际上也是写在一个看不见的行中，文字会默认在行高中垂直居中 行间距=行高-字体大小</p><p><code>line-height: 120%;</code>相对于字体大小</p><p><code>line-height: 1.5;</code></p><p>对于单行文本来说，可以将行高设置为和父元素高度一致，可以使单行文本在父元素中垂直居中</p><p><strong>font的完整缩写</strong>：<code>font: font-style font-variant font-weight font-size/line-height font-family</code></p><p><code>font: 30px/40px “宋体”;</code>字体大小/行高 字体</p><h3><span id="4-文本">4、文本</span></h3><p>(1) 大小写 </p><p>格式：<code>text-transform: 值;</code></p><p>可选值：</p><ul><li>none 默认值，定义带有小写字母和大写字母的标准的文本</li><li>capitalize 文本中每个单词开头字母大写</li><li>uppercase 所有字母大写</li><li>lowercase 所有字母小写</li></ul><p>(2) 文本修饰 </p><ul><li><code>text-decoration-line: 线的位置;</code><ul><li>none 没有线</li><li>overline 文字上方</li><li>line-through 穿过文字</li><li>underline 文字下方</li></ul></li><li><code>text-decoration-style: 线的样式;</code><ul><li>none 默认的样式，一条直线</li><li>dashed 虚线</li><li>dotted 点</li><li>solid 直线</li><li>wavy 波浪线</li></ul></li><li><code>text-decoration-color: 颜色值;</code></li><li>其他的不重要</li></ul><p>可以简写：<code>text-decoration: underline dashed red;</code></p><p>(3) 字母间距 </p><p><code>letter-spacing: 10px;</code></p><p>(4) 单词间距 </p><p><code>word-spacing: 1em;</code></p><p>(5) 文本对齐 </p><p><code>text-align: 值;</code></p><ul><li>left 左对齐</li><li>center 水平居中</li><li>right 右对齐</li><li>justify 两端对齐</li></ul><p>(6) 首行缩进 <code>text-indent: 2em;</code></p><p>可用于隐藏文字<code>text-indent: -99999px;</code></p><h2><span id="七-盒子模型框模型-box-module">七、、盒子模型(框模型 Box Module)</span></h2><h3><span id="1-盒子">1、盒子</span></h3><ul><li>CSS处理网页时，它认为每个元素都包含在一个不可见的盒子里</li><li>我们只需要将相应的盒子摆放到网页中相应的位置即可完成网页的布局</li></ul><h3><span id="2-盒子模型">2、盒子模型</span></h3><p>一个盒子具有内容区(content)、内边距(padding)、边框(border)、外边距(margin)</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811142849.jpg" alt="盒模型"></p><p>盒模型包括标准盒模型(W3C盒模型)和怪异盒模型(IE盒模型)</p><ul><li>标准盒模型<strong>width</strong>指的是内容区域<strong>content</strong>的宽度；<strong>height</strong>指的是内容区域<strong>content</strong>的高度。</li></ul><p><strong>标准盒模型下盒子的大小</strong> = <strong>content</strong> + <strong>border</strong> + <strong>padding</strong> + <strong>margin</strong></p><ul><li>怪异盒模型<strong>width</strong>指的是内容、边框、内边距总的宽度（content + border + padding）；<strong>height</strong>指的是内容、边框、内边距总的高度</li></ul><p><strong>怪异盒模型下盒子的大小=width（content + border + padding） + margin</strong></p><p>在默认情况下，CSS设置的盒子宽度仅仅是内容区的宽度，而非盒子的宽度。同样的高度类似。真正的盒子的宽度(在页面上呈现出来的宽度)和高度，需要加上一些其他的属性。</p><p>很明显，这种盒模型不直观，很容易出错，造成网页中其他元素的错位。</p><p>CSS3中可以通过<code>box-sizing</code>来指定盒模型，即可指定为</p><ul><li><code>content-box</code>：标准盒模型</li><li><code>border-box</code>：IE盒模型</li></ul><p>(1) <code>width</code>、<code>height</code> 设置内容区的宽高</p><p>(2) <code>border-width</code>、<code>border-color</code>、<code>border-style</code> 设置边框宽、颜色、样式</p><ul><li><p><code>border-width: 10px 2px 3px 5px;</code> 四个值：上、右、下、左 (顺时针)</p></li><li><p><code>border-width: 10px 2px 3px;</code>  三个值：上、左右、下</p></li><li><p><code>border-width: 10px 3px;</code>  两个值：上下、左右</p></li><li><p><code>border-width: 10px ;</code> 一个值：上下左右</p></li><li><p>除此之外，还可以分别设置每边的宽：<code>border-xxx-width: 10px;</code> 其中xxx表示<code>top</code>、<code>right</code>、<code>bottom</code>、<code>left</code></p></li><li><p><code>border-color: red blue green pink;</code> 与上方类似(四个值、三个值……)</p></li><li><p>单独一边的颜色<code>border-xxx-color: red;</code> 其中xxx表示<code>top</code>、<code>right</code>、<code>bottom</code>、<code>left</code></p></li><li><p><code>border-style: none;</code> </p><ul><li><code>solid</code>实线 </li><li><code>dotted</code>点状 </li><li><code>dashed</code>虚线 </li><li><code>double</code>双线 </li></ul></li><li><p><code>border-style: solid dotted dashed double;</code></p></li><li><p>某一边的样式<code>border-xxx-style: solid;</code> 其中xxx表示<code>top</code>、<code>right</code>、<code>bottom</code>、<code>left</code></p></li></ul><p>大部分浏览器中，边框的宽度和颜色都是有默认值的，而边框的样式默认值都是none</p><ul><li><p><strong>简写</strong> <code>border: 10px red solid;</code> 没顺序要求，不能分别指定四边</p></li><li><p>单独指定边：<code>border-xxx: 10px red solid;</code> 其中xxx表示<code>top</code>、<code>right</code>、<code>bottom</code>、<code>left</code></p></li></ul><p>(3) 内边距：指的是从边框到内容的距离</p><ul><li><p><code>padding-xxx: 10px;</code> 分别指定四个内边距 ，其中xxx表示<code>top</code>、<code>right</code>、<code>bottom</code>、<code>left</code></p></li><li><p><code>padding: 10px 20px 30px 40px;</code>和上面类似(四个值、三个值……)</p></li></ul><p>盒子可见框的大小由内容区、内边距、边框决定</p><p>(4) 外边距是当前盒子与其他盒子之间的距离，不会影响可见框的大小，而是影响盒子的位置</p><ul><li>分别设置四面外边距<code>margin-xxx: 10px;</code> 其中xxx表示<code>top</code>、<code>right</code>、<code>bottom</code>、<code>left</code></li></ul><p>还可以设置为<code>auto</code>，一般设置在水平方向，如<code>margin: 0 auto;</code></p><p>如果只给左右设置<code>auto</code>，那么那侧值会变成最大</p><ul><li><p>垂直方向，外边距默认就是0</p></li><li><p>如果左右同时设置为auto，两侧外边距会设置为相同值，就可以使元素自动在父元素中居中 <code>margin: 0 auto;</code></p></li></ul><p><strong>※ 垂直外边距的重叠</strong>：</p><ul><li><p>在网页中<strong>垂直方向</strong>的<strong>相邻外边距</strong>会发生外边距的重叠，即兄弟元素之间相邻外边距会<strong>取最大值</strong>而不是取和。</p></li><li><p>如果父子元素的<strong>垂直外边距相邻</strong>了，则子元素的外边距会设置给父元素</p></li></ul><p>可以使用boder和padding隔开相邻，但需要将其长度从内容宽高中去掉</p><p>浏览器为了在页面中没有样式时，也可以有一个比较好的现实效果，所以为很多的元素都设置了一些默认的margin和padding，而我们往往都是不需要的，因此要去掉</p><p>清除浏览器的默认样式：</p><pre><code class="css">*{    margin: 0;     padding:0;}</code></pre><p>内联元素盒子 <code>&lt;span&gt;&lt;/span&gt;</code></p><p>内联元素不能设置宽高，可以设置水平方向的内边距，可以设置垂直方向的内边距(但不会影响页面布局)，可以设置边框(垂直的还是不会影响页面布局)，支持水平方向外边距，不支持垂直外边距</p><p>通过<code>display</code>样式可以修改元素的类型</p><p>可选值： </p><ul><li><p>Inline: 内联元素</p></li><li><p>block: 块元素</p></li><li><p>inline-block: 行内块元素(有两者的特点，支持宽高，不独占一行)例如img</p></li><li><p>none：不会显示，并且元素不会在页面中继续占有位置</p></li></ul><p>还有隐藏元素的方式</p><p><code>visibility: visible;</code>隐藏和显示 (默认显示)</p><ul><li><p>hidden 隐藏 不显示但还是<strong>占位置</strong></p><p><code>opcity: 0;</code>透明度为0</p></li></ul><p>子元素默认是存在于父元素的内容区中，理论上子元素的最大可以等于父元素内容区的大小。如果子元素的大小超过了父元素的内容区，则超过的大小会在父元素以外的位置显示，超出父元素的内容，我们称为溢出的内容。通过overflow可以设置父元素如何处理溢出内容。</p><p><code>overflow: visible;</code>默认在外面显示</p><ul><li><p>hidden 溢出内容会被修剪，不会显示</p></li><li><p>scroll 为父元素添加滚动条，可以通过滑动滚动条查看，不论是否溢出均会添加滚动条</p></li><li><p>auto 根据需求自动添加滚动条</p></li></ul><h2><span id="八-布局">八、布局</span></h2><h3><span id="1-概念">1、概念</span></h3><p>文档：就是一个网页</p><p>文档流：处在网页的最底层，它表示的是一个页面中的位置。我们创建的元素默认都处在文档流中。</p><p>元素在文档流中的特点：</p><p>块元素：独占一行(自上向下排列)。块元素默认宽度是父元素的100%。块元素的默认高度被内容/子元素撑开。</p><p>内联元素：只占自身大小，会默认自左向右排列。如果一行中不足以容纳所有的内联元素，则换到下一行，继续自左向右。在文档流中，内联元素的宽度和高度默认都被内容撑开。</p><p>当元素的宽度的值设置为auto时，此时指定内边距不会影响可见框的大小，但是会自动修改宽度，以适应内边距。</p><p>块元素在文档流中默认垂直排列，自上向下依次排开。</p><p>如果希望块元素在页面中水平排列，可以使块元素<strong>脱离文档流</strong></p><h3><span id="2-浮动">2、浮动</span></h3><p><code>float: none;</code> 默认值，不浮动</p><ul><li><p>left 元素脱离文档流，向左侧浮动</p></li><li><p>right 脱离文档流，向页面右侧浮动</p></li></ul><p>当为一个元素设置浮动以后，元素会<strong>立即脱离文档流</strong>，元素脱离文档流以后，它下边的元素会立即向上移动。</p><p>元素浮动以后，会尽量向页面的左上或者右上浮动，直到遇到父元素的边框或其他的浮动元素。</p><p>如果浮动元素上边是一个没有浮动的块元素，则浮动元素不会超过块元素。</p><p>浮动的元素不会超过他上边的兄弟元素，最多和他一边齐</p><p>浮动的元素不会盖住文字，文字会自动<strong>环绕</strong>在浮动元素的周围，所以我们可以使用浮动来设置文字环绕图片的效果。</p><p><strong>块元素脱离文档流以后，高度和宽度都被内容撑开。内联元素脱离文档流以后，会变成块元素。</strong>浮动之后不区分内联、块元素。</p><p>学完盒模型和浮动就可以完成大部分的布局了，布局分为固定布局和自适应布局</p><ul><li>固定布局：网页大小固定不变，不随设备不同改变</li><li>自适应布局(响应)：根据浏览器屏幕不同，布局发生改变</li></ul><p>利用浮动进行简单布局</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811150253.jpg" alt="img"></p><p>注：盒子套盒子，垂直摆放div，水平摆放float，看好宽高不越界，水平居中margin: 0 auto;</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811150237.jpg" alt="img"></p><p>我们做一下上面这个效果：</p><pre><code class="html">&lt;style&gt;  *{    margin: 0;    padding: 0;  }  body{    min-width: 1200px;  }  .container{    width: 1200px;    margin: 0 auto;  }  header{    width: 100%;    height: 18vh;    background-color: green;  }  footer{    width: 100%;    height: 18vh;    background-color: #ddd;  }  main{    width: 100%;    height: 62vh;    background-color: orange;    margin: 1vh 0;  }  nav{    float: left;    width: 250px;    height: 100%;    background-color: skyblue;  }  article{    float: left;    width: 680px;    height: 100%;    background-color: #fffe03;    margin: 0 10px;  }  aside{    float: left;    width: 250px;    height: 100%;    background-color: pink;  }&lt;/style&gt;&lt;div class=&quot;container&quot;&gt;  &lt;header&gt;&lt;/header&gt;  &lt;main&gt;    &lt;nav&gt;&lt;/nav&gt;    &lt;article&gt;&lt;/article&gt;    &lt;aside&gt;&lt;/aside&gt;  &lt;/main&gt;  &lt;footer&gt;&lt;/footer&gt;&lt;/div&gt;</code></pre><p>甚至还可以嵌套更多：</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811153407.jpg" alt="img"></p><h3><span id="3-高度塌陷问题">3、高度塌陷问题</span></h3><p>(1) 问题说明</p><p>在文档流中，父元素的高度默认被子元素撑开，即父元素不设置高度，子元素多高，父元素就有多高。但是当为<strong>子元素设置浮动</strong>以后，子元素会完全脱离文档流，此时将会导致<strong>子元素无法撑起父元素的高度</strong>，导致<strong>父元素高度塌陷</strong>，父元素下的所有元素都会向上移动，这样会导致<strong>网页布局混乱</strong>。</p><p>可以将父元素写死，但是父元素的高度将不能自动适应子元素的高度，不建议使用</p><p>(2) BFC</p><p>根据W3C标准，页面中元素都有一个隐含的属性交Block Formatting Contex(BFC，块级格式化上下文)，该属性可以设置打开，默认关闭</p><p>当开启元素的BFC以后，元素将会具有如下的特性：</p><ul><li><p>父元素的垂直外边距不会和子元素重叠</p></li><li><p>该元素不会被浮动元素所覆盖</p></li><li><p>该元素可以包含浮动的子元素</p></li></ul><p>间接<strong>开启BFC</strong></p><ul><li><p>设置元素<strong>浮动</strong>   父元素、子元素都浮动 使用这种方式，可以撑开父元素，但是父元素的宽度会丢失，也会导致下边的元素上移，不推荐</p></li><li><p>设置元素<strong>绝对定位</strong></p></li><li><p>设置元素为<strong>inline-block</strong> 宽度也会丢失，不推荐</p></li><li><p>将元素的<strong>overflow</strong>设置一个非visible的值</p></li></ul><p><code>overflow: auto;</code></p><p>将<strong>overflow设置为<code>hidden</code></strong>是副作用最小的开启BFC的方式。IE6不支持，IE6具有另一个隐含属性hasLayout，与BFC类似，开启hasLayout副作用最小的<strong>zoom设置为1</strong>即可 放大1倍</p><p>zoom只在IE中支持，由于css不对的那条将会直接忽略，因此可以两种都写上</p><p>利用float写导航条</p><pre><code class="html">&lt;style&gt;  *{    margin: 0;    padding: 0;  }  .nav{    width: 1000px;    margin: 50px auto;    list-style: none;    background-color: #bfa;    overflow: auto;    zoom: 1;  }  .nav li{    float: left;    width: 249px;    height: 30px;    line-height: 30px;    text-align: center;    background-color: #6495ed;  }  .nav li:not(:last-of-type){    border-right: 1px solid #ddd;  }  .nav li a{    display: block;    width: 100%;    height: 100%;    text-decoration: none;    font-weight: bold;  }  .nav li a:hover{    background-color: #cc0000;  }&lt;/style&gt;&lt;ul class=&quot;nav&quot;&gt;  &lt;li&gt;&lt;a href=&quot;#&quot;&gt;首页&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;&lt;a href=&quot;#&quot;&gt;新闻&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;&lt;a href=&quot;#&quot;&gt;联系&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;&lt;a href=&quot;#&quot;&gt;关于&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</code></pre><h3><span id="4-清除浮动">4、清除浮动</span></h3><p>有时希望清除其他元素浮动对当前元素产生的影响，可以使用clear</p><p><code>clear: none;</code>默认值，不清除浮动影响</p><ul><li><p>left 清除左侧浮动元素对当前元素的影响</p></li><li><p>right 清除右侧……</p></li><li><p>both 清除两侧……实际是清除对它影响最大的那个</p></li></ul><p>可以在高度塌陷的父元素的最后，添加一个撑开父元素的空白div，对其进行清除浮动，但是添加了一个无用的结构，因此可以借用伪类：</p><pre><code class="css">.clearfix:after{  content:””;    display: block;    clear:both;}.clearfix{zoom:1;} /*IE6*/</code></pre><p>父元素多加一个类名clearfix</p><h3><span id="5-高度塌陷最终解决方案">5、高度塌陷最终解决方案</span></h3><p>经过修改后的clearfix是一个多功能的，既可以解决高度塌陷，又可以确保父元素和子元素的垂直外边距不会重叠</p><p>为父元素添加类名<code>clearfix</code></p><pre><code class="html">&lt;div class=&quot;parent clearfix&quot;&gt;  &lt;div class=&quot;child&quot;&gt;&lt;/div&gt;&lt;/div&gt;</code></pre><p>添加样式</p><pre><code class="css">.clearfix:before,.clearfix:after{  content: &#39;&#39;;  display: table;  clear: both;}.clearfix{  zoom: 1;}</code></pre><h3><span id="6-定位">6、定位</span></h3><p>定位：将指定的元素摆放到页面的任意位置</p><p><code>position: static;</code>默认值，元素没有开启定位</p><ul><li><p>relative 开启元素的相对定位</p></li><li><p>absolute 绝对定位</p></li><li><p>fixed 固定定位</p></li></ul><p>当开启了元素的定位，可以通过left、right、top、bottom四个属性来设置元素的偏移量，通常偏移量只使用两个就可以对一个元素进行定位，水平一个+垂直一个</p><p>相对定位</p><ul><li><p>开启了相对定位，而不设置偏移量时，元素不会发生任何变化</p></li><li><p>相对定位是相对于元素在文档流中<strong>原来的位置</strong>进行定位</p></li><li><p>相对定位的元素不会脱离文档流</p></li><li><p>相对定位会使元素<strong>提升一个层级</strong>(覆盖底下的)</p></li><li><p>相对定位不会改变元素的性质，块还是块，内联还是内联</p></li></ul><p>绝对定位</p><ul><li><p>开启绝对定位会使元素<strong>脱离文档流</strong></p></li><li><p>开启绝对定位如果不设置偏移量，元素位置不会发生变化</p></li><li><p>绝对定位是相对于离他最近的开启了定位的祖先元素进行定位的(一般情况，开启了子元素的绝对定位，会同时开启父元素的相对定位)；如果所有的祖先元素没有开启定位，则相对于浏览器窗口进行定位  </p></li><li><p>绝对定位会使元素提升一个层级</p></li><li><p>绝对定位会改变元素的性质，内联元素变成块元素，块元素的宽度和高度默认都被内容撑开</p></li></ul><p>固定定位</p><p>固定定位也是一种绝对定位，它的大部分特点都和绝对定位一样，不同的是：</p><ul><li><p>固定定位永远都会相对于浏览器窗口进行定位</p></li><li><p>固定定位会固定在浏览器窗口的某个位置，不会随滚动条滚动</p></li></ul><p>IE6不支持固定定位</p><p>元素的层级</p><p>如果定位元素的层级是一样的，则下边的元素会盖住上边的</p><p>通过<code>z-index</code>属性可以用来设置元素的层级</p><p>可以为<code>z-index</code>指定一个正整数作为值，该值将会作为当前元素的层级，层级越高，越优先显示</p><ul><li><p>对于没有开启定位的元素不能使用z-index</p></li><li><p>父元素的层级再高，也不会盖住子元素</p></li></ul><h2><span id="九-背景">九、背景</span></h2><h3><span id="1-设置元素的透明背景">1、设置元素的透明背景</span></h3><p><code>opacity: 1;</code>0~1之间的值 </p><ul><li>0完全透明 </li><li>1完全不透明 </li><li>0.5半透明</li></ul><p>IE8及以下的浏览器不支持，可以使用<code>filter: alpha(opacity=50);</code> 透明度0~100 </p><h3><span id="2-背景颜色">2、背景颜色</span></h3><p><code>background-color: red;</code></p><h3><span id="3-背景图">3、背景图</span></h3><p>(1) 添加背景图片</p><p><code>background-image: url(../img/1.jpg);</code></p><ul><li><p>如果背景图片大于元素，默认会显示图片的左上角</p></li><li><p>如果背景图片与元素一样大，则背景图片将会完全显示</p></li><li><p>如果背景图片小于元素大小，则会默认将背景图片平铺以充满元素</p></li><li><p>可以同时为一个元素指定背景颜色和背景图片，这样背景颜色将会作为背景图片的底色，一般情况下设置背景图片时都会同时指定一个背景颜色</p></li></ul><p>(2) 图片平铺</p><p><code>background-repeat: repert;</code> 默认值，背景图片会双方向重复(平铺)</p><ul><li><p>no-repeat  背景图片不会重复</p></li><li><p>repeat-x  背景图片沿水平方向重复</p></li><li><p>repeat-y  背景图片沿垂直方向重复</p></li><li><p>round 会将图片缩放之后进行平铺</p></li><li><p>space 产生相同的间距</p></li></ul><p>(3) 图片位置</p><p><code>background-position: 0% 0%;</code> 默认值，图片在左上角显示，水平 垂直</p><ul><li><p>center center  只给一个值，第二个值默认为center</p></li><li><p>1% 1%  左上角是0% 0%，右下角是100% 100%，只给一个值，第二个值默认为50%</p></li><li><p>10px 0px 只设置一个值，另一个值将是50%</p></li></ul><p>(4) 图片滚动</p><p><code>background-attachment: scroll;</code> 默认值，背景图片随着页面一起滚动</p><ul><li><p>fixed; 背景图片固定在某个位置，不随页面滚动。背景图片定位永远相对于浏览器窗口。</p><pre><code class="css">/* 容器滚动时，背景图片动作 */background-attachment: fixed; 背景图片位置固定不变background-attachment: scroll; 跟随滚动/* 当前容器滚动时 */background-attachment: scroll; 不会随内容一起滚动background-attachment: local; 跟随滚动</code></pre></li></ul><p>在IE6中对图片格式png24支持度不高，如皋使用的图片格式是png24，则会导致透明效果无法正常显示。</p><ul><li><p>可以使用png8代替png24，图片清晰度会下降。</p></li><li><p>使用JavaScript解决，在body标签的最后引入js文件</p></li></ul><pre><code class="html">&lt;!--[if IE 6]&gt;&lt;script src=&quot;https://cdn.bootcdn.net/ajax/libs/dd_belatedpng/0.0.8/dd_belatedpng.min.js&quot;&gt;&lt;/script&gt;&lt;script&gt;    DD_belatedPNG.fixed(“div,img”); // 选择器&lt;/script&gt;&lt;![endif]--&gt;</code></pre><h3><span id="4-精灵雪碧图css-sprite">4、精灵/雪碧图(CSS-Sprite)</span></h3><p>先看需求：给超链接和按钮设置背景图片、宽高、<code>:link</code>、<code>:hover</code>、<code>:active</code></p><p>设置完成之后会发现第一次切换图片的时候，会发现图片有一个非常快的闪烁，这个闪烁会造成一次不佳的用户体验。</p><p>产生问题的原因：</p><p>背景图片是以外部资源的形式加载进网页的，浏览器每加载一个外部资源就需要单独地发送一次请求，但是我们外部资源并不是同时加载，浏览器会在资源被使用时才去加载资源。在这个案例中，一上来浏览器只会加载<code>link.png</code>，由于hover和active的状态没有马上出发，所以hover.png、active.png并不是立即加载的；只有当hover、active被触发时浏览器才去加载hover.png、active.png；由于加载图片需要一定的时间，所以在加载和显示过程会有一段时间，背景图片无法显示，导致出现闪烁的情况。</p><p>解决方案：图片整合技术(CSS-Sprite)，将所有的小的背景素材放到同一张图片中，利用<code>background-position</code>进行位置调整得到想要的素材。</p><p>优点：</p><ul><li><p>将多个图片整合为一张图片，浏览器只需要发送一次请求，可以同时加载多个图片，提高访问效率，优化了用户体验</p></li><li><p>减小了图片的总大小</p></li></ul><h3><span id="5-背景图片简写属性">5、背景图片简写属性</span></h3><p>例如：</p><pre><code class="css">background-color: #bfa; /* 设置一个背景颜色 */background-image: url(img/pic.png); /* 设置一个背景图片 */background-repeat: no-repeat; /* 设置背景图片是否平铺 */background-position: center center; /* 设置背景图片的位置 */background-attachment: fixed; /* 设置背景图片不随滚动条滚动 */</code></pre><p>简写 </p><pre><code class="css">background: #bfa url(../img/1.png) center center no-repeat fixed;</code></pre><p>没有顺序要求，没有数量要求，不设置采用默认值</p><h2><span id="十-css-hack">十、CSS Hack</span></h2><p>有一些情况，有一些特殊的代码我们只需要在某些特定的浏览器中执行，而在其他的浏览器中不需要执行，这时就可以使用CSS Hack来解决该问题</p><p>CSS Hack实际上指的是一个特殊的代码，这段代码只在某些浏览器中可以识别，而其他浏览器都不能识别，通过这种方式，来为一些浏览器设置特殊的代码</p><h3><span id="1-条件hack">1、条件Hack</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811175041.gif" alt="img"></p><p>条件Hack只对IE浏览器有效，其他的浏览器都会将它识别为注释，IE10及以上的浏览器已经不支持这种方式</p><pre><code class="html">&lt;!--[if IE]&gt;  &lt;p&gt;这是IE6&lt;/p&gt;&lt;![endif]--&gt;&lt;!--[if IE 6]&gt;  &lt;p&gt;这是IE6&lt;/p&gt;&lt;![endif]--&gt;&lt;!--[if IE 8]&gt;  &lt;p&gt;这是IE8&lt;/p&gt;&lt;![endif]--&gt;&lt;!--[if lte IE 9]&gt;  &lt;p&gt;您使用的浏览器是IE及以下的&lt;/p&gt;&lt;![endif]--&gt;</code></pre><p><code>lt</code> 小于 <code>gt</code>大于 <code>lte</code>小于等于</p><p>可以利用条件Hack，为IE浏览器单独引入样式</p><pre><code class="html">&lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&gt;&lt;!--[if IE 8]&gt;  &lt;link rel=&quot;stylesheet&quot; href=&quot;style-ie8.css&quot;&gt;&lt;![endif]--&gt;</code></pre><h3><span id="2-属性级hack">2、属性级Hack</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811175712.gif" alt="img"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811175725.gif" alt="img"></p><h3><span id="3-选择符级hack">3、选择符级Hack</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811175741.gif" alt="img"></p><p> CSS Hack不到万不得已的情况尽量不要使用</p><h2><span id="十一-css3中其他属性">十一、CSS3中其他属性</span></h2><p>上面提到过CSS3中的选择器、盒模型、颜色，这里讲一下其他的</p><h3><span id="1-文本阴影text-shadow">1、文本阴影(text-shadow)</span></h3><p><code>text-shadow: none | &lt;length&gt; none | [&lt;shadow&gt;,]*&lt;shadow&gt; 或 none | &lt;color&gt; [,&lt;color&gt;]*</code></p><p>即：</p><p><code>text-shadow: [颜色(color) x轴(X Offset) y轴(Y Offset) 模糊半径(Blur)],[颜色 x轴 y轴 模糊半径],……</code></p><p>或</p><p><code>text-shadow: [x轴 y轴 模糊半径 颜色],[x轴 y轴 模糊半径 颜色],……</code></p><p>例如：</p><pre><code class="css">div{  text-shadow: -2px -2px 5px red;  /* text-shadow: 0px 0px 30px #fff;  text-shadow: 0px 0px 30px #fff,               0px 0px 50px red,               0px 0px 70px #fff;  text-shadow: 0px 1px 0px #fff;  text-shadow: -1px -1px 0px #fff,               -2px -2px 0px #eee,               -3px -3px 0px #ddd,               -4px -4px 0px #ccc;  text-shadow:  0 0 8px hsla(30,100%,40%,1); */}</code></pre><h3><span id="2-边框圆角">2、边框圆角</span></h3><p><code>border-radius: 值;</code></p><p>单独设置，<code>border-xxx-radius</code>设置不同方向</p><p>整合写为<code>border-radius: 左上 右上 右下 左上;</code></p><p>两个值：左上/右下 右上/左下; </p><p>三个值：左上 右上/左下 右下;</p><p>设置不同方向的半径值 水平/垂直</p><p><code>border-radius: 100px/50px;</code></p><p><code>border-radius: 100px 80px 50px 30px/20px 30px 40px 50px;</code></p><h3><span id="3-渐变">3、渐变</span></h3><p>渐变是CSS3中比较丰富多彩的一个特性，通过渐变我们可以实现许多绚丽的效果，有效减少图片的使用数量，并且具有很强的适应性和可扩展性。</p><p>(1) 线性渐变</p><p><code>linear-gradient([&lt;point&gt; || &lt;angle&gt;,]?&lt;stop&gt;,&lt;stop&gt;[,&lt;stop&gt;]]*)</code></p><ul><li>第一个参数表示线性渐变的方向<ul><li>to left：设置渐变从右到左，相当于270deg</li><li>to right：设置渐变从左到右，相当于90deg</li><li>to top：设置渐变从下到上，相当于0deg</li><li>to bottom：设置渐变从下到上，相当于180deg，这个是默认值，等同于留空不写</li><li>还可以指定度数，如45deg</li></ul></li><li>第二个参数是起点颜色，可以指定颜色的位置</li><li>第三个参数是终点颜色，可以在后面添加更多的参数，表示多种颜色的渐变</li></ul><p>例如：</p><pre><code class="css">background: linear-gradient(40deg, red 0%, red 10%, orange 12%, orange 15%, yellow 25%, green, blue, pink 60%);</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811185826.png" alt="image-20200811185824969"></p><p>(2) 径向渐变</p><p><code>radial-gradient([[&lt;shape&gt;||&lt;size&gt;] [at &lt;position&gt;]? , | at &lt;postition&gt;,]?&lt;color-stop&gt;[,&lt;color-stop&gt;]+)</code></p><ul><li>position：确定圆心的位置。如果提供两个参数，第一个表示横坐标，第二个表示纵坐标；如果只提供一个，第二值默认为50%，即center</li><li>shape：渐变的形状，ellipse表示椭圆形，circle表示圆形。默认为ellipse，如果元素的形状为正方形，啧ellipse和circle显示一样</li><li>size：渐变的大小，即渐变到哪里停止，它有四个值。closest-side：最近边，farthest-side：最远边，closest-corner：最近角，farthest-corner：最远角。默认是最远角farthest-corner</li><li>color：指定颜色</li></ul><pre><code class="css">background: radial-gradient(red, blue)background: radial-gradient(ellipse, red, blue) 或者circle(在非正方形中有区别，circle不会压缩、ellipse会压缩) 形状background: radial-gradient(at 50px 50px, red, blue) 位置(坐标)background: radial-gradient(circle closest-corner at 50px 50px, red, blue) 大小background: radial-gradient(red, red 50%, blue 50%, blue)</code></pre><p>(3) 重复渐变</p><pre><code class="css">background: repeating-linear-gradient(45deg, #fff 0%, #fff 10%, #000 10%, #000 20%)background: repeating-radial-gradient(circle at center center, #fff 0%, #fff 10%, #000 10%, #000 20%)</code></pre><h3><span id="4-背景样式">4、 背景样式</span></h3><p>设置背景图片的大小</p><p><code>background-size</code>: <code>auto</code>(原始图片大小) || number(数值) || percentage(百分比) || <code>cover</code>(放大铺满) || <code>contain</code>(缩小铺满)</p><p><code>background-size: 300px 500px</code> 同时设置宽高，建议保持与图片同比例，否则会造成图片失真变形</p><p><code>background-size: 300px;</code> 只设置宽度，高度auto也是一种选择(图片会保持比例自动缩放)</p><p>设置百分比是参照父容器可放置内容区域的百分比<code>background-size: 50% 50%</code></p><p>设置<code>contain</code>：按比例调整图片大小，使用图片宽高自适应整个元素的背景区域，使图片全部包含在容器中</p><ul><li>图片大于容器：有可能造成容器的空白区域，将图片缩小</li><li>图片小于容器：有可能造成容器的空白区域，将图片放大</li></ul><p>设置<code>cover</code>：与contain刚好相反，背景图片会按比例缩放至适应整个背景区域，如果背景区域不足以包含所有背景图片，图片内容会溢出</p><ul><li>图片大于容器：等比例缩小，会填满整个背景区域，有可能造成图片的某些区域不可见</li><li>图片小于容器：等比例放大，填满整个背景区域，图片有可能造成某个方向上内容的溢出</li></ul><p>一般轮播图就使用cover，方便在不同分辨率下显示完全</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811200801.png" alt></p><p>精灵图的部分使其居中并且放大，提升相应区域</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811200923.png" alt></p><h3><span id="5-边框背景图片">5、边框背景图片</span></h3><p>如下图所示，将图片等分为9个区域，将会和div平分为九个区域之后的格子一一对应</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811202120.png" alt="image-20200811202119076"></p><pre><code class="css">div {  margin: 20px auto;  width: 300px;  height: 300px;  border: 54px solid red;  /* 添加边框图片 */  /* border-image-source：可以指定边框图片的路径，默认知识填充到容器的四个角 */  border-image-source: url(&#39;img/borderbg.jpg&#39;);  /* 让它成为九宫格：border-imgage-slice：设置四个方向上的裁切距离, fill：做内容的内部填充 */  border-image-slice: 54 fill;}</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811202858.png" alt="image-20200811202856670"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811202922.png" alt="image-20200811202921067"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811202930.png" alt="image-20200811202929582"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811202943.png" alt="image-20200811202942535"></p><p>案例：按钮变形(上、右、下、左不变形，中间拉伸)</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811203003.png" alt="image-20200811203001861"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811203014.png" alt="image-20200811203012987"></p><p>如果是纹理、渐变的背景，需要设置round或repeat(纯色可以默认stretch)</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811203027.png" alt="image-20200811203026219"></p><h3><span id="6-过渡">6、 过渡</span></h3><p>通过过渡transition,我们可以在不使用Flash 动画或JavaScript 的情况下，当元素从一种样式变换为另一种样式时为元素添加效果.要实现这一点，必须规定两项内容: 1.规定希望把效果添加到哪个CSS属性上，2.规定效果的时长</p><p>(1) 语法:</p><p><code>transition: property duration timing-function delay;</code></p><p>(2)参数说明:</p><p>transition属性是-一个简写属性，用于设置四个过渡属性: </p><table><thead><tr><th>值</th><th>描述</th></tr></thead><tbody><tr><td>transition-property</td><td>规定设置过渡效果的CSS属性的名称</td></tr><tr><td>transition-duration</td><td>规定完成过渡效果费多少秒或毫秒</td></tr><tr><td>transition-timing-function</td><td>规定速度效果的速度曲线</td></tr><tr><td>transition-delay</td><td>定义过渡效果何时开始</td></tr></tbody></table><p>(3) 补充说明tansition-timing-function:属性规定过渡效果的速度曲线</p><table><thead><tr><th align="left">值</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left">linear</td><td align="left">规定以相同速度开始至结束的过渡效果（等于 cubic-bezier(0,0,1,1)）。</td></tr><tr><td align="left">ease</td><td align="left">规定慢速开始，然后变快，然后慢速结束的过渡效果（cubic-bezier(0.25,0.1,0.25,1)）。</td></tr><tr><td align="left">ease-in</td><td align="left">规定以慢速开始的过渡效果（等于 cubic-bezier(0.42,0,1,1)）。</td></tr><tr><td align="left">ease-out</td><td align="left">规定以慢速结束的过渡效果（等于 cubic-bezier(0,0,0.58,1)）。</td></tr><tr><td align="left">ease-in-out</td><td align="left">规定以慢速开始和结束的过渡效果（等于 cubic-bezier(0.42,0,0.58,1)）。</td></tr><tr><td align="left">cubic-bezier(<em>n</em>,<em>n</em>,<em>n</em>,<em>n</em>)</td><td align="left">在 cubic-bezier 函数中定义自己的值。可能的值是 0 至 1 之间的数值。</td></tr></tbody></table><p>cubic-bezier 函数可以到<a href="https://cubic-bezier.com/#.17,.67,.83,.67" target="_blank" rel="noopener">这个网站</a>上查看</p><p><code>steps(&lt;integer&gt;[,start| end]?) &lt;integer&gt;</code>：用于指定间隔个数（该值只能是正整数） 第二个参数可选，默认是end,表示开始值保持一次，若参数为start，表示开始值不保持</p><p>step-start: 直接位于结束处。相当于steps(1,start) </p><p>step-end: 位于开始处经过时间间隔后结束。相当于steps(1,end)</p><p>(4) eg: 点击之后移动到某位置，显示移动过程</p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811205702.png" alt="image-20200811205701467"></p><p>简写：<code>transition: 属性名称 过渡时间 时间函数 延迟</code></p><p>即</p><pre><code class="css">transition: left 2s linear 0s;</code></pre><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811205845.png" alt="image-20200811205843859"></p><p> (5) 使用建议</p><p>因为transition最早是由webkit内核河克确提出来的，moila和opera都量最近版本才支持这个属性，而IE浏览器是不支持，另外由于各大现代浏览器firefox、Safari、Chrome、Opera都还不支持W3C的标准，所以在应用transition时有必要加上各自的前缀</p><pre><code class="css">-moz-transition: all 5s ease 1s;-webkit-transition: all 1s ease 1s;-o-transition: all 1s ease 1s;transition: all 1s ease 1s;</code></pre><h3><span id="7-transform">7、 Transform</span></h3><p>通过CSS3 transform 转换，我们能够对元素进行移动、缩放、旋转、斜切等操作。</p><p>(1) 2D移动translate()</p><p> 使用translate()函数,你可以把元素从原来的位置移动，移动参照元素左上角原点</p><ul><li><p>语法: translate(tx) I translate(tx,tE)</p></li><li><p>tx是一个代表X轴(横坐标)移动的向量长度，当其值为正值时，元素向X轴右方向移动，反之其值为负值时，元素向X轴左方向移动。</p></li><li><p>ty是一个代表 Y轴(纵向标)移动的向量长度，当其值为正值时，元素向Y轴下方向移动，反之其值为负值时，元素向Y轴上方向移动。如果ty没有显式设置时,相当于ty=0。</p></li><li><p>也可以使用translateX(x)或者translateY(ty)</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811211227.png" alt="image-20200811211226225"></p></li></ul><p>(2) 2D缩放: scale() </p><p>缩放scale()函数让元素根据中心原点对对象进行缩放。<br>默认的值1，因此0.01到0.99之间的任何值都会使一个元素缩小；而任何大<br>于或等于1.01 的值都会让元素显得更大。缩放是参照元素中心点。</p><ul><li><p>语法: scale(sx|ty) | scale(sx,sy)</p></li><li><p>sx: 用来指定横向坐标(X轴)方向的缩放向量，如果值为0.01~0.99之间，会让对象在X轴方向缩小，如果值大于或等于1.01，对象在Y轴方向放大。</p></li><li><p>sy: 用来指定纵向坐标(Y轴)方向的缩放量，如果值为0.01~0.99之间，会让对象在Y轴方向缩小，如果值大于或等于1.01，对象在Y轴方向放大</p></li><li><p>也可以使用scaleX(sx)或者scaleY(sy)</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811211659.png" alt="image-20200811211647763"></p></li></ul><p>(3)2D旋转: rotate() </p><p>旋转rotate()函数通过指定的角度参数对元素根据对象原点指定一个2D旋转。它主要在二维空间内进行操作，接受一个角度值，用来指定旋转的幅度。如果这个值为正值，元素相对原点中心顺时针旋转；如果这个值为负值，元素相对原点中心逆时针旋转</p><ul><li><p>语法: rotate(a)</p></li><li><p>a: 代表的是一个旋转的角度值。 其取值可以是正的,也可以是负的。<br>如果取值为正值时，元素默认之下相对元素中心点顺时针旋转；如果<br>取值为负值时，元素默认之下相对元素中心点逆时针旋转</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811211907.png" alt="image-20200811211906427"></p></li></ul><p>(4) 2D斜切: skew()</p><p>能够让元素倾斜显示。它可以将一个对象以其中心位置围绕看X轴和Y轴按照一定的角度倾斜。这与rotate()函数的旋转不同，rotate()函数只是旋转，而不会改变元素的形状。skew()函数不会旋转，而只会改变元素的形状</p><ul><li><p>语法:skew(ax) | skew(ax,ay)</p></li><li><p>ax：用来指定元素水平方向(X轴方向)倾鈄的角度。</p></li><li><p>ay：用来指定元素垂直方向(Y轴方向)倾斜的角度。如果未显式的设置这个值,其默认为</p></li><li><p>也可以使用 skewX(sx)或者skewY(sy)</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811212119.png" alt="image-20200811212118202"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811212128.png" alt="image-20200811212126747"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811212138.png" alt="image-20200811212137749"></p></li></ul><p>同时设置多个transform属性值</p><pre><code class="css">transform: translateX(700px) rotate(-90deg);</code></pre><p>注意书写顺序</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811212258.png" alt="image-20200811212257492"></p><p>备注：可利用定位和translate实现任意元素居中</p><p>定位中left、top的百分比是参照父容器的宽高</p><p>translate的百分比是参照元素本身的宽高</p><h3><span id="8-3d转换">8、3D转换</span></h3><p> 三维变换使用基于二维变换相同的属性，可以让我们基于三个坐标方向对元素进行变换。</p><p>(1) 3D移动<br>方法：translate3d(x, y, z)使元素在这三个维度中移动，也可以分开写.如:<br>translateX(length)，translateY(length)，translateZ(length)</p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811212650.png" alt="image-20200811212648882"></p><p>(2) 3D缩放<br>scale3d(number,number,numben)使元素在这三个纬度中缩故，也可分开写，<br>如: scaleX()，scaleY()，scalZ()</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811212837.png" alt="image-20200811212836030"></p><p>(3) 3D旋转</p><ul><li><p>rotate3d(x, y, z, angle) ，指定需要进行旋转的坐标轴</p></li><li><p>rotatX(angle) 是元素依照x轴旋转;</p></li><li><p>rotateY(angle) 是元素依照y轴旋转;</p></li><li><p>rotateZ(angle) 是元素依照z轴旋转</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811213004.png" alt="image-20200811213003421"></p></li></ul><h3><span id="9-透视景深">9、透视/景深</span></h3><p>左手法则：大拇指指向当前坐标轴的下方向，手指环绕的方向就是正方向</p><ul><li>perspective(length) 为一个元素设置三维透视的距高。仅作用于元素的后代，而<br>不是其元素本身。当perspective:none/0时，相当于没有设perspetive(length)， 比如你要建立一个小立方体，长宽高都是200px，如果你的perspective&lt; 200px，那就相当于站在盒子里面看的结果，如果perspective非常大那就是站在非常远的地方看(立方体已经成了小正方形了) ， 意味着perspective属性指定了观察者与z=0平面的距离，使具有三维位置变换的元素产生透视效果</li><li>perspective-origin属性规定了镜头在平面上的位置，默认是放在元素的中心</li><li>tasom-style：使被蠡换的子元素保留其3D转换需要设置在父元素中</li></ul><table><thead><tr><th>值</th><th>描述</th></tr></thead><tbody><tr><td>flat</td><td>子元素将不保留其3D位置中面方式。</td></tr><tr><td>preseve-3d</td><td>子元素将保留其3D位置-立体方式。</td></tr></tbody></table><h3><span id="10-动画">10、 动画</span></h3><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811213732.png" alt="image-20200811213731091"></p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811213740.png" alt="image-20200811213739261"></p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811213749.png" alt="image-20200811213748177"></p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811213757.png" alt="image-20200811213755937"></p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811213808.png" alt="image-20200811213806970"></p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811213815.png" alt="image-20200811213814634"></p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811213823.png" alt></p><h3><span id="11-web字体-字体图标">11、 Web字体、字体图标</span></h3><p> 开发人员可以为自己的网页指定特殊的字体，无序考虑用户电脑上是否安装了此特殊字体</p><p> (1) 字体格式</p><p>不同浏览器所支持的字体格式不同</p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811204306.png" alt="image-20200811204305599"></p><p>获取这些种类的字体文件：</p><p><a href="https://www.iconfont.cn/webfont?spm=a313x.7781069.1998910419.d81ec59f2#!/webfont/index" target="_blank" rel="noopener">https://www.iconfont.cn/webfont?spm=a313x.7781069.1998910419.d81ec59f2#!/webfont/index</a></p><p>选中一种字体-本地下载-解压-根据demo在自己的项目中使用字体</p><pre><code class="html">&lt;script&gt;    @font-face{       font-family: ‘字体名’,       src: url(‘路径/字体文件’);       src: url(‘路径/字体文件’) format(‘格式’),       url……    }    .myFont{       font-family: 上面的字体名’    }&lt;/script&gt;&lt;span class=”myFont&gt;之前生成的文字&lt;/span&gt;</code></pre><p>注意：</p><ul><li><p>得自定义想生成对应字体文件的内容</p></li><li><p>使用网络资源生成web字体</p></li><li><p>使用：</p><ul><li>定义自定义字体</li><li>定义样式使用自定义字体</li><li>指定样式，调用样式</li></ul></li></ul><p> (2) 字体图标</p><p>常见的就是把网页常用的一些小图标，借助工具生成一个字体包，然后就可以像使用文字一样使用图标了</p><p> 优点</p><ul><li>将所有图标打包成字体库，减少请求</li><li>具有矢量性，可保证清晰度</li><li>使用灵活，便于维护</li></ul><p>fonticon或者fontawsome</p><h2><span id="十二-多列布局和弹性伸缩布局">十二、多列布局和弹性(伸缩)布局</span></h2><h3><span id="1-多列布局">1、 多列布局</span></h3><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811203754.png" alt="image-20200811203753419"></p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811203747.png" alt="image-20200811203746127"></p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811203737.png" alt="image-20200811203736789"></p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811203729.png" alt="image-20200811203728192"></p><h3><span id="2-伸缩布局">2、 伸缩布局</span></h3><p>传统布局方式的局限：</p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811203624.png" alt="image-20200811203622593"></p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811203602.png" alt="image-20200811203601481"></p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811203554.png" alt></p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811203546.png" alt="image-20200811203545505"></p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811203528.png" alt="image-20200811203527386"></p><p>d中的所有属性都是在子元素中设置的</p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811203446.png" alt="image-20200811203444454"></p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811203455.png" alt="image-20200811203454106"></p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811203503.png" alt="image-20200811203501719"></p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811203511.png" alt="image-20200811203509539"></p><p>案例：弹性导航栏</p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811203410.png" alt="image-20200811203409255"></p><p>案例：宽高自动适应</p><p> <img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811203322.png" alt="image-20200811203320408"></p><pre><code class="html">&lt;div class=&quot;layout&quot;&gt;  &lt;header&gt;header&lt;/header&gt;  &lt;main&gt;    &lt;aside&gt;left&lt;/aside&gt;    &lt;article&gt;right&lt;/article&gt;  &lt;/main&gt;  &lt;footer&gt;footer&lt;/footer&gt;&lt;/div&gt;&lt;style&gt;*{  margin: 0;  padding: 0;}.layout{  width: 500px;  height: 600px;  background-color: #ccc;  margin: 10px auto;  /* 设置父容器为伸缩盒子 */  display: flex;  /* 修改主轴方向 */  flex-direction: column;}header{  width: 100%;  height: 60px;  background-color: red;}main{  width: 100%;  background-color: green;  /* 让当前伸缩项占据父容器的剩余空间 */  flex: 1;  /* 让main称为弹性盒子 */  display: flex;}main &gt; aside{  height: 100%;  flex: 1;  background-color: pink;}main &gt; article{  height: 100%;  flex: 3;  background-color: purple;}footer{  width: 100%;  height: 80px;  background-color: blue;}&lt;/style&gt;</code></pre><p>jQuery全屏滚动插件</p><p><a href="http://www.jq22.com/jquery-info1124" target="_blank" rel="noopener">http://www.jq22.com/jquery-info1124</a></p><h2><span id="十三-其他">十三、其他</span></h2><h3><span id="1-重置样式表和默认样式表">1、重置样式表和默认样式表</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811214420.gif" alt="img"></p><h3><span id="2-对元素id和class还有文件的命名规范">2、对元素id和class还有文件的命名规范</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811214431.gif" alt="img"></p><h3><span id="3-cursor-point-设置指针">3、<code>cursor: point;</code> 设置指针</span></h3><h3><span id="4-解决ie6双边距">4、解决IE6双边距</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811214441.gif" alt="img"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200811214449.gif" alt="img"></p><h3><span id="5-利用jscsszip对css-js压缩">5、利用jsCssZip对CSS、js压缩</span></h3><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="笔记" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="前端" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="前端" scheme="https://wallleap.cn/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="web" scheme="https://wallleap.cn/tags/web/"/>
    
      <category term="CSS" scheme="https://wallleap.cn/tags/CSS/"/>
    
  </entry>
  
  <entry>
    <title>valine评论系统使用</title>
    <link href="https://wallleap.cn/2020/03/30/hexoValine/"/>
    <id>https://wallleap.cn/2020/03/30/hexoValine/</id>
    <published>2020-03-30T04:16:01.000Z</published>
    <updated>2025-01-24T09:22:15.352Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>最开始用这个评论是在使用Sakura主题的时候，但是那个时候没认真看教程</p><p>觉得它没有邮件提醒功能、而且有的时候还会报错，就没使用了</p><p>改用的来必力和gitment，gitment使用起来挺方便的，能够邮件提醒，而且报的错误也能解决</p><p>emmmm……一直到现在，想接着用valine，然后搜了很多篇教程，发现valine还是非常好用的</p><p>手动配图：真香.gif</p><p>然后接下来就讲下怎样<strong>添加valine</strong>，对其进行<strong>美化</strong>和添加评论功能吧</p><a id="more"></a><p>emmmm，版本<code>v1.4.0</code>之后更新了很多内容，不得不说，这段时间作者真的是高产啊</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustration/valine.png" alt></p><p>但是不想推翻重写，因此直接在这篇博文上修改</p><p><font color="#bac">更新内容</font></p><ul><li><input checked disabled type="checkbox"> 去除QQ头像配置的内容，更新为新的方法</li><li><input checked disabled type="checkbox"> 按照官网更新配置项解释</li><li><input checked disabled type="checkbox"> 添加自定义表情用法</li><li><input checked disabled type="checkbox"> 去除评论框跟随，官方已支持</li><li><input checked disabled type="checkbox"> </li><li><input disabled type="checkbox"> 美化的好多不能用了</li><li><input disabled type="checkbox"> </li><li><input checked disabled type="checkbox"> </li></ul><h2><span id="注册登录">注册登录</span></h2><p>前往<a href="https://leancloud.cn/dashboard/login.html#/signup" target="_blank" rel="noopener">LeanCloud注册</a>账号，如果已经有了账号，可以直接<a href="https://leancloud.cn/dashboard/login.html#/signin" target="_blank" rel="noopener">登录</a></p><p>接着进入<a href="https://leancloud.cn/dashboard/applist.html#/apps" target="_blank" rel="noopener">控制台</a>,左下角有两个按钮<code>快速入门</code>和<code>创建应用</code>，我们点击<kbd>创建应用</kbd>，随便输入一个名字，例如<code>valine</code>，其他默认，点击<kbd>创建</kbd></p><p>现在在控制台出现了你刚刚创建的应用，点击这个应用的右上角设置<strong><i class="fa fa-cog" aria-hidden="true"></i></strong>图标</p><p>进入了设置界面，点击<code>valine开发版</code>下面那栏里的<code>应用Keys</code></p><p>我们只需要里面的<code>AppID</code>和<code>AppKey</code>，现在先开始下面操作，这两个等下会用到</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustration/valine1.png" alt="valine应用keys"></p><p>当然啦，如果你的博客主题已经<strong>集成</strong>了valine，那么将这两个值填到<code>_config.yml</code>的相应位置就可以了，下面的教程就可以选择性查看了</p><h2><span id="修改博客渲染文件">修改博客渲染文件</span></h2><h3><span id="添加代码">添加代码</span></h3><p>现在到博客主题目录下打开<code>layout</code>目录，再进入<code>_partial</code>目录，在这个目录即<code>MyBlog/themes/主题名/layout/_partial</code>下新建<code>comment.ejs</code>文件，填入代码：</p><pre><code class="ejs">&lt;% if (theme.valine &amp;&amp; post.comments) { %&gt;    &lt;script src=&#39;//unpkg.com/valine/dist/Valine.min.js&#39;&gt;&lt;/script&gt;    &lt;!-- 当然啦，这个可以在&lt;head&gt;&lt;/head&gt;中引入 --&gt;    &lt;div id=&quot;vcomments&quot;&gt;&lt;/div&gt;    &lt;script&gt;    new Valine({        el: &#39;#vcomments&#39; ,        appId: &#39;&lt;%= theme.v_appId %&gt;&#39;,        appKey: &#39;&lt;%= theme.v_appKey %&gt;&#39;,        avatar:&#39;mp&#39;,         placeholder: &#39;just go go&#39; ，          enableQQ: true,          // 设置Bilibili表情包地址        emojiCDN: &#39;//i0.hdslb.com/bfs/emote/&#39;,         // 表情title和图片映射        emojiMaps: {            &quot;tv_doge&quot;: &quot;6ea59c827c414b4a2955fe79e0f6fd3dcd515e24.png&quot;,            &quot;tv_亲亲&quot;: &quot;a8111ad55953ef5e3be3327ef94eb4a39d535d06.png&quot;,            &quot;tv_偷笑&quot;: &quot;bb690d4107620f1c15cff29509db529a73aee261.png&quot;,            &quot;tv_再见&quot;: &quot;180129b8ea851044ce71caf55cc8ce44bd4a4fc8.png&quot;,            &quot;tv_冷漠&quot;: &quot;b9cbc755c2b3ee43be07ca13de84e5b699a3f101.png&quot;,            &quot;tv_发怒&quot;: &quot;34ba3cd204d5b05fec70ce08fa9fa0dd612409ff.png&quot;,            &quot;tv_发财&quot;: &quot;34db290afd2963723c6eb3c4560667db7253a21a.png&quot;,            &quot;tv_可爱&quot;: &quot;9e55fd9b500ac4b96613539f1ce2f9499e314ed9.png&quot;,            &quot;tv_吐血&quot;: &quot;09dd16a7aa59b77baa1155d47484409624470c77.png&quot;,            &quot;tv_呆&quot;: &quot;fe1179ebaa191569b0d31cecafe7a2cd1c951c9d.png&quot;,            &quot;tv_呕吐&quot;: &quot;9f996894a39e282ccf5e66856af49483f81870f3.png&quot;,            &quot;tv_困&quot;: &quot;241ee304e44c0af029adceb294399391e4737ef2.png&quot;,            &quot;tv_坏笑&quot;: &quot;1f0b87f731a671079842116e0991c91c2c88645a.png&quot;,            &quot;tv_大佬&quot;: &quot;093c1e2c490161aca397afc45573c877cdead616.png&quot;,            &quot;tv_大哭&quot;: &quot;23269aeb35f99daee28dda129676f6e9ea87934f.png&quot;,            &quot;tv_委屈&quot;: &quot;d04dba7b5465779e9755d2ab6f0a897b9b33bb77.png&quot;,            &quot;tv_害羞&quot;: &quot;a37683fb5642fa3ddfc7f4e5525fd13e42a2bdb1.png&quot;,            &quot;tv_尴尬&quot;: &quot;7cfa62dafc59798a3d3fb262d421eeeff166cfa4.png&quot;,            &quot;tv_微笑&quot;: &quot;70dc5c7b56f93eb61bddba11e28fb1d18fddcd4c.png&quot;,            &quot;tv_思考&quot;: &quot;90cf159733e558137ed20aa04d09964436f618a1.png&quot;,            &quot;tv_惊吓&quot;: &quot;0d15c7e2ee58e935adc6a7193ee042388adc22af.png&quot;,            // ... 更多表情        }     });    &lt;/script&gt;&lt;% } %&gt;</code></pre><p>再将如下代码放到需要的位置，一般是在文章底部</p><pre><code class="ejs">&lt;%- partial(&#39;_partial/comment&#39;) %&gt;</code></pre><h3><span id="判断讲解">判断讲解</span></h3><p>加入判断是为了方便开启和关闭评论，首先是主题配置文件<code>_config.yml</code>中,加入</p><pre><code class="yml">valine: true   # 修改为false关闭valine评论v_appId: 刚刚获取的appidv_appKey: 刚刚获取的appkey</code></pre><p>接着是写的文章中<code>文章名.md</code></p><pre><code class="markdown">---title: 文章名categories: 博客date: 2020-03-30 12:16:01comments: true    添加上这个，如果这篇文章不需要评论改为false，默认开启tags: hexo---正文</code></pre><p>这里需要注意一下：以前我使用<code>post.comments</code>没有问题，但是现在好像不行了，如果报的是有关<code>post</code>的错误，可以改为<code>page.comments</code>，或者直接去掉</p><h3><span id="配置项讲解">配置项讲解</span></h3><p>可以前往<a href="https://valine.js.org/" target="_blank" rel="noopener">valine官网</a>查看具体的使用</p><p><del>这里挑一些我们会用到的说下</del></p><p><font color="red" size="5">更新</font> 不写出已经丢弃的配置项</p><p>除了最后一个配置项，其他都要在末尾加上英文的<code>,</code></p><pre><code>el: &#39;#vcomments&#39;</code></pre><p>这个字段只要你不改<code>div</code>的代码就不需要修改,如果你<code>div</code>不想用<code>id</code>，而是改为了<code>class=&quot;valine&quot;</code>，那你就需要把这里改为<code>.valine</code></p><pre><code>appId: &#39;从LeanCloud的应用中得到的appId.&#39;</code></pre><pre><code>appKey: &#39;从LeanCloud的应用中得到的APP Key.&#39;</code></pre><pre><code>placeholder: `快来评论啊`</code></pre><p>评论框占位提示符，写了之后会在评论框内出现文字</p><pre><code>path: &#39;window.location.pathname&#39;</code></pre><p>当前<code>文章页</code>路径，用于区分不同的<code>文章页</code>，用这个默认值就行</p><blockquote><ul><li>I. 请保证每个<code>文章页</code>路径的唯一性，否则可能会出现不同<code>文章页</code>下加载相同评论列表的情况。</li><li>II. 如果值为<code>window.location.href</code>，可能会出现随便加<code>不同参数</code>进入该页面，而被判断成新页面的情况。</li></ul></blockquote><pre><code>avatar: &#39;mp&#39;</code></pre><p>看官网的介绍，可以配置为QQ头像</p><pre><code>meta: [&#39;nick&#39;,&#39;mail&#39;,&#39;link&#39;]</code></pre><p>评论者相关属性，这里有三个：昵称、邮箱、网页链接</p><pre><code>requiredFields: [&#39;nick&#39;,&#39;mail&#39;]</code></pre><p>设置<code>必填项</code>，默认为<code>[]</code>，匿名显示</p><pre><code>pageSize: &#39;5&#39;</code></pre><p>评论列表分页，每页条数,默认为10</p><pre><code>lang: &#39;en&#39;</code></pre><p>语言，默认为中文：zh-CN</p><pre><code>visitor: true</code></pre><p>文章访问量统计,这个还是挺好用的，等下说</p><pre><code>highlight: false</code></pre><p>代码高亮，默认开启，这个不推荐关闭，去掉这个字段就行</p><pre><code>avatarForce: false</code></pre><p>每次访问强制拉取最新的评论列表头像,不推荐设置为true，目前的评论列表头像会自动带上Valine的版本号</p><pre><code>recordIP: true</code></pre><p>设置为true之后会记录评论者IP，默认为false</p><pre><code>// 这里设置CDN, 默认微博表情CDNemojiCDN: &#39;https://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/&#39;, // 表情title和图片映射emojiMaps: {&quot;smile&quot;:&quot;e3/2018new_weixioa02_org.png&quot;,&quot;lovely&quot;:&quot;09/2018new_keai_org.png&quot;,// ... 更多表情} </code></pre><pre><code>enableQQ: true</code></pre><p>启用<code>昵称框</code>自动获取<code>QQ昵称</code>和<code>QQ头像</code>, 默认关闭</p><h2><span id="文章阅读量">文章阅读量</span></h2><p>可以使用不蒜子，但是偶尔会链接不到</p><p>按照valine的说法：</p><blockquote><p>如果开启了阅读量统计，Valine 会自动检测 leancloud 应用中是否存在Counter类，如果不存在会自动创建，无需手动创建~</p></blockquote><p>也就是如果没使用valine的话还需要自己添加一个<code>Counter类</code>，这个是在<code>LeanCloud</code>的<code>存储</code>中，但是我们现在配置了valine了就可以省事了</p><blockquote><p>Valine会自动查找页面中class值为leancloud_visitors的元素，获取其id为查询条件。并将得到的值填充到其class的值为leancloud-visitors-count的子元素里：</p></blockquote><p>so，我们只需要在<code>comment.ejs</code>的最后面加上这些即可</p><pre><code>&lt;span id=&quot;/&lt;%= page.path %&gt;&quot; class=&quot;leancloud_visitors&quot; data-flag-title=&quot;Your Article Title&quot;&gt;    &lt;em class=&quot;post-meta-item-text&quot;&gt;阅读量 &lt;/em&gt;    &lt;i class=&quot;leancloud-visitors-count&quot;&gt;1000000&lt;/i&gt;&lt;/span&gt;</code></pre><p>如果这个<code>id</code>不行的话，可以改为<code>page.permalink</code>完整的网址试下</p><h2><span id="头像配置"><del>头像配置</del></span></h2><blockquote><p>Valine 目前使用的是<a href="http://cn.gravatar.com/" target="_blank" rel="noopener">Gravatar</a> 作为评论列表头像。<br>请自行登录或注册Gravatar，然后修改自己的头像。<br>评论的时候，留下在Gravatar注册时所使用的邮箱即可。</p></blockquote><p><del>valine文档中说<code>感谢gravatar.cat.net提供的镜像服务</code>，那用QQ的可不可以呢</del></p><p><del>然后找到了这篇文章：</del></p><p><a href="https://blog.csdn.net/cungudafa/article/details/104638730/" target="_blank" rel="noopener">Valine-实现QQ邮箱识别生成头像地址（完美解决头像问题）</a></p><p><del>首先下载<a href="https://unpkg.com/valine@latest/dist/Valine.min.js" target="_blank" rel="noopener">valine.min.js</a> ，现在是1.3.10版本</del></p><p><del>放到主题的source目录的js目录下</del></p><p><del>再把刚刚上面引入的js改为</del></p><pre><code>&lt;script src=&#39;//unpkg.com/valine/dist/Valine.min.js&#39;&gt;&lt;/script&gt;改为&lt;script src=&#39;/js/Valine1.3.10.min.js&#39;&gt;&lt;/script&gt;</code></pre><p><del>打开js文件，搜索</del></p><pre><code>(m.cdn+a(e.get(&quot;mail&quot;))+m.params)+&#39;&quot;&gt;&#39;,</code></pre><p><del>从这里往前直到<code>var C=function(e,n,r)</code>，都替换为</del></p><pre><code> var C=function(e,n,r){    var qq_img=m.cdn+a(e.get(&quot;mail&quot;))+m.params;//默认gravator头像接口     if(e.get(&quot;mail&quot;).indexOf(&quot;@qq.com&quot;) &gt;= 0){        var prefix = e.get(&quot;mail&quot;).replace(/@.*/, &quot;&quot;);//前缀        var pattern=/^\d+$/g;  //正则表达式，数字        var result= prefix.match(pattern);//match 是匹配的意思        if(result!==null){            qq_img = &quot;//q1.qlogo.cn/g?b=qq&amp;nk=&quot;+ prefix +&quot;&amp;s=100&quot;;        }    }    var i=u.create(&quot;div&quot;,{class:&quot;vcard&quot;,id:e.id}),o=m.hide?&quot;&quot;:&#39;&lt;img class=&quot;vimg&quot; src=&quot;&#39;+ (qq_img)+&#39;&quot;&gt;&#39;,</code></pre><p><del>然后填的是QQ邮箱的话就会显示QQ头像辣</del></p><p><del>当然如果你实在很懒，可以直接使用这个已经修改好了的</del></p><pre><code class="javascript">&lt;script src=&#39;https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/js/Valine1.3.10.min.js&#39;&gt;&lt;/script&gt;</code></pre><p><font color="red" size="5">注</font> 由于新版的valine已经加入了QQ头像的内容，因此教程修改为以下内容：</p><p>仍然只需要引入官方原版的js文件，不需要在源代码中修改了</p><pre><code class="javascript">&lt;script src=&#39;//unpkg.com/valine/dist/Valine.min.js&#39;&gt;&lt;/script&gt;</code></pre><p>开启配置项中的昵称框自动获取<code>QQ昵称</code>和<code>QQ头像</code>功能</p><pre><code>enableQQ: true;</code></pre><h2><span id="自定义表情">自定义表情</span></h2><p>很早就想把valine里的表情修改为B站表情了，可惜没找到好用的教程，没想到作者居然加入了这个功能</p><p>使用方法：</p><pre><code class="yml">new Valine({    el:&#39;#vcomment&#39;,    appId:&#39;&lt;Your_APP_ID&gt;&#39;,    appKey:&#39;&lt;Your_APP_KEY&gt;&#39;,    // 设置表情包地址    emojiCDN: &#39;//i0.hdslb.com/bfs/emote/&#39;,     // 表情title和图片映射    emojiMaps: {        &quot;tv_doge&quot;: &quot;6ea59c827c414b4a2955fe79e0f6fd3dcd515e24.png&quot;,        &quot;tv_亲亲&quot;: &quot;a8111ad55953ef5e3be3327ef94eb4a39d535d06.png&quot;,        &quot;tv_偷笑&quot;: &quot;bb690d4107620f1c15cff29509db529a73aee261.png&quot;,        &quot;tv_再见&quot;: &quot;180129b8ea851044ce71caf55cc8ce44bd4a4fc8.png&quot;,        &quot;tv_冷漠&quot;: &quot;b9cbc755c2b3ee43be07ca13de84e5b699a3f101.png&quot;,        &quot;tv_发怒&quot;: &quot;34ba3cd204d5b05fec70ce08fa9fa0dd612409ff.png&quot;,        &quot;tv_发财&quot;: &quot;34db290afd2963723c6eb3c4560667db7253a21a.png&quot;,        &quot;tv_可爱&quot;: &quot;9e55fd9b500ac4b96613539f1ce2f9499e314ed9.png&quot;,        &quot;tv_吐血&quot;: &quot;09dd16a7aa59b77baa1155d47484409624470c77.png&quot;,        &quot;tv_呆&quot;: &quot;fe1179ebaa191569b0d31cecafe7a2cd1c951c9d.png&quot;,        &quot;tv_呕吐&quot;: &quot;9f996894a39e282ccf5e66856af49483f81870f3.png&quot;,        &quot;tv_困&quot;: &quot;241ee304e44c0af029adceb294399391e4737ef2.png&quot;,        &quot;tv_坏笑&quot;: &quot;1f0b87f731a671079842116e0991c91c2c88645a.png&quot;,        &quot;tv_大佬&quot;: &quot;093c1e2c490161aca397afc45573c877cdead616.png&quot;,        &quot;tv_大哭&quot;: &quot;23269aeb35f99daee28dda129676f6e9ea87934f.png&quot;,        &quot;tv_委屈&quot;: &quot;d04dba7b5465779e9755d2ab6f0a897b9b33bb77.png&quot;,        &quot;tv_害羞&quot;: &quot;a37683fb5642fa3ddfc7f4e5525fd13e42a2bdb1.png&quot;,        &quot;tv_尴尬&quot;: &quot;7cfa62dafc59798a3d3fb262d421eeeff166cfa4.png&quot;,        &quot;tv_微笑&quot;: &quot;70dc5c7b56f93eb61bddba11e28fb1d18fddcd4c.png&quot;,        &quot;tv_思考&quot;: &quot;90cf159733e558137ed20aa04d09964436f618a1.png&quot;,        &quot;tv_惊吓&quot;: &quot;0d15c7e2ee58e935adc6a7193ee042388adc22af.png&quot;,        // ... 更多表情    } })</code></pre><p>然后这里分享一下几个表情包：</p><p>cdn可以填<code>https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/tree/master/emote/xxx</code></p><p><code>xxx</code>代表里面的几个目录，每个里都提供了一个包含该目录下所有图片的<code>list.txt</code></p><pre><code>bili B站表情包，里面有两种cool 酷安表情包qq   QQ表情包tieba 百度贴吧weibo 微博</code></pre><p>我实在忘了这是在哪个地方找来的表情包了，所以暂时不声明原地址了，等找到了再加上</p><p>啊，还有一个老哥提供的，地址在下面</p><p><a href="https://github.com/blogimg/emotion" target="_blank" rel="noopener">https://github.com/blogimg/emotion</a></p><h2><span id="评论框美化">评论框美化</span></h2><p>默认的其实还可以，可素可以更漂亮肯定得加的</p><p>可以去看这篇博客<a href="https://immmmm.com/valine-diy/" target="_blank" rel="noopener">Valine 评论框美化及功能优化</a></p><p>这里我就直接搬过来啦</p><pre><code class="css">.v .vwrap{padding: 0 0 44px;}.v .veditor{min-height:7rem;resize:none;}.v .vwrap .vedit{padding-top:0}.v .vwrap .vheader{width: 80%;bottom:0;position: absolute;background: #f7f7f7;}.v .vinput{padding:10px 15px;}.v .vwrap .vheader .vinput{border-bottom:0px}.v .vwrap .vedit .vctrl{margin-top:-44px;right:0;position:absolute;margin-right:-3px;}.v .vwrap .vcontrol{    position:absolute;right:0;bottom:0;width:20%;padding-top:0px;}.v .vwrap .vcontrol .col.col-80{width: 100%;}.v .vbtn.vsubmit{border-radius: 0;padding: 0;color: #fff;line-height: 44px;width:100%;border: none;background:#1abc9c;}.v .vwrap .vedit .vctrl span.vpreview-btn,.v .vwrap .vcontrol .col.col-20,.v .vlist .vcard .vhead .vsys{display:none;}@media screen and (max-width: 520px){    .v .vwrap .vheader .vinput{width: 33.33%;padding:10px 5px;}}</code></pre><p>如果你需要底部有个图片可以加上</p><pre><code class="css">#veditor {    background-image: url(背景图片地址);    background-size: contain;    background-repeat: no-repeat;    background-position: right;    background-color: rgba(255,255,255,0);    resize: vertical;}</code></pre><p><code>placeholder</code>字段也能用上，使用今日诗词或者一言</p><pre><code class="javascript">&lt;script src=&quot;https://sdk.jinrishici.com/v2/browser/jinrishici.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;jinrishici.load(function(result) {    var jrsc_plac =  result.data.content + &quot;\n「&quot; + result.data.origin.title + &quot;」&quot; + result.data.origin.dynasty + &quot; · &quot; + result.data.origin.author    document.getElementById(&quot;veditor&quot;).setAttribute(&quot;placeholder&quot;,jrsc_plac);});&lt;/script&gt;&lt;style&gt;textarea[id=&#39;veditor&#39;]::placeholder{    color:#b3b3b3;font-size:14px;text-align:center;line-height:6rem;}&lt;/style&gt;</code></pre><h2><span id="评论框的功能">评论框的功能</span></h2><p>可能需要jQuery，先引入</p><pre><code>&lt;script src=&quot;https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js&quot;&gt;&lt;/script&gt;</code></pre><h3><span id="前端验证">前端验证</span></h3><p>valine自带的验证码也可以打开</p><p>主要是检查昵称和邮箱</p><pre><code class="ejs">&lt;script&gt;    document.body.addEventListener(&#39;click&#39;, function(e) {        if (e.target.classList.contains(&#39;vsubmit&#39;)) {            const email = document.querySelector(&#39;input[type=email]&#39;);            const nick = document.querySelector(&#39;input[name=nick]&#39;);            const reg = /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;            if (!email.value || !nick.value || !reg.test(email.value)) {                const str = `&lt;div class=&quot;valert text-center&quot;&gt;&lt;div class=&quot;vtext&quot;&gt;请填写正确的昵称和邮箱！&lt;/div&gt;&lt;/div&gt;`;                const vmark = document.querySelector(&#39;.vmark&#39;);                vmark.innerHTML = str;                vmark.style.display = &#39;block&#39;;                setTimeout(function() {                    vmark.style.display = &#39;none&#39;;                    vmark.innerHTML = &#39;&#39;;                }, 2500);            }        }    });&lt;/script&gt;</code></pre><h3><span id="点击回复评论框跟随"><del>点击回复评论框跟随</del></span></h3><p>还是immmmm的</p><blockquote><p>官方版本点击回复时都是跳回到页面上方的评论框进行回复，评论框是固定不动的。比较合理的是：点哪条的回复，评论框就显示在此条评论下方。避免页面跳上跳下。<br>相关 jQuery 代码：</p></blockquote><pre><code>$(document).ready(function(){    $(&#39;.vemoji-btn&#39;).text(&#39;😀&#39;);    $(&quot;#vcomments&quot;).on(&#39;click&#39;, &#39;span.vat&#39;,function(){        $(this).parent(&#39;div.vmeta&#39;).next(&quot;div.vcontent&quot;).after($(&quot;div.vwrap&quot;));        $(&#39;textarea#veditor&#39;).focus();    })})</code></pre><h2><span id="邮件提醒">邮件提醒</span></h2><p><strong>重头戏</strong>来了</p><p>好不容易搭了个博客，结果有人评论我们不能及时回复，这体验感多差</p><p>gitment、gitalk都是用的GitHub的issue，能够轻松地发送邮件提醒</p><p>而valine呢</p><p>有大佬就弄出了valine-Admin，可以结合LeanCloud实现邮箱提醒功能</p><p>项目地址：<a href="https://github.com/DesertsP/Valine-Admin" target="_blank" rel="noopener">Valine-Admin</a></p><h3><span id="云引擎设置">云引擎设置</span></h3><p>回到我们在LeanCloud创建的应用<code>valine</code>，点击左边的第二个选项<code>云引擎</code></p><p>点击<code>web组</code>下方的<code>设置</code></p><p>在代码库的下方填上<code>https://github.com/DesertsP/Valine-Admin.git</code>，点击方框右边的<kbd>保存</kbd></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustration/valine2.png" alt="代码库"></p><p>往下滑，在<code>自定义环境变量</code>这里添加如下变量</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustration/valine3.png" alt="变量"></p><p>这些下面有解释，我们先接着往下滑，把<code>Web 主机域名</code>启用</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustration/valine4.png" alt="Web主机域名"></p><table><thead><tr><th>变量</th><th>示例</th><th>说明</th></tr></thead><tbody><tr><td><strong>SITE_NAME</strong></td><td>Deserts</td><td>[必填]博客名称</td></tr><tr><td><strong>SITE_URL</strong></td><td><a href="https://deserts.io" target="_blank" rel="noopener">https://deserts.io</a></td><td>[必填]首页地址</td></tr><tr><td><strong><em>SMTP_SERVICE</em></strong></td><td>QQ</td><td>[新版支持]邮件服务提供商，支持 QQ、163、126、Gmail 以及 <a href="https://nodemailer.com/smtp/well-known/#supported-services" target="_blank" rel="noopener">更多</a></td></tr><tr><td><strong>SMTP_USER</strong></td><td><a href="mailto:xxxxxx@qq.com">xxxxxx@qq.com</a></td><td>[必填]SMTP登录用户</td></tr><tr><td><strong>SMTP_PASS</strong></td><td>ccxxxxxxxxch</td><td>[必填]SMTP登录密码（QQ邮箱需要获取独立密码）</td></tr><tr><td><strong>SENDER_NAME</strong></td><td>Deserts</td><td>[必填]发件人</td></tr><tr><td><strong>SENDER_EMAIL</strong></td><td><a href="mailto:xxxxxx@qq.com">xxxxxx@qq.com</a></td><td>[必填]发件邮箱</td></tr><tr><td><strong>ADMIN_URL</strong></td><td><a href="https://xxx.leanapp.cn/" target="_blank" rel="noopener">https://xxx.leanapp.cn/</a></td><td>[建议]Web主机二级域名，用于自动唤醒</td></tr><tr><td>BLOGGER_EMAIL</td><td><a href="mailto:xxxxx@gmail.com">xxxxx@gmail.com</a></td><td>[可选]博主通知收件地址，默认使用SENDER_EMAIL</td></tr><tr><td>AKISMET_KEY</td><td>xxxxxxxxxxxx</td><td>[可选]Akismet Key 用于垃圾评论检测，设为MANUAL_REVIEW开启人工审核，留空不使用反垃圾</td></tr><tr><td>MAIL_SUBJECT</td><td>${PARENT_NICK}，您在${SITE_NAME}上的评论收到了回复</td><td>[可选]@通知邮件主题（标题）模板</td></tr><tr><td>MAIL_TEMPLATE</td><td>见下文</td><td>[可选]@通知邮件内容模板</td></tr><tr><td>MAIL_SUBJECT_ADMIN</td><td>${SITE_NAME}上有新评论了</td><td>[可选]博主邮件通知主题模板</td></tr><tr><td>MAIL_TEMPLATE_ADMIN</td><td>见下文</td><td>[可选]博主邮件通知内容模板</td></tr></tbody></table><p>博客名、博客地址应该会填吧</p><p>邮件服务提供商，你使用的哪种就填哪个，比如我的是<code>xxx@qq.com</code>，那么就填<code>QQ</code>。</p><p>SMTP登录用户填上你自己的邮箱</p><p>登录密码并不是真的登录密码，网易的自己搜一下<code>网易邮箱登录授权码</code>；QQ邮箱的为独立密码</p><p>发件人填自己昵称</p><p>发件邮箱和博主收件地址都可以填刚才的邮箱</p><p>二级域名就是刚刚开启的哪个</p><p>AKISMET_KEY，有三种选择：不添加，不开启审核；添加后设置为MANUAL_REVIEW人工审核；设为你注册<a href="https://akismet.com/development/后获取的key自动审核拦截垃圾评论" target="_blank" rel="noopener">https://akismet.com/development/后获取的key自动审核拦截垃圾评论</a></p><h3><span id="设置邮件模板">设置邮件模板</span></h3><p>上面还有四个变量没说，这四个就是用来设置邮件模板的，当然，你也可以用LeanCloud自带的</p><p>两个标题就不说了</p><p>说下<code>@的</code>和<code>通知博主</code>的内容模板</p><p>可以直接设置为项目地址中给的</p><p>也可以用immmmm的：</p><p>MAIL_TEMPLATE 代码，自行替换logo图片地址：</p><pre><code class="html">&lt;div style=&quot;padding:2em 10%;color:#b3b3b1;width:420px;margin:0 auto;font-size:14px&quot;;&gt;    &lt;img style=&quot;display:block;width:50px;margin:0 auto&quot; src=&quot;https://图片地址/logo.png&quot;&gt;    &lt;p style=&quot;text-align:center;&quot;&gt;Hi，&lt;span style=&quot;color:#3eae5f&quot;&gt; ${PARENT_NICK} &lt;/span&gt;&lt;/p&gt;    &lt;p style=&quot;font-size:13px;text-align:center;&quot;&gt;有人回复了您在 &lt;strong style=&quot;font-weight:bold&quot;&gt; ${SITE_NAME} &lt;/strong&gt; 上的评论&lt;/p&gt;    &lt;hr style=&quot;width:64px;border:0;border-bottom:1px solid #e5e5e5;margin:24px auto;&quot;&gt;    &lt;div style=&quot;color:#333;overflow:hidden;&quot;&gt;        &lt;p style=&quot;display:inline-block;float:left;&quot;&gt;&lt;span style=&quot;color:#3eae5f;font-weight:bold&quot;&gt; 您 &lt;/span&gt;&lt;span&gt;说：&lt;/span&gt;&lt;/p&gt;        ${PARENT_COMMENT}    &lt;/div&gt;    &lt;div style=&quot;color:#333;overflow:hidden;&quot;&gt;        &lt;p style=&quot;display:inline-block;float:left;&quot;&gt;&lt;span style=&quot;color:#3eae5f;font-weight:bold&quot;&gt; ${NICK} &lt;/span&gt;&lt;span&gt;说：&lt;/span&gt;&lt;/p&gt;        ${COMMENT}    &lt;/div&gt;    &lt;p&gt;&lt;a style=&quot;color:#ffffff;text-decoration:none;display:inline-block;min-height:28px;line-height:28px;padding:0 13px;outline:0;background:#3eae5f;font-size:13px;text-align:center;font-weight:400;border:0;border-radius:999em&quot; href=&quot;${POST_URL}&quot; target=&quot;_blank&quot;&gt;点击查看&lt;/a&gt;&lt;/p&gt;    &lt;hr style=&quot;width:64px;border:0;border-bottom:1px solid #e5e5e5;margin:24px auto;&quot;&gt;    &lt;p&gt;&lt;a style=&quot;display:block;color:#b3b3b1;text-decoration:none;text-align:center;&quot; href=&quot;${SITE_URL}&quot; target=&quot;_blank&quot;&gt;${SITE_NAME}&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</code></pre><p>MAIL_TEMPLATE_ADMIN 通知博主邮件模板代码：</p><pre><code class="html">&lt;div style=&quot;line-height:24px;font-size:13px;&quot;&gt;    &lt;p&gt;&lt;span style=&quot;color:#3eae5f&quot;&gt; ${NICK} &lt;/span&gt; 说：&lt;/p&gt;    &lt;p &gt;${COMMENT}&lt;/p&gt;    &lt;p style=&quot;font-size:12px;line-height:12px;&quot;&gt;&lt;a style=&quot;color:#b3b3b1;text-decoration:none;&quot; href=&quot;${POST_URL}&quot; target=&quot;_blank&quot;&gt;${POST_URL}&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</code></pre><p>在这里面可用的变量有：</p><table><thead><tr><th>模板里的变量</th><th>说明</th></tr></thead><tbody><tr><td>SITE_NAME</td><td>博客名称</td></tr><tr><td>SITE_URL</td><td>博客首页地址</td></tr><tr><td>POST_URL</td><td>文章地址（完整路径）</td></tr><tr><td><em>PARENT_NICK</em></td><td>收件人昵称（被@者，父级评论人）</td></tr><tr><td><em>PARENT_COMMENT</em></td><td>父级评论内容</td></tr><tr><td>NICK</td><td>新评论者昵称</td></tr><tr><td>COMMENT</td><td>新评论内容</td></tr></tbody></table><p>在模板中使用的话，<code>${变量名}</code></p><p><em>斜体</em>的两个变量表示只能在被@的模板中使用</p><h3><span id="开始部署">开始部署</span></h3><p>点击左边的<code>部署</code>，再点击页面中的<kbd>Git源码部署</kbd></p><p>在出现的页面中<code>分支或版本号</code>下方的框中填入<code>master</code>，勾选☑️<code>下载最新依赖</code>复选框</p><p>再点击<kbd>部署</kbd>,接着等待部署完成即可</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustration/valine5.png" alt="部署"></p><h3><span id="设置定时任务">设置定时任务</span></h3><p>到上一步已经完成了邮箱通知提醒的功能了</p><p>但是免费的LeanCloud有休眠政策，具体能运行几小时忘记了，但是不能全天运行</p><p>也就是说可能会漏掉一些评论的提醒</p><p>刚好LeanCloud有一个定时任务，可以弥补这个缺陷</p><p>不能全天24h一直运行，那么我运行一会休息一会，在运行的那个时间段里把邮件补发一下总行吧</p><p>这就是定时任务所执行的内容</p><p>点击<code>定时任务</code>，创建两个定时任务，分别填：</p><p>名称：<code>自动唤醒</code>  生产环境：<code>self-wake</code>  Cron表达式：<code>0 0/30 7-23 * * ?</code><br>名称：<code>定时检查24小时内漏发的邮件通知</code>  生产环境：<code>resend-mails</code>  Cron表达式：<code>0 0 8 * * ?</code></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/pic/illustration/valine6.png" alt></p><hr><p>设置完成之后就OK啦</p><p>这样就拥有了一个完美的评论系统了</p><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      &lt;p&gt;最开始用这个评论是在使用Sakura主题的时候，但是那个时候没认真看教程&lt;/p&gt;
&lt;p&gt;觉得它没有邮件提醒功能、而且有的时候还会报错，就没使用了&lt;/p&gt;
&lt;p&gt;改用的来必力和gitment，gitment使用起来挺方便的，能够邮件提醒，而且报的错误也能解决&lt;/p&gt;
&lt;p&gt;emmmm……一直到现在，想接着用valine，然后搜了很多篇教程，发现valine还是非常好用的&lt;/p&gt;
&lt;p&gt;手动配图：真香.gif&lt;/p&gt;
&lt;p&gt;然后接下来就讲下怎样&lt;strong&gt;添加valine&lt;/strong&gt;，对其进行&lt;strong&gt;美化&lt;/strong&gt;和添加评论功能吧&lt;/p&gt;
    
    </summary>
    
    
      <category term="博客" scheme="https://wallleap.cn/categories/%E5%8D%9A%E5%AE%A2/"/>
    
      <category term="valine" scheme="https://wallleap.cn/categories/%E5%8D%9A%E5%AE%A2/valine/"/>
    
    
      <category term="hexo" scheme="https://wallleap.cn/tags/hexo/"/>
    
  </entry>
  
  <entry>
    <title>Markdown语法大全</title>
    <link href="https://wallleap.cn/2020/03/29/markdown/"/>
    <id>https://wallleap.cn/2020/03/29/markdown/</id>
    <published>2020-03-29T14:23:57.000Z</published>
    <updated>2025-01-24T09:22:15.353Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>这位老哥写的已经挺全了，刚好有一些语法忘记了，留个档</p><!-- toc --><ul><li><a href="#一-概述">一、概述</a><ul><li><a href="#11-设计理念">1.1 设计理念</a></li><li><a href="#12-内联-html-语法">1.2 内联 HTML 语法</a></li><li><a href="#13-特殊字符自动转义">1.3 特殊字符自动转义</a></li></ul></li><li><a href="#二-行内语法讲解">二、行内语法讲解</a><ul><li><a href="#21-注释的表述">2.1 注释的表述</a></li><li><a href="#22-分级标题-任务列表">2.2 分级标题、任务列表</a></li><li><a href="#23-缩进-换行-空行-对齐方式">2.3 缩进、换行、空行、对齐方式</a></li><li><a href="#24-斜体-粗体-删除线-下划线-背景高亮">2.4 斜体、粗体、删除线、下划线、背景高亮</a></li><li><a href="#25-超链接-页内链接-自动链接-注脚">2.5 超链接、页内链接、自动链接、注脚</a></li><li><a href="#26-无序列表-有序列表-定义型列表">2.6 无序列表、有序列表、定义型列表</a></li><li><a href="#27-插入图像">2.7 插入图像</a></li><li><a href="#28-多级引用">2.8 多级引用</a></li><li><a href="#29-转义字符-字体-字号-颜色">2.9 转义字符、字体、字号、颜色</a></li></ul></li><li><a href="#三-块语法讲解">三、块语法讲解</a><ul><li><a href="#31-内容目录">3.1 内容目录</a></li><li><a href="#32-代码块">3.2 代码块</a></li><li><a href="#33-流程图">3.3 流程图</a></li><li><a href="#34-表格">3.4 表格</a></li><li><a href="#35-latex-公式">3.5 LaTeX 公式</a></li><li><a href="#36-分隔线">3.6 分隔线</a></li><li><a href="#37-html-原始码">3.7 HTML 原始码</a></li><li><a href="#38-特殊字">3.8 特殊字</a></li></ul></li></ul><!-- tocstop --><h2><span id="一-概述">一、概述</span></h2><h3><span id="11-设计理念">1.1 设计理念</span></h3><ul><li>Markdown 易于阅读，方便创作web文档，利于各平台无缝分发。</li><li>Markdown 语法灵感最大的来源还是纯文本 email 的格式，完全由标点符号标签组成的纯文本。</li><li>Markdown 文件应该以纯文本形式原样发布，不应该包含标记标签和格式化指令。</li></ul><h3><span id="12-内联-html-语法">1.2 内联 HTML 语法</span></h3><ul><li>HTML 是一种发布格式，Markdown 是一种创作格式。</li><li>Markdown语法集合比较小，只是HTML标签的一小部分。</li><li>对于 Markdown 中未包含的标签, 可以直接使用 HTML标签，例如用 HTML <a> 标签替代 Markdown 的链接语法。</a></li></ul><h3><span id="13-特殊字符自动转义">1.3 特殊字符自动转义</span></h3><p>  <br>在 HTML 中, 有两个字符需要特殊对待: &lt; 和 &amp;，左尖括号用于起始标签。果你想将它们用作字面量, 你必须将它们转义为字符实体, 例如<code>&amp;lt;</code> 和 <code>&amp;amp;</code>。</p><h2><span id="二-行内语法讲解">二、行内语法讲解</span></h2><h3><span id="21-注释的表述">2.1 注释的表述</span></h3><ul><li>代码法<pre><code>&lt;div style=&#39;display: none&#39;&gt;哈哈我是注释，不会在浏览器中显示。&lt;/div&gt;</code></pre></li><li>html注释</li></ul><p>既然支持html语法，那也支持html注释，快捷键 comment + /。</p><pre><code>&lt;!--哈哈我是注释，不会在浏览器中显示。--&gt;&lt;!--哈哈我是多段注释，不会在浏览器中显示。    --&gt;</code></pre><ul><li>hack方法</li></ul><p>hack方法就是利用markdown的解析原理来实现注释的。<br>一般有的markdown解析器不支持上面的注释方法，这个时候就可以用hack方法。<br>hack方法比上面2种方法稳定得多，但是语义化太差。</p><pre><code>[//]: # (哈哈我是最强注释，不会在浏览器中显示。)[^_^]: # (哈哈我是最萌注释，不会在浏览器中显示。)[//]: &lt;&gt; (哈哈我是注释，不会在浏览器中显示。)[comment]: &lt;&gt; (哈哈我是注释，不会在浏览器中显示。)</code></pre><h3><span id="22-分级标题-任务列表">2.2 分级标题、任务列表</span></h3><ul><li><p>分级标题</p><pre><code># 一级标题## 二级标题### 三级标题#### 四级标题##### 五级标题###### 六级标题  &lt;!--最多6级标题--&gt;</code></pre><p>由于用了标记编辑器会把所有标题写到目录大纲中，在这里写的演示标题也会列进去，所以就不演示了。同学们自己在编辑器中观察，很简单，一级标题字号最大，依级递减。</p></li><li><p>任务列表<br>Markdown 语法：<br>```</p></li><li><input disabled type="checkbox"> <p>任务一 未做任务 <code>- + 空格 + [ ]</code></p></li><li><input checked disabled type="checkbox"> <p>任务二 已做任务 <code>- + 空格 + [x]</code></p><pre><code>效果如下：</code></pre></li></ul><p>[ ] 任务一 未做任务 - + 空格 + [ ]<br>[x] 任务二 已做任务 - + 空格 + [x]</p><h3><span id="23-缩进-换行-空行-对齐方式">2.3 缩进、换行、空行、对齐方式</span></h3><ul><li><p>首行缩进<br>不同特殊占位符所占空白是不一样大的。</p><pre><code>【1】 &amp;emsp;或&amp;#8195; //全角【2】 &amp;ensp;或&amp;#8194; //半角【3】 &amp;nbsp;或&amp;#160;  //半角之半角</code></pre></li><li><p>换行<br>由于markdown编辑器的不同,可能在一行字后面，直接换行回车，也能实现换行，但是在Visual Studio Code上，想要换行<strong>必须得在一行字后面空两个格子才行</strong>。</p></li></ul><p>空行<br>在编辑的时候有多少个空行(只要这一行只有回车或者space没有其他的字符就算空行)，<strong>在渲染之后，只隔着一行</strong>。</p><ul><li>对齐方式<br>代码：<pre><code>&lt;center&gt;行中心对齐&lt;/center&gt;&lt;p align=&quot;left&quot;&gt;行左对齐&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;行右对齐&lt;/p&gt;</code></pre>显示效果：</li></ul><center>行中心对齐</center><p align="left">行左对齐</p><p align="right">行右对齐</p><h3><span id="24-斜体-粗体-删除线-下划线-背景高亮">2.4 斜体、粗体、删除线、下划线、背景高亮</span></h3><p>代码：</p><pre><code>*斜体*或_斜体_**粗体*****加粗斜体***~~删除线~~++下划线++==背景高亮==     有一些不支持</code></pre><p>显示效果：<br>  <em>斜体</em>   <strong>粗体</strong>   <strong><em>加粗斜体</em></strong>   <del>删除线</del>   ++删除线++   ==背景高亮==</p><h3><span id="25-超链接-页内链接-自动链接-注脚">2.5 超链接、页内链接、自动链接、注脚</span></h3><ul><li>行内式<br>语法说明：</li></ul><p>[]里写链接文字，()里写链接地址, ()中的””中可以为链接指定title属性，title属性可加可不加。title属性的效果是鼠标悬停在链接上会出现指定的 title文字，链接地址与title前有一个空格。</p><p>代码：</p><pre><code>欢迎阅读 [择势勤](https://www.jianshu.com/u/16d77399d3a7 &quot;择势勤&quot;)</code></pre><p>显示效果：</p><p>欢迎阅读 <a href="https://www.jianshu.com/u/16d77399d3a7" target="_blank" rel="noopener" title="择势勤">择势勤</a></p><ul><li>参考式<br>参考式超链接一般用在学术论文上面，或者另一种情况，如果某一个链接在文章中多处使用，那么使用引用 的方式创建链接将非常好，它可以让你对链接进行统一的管理。</li></ul><p>语法说明：<br>参考式链接分为两部分，文中的写法 [链接文字][链接标记]，在文本的任意位置添加[链接标记]:链接地址。</p><p>如果链接文字本身可以做为链接标记，你也可以写成[链接文字][]<br>[链接文字]：链接地址的形式，见代码的最后一行。</p><p>代码：</p><pre><code>我经常去的几个网站[Google][1]、[Leanote][2]。[1]:http://www.google.com [2]:http://www.leanote.com</code></pre><p>显示效果：<br>我经常去的几个网站<a href="http://www.google.com" target="_blank" rel="noopener">Google</a>、<a href="http://www.leanote.com" target="_blank" rel="noopener">Leanote</a>。</p><ul><li>注脚<br>语法说明：</li></ul><p>在需要添加注脚的文字后加上脚注名字[^注脚名字],称为加注。 然后在文本的任意位置(一般在最后)添加脚注，脚注前必须有对应的脚注名字。</p><p>注意：经测试注脚与注脚之间必须空一行，不然会失效。成功后会发现，即使你没有把注脚写在文末，经Markdown转换后，也会自动归类到文章的最后。</p><p>代码：</p><pre><code>使用 Markdown[^1]可以效率的书写文档, 直接转换成 HTML[^2]。[^1]:Markdown是一种纯文本标记语言[^2]:HyperText Markup Language 超文本标记语言</code></pre><p>显示效果：</p><p>使用 Markdown<a href="Markdown是一种纯文本标记语言">^1</a>可以效率的书写文档, 直接转换成 HTML[^2]。</p><p>[^2]:HyperText Markup Language 超文本标记语言</p><p>注：脚注自动被搬运到最后面，请到文章末尾查看，脚注后方的链接可以直接跳转回到加注的地方。</p><ul><li>锚点（页内超链接）<br>网页中，锚点其实就是页内超链接，也就是链接本文档内部的某些元素，实现当前页面中的跳转。比如我这里写下一个锚点，点击回到目录，就能跳转到目录。 在目录中点击这一节，就能跳过来。还有下一节的注脚。这些根本上都是用锚点来实现的，只支持在标题后插入锚点，其它地方无效。</li></ul><p>代码：</p><pre><code>## 0. 目录{#index}</code></pre><p>显示效果：</p><p>跳转到目录</p><ul><li>自动链接<br>语法说明：<br>Markdown 支持以比较简短的自动链接形式来处理网址和电子邮件信箱，只要是用&lt;&gt;包起来， Markdown 就会自动把它转成链接。一般网址的链接文字就和链接地址一样，例如：</li></ul><p>代码：</p><pre><code>&amp;lt;http://example.com/&amp;gt; &amp;emsp;&amp;emsp; &amp;lt;address@example.com&amp;gt;</code></pre><p>显示效果：</p><p>&lt;<a href="http://example.com/" target="_blank" rel="noopener">http://example.com/</a>&gt; &emsp;&emsp;<br>&lt;<a href="mailto:address@example.com">address@example.com</a>&gt;</p><h3><span id="26-无序列表-有序列表-定义型列表">2.6 无序列表、有序列表、定义型列表</span></h3><p>= 无序列表</p><p>使用 *，+，- 表示无序列表。</p><p>代码：</p><pre><code>* 无序列表项 一+ 无序列表项 二- 无序列表项 三</code></pre><p>显示效果：</p><ul><li>无序列表项 一</li></ul><ul><li>无序列表项 二</li></ul><ul><li>无序列表项 三</li></ul><p>= 有序列表</p><p>有序列表则使用数字接着一个英文句点。<br>代码：</p><pre><code>1. 有序列表项 一2. 有序列表项 二3. 有序列表项 三</code></pre><p>显示效果：</p><ol><li>有序列表项 一</li><li>有序列表项 二</li><li>有序列表项 三</li></ol><p>= 定义型列表表</p><p>语法说明：</p><p>定义型列表由名词和解释组成。一行写上定义，紧跟一行写上解释。解释的写法:紧跟一个缩进(Tab)</p><p>代码</p><pre><code>Markdown:   轻量级文本标记语言（左侧有一个可见的冒号和四个不可见的空格）</code></pre><p>显示效果：</p><dl><dt>Markdown</dt><dd>轻量级文本标记语言，可以转换成html，pdf等格式</dd></dl><h3><span id="27-插入图像">2.7 插入图像</span></h3><p>语法中图片Alt的意思是如果图片因为某些原因不能显示，就用定义的图片Alt文字来代替图片。 图片Title则和链接中的Title一样，表示鼠标悬停与图片上时出现的文字。 Alt 和 Title 都不是必须的，可以省略，但建议写上。</p><p>Markdown 语法：</p><pre><code>&lt;center&gt;  &lt;!--开始居中对齐--&gt;![GitHub set up](http://zh.mweb.im/asset/img/set-up-git.gif &quot;图片Title&quot;)格式: ![图片Alt](图片地址 &quot;图片Title&quot;)&lt;/center&gt; &lt;!--结束居中对齐--&gt;</code></pre><p>效果如下：</p><center>![GitHub set up](http://zh.mweb.im/asset/img/set-up-git.gif "图片Title")</center><h3><span id="28-多级引用">2.8 多级引用</span></h3><p>语法说明：</p><p>引用需要在被引用的文本前加上&gt;符号和空格，允许多层嵌套，也允许你偷懒只在整个段落的第一行最前面加上<code>&gt;</code>。</p><p>代码：</p><pre><code>&gt;&gt;&gt; 请问 Markdwon 怎么用？ - 小白&gt;&gt; 自己看教程！ - 愤青&gt; 教程在哪？ - 小白</code></pre><p>显示效果：</p><blockquote><blockquote><blockquote><p>请问 Markdwon 怎么用？ - 小白<br>自己看教程！ - 愤青<br>教程在哪？ - 小白</p></blockquote></blockquote></blockquote><h3><span id="29-转义字符-字体-字号-颜色">2.9 转义字符、字体、字号、颜色</span></h3><ul><li>转义字符</li></ul><p>Markdown中的转义字符为\，转义的有：</p><pre><code>\\ 反斜杠\` 反引号 \* 星号 \_ 下划线 \{\} 大括号 \[\] 中括号 \(\) 小括号  \# 井号 \+ 加号 \- 减号 \. 英文句号 \! 感叹号</code></pre><ul><li>字体、字号、颜色<br>代码：<pre><code>&lt;font face=&quot;黑体&quot;&gt;我是黑体字&lt;/font&gt;&lt;font face=&quot;微软雅黑&quot;&gt;我是微软雅黑&lt;/font&gt;&lt;font face=&quot;STCAIYUN&quot;&gt;我是华文彩云&lt;/font&gt;&lt;font color=#0099ff size=12 face=&quot;黑体&quot;&gt;黑体&lt;/font&gt;&lt;font color=gray size=5&gt;gray&lt;/font&gt;&lt;font color=#00ffff size=3&gt;null&lt;/font&gt;</code></pre>显示效果：</li></ul><p><font face="黑体">我是黑体字</font><br><font face="微软雅黑">我是微软雅黑</font><br><font face="STCAIYUN">我是华文彩云</font><br><font color="#0099ff" size="12" face="黑体">黑体</font><br><font color="gray" size="5">gray</font><br><font color="#00ffff" size="3">null</font></p><h2><span id="三-块语法讲解">三、块语法讲解</span></h2><h3><span id="31-内容目录">3.1 内容目录</span></h3><p>在段落中填写 <code>[TOC]</code> 以显示全文内容的目录结构。</p><pre><code>[TOC]</code></pre><p>效果参见最上方的目录。</p><h3><span id="32-代码块">3.2 代码块</span></h3><p>对于程序员来说这个功能是必不可少的，插入程序代码的方式有两种，一种是利用缩进(Tab), 另一种是利用”`”符号（一般在ESC键下方）包裹代码。</p><ul><li>行内式<br>代码：<pre><code>C语言里的函数 `scanf()` 怎么使用？</code></pre>显示效果：</li></ul><p>C语言里的函数 <code>scanf()</code> 怎么使用？</p><ul><li>缩进式多行代码<br>缩进 4 个空格或是 1 个制表符</li></ul><p>一个代码区块会一直持续到没有缩进的那一行（或是文件结尾）。</p><p>代码：</p><pre><code>#include &amp;lt;stdio.h&amp;gt;int main(void){    printf(&amp;#34;Hello world\n&amp;#34;);}</code></pre><ul><li>用六个`包裹多行代码<br>代码：<pre><code>、、、include &lt;stdio.h&gt;int main(void){printf(&quot;Hello world\n&quot;);}、、、</code></pre>显示效果：<pre><code>include &lt;stdio.h&gt;int main(void){printf(&quot;Hello world\n&quot;);}</code></pre></li></ul><h3><span id="33-流程图">3.3 流程图</span></h3><p>编辑自有道云笔记，代码：</p><pre><code>```graph LRA--&gt;B``````sequenceDiagramA-&gt;&gt;B: How are you?B-&gt;&gt;A: Great!```</code></pre><p>显示效果：</p><pre><code>graph LRA--&gt;B</code></pre><pre><code>sequenceDiagramA-&gt;&gt;B: How are you?B-&gt;&gt;A: Great!</code></pre><h3><span id="34-表格">3.4 表格</span></h3><p>语法说明：</p><p>不管是哪种方式，第一行为表头，第二行分隔表头和主体部分，第三行开始每一行为一个表格行。<br>列于列之间用管道符|隔开。原生方式的表格每一行的两边也要有管道符。<br>第二行还可以为不同的列指定对齐方向。默认为左对齐，在-右边加上:就右对齐。<br><code>-</code> 左对齐，<code>:-:</code>中心对齐，<code>-:</code>右对齐</p><p>表格代码：</p><pre><code>|学号|姓名|序号||-|-|-||小明明|男|5||小红|女|79||小陆|男|192|</code></pre><p>原生方式写表格：</p><center>|学号|姓名|序号||-|-|-||小明明|男|5||小红|女|79||小陆|男|192|</center><h3><span id="35-latex-公式">3.5 LaTeX 公式</span></h3><ul><li>表示行内公式<br>代码：<pre><code>质能守恒方程可以用一个很简洁的方程式 `$E = m c^2 $`来表达。</code></pre>显示效果：</li></ul><p>质能守恒方程可以用一个很简洁的方程式 <code>$E = m c^2 $</code>来表达。</p><ul><li>表示整行公式</li></ul><p>大部分的浏览器支持的</p><pre><code>$$ 公式 $$</code></pre><p>有道云笔记 使用格式，</p><pre><code>```mathE = mc^2```</code></pre><p>块级公式：</p><pre><code>```mathx = \dfrac{-b \pm \sqrt{b^2 - 4ac}}{2a} ``````math[\frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} =1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}}{1+\frac{e^{-8\pi}} {1+\ldots} } } }]```</code></pre><p>显示效果：</p><pre><code class="math">x = \dfrac{-b \pm \sqrt{b^2 - 4ac}}{2a} </code></pre><pre><code class="math">[\frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} =1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}}{1+\frac{e^{-8\pi}} {1+\ldots} } } }]</code></pre><p>访问 <a href="https://links.jianshu.com/go?to=https%3A%2F%2Fmath.meta.stackexchange.com%2Fquestions%2F5020%2Fmathjax-basic-tutorial-and-quick-reference" target="_blank" rel="noopener">MathJax</a> 参考更多使用方法。</p><h3><span id="36-分隔线">3.6 分隔线</span></h3><p>你可以在一行中用三个以上的星号、减号、底线来建立一个分隔线，行内不能有其他东西。你也可以在星号或是减号中间插入空格。下面每种写法都可以建立分隔线：</p><p>代码：</p><pre><code>* * *********- - ------------</code></pre><p>显示效果都一样：</p><p>+++</p><h3><span id="37-html-原始码">3.7 HTML 原始码</span></h3><p>在代码区块里面， &amp; 、 &lt; 和 &gt; 会自动转成 HTML 实体，这样的方式让你非常容易使用 Markdown 插入范例用的 HTML 原始码，只需要复制贴上，剩下的 Markdown 都会帮你处理，例如：</p><p>代码：</p><pre><code>第一个例子：&lt;div class=&quot;footer&quot;&gt;© 2004 Foo Corporation&lt;/div&gt;第二个例子：&lt;center&gt;&lt;table&gt;&lt;tr&gt;&lt;th rowspan=&quot;2&quot;&gt;值班人员&lt;/th&gt;&lt;th&gt;星期一&lt;/th&gt;&lt;th&gt;星期二&lt;/th&gt;&lt;th&gt;星期三&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;李强&lt;/td&gt;&lt;td&gt;张明&lt;/td&gt;&lt;td&gt;王平&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/center&gt;</code></pre><p>显示效果：</p><p>第一个例子：</p><div class="footer">© 2004 Foo Corporation</div><p>第二个例子：</p><center><table><tr><th rowspan="2">值班人员</th><th>星期一</th><th>星期二</th><th>星期三</th></tr><tr><td>李强</td><td>张明</td><td>王平</td></tr></table></center><h3><span id="38-特殊字">3.8 特殊字</span></h3><center>特殊字符    描述    字符的代码空格符    &nbsp;<    小于号    &lt;>    大于号    &gt;&    和号    &amp;￥    人民币    &yen;©    版权    &copy;®    注册商标    &reg;°C    摄氏度    &deg;C±    正负号    &plusmn;×    乘号    &times;÷    除号    &divide;²    平方（上标²）    &sup2;³    立方（上标³）    &sup3;</center><p>文章转载自简书<br>版权归属 ©2019 择势勤<br>作者：择势勤<br>链接：<a href="https://www.jianshu.com/p/ebe52d2d468f#fnref1" target="_blank" rel="noopener">https://www.jianshu.com/p/ebe52d2d468f#fnref1</a></p><p>还有其他的，顺便也记一下</p><p>其他支持的 HTML 元素有：</p><pre><code>&lt;kbd&gt;&lt;/kbd&gt;&lt;b&gt;&lt;/b&gt;&lt;i&gt;&lt;/i&gt; &lt;em&gt;&lt;/em&gt; &lt;sup&gt;&lt;/sup&gt;  &lt;sub&gt;&lt;/sub&gt;  &lt;br /&gt;etc.</code></pre><p>横向流程图源码格式：</p><pre><code class="mermaid">graph LRA[方形] --&gt;B(圆角)    B --&gt; C{条件a}    C --&gt;|a=1| D[结果1]    C --&gt;|a=2| E[结果2]    F[横向流程图]</code></pre><p>竖向流程图源码格式：</p><pre><code class="mermaid">graph TDA[方形] --&gt; B(圆角)    B --&gt; C{条件a}    C --&gt; |a=1| D[结果1]    C --&gt; |a=2| E[结果2]    F[竖向流程图]</code></pre><p>标准流程图源码格式：</p><pre><code class="flow">st=&gt;start: 开始框op=&gt;operation: 处理框cond=&gt;condition: 判断框(是或否?)sub1=&gt;subroutine: 子流程io=&gt;inputoutput: 输入输出框e=&gt;end: 结束框st-&gt;op-&gt;condcond(yes)-&gt;io-&gt;econd(no)-&gt;sub1(right)-&gt;op</code></pre><p>标准流程图源码格式（横向）：</p><pre><code class="flow">st=&gt;start: 开始框op=&gt;operation: 处理框cond=&gt;condition: 判断框(是或否?)sub1=&gt;subroutine: 子流程io=&gt;inputoutput: 输入输出框e=&gt;end: 结束框st(right)-&gt;op(right)-&gt;condcond(yes)-&gt;io(bottom)-&gt;econd(no)-&gt;sub1(right)-&gt;op</code></pre><p>UML时序图源码样例：</p><pre><code class="sequence">对象A-&gt;对象B: 对象B你好吗?（请求）Note right of 对象B: 对象B的描述Note left of 对象A: 对象A的描述(提示)对象B--&gt;对象A: 我很好(响应)对象A-&gt;对象B: 你真的好吗？</code></pre><p>UML时序图源码复杂样例：</p><pre><code class="sequence">Title: 标题：复杂使用对象A-&gt;对象B: 对象B你好吗?（请求）Note right of 对象B: 对象B的描述Note left of 对象A: 对象A的描述(提示)对象B--&gt;对象A: 我很好(响应)对象B-&gt;小三: 你好吗小三--&gt;&gt;对象A: 对象B找我了对象A-&gt;对象B: 你真的好吗？Note over 小三,对象B: 我们是朋友participant CNote right of C: 没人陪我玩</code></pre><p>UML标准时序图样例：</p><pre><code class="mermaid">%% 时序图例子,-&gt; 直线，--&gt;虚线，-&gt;&gt;实线箭头  sequenceDiagram    participant 张三    participant 李四    张三-&gt;王五: 王五你好吗？    loop 健康检查        王五-&gt;王五: 与疾病战斗    end    Note right of 王五: 合理 食物 &lt;br/&gt;看医生...    李四--&gt;&gt;张三: 很好!    王五-&gt;李四: 你怎么样?    李四--&gt;王五: 很好!</code></pre><p>甘特图样例：</p><pre><code class="mermaid">%% 语法示例        gantt        dateFormat  YYYY-MM-DD        title 软件开发甘特图        section 设计        需求                      :done,    des1, 2014-01-06,2014-01-08        原型                      :active,  des2, 2014-01-09, 3d        UI设计                     :         des3, after des2, 5d    未来任务                     :         des4, after des3, 5d        section 开发        学习准备理解需求                      :crit, done, 2014-01-06,24h        设计框架                             :crit, done, after des2, 2d        开发                                 :crit, active, 3d        未来任务                              :crit, 5d        耍                                   :2d        section 测试        功能测试                              :active, a1, after des3, 3d        压力测试                               :after a1  , 20h        测试报告                               : 48h</code></pre><p><a href="http://markdown.xiaoshujiang.com/" target="_blank" rel="noopener">小书匠</a>中支持的语法非常多，可以去试用下哈</p><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="笔记" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Markdown" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/Markdown/"/>
    
    
      <category term="Markdown" scheme="https://wallleap.cn/tags/Markdown/"/>
    
  </entry>
  
  <entry>
    <title>使用Markdown Here对微信公众号文章进行排版</title>
    <link href="https://wallleap.cn/2020/03/27/wechatpaper/"/>
    <id>https://wallleap.cn/2020/03/27/wechatpaper/</id>
    <published>2020-03-27T14:36:35.000Z</published>
    <updated>2025-01-24T09:22:15.356Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>今天讲下微信公众号图文排版</p><h2><span id="0-图文基本排版">0、图文基本排版</span></h2><p>具体的大家可以自己去搜索一下，我在这推荐一个知乎里的问题，回答挺多的：</p><p>如何排版微信公众平台的文章？<a href="https://www.zhihu.com/question/23640203" target="_blank" rel="noopener">https://www.zhihu.com/question/23640203</a></p><p>下面的是截取自这些回答里的一些</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/wechat.png" alt="image-20200327200656044"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/WeChat2.png" alt="image-20200327200748051"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/WeChat3.png" alt="image-20200327200923050"></p><hr><p>当然啦，今天主要不是要讲这个</p><p>而是要讲【如何利用Markdown Here来进行图文排版】</p><h2><span id="1-安装markdown-here插件">1、安装Markdown Here插件</span></h2><p>首先我们前往Markdown Here官网：</p><p><a href="https://markdown-here.com/" target="_blank" rel="noopener">https://markdown-here.com/</a></p><p>两个链接随意点一个</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/WeChat4.png" alt="image-20200327201922712"></p><p>支持这些浏览器</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/WeChat5.png" alt="image-20200327202055329"></p><p>可以直接点击上面五个中对应浏览器前往应用商店下载，也可以点击最后一个去GitHub下载</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/WeChat7.png" alt="image-20200327202527773"></p><p>我使用的是Chrome，因此点击下方这个压缩包下载</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/WeChat8.png" alt="image-20200327202636890"></p><p>下载完成之后，解压该压缩包，注意选择解压到chrome或者自己新建一个文件夹，把解压出来的三个文件夹和一个json文件剪切进去</p><p>接着打开Chrome，进入扩展程序页面，开启开发者模式，加载已解压的压缩程序</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/WeChat9.png" alt="image-20200327204201350"></p><p>但是还是推荐到谷歌商店下载</p><p>若你想访问谷歌，可以使用这个插件 <a href="https://github.com/haotian-wang/google-access-helper" target="_blank" rel="noopener">https://github.com/haotian-wang/google-access-helper</a></p><h2><span id="2-修改css渲染">2、修改CSS渲染</span></h2><p>在标签栏的下方找到Markdown Here的图标</p><p>右击它，点击选项</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/WeChat10.png" alt="image-20200327204726919"></p><p>之后你会来到这个页面</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/WeChat11.png" alt="image-20200327204858341"></p><p>我们暂时需要修改的只有基本渲染CSS</p><p>而且修改的也不是所有的内容</p><h3><span id="修改所有的字体">修改所有的字体</span></h3><pre><code class="css">/* This is the overall wrapper, it should be treated as the `body` section. */.markdown-here-wrapper {   font-size: 14px;   color: #595959;   padding: 16px;}</code></pre><p>注释中说这个相当于body标签，而我们的内容基本上都放在这里面，因此可以修改大部分统一的样式</p><p>这里将字体修改为14像素</p><p>字体颜色设置为#595959，也就是稍微灰一点</p><p>向内缩进16像素</p><h3><span id="预格式文本及代码">预格式文本及代码</span></h3><pre><code class="css">pre, code {  font-size: 0.85em;  font-family: Consolas, Inconsolata, Courier, monospace;  color:#fff;}code {  margin: 0 0.15em;  padding: 0 0.3em;  white-space: pre-wrap;  border: 1px solid #EAEAEA;  background-color: black;  border-radius: 3px;  display: inline; /* added to fix Yahoo block display of inline code */}pre {  font-size: 1em;  line-height: 1.2em;}pre code {  white-space: pre;  overflow: auto; /* fixes issue #70: Firefox/Thunderbird: Code blocks with horizontal scroll would have bad background colour */  border-radius: 3px;  border: 1px solid #CCC;  padding: 0.5em 0.7em;  display: block !important; /* added to counteract the Yahoo-specific `code` rule; without this, code blocks in Blogger are broken */}/* In edit mode, Wordpress uses a `* { font: ...;} rule+style that makes highlightedcode look non-monospace. This rule will override it. */.markdown-here-wrapper[data-md-url*=&quot;wordpress.&quot;] code span {  font: inherit;}/* Wordpress adds a grey background to `pre` elements that doesn&#39;t go well withour syntax highlighting. */.markdown-here-wrapper[data-md-url*=&quot;wordpress.&quot;] pre {  background-color: transparent;}</code></pre><h3><span id="段落">段落</span></h3><pre><code class="css">/* This spacing has been tweaked to closely match Gmail+Chrome &quot;paragraph&quot; (two line breaks) spacing.Note that we only use a top margin and not a bottom margin -- this prevents the&quot;blank line&quot; look at the top of the email (issue #243).*/p {  /* !important is needed here because Hotmail/Outlook.com uses !important to     kill the margin in &lt;p&gt;. We need this to win. */  margin: 0 0 1.2em 0 !important;}</code></pre><p>每个段落在下方加外边距1.2个文字大小，即1.2*14px</p><h3><span id="表格-引用-列表等">表格、引用、列表等</span></h3><pre><code class="css">table, pre, dl, blockquote, q, ul, ol {  margin: 1.2em 0;}ul, ol {  padding-left: 2em;}li {  margin: 0.5em 0;}/* Space paragraphs in a list the same as the list itself. */li p {  /* Needs !important to override rule above. */  margin: 0.5em 0 !important;}/* Smaller spacing for sub-lists */ul ul, ul ol, ol ul, ol ol {  margin: 0;  padding-left: 1em;}/* Use Roman numerals for sub-ordered-lists. (Like Github.) */ol ol, ul ol {  list-style-type: lower-roman;}/* Use letters for sub-sub-ordered lists. (Like Github.) */ul ul ol, ul ol ol, ol ul ol, ol ol ol {  list-style-type: lower-alpha;}dl {  padding: 0;}dl dt {  font-size: 1em;  font-weight: bold;  font-style: italic;}dl dd {  margin: 0 0 1em;  padding: 0 1em;}blockquote, q {  border-left: 4px solid #42b983;  border-right: 4px solid #42b983;  padding: 0 1em;  background-color: #ecf8f2;  color: #888;  quotes: none;}/*blockquote::before, q::before {  content: none;}blockquote::after,  q::after{  position: relative;    content: &#39;\f10e&#39; !important;    font-size: 1rem;    float: right;    top: -1rem;    margin-right: -10px;    color: red;    font-family: FontAwesome;}  // emmmm，本来想加引号的，但是不支持*/table {  padding: 0;  border-collapse: collapse;  border-spacing: 0;  font-size: 1em;  font: inherit;  border: 0;}tbody {  margin: 0;  padding: 0;  border: 0;}table tr {  border: 0;  border-top: 1px solid #CCC;  background-color: white;  margin: 0;  padding: 0;}table tr:nth-child(2n) {  background-color: #F8F8F8;}table tr th, table tr td {  font-size: 1em;  border: 1px solid #CCC;  margin: 0;  padding: 0.5em 1em;}table tr th { font-weight: bold;  color: white;  background-color: #009688;}</code></pre><h3><span id="标题">标题</span></h3><pre><code class="css">h1, h2, h3, h4, h5, h6 {  text-align: center;  padding: 0;  font-weight: bold;  color: #000;  margin-bottom: 50px;}h1 {  margin:0 auto;  width: 500px;  line-height:36px;  font-size: 18px;  border: 2px solid #57d9ff;  background-color: #CCFFFF;  border-radius: 8px; }h2 {  font-size: 16px;  border-bottom: 5px solid #a9d3d6;}h3 {  width: 30%;  position: relative;    left:33%;    font-size: 15px;  border-bottom: 2px solid #FFFF66;  box-shadow: 10px 10px 5px #888888;}h4 {  font-size: 1.2em;  display: inline-block;  color: #333333;}h5 {  font-size: 1em;}h6 {  font-size: 14px;  color: #FFCC99;  margin-bottom: 10px;}</code></pre><p>一级标题应该不会在文中用到，主要是二级标题和三级标题</p><p>三级标题可以稍微改宽一点</p><p>六级标题我用来写最低下的”历史文章”四个字了</p><h3><span id="链接">链接</span></h3><pre><code class="css">/*a {  color: #7cc7ff; }a:hover{  color: orange;}*/a{  display: block;  margin: 0 auto;  text-align: center;  font-size: 14px;  color: #fff;  width: 300px;  line-height: 20px;  background-color: #FFCCCC;  box-shadow: 1px 1px 5px #888888;  border-radius: 10px;}a:hover {  color: red;} </code></pre><p>由于微信不让插入其他的链接，只能插已经发布的图文，因此直接改成了历史文章链接样式了</p><h3><span id="图片">图片</span></h3><pre><code class="css">img {  display: block;  margin: 0 auto;  height:auto;  max-width:100%;  border-radius:5px;  padding:2px;  border:2px solid #f3f3f3;  box-shadow: 0px 0px 10px #888888;}</code></pre><h3><span id="其他">其他</span></h3><pre><code class="css">/* 这个是分隔符 */hr {    width:30%;    height:1px;    border:2px;    background:#efefef;    margin:20px auto;}/* 这是强调的 */strong {  font-weight: bold;  color: #990033;}</code></pre><p>如果你想把其他的也给设置好自己的样式，可以把所有的标签都写一遍，然后F12查看标签，接着给该标签设置样式就行啦</p><h2><span id="3-使用演示">3、使用演示</span></h2><h3><span id="标题">标题</span></h3><p>一级标题，比如说你想把文章的标题再写一遍</p><pre><code class="markdown"># 使用Markdown Here给微信公众号图文排版</code></pre><h1><span id><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/image-20200327223934241.png" alt="image-20200327223934241"></span></h1><p>二级标题，用于设置文章下的大标题</p><pre><code class="markdown">## 我是二级标题</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/image-20200327224005387.png" alt="image-20200327224005387"></p><p>三级标题，大标题下的子标题</p><pre><code class="markdown">### 我是三级标题</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/image-20200327224028548.png" alt="image-20200327224028548"></p><p>如果觉得两层标题不够还可以把四级标题设置好样式</p><h3><span id="表格">表格</span></h3><pre><code class="markdown">| 表头1 | 表头二  | 表头三 || ----- | :--: | -----: || AAA   | 111  |  一一一 || BBB   | 222  |  二二二 || CCC   | 333  |  三三三 |</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/image-20200327224056078.png" alt="image-20200327224056078"></p><h3><span id="引用">引用</span></h3><pre><code class="markdown">&gt; 用大于号&gt; 这是引用的内容</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/image-20200327224130129.png" alt="image-20200327224130129"></p><h3><span id="列表">列表</span></h3><pre><code class="markdown">- 无序列表- 无序列表   可以使用- + *1.有序列表2.有序列表</code></pre><ul><li>无序列表</li><li>无序列表</li></ul><p>1.有序列表<br>2.有序列表</p><h3><span id="图片">图片</span></h3><pre><code class="markdown">![](图片链接)</code></pre><h3><span id="分隔符">分隔符</span></h3><pre><code class="markdown">---+++***</code></pre><hr><p>只写了一个</p><h3><span id="强调">强调</span></h3><pre><code class="markdown">**这是强调的内容**</code></pre><p>你知道吗<strong>这是强调的内容</strong>，哈哈</p><h3><span id="代码">代码</span></h3><pre><code class="markdown">`单行代码`​```代码语言代码块```</code></pre><h3><span id="历史文章">历史文章</span></h3><p>​<code>###### 历史文章</code></p><p><code>[](文章链接)</code></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/WeChat12.png" alt="image-20200327224130130"></p><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="笔记" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="微信" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/%E5%BE%AE%E4%BF%A1/"/>
    
    
      <category term="微信" scheme="https://wallleap.cn/tags/%E5%BE%AE%E4%BF%A1/"/>
    
  </entry>
  
  <entry>
    <title>HTML学习笔记</title>
    <link href="https://wallleap.cn/2020/03/27/html/"/>
    <id>https://wallleap.cn/2020/03/27/html/</id>
    <published>2020-03-27T09:33:14.000Z</published>
    <updated>2025-01-24T09:22:15.352Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2><span id="一-html简介">一、HTML简介</span></h2><h3><span id="1-html">1、HTML</span></h3><p>全称为超文本标记语言(Hypetext Markup Language)</p><ul><li><p>其中<font color="orange">超文本</font>是相对纯文本来说的，HTML除了纯文本内容(文字、字符)还可以包括图片、音频、视频</p></li><li><p>负责网页三要素中的结构</p></li><li><p>使用<font color="orange">标签(记)</font>的形式来标识网页中的不同组成部分 &lt;标签名&gt;</p><p>开始标签</p><h1><span id="结束标签"> 结束标签</span></h1><p></p></li><li><p>超文本标识——超链接</p></li></ul><h3><span id="2-网页标准格式">2、网页标准格式</span></h3><p>一个简单的网页可以只有文本内容，也就是内容只写入纯文本，文件名后缀为<code>.html</code>或<code>.htm</code>，浏览器将会把文本内容自动加到<code>body</code>标签中，基本内容如下</p><ul><li><p><code>&lt;html&gt;&lt;/html&gt;</code> 根标签 有且只有一个 网页内容写到里面</p></li><li><p>html两个子标签：</p><ul><li><p><code>&lt;head&gt;&lt;/head&gt;</code> 设置网页头部信息 不会在网页中直接显示 给浏览器看 解析网页</p></li><li><p><code>&lt;body&gt;&lt;/body&gt;</code> 网页主体 页面中可见部分写在这</p></li><li><p>head子标签：</p><ul><li><code>&lt;title&gt;&lt;/title&gt;</code>网页标题 将在浏览器标签栏显示</li></ul></li></ul></li></ul><p>标签关系——父标签、子标签、兄弟标签、祖先标签、后代标签</p><pre><code class="html">&lt;html&gt;   &lt;head&gt;      &lt;title&gt;&lt;/title&gt;   &lt;/head&gt;   &lt;body&gt;     文本内容在这   &lt;/body&gt;&lt;/html&gt;</code></pre><h3><span id="3-发展">3、发展</span></h3><ul><li>1993年6月：HTML第一个版本发布</li><li>1995年11月：HTML2.0</li><li>1997年1月：HTML3.2(W3C推荐)</li><li>1999年12月：HTML4.01(W3C推荐)</li><li>2000年底：XHTML1.0(W3C推荐)</li><li>2014年10月：HTML5(W3C推荐)</li></ul><p>html xml xhtml html5</p><h2><span id="二-html5标准">二、HTML5标准</span></h2><p>（1）Doctype 使用版本</p><blockquote><!DOCTYPE><p> 声明必须是 HTML 文档的第一行，位于 <html> 标签之前。</html></p><!DOCTYPE><p> 声明不是 HTML 标签；它是指示 web 浏览器关于页面使用哪个 HTML 版本进行编写的指令。</p><p>在 HTML 4.01 中，&lt;!DOCTYPE&gt; 声明引用 DTD，因为 HTML 4.01 基于 SGML。DTD 规定了标记语言的规则，这样浏览器才能正确地呈现内容。</p><p>HTML5 不基于 SGML，所以不需要引用 DTD。</p><p><strong>提示：</strong>请始终向 HTML 文档添加 &lt;!DOCTYPE&gt; 声明，这样浏览器才能获知文档类型。</p></blockquote><ul><li><p>HTML4</p><ul><li><p>过渡版</p><pre><code class="HTML">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot;&quot;http://www.w3.org/TR/html4/loose.dtd&quot;&gt;</code></pre></li></ul></li></ul><ul><li><p>严格版</p><pre><code class="html">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot;&quot;http://www.w3.org/TR/html4/strict.dtd&quot;&gt;</code></pre></li></ul><ul><li><p>框架版</p><pre><code class="html">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot;&quot;http://www.w3.org/TR/html4/frameset.dtd&quot;&gt;</code></pre></li></ul><ul><li><p>XHTML</p><pre><code class="HTML">&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;&quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;</code></pre></li></ul><ul><li><p>HTML5</p><p>html4.01和xhtml的文档声明这么麻烦，还好我们只要使用HTML5就OK了</p><pre><code class="html">&lt;!DOCTYPE html&gt;</code></pre><p>不写文档声明，有些浏览器会进入<strong>怪异模式</strong>(解析网页是出现异常)</p></li></ul><p>（2）一个最基本的HTML5页面</p><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt; &lt;!-- en英文、zh中文 --&gt;&lt;head&gt;  &lt;meta charset=&quot;UTF-8&quot;&gt;  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;  &lt;title&gt;网页标题&lt;/title&gt;&lt;/head&gt;&lt;body&gt;  &lt;h1&gt;网页正文&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><ul><li>Doctype 文档声明，必须放在html标签前面，且之前不能放置内容，即使是注释</li><li>html、head、body、title 根标签、头部标签、主体标签、网页标题，html中的lang属性规定元素内容的语言，当然，这个属性其他标签也有</li><li>meta元素可提供有关页面的元信息（meta-information），比如针对搜索引擎和更新频度的描述和关键词，这里charset设定的字符集为UTF-8，viewport主要针对移动端</li></ul><h2><span id="三-语法规范">三、语法规范</span></h2><ul><li><p>HTML中不区分大小写，一般使用小写</p></li><li><p>注释不能嵌套</p></li><li><p>标签必须结构完整，要么成对出现，要么自结束标签(浏览器尽最大努力正确解析页面，修正不符合语法规范的内容，但是有些情况会修正错)</p></li><li><p>HTML标签可以嵌套，但是不能交叉嵌套</p></li><li><p>属性必须有值，且值必须加引号(单引号、双引号都可以)</p></li></ul><h2><span id="四-概念">四、概念</span></h2><h3><span id="1-标记">1、标记</span></h3><p>又称标签，标签注重的是语义化，即什么标签做什么事(比如一个标题那么就用h$包裹)</p><h3><span id="2-元素">2、元素</span></h3><p>一个完整的标签(包含标签括起来的内容)</p><h3><span id="3-标签分类">3、标签分类</span></h3><p>（1）</p><ul><li>单标签：又称自结束标签，例如<code>&lt;img src=&#39;../img/1.png&#39; /&gt;</code>，推荐在后面的<code>&gt;</code>之前加上<code>/</code>，将标签闭合</li><li>双标签：具有开始标签和结束标签，内容写在开始标签和结束标签之间</li></ul><p>（2）</p><ul><li>替换</li><li>非替换</li></ul><p>（3）</p><ul><li>有内容：</li><li>空元素：没有闭合的标签，例如后面讲的link、hr、br、img等标签</li></ul><p>（4）(这个需要掌握，一般说HTML标签，就按这个分)</p><ul><li>行内元素：b、big、i、small、tt、abbr、acronym、cite、code、dfn、em、kbd、strong、samp、var、a、bbdo、br、img、map、object、q、script、span、sub、sup、button、input、label、select、textarea——只占据它对应标签的边框所包含的空间</li><li>块级元素：address、article、aside、audio、blockquote、canvas、dd、div、dl、fieldset、figcaption、figure、footer、form、h1-6、header、hgroup、hr、noscript、ol、output、p、pre、section、table、tfoot、ul、video——独占一行</li><li>行内块元素：input、td</li></ul><p>这个是常用的分类(然而MDN中并没有分行块元素)，CSS中还会再提到</p><p>可以看下<a href="https://blog.csdn.net/yayedecsdn/article/details/100864500" target="_blank" rel="noopener">HTML行内元素、块状元素、行内块状元素的区别</a>标签，<a href="https://blog.csdn.net/COCOLI_BK/article/details/85553078" target="_blank" rel="noopener">HTML元素分类</a>特点</p><p>其中img标签以前一直说的是行块标签，可是现在到浏览器中查看显示的是<code>display: inline</code>，可身为内联元素又可以设置高宽(这不就是行块元素特点嘛)</p><h3><span id="4-注释">4、注释</span></h3><p>HTML文档中的注释使用<code>&lt;!--  --&gt;</code>包裹，写在这里面的内容浏览器能够识别，但是不会在页面中渲染显示出来</p><pre><code class="html">&lt;!-- 注释内容，不会在页面中显示 好的注释习惯 简单明了功能：作者：日期：--&gt;</code></pre><h3><span id="5-属性">5、属性</span></h3><p>标签的属性（开始标签中 属性名=”属性值”）</p><p>例如：</p><pre><code class="html">&lt;p align=&quot;center&quot; style=&quot;font-size: 14px;&quot;&gt;文字&lt;/p&gt;</code></pre><ul><li><p><code>p</code>就是标签名，这是一个段落标签</p></li><li><p><code>align</code>、<code>style</code>就是这个标签的属性名</p></li><li><p>属性名等号后的就是属性值，属性值需要用引号引起来，属性名和属性值一般成对出现(如果属性值为BOOL类型，有些可以省略属性值，只写属性名)</p></li><li><p>lang属性</p></li><li><p>title属性</p></li></ul><h3><span id="6-路径">6、路径</span></h3><p>绝对路径：相对于根目录</p><p><strong>相对路径：</strong>相对于当前资源所在目录的位置</p><p>../ 上层路径</p><p>./ 当前路径</p><p>/ 下层</p><p>eg：</p><blockquote><ul><li>/<ul><li>img<ul><li>picture1.webp</li></ul></li><li>css<ul><li>base.css</li><li>index.css</li></ul></li><li>js<ul><li>script.js</li></ul></li><li>html<ul><li>blog.html</li></ul></li><li>favicon.ico</li><li>index.html</li></ul></li></ul></blockquote><ul><li><code>index.html</code>中引用<code>script.js</code>——<code>./js/script.js</code>或者<code>js/script.js</code></li></ul><ul><li><code>index.html</code>中引用<code>favicon.ico</code>——<code>/favicon.ico</code>，一般都是放在根目录，引用的时候使用绝对目录</li></ul><ul><li><code>blog.html</code>中引用<code>picture1.webp</code>——<code>../img/picture1.webp</code></li></ul><h3><span id="7-进制">7、进制</span></h3><p>满几进一</p><ul><li>二进制：满二进一，由0、1组成，例如<code>11111110</code>这八位表示2^0^ *0+2^1^ *1+2^2^+2^3^+2^4^+2^5^+2^6^+2^7^，即十进制254</li><li>八进制：满八进一，由0~7组成，例如<code>O12</code>表示十进制10(8^0^<em>2+8^1^</em>1)</li><li>十进制：满十进一，由0~9组成</li><li>十六进制：由<code>0~9、A~F</code>组成，例如<code>oX1e</code>表示十进制30</li></ul><h3><span id="8-单位">8、单位</span></h3><ul><li>1，看情况，有的是1=100%，有的1指的是1px</li><li>% 百分比，相同百分比参照物不同，得到的结果不同</li><li>px 像素，有物理像素、CSS像素、设备像素</li><li>vw/vh 1vw等于视口(PC端浏览器可视区域/移动端涉及3个)宽/高度的1%</li><li>em  1em为一个字体大小</li><li>rem  根标签的字体大小</li></ul><h2><span id="五-标签">五、标签</span></h2><h3><span id="1-根标签html">1、根标签<code>html</code></span></h3><p>一个HTML文档的根（顶级元素），所以它也被称为<em>根元素</em>。所有其他元素必须是此元素的后代。</p><p>通过<code>lang</code>属性指定整个页面的语言</p><pre><code class="html">&lt;html lang=&quot;en&quot;&gt;  ……&lt;/html&gt;</code></pre><h3><span id="2-头标签head">2、头标签<code>head</code></span></h3><p>规定文档相关的配置信息（元数据），包括文档的标题，引用的文档样式和脚本等。</p><ul><li><code>title</code>：网页标题</li></ul><pre><code class="html">&lt;title&gt;网页标题&lt;/title&gt;</code></pre><ul><li><p><code>meta</code>： 设置网页元信息（meta-information），比如网页字符集、针对搜索引擎和更新频度的描述和关键词，是一个单标签：</p><ul><li><p>编码</p><p>乱码问题原因：计算机只认识0、1，计算机中保存的内容需要转化为二进制编码来保存，读取内容时需要转换成正确的内容—-编码、解码  编码和解码采用的字符集不同</p><p>常见字符集：ASCII、ISO-8859-1、GBK、GB2312(中文系统的默认编码)、UTF-8(万国码)</p><p>解决：在中文系统的浏览器中，默认的都是GB2312编码，更改编码Unicode，解码 head中告诉浏览采用的编码字符集</p><pre><code class="html">&lt;meta charset=”utf-8”/&gt;  #编写使用的编码、字符集一致</code></pre></li></ul></li></ul><ul><li><p>重定向</p><pre><code class="html">&lt;meta http-equiv=”refresh” conternt=”秒数;url=网址” /&gt;</code></pre></li></ul><ul><li><p>网页关键字</p><pre><code class="html">&lt;meta name=&quot;keywords&quot; content=&quot;关键字内容1,2,3&quot; /&gt;</code></pre></li></ul><ul><li><p>网页描述</p><pre><code class="html">&lt;meta name=&quot;description&quot; content=&quot;描述&quot; /&gt;</code></pre><p>搜索引擎在检索页面时，会同时检索页面中的关键字和描述，但是不会影响页面在搜索引擎中的排名</p></li><li><p>与移动开发有关</p><pre><code class="html">&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;</code></pre></li></ul><ul><li><p><code>link</code>：定义文档与外部资源的关系</p><pre><code>属性 href -- 指定需要加载的资源的地址URI media -- 媒体类型rel -- 指定链接类型,设定是指对象和链接目标的关系,可选值, link还可以用Shortcut Icon等rev -- 指定链接类型 type -- 指定所连接文档的MIME类型，css的MIME是type/css,一般使用type=&quot;text/css&quot; </code></pre><p>其中rel和href是必须要设置的</p><ul><li><p>引入CSS样式</p><pre><code class="html">&lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&gt;</code></pre></li></ul></li></ul><ul><li><p>引入网页icon图标</p><pre><code class="hmtl">&lt;link rel=&quot;shortcut icon&quot; href=&quot;/favicon.ico&quot; type=&quot;image/x-icon&quot;&gt;</code></pre></li></ul><ul><li><p>很久以前，网速非常慢，网站主要就是分享文章的，就可以标识文档开始(告知搜索引擎)、文档上一页(浏览器可以回退)、下一页(浏览器可以提前加载此页)</p><pre><code class="html">&lt;link rel=&quot;start&quot; type=&quot;text/html&quot; href=&quot;http://www.dreamdu.com/xhtml/&quot; /&gt;&lt;link rel=&quot;prev&quot; type=&quot;text/html&quot; href=&quot;http://www.dreamdu.com/xhtml/alternate/&quot; /&gt;&lt;link rel=&quot;next&quot; type=&quot;text/html&quot; href=&quot;http://www.dreamdu.com/xhtml/attribute_rel/&quot; /&gt;</code></pre><p>如果还想了解lin标签的话，自己可以到<a href="https://www.cnblogs.com/chenkg/p/5003426.html" target="_blank" rel="noopener">html中link的用法</a>去看下</p></li></ul><ul><li><code>style</code>：包含文档的样式信息或者文档的部分内容。默认情况下，该标签的样式信息通常是CSS的格式。</li></ul><h3><span id="3-文档主体标签body">3、文档主体标签<code>body</code></span></h3><p>body 元素定义文档的主体。</p><p>body 元素包含文档的所有内容（比如文本、超链接、图像、表格和列表等等）</p><p><code>document.body</code>属性提供了可以轻松访问文档的 body 元素的脚本。</p><h3><span id="4-脚本">4、脚本</span></h3><ul><li><code>noscript</code>：如果页面上的脚本类型不受支持或者当前在浏览器中关闭了脚本，则在 HTML<code>&lt;noscript&gt;</code>元素中定义脚本未被执行时的替代内容。</li><li><code>script</code>：HTML <code>&lt;script&gt;</code> 元素用于嵌入或引用可执行脚本。</li></ul><h2><span id="六-常用标签">六、常用标签</span></h2><h3><span id="1-标题标签">1、标题标签</span></h3><p>一共有六级 h1 h2 h3 h4 h5 h6</p><pre><code class="html">&lt;h1&gt;这是一级标题&lt;/h1&gt;</code></pre><p>不关心显示的大小 注重标签语义 h1最重要，表示网页中的主要内容</p><p>对于搜索引擎来说，h1的重要性仅次于title</p><p>建议只使用h1-h3</p><h3><span id="2-段落标签">2、段落标签</span></h3><pre><code class="html">&lt;p&gt;这是一个段落&lt;/p&gt;</code></pre><p>表示一个段落/自然段，独占一行，两段之间有间隔</p><p>在HTML中，字符之间多个空格当成一个空格解析，换行也当成空格</p><pre><code>&lt;center&gt;需要居中的元素&lt;/center&gt;</code></pre><h3><span id="3-换行">3、换行</span></h3><pre><code class="html">&lt;br /&gt;</code></pre><h3><span id="4-水平线分割线">4、水平线(分割线)</span></h3><pre><code class="html">&lt;hr /&gt;</code></pre><h3><span id="5-容器">5、容器</span></h3><p><code>div</code>表示一个盒子或容器</p><p><code>span</code>没有特定的语义，通常用于样式的应用</p><h3><span id="6-文字标记">6、文字标记</span></h3><p><strong><code>&lt;b&gt;</code></strong>：加粗</p><p><strong><code>&lt;strong&gt;</code></strong>：强调</p><p><code>b</code>和<code>strong</code>的区别就在于前者是物理元素,仅表示加粗，后者是逻辑元素，表示强调的意思，<code>&lt;b&gt;</code>是在html中的标签，而在xhtml中只能使用<code>&lt;strong&gt;</code>，后者兼容性更好。</p><p><strong><code>&lt;i&gt;</code></strong>：斜体，现在主要用于文本图标</p><p><strong><code>&lt;em&gt;</code></strong>：次级强调</p><table><thead><tr><th align="left">文字标记</th><th align="left">语义</th></tr></thead><tbody><tr><td align="left"><code>&lt;blockquote&gt;</code></td><td align="left">长引用</td></tr><tr><td align="left"><code>&lt;q&gt;</code></td><td align="left">短引用</td></tr><tr><td align="left"><code>&lt;abbr&gt;</code></td><td align="left">缩写</td></tr><tr><td align="left"><code>&lt;address&gt;</code></td><td align="left">作者联系信息</td></tr><tr><td align="left"><code>&lt;pre&gt;</code></td><td align="left">预格式化的文本,常用于程序代码</td></tr><tr><td align="left"><code>&lt;code&gt;</code></td><td align="left">定义计算机代码文本。</td></tr><tr><td align="left"><code>&lt;del&gt;</code></td><td align="left">删除的文本</td></tr><tr><td align="left"><code>&lt;sub&gt;</code></td><td align="left">上标</td></tr><tr><td align="left"><code>&lt;sup&gt;</code></td><td align="left">下标</td></tr></tbody></table><h3><span id="7-超链接">7、超链接</span></h3><p>a标签可以将包含内容设置为超链接，设置了href属性后点击内容可跳转到相应的页面</p><pre><code class="html">&lt;a href=&quot;目标地址&quot; target=&quot;_blank&quot;&gt;链接&lt;/a&gt;</code></pre><p>常用属性：</p><ul><li><p>href:</p><ul><li>url：设置为链接之后可以跳转到该页面 <code>&lt;a href=&quot;https://wallleap.cn&quot;&gt;wallleap&lt;/a&gt;</code></li><li>#id：跳转到这个id名处 <code>&lt;h1 id=&quot;title&quot;&gt;&lt;/h1&gt;</code>，如果跳转的地方是一个超链接也可设置name属性，之后点击这个链接<code>&lt;a href=&quot;#title&quot;&gt;转到title&lt;/a&gt;</code>，就能跳转到这个地方，也可以加到链接后：<code>&lt;a href=&quot;https://a.c#title&quot;&gt;跳转锚点&lt;/a&gt;</code></li><li>mailto：打开默认邮件应用发送邮件到该邮箱 <code>&lt;a href=&quot;mailto:15579576761@163.com&quot;&gt;发送邮件&lt;/a&gt;</code>，也可以设置好主题、内容：<code>&lt;a href=&quot;mailto:15579576761@163.com?subject=Re:wallleap.cn%20某文章&amp;body=发送自wallleap&quot;&gt;发送邮件&lt;/a&gt;</code></li></ul></li><li><p>target: </p><ul><li><code>_self</code>：默认</li><li><code>_blank</code>：设置为内联框架的名称，可以在内联框架中打开页面</li><li><code>_top</code>：</li></ul></li></ul><blockquote><p><strong>提示：</strong>如果不使用 href 属性，则不可以使用如下属性：download, hreflang, media, rel, target 以及 type 属性。</p><p><strong>提示：</strong>被链接页面通常显示在当前浏览器窗口中，除非您规定了另一个目标（target 属性）。</p><p><strong>提示：</strong>请使用 CSS 来设置链接的样式。</p></blockquote><h3><span id="8-图片标签">8、图片标签</span></h3><p><code>&lt;img src=&quot;路径&quot; alt=&quot;图片描述&quot; width=&quot;200px&quot; height=&quot;200px&quot; /&gt;</code></p><p>alt：图片不能显示时，对图片描述；搜索引擎根据alt搜索图片</p><p>宽度和高度只设置一个，另一个也会等比例变化，同时指定按照值变化，自适应页面不设置宽高值</p><p>图片格式：</p><ul><li><p>JEPG(JPG)支持颜色多，可压缩，不支持透明；</p></li><li><p>GIF支持颜色少，支持简单的透明，支持动态图；</p></li><li><p>PNG支持颜色多，支持复杂的透明；</p></li><li><p>webp提供了有损压缩与无损压缩（可逆压缩）的图片文件格式，</p></li><li><p>base64：当图片很小时(一般小于8kb)，或者需要先加载的图片，可以转为base64</p></li></ul><p>效果不一致，使用效果好的；效果一致，使用小的。</p><h3><span id="9-列表标记">9、列表标记</span></h3><p><strong>1）ul、ol、li</strong></p><pre><code class="html">&lt;ol&gt;  &lt;li&gt;巴西&lt;/li&gt;  &lt;li&gt;阿根廷&lt;/li&gt;  &lt;li&gt;德国&lt;/li&gt;&lt;/ol&gt;</code></pre><ol><li>巴西</li><li>阿根廷</li><li>德国</li></ol><pre><code class="html">&lt;ul&gt;  &lt;li&gt;巴西&lt;/li&gt;  &lt;li&gt;阿根廷&lt;/li&gt;  &lt;li&gt;德国&lt;/li&gt;&lt;/ul&gt;</code></pre><ul><li>巴西</li><li>阿根廷</li><li>德国</li></ul><p><strong>2）dl、dt、dd</strong></p><pre><code class="html">&lt;dl&gt;  &lt;dt&gt;计算机&lt;/dt&gt;  &lt;dd&gt;用来计算的仪器 ... ...&lt;/dd&gt;  &lt;dt&gt;显示器&lt;/dt&gt;  &lt;dd&gt;以视觉方式显示信息的装置 ... ...&lt;/dd&gt;&lt;/dl&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="http://moretrue.cn/Public/uploads/ueditor/2/573db1f4e8657.JPG" alt="捕获.JPG"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200810164728.png" alt="image-20200810164726876"></p><p><code>ul</code>:无序列表，<code>li</code>子元素显示为默认的黑色圆点，也可通过参数自定义列表的符号，常用于新闻列表展示</p><p><code>ol</code>:有序列表，可以在列表前增加序号，如1，2，3，4；适用于排行榜；</p><p><code>dl</code>:自定义列表，可以包括标题及内容，可适合用制作风箱结构；</p><h3><span id="9-表格标签">9、表格标签</span></h3><p><code>table</code>:表格，适合于超过两行以上的数据呈现</p><p>一个表格<code>&lt;table&gt;</code>是由每行<code>&lt;tr&gt;</code>组成的，每行是由列<code>&lt;td&gt;</code>组成的。</p><blockquote><p>所以我们要记住，一个表格是由行组成的（行是由列组成的），而不是由行和列组成的。（td是嵌套在tr中的）</p></blockquote><p><code>&lt;table&gt;</code>下的 <code>&lt;caption&gt;</code>标签、 <code>&lt;thead&gt;</code>标签、<code>&lt;tbody&gt;</code>标签、<code>&lt;tfoot&gt;</code>标签</p><ul><li><code>&lt;caption&gt;</code>——表格的标题，一般是<code>table</code>的第一个子元素<em>(零个或一个)</em></li><li><code>&lt;colgroup&gt;</code>——表格列组标签，用来定义表中的一组列表<em>(零个或多个)</em></li><li><code>&lt;thead&gt;</code>——表头，表格的最上面一行<em>(零个或一个)</em><ul><li><code>&lt;tr&gt;</code>定义表格中的行<ul><li><code>&lt;th&gt;</code>定义表格内的表头单元格(列)，此元素内部的文本通常会呈现为粗体。</li></ul></li></ul></li><li><code>&lt;tbody&gt;</code>——表的主体部分<em>(零个或多个)</em><ul><li><code>&lt;tr&gt;</code>行<ul><li><code>&lt;th&gt;</code>表头列(如果没有<code>&lt;thead&gt;</code>就需要写上，显示在最上方；有<code>&lt;thead&gt;</code>可以<code>tr*3&gt;th+td*2</code>这种形式，位于最左一列)</li></ul></li><li><code>&lt;tr&gt;</code>行<ul><li><code>&lt;td&gt;</code>列</li></ul></li></ul></li><li><code>&lt;tfoot&gt;</code>——汇总行<em>(零个或一个)</em></li></ul><p>注：</p><p>1、如果写了<code>thead</code>、<code>tbody</code>、<code>tfoot</code>这三个部分，<strong>代码顺序书写可以随意</strong>，浏览器显示的时候还是按照<code>thead</code>、<code>tbody</code>、<code>tfoot</code>的顺序依次来显示内容。如果不写thead、tbody、tfoot，那么浏览器解析并显示表格内容的时候是从按照代码的从上到下的顺序来显示。</p><p>2、当表格非常大内容非常多的时候，如果用<code>thead</code>、<code>tbody</code>、<code>tfoot</code>标签的话，那么<strong>数据可以边获取边显示</strong>。如果不写，则必须等表格的内容全部从服务器获取完成才能显示出来。</p><p>3、<code>tbody</code>标签是“必须的”(并不是要求一定写)，即使你在代码中并没有写，在浏览器渲染之后<code>tr</code>和<code>td</code>还是会在<code>tbody</code>中</p><p>属性：表格中的属性差不多都是不推荐使用的(可以写样式)，但由于开发的时候写属性更方便一点，因此列出来</p><p>表格<code>&lt;table&gt;</code>的属性：</p><ul><li><code>border</code>：边框，单位px。</li><li><code>style=&quot;border-collapse:collapse;&quot;</code>：单元格的线和表格的边框线合并。</li><li><code>width</code>：宽度，单位px。</li><li><code>height</code>：高度，单位px。</li><li><code>bordercolor</code>：表格的边框颜色。</li><li><code>align</code>：<strong>表格</strong>的水平对齐方式，属性值可以填：<code>left</code> 、<code>right</code>、 <code>center</code>。<br>注意：这里不是设置表格里内容的对齐方式，如果想设置内容的对齐方式，要对单元格标签<code>&lt;td&gt;</code>进行设置）</li><li><code>cellpadding</code>：单元格内容到边的距离，单位px。默认情况下，文字是紧挨着左边那条线的，即默认情况下的值为0。<br>注意不是单元格内容到四条边的距离，而是到一条边的距离，默认是与左边那条线的距离。如果设置属性<code>dir=&quot;rtl&quot;</code>(在下面有讲)，那就指的是内容到右边那条线的距离。</li><li><code>cellspacing</code>：单元格和单元格之间的距离（外边距），单位px。默认情况下的值为0。</li><li><code>bgcolor=&quot;#99cc66&quot;</code>：表格的背景颜色。</li><li><code>background=&quot;路径src/...&quot;</code>：背景图片。<br>背景图片的优先级大于背景颜色。</li><li><code>bordercolorlight</code>：表格的上、左边框，以及单元格的右、下边框的颜色。</li><li><code>bordercolordark</code>：表格的右、下边框，以及单元格的上、左的边框的颜色。<br>这两个属性的目的是为了设置3D的效果。</li><li><code>dir</code>：公有属性，单元格内容的排列方式(direction)。 可以 取值：<code>ltr</code>：从左到右（left-to-right，默认），<code>rtl</code>：从右到左（right-to-left）   既然说<code>dir</code>是共有属性，如果把这个属性放在任意标签中，那表明这个标签的位置可能会从右开始排列。</li></ul><p>行 <code>&lt;tr&gt;</code>属性：</p><ul><li><code>dir</code>：公有属性，设置这一行单元格内容的排列方式。可以取值：<code>ltr</code>：从左到右（left-to-right，默认），<code>rtl</code>：从右到左（right-to-left）</li><li><code>bgcolor</code>：设置这一行的单元格的背景色。<br>注：没有background属性，即：无法设置这一行的背景图片，如果非要设置，可以用css实现。</li><li><code>height</code>：一行的高度</li><li><code>align=&quot;center&quot;</code>：一行的内容水平居中显示，取值：<code>left</code>、<code>center</code>、<code>right</code></li><li><code>valign=&quot;center&quot;</code>：一行的内容垂直居中，取值：<code>top</code>、<code>middle</code>、<code>bottom</code></li></ul><p>单元格(列)<code>td</code>/<code>th</code>属性：</p><ul><li><code>align</code>：内容的横向对齐方式，属性值可以填：<code>left</code>、<code>center</code>、<code>right</code></li><li><code>valign</code>：内容的纵向对齐方式，属性值可以填：<code>top</code>、<code>middle</code>、<code>bottom</code></li><li><code>width</code>：宽度——绝对值或者相对值(%)</li><li><code>height</code>：单元格的高度</li><li><code>bgcolor</code>：设置这个单元格的背景色</li><li><code>background</code>：设置这个单元格的背景图片</li></ul><p><font color="red">单元格合并</font>：如果要将两个单元格合并，那肯定就要删掉一个单元格。</p><ul><li><code>colspan</code>：横向合并。例如<code>colspan=&quot;2&quot;</code>表示当前单元格在水平方向上要占据两个单元格的位置。</li><li><code>rowspan</code>：纵向合并。例如<code>rowspan=&quot;2&quot;</code>表示当前单元格在垂直方向上要占据两个单元格的位置。 </li></ul><p>下面用几个例子讲解表格(这些代码只是演示上面的知识，开发中千万别这样用/手动狗头)</p><p>例子1：表格由行<code>tr</code>组成，行由列<code>td</code>组成</p><pre><code class="html">&lt;!-- 注：table标签一定要有 --&gt;&lt;table border=&quot;1&quot;&gt; &lt;!-- 为了看的更清楚一点，加上border属性(开发中不推荐)——给表格加上边框 --&gt;  &lt;tr&gt;    &lt;td&gt;孙悟空&lt;/td&gt;&lt;td&gt;未知&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;花果山&lt;/td&gt;  &lt;!-- 由于代码有点长，这里把td写到一行 --&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;猪八戒&lt;/td&gt;&lt;td&gt;未知&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;高老庄&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;沙和尚&lt;/td&gt;&lt;td&gt;未知&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;流沙河&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;唐三藏&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;东土大唐&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;白龙马&lt;/td&gt;&lt;td&gt;未知&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;鹰愁涧&lt;/td&gt;  &lt;/tr&gt;&lt;/table&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200810160014.png" alt="image-20200810160009152"></p><p>例子2：没有加<code>thead</code>用<code>th</code>来做表头、table部分属性</p><pre><code class="html">&lt;table border=&quot;1&quot; width=&quot;400&quot; height=&quot;200&quot; bordercolor=&quot;skyblue&quot; align=&quot;center&quot; cellpadding=&quot;10px&quot; cellspacing=&quot;5&quot; dir=&quot;ltr&quot;&gt;  &lt;tr&gt;    &lt;th&gt;姓名&lt;/th&gt;&lt;th&gt;年龄&lt;/th&gt;&lt;th&gt;性别&lt;/th&gt;&lt;th&gt;地点&lt;/th&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;孙悟空&lt;/td&gt;&lt;td&gt;未知&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;花果山&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;猪八戒&lt;/td&gt;&lt;td&gt;未知&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;高老庄&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;沙和尚&lt;/td&gt;&lt;td&gt;未知&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;流沙河&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;唐三藏&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;东土大唐&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;白龙马&lt;/td&gt;&lt;td&gt;未知&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;鹰愁涧&lt;/td&gt;  &lt;/tr&gt;&lt;/table&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200810161836.png" alt="image-20200810161835381"></p><p>例子3：有<code>thead</code>且<code>tbody</code>中不设置<code>th</code>、table部分属性+tr属性</p><pre><code class="html">&lt;table border=&quot;1&quot; width=&quot;500&quot; bordercolor=&quot;skyblue&quot; style=&quot;border-collapse:collapse;&quot; dir=&quot;rtl&quot;&gt;  &lt;thead&gt;    &lt;tr height=&quot;50&quot; valign=&quot;bottom&quot;&gt;      &lt;th&gt;姓名&lt;/th&gt;&lt;th&gt;年龄&lt;/th&gt;&lt;th&gt;性别&lt;/th&gt;&lt;th&gt;地点&lt;/th&gt;    &lt;/tr&gt;  &lt;/thead&gt;  &lt;tbody&gt;    &lt;tr&gt;      &lt;td&gt;孙悟空&lt;/td&gt;&lt;td&gt;未知&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;花果山&lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;td&gt;猪八戒&lt;/td&gt;&lt;td&gt;未知&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;高老庄&lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;td&gt;沙和尚&lt;/td&gt;&lt;td&gt;未知&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;流沙河&lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;td&gt;唐三藏&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;东土大唐&lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;td&gt;白龙马&lt;/td&gt;&lt;td&gt;未知&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;鹰愁涧&lt;/td&gt;    &lt;/tr&gt;  &lt;/tbody&gt;  &lt;tfoot&gt;    &lt;tr align=&quot;center&quot;&gt;      &lt;td&gt;取经四人一马组&lt;/td&gt;&lt;td&gt;以前全都是仙佛来着&lt;/td&gt;&lt;td&gt;单身汉&lt;/td&gt;&lt;td&gt;前几集就凑齐了&lt;/td&gt; &lt;!-- 汇总行--&gt;    &lt;/tr&gt;  &lt;/tfoot&gt;&lt;/table&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200810162233.png" alt="image-20200810162232527"></p><p>例子4：加上其他几个标签，有<code>thead</code>且<code>tbody</code>中设置<code>th</code>、th/td属性</p><pre><code class="html">&lt;table border=&quot;1&quot; width=&quot;550&quot; bordercolor=&quot;skyblue&quot; style=&quot;border-collapse:collapse;&quot;&gt;  &lt;caption&gt;取经四人组&lt;/caption&gt;  &lt;colgroup&gt; &lt;!-- 就是把列分组，每组可以加个类名这样的 --&gt;    &lt;col style=&quot;background-color: bisque;&quot;&gt;    &lt;col span=&quot;3&quot; style=&quot;background-color: rosybrown;&quot;&gt;    &lt;col span=&quot;2&quot; style=&quot;background-color: silver;&quot;&gt;  &lt;/colgroup&gt;  &lt;thead&gt;    &lt;tr&gt;      &lt;th width=&quot;100&quot; height=&quot;50&quot;&gt;物种&lt;/th&gt;&lt;th&gt;姓名&lt;/th&gt;&lt;th&gt;年龄&lt;/th&gt;&lt;th&gt;性别&lt;/th&gt;&lt;th&gt;地点&lt;/th&gt;    &lt;/tr&gt;  &lt;/thead&gt;  &lt;tfoot&gt;    &lt;tr align=&quot;center&quot;&gt;      &lt;th&gt;说明&lt;/th&gt;&lt;td&gt;取经四人一马组&lt;/td&gt;&lt;td&gt;以前全都是仙佛来着&lt;/td&gt;&lt;td&gt;单身汉&lt;/td&gt;&lt;td&gt;前几集就凑齐了&lt;/td&gt; &lt;!-- 汇总行--&gt;    &lt;/tr&gt;  &lt;/tfoot&gt; &lt;!-- 只要写了这三个，顺序无所谓，仍按照thead-&gt;tbody-&gt;tfoot --&gt;  &lt;tbody&gt;    &lt;tr&gt;      &lt;th&gt;猴子&lt;/th&gt;&lt;td&gt;孙悟空&lt;/td&gt;&lt;td&gt;未知&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;花果山&lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;th&gt;猪头&lt;/th&gt;&lt;td&gt;猪八戒&lt;/td&gt;&lt;td&gt;未知&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;高老庄&lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;th&gt;河妖？&lt;/th&gt;&lt;td&gt;沙和尚&lt;/td&gt;&lt;td&gt;未知&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;流沙河&lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;th&gt;金蝉&lt;/th&gt;&lt;td&gt;唐三藏&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;东土大唐&lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;th&gt;白龙&lt;/th&gt;&lt;td&gt;白龙马&lt;/td&gt;&lt;td&gt;未知&lt;/td&gt;&lt;td&gt;男&lt;/td&gt;&lt;td&gt;鹰愁涧&lt;/td&gt;    &lt;/tr&gt;  &lt;/tbody&gt;&lt;/table&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200810163542.png" alt="image-20200810163541447"></p><p>例子5：单元格合并</p><pre><code class="html">&lt;table bgcolor=&quot;#ccc&quot; border=&quot;1&quot; width=&quot;550&quot; bordercolor=&quot;red&quot; style=&quot;border-collapse:collapse;&quot;&gt;  &lt;tbody&gt;    &lt;tr bgcolor=&quot;yellow&quot;&gt;      &lt;th&gt;姓名&lt;/th&gt;&lt;th&gt;年龄&lt;/th&gt;&lt;th&gt;性别&lt;/th&gt;&lt;th&gt;地点&lt;/th&gt;&lt;th&gt;前世工作地点&lt;/th&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;td&gt;孙悟空&lt;/td&gt;&lt;td rowspan=&quot;3&quot;&gt;未知&lt;/td&gt;&lt;td rowspan=&quot;5&quot;&gt;男&lt;/td&gt;&lt;td&gt;花果山&lt;/td&gt;&lt;td bgcolor=&quot;yellowgreen&quot; rowspan=&quot;3&quot;&gt;天庭&lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;td&gt;猪八戒&lt;/td&gt;&lt;td&gt;高老庄&lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;td&gt;沙和尚&lt;/td&gt;&lt;td&gt;流沙河&lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;td&gt;唐三藏&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;&lt;td&gt;东土大唐&lt;/td&gt;&lt;td&gt;雷音寺&lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;td&gt;白龙马&lt;/td&gt;&lt;td&gt;未知&lt;/td&gt;&lt;td&gt;鹰愁涧&lt;/td&gt;&lt;td&gt;西海&lt;/td&gt;    &lt;/tr&gt;    &lt;tr&gt;      &lt;td colspan=&quot;5&quot; align=&quot;center&quot;&gt;西天取经&lt;/td&gt;    &lt;/tr&gt;  &lt;/tbody&gt;&lt;/table&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200810164637.png" alt="image-20200810164636116"></p><p>表格布局：还是在很久以前，表格用来做页面布局，还没学到CSS，提下这个概念，知道有这个东东就行。</p><h3><span id="10-表单">10、表单</span></h3><p>1）表单标记</p><pre><code class="html">&lt;form action=&quot;表单提交的处理程序地址&quot; method=&quot;表单提交方式(post/get)&quot; name=&quot;表单名称&quot;&gt;&lt;/form&gt;</code></pre><p><code>form</code>元素将所有的表单包含起来，也相应于表单的作用域。</p><p><code>get</code>和<code>post</code>提交方式的区别：<code>get</code>请求把表单的数据显式地放在URL中，并且对长度和数据值编码有所限制；<code>post</code>请求把表单数据放在HTTP请求体中，没有长度限制。</p><p>2）文本框</p><pre><code class="html">&lt;input type=&quot;text&quot; name=&quot;控件名称&quot; value=&quot;文本框输入值&quot; placeholder=&quot;提示信息&quot; disabled readonly required auotfocus /&gt;</code></pre><p>3）密码框</p><pre><code class="php+HTML">&lt;input type=&quot;password&quot; name=&quot;控件名称&quot; placeholder=&quot;提示信息&quot;/&gt;</code></pre><p>4）单选按钮：同一组单选按钮使用同一命名</p><pre><code class="php+HTML">&lt;input type=&quot;radio&quot; name=&quot;控件名称&quot; value=&quot;控件值&quot; checked /&gt;</code></pre><p>5）复选按钮</p><pre><code class="html">&lt;input type=&quot;checkbox&quot; name=&quot;控件名称&quot; value=&quot;控件值&quot; checked /&gt;</code></pre><p>6）下拉列表</p><pre><code class="html">&lt;select name=&quot;控件名称&quot; multiple=&quot;multiple&quot; size=&quot;数值&quot;&gt;    &lt;option value=&quot;控件值&quot; selected&gt;选项&lt;/option&gt;&lt;/select&gt;</code></pre><p>7）多行文本框</p><pre><code class="html">&lt;textarea name=&quot;控件名称&quot; cols=&quot;列数&quot; rows=&quot;行数&quot;&gt;&lt;/textarea&gt;</code></pre><p>8）隐藏控件</p><pre><code class="html">&lt;input type=&quot;hidden&quot; name=&quot;控件名称&quot; value=&quot;控件值&quot; /&gt;</code></pre><p>9）普通按钮</p><pre><code class="html">&lt;input type=&quot;button&quot; value=&quot;按钮文字&quot; /&gt;&lt;button type=&quot;button&quot;&gt;按钮文字&lt;/button&gt;</code></pre><p>10）发送按钮</p><pre><code class="html">&lt;input type=&quot;submit&quot; value=&quot;按钮文字&quot; /&gt;&lt;button type=&quot;submit&quot;&gt;按钮文字&lt;/button&gt;</code></pre><p>11）重置按钮</p><pre><code class="html">&lt;input type=&quot;reset&quot; value=&quot;按钮文字&quot;&gt;&lt;button type=&quot;reset&quot;&gt;按钮文字&lt;/button&gt;</code></pre><p>表单实例：</p><pre><code class="HTML">&lt;form action=&quot;&quot; method=&quot;post&quot;&gt;  &lt;fieldset&gt;    &lt;legend&gt;表单演示&lt;/legend&gt;    &lt;p&gt; &lt;label for=&quot;username&quot;&gt;姓名：&lt;/label&gt;&lt;input type=&quot;text&quot; name=&quot;username&quot; id=&quot;username&quot; placeholder=&quot;请输入姓名&quot;        required=&quot;required&quot; /&gt; &lt;/p&gt;    &lt;p&gt; &lt;label for=&quot;pwd&quot;&gt;密码：&lt;/label&gt;&lt;input type=&quot;password&quot; name=&quot;pwd&quot; id=&quot;pwd&quot; placeholder=&quot;请输入密码&quot; /&gt; &lt;/p&gt;    &lt;p&gt; 性别： &lt;label&gt;&lt;input type=&quot;radio&quot; name=&quot;sex&quot; value=&quot;1&quot; checked=&quot;checked&quot; /&gt;男&lt;/label&gt; &lt;label&gt;&lt;input type=&quot;radio&quot;          name=&quot;sex&quot; value=&quot;0&quot; /&gt;女&lt;/label&gt; &lt;/p&gt;    &lt;p&gt; 爱好： &lt;label&gt;&lt;input type=&quot;checkbox&quot; name=&quot;basketball&quot; value=&quot;basketball&quot; /&gt;篮球&lt;/label&gt; &lt;label&gt;&lt;input          type=&quot;checkbox&quot; name=&quot;football&quot; value=&quot;football&quot; /&gt;足球&lt;/label&gt; &lt;/p&gt;    &lt;p&gt; &lt;label for=&quot;bloodtype&quot;&gt;血型：&lt;/label&gt; &lt;select name=&quot;bloodtype&quot; id=&quot;bloodtype&quot;&gt;        &lt;option value=&quot;A&quot;&gt;A型&lt;/option&gt;        &lt;option value=&quot;B&quot;&gt;B型&lt;/option&gt;        &lt;option value=&quot;AB&quot;&gt;AB型&lt;/option&gt;        &lt;option value=&quot;O&quot;&gt;O型&lt;/option&gt;      &lt;/select&gt; &lt;/p&gt;    &lt;p&gt; &lt;label for=&quot;intro&quot;&gt;介绍&lt;/label&gt; &lt;textarea name=&quot;intro&quot; cols=&quot;30&quot; rows=&quot;3&quot; id=&quot;intro&quot;&gt;&lt;/textarea&gt; &lt;/p&gt; &lt;input      type=&quot;submit&quot; value=&quot;提交按钮&quot; /&gt; &lt;input type=&quot;reset&quot; value=&quot;重置按钮&quot; /&gt; &lt;input type=&quot;button&quot; value=&quot;自定义按钮&quot; /&gt;  &lt;/fieldset&gt;&lt;/form&gt;</code></pre><h3><span id="11-转义字符字符实体">11、转义字符(字符实体)</span></h3><p>&amp;实体名字;</p><p>&lt; <code>&amp;lt;</code></p><p>&gt; <code>&amp;gt;</code></p><p>空格 <code>&amp;nbsp;</code></p><p>版权<code>&amp;copy;</code></p><p>浏览器解析到实体时，会自动将其转换为相应字符。</p><table><thead><tr><th align="center">特殊字符</th><th align="center">描述</th><th align="center">字符的代码</th></tr></thead><tbody><tr><td align="center"></td><td align="center">空格符</td><td align="center"><code>&amp;nbsp;</code></td></tr><tr><td align="center">&lt;</td><td align="center">小于号</td><td align="center"><code>&amp;lt;</code></td></tr><tr><td align="center">&gt;</td><td align="center">大于号</td><td align="center"><code>&amp;gt;</code></td></tr><tr><td align="center">&amp;</td><td align="center">和号</td><td align="center"><code>&amp;amp;</code></td></tr><tr><td align="center">￥</td><td align="center">人民币</td><td align="center"><code>&amp;yen;</code></td></tr><tr><td align="center">©</td><td align="center">版权</td><td align="center"><code>&amp;copy;</code></td></tr><tr><td align="center">®</td><td align="center">注册商标</td><td align="center"><code>&amp;reg;</code></td></tr><tr><td align="center">°</td><td align="center">摄氏度</td><td align="center"><code>&amp;deg;</code></td></tr><tr><td align="center">±</td><td align="center">正负号</td><td align="center"><code>&amp;plusmn;</code></td></tr><tr><td align="center">×</td><td align="center">乘号</td><td align="center"><code>&amp;times;</code></td></tr><tr><td align="center">÷</td><td align="center">除号</td><td align="center"><code>&amp;divide;</code></td></tr><tr><td align="center">²</td><td align="center">平方2（上标2）</td><td align="center"><code>&amp;sup2</code></td></tr><tr><td align="center">³</td><td align="center">立方3（上标3）</td><td align="center"><code>&amp;sup3</code></td></tr></tbody></table><h3><span id="12-框架网页">12、<strong>框架网页</strong></span></h3><p>框架集和内联框架的作用类似，都是用于在一个页面中引入其他的外部的页面，框架集可以同时引入多个页面，而内联框架只能引入一个。在H5标准中，推荐使用框架集，而不使用内联框架。</p><p>将浏览器窗口分解为多个小窗口，每个小窗口均可以显示各自的网页</p><p><code>&lt;frameset rows=&quot;&quot; cols=&quot;&quot;&gt;</code>：框架网页集，<code>rows</code>为横向分隔，<code>cols</code>为纵向分隔，值可以是具体数值也可以是百分比，这两属性<code>frameset</code>必须选择一个，并且需要在属性中指定每一部分所占的大小。注意<code>frameset</code>标记是和<code>body</code>标记同级的标记，不能将<code>frameset</code>标记包含在<code>body</code>标记中，否则将无法看到框架网页的效果。</p><p><code>&lt;frame name=&quot;&quot; scr=&quot;&quot; /&gt;</code>：指定每一个小窗口的名称和链接的网页，窗口的名称可以用于超级链接的target属性。</p><p><code>frameset</code>中也可以再嵌套<code>frameset</code></p><p>水平分隔两个窗口，每个窗口各占50%</p><p>可引入一个外部的页面</p><pre><code class="html">&lt;iframe src=”路径” width=”” heigh=”” name=”名称”&gt;&lt;/iframe&gt;</code></pre><p><code>frameset</code>和<code>iframe</code>一样，它里面的内容都不会被搜索引擎所检索，所以如果搜索引擎检索到的页面是一个框架页的话，它是不能去判断里边的内容的.</p><p>内嵌框架<code>&lt;iframe&gt;</code>：可以在一个浏览器窗口种同时显式多个页面文档</p><p>在现实开发中，不推荐使用，使用框架集意味着页面中不能有自己的内容，只能引入其他的页面，而我们每单独加载一个页面，浏览器都需要重新发送一次请求，引入几个页面就需要发送几次请求，用户的体验比较差，如果非得用建议使用<code>framese</code>t而不是<code>iframe</code></p><pre><code class="html">&lt;frameset rows=”20%, *,30%”&gt;  &lt;frame src=&quot;header.html&quot; /&gt;  &lt;frameset cols=&quot;20%,40%,40%&quot;&gt;    &lt;frame src=&quot;body1.html&quot;&gt;    &lt;frame src=&quot;body2.html&quot;&gt;    &lt;frame src=&quot;body3.html&quot;&gt;  &lt;/frameset&gt;  &lt;frame src=&quot;footer.html&quot;&gt;&lt;/frameset&gt;</code></pre><h2><span id="七-html5新增标签">七、HTML5新增标签</span></h2><h3><span id="1-新增语义化标签">1、新增语义化标签</span></h3><p>以前布局，我们基本用div来做，但是div对于搜索引擎来说，是没有语义的。</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200810172732.png" alt="image-20200810172730433"></p><table><thead><tr><th align="center">标签名</th><th align="center">作用</th></tr></thead><tbody><tr><td align="center">header</td><td align="center">表示页眉</td></tr><tr><td align="center">nav</td><td align="center">表示导航</td></tr><tr><td align="center">article</td><td align="center">文章</td></tr><tr><td align="center">section</td><td align="center">章节</td></tr><tr><td align="center">main</td><td align="center">文档主要内容</td></tr><tr><td align="center">aside</td><td align="center">侧边栏</td></tr><tr><td align="center">hgroup</td><td align="center">头/标题组</td></tr><tr><td align="center">footer</td><td align="center">文档或页的页脚</td></tr></tbody></table><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200810174705.png" alt="image-20200810174704025"></p><pre><code class="html">&lt;header&gt;header&lt;/header&gt;&lt;nav&gt;nav&lt;/nav&gt;&lt;main&gt;  main  &lt;article&gt;    article    &lt;section&gt;section&lt;/section&gt;  &lt;/article&gt;  &lt;aside&gt;aside&lt;/aside&gt;&lt;/main&gt;&lt;footer&gt;footer&lt;/footer&gt;</code></pre><p>兼容性解决：</p><ul><li>IE9：行级元素在设置宽度的时候是失效的（需要将这些标签转换为块级元素）</li></ul><pre><code class="css">header, nav, main, article, section, aside, footer{  display: block;}</code></pre><ul><li><p>IE8：完全不支持语义标签(不支持HTML5)</p><ul><li>方法一：手动创建标签</li></ul><pre><code class="html">&lt;script&gt;  /* 手动创建标签：默认的标签的类型都是行级元素 */  document.createElement(&quot;header&quot;)  document.createElement(&quot;nav&quot;)  document.createElement(&quot;main&quot;)  document.createElement(&quot;article&quot;)  document.createElement(&quot;aside&quot;)  document.createElement(&quot;footer&quot;)&lt;/script&gt;</code></pre></li></ul><ul><li><p>方法二：引入第三方插件html5shiv.min.js</p><pre><code class="html">&lt;script src=&quot;https://cdn.bootcdn.net/ajax/libs/html5shiv/3.7.3/html5shiv.js&quot;&gt;&lt;/script&gt;</code></pre></li></ul><h3><span id="2-列表">2、列表</span></h3><p>HTML5中，对ol、dl进行了改良</p><ul><li><code>ol</code>：添加了<code>start</code>、<code>reversed</code>属性<ul><li><code>start</code>：可以指定列表编号从多少开始</li><li><code>reversed</code>：列表反向排序</li></ul></li></ul><pre><code class="html">&lt;ol start=&quot;3&quot;&gt;  &lt;li&gt;1&lt;/li&gt;  &lt;li&gt;2&lt;/li&gt;  &lt;li&gt;3&lt;/li&gt;&lt;/ol&gt;&lt;ol start=9 reversed&gt;  &lt;li&gt;列表值1&lt;/li&gt;  &lt;li&gt;列表值2&lt;/li&gt;  &lt;li&gt;列表值3&lt;/li&gt;  &lt;li&gt;列表值4&lt;/li&gt;  &lt;li&gt;列表值5&lt;/li&gt;&lt;/ol&gt;</code></pre><ul><li><p><code>dl</code>：重新定义，<code>dl</code>标签包含多个带名字列表项；每一项包含至少一条带名字的<code>dt</code>元素，用来表示术语；dt后紧跟一个或多个dd元素，用来表示定义。在一个元素内不允许有相同名字的dt元素，不允许有重复的术语。</p><ul><li>可以用于解释术语</li></ul><pre><code class="html">&lt;dl&gt;  &lt;dt&gt;&lt;dfn&gt;RSS&lt;/dfn&gt;&lt;/dt&gt;  &lt;dd&gt;RSS也叫聚合RSS，是在线共享内容的一种简易方式(也叫聚合内容)……&lt;/dd&gt;  &lt;dt&gt;&lt;dfn&gt;博客&lt;/dfn&gt;&lt;/dt&gt;  &lt;dd&gt;博客，又译为网络日志，部落格或部落阁等，是一种通常由个人管理……&lt;/dd&gt;&lt;/dl&gt;</code></pre></li></ul><ul><li><p>可以表示辅助信息</p><pre><code class="html">&lt;dl&gt;&lt;dt&gt;作者&lt;/dt&gt;&lt;dd&gt;xxx&lt;/dd&gt;&lt;dt&gt;出版社&lt;/dt&gt;&lt;dd&gt;xxx出版社&lt;/dd&gt;&lt;dt&gt;类别&lt;/dt&gt;&lt;dd&gt;文学&lt;/dd&gt;&lt;/dl&gt;</code></pre></li></ul><h3><span id="3-表单">3、表单</span></h3><p>(1) 表单新增的type属性</p><ul><li><code>email</code>提供了默认的电子邮箱的完整验证，要求必须包含@符号，同时必须包含服务器名称，如果不能满足验证，则会阻止当前的数据提交(submit)</li></ul><pre><code class="html">邮箱：&lt;input type=&quot;email&quot;&gt;</code></pre><ul><li><code>tel</code>并不是用来实现验证，它的本质目的是为了能够在移动端打开数字键盘，意味着限制了用户只能输入数字。</li></ul><pre><code class="html">电话：&lt;input type=&quot;tel&quot;&gt;</code></pre><ul><li><code>url</code> 验证只能输入合法的网址，必须包含http://</li></ul><pre><code class="html">网址：&lt;input type=&quot;url&quot;&gt;</code></pre><ul><li><code>number</code> 只能输入数字(包含小数点)，不能输入其他的字符<ul><li><code>max</code>：最大值，<code>min</code>：最小值，<code>value</code>：默认值</li></ul></li></ul><pre><code class="html">数量：&lt;input type=&quot;number&quot;&gt;&lt;input type=&quot;number&quot; value=&quot;60&quot; min=&quot;0&quot; max=&quot;100&quot;&gt;</code></pre><ul><li><code>search</code> 可以提供更人性化的输入体验</li></ul><pre><code class="html">搜索框：&lt;input type=&quot;search&quot;&gt;</code></pre><ul><li><code>range</code> 范围</li></ul><pre><code class="html">范围：&lt;input type=&quot;range&quot; min=&quot;0&quot; max=&quot;100&quot; value=&quot;50&quot;&gt;</code></pre><ul><li><code>color</code>颜色</li></ul><pre><code class="html">颜色：&lt;input type=&quot;color&quot;&gt;</code></pre><ul><li>日期时间相关</li></ul><pre><code class="html">时间：&lt;input type=&quot;time&quot;&gt; &lt;!-- 时分秒 --&gt;日期：&lt;input type=&quot;date&quot;&gt; &lt;!-- 年月日 --&gt;日期时间：&lt;input type=&quot;datetime&quot;&gt; &lt;!-- 目前大多数浏览器都不支持，只有Safari --&gt;日期时间：&lt;input type=&quot;datetime-local&quot;&gt; &lt;!-- 日期和时间 --&gt;月份：&lt;input type=&quot;month&quot;&gt;星期：&lt;input type=&quot;week&quot;&gt;</code></pre><p>(2) 表单新增的其他属性</p><ul><li><p><code>placeholder</code> 提示文本，提示占位</p></li><li><p><code>autofocus</code> 自动获取焦点</p></li><li><p><code>autocomplete</code>：自动完成 on off</p><p> 必须成功提交过：提交过才会记录</p><p>当前添加autocomplete的元素必须有name属性</p></li><li><p><code>required</code> 必须输入，如果没有输入则会阻止当前数据提交</p></li><li><p><code>pattern</code>：正则表达式验证</p></li><li><p><code>multiple</code> 可以选择多个文件，在email中，设置multiple属性之后可以输入多个邮箱地址，以逗号分隔</p></li><li><p><code>form</code> 指定表单id，会随着该表单一起提交</p></li></ul><pre><code class="html">&lt;form action=&quot;&quot; id=&quot;myFrm&quot;&gt;  用户名：&lt;input type=&quot;text&quot; name=&quot;username&quot; placeholder=&quot;请输入用户名&quot; autofocus autocomplete=&quot;on&quot;&gt;  手机号：&lt;input type=&quot;tel&quot; required pattern=&quot;^(\+86)?1\d{10)$&quot;&gt;  文件：&lt;input type=&quot;file&quot; name=&quot;photo&quot; multiple&gt;  邮箱：&lt;input type=&quot;email&quot; multiple&gt;  &lt;input type=&quot;submit&quot; value=&quot;提交&quot;&gt;&lt;/form&gt;地址：&lt;input type=&quot;text&quot; name=&quot;address&quot; form=&quot;myFrm&quot;&gt;</code></pre><p>(3) 新增表单元素</p><ul><li><code>datalist</code>创建选项列表 </li><li><code>option</code>创建选择，属性：<code>value</code>具体的值，<code>label</code>提示信息，辅助值</li></ul><p>注意：</p><ul><li><p>需要在<code>input</code>中加入<code>list</code>属性，值为<code>datalist</code>的id名</p></li><li><p>datalist在不同浏览器下表现不同，很少使用</p></li><li><p>option可以是单标签，也可以是双标签</p></li><li><p>当input的类型为url时，option的value必须带http前缀</p></li></ul><pre><code class="html">选择：&lt;input type=&quot;text&quot; list=&quot;selectors&quot;&gt;&lt;datalist id=&quot;selectors&quot;&gt;  &lt;option value=&quot;HTML&quot; label=&quot;结构&quot; /&gt;  &lt;option value=&quot;CSS&quot; label=&quot;表现&quot;&gt;&lt;/option&gt;  &lt;option value=&quot;JavaScript&quot; label=&quot;行为&quot;&gt;&lt;/option&gt;&lt;/datalist&gt;网址：&lt;input type=&quot;url&quot; list=&quot;urls&quot;&gt;&lt;datalist id=&quot;urls&quot;&gt;  &lt;option value=&quot;https://www.baidu.com&quot; label=&quot;百度&quot; /&gt;  &lt;option value=&quot;http://www.sohu.com&quot; label=&quot;搜狐&quot;&gt;&lt;/option&gt;  &lt;option value=&quot;http://www.163.com&quot; label=&quot;网易&quot;&gt;&lt;/option&gt;&lt;/datalist&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200810190154.png" alt="image-20200810190152657"></p><pre><code class="html">&lt;form action=&quot;&quot;&gt;  用户名：&lt;input type=&quot;text&quot; name=&quot;username&quot; &gt;&lt;br&gt;  密码：&lt;input type=&quot;password&quot; name=&quot;pwd&quot; &gt;&lt;br&gt;  加密：&lt;keygen&gt;&lt;/keygen&gt;&lt;br&gt;  &lt;input type=&quot;submit&quot; value=&quot;提交&quot;&gt;&lt;/form&gt;&lt;!-- 显示输出信息：只能显示不能修改1、语义性更强2、值需要你去设置，不能自动计算--&gt;&lt;output&gt;总金额：￥100.00&lt;/output&gt;</code></pre><p>(4) 新增的表单事件</p><ul><li><code>oninput</code>：监听当前指定元素内容的改变，只要内容改变(添加内容/删除内容)就会触发这个事件（鼠标粘贴的也能触发）</li></ul><p>对比：<code>onkeyup</code>：键盘弹起的时候触发，每一个键的弹起都会触发一次</p><ul><li><code>oninvalid</code>：当验证不通过时触发</li></ul><pre><code class="html">&lt;form action=&quot;&quot;&gt;  用户名：&lt;input type=&quot;text&quot; name=&quot;username&quot; id=&quot;username&quot;&gt;  电话：&lt;input type=&quot;tel&quot; name=&quot;phone&quot; pattern=&quot;^1\d{10}$&quot; id=&quot;phone&quot;&gt;  &lt;input type=&quot;submit&quot; value=&quot;提交&quot;&gt;&lt;/form&gt;&lt;script&gt;  document.getElementById(&#39;username&#39;).oninput = function(){    console.log(this.value);  }  document.getElementById(&#39;phone&#39;).oninvalid = function(){    // 设置默认的提示信息    this.setCustomValidity(&#39;请输入合法的11位手机号&#39;)  }&lt;/script&gt;</code></pre><h3><span id="4-进度条">4、进度条</span></h3><ul><li><code>progress</code><ul><li>max：最大值</li><li>value：当前进度值</li></ul></li><li><code>meter</code>度量器——衡量当前进度值 <ul><li>high：规定的较高的值</li><li>low：规定的较低的值</li><li>max：最大值</li><li>min：最小值</li><li>value：当前度量值</li></ul></li></ul><pre><code class="html">&lt;progress max=&quot;100&quot; value=&quot;50&quot;&gt;&lt;/progress&gt;&lt;meter max=&quot;100&quot; min=&quot;0&quot; high=&quot;80&quot; low=&quot;40&quot; value=&quot;10&quot;&gt;&lt;/meter&gt;&lt;meter max=&quot;100&quot; min=&quot;0&quot; high=&quot;80&quot; low=&quot;40&quot; value=&quot;50&quot;&gt;&lt;/meter&gt;&lt;meter max=&quot;100&quot; min=&quot;0&quot; high=&quot;80&quot; low=&quot;40&quot; value=&quot;90&quot;&gt;&lt;/meter&gt;</code></pre><h3><span id="5-多媒体标签音视频">5、多媒体标签——音视频</span></h3><p>embed：直接插入视频文件，本质调用本机上已经安装的软件，有兼容性</p><p>flash插件：安装flash，学习flash增加使用成本，苹果设备不支持flash</p><p>新增：</p><ul><li><code>audio</code>：音频<ul><li><code>src</code>: 播放的音频文件的路径</li><li><code>controls</code>: 音频播放器的控制面板</li><li><code>autoplay</code>: 自动播放</li><li><code>loop</code>: 循环</li></ul></li></ul><pre><code class="html">&lt;audio src=&quot;../mp3/a.mp3&quot; controls autoplay loop&gt;&lt;/audio&gt;</code></pre><ul><li><p><code>video</code>：视频</p><ul><li><p><code>src</code>: 播放的视频文件的路径</p></li><li><p><code>controls</code>: 视频播放器的控制面板</p></li><li><p><code>autoplay</code>: 自动播放</p></li><li><p><code>loop</code>: 循环</p></li><li><p><code>width</code>: 宽度</p></li><li><p><code>height</code>: 高度 设置宽高时，一般只会设置其一，使其等比缩放，如果同时设置宽度和高度，视频并不会真的调整到该大小，除非设置的刚好是等比例的</p></li><li><p><code>poster</code>: 当视频还没有完全下载，或者用户还没有点击播放前的默认显示的封面，默认显示的是当前视频文件的第一幅画面</p></li></ul></li></ul><pre><code class="html">&lt;video src=&quot;../medeas/1.mp4&quot; width=&quot;800&quot; controls poster=&quot;../imgs/1.png&quot; autoplay&gt;&lt;/video&gt;</code></pre><p>因为不同浏览器支持的音视频文件的格式不一样，因此需要考虑到浏览器是否支持，可以准备多个格式的视频文件，让浏览器自动选择：<code>source</code></p><pre><code class="html">&lt;video controls&gt;  &lt;source src=&quot;../medias/1.flv&quot; type=&quot;video/flv&quot;&gt;  &lt;source src=&quot;../medias/1.mp4&quot; type=&quot;video/mp4&quot;&gt;    您的浏览器不支持视频播放，请更换浏览器！&lt;/video&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200810191655.png" alt="image-20200810191653266"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200810191742.png" alt="image-20200810191740565"></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200810191754.png" alt="image-20200810191752979"></p><p>文件API、拖放API</p><h2><span id="八-h5中的dom操作">八、H5中的DOM操作</span></h2><h3><span id="1-获取dom元素">1、获取DOM元素</span></h3><p>query：查询 selector：选择器</p><ul><li><p><code>document.querySelector()</code>只能获取单个元素，如果获取的元素不止一个，那么只会返回满足条件的第一个元素</p></li><li><p><code>document.querySelectorAll()</code>获取满足条件的所有元素——数组</p></li></ul><h3><span id="2-操作类样式">2、操作类样式</span></h3><p><code>classList</code>当前元素的所有样式列表——数组</p><ul><li><p><code>.add</code>为元素添加指定类名，一次只能添加一个</p></li><li><p><code>.remove</code>为元素溢出指定名称的类样式，一次只能移除一个</p></li><li><p><code>.toggle</code>切换元素的样式，如果元素之前没有指定名称的样式则添加，如果有则移除</p></li><li><p><code>.contains</code>判断元素是否包含指定名称的样式，返回true/false</p></li></ul><pre><code class="javascript">document.querySelector(‘li’).classList.add(‘red’)document.querySelectorAll(‘li’)[1].classList.remove(‘blue’)document.querySelectorAll(‘li’)[2].classList.toggle(‘green’)console.log(document.querySelectorAll(‘li’)[2].classList.contains(‘green’))console.log(document.querySelectorAll(‘li’)[2].classList.item(0))</code></pre><p>以前：</p><pre><code class="javascript">document.querySelector(‘li’).className=‘red underline’</code></pre><h3><span id="3-自定义属性">3、自定义属性</span></h3><p>定义：</p><ul><li><p><code>data-</code>开头</p></li><li><p><code>data-</code>后必须至少有一个字符，多个单词使用-连接</p></li><li><p>建议小写、无特殊符号、非纯数字</p></li></ul><pre><code class="html">// 定义 data-单词-单词&lt;p data-test-a=&#39;AreYouOK&#39;&gt;test&lt;/p&gt;&lt;script&gt;window.onload = function(){  var p = document.querySelector(&#39;p&#39;)  // 获取自定义属性值  var value = p.dataset[&#39;testA&#39;] // 将data-后面的单词使用camel命名法连接  console.log(value);}&lt;/script&gt;</code></pre><h2><span id="九-常用api">九、常用API</span></h2><h3><span id="1-网络状态改变事件">1、网络状态改变事件</span></h3><ul><li><p>ononline: 网络连通的时候触发这个事件</p></li><li><p>onoffline: 网络断开时触发</p></li></ul><pre><code class="javascript">// window.ononline = function () {}window.addEventListener(&quot;online&quot;, function(){  // 操作  alert(&#39;网络连通了~&#39;)})// window.onoffline = function(){}window.addEventListener(&quot;offline&quot;, function(){  // 操作  alert(&#39;网络断开了~&#39;)})</code></pre><h3><span id="2-全屏接口的使用">2、全屏接口的使用</span></h3><ul><li>requireFullscreen(): 开启全屏显示</li></ul><p>不同浏览器需要添加不同的前缀：Chrome: webkit fireFox: moz ie: ms opera: o</p><ul><li><p>cancelFullScreen(): 退出全屏显示 只能使用document来实现</p></li><li><p>fullscreenElement: 是否是全屏状态 只能使用document来实现</p></li></ul><pre><code class="html">&lt;div&gt;  &lt;img src=&quot;&quot; alt=&quot;&quot;&gt;  &lt;input type=&quot;button&quot; id=&quot;full&quot; value=&quot;全屏&quot;&gt;  &lt;input type=&quot;button&quot; id=&quot;cancelFull&quot; value=&quot;退出全屏&quot;&gt;  &lt;input type=&quot;button&quot; id=&quot;isFull&quot; value=&quot;是否全屏&quot;&gt;&lt;/div&gt;&lt;script&gt;window.onload = function(){  var div = document.querySelector(&#39;div&#39;)  document.querySelector(&#39;#full&#39;).onclick = function(){    if(div.requestFullscreen){      div.requestFullscreen()    }else if(div.webkitRequestFullscreen){      div.webkitRequestFullscreen()    }else if(div.mozRequestFullscreen){      div.mozRequestFullscreen()    }else if(div.msRequestFullscreen){      div.msRequestFullscreen()    }else if(div.oRequestFullscreen){      div.oRequestFullscreen()    }  }  document.querySelector(&#39;#cancelFull&#39;).onclick = function(){    if(document.cancelFullScreen){      document.cancelFullScreen()    }else if(document.webkitCancelFullScreen){      document.webkitCancelFullScreen()    }else if(document.mozCancelFullScreen){      document.mozCancelFullScreen()    }else if(document.msCancelFullScreen){      document.msCancelFullScreen()    }else if(document.oCancelFullScreen){      document.oCancelFullScreen()    }  }  document.querySelector(&#39;#isFull&#39;).onclick = function(){    if(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullscreenElement || document.msFullscreenElement){      alert(&#39;true&#39;)    }else{      alert(&#39;false&#39;)    }  }}&lt;/script&gt;</code></pre><h3><span id="3-文件读取api">3、文件读取API</span></h3><p>FileReader：读取文件内容</p><ul><li><p>readAsText(): 读取文本文件(可以使用Txt打开的文件)，返回文本字符串，默认编码是UTF-8</p></li><li><p>readAsBinaryString(): 读取任意类型的文件。返回二进制字符串，这个方法不是用来读取文件展示给用户的，而是存储文件。例如：读取文件的内容，获取二进制数据，传递给后台，后台接收了数据之后，再将数据存储</p></li><li><p>readAsDataURL(): 没有返回值，参数是文件</p></li></ul><p>读取文件获取一段以data开头的字符串，这段字符串的本质就是DataURL。DataURL是一种将文件(这个文件一般就是指图像或者能够嵌入到文档的文件格式)嵌入到文档的方案，将资源转换为base64编码的字符串形式，并且将这些内容直接存储在url中à优化网站的加载速度和执行效率</p><pre><code class="html">&lt;!-- 展示图片，src：指定路径(资源定位--url)，src请求得是外部文件，一般是服务器资源，意味着它需要向服务器发送请求，它占用服务器资源 --&gt;&lt;img src=&quot;../imgs/1.jpg&quot; alt=&quot;&quot;&gt;</code></pre><ul><li>abort(): 中断读取</li></ul><p>FileReader提供一个完整的事件模型，用来捕获读取文件时的状态</p><ul><li><p>onabort: 读取文件中断时触发</p></li><li><p>onerror: 读取错误时触发</p></li><li><p>onload: 文件读取成功完成时触发</p></li><li><p>onloadend: 读取完成时触发，无论成功还是失败</p></li><li><p>onloadstart: 开始读取时触发</p></li><li><p>onprogress: 读取文件过程中持续触发</p></li></ul><pre><code class="html">&lt;!-- 实时预览图片 及时：onchange()预览：readAsDataURL()--&gt;&lt;form action=&quot;&quot;&gt;  文件；&lt;input type=&quot;file&quot; name=&quot;myFile&quot; id=&quot;myFile&quot; onchange=&quot;getFileContent();&quot;&gt;&lt;br&gt;  &lt;input type=&quot;submit&quot;&gt;&lt;/form&gt;&lt;img src=&quot;&quot; alt=&quot;&quot;&gt;&lt;script&gt;  function getFileContent(){    var reader = new FileReader()    var file = document.getElementById(&#39;myFile&#39;).files[0]    reader.readAsDataURL(file)    reader.onload = function(){      // console.log(reader.result);      document.querySelector(&#39;img&#39;).src = reader.result    }  }&lt;/script&gt;</code></pre><h3><span id="4-拖拽接口">4、拖拽接口</span></h3><p>为元素添加属性：draggable=”true”，图片和超链接默认就能拖拽</p><p>拖拽事件：</p><ul><li><p>(被)拖拽元素</p><ul><li>ondrag: 拖动整个过程都会调用</li><li>ondragstart: 当拖动开始时调用</li><li>ondragleave: 当鼠标离开拖拽元素时调用</li><li>ondragend: 当拖拽结束时调用</li></ul></li><li><p>目标元素</p><ul><li>ondragenter: 当拖拽元素进入时调用</li><li>ondragover: 当停留在目标元素上时调用</li><li>ondrop: 当在目标元素上松开鼠标时调用 浏览器默认会阻止这个事件，我们必须在ondragover中阻止浏览器的默认行为</li><li>ondragleave: 当鼠标离开目标元素时调用</li></ul></li></ul><pre><code class="html">&lt;style&gt;    .box1,.box2{      width: 200px;      height: 200px;      background-color: #ddd;      border: 1px solid red;      float: left;      margin-left: 30px;    }    #pe{      width: 100%;      background-color: orange;    }  &lt;/style&gt;&lt;div class=&quot;box1&quot;&gt;  &lt;p id=&quot;pe&quot; draggable=&quot;true&quot;&gt;拖动我啊&lt;/p&gt;&lt;/div&gt;&lt;div class=&quot;box2&quot;&gt;&lt;/div&gt;&lt;script&gt;var p = document.querySelector(&#39;#pe&#39;)var box2 = document.querySelector(&#39;.box2&#39;)var box1 = document.querySelector(&#39;.box1&#39;)p.ondragstart = function(){  console.log(&#39;p ondragstart&#39;)}p.ondragend = function(){  console.log(&#39;p ondragend&#39;)}p.ondragleave = function(){  // console.log(&#39;p ondragleave&#39;)}p.ondrag = function(){  // console.log(&#39;p ondrag&#39;)}box2.ondragenter = function(){  console.log(&#39;box2 ondragenter&#39;)}box2.ondragover = function(e){  // console.log(&#39;box2 ondragover&#39;)  // 阻止浏览器默认行为，使ondrop事件能被触发  e.preventDefault()}box2.ondrop = function(){  // console.log(&#39;box2 ondrop&#39;)  box2.appendChild(p)}box2.ondragleave = function(){  console.log(&#39;box2 ondragleave&#39;)}box1.ondragover = function(e){  // 阻止浏览器默认行为，使ondrop事件能被触发  e.preventDefault()}box1.ondrop = function(){  box1.appendChild(p)}&lt;/script&gt;改进：利用事件捕获&lt;div class=&quot;box1&quot;&gt;  &lt;p id=&quot;pe&quot; draggable=&quot;true&quot;&gt;拖动我啊&lt;/p&gt;  &lt;p id=&quot;p2&quot; draggable=&quot;true&quot;&gt;拖动我啊&lt;/p&gt;&lt;/div&gt;&lt;div class=&quot;box2&quot;&gt;&lt;/div&gt;&lt;div class=&quot;box3&quot;&gt;&lt;/div&gt;&lt;div class=&quot;box4&quot; draggable=&quot;true&quot;&gt;&lt;/div&gt;&lt;script&gt;// var obj = null // 被拖拽元素，全局变量不安全document.ondragstart = function(e){  /* 通过事件捕获来获取当前被拖拽的子元素 */  // console.log(e.target);  e.target.style.opacity = 0.5  e.target.parentNode.style.borderWidth = &#39;5px&#39;  // obj = e.target  /*    通过dataTransfer来实现数据的存储与获取  * setData(format, data)  ** 数据类型:text/html  text/uri-list  ** 数据:一般来说是字符串值  */  e.dataTransfer.setData(&#39;text/html&#39;, e.target.id)}document.ondragend = function(e){  e.target.style.opacity = 1  e.target.parentNode.style.borderWidth = &#39;1px&#39;}document.ondragleave = function(e){}document.ondrag = function(e){}document.ondragenter = function(e){  console.log(e.target);}document.ondragover = function(e){  // 阻止浏览器默认行为，使ondrop事件能被触发  e.preventDefault()}document.ondrop = function(e){  // e.target.appendChild(obj)  /*   通过e.dataTransfer.setData存储的数据，只能在drop事件中获取  */  var id = e.dataTransfer.getData(&#39;text/html&#39;)  console.log(id);  e.target.appendChild(document.getElementById(id))}document.ondragleave = function(e){}&lt;/script&gt;</code></pre><h3><span id="5-地理定位api">5、地理定位API</span></h3><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200810193306.png" alt="image-20200810193305180"></p><p>示例：</p><pre><code class="html">&lt;div class=&quot;map&quot;&gt;&lt;/div&gt;&lt;script&gt;var map = document.querySelector(&#39;.map&#39;)function getLocation(){  if(navigator.geolocation){    navigator.geolocation.getCurrentPosition(showPosition, showError, {})  }else{    map.innerHTML = &quot;Geolocation is not supported by this browser&quot;  }}function showPosition(position){  map.innerHTML = &#39;Latitude:&#39; + position.coords.latitude + &quot;&lt;br /&gt;Logitude:&quot; + position.coords.longitude}function showError(error){  ……}getLocation()&lt;/script&gt;</code></pre><p>百度地图API：<a href="http://lbsyun.baidu.com/jsdemo.htm#a1_2" target="_blank" rel="noopener">http://lbsyun.baidu.com/jsdemo.htm#a1_2</a></p><h3><span id="6-通信api">6、通信API</span></h3><p>Web Sockets是H5提供的在Web应用程序中客户端与服务器端之间进行的非HTTP的通信机制。</p><ul><li>建立与服务器之间的通信连接</li></ul><pre><code class="javascript">var webSocket = new WebSocket(&quot;ws://localhost:9000/socket&quot;)</code></pre><p>调用的是WebSocket对应，参数url必须以<code>ws</code>或<code>wss</code>开头</p><ul><li>发送数据</li></ul><pre><code class="javascript">webSocket.send(&quot;data&quot;)</code></pre><ul><li>监听</li></ul><pre><code class="javascript">// 通过获取onmessage事件句柄来接收服务器传过来的数据webSocket.onmessage = function(event){  var data = event.data  ...}// 通过获取onopen事件句柄来监听socket的打开事件webSocket.onopen = function(event){  // 开始通信时的处理}// 通过获取onclose事件句柄来监听socket的关闭事件webSocket.onclose = function(event){  // 通信结束时的处理}</code></pre><ul><li>关闭socket</li></ul><pre><code class="javascript">webSocket.close()</code></pre><p>可以通过读取readyState的属性值来获取WebSocket对象的状态，主要有：</p><ul><li>CONNECTING（0），表示正在连接</li><li>OPEN（1），表示已建立连接</li><li>CLOSING（2），表示正在关闭连接</li><li>CLOSED（2），表示已关闭连接</li></ul><h2><span id="十-web存储">十、web存储</span></h2><h3><span id="1-sessionstorage的使用">1、sessionStorage的使用</span></h3><p>可以存储数据到本地，存储的容量5MB左右。这个数据本质是存储在当前页面的内存中；它的生命周期为关闭当前页面，关闭页面，数据会自动清除；</p><ul><li><p>setItem(key, value): 存储数据，以键值对方式存储</p></li><li><p>getItem(key): 获取数据，通过指定名称的key获取对应的value值，如果找不到对应名称的key，那么就会获取null</p></li><li><p>removeItem(key): 删除数据，通过指定名称key删除对应的值，如果key值错误，不会报错，也不会删除数据</p></li><li><p>clear(): 清空所有存储的内容</p></li></ul><pre><code class="html">&lt;input type=&quot;text&quot; id=&quot;username&quot;&gt;&lt;br&gt;&lt;input type=&quot;button&quot; value=&quot;设置数据&quot; id=&quot;setData&quot;&gt;&lt;input type=&quot;button&quot; value=&quot;获取数据&quot; id=&quot;getData&quot;&gt;&lt;input type=&quot;button&quot; value=&quot;删除数据&quot; id=&quot;removeData&quot;&gt;&lt;script type=&quot;text/javascript&quot;&gt;  document.querySelector(&#39;#setData&#39;).onclick = function(){    var name = document.querySelector(&#39;#username&#39;).value    // 存储数据    window.sessionStorage.setItem(&quot;username&quot;, name)  }  document.querySelector(&#39;#getData&#39;).onclick = function(){    // 获取数据    var name = window.sessionStorage.getItem(&#39;username&#39;)    alert(name)  }  document.querySelector(&#39;#removeData&#39;).onclick = function(){    // 删除数据    window.sessionStorage.removeItem(&#39;username&#39;)  }&lt;/script&gt;</code></pre><p>调试器查看</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200810193555.png" alt="image-20200810193553627"></p><h3><span id="2-localstorage的使用">2、localStorage的使用</span></h3><p>存储的内容大概20MB；不同浏览器不能共享数据，但是在同一浏览器的不同窗口中可以共享数据；永久生效，它的数据是存储在硬盘上，并不会随着页面或者浏览器的关闭而清除</p><ul><li><p>setItem(key, value): 存储数据，以键值对方式存储</p></li><li><p>getItem(key): 获取数据，通过指定名称的key获取对应的value值，如果找不到对应名称的key，那么就会获取null</p></li><li><p>removeItem(key): 删除数据，通过指定名称key删除对应的值，如果key值错误，不会报错，也不会删除数据</p></li><li><p>clear(): 清空所有存储的内容</p></li></ul><pre><code class="html">&lt;input type=&quot;text&quot; id=&quot;username&quot;&gt;&lt;br&gt;&lt;input type=&quot;button&quot; value=&quot;设置数据&quot; id=&quot;setData&quot;&gt;&lt;input type=&quot;button&quot; value=&quot;获取数据&quot; id=&quot;getData&quot;&gt;&lt;input type=&quot;button&quot; value=&quot;删除数据&quot; id=&quot;removeData&quot;&gt;&lt;script type=&quot;text/javascript&quot;&gt;  document.querySelector(&#39;#setData&#39;).onclick = function(){    var name = document.querySelector(&#39;#username&#39;).value    // 存储数据    window.localStorage.setItem(&quot;username&quot;, name)  }  document.querySelector(&#39;#getData&#39;).onclick = function(){    // 获取数据    var name = window.localStorage.getItem(&#39;username&#39;)    alert(name)  }  document.querySelector(&#39;#removeData&#39;).onclick = function(){    // 删除数据    window.localStorage.removeItem(&#39;username&#39;)  }&lt;/script&gt;</code></pre><h2><span id="十一-应用程序缓存">十一、应用程序缓存</span></h2><p>概念：使用HTML5，通过创建cache manifest文件，可以轻松地创建web应用的离线版本</p><p>优势：</p><ul><li><p>可配置需要缓存的资源</p></li><li><p>网络无连接应用仍可用</p></li><li><p>本地读取缓存资源，提升访问速度，增强用户体验</p></li><li><p>减少请求，缓解服务器负担</p></li></ul><p>Cache Manifest基础：</p><ul><li>如需启用应用程序缓存，要在文档的html标签中包含manifest属性：</li></ul><pre><code class="html">&lt;!DOCTYPE html&gt;&lt;html  manifest=&quot;demo.appcache&quot;&gt;  ……&lt;/html&gt;</code></pre><p><code>manifest=”应用程序缓存清单文件的路径”</code></p><ul><li><p>每个指定了manifest的页面在用户对其访问时都会被缓存，如果未指定manifest属性，则页面不会被缓存(除非在manifest文件中直接指定了该页面)</p></li><li><p>Manifest文件的建议个文件扩展名为appcache，这个文件的本质是一个文本文件</p></li><li><p>注意：manifest文件需要配置正确的MIME-type，即”text/cache-manifest”，必须在web服务器上进行配置</p></li></ul><p><code>demo.appcache</code>：</p><pre><code>CACHE MANIFEST# 注释 上面必须是当前文件的第一句# 需要缓存的文件清单列表CACHE:./imgs/1.jpg./imgs/2.jpg# 配置每一次都需要重新从服务器获取的文件清单列表NETWORK:./imgs/3.jpg# 配置如果文件无法获取则使用指定的文件进行替代FALLBACK:./imgs/4.jpg ./imgs/loader.gif# /代表所有文件</code></pre><p>最后，列出一下已弃用的标签</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/20200810171347.png" alt="image-20200810171332092"></p><p>H5中还有两个知识点需要单独学习的：</p><p><a href="http://www.ruanyifeng.com/blog/2018/07/web-worker.html" target="_blank" rel="noopener">Web Worker 使用教程</a></p><p><a href="https://blog.csdn.net/u012468376/article/details/73350998" target="_blank" rel="noopener">学习HTML5 Canvas这一篇文章就够了</a></p><p>扩展文章</p><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element" target="_blank" rel="noopener">HTML 元素参考</a></p><p> <a href="https://www.cnblogs.com/chenkg/p/5003426.html" target="_blank" rel="noopener">html中link的用法</a></p><p><a href="https://juejin.im/post/6844903808854654984" target="_blank" rel="noopener">html5新标签总结</a></p><p><a href="https://www.w3school.com.cn/html/html5_new_elements.asp" target="_blank" rel="noopener">HTML5 新元素</a></p><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="笔记" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="前端" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="前端" scheme="https://wallleap.cn/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="web" scheme="https://wallleap.cn/tags/web/"/>
    
      <category term="HTML" scheme="https://wallleap.cn/tags/HTML/"/>
    
  </entry>
  
  <entry>
    <title>怎样拥有两个独立的Chrome</title>
    <link href="https://wallleap.cn/2020/03/24/chrometwo/"/>
    <id>https://wallleap.cn/2020/03/24/chrometwo/</id>
    <published>2020-03-24T02:33:14.000Z</published>
    <updated>2025-01-24T09:22:15.351Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2><span id="1-问题起源">1、问题起源</span></h2><ul><li>由于自己日常使用的Chrome安装了太多插件，调试的时候受插件的影响，很难进行识别，因此需要一个单独用于开发调试的Chrome</li><li>Chrome默认安装到C盘，移动到其他盘之后，再进行安装打开的仍是同一个浏览器</li></ul><h2><span id="2-解决方法">2、解决方法</span></h2><p>主要就是利用Chrome的多用户会生成一个独立的、相互隔离的浏览器</p><h3><span id="方法一-自己指定存放数据路径推荐">方法一、自己指定存放数据路径(推荐)</span></h3><p>1、进入Chrome所在目录(可以通过默认的快捷方式右击-属性-打开文件所在位置)，接着右击Chrome，选择创建快捷方式</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200716091222532.png" alt="创建快捷方式"></p><p>2、对创建的图标进行命名，右键属性</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200716091419216.png" alt="属性"></p><p>3、目标字段：添加<code>--user-data-dir</code>参数，设置一个目标存放数据，笔者设置的是<code>D:\ChromeData\WebChrome</code></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200716091648127.png" alt="修改目标"></p><p>4、确定（可以换一个图标再点确定！）</p><h3><span id="方法二-直接生成新用户快捷方式">方法二、直接生成新用户快捷方式</span></h3><p> 打开谷歌浏览器，点击如下的图标</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200716092020261.png" alt="点击图标"></p><p>点击添加</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200716092116598.png" alt="点击添加"></p><p>输入任意用户名，默认勾选添加快捷方式，点击添加</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200716092240575.png" alt="image-20200716092240575"></p><p>之后就可以使用独立的Chrome了</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200716092343187.png" alt="image-20200716092343187"></p><p>而且，你可以在开发工具中指定Chrome路径为你创建的快捷方式的路径，之后默认就会以该浏览器打开网页了</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200716092823125.png" alt="IDE指定Chromepath"></p><p>参考文档：</p><ul><li><a href="https://blog.csdn.net/lpw_cn/article/details/105574710" target="_blank" rel="noopener">Chrome双开(同一个版本配置两个独立的浏览器,附图)</a></li><li><a href="https://blog.csdn.net/QiaoRui_/article/details/86512063" target="_blank" rel="noopener">启动多个独立谷歌浏览器</a></li></ul><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="笔记" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="前端" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="前端" scheme="https://wallleap.cn/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="web" scheme="https://wallleap.cn/tags/web/"/>
    
  </entry>
  
  <entry>
    <title>web入门基础知识</title>
    <link href="https://wallleap.cn/2020/03/24/webBase/"/>
    <id>https://wallleap.cn/2020/03/24/webBase/</id>
    <published>2020-03-24T02:33:14.000Z</published>
    <updated>2025-01-24T09:22:15.356Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2><span id="1-软件开发架构">1、 软件开发架构：</span></h2><ul><li><p>C/S架构：客户端/服务器——用户需要下载安装客户端软件使用，服务器负责处理软件的业务逻辑、同时更新、不能跨平台、通信采用自有协议(相对安全)，客户端功能强大，可以减轻服务器端压力，但是客户端维护开发成本高(胖客户端)</p></li><li><p>✪ B/S架构：浏览器/服务器端——本质上也是C/S，使用浏览器作为软件的客户端，通过浏览器访问网页的形式来使用软件、软件不需要安装(使用浏览器访问指定的网址)、客户端不需要更新、可跨平台使用、通信采用通用的HTTP协议(相对不安全)，客户端维护成本低，可跨平台，但服务器负担重，缺点是客户端功能较简单，用户体验不如C/S(瘦客户端)</p></li></ul><h2><span id="2-软件开发流程">2、软件开发流程</span></h2><ol><li>网页设计师根据需求设计网页(PS)</li><li>前端工程师将设计做成静态网页(HTML+CSS)</li><li>后台工程师将静态网页修改为动态网页(JSP\PHP\Python\ASP) 提供接口  JS Nodejs</li></ol><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/illustration/image-20200705094201933.png" alt="image-20200705094201933"></p><h2><span id="3-技术">3、技术</span></h2><ul><li><p>前端：HTML(结构)、CSS(表现)、<strong>JavaScript</strong>(行为)——(W3C标准)</p><p>由浏览器负责解释执行，分别描述页面的结构、控制页面内中元素的样式、响应用户操作</p></li><li><p>后端：ASP、PHP、JSP、NodeJS等</p><p>由服务器负责执行</p></li></ul><ul><li>静态网页：由前端技术实现（扩展名：.htm或.html）</li><li>动态网页：由前端和后台（服务器端）技术共同实现（扩展名：.asp或.php或.jsp）</li></ul><h2><span id="4-工具">4、工具</span></h2><ul><li><p>浏览器：Firefox、Chrome、IE</p></li><li><p>编辑器：Sublime Text、VS code</p></li><li><p>调试工具：FireBug</p></li><li><p>图片工具：Photoshop</p></li><li><p>IDE（集成开发工具）：DW、WebStorm、Hbuilder</p></li><li><p>IE6浏览器兼容测试：ietester</p></li></ul><h2><span id="5-万维网联盟w3c">5、万维网联盟(W3C)</span></h2><p>万维网联盟(W3C)：World Wid Web Consortium 定义网页相关标准(HTML、CSS、DOM、HTTP、XML等)</p><p>WHATWG：网页超文本应用技术工作小组 推动网络HTML5标准</p><h2><span id="6-文档">6、文档</span></h2><p>W3School离线手册</p><p>CSS</p><p>Unicode</p><p>正则表达式</p><p>……</p><h2><span id="7-其他">7、其他</span></h2><h3><span id="1http">(1)HTTP</span></h3><blockquote><p>HTTP(Hyper Text Transfer Protocol，超文本传输协议)是一个简单的请求-响应协议，它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以ASCII码形式给出；而消息内容则具有一个类似MIME的格式。</p><p>HTTP是基于客户/服务器模式，且面向连接的。典型的HTTP事务处理有如下的过程：</p><p>（1）客户与服务器建立连接；</p><p>（2）客户向服务器提出请求；</p><p>（3）服务器接受请求，并根据请求返回相应的文件作为应答；</p><p>（4）客户与服务器关闭连接。</p></blockquote><p>状态码</p><table><thead><tr><th>消息</th><th>描述</th></tr></thead><tbody><tr><td>100 Continue</td><td>服务器仅接收到部分请求，但是一旦服务器并没有拒绝该请求，客户端应该继续发送其余的请求。</td></tr><tr><td>101 Switching Protocols</td><td>服务器转换协议：服务器将遵从客户的请求转换到另外一种协议。</td></tr></tbody></table><table><thead><tr><th>消息</th><th>描述</th></tr></thead><tbody><tr><td><strong>200</strong> OK</td><td>请求成功（其后是对GET和POST请求的应答文档。）</td></tr><tr><td>201 Created</td><td>请求被创建完成，同时新的资源被创建。</td></tr><tr><td>202 Accepted</td><td>供处理的请求已被接受，但是处理未完成。</td></tr><tr><td>203 Non-authoritative Information</td><td>文档已经正常地返回，但一些应答头可能不正确，因为使用的是文档的拷贝。</td></tr><tr><td>204 No Content</td><td>没有新文档。浏览器应该继续显示原来的文档。如果用户定期地刷新页面，而Servlet可以确定用户文档足够新，这个状态代码是很有用的。</td></tr><tr><td>205 Reset Content</td><td>没有新文档。但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容。</td></tr><tr><td>206 Partial Content</td><td>客户发送了一个带有Range头的GET请求，服务器完成了它。</td></tr></tbody></table><table><thead><tr><th>消息</th><th>描述</th></tr></thead><tbody><tr><td>300 Multiple Choices</td><td>多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址。</td></tr><tr><td>301 Moved Permanently</td><td>所请求的页面已经转移至新的url。</td></tr><tr><td>302 Found</td><td>所请求的页面已经临时转移至新的url。</td></tr><tr><td>303 See Other</td><td>所请求的页面可在别的url下被找到。</td></tr><tr><td>304 Not Modified</td><td>未按预期修改文档。客户端有缓冲的文档并发出了一个条件性的请求（一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档）。服务器告诉客户，原来缓冲的文档还可以继续使用。</td></tr><tr><td>305 Use Proxy</td><td>客户请求的文档应该通过Location头所指明的代理服务器提取。</td></tr><tr><td>306 <em>Unused</em></td><td>此代码被用于前一版本。目前已不再使用，但是代码依然被保留。</td></tr><tr><td>307 Temporary Redirect</td><td>被请求的页面已经临时移至新的url。</td></tr></tbody></table><table><thead><tr><th>消息</th><th>描述</th></tr></thead><tbody><tr><td>400 Bad Request</td><td>服务器未能理解请求。</td></tr><tr><td>401 Unauthorized</td><td>被请求的页面需要用户名和密码。</td></tr><tr><td>401.1</td><td>登录失败。</td></tr><tr><td>401.2</td><td>服务器配置导致登录失败。</td></tr><tr><td>401.3</td><td>由于 ACL 对资源的限制而未获得授权。</td></tr><tr><td>401.4</td><td>筛选器授权失败。</td></tr><tr><td>401.5</td><td>ISAPI/CGI 应用程序授权失败。</td></tr><tr><td>401.7</td><td>访问被 Web 服务器上的 URL 授权策略拒绝。这个错误代码为 IIS 6.0 所专用。</td></tr><tr><td>402 Payment Required</td><td>此代码尚无法使用。</td></tr><tr><td>403 Forbidden</td><td>对被请求页面的访问被禁止。</td></tr><tr><td>403.1</td><td>执行访问被禁止。</td></tr><tr><td>403.2</td><td>读访问被禁止。</td></tr><tr><td>403.3</td><td>写访问被禁止。</td></tr><tr><td>403.4</td><td>要求 SSL。</td></tr><tr><td>403.5</td><td>要求 SSL 128。</td></tr><tr><td>403.6</td><td>IP 地址被拒绝。</td></tr><tr><td>403.7</td><td>要求客户端证书。</td></tr><tr><td>403.8</td><td>站点访问被拒绝。</td></tr><tr><td>403.9</td><td>用户数过多。</td></tr><tr><td>403.10</td><td>配置无效。</td></tr><tr><td>403.11</td><td>密码更改。</td></tr><tr><td>403.12</td><td>拒绝访问映射表。</td></tr><tr><td>403.13</td><td>客户端证书被吊销。</td></tr><tr><td>403.14</td><td>拒绝目录列表。</td></tr><tr><td>403.15</td><td>超出客户端访问许可。</td></tr><tr><td>403.16</td><td>客户端证书不受信任或无效。</td></tr><tr><td>403.17</td><td>客户端证书已过期或尚未生效。</td></tr><tr><td>403.18</td><td>在当前的应用程序池中不能执行所请求的 URL。这个错误代码为 IIS 6.0 所专用。</td></tr><tr><td>403.19</td><td>不能为这个应用程序池中的客户端执行 CGI。这个错误代码为 IIS 6.0 所专用。</td></tr><tr><td>403.20</td><td>Passport 登录失败。这个错误代码为 IIS 6.0 所专用。</td></tr><tr><td>404 Not Found</td><td>服务器无法找到被请求的页面。</td></tr><tr><td>404.0</td><td>（无）–没有找到文件或目录。</td></tr><tr><td>404.1</td><td>无法在所请求的端口上访问 Web 站点。</td></tr><tr><td>404.2</td><td>Web 服务扩展锁定策略阻止本请求。</td></tr><tr><td>404.3</td><td>MIME 映射策略阻止本请求。</td></tr><tr><td>405 Method Not Allowed</td><td>请求中指定的方法不被允许。</td></tr><tr><td>406 Not Acceptable</td><td>服务器生成的响应无法被客户端所接受。</td></tr><tr><td>407 Proxy Authentication Required</td><td>用户必须首先使用代理服务器进行验证，这样请求才会被处理。</td></tr><tr><td>408 Request Timeout</td><td>请求超出了服务器的等待时间。</td></tr><tr><td>409 Conflict</td><td>由于冲突，请求无法被完成。</td></tr><tr><td>410 Gone</td><td>被请求的页面不可用。</td></tr><tr><td>411 Length Required</td><td>“Content-Length” 未被定义。如果无此内容，服务器不会接受请求。</td></tr><tr><td>412 Precondition Failed</td><td>请求中的前提条件被服务器评估为失败。</td></tr><tr><td>413 Request Entity Too Large</td><td>由于所请求的实体的太大，服务器不会接受请求。</td></tr><tr><td>414 Request-url Too Long</td><td>由于url太长，服务器不会接受请求。当post请求被转换为带有很长的查询信息的get请求时，就会发生这种情况。</td></tr><tr><td>415 Unsupported Media Type</td><td>由于媒介类型不被支持，服务器不会接受请求。</td></tr><tr><td>416 Requested Range Not Satisfiable</td><td>服务器不能满足客户在请求中指定的Range头。</td></tr><tr><td>417 Expectation Failed</td><td>执行失败。</td></tr><tr><td>423</td><td>锁定的错误。</td></tr></tbody></table><table><thead><tr><th>消息</th><th>描述</th></tr></thead><tbody><tr><td>500 Internal Server Error</td><td>请求未完成。服务器遇到不可预知的情况。</td></tr><tr><td>500.12</td><td>应用程序正忙于在 Web 服务器上重新启动。</td></tr><tr><td>500.13</td><td>Web 服务器太忙。</td></tr><tr><td>500.15</td><td>不允许直接请求 Global.asa。</td></tr><tr><td>500.16</td><td>UNC 授权凭据不正确。这个错误代码为 IIS 6.0 所专用。</td></tr><tr><td>500.18</td><td>URL 授权存储不能打开。这个错误代码为 IIS 6.0 所专用。</td></tr><tr><td>500.100</td><td>内部 ASP 错误。</td></tr><tr><td>501 Not Implemented</td><td>请求未完成。服务器不支持所请求的功能。</td></tr><tr><td>502 Bad Gateway</td><td>请求未完成。服务器从上游服务器收到一个无效的响应。</td></tr><tr><td>502.1</td><td>CGI 应用程序超时。　·</td></tr><tr><td>502.2</td><td>CGI 应用程序出错。</td></tr><tr><td>503 Service Unavailable</td><td>请求未完成。服务器临时过载或宕机。</td></tr><tr><td>504 Gateway Timeout</td><td>网关超时。</td></tr><tr><td>505 HTTP Version Not Supported</td><td>服务器不支持请求中指明的HTTP协议版本。</td></tr></tbody></table><h3><span id="2ip地址与arp">(2)IP地址与ARP</span></h3><p>IP地址（Internet Protocol Address）是指互联网协议地址，又译为网际协议地址。IP地址是<a href="https://baike.baidu.com/item/IP协议" target="_blank" rel="noopener">IP协议</a>提供的一种统一的地址格式，它为互联网上的每一个网络和每一台主机分配一个逻辑地址，以此来屏蔽物理地址的差异。</p><ul><li><p>公有地址（Public address）由Inter NIC（Internet Network Information Center因特网信息中心）负责。这些IP地址分配给注册并向Inter NIC提出申请的组织机构。通过它直接访问因特网。</p></li><li><p>私有地址（Private address）属于非注册地址，专门为组织机构内部使用。</p></li></ul><p>以下列出留用的内部私有地址</p><table><thead><tr><th></th><th>私有地址段</th></tr></thead><tbody><tr><td>A类</td><td>10.0.0.0–10.255.255.255</td></tr><tr><td>B类</td><td>172.16.0.0–172.31.255.255</td></tr><tr><td>C类</td><td>192.168.0.0–192.168.255.255</td></tr></tbody></table><p>地址解析协议，即ARP（Address Resolution Protocol），是根据<a href="https://baike.baidu.com/item/IP地址" target="_blank" rel="noopener">IP地址</a>获取<a href="https://baike.baidu.com/item/物理地址/2129" target="_blank" rel="noopener">物理地址</a>的一个<a href="https://baike.baidu.com/item/TCP%2FIP协议" target="_blank" rel="noopener">TCP/IP协议</a>。</p><h3><span id="3url与dns">(3)URL与DNS</span></h3><p>在WWW上，每一信息资源都有统一的且在网上唯一的地址，该地址就叫URL（Uniform Resource Locator,统一资源定位符），它是WWW的统一资源定位标志，就是指网络地址。</p><p>也可认为由4部分组成：协议、主机、端口、路径</p><p>URL的一般语法格式为：<code>protocol :// hostname[:port] / path / [;parameters][?query]#fragment</code></p><p>例如：<a href="https://loacalhost:3000/test" target="_blank" rel="noopener">https://loacalhost:3000/test</a></p><p><a href="ftp://192.168.10.100/" target="_blank" rel="noopener">ftp://192.168.10.100/</a></p><p>域名系统（服务）协议（DNS）是一种分布式网络目录服务，主要用于域名与 IP 地址的相互转换。</p><h3><span id="4浏览器">(4)浏览器</span></h3><ul><li>发展历程</li></ul><p>1992年，托尼哟翰逊(Tony Johnson)发布了Midas，它允许用户浏览UNIX和VMS网页上的文档。</p><p>1993年，NCSA发布了Mosaic浏览器。</p><p>1994年，网景公司(Netscape)发布了Navigator浏览器。</p><p>1995年，IE浏览器(Internet Explorer)的发布掀起了“浏览器之战”。</p><p>1996年，网景公司的Navigator浏览器所占有的浏览器市场份额达86%。微软公司开始将IE浏览器整合到OS(操作系统)中。</p><p>1996年9月，Opera浏览器面世。</p><p>1998年，网景公司启动其开源产品，开始推出Mozilla。这一年的下半年，网景公司被AOL(美国在线服务公司)收购。</p><p>2002年，Firefox(火狐)浏览器面世。</p><p>2003年，苹果公司发布Safari浏览器。</p><p>2004年，IE浏览器所占有的市场份额达到了历史顶峰-92%。自此以后，其市场份额开始下滑。</p><p>2006年6月，Firefox 3的发布创下了吉尼斯世界纪录——一天有800万人下载。</p><p>2006年10月，专为Windows XP、Windows Server 2003和Windows Vista而设计的IE 7面世。</p><p>2008年，谷歌公司发布Chrome浏览器。</p><p>2009年，专为Windows 7、Windows Server 2003与2008、Windows Vista和WindowsXP设计的IE 8面世。同年，Firefox 3.5面世。它是第一款支持多点触控的浏览器。</p><p>2010年，谷歌公司发布了Chrome 5.0浏览器。它是第一款稳定支持三个平台的浏览器，还是第一款有书签同步功能(bookmark synchronization)的浏览器。</p><p>2011年，微软发布IE 9，IE 9采用了新的JavaScript引擎Chakra，使网页加载速度更快，同时利用显卡GPU加速文字和图形的渲染，使CPU的负担大大减轻。另外，IE 9开始支持HTML5和CSS3。</p><p>2012年，Windows 8正式上市后，IE 10问世。</p><p>2013年，随着Windows 8.1的正式发布，IE 11问世。IE 11在IE 10的基础上再次扩大对HTML5和CSS3的支持，如支持HTML5拖放、HTML5全屏、CSS边框图、视频码率控制、视频字幕隐藏、媒体加密、WebGL等，使得IE 11全面支持HTML5新特性 [7] 。</p><ul><li>浏览器的种类很多，但是主流的内核只有四种，各种不同的浏览器，就是在主流内核的基础上，添加不同的功能构成 。</li></ul><p>1、Trident内核</p><p>代表产品为Internet Explorer，又称其为IE内核。Trident（又称为MSHTML），是微软开发的一种排版引擎。使用Trident渲染引擎的浏览器有：IE、傲游、世界之窗浏览器、Avant、腾讯TT、Netscape 8、NetCaptor、Sleipnir、GOSURF、GreenBrowser和KKman等  。</p><p>2、Gecko内核</p><p>代表作品为Mozilla Firefox。Gecko是一套开放源代码的、以C++编写的网页排版引擎，是最流行的排版引擎之一，仅次于Trident。使用它的最著名浏览器有Firefox、Netscape6至9  。</p><p>3、WebKit内核</p><p>代表作品有Safari、Chrome。WebKit是一个开源项目，包含了来自KDE项目和苹果公司的一些组件，主要用于Mac OS系统，它的特点在于源码结构清晰、渲染速度极快。缺点是对网页代码的兼容性不高，导致一些编写不标准的网页无法正常显示。</p><p>4、Presto内核</p><p>代表作品Opera。Presto是由Opera Software开发的浏览器排版引擎，供Opera 7.0及以上使用。它取代了1日版Opera 4至6版本使用的Elektra排版引擎，包括加入动态功能，例如网页或其部分可随着DOM及Script语法的事件而重新排版  。</p><ul><li>主流的浏览器分为IE、Chrome、Firefox、Safari等几大类，它们具有以下特点：</li></ul><p>1、IE浏览器。IE浏览器是微软推出的Windows系统自带的浏览器，它的内核是由微软独立开发的，简称IE内核，该浏览器只支持Windows平台。国内大部分的浏览器，都是在IE内核基础上提供了一些插件，如360浏览器、搜狗浏览器等。</p><p>2、Chrome浏览器。Chrome浏览器由Google在开源项目的基础上进行独立开发的一款浏览器，市场占有率第一，而且它提供了很多方便开发者使用的插件，因此该浏览器也是本书开发的主要浏览器。Chrome浏览器不仅支持Windows平台，还支持Linux、Mac系统，同时它也提供了移动端的应用（如Android和iOS平台）。</p><p>3、Firefox浏览器。Firefox浏览器是开源组织提供的一款开源的浏览器，它开源了浏览器的源码，同时也提供了很多插件，方便了用户的使用，支持Windows平台、Llnux平台和Mac平台 。</p><p>4、Safari浏览器。Safari浏览器主要是Apple公司为Mac系统量身打造的一款浏览器，主要应用在Mac和iOS系统中 。</p><p>5、欧朋（Opera）浏览器。</p><ul><li>移动端浏览器</li></ul><p>1、内置浏览器</p><p>手机操作系统厂商开发</p><p>2、可下载浏览器</p><p>独立于操作系统，QQ浏览器、UC浏览器</p><p>3、webview</p><p>独立程序，留给原生应用的一个操作系统浏览接口</p><p>4、代理浏览器</p><p>在服务端完成渲染，返回一个压缩页面</p><p>5、混合浏览器</p><p>代理浏览器、完备浏览器(在浏览器上渲染页面)结合——省流模式</p><h3><span id="o其他文章">(O)其他文章</span></h3><p><a href="https://www.jianshu.com/p/43dc210968a2" target="_blank" rel="noopener">web入门基础知识</a></p><p><a href="https://www.cnblogs.com/superjishere/p/11628235.html" target="_blank" rel="noopener">浏览器简介</a></p><p><a href="http://igoro.com/archive/what-really-happens-when-you-navigate-to-a-url/" target="_blank" rel="noopener">What really happens when you navigate to a URL</a></p><p><a href="https://www.cnblogs.com/starstone/p/4890409.html" target="_blank" rel="noopener">HTTP必知必会</a> <a href="https://www.cnblogs.com/tu-0718/p/10935910.html" target="_blank" rel="noopener">Vscode 小白使用介绍</a></p><p><a href="https://www.cnblogs.com/web-shu/p/11958885.html" target="_blank" rel="noopener">前端vscode常用快捷键总结</a></p><p><a href="https://segmentfault.com/a/1190000020361775" target="_blank" rel="noopener">Vscode前端开发插件大全</a></p><p><a href="https://www.cnblogs.com/summit7ca/p/6944215.html" target="_blank" rel="noopener">VsCode中使用Emmet神器快速编写HTML代码</a></p><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="笔记" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="前端" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="前端" scheme="https://wallleap.cn/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="web" scheme="https://wallleap.cn/tags/web/"/>
    
  </entry>
  
  <entry>
    <title>Sakura美化</title>
    <link href="https://wallleap.cn/2020/03/08/theme-beautify/"/>
    <id>https://wallleap.cn/2020/03/08/theme-beautify/</id>
    <published>2020-03-08T13:09:24.000Z</published>
    <updated>2025-01-24T09:22:15.354Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>接上次的文章</p><p><a href="https://mp.weixin.qq.com/s/ngKKulHlt9LghOWRlYPccg" target="_blank" rel="noopener">闲着也是闲着，不如搭个免费的博客玩玩</a></p><h2><span id="0-前言">0、前言</span></h2><p>做hexo的美化工作，主要是：</p><ul><li>修改布局文件(HTML模板ejs文件)，添加功能</li><li>做好CSS</li></ul><p>还是得对HTML(结构)、CSS(表现)、Javascript(行为)有一定的了解</p><h3><span id="目录和文件分析">目录和文件分析</span></h3><p>现在我们来分析一下<code>thems/Sakura</code>目录结构和文件</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/theme-sakura-pic1.jpg" alt="pic1"></p><p>首先分析下目录，主要包括以下几个</p><ul><li>languages    这个文件夹中存放的是语言文件，主要是博客中的一些字符以简体中文、繁体中文、英文显示的定义</li><li>layout   这个是布局文件夹，所有的博客页面HTML和JavaScript以ejs模板存放在这<ul><li>_partial   这里的和下面的都是公共的页面，可以引入到HTML的任意位置</li><li>_widget<ul><li>Search  这里存放的是页内搜索的ejs文件</li></ul></li></ul></li><li>source  这个文件夹和博客根目录下的source文件夹是一样的，但是为了存放主题相关的文件<ul><li>css   这里存放css文件</li><li>fonts   字体文件</li><li>images   图片</li><li>js   js文件</li></ul></li></ul><h3><span id="代码插入位置">代码插入位置</span></h3><p>再接着就是需要知道我们插入的文件和代码应该放到哪里</p><p>一般来说我们的js、css代码一般以文件形式存放到js、css文件夹中，接着再引入HTML代码中，引入位置一般在<code>&lt;head&gt;&lt;/head&gt;</code>标签中，当然啦css、js位置也不是固定的，你也可以直接放到HTML页面结构的中央和结尾，也就是<code>&lt;body&gt;&lt;/body&gt;</code>之间或者<code>&lt;/body&gt;</code>之后、<code>&lt;/head&gt;</code>之前</p><p>要是CSS或者JavaScript的代码很少的话，可以直接放到HTML中，不需要先存为文件再引入</p><p>具体参考：HTML中引入js、css的三种方式——行内样式、页面中样式、外部文件导入</p><p>就对应我们现在要修改的文件中的_partial目录下head.ejs——这个<code>&lt;html&gt;&lt;head&gt;&lt;/head&gt;</code>就在这个文件中</p><p>footer.ejs是底部元素，一般个人、企业版权写在这里，因为一般大部分文件都会引入，因此js代码也可以写在这</p><p>layout.ejs这个也是所有页面需要引入的，因为<code>&lt;/body&gt;&lt;/html&gt;</code>标签在这里</p><p>要是想将功能单独添加到某个页面，那你就找到那个ejs文件放到里面就醒了</p><hr><p>好了，下面就正式开始添加功能和修改样式了</p><h2><span id="1-博客原有功能修改">1、博客原有功能修改</span></h2><p>博客原有功能修改主要指的是博客根目录配置文件和主题配置文件的修改</p><p>因为大部分的都已经在上次的博客搭建文章中写了</p><p>就不多说了吧</p><h3><span id="不讲了的">不讲了的</span></h3><p>主要包括各种页面结构的修改：</p><ul><li>首页轮播图</li><li>站点图标、头像、赞赏等图片的链接修改</li><li>个人化的站点标题、副标题、描述、关键词等</li><li>导航栏的修改、添加、删除等</li></ul><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/theme-sakura-pic2.jpg" alt="pic2"></p><ul><li>通知的修改</li><li>startdash的图片、链接修改</li><li>社交链接的修改等</li></ul><p>这上面这些应该都是不需要再讲了的</p><h3><span id="导航栏中的关于">导航栏中的关于</span></h3><p>主要是关于&gt;我？这个的修改</p><p>这个要修改的文件在Sakura/source/js目录下，名字为<code>botui.js</code></p><p>主要修改content后面双引号里的内容，自行修改为自己的就行</p><pre><code class="javascript">function bot_ui_ini() {    var botui = new BotUI(&quot;hello-mashiro&quot;)    botui.message.add({        delay: 800,        content: &quot;Hi, there👋&quot;    }).then(function () {        botui.message.add({            delay: 1100,            content: &quot;这里是 Mashiro&quot;        }).then(function () {            botui.message.add({                delay: 1100,                content: &quot;一个可爱的蓝孩子~&quot;            }).then(function () {                botui.action.button({                    delay: 1600,                    action: [{                        text: &quot;然后呢？ 😃&quot;,                        value: &quot;sure&quot;                    }, {                        text: &quot;少废话！ 🙄&quot;,                        value: &quot;skip&quot;                    }]                }).then(function (a) {                    &quot;sure&quot; == a.value &amp;&amp; sure();                    &quot;skip&quot; == a.value &amp;&amp; end()                })            })        })    });    var sure = function () {            botui.message.add({                delay: 600,                content: &quot;😘&quot;            }).then(function () {                secondpart()            })        },        end = function () {            botui.message.add({                delay: 600,                content: &quot;![...](https://view.moezx.cc/images/2018/05/06/a1c4cd0452528b572af37952489372b6.md.jpg)&quot;            })        },        secondpart = function () {            botui.message.add({                delay: 1500,                content: &quot;目前就读于上海财经大学&quot;            }).then(function () {                botui.message.add({                    delay: 1500,                    content: &quot;向往技术却误入商科，但后来喜欢上了经济学…&quot;                }).then(function () {                    botui.message.add({                        delay: 1200,                        content: &quot;因为数据分析也需要Coder嘛&quot;                    }).then(function () {                        botui.message.add({                            delay: 1500,                            content: &quot;主攻 R 语言和 Python，略懂 STATA，偶尔也折腾 HTML/CSS/JavaScript/PHP&quot;                        }).then(function () {                            botui.message.add({                                delay: 1500,                                content: &quot;研究的方向，是经济/金融方向的数据分析（data science）以及机器学习（machine learning）&quot;                            }).then(function () {                                botui.message.add({                                    delay: 1800,                                    content: &quot;喜欢画画，希望有一天能够被称为画师&quot;                                }).then(function () {                                    botui.action.button({                                        delay: 1100,                                        action: [{                                            text: &quot;为什么叫Mashiro呢？ 🤔&quot;,                                            value: &quot;why-mashiro&quot;                                        }]                                    }).then(function (a) {                                        thirdpart()                                    })                                })                            })                        })                    })                })            })        },        thirdpart = function () {            botui.message.add({                delay: 1E3,                content: &quot;Mashiro以及站名都来自一部动画，因为和主角有一样的爱好~ 如果有兴趣可以找找首页上的视频~&quot;            }).then(function () {                botui.action.button({                    delay: 1500,                    action: [{                        text: &quot;为什么是白猫呢？ 🤔&quot;,                        value: &quot;why-cat&quot;                    }]                }).then(function (a) {                    fourthpart()                })            })        },        fourthpart = function () {            botui.message.add({                delay: 1E3,                content: &quot;因为对GitHub有种执念… &quot;            }).then(function () {                botui.message.add({                    delay: 1100,                    content: &quot;而且我真的是猫控！&quot;                }).then(function () {                    botui.action.button({                        delay: 1500,                        action: [{                            text: &quot;域名有什么含意吗？(ง •_•)ง&quot;,                            value: &quot;why-domain&quot;                        }]                    }).then(function (a) {                        fifthpart()                    })                })            })        },        fifthpart = function () {            botui.message.add({                delay: 1E3,                content: &quot;emmmm，看备案信息你就知道了=.= 本来想要zheng.xin的，但50万真买不起。。&quot;            }).then(function () {                botui.message.add({                    delay: 1600,                    content: &quot;那么，仔细看看我的博客吧？ ^_^&quot;                })            })        } }</code></pre><h3><span id="导航栏中的客户端">导航栏中的客户端</span></h3><p>这个可以使用Fusion App把你自己的博客封装成APP</p><p>然后放到蓝奏云上，生成二维码放上来</p><h3><span id="导航栏中的rss">导航栏中的RSS</span></h3><p>根目录下的配置文件中已经有了</p><pre><code class="yml">#RSSfeed:  type: atom  path: atom.xml  limit: 20  hub:  content:  content_limit: 140  content_limit_delim: &#39; &#39;  order_by: -date</code></pre><p>这些代码</p><p>因此我们只需要安装好插件就行了</p><pre><code class="shell">npm install hexo-generator-feed</code></pre><p>当然啦，如果本来就能显示内容，那么就不需要安装了</p><h3><span id="每个页面中的顶部图片">每个页面中的顶部图片</span></h3><p>就比如关于&gt;主题这个页面</p><p>我们进入根目录下，再进入source目录找到theme-sakura，进入该目录点击index.md，修改photos后面的内容，改为你想要的图片链接</p><pre><code class="markdown">---title: theme-sakuracomments: falsedate: 2019-01-04 22:53:25keywords: Hexo 主题 Sakura 🌸description:photos: https://static.2heng.xin/wp-content/uploads//2018/05/sakura2.jpeg---Hexo主题Sakura修改自WordPress主题[Sakura](https://github.com/mashirozx/Sakura/)，感谢原作者[Mashiro](https://2heng.xin/)</code></pre><h3><span id="文章上面和首页文章列表的图片">文章上面和首页文章列表的图片</span></h3><p>也是修改的你新建的文章中的photos后面的图片链接</p><p>这些都是这个主题支持的内容，在上篇文章中写过的文章模板</p><pre><code>---title: 文章标题author: 作者名avatar: 作者头像链接authorLink: hojun.cn  #作者的域名authorAbout: 一个好奇的人  #关于authorDesc: 一个好奇的人  #作者描述categories: 分类date: 2018-12-12 22:16:01 #时间，这个一般都自动创建comments: true  # 是否需要留言tags:        # 下面可以写多个标签 - web - 悦读keywords: Sakura   # 文章关键词description: hexo-sakura主题使用教程   #文章描述photos: https://static.2heng.xin/wp-content/uploads//2019/02/wallhaven-672007-1-1024x576.png---</code></pre><h3><span id="清单下的番组">清单下的番组</span></h3><p>这个是在source下bangumi下的index.md中修改的</p><pre><code>---layout: bangumititle: bangumicomments: falsedate: 2019-02-10 21:32:48keywords:description:bangumis:  - img: https://lain.bgm.tv/pic/cover/l/0e/1e/218971_2y351.jpg # 番的图片    title: 朝花夕誓——于离别之朝束起约定之花  # 番的中文标题    status: 已追完  # 追番状态    progress: 100   # 追番进度    jp: さよならの朝に約束の花をかざろう   # 日文标题    time: 2018-02-24 SUN.  # 这个是出版的时间    desc: 这里是你追的番的描述---</code></pre><h3><span id="清单下的歌单和左下角悬浮歌单">清单下的歌单和左下角悬浮歌单</span></h3><p>主要是修改你的id，歌单id的获取方式：</p><p>进入<a href="https://music.163.com/" target="_blank" rel="noopener">网易云网页版</a>登录账号，选择一个歌单打开</p><p>浏览器的链接将会显示id，就最后一个字段，把那些数字复制到下面就行</p><p><code>https://music.163.com/?from=infinity#/playlist?id=2162711186</code></p><p>清单下的歌单文件是source/music/index.md</p><pre><code>---title: musicdate: 2018-12-20 23:14:28keywords: 喜欢的音乐description: comments: falsephotos: https://cdn.jsdelivr.net/gh/honjun/cdn@1.4/img/banner/music.jpg---{% raw %}<meting-js server="netease" type="playlist" id="2731690811" mutex="true"></meting-js><meting-js server="netease" type="playlist" id="419239189" mutex="true"></meting-js>{% endraw %}</code></pre><p>悬浮音乐的代码在主题配置文件中</p><pre><code>aplayer:   id: 2660651585  server: netease  type: playlist  fixed: true  autoplay: false  loop: all  order: random  preload: auto  volume: 0.7  mutex: true</code></pre><h3><span id="友链添加">友链添加</span></h3><p>友链修改文件在source/links/index.md</p><pre><code>---layout: linkstitle: linksdate: 2018-12-19 23:11:06keywords: 友人帐description: comments: truephotos: https://cdn.jsdelivr.net/gh/honjun/cdn@1.4/img/banner/links.jpglinks:  - group: 个人项目    desc: 充分说明这家伙是条咸鱼 &lt; (￣︶￣)&gt;    items:    - url: https://shino.cc/fgvf      img: https://cloud.moezx.cc/Picture/svg/landscape/fields.svg      name: Google      desc: Google 镜像    - url: https://shino.cc/fgvf      img: https://cloud.moezx.cc/Picture/svg/landscape/fields.svg      name: Google      desc: Google 镜像  - group: 小伙伴们    desc: 欢迎交换友链 ꉂ(ˊᗜˋ)    items:  ## 友链添加主要是在这里    - url: https://shino.cc/fgvf # 他的链接      img: https://cloud.moezx.cc/Picture/svg/landscape/fields.svg # 他的头像      name: Google # 他的博客名      desc: Google 镜像 # 博客描述    - url: https://shino.cc/fgvf      img: https://cloud.moezx.cc/Picture/svg/landscape/fields.svg      name: Google      desc: Google 镜像---</code></pre><p>不需要修改layout目录下文件的应该就这些了</p><p>接下来就直接按点添加功能和美化了</p><h2><span id="2-添加标题恶搞">2、添加标题恶搞</span></h2><p>默认的是离开时候还是现实自己的文字标题</p><p>添加之后离开和回到这个页面时显示</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/theme-sakura-pic3.jpg" alt="pic3"></p><blockquote><p> 挺多的js都直接集合到我的cdn中了，大部分都是网上到处搜查的</p></blockquote><p>为了达到上述效果,我们可以在head.ejs或者footer.ejs中加入代码</p><pre><code class="javascript">&lt;script src=&quot;https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/js/hititle.js&quot;&gt;&lt;/script&gt;</code></pre><p>例如加入到head.ejs的该位置</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/theme-sakura-pic4.jpg" alt="pic4"></p><p>加入之后刷新就能生效了</p><h2><span id="3-添加鼠标单击特效">3、添加鼠标单击特效</span></h2><p>单击特效有很多种，这里给出三个吧</p><p>现在我们把这个代码放到layout.ejs的<code>&lt;/body&gt;</code>标签前</p><pre><code class="javascript">&lt;!-- 点击出现社会主义彩色文字 --&gt;&lt;script src=&quot;https://cdn.jsdelivr.net/gh/wallleap/cdn/js/shehuizhuyi.js&quot;&gt;&lt;/script&gt;&lt;!-- 点击出现爱心 --&gt;&lt;script src=&quot;https://cdn.jsdelivr.net/gh/wallleap/cdn/js/love.js&quot;&gt;&lt;/script&gt;&lt;!-- 点击出现彩色气球爆炸效果 --&gt;&lt;canvas class=&quot;fireworks&quot; style=&quot;position: fixed;left: 0;top: 0;z-index: 1; pointer-events: none;&quot; &gt;&lt;/canvas&gt; &lt;script type=&quot;text/javascript&quot; src=&quot;//cdn.bootcss.com/animejs/2.2.0/anime.min.js&quot;&gt;&lt;/script&gt; &lt;script src=&quot;https://cdn.jsdelivr.net/gh/wallleap/cdn/js/clickBom.js&quot;&gt;&lt;/script&gt;</code></pre><p>可以分别添加一种，也可以多种混合，但是三种一起使用感觉效果不太好</p><p>下面是前两者结合的效果：</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/theme-sakura-pic5.gif" alt="pic5"></p><p>最后一种：</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/theme-sakura-pic6.gif" alt="pic6"></p><h2><span id="4-鼠标滑过的特效">4、鼠标滑过的特效</span></h2><p>这个有很多个，这里展示一个</p><p>把代码插入到layout.ejs中</p><pre><code class="javascript">&lt;script src=&quot;https://cdn.jsdelivr.net/gh/wallleap/cdn/js/xuehua.js&quot;&gt;&lt;/script&gt;</code></pre><p>效果如下：</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/theme-sakura-pic7.gif" alt="pic7"></p><h2><span id="5-背景显示飘动的彩带">5、背景显示飘动的彩带</span></h2><p>在需要的页面添加，我们还是在所有页面都加上，layout.ejs中加入代码</p><pre><code class="javascript">&lt;script src=&quot;https://cdn.jsdelivr.net/gh/wallleap/cdn/js/piao.js&quot;&gt;&lt;/script&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/theme-sakura-pic8.gif" alt="pic8"></p><h2><span id="6-背景添加动态线条随鼠标动">6、背景添加动态线条，随鼠标动</span></h2><p>仍是layout.ejs中</p><pre><code class="javascript">&lt;script src=&quot;https://cdn.jsdelivr.net/gh/wallleap/cdn/js/canvas-nest.min.js&quot;&gt;&lt;/script&gt;</code></pre><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/theme-sakura-pic9.gif" alt="pic9"></p><h2><span id="7-樱花飘落或雪花飘落">7、樱花飘落或雪花飘落</span></h2><p>仍是layout.ejs中</p><p>樱花飘落：</p><pre><code class="javascript">&lt;script src=&quot;https://cdn.jsdelivr.net/gh/wallleap/cdn/js/sakura.js&quot;&gt;&lt;/script&gt;</code></pre><p>雪花飘落：</p><pre><code class="javascript">&lt;script src=&quot;https://cdn.jsdelivr.net/gh/wallleap/cdn/js/xuehuapiaoluo.js&quot;&gt;&lt;/script&gt;或者&lt;script src=&quot;https://cdn.jsdelivr.net/gh/wallleap/cdn/js/snow.js&quot;&gt;&lt;/script&gt;</code></pre><p>选择一个就行了</p><h2><span id="8-禁用一些按键">8、禁用一些按键</span></h2><p>为了阻止某些使用F12、Ctrl+Alt+I调用开发者选项的用户</p><p>还有禁用了鼠标右键，可以开启禁用鼠标左键拖动选择文字</p><pre><code>&lt;script src=&quot;https://cdn.jsdelivr.net/gh/wallleap/cdn/js/noSomeKey.js&quot;&gt;&lt;/script&gt;</code></pre><h2><span id="9-添加画板娘">9、添加画板娘</span></h2><p>这次不用插件来添加，直接引入js代码</p><pre><code class="javascript">&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/font-awesome/css/font-awesome.min.css&quot;&gt; &lt;!-- 这条在sakura中已有，可不添加 --&gt;&lt;script src=&quot;https://cdn.jsdelivr.net/gh/stevenjoezhang/live2d-widget@latest/autoload.js&quot;&gt;&lt;/script&gt;</code></pre><p>仓库链接在这：<a href="https://github.com/stevenjoezhang/live2d-widget" target="_blank" rel="noopener">live2d-widget</a></p><h2><span id="10-添加前往github的彩带">10、添加前往GitHub的彩带</span></h2><p>前往<a href="https://github.blog/2008-12-19-github-ribbons/" target="_blank" rel="noopener">GitHub Ribbons</a></p><p>挑选样式之后，复制相应的代码，粘贴到合适的地方</p><h2><span id="11-加入预加载">11、加入预加载</span></h2><p>sakura主题集成了图片懒加载，可是页面放到GitHub加载还是很慢，matery主题就加入了预加载，从那里得到灵感，因此我们加入预加载</p><p>还是在layout.ejs中添加</p><pre><code class="javascript">&lt;script src=&quot;//instant.page/3.0.0&quot; type=&quot;module&quot; defer integrity=&quot;sha384-OeDn4XE77tdHo8pGtE1apMPmAipjoxUQ++eeJa6EtJCfHlvijigWiJpD7VDPWXV1&quot;&gt;&lt;/script&gt;</code></pre><p>官网在这：<a href="https://instant.page/" target="_blank" rel="noopener">预加载</a></p><h2><span id="12-添加计数">12、添加计数</span></h2><p>主要使用的是不蒜子和LeanCloud</p><p>LeanCloud我使用起来并不理想，因此不讲，想要了解自己去官网看</p><p>不蒜子的：</p><pre><code class="JavaScript">&lt;script src=&quot;//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js&quot;&gt;&lt;/script&gt;</code></pre><p>再进入不蒜子官网查看访问量pv、用户访问量uv使用</p><h2><span id="13-添加一言api-诗词">13、添加一言API、诗词</span></h2><p><a href="https://www.jinrishici.com/#" target="_blank" rel="noopener">今日诗词</a>提供了诗词的API</p><p>我们将这个诗词放到以前的描述那里，社交图标的上方</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/theme-sakura-pic10.jpg" alt="pic10"></p><p>修改headertop.ejs，找到下面的代码</p><pre><code class="html">&lt;div class=&quot;header-info&quot;&gt;  &lt;!-- 找到这个位置 --&gt;        &lt;!-- &lt;p&gt;&lt;%= theme.description %&gt;&lt;/p&gt; --&gt;  把这句注释掉        &lt;p id=&quot;jinrishici-sentence&quot;&gt;正在加载今日诗词....&lt;/p&gt;        &lt;script src=&quot;https://sdk.jinrishici.com/v2/browser/jinrishici.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;  &lt;!-- 添加上面的两句 --&gt;        &lt;div class=&quot;top-social_v2&quot;&gt; </code></pre><p>一言API的可以前往这个网址查看使用：</p><p><a href="https://developer.hitokoto.cn/sentence/" target="_blank" rel="noopener">一言开发者中心</a></p><p>正好通知也没有啥用，把他换掉</p><p>进入index.ejs，查找<code>&lt;%= theme.notice%&gt;</code>，将其替换为下面的</p><pre><code class="ejs">      &lt;span id=&quot;hitokoto&quot; style=&quot;margin-left:5px;&quot;&gt; :D 获取中...&lt;/span&gt;      &lt;p align=&quot;right&quot; id=&quot;afrom&quot;&gt;&lt;/p&gt;      &lt;script src=&quot;https://cdn.jsdelivr.net/npm/bluebird@3/js/browser/bluebird.min.js&quot;&gt;&lt;/script&gt;      &lt;script src=&quot;https://cdn.jsdelivr.net/npm/whatwg-fetch@2.0.3/fetch.min.js&quot;&gt;&lt;/script&gt;      &lt;script&gt;        fetch(&#39;https://v1.hitokoto.cn/?c=h&#39;)          .then(function (res){            return res.json();          })          .then(function (data) {            var hitokoto = document.getElementById(&#39;hitokoto&#39;);            var afrom = document.getElementById(&#39;afrom&#39;);            hitokoto.innerText = data.hitokoto;            afrom.innerText =  &#39;——【&#39; + data.from + &#39; &#39; + data.from_who + &#39;】&#39;;          })          .catch(function (err) {            console.error(err);          })      &lt;/script&gt;</code></pre><h2><span id="14-添加底部的网站运行时间">14、添加底部的网站运行时间</span></h2><p>在footer.ejs选择合适位置添加代码</p><pre><code class="html">&lt;span id=&quot;timeDate&quot;&gt;载入天数...&lt;/span&gt;&lt;span id=&quot;times&quot;&gt;载入时分秒...&lt;/span&gt;&lt;script&gt;  var now = new Date();   function createtime() {     var grt= new Date(&quot;03/08/2020 16:44:00&quot;);//此处修改你的建站时间或者网站上线时间     now.setTime(now.getTime()+250);     days = (now - grt ) / 1000 / 60 / 60 / 24; dnum = Math.floor(days);     hours = (now - grt ) / 1000 / 60 / 60 - (24 * dnum); hnum = Math.floor(hours);     if(String(hnum).length ==1 ){hnum = &quot;0&quot; + hnum;} minutes = (now - grt ) / 1000 /60 - (24 * 60 * dnum) - (60 * hnum);     mnum = Math.floor(minutes); if(String(mnum).length ==1 ){mnum = &quot;0&quot; + mnum;}     seconds = (now - grt ) / 1000 - (24 * 60 * 60 * dnum) - (60 * 60 * hnum) - (60 * mnum);     snum = Math.round(seconds); if(String(snum).length ==1 ){snum = &quot;0&quot; + snum;}     document.getElementById(&quot;timeDate&quot;).innerHTML = &quot;本站已安全运行 &quot;+dnum+&quot; 天 &quot;;     document.getElementById(&quot;times&quot;).innerHTML = hnum + &quot; 小时 &quot; + mnum + &quot; 分 &quot; + snum + &quot; 秒&quot;;   }   setInterval(&quot;createtime()&quot;,250);&lt;/script&gt;</code></pre><h2><span id="15-添加底部动态滚动文字">15、添加底部动态滚动文字</span></h2><pre><code class="ejs">&lt;div id=&quot;binft&quot;&gt;&lt;/div&gt;  &lt;script&gt;    var binft = function (r) {      function t() {        return b[Math.floor(Math.random() * b.length)]      }        function e() {        return String.fromCharCode(94 * Math.random() + 33)      }      function n(r) {        for (var n = document.createDocumentFragment(), i = 0; r &gt; i; i++) {          var l = document.createElement(&quot;span&quot;);          l.textContent = e(), l.style.color = t(), n.appendChild(l)        }        return n      }      function i() {        var t = o[c.skillI];        c.step ? c.step-- : (c.step = g, c.prefixP &lt; l.length ? (c.prefixP &gt;= 0 &amp;&amp; (c.text += l[c.prefixP]), c.prefixP++) : &quot;forward&quot; === c.direction ? c.skillP &lt; t.length ? (c.text += t[c.skillP], c.skillP++) : c.delay ? c.delay-- : (c.direction = &quot;backward&quot;, c.delay = a) : c.skillP &gt; 0 ? (c.text = c.text.slice(0, -1), c.skillP--) : (c.skillI = (c.skillI + 1) % o.length, c.direction = &quot;forward&quot;)), r.textContent = c.text, r.appendChild(n(c.prefixP &lt; l.length ? Math.min(s, s + c.prefixP) : Math.min(s, t.length - c.skillP))), setTimeout(i, d)      }      var l = &quot;&quot;,      o = [&quot;青青陵上柏，磊磊涧中石。&quot;, &quot;人生天地间，忽如远行客。&quot;,&quot;斗酒相娱乐，聊厚不为薄。&quot;, &quot;驱车策驽马，游戏宛与洛。&quot;,&quot;洛中何郁郁，冠带自相索。&quot;,&quot;长衢罗夹巷，王侯多第宅。&quot;,&quot;两宫遥相望，双阙百余尺。&quot;,&quot;极宴娱心意，戚戚何所迫？&quot;].map(function (r) {      return r + &quot;&quot;      }),      a = 2,      g = 1,      s = 5,      d = 75,      b = [&quot;rgb(110,64,170)&quot;, &quot;rgb(150,61,179)&quot;, &quot;rgb(191,60,175)&quot;, &quot;rgb(228,65,157)&quot;, &quot;rgb(254,75,131)&quot;, &quot;rgb(255,94,99)&quot;, &quot;rgb(255,120,71)&quot;, &quot;rgb(251,150,51)&quot;, &quot;rgb(226,183,47)&quot;, &quot;rgb(198,214,60)&quot;, &quot;rgb(175,240,91)&quot;, &quot;rgb(127,246,88)&quot;, &quot;rgb(82,246,103)&quot;, &quot;rgb(48,239,130)&quot;, &quot;rgb(29,223,163)&quot;, &quot;rgb(26,199,194)&quot;, &quot;rgb(35,171,216)&quot;, &quot;rgb(54,140,225)&quot;, &quot;rgb(76,110,219)&quot;, &quot;rgb(96,84,200)&quot;],      c = {        text: &quot;&quot;,        prefixP: -s,        skillI: 0,        skillP: 0,        direction: &quot;forward&quot;,        delay: a,        step: g      };      i()      };      binft(document.getElementById(&#39;binft&#39;));  &lt;/script&gt;</code></pre><h2><span id="16-加入天气插件">16、加入天气插件</span></h2><p>前往</p><p><a href="https://cj.weather.com.cn/plugin/pc" target="_blank" rel="noopener">中国天气</a></p><p>这个网址查看详情</p><p>可以选用小视图，添加到link.ejs中</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/theme-sakura-pic11.jpg" alt="pic11"></p><h2><span id="17-顶部加载条">17、顶部加载条</span></h2><p>这个sakura有，但是想记录一下</p><p>head中加入</p><pre><code class="html">&lt;script src=&quot;//cdn.bootcss.com/pace/1.0.2/pace.min.js&quot;&gt;&lt;/script&gt;&lt;link href=&quot;//cdn.bootcss.com/pace/1.0.2/themes/pink/pace-theme-flash.css&quot; rel=&quot;stylesheet&quot;&gt;&lt;style&gt;  .pace .pace-progress {  background: #1E92FB; /*进度条颜色*/  height: 3px;  }  .pace .pace-progress-inner {  box-shadow: 0 0 10px #1E92FB, 0 0 5px     #1E92FB; /*阴影颜色*/  }  .pace .pace-activity {  border-top-color: #1E92FB;    /*上边框颜色*/  border-left-color: #1E92FB;    /*左边框颜色*/  }&lt;/style&gt;</code></pre><h2><span id="18-修改鼠标样式">18、修改鼠标样式</span></h2><p>在style.css中添加,sakura已有，可不管，当然啦，也可以去找一下其他好看的图标</p><pre><code>// 鼠标样式  * {      cursor: url(&quot;https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/cursor/normal.cur&quot;),auto!important  }  :active {      cursor: url(&quot;https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/cursor/No_Disponible.cur&quot;),auto!important  }</code></pre><h2><span id="19-评论系统">19、评论系统</span></h2><p>sakura自带的系统为valine，我们可以自行替换成其他的</p><p>就比如来必力，前往</p><p><a href="https://www.livere.com/" target="_blank" rel="noopener">来必力官网</a></p><p>注册登录后，点击菜单栏的【安装】，出现这个界面，直接点击【现在安装】</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/theme-sakura-pic12.jpg" alt="pic12"></p><p>前往管理页，点击左边的【代码管理】</p><p>你将会看到这样的代码</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/theme-sakura-pic13.jpg" alt="pic13"></p><p>data-uid后面的双引号中的内容剪切出来</p><p>然后到主题配置文件下修改，找到最下面的valine</p><p>将valine改成false，添加最后两行</p><pre><code class="yml"># Valinevaline: falsev_appId: Cu2IPPUW8BnkmwzFa8WrS9VC-gzGzoHszv_appKey: kgcNfaHXq91mfCaAIcbjGChl# liverelivere: true  data_uid: 刚刚复制的uid</code></pre><p>接着把下面的代码复制到comment.ejs中</p><pre><code class="html">&lt;% if (theme.livere &amp;&amp; post.comments) { %&gt;&lt;!-- 来必力City版安装代码 --&gt;&lt;div id=&quot;lv-container&quot; data-id=&quot;city&quot; data-uid=&quot;&lt;%= theme.livere.data_uid %&gt;&quot;&gt;&lt;script type=&quot;text/javascript&quot;&gt;   (function(d, s) {       var j, e = d.getElementsByTagName(s)[0];       if (typeof LivereTower === &#39;function&#39;) { return; }       j = d.createElement(s);       j.src = &#39;https://cdn-city.livere.com/js/embed.dist.js&#39;;       j.async = true;       e.parentNode.insertBefore(j, e);   })(document, &#39;script&#39;);&lt;/script&gt;&lt;noscript&gt;为正常使用来必力评论功能请激活JavaScript&lt;/noscript&gt;&lt;/div&gt;&lt;!-- City版安装代码已完成 --&gt;&lt;% } %&gt;</code></pre><h2><span id="20-在线联系">20、在线联系</span></h2><p>DaoVoice 在线联系</p><p>前往网站</p><p><a href="https://www.daovoice.io/" target="_blank" rel="noopener">DaoVoice</a></p><p>可以使用GitHub或者微信登录，也可以前往</p><p><a href="http://dashboard.daovoice.io/get-started?invite_code=da070d64" target="_blank" rel="noopener">这里注册</a></p><p>邀请码填：<code>da070d64</code></p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/theme-sakura-pic14.jpg" alt="pic14"></p><p>进入之后点击应用设置，再点击安装到网站</p><p>再将第一个代码复制到<code>&lt;/head&gt;</code>之前，将下面两个复制到layout.ejs</p><h2><span id="21-添加优美的标签页">21、添加优美的标签页</span></h2><p>这个标签页和分类页提取自matery，不得不说matery真的很漂亮</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/theme-sakura-pic15.jpg" alt="pic15"></p><p>首先我们要创建几个文件，文件所在目录如下</p><ul><li><p>layout</p><p>tags.ejs</p><ul><li><p>_widget</p><p>tag-cloud.ejs</p><p>tag-wordcloud.ejs</p></li></ul></li></ul><p>也就是tags.ejs放在layout根目录下，下面两个文件放在layout子目录_widget下</p><p>tags.ejs</p><pre><code class="ejs">&lt;%- partial(&#39;_partial/header&#39;) %&gt;&lt;main class=&quot;content&quot;&gt;    &lt;%- partial(&#39;_widget/tag-cloud&#39;) %&gt;    &lt;%- partial(&#39;_widget/tag-wordcloud&#39;) %&gt;&lt;/main&gt;</code></pre><p>_widget/tag-cloud.ejs</p><pre><code class="ejs">&lt;%var colorArr = [&#39;#F9EBEA&#39;, &#39;#F5EEF8&#39;, &#39;#D5F5E3&#39;, &#39;#E8F8F5&#39;, &#39;#FEF9E7&#39;,    &#39;#F8F9F9&#39;, &#39;#82E0AA&#39;, &#39;#D7BDE2&#39;, &#39;#A3E4D7&#39;, &#39;#85C1E9&#39;, &#39;#F8C471&#39;, &#39;#F9E79F&#39;, &#39;#FFF&#39;];var colorCount = colorArr.length;var hashCode = function (str) {    if (!str &amp;&amp; str.length === 0) {        return 0;    }    var hash = 0;    for (var i = 0, len = str.length; i &lt; len; i++) {        hash = ((hash &lt;&lt; 5) - hash) + str.charCodeAt(i);        hash |= 0;    }    return hash;};var i = 0;var isTag = is_tag();%&gt;&lt;div id=&quot;tags&quot; class=&quot;container chip-container&quot;&gt;    &lt;div class=&quot;card&quot;&gt;        &lt;div class=&quot;card-content&quot;&gt;            &lt;div class=&quot;tag-title center-align&quot;&gt;                &lt;i class=&quot;fa fa-tags&quot;&gt;&lt;/i&gt;&amp;nbsp;&amp;nbsp;文章标签            &lt;/div&gt;            &lt;div class=&quot;tag-chips&quot;&gt;                &lt;% site.tags.map(function(tag) { %&gt;                &lt;%                    i++;                    var color = i &gt;= colorCount ? colorArr[Math.abs(hashCode(tag.name) % colorCount)]                            : colorArr[i - 1];                %&gt;                &lt;a href=&quot;&lt;%- url_for(tag.path) %&gt;&quot; title=&quot;&lt;%- tag.name %&gt;: &lt;%- tag.length %&gt;&quot;&gt;                    &lt;span class=&quot;chip center-align waves-effect waves-light                            &lt;% if (isTag &amp;&amp; tag.name == page.tag) { %&gt; chip-active &lt;% } else { %&gt; chip-default &lt;% } %&gt;&quot;                            data-tagname=&quot;&lt;%- tag.name %&gt;&quot; style=&quot;background-color: &lt;%- color %&gt;;&quot;&gt;&lt;%- tag.name %&gt;                        &lt;span class=&quot;tag-length&quot;&gt;&lt;%- tag.length %&gt;&lt;/span&gt;                    &lt;/span&gt;                &lt;/a&gt;                &lt;% }); %&gt;            &lt;/div&gt;        &lt;/div&gt;    &lt;/div&gt;&lt;/div&gt;</code></pre><p>_widget/tag-wordcloud.ejs</p><pre><code class="ejs">&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://cdn.jsdelivr.net/gh/wallleap/cdn/css/jqcloud.css&quot;&gt;&lt;style type=&quot;text/css&quot;&gt;    #tag-wordcloud {        width: 100%;        height: 300px;    }&lt;/style&gt;&lt;div class=&quot;container&quot; data-aos=&quot;fade-up&quot;&gt;    &lt;div class=&quot;card&quot;&gt;        &lt;div id=&quot;tag-wordcloud&quot; class=&quot;card-content&quot;&gt;&lt;/div&gt;    &lt;/div&gt;&lt;/div&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;https://cdn.jsdelivr.net/gh/wallleap/cdn/js/jqcloud-1.0.4.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;    &lt;%    let tagWordArr = [];    site.tags.map(function(tag) {        tagWordArr.push({&#39;text&#39;: tag.name, &#39;weight&#39;: tag.length, &#39;link&#39;: tag.permalink});    });    let tagWords = JSON.stringify(tagWordArr);    %&gt;    $(&#39;#tag-wordcloud&#39;).jQCloud(&lt;%- tagWords %&gt;, {        autoResize: true    });&lt;/script&gt;</code></pre><p>将这段代码复制到style.css末尾</p><pre><code class="css">.chip-container {    margin-top: 60px;}.chip-container .tag-title {    margin-bottom: 10px;    color: #3C4858;    font-size: 1.75rem;    font-weight: 400;}.chip-container .tag-chips {    margin: 1rem auto 0.5rem;    max-width: 850px;    text-align: center;}.chip-container .tags-posts {    margin-top: 20px;}.chip-container .chip-default {    color: #34495e;}.chip-container .chip-active {    color: #FFF !important;    background: linear-gradient(to bottom right, #FF5E3A 0%, #FF2A68 100%) !important;    box-shadow: 2px 5px 10px #aaa !important;}.chip-container .chip {    margin: 10px 10px;    padding: 19px 14px;    display: inline-flex;    line-height: 0;    font-size: 1rem;    font-weight: 500;    border-radius: 5px;    cursor: pointer;    box-shadow: 0 3px 5px rgba(0, 0, 0, .12);}.chip-container .chip:hover {    color: #fff;    background: linear-gradient(to right, #4cbf30 0%, #0f9d58 100%) !important;}.chip .tag-length {    margin-left: 5px;    margin-right: -2px;    font-size: 0.5rem;}.chip-default .tag-length {    color: #e91e63;    margin-top: 1px;}.chip-active .tag-length {    color: #fff;}.tag-title.center-align{    margin-top: 100px;    text-align : center;}</code></pre><p>使用命令<code>hexo new page &quot;tags&quot;</code>，修改博客根目录下source/tags/index.md</p><pre><code>---title: tagsdate: 2020-03-09 13:50:05layout: tags---</code></pre><p>在<a href="http://localhost:4000/tags查看页面" target="_blank" rel="noopener">http://localhost:4000/tags查看页面</a></p><h2><span id="22-添加优美的分类页">22、添加优美的分类页</span></h2><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/theme-sakura-pic16.jpg" alt="pic16"></p><p>接着创建几个文件，文件所在目录如下</p><ul><li><p>layout</p><p>categories.ejs</p><ul><li><p>_widget</p><p>category-cloud.ejs</p><p>category-radar.ejs</p></li></ul></li></ul><p>也就是categories.ejs放在layout根目录下，下面两个文件放在layout子目录_widget下</p><p>添加代码</p><p>categories.ejs</p><pre><code class="ejs">&lt;%- partial(&#39;_partial/header&#39;) %&gt;&lt;main class=&quot;content&quot;&gt;    &lt;%- partial(&#39;_widget/category-cloud&#39;) %&gt;    &lt;% if (site.categories &amp;&amp; site.categories.length &gt; 0) { %&gt;    &lt;%- partial(&#39;_widget/category-radar&#39;) %&gt;    &lt;% } %&gt;&lt;/main&gt;</code></pre><p>category-cloud.ejs</p><pre><code class="ejs">&lt;%var colorArr = [&#39;#F9EBEA&#39;, &#39;#F5EEF8&#39;, &#39;#D5F5E3&#39;, &#39;#E8F8F5&#39;, &#39;#FEF9E7&#39;,    &#39;#F8F9F9&#39;, &#39;#82E0AA&#39;, &#39;#D7BDE2&#39;, &#39;#A3E4D7&#39;, &#39;#85C1E9&#39;, &#39;#F8C471&#39;, &#39;#F9E79F&#39;, &#39;#FFF&#39;];var colorCount = colorArr.length;var hashCode = function (str) {    if (!str &amp;&amp; str.length === 0) {        return 0;    }    var hash = 0;    for (var i = 0, len = str.length; i &lt; len; i++) {        hash = ((hash &lt;&lt; 5) - hash) + str.charCodeAt(i);        hash |= 0;    }    return hash;};var i = 0;var isCategory = is_category();%&gt;&lt;div id=&quot;category-cloud&quot; class=&quot;container chip-container&quot;&gt;    &lt;div class=&quot;card&quot;&gt;        &lt;div class=&quot;card-content&quot;&gt;            &lt;div class=&quot;tag-title center-align&quot;&gt;                &lt;i class=&quot;fa fa-bookmark&quot;&gt;&lt;/i&gt;&amp;nbsp;&amp;nbsp;文章分类            &lt;/div&gt;            &lt;div class=&quot;tag-chips&quot;&gt;                &lt;% if (site.categories &amp;&amp; site.categories.length &gt; 0) { %&gt;                &lt;% site.categories.map(function(category) { %&gt;                &lt;%                    i++;                    var color = i &gt;= colorCount ? colorArr[Math.abs(hashCode(category.name) % colorCount)]                            : colorArr[i - 1];                %&gt;                &lt;a href=&quot;&lt;%- url_for(category.path) %&gt;&quot; title=&quot;&lt;%- category.name %&gt;: &lt;%- category.length %&gt;&quot;&gt;                    &lt;span class=&quot;chip center-align waves-effect waves-light                            &lt;% if (isCategory &amp;&amp; category.name == page.category) { %&gt; chip-active &lt;% } else { %&gt; chip-default &lt;% } %&gt;&quot;                            style=&quot;background-color: &lt;%- color %&gt;;&quot;&gt;&lt;%- category.name %&gt;                        &lt;span class=&quot;tag-length&quot;&gt;&lt;%- category.length %&gt;&lt;/span&gt;                    &lt;/span&gt;                &lt;/a&gt;                &lt;% }); %&gt;                &lt;% } else { %&gt;                &lt;%= __(&#39;categoryEmptyTip&#39;) %&gt;                &lt;% } %&gt;            &lt;/div&gt;        &lt;/div&gt;    &lt;/div&gt;&lt;/div&gt;</code></pre><p>category-radar.ejs</p><pre><code class="ejs">&lt;style type=&quot;text/css&quot;&gt;    #category-radar {        margin-top: 50px;        width: 100%;        height: 360px;    }&lt;/style&gt;&lt;div class=&quot;container&quot; data-aos=&quot;fade-up&quot;&gt;    &lt;div class=&quot;card&quot;&gt;        &lt;div id=&quot;category-radar&quot; class=&quot;card-content&quot;&gt;&lt;/div&gt;    &lt;/div&gt;&lt;/div&gt;&lt;script type=&quot;text/javascript&quot; src=&quot;https://cdn.jsdelivr.net/gh/wallleap/cdn/js/echarts.min.js&quot;&gt;&lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt;    let radarChart = echarts.init(document.getElementById(&#39;category-radar&#39;));    &lt;%        var categories = site.categories;        // Find the maximum and average values of the post categories.        var radarValueArr = [];        categories.some(function(category) {            radarValueArr.push(category.length);        });        var max = Math.max.apply(null, radarValueArr) + Math.min.apply(null, radarValueArr);        // Calculate the data needed for the radar chart.        var indicatorArr = [];        categories.map(function(category) {            indicatorArr.push({&#39;name&#39;: category.name, &#39;max&#39;: max});        });        var indicatorData = JSON.stringify(indicatorArr);        var radarValueData = JSON.stringify(radarValueArr);    %&gt;    let option = {        title: {            left: &#39;center&#39;,            text: &#39;文章分类雷达图&#39;,            textStyle: {                fontWeight: 500,                fontSize: 22            }        },        tooltip: {},        radar: {            name: {                textStyle: {                    color: &#39;#3C4858&#39;                }            },            indicator: &lt;%- indicatorData %&gt;,            nameGap: 5,            center: [&#39;50%&#39;,&#39;55%&#39;],            radius: &#39;66%&#39;        },        series: [{            type: &#39;radar&#39;,            color: [&#39;#3ecf8e&#39;],            itemStyle: {normal: {areaStyle: {type: &#39;default&#39;}}},            data : [                {                    value : &lt;%- radarValueData %&gt;,                    name : &#39;文章分类数量&#39;                }            ]        }]    };    radarChart.setOption(option);&lt;/script&gt;</code></pre><p>接着用命令<code>hexo new page &quot;categories&quot;</code>创建分类页</p><p>修改博客根目录下source/categories/index.md</p><pre><code>---title: categoriesdate: 2020-03-09 13:50:05layout: categories---</code></pre><p><a href="http://localhost:4000/categories" target="_blank" rel="noopener">http://localhost:4000/categories</a></p><p>查看</p><p>接着就是把这两个页面放到导航栏上面去</p><p>修改主题配置文件，将这两行代码放到留言板之前</p><pre><code class="yml">    标签: {path: /tags/, fa: fa-tag }  分类: {path: /categories/, fa: fa-bookmark }</code></pre><h2><span id="23-已经集成的一些插件">23、已经集成的一些插件</span></h2><p>emoji表情：</p><p><a href="https://github.com/crimx/hexo-filter-github-emojis" target="_blank" rel="noopener">hexo-filter-github-emojis</a></p><p>正常插件安装版画板娘：</p><p><a href="https://github.com/EYHN/hexo-helper-live2d" target="_blank" rel="noopener">hexo-helper-live2d</a></p><p>图片懒加载(sakura已经有了，但不是这种方式)：</p><p><a href="https://www.npmjs.com/package/hexo-lazyload-image" target="_blank" rel="noopener">hexo-lazyload-image</a></p><p><a href="https://github.com/barretlee/hexo-lazyload-image-enhance" target="_blank" rel="noopener">hexo-lazyload-image-enhance</a></p><p>字数统计(好像没装)：</p><p><a href="https://github.com/willin/hexo-wordcount" target="_blank" rel="noopener">hexo-wordcount</a></p><p>fancybox：</p><p><a href="https://github.com/honjun/hexo-tag-fancybox_img" target="_blank" rel="noopener"><em>hexo</em>-tag-<em>fancybox</em>_img</a></p><p>bilibili：</p><p><a href="https://github.com/honjun/hexo-tag-bili" target="_blank" rel="noopener">hexo-tag-bili</a></p><h2><span id="24-文章末尾版权信息添加">24、文章末尾版权信息添加</span></h2><p>首先我们在_partial目录下新建文件article_copyright.ejs</p><pre><code class="ejs">&lt;div class=&quot;my_post_copyright&quot;&gt;  &lt;script src=&quot;//cdn.bootcss.com/clipboard.js/1.5.10/clipboard.min.js&quot;&gt;&lt;/script&gt;  &lt;!-- JS库 sweetalert 可修改路径 --&gt;  &lt;script src=&quot;https://cdn.bootcss.com/jquery/2.0.0/jquery.min.js&quot;&gt;&lt;/script&gt;  &lt;script src=&quot;https://unpkg.com/sweetalert/dist/sweetalert.min.js&quot;&gt;&lt;/script&gt;  &lt;p&gt;&lt;span&gt;本文标题:&lt;/span&gt;&lt;%= post.title %&gt;&lt;/p&gt;  &lt;p&gt;&lt;span&gt;文章字数:&lt;/span&gt;&lt;span class=&quot;post-count&quot;&gt;&lt;%=wordcount(post.content) %&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;span&gt;文章作者:&lt;/span&gt;&lt;a  title=&quot;&lt;%=config.author%&gt;&quot;&gt;&lt;%=config.author%&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;span&gt;发布时间:&lt;/span&gt;&lt;%= post.date.format(&quot;YYYY-MM-DD, HH:mm:ss&quot;) %&gt;&lt;/p&gt;  &lt;p&gt;&lt;span&gt;最后更新:&lt;/span&gt;&lt;%= post.updated.format(&quot;YYYY-MM-DD, HH:mm:ss&quot;) %&gt;&lt;/p&gt;  &lt;p&gt;&lt;span&gt;原始链接:&lt;/span&gt;&lt;/span&gt;&lt;a class=&quot;post-url&quot; href=&quot;&lt;%- url_for(post.path) %&gt;&quot; title=&quot;&lt;%= post.title %&gt;&quot;&gt;&lt;%= post.permalink %&gt;&lt;/a&gt;    &lt;span class=&quot;copy-path&quot;  title=&quot;点击复制文章链接&quot;&gt;&lt;i class=&quot;fa fa-clipboard&quot; data-clipboard-text=&quot;{{ page.permalink }}&quot;  aria-label=&quot;复制成功！&quot;&gt;&lt;/i&gt;&lt;/span&gt;  &lt;/p&gt;  &lt;p&gt;&lt;span&gt;许可协议:&lt;/span&gt;&lt;i class=&quot;fa fa-creative-commons&quot;&gt;&lt;/i&gt; &lt;a rel=&quot;license&quot; href=&quot;https://creativecommons.org/licenses/by-nc-nd/4.0/&quot; target=&quot;_blank&quot; title=&quot;Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)&quot;&gt;署名-非商业性使用-禁止演绎 4.0 国际&lt;/a&gt; 转载请保留原文链接及作者。&lt;/p&gt;  &lt;/div&gt;&lt;script&gt;     var clipboard = new Clipboard(&#39;.fa-clipboard&#39;);    $(&quot;.fa-clipboard&quot;).click(function(){      clipboard.on(&#39;success&#39;, function(){        swal({             title: &quot;&quot;,             text: &#39;复制成功&#39;,          icon: &quot;success&quot;,           showConfirmButton: true          });    });    });  &lt;/script&gt;</code></pre><p>需要文章字数的，一定要先安装字数统计插件，上面列出了，不用就把那行注释掉</p><p>接着将下面的代码</p><pre><code class="ejs">        &lt;% if (post.copyright) { %&gt;          &lt;%- partial(&#39;../_partial/article_copyright&#39;) %&gt;        &lt;% } %&gt;</code></pre><p>复制到common-article.ejs的这个位置</p><p><img src="https://cdn.jsdelivr.net/gh/wallleap/cdn@latest/img/loader/orange.progress-bar-stripe-loader.svg" data-original="https://cdn.jsdelivr.net/gh/wallleap/cdn/img/pic/test/theme-sakura-pic17.jpg" alt="pic17"></p><p>将CSS代码复制到style.css中</p><pre><code class="css">.my_post_copyright {  width: 85%;  max-width: 45em;  margin: 2.8em auto 0;  padding: 0.5em 1.0em;  border: 1px solid #d3d3d3;  font-size: 0.93rem;  line-height: 1.6em;  word-break: break-all;  background: rgba(255,255,255,0.4);}.my_post_copyright p{margin:0;}.my_post_copyright span {  display: inline-block;  width: 5.2em;  color: #b5b5b5;  font-weight: bold;}.my_post_copyright .raw {  margin-left: 1em;  width: 5em;}.my_post_copyright a {  color: #808080;  border-bottom:0;}.my_post_copyright a:hover {  color: #a3d2a3;  text-decoration: underline;}.my_post_copyright:hover .fa-clipboard {  color: #000;}.my_post_copyright .post-url:hover {  font-weight: normal;}.my_post_copyright .copy-path {  margin-left: 1em;  width: 1em;  +mobile(){display:none;}}.my_post_copyright .copy-path:hover {  color: #808080;  cursor: pointer;}</code></pre><h2><span id="25-添加404页面">25、添加404页面</span></h2><p>在博客根目录的source目录下新建404.html</p><p>随便找个模板，在开头加入代码</p><pre><code>---layout: false---</code></pre><p>例如</p><pre><code class="html">---layout: false---&lt;!doctype html&gt;&lt;html&gt;&lt;head&gt;&lt;meta charset=&quot;utf-8&quot;&gt;&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0&quot;&gt;&lt;meta name=&quot;apple-mobile-web-app-capable&quot; content=&quot;yes&quot;&gt;&lt;meta name=&quot;apple-mobile-web-app-status-bar-style&quot; content=&quot;black&quot;&gt;&lt;meta name=&quot;format-detection&quot; content=&quot;telephone=no&quot;&gt;&lt;title&gt;404&lt;/title&gt;&lt;style&gt;html, body{height:95%;}body{background: #0f3854;background: -webkit-radial-gradient(center ellipse, #0a2e38 0%, #000000 70%);background: radial-gradient(ellipse at center, #0a2e38 0%, #000000 70%);background-size: 100%;}p{margin:0;padding:0;}#clock{font-family: &#39;Share Tech Mono&#39;, monospace;color: #ffffff;text-align: center;position: absolute;left: 50%;top: 50%;-webkit-transform: translate(-50%, -50%);transform: translate(-50%, -50%);color: #daf6ff;text-shadow: 0 0 20px #0aafe6, 0 0 20px rgba(10, 175, 230, 0);}#clock .time{letter-spacing: 0.05em;font-size: 60px;padding: 5px 0;}#clock .date{letter-spacing:0.1em;font-size:15px;}#clock .text{letter-spacing: 0.1em;font-size:12px;padding:20px 0 0;}&lt;/style&gt;&lt;script type=&quot;text/javascript&quot;&gt;!function(e,t){&quot;object&quot;==typeof exports&amp;&amp;&quot;undefined&quot;!=typeof module?module.exports=t():&quot;function&quot;==typeof define&amp;&amp;define.amd?define(t):e.Vue=t()}(this,function(){&quot;use strict&quot;;function e(e){return void 0===e||null===e}function t(e){return void 0!==e&amp;&amp;null!==e}function n(e){return!0===e}function r(e){return!1===e}function i(e){return&quot;string&quot;==typeof e||&quot;number&quot;==typeof e}function o(e){return null!==e&amp;&amp;&quot;object&quot;==typeof e}function a(e){return&quot;[object Object]&quot;===Ti.call(e)}function s(e){return&quot;[object RegExp]&quot;===Ti.call(e)}function c(e){return null==e?&quot;&quot;:&quot;object&quot;==typeof e?JSON.stringify(e,null,2):String(e)}function u(e){var t=parseFloat(e);return isNaN(t)?e:t}function l(e,t){for(var n=Object.create(null),r=e.split(&quot;,&quot;),i=0;i&lt;r.length;i++)n[r[i]]=!0;return t?function(e){return n[e.toLowerCase()]}:function(e){return n[e]}}function f(e,t){if(e.length){var n=e.indexOf(t);if(n&gt;-1)return e.splice(n,1)}}function p(e,t){return ji.call(e,t)}function d(e){var t=Object.create(null);return function(n){return t[n]||(t[n]=e(n))}}function v(e,t){function n(n){var r=arguments.length;return r?r&gt;1?e.apply(t,arguments):e.call(t,n):e.call(t)}return n._length=e.length,n}function h(e,t){t=t||0;for(var n=e.length-t,r=new Array(n);n--;)r[n]=e[n+t];return r}function m(e,t){for(var n in t)e[n]=t[n];return e}function g(e){for(var t={},n=0;n&lt;e.length;n++)e[n]&amp;&amp;m(t,e[n]);return t}function y(){}function _(e,t){var n=o(e),r=o(t);if(!n||!r)return!n&amp;&amp;!r&amp;&amp;String(e)===String(t);try{return JSON.stringify(e)===JSON.stringify(t)}catch(n){return e===t}}function b(e,t){for(var n=0;n&lt;e.length;n++)if(_(e[n],t))return n;return-1}function $(e){var t=!1;return function(){t||(t=!0,e.apply(this,arguments))}}function C(e){var t=(e+&quot;&quot;).charCodeAt(0);return 36===t||95===t}function x(e,t,n,r){Object.defineProperty(e,t,{value:n,enumerable:!!r,writable:!0,configurable:!0})}function w(e){if(!zi.test(e)){var t=e.split(&quot;.&quot;);return function(e){for(var n=0;n&lt;t.length;n++){if(!e)return;e=e[t[n]]}return e}}}function k(e,t,n){if(Ui.errorHandler)Ui.errorHandler.call(null,e,t,n);else{if(!qi||&quot;undefined&quot;==typeof console)throw e;console.error(e)}}function A(e){return&quot;function&quot;==typeof e&amp;&amp;/native code/.test(e.toString())}function O(e){lo.target&amp;&amp;fo.push(lo.target),lo.target=e}function S(){lo.target=fo.pop()}function T(e,t){e.__proto__=t}function E(e,t,n){for(var r=0,i=n.length;r&lt;i;r++){var o=n[r];x(e,o,t[o])}}function j(e,t){if(o(e)){var n;return p(e,&quot;__ob__&quot;)&amp;&amp;e.__ob__ instanceof go?n=e.__ob__:mo.shouldConvert&amp;&amp;!oo()&amp;&amp;(Array.isArray(e)||a(e))&amp;&amp;Object.isExtensible(e)&amp;&amp;!e._isVue&amp;&amp;(n=new go(e)),t&amp;&amp;n&amp;&amp;n.vmCount++,n}}function N(e,t,n,r){var i=new lo,o=Object.getOwnPropertyDescriptor(e,t);if(!o||!1!==o.configurable){var a=o&amp;&amp;o.get,s=o&amp;&amp;o.set,c=j(n);Object.defineProperty(e,t,{enumerable:!0,configurable:!0,get:function(){var t=a?a.call(e):n;return lo.target&amp;&amp;(i.depend(),c&amp;&amp;c.dep.depend(),Array.isArray(t)&amp;&amp;D(t)),t},set:function(t){var r=a?a.call(e):n;t===r||t!==t&amp;&amp;r!==r||(s?s.call(e,t):n=t,c=j(t),i.notify())}})}}function L(e,t,n){if(Array.isArray(e)&amp;&amp;&quot;number&quot;==typeof t)return e.length=Math.max(e.length,t),e.splice(t,1,n),n;if(p(e,t))return e[t]=n,n;var r=e.__ob__;return e._isVue||r&amp;&amp;r.vmCount?n:r?(N(r.value,t,n),r.dep.notify(),n):(e[t]=n,n)}function I(e,t){if(Array.isArray(e)&amp;&amp;&quot;number&quot;==typeof t)return void e.splice(t,1);var n=e.__ob__;e._isVue||n&amp;&amp;n.vmCount||p(e,t)&amp;&amp;(delete e[t],n&amp;&amp;n.dep.notify())}function D(e){for(var t=void 0,n=0,r=e.length;n&lt;r;n++)t=e[n],t&amp;&amp;t.__ob__&amp;&amp;t.__ob__.dep.depend(),Array.isArray(t)&amp;&amp;D(t)}function M(e,t){if(!t)return e;for(var n,r,i,o=Object.keys(t),s=0;s&lt;o.length;s++)n=o[s],r=e[n],i=t[n],p(e,n)?a(r)&amp;&amp;a(i)&amp;&amp;M(r,i):L(e,n,i);return e}function P(e,t){return t?e?e.concat(t):Array.isArray(t)?t:[t]:e}function R(e,t){var n=Object.create(e||null);return t?m(n,t):n}function F(e){var t=e.props;if(t){var n,r,i,o={};if(Array.isArray(t))for(n=t.length;n--;)&quot;string&quot;==typeof(r=t[n])&amp;&amp;(i=Li(r),o[i]={type:null});else if(a(t))for(var s in t)r=t[s],i=Li(s),o[i]=a(r)?r:{type:r};e.props=o}}function B(e){var t=e.directives;if(t)for(var n in t){var r=t[n];&quot;function&quot;==typeof r&amp;&amp;(t[n]={bind:r,update:r})}}function H(e,t,n){function r(r){var i=yo[r]||_o;c[r]=i(e[r],t[r],n,r)}&quot;function&quot;==typeof t&amp;&amp;(t=t.options),F(t),B(t);var i=t.extends;if(i&amp;&amp;(e=H(e,i,n)),t.mixins)for(var o=0,a=t.mixins.length;o&lt;a;o++)e=H(e,t.mixins[o],n);var s,c={};for(s in e)r(s);for(s in t)p(e,s)||r(s);return c}function U(e,t,n,r){if(&quot;string&quot;==typeof n){var i=e[t];if(p(i,n))return i[n];var o=Li(n);if(p(i,o))return i[o];var a=Ii(o);if(p(i,a))return i[a];var s=i[n]||i[o]||i[a];return s}}function V(e,t,n,r){var i=t[e],o=!p(n,e),a=n[e];if(K(Boolean,i.type)&amp;&amp;(o&amp;&amp;!p(i,&quot;default&quot;)?a=!1:K(String,i.type)||&quot;&quot;!==a&amp;&amp;a!==Mi(e)||(a=!0)),void 0===a){a=z(r,i,e);var s=mo.shouldConvert;mo.shouldConvert=!0,j(a),mo.shouldConvert=s}return a}function z(e,t,n){if(p(t,&quot;default&quot;)){var r=t.default;return e&amp;&amp;e.$options.propsData&amp;&amp;void 0===e.$options.propsData[n]&amp;&amp;void 0!==e._props[n]?e._props[n]:&quot;function&quot;==typeof r&amp;&amp;&quot;Function&quot;!==J(t.type)?r.call(e):r}}function J(e){var t=e&amp;&amp;e.toString().match(/^\s*function (\w+)/);return t?t[1]:&quot;&quot;}function K(e,t){if(!Array.isArray(t))return J(t)===J(e);for(var n=0,r=t.length;n&lt;r;n++)if(J(t[n])===J(e))return!0;return!1}function q(e){return new bo(void 0,void 0,void 0,String(e))}function W(e){var t=new bo(e.tag,e.data,e.children,e.text,e.elm,e.context,e.componentOptions);return t.ns=e.ns,t.isStatic=e.isStatic,t.key=e.key,t.isComment=e.isComment,t.isCloned=!0,t}function Z(e){for(var t=e.length,n=new Array(t),r=0;r&lt;t;r++)n[r]=W(e[r]);return n}function G(e){function t(){var e=arguments,n=t.fns;if(!Array.isArray(n))return n.apply(null,arguments);for(var r=0;r&lt;n.length;r++)n[r].apply(null,e)}return t.fns=e,t}function Y(t,n,r,i,o){var a,s,c,u;for(a in t)s=t[a],c=n[a],u=wo(a),e(s)||(e(c)?(e(s.fns)&amp;&amp;(s=t[a]=G(s)),r(u.name,s,u.once,u.capture,u.passive)):s!==c&amp;&amp;(c.fns=s,t[a]=c));for(a in n)e(t[a])&amp;&amp;(u=wo(a),i(u.name,n[a],u.capture))}function Q(r,i,o){function a(){o.apply(this,arguments),f(s.fns,a)}var s,c=r[i];e(c)?s=G([a]):t(c.fns)&amp;&amp;n(c.merged)?(s=c,s.fns.push(a)):s=G([c,a]),s.merged=!0,r[i]=s}function X(n,r,i){var o=r.options.props;if(!e(o)){var a={},s=n.attrs,c=n.props;if(t(s)||t(c))for(var u in o){var l=Mi(u);ee(a,c,u,l,!0)||ee(a,s,u,l,!1)}return a}}function ee(e,n,r,i,o){if(t(n)){if(p(n,r))return e[r]=n[r],o||delete n[r],!0;if(p(n,i))return e[r]=n[i],o||delete n[i],!0}return!1}function te(e){for(var t=0;t&lt;e.length;t++)if(Array.isArray(e[t]))return Array.prototype.concat.apply([],e);return e}function ne(e){return i(e)?[q(e)]:Array.isArray(e)?ie(e):void 0}function re(e){return t(e)&amp;&amp;t(e.text)&amp;&amp;r(e.isComment)}function ie(r,o){var a,s,c,u=[];for(a=0;a&lt;r.length;a++)s=r[a],e(s)||&quot;boolean&quot;==typeof s||(c=u[u.length-1],Array.isArray(s)?u.push.apply(u,ie(s,(o||&quot;&quot;)+&quot;_&quot;+a)):i(s)?re(c)?c.text+=String(s):&quot;&quot;!==s&amp;&amp;u.push(q(s)):re(s)&amp;&amp;re(c)?u[u.length-1]=q(c.text+s.text):(n(r._isVList)&amp;&amp;t(s.tag)&amp;&amp;e(s.key)&amp;&amp;t(o)&amp;&amp;(s.key=&quot;__vlist&quot;+o+&quot;_&quot;+a+&quot;__&quot;),u.push(s)));return u}function oe(e,t){return o(e)?t.extend(e):e}function ae(r,i,a){if(n(r.error)&amp;&amp;t(r.errorComp))return r.errorComp;if(t(r.resolved))return r.resolved;if(n(r.loading)&amp;&amp;t(r.loadingComp))return r.loadingComp;if(!t(r.contexts)){var s=r.contexts=[a],c=!0,u=function(){for(var e=0,t=s.length;e&lt;t;e++)s[e].$forceUpdate()},l=$(function(e){r.resolved=oe(e,i),c||u()}),f=$(function(e){t(r.errorComp)&amp;&amp;(r.error=!0,u())}),p=r(l,f);return o(p)&amp;&amp;(&quot;function&quot;==typeof p.then?e(r.resolved)&amp;&amp;p.then(l,f):t(p.component)&amp;&amp;&quot;function&quot;==typeof p.component.then&amp;&amp;(p.component.then(l,f),t(p.error)&amp;&amp;(r.errorComp=oe(p.error,i)),t(p.loading)&amp;&amp;(r.loadingComp=oe(p.loading,i),0===p.delay?r.loading=!0:setTimeout(function(){e(r.resolved)&amp;&amp;e(r.error)&amp;&amp;(r.loading=!0,u())},p.delay||200)),t(p.timeout)&amp;&amp;setTimeout(function(){e(r.resolved)&amp;&amp;f(null)},p.timeout))),c=!1,r.loading?r.loadingComp:r.resolved}r.contexts.push(a)}function se(e){if(Array.isArray(e))for(var n=0;n&lt;e.length;n++){var r=e[n];if(t(r)&amp;&amp;t(r.componentOptions))return r}}function ce(e){e._events=Object.create(null),e._hasHookEvent=!1;var t=e.$options._parentListeners;t&amp;&amp;fe(e,t)}function ue(e,t,n){n?Co.$once(e,t):Co.$on(e,t)}function le(e,t){Co.$off(e,t)}function fe(e,t,n){Co=e,Y(t,n||{},ue,le,e)}function pe(e,t){var n={};if(!e)return n;for(var r=[],i=0,o=e.length;i&lt;o;i++){var a=e[i];if(a.context!==t&amp;&amp;a.functionalContext!==t||!a.data||null==a.data.slot)r.push(a);else{var s=a.data.slot,c=n[s]||(n[s]=[]);&quot;template&quot;===a.tag?c.push.apply(c,a.children):c.push(a)}}return r.every(de)||(n.default=r),n}function de(e){return e.isComment||&quot; &quot;===e.text}function ve(e,t){t=t||{};for(var n=0;n&lt;e.length;n++)Array.isArray(e[n])?ve(e[n],t):t[e[n].key]=e[n].fn;return t}function he(e){var t=e.$options,n=t.parent;if(n&amp;&amp;!t.abstract){for(;n.$options.abstract&amp;&amp;n.$parent;)n=n.$parent;n.$children.push(e)}e.$parent=n,e.$root=n?n.$root:e,e.$children=[],e.$refs={},e._watcher=null,e._inactive=null,e._directInactive=!1,e._isMounted=!1,e._isDestroyed=!1,e._isBeingDestroyed=!1}function me(e,t,n){e.$el=t,e.$options.render||(e.$options.render=xo),$e(e,&quot;beforeMount&quot;);var r;return r=function(){e._update(e._render(),n)},e._watcher=new Lo(e,r,y),n=!1,null==e.$vnode&amp;&amp;(e._isMounted=!0,$e(e,&quot;mounted&quot;)),e}function ge(e,t,n,r,i){var o=!!(i||e.$options._renderChildren||r.data.scopedSlots||e.$scopedSlots!==Vi);if(e.$options._parentVnode=r,e.$vnode=r,e._vnode&amp;&amp;(e._vnode.parent=r),e.$options._renderChildren=i,t&amp;&amp;e.$options.props){mo.shouldConvert=!1;for(var a=e._props,s=e.$options._propKeys||[],c=0;c&lt;s.length;c++){var u=s[c];a[u]=V(u,e.$options.props,t,e)}mo.shouldConvert=!0,e.$options.propsData=t}if(n){var l=e.$options._parentListeners;e.$options._parentListeners=n,fe(e,n,l)}o&amp;&amp;(e.$slots=pe(i,r.context),e.$forceUpdate())}function ye(e){for(;e&amp;&amp;(e=e.$parent);)if(e._inactive)return!0;return!1}function _e(e,t){if(t){if(e._directInactive=!1,ye(e))return}else if(e._directInactive)return;if(e._inactive||null===e._inactive){e._inactive=!1;for(var n=0;n&lt;e.$children.length;n++)_e(e.$children[n]);$e(e,&quot;activated&quot;)}}function be(e,t){if(!(t&amp;&amp;(e._directInactive=!0,ye(e))||e._inactive)){e._inactive=!0;for(var n=0;n&lt;e.$children.length;n++)be(e.$children[n]);$e(e,&quot;deactivated&quot;)}}function $e(e,t){var n=e.$options[t];if(n)for(var r=0,i=n.length;r&lt;i;r++)try{n[r].call(e)}catch(n){k(n,e,t+&quot; hook&quot;)}e._hasHookEvent&amp;&amp;e.$emit(&quot;hook:&quot;+t)}function Ce(){jo=Ao.length=Oo.length=0,So={},To=Eo=!1}function xe(){Eo=!0;var e,t;for(Ao.sort(function(e,t){return e.id-t.id}),jo=0;jo&lt;Ao.length;jo++)e=Ao[jo],t=e.id,So[t]=null,e.run();var n=Oo.slice(),r=Ao.slice();Ce(),Ae(n),we(r),ao&amp;&amp;Ui.devtools&amp;&amp;ao.emit(&quot;flush&quot;)}function we(e){for(var t=e.length;t--;){var n=e[t],r=n.vm;r._watcher===n&amp;&amp;r._isMounted&amp;&amp;$e(r,&quot;updated&quot;)}}function ke(e){e._inactive=!1,Oo.push(e)}function Ae(e){for(var t=0;t&lt;e.length;t++)e[t]._inactive=!0,_e(e[t],!0)}function Oe(e){var t=e.id;if(null==So[t]){if(So[t]=!0,Eo){for(var n=Ao.length-1;n&gt;jo&amp;&amp;Ao[n].id&gt;e.id;)n--;Ao.splice(n+1,0,e)}else Ao.push(e);To||(To=!0,co(xe))}}function Se(e){Io.clear(),Te(e,Io)}function Te(e,t){var n,r,i=Array.isArray(e);if((i||o(e))&amp;&amp;Object.isExtensible(e)){if(e.__ob__){var a=e.__ob__.dep.id;if(t.has(a))return;t.add(a)}if(i)for(n=e.length;n--;)Te(e[n],t);else for(r=Object.keys(e),n=r.length;n--;)Te(e[r[n]],t)}}function Ee(e,t,n){Do.get=function(){return this[t][n]},Do.set=function(e){this[t][n]=e},Object.defineProperty(e,n,Do)}function je(e){e._watchers=[];var t=e.$options;t.props&amp;&amp;Ne(e,t.props),t.methods&amp;&amp;Re(e,t.methods),t.data?Le(e):j(e._data={},!0),t.computed&amp;&amp;De(e,t.computed),t.watch&amp;&amp;Fe(e,t.watch)}function Ne(e,t){var n=e.$options.propsData||{},r=e._props={},i=e.$options._propKeys=[],o=!e.$parent;mo.shouldConvert=o;for(var a in t)!function(o){i.push(o);var a=V(o,t,n,e);N(r,o,a),o in e||Ee(e,&quot;_props&quot;,o)}(a);mo.shouldConvert=!0}function Le(e){var t=e.$options.data;t=e._data=&quot;function&quot;==typeof t?Ie(t,e):t||{},a(t)||(t={});for(var n=Object.keys(t),r=e.$options.props,i=n.length;i--;)r&amp;&amp;p(r,n[i])||C(n[i])||Ee(e,&quot;_data&quot;,n[i]);j(t,!0)}function Ie(e,t){try{return e.call(t)}catch(e){return k(e,t,&quot;data()&quot;),{}}}function De(e,t){var n=e._computedWatchers=Object.create(null);for(var r in t){var i=t[r],o=&quot;function&quot;==typeof i?i:i.get;n[r]=new Lo(e,o,y,Mo),r in e||Me(e,r,i)}}function Me(e,t,n){&quot;function&quot;==typeof n?(Do.get=Pe(t),Do.set=y):(Do.get=n.get?!1!==n.cache?Pe(t):n.get:y,Do.set=n.set?n.set:y),Object.defineProperty(e,t,Do)}function Pe(e){return function(){var t=this._computedWatchers&amp;&amp;this._computedWatchers[e];if(t)return t.dirty&amp;&amp;t.evaluate(),lo.target&amp;&amp;t.depend(),t.value}}function Re(e,t){e.$options.props;for(var n in t)e[n]=null==t[n]?y:v(t[n],e)}function Fe(e,t){for(var n in t){var r=t[n];if(Array.isArray(r))for(var i=0;i&lt;r.length;i++)Be(e,n,r[i]);else Be(e,n,r)}}function Be(e,t,n){var r;a(n)&amp;&amp;(r=n,n=n.handler),&quot;string&quot;==typeof n&amp;&amp;(n=e[n]),e.$watch(t,n,r)}function He(e){var t=e.$options.provide;t&amp;&amp;(e._provided=&quot;function&quot;==typeof t?t.call(e):t)}function Ue(e){var t=Ve(e.$options.inject,e);t&amp;&amp;Object.keys(t).forEach(function(n){N(e,n,t[n])})}function Ve(e,t){if(e){for(var n=Array.isArray(e),r=Object.create(null),i=n?e:so?Reflect.ownKeys(e):Object.keys(e),o=0;o&lt;i.length;o++)for(var a=i[o],s=n?a:e[a],c=t;c;){if(c._provided&amp;&amp;s in c._provided){r[a]=c._provided[s];break}c=c.$parent}return r}}function ze(e,n,r,i,o){var a={},s=e.options.props;if(t(s))for(var c in s)a[c]=V(c,s,n||{});else t(r.attrs)&amp;&amp;Je(a,r.attrs),t(r.props)&amp;&amp;Je(a,r.props);var u=Object.create(i),l=function(e,t,n,r){return Ye(u,e,t,n,r,!0)},f=e.options.render.call(null,l,{data:r,props:a,children:o,parent:i,listeners:r.on||{},injections:Ve(e.options.inject,i),slots:function(){return pe(o,i)}});return f instanceof bo&amp;&amp;(f.functionalContext=i,f.functionalOptions=e.options,r.slot&amp;&amp;((f.data||(f.data={})).slot=r.slot)),f}function Je(e,t){for(var n in t)e[Li(n)]=t[n]}function Ke(r,i,a,s,c){if(!e(r)){var u=a.$options._base;if(o(r)&amp;&amp;(r=u.extend(r)),&quot;function&quot;==typeof r&amp;&amp;(!e(r.cid)||void 0!==(r=ae(r,u,a)))){ft(r),i=i||{},t(i.model)&amp;&amp;Ge(r.options,i);var l=X(i,r,c);if(n(r.options.functional))return ze(r,l,i,a,s);var f=i.on;i.on=i.nativeOn,n(r.options.abstract)&amp;&amp;(i={}),We(i);var p=r.options.name||c;return new bo(&quot;vue-component-&quot;+r.cid+(p?&quot;-&quot;+p:&quot;&quot;),i,void 0,void 0,void 0,a,{Ctor:r,propsData:l,listeners:f,tag:c,children:s})}}}function qe(e,n,r,i){var o=e.componentOptions,a={_isComponent:!0,parent:n,propsData:o.propsData,_componentTag:o.tag,_parentVnode:e,_parentListeners:o.listeners,_renderChildren:o.children,_parentElm:r||null,_refElm:i||null},s=e.data.inlineTemplate;return t(s)&amp;&amp;(a.render=s.render,a.staticRenderFns=s.staticRenderFns),new o.Ctor(a)}function We(e){e.hook||(e.hook={});for(var t=0;t&lt;Ro.length;t++){var n=Ro[t],r=e.hook[n],i=Po[n];e.hook[n]=r?Ze(i,r):i}}function Ze(e,t){return function(n,r,i,o){e(n,r,i,o),t(n,r,i,o)}}function Ge(e,n){var r=e.model&amp;&amp;e.model.prop||&quot;value&quot;,i=e.model&amp;&amp;e.model.event||&quot;input&quot;;(n.props||(n.props={}))[r]=n.model.value;var o=n.on||(n.on={});t(o[i])?o[i]=[n.model.callback].concat(o[i]):o[i]=n.model.callback}function Ye(e,t,r,o,a,s){return(Array.isArray(r)||i(r))&amp;&amp;(a=o,o=r,r=void 0),n(s)&amp;&amp;(a=Bo),Qe(e,t,r,o,a)}function Qe(e,n,r,i,o){if(t(r)&amp;&amp;t(r.__ob__))return xo();if(!n)return xo();Array.isArray(i)&amp;&amp;&quot;function&quot;==typeof i[0]&amp;&amp;(r=r||{},r.scopedSlots={default:i[0]},i.length=0),o===Bo?i=ne(i):o===Fo&amp;&amp;(i=te(i));var a,s;if(&quot;string&quot;==typeof n){var c;s=Ui.getTagNamespace(n),a=Ui.isReservedTag(n)?new bo(Ui.parsePlatformTagName(n),r,i,void 0,void 0,e):t(c=U(e.$options,&quot;components&quot;,n))?Ke(c,r,e,i,n):new bo(n,r,i,void 0,void 0,e)}else a=Ke(n,r,e,i);return t(a)?(s&amp;&amp;Xe(a,s),a):xo()}function Xe(n,r){if(n.ns=r,&quot;foreignObject&quot;!==n.tag&amp;&amp;t(n.children))for(var i=0,o=n.children.length;i&lt;o;i++){var a=n.children[i];t(a.tag)&amp;&amp;e(a.ns)&amp;&amp;Xe(a,r)}}function et(e,n){var r,i,a,s,c;if(Array.isArray(e)||&quot;string&quot;==typeof e)for(r=new Array(e.length),i=0,a=e.length;i&lt;a;i++)r[i]=n(e[i],i);else if(&quot;number&quot;==typeof e)for(r=new Array(e),i=0;i&lt;e;i++)r[i]=n(i+1,i);else if(o(e))for(s=Object.keys(e),r=new Array(s.length),i=0,a=s.length;i&lt;a;i++)c=s[i],r[i]=n(e[c],c,i);return t(r)&amp;&amp;(r._isVList=!0),r}function tt(e,t,n,r){var i=this.$scopedSlots[e];if(i)return n=n||{},r&amp;&amp;m(n,r),i(n)||t;var o=this.$slots[e];return o||t}function nt(e){return U(this.$options,&quot;filters&quot;,e,!0)||Ri}function rt(e,t,n){var r=Ui.keyCodes[t]||n;return Array.isArray(r)?-1===r.indexOf(e):r!==e}function it(e,t,n,r){if(n)if(o(n)){Array.isArray(n)&amp;&amp;(n=g(n));var i;for(var a in n){if(&quot;class&quot;===a||&quot;style&quot;===a)i=e;else{var s=e.attrs&amp;&amp;e.attrs.type;i=r||Ui.mustUseProp(t,s,a)?e.domProps||(e.domProps={}):e.attrs||(e.attrs={})}a in i||(i[a]=n[a])}}else;return e}function ot(e,t){var n=this._staticTrees[e];return n&amp;&amp;!t?Array.isArray(n)?Z(n):W(n):(n=this._staticTrees[e]=this.$options.staticRenderFns[e].call(this._renderProxy),st(n,&quot;__static__&quot;+e,!1),n)}function at(e,t,n){return st(e,&quot;__once__&quot;+t+(n?&quot;_&quot;+n:&quot;&quot;),!0),e}function st(e,t,n){if(Array.isArray(e))for(var r=0;r&lt;e.length;r++)e[r]&amp;&amp;&quot;string&quot;!=typeof e[r]&amp;&amp;ct(e[r],t+&quot;_&quot;+r,n);else ct(e,t,n)}function ct(e,t,n){e.isStatic=!0,e.key=t,e.isOnce=n}function ut(e){e._vnode=null,e._staticTrees=null;var t=e.$vnode=e.$options._parentVnode,n=t&amp;&amp;t.context;e.$slots=pe(e.$options._renderChildren,n),e.$scopedSlots=Vi,e._c=function(t,n,r,i){return Ye(e,t,n,r,i,!1)},e.$createElement=function(t,n,r,i){return Ye(e,t,n,r,i,!0)}}function lt(e,t){var n=e.$options=Object.create(e.constructor.options);n.parent=t.parent,n.propsData=t.propsData,n._parentVnode=t._parentVnode,n._parentListeners=t._parentListeners,n._renderChildren=t._renderChildren,n._componentTag=t._componentTag,n._parentElm=t._parentElm,n._refElm=t._refElm,t.render&amp;&amp;(n.render=t.render,n.staticRenderFns=t.staticRenderFns)}function ft(e){var t=e.options;if(e.super){var n=ft(e.super);if(n!==e.superOptions){e.superOptions=n;var r=pt(e);r&amp;&amp;m(e.extendOptions,r),t=e.options=H(n,e.extendOptions),t.name&amp;&amp;(t.components[t.name]=e)}}return t}function pt(e){var t,n=e.options,r=e.extendOptions,i=e.sealedOptions;for(var o in n)n[o]!==i[o]&amp;&amp;(t||(t={}),t[o]=dt(n[o],r[o],i[o]));return t}function dt(e,t,n){if(Array.isArray(e)){var r=[];n=Array.isArray(n)?n:[n],t=Array.isArray(t)?t:[t];for(var i=0;i&lt;e.length;i++)(t.indexOf(e[i])&gt;=0||n.indexOf(e[i])&lt;0)&amp;&amp;r.push(e[i]);return r}return e}function vt(e){this._init(e)}function ht(e){e.use=function(e){if(e.installed)return this;var t=h(arguments,1);return t.unshift(this),&quot;function&quot;==typeof e.install?e.install.apply(e,t):&quot;function&quot;==typeof e&amp;&amp;e.apply(null,t),e.installed=!0,this}}function mt(e){e.mixin=function(e){return this.options=H(this.options,e),this}}function gt(e){e.cid=0;var t=1;e.extend=function(e){e=e||{};var n=this,r=n.cid,i=e._Ctor||(e._Ctor={});if(i[r])return i[r];var o=e.name||n.options.name,a=function(e){this._init(e)};return a.prototype=Object.create(n.prototype),a.prototype.constructor=a,a.cid=t++,a.options=H(n.options,e),a.super=n,a.options.props&amp;&amp;yt(a),a.options.computed&amp;&amp;_t(a),a.extend=n.extend,a.mixin=n.mixin,a.use=n.use,Bi.forEach(function(e){a[e]=n[e]}),o&amp;&amp;(a.options.components[o]=a),a.superOptions=n.options,a.extendOptions=e,a.sealedOptions=m({},a.options),i[r]=a,a}}function yt(e){var t=e.options.props;for(var n in t)Ee(e.prototype,&quot;_props&quot;,n)}function _t(e){var t=e.options.computed;for(var n in t)Me(e.prototype,n,t[n])}function bt(e){Bi.forEach(function(t){e[t]=function(e,n){return n?(&quot;component&quot;===t&amp;&amp;a(n)&amp;&amp;(n.name=n.name||e,n=this.options._base.extend(n)),&quot;directive&quot;===t&amp;&amp;&quot;function&quot;==typeof n&amp;&amp;(n={bind:n,update:n}),this.options[t+&quot;s&quot;][e]=n,n):this.options[t+&quot;s&quot;][e]}})}function $t(e){return e&amp;&amp;(e.Ctor.options.name||e.tag)}function Ct(e,t){return&quot;string&quot;==typeof e?e.split(&quot;,&quot;).indexOf(t)&gt;-1:!!s(e)&amp;&amp;e.test(t)}function xt(e,t,n){for(var r in e){var i=e[r];if(i){var o=$t(i.componentOptions);o&amp;&amp;!n(o)&amp;&amp;(i!==t&amp;&amp;wt(i),e[r]=null)}}}function wt(e){e&amp;&amp;e.componentInstance.$destroy()}function kt(e){for(var n=e.data,r=e,i=e;t(i.componentInstance);)i=i.componentInstance._vnode,i.data&amp;&amp;(n=At(i.data,n));for(;t(r=r.parent);)r.data&amp;&amp;(n=At(n,r.data));return Ot(n)}function At(e,n){return{staticClass:St(e.staticClass,n.staticClass),class:t(e.class)?[e.class,n.class]:n.class}}function Ot(e){var n=e.class,r=e.staticClass;return t(r)||t(n)?St(r,Tt(n)):&quot;&quot;}function St(e,t){return e?t?e+&quot; &quot;+t:e:t||&quot;&quot;}function Tt(n){if(e(n))return&quot;&quot;;if(&quot;string&quot;==typeof n)return n;var r=&quot;&quot;;if(Array.isArray(n)){for(var i,a=0,s=n.length;a&lt;s;a++)t(n[a])&amp;&amp;t(i=Tt(n[a]))&amp;&amp;&quot;&quot;!==i&amp;&amp;(r+=i+&quot; &quot;);return r.slice(0,-1)}if(o(n)){for(var c in n)n[c]&amp;&amp;(r+=c+&quot; &quot;);return r.slice(0,-1)}return r}function Et(e){return fa(e)?&quot;svg&quot;:&quot;math&quot;===e?&quot;math&quot;:void 0}function jt(e){if(!qi)return!0;if(da(e))return!1;if(e=e.toLowerCase(),null!=va[e])return va[e];var t=document.createElement(e);return e.indexOf(&quot;-&quot;)&gt;-1?va[e]=t.constructor===window.HTMLUnknownElement||t.constructor===window.HTMLElement:va[e]=/HTMLUnknownElement/.test(t.toString())}function Nt(e){if(&quot;string&quot;==typeof e){var t=document.querySelector(e);return t||document.createElement(&quot;div&quot;)}return e}function Lt(e,t){var n=document.createElement(e);return&quot;select&quot;!==e?n:(t.data&amp;&amp;t.data.attrs&amp;&amp;void 0!==t.data.attrs.multiple&amp;&amp;n.setAttribute(&quot;multiple&quot;,&quot;multiple&quot;),n)}function It(e,t){return document.createElementNS(ua[e],t)}function Dt(e){return document.createTextNode(e)}function Mt(e){return document.createComment(e)}function Pt(e,t,n){e.insertBefore(t,n)}function Rt(e,t){e.removeChild(t)}function Ft(e,t){e.appendChild(t)}function Bt(e){return e.parentNode}function Ht(e){return e.nextSibling}function Ut(e){return e.tagName}function Vt(e,t){e.textContent=t}function zt(e,t,n){e.setAttribute(t,n)}function Jt(e,t){var n=e.data.ref;if(n){var r=e.context,i=e.componentInstance||e.elm,o=r.$refs;t?Array.isArray(o[n])?f(o[n],i):o[n]===i&amp;&amp;(o[n]=void 0):e.data.refInFor?Array.isArray(o[n])&amp;&amp;o[n].indexOf(i)&lt;0?o[n].push(i):o[n]=[i]:o[n]=i}}function Kt(e,n){return e.key===n.key&amp;&amp;e.tag===n.tag&amp;&amp;e.isComment===n.isComment&amp;&amp;t(e.data)===t(n.data)&amp;&amp;qt(e,n)}function qt(e,n){if(&quot;input&quot;!==e.tag)return!0;var r;return(t(r=e.data)&amp;&amp;t(r=r.attrs)&amp;&amp;r.type)===(t(r=n.data)&amp;&amp;t(r=r.attrs)&amp;&amp;r.type)}function Wt(e,n,r){var i,o,a={};for(i=n;i&lt;=r;++i)o=e[i].key,t(o)&amp;&amp;(a[o]=i);return a}function Zt(e,t){(e.data.directives||t.data.directives)&amp;&amp;Gt(e,t)}function Gt(e,t){var n,r,i,o=e===ga,a=t===ga,s=Yt(e.data.directives,e.context),c=Yt(t.data.directives,t.context),u=[],l=[];for(n in c)r=s[n],i=c[n],r?(i.oldValue=r.value,Xt(i,&quot;update&quot;,t,e),i.def&amp;&amp;i.def.componentUpdated&amp;&amp;l.push(i)):(Xt(i,&quot;bind&quot;,t,e),i.def&amp;&amp;i.def.inserted&amp;&amp;u.push(i));if(u.length){var f=function(){for(var n=0;n&lt;u.length;n++)Xt(u[n],&quot;inserted&quot;,t,e)};o?Q(t.data.hook||(t.data.hook={}),&quot;insert&quot;,f):f()}if(l.length&amp;&amp;Q(t.data.hook||(t.data.hook={}),&quot;postpatch&quot;,function(){for(var n=0;n&lt;l.length;n++)Xt(l[n],&quot;componentUpdated&quot;,t,e)}),!o)for(n in s)c[n]||Xt(s[n],&quot;unbind&quot;,e,e,a)}function Yt(e,t){var n=Object.create(null);if(!e)return n;var r,i;for(r=0;r&lt;e.length;r++)i=e[r],i.modifiers||(i.modifiers=ba),n[Qt(i)]=i,i.def=U(t.$options,&quot;directives&quot;,i.name,!0);return n}function Qt(e){return e.rawName||e.name+&quot;.&quot;+Object.keys(e.modifiers||{}).join(&quot;.&quot;)}function Xt(e,t,n,r,i){var o=e.def&amp;&amp;e.def[t];if(o)try{o(n.elm,e,n,r,i)}catch(r){k(r,n.context,&quot;directive &quot;+e.name+&quot; &quot;+t+&quot; hook&quot;)}}function en(n,r){if(!e(n.data.attrs)||!e(r.data.attrs)){var i,o,a=r.elm,s=n.data.attrs||{},c=r.data.attrs||{};t(c.__ob__)&amp;&amp;(c=r.data.attrs=m({},c));for(i in c)o=c[i],s[i]!==o&amp;&amp;tn(a,i,o);Gi&amp;&amp;c.value!==s.value&amp;&amp;tn(a,&quot;value&quot;,c.value);for(i in s)e(c[i])&amp;&amp;(aa(i)?a.removeAttributeNS(oa,sa(i)):ra(i)||a.removeAttribute(i))}}function tn(e,t,n){ia(t)?ca(n)?e.removeAttribute(t):e.setAttribute(t,t):ra(t)?e.setAttribute(t,ca(n)||&quot;false&quot;===n?&quot;false&quot;:&quot;true&quot;):aa(t)?ca(n)?e.removeAttributeNS(oa,sa(t)):e.setAttributeNS(oa,t,n):ca(n)?e.removeAttribute(t):e.setAttribute(t,n)}function nn(n,r){var i=r.elm,o=r.data,a=n.data;if(!(e(o.staticClass)&amp;&amp;e(o.class)&amp;&amp;(e(a)||e(a.staticClass)&amp;&amp;e(a.class)))){var s=kt(r),c=i._transitionClasses;t(c)&amp;&amp;(s=St(s,Tt(c))),s!==i._prevClass&amp;&amp;(i.setAttribute(&quot;class&quot;,s),i._prevClass=s)}}function rn(e){function t(){(a||(a=[])).push(e.slice(v,i).trim()),v=i+1}var n,r,i,o,a,s=!1,c=!1,u=!1,l=!1,f=0,p=0,d=0,v=0;for(i=0;i&lt;e.length;i++)if(r=n,n=e.charCodeAt(i),s)39===n&amp;&amp;92!==r&amp;&amp;(s=!1);else if(c)34===n&amp;&amp;92!==r&amp;&amp;(c=!1);else if(u)96===n&amp;&amp;92!==r&amp;&amp;(u=!1);else if(l)47===n&amp;&amp;92!==r&amp;&amp;(l=!1);else if(124!==n||124===e.charCodeAt(i+1)||124===e.charCodeAt(i-1)||f||p||d){switch(n){case 34:c=!0;break;case 39:s=!0;break;case 96:u=!0;break;case 40:d++;break;case 41:d--;break;case 91:p++;break;case 93:p--;break;case 123:f++;break;case 125:f--}if(47===n){for(var h=i-1,m=void 0;h&gt;=0&amp;&amp;&quot; &quot;===(m=e.charAt(h));h--);m&amp;&amp;wa.test(m)||(l=!0)}}else void 0===o?(v=i+1,o=e.slice(0,i).trim()):t();if(void 0===o?o=e.slice(0,i).trim():0!==v&amp;&amp;t(),a)for(i=0;i&lt;a.length;i++)o=on(o,a[i]);return o}function on(e,t){var n=t.indexOf(&quot;(&quot;);return n&lt;0?&#39;_f(&quot;&#39;+t+&#39;&quot;)(&#39;+e+&quot;)&quot;:&#39;_f(&quot;&#39;+t.slice(0,n)+&#39;&quot;)(&#39;+e+&quot;,&quot;+t.slice(n+1)}function an(e){console.error(&quot;[Vue compiler]: &quot;+e)}function sn(e,t){return e?e.map(function(e){return e[t]}).filter(function(e){return e}):[]}function cn(e,t,n){(e.props||(e.props=[])).push({name:t,value:n})}function un(e,t,n){(e.attrs||(e.attrs=[])).push({name:t,value:n})}function ln(e,t,n,r,i,o){(e.directives||(e.directives=[])).push({name:t,rawName:n,value:r,arg:i,modifiers:o})}function fn(e,t,n,r,i,o){r&amp;&amp;r.capture&amp;&amp;(delete r.capture,t=&quot;!&quot;+t),r&amp;&amp;r.once&amp;&amp;(delete r.once,t=&quot;~&quot;+t),r&amp;&amp;r.passive&amp;&amp;(delete r.passive,t=&quot;&amp;&quot;+t);var a;r&amp;&amp;r.native?(delete r.native,a=e.nativeEvents||(e.nativeEvents={})):a=e.events||(e.events={});var s={value:n,modifiers:r},c=a[t];Array.isArray(c)?i?c.unshift(s):c.push(s):a[t]=c?i?[s,c]:[c,s]:s}function pn(e,t,n){var r=dn(e,&quot;:&quot;+t)||dn(e,&quot;v-bind:&quot;+t);if(null!=r)return rn(r);if(!1!==n){var i=dn(e,t);if(null!=i)return JSON.stringify(i)}}function dn(e,t){var n;if(null!=(n=e.attrsMap[t]))for(var r=e.attrsList,i=0,o=r.length;i&lt;o;i++)if(r[i].name===t){r.splice(i,1);break}return n}function vn(e,t,n){var r=n||{},i=r.number,o=r.trim,a=&quot;$$v&quot;;o&amp;&amp;(a=&quot;(typeof $$v === &#39;string&#39;? $$v.trim(): $$v)&quot;),i&amp;&amp;(a=&quot;_n(&quot;+a+&quot;)&quot;);var s=hn(t,a);e.model={value:&quot;(&quot;+t+&quot;)&quot;,expression:&#39;&quot;&#39;+t+&#39;&quot;&#39;,callback:&quot;function ($$v) {&quot;+s+&quot;}&quot;}}function hn(e,t){var n=mn(e);return null===n.idx?e+&quot;=&quot;+t:&quot;var $$exp = &quot;+n.exp+&quot;, $$idx = &quot;+n.idx+&quot;;if (!Array.isArray($$exp)){&quot;+e+&quot;=&quot;+t+&quot;}else{$$exp.splice($$idx, 1, &quot;+t+&quot;)}&quot;}function mn(e){if(Ko=e,Jo=Ko.length,Wo=Zo=Go=0,e.indexOf(&quot;[&quot;)&lt;0||e.lastIndexOf(&quot;]&quot;)&lt;Jo-1)return{exp:e,idx:null};for(;!yn();)qo=gn(),_n(qo)?$n(qo):91===qo&amp;&amp;bn(qo);return{exp:e.substring(0,Zo),idx:e.substring(Zo+1,Go)}}function gn(){return Ko.charCodeAt(++Wo)}function yn(){return Wo&gt;=Jo}function _n(e){return 34===e||39===e}function bn(e){var t=1;for(Zo=Wo;!yn();)if(e=gn(),_n(e))$n(e);else if(91===e&amp;&amp;t++,93===e&amp;&amp;t--,0===t){Go=Wo;break}}function $n(e){for(var t=e;!yn()&amp;&amp;(e=gn())!==t;);}function Cn(e,t,n){Yo=n;var r=t.value,i=t.modifiers,o=e.tag,a=e.attrsMap.type;if(&quot;select&quot;===o)kn(e,r,i);else if(&quot;input&quot;===o&amp;&amp;&quot;checkbox&quot;===a)xn(e,r,i);else if(&quot;input&quot;===o&amp;&amp;&quot;radio&quot;===a)wn(e,r,i);else if(&quot;input&quot;===o||&quot;textarea&quot;===o)An(e,r,i);else if(!Ui.isReservedTag(o))return vn(e,r,i),!1;return!0}function xn(e,t,n){var r=n&amp;&amp;n.number,i=pn(e,&quot;value&quot;)||&quot;null&quot;,o=pn(e,&quot;true-value&quot;)||&quot;true&quot;,a=pn(e,&quot;false-value&quot;)||&quot;false&quot;;cn(e,&quot;checked&quot;,&quot;Array.isArray(&quot;+t+&quot;)?_i(&quot;+t+&quot;,&quot;+i+&quot;)&gt;-1&quot;+(&quot;true&quot;===o?&quot;:(&quot;+t+&quot;)&quot;:&quot;:_q(&quot;+t+&quot;,&quot;+o+&quot;)&quot;)),fn(e,Aa,&quot;var $$a=&quot;+t+&quot;,$$el=$event.target,$$c=$$el.checked?(&quot;+o+&quot;):(&quot;+a+&quot;);if(Array.isArray($$a)){var $$v=&quot;+(r?&quot;_n(&quot;+i+&quot;)&quot;:i)+&quot;,$$i=_i($$a,$$v);if($$c){$$i&lt;0&amp;&amp;(&quot;+t+&quot;=$$a.concat($$v))}else{$$i&gt;-1&amp;&amp;(&quot;+t+&quot;=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{&quot;+hn(t,&quot;$$c&quot;)+&quot;}&quot;,null,!0)}function wn(e,t,n){var r=n&amp;&amp;n.number,i=pn(e,&quot;value&quot;)||&quot;null&quot;;i=r?&quot;_n(&quot;+i+&quot;)&quot;:i,cn(e,&quot;checked&quot;,&quot;_q(&quot;+t+&quot;,&quot;+i+&quot;)&quot;),fn(e,Aa,hn(t,i),null,!0)}function kn(e,t,n){var r=n&amp;&amp;n.number,i=&#39;Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = &quot;_value&quot; in o ? o._value : o.value;return &#39;+(r?&quot;_n(val)&quot;:&quot;val&quot;)+&quot;})&quot;,o=&quot;var $$selectedVal = &quot;+i+&quot;;&quot;;o=o+&quot; &quot;+hn(t,&quot;$event.target.multiple ? $$selectedVal : $$selectedVal[0]&quot;),fn(e,&quot;change&quot;,o,null,!0)}function An(e,t,n){var r=e.attrsMap.type,i=n||{},o=i.lazy,a=i.number,s=i.trim,c=!o&amp;&amp;&quot;range&quot;!==r,u=o?&quot;change&quot;:&quot;range&quot;===r?ka:&quot;input&quot;,l=&quot;$event.target.value&quot;;s&amp;&amp;(l=&quot;$event.target.value.trim()&quot;),a&amp;&amp;(l=&quot;_n(&quot;+l+&quot;)&quot;);var f=hn(t,l);c&amp;&amp;(f=&quot;if($event.target.composing)return;&quot;+f),cn(e,&quot;value&quot;,&quot;(&quot;+t+&quot;)&quot;),fn(e,u,f,null,!0),(s||a||&quot;number&quot;===r)&amp;&amp;fn(e,&quot;blur&quot;,&quot;$forceUpdate()&quot;)}function On(e){var n;t(e[ka])&amp;&amp;(n=Zi?&quot;change&quot;:&quot;input&quot;,e[n]=[].concat(e[ka],e[n]||[]),delete e[ka]),t(e[Aa])&amp;&amp;(n=eo?&quot;click&quot;:&quot;change&quot;,e[n]=[].concat(e[Aa],e[n]||[]),delete e[Aa])}function Sn(e,t,n,r,i){if(n){var o=t,a=Qo;t=function(n){null!==(1===arguments.length?o(n):o.apply(null,arguments))&amp;&amp;Tn(e,t,r,a)}}Qo.addEventListener(e,t,to?{capture:r,passive:i}:r)}function Tn(e,t,n,r){(r||Qo).removeEventListener(e,t,n)}function En(t,n){if(!e(t.data.on)||!e(n.data.on)){var r=n.data.on||{},i=t.data.on||{};Qo=n.elm,On(r),Y(r,i,Sn,Tn,n.context)}}function jn(n,r){if(!e(n.data.domProps)||!e(r.data.domProps)){var i,o,a=r.elm,s=n.data.domProps||{},c=r.data.domProps||{};t(c.__ob__)&amp;&amp;(c=r.data.domProps=m({},c));for(i in s)e(c[i])&amp;&amp;(a[i]=&quot;&quot;);for(i in c)if(o=c[i],&quot;textContent&quot;!==i&amp;&amp;&quot;innerHTML&quot;!==i||(r.children&amp;&amp;(r.children.length=0),o!==s[i]))if(&quot;value&quot;===i){a._value=o;var u=e(o)?&quot;&quot;:String(o);Nn(a,r,u)&amp;&amp;(a.value=u)}else a[i]=o}}function Nn(e,t,n){return!e.composing&amp;&amp;(&quot;option&quot;===t.tag||Ln(e,n)||In(e,n))}function Ln(e,t){return document.activeElement!==e&amp;&amp;e.value!==t}function In(e,n){var r=e.value,i=e._vModifiers;return t(i)&amp;&amp;i.number||&quot;number&quot;===e.type?u(r)!==u(n):t(i)&amp;&amp;i.trim?r.trim()!==n.trim():r!==n}function Dn(e){var t=Mn(e.style);return e.staticStyle?m(e.staticStyle,t):t}function Mn(e){return Array.isArray(e)?g(e):&quot;string&quot;==typeof e?Ta(e):e}function Pn(e,t){var n,r={};if(t)for(var i=e;i.componentInstance;)i=i.componentInstance._vnode,i.data&amp;&amp;(n=Dn(i.data))&amp;&amp;m(r,n);(n=Dn(e.data))&amp;&amp;m(r,n);for(var o=e;o=o.parent;)o.data&amp;&amp;(n=Dn(o.data))&amp;&amp;m(r,n);return r}function Rn(n,r){var i=r.data,o=n.data;if(!(e(i.staticStyle)&amp;&amp;e(i.style)&amp;&amp;e(o.staticStyle)&amp;&amp;e(o.style))){var a,s,c=r.elm,u=o.staticStyle,l=o.normalizedStyle||o.style||{},f=u||l,p=Mn(r.data.style)||{};r.data.normalizedStyle=t(p.__ob__)?m({},p):p;var d=Pn(r,!0);for(s in f)e(d[s])&amp;&amp;Na(c,s,&quot;&quot;);for(s in d)(a=d[s])!==f[s]&amp;&amp;Na(c,s,null==a?&quot;&quot;:a)}}function Fn(e,t){if(t&amp;&amp;(t=t.trim()))if(e.classList)t.indexOf(&quot; &quot;)&gt;-1?t.split(/\s+/).forEach(function(t){return e.classList.add(t)}):e.classList.add(t);else{var n=&quot; &quot;+(e.getAttribute(&quot;class&quot;)||&quot;&quot;)+&quot; &quot;;n.indexOf(&quot; &quot;+t+&quot; &quot;)&lt;0&amp;&amp;e.setAttribute(&quot;class&quot;,(n+t).trim())}}function Bn(e,t){if(t&amp;&amp;(t=t.trim()))if(e.classList)t.indexOf(&quot; &quot;)&gt;-1?t.split(/\s+/).forEach(function(t){return e.classList.remove(t)}):e.classList.remove(t);else{for(var n=&quot; &quot;+(e.getAttribute(&quot;class&quot;)||&quot;&quot;)+&quot; &quot;,r=&quot; &quot;+t+&quot; &quot;;n.indexOf(r)&gt;=0;)n=n.replace(r,&quot; &quot;);e.setAttribute(&quot;class&quot;,n.trim())}}function Hn(e){if(e){if(&quot;object&quot;==typeof e){var t={};return!1!==e.css&amp;&amp;m(t,Ma(e.name||&quot;v&quot;)),m(t,e),t}return&quot;string&quot;==typeof e?Ma(e):void 0}}function Un(e){za(function(){za(e)})}function Vn(e,t){(e._transitionClasses||(e._transitionClasses=[])).push(t),Fn(e,t)}function zn(e,t){e._transitionClasses&amp;&amp;f(e._transitionClasses,t),Bn(e,t)}function Jn(e,t,n){var r=Kn(e,t),i=r.type,o=r.timeout,a=r.propCount;if(!i)return n();var s=i===Ra?Ha:Va,c=0,u=function(){e.removeEventListener(s,l),n()},l=function(t){t.target===e&amp;&amp;++c&gt;=a&amp;&amp;u()};setTimeout(function(){c&lt;a&amp;&amp;u()},o+1),e.addEventListener(s,l)}function Kn(e,t){var n,r=window.getComputedStyle(e),i=r[Ba+&quot;Delay&quot;].split(&quot;, &quot;),o=r[Ba+&quot;Duration&quot;].split(&quot;, &quot;),a=qn(i,o),s=r[Ua+&quot;Delay&quot;].split(&quot;, &quot;),c=r[Ua+&quot;Duration&quot;].split(&quot;, &quot;),u=qn(s,c),l=0,f=0;return t===Ra?a&gt;0&amp;&amp;(n=Ra,l=a,f=o.length):t===Fa?u&gt;0&amp;&amp;(n=Fa,l=u,f=c.length):(l=Math.max(a,u),n=l&gt;0?a&gt;u?Ra:Fa:null,f=n?n===Ra?o.length:c.length:0),{type:n,timeout:l,propCount:f,hasTransform:n===Ra&amp;&amp;Ja.test(r[Ba+&quot;Property&quot;])}}function qn(e,t){for(;e.length&lt;t.length;)e=e.concat(e);return Math.max.apply(null,t.map(function(t,n){return Wn(t)+Wn(e[n])}))}function Wn(e){return 1e3*Number(e.slice(0,-1))}function Zn(n,r){var i=n.elm;t(i._leaveCb)&amp;&amp;(i._leaveCb.cancelled=!0,i._leaveCb());var a=Hn(n.data.transition);if(!e(a)&amp;&amp;!t(i._enterCb)&amp;&amp;1===i.nodeType){for(var s=a.css,c=a.type,l=a.enterClass,f=a.enterToClass,p=a.enterActiveClass,d=a.appearClass,v=a.appearToClass,h=a.appearActiveClass,m=a.beforeEnter,g=a.enter,y=a.afterEnter,_=a.enterCancelled,b=a.beforeAppear,C=a.appear,x=a.afterAppear,w=a.appearCancelled,k=a.duration,A=ko,O=ko.$vnode;O&amp;&amp;O.parent;)O=O.parent,A=O.context;var S=!A._isMounted||!n.isRootInsert;if(!S||C||&quot;&quot;===C){var T=S&amp;&amp;d?d:l,E=S&amp;&amp;h?h:p,j=S&amp;&amp;v?v:f,N=S?b||m:m,L=S&amp;&amp;&quot;function&quot;==typeof C?C:g,I=S?x||y:y,D=S?w||_:_,M=u(o(k)?k.enter:k),P=!1!==s&amp;&amp;!Gi,R=Qn(L),F=i._enterCb=$(function(){P&amp;&amp;(zn(i,j),zn(i,E)),F.cancelled?(P&amp;&amp;zn(i,T),D&amp;&amp;D(i)):I&amp;&amp;I(i),i._enterCb=null});n.data.show||Q(n.data.hook||(n.data.hook={}),&quot;insert&quot;,function(){var e=i.parentNode,t=e&amp;&amp;e._pending&amp;&amp;e._pending[n.key];t&amp;&amp;t.tag===n.tag&amp;&amp;t.elm._leaveCb&amp;&amp;t.elm._leaveCb(),L&amp;&amp;L(i,F)}),N&amp;&amp;N(i),P&amp;&amp;(Vn(i,T),Vn(i,E),Un(function(){Vn(i,j),zn(i,T),F.cancelled||R||(Yn(M)?setTimeout(F,M):Jn(i,c,F))})),n.data.show&amp;&amp;(r&amp;&amp;r(),L&amp;&amp;L(i,F)),P||R||F()}}}function Gn(n,r){function i(){w.cancelled||(n.data.show||((a.parentNode._pending||(a.parentNode._pending={}))[n.key]=n),v&amp;&amp;v(a),b&amp;&amp;(Vn(a,f),Vn(a,d),Un(function(){Vn(a,p),zn(a,f),w.cancelled||C||(Yn(x)?setTimeout(w,x):Jn(a,l,w))})),h&amp;&amp;h(a,w),b||C||w())}var a=n.elm;t(a._enterCb)&amp;&amp;(a._enterCb.cancelled=!0,a._enterCb());var s=Hn(n.data.transition);if(e(s))return r();if(!t(a._leaveCb)&amp;&amp;1===a.nodeType){var c=s.css,l=s.type,f=s.leaveClass,p=s.leaveToClass,d=s.leaveActiveClass,v=s.beforeLeave,h=s.leave,m=s.afterLeave,g=s.leaveCancelled,y=s.delayLeave,_=s.duration,b=!1!==c&amp;&amp;!Gi,C=Qn(h),x=u(o(_)?_.leave:_),w=a._leaveCb=$(function(){a.parentNode&amp;&amp;a.parentNode._pending&amp;&amp;(a.parentNode._pending[n.key]=null),b&amp;&amp;(zn(a,p),zn(a,d)),w.cancelled?(b&amp;&amp;zn(a,f),g&amp;&amp;g(a)):(r(),m&amp;&amp;m(a)),a._leaveCb=null});y?y(i):i()}}function Yn(e){return&quot;number&quot;==typeof e&amp;&amp;!isNaN(e)}function Qn(n){if(e(n))return!1;var r=n.fns;return t(r)?Qn(Array.isArray(r)?r[0]:r):(n._length||n.length)&gt;1}function Xn(e,t){!0!==t.data.show&amp;&amp;Zn(t)}function er(e,t,n){var r=t.value,i=e.multiple;if(!i||Array.isArray(r)){for(var o,a,s=0,c=e.options.length;s&lt;c;s++)if(a=e.options[s],i)o=b(r,nr(a))&gt;-1,a.selected!==o&amp;&amp;(a.selected=o);else if(_(nr(a),r))return void(e.selectedIndex!==s&amp;&amp;(e.selectedIndex=s));i||(e.selectedIndex=-1)}}function tr(e,t){for(var n=0,r=t.length;n&lt;r;n++)if(_(nr(t[n]),e))return!1;return!0}function nr(e){return&quot;_value&quot;in e?e._value:e.value}function rr(e){e.target.composing=!0}function ir(e){e.target.composing&amp;&amp;(e.target.composing=!1,or(e.target,&quot;input&quot;))}function or(e,t){var n=document.createEvent(&quot;HTMLEvents&quot;);n.initEvent(t,!0,!0),e.dispatchEvent(n)}function ar(e){return!e.componentInstance||e.data&amp;&amp;e.data.transition?e:ar(e.componentInstance._vnode)}function sr(e){var t=e&amp;&amp;e.componentOptions;return t&amp;&amp;t.Ctor.options.abstract?sr(se(t.children)):e}function cr(e){var t={},n=e.$options;for(var r in n.propsData)t[r]=e[r];var i=n._parentListeners;for(var o in i)t[Li(o)]=i[o];return t}function ur(e,t){if(/\d-keep-alive$/.test(t.tag))return e(&quot;keep-alive&quot;,{props:t.componentOptions.propsData})}function lr(e){for(;e=e.parent;)if(e.data.transition)return!0}function fr(e,t){return t.key===e.key&amp;&amp;t.tag===e.tag}function pr(e){e.elm._moveCb&amp;&amp;e.elm._moveCb(),e.elm._enterCb&amp;&amp;e.elm._enterCb()}function dr(e){e.data.newPos=e.elm.getBoundingClientRect()}function vr(e){var t=e.data.pos,n=e.data.newPos,r=t.left-n.left,i=t.top-n.top;if(r||i){e.data.moved=!0;var o=e.elm.style;o.transform=o.WebkitTransform=&quot;translate(&quot;+r+&quot;px,&quot;+i+&quot;px)&quot;,o.transitionDuration=&quot;0s&quot;}}function hr(e){return is=is||document.createElement(&quot;div&quot;),is.innerHTML=e,is.textContent}function mr(e,t){var n=t?zs:Vs;return e.replace(n,function(e){return Us[e]})}function gr(e,t){function n(t){l+=t,e=e.substring(t)}function r(e,n,r){var i,s;if(null==n&amp;&amp;(n=l),null==r&amp;&amp;(r=l),e&amp;&amp;(s=e.toLowerCase()),e)for(i=a.length-1;i&gt;=0&amp;&amp;a[i].lowerCasedTag!==s;i--);else i=0;if(i&gt;=0){for(var c=a.length-1;c&gt;=i;c--)t.end&amp;&amp;t.end(a[c].tag,n,r);a.length=i,o=i&amp;&amp;a[i-1].tag}else&quot;br&quot;===s?t.start&amp;&amp;t.start(e,[],!0,n,r):&quot;p&quot;===s&amp;&amp;(t.start&amp;&amp;t.start(e,[],!1,n,r),t.end&amp;&amp;t.end(e,n,r))}for(var i,o,a=[],s=t.expectHTML,c=t.isUnaryTag||Pi,u=t.canBeLeftOpenTag||Pi,l=0;e;){if(i=e,o&amp;&amp;Bs(o)){var f=o.toLowerCase(),p=Hs[f]||(Hs[f]=new RegExp(&quot;([\\s\\S]*?)(&lt;/&quot;+f+&quot;[^&gt;]*&gt;)&quot;,&quot;i&quot;)),d=0,v=e.replace(p,function(e,n,r){return d=r.length,Bs(f)||&quot;noscript&quot;===f||(n=n.replace(/&lt;!--([\s\S]*?)--&gt;/g,&quot;$1&quot;).replace(/&lt;!\[CDATA\[([\s\S]*?)]]&gt;/g,&quot;$1&quot;)),t.chars&amp;&amp;t.chars(n),&quot;&quot;});l+=e.length-v.length,e=v,r(f,l-d,l)}else{var h=e.indexOf(&quot;&lt;&quot;);if(0===h){if(_s.test(e)){var m=e.indexOf(&quot;--\x3e&quot;);if(m&gt;=0){n(m+3);continue}}if(bs.test(e)){var g=e.indexOf(&quot;]&gt;&quot;);if(g&gt;=0){n(g+2);continue}}var y=e.match(ys);if(y){n(y[0].length);continue}var _=e.match(gs);if(_){var b=l;n(_[0].length),r(_[1],b,l);continue}var $=function(){var t=e.match(hs);if(t){var r={tagName:t[1],attrs:[],start:l};n(t[0].length);for(var i,o;!(i=e.match(ms))&amp;&amp;(o=e.match(ps));)n(o[0].length),r.attrs.push(o);if(i)return r.unarySlash=i[1],n(i[0].length),r.end=l,r}}();if($){!function(e){var n=e.tagName,i=e.unarySlash;s&amp;&amp;(&quot;p&quot;===o&amp;&amp;cs(n)&amp;&amp;r(o),u(n)&amp;&amp;o===n&amp;&amp;r(n));for(var l=c(n)||&quot;html&quot;===n&amp;&amp;&quot;head&quot;===o||!!i,f=e.attrs.length,p=new Array(f),d=0;d&lt;f;d++){var v=e.attrs[d];$s&amp;&amp;-1===v[0].indexOf(&#39;&quot;&quot;&#39;)&amp;&amp;(&quot;&quot;===v[3]&amp;&amp;delete v[3],&quot;&quot;===v[4]&amp;&amp;delete v[4],&quot;&quot;===v[5]&amp;&amp;delete v[5]);var h=v[3]||v[4]||v[5]||&quot;&quot;;p[d]={name:v[1],value:mr(h,t.shouldDecodeNewlines)}}l||(a.push({tag:n,lowerCasedTag:n.toLowerCase(),attrs:p}),o=n),t.start&amp;&amp;t.start(n,p,l,e.start,e.end)}($);continue}}var C=void 0,x=void 0,w=void 0;if(h&gt;=0){for(x=e.slice(h);!(gs.test(x)||hs.test(x)||_s.test(x)||bs.test(x)||(w=x.indexOf(&quot;&lt;&quot;,1))&lt;0);)h+=w,x=e.slice(h);C=e.substring(0,h),n(h)}h&lt;0&amp;&amp;(C=e,e=&quot;&quot;),t.chars&amp;&amp;C&amp;&amp;t.chars(C)}if(e===i){t.chars&amp;&amp;t.chars(e);break}}r()}function yr(e,t){var n=t?qs(t):Js;if(n.test(e)){for(var r,i,o=[],a=n.lastIndex=0;r=n.exec(e);){i=r.index,i&gt;a&amp;&amp;o.push(JSON.stringify(e.slice(a,i)));var s=rn(r[1].trim());o.push(&quot;_s(&quot;+s+&quot;)&quot;),a=i+r[0].length}return a&lt;e.length&amp;&amp;o.push(JSON.stringify(e.slice(a))),o.join(&quot;+&quot;)}}function _r(e,t){function n(e){e.pre&amp;&amp;(s=!1),Os(e.tag)&amp;&amp;(c=!1)}Cs=t.warn||an,Ts=t.getTagNamespace||Pi,Ss=t.mustUseProp||Pi,Os=t.isPreTag||Pi,ks=sn(t.modules,&quot;preTransformNode&quot;),ws=sn(t.modules,&quot;transformNode&quot;),As=sn(t.modules,&quot;postTransformNode&quot;),xs=t.delimiters;var r,i,o=[],a=!1!==t.preserveWhitespace,s=!1,c=!1;return gr(e,{warn:Cs,expectHTML:t.expectHTML,isUnaryTag:t.isUnaryTag,canBeLeftOpenTag:t.canBeLeftOpenTag,shouldDecodeNewlines:t.shouldDecodeNewlines,start:function(e,a,u){var l=i&amp;&amp;i.ns||Ts(e);Zi&amp;&amp;&quot;svg&quot;===l&amp;&amp;(a=Rr(a));var f={type:1,tag:e,attrsList:a,attrsMap:Dr(a),parent:i,children:[]};l&amp;&amp;(f.ns=l),Pr(f)&amp;&amp;!oo()&amp;&amp;(f.forbidden=!0);for(var p=0;p&lt;ks.length;p++)ks[p](f,t);if(s||(br(f),f.pre&amp;&amp;(s=!0)),Os(f.tag)&amp;&amp;(c=!0),s)$r(f);else{wr(f),kr(f),Tr(f),Cr(f),f.plain=!f.key&amp;&amp;!a.length,xr(f),Er(f),jr(f);for(var d=0;d&lt;ws.length;d++)ws[d](f,t);Nr(f)}if(r?o.length||r.if&amp;&amp;(f.elseif||f.else)&amp;&amp;Sr(r,{exp:f.elseif,block:f}):r=f,i&amp;&amp;!f.forbidden)if(f.elseif||f.else)Ar(f,i);else if(f.slotScope){i.plain=!1;var v=f.slotTarget||&#39;&quot;default&quot;&#39;;(i.scopedSlots||(i.scopedSlots={}))[v]=f}else i.children.push(f),f.parent=i;u?n(f):(i=f,o.push(f));for(var h=0;h&lt;As.length;h++)As[h](f,t)},end:function(){var e=o[o.length-1],t=e.children[e.children.length-1];t&amp;&amp;3===t.type&amp;&amp;&quot; &quot;===t.text&amp;&amp;!c&amp;&amp;e.children.pop(),o.length-=1,i=o[o.length-1],n(e)},chars:function(e){if(i&amp;&amp;(!Zi||&quot;textarea&quot;!==i.tag||i.attrsMap.placeholder!==e)){var t=i.children;if(e=c||e.trim()?Mr(i)?e:tc(e):a&amp;&amp;t.length?&quot; &quot;:&quot;&quot;){var n;!s&amp;&amp;&quot; &quot;!==e&amp;&amp;(n=yr(e,xs))?t.push({type:2,expression:n,text:e}):&quot; &quot;===e&amp;&amp;t.length&amp;&amp;&quot; &quot;===t[t.length-1].text||t.push({type:3,text:e})}}}}),r}function br(e){null!=dn(e,&quot;v-pre&quot;)&amp;&amp;(e.pre=!0)}function $r(e){var t=e.attrsList.length;if(t)for(var n=e.attrs=new Array(t),r=0;r&lt;t;r++)n[r]={name:e.attrsList[r].name,value:JSON.stringify(e.attrsList[r].value)};else e.pre||(e.plain=!0)}function Cr(e){var t=pn(e,&quot;key&quot;);t&amp;&amp;(e.key=t)}function xr(e){var t=pn(e,&quot;ref&quot;);t&amp;&amp;(e.ref=t,e.refInFor=Lr(e))}function wr(e){var t;if(t=dn(e,&quot;v-for&quot;)){var n=t.match(Gs);if(!n)return;e.for=n[2].trim();var r=n[1].trim(),i=r.match(Ys);i?(e.alias=i[1].trim(),e.iterator1=i[2].trim(),i[3]&amp;&amp;(e.iterator2=i[3].trim())):e.alias=r}}function kr(e){var t=dn(e,&quot;v-if&quot;);if(t)e.if=t,Sr(e,{exp:t,block:e});else{null!=dn(e,&quot;v-else&quot;)&amp;&amp;(e.else=!0);var n=dn(e,&quot;v-else-if&quot;);n&amp;&amp;(e.elseif=n)}}function Ar(e,t){var n=Or(t.children);n&amp;&amp;n.if&amp;&amp;Sr(n,{exp:e.elseif,block:e})}function Or(e){for(var t=e.length;t--;){if(1===e[t].type)return e[t];e.pop()}}function Sr(e,t){e.ifConditions||(e.ifConditions=[]),e.ifConditions.push(t)}function Tr(e){null!=dn(e,&quot;v-once&quot;)&amp;&amp;(e.once=!0)}function Er(e){if(&quot;slot&quot;===e.tag)e.slotName=pn(e,&quot;name&quot;);else{var t=pn(e,&quot;slot&quot;);t&amp;&amp;(e.slotTarget=&#39;&quot;&quot;&#39;===t?&#39;&quot;default&quot;&#39;:t),&quot;template&quot;===e.tag&amp;&amp;(e.slotScope=dn(e,&quot;scope&quot;))}}function jr(e){var t;(t=pn(e,&quot;is&quot;))&amp;&amp;(e.component=t),null!=dn(e,&quot;inline-template&quot;)&amp;&amp;(e.inlineTemplate=!0)}function Nr(e){var t,n,r,i,o,a,s,c=e.attrsList;for(t=0,n=c.length;t&lt;n;t++)if(r=i=c[t].name,o=c[t].value,Zs.test(r))if(e.hasBindings=!0,a=Ir(r),a&amp;&amp;(r=r.replace(ec,&quot;&quot;)),Xs.test(r))r=r.replace(Xs,&quot;&quot;),o=rn(o),s=!1,a&amp;&amp;(a.prop&amp;&amp;(s=!0,&quot;innerHtml&quot;===(r=Li(r))&amp;&amp;(r=&quot;innerHTML&quot;)),a.camel&amp;&amp;(r=Li(r)),a.sync&amp;&amp;fn(e,&quot;update:&quot;+Li(r),hn(o,&quot;$event&quot;))),s||Ss(e.tag,e.attrsMap.type,r)?cn(e,r,o):un(e,r,o);else if(Ws.test(r))r=r.replace(Ws,&quot;&quot;),fn(e,r,o,a,!1,Cs);else{r=r.replace(Zs,&quot;&quot;);var u=r.match(Qs),l=u&amp;&amp;u[1];l&amp;&amp;(r=r.slice(0,-(l.length+1))),ln(e,r,i,o,l,a)}else un(e,r,JSON.stringify(o))}function Lr(e){for(var t=e;t;){if(void 0!==t.for)return!0;t=t.parent}return!1}function Ir(e){var t=e.match(ec);if(t){var n={};return t.forEach(function(e){n[e.slice(1)]=!0}),n}}function Dr(e){for(var t={},n=0,r=e.length;n&lt;r;n++)t[e[n].name]=e[n].value;return t}function Mr(e){return&quot;script&quot;===e.tag||&quot;style&quot;===e.tag}function Pr(e){return&quot;style&quot;===e.tag||&quot;script&quot;===e.tag&amp;&amp;(!e.attrsMap.type||&quot;text/javascript&quot;===e.attrsMap.type)}function Rr(e){for(var t=[],n=0;n&lt;e.length;n++){var r=e[n];nc.test(r.name)||(r.name=r.name.replace(rc,&quot;&quot;),t.push(r))}return t}function Fr(e,t){e&amp;&amp;(Es=ic(t.staticKeys||&quot;&quot;),js=t.isReservedTag||Pi,Hr(e),Ur(e,!1))}function Br(e){return l(&quot;type,tag,attrsList,attrsMap,plain,parent,children,attrs&quot;+(e?&quot;,&quot;+e:&quot;&quot;))}function Hr(e){if(e.static=zr(e),1===e.type){if(!js(e.tag)&amp;&amp;&quot;slot&quot;!==e.tag&amp;&amp;null==e.attrsMap[&quot;inline-template&quot;])return;for(var t=0,n=e.children.length;t&lt;n;t++){var r=e.children[t];Hr(r),r.static||(e.static=!1)}}}function Ur(e,t){if(1===e.type){if((e.static||e.once)&amp;&amp;(e.staticInFor=t),e.static&amp;&amp;e.children.length&amp;&amp;(1!==e.children.length||3!==e.children[0].type))return void(e.staticRoot=!0);if(e.staticRoot=!1,e.children)for(var n=0,r=e.children.length;n&lt;r;n++)Ur(e.children[n],t||!!e.for);e.ifConditions&amp;&amp;Vr(e.ifConditions,t)}}function Vr(e,t){for(var n=1,r=e.length;n&lt;r;n++)Ur(e[n].block,t)}function zr(e){return 2!==e.type&amp;&amp;(3===e.type||!(!e.pre&amp;&amp;(e.hasBindings||e.if||e.for||Ei(e.tag)||!js(e.tag)||Jr(e)||!Object.keys(e).every(Es))))}function Jr(e){for(;e.parent;){if(e=e.parent,&quot;template&quot;!==e.tag)return!1;if(e.for)return!0}return!1}function Kr(e,t,n){var r=t?&quot;nativeOn:{&quot;:&quot;on:{&quot;;for(var i in e){var o=e[i];r+=&#39;&quot;&#39;+i+&#39;&quot;:&#39;+qr(i,o)+&quot;,&quot;}return r.slice(0,-1)+&quot;}&quot;}function qr(e,t){if(!t)return&quot;function(){}&quot;;if(Array.isArray(t))return&quot;[&quot;+t.map(function(t){return qr(e,t)}).join(&quot;,&quot;)+&quot;]&quot;;var n=ac.test(t.value),r=oc.test(t.value);if(t.modifiers){var i=&quot;&quot;,o=&quot;&quot;,a=[];for(var s in t.modifiers)uc[s]?(o+=uc[s],sc[s]&amp;&amp;a.push(s)):a.push(s);a.length&amp;&amp;(i+=Wr(a)),o&amp;&amp;(i+=o);return&quot;function($event){&quot;+i+(n?t.value+&quot;($event)&quot;:r?&quot;(&quot;+t.value+&quot;)($event)&quot;:t.value)+&quot;}&quot;}return n||r?t.value:&quot;function($event){&quot;+t.value+&quot;}&quot;}function Wr(e){return&quot;if(!(&#39;button&#39; in $event)&amp;&amp;&quot;+e.map(Zr).join(&quot;&amp;&amp;&quot;)+&quot;)return null;&quot;}function Zr(e){var t=parseInt(e,10);if(t)return&quot;$event.keyCode!==&quot;+t;var n=sc[e];return&quot;_k($event.keyCode,&quot;+JSON.stringify(e)+(n?&quot;,&quot;+JSON.stringify(n):&quot;&quot;)+&quot;)&quot;}function Gr(e,t){e.wrapData=function(n){return&quot;_b(&quot;+n+&quot;,&#39;&quot;+e.tag+&quot;&#39;,&quot;+t.value+(t.modifiers&amp;&amp;t.modifiers.prop?&quot;,true&quot;:&quot;&quot;)+&quot;)&quot;}}function Yr(e,t){var n=Ps,r=Ps=[],i=Rs;Rs=0,Fs=t,Ns=t.warn||an,Ls=sn(t.modules,&quot;transformCode&quot;),Is=sn(t.modules,&quot;genData&quot;),Ds=t.directives||{},Ms=t.isReservedTag||Pi;var o=e?Qr(e):&#39;_c(&quot;div&quot;)&#39;;return Ps=n,Rs=i,{render:&quot;with(this){return &quot;+o+&quot;}&quot;,staticRenderFns:r}}function Qr(e){if(e.staticRoot&amp;&amp;!e.staticProcessed)return Xr(e);if(e.once&amp;&amp;!e.onceProcessed)return ei(e);if(e.for&amp;&amp;!e.forProcessed)return ri(e);if(e.if&amp;&amp;!e.ifProcessed)return ti(e);if(&quot;template&quot;!==e.tag||e.slotTarget){if(&quot;slot&quot;===e.tag)return mi(e);var t;if(e.component)t=gi(e.component,e);else{var n=e.plain?void 0:ii(e),r=e.inlineTemplate?null:li(e,!0);t=&quot;_c(&#39;&quot;+e.tag+&quot;&#39;&quot;+(n?&quot;,&quot;+n:&quot;&quot;)+(r?&quot;,&quot;+r:&quot;&quot;)+&quot;)&quot;}for(var i=0;i&lt;Ls.length;i++)t=Ls[i](e,t);return t}return li(e)||&quot;void 0&quot;}function Xr(e){return e.staticProcessed=!0,Ps.push(&quot;with(this){return &quot;+Qr(e)+&quot;}&quot;),&quot;_m(&quot;+(Ps.length-1)+(e.staticInFor?&quot;,true&quot;:&quot;&quot;)+&quot;)&quot;}function ei(e){if(e.onceProcessed=!0,e.if&amp;&amp;!e.ifProcessed)return ti(e);if(e.staticInFor){for(var t=&quot;&quot;,n=e.parent;n;){if(n.for){t=n.key;break}n=n.parent}return t?&quot;_o(&quot;+Qr(e)+&quot;,&quot;+Rs+++(t?&quot;,&quot;+t:&quot;&quot;)+&quot;)&quot;:Qr(e)}return Xr(e)}function ti(e){return e.ifProcessed=!0,ni(e.ifConditions.slice())}function ni(e){function t(e){return e.once?ei(e):Qr(e)}if(!e.length)return&quot;_e()&quot;;var n=e.shift();return n.exp?&quot;(&quot;+n.exp+&quot;)?&quot;+t(n.block)+&quot;:&quot;+ni(e):&quot;&quot;+t(n.block)}function ri(e){var t=e.for,n=e.alias,r=e.iterator1?&quot;,&quot;+e.iterator1:&quot;&quot;,i=e.iterator2?&quot;,&quot;+e.iterator2:&quot;&quot;;return e.forProcessed=!0,&quot;_l((&quot;+t+&quot;),function(&quot;+n+r+i+&quot;){return &quot;+Qr(e)+&quot;})&quot;}function ii(e){var t=&quot;{&quot;,n=oi(e);n&amp;&amp;(t+=n+&quot;,&quot;),e.key&amp;&amp;(t+=&quot;key:&quot;+e.key+&quot;,&quot;),e.ref&amp;&amp;(t+=&quot;ref:&quot;+e.ref+&quot;,&quot;),e.refInFor&amp;&amp;(t+=&quot;refInFor:true,&quot;),e.pre&amp;&amp;(t+=&quot;pre:true,&quot;),e.component&amp;&amp;(t+=&#39;tag:&quot;&#39;+e.tag+&#39;&quot;,&#39;);for(var r=0;r&lt;Is.length;r++)t+=Is[r](e);if(e.attrs&amp;&amp;(t+=&quot;attrs:{&quot;+yi(e.attrs)+&quot;},&quot;),e.props&amp;&amp;(t+=&quot;domProps:{&quot;+yi(e.props)+&quot;},&quot;),e.events&amp;&amp;(t+=Kr(e.events,!1,Ns)+&quot;,&quot;),e.nativeEvents&amp;&amp;(t+=Kr(e.nativeEvents,!0,Ns)+&quot;,&quot;),e.slotTarget&amp;&amp;(t+=&quot;slot:&quot;+e.slotTarget+&quot;,&quot;),e.scopedSlots&amp;&amp;(t+=si(e.scopedSlots)+&quot;,&quot;),e.model&amp;&amp;(t+=&quot;model:{value:&quot;+e.model.value+&quot;,callback:&quot;+e.model.callback+&quot;,expression:&quot;+e.model.expression+&quot;},&quot;),e.inlineTemplate){var i=ai(e);i&amp;&amp;(t+=i+&quot;,&quot;)}return t=t.replace(/,$/,&quot;&quot;)+&quot;}&quot;,e.wrapData&amp;&amp;(t=e.wrapData(t)),t}function oi(e){var t=e.directives;if(t){var n,r,i,o,a=&quot;directives:[&quot;,s=!1;for(n=0,r=t.length;n&lt;r;n++){i=t[n],o=!0;var c=Ds[i.name]||lc[i.name];c&amp;&amp;(o=!!c(e,i,Ns)),o&amp;&amp;(s=!0,a+=&#39;{name:&quot;&#39;+i.name+&#39;&quot;,rawName:&quot;&#39;+i.rawName+&#39;&quot;&#39;+(i.value?&quot;,value:(&quot;+i.value+&quot;),expression:&quot;+JSON.stringify(i.value):&quot;&quot;)+(i.arg?&#39;,arg:&quot;&#39;+i.arg+&#39;&quot;&#39;:&quot;&quot;)+(i.modifiers?&quot;,modifiers:&quot;+JSON.stringify(i.modifiers):&quot;&quot;)+&quot;},&quot;)}return s?a.slice(0,-1)+&quot;]&quot;:void 0}}function ai(e){var t=e.children[0];if(1===t.type){var n=Yr(t,Fs);return&quot;inlineTemplate:{render:function(){&quot;+n.render+&quot;},staticRenderFns:[&quot;+n.staticRenderFns.map(function(e){return&quot;function(){&quot;+e+&quot;}&quot;}).join(&quot;,&quot;)+&quot;]}&quot;}}function si(e){return&quot;scopedSlots:_u([&quot;+Object.keys(e).map(function(t){return ci(t,e[t])}).join(&quot;,&quot;)+&quot;])&quot;}function ci(e,t){return t.for&amp;&amp;!t.forProcessed?ui(e,t):&quot;{key:&quot;+e+&quot;,fn:function(&quot;+String(t.attrsMap.scope)+&quot;){return &quot;+(&quot;template&quot;===t.tag?li(t)||&quot;void 0&quot;:Qr(t))+&quot;}}&quot;}function ui(e,t){var n=t.for,r=t.alias,i=t.iterator1?&quot;,&quot;+t.iterator1:&quot;&quot;,o=t.iterator2?&quot;,&quot;+t.iterator2:&quot;&quot;;return t.forProcessed=!0,&quot;_l((&quot;+n+&quot;),function(&quot;+r+i+o+&quot;){return &quot;+ci(e,t)+&quot;})&quot;}function li(e,t){var n=e.children;if(n.length){var r=n[0];if(1===n.length&amp;&amp;r.for&amp;&amp;&quot;template&quot;!==r.tag&amp;&amp;&quot;slot&quot;!==r.tag)return Qr(r);var i=t?fi(n):0;return&quot;[&quot;+n.map(vi).join(&quot;,&quot;)+&quot;]&quot;+(i?&quot;,&quot;+i:&quot;&quot;)}}function fi(e){for(var t=0,n=0;n&lt;e.length;n++){var r=e[n];if(1===r.type){if(pi(r)||r.ifConditions&amp;&amp;r.ifConditions.some(function(e){return pi(e.block)})){t=2;break}(di(r)||r.ifConditions&amp;&amp;r.ifConditions.some(function(e){return di(e.block)}))&amp;&amp;(t=1)}}return t}function pi(e){return void 0!==e.for||&quot;template&quot;===e.tag||&quot;slot&quot;===e.tag}function di(e){return!Ms(e.tag)}function vi(e){return 1===e.type?Qr(e):hi(e)}function hi(e){return&quot;_v(&quot;+(2===e.type?e.expression:_i(JSON.stringify(e.text)))+&quot;)&quot;}function mi(e){var t=e.slotName||&#39;&quot;default&quot;&#39;,n=li(e),r=&quot;_t(&quot;+t+(n?&quot;,&quot;+n:&quot;&quot;),i=e.attrs&amp;&amp;&quot;{&quot;+e.attrs.map(function(e){return Li(e.name)+&quot;:&quot;+e.value}).join(&quot;,&quot;)+&quot;}&quot;,o=e.attrsMap[&quot;v-bind&quot;];return!i&amp;&amp;!o||n||(r+=&quot;,null&quot;),i&amp;&amp;(r+=&quot;,&quot;+i),o&amp;&amp;(r+=(i?&quot;&quot;:&quot;,null&quot;)+&quot;,&quot;+o),r+&quot;)&quot;}function gi(e,t){var n=t.inlineTemplate?null:li(t,!0);return&quot;_c(&quot;+e+&quot;,&quot;+ii(t)+(n?&quot;,&quot;+n:&quot;&quot;)+&quot;)&quot;}function yi(e){for(var t=&quot;&quot;,n=0;n&lt;e.length;n++){var r=e[n];t+=&#39;&quot;&#39;+r.name+&#39;&quot;:&#39;+_i(r.value)+&quot;,&quot;}return t.slice(0,-1)}function _i(e){return e.replace(/\u2028/g,&quot;\\u2028&quot;).replace(/\u2029/g,&quot;\\u2029&quot;)}function bi(e,t){var n=_r(e.trim(),t);Fr(n,t);var r=Yr(n,t);return{ast:n,render:r.render,staticRenderFns:r.staticRenderFns}}function $i(e,t){try{return new Function(e)}catch(n){return t.push({err:n,code:e}),y}}function Ci(e,t){var n=(t.warn,dn(e,&quot;class&quot;));n&amp;&amp;(e.staticClass=JSON.stringify(n));var r=pn(e,&quot;class&quot;,!1);r&amp;&amp;(e.classBinding=r)}function xi(e){var t=&quot;&quot;;return e.staticClass&amp;&amp;(t+=&quot;staticClass:&quot;+e.staticClass+&quot;,&quot;),e.classBinding&amp;&amp;(t+=&quot;class:&quot;+e.classBinding+&quot;,&quot;),t}function wi(e,t){var n=(t.warn,dn(e,&quot;style&quot;));n&amp;&amp;(e.staticStyle=JSON.stringify(Ta(n)));var r=pn(e,&quot;style&quot;,!1);r&amp;&amp;(e.styleBinding=r)}function ki(e){var t=&quot;&quot;;return e.staticStyle&amp;&amp;(t+=&quot;staticStyle:&quot;+e.staticStyle+&quot;,&quot;),e.styleBinding&amp;&amp;(t+=&quot;style:(&quot;+e.styleBinding+&quot;),&quot;),t}function Ai(e,t){t.value&amp;&amp;cn(e,&quot;textContent&quot;,&quot;_s(&quot;+t.value+&quot;)&quot;)}function Oi(e,t){t.value&amp;&amp;cn(e,&quot;innerHTML&quot;,&quot;_s(&quot;+t.value+&quot;)&quot;)}function Si(e){if(e.outerHTML)return e.outerHTML;var t=document.createElement(&quot;div&quot;);return t.appendChild(e.cloneNode(!0)),t.innerHTML}var Ti=Object.prototype.toString,Ei=l(&quot;slot,component&quot;,!0),ji=Object.prototype.hasOwnProperty,Ni=/-(\w)/g,Li=d(function(e){return e.replace(Ni,function(e,t){return t?t.toUpperCase():&quot;&quot;})}),Ii=d(function(e){return e.charAt(0).toUpperCase()+e.slice(1)}),Di=/([^-])([A-Z])/g,Mi=d(function(e){return e.replace(Di,&quot;$1-$2&quot;).replace(Di,&quot;$1-$2&quot;).toLowerCase()}),Pi=function(){return!1},Ri=function(e){return e},Fi=&quot;data-server-rendered&quot;,Bi=[&quot;component&quot;,&quot;directive&quot;,&quot;filter&quot;],Hi=[&quot;beforeCreate&quot;,&quot;created&quot;,&quot;beforeMount&quot;,&quot;mounted&quot;,&quot;beforeUpdate&quot;,&quot;updated&quot;,&quot;beforeDestroy&quot;,&quot;destroyed&quot;,&quot;activated&quot;,&quot;deactivated&quot;],Ui={optionMergeStrategies:Object.create(null),silent:!1,productionTip:!1,devtools:!1,performance:!1,errorHandler:null,ignoredElements:[],keyCodes:Object.create(null),isReservedTag:Pi,isReservedAttr:Pi,isUnknownElement:Pi,getTagNamespace:y,parsePlatformTagName:Ri,mustUseProp:Pi,_lifecycleHooks:Hi},Vi=Object.freeze({}),zi=/[^\w.$]/,Ji=y,Ki=&quot;__proto__&quot;in{},qi=&quot;undefined&quot;!=typeof window,Wi=qi&amp;&amp;window.navigator.userAgent.toLowerCase(),Zi=Wi&amp;&amp;/msie|trident/.test(Wi),Gi=Wi&amp;&amp;Wi.indexOf(&quot;msie 9.0&quot;)&gt;0,Yi=Wi&amp;&amp;Wi.indexOf(&quot;edge/&quot;)&gt;0,Qi=Wi&amp;&amp;Wi.indexOf(&quot;android&quot;)&gt;0,Xi=Wi&amp;&amp;/iphone|ipad|ipod|ios/.test(Wi),eo=Wi&amp;&amp;/chrome\/\d+/.test(Wi)&amp;&amp;!Yi,to=!1;if(qi)try{var no={};Object.defineProperty(no,&quot;passive&quot;,{get:function(){to=!0}}),window.addEventListener(&quot;test-passive&quot;,null,no)}catch(e){}var ro,io,oo=function(){return void 0===ro&amp;&amp;(ro=!qi&amp;&amp;&quot;undefined&quot;!=typeof global&amp;&amp;&quot;server&quot;===global.process.env.VUE_ENV),ro},ao=qi&amp;&amp;window.__VUE_DEVTOOLS_GLOBAL_HOOK__,so=&quot;undefined&quot;!=typeof Symbol&amp;&amp;A(Symbol)&amp;&amp;&quot;undefined&quot;!=typeof Reflect&amp;&amp;A(Reflect.ownKeys),co=function(){function e(){r=!1;var e=n.slice(0);n.length=0;for(var t=0;t&lt;e.length;t++)e[t]()}var t,n=[],r=!1;if(&quot;undefined&quot;!=typeof Promise&amp;&amp;A(Promise)){var i=Promise.resolve(),o=function(e){console.error(e)};t=function(){i.then(e).catch(o),Xi&amp;&amp;setTimeout(y)}}else if(&quot;undefined&quot;==typeof MutationObserver||!A(MutationObserver)&amp;&amp;&quot;[object MutationObserverConstructor]&quot;!==MutationObserver.toString())t=function(){setTimeout(e,0)};else{var a=1,s=new MutationObserver(e),c=document.createTextNode(String(a));s.observe(c,{characterData:!0}),t=function(){a=(a+1)%2,c.data=String(a)}}return function(e,i){var o;if(n.push(function(){if(e)try{e.call(i)}catch(e){k(e,i,&quot;nextTick&quot;)}else o&amp;&amp;o(i)}),r||(r=!0,t()),!e&amp;&amp;&quot;undefined&quot;!=typeof Promise)return new Promise(function(e,t){o=e})}}();io=&quot;undefined&quot;!=typeof Set&amp;&amp;A(Set)?Set:function(){function e(){this.set=Object.create(null)}return e.prototype.has=function(e){return!0===this.set[e]},e.prototype.add=function(e){this.set[e]=!0},e.prototype.clear=function(){this.set=Object.create(null)},e}();var uo=0,lo=function(){this.id=uo++,this.subs=[]};lo.prototype.addSub=function(e){this.subs.push(e)},lo.prototype.removeSub=function(e){f(this.subs,e)},lo.prototype.depend=function(){lo.target&amp;&amp;lo.target.addDep(this)},lo.prototype.notify=function(){for(var e=this.subs.slice(),t=0,n=e.length;t&lt;n;t++)e[t].update()},lo.target=null;var fo=[],po=Array.prototype,vo=Object.create(po);[&quot;push&quot;,&quot;pop&quot;,&quot;shift&quot;,&quot;unshift&quot;,&quot;splice&quot;,&quot;sort&quot;,&quot;reverse&quot;].forEach(function(e){var t=po[e];x(vo,e,function(){for(var n=arguments,r=arguments.length,i=new Array(r);r--;)i[r]=n[r];var o,a=t.apply(this,i),s=this.__ob__;switch(e){case&quot;push&quot;:case&quot;unshift&quot;:o=i;break;case&quot;splice&quot;:o=i.slice(2)}return o&amp;&amp;s.observeArray(o),s.dep.notify(),a})});var ho=Object.getOwnPropertyNames(vo),mo={shouldConvert:!0,isSettingProps:!1},go=function(e){if(this.value=e,this.dep=new lo,this.vmCount=0,x(e,&quot;__ob__&quot;,this),Array.isArray(e)){(Ki?T:E)(e,vo,ho),this.observeArray(e)}else this.walk(e)};go.prototype.walk=function(e){for(var t=Object.keys(e),n=0;n&lt;t.length;n++)N(e,t[n],e[t[n]])},go.prototype.observeArray=function(e){for(var t=0,n=e.length;t&lt;n;t++)j(e[t])};var yo=Ui.optionMergeStrategies;yo.data=function(e,t,n){return n?e||t?function(){var r=&quot;function&quot;==typeof t?t.call(n):t,i=&quot;function&quot;==typeof e?e.call(n):void 0;return r?M(r,i):i}:void 0:t?&quot;function&quot;!=typeof t?e:e?function(){return M(t.call(this),e.call(this))}:t:e},Hi.forEach(function(e){yo[e]=P}),Bi.forEach(function(e){yo[e+&quot;s&quot;]=R}),yo.watch=function(e,t){if(!t)return Object.create(e||null);if(!e)return t;var n={};m(n,e);for(var r in t){var i=n[r],o=t[r];i&amp;&amp;!Array.isArray(i)&amp;&amp;(i=[i]),n[r]=i?i.concat(o):[o]}return n},yo.props=yo.methods=yo.computed=function(e,t){if(!t)return Object.create(e||null);if(!e)return t;var n=Object.create(null);return m(n,e),m(n,t),n};var _o=function(e,t){return void 0===t?e:t},bo=function(e,t,n,r,i,o,a){this.tag=e,this.data=t,this.children=n,this.text=r,this.elm=i,this.ns=void 0,this.context=o,this.functionalContext=void 0,this.key=t&amp;&amp;t.key,this.componentOptions=a,this.componentInstance=void 0,this.parent=void 0,this.raw=!1,this.isStatic=!1,this.isRootInsert=!0,this.isComment=!1,this.isCloned=!1,this.isOnce=!1},$o={child:{}};$o.child.get=function(){return this.componentInstance},Object.defineProperties(bo.prototype,$o);var Co,xo=function(){var e=new bo;return e.text=&quot;&quot;,e.isComment=!0,e},wo=d(function(e){var t=&quot;&amp;&quot;===e.charAt(0);e=t?e.slice(1):e;var n=&quot;~&quot;===e.charAt(0);e=n?e.slice(1):e;var r=&quot;!&quot;===e.charAt(0);return e=r?e.slice(1):e,{name:e,once:n,capture:r,passive:t}}),ko=null,Ao=[],Oo=[],So={},To=!1,Eo=!1,jo=0,No=0,Lo=function(e,t,n,r){this.vm=e,e._watchers.push(this),r?(this.deep=!!r.deep,this.user=!!r.user,this.lazy=!!r.lazy,this.sync=!!r.sync):this.deep=this.user=this.lazy=this.sync=!1,this.cb=n,this.id=++No,this.active=!0,this.dirty=this.lazy,this.deps=[],this.newDeps=[],this.depIds=new io,this.newDepIds=new io,this.expression=&quot;&quot;,&quot;function&quot;==typeof t?this.getter=t:(this.getter=w(t),this.getter||(this.getter=function(){})),this.value=this.lazy?void 0:this.get()};Lo.prototype.get=function(){O(this);var e,t=this.vm;if(this.user)try{e=this.getter.call(t,t)}catch(e){k(e,t,&#39;getter for watcher &quot;&#39;+this.expression+&#39;&quot;&#39;)}else e=this.getter.call(t,t);return this.deep&amp;&amp;Se(e),S(),this.cleanupDeps(),e},Lo.prototype.addDep=function(e){var t=e.id;this.newDepIds.has(t)||(this.newDepIds.add(t),this.newDeps.push(e),this.depIds.has(t)||e.addSub(this))},Lo.prototype.cleanupDeps=function(){for(var e=this,t=this.deps.length;t--;){var n=e.deps[t];e.newDepIds.has(n.id)||n.removeSub(e)}var r=this.depIds;this.depIds=this.newDepIds,this.newDepIds=r,this.newDepIds.clear(),r=this.deps,this.deps=this.newDeps,this.newDeps=r,this.newDeps.length=0},Lo.prototype.update=function(){this.lazy?this.dirty=!0:this.sync?this.run():Oe(this)},Lo.prototype.run=function(){if(this.active){var e=this.get();if(e!==this.value||o(e)||this.deep){var t=this.value;if(this.value=e,this.user)try{this.cb.call(this.vm,e,t)}catch(e){k(e,this.vm,&#39;callback for watcher &quot;&#39;+this.expression+&#39;&quot;&#39;)}else this.cb.call(this.vm,e,t)}}},Lo.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},Lo.prototype.depend=function(){for(var e=this,t=this.deps.length;t--;)e.deps[t].depend()},Lo.prototype.teardown=function(){var e=this;if(this.active){this.vm._isBeingDestroyed||f(this.vm._watchers,this);for(var t=this.deps.length;t--;)e.deps[t].removeSub(e);this.active=!1}};var Io=new io,Do={enumerable:!0,configurable:!0,get:y,set:y},Mo={lazy:!0},Po={init:function(e,t,n,r){if(!e.componentInstance||e.componentInstance._isDestroyed){(e.componentInstance=qe(e,ko,n,r)).$mount(t?e.elm:void 0,t)}else if(e.data.keepAlive){var i=e;Po.prepatch(i,i)}},prepatch:function(e,t){var n=t.componentOptions;ge(t.componentInstance=e.componentInstance,n.propsData,n.listeners,t,n.children)},insert:function(e){var t=e.context,n=e.componentInstance;n._isMounted||(n._isMounted=!0,$e(n,&quot;mounted&quot;)),e.data.keepAlive&amp;&amp;(t._isMounted?ke(n):_e(n,!0))},destroy:function(e){var t=e.componentInstance;t._isDestroyed||(e.data.keepAlive?be(t,!0):t.$destroy())}},Ro=Object.keys(Po),Fo=1,Bo=2,Ho=0;!function(e){e.prototype._init=function(e){var t=this;t._uid=Ho++,t._isVue=!0,e&amp;&amp;e._isComponent?lt(t,e):t.$options=H(ft(t.constructor),e||{},t),t._renderProxy=t,t._self=t,he(t),ce(t),ut(t),$e(t,&quot;beforeCreate&quot;),Ue(t),je(t),He(t),$e(t,&quot;created&quot;),t.$options.el&amp;&amp;t.$mount(t.$options.el)}}(vt),function(e){var t={};t.get=function(){return this._data};var n={};n.get=function(){return this._props},Object.defineProperty(e.prototype,&quot;$data&quot;,t),Object.defineProperty(e.prototype,&quot;$props&quot;,n),e.prototype.$set=L,e.prototype.$delete=I,e.prototype.$watch=function(e,t,n){var r=this;n=n||{},n.user=!0;var i=new Lo(r,e,t,n);return n.immediate&amp;&amp;t.call(r,i.value),function(){i.teardown()}}}(vt),function(e){var t=/^hook:/;e.prototype.$on=function(e,n){var r=this,i=this;if(Array.isArray(e))for(var o=0,a=e.length;o&lt;a;o++)r.$on(e[o],n);else(i._events[e]||(i._events[e]=[])).push(n),t.test(e)&amp;&amp;(i._hasHookEvent=!0);return i},e.prototype.$once=function(e,t){function n(){r.$off(e,n),t.apply(r,arguments)}var r=this;return n.fn=t,r.$on(e,n),r},e.prototype.$off=function(e,t){var n=this,r=this;if(!arguments.length)return r._events=Object.create(null),r;if(Array.isArray(e)){for(var i=0,o=e.length;i&lt;o;i++)n.$off(e[i],t);return r}var a=r._events[e];if(!a)return r;if(1===arguments.length)return r._events[e]=null,r;for(var s,c=a.length;c--;)if((s=a[c])===t||s.fn===t){a.splice(c,1);break}return r},e.prototype.$emit=function(e){var t=this,n=t._events[e];if(n){n=n.length&gt;1?h(n):n;for(var r=h(arguments,1),i=0,o=n.length;i&lt;o;i++)n[i].apply(t,r)}return t}}(vt),function(e){e.prototype._update=function(e,t){var n=this;n._isMounted&amp;&amp;$e(n,&quot;beforeUpdate&quot;);var r=n.$el,i=n._vnode,o=ko;ko=n,n._vnode=e,n.$el=i?n.__patch__(i,e):n.__patch__(n.$el,e,t,!1,n.$options._parentElm,n.$options._refElm),ko=o,r&amp;&amp;(r.__vue__=null),n.$el&amp;&amp;(n.$el.__vue__=n),n.$vnode&amp;&amp;n.$parent&amp;&amp;n.$vnode===n.$parent._vnode&amp;&amp;(n.$parent.$el=n.$el)},e.prototype.$forceUpdate=function(){var e=this;e._watcher&amp;&amp;e._watcher.update()},e.prototype.$destroy=function(){var e=this;if(!e._isBeingDestroyed){$e(e,&quot;beforeDestroy&quot;),e._isBeingDestroyed=!0;var t=e.$parent;!t||t._isBeingDestroyed||e.$options.abstract||f(t.$children,e),e._watcher&amp;&amp;e._watcher.teardown();for(var n=e._watchers.length;n--;)e._watchers[n].teardown();e._data.__ob__&amp;&amp;e._data.__ob__.vmCount--,e._isDestroyed=!0,e.__patch__(e._vnode,null),$e(e,&quot;destroyed&quot;),e.$off(),e.$el&amp;&amp;(e.$el.__vue__=null),e.$options._parentElm=e.$options._refElm=null}}}(vt),function(e){e.prototype.$nextTick=function(e){return co(e,this)},e.prototype._render=function(){var e=this,t=e.$options,n=t.render,r=t.staticRenderFns,i=t._parentVnode;if(e._isMounted)for(var o in e.$slots)e.$slots[o]=Z(e.$slots[o]);e.$scopedSlots=i&amp;&amp;i.data.scopedSlots||Vi,r&amp;&amp;!e._staticTrees&amp;&amp;(e._staticTrees=[]),e.$vnode=i;var a;try{a=n.call(e._renderProxy,e.$createElement)}catch(t){k(t,e,&quot;render function&quot;),a=e._vnode}return a instanceof bo||(a=xo()),a.parent=i,a},e.prototype._o=at,e.prototype._n=u,e.prototype._s=c,e.prototype._l=et,e.prototype._t=tt,e.prototype._q=_,e.prototype._i=b,e.prototype._m=ot,e.prototype._f=nt,e.prototype._k=rt,e.prototype._b=it,e.prototype._v=q,e.prototype._e=xo,e.prototype._u=ve}(vt);var Uo=[String,RegExp],Vo={name:&quot;keep-alive&quot;,abstract:!0,props:{include:Uo,exclude:Uo},created:function(){this.cache=Object.create(null)},destroyed:function(){var e=this;for(var t in e.cache)wt(e.cache[t])},watch:{include:function(e){xt(this.cache,this._vnode,function(t){return Ct(e,t)})},exclude:function(e){xt(this.cache,this._vnode,function(t){return!Ct(e,t)})}},render:function(){var e=se(this.$slots.default),t=e&amp;&amp;e.componentOptions;if(t){var n=$t(t);if(n&amp;&amp;(this.include&amp;&amp;!Ct(this.include,n)||this.exclude&amp;&amp;Ct(this.exclude,n)))return e;var r=null==e.key?t.Ctor.cid+(t.tag?&quot;::&quot;+t.tag:&quot;&quot;):e.key;this.cache[r]?e.componentInstance=this.cache[r].componentInstance:this.cache[r]=e,e.data.keepAlive=!0}return e}},zo={KeepAlive:Vo};!function(e){var t={};t.get=function(){return Ui},Object.defineProperty(e,&quot;config&quot;,t),e.util={warn:Ji,extend:m,mergeOptions:H,defineReactive:N},e.set=L,e.delete=I,e.nextTick=co,e.options=Object.create(null),Bi.forEach(function(t){e.options[t+&quot;s&quot;]=Object.create(null)}),e.options._base=e,m(e.options.components,zo),ht(e),mt(e),gt(e),bt(e)}(vt),Object.defineProperty(vt.prototype,&quot;$isServer&quot;,{get:oo}),Object.defineProperty(vt.prototype,&quot;$ssrContext&quot;,{get:function(){return this.$vnode.ssrContext}}),vt.version=&quot;2.3.4&quot;;var Jo,Ko,qo,Wo,Zo,Go,Yo,Qo,Xo,ea=l(&quot;style,class&quot;),ta=l(&quot;input,textarea,option,select&quot;),na=function(e,t,n){return&quot;value&quot;===n&amp;&amp;ta(e)&amp;&amp;&quot;button&quot;!==t||&quot;selected&quot;===n&amp;&amp;&quot;option&quot;===e||&quot;checked&quot;===n&amp;&amp;&quot;input&quot;===e||&quot;muted&quot;===n&amp;&amp;&quot;video&quot;===e},ra=l(&quot;contenteditable,draggable,spellcheck&quot;),ia=l(&quot;allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,translate,truespeed,typemustmatch,visible&quot;),oa=&quot;http://www.w3.org/1999/xlink&quot;,aa=function(e){return&quot;:&quot;===e.charAt(5)&amp;&amp;&quot;xlink&quot;===e.slice(0,5)},sa=function(e){return aa(e)?e.slice(6,e.length):&quot;&quot;},ca=function(e){return null==e||!1===e},ua={svg:&quot;http://www.w3.org/2000/svg&quot;,math:&quot;http://www.w3.org/1998/Math/MathML&quot;},la=l(&quot;html,body,base,head,link,meta,style,title,address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,div,dd,dl,dt,figcaption,figure,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,menuitem,summary,content,element,shadow,template&quot;),fa=l(&quot;svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view&quot;,!0),pa=function(e){return&quot;pre&quot;===e},da=function(e){return la(e)||fa(e)},va=Object.create(null),ha=Object.freeze({createElement:Lt,createElementNS:It,createTextNode:Dt,createComment:Mt,insertBefore:Pt,removeChild:Rt,appendChild:Ft,parentNode:Bt,nextSibling:Ht,tagName:Ut,setTextContent:Vt,setAttribute:zt}),ma={create:function(e,t){Jt(t)},update:function(e,t){e.data.ref!==t.data.ref&amp;&amp;(Jt(e,!0),Jt(t))},destroy:function(e){Jt(e,!0)}},ga=new bo(&quot;&quot;,{},[]),ya=[&quot;create&quot;,&quot;activate&quot;,&quot;update&quot;,&quot;remove&quot;,&quot;destroy&quot;],_a={create:Zt,update:Zt,destroy:function(e){Zt(e,ga)}},ba=Object.create(null),$a=[ma,_a],Ca={create:en,update:en},xa={create:nn,update:nn},wa=/[\w).+\-_$\]]/,ka=&quot;__r&quot;,Aa=&quot;__c&quot;,Oa={create:En,update:En},Sa={create:jn,update:jn},Ta=d(function(e){var t={},n=/;(?![^(]*\))/g,r=/:(.+)/;return e.split(n).forEach(function(e){if(e){var n=e.split(r);n.length&gt;1&amp;&amp;(t[n[0].trim()]=n[1].trim())}}),t}),Ea=/^--/,ja=/\s*!important$/,Na=function(e,t,n){if(Ea.test(t))e.style.setProperty(t,n);else if(ja.test(n))e.style.setProperty(t,n.replace(ja,&quot;&quot;),&quot;important&quot;);else{var r=Ia(t);if(Array.isArray(n))for(var i=0,o=n.length;i&lt;o;i++)e.style[r]=n[i];else e.style[r]=n}},La=[&quot;Webkit&quot;,&quot;Moz&quot;,&quot;ms&quot;],Ia=d(function(e){if(Xo=Xo||document.createElement(&quot;div&quot;),&quot;filter&quot;!==(e=Li(e))&amp;&amp;e in Xo.style)return e;for(var t=e.charAt(0).toUpperCase()+e.slice(1),n=0;n&lt;La.length;n++){var r=La[n]+t;if(r in Xo.style)return r}}),Da={create:Rn,update:Rn},Ma=d(function(e){return{enterClass:e+&quot;-enter&quot;,enterToClass:e+&quot;-enter-to&quot;,enterActiveClass:e+&quot;-enter-active&quot;,leaveClass:e+&quot;-leave&quot;,leaveToClass:e+&quot;-leave-to&quot;,leaveActiveClass:e+&quot;-leave-active&quot;}}),Pa=qi&amp;&amp;!Gi,Ra=&quot;transition&quot;,Fa=&quot;animation&quot;,Ba=&quot;transition&quot;,Ha=&quot;transitionend&quot;,Ua=&quot;animation&quot;,Va=&quot;animationend&quot;;Pa&amp;&amp;(void 0===window.ontransitionend&amp;&amp;void 0!==window.onwebkittransitionend&amp;&amp;(Ba=&quot;WebkitTransition&quot;,Ha=&quot;webkitTransitionEnd&quot;),void 0===window.onanimationend&amp;&amp;void 0!==window.onwebkitanimationend&amp;&amp;(Ua=&quot;WebkitAnimation&quot;,Va=&quot;webkitAnimationEnd&quot;));var za=qi&amp;&amp;window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout,Ja=/\b(transform|all)(,|$)/,Ka=qi?{create:Xn,activate:Xn,remove:function(e,t){!0!==e.data.show?Gn(e,t):t()}}:{},qa=[Ca,xa,Oa,Sa,Da,Ka],Wa=qa.concat($a),Za=function(r){function o(e){return new bo(E.tagName(e).toLowerCase(),{},[],void 0,e)}function a(e,t){function n(){0==--n.listeners&amp;&amp;s(e)}return n.listeners=t,n}function s(e){var n=E.parentNode(e);t(n)&amp;&amp;E.removeChild(n,e)}function c(e,r,i,o,a){if(e.isRootInsert=!a,!u(e,r,i,o)){var s=e.data,c=e.children,l=e.tag;t(l)?(e.elm=e.ns?E.createElementNS(e.ns,l):E.createElement(l,e),g(e),v(e,c,r),t(s)&amp;&amp;m(e,r),d(i,e.elm,o)):n(e.isComment)?(e.elm=E.createComment(e.text),d(i,e.elm,o)):(e.elm=E.createTextNode(e.text),d(i,e.elm,o))}}function u(e,r,i,o){var a=e.data;if(t(a)){var s=t(e.componentInstance)&amp;&amp;a.keepAlive;if(t(a=a.hook)&amp;&amp;t(a=a.init)&amp;&amp;a(e,!1,i,o),t(e.componentInstance))return f(e,r),n(s)&amp;&amp;p(e,r,i,o),!0}}function f(e,n){t(e.data.pendingInsert)&amp;&amp;(n.push.apply(n,e.data.pendingInsert),e.data.pendingInsert=null),e.elm=e.componentInstance.$el,h(e)?(m(e,n),g(e)):(Jt(e),n.push(e))}function p(e,n,r,i){for(var o,a=e;a.componentInstance;)if(a=a.componentInstance._vnode,t(o=a.data)&amp;&amp;t(o=o.transition)){for(o=0;o&lt;S.activate.length;++o)S.activate[o](ga,a);n.push(a);break}d(r,e.elm,i)}function d(e,n,r){t(e)&amp;&amp;(t(r)?r.parentNode===e&amp;&amp;E.insertBefore(e,n,r):E.appendChild(e,n))}function v(e,t,n){if(Array.isArray(t))for(var r=0;r&lt;t.length;++r)c(t[r],n,e.elm,null,!0);else i(e.text)&amp;&amp;E.appendChild(e.elm,E.createTextNode(e.text))}function h(e){for(;e.componentInstance;)e=e.componentInstance._vnode;return t(e.tag)}function m(e,n){for(var r=0;r&lt;S.create.length;++r)S.create[r](ga,e);A=e.data.hook,t(A)&amp;&amp;(t(A.create)&amp;&amp;A.create(ga,e),t(A.insert)&amp;&amp;n.push(e))}function g(e){for(var n,r=e;r;)t(n=r.context)&amp;&amp;t(n=n.$options._scopeId)&amp;&amp;E.setAttribute(e.elm,n,&quot;&quot;),r=r.parent;t(n=ko)&amp;&amp;n!==e.context&amp;&amp;t(n=n.$options._scopeId)&amp;&amp;E.setAttribute(e.elm,n,&quot;&quot;)}function y(e,t,n,r,i,o){for(;r&lt;=i;++r)c(n[r],o,e,t)}function _(e){var n,r,i=e.data;if(t(i))for(t(n=i.hook)&amp;&amp;t(n=n.destroy)&amp;&amp;n(e),n=0;n&lt;S.destroy.length;++n)S.destroy[n](e);if(t(n=e.children))for(r=0;r&lt;e.children.length;++r)_(e.children[r])}function b(e,n,r,i){for(;r&lt;=i;++r){var o=n[r];t(o)&amp;&amp;(t(o.tag)?($(o),_(o)):s(o.elm))}}function $(e,n){if(t(n)||t(e.data)){var r,i=S.remove.length+1;for(t(n)?n.listeners+=i:n=a(e.elm,i),t(r=e.componentInstance)&amp;&amp;t(r=r._vnode)&amp;&amp;t(r.data)&amp;&amp;$(r,n),r=0;r&lt;S.remove.length;++r)S.remove[r](e,n);t(r=e.data.hook)&amp;&amp;t(r=r.remove)?r(e,n):n()}else s(e.elm)}function C(n,r,i,o,a){for(var s,u,l,f,p=0,d=0,v=r.length-1,h=r[0],m=r[v],g=i.length-1,_=i[0],$=i[g],C=!a;p&lt;=v&amp;&amp;d&lt;=g;)e(h)?h=r[++p]:e(m)?m=r[--v]:Kt(h,_)?(x(h,_,o),h=r[++p],_=i[++d]):Kt(m,$)?(x(m,$,o),m=r[--v],$=i[--g]):Kt(h,$)?(x(h,$,o),C&amp;&amp;E.insertBefore(n,h.elm,E.nextSibling(m.elm)),h=r[++p],$=i[--g]):Kt(m,_)?(x(m,_,o),C&amp;&amp;E.insertBefore(n,m.elm,h.elm),m=r[--v],_=i[++d]):(e(s)&amp;&amp;(s=Wt(r,p,v)),u=t(_.key)?s[_.key]:null,e(u)?(c(_,o,n,h.elm),_=i[++d]):(l=r[u],Kt(l,_)?(x(l,_,o),r[u]=void 0,C&amp;&amp;E.insertBefore(n,_.elm,h.elm),_=i[++d]):(c(_,o,n,h.elm),_=i[++d])));p&gt;v?(f=e(i[g+1])?null:i[g+1].elm,y(n,f,i,d,g,o)):d&gt;g&amp;&amp;b(n,r,p,v)}function x(r,i,o,a){if(r!==i){if(n(i.isStatic)&amp;&amp;n(r.isStatic)&amp;&amp;i.key===r.key&amp;&amp;(n(i.isCloned)||n(i.isOnce)))return i.elm=r.elm,void(i.componentInstance=r.componentInstance);var s,c=i.data;t(c)&amp;&amp;t(s=c.hook)&amp;&amp;t(s=s.prepatch)&amp;&amp;s(r,i);var u=i.elm=r.elm,l=r.children,f=i.children;if(t(c)&amp;&amp;h(i)){for(s=0;s&lt;S.update.length;++s)S.update[s](r,i);t(s=c.hook)&amp;&amp;t(s=s.update)&amp;&amp;s(r,i)}e(i.text)?t(l)&amp;&amp;t(f)?l!==f&amp;&amp;C(u,l,f,o,a):t(f)?(t(r.text)&amp;&amp;E.setTextContent(u,&quot;&quot;),y(u,null,f,0,f.length-1,o)):t(l)?b(u,l,0,l.length-1):t(r.text)&amp;&amp;E.setTextContent(u,&quot;&quot;):r.text!==i.text&amp;&amp;E.setTextContent(u,i.text),t(c)&amp;&amp;t(s=c.hook)&amp;&amp;t(s=s.postpatch)&amp;&amp;s(r,i)}}function w(e,r,i){if(n(i)&amp;&amp;t(e.parent))e.parent.data.pendingInsert=r;else for(var o=0;o&lt;r.length;++o)r[o].data.hook.insert(r[o])}function k(e,n,r){n.elm=e;var i=n.tag,o=n.data,a=n.children;if(t(o)&amp;&amp;(t(A=o.hook)&amp;&amp;t(A=A.init)&amp;&amp;A(n,!0),t(A=n.componentInstance)))return f(n,r),!0;if(t(i)){if(t(a))if(e.hasChildNodes()){for(var s=!0,c=e.firstChild,u=0;u&lt;a.length;u++){if(!c||!k(c,a[u],r)){s=!1;break}c=c.nextSibling}if(!s||c)return!1}else v(n,a,r);if(t(o))for(var l in o)if(!j(l)){m(n,r);break}}else e.data!==n.text&amp;&amp;(e.data=n.text);return!0}var A,O,S={},T=r.modules,E=r.nodeOps;for(A=0;A&lt;ya.length;++A)for(S[ya[A]]=[],O=0;O&lt;T.length;++O)t(T[O][ya[A]])&amp;&amp;S[ya[A]].push(T[O][ya[A]]);var j=l(&quot;attrs,style,class,staticClass,staticStyle,key&quot;);return function(r,i,a,s,u,l){if(e(i))return void(t(r)&amp;&amp;_(r));var f=!1,p=[];if(e(r))f=!0,c(i,p,u,l);else{var d=t(r.nodeType);if(!d&amp;&amp;Kt(r,i))x(r,i,p,s);else{if(d){if(1===r.nodeType&amp;&amp;r.hasAttribute(Fi)&amp;&amp;(r.removeAttribute(Fi),a=!0),n(a)&amp;&amp;k(r,i,p))return w(i,p,!0),r;r=o(r)}var v=r.elm,m=E.parentNode(v);if(c(i,p,v._leaveCb?null:m,E.nextSibling(v)),t(i.parent)){for(var g=i.parent;g;)g.elm=i.elm,g=g.parent;if(h(i))for(var y=0;y&lt;S.create.length;++y)S.create[y](ga,i.parent)}t(m)?b(m,[r],0,0):t(r.tag)&amp;&amp;_(r)}}return w(i,p,f),i.elm}}({nodeOps:ha,modules:Wa});Gi&amp;&amp;document.addEventListener(&quot;selectionchange&quot;,function(){var e=document.activeElement;e&amp;&amp;e.vmodel&amp;&amp;or(e,&quot;input&quot;)});var Ga={inserted:function(e,t,n){if(&quot;select&quot;===n.tag){var r=function(){er(e,t,n.context)};r(),(Zi||Yi)&amp;&amp;setTimeout(r,0)}else&quot;textarea&quot;!==n.tag&amp;&amp;&quot;text&quot;!==e.type&amp;&amp;&quot;password&quot;!==e.type||(e._vModifiers=t.modifiers,t.modifiers.lazy||(e.addEventListener(&quot;change&quot;,ir),Qi||(e.addEventListener(&quot;compositionstart&quot;,rr),e.addEventListener(&quot;compositionend&quot;,ir)),Gi&amp;&amp;(e.vmodel=!0)))},componentUpdated:function(e,t,n){if(&quot;select&quot;===n.tag){er(e,t,n.context);(e.multiple?t.value.some(function(t){return tr(t,e.options)}):t.value!==t.oldValue&amp;&amp;tr(t.value,e.options))&amp;&amp;or(e,&quot;change&quot;)}}},Ya={bind:function(e,t,n){var r=t.value;n=ar(n);var i=n.data&amp;&amp;n.data.transition,o=e.__vOriginalDisplay=&quot;none&quot;===e.style.display?&quot;&quot;:e.style.display;r&amp;&amp;i&amp;&amp;!Gi?(n.data.show=!0,Zn(n,function(){e.style.display=o})):e.style.display=r?o:&quot;none&quot;},update:function(e,t,n){var r=t.value;r!==t.oldValue&amp;&amp;(n=ar(n),n.data&amp;&amp;n.data.transition&amp;&amp;!Gi?(n.data.show=!0,r?Zn(n,function(){e.style.display=e.__vOriginalDisplay}):Gn(n,function(){e.style.display=&quot;none&quot;})):e.style.display=r?e.__vOriginalDisplay:&quot;none&quot;)},unbind:function(e,t,n,r,i){i||(e.style.display=e.__vOriginalDisplay)}},Qa={model:Ga,show:Ya},Xa={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]},es={name:&quot;transition&quot;,props:Xa,abstract:!0,render:function(e){var t=this,n=this.$slots.default;if(n&amp;&amp;(n=n.filter(function(e){return e.tag}),n.length)){var r=this.mode,o=n[0];if(lr(this.$vnode))return o;var a=sr(o);if(!a)return o;if(this._leaving)return ur(e,o);var s=&quot;__transition-&quot;+this._uid+&quot;-&quot;;a.key=null==a.key?s+a.tag:i(a.key)?0===String(a.key).indexOf(s)?a.key:s+a.key:a.key;var c=(a.data||(a.data={})).transition=cr(this),u=this._vnode,l=sr(u);if(a.data.directives&amp;&amp;a.data.directives.some(function(e){return&quot;show&quot;===e.name})&amp;&amp;(a.data.show=!0),l&amp;&amp;l.data&amp;&amp;!fr(a,l)){var f=l&amp;&amp;(l.data.transition=m({},c));if(&quot;out-in&quot;===r)return this._leaving=!0,Q(f,&quot;afterLeave&quot;,function(){t._leaving=!1,t.$forceUpdate()}),ur(e,o);if(&quot;in-out&quot;===r){var p,d=function(){p()};Q(c,&quot;afterEnter&quot;,d),Q(c,&quot;enterCancelled&quot;,d),Q(f,&quot;delayLeave&quot;,function(e){p=e})}}return o}}},ts=m({tag:String,moveClass:String},Xa);delete ts.mode;var ns={props:ts,render:function(e){for(var t=this.tag||this.$vnode.data.tag||&quot;span&quot;,n=Object.create(null),r=this.prevChildren=this.children,i=this.$slots.default||[],o=this.children=[],a=cr(this),s=0;s&lt;i.length;s++){var c=i[s];c.tag&amp;&amp;null!=c.key&amp;&amp;0!==String(c.key).indexOf(&quot;__vlist&quot;)&amp;&amp;(o.push(c),n[c.key]=c,(c.data||(c.data={})).transition=a)}if(r){for(var u=[],l=[],f=0;f&lt;r.length;f++){var p=r[f];p.data.transition=a,p.data.pos=p.elm.getBoundingClientRect(),n[p.key]?u.push(p):l.push(p)}this.kept=e(t,null,u),this.removed=l}return e(t,null,o)},beforeUpdate:function(){this.__patch__(this._vnode,this.kept,!1,!0),this._vnode=this.kept},updated:function(){var e=this.prevChildren,t=this.moveClass||(this.name||&quot;v&quot;)+&quot;-move&quot;;if(e.length&amp;&amp;this.hasMove(e[0].elm,t)){e.forEach(pr),e.forEach(dr),e.forEach(vr);var n=document.body;n.offsetHeight;e.forEach(function(e){if(e.data.moved){var n=e.elm,r=n.style;Vn(n,t),r.transform=r.WebkitTransform=r.transitionDuration=&quot;&quot;,n.addEventListener(Ha,n._moveCb=function e(r){r&amp;&amp;!/transform$/.test(r.propertyName)||(n.removeEventListener(Ha,e),n._moveCb=null,zn(n,t))})}})}},methods:{hasMove:function(e,t){if(!Pa)return!1;if(null!=this._hasMove)return this._hasMove;var n=e.cloneNode();e._transitionClasses&amp;&amp;e._transitionClasses.forEach(function(e){Bn(n,e)}),Fn(n,t),n.style.display=&quot;none&quot;,this.$el.appendChild(n);var r=Kn(n);return this.$el.removeChild(n),this._hasMove=r.hasTransform}}},rs={Transition:es,TransitionGroup:ns};vt.config.mustUseProp=na,vt.config.isReservedTag=da,vt.config.isReservedAttr=ea,vt.config.getTagNamespace=Et,vt.config.isUnknownElement=jt,m(vt.options.directives,Qa),m(vt.options.components,rs),vt.prototype.__patch__=qi?Za:y,vt.prototype.$mount=function(e,t){return e=e&amp;&amp;qi?Nt(e):void 0,me(this,e,t)},setTimeout(function(){Ui.devtools&amp;&amp;ao&amp;&amp;ao.emit(&quot;init&quot;,vt)},0);var is,os=!!qi&amp;&amp;function(e,t){var n=document.createElement(&quot;div&quot;);return n.innerHTML=&#39;&lt;div a=&quot;&#39;+e+&#39;&quot;&gt;&#39;,n.innerHTML.indexOf(t)&gt;0}(&quot;\n&quot;,&quot;&amp;#10;&quot;),as=l(&quot;area,base,br,col,embed,frame,hr,img,input,isindex,keygen,link,meta,param,source,track,wbr&quot;),ss=l(&quot;colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source&quot;),cs=l(&quot;address,article,aside,base,blockquote,body,caption,col,colgroup,dd,details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,title,tr,track&quot;),us=/([^\s&quot;&#39;&lt;&gt;\/=]+)/,ls=/(?:=)/,fs=[/&quot;([^&quot;]*)&quot;+/.source,/&#39;([^&#39;]*)&#39;+/.source,/([^\s&quot;&#39;=&lt;&gt;`]+)/.source],ps=new RegExp(&quot;^\\s*&quot;+us.source+&quot;(?:\\s*(&quot;+ls.source+&quot;)\\s*(?:&quot;+fs.join(&quot;|&quot;)+&quot;))?&quot;),ds=&quot;[a-zA-Z_][\\w\\-\\.]*&quot;,vs=&quot;((?:&quot;+ds+&quot;\\:)?&quot;+ds+&quot;)&quot;,hs=new RegExp(&quot;^&lt;&quot;+vs),ms=/^\s*(\/?)&gt;/,gs=new RegExp(&quot;^&lt;\\/&quot;+vs+&quot;[^&gt;]*&gt;&quot;),ys=/^&lt;!DOCTYPE [^&gt;]+&gt;/i,_s=/^&lt;!--/,bs=/^&lt;!\[/,$s=!1;&quot;x&quot;.replace(/x(.)?/g,function(e,t){$s=&quot;&quot;===t});var Cs,xs,ws,ks,As,Os,Ss,Ts,Es,js,Ns,Ls,Is,Ds,Ms,Ps,Rs,Fs,Bs=l(&quot;script,style,textarea&quot;,!0),Hs={},Us={&quot;&amp;lt;&quot;:&quot;&lt;&quot;,&quot;&amp;gt;&quot;:&quot;&gt;&quot;,&quot;&amp;quot;&quot;:&#39;&quot;&#39;,&quot;&amp;amp;&quot;:&quot;&amp;&quot;,&quot;&amp;#10;&quot;:&quot;\n&quot;},Vs=/&amp;(?:lt|gt|quot|amp);/g,zs=/&amp;(?:lt|gt|quot|amp|#10);/g,Js=/\{\{((?:.|\n)+?)\}\}/g,Ks=/[-.*+?^${}()|[\]\/\\]/g,qs=d(function(e){var t=e[0].replace(Ks,&quot;\\$&amp;&quot;),n=e[1].replace(Ks,&quot;\\$&amp;&quot;);return new RegExp(t+&quot;((?:.|\\n)+?)&quot;+n,&quot;g&quot;)}),Ws=/^@|^v-on:/,Zs=/^v-|^@|^:/,Gs=/(.*?)\s+(?:in|of)\s+(.*)/,Ys=/\((\{[^}]*\}|[^,]*),([^,]*)(?:,([^,]*))?\)/,Qs=/:(.*)$/,Xs=/^:|^v-bind:/,ec=/\.[^.]+/g,tc=d(hr),nc=/^xmlns:NS\d+/,rc=/^NS\d+:/,ic=d(Br),oc=/^\s*([\w$_]+|\([^)]*?\))\s*=&gt;|^function\s*\(/,ac=/^\s*[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\[&#39;.*?&#39;]|\[&quot;.*?&quot;]|\[\d+]|\[[A-Za-z_$][\w$]*])*\s*$/,sc={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},cc=function(e){return&quot;if(&quot;+e+&quot;)return null;&quot;},uc={stop:&quot;$event.stopPropagation();&quot;,prevent:&quot;$event.preventDefault();&quot;,self:cc(&quot;$event.target !== $event.currentTarget&quot;),ctrl:cc(&quot;!$event.ctrlKey&quot;),shift:cc(&quot;!$event.shiftKey&quot;),alt:cc(&quot;!$event.altKey&quot;),meta:cc(&quot;!$event.metaKey&quot;),left:cc(&quot;&#39;button&#39; in $event &amp;&amp; $event.button !== 0&quot;),middle:cc(&quot;&#39;button&#39; in $event &amp;&amp; $event.button !== 1&quot;),right:cc(&quot;&#39;button&#39; in $event &amp;&amp; $event.button !== 2&quot;)},lc={bind:Gr,cloak:y},fc={staticKeys:[&quot;staticClass&quot;],transformNode:Ci,genData:xi},pc={staticKeys:[&quot;staticStyle&quot;],transformNode:wi,genData:ki},dc=[fc,pc],vc={model:Cn,text:Ai,html:Oi},hc={expectHTML:!0,modules:dc,directives:vc,isPreTag:pa,isUnaryTag:as,mustUseProp:na,canBeLeftOpenTag:ss,isReservedTag:da,getTagNamespace:Et,staticKeys:function(e){return e.reduce(function(e,t){return e.concat(t.staticKeys||[])},[]).join(&quot;,&quot;)}(dc)},mc=function(e){function t(t,n){var r=Object.create(e),i=[],o=[];if(r.warn=function(e,t){(t?o:i).push(e)},n){n.modules&amp;&amp;(r.modules=(e.modules||[]).concat(n.modules)),n.directives&amp;&amp;(r.directives=m(Object.create(e.directives),n.directives));for(var a in n)&quot;modules&quot;!==a&amp;&amp;&quot;directives&quot;!==a&amp;&amp;(r[a]=n[a])}var s=bi(t,r);return s.errors=i,s.tips=o,s}function n(e,n,i){n=n||{};var o=n.delimiters?String(n.delimiters)+e:e;if(r[o])return r[o];var a=t(e,n),s={},c=[];s.render=$i(a.render,c);var u=a.staticRenderFns.length;s.staticRenderFns=new Array(u);for(var l=0;l&lt;u;l++)s.staticRenderFns[l]=$i(a.staticRenderFns[l],c);return r[o]=s}var r=Object.create(null);return{compile:t,compileToFunctions:n}}(hc),gc=mc.compileToFunctions,yc=d(function(e){var t=Nt(e);return t&amp;&amp;t.innerHTML}),_c=vt.prototype.$mount;return vt.prototype.$mount=function(e,t){if((e=e&amp;&amp;Nt(e))===document.body||e===document.documentElement)return this;var n=this.$options;if(!n.render){var r=n.template;if(r)if(&quot;string&quot;==typeof r)&quot;#&quot;===r.charAt(0)&amp;&amp;(r=yc(r));else{if(!r.nodeType)return this;r=r.innerHTML}else e&amp;&amp;(r=Si(e));if(r){var i=gc(r,{shouldDecodeNewlines:os,delimiters:n.delimiters},this),o=i.render,a=i.staticRenderFns;n.render=o,n.staticRenderFns=a}}return _c.call(this,e,t)},vt.compile=gc,vt});&lt;/script&gt;&lt;/head&gt;&lt;body&gt;&lt;div id=&quot;clock&quot;&gt;    &lt;p class=&quot;date&quot;&gt;...404 error page...&lt;/p&gt;    &lt;p class=&quot;time&quot;&gt;{{ time }}&lt;/p&gt;    &lt;p class=&quot;text&quot;&gt;{{ date }}&lt;/p&gt;&lt;/div&gt;&lt;script&gt;var clock = new Vue({    el: &#39;#clock&#39;,    data: {        time: &#39;&#39;,        date: &#39;&#39;    }});var week = [&#39;星期日&#39;, &#39;星期一&#39;, &#39;星期二&#39;, &#39;星期三&#39;, &#39;星期四&#39;, &#39;星期五&#39;, &#39;星期六&#39;];var timerID = setInterval(updateTime, 1000);updateTime();function updateTime() {    var cd = new Date();    clock.time = zeroPadding(cd.getHours(), 2) + &#39;:&#39; + zeroPadding(cd.getMinutes(), 2) + &#39;:&#39; + zeroPadding(cd.getSeconds(), 2);    clock.date = zeroPadding(cd.getFullYear(), 4) + &#39;-&#39; + zeroPadding(cd.getMonth()+1, 2) + &#39;-&#39; + zeroPadding(cd.getDate(), 2) + &#39; &#39; + week[cd.getDay()];};function zeroPadding(num, digit) {    var zero = &#39;&#39;;    for(var i = 0; i &lt; digit; i++) {        zero += &#39;0&#39;;    }    return (zero + num).slice(-digit);}&lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre><p>部署到GitHub之后，访问不存在的页面就会跳出来了</p><h2><span id="26-域名绑定">26、域名绑定</span></h2><p>在博客根目录下的source目录下新建CNAME文件，如果主题source目录下有这个文件请删除</p><p>修改CNAME文件，内容为你的域名，例如我的</p><pre><code>wallleap.cn</code></pre><p>接着去GitHub pages更改域名，开启SSL</p><p>前往阿里云的的域名解析页面添加CANME解析</p><p>@ –&gt; 用户名.github.io</p><p>之后就可以以新的域名访问了<a href="https://wallleap.cn">https://wallleap.cn</a></p><h2><span id="27-网站收录">27、网站收录</span></h2><p>主要是向各大搜索引擎提交链接</p><p>谷歌收录</p><p><a href="http://zhanzhang.so.com/sitetool/site_manage" target="_blank" rel="noopener">360站长平台</a></p><p><a href="https://ziyuan.baidu.com/linksubmit/" target="_blank" rel="noopener">百度搜索资源平台</a></p><p>百度的需要注意一下，由于GitHub将百度封了，因此你需要把博客双线部署到gitlab或coding才能收录</p><p><a href="http://zhanzhang.sogou.com/" target="_blank" rel="noopener">搜狗站长平台</a></p><h2><span id="28-网站分析">28、网站分析</span></h2><h3><span id="百度统计">百度统计</span></h3><p>前往百度统计官网</p><p><a href="https://tongji.baidu.com/web/welcome/login" target="_blank" rel="noopener">Baidu统计</a></p><p>以百度账号登录后，点击【管理】，在【网站列表】中新增网站</p><p>获取代码，将代码复制到标签前，进行代码检测</p><p>以后就可以查看访问情况了</p><h3><span id="cnzz">cnzz</span></h3><p>点击</p><p><a href="https://passport.umeng.com/login?appId=cnzz" target="_blank" rel="noopener">友盟+</a></p><p>前往，注册登录后，添加站点</p><p>根据自己喜好获取代码</p><p>粘贴到<script>!function(e){var c=Array.prototype.slice.call(document.querySelectorAll("img[data-original]"));function i(){for(var r=0;r<c.length;r++)t=c[r],0<=(n=t.getBoundingClientRect()).bottom&&0<=n.left&&n.top<=(e.innerHeight||document.documentElement.clientHeight)&&function(){var t,n,e,i,o=c[r];t=o,n=function(){c=c.filter(function(t){return o!==t})},e=new Image,i=t.getAttribute("data-original"),e.onload=function(){t.src=i,n&&n()},e.src=i}();var t,n}i(),e.addEventListener("scroll",function(){var t,n;t=i,n=e,clearTimeout(t.tId),t.tId=setTimeout(function(){t.call(n)},500)})}(this);</script><script>window.addEventListener("load",function(){var t=/\.(gif|jpg|jpeg|tiff|png)$/i,r=/^data:image\/[a-z]+;base64,/;Array.prototype.slice.call(document.querySelectorAll("img[data-original]")).forEach(function(a){var e=a.parentNode;"A"===e.tagName&&(e.href.match(t)||e.href.match(r))&&(e.href=a.dataset.original)})});</script>前</p><p>之后就可以点击查看信息了</p><h2><span id="29-小技巧">29、小技巧</span></h2><h3><span id="博客备份">博客备份</span></h3><p>有一个博客备份插件</p><p><a href="https://github.com/coneycode/hexo-git-backup" target="_blank" rel="noopener">hexo-git-backup</a></p><p>能够传到backup分支</p><p>我们还可以把整个博客文件夹上传到GitHub私有仓库</p><h3><span id="本地预览">本地预览</span></h3><p>使用命令换个端口预览<code>hexo s -p 5000</code></p><p>也可以添加下列代码到博客配置文件中,使用hexo s时将会以5000端口运行</p><pre><code>server:  port: 5000  compress: true  header: true</code></pre><h3><span id="快捷命令">快捷命令</span></h3><p>alias设置命令别名，将下面代码复制到<strong><em>.bashrc</em></strong>文件中</p><pre><code class="shell">alias hs=&#39;hexo clean &amp;&amp; hexo g &amp;&amp; hexo s&#39;alias hd=&#39;hexo clean &amp;&amp; hexo g &amp;&amp; hexo d&#39;alias gp=&#39;git add . &amp;&amp; git commit -m &quot;update&quot; &amp;&amp; git push -f&#39;</code></pre><p>以后输入hs命令就可以本地预览、hd部署、gp上传到仓库</p><h2><span id="30-静态资源压缩">30、静态资源压缩</span></h2><p>博客使用了图片懒加载和预加载虽然加快了一点速度，但访问还是有点慢</p><p>那么直接把html、css、js代码中的空格去掉，进行压缩</p><p>还有压缩一下图片，能够一定程度上缩小public文件夹的大小</p><p>参考这篇文章：</p><p><a href="https://segmentfault.com/a/1190000019842178" target="_blank" rel="noopener">Hexo博客使用gulp压缩静态资源</a></p><p>安装全局gulp</p><p><code>npm install gulp -g</code></p><p>安装插件</p><pre><code>npm install gulp-minify-css gulp-uglify gulp-htmlmin gulp-htmlclean gulp gulp-imagemin --save# 解决【Gulp打包问题】 GulpUglifyError: unable to minify JavaScript# 解决 gulp-uglify 压缩JavaScript 不兼容 es5 语法的问题npm install babel-core@6.26.3 --savenpm install gulp-babel@7.0.1 --savenpm install babel-preset-es2015@6.24.1 --save# gulp-babel 取消严格模式方法(&quot;use strict&quot;)npm install babel-plugin-transform-remove-strict-mode --save</code></pre><p>问题：如果安装<code>gulp-imagemin</code>错误请执行以下语句<br><code>sudo npm i gulp-imagemin --unsafe-perms</code></p><p>博客根目录创建gulpfile.js</p><p>上面文章中有一句在这个版本会报错，已修改</p><pre><code class="javascript">var gulp = require(&#39;gulp&#39;);var minifycss = require(&#39;gulp-minify-css&#39;);var uglify = require(&#39;gulp-uglify&#39;);var htmlmin = require(&#39;gulp-htmlmin&#39;);var htmlclean = require(&#39;gulp-htmlclean&#39;);var imagemin = require(&#39;gulp-imagemin&#39;);var babel = require(&#39;gulp-babel&#39;);// 压缩css文件gulp.task(&#39;minify-css&#39;, function (done) {    return gulp.src(&#39;./public/**/*.css&#39;)        .pipe(minifycss())        .pipe(gulp.dest(&#39;./public&#39;));    done();});// 压缩html文件gulp.task(&#39;minify-html&#39;, function (done) {    return gulp.src(&#39;./public/**/*.html&#39;)        .pipe(htmlclean())        .pipe(htmlmin({            removeComments: true,            minifyJS: true,            minifyCSS: true,            minifyURLs: true,        }))        .pipe(gulp.dest(&#39;./public&#39;));    done();});// 压缩js文件gulp.task(&#39;minify-js&#39;, function (done) {    return gulp.src([&#39;./public/**/*.js&#39;, &#39;!./public/**/*.min.js&#39;])        .pipe(babel({            //将ES6代码转译为可执行的JS代码            presets: [&#39;es2015&#39;] // es5检查机制        }))        .pipe(uglify())        .pipe(gulp.dest(&#39;./public&#39;));    done();});// 压缩 public/images 目录内图片(Version&lt;3)// gulp.task(&#39;minify-images&#39;, function () {//     gulp.src(&#39;./public/images/**/*.*&#39;)//         .pipe(imagemin({//             optimizationLevel: 5, //类型：Number  默认：3  取值范围：0-7（优化等级）//             progressive: true, //类型：Boolean 默认：false 无损压缩jpg图片//             interlaced: false, //类型：Boolean 默认：false 隔行扫描gif进行渲染//             multipass: false, //类型：Boolean 默认：false 多次优化svg直到完全优化//         }))//         .pipe(gulp.dest(&#39;./public/images&#39;));// });// 压缩 public/images 目录内图片(Version&gt;3)gulp.task(&#39;minify-images&#39;, function (done) {    gulp.src(&#39;./public/images/**/*.*&#39;)        .pipe(imagemin([            imagemin.gifsicle({interlaced: true}),            // imagemin.jpegtran({progressive: true}), // 版本升级，改用下面这个            imagemin.mozjpeg({progressive: true,}),            imagemin.optipng({optimizationLevel: 5}),            imagemin.svgo({                plugins: [                    {removeViewBox: true},                    {cleanupIDs: false}                ]            })        ]))        .pipe(gulp.dest(&#39;./public/images&#39;));    done();});//4.0以前的写法 //gulp.task(&#39;default&#39;, [//  &#39;minify-html&#39;, &#39;minify-css&#39;, &#39;minify-js&#39;, &#39;minify-images&#39;//]);//4.0以后的写法// 执行 gulp 命令时执行的任务gulp.task(&#39;default&#39;, gulp.series(gulp.parallel(&#39;minify-html&#39;, &#39;minify-css&#39;, &#39;minify-js&#39;, &#39;minify-images&#39;)), function () {    console.log(&quot;----------gulp Finished----------&quot;);    // Do something after a, b, and c are finished.});</code></pre><p>根目录下创建.babelrc</p><pre><code>{    &#39;presets&#39;: [&#39;es2015&#39;],    &quot;plugins&quot;: [&quot;transform-remove-strict-mode&quot;]}</code></pre><p>每次生成静态文件的时候，使用命令<code>hexo g &amp;&amp; gulp</code>即可，能够压缩几兆的空间</p><hr><p>好的呢，这篇文章就到这里啦</p><p>这么多，可算是累死我了</p><p>再见~</p><p>参考文档：</p><p><a href="https://blog.csdn.net/qq_36759224/article/details/85420403" target="_blank" rel="noopener">Hexo 博客优化之博客美化系列</a></p><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="笔记" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Hexo" scheme="https://wallleap.cn/categories/%E7%AC%94%E8%AE%B0/Hexo/"/>
    
    
      <category term="主题" scheme="https://wallleap.cn/tags/%E4%B8%BB%E9%A2%98/"/>
    
      <category term="Hexo" scheme="https://wallleap.cn/tags/Hexo/"/>
    
      <category term="Blog" scheme="https://wallleap.cn/tags/Blog/"/>
    
      <category term="博客" scheme="https://wallleap.cn/tags/%E5%8D%9A%E5%AE%A2/"/>
    
  </entry>
  
</feed>
