React,Vue,Css3 ...等热门面试题

## 面试

* Fiber

    React Fiber是react执行渲染时的一种新的调度策略 JavaScript是单线程的 一旦组件开始更新

    主线程就一直被React控制 这个时候如果再次执行交互操作 就会卡顿 页面首次渲染会创建一颗结构

    一模一样的的Fiber节点树 Fiber在update的时候

    会从原来的Fiber clone出一个新的Fiber 俩个Fiber diff出的变化 在更新结束后会取代之前的

    Fiber节点树 渲染过程采用切片的方式 每执行一会儿 就歇一会儿 如果有优先级更高的任务 就会先去执行

    降低页面发生卡顿的可能性, Fiber分片优先级分同步 异步 NoWork sync async

    Fiber 更新任务分成俩个阶段 调和阶段 和 交付阶段, 调和阶段 找出要做的更新工作可以被打断 例如 setState

    useState, 交付阶段需要提交所有更新并渲染 被设置为不能打断(然后说一下协调的三种diff算法),fiber是个链表

    有child和sibing属性 指向第一个子节点和相邻的兄弟节点 从而构成fiber tree

vue 用 keep-alive 做缓存

react 有一个

* react vue 区别:

  1. 响应式原理不同

    vue: vue会遍历data数据对象, 使用Object.definedProperty 将每个属性都转换为getter和setter

    每个Vue组件实例都有一个对应的watcher实例, 在组件初次渲染的时候会记录组件用到了那些数据, 当数据发生改变的时候,

    会触发setter方法, 并通知所有依赖这个数据的watcher实例调用update方法去触发组件的compile渲染方法, 进行渲染数据

    react: React主要是通过setState, useState方法来更新状态, 状态更新之后, 组件也会重新渲染

       

  2. 监听数据变化的实现原理不同

    vue: Vue通过 getter/setter以及一些函数的劫持, 能精确知道数据变化

    react: React默认是通过比较引用的方式(diff)进行的

  3. React 使用JSX编写组件, Vue 提供了模板语法 允许在HTML中直接绑定数据和方法 Vue也支持JSX


 

## type interface 区别

  * type 可以定义基本类型 联合类型 交叉类型以及字面量类型等 而 interface 主要用于定义对象的结构

  * type 支持映射类型 可以通过映射操作来生成新的类型 例如 可以通过 Partial 将一个接口的所有属性变为可选属性 而 interface 不提供类似的映射操作

  Exclude<T, U> -- 从T中剔除可以赋值给U的类型

  Extract<T, U> -- 提取T中可以赋值给U的类型

  NonNullable<T> -- 从T中剔除null和undefined

  ReturnType<T> -- 获取函数返回值类型

  InstanceType<T> -- 获取构造函数类型的实例类型

  Pick -- 从原来接口中选择一部分属性

  Record<K,T> 构造具有给定类型T的一组属性K的类型 在将一个类型的属性映射到另一个类型的属性时 Record非常方便。


 

## webpack

  entry: 入口文件 (打包哪些文件)

    * 单入口

    * 多入口

  output: 输出 (把入口文件按照输出配置以 hash的方式输出文件)

  loader: 把 react vue e6 e7 less sass 解析成 e5 css

    less-loader: 解析 less

    babel-loader: 解析 jsx vue e6, e7 解析成 e5

  plugin:

    * CommonsChunkPlugin: 公共业务模块与类库或框架分开打包

    * DllPlugin: 加快本地编译速度

        第三方的文件 单独打包

        把我们的自己的文件 单独打包

  mode: development | production | none 配置环境



 

* 怎么解决 css 命名冲突

  1. css-module (以对象的方式去写, 使用起来不方便)

  2. css in js (css封装成了组件的方式使用 不好区分哪些是CSS组件 哪些是业务组件)

  3. react-css-modules: 我们公司使用这个, 只需要把 className 改成 styleName 就可以了, 编译完以后

                        会给class 和 这个class 对应的样式自动添加唯一的 哈希值


 

* BFC 渲染规则

1. BFC垂直方向的距离重叠

2. BFC是一个独立的容器, 外面的元素不会影响里面的元素

* BFC 触发条件

1. 浮动元素 (元素的 float 不是 none)

2. 绝对定位元素 (元素的 position 为 absolute 或 fixed)

3. 行内块元素 (元素的 display 为 inline-block)

4. overflow 值不为 visible 的块元素 -弹性元素 (display为 flex 或 inline-flex元素的直接子元素)


 

* 怎么隐藏元素

  1. display: none;

  2. visibility: hidden;

  3. transform: translate(很大的值)

  4. transform: scale(0)

  5. 设置 absolute 给 top left 很大的值

  6. opacity: 0 透明度设置成0

  7. z-index: -1000


 

* display: none; visibility: hidden 区别:

none 完全不可见, hidden 元素不可见 但占用着空间


 

* css3 有哪些新特性:

transfrom, transition, animation, border-radius, box-shadow, flex,

will-change, ::before, ::after, columns, :root


 

* css3 新增伪类

::after, ::before, :disabled, :focus, :checked :root, :nth-child

* css3 动画:

1. transform: translate(移动) rotate(旋转) scale(缩放)

  translate(x, y) 移动

  translateX(x)

  translateY(y)

  translate3D(x, y, z) 使用这个, 因为有伪GPU加速

2. transition: css属性 执行时间 动画效果 延迟几秒执行

3. animation:

  @keyframes 名称 {

    0% {

      margin-left: 100px;

      width:100px;

    }

    40% {

      margin-left: 0;

      width:200px;

    }

    100% {

      margin-left: 0;

      width:200px;

    }

  }

  animation: 名称 执行时间 动画 延迟执行时间 执行次数 正反是否都有动画

  * css3动画(怎么说):

    transform transition animation @keyframes, transform一般是和transition联用,

    transform 包括 translate translateX translateY translate3D, 我们平时用 translate3D(0, 0, 0)

    translate3D 有伪GPU渲染, rotate, scale, transition css属性 执行时间 执行动画 延迟的执行时间.

    animation 和 @keyframes联用, @keyframes 名字 里面是从0%开始一直设置到100%, animation: 名字 执行时间

    执行动画 延迟时间 执行次数 正反是否都有动画, transform 需要事件触发, animation自动执行

    div {

      transform: translate3D(x, y, z);

      transition: transform ....

    }

    div:hover {

      transform: translate3D(x, y, z);

    }


 

* 如何美化 checkbox: -webkit-appearance: none

* rgba 和 opacity 区别:

rgba 只作用于当前元素

opacity 作用于所有元素

* 如何使用CSS实现硬件加速, GPU渲染

transfrom: translate3D(100, 0, 0): 写上Z轴 会使用伪 GPU渲染

will-change: 绝对的GPU渲染

* defer async

    <script defer></script>

    <script async></script>

    defer要等到整个页面 渲染结束 (DOM 结构完全生成,以及其他脚本执行完成),才会执行

    async一旦下载完, 渲染引擎就会中断渲染, 执行这个脚本以后, 再继续渲染

    如果有多个defer脚本, 会按照它们在页面出现的顺序加载

    而多个async脚本是不能保证加载顺序的

    defer是“渲染完再执行”, async是“下载完就执行”


 

* 如何截取多行文字

display: -webkit-box;

overflow: hidden;

-webkit-line-clamp: 2;

-webkit-box-orient: vertical;


 

* position 有哪些属性:

    absolute, sticky(粘性定位没触发时 === relative, 触发了 === fixed) , fixed, relative

    0,0点:

      absolute: 从当前的父元素一级一级的网上找 哪个父元素设置了 relative 或者 absolute 就以这个父元素的左上角为 0, 0

      fixed: 浏览器的左上角

      relative: 正常的位置 相对于自身


 

* 重绘和回流

重绘: DOM树没有元素增加或删除, 只是样式的改变, 针对浏览器对某一元素进行单独的渲染, 这个过程就叫做重绘

回流: DOM树中的元素被增加或者删除, 导致浏览器需要重新的去渲染整个DOM树, 回流比重绘更消耗性能, 发生回流必定重绘, 重绘不一定会导致回流

引起回流

  添加或者删除可见的DOM元素

  元素的位置发生变化

  元素的尺寸发生变化(包括外边距、内边距、边框大小、高度和宽度等)

  内容发生变化 文本或者图片被另一个不同尺寸的图片所代替

  页面开始渲染的时候

  浏览器的窗口尺寸变化(回流是根据视口的大小来计算元素的位置和大小)


 

引起重绘

    color

    visibility

    background

    box-shadow

    background-size


 

* 怎么减少重绘 回流

    1. css

      1). 不要使用 table 布局,可能很小的一个改动会造成整个 table 的重新布局

      2). 集中改变样式className

      3). 使用 position: absolute / fixed; 脱离文档流

      4). 利用 transform translate 去代替 left top

      5). 避免使用CSS表达式 例如: calc()

    * js

      1). 动态插入多个节点时,可以使用文档碎片(DocumnetFragment),创建后一次插入,避免多次的渲染性能

      2). 使用 resize 事件时,做防抖和节流处理

      3). 可以先为元素设置为不可见: display: none, 操作结束后再把它显示出来。

【React 部分】

* 错误边界:

    作用:

      getDerivedStateFromError 是个静态方法, 用来显示降级 UI

      componentDidCatch 请求后台接口 把错误上报

    具体实现:

      class ErrorBoundary extends React.Component {

        constructor(props) {

          super(props);

          this.state = {

            errorInfo: null

          };

        }

        static getDerivedStateFromError (error) {

          // 更新 state 使下一次渲染可以显示降级 UI

          return { errorInfo: true }

        }

        // 报错

        componentDidCatch(error, errorInfo) {

          // 请求后台接口 把错误上报

          console.log(errorInfo, 'errorInfo');

          console.log(error, 'error');

        }

        render() {

          if (this.state.errorInfo) {

            return (

              <div>

                错误替代展示

              </div>

            );

          }

          return this.props.children;

        }  

      }


 

* useEffect 可以模拟哪些生命周期

  1. componentDidMount // 加载完成的

      useEffect(() => {

      }, [])

  2. componentDidUpdate // 更新阶段

      useEffect(() => {

      }, [有值])

  3. componentWillUnmount // 卸载阶段

      useEffect(() => {

        return () => {

        }

      }, [])


 

* useRef 和  createRef 区别

    useRef 只执行一次, 有初始值, 可以用于保存上一次的值

    createRef 每次组件渲染都会执行, 没有初始值


 

* setInterval, setTimeout 特性

const [time, setTime] = useState(3)

useEffect(() => {

  // setInterval, setTimeout 会形成闭包, 会保存 time 的复制值, 该值与外面的 time

  // 不是同一个值

  setInterval(() => {

    setTime(time - 1)

    // 始终输出 3

    console.log(time, 'xxx');

  }, 1000)

}, [])


 

* hook 规则

* hook 不要在循环, 条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们

* hook 只能在函数组件里调用 (包括自定义 hook), 不能用在 普通函数 或者 类组件里

* hook 为什么必须要放到最顶层?

  hook 必须按照一定的顺序执行, (举个例子) 否则就会报错, 所以必须放在最顶层


 

* react 常用hook

useState, useEffect, useRef, useCallback, useMemo, useContext, useImperativeHandle

useLayoutEffect

useState(值)

useState(() => {

  return 值

})



 

* react 可不可以用map循环的index当做key:

可以, 但可能会有问题, 例如以前讲的小花, 小蓝, 小白那个表单例子, 所以最好使用唯一 id 或者给唯一 key


 

* react 在⽣命周期中的哪⼀步你应该发起 AJAX 请求: useEffect 或者 componentDidMount


 

* shouldComponentUpdate 作用: 优化子组件是否渲染


 

* dva的使用方式:

namespace, state, reducers, effects, 具体的参数使用方法也要背出来

画了一个股票趋势图 当时用 canvas画的


 

* React 父子组件传值方式:

1. props

2. Context

3. redux dva

4. 发布订阅

5. useImperativeHandle + forwardRef

6. render props

7. Hoc


 

* Context 用法: 先定义一个单独的js, 用来导出 context, export const ThemeContext = React.createContext(默认值)

               然后再父组件里 引入这个 context, 默认值 是整个<ThemeContext.Provider>不写, 会走默认值

<!-- 父组件 -->

<ThemeContext.Provider value="参数">

  <组件 />

</ThemeContext.Provider>

<!-- 子组件 -->

