总体笔记总结

  1. link和@import方式引入外部css文件的区别???
                1. link是html语法, @import是css语法
                2. link可以引入多种文件, @import只能引入css文件
                3. link方式引入文件是在html文档开始加载时就开始引入css文件, 而@import必须等到html文档加载完毕之后才开始引入css文件
                4. link方式引入的css代码, 将来可以通过javascript修改, 而@import方式引入的css代码后期无法修改
                综上所述, 我们以后引入css文件 建议使用link标签方式引入

  2.                 绝大部分的浏览器的默认情况下, html的盒模型的盒子大小是累加的, 即
                    盒子的宽 = width + 左右padding + 左右border-width + 左右margin
                    盒子的高 = height + 上下padding + 上下border-width + 上下margin
                    
                    但是这种计算规则, 需要我们特别注意, 注意当盒子的大小越来越大时, 其占据的空间也会越来越多, 这样会不会侵占别的html元素原有的空间, 导致别的html元素空间不足, 布局发生错乱.
                    
                    解决办法:
                        1. 人为计算, 当需要增加盒子大小时, 可以人为缩小原有的宽度或者高度, 让盒子大小始终保持一个固定值
                        2. 直接改变html对盒模型盒子大小的计算规则 box-sizing
                            html里的盒模型分为两种:
                            2.1 普通盒模型 content-box, 绝大部分的浏览器的默认值
                            2.2 怪异盒模型 border-box, IE浏览器的默认盒模型
                            以上两种盒模型的组成部分相同, 书写语法相同, 区别只是盒子的总大小的计算方式不同
                    
                        怪异盒模型的盒子的宽 = width(包含左右padding和左右border-width) + 左右margin
                        怪异盒模型的盒子的高 = height(包含上下padding和上下border-width) + 上下margin
                    
                    怪异盒模型当设置了padding或者border, 系统会自动缩小元素的width或者height, 来保证content, padding, border三者组成的盒子的总大小保持不变     

  3. 盒模型的margin在使用过程中的会出现的问题
                    问题1: 相邻兄弟元素在垂直方向上margin会出现"融合"
                        融合标准: 以两者之间较大的值为最终有效值.
                        解决办法:
                            1. 在设置的时候只给两者其中的一个设置即可, 不要两个都设置
                            2. 给两个元素其中的任意一个添加display:inline-block;
                    
                    问题2: 嵌套的父子元素在垂直方向上margin会出现"传递"
                        传递现象: 给子元素设置了margin-top, 产生的效果像是子元素的margin-top传递给了父元素.
                        解决办法:
                            1. 用父元素的padding代替子元素的margin实现相同的效果
                            2. 给父级加边框
                            3. 给父级加overflow:hidden
                            4. 给父级或子级任意一个添加display:inline-block

  4. 我们以后在通过浮动进行布局的时候, 需要注意两种情况下会出现问题
                1. 相邻兄弟, 一个元素浮动, 会影响其下面的元素, 如果其下面的元素想要消除这种影响, 需要使用clear样式
                    clear样式: 消除浮动产生的影响, 切记: 要添加给受到浮动影响的元素, 而不要加给浮动的元素!
                    clear: 
                        1.1 left, 消除左浮动的影响
                        1.2 right, 消除右浮动的影响
                        1.3 both, 消除左或者右浮动的影响
                
                2. 父子元素, 如果父元素没有设置高度, 其目的是为了让内容撑出其高度, 但是如果此时父级里的所有子元素都浮动, 那么因为浮动元素脱离文档流, 会导致父级无法再根据其内容撑出高度了, 父级高度为0, 如果此时父元素底下还有兄弟元素, 其兄弟元素就会大大的受影响, 我们称之为"父级塌陷".
                    解决办法:
                        2.1 给父级人为设置高度, 但是不灵活
                        2.2 给父级添加overflow:hidden, 让父标签闭合高度
                        2.3 在所有浮动元素的后面添加一个非行的空元素, 给其添加clear:both
    .box::after {
    				display: block;
    				content: "";
    				clear: both;
    }

  5. 定位布局: 对元素的位置进行精准的调整, 固定元素位置, 调整元素层级
                position样式, 对页面进行定位布局
                定位的值一共有4种, 意味着定位的方式有4种
                1. static 静态定位
                2. relative 相对定位
                3. absolute 绝对定位
                4. fixed 固定定位
                注意: 当我们需要对元素定位时, 只设置了position, 元素是不会移动的, position只是规定了元素的定位方式, 并没有去设置元素应该如何改变其位置, 如果想要真正的移动元素, 我们还需要top, bottom, left, right 这四个样式配合使用. 而且, top, bottom, left, right这四个样式只对非static定位的元素其作用
                1. static 静态定位, 该值是position的默认值, 所有元素默认都是静态定位
                2. relative 相对定位
                    2.1 相对定位的元素 不完全脱离文档流, 会保留自己的位置, 但是层级却会提升
                    2.2 相对定位的元素, 其进行移动的参考位置是自身当前的位置.
                3. absolute 绝对定位
                    3.1 绝对定位的元素完全脱离文档流, 失去自己之前的位置, 层级提升
                    3.2 绝对定位的元素的在确定自己的参考时, 永远会去选择距离它最近的, 设置了非static定位的祖先元素当做参考位置. 如果其所有的祖先元素都没有定位, 此时, 它才会相对于整个浏览器文档进行定位.
                4. fixed 固定定位
                  4.1 固定定位的元素完全脱离文档流, 失去自己之前的位置, 层级提高
                  4.2 固定定位的元素的参考是整个浏览器文档
                  4.3 固定定位的元素不会跟随浏览器滚动而滚动
                将来在给元素进行定位时需要注意的问题:
                1. 在不设置宽度的情况下, 不完全脱离文档流的元素(relative), 其宽度参考父级, 完全脱离文档流的定位元素(absolute, fixed)其宽度由内容撑出
                2. 完全脱离文档流的元素会失去自己的位置, 与浮动元素一样, 也会影响其下面的元素, 但是, 受到影响的元素不能通过clear样式清除影响, 只能通过margin或者padding间接解决
                
                3. 多个浮动元素的层级是相等的, 但是多个绝对定位的元素的层级不等, 写的越晚, 层级越高


  6. 伪类选择器    1. :link 能找到定义了超链接且未被访问过的元素  2. :visited 能找到定义了超链接且已经访问过的元素  3. :hover 能找到鼠标在其上面的元素   4. :active 能找到鼠标在其上面 按下不松手的元素 注意: 如果我们将来 对同一个元素需要设置以上4个伪类里的多个时, 在选择器的书写上是要遵循以下顺序:     link-visited-hover-active


  7.             隐藏元素可以通过可以通过3个手法实现
                1. display样式 隐藏元素, 并失去自己之前的位置
                2. visibility样式 隐藏元素, 会保留自己之前的位置
                3. opacity样式 仅仅是把元素的透明度变为0, 元素本身并没有隐藏, 以后不要用此方法隐藏元素


    1. 与背景有关的css样式
                  1. 背景色 background-color
                  2. 背景图 background-image
                  3. 背景图的平铺方式 background-repeat
                  4. 背景图的位置 background-position
                  5. 背景图是否跟随网页滚动 background-attachment(scroll,fixed)
                  6. 背景图大小 background-size                                                                                        7. 第一张完整背景图开始出现的位置 background-origin
                      7.1 border-box 第一张完整背景图从border开始平铺
                      7.2 padding-box 第一张完整背景图从padding开始平铺
                      7.3 content-box 第一张完整背景图从content开始平铺
                      
                  8. 背景剪裁 background-clip 从设置的起始外置向外剪裁, 保留里面的背景
                      8.1 border-box 从border开始向外剪裁
                      8.2 padding-box 从padding开始向外剪裁
                      8.3 content-box 从content开始向外剪裁
                  
                  注意: 如果合写, 又同时需要设置background-orgin和background-clip, 第一个出现的为background-orgin, 第二个出现为background-clip    
                  背景样式的合写语法:
                  background 书写顺序没有要求, 但是如果需要同时书写bg-position和bg-size, 两者一定要先写bg-position, 再写bg-size, 中间要用/分开. 哪怕只需要写一个值, 也可以使用background                                                                                                                                                             背景图 background-image: url(img/big.jpg);
                          1. 默认背景图铺满整个元素
                          2. 背景图如果要显示需要元素由尺寸大小, 没有大小, 背景图无法显示
                          3. 背景图因为是背景, 不影响继续向元素里添加内容并显示
                       
                          背景图的平铺方式 background-repeat: no-repeat;
                          1. repeat 默认值, 水平竖直都平铺
                          2. repeat-x 水平方向平铺
                          3. repeat-y 竖直方向平铺
                          4. no-repeat 不平铺
                       
                          背景图的位置 第一个值表示x方向的位置, 第二个值表示y方向的位置 background-position: 50% 50%;
                          写法1: px值
                          写法2: 方位单词
                              x方向: left center right
                              y方向: top center bottom
                          写法3: 百分比
                              x方向: 容器的宽-背景图的宽*百分比x
                              y方向: 容器的高-背景图的高*百分比y
                       
                       背景图是否跟随网页滚动background-attachment: fixed
                          1. scroll 默认值
                          2. fixed 固定不滚动
                       
                       背景图尺寸 background-size:
                          写法1: px数值
                          写法2: 百分比 分别相对于容器的宽度和高度
                          写法3: cover
                          写法4: contain
                       
                       cover和contain首先都会按比例缩放图片
                       cover会铺满宽和高较大的方向, 有可能造成背景图无法展示完全
                       contain会铺满宽和高较小的方向, 有可能造成背景图无法填满整个容器


  8. .sjx {
    	width: 0px;
    	height: 0px;
    	border: 50px solid;
    	border-color: lightgray transparent transparent transparent;
    	margin: 30px auto 0;
    }

    css设置三角形

  9. 盒阴影: box-shadow 添加给元素的阴影
                box-shadow: 0px 5px 5px 0px red inset;
                    1. 水平阴影的位置, px值 必须
                    2. 垂直阴影的位置, px值 必须
                    3. 阴影模糊程度, px值, 值越大, 越模糊
                    4. 阴影的大小, px值, 值越大, 阴影越宽
                    5. 阴影的颜色
                    6. 内阴影


  10.             1. selector:first-child 匹配符合selector选择器, 且必须是其父元素的第一个子节点的元素
                2. selector:last-child 匹配符合selector选择器, 且必须是其父元素的最后一个子节点的元素
                3. selector:nth-child(n) 匹配符合selector选择器, 且必须是其父元素的第n个子节点的元素
                4. selector:nth-last-child(n) 匹配符合selector选择器, 且必须是其父元素的倒数第n个子节点的元素
                5. selector:first-of-type 匹配符合selector选择器, 且是与它同类型, 同级的兄弟元素里的第一个元素
                6. selector:last-of-type 匹配符合selector选择器, 且是与它同类型, 同级的兄弟元素里的最后一个元素
                7. selector:nth-of-type(n)  匹配符合selector选择器, 且必须是与它同类型, 同级的兄弟元素里的第n个子节点的元素
                8. selector:nth-last-of-type(n) 匹配符合selector选择器, 且必须是与它同类型, 同级的兄弟元素里的倒数第n个子节点的元素
                    对于selector:nth-child()和selector:nth-last-child()还支持如下写法
                  1. selector:nth-child(odd/event) 匹配符合selector选择器, 且必须是其父元素的第奇数个/偶数个子节点的元素
                  
                  2. selector:nth-last-child(odd/event) 匹配符合selector选择器, 且必须是其父元素的第奇数个/偶数个子节点的元素, 注意, 从下往上数
                  
                  3. selector:nth-child(n+number) 匹配符合selector选择器, 且必须是其父元素的从第number个一直到最后的所有子节点的元素
                  
                  4. selector:nth-last-child(n+number) 匹配符合selector选择器, 且必须是其父元素的从第number个一直到最后的所有子节点的元素, 注意, 顺序从下往上
                  
                  nth-of-type和nth-last-of-type均可使用以上nth-child和nth-last-child的所有写法


  11.           1. 与表单元素有关的CSS3新增伪类选择器
                    1.1  selector:enabled 可以找到每个启用的input元素
                    1.2  selector:disabled 可以找到每个禁用的input元素
                    1.3  selector:focus 可以找到每个正在聚焦(正在使用)的input元素
                
                2. CSS3新增 "属性选择器"     
                    属性选择器: 根据元素的属性和属性值来查找元素 
                    
                    2.1 selector[attr=value] 找到符合selector的元素且attr属性的值要为value
                    2.2 selector[attr^=value] 找到符合selector的元素且attr属性的值要以value开头 eg:div[class^=first]
                    2.3 selector[attr$=value] 找到符合selector的元素且attr属性的值要以value结尾
                    2.4 selector[attr*=value] 找到符合selector的元素且attr属性的值包含value


  12. 滤镜: CSS3的新样式, 定义了img的可视效果
                1. 亮度 Brightness: 控制图片的亮度, 参数接收大于0的数或者百分比
                2. 对比度 Contrast: 控制图片的对比度, 参数接收的值大于或等于0, 主要对比图像里浅色和深色之间的差异
                3. 灰度 Grayscale 取值范围不能为负值, 值越大越明显, 0%没有效果, 100%完全灰度
                4. 饱和度 Saturate 控制色彩饱和度, 0%将彻底删除图像中的所有颜色, 100%图像完全饱和与原始图像一样, 大于100%更鲜艳, 值不允许是负值
                5. 褐色 Sepia 控制图片的褐色色调, 最好的适用场景, 将图片调为老照片效果, 0%原始图效果, 值100%老照片效果
                6. 反色 Invert 翻转图片中的所有颜色 0%与原图相同, 100%完全反色
                7. 模糊 Blur 把颜色混合在一起, 分布在其边缘上, 值越大越模糊 单位可以接受除了百分比之外的其他单位


  13. <!-- 
                渐变:我们可以让元素的背景图在两个或者多个指定颜色之间平稳的过渡, 包括透明色也可以进行渐变
                
                CSS3里的渐变(Gradients)分为两种:
                1. 线性渐变 Linear Gradient: 沿着某一方向的渐变效果
                    1.1 普通线性渐变 linear-gradient
                    1.2 重复线性渐变    repeating-linear-gradient
                
                2. 径向渐变 Radial Gradient: 四周发散的渐变效果
                    2.1 普通径向渐变  radial-gradient
                    2.2 重复径向渐变  repeating-radial-gradient
             -->
             
            <!-- 
                线性渐变语法:
                写法1: -webkit-linear-gradient(颜色值1, 颜色值2,...)
                线性渐变的默认渐变方向是: 从上到下, 每种颜色所占据的区域大小相同
                
                写法2: -webkit-linear-gradient(渐变的起始方向, 颜色值1, 颜色值2,...)
                表示方位的写法:
                left, right, top, bottom
                一个值表示正方向, 即上下左右; 两个值表示角
                
                写法3: -webkit-linear-gradient(渐变的结束方向, 颜色值1, 颜色值2,...)
                表示方位的写法:
                left, right, top, bottom
                一个值表示正方向, 即上下左右; 两个值表示角
                只是需要在最前面加一个 to 单词
                注意: 如果加了to单词, 线性渐变的值不能用-webkit-前缀
                
                写法4: -webkit-linear-gradient(方向, 颜色值1 百分比, 颜色值2 百分比,...)
                百分比的含义: 表示对应颜色开始出现渐变的位置.
             --> 
             
             <!-- 
                径向渐变语法:
                    radial-gradient(半径 形状 at 圆心, 颜色值1 百分比, 颜色值2 百分比, ...)
                    background-image: radial-gradient(150px 100px ellipse at center center, red, blue, yellow);
                半径:
                    1. 直接使用px数值
                    2. 使用角或者边的位置表示半径的大小
                        最远边: farthest-side
                        最近边: closest-side
                        最远角: farthest-corner
                        最近角: closest-corner
                形状:
                    1. 圆形: circle
                    2. 椭圆: ellipse(如果形状是椭圆, 写半径的时候需要写两个半径)
                    
                圆心:
                    1. px数值  100px 200px
                    2. 通过方位词: left center right top bottom
              -->


  14. 蒙版: 背景蒙版, 可以让使用者通过部分或者完全隐藏一个元素的可见区域, 实现图像的增强效果
                
                充当蒙版的图片格式必须要是png, 因为背景透明
                蒙版图片的效果, 系统会将其原来不透明的区域变为透明, 之前透明的区域变为不透明, 那么变为透明的区域正好可以显示出底下的图像, 而且会呈蒙版图片的轮廓显示底部的图像
                与蒙版有关的样式:
                1. -webkit-mask-image
                2. -webkit-mask-repeat
                    2.1 repeat 默认值, 水平竖直都平铺
                    2.2 no-repeat 不平铺
                    2.3 repeat-x 水平平铺
                    2.4 repeat-y 竖直平铺
                    2.5 space 保证图像完整性和尺寸, 两边图像靠边, 左右空间等距
                    2.6 round 保证图像完整性, 尽可能的铺满, 有可能会压缩图像尺寸
                3. -webkit-mask-position
                4. -webkit-mask-size
                eg:
                -webkit-mask-image: url(img/circle.png);
                -webkit-mask-size:120px;
                -webkit-mask-repeat: round;
                -webkit-mask-repeat: space;


  15. 1. inline-block的元素左右有缝隙
                原因: 人为敲的空白符导致的, 间距为4px
                    a. 去掉空白符 保证代码在一行显示(基本不用)
                    b. 浮动
                    c. 给inline-block的父级的font-size设置为0, 兼容较好
                    再去了解更多的解决办法
                
                2. inline-block的元素底部也有缝隙
                原因: 行元素的默认垂直方向的对齐方式导致的
                    a. 浮动
                    b. 给inline-block的父级的font-size设置为0, 兼容较好
                    再去了解更多的解决办法 vertical-algin

  16. em, rem是长度单位
                1. em: 一个em单位相当于当前元素的font-size对应的px值大小
                2. rem: 一个rem单位相当于根元素(html元素)的font-size对应得px值的大小

  17. vertical-align: 用来处理行元素在垂直方向上的对齐方式
                1. baseline 基线对齐 默认值 基线的位置在当前行里字母x的底切线
                2. sub 下标对齐
                3. super 上标对齐
                4. top 元素的顶端与该行的顶端对齐
                5. middle 元素的中垂点与当前行的x字母的中心点对齐
                6. bottom 元素的底端与该行的底端对齐
                7. text-top 元素的顶端与当前行内容的顶端对齐
                8. text-bottom 元素的低端与当前行内容的低端对齐
                
                vertical-algin的适用场景
                1. 多个行元素垂直居中
                2. 去除inline-block元素底部的缝隙

  18. <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
            <!-- 
                1. width 要设置的虚拟窗口的大小, 我们一般设置成设备宽度, 即device-width
                2.initial-scale 初始的缩放比例, 一般给1, 第一显示的时候按原始大小显示
                3. minimum-scale 最小的缩放比例(0.1)
                4. maximum-scale 最大的缩放比例(10)
                5. user-scalable 是否允许用户缩放
             -->
                viewport 视窗/视口
                作用: 解决移动端页面适配显示的问题
                
                移动端的设备尺寸远远小于PC端, 所以浏览器给每个移动端窗口设置了一个"虚拟窗口", 这个"虚拟窗口"的大小要远远大于实际移动端设备的大小, 默认情况下, 绝大部分的设备的"虚拟窗口"的宽度是980px
                如果我们直接把一个PC的网站移植到移动端, 所有的内容都要参考980px或者1024px去显示, 这样就导致所有的内容被缩小
                
                但是我们可以人为修改"虚拟窗口"的宽度, 来让内容1:1显示, 而不至于被缩小
                
                我们通过meta标签来实现"虚拟窗口"的修改
                
                我们设置了viewport之后, 将"虚拟窗口"的大小与设备大小保持一致, 这样内容不会缩小, 保持正常的尺寸显示. 但是整个网页无法完全展示, 用户可以通过滑动查看剩余内容


  19. BFC(Block Formatting Context) 块级格式化上下文
             它指的是页面里的任意一块独立的渲染区域,该区域内只有Block-Level Box
             成为BFC区域的元素内部,有自己的一系列排布规则
             
             概念1: Box:页面布局的基本单位,一个页面是由多个盒子组成的,元素的默认类型或者display样式,最终决定了这个box的类型
             概念2: Formatting Context  这是css2.1提出的概念,指页面的一块独立的渲染区域,不同类型的盒子,会形成不同的渲染区域,其内部的渲染规则也不同,最常见的Formatting Context,是BFC(Block Formatting Context)和IFC(Inline Formatting Context)
             
             BFC的布局规则
             1.内部所有的行box(一行里所有的行元素组合)和块box都会在垂直向上,一个一个的放置
             2.box在垂直方向上的距离由margin决定,而且属于同一个BFC区域里相邻的两个box之间的margin会发生融合
             3.内部所有的box的margin的左边缘,永远元素会与其父元素的左边缘相接触(如果发生了从右往左的格式化,效果相反)
             4.BFC区域不会与float的重叠
             5.BFC区域是页面一个独立的渲染区域,容器里的子元素不会影响容器外的元素,外面也无法影响里面的。
             6.成为BFC的元素在计算高度的时候,也会把浮动元素的高度计算在内。
             
             如何创建一个BFC区域???
             1.float的值不为none
             2.position的值不是static也不是relative
             3.display的值是inline-block,table-cell,flex,table-caption 或者inline-flex     
             4.overflow的值不是visible


  20. 弹性布局有关的样式:
                一.容器有关的样式
                    1.将元素设置为容器:display: flex/inline-flex
                    
                    2.修改主轴的方向: flex-direction
                        a.row 默认值,水平方向,从左到右
                        b.row-reverse 水平方向,从右到左
                        c.column 垂直方向,从上到下
                        d.column-reverse 垂直方向,从下到上
                        
                    3.换行方式:flex-wrap
                        a.nowrap 默认值,不换行,如果宽度不够,会强制压缩弹性子元素的宽
                        b.wrap  换行,但是行与行之间有距离
                        c.wrap-reverse  反向换行,第一行在最下面,之后依次向上排布,之间依然有距离
                
                    4.弹性子元素在主轴上的对齐方式:justify-content
                        a.flex-start 默认值,主轴起点方向对齐
                        b.flex-end 主轴终点方向对齐
                        c.center 主轴居中
                        d.space-between 两端元素靠边,间距等距
                        e.space-around 所有子元素左右距离相等
                        
                    5.弹性子元素在侧轴上的对齐方式:align-items
                        a.flex-start 侧轴起点方向对齐
                        b.flex-end 侧轴终点方向对齐
                        c.center 侧轴居中
                        d.stretch 默认值,规定弹性子元素的默认高度,该值只有在不设置弹性子元素的高度的时候才会起效果,默认高度撑满整个侧轴
                        e.baseline 基线对齐
                    6.多行弹性子元素在侧轴的对齐方式:align-content
                        a.flex-start 侧轴起点方向对齐
                        b.flex-end 侧轴终点方向对齐
                        c.center 侧轴居中
                        d.stretch 默认值,规定弹性子元素的默认高度/宽度,该值只有在不设置弹性子元素的高度的时候才会起效果,默认高度撑满整个侧轴
                        e.space-between 侧轴两端元素靠边,元素侧轴方向之间等距
                        f.space-around 所有元素侧轴方向等距
                
                
                二.弹性元素有关的样式
                    1.弹性子元素的排布顺序 order 默认值都是0,值越小越靠前,
                    2.单独调整弹性子元素在侧轴上的对齐方式 align-self
                    
                        值与align-items一样
                    3.弹性子元素多余空间的分配比例 flex-grow
                        默认值是0,设置的数字,表示多余空间分配比列
                    4.弹性子元素当主轴空间不足时的收缩比例 flex-shrink
                            默认值是1
                        元素收缩值的计算公式
                        步骤1:计算权重
                        (每个元素原始的宽*各自的收缩比例)的和
                        步骤2
                        元素的宽*收缩比例/权重*溢出量
                        
                        div1:180px
                        div2:220px
                        div3:150px
                        溢出量:容器宽(400)-(180+220+150)=150px
                        
                        权重:180*1+220*1+150*1=550px
                        
                        div1的收缩量:180*1/550 *150=49.090909
                        div2的收缩量:220*1/550 *150=60
                        div3的收缩量:150*1/550 *150=40.909090
                    5.弹性子元素的基础主轴长度 flex-basis,可以理解成弹性子元素在主轴方向上的理想尺寸或者假设尺寸,但是并不一定成为最终的显示尺寸,默认值为auto,先看元素是否设置了width或者height,如果设置了,就按设置的尺寸,如果没有设置,按照内容大小表示
                    
                        默认值:auto(flex-basis相当于没写)
                        flex-basis的特点
                            1.flex-basis永远控制的是元素主轴方向的大小
                            2.flex-basis当主轴是水平时,控制的是元素的宽度,当主轴式垂直时,控制的是元素的高度
                            3.flex-basis的优先级大于width或者height
                            4.flex-basis只对弹性子元素起作用
                            
                            flex样式 是flex-grow、flex-shrink、flex-basis的合写形式
                            flex的默认值:0 1 auto;
                            
                            flex的常用值写法
                            flex:auto 相当于1 1 auto
                            flex:none 相当于0 0 auto
                            flex:1       相当于1 1 0%


  21. /* 
                    产生一个随机数:Math.random();
                    
                    产生一个(0,1)的随机数:Math.random();
                    产生(0,n)的随机数:Math.random()*n;
                    产生(0,n]的随机数:Math.random()*(n+1);
                    产生[0,n]的随机整数:Math.floor(Math.random()*(n+1));
                    产生[m,n]的随机整数:Math.floor(Math.random()*(n-m+1)+m);
                 */
                /* 
                    JS里对小数进行取整
                    1.向上取整    Math.ceil(小数); 9.00001 -- 10
                    2.向下取整    Math.floor(小数); 9.99999 -- 9
                    3.四舍五入    Math.round(小数);
                 */

  22. <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="utf-8">
    		<title>数组操作函数</title>
    	</head>
    	<body>
    		<script type="text/javascript">
    			/* 
    				我们将来可能对数组进行各种各样的操作,如:
    				1.往数组里添加元素
    				2.从数组里删除元素
    				3.对数组进行截取
    				4.对数组里的数据进行排序
    				5.打乱数组里的值
    				...
    			
    			 */
    			var arr1 = [1, 2, 3, 4, 5];
    			/* 
    				1.push(值)	向数组的 尾部添加 元素
    					可以一次加一个值,也可以一次加多个值,该方法执行完毕后,会返回添加后的 新数组元素个数
    				
    			 */
    			var newCount = arr1.push("abc");
    			console.log(arr1, newCount);
    			var newCount1 = arr1.push("abcq", 10, 30, null);
    			console.log(arr1, newCount1);
    
    
    			/* 
    				2.pop(值)	向数组的 尾部删除 元素
    					一次只能删除一个值,该方法执行完之后得到 被删除的值
    			 */
    			var newCount2 = arr1.pop();
    			console.log(arr1, newCount2);
    			var newCount3 = arr1.pop();
    			console.log(arr1, newCount3);
    
    			/* 
    				3.unshift() 向数组的 头部添加 元素
    					一次可以添加一个,也可以一次添加多个,该方法执行过后,也会返回添加后的 新数组元素个数
    			 */
    			var newCount4 = arr1.unshift("abcd");
    			console.log(arr1, newCount4);
    			var newCount5 = arr1.unshift("abcde", "xxx");
    			console.log(arr1, newCount5);
    
    			/*
    				4.shift() 向数组的 头部删除 元素
    					一次只能删除一个,该方法执行过后,返回 被删除的值 
    			*/
    			var newCount7 = arr1.shift();
    			console.log(arr1, newCount7);
    			var newCount8 = arr1.shift();
    			console.log(arr1, newCount8);
    
    			/* 
    				5.splice(1,2,3)	从数组的某个位置上 删除/添加 值
    					参数1:要删除开始位置的下标
    					参数2:要删除的元素的个数
    					参数3及以后的参数,需要添加的值
    					
    					如果只想删除,参数3及以后不写
    					如果只想插入,参数2给0,表示不删除,其他参数照写
    					
    			 */
    			arr1.splice(2, 2, "666", "999", "你好");
    			console.log(arr1);
    			arr1.splice(2, 0, "6661", "9991");
    			console.log(arr1);
    
    			/*
    				6.slice(1,2)	获取子数组,该方法执行完之后会得到一个新的子数组
    					参数1:开始截取的起始位置的下标,可以给负值,表示从后往前数
    					参数2:截取的结束位置的下标,可以给负值,表示从后往前数
    					
    					注意:
    						1.结束位置对应的值不会被截取到
    						2.要保证起点永远在终点的左边
    						3.如果不写结束位置,结束位置默认在最后
    					
    			 */
    			var newCount10 = arr1.slice(1, 3);
    			console.log(newCount10);
    			var newCount11 = arr1.slice(1, -3);
    			console.log(newCount11);
    			var newCount11 = arr1.slice(1, );
    			console.log(newCount11);
    
    			/* 
    				7.sort方法 数组排序方法
    				其默认排序规则,是按照字符串的排序规则进行排序,而且排序的结果 为升序
    				如果想自定义排序规则,请给sort方法传入一个回调函数;(当一个函数充当实参,传入到另一个函数里,那么我们称这个函数叫"回调函数")
    				
    			 */
    			var arr1 = [9, 7, "abc", "B", 5, 100, 3, "黄", "红", "绿", "蓝"];
    			var newArr1 = arr1.sort();
    			console.log(newArr1);
    
    			/* 
    				1.sort默认会排成升序
    				2.每次sort会从数组里拿两个值比较
    				分析出:当第一个参数大于第二个参数是,理论上应该去交换位置,最终才可以排成升序
    				
    				
    				
    			 */
    			var arr2 = [9, 100, 3, 1, 5];
    			arr2.sort(function(a, b) {
    				console.log(a, b);
    				/* 
    					sort方法当接收到 return的值后,会判断其与 0 的关系,来决定是否交换两值
    					return的值 > 0,交换两个值的位置
    					return的值 == 0,不交换两个值的位置
    					return的值 < 0,不交换两个值的位置
    					
    				 */
    				return Math.random() - 0.5;
    				// return a - b;
    				// return -(a-b);
    			});
    			console.log(arr2);
    
    			/* 
    				需求:升序,前面比后面大,交换....升序
    				sort原理: return的值大于0,交换
    							
    				100 50
    				7 6
    				5 1
    				前面比后面大===return的值大于0
    							
    				需求:降序,后面比前面大,交换...降序
    				1 100
    				2 33
    				后面比前面大===ruturn的值小于0
    			 */
    		</script>
    	</body>
    </html>
    

  23. title>JS获取元素的方式</title>		
    <script type="text/javascript">
    			/* 
    				JS获取元素的方式
    				1. 通过id值获取元素 getElementById("id值"), 该方法得到的直接就是html元素
    				2. 通过class值获取元素 getElementsByClassName("class值"), 该方法得到的是所有对应class值的数组
    				3. 通过标签名获取元素 getElementsByTagName("标签名"), 该方法得到的是对应标签名的数组
    				
    				4. 通过选择器获取一个元素 querySelector("选择器"), 但是不管选择器找到的是几个元素, 该方法永远返回找到的第一个元素
    				5. 通过选择器获取所有元素 querySelectorAll("选择器"),找到的是对应选择器表示的所有元素的数组
    			 */
    			// var div1Ele = document.getElementById("div1");
    			// console.log(div1Ele);
    			
    			// var classEles = document.getElementsByClassName("bgc");
    			// console.log(classEles);
    			
    			// var tagEles = document.getElementsByTagName("div");
    			// console.log(tagEles);
    			
    			// var s1 = document.querySelector("ul>li");
    			// console.log(s1);
    			
    			// var s2 = document.querySelectorAll("ul>li");
    			// console.log(s2);
    			
    			
    			/* 
    				JS如何操作元素的样式和元素的属性
    				1. JS操作元素的样式: 我们通过style属性来进行操作
    				2. JS操作元素的属性: 
    			 */
    			function btn1Click() {
    				var div1 = document.querySelector("#div1");
    				var div2 = document.querySelector(".bgc");
    				// 通过style属性 设置div1的样式(宽, 高, 背景色)
    				// div1.style = "width: 200px; height: 200px; background-color: red;"
    				var ranSize = Math.floor(Math.random()*51+50);
    				
    				div1.style.width = 300 + "px";  //"300px"
    				div1.style.height = 300 + "px"; //"300px"
    				div2.style.width = div1.style.width;
    				div2.style.height = div1.style.height;
    				
    				var r = Math.floor(Math.random()*256);
    				var g = Math.floor(Math.random()*256);
    				var b = Math.floor(Math.random()*256);
    				
    				div1.style.backgroundColor = "rgb("+r+","+g+","+b+")";
    				div2.style.backgroundColor = div1.style.backgroundColor;
    				
    				var reg = Math.floor(Math.random()*361);
    				div1.style.transform = "rotateZ(" + reg + "deg)";
    			}
    </script>
    
    

 

