前端学习笔记

131 阅读20分钟

1、css写骰子:flex

2、作用域:

  • 闭包中自由变量的查找是在函数定义的地方向上级作用域查找

  • this在各个场景中的取值是在函数执行的地方确认的,不是在含住定义的地方确认的

一、布局问题:

1、什么是BFC?如何应用?

是一个独立的渲染区域,内部元素的渲染不会影响到外边元素

形成BFC的条件:

  • float不是none
  • position不是absolute或fiexd
  • overflow不是visible

常见应用:

  • 清除浮动

image-20230709111208511转存失败,建议直接上传图片文件

2、margin负值、纵向重叠

​ margin-top和margin-left为负,自身向上向左偏移

​ margin-right为负,右边元素向左移

​ margin-bottom为负,下边元素向上移

3、设备布局,双飞翼布局(三栏布局,两侧固定,中间宽度自适应)

image-20230709174327160转存失败,建议直接上传图片文件

4、手写clearFix

.clearfix:after{
        content:'';
        display:table;
        clear:both;
}
.clearfix{
    *zoom:1 /*兼容ie*/
}

5、flex写三色骰子

  /*  画骰子:宽高自适应*/
        .box1{
            display: inline-block;
            background-color: aqua;
            overflow: hidden;
            /* display: flex; */
            /* flex-direction: column; */
        }

image-20230709205206974转存失败,建议直接上传图片文件

6、居中

水平居中,垂直居中

7、line-height 如何继承

  • 写具体数值,如30px。则继承
  • 写比例,如 2/1.5 ,则继承该比例,下图如果line-height:2,则p的line-height继承2,为32px
  • 百分比,如 200% ,则计算出来的值40px再继承
image-20230709202102873转存失败,建议直接上传图片文件

这个图片的答案是40px

8、单位:px,em,rem

  • px: 绝对长度单位,最常用

  • em : 相对于长度单位,相对于父元素,不常用

  • rem : 相对于长度单位,相对于根元素,常用于响应式布局

    定义在html{
        font-size:100px;    //相当于一个rem
    }
    

9、响应式的常用方案

  • media-query,根据不同的屏幕宽度设置根元素font-size

  • 再通过rem,基于根元素的相对单位

  • image-20230709203408706转存失败,建议直接上传图片文件
  • rem的弊端,具有阶梯型,有时可用vh和vw,wmax,xmin

二、js基础知识

作用域相关:

1、自由变量

  • 除了==null 以外,其他的一律用===判断。

  • 闭包自由变量的查找是从函数定义的地方不是在函数执行的地方

  • 函数的this是在哪里调用,this就是哪里

2、改变this指向的方法

  • apply('this',['参数1','参数2','参数3',...]) 立即执行
  • call('this','参数1','参数2',...) 立即执行
  • bind('this', '参数1','参数2',...) 返回一个函数需要调用才能执行,并且有返回值

3、手写bind

//手写bind
Function.prototype.myBind = function(){
    let arr = Array.prototype.slice.call(arguments)  //将剩余参数转换为数组
    let first = arr.shift()  //删除数组第一个元素并返回。影响原数组
    let self = this
    return function(){   //返回一个函数
        return self.apply(first,arr)  //bind有返回值
    }
}
//使用
function a(a,b){
    console.log(this.name)
    return '返回值'
}
let obj = {x:1,y:2}
let bindObj = obj.bind(obj,3,4)
let res = bindObj()

4、手写深拷贝

function deepClone(obj = {}){
    if(typeof obj !== 'object' || obj == null){
        return obj
    }
    let res 
    if(obj instanceof Array){
        res = []
    }else{
        res = {}
    }
    for (let key in obj){
        if(obj.hasOwnProperty(key)){
            res[key] = deepClone(obj[key])
        }
    }
    return res
}
let obj1 = {
    a:1,
    b:[1,2,3],
    c:{
        name:'zs'
    }
}
let obj2 = deepClone(obj1)
obj2.a = 23
obj1应该也不会发生变化

