设计思路:用父盒子包含四个子盒子,父盒子使用相对定位,子盒子使用绝对定位,此时这四个盒子会全部定位在父盒子的左上角,统一对四个子盒子设置关键帧动画,使得子盒子会围绕父盒子四个角依次移动,然后将四个盒子的动画延迟时间(即animaiton-delay属性)设置为总动画时间的0(第一个不用设置),-1/4s, -2/4s, -3/4s,这里设置为负数为了让在显示的时候,其他三个圆提前位于父盒子的其余角位置。
使用render-props模式和高阶组件实现组件复用
props-render模式
1 | import React from 'react'; |
Mouse组件的render属性必须是一个函数,且可以用childre属性来代替,这样的话可以将函数直接作为Mouse组件的一个文本节点(文本节点拥有props.children,而不用作为Mouse的属性。
高阶组件
使用步骤:
1、创建一个函数,名称约定以with开头;
2、指定函数参数,参数应该以大写字母开头(作为要渲染的组件)
3、在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
4、在该组件中,渲染参数组件,同时将状态通过props传递给参数组件
1 | import React from 'react'; |
组件的生命周期
组件的生命周期可分为挂载、更新和卸载三个阶段,其中还包含有对错误的处理
挂载
当组件实例被创建并插入到DOM中,其生命周期调用顺序如下:
1 | constructor() |
1 | static getDrivedStateFromProps() |
1 | render() |
1 | componentDidMount() |
更新
当组件的props和state发生改变时会出发更新。组件更新的生命周期调用顺序如下:
1 | static getDerivedStateFromProps() |
1 | shouldCompnentUpdate() |
1 | render() |
1 | getSnapshotBeforeUpdate() |
1 | componentDidUpdate() |
卸载
当组件从Dom中移除时会调用以下方法:
1 | componentWillUnmount() |
错误处理
渲染过程、生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:
static getDrivedStateFromError()
componentDidCatch()
CSS常见布局
一、两栏布局,右侧自适应
方法一:使用flex布局
将box父盒子的display属性设置为flex,左栏的宽度按需设计,右栏添加flex:1属性
关于flex:1的解释
flex :1 是flex-grow:1 flex-shrink: 1 flex-basis:0的缩写。
flex-grow:1 将自身的宽度扩大2倍,如果其余的同级盒子没有设置该属性,最后一个盒子如果设置该属性后则会自动填满父盒子。
flex-shrink:1 将自身的宽度缩小为原来的二分之一,如果第一个子盒子的宽度特别大,超出的父盒子的宽度,剩余盒子设置flex-shrink:1 属性之后,会自动缩小自身的宽度来适应前面的盒子宽度。
flex-basis:0(这个属性不是很明白能干嘛)
方法二:使用float布局
1、使用flaot左浮左边栏
2、右边栏用margin-left隔开
3、为父元素田间BFC,防止下方的元素飞到上方内容
关于BFC的解释
BFC是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素
一个HTML元素要创建BFC,则满足下列的任意一个或多个条件即可:
1、float的值不是none。
2、position的值不是static或者relative。
3、display的值是inline-block、table-cell、flex、table-caption或者inline-flex
4、overflow的值不是visible
创建一个BFC的通常使用 overflow:hidden来创建
应用场景
1、属于同一BFC的两个相邻块元素的margin会发生重叠,要避免这种情况,可以新创建一个BFC,让这两个相邻块元素处于不同的BFC;
2、当父盒子的宽度没有设置的时候,设置父盒子的背景颜色是无法显示的,即使父盒子里面有子盒子占据空间,这时候给父盒子设置一个BFC,父盒子就可以显示了。
3、对于浮动元素,可能会造成文字环绕的情况(Figure1),但这并不是我们想要的布局(Figure2才是想要的)。要解决这个问题,我们可以用外边距,但也可以用BFC。
4、在多列布局中使用BFC
如果我们创建一个占满整个容器宽度的多列布局,在某些浏览器中最后一列有时候会掉到下一行。这可能是因为浏览器四舍五入了列宽从而所有列的总宽度会超出容器。但如果我们在多列布局中的最后一列里创建一个新的BFC,它将总是占据其他列先占位完毕后剩下的空间。
如果没有在最后一个设置BFC,则最后一个会掉下来落在第一个盒子的下面。
二、三栏布局中间自适应
方法一:使用flex布局
为父盒子添加display:flex,设置左右两边盒子的宽度,中间盒子设置flex:1或者width:100%即可架构剩下的空间填满。
方法二: 两边使用float,中间使用margin
注意:中间的盒子要放在最后面,否则右边的盒子会掉下来
缺陷:右边在主题内容之前,如果是响应式布局,则不能实现简单的换行展示。
方法三:两边使用绝对定位,中间使用margin
和flaot布局相似
方法四: 使用grid布局
ES6中新增扩展一(数组篇)
一、扩展运算符
核心概念:将数组变成参数序列
1、可用于函数调用时将一个数组变成参数序列
1 | function push(array, ...item){ |
2.将某些数据结构转化为数组
1 | [...document.querySelectorAll('div')] |
3、数组的合并
1 | let arr1=[2,3,4,5]; |
4、与解构赋值结合起来使用
注意:扩展运算符自能放在参数的最后一个位置,否则会报错
1 | const [first, ...rest] = [1,2,3,4,5,6]; |
5、将字符串转化为数组
1 | console.log(...'javascript'); // j a v a s c r i p t |
可见如果扩展运算符后面不是数组而是字符串的时候也可以正常扩展
二、数组的构造函数新增方法
1、Array.from
将类似数组的对象和可遍历的对象(iterable)包括Set和Map转化成数组
1 | let arrayLike = { |
注意,如果key从0开始,如果key值得存在,则生成的数组对应的index处的值为undefined
2.Array.of()
将一组值转换为数组
1 | console.log(Array(2,3,4)); //[ 2, 3, 4 ] |
三、实例对象新增方法
1.copyWithin()
将指定位置的数组元素复制到其他的位置,返回覆盖后的新数组
1 | let arr= [3,4,9,5,6,7]; |
第一个参数表示要开始替换的数组索引号,第二个参数表示从那个位置开始复制数组,最后一个表示那个位置结束(但是不包括这个位置的值)
2.find()
返回第一个符合条件的数组元素,find的参数是一个函数,函数参数可包含(value,index,array)
3.findIndex()
返回第一个符合条件的数组元素对应得索引号,如果所有的元素都不符合条件,则返回-1.
4.fill()
将给定值作为函数的参数,覆盖原有数组的值,可接受第二个和第三个参数,表示覆盖的初始位置和终始位置(不包含终始位置)
1 | let arr=[1,2,3,4]; |
5、flat()
将数组扁平化处理,返回一个新数组,对原数组没有影响
1 |
|
flatMap()
对每个数组成员执行map方法,然后再将返回的结果执行flat方法
1 | let arrs = [3,3,4,6,7,8]; |
函数集锦
用原生JS实现apply/call/bind
apply、call和bind的区别
1、相同点:
这三个的作用就是改变他们前面的函数执行时的上下文,即改变函数运行时的this指向,第一个参数都表示是this的指向,在this为null或者为undefined的时候,this默认指向window。
2、不同点:
apply的第二个参数是函数执行时传入的参数,apply时以数组的方式传入,一次传多个参数,只传一次,且函数是立即执行。
call 的第二个参数在函数执行的时候以参数列表的形式传入,一次传多个参数,只传一次,函数时立即执行的。
bind的第二个参数是动态传递参数,可以分多次传,但不会立即执行, 且会返回一个函数。
1 | fn.bind(obj,1,2); |
实现apply、call和bind
简单说一下他们的实现原理,fn.call(o) 它的实现原理就是o上定义一个新的属性m,m的值为要执行的这个函数fn,
即 :o.m=fn; 执行这个函数 fn(); 最后删除m这个属性。
那么这个函数是怎么获取呢,可以通过this来获取,this指向这个调用的函数fn
Function.prototype.apply()
1 | Function.protoptype.apply=function(context=window, args){ |
Function.prototype.call()
1 | //传入的参数是参数离列表 |
Function.prototype.bind()
1 |
对象是怎么new出来的
以前对通过new一个构造函数生成实例对象不是很理解,今天终于弄明白了,嘿嘿。
首先要明白实例对象生成的过程中它经过了哪些步骤。
1、它既然是一个对象,所以要先创建一个空对象。
1 | const obj={} |
2.实例对象可以使用构造函数原型上的属性和方法,所以要将实例对象的__proto__指向构造函数的原型
1 | obj.__proto__ =Func.prototype; |
3、实例对象可以访问到构造函数的属性,所以要将构造函数的this绑定到实例对象上,让this.name 变成obj.name(举个栗子)
1 | let result = Func.apply(obj, args) //args为构造函数的参数 |
现在这个空对象就可以访问到构造函数的构造函数原型上的所有属性和方法。
1 |
|
4、构造函数没有return语句,则需要把新创建的对象给返回回去(这里补充一个知识点,构造函数的返回值如果是原始类型的值,则直接被忽略,如果返回的是对象,则创建的实例对象具有的属性是返回对象的属性,并不具有构造函数没有返回的属性),所以我们要把所有的属性给返回回去;
1 | return result instaneOf Object ? result: obj; |
最后,附上完整new 函数代码
1 | function myNew(Func, ...args){ |
表单和控件
获取表单
1 | documen.forms.my; //my是form的name属性值 |
获取表单里的元素
form.element.one; //one为表单元素中的name属性值
也可以直接通过form.name直接获取
通过元素反向引用表单
1 | name.form // name为元素的name属性值 |
表单控件
input 和textarea
我们可以通过 input.value
(字符串)或 input.checked
(布尔值)来访问复选框(checkbox)中的它们的 value
。
select和option
select 有三个重要的属性:options、value、selectedIndex
select.options
——select.value
—— 当前所选择的select.selectedIndex
—— 当前所选择的
1 | <select id="select"> |
表单的focus和blur事件
focus事件可用于再input中输入内容时使用,blur事件用于鼠标离开input,并点击了页面的其他地方。
tabindex属性
tabindex属性可以将不支持聚焦得元素变成可聚焦的元素,它的值为可以为-1 0 ,1,2,3。。。
且在按tab键去遍历的时候,tabindex的优先级会大于可聚焦元素(input a button),tabindex为0和聚焦元素同级。
前端知识点笔记
label标签的 for 属性
for 属性规定 label 与哪个表单元素绑定。 for属性的值就是要绑定元素的id。
在html中,
JSdocument.execCommand 实现复制功能
点击button,复制input的内容
1 | input.select(); //select() 方法用于选取文本域中的内容。 |
箭头函数只有行的时候如果要加上{}则必须用return返回
赋值和对象的转换
1 | let i=1; |
value和innerHtml的使用场景
对于表单控件(输入类),都可以用value属性,对于非表单控件(div, span ,em),可以用innerHTML;
input标签都有value属性,但都没有innerHTML属性,只能用value;
SELECT标签和OPTION标签,即有value属性也有innerHTML属性,但是一个是取回值,一个是取回文本,这两个可能相同也可能不同,具体要看你想要哪个值.
TEXTAREA标签也没有innerHTML属性,有value属性和innerText属性.
padStart 和padEnd函数
可用于自动编号的补0操作
1 | let a= '1'; |
验证码输入一个数字后自动转移光标到下一个输入框,按回车键后自动将光标转移到前一个输入框
1 | const inputs = document.querySelectorAll('input'); |
将一个数字范围映射到另一个数字范围
1 |
|
Location reload()
eload()方法用于刷新当前文档。
reload() 方法类似于你浏览器上的刷新页面按钮。
如果把该方法的参数设置为 true,那么无论文档的最后修改日期是什么,它都会绕过缓存,从服务器上重新下载该文档。这与用户在单击浏览器的刷新按钮时按住 Shift 健的效果是完全一样。
1 | <button onclick="location.reload()">Reload</button> |
contextmenu事件
当按下鼠标右键时触发
1 | li.addEventListener('contextmenu' ,(e)=>{ |