前端面试题

1. JS的原型与原型链

每一个对象实例都有自己对应的构造器, 对应的构造器都拥有一个prototype的属性, 值为一个plain object,这就是 构造器的原型,而且对象实例的proto属性也指向构造器的原型:

a.__proto__ === A.prototype

所以构造器原型上的属性方法是可以被对象实例访问到。

因为每一个构造器是prototype原型都是一个对象,理论上来说,这个原型对象也可能是另一个构造器的实例,这样就形成了一条原型的链,在原型链上的实例对象,是可以访问到上级原型对象上的属性方法, 原型链的顶层是Object

2. 跨域和同源策略

所谓的同源策略其实是浏览器的一种机制,只允许在同源,也就是同协议、同域名、同端口的的情况下才能进行数据交互。

但是我们在开发项目的过程中, 往往一个项目的接口不止一个域,所以往往就需要做跨域的处理,通常的跨域方式有这么几种:

1、JSONP,主要依赖的是script标签不受同源策略影响,src指向某一个接口的地址, 同步需要传递callback回调函数名字, 这样当接口调用成功后, 本地创建的全局回调函数就会执行, 并且接收到数据。不使用img标签的原因是因为img标签无法执行js语句

2、CORS,依赖服务端对前端的请求头信息进行放行,不做限制。

3、代理访问,前端访问不存在跨域问题的代理服务器,代理服务器再去访问目标服务器(服务器之间没有跨域限制)

3. Redux解决了什么问题?是如何工作的

React框架这样的纯视图的框架对组件通信、状态共享等方面没有太好的解决方案,只能利用状态提升、context之类的技术方案来解决, 但是当组件结构、数据流向都比较复杂的时候,React本身的Api解决这样的问题就有些吃力。

此时就可以使用Flux、Redux这样的架构方式来解决问题。

我们之前的项目就在使用Redux,后来又有了rtk,让redux在react组件中的使用更加方便,在一些小型的项目, 或者一些小模块中, 利用useReducerHook来进行state与dispatch的构建,也能快速优雅的解决问题。

之前的原生Redux中,真实项目开发中,往往需要搭配redux-thunk来进行异步action处理,以及react-redux来进行组件与store的连接, 如果不使用react-redux的话,会比较繁琐。

redux的结构分为store、Views、Actions、Reducer。

Store中存储的状态在视图中可以通过getState来获取,也可以通过subscribe方法进行监听,当视图产生UI操作的时候, 可以调用actions的方法来生成action后,利用dispatch方法进行派发,此时reducer就会被调用,并且接收到当前的状态与action对象, 经过计算出,返回新状态,Store就会进行状态的更新。

利用react-redux后,组件可以通过connect搭配mapStateToProps及mapDispatchToProps参数来获取store中的状态及actions中的方法。

使用RTK之后,创建reducer更近方便,利用useSelector和useDispatch可以更快更高效取用状态及派发action。

对于一个小的场景,比如之前我做的注册模块有三个步骤,每个步骤都需要用到第一步用户填写的手机号等信息,这样就形成了几个小组件间的状态共享, 如果使用父组件状态提升会导致数据流向不清晰,也犯不上使用redux-store进行存储,于是就是使用useReducer快速的创建了一个小的store,内部集成了state与dispatch,搭配Context,也能很高效的解决问题。

4.redux-thunk中间件

这个是解决redux架构中actions方法无法异步返回action的问题的,比如我们在做登录、获取用户信息、获取全局定位城市、获取全局配置等等场景,都是要调用接口异步获取的,此时就可以利用redux-thunk来解决。

thunk中间件的原理其实就是对dispatch进行改写,使其不仅可以接受action对象, 也可以接收一个函数,并且会将dispatch动作及getState方法传入到此函数中,我们就可以在这个函数中进行异步动作,异步执行完成后,再去dispatch对应的action。

5. 说说你了解的Immutable

Immutable是解决Mutable数据操作中的一些问题的,因为JS数据分为引用类型与基础类型, 引用类型在赋值传递的时候往往其实是地址传递,可能会出现一些预料外问题。