子组件也引入 封装好的 context, 然后使用 const theme = useContext(ThemeContext) 获取值

reudx 他封装了一个 HOC的高阶组件,在这里设置了 setState 属性,

监听所有的 st阿特xiugai ,一旦改变了 会调用 setState 重新触发页面


 

* 什么是 redux:

  作用: reudx 就是react的状态管理工具

  redux 工作流程: (视图触发 action, action 触发 reduce, reduce 修改state 重新渲染视图)

                 view(视图) -> action -> reduce (reduce 修改state) -> 重新渲染视图

  中间件: redux react-redux redux-actions redux-persist redux-promise redux-thunk

  redux 可以结构出来 createStore, applyMiddleware, 通过 createStore(reduce, 中间件)

  创建一个 store, applyMiddleware 是用来加载中间件的


 

* redux 原理

let createStore = (reducer) => {

  // 获取状态对象

  let state;

  // 存放所有的监听函数

  let listeners = [];

  // 获取所有 state 的方法

  let getState = () => state;

  // 提供一个方法供外部调用派发 action

  let dispath = (action) => {

    //调用管理员reducer得到新的state

    state = reducer(state, action);

    //执行所有的监听函数

    listeners.forEach((l) => l())

  }

  //订阅状态变化事件,当状态改变发生之后执行监听函数

  let subscribe = (listener) => {

    listeners.push(listener);

  }

  dispath();

  return {

    getState,

    dispath,

    subscribe

  }

}


 

* react 脚手架:

  1. umi + Dva

  2. create-react-app

  3. next.js

  4. remix


 

* react 状态管理工具:

  1. mobx

  2. Dva: dva 基于 redux 和 redux-saga 封装的, 还额外内置了 react-router 和

    fetch 所以也可以理解为一个轻量级的应用框架

  3. redux

  4. redux-saga


 

【HTML 部分】

* html语义化

ol: 有序列表

dl dt dd: 定义列表

p: 段落

h1 - h6: 标题

em: 强调(弱一点)

strong: 强调(强一点)

header: 定义页眉

footer: 定义页脚

article: 文档内的文章


 

* HTML5 新特性

  canvas, svg, localStorage, sessionStorage, WebSocket, FormData, Worker,

  pushState, replaceState, audio, video, postMessage


 

* 说说 title 和 alt 属性

  1. 两个属性都是当鼠标滑动到元素上的时候显示

  2. alt是img的特有属性, 是图片内容的等价描述, 图片无法正常显示时候的替代文字


 

* viewport

  <meta name="viewport" content="width=device-width,initial-scale=1.0,minimu" />

  width 设置viewport宽度, 为⼀个正整数, 或字符串‘device-width’

  device-width 设备宽度

  height 设置viewport⾼度, ⼀般设置了宽度, 会⾃动解析出⾼度, 可以不⽤设置

  initial-scale 默认缩放⽐例(初始缩放⽐例), 为⼀个数字, 可以带⼩数

  minimum-scale 允许⽤户最⼩缩放⽐例, 为⼀个数字, 可以带⼩数

  maximum-scale 允许⽤户最⼤缩放⽐例, 为⼀个数字, 可以带⼩数


 

* 图片优化:

  懒加载 雪碧图(怎么更改) icon base64 webp


 

* 请求报文

  1. 请求协议 HTTP 请求地址

  2. 请求方法 post

  3. 请求头部


 

# 响应报文

  1. HTTP响应报文也由三部分组成: 响应行、响应头、响应体

<!-- 强缓存 1.0 -->

第一次请求后台接口的时候, 后台会返回一个 Expires, 他的值是一个时间

第二次再请求后台接口, 浏览器会把我们电脑的系统时间 和 Expires的时间 进行比较, 如果时间相同 不发送请求 直接从浏览器里取出数据给前端, 如果不同

       重新发送请求给后台, 然后后台返回新的 Expires缓存时间

<!-- 强缓存 1.1 -->

第一次请求后台接口的时候, 后台会返回一个 Cache-Control: max-age=3000s

第二次请求后台接口的时候, 浏览器会看 第二次请求和第一次请求的时间间隔 如果在3000s 以内 不发送请求 直接从浏览器里取出数据给前端, 然后重置 max-age=3000s

       如果超过了 3000s, 重新发送请求给后台, 然后后台返回新的 max-age的缓存时间

<!-- 协商缓存 1.0 -->

第一次请求后台接口的时候, 后台会返回一个 Last-Modified请求头给浏览器, Last-Modified保存的是一个时间

第二次请求后台接口浏览器会自动带上 If-Modified-Since = 时间 请求头给后台, 后台比较 浏览器发送的时间 和 后台存储的时间 是否相同, 如果相同 后台返回

       304状态码, 表示前端使用浏览器缓存, 如果不同, 后台返回 200 和 Last-Modified = 新的时间

<!-- 协商缓存 1.1 -->

第一次请求后台接口, 后台返回一个 ETag = 哈希值

第二次请求后台接口浏览器会自动带上 If-None-Match = 哈希值 请求头给后台, 后台比较 浏览器发送的哈希值 和 后台存储的哈希值 是否相同, 如果相同 后台返回

       304状态码, 表示前端使用浏览器缓存, 如果不同, 后台返回 200 和 ETag = 哈希值



 

* 浏览器缓存: 浏览器缓存分为强缓存和协商缓存

  强缓存:

    (Expires 保存一个过期时间, 只要没到时间就会走强缓存)

    Expires (http1.0规范)

    (max-age保存多少秒, 下次请求只要没超过这个值 就会走强缓存, 然后重置这个保存的秒数, 如果超过了这个值, 就不走强缓存)

    Cache-Control: max-age=xxxs (http1.1规范)

  协商缓存:

    Last-Modified (1.0 值为资源最后更新时间, 随服务器response返回)

    If-Modified-Since (通过⽐较两个时间来判断资源在两次请求期间是否有过修改, 如果没有修改, 则命中协商缓存)

    ETag (1.1 表示资源内容的唯⼀标识, 随服务器 response 返回)

    If-None-Match (服务器通过⽐较请求头部的 If-None-Match 与当前资源的 ETag 是 否⼀致来判断资源是否在两次请求之间有过修改, 如果没有修改, 则命中协商缓存)


 

* 状态码:

200 表示从客户端发来的请求在服务器端被正确处理

301 永久性重定向 表示资源已被分配了新的 URL ni TM 瞎?

302 临时性重定向 表示资源临时被分配了新的 URL

304 使用缓存的内容

401 表示发送的请求需要有通过 HTTP 认证的认证信息

404 表示在服务器上没有找到请求的资源

500 表示服务器端在执⾏请求时发⽣了错误



 

【Javascript 部分】

* 0.1 + 0.2为什么不等于0.3 ? 因为计算机的浮点运算

实现方式: (0.1 * 100 + 0.2 * 100) / 100


 

* == 和 ===有什么区别

== 比较值, === 比较值 和 类型


 

* 闭包: 可以访问函数内变量的函数叫做闭包

  (释放闭包 变量 = null)

  什么时候使用闭包: 封装比较复杂的公共方法的时候 会使用闭包

  闭包优点: 不污染全局变量

  缺点: 可能会造成内存溢出

  为什么闭包可能会造成内存溢出: 全局变量引用了闭包里的变量, 全局变量不会释放的, 所以闭包里的变量不能释放

  function fun () {

    let sum = 0

    return () => {

      sum += 1

      console.log(sum);

    }

  }

  const f = fun()

  f() // 1

  f() // 2

  f() // 3

  释放闭包

  f = null


 

* 数组8种循环

forEach, filter, map, find, findIndex, reduce, (some, every)

  const arr1 = [1, 2, 3, 4]

  // map 返回一个长度不变的新数组, 不能终止循环

  const x1 = arr1.map(dt => dt + 1)

  // [undefined, undefined, 3, 4]

  arr1.map(dt => {

    if (dt > 2) {

      return dt

    }

  })

  reduce: reduce 有2个参数, 第一个参数是一个回调函数, 第二个参数是个默认值, 默认值的可以是任意, 如果没有默认值

  回调函数里的第一个参数 取数组的第一个值, 有默认值, 数组的第一个参数等于这个默认值

  // 最终返回 reduce 里 retrun 出来的值, 不能终止循环

  // 第一次: v1 = 1, v2 = 2

  // 第二次: v1 = 3, v2 = 3

  // 第三次: v1 = 6, v2 = 4

  // 最终 返回 10

  arr1.reduce((v1, v2) => {

    return v1 + v2

  })

  // 第一次: v1 = {}, v2 = 1

  // 第二次: v1 = { 1 }, v2 = 2

  // 第三次: v1 = { 1, 2 }, v2 = 3

  // 第四次: v1 = { 1, 2, 3 }, v2 = 4

  // 最终返回 { 1, 2, 3, 4 }

  arr1.reduce((v1, v2) => {

    v1[v2] = v2

    return v1

  }, {})

  // filter: 用来过滤, 返回一个长度可变的数组, 不能终止循环

  arr1.filter(dt => dt > 2)

  // forEach: 普通循环, 没有返回值, 不能终止循环,

  arr1.forEach(dt => { })

  // find: 查找一个值, 返回查找到的当前值, 可以终止循环

  arr1.find(dt => {

    return dt === 3

  })

  // findIndex: 查找一个值, 返回查找到的值的下标, 可以终止循环

  arr1.findIndex(dt => {

    return dt === 3

  })

call apply bind 这3中 都可以

* 普通函数 this定义: this 代表当前正在执行的对象

* 箭头函数 this定义: 箭头函数没有自己的 this, 是在定义的时候获取上下文的 this, 箭头函数的 this 一旦固定了 就不用不会再变了

* JS中浅拷贝 和 深拷贝:

深拷贝: 递归 和 JSON.stringify, 其他都是浅拷贝, JSON.stringify 会过滤掉空值 一般不用

浅拷贝方法: Object.assign, ...扩展运算符, Object.create


 

* js 事件循环机制: 基础任务, 宏任务, 微任务, 先执行基础任务, 然后执行 微任务, 再执行宏任务

微任务: promise, async, await

宏任务: setTimeout, setInterval, I/O


 

* 防抖, 节流:

防抖: 多次触发 如果没到时间 那么一直不会触发回调函数

用途: 搜索 提交

function debounce(fn, delay){

  let timer = null;

  return function(){

    clearTimeout(timer);

    timer = setTimeout(()=> {

      fn.apply(this, arguments);

    }, delay)

  }

}

节流是让你的操作, 每隔一段时间触发一次。适用于多次触发要多次生效的场景。

用途: 搜索, mousemove

function throttle(fn, delay){

  let valid = true;

  return function(){

    if(valid) {

      setTimeout(()=> {

        fn.apply(this, arguments);

        valid = true;

      }, delay)

      valid = false;

    }

  }

}


 

* bind apply call 区别:

bind: 函数.bind(对象, 参数)

作用: 改变 this 指针

特点: 返回一个绑定了this的新函数, 不立刻执行

apply: 函数.apply(对象, [参数1, 参数2, ...])

作用: 改变 this 指针

特点: 立刻执行

call: 函数.call(对象, 参数1, 参数2, ...)

作用: 改变 this 指针

特点: 立刻执行



 

* 箭头函数的特点 (普通函数箭头函数的区别):

  1. 箭头函数没有自己的this

  2. 箭头函数不能当构造函数使用

  3. 箭头函数没有 arguments

  4.

对,但是修改以后各自是各自的,

* in hasOwnProperty 区别:

in 操作符用来判断某个属性属于某个对象 可以是对象的直接属性 也可以是通过 prototype 继承的属性

hasOwnProperty 当前属性必须是 直接属性

props

this.$emit

eventBus

* cookie localstorage session 区别: (cookie 可以跨域 设置domain, 【跨域是指共享token】)

  1. 请求接口会自动带上 cookie

  2. sessionStorage 和 localStorage 不会⾃动把数据发给服务器, 仅在本地保存

  3. cookie 数据⼤⼩不能超过4k

  4. sessionStorage 和 localStorage 虽然也有存储⼤⼩的限制, 但⽐ cookie ⼤得 多, 可以达到5M或更⼤

  5. localStorage 存储持久数据, 浏览器关闭后数据不丢失除⾮主动删除数据

  6. sessionStorage 数据在当前浏览器窗⼝关闭后⾃动删除

  7. 设置的 cookie 过期时间之前⼀直有效, 即使窗⼝或浏览器关闭


 

