前端css、js、react

172 阅读12分钟

css篇

  • 一、CSS中容易被忽视的 position属性sticky

  • 为什么要学习position:sticky

在开发移动端app时,经常会碰到需要这样一种情况 —— 网站滚动到一定高度的时候,让一部分内容作为navbar,也就是置顶显示,我们一般会使用js监听scroll事件来实现,但是新增的css属性position:sticky可以简单实现

  • 用法
{
     position: 'sticky',
          top: 0
}
  • 直接拿去玩~~~😂

例子:

image.png

image.png 兼容性: image.png

  • 二、css BFC

BFC 全称:Block Formatting Context, 名为 "块级格式化上下文"。

W3C官方解释为:BFC它决定了元素如何对其内容进行定位,以及与其它元素的关系和相互作用,当涉及到可视化布局时,Block Formatting Context提供了一个环境,HTML在这个环境中按照一定的规则进行布局。

简单来说就是,BFC是一个完全独立的空间(布局环境),让空间里的子元素不会影响到外面的布局。那么怎么使用BFC呢,BFC可以看做是一个CSS元素属性

出发BFC的条件:

  • overflow: hidden
  • display: inline-block
  • position: absolute
  • position: fixed
  • display: table-cell
  • display: flex

BFC规则

  • BFC就是一个块级元素,块级元素会在垂直方向一个接一个的排列
  • BFC就是页面中的一个隔离的独立容器,容器里的标签不会影响到外部标签
  • 垂直方向的距离由margin决定, 属于同一个BFC的两个相邻的标签外边距会发生重叠
  • 计算BFC的高度时,浮动元素也参与计算

解决了什么问题

  • 父级元素内的子元素进行float浮动后父元素高度塌陷,此时父级设置颜色会失效。给父级设置display:inline-block;

  • 三、css 垂直水平居中

  • 1、行内元素

{
 text-align:'center'
}
  • 2、块元素

flex布局

    justify-content: center;
    align-items: center;
.box {
    width: 200px;
    height: 200px;
    border: 1px solid red;
    display: flex;
    justify-content: center;
    align-items: center;
    }
.children-box 
  {
    width: 100px;
    height: 100px;
    background: yellow;
  }

绝对定位(两种)

.box {
    width: 200px;
    height: 200px;
    border: 1px solid red;
    position: relative;

}
.children-box {
    width: 100px;
    height: 100px;
    background: yellow;
    position: absolute;
    left: 50%;
    top: 50%;
    margin: 0 auto;
    transform: translate(-50%, -50%); //定义2d旋转位置 
}
.box {
    width: 200px;
    height: 200px;
    border: 1px solid red;
    position: relative;

}
.children-box {
    width: 100px;
    height: 100px;
    background: yellow;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    margin: auto ;
}
  • 四、CSS盒模型

juejin.cn/post/702588…

  • 1、 W3C 标准盒模型:

属性width,height只包含内容content,不包含border和padding。

content-box(标准盒模型): width = 内容的宽度 height = 内容的高度

    1. IE 盒模型:

属性width,height包含border和padding,指的是content+padding+border。

border-box(IE盒模型):width = border + padding + 内容的宽度;height = border + padding + 内容的高度。

  • JS篇

  • 表单校验金额

<Form.Item
    label="支付金额"
    name="paymentAmount"
    rules={[{ required: true, message: '请输入支付金额!' },
    { pattern: new RegExp(/^\d{1,9}(\.\d{1,2})?$/), message: '支付金额最多9位整数,2位小数!' }]}>
    <InputNumber placeholder='请输入支付金额' maxLength={40} min={0} style={{ width: 200, height: 36 }} step={0.01}/>
</Form.Item>
  • js事件循环机制 eventLoop

console.log('script start');

setTimeout(function() { console.log('setTimeout'); }, 0); 

Promise.resolve().then(function()
{
 console.log('promise1');
}).then(function() {
console.log('promise2'); 
});

console.log('script end');
  • 正确答案是:script start, script end, promise1, promise2, setTimeout

原文地址: juejin.cn/post/684490…

  • Promise (手写promiseAll)

let p1 = new Promise((reslove, reject) => {
    setTimeout(() => {return reslove('我是P1'); }, 0)
})

let p2 = new Promise((reslove, reject) => {
    return reslove('我是P22222');
})

let p3 = new Promise((reslove, reject) => {
    setTimeout(() => {return reject('我是P333错了');}, 300)
})

function promiseAll(promiseList) {
    let result = []
    let num = 0
return new Promise((resolve, reject) => {
    for (let i = 0; i < promiseList.length; i++) {
        promiseList[i].then(res => {
            num++
            result[i] = res
        if (num === promiseList.length) {
                resolve(result);
            }
        }).catch(res => {
        reject(res);
        })
        }
    })
}