Immutable的思想就是构建持久化、不可变的、结构共享的状态,当对immutable数据进行操作的时候,实质上不会更改原数据,而是会根据原数据生成一个新的immutable数据,这样就可以避免刚才说的问题, 并且immutable采用的是结构共享的方式,生成新的immutable数据的时候不会完全重建,而是只重新更改后的节点及其父节点,其他节点与上次的immutable数据共享, 节省了新建的性能开支。

优点:出问题的概率小,更稳定,比起深拷贝来说性能也更高

缺点:需要重新学习Api,学习成本高,也需要额外导入对应的文件, 增加打包体积

其实immutable数据在redux中非常适合,不必再考虑reducer中每次都需要新建新状态的问题,而RTK中已经内部使用了immutable来构建状态。

6. redux是如何通知react数据变更,说通知的过程

redux中store中的状态需要利用getState方法来获取,在组件中更新的场景只有属性和状态变化才能引起组件的re-render,所以在正常开发中,往往需要将store中的状态对应的挂载到组件自身的state上,等到store中状态变化的时候同时更新组件自己的状态,这样就能引起组件的重新渲染最新的数据。

这个时候就需要利用store,.subscribe方法来监听store中状态的变化, 当store中状态变化,组件再去更新自己的状态。

利用react-redux的connect之后,就不要写这样的代码了,因为connect根据mapStateToProps生成的容器组件已经去监听状态变化,UI组件只需要在属性上等待接收即可。

7. React中key的作用

React组件在更新的时候,react就会生成新的完整的虚拟DOM树与之前的虚拟dom进行比对,然后再对有更新的节点相关的位置进行更新。

对比之前往往需要进行匹配和比对,为了匹配的更精准,react希望在列表循环等位置去手动为Element添加key属性,这样对比的时候就可以通过查找key来进行精准匹配。

我之前做项目的时候也碰到过这样的情况,一般都是用数据的id来作为key, 有一次出现了问题,后端给的数据里头没有id这样的主键·,顺手用了索引做key,在数据列表的顶部添加item的时候对比出错了,最后还是找后端添加唯一id才处理好。


8. 说说setState

setState是react类组件更新自身状态的唯一方法,因为react作为纯视图框架,不像vue那样对数据都进行了数据劫持绑定在实例上,当数据更新的时候组件实例对应的watcher会执行,从而引起后续re-render的过程。react只能主动调用setState方法来通知组件进行状态的更新以后虚拟dom的更新、对比、re-render。

setState可以传入一个state对象,采用的是批量更新的方式,并且多个setState会合并,提高更新性能,setState也可以传入一个函数,接收到当前的状态返回出要批量更新的state对象。

setState一般情况下是异步的,也就是说,执行setState之后, 不会马上进行render,并且this.state上的状态也不会马上更新,会等到下一次事件循环中才会去执行。

setState在一些react-api外的方法中可能会是同步的,比如setTimeout、原生dom事件中。