5、实际开发中闭包的应用

闭包:函数作为参数被传递,函数作为结果被返回

//实例:隐藏数据,只提供api
function catchData(){
    let data = {}
    return {
        set:function(key,value){
            data[key] = value
        },
        get:function(key){
            return data[key]
        }
    }
}
let c = catchData()
c.set('age',18)
c.set('name',"zhangsan")
c.get('name')

6、创建10个<a>依次弹出相应数据

//创建10个a标签,内容分别是1-10,点击标签内容弹出相应值
let app = document.querySelector('#app')
for(let i=0;i<10;i++){
    let a =  document.createElement('a')
    a.innerHTML = i+'<br />'
    a.addEventListener('click',function(e){
        e.preventDefault()
        alert(i)
    })
    app.appendChild(a)
}

原型与原型链

js是基于原型继承的。

1、js继承的几种方式

  1. 原型链继承

    • Child.prototype = new Person()
  2. 构造函数继承(借助call)

    • function Child(){
          Person.call(this)
      }
      
  3. 原型链构函数组合继承(以上两种结合)

  4. 原型继承

    • 对象,借助Object.create()
  5. 寄生继承

    • 对象,嵌套一层 clone(parent,child)
  6. 寄生组合继承 = Class继承的原理

    • 3与5结合

2、原型链

对象继承时遵循的一条规则。

查找对象的属性和方法时,先找自身,如果找不到,就往他的原型上面去找。

举个例子:Student类 继承了Person类,zhangsan是Student类实例化出的对象,查找zhangsan 的属性name时,先查Student,再找Person,再找Object。

原型链是基于一个__proto__这个指针进行查找。zs.__proto__ === Student.prototype , .....

优化

1、防抖

频繁触发不执行,过一段时间之后执行

function debounce(fn,delay){
    let timer 
    return function(){
        if(timer){
            clearTimeOut(timer)
        }
        timer = setTimeOut(()=>{
            fn.apply(this,arguments)
            timer = null
        },delay)
    }
}

2、节流

频繁触发,每隔一段时间执行一次

function throttle(fn,delay){
    let timer 
    return function(){
        if(timer){
            return
        }
        timer = setTimeout(()=>{
            fn.apply(this,arguments)
            timer = null
        },delay)
    }
}

异步Promise:

1、手写promise封装加载图片

function loadImg(src){
    const p = new Promise((resolve,reject)=>{
        let img = document.createElement('img')
        img.onLoad = ()=>{
            resolve()
        }
        img.onerror = () =>{
            reject('加载失败')
        }
        img.src = src
    })
    return p
}
const url = "http://*****"
const p = loadImg(url) 
p.then(res =>{
    
}).catch(err =>{
    
})

2、手写promise

const p = new Promise((resolve,reject)=>{
    console.log('同步执行')
    resolve()
})

//开始写
class MyPromise(){
    state = 'pending'   //fulfilled , rejected
    value = "",
    reason = ""     
    constructor(fn){
        const myResolve = (value) =>{
            if(this.state === pending){
                this.state = 'fulfilled'
                this.value = value
            }
        }
        const myReject = (reason) =>{
            if(this.state === pending){
                this.state = 'rejected'
                this.reason = reason
            }
        }
        try{
            fn(myResolve,myReject)
        }catch (err){
            myReject('错误'+err)
        }
    }
    then(fn1,fn2){
        
    }
}

1、数组转换:tree递归

function curcle(res,ele){
     res.forEach(item = >{
                if(item.id==ele.pid){
                    item.children.push(ele)
                }else{
                    curcle(item.children,ele)
                }
     })
}
function toTree(data){
    let res = []
    data.forEach(ele=>{
        ele.children = []
        if(!ele.pid && ele.pid!==0){
            res.push(ele)
        }else{
            curcle(res,ele)
        }
    })
    return res
}