* 为什么会有跨域: 因为浏览器的同源策略(CORS) 即协议, 域名, 端口 只要有一个不同 就会产生跨域

* 怎么解决跨域:

jsonp

  <script src="后台接口地址"></script>

  后台返回一个函数调用方法 例如 abc(数据)

  前端在js里设置一个 abc 方法, 后台调用 前端的abc方法传递数据

node代理

  node 请求后台接口(node请求后台接口不走浏览器所以不会跨域), 前端请求node的接口

  node怎么做跨域的: node 在本地封装多个接口, 前端请求本地的node接口, node再去请求后台接口 返回数据

后台设置请求头

  response.setHeader("Access-Control-Allow-Origin", "*")


 

* 事件代理优点:

  1. 新插入标签可以不用注册事件

  2. 减少内存占用


 

* 页面埋点 PV, UV: 自定义一个调用方法 例如 sendPv 像如下传递参数

sendPv({

  'pid': '111'

})


 

* RESTful规范

1. GET: 从服务器取出资源(一项或多项)

2. POST: 在服务器新建一个资源)

3. PUT: 在服务器更新资源(客户端提供改变后的完整资源)

5. DELETE: 从服务器删除资源



 

* 设计模式: 观察者模式, 策略模式, 单例 多例模式, 迭代器模式, 工厂模式

  // 发布 订阅 存回调函数对象

  const obj = {

    // 存所有回调函数的对象

    list: {},

    // 发布方法

    publish (key) {

      this.list[key].map(dt => {

        dt()

      })

    },

    // 订阅

    subscribe (key, callback) {

      if (!this.list[key]) {

        this.list[key] = []

      }

      this.list[key].push(callback)

    },

  }

  obj.subscribe('vip', () => {

    console.log('加载 /home');

  })

  obj.subscribe('vip', () => {

    console.log('加载 /home2');

  })

  obj.subscribe('vip', () => {

    console.log('加载 /home3');

  })

  obj.subscribe('svip', () => {

    console.log('加载 /home4');

  })

  // 登录

  obj.publish('svip')

* Promise

Promise: 解决回调地狱

三种状态: 进行中, 已成功, 已失败, 状态一旦固定了 状态就永远不会再变了

Promise.reject 立刻拿到一个 已失败的 promise

Promise.resolve 立刻拿到一个 已成功的 promise

Promise.all([p1, p2, p3]): 参数是一个数组, 数组里面包含N个promise, 只有所有的promise 都是已成功, 整个Promise.all的状态才是已成功,

                           只要有一个返回已失败, 整个Promise.all的状态就是已失败

Promise.race([p1, p2, p3]): 参数是一个数组, 数组里面包含N个promise, 最先执行完的 promise 的状态就是 Promise.race 的状态



 

* 移动端如何做手机适配:

  可以使用 vh, vw, vmax, vmin 或者 rem, rem 是要引入一个阿里js, 在 html 标签上添加一个 font-size


 

* 移动端如何做绝对一像素:

方案1: 设置 meta viewport 属性 整个页面 initial-scale: 0.5

方案2: 单独设置当前元素 tranform: scale(0.5);


 

* 前端权限: 静态权限, 动态权限

1. 静态权限实现方案: (权限固定的, 前端自己保存 Menu 的列表)

前端先记录一个路由权限数组, 例如

const obj = [

  pt: ['/company/friendCircle'],

  vip: ['/vip1', '/vip2'],

]

然后登录以后后台返回一个该用户的权限值, 例如 pt, 然后前段通过 obj 匹配该用户能访问的路由列表,

如果当前访问的 pathname 在 可以访问的路由数组里, 那用户访问该路由, 否则不能访问该路由


 

2. 动态路由实现方案:

前端不记录权限数组, 直接返回一个 Menu 列表后台, 前端递归解析列表数据, 展示 menu菜单, Menu 的数据格式是前端给后台的,

具体需要的哪些字段是前端定的

前端循环后台给的 Menu 列表, 把所有 path 路径保存到一个数组里 [path, path, ....], 然后在全局路由守卫里判断每次用户访问的路由 pathname 是否在这个

数组里, 如果在让用户访问, 如果不在, 跳转到登录页面


 

* Websocket: 长链接

const ws = new WebSocket('ws://地址', 参数)

Websocket 第二个参数可以做鉴权, 传递之后是放到请求头里 const ws = new WebSocket('ws://地址', 登录token)

我们使用插件: socket.io-client

<!-- 普通 -->

ws://

<!-- 加密 -->

wss://

事件

open: ws.onopen 连接建立时触发

message: ws.onmessage 客户端接收服务端数据时触发

error: ws.onerror 通信发生错误时触发

close: ws.onclose 连接关闭时触发

send: 发送数据

close: 关闭数据


 

ws.onopen = function () {

  // Web Socket 已连接上,使用 send() 方法发送数据

  ws.send("发送数据");

    alert("数据发送中...");

  }

}

ws.onmessage = function (evt) {

  var received_msg = evt.data;

  alert("数据已接收...");

}

ws.onclose = function () {

  // 关闭 websocket

  alert("连接已关闭...");

}


 

WebSocket 心跳检测: 检测前端和后台链接是不是还存在, 如果前端已经断开链接, 通知后台, 后台关闭连接

实现: setInterval 每隔10s时间调用 send()

WebSocket 实际用法:

1. 网站右上角的实时消息

2. echarts 实时图表的数据


 

* 前台路由 后台路由区别

前台路由是页面路由

后台路由是接口

const

* 浏览器渲染html的过程 https://www.cnblogs.com/whalesea/p/10980849.html

    1. 解析html

    2. 构建dom树

    3. dom树结合css文件 构建呈现树

    4. 布局

    5. 绘制

* 链表: 单向链表, 双向链表, 循环链表

* 对象 e6 新属性:

Object.values(对象)

Object.keys(对象)

Object.entries(对象)

Object.defineProperties(obj, props)

Object.prototype Function.prototype

例:

var obj = {};

Object.defineProperties(obj, {

  'property1': {

    value: true, // 值

    writable: true // 是否可读写

  },

  'property2': {

    value: 'Hello',

    writable: false

  }

});


 

* async:

    1. 定义一个函数 在函数前面写 async, 在函数内部可以写 await, await 后面可以跟任意的类型, 一般我们是跟 promise

       await 后面的 promise 只有是已成功, 才会继续往下执行其他的代码

    2. await 有返回值

    3. 如果 前面的 await 异常了, 还想继续往下执行

      1) try catch

      2) catch

async function fn () {

  const x1 = await 方法()

  const x2 = await 方法()

}

三个接口 A, B, C, 然后 A B 没有任何依赖, C 一来 AB的返回值, 怎么实现继

async function fn () {

  const x1 = await Promise.all([A, B])

  const x2 = await C(x1)

}


 

【git部分】

* 进公司之后 git 的配置

* git config --global user.name "gitlab右上角的名字"

* git config --global user.email git账号是公司给你新开的

* 配置 公钥 私钥

  ssh-keygen -t rsa -C "公司给你的登录账号"

  cat ~/.ssh/id_rsa.pub // 打开公钥, 拷贝所有内容

* git clone: 拷贝 git 项目到本地

* git status: 查看本地文件状态

* git log: 查看提交日志

* git add .: 跟踪文件 把代码保存到缓冲区

* git commit -m '描述文字(有意义的)': 把项目提交到本地仓库

* git push origin 分支名: 提交到远程仓库 (当前在哪个分支下面 就只能往当前这个分支 push)

* git branch -v: 查看本地分支, *号表示当前在哪个分支上

* git branch -a: 查看本地和远程的分支

* git checkout -b 分支名: 基于本地分支创建自己本地的分支

* git checkout 分支名: 切换分支 (只有当前分支上没有任何修改了 才能切换分支)

* git checkout -b 分支名 origin/分支名: 基于远程分支创建自己本地的分支

* git pull origin 分支名: 从远程分支拉取最新代码合并到你当前分支

* git fetch: 同步远程分支

* git diff: 比较代码

* git reset --hard commit哈希值: 回滚代码

* git stash save '描述': 缓存 stash

* git stash list: 查看存储的栈列表

* git stash apply stash@{1}: 回到某一个存储上 从0开始

* git stash apply: 回到最近储藏

* git push -f origin 分支: 强制提交

* git revert commit的哈希值: 撤回某一条提交

* git reset .: 撤回 add

* git checkout .: 撤回所有修改的文件

* git reflog: 查看所有的日志

* git cherry-pick

代码回滚有4种方式:

  1. git reset --hard commit哈希值, 它会回滚回退当前commit之前的所有提交, 所以回滚的时候要注意, 别把其他人的代码也回滚了

  2. git revert commit的哈希值: 撤回某一条提交, 不会影响其他的提交

  3. git reset --mixed HEAD~1

     回退一个版本,且会将暂存区的内容和本地已提交的内容全部恢复到未暂存的状态,不影响原来本地文件(未提交的也不受影响)

  4  git reset --soft HEAD~1

     回退一个版本,不清空暂存区,将已提交的内容恢复到暂存区,不影响原来本地的文件(未提交的也不受影响)


 

* 事件

  1. 注册事件

    1) DOM0

      el.onclick = () => {

       

      }

      给相同节点 连续注册2次 后面的会覆盖前面的

      el.onclick = () => {

      }

   

    2) DOM2

      false: 未开启捕获, 从里向外执行事件

      true: 开启捕获, 从外向里执行事件

      el.addEventListener(事件, 回调函数, false)

      2. 删除事件

      el.removeEventListener(事件, 回调函数, false)

      3. 取消默认事件

      event.preventDefault()

      4. 阻止冒泡

      event.stopPropagation()

      5. 事件阶段

      捕获阶段 目标阶段 冒泡阶段


 

* new 类 经过哪几步

    1. 创建一个空对象 {}

    2. 执行构造器 把 {} 赋值给 this

    3. 给 {} 赋值

    4. 返回对象给类的实例


 

  * 单点登录

    1. cookie 设置 domian 域名

    2. iframe + postMessage

      发送页面

      // message: 我要发送的 token

      // targetOrigin: 另外一个项目的URL地址

      iframeWindow.postMessage(token, targetOrigin)

      <iframe src="另外一个项目的URL地址"></iframe>

     

      在另外一个项目监听

      window.addEventListener('message', e => {

        // e.data 就是 token

        console.log(e.data);

      }, false);

    3. sso


 

* class

    要背:

      1) 类怎么定义

      2) 类的静态方法怎么定义

      3) 继承

      4) 子类为什么要调用 super

  1. class 怎么实现继承

    class A {

      constants () {

        // 构造函数

      }

    }

    class B extends A {

      // constructor 不写默认会调用

      constructor () {

        // 调用 super 是因为 子类没有自己的 this, 需要继承父类的 this

        // super === 父类的构造函数 (A 的 constants)

        super()

      }

    }


 

* httponly: 不可修改的 cookie

  为什么安全: 因为前端没法改


 

* cdn oss:

    cdn 是内容分发网络, 是一组分散在不同地理位置的web服务器 打开网站时 找最近的cdn服务器访问 可以加速访问网站速度

    oss 存储文件的服务器 公司项目一般存在 oss 上


 

* window.onload 页面dom 和 image js css 全部加载完成触发

  window.addEventListener('DOMContentLoaded', () => {

    // 页面 dom 加载完成

    // 等所有的 script link img iframe

  })


 

* 前端性能优化

  javascript:

    * 把 script 放在 html 底部

    * 使用 e6, 7 减少代码量

  css:

    * 尽可能使用 css3 属性

  图片:

    * 雪碧图

    * webp

    * icon

    * 图片懒加载

    * 避免图片src属性为空

  http:

    * 尽可能减少页面 http 请求

    * cdn

    * 浏览器缓存 包括 强缓存 和 协商缓存

    * 避免重定向

    * 开启 Gzip 压缩

  react, vue:

    * 路由懒加载

    * SSR (前后端同构)

  webpack:

    * alias 配置别名

    * extensions 在导⼊语句没带⽂件后缀时 webpack会⾃动带上后缀后

    * externals 配置某些文件不打包

    * ExtractTextPlugin 拆分 CSS 打包

    * DllPlugin 第三方文件单独打包缓存

    * entry 拆分多个模块



 

* 移动端开发:

    wap: 手机浏览器看的网站 wap

    hybrid: 混合开发 HTML5 + CSS3 + 原生壳(安卓, IOS壳)

    原生app: 开发系 安卓 IOS APP (React Native, Flutter)

   


 