componentDidMount() {
setTimeout(() => {
this.setState(
{ count: 1 })
console.log(this.state.count)1,1000)
document.querySelector('#btn').onclick= (e) => {
this.setState({ count: 1 })
console.log(this.state.count)}
}

最近的18+版本已经修复了这个问题, 所有情况下都是异步的

9. 在业务中,初始数据的获取用在那些生命周期方法中

初始数据的获取肯定要在初始化阶段的生命周期钩子函数中执行。

constructor理论上可以执行, 因为初始数据的获取往往都是异步的动作。

static getDerivedStateFromProps, 不可以,因为这是一个静态方法, 内部没有this, 不适合做这个事儿,render 要保证纯净, 不能做这个事情,也可能会导致死循环

componentDidMount的时候比较适合。实际上,react官方说明, render前应该保持纯净不执行副作用动作, 所以初始数据的获取最应该放在componentDidMount中,在vue中对应的应该在mounted中执行。

10. 用hooks编程的话如何取模拟生命周期

函数组件中利用hooks模拟生命周期主要使用的就是useEffect

如果useEffect的依赖数组为空数组, 此时,模拟的是componentDidMount

如果useEffect的依赖数组不为空,此时, 模拟的是componnetDidUpdate

如果useEffect的回调函数中返回一个函数, 此时,这个返回的函数模拟的是componentWillUnmount

11. React-hooks有哪些 ,用法

React中提供了很多Hooks,常用的有如下几个:

1、useState用来创建持久化的状态
2、useRef可以创建一个ref对象, 可以用于标记dom和子组件,也可以创建一个不会变化的持久数据
3、useEffect 可以用来监听数据变化以及模拟生命周期函数
4、useContext 可以高效便捷的取用Context中传递的value
5、useCallback 用于缓存函数,避免函数重复构建, 提高性能
6、useMemo 实现vue中计算属性的能力,缓存计算结果, 提高性能
7、useLayoutEffect,与useEffect的区别是在render前同步执行
8、useReducer 可以创建一个小型的store, 往往需要搭配Context使用
9、useImperativeHandle 可以给React.forward处理后的函数组件接收到的ref对象拓展功能
10、useid 可以创建一个唯一id

其他的工具中也都有提供一些特别好用的hooks

react-router-dom:
1、useLocation
2、useHistory V5
3、useParams
4、useNavigate V6
5、useSearchParams V6

reduxjs/toolkit
1、useDispatch
2、useSelector

12. var, let, const的区别

ES6新增了定义变量的关键字 let和const, 分别用于定义块级变量和常量

let, const不会声明提前, 存在暂时性死区

外部无法使用到内部的let和const定义的变量, 存在块级作用域限制

const 定义的常量, 无法更改。

13. 自己封装过组件吗?

封装过的,而且在之前vue和react项目开发中都多多少少的封装过一些组件,有UI组件,封装了样式及单纯的数据渲染、逻辑组件(封装了数据获取、数据分析处理等功能)

之前做vue项目的时候封装过一个图表的组件,在里面主要去实例化了图表的实例,根据传入的api的接口不同调用不同的接口来获取数据, 又根据传入的数据处理函数的不同,对获取到的处理进行不同的处理,还监听了浏览器尺寸变化, 去动态调整图表的尺寸。

还一个移动端项目里封装过一个通用的列表组件,组件中可以自动获取数据、封装了下拉刷新上拉加载的逻辑,还可以根据传入的属性不同, 调整列表布局为一栏或者两栏布局。

当时用react做管理系统的项目的时候封装了权限控制组件,组件可以自动从store中获取到用户相关的权限信息,以及搭配一些属性来控制对应的内部的组件在没有权限的情况下禁用或者不显示的情况。

还封装了错误捕捉ErrorBoundary组件,当捕获到错误的时候更改自身状态,来控制渲染一个备用UI,后来又根据需求,在内部实现了组件加载失败重试的功能,主要靠的是封装了一个函数来进行组件的懒加载,将渲染的内容处理成函数,当点击ErrorBoundary的错误备用UI中重试按钮后,更新错误状态重新渲染,引起渲染函数的函数重新执行,重新加载。

其他的还有:

1、富文本编辑组件,主要是封装成自定义的表单控件,方便在Form中使用。

2、在一个ToC的项目中封装了一个登录鉴权组件,可以根据传入的参数不同,在没有登录的时候,控制内部的组件是否渲染,或者阻止内部组件的用户操作,点击后弹出未登录是否去登录的提示信息。

14. 数组常用的合并方法

1、concat
2、[...a, ...b]展开运算符
3、[a, b].flat()
......

15. map和forEach的区别

这两个都是数组的方法,也都能起到遍历数组的作用,区别就是map·可以返回一个新数组,新数组中的每一个元素,都可以在遍历数组的过程中,根据当前数组的每一个元素来进行创建。

16. 做项目的过程中遇到的跨域问题是怎么解决的

回答的时候因回答什么是跨域问题,在什么情况下会遇到跨域问题,怎么解决的
项目中最常用的方法就是CORS

一、为什么会出现跨域问题


浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域。
在前后端分离的模式下,前后端的域名是不一致的,此时就会发生跨域访问问题。在请求的过程中我们要想回去数据一般都是post/get请求,所以..跨域问题出现。
出于浏览器的同源策略限制。

同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响

可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。

所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)