2、使用setTimeOut实现每隔一秒执行

function myInterval(fn,count=10,time){
    let timer 
    interval()
    function interval(i=0){
        if(i>=count) {
            clearTimeOut(timer)
            return
        }
        timer = setTimeOut(()=>{
            console.log(i)
            fn()
            i++
            interval(i)
        },time)
    }
}

三、webpack

1、前端为何要进行打包构建

  • 代码产出方面
    • 压缩代码,体积小,加载更快
    • (开发)可以编译高级语言和语法,es6、less、sass预处理
    • 兼容性和错误提示(Polyfill、postcss、eslint)
  • 前端工程化,对于团队研发比较友好
    • 统一、高效的开发环境
    • 统一的构建流程和产出标准
    • 集成公司的构建规范(测试、上线等)

2、module、chunk、bundle的区别

3、loader和plugin的区别

  • loader模块转换器,本质就是函数,在该函数中进行内容的转化(翻译官)

    • 常用的loader:

      ts-loader, less-loader,i18n-loaderimg-loaderbabel-loadervue-loader

  • plugin 扩展插件: 对webpack的扩充

    • 常用的插件:

      • html-webpack-plugin: index.html打包

      • webpack-dev-server: 本地服务器

      • webpack-margin:把内容较多的config.js抽离合并,prod,dev,base


前端框架及项目面试-聚焦Vue3/React/Webpack

vue系列

一、vue原理

1、mvvm、响应式,双向绑定:

vue2 核心api: Object.defineProperty

vue3 Proxy

//vue2 核心api
    Object.defineProperty(target,key,{
        get(){
            return value
        },
        set(newValue){
            if(newValue !== value){
                //深度监听 
                observer(newValue)
                //设置新值
                value = newValue
                //触发更新视图
                updateView()
            }
        }
    })

observer(){
    
}

//vue3 proxy
//响应式
function reactive(target) {
    if(typeof target !=='object'  && target != null){
        return target
    }
    const handle = {
        get(target, key, receiver) {
            const res = Reflect.get(target, key, receiver)
            console.log(`获取${key}:${res}`)
            return  reactive(res)   //深度获取
        },
        set(target, key, value, receiver) {
            const res = Reflect.set(target, key, value, receiver)
            console.log(`设置${key}:${value}`)
            return res
        },
        deleteProperty(target, key) {
            const res = Reflect.deleteProperty(target, key)
            console.log(`删除${key}:${res}`)
            return res
        }
    }
    // Proxy相当于在对象外层加拦截
    const observed = new Proxy(target, handle)
    return observed
}
  • defineProperty 缺点:

    • 需遍历对象的每一个属性进行监听,递归到底,一次性计算量大性能问题
    • 无法监听新增/删除的属性(Vue.set Vue.delete)
    • 不具备监听数组的能力,需特殊处理
  • Proxy :

    针对一整个对象,对象的所有操作都可以进行监听,并返回一个新的对象

    • 缺点:不兼容ie,无法polyfill

2、虚拟dom

  • 用js模拟dom结果
  • 新旧v-node对比,得出更小的更新范围,最后更新dom
  • 数据驱动视图的模式下,有效控制dom操作

第一步: dom计算耗时,用js模拟DOM结构

<div id="div1" class="box">
    <p>vdom段落</p>
    <ul style="color:#ddd">
        <li> 这是第一项</li>
    </ul>
</div>
//用js模拟DOM结构
let tree = {
    tag:"div",                   //标签
    props:{                      //属性
        id:"div1",
        className:"box",
    },
    children:[{                  //子元素
        tag:"p",
        children:"vdom段落"
    },{
        tag:"ul",
        props:{
            style:"cloor:#ddd"
        },
        children:[{
            tag:li,
            chuldren:"这是第一项"
          },
              //......
        ]
    }],
    on{                            //事件
        click:"handleClick",
    }
}