* 内存泄漏:

    1. 闭包

    2. 死循环

   

* 网页白屏可能是哪些原因引起的:

    1. 网络延迟 加载慢

    2. css 隐藏了页面模块

    3. js 加载慢阻塞整个页面加载

    4. 进行比较大运算 页面卡死

    5. 路由错误 加载页面没有内容



 

* 原型链 prototype

    创建一个函数 或者 class 类, js会自动为函数或者类添加一个 prototype 属性, 默认包含2个属性

    constructor 和 __proto__, constructor 指向当前类, __proto__ 指向上一级 prototype

    这就形成了原型链


 

* 继承

  // 1. E6 class 继承: 引用类型 共享的

  // 2. 原型链继承: 引用类型 共享的

  // 3. 寄生组合式继承 (最终寄生组合式继承, 因为如果有引用类型其他继承是共享的)

  function inheritPrototype(subType, superType){

    var prototype = Object.create(superType.prototype); // 创建对象 创建父类原型的一个副本

    prototype.constructor = subType;                    // 增强对象 弥补因重写原型而失去的默认的constructor 属性

    subType.prototype = prototype;                      // 指定对象 将新创建的对象赋值给子类的原型

  }

  // 父类初始化实例属性和原型属性

  function SuperType () {

    this.colors = ["red", "blue", "green"];

  }

  // 借用构造函数传递增强子类实例属性(支持传参和避免篡改)

  function SubType () {

    SuperType.call(this);

  }

  // 将父类原型指向子类

  inheritPrototype(SubType, SuperType);

  // { colors: ["red", "blue", "green"];}

  var instance1 = new SubType();

  // { colors: ["red", "blue", "green"];}

  var instance2 = new SubType();

  instance1.colors.push("2"); // ["red", "blue", "green", "2"]

  instance1.colors.push("3"); // ["red", "blue", "green", "3"]

  console.log(instance2.colors, 1111);



 

2 3 都用过




 

* 需求文档: prd

* 做项目流程 (PM 产品, QA 测试, FE 前端, UI 设计, dev 后台)

    1. 项目需求评审 大概2-3次

      * 第一次 提很多不合理问题 PM记录 改第二版 (第一次评审前, PM 会把需求发给你所有人)

      * 第二次 提很多不合理问题 PM 继续改

      * 第三次 终审

      // 敏捷开发: 一个项目 一组固定的人 (QA FE DEV) 一个月迭代2版

      // 半个月迭代一次 14 - 4 = 10, 4天 6天

    2. UI出图

    3. 前端 后台给出具体的确定排期 (UI给图)

    4. QA 给出测试排期 (技术评审 - 前端后台 对具体的视线方案)

    5. 提测改bug

    6. 发布上线, 跟踪线上问题


 

* apply:

    Function.prototype.apply = function (context, args) {

      // 不传默认是全局, window

      context = context || window

      // args不传时默认是空数组, 防止下面用spread操作符时报错

      args = args ? args : []

      // 把this存到context.fn, 这里的this是调用的函数

      context.fn = this

      // 执行调用的函数, this指向context, 参数用spread操作符扩展

      const res = context.fn(...args)

      // 删除, 不污染context

      delete context.fn

      // 返回res

      return res

    }

  call:

    Function.prototype.call = function (context, ...args) {

      // 不传默认是全局, window

      context = context || window

      // args不传时默认是空数组, 防止下面用spread操作符时报错

      args = args ? args : []

      // 把this存到context.fn, 这里的this是调用的函数

      context.fn = this

      // 执行调用的函数, this指向context, 参数用spread操作符扩展

      const res = context.fn(...args)

      // 删除, 不污染context

      delete context.fn

      // 返回res

      return res

    }


 

* 从输入一个URL到浏览器页面展现到底发生了什么:

    输入网址

    DNS解析

    建立TCP/IP链接(三次握手四次挥手)

    发送HTTP请求

    服务器处理请求

    服务器返回HTTP响应

    浏览器渲染页面并展现(结合html渲染详细说)

    断开连接


 

* 怎么设置git 不上传某些文件:

    设置 .gitignore


 

* 为什么会有跨域: 因为浏览器的同源策略(CORS) 即协议, 域名, 端口 只要有一个不同 就会产生跨域

* 怎么解决跨域: jsonp, node代理, 后台设置请求头

* 上线后怎么解决跨域 设置 nginx


 

* 怎么看依赖包文件版本: pageage.json


 

* hash history 区别:

    1. hash 带 #, history 不带 #

    2. history 上线后刷新页面后会展示404 找不到页面(设置nginx可以解决这个问题), hash刷新正常展示


 

* 项目上传 使用 阿里云


 

* 为什么会有跨域: 因为浏览器的同源策略(CORS) 即协议, 域名, 端口 只要有一个不同 就会产生跨域

* 怎么解决跨域:

  jsonp

    <script src="后台接口地址"></script>

    后台返回一个函数调用方法 例如 abc(数据)

    前端在js里设置一个 abc 方法, 后台调用 前端的abc方法传递数据

  node代理

    node 请求后台接口(node请求后台接口不走浏览器所以不会跨域), 前端请求node的接口

  后台设置请求头

    response.setHeader("Access-Control-Allow-Origin", "*")



 

* add(1)(2)(3)

  function add (a) {

    return b => {

      return c => {

        return a + b + c

      }

    }

  }


 

* fetch axios 区别:

    fetch 是浏览器自带的

    fetch只对网络请求报错 对400 500都当做成功的请求

    fetch默认不会带cookie

    fetch不支持 abort 不支持超时控制

    axios 体积小 没有fetch的这些问题

* umi4 && umi3区别

    * umi4 只能通过 plugin.ts 配置 index.html

    * 通过 useLocation, useParams, useSearchParams 获取路由参数

    * 支持 vue

    * 默认集成 react18, router6

    * 支持 vite webpack 2种构建方式

    * children 改成了 <Outlet />

    * 构建速度更快


 

* javascript 数据类型

    boolean, null, undefined, number, string, symbol, BigInt, object

    基础类型 引用类型 区别: 基础类型存在栈里的, 引用类型存在堆里的


 

* typeof 是否能正确判断类型 ?

  1. 不能正确判断引用类型 数组([]) 和 对象({}) 都会返回 'object'

  2. 使用 Object.prototype.toString.call(对象) 获取对象类型


 

* JS中浅拷贝 和 深拷贝

深拷贝: 递归 和 JSON.stringify, 其他都是浅拷贝, JSON.stringify 会过滤掉空值 一般不用

浅拷贝方法: Object.assign, ...扩展运算符, Object.create

公司使用 _.cloneDeep 实现深拷贝

区别: 浅拷贝共享引用类型, 深拷贝所有数据不共享包括引用类型


 

## 移动端

  一. 移动端类型

    wap (手机浏览器 输入网址)

    hybrid (混合 前端 + 安卓 前端 + IOS) 壳子原生 + H5 + C3

    原生app (安卓 IOS) / Flutter RN uniapp taro

  二. hybrid 壳子

    安卓 WebView 封装了一个自定义的浏览器(https://www.douyin.com)

    IOS WKWebView 封装了一个自定义的浏览器

  三. 布局类型

    自适应布局

      rem 以 html 的 fong-size 作为参考

      vh: 整个页面的宽度100vh

      vw: 整个页面的宽度100vw

    响应式布局 @media

    @media screen and (max-width: 1000){

      .div {

        color:

        flex:

      }

    }

  四. 前端 + 安卓 IOS 代码交互

    ios: WebViewJavascriptBridge

    android: jsbridge

  五. 调试 hybrid

    1. 手机连接USB线 输入chrome://inspect/#devices

    2. 点击inspect 进行调试

    3. 手机 和 电脑 必须是同网段



 

【React】

* 高阶组件(HOC)

    1. 定义: 高阶组件是参数为组件, 返回值为新组件的函数

    2. 都用过哪些高阶组件: connect, withRoute

    function Hoc (Com) {

      return () => {

        return <Com />

      }

    }


 

* setState 是同步还是异步

    setState 有同步也有异步

    异步: react 原生事件里的 setState, 写在生命周期里的 setState

    同步: 原生js事件, setTimeout, setInterval, ajax

    为什么是异步: 如果是同步, 比如我连续调用5次, 页面就会刷新5次, 浪费性能, 所以把它做成异步的, 对同一个值进行多次 setState, setState

                的批量更新策略会对其进行覆盖 取最后一次的执行结果, 如果是同时 setState 多个不同的值, 在更新时会对其进行合并批量更新


 

* 展示组件(基础组件): 公共组件 不包含 redux, dva 等具体状态, 大部分参数是父组件传入

  容器组件: 父组件 包含 redux, dva 等状态, 传递数据给展示组件


 

* Hook 使用

  // useState

    1) 用法 || 初始化的方式

    2) useState都是异步的, 修改完一个 useState的值以后, 如果想立刻拿到修改后的值, 可以使用useState结构出来第二个方法的回调函数的方式,

       这个回调函数有一个参数, 这个参数就是上一次修改后的值

    3) 为什么是异步: 如果是同步, 比如我连续调用5次, 页面就会刷新5次, 浪费性能, 所以把它做成异步的, 对同一个值进行多次 useState, useState

                  的只有最后一次才会生效, 连续调用多个不同的值, 在更新时会对其进行合并成一次然后渲染

  export default () => {

    // 第一种 直接给值

    const [x1, setX1] = useState(1)

    // 第二种 回调函数

    const [x2, setX2] = useState(() => {

      // ajax

      if (true) {

        return 3

      } else {

        return 2

      }

    })

    const onClick = opt => {

      // 异步 (等一小会)

      setX1(x1 + 1)

      // prevState 上一次的值

      setX1(prevState => {

        console.log(prevState, 'prevState');

        // return 出去的值就是 x1 的值

        return prevState

      })

      console.log(x1, 'x1');

    }

    return (

      <div>

        <h1>{x1}</h1>

        <Button onClick={onClick}>改变x1</Button>

      </div>

    )

  };



 

  // useRef, createRef 区别

  // 1. createRef 只要组件重新渲染 createRef 就会重新重新执行

  export default () => {

    const [x1, setX1] = useState(() => {

      return 1

    })

    // 特性: useRef 页面渲染的时候只执行一次

    // 特性: { current: undefined } 只有 current 可以读写

    // 作用1. 获取DOM

    // 作用2. 存储上一次的值

    const ref = useRef()

    const ref2 = createRef()

    const onClick = opt => {

      // ref.current === 1

      ref.current = x1

      ref2.current = x1

      // 异步

      setX1(x1 + 1)

    }

    return (

      <div>

        <h1>{x1}</h1>

        <h1 ref={ref}>小花</h1>

        <h1 ref={ref2}>小蓝</h1>

        <Button onClick={onClick}>改变x1</Button>

      </div>

    )

  }



 

  /*

    用法: useCallback(回调函数, 依赖)

    特性: 缓存一个函数

      1. 如果依赖有值 如果依赖的值改变了 会缓存一个新的函数

      2. 依赖是空 useCallback 永远只执行一次

    用途: 因为如果父组件给子组件传递了一个函数 父组件刷新 子组件也会刷新, 为了避免子组件也刷新, 一般和16.6的 memo 一起联用, 然后具体答 memo用法

   

    const fn = useCallback(() => {

     

    }, [])

    // 如果依赖改变了 重新返回指针变了的新函数

    const fn = useCallback(() => {

    }, [x])

   

  */

  export default function UseCallback () {

    const [x, setX] = useState(0)

    // console.log('我是父组件,我执行了');

    // fn 保存的 123指针 -> () => console.log(1)

    // 第一次渲染 fn 123

    // 第二次 直接把 123 指针 给 fn

    // 第三次 直接把 123 指针 给 fn

    const fn = useCallback(() => {

      // console.log(1)

    }, [])

    return (

      <>

        <h1 onClick={() => setX(x + 1)}>x: {x}</h1>

        <QuseCallBack

          fn={fn} // 123

        />

      </>

    )

  }


 

  /*

    用法: useMemo 有2个参数, 第一个参数一个回调函数, 第二个参数是个依赖

    特性:

      1. 缓存一个 回调函数里 retrun 出来的值

      2. useMemo 立刻执行的

    const fn = useMemo(() => {

      return 123

      return () => {

       

      }

    }, [])

  */

  export default function UseCallback (props) {

    const [x, setX] = useState(20000)

    const [y, setY] = useState(20000)

    // 第一次刷新 x === 20000

    // 第二次 x === 20001

    const x1 = useMemo(() => {

      console.log('我执行了');

      let sum = 0

      for (let i = 0; i < x; i++) {

        sum += i

      }

      return sum

    }, [x])

    return (

      <div style={{background: '#000'}}>

        <h1>x: {x1}</h1>

        <h1>y: {y}</h1>

        <h1 onClick={() => setX(x + 1)}>x: {x}</h1>

        <h1 onClick={() => setY(y + 1)}>y: {y}</h1>

      </div>

    )

  }


 