二、什么是跨域


当一个请求 url 的协议、域名、端口三者之间任意一个与当前页面 url 不同即为跨域


当前页面url 被请求页面url 是否跨域原因

http://www.test.com/ http://www.test.com/index.html 否 同源(协议、域名、端口号相同)
http://www.test.com/ https://www.test.com/index.html 跨域 协议不同(http/https)
http://www.test.com/ 百度一下,你就知道 跨域 主域名不同(test/baidu)
http://www.test.com/ http://blog.test.com/ 跨域 子域名不同(www/blog)
http://www.test.com:8080/ http://www.test.com:7001/ 跨域 端口号不同(8080/7001)

17. cookie和session的跨域怎么解决

cookie和session是为HTTP请求挂载状态的,也就说在前后端交互的过程中,往往需要利用cookie或者session来对客户端进行标记。

根据cookie的设置及使用方式又分为所谓的session、token。

传统的cookie验证方式是这样的:

1、客户端在向服务端登录的时候,服务端直接通过响应头上set-cookie字段,为浏览器客户端注入cookie,或者将对应的信息放置在响应内容中,客户端自行将其存储在cookie中。
2、客户端在每次发送请求的时候就可以将cookie携带在请求头上进行数据的传递,服务端拿到此次请求头中的cookie进行验证。
3、客户端自行设置cookie或者由服务端设置cookie的时候也去设置此cookie的过期时间,当cookie过期后相当于登录过期了。

session的验证方式:

1、客户端在向服务端登录的时候,服务端自行建立客户信息表,并且建立生命周期机制,服务端再将此信息通过cookie或者数据直接返回的形式,返回给客户端。
2、客户端拿到验证信息后,可以选择存储在cookie中获取localStorage中都可以。
3、每次请求的时候携带验证信息(cookie就自动写到,localStorage需要取出携带)
4、服务端接收到请求后,判断该验证信息是否过期

session的这种方式,根据验证信息的加密情况,也被称为token。

cookie和session本质是都是利用cookie或者localStorage来进行数据交互,而cookie和localStorage又都有跨域的限制:

cookie通过设置domain可以实现一级域名下的二级域名之间可以互相访问,localStorage则不能跨域。

解决跨域一般可以采用代理/中间服务器的形式,利用websocket也可以解决。B可以将自己的cookie通过请求告知C,A想要获取B的cookie的时候去请求C。

18. react父子组件通信

父组件可以将某些数据当做属性传递给子组件,也可以将一些方法当做属性传递子组件,这样子组件就可以访问到父组件的数据并且也能调用父组件的方法。

父组件可以通过ref对子组件进行标记,从而得到子组件的实例,需要注意的是,子组件如果是函数组件需要利用React.forwardRef进行ref转发,从而再次标记到子组件内部的某些内容,或者子组件可以利用useImperativeHandle这个Hook来对转发进来的ref对象进行拓展,父组件就可以调用到这些拓展的方法。

19. 如何禁用浏览器的前进和后退功能

浏览器的前进后退是依赖window.history Api, 记录页面内的跳转历史,通过前进后退按钮来控制历史回退。

那么就可以在进入项目后,以及路由跳转的时候,都通过

window.history.pushState(null, null, document.URL)

本质就是在进入页面或者路由跳转的时候在历史记录中保存一条没有意义的记录,这样用户在点击前后后退按钮的时候就没有效果。

20. sass和 scss的区别

scss只是sass的一个语言版本,.scss文件的特点是层级靠{}来区分,.sass文件的特点的层级靠缩进来区分

21. vue中组件模板为什么只有一个根元素

Vue3中已经优化了这个问题,组件的模板内部可以有多个根元素,这是因为vue对模板编译的语法做了优化,之前2.0的时候组件在编译模板的时候会以最外面的根元素作为虚拟DOM的根结点进行编译,Vue3中以最外层的组件模板的template标签作为根元素进行编译。

