1、css写骰子:flex
2、作用域:
-
闭包中自由变量的查找是在函数定义的地方向上级作用域查找
-
this在各个场景中的取值是在函数执行的地方确认的,不是在含住定义的地方确认的
一、布局问题:
1、什么是BFC?如何应用?
是一个独立的渲染区域,内部元素的渲染不会影响到外边元素
形成BFC的条件:
- float不是none
- position不是absolute或fiexd
- overflow不是visible
常见应用:
- 清除浮动
2、margin负值、纵向重叠
margin-top和margin-left为负,自身向上向左偏移
margin-right为负,右边元素向左移
margin-bottom为负,下边元素向上移
3、设备布局,双飞翼布局(三栏布局,两侧固定,中间宽度自适应)
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; */
}
6、居中
水平居中,垂直居中
7、line-height 如何继承
- 写具体数值,如30px。则继承
- 写比例,如 2/1.5 ,则继承该比例,下图如果line-height:2,则p的line-height继承2,为32px
- 写百分比,如 200% ,则计算出来的值40px再继承
![]()
这个图片的答案是40px
8、单位:px,em,rem
-
px: 绝对长度单位,最常用
-
em : 相对于长度单位,相对于父元素,不常用
-
rem : 相对于长度单位,相对于根元素,常用于响应式布局
定义在html{ font-size:100px; //相当于一个rem }
9、响应式的常用方案
-
media-query,根据不同的屏幕宽度设置根元素font-size
-
再通过rem,基于根元素的相对单位
-
-
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继承的几种方式
-
原型链继承
- Child.prototype = new Person()
-
构造函数继承(借助call)
-
function Child(){ Person.call(this) }
-
-
原型链构函数组合继承(以上两种结合)
-
原型继承
- 对象,借助Object.create()
-
寄生继承
- 对象,嵌套一层 clone(parent,child)
-
寄生组合继承 = 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-loader、img-loader、babel-loader、vue-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
3、diff算法
diff即对比,两棵树做对比
传统比较:1、遍历tree1 2、遍历tree2 3排序对比 =》时间复杂度n(O^3)不可取
优化后:
- 只比较同一层的结点
- 如果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语法
- 模板编译为render函数,执行render函数返回vnode
- 基于vnode,再进行patch和diff
- 使用webpack vue-loader, 会在开发环境下编译模板
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.minx3: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('../***'))}
- vue2:
-
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去改变statestore.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:
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) //调用函数