* JSX

  是一个包含 type, props, children 等的对象


 

* class 生命周期

    (react 16.4 之前)

    三个阶段: 加载阶段 更新阶段 卸载阶段

      加载阶段:

        constructor

        render 渲染的生命周期

        componentDidMount 加载完成

dva redux 直接用

      更新阶段:

        shouldComponentUpdate(nextProps, nextState): nextProps && this.props(旧的)

        render 渲染的生命周期

        componentDidUpdate 更新完成

      卸载阶段:

        componentWillUnmount


 

    (react 16.4 之后)

    三个阶段: 加载阶段 更新阶段 卸载阶段

      加载阶段:

        constructor

        // getDerivedStateFromProps:

          // 1. 里面没有 this

          // 2. return 的值 是改变 state, 如果你不想改变 state 就返回 return null

        static getDerivedStateFromProps(nextProps, state)

        render

        componentDidMount

      更新阶段:

        getDerivedStateFromProps(nextProps, state)

        shouldComponentUpdate(nextProps, nextState)

        render

        getSnapshotBeforeUpdate(prevProps, prevState)

        componentDidUpdate

      卸载阶段:

        componentWillUnmount

读的死后注意 别跟读的一样

* PureComponent: 优化子组件渲染, 自动判断基础类型数据, 引用类型数据根据指针是否变化判断子组件是否渲染


 

* 虚拟DOM: JSX是一个对象, 包含type表示节点类型, props属性, props下包含children属性, 子节点又包含

    type属性, props 属性 children属性, js通过使用 document.createElement, document.craeteTextNode

    递归循环这个对象, 创建DOM树, 然后用这个DOM树和真实DOM进行比较, 这颗DOM树叫做虚拟DOM, react diff算法有

    三种方式, 1. 最外层节点不同, 重新渲染全部节点, 最外层节点相同则不重新渲染全部节点 2. 相同节点的属性不同, 只

    渲染不同的属性, 不重新渲染节点 3. key相同不重新渲染节点, key不同重新渲染该节点


 

* react18: 主要更新的是 Concurrent 并发 (分为高优先级[不能打断]和低优先级[可以打断])

    1. startTransition: startTransition(() => {  }): 不用 isPending 直接使用 这个方法

    2. const [isPending, startTransition] = useTransition: startTransition(() => {})

    3. useDeferredValue(值): 你不能改父组件 父组件传了一个值给子组件 在子组件里把这个值变成低优先级

    4. useId(): 生成一个 id

    5. useInsertionEffect(() => {}, []) // DOM变更前同步触发 主要用来加载 css in js

    6. const root = 新增了 ReactDOM.createRoot, 然后使用 root.render 渲染页面

    7. <Suspense fallback={<h1>loading...</h1>}>

props 或者 slot 注入

* 不使用 antd 封装 Modal 组件, 使用 ReactDOM.createPortal 把 Modal 插入到body下面和root同级

  (为什么要和root同级, 如果不同级 放在 root内, 在页面里某个div如果有overflow: hidden, Modal 可能无法

  做全屏遮罩)

  <Modal

    open={open} // bool

    title={title} // string

    close={close} // bool

    footer={footer} // 按钮扩展

    onOk

    onCancel

    onClose // 完全关闭

    chilren

    className

    width

    height

    遮罩

    style

    确定文字

    取消文字

    loading

  >

  </Modal>



 

* 多页面应用 单页面应用

  1. 单页应用(SPA)

    1) 只有一个 html 文件, 通过切换路由展示不同页面

    2) 页面局部刷新

    3) 不利于 SEO (SEO: 搜索引擎的优化, 让我们的网站在搜索的时候靠前)

    4) 不需要重复加载 css js, 所以页面切换后渲染快

    5) 可以使用 hash 也可以使用 history 模式(history模式上线后, 刷新浏览器后页面会找不到, 所以需要配置 nginx)

  2. 多页面应用

    1) 多个html页面

    2) 切换页面 整个页面全部重新加载 用户体验差

    3) SEO实现容易


 

* react 实现 keep-alive缓存: react-activation


 

* react 16.8 之前 事件代理 事件委托 注册在 document.onClick

  react 16.8 root


 

* useMemo 用途

    1. 累加求和 缓存一个值 公司的项目缓存用户金额

      const value = useMemo(() => {

        let sum = 0

        for (let i = 0; i < 10000; i++) {

          sum += 1

        }

        return sum

      }, [])  

    2. 优化 Context 渲染 子组件重复渲染 (因为 Context 更新以后, 子组件都会更新, 使用 useMemo 缓存 Context 的 value值

        防止子组件重复刷新)

      useMemo(() => {

        return value

      }, [])



 

【CSS部分】

* CSS 盒模型

    context(内容) -> padding -> border -> margin

    box-sizing: content-box; // 标准盒模型: padding border 都会累加到 元素的宽高里

    box-sizing: border-box; // 怪异盒模型: padding border 不会都会累加到 元素的宽高里


 

* css 权重

    !important(10000) > style(1000) > id(100) > class, 伪类选择器(10) > 标签选择器(1)

      > 通用选择器(*), 子选择器(>), 相邻选择器(+)同胞选择器(~), 权重值为(0)


 

* :root用法: 自定义属性

    :root {

      --名字

    }

    // 引用

    var(--名字)


 

* css插件: 使用 normalize.css(normalize.css 解决浏览器的 css 兼容问题), 不用 reset.css

   

* 怎么让一个元素 上下左右居中 (3种)

  1. display: flex; // 设置 flex 必须要设置父元素

    justify-content: center; // x 轴居中

    align-items: center; // y 轴居中

  2. position: absolute;

    top: 0;

    right: 0;

    bottom: 0;

    left: 0;

    margin: auto;

  3. position: absolute;

      top: 50%;

      left: 50%;

      transform: translate(-50%, -50%);


 

【VUE2部分】

* v-if v-show 区别:

    v-show隐藏则是为该元素添加等价于display: none, v-if是将dom元素整个添加或删除


 

* watch 怎么对对象做深度监听, deep: true


 

* watch computed 区别:

    computed 可以缓存数据 首次渲染会执行, watch 不能缓存数据 首次渲染不会执行


 

* vue 生命周期:

    beforeCreate: 在实例初始化之后,进行数据侦听和事件

    created: 在实例创建完成后被立即同步调用

    beforeMount

    mounted: 实例被挂载后调用

    beforeUpdate

    updated: 在数据更改导致的虚拟 DOM 重新渲染和更新完毕之后被调用

    activated: 被 keep-alive 缓存的组件激活时调用

    deactivated: 被 keep-alive 缓存的组件失活时调用

    beforeDestroy

    destroyed: 实例销毁后调用

    errorCaptured: 在捕获一个来自后代组件的错误时被调用


 

* vue 父子组件传值方式:

    props(包括父组件给子组件传递函数)

    $emit

    ref

    provide / inject 注入

    vuex

    eventBus 定义一个事件总线 使用$on 绑定 $emit 触发

    发布订阅



 

* MVVM:

1. 创建了入口函数, 分别new了一个数据观察者Observer和一个指令解析器Compile

2. 创建Compile解析所有DOM节点上的vue指令, 提交到更新器 Updater (实际上是一个对象)

  1) 判断el是不是元素节点, 如果不是, 就要取到el这个标签, 然后传入vm实例

  2) 递归拿到所有子节点, 便于下一步去解析它们 (这一步会频繁触发页面的回流和重绘, 所以会需要把节点先存入文档碎片对象中,

     就相当于把他们放到了内存中, 减少了页面的回流和重绘)

  3) 在文档碎片对象中编译好模板

  4) 最后再把文档碎片对象追加到根元素上

3. 更新器 Updater 把数据(如 {{}}, msg, @click) 替换, 完成页面初始化渲染

4. 更新器 Observer 使用 Object.defineProperty 劫持数据, 其中的 getter 和 setter 通知变化给 依赖器 Dep,

  触发 gettter 订阅数据变化时, 往dep中添加观察者, 触发 setter 当数据变化时 将newVal赋值为新值 并用notify通知dep变化

  Observer 用来监听所有数据, 会将 vm 实例的 data 作为参数传入

  1) 递归, 将data中所有的属性 对象 子对象都遍历出来

  2) 对每个key, 使用Object.defineProperty劫持数据 (Object.defineProperty()的作用就是直接在一个对象上定义一个新属性, 或者修改一个已经存在的属性)

  3) Object.defineProperty下有get方法和set方法

  4) 在劫持数据之前, 创建依赖器Dep实例dep

  5) 对于gettter, 订阅数据变化时, 往dep中添加观察者

  6) 对于setter, 当数据变化时, 将newVal赋值为新值, 并用notify通知dep变化

                       

5. 依赖器 Dep 中加入观察者 Watcher, 当数据发生变化时, 通知Watcher更新

    // 数据依赖器

    class Dep {

      constructor() {

        this.subs = [];

      }

      // 收集观察者

      addSub (watcher) {

        this.subs.push(watcher);

      }

      // 通知观察者去更新

      notify () {

        console.log("通知了观察者", this.subs);

        this.subs.forEach(w =>w.update())

      }

    }

6. Watcher 取到旧值和新值, 在回调函数中通知Updater更新视图

7. 预编译 解析指令 Compile 中每个指令都new了一个Watcher, 用于触发Watcher的回调函数进行更新


 

* vue 指令:

    v-if, v-show, v-bind, v-on, v-html, v-model, v-text


 

* vuex: 视图 -> action -> mutations 修改state -> 渲染视图

    namespaced

    state

    mutations

    actions

    modules


 

* v-for v-if 同时使用

    在 vue2中 在一个元素上同时使用 v-if 和 v-for 时 v-for 会优先作用

    在 vue3中 v-if 总是优先于 v-for 生效 所以不能写一起



 

【TS】


 

* 优势:

    1. TS 是强类型语言 实时报错

    2. 划过实时展示 api


 

* axios特点:

    基于promise的异步ajax请求库

    浏览器端/node端都可以使用

    支持请求/响应拦截器

    支持请求取消

    请求/响应数据转换

    批量发送多个请求

* git merge, git rebase 区别

  git merge:

    记录下合并动作,很多时候这种合并动作是垃圾信息

    不会修改原 commit ID

    冲突只解决一次

    分支看着不大整洁,但是能看出合并的先后顺序

    记录了真实的 commit 情况,包括每个分支的详情

  git rebase:

    改变当前分支 branch out 的位置

    得到更简洁的项目历史

    每个 commit 都需要解决冲突

    修改所有 commit ID


 

* nginx 配置反向代理:

    server {

      listen       80;

      server_name  192.168.17.129;

      location / {

        // nginx 监听 server_name 转到proxy_pass配置的对应服务器上

        proxy_pass  http://127.0.0.1:8080

      }

    }


 

*

  (B: 企业, C: 用户)

  ToB即面向企业

  ToC即面向普通用户

  BtoB: (企业面向企业) 阿里巴巴

  BtoC: (企业面向用户) 抖音 小程序

  CtoC: (用户面相用户) 淘宝店铺


 

* 移动端基准 720 x 1280


 

* CSS 的优化

  1. umi 异步加载 css 拆包

  2. 通过 webpack 的 CommonsChunkPlugin 拆包

  3. css3

  4. 解决css 命名冲突 冲突3个插件

  5. 减少回流与重绘

  6. 不适用 expression


 

* 为什么JS是单线程的还能够执行异步操作:

    因为浏览器是多线程的, Ajax请求确实是异步的 这请求是由浏览器新开一个线程请求 所以不会阻塞js



 

* undefinde null x

    1. null == undefined  // true

        null === undefined // false

    2. typeof undefined // undefined

       typeof null  // object

axios

* 浅比较: === 是浅比较

  深比较: 深比较也称原值相等 深比较是指检查两个对象的所有属性是否都相等, 深比较需要以递归的方式遍历两个对象的所有属性


 