接下来,通过snabbdom 工具学习 vdom

image-20230811125455331转存失败,建议直接上传图片文件

3、diff算法

diff即对比,两棵树做对比

传统比较:1、遍历tree1 2、遍历tree2 3排序对比 =》时间复杂度n(O^3)不可取

image-20230811131913196转存失败,建议直接上传图片文件

优化后:

  • 只比较同一层的结点
  • 如果tag不相同,则直接删除tag重建,不在深度遍历
  • 如果tag和key都相同,则认为相同,不向下深度对比

备注:1、把vnode渲染在空元素上patch(elem,vnode);2、新的vnode对比更新已有的vnode patch(vnode,newVnode)

核心概念:h(相当于render)、vnode、patch、diff、key

4、路由原理

会触发网页跳转,但不会刷新页面,SPA必备特点

  • hash模式

    • location.href 改变路由 或者浏览器前进后退按钮改变路由
    • 通过window.onhashchange来监听路由的变化
    window.onhashchange = (event) =>{          //重点!window.onhashchange监听
        //hash变化时会监听触发
         console.log('newURL',event.newURL);
        console.log('oldURL',event.oldURL);
        console.log('hash内容',location.hash);
    }
    //可以通过浏览器的前进后退按钮触发
    //也可以手动触发,如点击按钮改变路径
    document.getElementById('box').addEventListener('click',function(){
        location.href = "#/user"
    })
    
    
  • history模式 (每次刷新会丢失页面,故需要后端支持)

    • 通过history的api,比如pushState(),replaceState()改变路由
    • 通过window.onpopstate(){}监听路由的变化
    document.getElementById('box').addEventListener('click',function(){
        const state ={ name:'page1'}
        console.log('要切换路由到page1');
        history.pushState(state,'','page1')   //重要!监听点击某个元素跳转
    })
     
    window.onpopstate = (event) =>{
        console.log('onpopstate',event.state,location.pathname);//!!! 监听浏览器前进后退 
    }
    

5、模板编译

模板:有指令、插值、js表达式、循环、判断

html是标签语言。不是图灵完备的语言(顺序执行+循环+判断)

模板一定是转成js代码,才能有循环判断这些 =》这个过程就是模板编译

with语法

image-20230811173825091转存失败,建议直接上传图片文件
  • 模板编译为render函数,执行render函数返回vnode
  • 基于vnode,再进行patch和diff
  • 使用webpack vue-loader, 会在开发环境下编译模板
image-20230811173353649转存失败,建议直接上传图片文件

6、组件渲染更新过程

二、vue3内容

1、vue3生命周期:

  • option API: beforeCreate(),created(),beforeMount(),mounted(),beforeUpdate(),updata(),beforeUnmount(),unmounted()

  • composition API:

    import {onBeforeMount,onMounted,updata,beforeUnmount,unmounted} from 'vue'
    setup(){   //setup 相当于beforeCreate(),created()
        onBeforeMount(()=>{
            
        }),
        onMounted(()=>{
            
        })
            ...
    }
    

2、option API 与composition API

composition API 代码更规整,逻辑清晰,有更好的类型推导(optionAPI的this.**)

3、ref toRef 和toRefs

ref:

  • 生成值类型(number,boolea,string)的响应式数据

  • 还可以获取dom元素

reactive、toRef

  • 一个普通对象实现响应式用 reactive ,
  • 响应式对象里的某一个属性单独拿出来做响应式用toRef

toRefs

  • 将一个reactive响应式对象中的每一个属性都作为响应式数据