22. 线程和进程的区别

进程是资源分配的最小单元,线程是代码执行的最小单元。

一个应用程序可能会开启多个进程,进程之间数据不共享,一个进程内部可以开启多个线程,线程之间的数据可以共享的,所以多线程的情况下,往往要考虑的是线程间的执行顺序问题。

浏览器其实也可以通过webWorkers开启多线程。

23. 做过那些优化(react或者vue)、前端优化

优化开发效率、优化用户体验、优化页面性能

a.开发效率

工程化,如何优化打包效率:区分开发与生产模式,代码压缩、兼容性处理、Tree Shaking,npm包缓存(减少流水线下载依赖包的时间)

代码:规范代码,进行合理代码封装与抽离,减少更新维护成本。

b.优化用户体验

界面美化风格统一,用户交互优化(成功失败提醒、hover手型、骨架屏等交互效果),用户体验优化(错误捕捉ErrorBoundary与重试,避免白屏;懒加载,减少首屏加载时间;数据缓存,合适的接口数据缓存在本地存储, 减少接口调用、keep-alive;)。

c.优化页面性能

代码性能优化:

无意义的重复去执行代码(缓存),算法不够优秀(优化,空间换时间)

react:数据变化后组件都会考虑重新执行及re-render的动作,如果数据没有发生本质上的更改,此次re-render的过程就是无意义的浪费性能,类组件可以去继承PureComponent,但是PureComponnet是浅层对比,很可能会影响到页面功能,本应该重新渲染也没有重新渲染,此时就需要用ShouldComponentUpdate来进行精准的判断是否继续执行后续动作。函数组件因为属性变化或者某些持久状态变化也会导致整个函数重新执行,此时函数内部的逻辑都需要做缓存,可以利用useMemo、useCallback、useRef之类的Hook来进行缓存,减少代码重复计算及函数或者数据的重复创建、赋值、销毁。

vue:计算属性只有在依赖的数据变化才会重新计算,但是如果计算过程非常复杂,可以考虑手动创建缓存来减少计算。

24.react生命周期?16版本删除了什么钩子函数?为什么删除?有什么替代的?说说这些钩子函数吧

react和vue一样,生命周期也分为三个阶段:初始化、运行中、销毁。

react生命周期在16后面版本进行了更新迭代,初始化阶段生命周期顺序:

先执行constructor,在这里可以创建组件实例,传递属性,挂载初始状态、this-binding

然后执行static getDerivedStateFromProps, 这是新增的一个生命周期钩子函数,本身是一个静态方法,内部不能访问this,可以根据当前的属性和状态生成一个派生的状态,但是需要注意的是,任何属性和状态变化都会再次引起这个钩子函数的执行,所以往往会存在一些性能问题。需要自行做缓存优化。

接下来删除了一个componentWillMount,类似与vue中的beforeMount,因为react提倡render前不做副作用动作,保持纯净,所以被废弃。

接下来执行render函数,在render中也不要做副作用动作,容易造成死循环,作用就是生成虚拟dom结构。

最后执行的是componentDidMount,在这里可以做副作用动作了,初始的调用接口、dom操作、一些插件的初始化、事件的绑定。

当组件的属性和状态变化或者执行forceUpdate的时候, 会引起运行中阶段的钩子函数执行。

接下来删除了一个componentWillReceiveProps,这个钩子函数会在属性更新的时候执行,我们之前可以在这里根据更新后的属性来更改自身的状态或者做一些其他的逻辑动作,现在可以利用getDerivedStateFromProps替代,并且因为render前不能做副作用,所以废弃。

首先还是执行static getDerivedStateFromProps, 再次进行派生状态的计算生成。

接下来会执行shouldComponentUpdate,在这个钩子函数中可以通过this上当前的属性和状态与函数参数中接收到更改后的属性和状态进行对比,判断是否要继续执行其他的生命周期钩子函数,提升性能,需要注意的是forceUpdate执行后,不会执行shouldComponentUpdate,也可以利用PureComponent父类中默认的浅对比的shouldComponentUpdate来优化效果。