*

  pushState 他有3个参数: 将当前URL和history.state加入到 history 中 并用新的state和URL替换当前

    state : 与要跳转到的URL对应的状态信息

    title : 目前没有浏览器支持 可以留空

    url : 要跳转的url地址 不能跨域

  history.pushState('a=1&b=2', '', '/xxx/login');

  // onpopstate : 回退 前进触发该事件

  window.onpopstate = function (e){

    if (e === '/xxx/login') {

      setX(<Login />)

    }

    if (e === '/index') {

      setX(<Home />)

    }

  };


 

* Worker: 创建一个新线程 (大量的计算)

    new Worker(加载一个js文件), 在js文件里执行逻辑

    worker.onmessage = function (e){

      e.data // 获取数据

    }


 

* canvas svg:

    1. canvas 可以兼容全部浏览器 主要做位图 (jpg, webp)

    2. svg 不兼容低版本浏览器, 矢量图


 

* 高阶函数 (高阶函数是指至少满足下列条件之一的函数):

    1. 函数可以作为参数被传递

    2. 函数可以作为返回值输出

   

* js 给定一个整数数组 nums 和一个整数目标值 target 找出该数组中和为目标值 target的那两个整数 并返回它们的数组下标

  https://blog.csdn.net/Gaozhengw/article/details/108668818

闭包 或者 写了一个死循环 都会造成

比如注册事件 没有

* 自定义指令

    directives: {

      focus: {

        // inserted: 被绑定元素插入父节点时调用

        inserted: function (el) {

          el.focus()

        },

        // 只调用一次,指令第一次绑定到元素时调用

        bind: function () {

        },

        // 所在组件的 VNode 更新时调用

        update: function () {

        }

      }

    }


 

* 线上js代码执行出错, 怎么捕获这个错误进行其他操作

    1. chrome sources 打断点

    2. 写错误边界

    3. 第三方的错误监听网站 例如 fundebug:

        用户行为记录: 用户怎么操作的产生了错误

        Source Map: 报错定位到 react 具体哪行

* forEach:

    1. break

    2. throw new Error() 抛出异常

* js的垃圾回收机制:

    1. 标记清除: 是当变量进入环境时 将这个变量标记为"进入环境" 当变量离开环境时 则将其标记为"离开环境" 标记"离开环境"的就回收内存

    2. 引用计数: 跟踪记录每个值被引用的次数

        声明了一个变量并将一个引用类型的值赋值给这个变量 这个引用类型值的引用次数就是1

        同一个值又被赋值给另一个变量 这个引用类型值的引用次数加1

        当包含这个引用类型值的变量又被赋值成另一个值了 那么这个引用类型值的引用次数减1

        当引用次数变成0时 说明没办法访问这个值了

        当垃圾收集器下一次运行时 它就会释放引用次数是0的值所占的内存

        // { } 123

        const x1 = {} // 引用次数 1

        const x2 = x1 // 引用次数 2

        x1 = 小花 // 引用次数减一



 

* Grid: 那就是 flex 布局是一维布局 Grid 布局是二维布局

    display:

      grid: 设置成块级布局

      inline-grid: 内联

    grid-template-columns: 几列

      auto: 每一列宽度自适应

      px: 具体宽度

      fr: 等价于 flex-grow

    gap: 等价于 grid-row-gap && grid-column-gap

      grid-column-gap: 设置列间距

      grid-row-gap: 设置行间距

    grid-template-rows: 设置行的高度

    auto-fill: 表示自动填充 让一行(或者一列)中尽可能的容纳更多的单元格

    minmax(最小值, 最大值): 分别为最小值和最大值

    repeat(列数, 每列宽度):

      num: 具体数字表示有几列

      auto-fill: 表示自动填充 让一行(或者一列)中尽可能的容纳更多的单元格

    grid-template-areas: 属性用于定义区域 一个区域由一个或者多个单元格组成 一般和 grid-area联用

    justify-items: 设置单元格内容的水平位置(左中右)

    align-items: 设置单元格内容的垂直位置(上中右)

    grid-auto-rows: 设置默认高度 优先级 < grid-template-rows

    grid-row: 等价于 grid-column-start grid-column-end

    grid-row: 等价于 grid-row-start grid-column-end



 

## 微前端: 可以把多个项目整合成一个项目, 不限项目框架, 可以是任何语言 比如把 vue react jq 的项目合成一个项目

  1. 为什么要用微前端 ?

    什么时候用到微前端:

      1) 比如我重构一个项目, 不可能花几个月时间整个重构这个项目, 时间太长了, 所以可以考虑使用微前端, 使用微前端逐个页面在新项目里进行重构

      2) 比如我现在是 react项目, 要把 vue 的项目的某些页面整合到我的 react 项目里 也可以使用微前端

  2. 优点: 不限框架 jq + react

  3. 微前端底层 single-spa

  4. 你的微前端用的哪个 qiankun

  5. 具体用法

      * 有一个主入口文件

      * 在入口文件里配置路由

      * 没一个子入口 在 react 配置生命周期

      * 配置 react 跨域, 打包成 umd 库格式等

      * setGlobalState, onGlobalStateChange 数据交互

      * umi 是在 config.js 配置 qiankun 注册子应用



 

## SEO: 搜索引擎 排名靠前

  1. 语义化书写HTML代码

  2. 页内链接 要加 title 属性加以说明

    <a href="https://www.360.cn" title="360安全中心" class="logo"></a>

  3. img标签应使用 "alt" 属性加以说明

    <img src="cat.jpg" width="300" height="200" alt="猫" />

  4. 使用 meta description

    <meta name='description' content=''>

  5. 使用 meta keywords

    <meta name='keywords' content='SEO,搜索引擎优化,网页排名优化'>



 

## CSRF攻击: 跨站请求伪造攻击

怎么防御:

  1. 只使用JSON API

  2. 验证HTTP Referer字段

  3. 在请求地址中添加tokon验证



 

## never

  定义: 表示的是那些永不存在的值的类型

  作用:

    1. 当成函数返回值类型使用

   

      function error(message: string): never {

        throw new Error(message)

      }

    2.

      function infiniteLoop(): never {

        while (true) {

        }

      }

   

    3.

      interface Foo {

        type: 'foo' // 字符串常量

      }

     

      interface Bar {

        type: 'bar'

      }

     

      interface Baz {

        type: 'baz'

      }

     

      type All = Foo | Bar | Baz

     

      function handleValue (val: All) {

        switch (val.type) {

          case 'foo':

            // 这里 val 被收窄为 Foo

            break

          case 'bar':

            // val 在这里是 Bar

            break

          default:

            // val 在这里是 never

            const exhaustiveCheck: never = val

            break

        }

      }

     

* 执行顺序

useInsertionEffect

useLayoutEffect

useEffect

useInsertionEffect 用来加载 css in js

useLayoutEffect 处理页面有闪屏效果


 

子组件

* 公共组件库搭建

    1. 扔到公有的 npm (例如 antd), 公司自己的组件库 发布到 私有的 npm

    2. 搭建公司自己的本地 npm 仓库

    3. 放到 gitlab, 使用双向 npm link



 

* react 优化

  1. 代码拆分 150 - 200

  2.

     * showdComponetUpdate(nextProps, nextState)

     * memo(组件, (nextProps, prevProps) => true), memo 自动比较上一次的值 和 下一次的值, 基础类型比较值, 引用类型

       比较指针, 我们一般使用 react-fast-compare 插件自动的比较 memo的nextProps, prevProps

     * useCallback, useMemo

  3. PureComponent === memo不写第二个参数

  4. 异步加载 懒加载

  5. 组件卸载前进行清理操作 比如 清楚 setInterval 定时器, addEventListener

  6. Fragment 和 <></>

  7. Context



 

## vite webpack 区别:

  1. webpack 服务器启动速度比 vite 慢

      由于vite启动的时候不需要打包 也就无需分析模块依赖 编译 所以启动速度非常快

  2. vite热更新比webpack快

      vite在HRM方面 当某个模块内容改变时 让浏览器去重新请求该模块 而不是像webpack重新将该模块的所有依赖重新编译

  3. vite使用Go编写预构建依赖 而webpack基于nodejs, 比node快 10-100 倍

  4. vite生态不及webpack加载器 插件不够丰富

  5. vite打包到生产环境时 vite使用传统的rollup进行打包, 生产环境的构建对于css和代码分割不够友好 所以 vite的优势是体现在开发阶段

  6. 没被大规模重度使用 会隐藏一些问题



 

## webpack5模块联邦:

  1. 它提供了一套在不同项目构建之间的调度 运行机制 跟微前端很像

  2. 包含了三个主要的模块

    * webpack构建: 一个独立项目通过webpack打包编译而产生资源包

    * remote: 一个暴露模块供其他webpakc构建消费的webpack构建

    * host: 一个消费其他remote模块的webpack构建


 

## 为什么放弃 class 使用 hook

  * 复杂组件变得难以理解, class 组件有很多生命周期, 相同的方法可能要在不同的生命周期里重复掉用

    如果用 hook 可以只在 useEffect 调用一次相同方法就可以了

  * hook 用面向对象的方式去写代码 达到复用最大化

  * class 要写 this, 写hook不用写 this

  * hook 可以封装自定义 hook



 

## 什么时候用自定义 hook:

  * 需要使用 react 自带的 hook 封装公共方法

  * 在 react 自带的 hook 的基础上 拓展一些新功能



 

## 自定义hook: 以 use 开头函数组件就是 自定义 hook

  * 为什么用自定义hook: 需要使用react自带的hook 或者使用自己封装的自定义的hook 实现复用逻辑

  * 实时获取 鼠标 mousemove 滑动的位置

  * 鼠标 onscroll 滚动的位置

  * 跟踪设备屏幕方向的状态

  * useData.jsx 保存静态数据

  * 模拟 react 生命周期

  * 封装 axios 请求方法



 

## POST GET 区别

  * post更安全 (post不会作为url的一部分, 不会被缓存, 参数不会被保存在浏览器历史中而是保存在服务器日志中)

  * post发送的数据更大, get有url长度限制

  * post能发送更多的数据类型, get只能发送ASCII字符

  * post用于修改和写入数据, get 用来获取数据

我们都是用分页插件 插件里可以监听下一个的回调

## setTimeout setInterval 的问题

  * setTimeout setInterval 执行的时间 不准确 需要修正

  * 浏览器最小化 或者 切换到其他 浏览器tab的时候 setTimeout setInterval 可能会不执行了

    (怎么解决: 我们监听 onvisibilitychange 这个事件, 判断 document.hidden 如果是真

    表示当前页面隐藏了, 我们就清楚 setTimeout setInterval 的 id, 如果是假 重新执行

    setTimeout setInterval)

    // 浏览器可见状态切换事件

    document.addEventListener('visibilitychange', () => {

      // document.hidden === true 当前页面隐藏

      if(document.hidden) {

        clearInterval(id)

      }

      // document.hidden === false 当前页面打开

      if (!document.hidden) {

        id = setInterval(function() {

          console.log(1111);

        }, 500)

      }

    })

  * 修正 setTimeout setInterval 不准确

    let start = new Date().getTime()

    let count = 0

    let interval = 1000;

    let offset = 0;//误差时间

    let nextTime = interval - offset; //原本间隔时间 - 误差时间

    let timer = setTimeout(doFunc,nextTime);

    function doFunc(){

      count++

      offset = new Date().getTime() - (start + count * interval);

      nextTime = interval - offset;

      if (nextTime < 0) { nextTime = 0; }

      if(count < 10){

        timer = setTimeout(doFunc,nextTime);

      }

    }



 

## React 合成事件

    1. React的事件不是注册在当前 jsx 标签上

    2. React的事件是合成事件, 例如 onChange 事件 包含 blur, change, input, keydown, keyup

    3. react并不是一开始把所有的事件都绑定在document上, 而是采取了一种按需绑定 比如发现了onClick事件, 再去绑定document click事件

    4. 节点上的原生事件的执行是在目标阶段 然而合成事件的执行是在冒泡阶段 所以原生事件会先合成事件执行

    5. 事件名称命名方式不同 合成事件以驼峰的方式写 原生事件 全部小写


 