24. 

/* 
                DOM: Document Object Model文档对象模型, 处理文档内容有关的所有操作 
                
                DOM树: 指的是一个树状结构, 当html文档加载时, 浏览器会根据html文档里的代码, 将所有内容结合内容之间的嵌套关系, 生成一个树状结构, 我们称之为DOM树. DOM树是由一个一个的"节点"组成
                
                DOM的节点类型
                1. 元素节点 body标签
                2. 文本节点 文字内容
                3. 注释节点 注释
                4. 声明节点 <!DOCTYPE html>
                5. 文档节点 document
                
                节点之间的关系
                1. 父节点
                2. 子节点
                3. 兄弟节点
             */

一. 特殊节点的获取方式

            1. 文档节点
            console.log(document);
            
            2. html节点
            console.log(document.documentElement, document.querySelector("html"));
            
            3. body节点
            console.log(document.body, document.querySelector("body"));
            4. head节点
            console.log(document.head, document.querySelector("head"));
            
            5. 声明节点
            console.log(document.doctype);

二. 通过节点之间的关系获取元素

           1. 获取到某个节点的所有子节点(子节点可以有多个)
            console.log(document.head.childNodes);
            2. 获取到某个节点的所有子元素节点(子节点可以有多个)
            console.log(document.head.children);
            
            3. 获取某个节点的父节点(有且只有一个)
            // console.log(document.body.parentNode);
            // console.log(document.body.parentNode.parentNode);
            // console.log(document.body.parentNode.parentNode.parentNode);
            
             4.1 获取某个节点的第一个子节点
             4.2 获取某个节点的第一个元素子节点
             // console.log(document.head.firstChild);
             // console.log(document.head.firstElementChild);
           
             5.1 获取某个节点的最后一个子节点
             5.2 获取某个节点的最后一个元素子节点
             // console.log(document.head.lastChild);
             // console.log(document.head.lastElementChild);
          
             6.1 获取某个节点的上一个兄弟节点
             6.2 获取某个节点的上一个元素兄弟节点
             // console.log(document.body.previousSibling);
             // console.log(document.body.previousElementSibling);
         
             7.1 获取某个节点的下一个兄弟节点
             7.2 获取某个节点的下一个元素兄弟节点
             // console.log(document.head.nextSibling);
             // console.log(document.head.nextElementSibling);

三. 操作元素节点的内容
            1. innerHTML
            2. innerText
            3. textContent
            
            以上三个属性, 权限均为读写, 即: 既可以设置内容, 也可以获取内容    

三种操作节点内容方式的区别:
            innerHTML
            在设置内容时, 如果内容里有html字符串, 会被解析成标签, 如果有多余的空格, 只会保留一个空格
            在获取内容时, 原样获取
         
            innerText
            在设置内容时, 如果内容里有html字符串, 不会被解析成标签而是以文本形式输出, 如果有多余的空格, 只会保留一个空格
            在获取内容时, 如果有多余的空格, 只保留一个空格, 其余原样获取
            
            textContent
            在设置内容时, 如果内容里有html字符串, 不会被解析成标签而是以文本形式输出, 如果有多余的空格, 只会保留一个空格
            在获取内容时, 原样获取
 四. 操作元素的属性
                1.      点语法   语法: 元素.属性名
                2.1  设置元素属性   元素.setAttribute("属性名", "属性值");
                2.2  获取元素属性   元素.getAttribute("属性名");     

五. 操作元素的样式
                1. 通过style属性操作元素的样式, 即可设置, 又可获取
            注意: 通过style属性进行操作, 永远只能操作行间样式!!!
                2. 获取元素的非行间样式  getComputedStyle(元素)方法
            该方法的返回值是一个样式对象, 该对象里存储了元素的所有样式, 样式的表现形式有如下特点:
                    2.1 如果是颜色值, 以rgb表示
                    2.2 如果是复合属性, 值为全值
            但是, 我们最终是想要获取对象里的某个样式值, 获取对象某个值, 有两种语法:
                a. 点语法  对象.属性名
                b. 中括号语法: 对象["属性名"]
                
                1、点号要求后面的运算元是合法的标识符(即合法的变量命名),对于不合法的不可以使用
                2、中括号要求的则是一个字符串即可,不必是合法的变量命名。

六. 动态创建, 添加, 删除元素节点

            创建元素
            语法:
            document.createElement("标签名");
            返回值: 创建好的html标签
            
            添加元素
            语法:
            父元素.appendChild(新创建元素);
            
            删除元素
            语法:
            父元素.removeChild(要删除的元素);

面试题:NodeList和HTMLCollection的区别(静态/动态)???!!!!

HTMLCollection是元素集合,NodeList是节点集合(既包括元素,也包括节点)

NodeList是静态的,初始有多少,添加之后不会影响数组里的值;

HtmlCollection是动态的,添加之后会更新数组里的元素

HTMLCollection 和 NodeList 本身无法使用数组的方法:pop()、push() 或 join() 等。除非转化为数组

具体链接:HTMLCollection 和 NodeList 的区别 - 掘金

使用 getElementsByTagName() 方法返回的就是一个 HTMLCollection 对象。

NodeList对象是节点的集合。它可以通过以下方法得到:

  • 一些旧版本浏览器中的方法(如 getElementsByClassName()),返回的是 NodeList 对象,而不是 HTMLCollection 对象。
  • 所有浏览器的 Node.childNodes 属性返回的是 NodeList 对象。
  • 大部分浏览器的 document.querySelectorAll()返回 NodeList 对象

25.    

DOM获取元素的尺寸和位置
                尺寸:
                    1.内尺寸:clientWidth / clientHeight    由元素content大小 + 元素padding大小组成
                    2.外尺寸offsetWidth / offsetHeight    由元素content大小 + 元素padding大小组成 + 元素border大小
                注意点:
                    1.行元素内尺寸获取不到
                    
                位置:
                    1.内位置:clientLeft / clienttTop
                        实际上就是border-left-width和border-top-width,如果元素没有设置边框,内位置为0
                    2.外位置:offsetLeft /offsetTop
                        offsetLeft:当前元素的左边框的外边缘距离它最近的,设置了非static定位的祖先元素左边框的内边缘
                        offsetTop:当前元素的上边框的外边缘距离它最近的,设置了非static定位的祖先元素的上边框的内边缘

滚动尺寸:scrollWidth / scrollHeight
                    scrollWidth:    滚动元素的内容的宽度 + 滚动元素左右的padding
                    scrollHeight:    滚动元素的内容的高度 + 滚动元素上下的padding
                注意:     如果水平方向内容溢出,可滚动元素的右padding失效
                滚动位置:scrollLeft / scrollTop

26.

顺序结构执行的代码,正常情况下,应该先对变量进行声明,再使用变量,如果使用变量在声明之前,系统会报错!
                但是JS里,系统会先将所有的变量和函数的声明提升到其作用域的最顶端,但是这个提升是系统内部在做,我们感知不到
                
                变量提升产生的后果:
                JS里,函数或者变量都可以先使用,再声明
                
                变量提升的几种情况:
                    1. 变量提升,只提升变量的声明,其赋的初始值不会被提升
                    2. 函数声明提升时,函数体本身会被提升到其作用域的最上面.但是,函数表达式在执行时,只会提升变量,不会将函数体提升,如果提前调用,会报错
                    3.正常顺序下,函数与变量重名,变量的优先级高;在触发变量提升的情况下,函数的优先级高
                    4.如果函数与函数重名,不管是否触发变量提升机制,永远是最后面的覆盖前面的

27. 

1.回调函数: 将一个函数体当作实参传入到另外一个函数里边,那么这个充当实参的函数,就叫回调函数
                适用场景:
                    1.1 解决异步数据回传问题
                    2.2 解决获取保密数据问题
                    
 2.闭包函数: 将一个函数声明写到另一个函数声明里,形成了函数声明的嵌套写法,里面的函数可以只声明,也可以作为外部函数的返回值返回,这样里面的函数声明我们称之为闭包函数
                闭包函数的特点:
                1.在闭包函数里可以访问到其外部函数里的局部变量
                2.一旦闭包函数里使用了外部函数里的局部变量,闭包就会将其的生命周期延长至与全局变量的生命周期保持一致

面试题
                闭包的概念:
                在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
                闭包的用途:
                闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是延长局部变量的生命周期。

缺点:

                1.函数执行完后,函数内的局部变量没有释放,占用内存时间会变长

                2.容易造成内存泄露

解决:

                1.能不用闭包就不用

                2.及时释放

内存溢出:

                *一种程序运行出现的错误

                *当程序运行需要的内存超过了剩余内存时,就出抛出内存溢出的错误

内存泄露:

                *占用的内存没有及时释放

                *内存泄露积累多了就容易导致内存溢出

        的内存泄露:

                1. 意外的全局变量

                2.没有及时清理的定时器或回调函数

                3.闭包

28.

BOM: Broswer Object Model 浏览器对象模型
                作用: 用来处理浏览器有关的操作
                
                BOM是由一系列对象组成的, 每个对象都负责与浏览器有关的一类功能
                window对象 是BOM里的核心对象, 也是顶层对象, 其余的所有对象都是从window里延伸出来的, 它们都是window的子对象

window的子对象
                1. document
                2. history
                3. location
                4. navigator
                5. screen

window对象 代指运行当前文档的 浏览器窗口 
JS里, 所有的全局变量和全局函数都是window的属性

this的代指情况:
                1. this在行间绑定的元素事件的函数内, 代指的是window,如果想让this代指的依然是对应的标签,需要在行间调用函数的时候,传入this,则函数的形参接收到的就是对应的标签
                2. this在JS绑定的元素事件的函数内, 代指的是触发了该事件的元素
                3. this在全局函数内, 代指的是window
                4. 直接在script标签里写this, 代指的是window
                5. 系统自带的函数传入的回调函数里的this, 代指的是window

window里与操作浏览器窗口有关的方法
                1. open(url, name, features)  新建一个浏览器窗口
                url: 打开的新窗口的地址
                name: 打开的窗口的名字
                features: 打开的窗口的特性: 如大小,位置等
                返回值: 新打开的窗口的window对象
                
                注意: open方法只要调用一次, 就会新创建一个窗口. 但是如果窗口的name值相同, 相同name值的窗口只能有一个.

                2. close() 关闭窗口

                3.1 moveTo(x, y) 将窗口移动到x,y的位置
                3.2 moveBy(x, y) 将窗口在当前的位置基础上加上x,y之后的新位置, x,y也可以为负值

                4.1 resizeTo(w, h) 将窗口的尺寸重置成w和h
                4.2 resizeBy(w, h) 将窗口的尺寸在原有尺寸的基础上增加w和h的增量, w, h可以为负值

2. history 
                该对象用来存储用户的历史记录(表示页面的访问顺序, 仅存储最近访问的 有限条目的url信息)
                H5之前, history对象是禁止访问的

history对象的常用方法
                1. back() 在历史记录中后退, 等效于, 点击了浏览器工具栏里的"后退"按钮
                2. forward() 在历史记录中前进, 等效于, 点击了浏览器工具栏里的"前进"按钮
                3. go() 在历史记录中根据传入的索引值加载页面(0表示当前页, 上一页是-1, 下一页是1, 依次类推)
                
                注意: 通过history的方法进行的页面切换, 切换的页面是读取的缓存, 并不是重新加载的!!!

3. location对象
                    存储的是当前文档的位置有关的信息
                    
                location常用的属性和方法
                1. protocol 协议名
                2. host ip+端口号
                3. hostname ip
                4. port 端口号
                5. pathname 资源的具体路径
                6. search 参数列表 以?开头, 数据以键值对的形式拼接, 多个键值对中间用&相连
                7. hash #开头, 这个值不会影响服务器的判断
                8. href 完整的url地址
                
                9. reload() 重新加载页面
                10. replace() 会替换掉当前网址, 但是不会产生历史记录

4. navigator对象
                该对象存储与浏览器有关的信息, 如版本, 内核, 系统
                
                navigator对象的常用属性
                1. appName 应用的名字
                2. appVersion 应用的版本
                3. cookieEnabled 是否开启cookie
                4. userAgent 浏览器的所有基础信息

5. screen对象
                存储了客户端的屏幕信息
                1. width, height
                2. availWidth, availHeight

29.

JS将触发之后的详细数据,封装成了一个对象,我们称之为"事件对象",任何一个事件里都可以直接获取.
                获取方式,事件触发的函数里,直接通过event获取
                
                注意:低版本的火狐浏览器,是无法直接获取到event对象的;需要通过兼容性写法获取事件对象,给事件函数填写形参,第一个形参就是事件对象    var event = e || event;

事件分类:
                1.鼠标事件
                2.键盘事件
                3.表单实现
                4.移动端事件

一.鼠标事件

1.鼠标左键单击事件 onclick

2.鼠标左键双击事件 ondblclick

3.1  鼠标移入事件        onmouseenter
3.2  鼠标移出事件        onmouseleave
                       