接下来还会执行render,之后就是static getSnapshotBeforeUpdate,在这里可以提前进行一些数据的计算,将结果返回到componentDidUpdate中,进行逻辑分离, 之前在componentDidUpdate前会执行一个componentWillUpdate,因为功能能力被getSnapshotBeforeUpdate取代,自身也不能做副作用,所以被废弃。

最后执行componentDidUpdate,在这里可以进行插件更新、dom的操作,也可以根据this上最新的属性和方法与函数参数中接收到的之前的属性和方法对比,判断此次更新的是哪一个数据,起到监听数据变化的作用。

当组件销毁的时候,例如路由切换、组件切换,对应组件的是componentWillUnmount会执行,在这里我们可以做一些善后工作,例如清除计时器、事件解绑等。

25. vue和react之间的共同点以及不同点

共同点:

都是组件化框架、都是数据驱动视图、组件通信方式大致相同(父子、非父子、跨组件传参、状态共享)、都有自己的单页面应用路由工具,都遵循单向数据流,组件数据也是分为属性和状态,都有虚拟dom机制。

不同点:

vue是一个MVVM完整架构框架,每一个组件实例都是一个ViewModel,组件实例也拥有着自己的响应式的状态及生命周期触发机制,当数据更新的时候会与视图进行通信。

而React只是一个视图层框架,只执行了视图层面的一些逻辑实现(条件、循环),属性与状态没有响应式机制,属性与状态更新需要通知react进行视图更新。

vue拥有指令系统,可以方便、快捷的对DOM进行操控,React拥抱JS,绝大部分的逻辑开发都要通过JS来实现。

React使用JSX来构建组件DOM结构,Vuex采用模板的实现来构建(React和Vue都可以通过createElement方法来构建DOM结构)

DIFF算法略有不同,React的fiber算法是切片异步对比,减少阻塞的可能性。

vue-router采用集中式路由配置,并且实现了大量的路由守卫Api,react-router在V6版本中又开始·允许集中配置,V4、V5一直推荐路由分离配置,用Route来表现路由,只有history.listen可以做路由监听(V6中消失),其他的时候只能在组件中监听路由属性变化来表现路由守卫。

逻辑复用实现方式也不一样,vue采用自定义指令、mixin(2.0)、组合式函数(3.0)来实现逻辑复用。React利用高阶组件(HOC)、自定义Hook、无渲染组件来实现逻辑复用。

26. 什么是虚拟DOM

现代的框架为了减少真实DOM的操作,提高网页的性能,创造出来的一种DOM渲染机制,就是虚拟dom机制。

前端的渲染的流程就变成了,先通过逻辑根据模板去生成虚拟dom结构,这是一段用于表现真实DOM渲染情况的JS对象,然后将其根据规则渲染成真实DOM在页面上。

{ tag: '', attrs: {  }, content: '...' }

当数据发生变化的时候, 会根据当前的虚拟dom结构生成一个新的虚拟dom结构(注意,每次生成的都是完整的虚拟dom树结构,不过不需要担心创建新的虚拟dom结构的成本,因为在创建的过程中还是回大量复用的) ,然后将两个虚拟dom树进行对比,然后根据规则对需要更新的部分进行更新渲染。

这样就大量的减少了真实DOM的操作,因为在更新、对比的过程中进行了很多的合并操作。

27.ES6新增的方法:

let和const,解构赋值、模板字符串、箭头函数。

Symbol、Map、Set三种常用的数据类型。

Proxy重新定义了数据劫持的能力

Reflect定义了一套标准化的数据操作的方式

Promise确实的解决了异步逻辑嵌套及回调地狱问题。定义了异步逻辑的三种状态pending、rejected、fullfilled, 搭配then、catch、all、race等方法以及async await语法糖,大量简化了异步操作。

Generator函数,可以将异步逻辑划片执行。

新增了class类的概念

ES6 Modules

28. webpack配置并且是否熟知代码分割流程及操作