## 伪元素, 伪类 区别

  伪类

    :checked

    :hover

    :disabled

    :active


 

  伪元素

    ::before 在选择器前添加content指定的内容 并且设定样式

    ::after 在选择器后添加content指定的内容 并且设定样式

    ::selection 修改鼠标选中项的样式


 

  伪类是使用":"设置指定元素在特定状态下的样式

  伪元素是使用"::"设置指定元素特定部分的内容和样式

   

## 图片懒加载

    使用 react-lazyload 实现

    原理:

      * 给所有图片添加 data-src 属性, <img src="占位默认图片" data-src="图片" />

      * 计算浏览器可视区域大小

      * 计算每张图片的大小 位置

      * 如果在浏览器可视区域内的图片 就把 data-src的地址 替换给 src, 加载图片

     

## git flow 规范

  * 分支:

      master 上线分支

      hotfix: 线上有bug 修改bug的分支

      release: 预发布 (预上线)

      develop: 开发分支

      feature/项目类型 + 时间: 临时分支

  * 开发流程

      1. 先创建 feature 分支

      2. 基于这个分支 创建自己的分支

      3. 在自己分支上开发完成后 合并到 feature 分支

      4. 整个项目开发以后 把 feature 分支合并到 develop 分支, 使用流水线发布到测试环境

      5. develop 测试完以后, 把 develop 合并到 release 分支, 使用流水线发布到预发布环境

      6. release 测试完以后 合并到 master 分支, 使用流水线 发布上线

  * 提交代码前缀

      feat: 新功能 新特性 (git commit -m 'feat: 描述文字')

      fix: 修改 bug

      perf: 更改代码 以提高性能(在不影响代码内部行为的前提下 对程序性能进行优化)

      refactor: 代码重构(重构 在不影响代码内部行为 功能下的代码修改)

      docs: 文档修改


 

* 泛型

    泛型分为 泛型接口 或者 泛型别名 type, 泛型有一个或几个泛型变量, 可以适应各种类型


 

## Promise.all 其中有1个 或几个失败了 还想拿到返回值, 主要使用 map循环 监听 catch

  Promise.all(

    [

      Promise.reject({ code: 500, msg: "服务异常" }),

      Promise.resolve({ code: 200, list: [] }),

      Promise.resolve({ code: 200, list: [] })

    ].map(p => p.catch(e => e))

  )

    .then(res => {

      console.log("res=>", res);

    })

    .catch(error => {

      console.log("error=>", error);

    });



 

* umi 优点

    1. umi 是企业级的 在稳定性非常好

    2. 有很多插件 Umi 本身也是由插件构成

    3. 支持 MFSU, 有比 Vite 还快的 Webpack 打包方案

    4. 自动集成 React Router 6

    5. 默认最快的请求

    6. 自带 SSR

    7. 自带 ESLint 和 Jest

    8. 集成了 React 18

    9. 集成了配置式路由 补丁方案 antd 微前端 国际化 权限等

  umi 缺点

    1. 文档较少

    2. 前期上手有难度


 

* vue data 为什么以闭包写: 为了变量不共享, 如果以对象的方式写 变量是共享


 

* 封装公共组件思路

    0. 基于 antd 进行二次封装, 必须支持 antd 自带的所有属性, 基于antd组件 额外拓展你的接口

    1. 封装组件 基于当前业务 额外的联想其他可能的接口

    2. 额外拓展出来其他功能

    3. 基础组件 不能写 状态管理工具 dva redux mobx

    4. 特殊的需要配置的功能不要写 styleName

    5. className 统一命名开头的 class 规范

    6. 基础组件库 业务组件库 最好用TS

    7. 要写 README.md 使用文档

    8. 要做单元测试 jest


 

* js 判断对象是否为空

    1. JSON.stringify(obj) === '{}'

    2. Object.keys().length()


 

* vh, vw, vmin, vmax

    vw: 视窗宽度的百分比 (1vw 代表视窗的宽度为 1%) 整个页面 宽度 100vw

    vh: 视窗高度的百分比 整个页面 高度 100vh

    vmin: 取 当前 vw 和 vh 中较小的一个值

    vmax: 取 当前 vw 和 vh 中较大的一个值

* vw vh 相对 rem 优点

  1. 相对于 rem, vw vh 不用在 html 里动态生成 font-size


 

* flex: 弹性盒模型

    flex-direction: 主轴方向

      row             默认,从左到右

      row-reverse     从右向左

      column          从上到下

      column-reverse  从下到上

    justify-content: 伸缩项目在主轴上的对齐方式

      flex-start      默认,向伸缩容器的起始位置对齐

      flex-end        向伸缩容器的的终点位置对齐

      centent         向伸缩容器的中间位置对齐

      space-between   伸缩项目平均分布 第一个伸缩项目在伸缩容器中最开始位置 最后一个伸缩项目在伸缩容器中的终点位置

      space-around    伸缩项目平均分布在伸缩容器里 两端保留一半的空间

    align-items: 伸缩项目在侧轴上的对齐方式(对单行对齐进行控制)

      flex-start      侧轴的起始位置

      flex-end        侧轴的结束位置

      center          侧轴中心位置

      baseline        基线对齐

      stretch         默认,如果没设置高度 伸缩项目默认填充整个容器 如果有max-height max-width等属性 会受到影响

    flex-grow: 伸缩项目占用多少剩余空间

    gap: 网格行与列之间的间距


 

  flex: 1 实际上是

    flex-grow: 1; // 表示项目在剩余空间中的增长比例, 默认值为 0

    flex-shrink: 1; // 表示项目在空间不足时的缩小比例, 默认值 1

    flex-basis: 0%; // 表示项目在没有设置宽度时的初始宽度, basis 和 width同时存在 basis 会把 width 覆盖掉, 默认值 auto

  flex: initial === flex: 0 1 auto  初始值, 常用

  flex: 0 === flex: 0 1 0%  适用场景少

  flex: none === flex: 0 0 auto 推荐

  flex: 1 === flex: 1 1 0%  推荐

  flex: auto === flex: 1 1 auto 适用场景少


 

* ES6 模块与 CommonJS 模块的差异

    1. CommonJS 模块输出的是一个值的拷贝 ES6 模块输出的是值的引用

    2. CommonJS 模块是运行时加载 ES6 模块是编译时输出接口

    3. CommonJS 模块的require()是同步加载模块, ES6 模块的import命令是异步加载 有一个独立的模块依赖的解析阶段


 

* 接口的管理工具

    1. RAP (阿里)

    2. yapi


 

* token过期, 自动重新获取新的 token: 在 axios 请求拦截里, 拦截错误的状态码, 如果检测是 token 失效,

    重新发一个请求给后台, 请求的参数带上过期的 token 给后台, 调用一个后台的接口

    axios.post('/login2', { data: '过期的 token ' })


 

* 前端工程化

  1. 开发

    1) 框架选型: 例如 umi + dva, create-react-app, vue-cli, nextjs

    2) 组件化: 例如 搭建公司自己内部的 组件库

    3) 微前端

    4) 自动化测试: 例如 使用 Jest 来自动化测试代码的功能

  2. 构建

    1) webpack

    2) vite

  3. 部署

    1) 静态资源部署 OSS

    2) 自动化构建: 例如 阿里云流水线

  4. 性能

    1) CDN 内容分发网络

    2) 按需加载

    3) 错误监控工具: 例如 Sentry 来监控应用中的错误和异常

  5. 规范

    1) eslint 代码检测

    2) git flow 规范

    3) 前后端接口规范

    4) TS 编程规范


 

* const let var 区别:    

    const:

        1. 定义常量

        2. 没有变量提升

        3. 有块级作用域

        4. const 执行速度更快

      let

        1. 定义变量

        2. 没有变量提升

        3. 有块级作用域

      var

        1. 变量提升


 

## 学习途径

  * 掘金

  * 大牛的博客

  * 项目中学

      1) 封装公共组件 不断地迭代组件 不断提升项目开发效率

      2) 项目中碰到各种问题 通过 google issur 等解决问题

  * 看很多书籍

  * chatGpt


 

* 你找公司目前主要看重什么

  1. 看公司的技术 是否符合 react vue,

  2. 工资福利 是否符合我预期

  3. 环境 氛围 干的顺心

  4. 项目稳定长期


 

* 比如有100个接口 每次执行2个, 这2个执行完成功以后, 再执行后2个接口

  // 每次执行2个 这2个接口执行完成以后 再执行后俩接口

  const arr = [f1, f2, f3, f4]

  方法一

  async function fun () {

    for (let i = 0; i < 4; ) {

      const xxx = await Promise.all([arr[i++](), arr[i++]()])

      console.log(xxx, 'xxx');

    }

  }

  方法二

  async function fun2 () {

    const xxx = await Promise.all([arr.shift()(), arr.shift()()])

    console.log(xxx, 'xxx');

    if (arr.length) {

      fun2()

    }

  }

  fun2()


 

* 微信小程序登录流程

    小程序调用wx.login接口获取临时登录凭证code

    小程序调用wx.getUserInfo接口获取用户信息 此时会弹出授权窗口让用户确认授权

    用户确认授权后 小程序可以获取到用户的加密数据encryptedData和加密算法的初始向量iv

    小程序将code、encryptedData和iv发送到后端服务器

    后端服务器使用小程序的AppID和AppSecret以及code调用微信的接口 获取到用户的唯一标识openid和会话密钥session_key

    后端服务器使用session_key解密encryptedData和iv 获取到用户的真实信息

    后端服务器根据需要进行业务逻辑处理 并生成自己的用户凭证 返回给小程序

    小程序保存后端服务器返回的用户凭证 用于后续的请求和身份验证


 

* http1.0, http1.1 区别:

    1. HTTP 1.1引入了更灵活的缓存机制 包括实体标签 ETag 和条件请求 If-Modified-Since、If-None-Match 使得缓存更加高效和可靠

    2. HTTP 1.1引入了更多的状态码 包括201(已创建), 202(已接受)

    3. HTTP 1.1引入了传输编码机制 可以对实体内容进行压缩和分块传输 提高传输效率


 

* 五险一金

    折现了

    为啥折现(不问不说): 上家公司是按最低比例交五险 没有一金 所以选择折现了

    折现多少钱(不问不说): 几百钱


 

* 进程, 线程

    一个进程可以有多个线程 一个运行的xx.exe就是一个进程

    进程是操作系统资源分配的基本单位 而线程是处理器任务调度和执行的基本单位


 

* XSS漏洞:

  定义: 用户使用表单提交的方式 提交恶意的 javascript 代码, 例如在 input 写 <script>while(true) alert('哈哈哈')</script>

  怎么防止是 把 script 的左右尖括号 转换成 &gt;script&lt; 现在vue react自动的转, 不需要特殊处理


 

* HTTP和HTTPS的区别主要表现在以下几个方面:

  安全性: HTTP是明文传输 而HTTPS是具有安全性的SSL加密传输协议 HTTPS 全性高于HTTP协议

  证书与费用: HTTPS协议需要申请证书 因而需要一定费用 而HTTP则不需要

  连接方式与端口: HTTP用的端口80 HTTPS是443。

  连接状态: HTTP的连接是无状态的 而HTTPS协议需要服务器和客户端进行三次握手以建立连接


 

* HTTP1.0和HTTP2.0的主要区别包括

  连接方式: HTTP1.0每次请求都需要建立新的连接 这意味着每次请求都需要进行TCP握手 会导致大量延迟。

          而HTTP2.0支持多路复用 可以在一个TCP连接上并发多个请求或响应。

  数据格式: HTTP1.0的数据是文本格式 方便阅读但不利于传输和解析 而HTTP2.0的数据是二进制格式,更利于传输和解析。

  头部压缩: HTTP1.0的请求头部信息是明文 会导致大量的冗余数据。而HTTP2.0的请求头部是经过了压缩的 减少了数据传输量

  服务器推送: 在HTTP1.0中 客户端需要明确请求服务器才能获取资源。而在HTTP2.0中,服务器可以主动向客户端推送资源

  安全性: HTTP2.0支持加密传输,提高了安全性。


 