4.1  鼠标移入事件        onmouseover
4.2  鼠标移出事件        onmouseout
   注意:onmouseenter,onmouseleave  的效率要高于  onmouseover,onmouseout,因为其不存"事件冒泡"

5.鼠标移动事件    onmousemove

6.鼠标按键按下事件        onmousedown

7.鼠标抬起事件        onmouseup

8.鼠标右键单击事件    oncontextmenu

9.鼠标滚轮单击事件    onmousewheel

 二.键盘事件
                键盘事件我们一般添加给浏览器窗口

1.  键盘按下事件  onkeydown

2.  键盘抬起事件  onkeyup

三.表单事件

1.控件内容变化事件 onchange
                该事件会在输入框内容输入完毕(失去焦点,点击提交按钮,回车)且与上一次比较内容发生了变化时,change时间才会触发!

2.获得焦点事件        onfocus

3.失去焦点事件        onblur

4.内容键入事件        oninput  (自己查 防抖/节流 )

鼠标位置的常用常用属性
                1.clientX/clientY    鼠标距离浏览器左端和上端的距离
                2.offsetX/offsetY    鼠标距离事件源对象左端和上端的距离
                3.pageX/pageY        鼠标距离这个实际页面左端和上端的距离
                4.screenX/screenY    鼠标距离屏幕左端和上端的距离

30.

事件流模型: 描述的是JS里一个事件触发之后的完整步骤,该步骤一共分三步:
                1.事件捕获:事件流模型的第一阶段,当某个节点上的事件被触发时,系统会从根节点开始,一直向下寻找,直到找到真正触发了该事件的节点,沿途在其直系继承树上的所有节点如果绑定了相同的事件,这些事件依次触发
                2.事件触发:事件流模型的第二阶段,绑定的事件被触发.
                3.事件冒泡:事件流模型的第三阶段,绑定事件被触发之后,会沿着直系继承树一直向上传递,一直再传递回根节点,沿途绑定了相同事件的节点会依次触发
                用"事件冒泡"的特点可以进行"事件代理"        (这是冒泡事件的优点)
                阻止事件冒泡:事件对象.stopPropagation()    (这是冒泡事件的缺点)

阻止事件冒泡:意思是阻止事件的传播,但是不影响本身事件的执行
                    事件对象.stopPropagation()

事件代理:利用事件冒泡的特点,将原本应该绑定给某个元素的事件绑定给了其某个直系祖先元素.     但是这样无法通过this知道真正触发了该事件的元素是谁,我们需要通过"事件源"对象来获取真正触发了该事件的元素                    // 获取"事件源"对象       event.target

事件捕获:默认JS是不响应该阶段的,如果想要响应该阶段,需要通过addEventListener的方式给元素绑定事件,而不能再通过之前的语法绑定事件
                addEventListener("事件名",回调函数,是否把事件的传播方式设置成捕获);

面试题:普通绑定事件的方式与addEventListener绑定事件的区别是什么?

1、普通绑定不能绑定多个事件,后面绑定的会覆盖前面的。addEventListener()可以绑定多个事件,按顺序执行

2、addEventListener方式,不支持低版本IE。

3、普通方式绑定事件后,不可以取消;addEventListener绑定后,可以用removeEvenListener 取消

阻止默认事件的执行:
                事件对象.
preventDefault();

判断鼠标滚轮方向:

                非火狐浏览器监听滚轮事件 事件名叫 onmousewheel
                火狐浏览器监听滚轮事件 事件名叫 DOMMouseScroll
                
                非火狐浏览器 绑定滚轮事件 普通绑定即可
                火狐浏览器 绑定滚轮事件 需要通过addEventListener
                
                非火狐浏览器 监听滚轮事件方向  通过wheelDelta属性  值大于0,向上;值小于0,向下;
                火狐浏览器 监听滚轮事件方向 需通过detail属性  值小于0,向上;值大于0,向下;

31.

数据持久化
                1.cookie
                2.localStorage
                3.sessionStorage    (自我学习)

cookie介绍
                有时候我们也会用其复数形式cookies,这是服务器保存在客户端的数据片段,以键值对的形式(key= value)保存,简单来说,cookie就是服务端留给计算机用户浏览器端的小文件(文本)
                一旦客户端存储了该cookie,将来再次访问服务器的时候,会自动携带该cookie,发送给服务端,服务端就可以知晓用户信息,定制化做操作
                cookie的特点:
                1.cookie本质上是一段文本
                2.格式以键值对形式存在(key=value)
                3.cookie在服务端创建,保存在客户端
                4.一旦cookie保存,以后的请求都会自动携带cookie
                
                cookie的缺点:
                1.存储大小只有4Kb
                2.默认的生命周期一般无法满足要求,需要设置
                3.可以手动清除
                4.前端没有自带的cookie操作函数,需要自己封装

默认情况下cookie的生命周期为 浏览器打开期间,在此期间,刷新浏览器,cookie依旧存在,但是浏览器关闭,cookie被销毁

// 如果想要修改,cookie本质上是替换,cookie如果key相同,会覆盖掉之前写的

设置cookie的生命周期有两种办法
                    1.    给本条cookie设置一个失效时刻
                        语法: key=value,expires=时刻字符串
                    2.    给本条cookie设置一个存活时间
                        语法: key=value,max-age=秒数

删除cookie的本质是先覆盖再让新设置立刻失效  //document.cookie = "school = ;max-age = -1";
                
 面试题:cookie,localStorage,sessionStorage三者的区别           

1.生命周期

cookies可设置失效时间,没有设置的话,默认是关闭浏览器后失效

​localStorage是属于本地存储的一种,是永久存储,除非手动删除,否则一直有效。

​sessionStorage也是属于本地存储的一种,是临时存储,在关闭当前页面或者当前浏览器窗口前有效。

2.存储数据的大小

cookies存储的数据大小在4k左右。

​ localStorage存储的数据大小在5M左右。

​ sessionStorage存储的数据大小在5M左右。

3.作用范围

cookies只能作用于当面目录以及当前的子目录。

​ localStorage是同一浏览器不同标签页之间数据可以共享。

​ sessionStorage中存储的数据是只能在当前标签页中使用。

4.安全性

​ cookies的安全性比loaclStorage和sessionStorage的安全性好。

32.        数组去重

            // 1.es6
			console.log(Array.from(new Set(arr))); 
			
			// 2.利用for嵌套for,然后使用splice()去重。双层循环,外层循环元素,内层循环时比较值。值相同时,则删去这个值。 
			function unique(arr){
				var array = [];
				for (var i = 0; i < arr.length;i++) {
					for (var j = i+1; j < arr.length; j++) {
						// if (arr[i] == arr[j]) {
						// 	arr.splice(j,1);
						// 	j--;
						// }
						if (arr[i] == arr[j]) {
							array.push(arr[i]);
						}
					}
				}
				// return arr;   //NaN和{}没有去重
				return array;
			}
			console.log(unique(arr));
			console.log(unique(arr_arr));
			
			// 3.利用indexOf()去重 , 新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组。
			function unique(arr){
				var array = [];
				for(var i = 0; i < arr.length; i++){
					if (array.indexOf(arr[i]) === -1) {
						array.push(arr[i]);
					}
				}
				return array;
			}
			console.log(unique(arr));
			
			// 4.第三种去重方式 sort()排序 
			function unique(arr){
				arr = arr.sort();
                // var array = [arr[0]];
				var array = [];
				for (var i = 0; i < arr.length; i++) {
					// if(arr[i] !== arr[i - 1]){
					// array.push(arr[i]);
					// }
					if(arr[i] !== arr[i + 1]){
					array.push(arr[i]);
					}
				}
				return array;    //NaN、{}没有去重
			}
			console.log(unique(arr));
			
			// 5.利用filter返回新数组(省去定义新数组),利用indexOf数组只能查找到第一个的下标,进行等比
			// filter:条件成立的元素组成新数组
				var arr3 = ['apple', 'apps', 'pear', 'apple', 'orange', 'apps'];
				var newArr3 = arr3.filter(function(item, index) {
					return arr3.indexOf(item) === index; // 因为indexOf 只能查找到第一个  
				});
				console.log(newArr3);

33.

JS中如何修改this的指向, 这些方法有什么区别?

1. 触发函数.call(修改的this代指对象, 参数1, 参数2, ....) 

2. 触发函数.apply(修改的this代指对象, 参数数组)

3. 非立刻触发函数.bind(修改的this代指对象, , 参数1, 参数2, ....) 该方法有返回值, 返回值为一个新的函数, 这个新函数里的this就是已经被修改过的this

相同点: 都可以修改this的指向

不同点: 

1. call和apply适用于立刻触发的函数, bind适用于非立刻触发的函数

2. call方法以实参列表的形式传入实参, apply以数组的形式传入实参

编程思想:
                1. 面向对象(OOP): 以事物为中心
                2. 面向过程(OPP): 以事件为中心

JS的面向对象的概念里是没有"类"的概念的

但是有一个功能与"类"相似的东西, 在行使"类"的功能, 我们称之为"构造函数" 
                构造函数看起来, 有两个特点:
                1. 函数名首字母大写
                2. 调用的时候需要new关键字配合调用
                
                JS里创建对象的两种方式:
                1. 字面量/语法糖 直接创建对象
                2. 通过构造函数创建对象

面试题
 JS面向对象里 调用构造函数时的new关键字的作用

                1. 创建一个空对象  var obj = {};
                2. 设置原型链(将空对象的__proto__设置成该构造函数的原型对象) obj.__proto__ = Student.prototype;
                3. 调用Student方法, 并修改其内部this的指向为这个空对象, 给空对象赋值 Student.call(obj);
                4. 将赋完值的对象返回 return obj;
                
                1、创建一个空对象: 并且 this 变量引入该对象,同时还继承了函数的原型
                2、设置原型链  空对象指向构造函数的原型对象
                3、执行函数体 修改构造函数 this 指针指向空对象,并执行函数体
                4、判断返回值 返回对象就用该对象,没有的话就创建一个对象

JS面向对象里的重要概念
                1. 原型对象  ***
                2. constructor属性  *
                3. __proto__属性  **
                4. 原型链  ***
                5. 原型链的应用  **继承

概念一: 原型对象  prototype
                原型对象是构造函数的属性, 属于构造函数, 本质上就是一个对象, JS会为每个构造函数内置一个原型对象, 可以通过语法 构造函数.prototype获取. 
                因为原型对象是对象, 所以我们可以为其新增属性或者方法, 而且, 给原型对象添加的属性或者方法能被构造函数实例化的每一个实例对象所拥有!

概念二:  constructor属性
                    该属性属于原型对象, 任何一个构造函数的原型对象都再带一个constructor属性, 该属性指向原型对象所属的构造函数

概念三: __proto__属性
                该属性属于实例对象(new出来的对象), 该属性指向实例对象所属的构造函数的原型对象

概念四: 原型链
                对象查找自身属性或者方法时的流程
                1. 先到自身内部查找是否有对应属性或方法,如果有,直接用
                2. 如果没有, 会继续到实例对象的构造函数的原型对象里查找是否有对应属性或者方法, 如果有, 也可使用
                3. 如果还没有, 继续到当前实例对象的构造函数的原型对象的构造函数的原型对象里继续查找.如果有, 还可以使用
                4. 直到找到构造函数为Object的原型对象里, 如果还未找到, 属性会报undefined, 函数会报错!
                
                以上实例在沿着原型对象不断查找其属性或者方法的过程, 形成的链式结构, 我们称之为"原型链"!
                (原型链实际上是一堆原型对象串起来的链)

继承: 面向对象的三大特性之一
            //继承方法1: 通过修改this的指向实现继承
            //继承方法2: 通过原型链实现继承

原型继承是 js 的一种继承方式,原型链作为实现继承的主要方法,其基本思路是利用原型让一个引用类型继承另一个引用类型的属性和方法, 
            原型继承:利用原型中的成员可以和其相关的对象共享这一特性,可以实现继承,这种实现继承的方式,就叫做原型继承 

34.  ajax

AJAX 前端的网络请求
                是客户端向服务端发起的请求(request
                服务端接收到本次请求后, 对请求做分析, 了解本次请求的意图(图片, 数据, 文件等...), 之后对本次请求做响应(response)
                客户端最终会拿到服务端响应的数据, 进而对数据做进一步的处理
                我们目前的哪些操作会发起网络请求
                1. 有些标签会发起网络请求, 如具有src或者href或者action属性或者url样式的标签
                2. form表单
                3. 浏览器本身(在浏览器的地址栏)
                4. JS发起网络请求(ajax, axios, fetch...)

ajax之所以能发起网络请求, 依赖于一个核心对象: XMLHttpRequest对象

/*
                    1. 创建网络请求对象
                    IE不支持XMLHttpRequest对象, 所以我们需要对IE进行兼容
                */
                if (window.XMLHttpRequest) {
                    var request = new XMLHttpRequest();
                } else {
                    var request = ActiveXObject("Microsoft.XMLHTTP");
                }

                /*
                    2. 监听网络请求的状态, 捕捉网络请求成功时的状态, 进而获取请求成功的数据
                    在一次网络请求的发起中, 网络请求的状态是不断发生变化的, 可以分为如下几个阶段
                        1: 请求已建立
                        2: 请求已被发送
                        3: 请求已处理, 并已经获取到了部分数据
                        4: 请求已完成, 并已获取到了所有数据
                我们需要通过请求对象的readyState属性来进行判断
                
                readyState==4仅仅能判断出我们拿到了服务器的数据, 但是本次请求是否成功,我们无法知道, 需要进一步判断
                通过http的状态码来判断本次请求是否成功!!!
                http的状态码: 表示本次请求的具体状态
                1xx: 服务端接收到请求, 需要请求者继续操作
                2xx: 成功, 操作被成功接收且正确处理  200
                3xx: 重定向, 需要请求者进一步操作, 304
                4xx: 客户端错误 404
                5xx: 服务端错误 500
                6xx: 服务端错误
                */
                request.onreadystatechange = function() {
                    if (this.readyState == 4) {
                        if (this.status >= 200 && this.status < 300) {
                            // 真的请求成功了, 我们可以获取数据了, 原生的AJAX, 服务端返回的数据, 存储在请求对象里, 我们可以通过请求对象的responseText属性中存储
                            console.log(this.responseText);
                        }
                    }
                }

                /*
                    3. 设置请求的地址和请求类型
                    open("请求类型", "请求地址", 是否异步请求)
                */
                // request.open("GET", "http://10.10.11.224:6789/netGet", true);
                /*
                    如果发起get请求, 且想传递数据, 数据的格式为键值对格式, 即key=value, 将键值对直接拼接到url地址的后面, 以?开头, 多个键值对之间用&分隔
                */
                request.open("GET", `http://10.10.11.224:6789/netGet?username=${i1.value}&password=${i2.value}`, true);
                //4. 发起网络请求
                //  request.send(); //get

/*
                 4. 设置请求的数据的格式
                    因为post请求在将来传递数据时, 可能传递的数据格式各种各样, 所以, 我们需要在发起post请求时指定本次请求的数据格式
                    post请求的常用数据格式:
                        1. 键值对格式 key=value
                        2. json数据格式
                        3. 表单数据格式form-data格式
                */
               // 设置请求的数据的格式(post请求必须设置下面这个)
               request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
                //5. 发起网络请求
                request.send(`username=${i1.value}&password=${i2.value}`);

GET请求和POST请求的区别
                以上两种是最常用的网络请求的类型, 但是网络请求的类型不止GET和POST
                注意:不要通过字面意思去立即get和post的功能, get和post两种请求数据的方式既可以向服务器发送数据, 也可以从服务器获取数据
                get和post的区别:
                1. get请求如果需要传递数据, 数据是以键值对的形式直接拼接到url网址的后面, 而post请求如果需要传递数据, 数据不会拼接在url网址的后面, 而且将数据存到请求体里
                2. get请求传递的数据大小由限制, 最多1024字节, 而post请求传递的数据大小没有限制
                3. get方法提交的数据, 因为直接拼接到url网址的后面, 直接可见, 所以存在安全问题. 而post请求传递的数据存在请求体里, 不可见, 相对安全

JSON数据结构 其本质上就是一个字符串, 但是该字符串有对应的书写规则
                    1. 最外层单引号 
                    2. 可以由数组或者对象嵌套组成
                    3. 如果数据结构为对象, 那么对象的key要用双引号引起; value的数据类型根据需求可以任意

JS如何操作JSON字符串
                1. 将json串转成JS的原生数据类型, 如: 数组, 对象
                语法: JSON.parse(json串)   返回值: 数组或者对象
                2. 将JS的原生数据类型, 如数组或者对象 转成json串
                语法: JSON.stringify(数组/对象) 返回值:json串

跨域:
                1. 什么是跨域?
                当客户端的位置的url里的协议名, 域名, 端口三者与所请求的服务端的url里的协议名, 域名, 端口有一个不一致, 就认为跨域
                2. 为什么会存在跨域问题?
                JS有"同源策略", 这是浏览器的基本安全功能, 同源策略会阻止一个域的JS向另一个域的内容进行交互
                3. 如果跨域, 浏览器会阻止哪些操作?
                    3.1 不允许通过JS发起网络请求
                    3.2 无法读取非同源页面里的cookie, localStorage
                    3.3 无法操作非同源页面里的DOM
                4. 如何解决跨域问题?
                    4.1 前端主导的解决跨域的办法  jsonp解决跨域
                    4.2 后端主导的解决跨域的办法  CORS解决跨域

jsonp跨域 (json with padding)
                jsonp解决跨域的特点: jsonp是解决客户端与服务端跨域常用的办法, 最大的特点: 简单好用, 兼容性好(兼容低版本IE); 缺点: 只能发起get请求, 无法发起post请求
                jsonp解决跨域的原理: 利用html标签发起网络请求不存在"同源策略"的特点, 通过标签发起请求, 实现了跨域的数据通信

jq的ajax请求, 是对原生ajax的封装, 语法上更简洁统一
                $.ajax({
                    type:请求的类型: get/post,
                    url:请求地址,
                    async:是否异步,
                    data:需要传递的数据, 格式为对象,
                    dataType:预期的服务器的响应类型,
                    success: 成功的回调函数,
                    error: 失败的回调函数,
                    contentType:传递的数据的格式,
                    jsonp:传递的回调函数的键值对的key,
                    jsonpCallback:传递的回调函数的键值对的value
                });

简述ajax发起网络请求的基本步骤

1.创建XMLHttpRequest

let xhr=new XMLHttpRequest;

2.连接服务器

xhr.open("get","goods.json",true)

true代表异步,false代表同步。goods.json代表请求的路径

3.向服务器发送请求

xhr.send()

4.接受服务器响应的数据

ajax的作用:向http服务器发送请求,并可以接收到http服务器响应的数据

什么是跨域?为什么会出现跨域?如何解决问题?

跨域的概念:

浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域

为什么会有跨域?

在前后端分离的模式下,前后端的域名是不一致的,此时就会发生跨域访问问题。在请求的过程中我们要想获取数据一般都是post/get请求,所以..跨域问题出现

跨域问题来源于JavaScript的同源策略,即只有 协议+主机名+端口号(如存在)相同,则允许相互访问。也就是说JavaScript只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。跨域问题是针对JS和ajax的,html本身没有跨域问题,比如a标签、script标签、甚至form标签(可以直接跨域发送数据并接收数据)等

怎么解决跨域问题?

前端: jsonp, 后端cors

说出你所知道的http的状态码及其含义

Get请求和post请求的区别是什么?

1)使用Get请求时,参数在URL中显示,而使用Post请求,则不会显示出来; 

(2)Post传输的数据量大,而Get方法由于受到URL长度的限制,只能传递大约1024字节. 

(3)Get请求请求需注意缓存问题,Post请求不需担心这个问题; 

(4)Post请求必须设置Content-Type值为application/x-form-www-urlencoded; 

(5)发送请求时,因为Get请求的参数都在url里,所以send函数发送的参数为null,而Post请求在使用send方法时,却需赋予其参数; 

(6)GET方式请求的数据会被浏览器缓存起来,因此其他人就可以从浏览器的历史记录中读取到这些数据,例如账号和密码等。在某种情况下,GET方式会带来严重的安全问题。而POST方式相对来说就可以避免这些问题。

请解释JSONP的工作原理,以及它为什么不是真正的AJAX

JSONP 是一种非正式传输协议,允许用户传递一个callback给服务端,然后服务端返回数据时会将这个callback 参数作为函数名来包裹住 JSON 数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。当GET请求从后台页面返回时,可以返回一段JavaScript代码,这段代码会自动执行,可以用来负责调用后台页面中的一个callback函数。

实质不同

ajax的核心是通过xmlHttpRequest获取非本页内容

jsonp的核心是动态添加script标签调用服务器提供的js脚本

jsonp只支持get请求,ajax支持get和post请求

HTTP浏览器输入URL后发生了什么

1.DNS域名解析;

2.建立TCP连接;

3.发送HTTP请求;

4.服务器处理请求

5.返回响应结果;

6.关闭TCP连接;

7.浏览器解析HTML;

8.浏览器布局渲染;

35.

jQuery 是一个高效、精简并且功能丰富的 JavaScript 工具库。它提供的 API 易于使用且兼容众多浏览器,这让诸如 HTML 文档遍历和操作、事件处理、动画和 Ajax 操作更加简单。
                
                1. JQ查找元素
                2. JQ操作DOM
                3. JQ动画

一. JQ获取元素
                语法:
                    $("选择器")
                返回值: Jq对象
                
                牢记: $()方法只要调用, 得到的一定是jq对象, 不再是之前的DOM对象. Jq对象有一套自己的方法, 这些方法只能由jq对象使用, 且jq对象不再支持之前的DOM对象的所有方法和属性

二. 如何操作jq对象的css样式
                设置样式 
                语法: 
                    一次设置一个样式
                    css("样式名", "样式值")
                    一次设置多个样式
                    css({
                        样式名:"样式值",
                        样式名:"样式值",
                        ...
                    })
                注意: 如果jq对象里有多个元素, 设置样式时, 全部设置上
                
                获取样式
                语法: 
                    css("样式名")
                    返回值: 获取到的样式值
                注意: 如果jq对象里有多个元素, 获取样式时, 只能获取到第一个

三. jq对象和dom对象之间如何相互转化?
                    jq对象转dom对象: 通过下标从jq对象里取出, 取出的就是dom对象
                    dom对象转jq对象: 将dom对象直接放进$()里, 返回出来的就是jq对象

                    // var jqDiv1 = $("#div1");
                    // jqDiv1[0].style.background = "red";
            
                    // var domDiv1 = document.querySelector("#div1");
                    // $(domDiv1).css("background", "red");