webpack包含mode模式、entry入口、output出口、plugins插件、loader、resolve、devServer开发服务器相关的基本配置。

组件模块化导入的时候可以采用懒加载的形式,就会单独打包对应的代码。

在webpack中可以通过多入口的配置,每个入口都会去查询对应的依赖关系图,进行单独的代码。

在模块化开发过程中往往会将各种不同类型的文件都看做是模块来进行互相导入导出,比如将图片、css、sass之类的文件都可以导入到js文件中,但是在打包的过程中需要将这些文件都寻找到然后单独的分离出去,这样就需要用到对应的loader来进行文件处理,不同的文件、不同的loader,处理方式也不一样。

比如css文件可以利用style-loader将其代码生成style标签放入到head中,也可以利用minicssextra插件中 loader将其抽离成css文件。

img文件可以利用url-loader和file-loader对其进行base64的转化或者单独抽出。

29. git常用命令以及工作中都怎么工作

git init 初始化仓库
git status 查看当前各个区域的代码状态。
git log查看commit记录
git reflog查看完整记录
git add 添加工作区代码到暂存区
Git commit 暂存区代码的提交
git reset 代码的版本回退
git stash 将暂存处代码收起来
git stash pop 将收起来的暂存区的代码释放出来
Git tag 可以打标签
Git branch 基于当前分支创建一个分支
git checkout 切换分支
git merge 合并分支
git remote add origin 添加远端仓库地址
git clone 克隆仓库

git push 上传对应分支代码
git fetch 同步本地与远端所有代码

公司中每一个项目都会有一个对应的远端仓库(gitLab),我们需要创建账号并配置权限。

一般公司会有几个主要分支,分别对应4个环境,当代码更新的时候会通过流水线自动部署到对应的环境:

  1. 发布分支(prod、master)这个分支代码对应的就是线上的代码

  2. UAT分支(uat),这个分支上的代码对应的是公司内部演示用的分支

  3. TEST分支(test),这个分支的代码是用于测试

  4. DEV分支(dev),研发自测分支

正常功能开发或者bug修复, 从dev分支拉取代码,进行开发就可以。

如果是解决线上bug,应该从master拉取一个分支(hotfix__), 然后开发完成后将其合并到test或者uat,测试没有问题后,将其合并到master。还要将hotfix上对应的commit合并到dev分支, 专业dev分支也就修复了这个bug。

如果当前版本代码需要回退(功能不做了,要么要去先着急干别的), 执行git reset --hard, 再回到当前的commit也是git reset 只不过需要注意,此时要通过git reflog来查看时间最后的一次commit。

30. 协商缓存和强缓存区别

协商缓存和强缓存指的都是浏览器对静态资源文件的缓存机制。描述的就是什么时候去服务器请求,什么时候直接读取缓存中的文件。

强缓存是客户端直接查看本地的缓存文件是否过期,如果没有过期就直接取用。

查看过期的方法主要是依赖响应头上的expires(绝对时间)和cache-control(相对时间)上的时间来对比的

协商缓存指的是客户端去询问服务器对应的文件是否有更新,如果有更新才会重新请求。

依靠的是响应头上的last-modified(最后更新时间)及etag(内容变更的标识)来确认文件是否有更新。

一个文件是否重新请求要经过强缓存和协商缓存的完整过程后才能决定

  1. 强制缓存和协商缓存都针对静态资源

  2. 强制缓存在前,协商缓存在后。

  3. 资源未过期触发强制缓存,资源过期后再触发协商缓存。

  4. 判断过期的方法expires(绝对时间)、cache-control(相对时间)

  5. 判断资源是否有更新(Last-Modified 和 ETag)

需要注意的是,即使是静态资源,也是依靠url来进行缓存的,也就是说只要是url地址不一样,就一定会去获取最新的数据。

所以我们往往会有这样的需求,在静态文件的src后面添加一个时间戳,获取在打包的时候动态的生成带有hash值的文件名,这样可以阻止浏览器缓存,使用户获取到最新的文件,使用到最新的功能。

持续更新中...

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值