promiseAll([p1, p2, p3]).then(res => {
    console.log(res)
})

// 手写promiseRace
function promiseRace(promiseList) {
    return new Promise((resolve, reject) => {
        for (let i = 0; i < promiseList.length; i++) {
            promiseList[i].then(resolve, reject);
        }
    })
}
  • 防抖

在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。如可以对获取input输入内容的函数上使用防抖功能。这里可以结合自己做的demo或者自己在使用在项目上的实例去说,效果会更好。

function debounce(func,time){
    let timer return function(){ 
        clearTimeout(timer) 
        let args = arguments timer = setTimeout(() => { func.apply(this,args) },time) 
    }
}
  • 节流

规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。如可以在对监听scroll的函数上使用节流。

function throttle(fun,time){
    let t1 = 0; // 初始时间
    return function(){
        let t2 = new Date(); // 当前时间
        if(t2-t1>time){
            fun.apply(this,arguments)
            t1=t2
        }
    }
}
  • 使用postMessage进行react和iframe的数据通信

1、首先在父组件(react文件)内引入iframe

    <iframe 
        style={{border:0,width:"100%",height:"100%",}}
        src='/url.html'
        id='iframeDom'
        //onLoad={this.sendToken}
    />

2、然后在父组件(react文件)内的Button按钮上绑定点击事件

<Button type="primary" onClick={this.handleClick.bind(this)}>点我</Button>

3、继续在父组件(react文件)内编写handleClick方法,使用postMessage传递数据。

handleClick = () =>{ //必须是iframe加载完成后才可以向子域发送数据 const childFrameObj = document.getElementById('calculation'); childFrameObj.contentWindow.postMessage(1233, '*'); //window.postMessage };

4.在iframe页面编写回掉函数并监听message事件

//回调函数
function receiveMessageFromIndex ( event ) {
    console.log( '我是iframe,我接受到了:', event.data );
}

//监听message事件
window.addEventListener("message", receiveMessageFromIndex, false);

===or===

constructor(){
    super();
    this.state = {
        modelId : ''    //1.初始化一个接收数据的变量
    };
};
componentDidMount(){    //问题延申:React数据获取为什么一定要在componentDidMount里面调用?
    let self = this;    //为了避免作用域及缓存
    window.receiveMessageFromIndex = function ( event ) {
        if(event!=undefined){
            console.log( '我是react,我接受到了来自iframe的模型ID:', event.data );
            self.setState({
                modelId:event.data  //2.给变量赋值
            })
        }
    }
    //监听message事件
    window.addEventListener("message", receiveMessageFromIndex, false);
};
  • forEach终止执行

// 可以使用 抛出错误 的方式来真正的终止forEach循环:
let arr = [1, 2, 3]

try {
    arr.forEach(item => {
        if (item === 2) {
            throw Error(‘终止for循环’);
        }
    })
} catch(err) {
    if (err.message === 'End Loop') throw err // 抛出真正的错误
}
console.log('不影响下边代码执行')

React篇

  • React 中为什么不能在if 循环里写hooks。

react用链表来严格保证hooks的顺序。hook 相关的所有信息收敛在一个 hook 对象里,而 hook 对象之间以单向链表的形式相互串联。

以useState为例,在它的实现过程之有mountupdate两个阶段

  • mountState(首次渲染)阶段会按useState声明的顺序构建出一个链表并渲染;

  • updateState 阶段会按顺序去遍历之前构建好的链表,取出对应的数据信息进行渲染。hooks 的渲染是通过“依次遍历”来定位每个 hooks 内容的。如果前后两次读到的链表在顺序上出现差异,那么渲染的结果自然是不可控的。

  • 必须按照顺序调用从根本上来说是因为 useState 这个钩子在设计层面并没有“状态命名”这个动作,也就是说你每生成一个新的状态,React 并不知道这个状态名字叫啥,所以需要通过顺序来索引到对应的状态值。

  • React 组件map中key的作用是什么?

key有利于提高程序运行和渲染效率

  • react的虚拟dom使用diff算法比对更改前后发生的最小差异,再对真实dom进行更改 我们用key的真实目的是为了标识在前后两次渲染中元素的对应关系,防止发生不必要的更新操作。
  • 那为什么不能用index代替key呢?
    如果我们用index来标识key,数组在执行插入、排序等操作之后,原先的index并不再对应到原先的值,那么这个key就失去了本身的意义
  • React中组件和组件之间的有哪些通信方式

  1. 父组件给子组件传递信息通过props,子组件像父组件报告可以使用函数
  2. 使用 React 中的两个内置hooks,useContentuseReducer
  3. 使用Redux实现通信,而且还可以跨页面级别组件通信
  • react内监听Iframe加载完成

componentDidMount(){
     window.addEventListener('load',this.uploadApi())
}

  uploadApi = () =>{
    let that = this;
    let _iframe = document.getElementById('iframeWin');
    _iframe.onload = function () {
        console.log("iframe加载完成")
        that.setState({framflag:false})
    };
  }
  • react-memo

  1. memo: 结合了pureComponent纯组件和 componentShouldUpdate功能,会对传入的props进行一次对比,然后根据第二个函数返回值来进一步判断哪些props需要更新。
  2. 要注意memo是一个高阶组件函数式组件类组件都可以使用。memo接收两个参数:
    • 第一个参数:组件本身,也就是要优化的组件。
    • 第二个参数:(pre, next) => boolean,pre:之前的数据。next:现在的数据,返回一个布尔值,若为true则不更新,为false更新。

性能优化例子

import React, { Component } from 'react';  
import { Button } from 'antd-mobile';  
  
const Child = () => {  
    return <div>  
        {console.log('子组件渲染了吗????')}  
        我是子组件
    </div>  
}  
  
class Index extends Component{  
  
  constructor(props){  
      super(props)  
      this.state={  
        flagtrue  
      }  
  }  
  
  const HOCChild = memo(Child(pre, next) => {  
    return true  
    })
  
  render(){  
      const { flag } = this.state  
      return <div style={{padding: 20}}>  
          // 更新父级子组件会执行更新
          <Child/>  
         // <HOCChild/> 这样就不更新了
          <Button  
            color="primary"   
            onClick={() => this.setState({ flag: !flag })}  
          >状态切换{JSON.stringify(flag)}</Button>  
      </div>  
  }  
}  
  
export default Index;

第二个参数作用

import React, { Component, memo } from 'react';  
import { Button } from 'antd-mobile';  
  
  
const Child = (number }) => {  
    return <div>  
        {console.log('子组件渲染')}  
        大家好,我是小杜杜~  
        <p>传递的数字:{number}</p>  
    </div>  
}  
  