四. JQ常用的伪类选择器      

            :first 找到jq对象里第一个对象
            // $("li:first").css("background", "red");
            :last 找到jq对象里最后一个对象
            // $("li:last").css("background", "blue");
            :even 找到jq对象里序号为偶数的对象, 序号从0开始
            // $("li:even").css("background", "blue");
            :odd 找到jq对象里序号为奇数的对象, 序号从0开始
            // $("li:odd").css("background", "red");
            :eq(index) 找到jq对象里序号为index的对象, 序号从0开始
            // var index = 5;
            // $(`li:eq(${index})`).css("background", "yellow");
            // $("li").eq(index).css("background", "yellow");
            :gt(index) 找到jq对象里序号大于index的所有对象, 序号从0开始
            // $("li:gt(5)").css("background", "red");
            :lt(index) 找到jq对象里序号小于index的所有对象, 序号从0开始
            // $("li:lt(5)").css("background", "blue");
            :hidden 找到当前隐藏的元素, 首先默认不显示的标签, 如head, meta等, 其次, 人为设置了display:none的标签
            // console.log($(":hidden"));
            :visible  找到当前显示的元素, 首先默认显示的标签, 如html, body等, 其次, 没设置display:none的标签
            // console.log($(":visible"));

           :disabled 获取到禁用的表单元素
            // console.log($(":disabled"));
            :enabled 获取到可用的表单元素
            // console.log($(":enabled"));
            :checked 获取到的是被选中的选框控件
            // console.log($(":checked"));
            :focus 获取到的是获得焦点的输入框控件
            // console.log($(":focus"));

二、JQ操作DOM

                1. 获取DOM节点
                    1.1 JS: document.getElementxxxx(), document.querySelectorxxxx()
                    1.2 JQ: $("选择器")
                    
                2. 操作元素节点的内容
                    2.1 JS: innerHTML, innerText, textContent, value
                    2.2 JQ: html(), text(), val()
                
                3. 操作元素节点的样式
                    3.1 JS: style属性, 读写性; getComputedStyle() 只读;
                    3.2 JQ: css() 读写性;
                
                4. 操作元素节点的属性
                    4.1 JS: 点语法/getAttribute,setAttribute
                    4.2 JQ: attr() / prop()
                    
                5. 创建, 插入, 删除元素节点
                    5.1 JS: createElement(), appendChild()/insertBefore(), removeChild()
                    5.2 JQ:
                        a. 创建节点  $("html字符串")
                        b. 将节点插入到文档里
                        c. 删除节点 

  

html()方法 读写:
            类似于JS里的innerHTML
            在设置内容时, 如果jq对象里有多个元素, 都设置上内容, 如果内容里有html标签的字符串, 该字符串会被解析成真正的标签
            在获取内容时, 如果jq对象里有多个元素, 只能获取到第一个元素的内容
text() 读写:
                类似于JS里的innerText
                在设置内容时, 如果jq对象里有多个元素, 都设置上内容, 如果内容里有html标签的字符串, 该字符串不会被解析成真正的标签, 原样输出
                在获取内容时, 如果jq对象里有多个元素, 能获取到所有元素的内容
val() 读写:
                在设置value时, 如果jq对象里有多个元素, 都设置上
                在获取value时, 如果jq对象里有多个元素, 只获取第一个元素的value值                attr() 读写:
                1. 设置属性  jq对象.attr("属性名", "属性值")
                2. 获取属性  jq对象.attr("属性名")
                
                注意: 设置的时候如果jq读写里有多个元素, 都设置上; 获取的时候jq对象里如果有多个元素, 只获取第一个
prop() 读写性:
                1. 设置属性  jq对象.prop("属性名", "属性值")
                2. 获取属性  jq对象.prop("属性名")
attr和prop的区别:
                1. 如果要操作系统自带属性, 建议使用prop; 如果要操作自定义属性, 建议使用attr
                2. 当操作属性名和属性值相同的属性时, 建议使用prop                     

创建节点
                $("html字符串")
            1. 创建div
            // var divJQ = $("<div></div>");
            2. 设置需要的样式或者属性
            // divJQ.css({width:200, height:200, background:"red"});
            // divJQ.prop("class", "div1");
            // divJQ.html("这是div1");
            3. 将新创建的div插入到当前文档
                    3.1. 父jq对象.append(新创建的jq对象/DOM元素/html字符串/元素数组) 将新元素添加到父jq对象里面的末尾

                    3.2. 新创建的jq对象.appendTo(jq对象/选择器/DOM元素); 将新元素添加到父jq对象里面的末尾

                    3.3. jq父节点对象.prepend(新创建的jq对象/DOM元素/html字符串/元素数组) 将新创建的节点添加到父节点里面的最前面

                    3.4. 新创建节点.prependTo(jq对象/选择器/DOM元素) 将新创建的节点添加到父节点里面的最前面

                    3.5. 兄弟jq节点.after(新创建的jq对象/DOM元素/html字符串/元素数组) 往兄弟节点的后面插入新的节点

                    3.6. 兄弟jq节点.before(新创建的jq对象/DOM元素/html字符串/元素数组) 往兄弟节点的前面插入新的节点

删除节点
                想要删除的jq节点.remove();
                想要删除的jq节点.detach();
                
                以上两个方法都可以删除节点, 都有返回值,返回值是已经删掉的所有元素的jq对象
                
                remove()和detach()的区别:
                remove在移除元素的时候, 会解绑其绑定上的所有事件
                detach在移除元素的时候, 不会解绑其绑定上的所有事件

遍历jq对象 可以用for...in 但是会将jq对象的所有的key全部遍历出来, 我们没有必要获取所有的key ,我们只想拿到jq里所有的标签
            var imgJq = $("img");
            for(var n in imgJq){
                // console.log(n);
            }
            
            // 所以 遍历jq对象, 请使用jq自带的方法, each方法, 方法需要传入回调函数, 第一个参数为key, 第二个参数为value(dom元素)
            imgJq.each(function(i, v){
                console.log(i, v);
            });

三、 JQ动画

                    animate(样式对象, 动画运动时间, 回调函数)
                    注意: 只有数字值可创建动画(比如 "margin:30px")。字符串值无法创建动画(比如 "background-color:red")。
                    还可以通过+=或者-=创建相对动画

               ("#d").animate({
                    width:"+=10",
                    height:"+=10",
                    // borderRadius:"50%"
                }, 1000, function(){
                    alert("dDiv动画执行完毕");
                });
                $("#a").show(1000);
                $("#b").show("fast"); // slow, normal, fast
                $("#c").show(2000, function(){
                    alert("cDiv已完全显示");
                });
                $("#a").hide(1000);
                $("#b").hide("fast"); // slow, normal, fast
                $("#c").hide(2000, function(){
                    alert("cDiv已完全隐藏");
                });
                $("#a").toggle(1000);
                $("#b").toggle("fast"); // slow, normal, fast
                $("#c").toggle(2000, function(){
                    alert("cDiv动画执行完毕!");
                });
                $("#a").slideUp(1000);
                $("#b").slideUp("normal");
                $("#c").slideUp(2000, function(){
                    alert("cDiv上拉完毕!");
                });
                $("#a").slideDown(1000);
                $("#b").slideDown("normal");
                $("#c").slideDown(2000, function(){
                    alert("cDiv下拉完毕!");
                });
                $("#a").slideToggle(1000);
                $("#b").slideToggle("normal");
                $("#c").slideToggle(2000, function(){
                    alert("cDiv执行完毕!");
                });
                $("#a").fadeOut(1000);
                $("#b").fadeOut("fast");
                $("#c").fadeOut(2000, function(){
                    alert("cDiv淡出效果完毕!");
                });
                $("#a").fadeIn(1000);
                $("#b").fadeIn("fast");
 
                $("#c").fadeIn(2000, function(){
                    alert("cDiv淡入效果完毕!");
                });
                $("#a").fadeTo(1000, 0.5);
                $("#b").fadeTo("fast", 0.3);
                $("#c").fadeTo(2000, 0.1, function(){
                    alert("cDiv淡入效果完毕!");
                });
                $("#a").fadeToggle(1000);
                $("#b").fadeToggle("fast");
                $("#c").fadeToggle(2000, function(){
                    alert("cDiv效果执行完毕!");
                });
JQ通过节点之间的关系获取节点
            //1. 查找某个节点的父节点  parent("选择器") 填写选择器的话可以对父节点做过滤
            // $("div").parent("body").css("background", "red");
            //2.查找某个节点的所有祖先节点  parents("选择器") 填写选择器的话可以对祖先节点做过滤
            // $("#div7").parents("div").css("background", "blue");
            //3. 查找某个节点的所有祖先节点直到某个祖先节点停止, 不包含指定的节点  parentsUntil("指定的节点的选择器", "过滤选择器")
            // $("#div7").parentsUntil("html", "div").css("background", "red");
            //4. 查找到某个节点的所有子节点 children("选择器") 可以通过填入选择器对找到的子节点做过滤
            // $("#div1").children("div+div").css("background", "red");
            //5. 查找到某个节点的所有后代节点  find("选择器") 选择器为*找到所有后代节点, 当然可以改变选择器对后代节点过滤
            // $("#div1").find("*").css("background", "blue");
            
            //6. 查找到某个节点的上一个兄弟节点 prev("选择器") 填写选择器用于对找到的兄弟节点进行过滤
            // $("#div3").prev().css("background", "red");
            
            //7. 查找到某个节点的下一个兄弟节点 next("选择器") 填写选择器用于对找到的兄弟节点进行过滤
            // $("#div3").next().css("background", "blue");
            
            //8. 查找到某个节点的上面所有的兄弟节点 prevAll("选择器") 填写选择器用于对找到的兄弟节点进行过滤
            // $("#div4").prevAll().css("background", "yellow");
            
            //9. 查找到某个节点的下面所有的兄弟节点 nextAll("选择器") 填写选择器用于对找到的兄弟节点进行过滤
            // $("#div2").nextAll().css("background", "red");
            
            //10. 查找到某个节点的所有兄弟节点 siblings("选择器") 填写选择器用于对找到的兄弟节点进行过滤
            // $("#div3").siblings().css("background", "red");
            
            // 11. 查找节点上面的所有兄弟节点直到某个节点停住,不会找到指定的节点,第二个参数可以继续为找到的节点过滤,prevUnitl("找到的节点选择器","过滤选择器")
            // $("#li10").prevUntil("#li2",".abc").css("background","lightcoral")
            
            // 12. 查找节点下面的所有兄弟节点直到某个节点停住,不会找到指定的节点,第二个参数可以继续为找到的节点过滤,nextUntil("找到的节点选择器","过滤选择器")
            // $("#li1").nextUntil("#li10",".abc").css("background-color","lightcoral")

JQ获取元素尺寸
                1. width(), height() 元素的content大小
                2. innerWidth(), innerHeight() 元素的内容大小加上padding大小
                3. outerWidth(), outerHeight() 元素的内容大小加上padding大小加上border大小
                4. outerWidth(true), outerHeight(true) 元素的内容大小加上padding大小加上border大小加上margin大小

JQ获取元素位置
                1. offset() 返回的是目标元素的边框的外边缘距离浏览器窗口左边和上边的距离对象
                2. position() 返回的是目标元素的margin的外边缘距离离它最近的, 设置了非static定位的祖先元素的左边框和上边框的内边缘
                以上两个方法均会放回一个位置对象, 该对象里存储两个值, left和top, 用于表示元素的位置

JQ事件
         1.jq对象.事件函数名();
         2.jq对象.on();

  1.on()绑定事件基础版
               语法:
                    $().on("事件名",回调函数)
  2.on()一次绑定多个事件
                语法:
                $().on("事件名1 事件名2...",回调函数)
  3.on()一次绑定多个事件,每个事件触发不同的函数
                语法:
                $().on({
                    事件名1:回调函数1,
                    事件名2:回调函数2,
                    ...
                })

JQ取消事件
                对象名.unbind();

文档加载时间
                1.load事件:    JS的原生事件,该事件会在绑定的对象完全加载完毕之后触发
                2.ready事件:    JS的事件,该事件会在页面的DOM结构加载完毕之后触发
                一个页面的响应的基本顺序:
                1.    域名解析
                2.    加载html文件
                3.    加载css和js
                4.    加载图片等其他信息
                // 面试题:当在浏览器的地址栏写入网址并敲了回车之后发生了什么,过程描述的越详细越好.
                
                load事件与ready事件的区别:
                load事件会等到页面里的所有资源加载完毕之后触发;而ready事件只会等到页面里的DOM结构加载完成之后就会触发