setup(){
    const ageRef = ref(20)   //ref定义值类型的变量
    const nameRef = ref('何苗苗')
    const state = reactive({   //reactive定义数组类型的变量
        name:nameRef,
        number:12345
    })
    const numberRef = toRef(state,'number')  //toRef将reactice的某个值变为ref形式
    setTimeout(()=>{
        ageRef.value = 25   //修改值类型的变量需要用.value
        state.number = 56789
    },1200)
    
    return {
        ageRef,
        state,
        ...toRefs(state)  //
    }
}
  • 为什么要使用ref ? :在setup、compunted、合成函数都有可能返回值类型需要去做响应式。
  • **为什么要用.value ?: ** ref是一个对象,为了不丢失响应式需要value存储值;通过.value属性的get和set 实现响应式;模板和reactive里不需要,其他都需要
  • 为什么需要toRef和toRefs? 将响应式对象分散,延续响应式不是创建响应式

4、setup:

没有this,可通过getCurrentInstance获取当前实例

export default{
    data(){
        return{
            x:1  
        }
    }
    setup(){  
        const instance = getCurrentInstance()
        console.log(instance.data.x) //undefined  相当于ceracted生命周期,组件实例还没有获取成功
        onMounted(()=>{
            console.log(instance.data.x)  // 1
        })
    }
}

5、vue3升级了那些重要功能

  • creatApp 2: new Vue(); vue.minx 3: let app = creactApp(); app.minx

  • compositonAPI 生命周期,ref相关 , reactive, setup, watch和watchEvent,readonly,实现逻辑复用

  • emit 子组件中,props:{...} emit:[onSay]

  • 多事件处理 <button @click="one($event) two($event)"></button>

  • Fragment 不再是单一根节点

  • 移除.async 父子组件同步数据时,直接用v-model语法糖

  • 异步组件

    • vue2:component:{'header':()=>import('../***')}
    • vue3 : component:{'header':defineAsyncComponent(()=>import('../***'))}
  • Teleport 组件可以放在页面任何地方 <teleport to="body"> ... </teleport>

  • suspense 类似封装的插槽

  • 移除了filter

6、vue3为什么比vue2快

  • Proxy响应式
  • **PatchFlag静态标记 ** 给动态节点加一个标记,做diff算法的时候会快一些
  • hoistStatic 静态变量提升
  • cacheHandle 缓存事件
  • SSR优化 输出的时候,静态节点不走v-dom的逻辑,直接转为字符串
  • tree-shaking 根据模板内容,动态去引入相应的api

7、vite为什么快

前端打包工具,优势:开发环境下无需打包,启动快

开发环境使用 ES6 Module,无需打包,非常快

  • export 导出
  • import引入
  • 动态加载,即按需引入

react系列

一、基础使用 react16

函数组件: 纯函数,输入props输出jsx,没有实例,没有生命周期,没有state