const HOCChild = memo(Child(pre, next) => {  
    if(pre.number === next.numberreturn true  
    if(next.number < 7return false  
    return true  
})  
  
class Index extends Component{  
  
  constructor(props){  
      super(props)  
      this.state={  
        flagtrue,  
        number1  
      }  
  }  
  
  render(){  
      const { flag, number } = this.state  
      return <div style={{padding: 20}}>  
          <HOCChild number={number} />  
          <Button  
            color="primary"   
            onClick={() => this.setState({ flag: !flag})}  
          >状态切换{JSON.stringify(flag)}</Button>  
        <Button  
            color="primary"  
            style={{marginLeft: 8}}   
            onClick={() => this.setState({ number: number + 1})}  
        >数字加一:{number}</Button>  
      </div>  
  }  
}  
  
export default Index;
当数字小于7,才会出发`Child`的更新,通过返回的布尔值来控制。根据条件按需更新。
  • React-memo的注意事项与总结

React.memoPureComponent的区别:

  • 服务对象不同:PureComponent 服务与类组件,React.memo既可以服务于类组件,也可以服务与函数式组件,useMemo服务于函数式组件(后续讲到)
  • 针对的对象不同:PureComponent针对的是propsstateReact.memo只能针对props来决定是否渲染

这里还有个小的注意点:memo的第二个参数的返回值与shouldComponentUpdate的返回值是相反的,经常会弄混,还要多多注意

  • memo:返回 true 组件不渲染 , 返回 false 组件重新渲染。

  • shouldComponentUpdate: 返回 true 组件渲染 , 返回 false 组件不渲染。

  • React-forwardRef

父组件想要获取孙组件上的信息,我们直接用ref传递会怎样(父级可以拿到子集、孙子级数据)

image.png

  • 接下来看看利用forwardRef来转发下ref,就可以解决这个问题了:
import React, { Component, forwardRef } from 'react';  
  
const Son = ({sonRef}) => {  
    return <div>  
        <p>孙组件</p>  
        <p ref={sonRef}>大家好,我是小杜杜~</p>  
    </div>  
}  
  
const Child = ({ childRef }) => {  
    return <div>  
       <div>子组件</div>  
        <Son sonRef={childRef} />  
    </div>  
}  
  
const ForwardChild = forwardRef((props, ref) => <Child childRef={ref} {...props} />)  
  
class Index extends Component{  
  
  constructor(props){  
    super(props)  
  }  
  node = null  
  
  componentDidMount(){  
      console.log(this.node)  
  }  
  
  render(){  
    return <div style={{padding: 20}}>  
        <div>父组件</div>  
        <ForwardChild ref={(node) => this.node = node} />  
    </div>  
  }  
}  
  
export default Index;
  • 如此以来就解决了不能在react组件中传递ref的问题,至于复用的组件可能会用到,

  • react hooks mobx 使用

版本
"mobx": "^5.15.4",
"mobx-react": "^6.1.8",
    
import React, { useEffect } from 'react';
import { inject, observer } from 'mobx-react';

const Index =(props)=>{
    const { handelClon } = props;
    useEffect(()=>{
        console.log(11111,props)
    },[])

    return <div onClick={handelClon}>11111</div>
}

export default inject("departmentStore")(observer(Index));
  • React-Fragment 与 <></>的不同

React中,组件是不允许返回多个节点的,我们都知道<></><Fragment></Fragment>的简写,从原则上来说是一致的。实际上,Fragment 这个组件可以赋值 key,也就是索引,<></>不能赋值。

  • React生命周期

在17.0的版本,官方彻底废除 componentWillMountcomponentWillReceivePropscomponentWillUpdate 如果还想使用的话可以使用:UNSAFE_componentWillMount()UNSAFE_componentWillReceiveProps()UNSAFE_componentWillUpdate()

  • React v16.8中的hooks

  • useState:定义变量,可以理解为他是类组件中的this.state

useState有点类似于PureComponent,会进行一个比较浅的比较,如果是对象的时候直接传入并不会更新,这点一定要切记。

  • useEffect:副作用,你可以理解为是类组件的生命周期,也是我们最常用的钩子

那么什么是副作用呢?副作用(Side Effect:是指 function 做了和本身运算返回值无关的事,如请求数据、修改全局变量,打印、数据获取、设置订阅以及手动更改 React 组件中的 DOM 都属于副作用操作都算是副作用。当useEffect不设立第二个参数时,无论什么情况,都会执行

useEffect(()=>{
    //数据请求 挂载
    return()=>{
    //组件卸载
    }
},[]//数组内可放基本类型作为监听更新
  • useContext

useContent:上下文,类似于Context:其本意就是设置全局共享数据,使所有组件可跨层级实现共享

useContent的参数一般是由createContext的创建,通过 CountContext.Provider 包裹的组件,才能通过 useContext 获取对应的值

<CountContext.Provider value={count}>  
   <Child />  
   <Brother/>
</CountContext.Provider>
  • useReducer: 它类似于redux功能的api。
const [state, dispatch] = useReducer(reducer, initialArg, init);
复制代码
  • state:更新后的state
  • dispatch:可以理解为和useStatesetState一样的效果
  • reducer:可以理解为reduxreducer
  • initialArg:初始值
  • init:惰性初始化

例子::

import React, { useReducer } from 'react';  
import { Button } from 'antd-mobile';  
  
const Index = () => {  
  
  const [count, dispatch] = useReducer((state, action)=> {  
    switch(action?.type){  
      case 'add':  
        return state + action?.payload;  
      case 'sub':  
        return state - action?.payload;  
      default:  
        return state;  
    }  
  }, 0);  
  
  return <div style={{padding: 20}}>  
    <div>count:{count}</div>  
    <Button  
      color='primary'  
      onClick={() => dispatch({type: 'add', payload: 1})}  
    >  
      加1  
    </Button>  
    <Button  
      color='primary'  
      style={{marginLeft: 8}}  
      onClick={() => dispatch({type: 'sub', payload: 1})}  
    >  
      减1  
    </Button>  
  </div>  
}  
   
export default Index
  • useMemo

useMemo:与memo的理念上差不多,都是判断是否满足当前的限定条件来决定是否执行callback函数,而useMemo的第二个参数是一个数组,通过这个数组来判定是否更新回掉函数

当一个父组件中调用了一个子组件的时候,父组件的 state 发生变化,会导致父组件更新,而子组件虽然没有发生改变,但也会进行更新。

简单的理解下,当一个页面内容非常复杂,模块非常多的时候,函数式组件会从头更新到尾,只要一处改变,所有的模块都会进行刷新,这种情况显然是没有必要的。

我们理想的状态是各个模块只进行自己的更新,不要相互去影响,那么此时用useMemo是最佳的解决方案。

这里要尤其注意一点,只要父组件的状态更新,无论有没有对自组件进行操作,子组件都会进行更新useMemo就是为了防止这点而出现的