正常情况下,标签的执行顺序是从上到下的,所以我们在写script标签时,建议写到body里边的最下边,目的是为了让我们写的所有的html标签先加载,当标签加载完毕之后,再执行script里的代码,如果代码里操作当前页面里标签的代码,就能正常执行.
            但是,script标签的位置其实是可以任意的,但是是有前提的`
            1.如果该script标签是一个第三方的js文件,那么该标签位置可以随意,但是我们建议写到head里
            2.如果script标签是我们自己书写js的标签
                2.1 如果我们不需要书写获取html标签的代码,该script标签也可以随便放置,建议还是放到head里
                2.2 如果我们需要书写html标签的代码,那么建议写到body里面的最下边,如果我们还想要随便防止位置,请给window绑定一个onload事件,保证文档加载完毕之后在执行我们的JS

ES6  数组常用方法

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>04_ES6数组常用方法</title>
		<script src="js/jquery.min.js" type="text/javascript" charset="utf-8"></script>
	</head>
	<body>
		<ul></ul>
		<script type="text/javascript">
			/* 
				1. foreach()
				2. map()
				3. filter()
				4. reduce()
				5. some()
				以上的所有方法均对数组进行的遍历操作
			 */
			// 1. foreach() 该方法仅仅对数组做遍历, 没有返回值, 也不会修改原数组
			// var arr1 = [100, 200, 300, 400, 500];
			// arr1.forEach(function(item, index){
			// 	// 参数1: 遍历出的数组里的每个值
			// 	// 参数2: 遍历出的数组里的每个值的下标
			// 	console.log(item, index);
			// });
			// console.log(arr1);
			
			// 2. map() 对数组做遍历, 遍历的同时可以对数组里的值做修改, 返回值是修改过后的新数组, 但是依然不改变原数组
			// var arr2 = [100, 200, 300, 400, 500];
			// var newArr2 = arr2.map(function(item, index){
			// 	// 参数1: 遍历出的数组里的每个值
			// 	// 参数2: 遍历出的数组里的每个值的下标
			// 	// console.log(item, index);
			// 	// 修改的值需要被return出来, 才会被存进新的数组里
			// 	if(index % 2) {
			// 		return item + "aaa";
			// 	}else {
			// 		return item + "bbb";
			// 	}
			// });
			// console.log(newArr2);
			// console.log(arr2);
			
			/* 
				3. filter() 对数组进行过滤  返回值是过滤后的新数组, 不改变原数组 
			 */
			// var arr3 = [1, 2, 3, 4, 5, 6, 7, 8, 9];
			// var filterArr = arr3.filter(function(item, index){
			// 	// 参数1: 遍历出的数组里的每个值
			// 	// 参数2: 遍历出的数组里的每个值的下标
			// 	console.log(item, index);
			// 	// 制定过滤规则, 规则需要写到return后面
			// 	// return item > 5;
			// 	return item % 2 == 0;
			// });
			// console.log(filterArr);
			// console.log(arr3);
			
			// var proArr = [
			// 	{id:"001", pName:"羽绒服", pPrice:666, type:"服饰"},
			// 	{id:"002", pName:"苹果", pPrice:2, type:"水果"},
			// 	{id:"003", pName:"牛肉", pPrice:30, type:"肉类"},
			// 	{id:"004", pName:"橘子", pPrice:5, type:"水果"},
			// 	{id:"005", pName:"拖鞋", pPrice:7, type:"服饰"},
			// ];
			// var filterProArr = proArr.filter(function(pro, index){
			// 	return pro.type == "水果";
			// });
			// console.log(filterProArr);
			
			/* 
				4. reduce() 数组聚合, 作用: 将数组里的值聚合成一个值
				该方法接收两个参数
				参数1: 回调函数
				参数2: 聚合值的初始值
				
				第一个回调函数里也接收两个参数
				参数1: 每次遍历后聚合成的值
				参数2: 每次遍历的数组里的值
				
				返回值: 最终聚合成的值
			 */
			// 1. 数组求和
			// var numArr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
			
			// var sum = 0;
			// numArr.forEach(function(v){
			// 	sum += v;
			// })
			// console.log(sum);
			
			// var res = numArr.reduce(function(a, b){
			// 	return a + b;
			// }, 0);
			// console.log(res);
			
			var proArr = [
				{id:"001", pName:"羽绒服", pPrice:666, type:"服饰"},
				{id:"002", pName:"苹果", pPrice:2, type:"水果"},
				{id:"003", pName:"牛肉", pPrice:30, type:"肉类"},
				{id:"004", pName:"橘子", pPrice:5, type:"水果"},
				{id:"005", pName:"拖鞋", pPrice:7, type:"服饰"},
			];
			// 想把所有type值是水果的商品的名字单独存放到一个数组里
			// var pNameArr = proArr.reduce(function(a, b){
			// 	if(b.type == "水果") {
			// 		a.push(b.pName);
			// 	}
			// 	return a;
			// }, []);
			// console.log(pNameArr);
			// pNameArr.forEach(function(item){
			// 	$("<li/>").html(item).appendTo("ul");
			// })
			
			// var pNameStr = proArr.reduce(function(a, b){
			// 	if(b.type == "水果"){
			// 		a += `<li>${b.pName}</li>`
			// 	}
			// 	return a;
			// }, "");
			// $("ul").append(pNameStr);
			
			
			/* 
				5. some() 用来判断符合规则的一个值是否存在数组里, 如果符合规则, 返回值是true, 否则返回false
				注意: 如果符合规则, 就停止遍历
			 */
			var arr = [1, 2, 3, 4, 5];
			var res = arr.some(function(item, index){
				console.log(item, index);
				return item > 4;
			});
			console.log(res);
		</script>
	</body>
</html>

36.

深拷贝浅拷贝的区别?

如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址

即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址

浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝

深拷贝开辟一个新的栈,两个对象属性完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性

什么是防抖和节流?有什么区别?

本质上是优化高频率执行代码的一种手段

如:浏览器的 resize、scroll、keypress、mousemove 等事件在触发时,会不断地调用绑定在事件上的回调函数,极大地浪费资源,降低前端性能

为了优化体验,需要对这类事件进行调用次数的限制,对此我们就可以采用throttle(防抖)和debounce(节流)的方式来减少调用频率

  • 节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
  • 防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时

说说em/px/rem/vh/vw区别

px,表示像素,所谓像素就是呈现在我们显示器上的一个个小点,每个像素点都是大小等同的,所以像素为计量单位被分在了绝对长度单位中

em是相对长度单位。相对于当前对象内文本的字体尺寸。

rem,相对单位,相对的只是HTML根元素font-size的值

vw ,就是根据窗口的宽度,分成100等份,100vw就表示满宽,50vw就表示一半宽。(vw 始终是针对窗口的宽),同理,vh则为窗口的高度

null和undefined的区别?

null是一个表示"无"的对象,转为数值时为0;undefined是一个表示"无"的原始值,转为数值时为NaN。

当声明的变量还未被初始化时,变量的默认值为undefined。 null用来表示尚未存在的对象

undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。典型用法是:

(1)变量被声明了,但没有赋值时,就等于undefined。

(2)调用函数时,应该提供的参数没有提供,该参数等于undefined。

(3)对象没有赋值的属性,该属性的值为undefined。

(4)函数没有返回值时,默认返回undefined。

null表示"没有对象",即该处不应该有值。典型用法是:

(1) 作为函数的参数,表示该函数的参数不是对象。

(2) 作为对象原型链的终点。

同步和异步的区别?

同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)。

同步,就是调用某个东西是,调用方得等待这个调用返回结果才能继续往后执行。

异步,和同步相反  调用方不会理解得到结果,而是在调用发出后调用者可用继续执行后续操作,被调用者通过状体来通知调用者,或者通过回掉函数来处理这个调用

什么是渐进增强与优雅降级

渐进增强:针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进,达到更好的用户体验。

优雅降级:一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。

说说你对ES6里的let和const关键字的理解

ES6中的let关键字

作用:与var类似 ,用于声明一个变量

特点在块作用域内有效(块作用域由 { } 包括,if语句和for语句里面的{ }也属于块作用域),不能重复声明,不会预处理,不存在变量提升

ES6中的const关键字

作用:定义一个常量

特点: const 定义的变量与 let 变量类似,但不能重新赋值

说说你对ES6里promise的理解

概念:

Promise 是异步编程的一种解决方案,所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise 对象有以下两个特点。

(1)对象的状态不受外界影响。 Promise 对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。 只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected 。

优点:

避免了回调地狱(函数作为参数层层嵌套,难以阅读的现象);

缺点:

取消Promise,一旦新建它就会立即执行,无法中途取消。

如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。

当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

说说你对ES6里箭头函数的理解

  1. ES6中允许使用箭头=>来定义箭头函数, 语法简介
  2. 箭头函数继承而来的this指向永远不变
  3. call()/.apply()/.bind()无法改变箭头函数中this的指向
  4. 箭头函数不能作为构造函数使用
  5. 箭头函数没有自己的arguments
  6. 箭头函数没有原型prototype

什么叫做回调地狱? 如何解决回调地狱?

在使用JavaScript时,为了实现某些逻辑经常会写出层层嵌套的回调函数,如果嵌套过多,会极大影响代码可读性和逻辑,这种情况也被成为回调地狱。

解决回调地狱有很多方法,比如:Promise 对象、Generator 函数、async 函数

以任意的方式写出一个深拷贝对象的简要代码

答: https://blog.csdn.net/qwe435541908/article/details/88974932

为什么post请求跨域会出问题?如何解决?

有些post请求在跨域发起时, 会先发起一个: 带预检的跨域请求

      带预检的跨域请求: 在真正的请求发起之前, 系统会先发送一个method为options的预请求, 用于试探服务器是否真正接受马上要发起的那个真正的请求, 如果预请求得到的回应是拒绝的, 之后真正的post请求就不会再发起了

解决办法, 设置白名单时要再设置对应的请求特点, 让服务器通过options请求

37.   vue

Vue特点:

1、渐进式: 可以理解成一步一步, 在使用vue的时候, 我们不需要在项目一开始就把整个vue的项目搭建完毕, 而是可以一步一步的去替换之前的代码
 2、自底向上逐层应用:由底层开始, 先把基础的东西搭建完毕, 再往上逐渐增加新的或者复杂的功能
 3、MVVM设计模式: Model(模型)-View(视图)-ViewModel(视图模型)的缩写, 这是一种基于前端开发框架的架构模式, 其核心为ViewModel, 由它控制Model与View之间的数据通信, 使得Model的状态可以直接影响View, 而View的变化也可以影响Model

4、声明式渲染:其实就是一种新颖的语法,Vue允许我们用简洁的语法将数据和DOM绑定在一起

5、单页面应用(SPA):Single Page Application,不发生页面跳转的应用,但是页面的内容可以根据地址栏的路由,对应的切换

1.数据绑定
           1.1值绑定
                1.1.1 插值表达式 {{}},在{{}}里面可以填写data里的变量,表达式,函数调用
                1.1.2 v-text 和 v-html指令
                    两者的区别:当数据里存在html字符串时,v-html会将其解析成标签,而v-text只会原样输出,{{}}也只会原样输出
                面试题:如何解决vue的"文本闪烁"问题
                文本闪烁:当网络没有及时请求到vue.js文件,会导致vue的语法环境无法被识别,所有的内容会原样输出,用户会看到原始的语法,体验感不好
                解决办法:通过v-cloak指令解决
                解决原理:当vue.js文件无法加载完毕时,v-cloak无法被正确识别,会被系统当作普通的自定义属性,然后我们通过书写css样式将所有添加了v-cloak属性的标签display:none,但是一旦vue.js加载完毕,vue就可以识别v-cloak,并将其之前添加的display:none去除,保证页面正常显示.   // css代码: [v-clock] { display: none; }

           1.2 属性绑定
                1.2.1 语法:v-bind:属性名 = "vue的编译环境"
                      简写:   :属性名 = "vue的编译环境"

                1.2.2 style属性绑定    :style="样式字符串/对象/数组/"
                1.2.3 class属性绑定    :class="class名/对象/数组"

            1.3 事件绑定
                语法: v-on:事件名 = “函数名/函数名()/简单语句”
                a. vue在绑定事件的时候,函数名后面的小括号可加可不加,当事件触发时,需要向触发函数里传递数据,建议加小括号,不需要传递数据,小括号不加
                b. vue在触发事件时,如果不需要要传参,触发函数的第一个形参默认就是事件对象event
                c. vue在触发事件时,如果传参了,那么我们无法默认获取时间对象,如果想要获取到,需要在函数调用的时候,传入实参$event,该实参就是事件对象

                简写: @事件名="函数名/函数名()/简单语句"
                vue提供了事件修饰符,来快速解决事件触发后的有关操作,语法: @事件名.修饰符1.修饰符2...="函数名/函数名()/简单语句"
                1. stop阻止事件冒泡
                2. prevent阻止默认事件执行
                3. once一次性事件
                4. 键盘修饰符
                    字母键,left,right,up,down,enter,tab,ctrl,space,shift,alt或者可以直接写键码对应的数字也可以
            1.4 双向数据绑定
             vue里默认的属性绑定是单向的,即model向view,只有数据发生变化才会影响视图,而视图发生变化并不会影响数据.如果想要进行双向数据绑定,不要再使用v-bind指令,改用v-model指令,v-model指令不仅可以让model影响view,也可以让view影响model

vue的常用指令:
        1. v-text/v-html: 值绑定
        2. v-bind: 属性绑定
        3. v-on: 事件绑定
        4. v-model: 双向数据绑定

        5. v-if/v-else 控制元素显示隐藏
        6. v-show 控制元素显示隐藏

        v-if和v-show都是根据布尔值的真假来决定是否显示/隐藏元素, 区别: v-if隐藏/显示元素是将其直接从文档中移除/添加, v-show隐藏/显示元素是设置display:none/block

        7. v-for 列表渲染
            语法:
                v-for="(值, 下标) in 数组"
                v-for="(value, key, 索引) in 对象"
            Vue为了提升将来如果发生数据重拍的效率问题, 建议给每个v-for的标签v-bind一个key属性, key属性的值要求具有唯一性, 尽可能从循环的数据里找, 实在找不到再用下标

           Vue默认采取的是”就地渲染“策略

传值:

1. 父向子:

父实例(组件)向子组件里传数据: 通过props字段设置自定义属性
         1. 给子组件设置props字段,用于接收从外部传入的值
         2. 在使用组件的地方已属绑定的方式给自定义属性赋值
         3. 子组件在组件内通过设置的props获取数据,展示

注: 如果输入属性的类型是一个数组或者对象,在设置默认值的时候,默认值需要写成一个函数,将默认值return出来
            c1_arr: {type: Array, default(){return [111,222,333,444,555]}}

2. 子向父

子组件想父实例(组件)传值,通过"自定义事件"实现传值
        1.给子组件绑定自定义事件,赋值父实例(组件)的方法
        2.合适的时机触发自定义事件,调用父实例(组件)的方法
        3.在父实例(组件)的函数里获取到传入的值,将其赋值给父实例(组件)的data数据源变量,展示

触发自定义事件:
                       语法:this.$emit("事件名")

3. 兄弟之间

消息的监听和发送
        1.vue要求,消息的发送者和监者需要是和发送同一个vue实例,且作用域要覆盖需要传递数据的两个组件,所以建议再新建一个vue实例,专门用于消息的监听
        2.数据的发送方 为 消息的发送者;数据的接收方 为 消息的监听者
        3.一定要保证消息的监听 先于 消息的发送 执行

           /*
                设置监听
                $on("要监听的事件名",监听到之后的回调函数)
            */
            eventVue.$on("c2_event",v=>{
                console.log(v);
                this.c2Msg = v;
            });

                // 触发监听
                eventVue.$emit("c2_event",this.c1Msg)

// 子组件点击调用app实例的方法

// 如何在子组件里获取到其父组件的实例对象
                /*
                    $parent:获取到当前组件的父组件
                    $root:获取到当前组件的根组件
                */
                // console.log(this.$parent);
                // console.log(this.$root);
                // console.log(this.$parent.appFunc1());
                // console.log(this.$parent.appMsg);

// app实例点击调用子组件的方法
                /*
                    在父组件里获取到某个子组件实例,我们通过给子组件添加唯一标识的形式来唯一标记子组件,通过ref属性
                    1.先给对应的子组件添加ref属性
                    2.在父组件里,通过$refs获取所有的子组件实例集合
                */

                console.log(this.$refs.c2.c2Func());
                console.log(this.$refs.c2.c2Msg)

过滤器: vue里的过滤器的作用是用来对数据做格式化的
        vue提供两种过滤器
        1. 全局过滤器
            语法:
                Vue.filter("过滤器的名字", 配置回调)

        <p>{{n.time | formatDate}}</p>

         Vue.filter("formatDate", function (v) {
                // 回调的第一个参数, 为使用过滤器时 | 前的数据
                console.log("formatDate被调用了", v);

                return 数据;
         });
        2. 局部过滤器

        注意: 过滤器创建完毕之后, 只能在{{}}或者v-bind的地方使用
        语法:  数据 | 过滤器名

        // 局部过滤器

        <p>{{pro.pPrice | currency(1)}}元</p>
        filters:{
            // 注意:如果过滤器在使用的时候需要传值, 在使用的同时加(), 把需要传递的值写入(), 对应过滤器的回调从第2个形参开始才是()里传入的实参, 第1个永远都是需要过滤的数据.
            currency(v, count){
                return v.toFixed(count);
            }
        }

计算属性和侦听器
    /*
        当我们需要在模板中放入太多的逻辑时, 会让模板过重且难以维护, 此时, 可以考虑使用计算属性

        但是直接将复杂的逻辑封装成函数也能解决模板内过于复杂的问题, 但是, "太可怕了", 因为如果写成函数调用, 只要页面重新渲染, 所有的函数调用都会重新执行, 严重影响代码执行效率

        计算属性的特点:
        1. 计算属性主要用于解决模板内逻辑过于复杂, 可以进复杂的计算, 并返回计算的结果
        2. 计算属性是基于其依赖的数据进行缓存的, 只有当其依赖的数据发生变化, 计算属性才会重新进行计算.
        3. 计算属性不但可以依赖data属性, 也可以依赖props数据
        4. 完整的计算属性, 其属性值不是一个function, 而是一个对象, 该对象里自带两个方法set(){}和get(){}, 这两个方法不需要我们手动调用, vue会根据我们如何操作计算属性(是设置还是获取)来决定调用哪个方法. 如果是设置就自动调用set方法, 新值为set的第一个形参, 如果是获取, 就自动调用get方法.
        5. 计算属性的函数式写法其实就是计算属性完整写法里的get方法
     */

    /*
        侦听器 watch
        vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性, 当我们需要监听某个数据是否发生变化时, 我们可以考虑使用watch进行监听
        1. 用于观察一个值, 当被观察的值发生变化时, 我们能立刻直到其发生了变化, 可以进行后续的操作
        2. watch不断可以侦听vue实例的data和props数据, 还可以进一步侦听其他的一些全局对象, 如路由router对象
        3. 设置侦听器的时候, 侦听器的名字不可自定义, 其名字就是需要侦听的数据的名字
        4. 完整的侦听器, 值是一个对象, 对象里的侦听回调函数名字为handler
        5. 当写成一个对象, 可以添加immediate字段, 让侦听器在初始化时就触发一次, 还可以添加deep字段, 让侦听器侦听对象属性值的变化
     */

监听路由传参:

        $route: {
            handler(){
                let { id, pName, pPrice } = this.$route.query;
                this.id = id;
                this.pName = pName;
                this.pPrice = pPrice;
            },
            immediate: true
        }

组件的生命周期

        beforeCreate()  // 当vue实例创建完毕之前触发,此时无法获取data数据源,而dom节点但还未真正创建好.
        created()  //当vue实例创建完毕触发,此时可以获取data数据源,而dom节点但还未真正创建好.
        beforeMount()   //实例创建完毕但是挂载之前触发,此时可以获取data数据源,而dom节点但还未真正创建好.
        mounted()  // 实例创建完毕并且挂载完毕之后触发,此时可以获取data数据源,dom节点也真正创建好.
        beforeUpdate()  // data数据已修改,但是页面还未更新完毕
        updated() // data数据已修改,页面更新完毕
        
            以下两个事件只要触发,表示组件或者实例将要被销毁了,此时,我们可以在销毁之前做一些收尾工作,如:
            1.清除计时器,清除事件监听
            2.保存临时数据
            3.保存进度

            建议:以上代码的处理,写到beforeDestroy里
            beforeDestroy()
            destroyed()  //该事件触发,表示当前组件或者实例已经被销毁,包括当前实例或者组件的所有后代组件,也一并销毁

vueX

   computed: {
        // 如果需要获取模块化里的state数据, store.state.模块名.state名
        getCart(){
            return this.$store.state.c.cart
        },
        getStu(){
            return this.$store.state.s.scores
        },
        /*
            如果需要通过mapState获取模块里的state数据, 我们还是需要现指定模块, 再找数据, 语法:
            ...mapState("模块名", []/{})

            注意: 以上语法需要先设置模块的namespace
         */
        ...mapState("c",[
            "cart"
        ]),
        ...mapState("s", {
            getS: "scores"
        }),

        /*
            获取模块对应的getters, 语法: store.getters["模块名/getters属性名"]
         */
        getGreat70(){
            return this.$store.getters["s/getJGScoreVueX"]
        }
        /*
            如果使用mapGetters辅助函数获取模块里的getters, 语法:
            ...mapGetters("模块名", []/{})
         */,
        ...mapGetters("s",[
            "getJGScoreCount",
            "getScoreGrand",
        ]),
        ...mapGetters("s", {
            grandCount: "getGrandCount"

        })
    }


  methods:{
    addpro(){
      /*
        如果想触发模块里的mutations,语法:
        store.commit("模块名/mutations名",数据)
      */
      this.$store.commit('cart/addCartPro',{pro:{name:'西瓜👀',price:8}})
    },
    /*
        如果想通过mapMutations触发对应模块的mutations,语法:
            ...mapMutations("模块名",[]/{})
    */
    ...mapMutations("cart",[
        'addCartType'
    ])
  }

件内守卫分三个
        1. 进入组件之前
        2. 组件路由更新之前
        3. 离开组件之前

        当我们判断路由权限的时候, 需要当前组件的数据源为辅助判断时, 可以考虑使用组件内守卫

   beforeRouteEnter(to, from, next) {
        // 在渲染该组件的对应路由被 confirm 前调用
        // 不!能!获取组件实例 `this`
        // 因为当守卫执行前,组件实例还没被创建
        next(vm => {
            console.log("进入到折扣组件之前调用" + vm);
        });
    },
    beforeRouteUpdate(to, from, next) {
        // 在当前路由改变,但是该组件被复用时调用
        // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
        // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
        // 可以访问组件实例 `this`
        console.log("折扣组件路由改变时调用");
        next();
    },
    beforeRouteLeave(to, from, next) {
        // 导航离开该组件的对应路由时调用
        // 可以访问组件实例 `this`
        console.log("离开折扣组件时调用");
        next();
    }

 路由重定向

// 路由重定向 redirect字段 用于设置重定向的值
{path: "/", redirect: "/home"},
// 一级重定向
// {path: "/mine", redirect: "/mine/info"},
// 二级重定向, 并且一开始不用点击就传值
{path: "", redirect: to => {
		return {path: "info", query: {id: 6688, pName: '🍎', pPrice: 3.58}}					    }},
// 三级重定向
{path: "", redirect: "first"},

 路由

<!--切换路由并通过query方式传值-->
<router-link :to="{path: '/cart', query: {id: 111, pName: '🐵', pPrice: 100}}">购物车</router-link>

<!--切换路由并通过params方式传值-->
<router-link :to="{name: 's', params: {id: 666, type: '羽绒服'}}">折扣专区</router-link>


{path: "/cart", component: () => import("../views/Cart")},
    
{path: "/sale/:id/:type", component: () => import("../views/Sale"), name: "s"},

// JS的语法切换路由
this.$router.push({path: "/product", query: {id: 123, pName: '🍊', pPrice: 3.32}}).catch(() => {});

Vue的动画

Vue的动画需要被包裹在transition组件里, 我们需要给该组件设置name并设置值
vue的过渡/离开动画一共有6个class
        1. v-enter: 设置进入动画的初始值
        2. v-enter-to: 设置进去动画的结束值
        3. v-enter-active: 设置进去动画的动画执行样式和动画时间

        4. v-leave: 设置离开动画的初始值
        5. v-leave-to: 设置离开动画的结束值
        6. v-leave-active: 设置离开动画的动画执行样式和动画时间

        v值需要与transition的name值保持一致

<transition name="fade">
    <p v-if="show">hello</p>
</transition>

css部分
.fade-enter-active, .fade-leave-active {
  transition: all .5s;
}
.fade-leave,.fade-enter-to{
  opacity: 1;
  /*transform: rotateZ(0deg);*/
}
.fade-enter, .fade-leave-to {
  opacity: 0;
  /*transform: rotateZ(360deg);*/
}
关键帧动画
<!--
  如果使用vue动画执行关键帧动画, 因为关键帧动画需要多个关键点, 所以, 不需要再设置初始值和结束值, 也就意味着(v-enter, v-enter-to, v-leave, v-leave-to)都可以不设置, 直接用@keyframe代替, 只写(v-enter-active和v-leave-active)
-->


.rb-leave-active{
  animation: moveImg 1s linear;
}
.rb-enter-active{
  animation: moveImg 1s reverse linear;
}
@keyframes moveImg {
  0%{transform: translate(0,0);opacity: 1}
  25%{transform: translate(200px,0);opacity: .8}
  50%{transform: translate(200px,200px);opacity: .6}
  75%{transform: translate(0,200px);opacity: 4}
  100%{transform: translate(0,0);opacity: 0}
}
vue第三方动画


<!--
         如果通过vue动画来配和animation.css第三方动画执行,不需要再给transition组件设置name,也不需要书写6个class,直接给transition组件设置leave-active-class和 enter-active-class即可,值就像之前一样,直接设置所需要的animation.css的class即可
-->
    <transition leave-active-class="animate__animated animate__lightSpeedOutRight"
                enter-active-class="animate__animated animate__fadeIn">
      <img src="../assets/4.jpg" width="100px" alt="" v-if="show">
    </transition>

在vue-cli脚手架项目里,引入路径时,@与~的意思
@import "~animate.css";
多个元素/组件动画

<button @click="show = !show">toggle</button>
<transition enter-active-class="animate__animated animate__lightSpeedOutRight"
                leave-active-class="animate__animated animate__fadeIn" mode="out-in">
      <!--
            默认情况下, 如果多个元素做动画, 动画是同时开始同时结束, 如果想要规定执行的顺序, 给transition组件设置mode属性, 规定过渡的模型
            1. in-out: 先执行进入, 在执行离开
            2. out-in: 先执行离开, 在执行进入

            注意: 如果做动画的多个组件是相同的标签, vue不会真正的切换元素而仅仅只会替换内容, 所以可以通过给标签加key的形式唯一区别, 这样vue就不会只渲染内容了
      -->
      <h2 v-if="show" style="width: 200px; background: yellowgreen">这是h2元素</h2>
      <h2 v-else style="width: 200px; background: oldlace" key="a">这是h3元素</h2>
</transition>

axios

axios的post请求

注意:
        1.axios在发起post发起的请求时,默认的是json
        2.如果想要将post传递的数据格式修改成非json格式,如键值对,直接将传入的参数对象通过qs模块处理成键值对即可,我们需要qs模块的帮助

       import qs from "qs";

       qs.stringify({
        username: this.username,
        password: this.password
      })

vue.config.js

vue-cli项目  在开发阶段,如果需要发起网络请求,绝大部分情况下会出现跨域问题
    解决办法:想办法让我们当前的vue-cli项目发起请求时,与服务器同源--我们要通过"请求代理"来实现
    "请求代理的实现步骤":
    1.在当前项目的根目录里,新建一个vue.config.js文件
    2.通过devServer字段设置请求的转发,具体代码参见vue.config.js文件

    注意事项:
    1.如果只需要设计一个代理地址,发起请求时,url地址注意写相对路径(即去掉原有的域名+端口,只保留路由地址)
    2.如果需要设置多个代理地址,首先在配置proxy字段时,需要人为通过key值区分不同的代理地址,在发起请求时,url地址注意写相对路径(即去掉原有的域名+端口,写成 /key/路由地址)
    3.为了保证路由域服务器一致,需要设置pathRewrite字段,保证请求真正发起时,去掉key,保证路由的正确性.

// vue.config.js文件,当运行npm run serve命令时,系统会自动检测项目的根目录是否存在该文件,如果有自动加载,该文件语法为CommonJS,我们需要向外导出一个exports对象,
// vue.config.js文件为整个脚手架项目的配置文件
module.exports = {
    // devServer字段,值为一个对象,用来对vue-cli的开发时服务器做配置
    devServer: {
        //配置服务器的端口
        port: 8123,
        open: true,
        //设置当前服务器的请求代理转发
        // proxy: "http://127.0.0.1:1234",
        // 设置多个请求代理
        proxy:{
            "/api1": {
                target:"http://127.0.0.1:1234",
                // 真正发起请求时,
                pathRewrite:{
                    // /api1/axiosGet => axiosGet
                    "^/api1":""
                }
            },
            "/api2": {
                target:"http://127.0.0.1:5678"
            }
        }
    }
};

    state:vueX的唯一数据源,存储在state的数据可以被整个项目共享,数据的类型可以任意,可以是基本数据类型,也可以是复杂数据类型

    如果想要修改store里的数据,唯一的办法就是通过操作mutations,mutations的使用语法类似于methods

    注意:mutations里只能写同步的代码

// 修改num数据,让其自增
incrementX(state, payload) {
    // 函数的第一个参数可以让我们获取到state里的数据
    // 函数的第二个参数,可以接受外界传入的实参,建议传入的类型为对象类型
    state.num += payload.x;
},

 注意: vueX里的数据更新依然遵循vue的数据监听规则, 即复杂数据类型的数组和对象在某些情况下, vue无法检测, 需要额外处理

 // v.type = payload.type;
 Vue.set(v, 'type', payload.type)

actions: {
        Action 类似于 mutation,不同在于:
       Action 提交的是 mutation,而不是直接变更状态。
       Action 可以包含任意异步操作。
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。
       

reqCityData(context) {
     context.commit("getCity", {city: response.data});
}

 getters:{}

 getters可以理解成vueX的计算属性,有时候我们需要对获取到的state数据进一步做处理,而且还不想在组件里操作,可以考虑通过getter实现
    getters: {
        1.vueX里getters里的第一个形参,可以直接获取到state数据源
        2.统计所有及格的人数,getters里的第二个形参,可以允许我们在一个getters里操作其他的getters
        getScoreGrand: (state, getters) => grand => {}

页面调用 vueX的state:mapState

<script>
/*
  mapState辅助函数,作用:简化计算属性的书写
*/
import {mapState} from 'vuex';

export default {
  name: "Cone",
  data() {
    return {
      cOneNum: 777
    }
  },
  // vueX建议在获取state的数据时,通过计算属性获取
  computed:{
    getCart(){
      return this.$store.state.cart;
    }
  },
  // mapState语法生成计算属性
  computed:mapState({
    // 写法1 函数写法
   getCart:state => state.cart,
  
    // 写法2 字符串写法,字符串里的值需与state里的数据key值保持一致
    getCart:'cart',
  
    // 写法3 如果想要获取局部的this 声明成普通函数
    getCount(state){
      return state.num + this.cOneNum
    }
  }),
  // 通过展开运算符将 mapState 计算属性与本地计算属性合并
  computed: {
    getConNum() {
      return `组件1的num是:${this.cOneNum}`
    },
    // 使用对象展开运算符将此对象混入到外部对象中
    ...mapState({
      getCount(state){
            return state.num + this.cOneNum
          },
      getCart:state => state.cart,
    }),
    ...mapState([
        // 用这种 数组 的方法计算属性的 名字 不能改
      'num',
      'cart'
    ]),
      getCount(state){
        return state.num + this.cOneNum
      }
  }
}
</script>

页面调用 vueX的state:mapGetters

<script>
import {mapState,mapGetters} from 'vuex'
export default {
  name: "Ctwo",
  data(){
    return {
      grand:''
    }
  },
  computed:{
    ...mapState([
      'scores'
    ]),
    getPassScores(){
      return this.$store.state.scores.filter(p=>p.score>=70)
    },
    // 将vueX的getters封装进组件的计算属性里
    getJSScore1(){
      return this.$store.getters.getJSScoreVueX;
    },
    getJGCount(){
      return this.$store.getters.getJSScoreCount
    },
    getGrand(){
      return this.$store.getters.getScoreGrand
    },
    // 使用mapGetters辅助函数将getters与组件的计算属性合并在一起
    // ...mapGetters([
    //     'getGrandCount'
    // ]),
    ...mapGetters({
      getGrandCount:'getGrandCount',
    }),
  }
}
</script>

页面调用 vueX的state:mapMutations

<script>
import {mapState,mapMutations} from 'vuex';
export default {
  name: "Cthree",
  computed:{
    ...mapState([
        "cart",
        'num',
        'scores'
    ])
  },
  methods:{
    setNum(){
      // 按钮点击事件,触发修改vueX的num值
      // 注意:千万不要手动调用mutation里的方法,要通过commit通知vueX,让其去调用
      // this.$store.commit('increment');
      this.$store.commit('incrementX',{x:5})
    },
    addPro(){
      this.$store.commit('addCartPro',{pro:{name:'桃子🍑',price:'6'}})
    },
    addProType(){
      this.$store.commit('addCartType',{type:'水果'})
    },
    ...mapMutations([
        'increment',
        'incrementX'
    ]),
    // 如果想要给直接注入的mutations重新起名,重新起的名字就会变为方法名
    ...mapMutations({
      add:'increment',
      add5:'incrementX'
    }),
  }
}
</script>

页面调用 vueX的state:mapActions


<script>
import {mapState,mapActions} from  'vuex'
import Vue from "vue";
export default {
  name: "Cfour",
  methods:{
    /*
      mapActions方法传入数组,注入的值可以直接充当函数名使用
      mapActions方法传入对象,注入的值依然可以充当函数名使用,但是可以重新起名
    */
    ...mapActions([
        'reqCityData'
    ]),
    ...mapActions({
      reqCD:'reqCityData'
    })
  },
  computed:{
    ...mapState([
        'cityArray'
    ])
  },
  created() {
    // 触发action,通过dispatch方法
    // this.$store.dispatch('reqCityData').then(data=>{
    //   console.log(data)
    // }).catch(error=>{
    //   console.log(error)
    // });
    // this.reqCityData();
    this.reqCD().then(response=>{
      console.log(response)
    });
  }
}
</script>

keep-alive
            1. keep-alive组件:可以让其包裹的组件保持活性,不被默认生命周期销毁,减少组建的销毁效率,提高页面的运行效率
             2. 注意: 凡是被keep-alive组件包裹的组件不会再被销毁,也就意味着其生命周期里的beforeCreate,created,beforeMount,mounted,beforeDestroy,destroyed不再执行</h3>
             3. 如果想要选择性缓存,可以通过keep-alive的include或者exclude属性指定缓存哪些,或者不缓存哪些
                  3.1 include 写谁保活谁,不写谁,谁死
                  3.1 exclude 写谁,谁死;不写谁,谁保活
            注意:值可以是字符串,变量(属性绑定,值一般写成数组),正则(属性绑定),exclude>include  死的优先级高

<keep-alive include="C1,KAComponent">
	<router-view />
</keep-alive>
<keep-alive exclude="C1,KAComponent">
	<router-view />
</keep-alive>
<keep-alive :include="/^C[a-zA-Z0-9]+/">
	<router-view />
</keep-alive>

我们现在如果想要给一个组件传递数据,有哪些方法???
    1. 给组件的data数据源添加数据
    2. 利用组件间通信,将其他组件的data数据源的数据传递到本组建里
    3. 通过路由切换,在切换的同时进行传值,query/params
    4. vueX,将state里的数据展示到组建里
    5. 插槽,向组件里写入内容

    插槽:用于接收组件里写入的内容,这些内容可以是文本,html标签,也可以是其他组件.利用插槽,我们可以将一个组件的模板结构予以更加灵活地展示.
    传入到组件的内容部分的所有内容,会被该组件模板里的slot组件加载

    具名插槽:如果我们想要指定传入的内容应该展示的位置,还想让内容分部到模板里的不同地方,我们必定是需要多个slot
    1. 如果写了多个slot但没有任何的处理,vue会把传入的内容在每个slot里都展示一遍
    2. 想要将内容正确的分配,首先将不同位置的内容先用template包裹,然后给组件模板里的slot设置name属性,将其区分开,最后通过v-slot指令指定template模板需要进入的slot

    作用于插槽:
      1.默认情况下,要传入一个组件里的内容如果要做数据绑定,其默认合法的作用域为其父级的作用域,而不是该组件自身的作用域
      2.但是传入的内容最终终究会在组件的模板里显示,那么如果想让组件传入的内容享受到其对应组件的作用域数据,需要指定作用域
        实现原理:
          1. 将子组件的数据作为slot的一个attribute(属性)绑定上去
          2. 凡是绑定给<slot>组件上的attribute都被称为插槽的属性,我们可以在使用v-slot的时候定义我们提供给<slot>的插槽prop的名字
          3. 之后外界传入的内容可以通过我们自定义的名字使用prop上绑定的数据(v-slot自定义的prop名字.slot属性绑定的名字)

<slot name="s1"></slot>
<button>{{ type }}</button><br>
<slot name="s2"></slot>

页面引入
<Child2 type="注册">
    <template v-slot:s1>
      <input type="text" placeholder="确认密码"><br>
    </template>
    <template v-slot:s2>
      <input type="checkbox"><span>霸王协议...</span>
    </template>
</Child2>

 38.

01_webpack核心概念:
    1. 入口(entry):用于指定从哪个文件开始构建依赖关系
    2. 输出(output):当webpack根据依赖关系图将资源打包完成之后,打包之后的文件位置
    3. 加载器(loader):用来预处理文件,操作的是文件,比如:loader可以将A文件转化为B文件
    4. 插件(plugin):是一个扩展器,增强wenpack功能,其不操作文件,只是针对loader处理结束之后,在webpack的整个打包过程中的某个节点,执行广泛任务,比如:loader将less文件转化为css文件之后,我们可以利用某个插件,将其合并成一个css文件

    5. 模式(mode):webpack里有两种模式:开发(development),生产(production)

    注意:
    webpack默认能直接处理js文件和json文件,其他的资源无法处理,需要借助loader/plugin
02_打包css文件:

/*
    该文件为webpack的配置文件
    作用: 让webpack知道自己应该干什么, 执行该文件的命令: webpack

    该文件的语法遵循commonJS
*/
const { resolve } = require("path");
module.exports = {
    //入口文件
    entry:"./src/main.js",
    //出口路径
    output:{
        // 文件名
        filename:"buildMain.js",
        // 出口位置
        path: resolve(__dirname, "dist")
    },
    // loader配置
    module:{
       // 详细的loader配置, key为rules, 值是一个数组
       rules:[
           //处理css文件
           {
                // 匹配哪些文件
                test: /\.css$/,
                // 匹配上之后, 用数组里的loader进行处理
                use: [
                    // 处理css文件需要用到两个loader, 分别是style-loader和css-loader
                    /*
                        loader的执行顺序问题: webpack里的loader, 如果处理一类文件的时候用到了多个loader, 那么一定要注意书写顺序; loader的执行顺序是: 从下到上, 从右到左
                    */
                    // 2. style-loader 创建一个style标签, 将js中的资源插入到行间, 再把标签添加到head里
                    "style-loader",
                    // 1. css-loader 将css代码编程commentJS模块, 本质上是转成了js的字符串
                    "css-loader"
                ]
           },
           // 处理less文件 需要使用less-loader
           {
                test: /\.less$/,
                use: [
                    // 3. style-loader 创建一个style标签, 将js中的资源插入
                    "style-loader",
                    // 2. css-loader 将css代码编程commentJS模块, 本质上是转成了js的字符串
                    "css-loader",
                    // 1. 将less文件编译成css文件
                    "less-loader"
                ]
           }
       ] 
    },
    //模式
    mode:"development"
    // mode:"production"
}

03_打包html文件:

想要打包html文件, 不是使用loader, 而是使用plugin, 我们需要html-webpack-plugin这个插件帮助我们自动生成html文件

// 1. 引入plugin
const HtmlWebpackPlugin = require("html-webpack-plugin");
// 2. 在module.exports对象里的plugins字段里创建对应插件的实例对象, 或者也可以在创建实例对象的同时对该插件进行配置

// HtmlWebpackPlugin插件默认情况下会创建一个空的html文件, 并自动将打包好的js文件通过script标签引入

// 如果想要让HtmlWebpackPlugin将原有的html文件里的所有内容进行打包, 需要给该构造函数传入参数进行配置, 通过对象的template属性

            // 将src里的index.html文件里的内容拷贝给打包出来的html文件
            template:"./src/index.html",
            // 设置打包文件的文件名
            filename:"./home.html",
            // 压缩html代码
            minify: {
                // 清理空格
                collapseWhitespace:true,
                // 清理注释
                removeComments:true,
                // 压缩行内的css
                minifyCSS:true,
                // 压缩行内js
                minifyJS:true,
                // 删除内容为空的标签
                removeEmptyElements:true
            }

const { resolve } = require("path");
// 1. 引入plugin
const HtmlWebpackPlugin = require("html-webpack-plugin");
// 2. 在module.exports对象里的plugins字段里创建对应插件的实例对象, 或者也可以在创建实例对象的同时对该插件进行配置

module.exports = {
    // 入口文件
    entry:"./src/main.js",
    // 出口位置
    output:{
        filename:'build.js',
        path:resolve(__dirname, "dist")
    },
    // 配置所有的加载器loader
    module:{
        rules:[
            // css需要的loader
            {
                test:/\.css$/,
                use:["style-loader", "css-loader"]
            },
            // less需要的loader
            {
                test:/\.less$/,
                use:["style-loader", "css-loader", "less-loader"]
            }
        ]
    },
    // 配置所有的插件plugin
    plugins:[
        // HtmlWebpackPlugin插件默认情况下会创建一个空的html文件, 并自动将打包好的js文件通过script标签引入
        // 如果想要让HtmlWebpackPlugin将原有的html文件里的所有内容进行打包, 需要给该构造函数传入参数进行配置, 通过对象的template属性
        new HtmlWebpackPlugin({
            // 将src里的index.html文件里的内容拷贝给打包出来的html文件
            template:"./src/index.html",
            // 设置打包文件的文件名
            filename:"./home.html",
            // 压缩html代码
            minify: {
                // 清理空格
                collapseWhitespace:true,
                // 清理注释
                removeComments:true,
                // 压缩行内的css
                minifyCSS:true,
                // 压缩行内js
                minifyJS:true,
                // 删除内容为空的标签
                // removeEmptyElements:true
            }
        })
    ],
    mode:"development"
}

04_打包图片:

有些时候我们可能会直接在html文件里加载其他的资源, 如图片, 或者字体等
        默认情况下直接在html文件里加载的其他资源无法显示,我们也需要对应的loader来处理, 名字为: html-loader

        在webpack5之前, 处理静态资源需要使用到url-loader, file-loader, raw-loader这个loader, 但是目前这三个loader已经停止更新了, webpack5推荐我们使用asset module代替了这些loader

module:{
        rules:[
            {
                test:/\.css$/,
                use:[
                    "style-loader",
                    "css-loader"
                ]
            },
            // 通过webpack5的assetModule来处理图片资源
            {
                test:/\.(png|jpg|jpeg|gif)$/,
                // type值为asset的意思将文件单独导出或者导出为dataURL, 通过设置临界大小, 默认是8kb, 小于8kb的都会被转成dataURL
                type:"asset",
                parser:{
                    dataUrlCondition: {
                        // 小于100kb的全部转化为baseUrl, 没有实体文件了
                        maxSize:100*1024
                    }
                },
                // 设置实体静态文件的位置
                generator:{
                    // 命名方式[hash]:名字以哈希值表示
                    // 命名方式[ext]: 后缀与之前保持一致
                    filename: "static/[hash][ext]"
                }
            },
            // 处理html文件里的链接地址
            {
                test:/\.html$/,
                use:[
                    "html-loader"
                ]
            }
        ]
},

05_打包其他资源:

其他资源我们只需要将其原样输出即可, 并不需要压缩或者转为其他格式
webpack5里的其他资源的处理, 我们还是可以通过assetModule进行处理

// 通过assetMoudel处理字体资源
{
    test:/\.ttf|eot|woff$/,
    // 导出原样的文件
    type:"asset/resource",
    generator:{
         filename:"fonts/[name].[hash:8][ext]"
    }
}

06_开发者服务器:

webpack自带本地服务器, 我们可以通过devServer来启用

// webpack的开发者服务器, 需要先安装webpack-dev-server,
// 启动webpack-dev-server的命令: npx webpack-dev-server

const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
    entry:"./src/main.js",
    output:{
        filename:"build.js",
        path:resolve(__dirname, "dist")
    },
    module:{
        rules:[
            // 处理css资源
            {
                test:/\.css$/,
                use:[
                    "style-loader",
                    "css-loader"
                ]
            },
            // 处理less资源
            {
                test:/\.less$/,
                use:[
                    "style-loader",
                    "css-loader",
                    "less-loader"
                ]
            },
            // 处理图片资源 assetMoudel
            {
                test:/\.(png|jpg|gif|jpeg)$/,
                type:"asset",
                parser:{
                    dataUrlCondition:{
                        maxSize:100*1024
                    }
                },
                generator:{
                    filename:"images/[hash][ext]"
                }
            },
            // 处理其他资源
            {
                exclude:/\.(html|js|css|less|jpg|png|jpeg|gif)$/i,
                type:"asset/resource",
                generator:{
                    filename:"static/[name].[hash:6][ext]"
                }
            },
            // 处理html文件里的链接地址
            {
                test:/\.html$/,
                loader:"html-loader"
                // use:[
                //     "html-loader"
                // ]
            }
        ]
    },
    plugins:[
        new HtmlWebpackPlugin({
            template:"./src/index.html"
        })
    ],
    mode:"development",
    // webpack的开发者服务器, 需要先安装webpack-dev-server,
    // 启动webpack-dev-server的命令: npx webpack-dev-server
    devServer:{
        // 设置端口
        port:3000,
        // 自动打开
        open:true,
        // 自动启用gzip压缩
        compress:true,
        // 打开的文件目录
        static:resolve(__dirname, "dist")
    }
}

07_提取css文件:

// 提取css文件的plugin
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

// "style-loader",
// 如果想要单独抽离成css文件, 不能再用style-loader

// 抽离并生成css文件
        new MiniCssExtractPlugin({
            filename:"css/index.css"
        })

const {resolve} = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
    entry:"./src/js/main.js",
    output:{
        filename: 'js/build.js',
        path:resolve(__dirname, "build")
    },
    module:{
        rules:[
            // 处理css文件
            {
                test:/\.css$/,
                use:[
                    // "style-loader",
                    // 如果想要单独抽离成css文件, 不能再用style-loader
                    MiniCssExtractPlugin.loader,
                    "css-loader"
                ]
            },
            // 处理less文件
            {
                test:/\.less$/,
                use:[
                    // "style-loader",
                    // 如果想要单独抽离成css文件, 不能再用style-loader
                    MiniCssExtractPlugin.loader,
                    "css-loader",
                    "less-loader"
                ]
            },
            // 处理图片资源
            {
                test:/\.(png|jpg)$/,
                type:"asset",
                parser:{
                    dataUrlCondition:{
                        maxSize:100*1024
                    }
                },
                generator:{
                    filename:"images/[hash][ext]"
                }
            }
        ]
    },
    plugins:[
        // 打包生成html文件
        new HtmlWebpackPlugin({
            template:"./src/index.html"
        }),
        // 抽离并生成css文件
        new MiniCssExtractPlugin({
            filename:"css/index.css"
        })
    ],
    mode:"development"
}

08_css样式兼容:

// 处理less文件,同理处理css文件
{
    test:/\.less$/,
    use:[
       MiniCssExtractPlugin.loader,
       "css-loader",
       // css兼容性处理
       {
             loader:"postcss-loader",
             options:{
                 postcssOptions:{
                       plugins:[
                           "postcss-preset-env"
                      ]
                 }
             }
        },
        "less-loader"
     ]
}

09_压缩css文件:
// 压缩css文件的plugn
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin");

    // 压缩css文件也可以写到optimization
    optimization:{
        minimizer:[
           new CssMinimizerWebpackPlugin()
        ]
    },
    plugins:[
        new HtmlWebpackPugin({
            template:"./src/index.html"
        }),
        new MiniCssExtractPlugin({
            filename:"css/index.css"
        }),
        // new CssMinimizerWebpackPlugin()
    ],
    // mode:"development"
    mode:"production"

10_vue项目:

const { resolve } = require("path");
// 打包html文件的plugin
const HtmlWebpackPugin = require("html-webpack-plugin");
// 提取css文件的plugin
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// 压缩css文件的plugn
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const { VueLoaderPlugin } = require("vue-loader");

module.exports = {
    //入口文件
    entry: "./src/main.js",
    //出口路径
    output: {
        // 文件名
        filename: "build.js",
        // 出口位置
        path: resolve(__dirname, "dist")
    },
    //模式
    mode: "development",
    // 插件
    plugins: [
        new HtmlWebpackPugin({
            template: "./src/index.html",
            minify: {
                // 去除空格
                collapseWhitespace: true
            }
        }),
        new CleanWebpackPlugin(),
        new MiniCssExtractPlugin({
            filename: "[name].css"
        }),
        new VueLoaderPlugin(),
        new CssMinimizerWebpackPlugin()
    ],

    // loader配置
    module: {
        // 详细的loader配置, key为rules, 值是一个数组
        rules: [
            // 处理vue文件
            {
                test: /\.vue$/,
                loader: "vue-loader"
            },
            // 处理css
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    "css-loader",
                    // css兼容性处理
                    {
                        loader: "postcss-loader",
                        options: {
                            postcssOptions: {
                                plugins: [
                                    "postcss-preset-env"
                                ]
                            }
                        }
                    }
                ]
            },
            // 处理less
            // 处理图片
            // 处理其他资源
            // 处理行间的地址链接
            {
                test:/\.html$/,
                loader:"html-loader"
            },
            // 处理js
            {
                test:/\.js$/,
                // 不处理node_modules里的
                exclude:/node_modules/,
                use:{
                    loader:"babel-loader",
                    options:{
                        presets:["@babel/preset-env"],
                        plugins:["@babel/plugin-proposal-class-properties"]
                    }
                }
            }
        ]
    },
    // 配置开发服务器
    devServer: {
        static:"./dist",
        open:true,
        port:3000
    }
}

39. 微信小程序:

默认写法直接遍历数组名,不需要通过变量去声明数组里的值和下标
默认的数组的值的名字为item,下标默认为index

小程序建议,我们在通过wx:for进行渲染时,也要给每个渲染项添加唯一标识,通过wx:key设置,值有两种写法:
        1. 字符串/数字,建议使用循环数组里的item的某个唯一属性值
        2. 直接使用*this标识key值,*this直接代指数组里的item,但是这种写法要保证item的值是数字或字符串,如果是对象,不能这么用
<view wx:for="{{personArr}}" wx:key="id">{{item,index}}</view>
<view wx:for="{{scoreArr}}" wx:key="*this">{{item,index}}</view>


默认情况下在使用wx:for的时候,循环变量和下标的名字为item和index
如果想要重命名两个变量,需要分别通过wx:for-itemwx:for-index重新指定循环的变量名和下标名
 <view wx:for="{{personArr}}" wx:key="id" wx:for-item="v" wx:for-index="i">
 下标:{{i}},姓名:{{v.name}},价格:{{v.price}},数量:{{v.count}}
</view>

---------------------if---------------------

<view wx:if="{{len>5}}">1</view>
<view wx:elif="{{len>2}}">2</view>
<view wx:else>3</view>
block组件,能整体包裹需要的组件,但是不会最终渲染,和vue的templete组件一样
<block></block>

生命周期函数:
监听页面加载: onLoad()
监听页面初次渲染完成: onReady()
监听页面显示: onShow()
监听页面隐藏: onHide()
监听页面卸载: onUnload()
页面相关事件处理函数--监听用户下拉动作: onPullDownRefresh()
页面上拉触底事件的处理函数: onReachBottom()
用户点击右上角分享: onShareAppMessage()

     * 小程序里的组件分为两种:
     * 1. UI组件:只做单纯的数据展示,没有js逻辑
     *  该组件只需要wxml和wxss文件即可
     * 2. 副作用组件:完整的展示+JS逻辑
     *  所需要的文件与page一致,需要wxml,wxss,json,js四个文件
     * 
     * 展示一个完整的副作用组件,首先需要将其引入到使用的page里,在对应page的json文件里,通过usingComponents字段引入展示的组件
     * 
     * 展示一个UI组件,不能再用usingComponents方式引入
     *  1. 通过import组件引入
     *  2. 通过template组件引入

微信里绑定事件,只能写函数名,不能加()
    如果想要在触发事件的同时传递参数,需要设置以 data- 开头的自定义属性
    自定义属性在起名字的时候
    写法1: data-a
    写法2: data-aB
    写法3: data-a-b
    当属性被传入到dataset里时, key会变化, 会变成
    写法1: a
    写法2: ab
    写法3: aB

bindtap="btnClick1" data-a="{{personArr}}"  data-bgcCol="000" data-b-r="5"

微信小程序里的事件也是会传播的,而且可以通过不同的事件绑定方式来响应不同的事件流阶段,也可以通过不同的绑定方式来阻止事件的传播
    bind开头的绑定方式,事件是会冒泡的  bindtap="tap3
    catch开头的绑定方式,事件是不会冒泡的  catchtap="tap2"

如果想要使得元素触发事件流的捕获阶段,在绑定事件的事件前添加capture:即可响应捕获阶段                capture-bind:tap="tap6"

     * 微信小程序里声明组件绑定的事件函数,直接将函数声明在Page()的配置对象里即可
     * 
     * 事件触发之后传递来的数据,存储在事件对象的dataset

	btnClick2(m) {
		console.log(m);
		let i = m.currentTarget.dataset.m;
		console.log(i);
		this.data.personArr.splice(i, 1);
		/**
		 * 在微信你小程序里,修改data数据的方法是调用setData(),setData在微信小程序的逻辑层是同步的,但是在渲染层是异步的(为了减少渲染次数,提高渲染效率)
		 * 该方法接收两个参数:
		 * 参数1:对象,该对象里填写需要修改的data的键值对
		 * 参数2(可选的):回调,该回调会在异步渲染完毕之后触发,如果我们将来有一些逻辑需要保证在页面渲染完毕之后执行,可以写到第二个回调里
		 */
		this.setData({
			personArr: this.data.personArr,
			abc: Math.floor(Math.random() * 101)
		}, () => {
			console.log(this.data.personArr, this.data.abc);
		})
	},
	inputV(ee) {
		console.log(ee.detail.value);
		this.setData({
			v: ee.detail.value
		})
	},

animation动画:

    微信小程序里可以直接使用原生的css动画:过渡/关键帧
    transitionend 过渡的任何一个动画样式执行结果都会触发一次,而不是整个过渡动画执行结束时才触发
    animationstart:关键帧动画开始执行时触发
    animationiteration:关键帧动画开始一次新的执行时触发
    animationend:关键帧动画执行结束触发,如果设置infinite,该事件不会触发

<view class="v1" bind:transitionend="tranEnd"></view>
<view class="v2" bind:animationstart="aniStart" bind:animationiteration="aniIter" bind:animationend="aniEnd" ></view>


<view class="v3" id="v3"></view>
<button bindtap="goV3" >点击触发v3的小程序动画</button>

    // 触发小程序动画
    goV3(){
        // let aniData = wx.createAnimation().backgroundColor('yellow').export();
        // this.setData({
        //     v3Animation: aniData
        // });
        this.animate(".v3",[
            {opacity:1,rotate:0,backgroundColor:"yellow"},
            {opacity:.5,rotate:45,backgroundColor:"red"},
            {opacity:0,rotate:120,backgroundColor:"blue"}
        ],2000,()=>{
            console.log('动画执行结束');
            // this.clearAnimation('#v3', { opacity: true, rotate: true }, function () {
            //     console.log("清除了#v3上的opacity和rotate属性")
            //   })
        })
    },

-------------------页面通信---------------------
    微信你程序的页面通讯分两种:
    1. 页面(page)与组件(component)间相互通信
    2. 组件(component)与组件(component)间通信

    3.页面(page)与页面(page)之间相互通信

    页面通信的原理:
    1. 通过自定义属性+数据绑定的方式 实现:页面(父组件)向子组件传递数据 
    2. 通过自定义事件 实现:组件向父页面(父组件)传递数据
    3. 如果以上两种方法无法满足快速传值的需求,可以直接通过selectComponent方法直接拿到某个组件实例对象,类似于vue里的refs

    // 获取child9的data数据
    getC9Data(){
        let c9Obj= this.selectComponent("#c9");
        console.log(c9Obj);
        this.setData({
            comPer:c9Obj.data.c9Arr
        })
    },


    注意:   微信小程序的自定义事件的监听范围
    1. 自定义事件如果触发了一个自定义事件,引用了该组件的组件外部的地方,可以监听到自定义事件触发,除此之外,组件内部,以及该组件的父组件的内外均不可以监听到
    2. 自定义事件默认不冒泡,如果要在触发自定义时,让自定义事件冒泡,需要在调用triggerEvent时,传入第三个参数,{bubbles:true}
    3. 想要在自定义事件传播的同时,穿透到父组件内部,还是设置在调用triggerEvent时,设置第三个参数,{composed:true}

         getC4Data({detail}){
                // 自定义事件触发的函数,传递的数据存储在事件对象的detail属性中
         },

navigateTo   switchTab跳转

        wx.navigateTo({
          url: `../circleTwo/circleTwo?name=${this.data.index}`
        })
        // 不传参数,默认返回一层
        // wx.navigateBack();

        // 通过delta可以规定返回几层
        wx.navigateBack({
          delta: 2,
        })
        wx.switchTab({
            url: "../netWork/netWork"
        })

微信小程序的授权登录流程:
    1. 先通过wx.login()获取code(临时登陆凭证)
    2. 拿到code之后,先向我们的服务器发送请求,并携带code传递到后台
    3. 当服务器拿到code之后,需要向为微信服务器发起认证请求,并需要提供用户的三个凭证:appid(小程序的唯一标识),appsecret(小程序密钥),code(临时登陆凭证),还有一些额外参数
    4. 微信服务器进行验证,通过之后像我们的服务器返回session_key(回话密钥)和openid(用户的唯一标识)
    5. 我们的服务器拿到session_key和openid之后,自定义登陆状态(生成token),并返回给前端,前端拿到之后将其持久化存储
    6. 之后小程序在发起业务逻辑请求时,都携带token,用于用户的身份验证

40.react

React框架: FaceBook的开源库

React特点:

1.React是一个基于JavaScript的前端UI视图框架

2.虚拟DOM(Virtual DOM): 顾名思义不是真正的DOM结构, 它不需要浏览器API的支持, 其本质上是一个JS对象. 该对象其实就是在真正的DOM结构的基础上创建的一个抽象层, 完整反映了真实的DOM结构, 当页面的数据或者结构发生变化时, React会通过比较算法(diff算法)比较出上一次与本次虚拟DOM的不同, 仅仅将变化的部分同步到真实的DOM中

3.JSX语法(JavaScript XML), 可以让我们用JS的语法书写HTML

4.组件化

5.灵活(React推崇 all in JS)

React与Vue的区别???(面试题)

1. React里的数据是不可变的, react的数据流是单向的, 从上至下, 从父向子流动, 如果想要更新视图, 两步骤:1. 人为调用setState方法. 2. 当setState触发后, react会重新render虚拟dom, 让数据发生变化

2. 通过js来操作一切

3. 组件的类型比较多, class组件/function组件/高阶组件/样式组件/容器组件/副作用组件...

4. React的拓展性很强, 很多功能, 都交给了react社区

           1. 创建虚拟dom
           语法:
            React.createElement(参数1,参数2,参数3);
            参数1:节点名字
            参数2:节点的属性
            参数3及以后的参数:节点的内容

            例子:创建一个h2标签,设置id值为el2,设置内容"hello react"
           2. 将其渲染到容器app中 ReactDOM.render(newH2,app)
        */
        let newH2 = React.createElement("h2",{id:"el2"},"hello,react");
        console.log(newH2)

        let app = document.getElementById("app")
        ReactDOM.render(newH2,app)

react.js react的核心文件,支持react语法
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
react-dom.js 支持react有关dom的操作
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>

babel编译器, 正确编译jsx语法
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

<body>
    <div id="app"></div>
    <!--如果想要在script标签里书写jsx代码, 需要设置script标签的type属性, 值为: text/babel-->
    <script type="text/babel">
        /*
            jsx语法: 可以让我们利用js的语法书写html, 其本质就是React.createElement()的语法糖.
         */
        let ele = <div id="div1">
            <p className="p1">hi, react</p>
        </div>
        ReactDOM.render(ele, app);


        /*
            书写JSX语法时的注意事项
            1. JSX语法的结构可以被包裹在小括号里, 小括号也可以不写
            2. JSX语法结构里, 可以写注释, 但是注释比较特殊
            3. JSX的语法结构里, 可以通过{}进行声明式渲染, 在react里, react的编译环境是1个{}, 在{}里可以写变量, 表达式, 函数调用
            4. 如果JSX的语法结构里需要书写单标签, 单标签必须以/结尾, 不写会报错
            5. JSX语法结构里, 有且只能有一个根标签
        */
        let msg = "hello, JSX";
        let a = 10;
        let b = 20;
        ReactDOM.render(
            (<div className="wrap">
                {/*这是JSX里的注释*/}
                <p>msg的值是:{msg}</p>
                <p>a+b的值是:{a + b}</p>
                <input type="text" />
            </div>), app);

        // React里的列表渲染 类似于vue的v-for; React里没有指令, 所谓的列表渲染直接使用的是原生的数组或者对象的遍历方法, 这里为了兼顾数组或者对象的变量, 我们使用map方法

        let personInfo = [
            { id: "001", name: "张三", age: 20 },
            { id: "002", name: "李四", age: 18 },
            { id: "003", name: "王五", age: 22 },
            { id: "004", name: "赵六", age: 19 },
            { id: "005", name: "毛毛", age: 24 }
        ];
        // 遍历数组
        ReactDOM.render((
            <ul>
                {
                    personInfo.map((per, index) => {
                        if (per.age > 20) {
                            return <li key={per.id}>第{index + 1}个人, 姓名:{per.name}, 年龄:{per.age}</li>
                        } else {
                            return <p key={per.id}>第{index + 1}个人, 姓名:{per.name}, 年龄:{per.age}</p>
                        }
                    })
                }
            </ul>
        ), app);


        let obj = {
            name: "张三",
            age: 20,
            sex: "男",
            school: "黄淮学院",
            schoolNum: "112233"
        };
        // 遍历对象
        ReactDOM.render((
            <ul>
                {
                    Object.keys(obj).map((key, index) => {
                        return <li key={key}>key:{key}, value:{obj[key]}</li>
                    })
                }
            </ul>
        ), app);
    </script>
</body>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>04_设置css样式</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
    <style type="text/css">
        .eleColor {
            color: red;
        }
        .eleBgc {
            background-color: aquamarine;
        }
        .eleBorder {
            border: 2px solid;
        }
        .eleFontSize {
            font-size: 30px;
        }
    </style>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
    let app = document.getElementById("app");

    /*
        1. 如果通过class设置样式, 在JSX语法里的class属性名需要修改成className
    */
    ReactDOM.render((
        <div>
            <h2 className="eleColor">这是h2标签</h2>
            <p className="eleBgc eleBorder">这是段落标签</p>
            <div className="eleFontSize">这是div标签</div>
        </div>
    ), app);

    /*
        2. 如果要给JSX语法结构里的标签设置行间css样式, 依然适用style属性, 但是style的值不再支持字符串, 而是一个对象, 而对象必须写到react的编译环境里
    */
    ReactDOM.render((
        <ul style={{border:"1px solid red"}}>
            <li>列表项1</li>
            <li>列表项2</li>
            <li>列表项3</li>
        </ul>
    ), app)

    // 写法1
    let liStyle1 = {backgroundColor:"red", fontSize:"25px"};

    ReactDOM.render((
        <ul style={{border:"1px solid red"}}>
            <li style={liStyle1}>列表项1</li>
            <li>列表项2</li>
            <li>列表项3</li>
        </ul>
    ), app);


    // 写法2 需求: 每个li都需要一个边框, 但是背景色不一样
    let liBorder = {border:"1px solid blue"}
    let liS1 = {backgroundColor:"red"};
    let liS2 = {backgroundColor:"yellow"};
    let liS3 = {backgroundColor:"blue"};

    ReactDOM.render((
        <ul style={{border:"1px solid red"}}>
            <li style={{...liS1,...liBorder}}>列表项1</li>
            <li style={{...liS2,...liBorder}}>列表项2</li>
            <li style={{...liS3,...liBorder}}>列表项3</li>
        </ul>
    ), app);
</script>
</body>
</html>

组件介绍

React允许我们将UI拆分成独立的可复用的代码片段, 并对每个片段进行独立的构思, 我们将每个独立的片段称之为"组件"

        React里, 组件根据功能可以分为两大类:
        1. 函数级组件: 本质上就是一个函数, 将组件的模板结构return出来. 语法相对于class组件来说, 简单清晰. 但是因为其本质是一个函数, 导致其无法描述组件的生命周期, 一般仅仅作为展示类组件使用.

        2. class级组件: 本质上是一个类(ES6), 每一个组件都是类实例化的对象, 因为语法是面向对象的语法, 所以可以设置生命周期, 进行复杂的逻辑编写 

函数式组件的运行过程:
        1. React支持直接将函数名当做组件名使用
        2. 不管使用方式是函数调用还是组件名调用, 当React执行到时都会找到对应名字的函数, 然后对该函数进行调用
        3. 如果是函数调用语法, 可以直接向该函数里传入实参, 就可以将数据传入到组件内部
        4. 如果是组件名的语法, 想要向组件内部传值, 需要将传递的值以自定义属性的形式书写, 自定义属性可以写多个, 这些自定义属性全部会赋值给函数的第一个形参

    function Welcome(props){
        console.log("props的值是:", props);
        return <div>hello,{props.name}!!!</div>
    }
    // ReactDOM.render(Welcome(), app);
    // ReactDOM.render(<Welcome/>, app);


    // ReactDOM.render(Welcome({name:'reactDOM'}), app);
    ReactDOM.render(<Welcome name="reactDOM" age="20"/>, app);

 class组件的执行过程
        1. 当React执行到<Welcome1/>, 会去查找有没有同名的函数或者class
        2. 当找到名字为Welcome1的class时, 通过Welcome1这个class实例化(new)了一个对象
        3. 当对象实例化完毕之后, 会自动执行render方法, 将JSX语法返回
        4. 如果此时需要给组件传递参数, 还是通过自定义属性, 而且这些自定义属性全部会自动赋值给实例化的这个对的props属性, 这个props是内置的, 不需要声明

    // class组件
    class Welcome1 extends React.Component {
        // 在class组件里通过render方法返回JSX结构
        render(){
            return <div>hello,{this.props.name}, {this.props.sex}, {this.props.age}!</div>
        }
    }
    ReactDOM.render(<Welcome1 name="阿K" sex="女" age="59"/>, app);

React组件
                如果想要获取外界传入的数据,是通过自身的props获取的
                如果想要获取到传递个组件的内容部分的数据,是通过自身的props对象的children属性获取的

                注意:向组件内部传值时,自定义属性的名字不要叫children

React里绑定事件的语法:
                on事件名={触发函数} 注意:事件名首字母大写
            写法1: on事件名={this.函数名}
                特点:
                    1.1 不能传参
                    1.2 无法获取this
                    1.3 事件对象建议通过event获取
            写法2: on事件名={this.函数名.bind(this)}
                特点:
                    2.1 可以传递参数
                    2.2 也可以获取this,this,为当前组件实例
                    2.3 也可以直接通过event获取事件对象
            写法3: 通过constructor构造函数,一次性修改this指向
                特点:
                    3.1 无法传递参数
                    3.2 可以获取this
                    3.3 可以直接通过event获取事件对象
            写法4: on事件名={()=>this.函数名()}
                特点:
                    4.1 可以传递参数
                    4.2 可以获取this
                    4.3 可以通过event获取事件对象

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>03_事件</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
    <div id="app"></div>
    <script type="text/babel">
        /*
            React里绑定事件的语法:
                on事件名={触发函数} 注意:事件名首字母大写
            写法1: on事件名={this.函数名}
                特点:
                    1.1 不能传参
                    1.2 无法获取this
                    1.3 事件对象建议通过event获取
            写法2: on事件名={this.函数名.bind(this)}
                特点:
                    2.1 可以传递参数
                    2.2 也可以获取this,this,为当前组件实例
                    2.3 也可以直接通过event获取事件对象
            写法3: 通过constructor构造函数,一次性修改this指向
                特点:
                    3.1 无法传递参数
                    3.2 可以获取this
                    3.3 可以直接通过event获取事件对象
            写法4: on事件名={()=>this.函数名()}
                特点:
                    4.1 可以传递参数
                    4.2 可以获取this
                    4.3 可以通过event获取事件对象
        */
        class Child1 extends React.Component{
            // 每个组件实例都有一个构造函数constructor,该行会在组件实例化时触发,而且在组件的整个生命周期的过程中只触发一次,我们可以在该函数里做一些数据的初始化工作
            constructor(props){
                super(props);
                // 在组件初始化时,修改一次this的指向即可
                this.btn3Click = this.btn3Click.bind(this);
            }
            render(){
                return <fieldset>
                        <legend><h2>Child1组件</h2></legend>
                        <button onClick={this.btn1Click}>按钮1</button>
                        <button onClick={this.btn2Click.bind(this,100,"hello,点击事件")}>按钮2</button>
                        <button onClick={this.btn3Click}>按钮3</button>
                        <button onClick={()=>this.btn4Click(100,"abc")}>按钮4</button>
                    </fieldset>
            }
            btn1Click(e){
                let even = e || event
                console.log('按钮1点击事件触发',this,even,event);
            }
            btn2Click(a,s){
                console.log('按钮2点击事件触发',this,a,s,event);
            }
            btn3Click(){
                console.log('按钮3点击事件触发',this,event);
            }
            btn4Click(a,b){
                console.log('按钮4点击事件触发',a,b,this,event);
            }
        }
        ReactDOM.render(<Child1/>, app);
    </script>
</body>
</html>

React里的props和state
        props:react里的组件输入属性,类似于vue里的prop或者小程序里的properties,作用都是从外界接收传入到组件内部的数据并展示
            特点:
                1.props在函数组件里为第一个形参,在class组件里是内置的
                2.props本身只读,不可修改!
        state:react里的组件状态数据对象,类似于vue里的data或者小程序的data,作用为执行组件里的逻辑数据
            特点:
                1. state属性也是内置的,但是需要初始化
                2. state是读写性的
                3. state在修改的时候,为了让react重新渲染页面,所以不可以直接修改,需要调用setState方法修改

函数组件可以根据功能拓展出一些针对性较强的逻辑组件:

        // 1.代理组件(Proxy Component)
        // 项目中需要频繁使用type值为button的按钮

        // 2. 容器组件(Container Component),只接收数据用于展示,没有任何的逻辑功能

        3. 高阶组件(HOC:higherOrderComponent)
                具体而言,高阶组件是参数为组件,返回值为新组件的函数。接受的组件的类型可以是函数组件也可以是class组件

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title>05_函数组件拓展</title>
		<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
		<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
		<!-- react.dom -->
		<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
	</head>
	<body>
		<div id="app"></div>
		<script type="text/babel">
			/*
            函数组件可以根据功能拓展出一些针对性较强的逻辑组件
        */
        // 1.代理组件(Proxy Component)
        // 项目中需要频繁使用type值为button的按钮
        const Button = (props) => {
            return <button type="button" {...props}>{props.children}</button>
        }
        ReactDOM.render(<Button className="login" size="20">登录按钮</Button>,app)

        // 2. 容器组件(Container Component),只接收数据用于展示,没有任何的逻辑功能
        const NewsList = ({newsArr})=>(
				<ul>
                    {newsArr.map(n=> <li key={n}>{n}</li>)}
                </ul>
        )
        let newsArr = ["新闻1","新闻2","新闻3","新闻4","新闻5"]
        // ReactDOM.render(<NewsList newsArr={newsArr}/>,app)

        /* 
            3. 高阶组件(HOC:higherOrderComponent)
                具体而言,高阶组件是 参数为组件,返回值为新组件 的函数。接受的组件的类型可以是函数组件也可以是class组件
        */
        // 需求, 项目里有多个组件在初始化的时候需要从localStorage里获取数据,对自身的state进行初始化
        class MyComponent extends React.Component{
            state={
                info:localStorage.getItem("user") || ""
            }
            render(){
                return <div>用户的信息:{this.state.info}</div>
            }
        }

        // HOC
        const initLsData = (key) => (MyComponent) => {
            return class extends React.Component{
                constructor(props){
                    super(props);
                    this.state = {
                        info:localStorage.getItem(key) || "abc"
                    }
                } 
                render(){
                return <MyComponent info={this.state.info}></MyComponent>
            }
        } 
        }

        class Child1 extends React.Component{
            render(){
                return <div>Child1初始化的ls数据是:{this.props.info}</div>
            }
        }
        class Child2 extends React.Component{
            render(){
                return <div>Child2初始化的ls数据是:{this.props.info}</div>
            }
        }
        const LsComponent3 = initLsData("user")(Child1);
        const LsComponent4 = initLsData("token")(Child2);


        
        /* 
            通过"函数柯里化"来处理多次传值的高阶组件
                函数柯里化:通过函数调用后继续返回函数的方式,实现多次的接收参数并最后统一处理的函数编码形式
        */
       function sum(a){
           return (b)=>{
                return (c)=>{
                    return a+b+c
                }
           }
       }
       let res = sum(5)(6)(7)
       console.log(res);



        ReactDOM.render(<div>
            <LsComponent3></LsComponent3>
            <LsComponent4></LsComponent4>
            </div>,app)
    </script>
	</body>
</html>

react里父组件向子组件传值:props
            给子组件设置props

            <Child1 c1Msg={this.state.appMsg} c1Arr={this.state.appGoods}/>

 react里子组件向父组件传递数据,通过的是回调函数
            1. 给子组件设置一个自定义属性,值为一个函数

             <Child1 c1Callback={this.getC1Data.bind(this)}/>

              父组件接收函数:getC1Data(v){ }

              子组件点击传值事件:sendToApp(){
                            {/*核心代码*/}
                        this.props.c1Callback(this.state.c1Data)
              }

Content传值
        // Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
        // 为当前的 theme 创建一个 context,设置默认值为blue,(默认值的意思是当你什么也不传的时候起效果)。

        const ColorContext = React.createContext('blue');
        class App extends React.Component {
            render() {
                return <ColorContext.Provider  value="green">
                <Toolbar/>
                </ColorContext.Provider >
            }
        }

        function Toolbar(props) {
        return (
            <div>
                <ThemedButton/>
            </div>
        );
        }

        class ThemedButton extends React.Component {
            render() {
                return <Button/>
            }
        }

        class Button extends React.Component {
            // 获取ColorContext
            // 挂载在Button类里的contextType 属性会被ColorContext赋值的对象赋值,只有contextType被赋值后,我们才能用this.context语法来获取离我们最近的context的值
            static contextType  = ColorContext
            render() {
                console.log(this);
                return <button style={{color:this.context}}>按钮</button>
            }
        }

        ReactDOM.render(<App/>,app)

01_ref

	<body>
		<div id="app"></div>
		<script type="text/babel">
			/*
			新版的React需要我们通过React.createRef()来使用ref功能
			*/
		   class Child1 extends React.Component {
			   constructor(props) {
			       super(props);
				   //创建用户名ref
				   this.usernameRef = React.createRef();
			   }
			   //创建密码ref
			   passworkRef=React.createRef();
			   render(){
				   return <fieldset>
						<legend><h2>Child1组件</h2></legend>
						<input type="text" placeholder="用户名" ref={this.usernameRef}/>
						<input type="text" placeholder="密码" ref={this.passworkRef}/>
						<button onClick={()=>this.login()}>登录</button>
				   </fieldset>
			   }
			   login(){
				   //通过ref获取输入框的值
				   let un = this.usernameRef.current.value;
				   let pw = this.passworkRef.current.value;
				   console.log(un,pw)
			   }
		   }
		    ReactDOM.render(<Child1 />,app);
		</script>
	</body>

02_setState方法解析

面试题:
            React里的setState是同步的还是异步的?
            请你谈谈React里的setState方法.

setState存在的问题:

1.setState在更新数据的时候,有时候是同步更新,有时候是异步更新的

2.在React控制的事件处理函数中,以及React的生命周期函数中,setState总是异步更新数据的

3.在React控制之外的事件中,setState总是同步更新数据的

以后在使用setState时的注意点:

* 如果意识到setState是异步的,不要在调用完setState之后立刻获取state的值

* 也不要依赖state的当前值进行下一个值

* setState方法不总是立刻更新的,会在将来延迟或者批量更新

解决办法:

1.如果setState是异步更新想要在调用完setState之后获取到最新修改的值,我们需要给setState添加第二个参数,该参数是一个回调函数,在该函数里可以获取的state的值就是最新的

2.利用async和await也可以等待异步的执行完毕,进而获取最新的state

3.如果想要利用state的当前值计算state的值,setState的第一个参数不要在声明成对象,也声明成一个函数,函数里接受两个参数,当前的state和当前的props,我们利用这两个参数进行最新state值的计算

    /*
      面试题: ajax,axios,fetch三者的区别
     */
    //fetch: ES6之后推出的网络请求方式, 语法基于Promise, 号称ajax的替代, 代码结构上比ajax简单的多,但是, fetch的内部实现的网络请求核心对象不是XMLHttpRequest, 而是ES规范里的新的实现方式fetch不是一个插件,而是原生的JS实现的网络请求
    /*
       fetch的不足:
       1. fetch不自带cookie,需要设置credentials:"include",
       2. 兼容性不太好, ie不支持
       3. 不支持阻断请求
       4. 没办法监听请求进度
    */

            fetch("http://127.0.0.1:4567/fGet",{
                credentials:"include",  //自动携带cookie
            })
                .then(response=>{
                    // 发起fetch请求的第一个then获取到的是一个response对象,该对象里无法直接获取返回的数据, 但是该对象里有本次请求的状态数据,如:状态码,请求状态文本等; 如果想要获取到本次请求的数据,需要对该对象进行一次处理,并将处理的结果返回
                    console.log(response);
                    /*
                       处理response对象的方法
                       blob() 二进制对象
                       text() 文本值
                       formData() 表单数据对象
                       json() json解析的结果
                     */
                    return response.json();
                }).then(data=>{
                console.log(data)
            }).catch(error=>{
                console.log(error)
            })

        React组件的生命周期分为三个阶段(老版):
        1. 初始化阶段
            1. constructor()
            2. componentWillMount()
            3. render()
            4. componentDidMount()
        2. 更新阶段
            1. shouldComponentUpdate()
            2. componentWillUpdate()
            3. render()
            4. componentDidUpdate()
        3. 卸载阶段
            1. componentWillUnmount()

        以上生命周期比较重要的是
        1. render()
        2. componentWillMount()
        3. componentDidMount()

        需要理解的生命周期
        1.shouldComponentUpdate()
        2.render()

    class Child1 extends PureComponent {
        constructor(props) {
            super(props);
            // 组件的构造函数,最先触发的生命周期,在整个组件运行期间只走一次,我们经常在该函数里初始化state,或者修改函数的this指向或者做一些初始化操作
            console.log("Child1的constructor触发了")
            this.state = {
                c1Msg:"今天周三~"
            }
        }

        UNSAFE_componentWillMount() {
            // 组件实例创建完毕,将要被挂在之前触发
            console.log("Child1的componentWillMount触发了")
        }
        changeC1Msg(){
            this.setState({
                c1Msg:"今天是周三下午"
            })
        }
        render() {
            /*
                该方法用于渲染UI界面,该方法的注意事项:
                    1. 永远不要手动调用该方法
                    2. 绝大部分情况下返回的是jsx结构
                    3. 该方法除了在组件首次实例化时会调用之外,将来调用setState或者组件的父组件更新,该方法也会调用,重新渲染页面
                    4. 该方法也可以被阻止触发,可以利用shouldComponentUpdate()阻止组件重新渲染

             */
            console.log("Child1的render触发了")
            return <fieldset>
                <legend><h2>Child1组件</h2></legend>
                <p>msg的值是:{this.state.c1Msg}</p>
                <button onClick={()=>this.changeC1Msg()}>点击修改c1msg</button>
            </fieldset>

        }
        componentDidMount(){
            // 该方法在组件挂载完毕之后触发,我们一般会在此处发起网络请求
            console.log("Child1的componentDidMount触发了")
        }
        // shouldComponentUpdate(newPros,newState){
        //     // 该方法会在执行渲染之前被调用,默认有返回值,值为true,如果返回值为false,就不会继续执行render方法,相当于禁止了组件的数据更新
        //     console.log("Child1的shouldComponentUpdate触发了")
        //     /*
        //     我们将来可以在该方法里对频繁修改的变量做判断,提高react的渲染效率
        //     但是该方法在比较的时候,注意,如果是复杂数据类型,不建议比较
        //     如果懒得写shouldComponentUpdate,可以考虑将组建的继承由Component修改成PureComponent
        //      */
        //     if (this.state.c1Msg !== newState.c1Msg){
        //         return true
        //     }
        //     else {
        //         return false
        //     }
        // }
        UNSAFE_componentWillUpdate(){
            // 该方法在组件更新数据之前被调用
            console.log("Child1的componentWillUpdate触发了")

        }
        componentDidUpdate(){
            // 该方法在组件数据更新完毕之后被调用
            console.log("Child1的componentDidUpdate触发了")
        }
        componentWillUnmount(){
            // 该方法在组件将要被卸载的时候触发,我们可以在此时做一些收尾的事情:如关闭定时器,取消监听,暂停音频视频数据等
            console.log("Child1的componentWillUnmount触发了")

        }

    }

        React组件的生命周期分为三个阶段(新版):
        1. 初始化阶段
            1. constructor()
            2. getDerivedStateFromProps()
            3. render()
            4. componentDidMount()
        2. 更新阶段
            1. getDerivedStateFromProps()
            2. shouldComponentUpdate()
            3. render()
            4. getSnapshotBeforeUpdate()
            5. componentDidUpdate()
        3. 卸载阶段
            1. componentWillUnmount()

        以上生命周期比较重要的是:
        1. constructor()
        2. componentDidMount()
        3. componentWillUnmount()
        4. getDerivedStateFromProps()

        需要理解的生命周期
        1. shouldComponentUpdate()
        2. render()
        3. getSnapshotBeforeUpdate()

static getDerivedStateFromProps(newProps, newState){
            // 该方法是一个静态方法, 无法在其内部, 访问this, 该方法在render之前调用, 并且会在之后持续触发(组件接收到新的props或者调用了setState时, 或者触发强制更新时都会触发), 该方法会返回一个对象, 用来更新state, 如果返回null表示不需要更新任何内容
            console.log("Child1的getDerivedStateFromProps触发了!",newProps, newState);
            return {child1Data:newProps.c1Data};
}

路由跳转

import { Component,lazy,Suspense } from 'react';
import './BaseComponent.css';
import { Link,Route,withRouter,Redirect } from 'react-router-dom';
import Home from './Page/Home/Home';
// import Product from './Page/Product/Product';
// import Cart from './Page/Cart/Cart';
// import News from './Page/News/News';
// import Mine from './Page/Mine/Mine'
//我们可以利用React自身的懒加载功能,对import的组件进行懒加载,我们需要使用到React自带的lazy()方法和Suspense组件
const Product = lazy(()=>import("./Page/Product/Product"));
const Cart = lazy(()=>import("./Page/Cart/Cart"));
const News = lazy(()=>import("./Page/News/News"));
const Mine = lazy(()=>import("./Page/Mine/Mine"));


class BaseComponent extends Component{
    constructor(props) {
        super(props);
        this.state={
            id:1234,
            pName:"蛋糕",
            pPrice:333,
            score:[1,2,3,4,5]
        }
    }
    goCart(){
        // React里通过js切换路由,我们通过this.props的history的push方法来实现地址栏的路由切换;注意:默认情况下组件的props不会拥有history属性,如果想要让props内置上history,需要利用react-router-dom的withRouter方法,来包裹对应的组件,该方法会为包裹组件的props注入history
        // console.log(this.props.history)
        // this.props.history.push("/cart")
        this.props.history.push({pathname:`/cart/张三/20/${JSON.stringify(['桃子🍑','西瓜🍉',"草莓🍓"])}`})
    }
    render() {
        return (
            <div className="base">
                <header>
                    <Link to="/">首页</Link>
                    {/*<Link to={"/product"}>商品</Link>*/}

                    {/*query传值*/}
                    <Link to={{pathname:"/product",query:{id:this.state.id,pName:this.state.pName,pPrice:this.state.pPrice,score:this.state.score}}}>商品</Link>

                    {/*search传值*/}
                    {/*<Link to={{pathname:"/product",search:`?id=${this.state.id}&pName=${this.state.pName}&pPrice=${this.state.pPrice}&score=${JSON.stringify(this.state.score)}`}}>商品</Link>*/}

                    <button onClick={()=>this.goCart()}>购物车</button>

                    {/*state传值*/}
                    <Link to={{pathname:"/news",state:{id:111,name:"小花🔅",friend:{name:"小哈😄"}}}}>新闻</Link>
                    <Link to={"/mine"}>我的</Link>
                </header>
                <section>
                    <Suspense fallback={null}>
                        <Route path={"/"} exact render={()=><Redirect to={"/home"}/>}/>
                        <Route path={"/home"} component={Home}/>
                        <Route path={"/product"} component={Product}/>
                        <Route path={"/cart/:name/:age/:info"} component={Cart}/>
                        <Route path={"/news"} component={News}/>
                        <Route path={"/mine"} component={Mine}/>
                    </Suspense>
                </section>
            </div>
        )
    }
}
export default withRouter(BaseComponent);

React脚手架在发起网络请求的时候需要对请求做代理, 不然会跨域
    我们还是通过proxy代理来实现请求的转发
    1. 直接设置proxy, 在package.json里设置即可(proxy的值只能是字符串,不能写成对象进而设置多个代理)
    2. 如果需要设置多个代理, 我们只能使用插件 http-proxy-middleware
        2.1 安装
        2.2 在src目录下 新建一个setupProxy.js文件
        2.3 具体代码详见文件

    Redux 一种状态管理概念,用于应对前端愈发复杂的状态管理
    管理不断变化的 state 非常困难。如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,依次地,可能会引起另一个 view 的变化。直至你搞不清楚到底发生了什么。state 在什么时候,由于什么原因,如何变化已然不受控制。 当系统变得错综复杂的时候,想重现问题或者添加新功能就会变得举步维艰。


Redux里有三个概念:
    1. Action:本质上是一个JavaScript对象,作用是通过store告知reducers具体要如何操作数据,每个action对象必须拥有一个字段来做标识,我们一般将其命名为type

    2. Store:本质上也是一个对象,将action与reducers关联在一起,居中协调,该对象具有以下方法,用于具体操作redux:
        2.1 getState() 用于获取数据
        2.2 dispatch() 用于更新数据
        2.3 subscribe() 用于监听数据变化
        2.4 通过subscribe()的返回值取消监听

    3. Reducers:本质上是一个函数,而且其必须要声明称一个纯函数(其结果受到其参数的影响,除此之外不再受其他数据的影响),该函数真正操作数据,并返回操作后的结果

// store.js
import {createStore,combineReducers} from  'redux';
import goodsReducers from '../reducer/goods_reducer';
// combineReducers({goodsReducer,可以放好几个reducer,进行整合,然后再放进createStore})
export default createStore(goodsReducers);




// goods_reducer.js
import { ALL_GOODS, ADD_GOODS, DELETE_GOODS, UPDATE_GOODS } from '../action/goods_action';
// 初始化数据源
let initState = {
    carts:[
        {id:1, name:"山东大葱", price:5, count:10},
        {id:2, name:"青岛啤酒", price:6, count:5},
        {id:3, name:"东北冻梨", price:4, count:20}
    ]
};

// 初始化reducers
export default function (state=initState, action) {
    switch (action.type) {
        case ALL_GOODS:{
            return state;
        }
        case ADD_GOODS:{
            return {
                carts:[...state.carts, action.payload]
            }
        }
        case DELETE_GOODS:{
            return {
                carts:state.carts.filter(goods=>{
                    return goods.id !== action.payload.id;
                })
            }
        }
        case UPDATE_GOODS:{
            console.log(action.payload)
            return {
                carts: state.carts.map(goods=>{
                    console.log(goods)
                    return goods.id === action.payload.id ? action.payload : goods;
                })
            }
        }
        default:{
            return state;
        }
    }
}





// goods_action.js
// action的type值一般设置成常量, 用于标识action的行为
export const ALL_GOODS = "ALL_GOODS";
export const ADD_GOODS = "ADD_GOODS";
export const DELETE_GOODS = "DELETE_GOODS";
export const UPDATE_GOODS = "UPDATE_GOODS";

export function allGoods() {
    return {type:ALL_GOODS}
}
export function addGoods(id, name, price, count) {
    return {type:ADD_GOODS, payload:{id, name, price, count}}
}
export function deleteGoods(id) {
    return {type:DELETE_GOODS, payload:{id}}
}
export function updateGoods(id, name, price, count) {
    return {type:UPDATE_GOODS, payload:{id, name, price, count}}
}





//reactRedux
import { updateGoods,addGoods } from "../../../redux/actions/goods_action";
import Cart from "./Cart";
import { connect } from "react-redux"
import user from "../../../redux/reducers/user_reducer";

const mapDispatchToProps = (dispatch)=>{
    return {
        updatePre:(id, name, price, count)=>{
            dispatch(updateGoods(id, name, price, count))
        },
        addPre:(id, name, price, count)=>{
            dispatch(addGoods(id, name, price, count))
        }
    }
}

const mapStateToProps = (state)=>{
    return {
        user:state.user,
        carts:state.goods.carts
    }
}

const CartContainer = connect(mapStateToProps,mapDispatchToProps)(Cart)
export default CartContainer

<Route path={"/reactRedux/home"} component={HomeContainer}/>

 {/*Provider包裹组件的范围都可以获取到store实例,进而去获取数据或者派发事件方法*/}
      <Provider store={Store}>
          <App />
      </Provider>

hook组件:

1.useState

2.useEffect

3.useLayoutEffect

4.useRef

5.useCallback&&useMemo

6.useContext

1.useState讲解

        ①可以让我们在函数级组件里声明数据源,修改数据源并且让页面重新渲染

        ②该方法返回的是一个数组,数组里有两个值,一个是声明的变量值,一个是修改该变量的函数体

    let [name,setName] = useState('张三');
    const addCount = ()=>{
        setCount(count+1)
    }

2.useEffect

import {useEffect,useState} from 'react'
const Effect = () =>{
    const [count,setCount] = useState(0);
    let [name,setName] = useState('张三');

    useEffect(()=>{
        // console.log("useEffect触发了!")
        console.log("count发起网络请求")
        console.log("name发起网络请求")
        // setCount(count+100);
        // setName("李"+Math.floor(Math.random()*(20-5+1)+5))
        return () => {
            // console.log("useEffect的返回值函数触发了")
        }
    },[name])
    return (
        <div className={'hookStyle'}>
            <h2>useEffect讲解</h2>
            <p>useEffect可以让我们在组件里执行副作用操作,可以把 useEffect Hook 当做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个生命周期的组合</p>
            <p>useEffect的回调函数会在组件挂载完毕时与页面渲染完毕时触发;该回调函数的返回值也是一个函数,这个函数会在页面渲染之前触发与组件卸载之前出发</p>
            <p>
                如果想要useEffect只响应挂载阶段,其返回值函数只响应将要卸载阶段,可以给useEffect添加第二个参数,把该参数设置成[]即可
            </p>
            <p>
                而且可以在数据里填写变量让useEffect选择性监听变量
            </p>
            <hr/>
            <p>count的值是:{count}</p>
            <button onClick={()=>setCount(count+1)}>点击修改count</button>
            <p>name是:{name}</p>
            <button onClick={()=>setName("张"+Math.floor(Math.random()*(10-5+1)+5))}>点击修改name的值</button>
        </div>
    )
}
export default Effect

3.useLayoutEffect

import {useState, useEffect, useLayoutEffect} from "react";

const Layout = () => {
    const [value, setValue] = useState(0);
    // useEffect(()=>{
    //     if(value === 30){
    //         let i = 0;
    //         while (i<100){
    //             i++
    //         }
    //         setValue(600)
    //     }
    // })
    useLayoutEffect(()=>{
        if(value === 30){
            let i = 0;
            while (i<100){
                i++
            }
            setValue(600)
        }
    })
    return (
        <div className={'hookStyle'}>
            <h2>useLayoutEffect</h2>
            <p>功能与useEffect相似, 将来能用useEffect就用useEffect, 如果使用useEffect的时候出现了问题, 可以试着改成useLayoutEffect</p>
            <p>两者的区别是, useEffect的触发时机是在浏览器渲染完成之后, 而useLayoutEffect是在浏览器把内容渲染到界面之前触发; useEffect的执行是异步的, useLayoutEffect是同步的</p>
            <hr/>
            <h4>value的值是: {value}</h4>
            <button onClick={() => setValue(30)}>点击修改value值</button>
        </div>
    )
}
export default Layout;

4.useRef

import { useRef,useEffect,useState,useLayoutEffect } from "react"
const Ref = () =>{
    const usernameRef = useRef();
    const passwordRef = useRef();
    const useLogin = () => {
        console.log(usernameRef.current.value,passwordRef.current.value)
    }
    return (
        <div className={'hookStyle'}>
            <h2>useRef讲解</h2>
            <p>useRef可以让我们行使之前createRef相似的功能,我们可以利用其快速找到元素</p>
            <fieldset>
                <legend><h2>登录模块</h2></legend>
                <input type="text" placeholder={"用户名"} ref={usernameRef}/>
                <input type="text" placeholder={"密码"} ref={passwordRef}/>
                <button onClick={useLogin}>登录</button>
            </fieldset>
        </div>
    )
}
export default Ref

5.useCallback&&useMemo

import { useState,useCallback,useMemo,memo } from "react";
const ParentComp = () => {
    const  [count,setCount] = useState(0);
    const  [name,setName] = useState('hello,parent!');
    const [age,setAge] = useState(0);
    const increment = () => {
      setCount(count+1)
    }
    // const changeName = (newName) => {
    //     setName(newName)
    // }

    // const info = {name,age}

    // 用useCallback将changeName函数进行缓存
    const changeName = useCallback((newName) => {
        setName(newName)
    },[])

    // 用useMemo来将对象缓存,使其指向同一个地址
    const info = useMemo(()=>({name,age}),[name,age])

  return (
      <div>
          <button onClick={increment}>点击次数:{count}</button>
          <ChildComp name={name} onClick={changeName} perInfo={info}/>
      </div>
  )
}
let ChildComp = (props) => {
    console.log("子组件被渲染了....")
  return (<div>
      <h2>这是子组件ChildComp,{props.name}</h2>
      <button onClick={()=>props.onClick("hi,I am child")}>点击修改name的值</button>
      <p>info的信息:{props.perInfo.name},{props.perInfo.age}</p>
  </div>)
}
ChildComp = memo(ChildComp)
const CallbackMemo = () =>{
    return (
        <div className={'hookStyle'}>
            <h2>useCallback讲解&&useMemo讲解</h2>
            <p>useCallback和useMemo一般配合使用,用于解决React hook产生的无用渲染,提高性能</p>
            <p>默认情况下,当父组件的数据发生了变化,父组件会重新渲染,但是此时,也会导致其内部的子组件重新渲染,这是没有必要的,此时可以用React的memo方法来解决,memo是一个函数,该函数调用的时候可以传入一个组件,结果返回一个新的组件,返回的新组件会与传的组件的参数进行浅比较,只有当参数发生变化时,才会重新渲染该组件</p>
            <p>但是memo整个方法只能对参数进行浅比较,如果传入了非基本数据类型,那么memo在浅比较时,依然会认为不等,那么依旧重新渲染</p>
            <p>useCallback传入一个函数,返回一个新的函数,在内部useCallback会将传入的函数缓存</p>
            <p>当给子组件传入的参数是一个对象或者数组时,因为父组件改变会引起其重新渲染,那么每次都会传递给子组件一个新的数组或者对象,而在JS里,数组和对象进行的是地址比较,所以每次都会认为不等,进而还会导致子组件每次重新渲染</p>
            <p>通过useMemo,useMemo传入一个对象,返回的对象永远指向同一个地址</p>
            <ParentComp/>
        </div>
    )
}
export default CallbackMemo

6.useContext

import {useState,useContext,createContext} from "react";
// 1.创建一个context
const ContextNum = createContext()

const Item1 = () => {
  return (<fieldset>
      <legend><h2>这是Item1组件</h2></legend>
      <Item2/>
  </fieldset>)
}
const Item2 = () => {
    return (<fieldset>
        <legend><h3>这是Item2组件</h3></legend>
        <Item3/>
    </fieldset>)
}
const Item3 = () => {
    // 3.通过useContext获取数据
    const num = useContext(ContextNum)
    return (<fieldset>
        <legend><h4>这是Item3组件</h4></legend>
        <p>传入的num值是:{num
        }</p>
    </fieldset>)
}

const Context = () =>{

    const [num,setNum] = useState(0);
    return (
        <div className={'hookStyle'}>
            <h2>useContext讲解</h2>
            <p>用于跨组件树之间的数据传递,让我们不必一层一层的传递数据</p>
            {/*2.通过context的Provider包裹想要传递数据的组件树的根组件*/}
            <ContextNum.Provider value={num}>
                <Item1/>
            </ContextNum.Provider>
            <p>num的值:{num}</p>
            <button onClick={()=>setNum(num+1)}>点击修改num</button>
        </div>
    )
}
export default Context

JS跳转新闻组件

    let navigateNews = useNavigate()
    const goNews = () => {
        // search方式传值
        // navigateNews("/news?name=张三&age=20")

        // params方式传值
        // navigateNews(`/news/${name}/${age}`)

        // state方式传值
        navigateNews("/news",{state:{id:111,name:"小花🔅"}})
    }

<Suspense fallback={null}>
        {/* 6版本里面, Route组件必须包裹在Routes组件里 */}
        <Routes>
             {/* 在6版本里, Route组件没有component属性, 要用element属性代替, 值是组件形式 */}
             {/*重定向没有Redirect, 用element属性代替, 值是组件形式*/}
             <Route path={'/'} element={<Navigate to={'home'}/>}/>
             <Route path={'/home'} element={<Home/>}/>
             <Route path={'/product'}  element={<Product/>}/>
             <Route path={'/mine'}  element={<Mine/>}>
             {/* router6里的路由嵌套直接把二级路由对应的Route组件写到一级路由的Route组件里面, 不再写到对应的一级路由的页面里 */}
             {/* 路由重定向 */}
             {/*<Route path={''} element={<Navigate to={'homepage'}/>} />*/}
             {/* 或者index属性表示默认显示的二级路由, 注意: 使用index, 不要加path, 不然无效 */}
             <Route index element={<Homepage />}  />
             <Route path={'cart/:userId/:userCarts'} element={<Cart />} />
             <Route path={'address'} element={<Address />} />
             </Route>
             <Route path={'news'} element={<News />} />
             /*<Route path={'news/:name/:age'} element={<News />} />*/}
         </Routes>
</Suspense>

获取search方式传递来的数据

import "./Product.css";
import { useSearchParams } from 'react-router-dom';

const Product = () => {
    // 获取search传来的数据,我们先通过useSearchParams获取到searchParams对象,该对象有一系列的方法,可以操作传递来的数据
    const [searchParams] = useSearchParams();
    console.log(searchParams)
    // 1. searchParams.forEach()遍历所有的search参数及值
    searchParams.forEach(v=>{
        console.log(v)
    })
    // 2. searchParams.get("key") 获取某个键值对的值
    let sName = searchParams.get("name");
    let sAge = searchParams.get("age");
    let sClasses = JSON.parse(searchParams.get("classes"))
    console.log(sName,sAge,sClasses)

    // 3. searchParams.has("key") 判断参数里是否有对应的键值对,返回值是布尔
    console.log(searchParams.has("name"))
    console.log(searchParams.has("age"))
    // 4. searchParams.keys() 得到参数的所有key的集合,是个迭代器,我们可以遍历
    console.log(searchParams.keys())
    let obj = searchParams.keys();
    // console.log(obj.next())
    // console.log(obj.next())
    // console.log(obj.next())
    // console.log(obj.next().value)
    // console.log(obj.next().value)
    // console.log(obj.next().value)
    for (let k of obj){
        console.log(k)
    }

    // 5. searchParams.values() 得到参数的所有的value的集合,是个迭代器
    let vObj = searchParams.values();
    for (let k of vObj){
        console.log(k)
    }

    /* 6. 其他方法:
        searchParams.append(),
        searchParams.delete(),
        searchParams.getAll(),
        searchParams.set(),
        searchParams.sort(),
        searchParams.toString()
     */


    return (
        <div className={"product"}>
            <h2>首页组件product</h2>
            <hr/>
            <p>姓名:{sName},年龄:{sAge}</p>
            <h3>所学课程</h3>
            {
                sClasses.map(v=>{
                    return <span key={v}>{v}--</span>
                })
            }
        </div>
    )
}
export default Product

//获取params方式传递来的数据
    const cartData = useParams();

state:

let stateData = useLocation()
    console.log(stateData)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小坚果_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值