* 您提到的应该是TCP的“三次握手”和“四次挥手”。以下是它们的详细说明:

    URG: 紧急指针 (urgent pointer)有效。

    ACK: 确认序号有效。(为了与确认号ack区分开, 我们用大写表示)

    PSH: 接收方应该尽快将这个报文交给应用层。

    RST: 重置连接。

    SYN: 发起一个新连接。

    FIN: 释放一个连接。

    seq序号 ack序号: 用于确认数据是否准确, 是否正常通信

  三次握手: 这是TCP建立连接的过程。

  第一次握手: 客户端向服务器发送一个SYN报文(请求连接) 等待服务器的确认。

  第二次握手: 服务器收到SYN报文后, 确认客户的SYN(ACK=客户SYN+1), 同时自己也发送一个SYN报文(SYN=服务器SYN), 即SYN+ACK报文, 此时服务器进入SYN_RECV状态。

  第三次握手: 客户端收到服务器的SYN+ACK报文, 向服务器发送确认报文ACK(ACK=服务器SYN+1), 此包发送完毕, 客户端和服务器进入ESTABLISHED状态, 完成TCP三次握手。

  四次挥手: 这是TCP断开连接的过程。

  第一次挥手: 客户端向服务器发送FIN报文, 请求关闭连接, 然后进入FIN_WAIT_1状态。

  第二次挥手: 服务器收到FIN报文后, 向客户端发送确认报文ACK, 然后进入CLOSE_WAIT状态。此时客户端进入FIN_WAIT_2状态。

  第三次挥手: 当服务器准备关闭连接时, 向客户端发送FIN报文, 然后进入LAST_ACK状态。

  第四次挥手: 客户端收到FIN报文后, 向服务器发送确认报文ACK, 然后进入TIME_WAIT状态。一段时间后, 客户端和服务器都进入CLOSED状态, 完成TCP四次挥手。

  总的来说, “三次握手”是为了建立可靠的连接, “四次挥手”则是为了优雅地断开连接。


 

* 实时通信

    1. 长轮训 短轮询

    2. Websocket

    3. mqtt

    4. SSE (Server-Sent Events): HTML5 自带的 基于 HTML5的


 

* CSS 怎么清楚 float 浮动

    1. 清除内浮动: zoom: 1; overflow: hidden

    2. 清除外浮动: clear: both


 

* 父组件调用子组件方法或者属性

    1. useImperativeHandle + forwardRef 实现父组件调用子组件里的方法

    2. render props: 父组件给子组件传递一个 render 属性, render 是一个回调函数


 

* webpack 性能优化

    1. 合理配置 resolve 属性: 设置resolve.modules来指定模块搜索路径, 减少查找时间

    2. Loader缓存: 例如启用 Babel 的 loader缓存(cacheDirectory选项), 这样重复编译时就可以跳过未发生变化的模块

    3. 使用 terser-webpack-plugin 开启代码压缩

    4. 将静态资源上传至CDN, 减轻服务器压力并加快资源加载速度

    5. Source Map优化: 开发环境开启 Source Map, 生产环境关闭 Source Map

    6. 使用 externals 配置排除不需要被打包的库

    7. image-webpack-loader、file-loader、url-loader等优化图片大小, 甚至将小图转为base64编码内联进CSS

    8. 使用 HMR(webpack-dev-server 和 Hot) 优化, 仅替换更新的部分而不是整个页面,提高开发效率

    9. DllPlugin DllReferencePlugin: 主要用于预编译项目的第三方库或不经常改变的基础模块, 将其打包为一个或多个单独的动态链接库(DLL)文件

    生成的 manifest.json 文件记录模块与DLL bundle之间的映射关系

    通过 DllReferencePlugin 引用之前生成的 manifest.json 文件, Dll 打包以后是独立存在的 只要第三方库没有变化 就不用再重新打包Dll

    每次只会重新编译我们自己的代码

* umi nextjs

  1. 如果要做 SSR, next.js 是非常好的选择 (Umi 也支持 SSR)

  2. Umi 的扩展性会更好, 并且 Umi 做了很多实用的功能, 比如配置式路由、补丁方案、antd 的接入、

  微前端、国际化、权限等, 同时 Umi 会更稳定,因为他锁了能锁的全部依赖,定期主动更新。

  某一个子版本的 Umi 不会因为重装依赖之后而跑不起来


 

* 为啥加薪

  1. 年底绩效考核 绩效优秀

    A 优秀

    B 及格

    C 不及格

  2. 为啥是优秀:

    * 项目从来没延期 有时候可以提前上线

    * bug量少 线上基本没有bug

    * 封装了很多公共组件 提高了前端组的开发效率


 

* 微前端的坑

1. 跨域问题: 子应用在被主应用加载时 可能会遇到跨域问题 解决方法通常是在子应用的构建配置中设置正确的 CORS headers 允许跨域请求

2. CSS冲突: 公共配置样式采用不同的CSS前缀


 

* 面相对象编程

  1. 代码复用: 通过继承机制, 子类可以复用父类的属性和方法, 减少了重复代码, 提高了开发效率

  2. 模块化和封装: OOP允许将数据和操作数据的方法封装在一个类中, 隐藏了实现细节, 降低了各部分之间的耦合度, 使得代码更易于理解、维护和修改

  3. 易维护性: 由于模块化和封装的特点, 当需求发生变化时, 通常只需要修改相关的类而不会影响到整个系统, 这降低了维护成本并提高了系统的稳定性

  4. 扩展性: 通过继承和接口的实现, 可以轻松地在现有代码基础上添加新功能或修改旧功能, 支持软件的持续迭代和升级, 增加了系统的灵活性和可扩展性



 

* 公司里前端怎么怎么和产品 测试 后台合作的

  1. 与产品经理:

  项目初期,前端开发需与产品经理密切沟通,明确产品需求、用户界面设计等, 确保吃透需求, 参与产品原型的设计评审, 提出一些建议,

  根据产品路线图参与迭代计划的制定, 确定开发优先级和时间表

  2. 与测试团队:

  首先要和QA协调好测试排期, 然后使用QA提供的测试用例进行测试, 抓紧修复QA提出的bug, 不要阻塞项目进度

  3. 与后台开发:

  协助后台一起制定API接口规范, 包括数据格式 请求响应模式等, 确保前后端数据交互顺畅, 项目开发过程中会和后台进行前后端联调 确保数据交互无误,

  我们公式使用 yapi 进行接口管理, 全部联调完会使用阿里云流水线进行发布上线

  4. 其他

  我们公司每天会通过 站会分享进度、解决问题, 使用云效进行项目排期跟踪和代码管理, 每月会有代码审查



 

* vue 父子组件执行顺序

  1. 加载渲染过程

  父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted

  2. 更新过程

  父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated

  3. 销毁过程

  父create -> 子created -> 子mounted -> 父mounted


 

* redux mobx dva 区别:

  redux 是 单 store, (视图触发 action, action 触发 reduce, reduce 修改state 重新渲染视图)

  mobx 是 多 store, 是响应式编程, MobX基于观察者模式, 采用声明式数据绑定, 自动跟踪依赖并优化更新


 

* vue 修饰符

  .prevent: 阻止默认事件行为

  .stop: 阻止事件冒泡

  .once: 只触发一次事件

  .capture: 事件捕获模式 事件将在捕获阶段触发 而不是冒泡阶段

  .self: 只有事件是从触发元素本身触发时才会触发 如果事件是从内部元素冒泡上来的 则不会触发

  .native: 监听组件根元素的原生事件 通常用于在组件上监听原生事件 而不是组件内部的事件

  .once: 只触发一次事件 指令只会执行一次 之后会自动解绑


 

* 数组有哪些方法

    push(): 在数组的末尾添加一个或多个元素 并返回新的长度

    pop(): 移除数组的最后一个元素并返回它

    unshift(): 在数组的开头添加一个或多个元素 并返回新的长度

    shift(): 移除数组的第一个元素并返回它

    splice(): 用于插入、删除或替换数组中的元素可以指定开始位置、删除数量以及要插入的元素

    slice(): 返回一个新的数组 包含从开始到结束(不包括结束)的数组部分原数组不变

    concat(): 连接两个或多个数组 并返回一个新的数组 原数组不变

    join(): 将数组的所有元素连接成一个字符串 元素之间可以指定分隔符

    toString(): 将数组转换为逗号分隔的字符串

    indexOf(): 返回数组中找到的第一个指定值的索引 未找到返回-1

    lastIndexOf(): 返回数组中找到的最后一个指定值的索引 未找到返回-1

    includes(): 判断数组是否包含某个指定的值 返回布尔值

    reverse(): 反转数组中的元素顺序

    sort(): 对数组的元素进行排序 默认按照字典顺序排序 可以传入比较函数来自定义排序逻辑

    fill(): 用一个固定值填充数组中从起始索引到终止索引内的全部元素

    map(): 创建一个新数组 其结果是该数组中的每个元素都调用一个提供的函数处理后的值

    filter(): 创建一个新数组, 其包含通过所提供函数实现的测试的所有元素

    reduce() 和 reduceRight(): 对数组中的每个元素执行一个由您提供的reducer函数(升序或降序),将其结果汇总为单个返回值

    forEach(): 遍历数组中的每一项元素并执行提供的函数


 

* 泛型优点

    代码重用

    一个函数 类或接口可以应用于多种类型的数据 而无需为每种类型重复编写相同的逻辑 提高了代码的可重用性

    类型安全

    通过指定泛型参数, TypeScript 编译器能够在编译阶段捕获类型错误 从而避免了运行时的类型不匹配问题。这增强了代码的稳定性和可靠性。

    清晰的API文档和自我解释代码

    泛型代码的使用往往直观地表明了类型的关系和约束 使得阅读和理解代码变得更加容易。泛型参数作为函数或类签名的一部分 为API的使用者提供了明确的类型指导。

    减少类型转换

    由于泛型确保了类型的安全匹配 因此在很多情况下不需要手动进行类型转换。这减少了因不当类型转换引起的bug 同时也简化了代码。

    泛型约束

    使用泛型约束 可以限制泛型参数必须符合某种特定的类型结构或实现某些方法 从而能够在泛型代码内部安全地使用这些特性的功能 进一步提升类型安全性。

    实用类型的支持

    TypeScript 提供了一系列实用类型 如 Partial<T>, Readonly<T>, Pick<T, K>, Record<K, T> 等 这些泛型工具可以帮助开发者更容易地操作和转换复杂类型 进一步提升开发效率和类型表达能力。

    递归类型与条件类型

    TypeScript 的类型系统支持递归类型定义和条件类型 这让开发者能够创建极其复杂和精确的类型定义 满足各种高级编程需求 尤其是在构建库或框架时尤为重要。




 

* 用ref 传递数据会有什么问题

    破坏数据流向的单向性: Vue 单向数据流 即父组件通过props向子组件传递数据 子组件通过events向父组件发送消息

                      使用ref直接访问子组件实例 可能会破坏这种单向数据流向 导致数据流向不清晰 增加维护成本

    耦合度高: 通过ref直接访问子组件实例 会增加组件之间的耦合度 使得组件之间的关系变得紧密

    难以追踪数据来源: 使用ref传递数据会使得数据来源变得不明确 难以追踪数据的流向和变化 这可能导致代码的可读性和可维护性下降

    不利于组件的独立性: 通过ref传递数据会使得组件的独立性降低

    ref可以在一些特定情况下方便地访问子组件实例 不要过度使用ref



 

* 上家公司做过哪些复杂的东西

  1. 封装了 QButton, QImage, QBox 等组件

  2. 说命名冲突

  3. 0-1搭建框架


 

* const let var 区别:

  1. var 有变量提升, 使用var声明的变量会被提升到其所在作用域的顶部, var 声明的变量具有函数作用域或全局作用域, 可以重新赋值,也可以改变变量的类型

  2. let 没有变量提升, let声明的变量不会被提升, 如果在声明前访问它们, 会导致引用错误, let 有块级作用域, 可以重新赋值

  3. const 没有变量提升, 有块级作用域, 一旦被赋值,其值就不能再改变


 

* vue 路由守卫

  1. beforeEach 全局路由守卫

  2. afterEach 全局后置钩子

  3. beforeEnter 路由独享的守卫

  4. beforeRouteEnter 渲染该组件的对应路由被验证前调用

  5. beforeRouteUpdate 当前路由改变,但是该组件被复用时调用

  6. beforeRouteLeave 导航离开渲染该组件的对应路由时调用


 

* 最近关注哪些新技术

  1. vue3 前一段刚自学完

  2. 正在学 RN

  3. 学了一些CSS3的特性 比如

      will-change 绝对 GPU加速

      :where 合并css公共部分

      :root 定义全局样式


 

* any 和 unknown 区别

   1. 可以将任何东西赋给 unknown 类型, 但在进行类型检查或类型断言之前, 不能对 unknown 进行操作

   2. 可以把任何东西分配给any类型, 也可以对any类型进行任何操作

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值