class组件: class List extends React.Component{ //内容 }

1、jsx语法

style、class、条件渲染,列表渲染,三元运算,逻辑运算&& 、表单(受控组件)|、父子组件通信

2、事件event

写法:需要bind改变this指向,或者事件定义函数采用箭头函数,在最后追缴一个参数event

  • react的event是SynrtheicEvent: 是合成的,模拟dom事件所有能力
  • event.nativeEvent 才是原生对象event
  • 所有的事件都挂载在document上。17版本改了,绑定到了root上,优点有利于微前端中使用多个版本

3、setState

  • 不可变值:定义数据 state:{num:1} , 修改数据,this.setState({name:1})
  • 可能是异步更新: 直接使用是异步的,在定义的事件中或者setTimeout中使用是同步的
  • 可能会被合并:因为异步的执行逻辑所以会合并(例如:object.assign({name:1},{name:1})); 但如果传入的是一个函数的话不会合并,this.setState(()=>{return ...})

注意:18版本以后全部都是异步更新,都会合并

生命周期:

  • componentWillMount 即将渲染

  • componentDidMount 渲染完成

  • shouldComponentUpdata 是否应该渲染

  • componentWillUpdate 即将更新

  • componentDidUpdata 完成更新

  • componentWillUnmount 即将销毁

4、非受控组件

类似于vue的 ref = "tree" , this.$ref.tree ...

 this.myref =  React.creactRef()
 return <div>
       <input type="file" ref = {this.myref}
 </div>

使用场景:

  • 必须手动操作dom,setState实现不了

  • 例如文件上传 、富文本编辑器需要传入dom元素

5、Portals

类似vue3的 teleport 传送门

return ReactDom.createPortal(
    <div className="modal">{this.props.children}</div>,
    documnet.body //DOM节点 
)

6、context

使用场景: 组件嵌套多层时 向下传递数据

const ThemeContext = React.creactContext('light')  //1、创建context
//最外层组件
class App extends React.Component{ 
    ...
    render(){
        return 
        <ThemeContext.Provider value={this.state.theme}>  //2、用创建的context .Provider包裹底层元素
            <Box>
               <Mybtn></Mybtn>
            </Box>
        </ThemeContext>
        
    }
}
//底层组件class
class Mybtn extends React.Component{
    static contextType = ThemeContext        //同3、底层组件绑定
    render(){
        const theme = this.context          //4、底层组件使用
    }
} 
Mybtn.contextType = ThemeContext             //同3
//底层函数组件(没有this)
function Mybtn2(){
    return <ThemeContext.consumer>          //函数4,用context.consumer包裹
        {value=><p>值是{value}</p>}
    </ThemeContext.consumer> 
}

7、异步加载组件

const AsyncCmp = React.lazy(()=>import "./***")   //1 异步引入
calss App extends React.component {
    ...
    return<div>
        <React.Suspense fallback={<div>loading...</div>}>    // 2 包裹组件,fallback可以引入加载状态的组件
             <AsyncCmp></AsyncCmp>
        </React.Suspense>
    </div>
} 

8、高阶组件HOC

是一种模式,抽离公共逻辑,用高阶组件包裹有共同逻辑的子组件,可以透传所有props

例如redux的connext 是高阶组件

9、性能优化 SCU!!

SCU: 生命周期 shouldComponentUpdate()

shouldComponentUpdate(nextProps,nexState){
    if(_.isEqual(this.props,nextProps)){  //判断当前props和修改后的props是否相等
        return false
    }
    return true
}

坑:react默认,父组件有更新,子组件无条件更新,出现重复渲染

必须配合state的不可变值一起写(不能用push、pop、shift更新setState)

  • 纯组件PureComponent:默认对SCU进行浅比较

    用法 class A extends React.PureComponent() 创建

  • memo

    用法export default React.memo()

10、Redux

  • 基本概念:store、state、action、reducer

    • **createStare**创建store
    • **store.getState()**获取数据
    • store.dispatch() 改变数据,派发action,通过action去改变state
    • store.subscrible() 订阅发生的变化
    import { createStore } from 'redux'                //引入
    function counterReducer(state = { value: 0 }, action) {   //定义reducer:动作
      switch (action.type) {
        case 'counter/incremented':
          return { value: state.value + 1 }
        case 'counter/decremented':
          return { value: state.value - 1 }
        default:
          return state           //返回state具体的数据
      }
    }
    let store = createStore(counterReducer)            //store:数据存放区域(管理员)
    
    console.log(store.getState());  //getState()获取store的数据
    store.dispatch({ type: 'counter/incremented' })   //使用dispatch修改数据,改变state的唯一方法,异步的
    store.subscribe(() => console.log(store.getState()))   //subscribe:监听数据是否发生变化
    
  • 单项数据流

    • dispatch(派发action) -> Reducer改变成新的state -> subcribe监听触发通知 -> 更新到视图
  • react-redux

    • 全局引入Provider并包裹所有组件
    • 在组件里使用的时候用高阶组件**connext()**包裹
  • 异步

    • return 一个函数,参数是dispatch,获取ajax异步数据后再进行dispatch
  • 中间件

    修改dispatch时拦截再进行扩展

    • redux-thunk:用于异步操作
    • redux-logger:用于日志记录

react-router

路由模式(h5,hash),路由配置(懒加载、动态路由)

二、react hooks

函数组件,没有state,没有生命周期,hook可以完善函数组件的功能

hook 钩子,把功能勾到函数中来,实现组件逻辑复用

1、useState()

import React,{ useState , useEffect } from 'react'
function clickDemo(){
    //数组的结构,返回数组[]
    const [count,setCount] = useState(0)  //传入一个初始值
    const [name,setName] = useState('何苗苗')
    
    //...   useEffect()钩子操作
    
    function clickHandle(){
        setCount(count+1) 
        setName('名字是'+name )
    }
    return <div>
        <p>点击了{count}次</p>
        <button onClick={clickHandle}>点击</button>
    </div>
}

2、用useEffect模拟组件生命周期

...
useEffect(()=>{
    console.log('可以发送ajax请求')
})
useEffect(()=>{
     console.log('模拟class组件的didMount周期')
},[])
useEffect(()=>{
     console.log('模拟class组件的didUpdate周期')
},[count,name])
useEffect(()=>{
    let timerId = window.setInterval(()=>{
        console.log(Date.now())
    },1000)
    //返回一个函数,来模拟 willUnMount (执行下一次useEffect之前,会调用return的函数)
    return ()=>{
        window.clearInterval(timerId)
    }
},[])
...

3、其他hooks

  • useRef (元素绑定)

    function UseRef(){
      const btnRef = useRef(null)  //可以用来绑定一个元素
      useEffect(()=>{
         console.log(btnRef.current)  //当前DOM节点
      },[])
      return <div> 
         <button ref={btnRef}>这是一个按钮</button>
      </div>
    }
    
  • useContext : (隔层传递)

    const ThemeContext = React.createContext() //1、创建Context
    
    function ThemeButton(){
      //3、使用。如果想直接使用最外层父组件的值
      const theme = useContext(ThemeContext)
      return <button style={{background:theme.background}}>最内层组件</button>
    }
    
    function Middle(){
      return <div><ThemeButton />中间层</div>
    }
    
    const themes = {
      light:{
        foreground:"#000",
        background:"#eee"
      },
      dark:{
         foreground:"#fff",
         background:"#222"
      }
    }
    function App(){    //2、最外层元素需要包裹,传入需要修改的值
      return <ThemeContext.Provider value = {themes.dark}>  
        <div>
          <Middle />
        </div>
      </ThemeContext.Provider>
    }
    
  • useReducer (useState的升级版:单个组件状态管理,跟redux完全不一样)

    const initialState = { count:0 }
    const reducer = (state:any,action:any) =>{
        //可抽离到外部文件
        switch (action.type){
            case 'increment':
                return { count:state.count +1 }
            case 'decrement':
                return { count:state.count -1 }
        }
    }
    function App() {
     //很像:const [count,setCount] = useState(0)
      const [state,dispatch] = useReducer(reducer,initialState)
      return ( 
            <div>
                count : {state.count}
                <Button onClick={()=>dispatch({type:'increment'})}>加1</Button>
                <Button onClick={()=>dispatch({type:'decrement'})}>减1</Button>
            </div>)
    }
    export default App
    
  • **useMemo ** 缓存数据

    父组件向子组件传值

    //问题:父组件变化,子组件会无条件的重新渲染
    //解决:1、SCU或PureComponent  2、useMemo
    /** useMemo的使用
    第一步:子组件用memo包裹
    第二步:父组件传给子组件的数据(即需要缓存的数据)用useMemo缓存,第一个参数是回调,需要return数据,第二个参数是依赖。只有依赖的数据发生变化时,子组件才会渲染
    */
    import { memo , useMemo } from 'react'
    //子组件
    const Child = memo(({userInfo}) =>{
      console.log('Child reder...')
      return <div> 子组件 {userInfo.name } </div>
    }) 
    //父组件
    const Parent = () =>{
      const [name,setName] = useState('hemm')
      // const userInfo = {name,age:1}
      const userInfo = useMemo(()=>{
        return {name,age:1}
      },[])
      return <div>
          <Child userInfo={userInfo} />
        </div>
    }
    
    
  • **useCallback ** 缓存函数

    子组件向父组件传值,如传递一个函数

    上边的例子,如果子组件在向父组件传递一个函数的话,useMemo就会失效,这个时候就需要用到useCallback来包裹函数

    /**useCallback的使用
    子组件传递给父组件的函数用useCallback包裹,第一个参数是个回调,第二个参数是个空依赖[]
    */
    import { memo , useMemo, useCallback} from 'react'
    //子组件
    const Child = memo(({userInfo}) =>{
      console.log('Child reder...')
      return <div> 子组件 {userInfo.name }
        <input onChange ={onChange}  />
      </div>
    }) 
    //父组件
    const Parent = () =>{
      /**const onChange =  (e)=>{
        console.log(e.target.value)
      }*/
      const onChange = useCallback((e)=>{
         console.log(e.target.value)
      },[])
        return <div>
          <Child userInfo={userInfo} onChange = {onChange}/>
        </div>
    }
    

4、react hooks遇到的坑

  • useState初始化只能初始化一次
  • 依赖为[]时:re-render不会重新执行effect函数 ; 没有依赖,re-render会重新执行effect函数
  • useEffect内部,不能修改state; useEffect依赖引用类型会出现死循环

5、自定义hook

  • 本质是一个函数,以use开头!!
  • 内部可以使用useState,useEffect, 接收一个参数,返回一些数据
  • 自定义返回结果,格式不限

6、第三方hooks:

image-20230821174312887转存失败,建议直接上传图片文件 image-20230821174046983转存失败,建议直接上传图片文件 image-20230821174529321转存失败,建议直接上传图片文件

TS

基本使用

//1、类型推断 2、类型注解
let str:string = 'asa'
//类型断言 as
let numarr = [1,2,3]
const result = numarr.find(item =>item>2)  as number
result * 2
//基本数据类型和联合类型
let v1:string = "abc"
let v2:number = 123
let v3:boolean = true
let nu:null = null
let un:undefined = undefined

let v4:string|null = '2121'   //联合类型 可以是string也可以是null
let v5:1|2|3 =3      //v5只能是1,2,3中的一个值

//数组
let arr:number[] = [1,2,3] //数组方式1
let arr2:Array<string> = ['a','b','c'] //数组方式2

//元组tuple:一个已知元素类型和数量的数组
let arr3:[number,string,number] = [1,'b',2]  //元组,一一对应
let arr4:[number,string,number?] = [1,'a',2]  //可选值加?

//枚举
enum MyEnum {
    A = 1,     //A定义为1,下边依次增加. 不写默认是0
    B,
    C
}
enum MyEnum2 {  //字符串类型枚举
    Up = 'UP',     
    Down = 'Down',
    Left = 'LEFT'
}
console.log(MyEnum.A)  //1
console.log(MyEnum[1]) //A 

//any 任意类型

//void 函数没有返回值的使用
//function 函数名(参数1:类型如string,参数2:类型如number,可选参数?:类型,剩余参数-数组):返回值类型(没有就用void) {   }
function MyFn(a:number =10,b:number,c?:string,...rest:number[]):number{  
     return a+b
}

//object 对象类型


//接口interface
interface Obj {
    name:string,
    age:number
}
const myobj:Obj = {
   name:"zhangsan",
   age:10
}

//type 类型别名
let num1:string | number = 10
    //---两者相等----
type MyMun = string|number
let num2:MyMun = 10

//泛型
function fn (a:number,b:number):number[]{
    return [a,b]
}
   // 把number都用T代替
function fn2<T> (a:T,b:T):T[]{
    return [a,b]
}
fn2<number>(1,2)   //调用函数