JS部分
- 
js脚本延迟加载的方式有哪些?
延迟加载就是等页面加载完成之后再加载js文件。延迟加载有助于提高页面的加载速度 一般有一下几种方式: defer属性:会让脚本的加载与文档的解析同步解析,然后在文档解析完成之后再执行脚本文件,这样的话可以使页面渲染不被阻断 async属性:脚本异步加载,不阻断页面解析过程,但是脚本加载完成之后会立即执行脚本。如果文档没有解析完成的话,同样会阻塞 动态创建DOM: 监听文档加载完成事件,加载完成之后动态创建script标签来引入脚本 使用setTimeout延迟加载:设置一个定时器来加载js脚本 将脚本标签放到文档底部,尽量在最后面加载
 - 
ES6模块与CommonJS模块有什么异同?
ES6模块与CommonJS模块区别: 1.CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。 2.CommonJS 模块的require()是同步加载模块,ES6 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段。 3.CommonJS是对模块的浅拷贝,ES6 Module是对模块的引入,即ES6 Module只存只读,不能改变其值,具体点就是指针指向不能变,类似const 。 4.import的接口是read-only(只读状态),不能修改其变量值。 即不能修改其变量的指针指向,但可以改变变量内部指针指向。可以对commonJS对重新赋值(改变指针指向),但是对ES6 Module赋值会编译报错。 ES6模块与CommonJS模块的共同点: 1.CommonJS和ES6 Module都可以对引⼊的对象进⾏赋值,即对对象内部属性的值进行改变。
 - 
for...in和for...of的区别?
 
for..in 是遍历对象获取的是对象的键名,,会遍历原型链,性能很差不推荐使用
for...of是ES6新增的方法,可以遍历数组和对象,并返回各项的值
- 
对Promise的理解 promise是异步编程的解决方案,promise有三种状态:pendding(进行中)、resolved(已完成)、rejected(失败); promise缺点:无法取消Promise,一旦创建它就会立即执行,无法中途取消;如果不设置回调函数,promise内部抛出的错误不能反应到外部;当处于pendding状态时,无法得知目前进展到哪个阶段一旦状态发生改变就不能修改 promise解决了异步编程的回调地狱
 - 
对async/await的理解
Promise 通过 then 链来解决多层回调的问题,现在又用 async/await 来进一步优化它 async/await是generator的语法糖,它是为优化then链而开发出来的。async函数返回一个promise对象。 async/await对比promise优势: 代码读起来更加同步 错误处理友好,async/wawit可以用try/catch,promise的错误捕捉非常冗余 调试友好
 - 
对象创建的方式有哪些?
- 字面量形式直接创建
 - 工厂模式:使用函数来封装创建对象的细节,从而达到复用的目的;缺点;创建的对象不能和类型创建练习
 - 构造函数:使用构造函数创建对象;缺点:函数属性重复创建,浪费空间
 - 原型模式:原型中添加共有的属性和方法;解决了函数复用问题,但是如果属性是引用类型会导致实例共享同一份数据
 - 组合构造函数和原型模式:通过构造函数初始化属性,通过原型来初始化方法;缺点是:使用了两种不同的模式,所以代码的封装性不够好
 - 动态原型模式:将原型方法赋值的创建过程移动到了构造函数内部
 - 寄生构造函数模式:这一种模式和工厂模式的实 现基本相同,我对这个模式的理解是,它主要是基于一个已有的类型,在实例化时对实例化的对象进行扩展。这样既不用修改原来的构造函 数,也达到了扩展对象的目的。它的一个缺点和工厂模式一样,无法实现对象的识别。
 
 - 
对象继承的方式有哪些?
- 原型链继承:会导致所有实例共享一份引用数据
 - 构造函数继承:无法实现函数的复用
 - 组合继承:原型和构造函数结合
 - 原型式继承:基于已有对象来创建新的对象,缺点与原型链继承相同
 - 寄生式继承:,寄生式继承的思路是创建一个用于封装继承过程的函数,通过传入一个对象,然后复制一个对象的副本,然后对象进行扩展,最后返回这个对象。缺点是无法实现函数的复用
 - 寄生组合式继承:组合继承的缺点就是使用超类型的实例做为子类型的原型,导致添加了不必要的原型属性。
 
 - 
哪些情况会导致内存泄漏?
- 意外的全局变量,未声明的变量
 - 闭包
 - 被遗忘的定时器或回调函数
 
 - 
JSON.stringify深拷贝的缺点
1、如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式 2、如果obj里面有RegExp,则打印出来是空对象 3、如果对象中有函数或者undefined,则会直接被丢掉 4、如果json里有对象是由构造函数生成的,则会丢掉对象的constructon 5、如果对象中存在循环引用的情况也无法正确实现深拷贝 6、如果对象中存在NAN,则序列化后会变成null
 
10.将数组转为对象
Object.assign()
...拓展语法
reduce
for..in
map
foreach
11.将对象转成数组
Array.from()
Object.keys(obj) -----根据对象的键形成的数组(常用)
Object.entries(obj) ---- 根据对象的键值对形成的数组
Object.values(obj)----
Reflect
12.浅拷贝和深拷贝
- 浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址
 - 深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址
 
浅拷贝:
    -   `Object.assign`
    -   `Array.prototype.slice()`, `Array.prototype.concat()`
    -   使用拓展运算符实现的复制
    
深拷贝:
   -   _.cloneDeep()
    -   jQuery.extend()
    -   JSON.stringify() (但是这种方式存在弊端,会忽略`undefined`、`symbol`和`函数`)
    -   手写循环递归
13.真实dom和虚拟dom区别 14.前端性能优化 15.前端安全
我们常见的Web攻击方式有
-   XSS (Cross Site Scripting) 跨站脚本攻击
-   CSRF(Cross-site request forgery)跨站请求伪造
-   SQL注入攻击
预防xss: 在使用 `.innerHTML`、`.outerHTML`、`document.write()` 时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用 `.textContent`、`.setAttribute()` 等如果用 `Vue/React` 技术栈,并且不使用 `v-html`/`dangerouslySetInnerHTML` 功能,就在前端 `render` 阶段避免 `innerHTML`、`outerHTML` 的 XSS 隐患;DOM 中的内联事件监听器,如 `location`、`onclick`、`onerror`、`onload`、`onmouseover` 等,`<a>` 标签的 `href` 属性,JavaScript 的 `eval()`、`setTimeout()`、`setInterval()` 等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API,很容易产生安全隐患,请务必避免
预防csrf:
    攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求,利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目
    -   阻止不明外域的访问
-   -   同源检测
-   Samesite Cookie
-   提交时要求附加本域才能获取的信息
-   -   CSRF Token
    -   双重Cookie验证
16.webpack loader和plugin 17. 跨域怎么解决
    JSONP
    代理proxy
    后端开启cors
19.vue3
特点:
    -   速度更快
    -   体积减少
    -   更易维护
    -   更接近原生
    -   更易使用
    
新特性:
 -   framents 多根节点
-   Teleport
-   composition Api
-   createRenderer
    
20.js事件循环机制
为了解决单线程运行阻塞问题,`JavaScript`用到了计算机系统的一种运行机制,这种机制就叫做事件循环(Event Loop)
同步任务进入主线程,即主执行栈,异步任务进入任务队列,主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。上述过程的不断重复就是事件循环
异步任务还可以细分为微任务与宏任务
常见的微任务有:
-   Promise.then
-   MutaionObserver
-   Object.observe(已废弃;Proxy 对象替代)
-   process.nextTick(Node.js)
常见的宏任务有:
-   script (可以理解为外层同步代码)
-   setTimeout/setInterval
-   UI rendering/UI事件
-   postMessage、MessageChannel
-   setImmediate、I/O(Node.js)
它的执行机制是:
-   执行一个宏任务,如果遇到微任务就将它放到微任务的事件队列中
-   当前宏任务执行完成后,会查看微任务的事件队列,然后将里面的所有微任务依次执行完
20.大文件上传
分片上传
分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块(Part)来进行分片上传
上传完之后再由服务端对所有上传的文件进行汇总整合成原始的文件
大致流程如下:
1.  将需要上传的文件按照一定的分割规则,分割成相同大小的数据块;
1.  初始化一个分片上传任务,返回本次分片上传唯一标识;
1.  按照一定的策略(串行或并行)发送各个分片数据块;
1.  发送完成后,服务端根据判断数据上传是否完整,如果完整,则进行数据块合成得到原始文件
21.webscoket遇到的问题
VUE部分
- Vue的基本原理
 
当一个Vue实例创建时,VUe会遍历data中的属性,用Object.defineProperty(vue3.0使用proxy)将他们转为getter/setter,并且在内部追踪其相关依赖,在属性被访问和修改时通知变化。每个组件都有相应的watcher程序实例,他会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。
- 双向数据绑定原理
 
Vue采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter和getter.在数据变动时发布消息给订阅者,触发相应的监听回调。主要分为一下几个步骤: 1. 需用监听的数据对象进行递归遍历,加上setter/setter 2. vue2 vue2使用了Object.definProperty来设置属性setter和getter.在属性被访问时,调用geter函数,并记录其依赖notify。当数据被改变时会触发setter函数,在其内部派发更新
- solt是什么?有什么作用?原理是什么?
 
solt是插槽,是Vue的内容分发机制,组件内部的模板引擎使用solt元素作为承载分发内容的出口。 默认插槽、具名插槽、作用域插槽
- $nextTick原理及其作用
 
Vue的nextTick本质是JS执行原理eventloop的一种应用。nextTick的核心是利用原生JS方法来模拟对应的宏任务和微任务,本质时为了利用JS的异步回调任务队列来实现Vue框架中自己的异步回调队列。 nextTick不仅是Vue内部的异步队列的调用方法,同时也允许开发者在实际项目中使用这个方法来满足实际应用中对DOM更新数据时机的后续处理
5.Vue单页面应用于多页面应用的区别
SPA:指只有一个主页面的应用,一开始只需要加载一次JS\cs等相关资源。所有内容包含在主页面,对每个功能模块组件化。单页应用跳转,就是切换相关组件,仅刷新局部资源。不利于SEO。首频加载时间长,体验佳
MPA:只多个独立页面应用,每个页面必须重复加载js\cs等相关西苑。多页应用跳转,需要刷新整页资源。利于SEO。首频加载快,但是后续体验不佳
- Vue中封装的数组方法有哪些,其如何实现页面更新?
 
Vue中对数据相应式处理利用的是Object.defineProperty对数据进行拦截,而这个方法不能监听到数组内部变化,所以需要操作重写,让Vue能监听到其中的变化
push/pop/shift/unshift/splice/sort/reverse
Vue如何让数组实现更新:Vue重写了数组中的原生方法,首先获取到这个数组的_ob_(Observer对象),如果有新的值就调用observeArray继续对新的值观察变化(target_proto_==arrayMethods改变数组实例的原型),然后手动调用notify,通知渲染watcher,执行update
- Vue data中的某一个属性值发生改变后,视图会立即同步执行重新渲染吗?
 
data中的属性发生改变后,vue会将其加入更新队列中,如果属性频繁变更会改变队列中的值后再做真是改变。如果同一个watcher被多次触发,只会被推入到队列中一次。然后在下一个事件循环tick中,Vue刷新队列并执行实际(已去重)工作
- 子组件可以修改父组件的数据吗?
 
Vue遵循单向数据流规则,不允许子组件修改父组件的数据,如果实在要修改,可以通过事件触发机制,通知父组件修改数据
- React和Vue的异同
 
相同点: (1)都将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库 (2)都有自己的构建工具,能让你得到一个根据最佳实践设置的项目模板 (3)都是用了虚拟DOM提高重绘性能 (4)都有props概念,允许组件间的数据传递 (5)都鼓励组件化应用,将应用分拆成一个个功能明确模块,提高复用性。 不同之处: (1)数据流: Vue默认支持数据双向绑定,而React一直提倡单项数据流 (2)虚拟DOM: 具体细节不同Vue 宣称可以更快地计算出 Virtual DOM 的差异,这是由于它在渲染过程中,会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。对于 React 而言,每当应用的状态被改变时,全部子组件都会重新渲染。当然,这可以通过 PureComponent/shouldComponentUpdate 这个生命周期方法来进行控制,但 Vue 将此视为默认的优化。 (3)组件化 React和Vue最大的不同就是模板的编写。Vue鼓励写近视常规HTML的模板。写起来很接近标准HTML元素,只是多了一些属性。React推荐你所有模板通用JSX。具体来讲:React 中 render 函数是支持闭包特性的,所以 import 的组件在 render 中可以直接调用。但是在 Vue 中,由于模板中使用的数据都必须挂在 this 上进行一次中转,所以 import 一个组件完了之后,还需要在 components 中再声明下 (4)监听数据变化的实现原理不同 Vue 通过 getter/setter 以及一些函数的劫持,能精确知道数据变化,不需要特别的优化就能达到很好的性能React 默 认 是 通 过 比 较 引 用 的 方 式 进 行 的 , 如 果 不 优 化(PureComponent/shouldComponentUpdate)可能导致大量不必要的vDOM 的重新渲染。这是因为 Vue 使用的是可变数据,而 React 更强调数据的不可变。 (5)高阶组件 react 可以通过高阶组件(HOC)来扩展,而 Vue 需要通过 mixins 来扩展。高阶组件就是高阶函数,而 React 的组件本身就是纯粹的函数,所以高阶函数对 React 来说易如反掌。相反 Vue.js 使用HTML 模板创建视图组件,这时模板无法有效的编译,因此 Vue 不能采用 HOC 来实现。
(6)构建工具 两者都有自己的构建工具:
React ==> Create React APP
Vue ==> vue-cli
(7)跨平台
React ==> React Native
Vue ==> Weex
- Vue的优点
 
轻量级框架:只关注视图层,是一个构建数据的视图集合,大小只有
几十 kb ;
简单易学:国人开发,中文文档,不存在语言障碍 ,易于理解和学
习;
双向数据绑定:保留了 angular 的特点,在数据操作方面更为简单;
组件化:保留了 react 的优点,实现了 html 的封装和重用,在构建单页面应用方面有着独特的优势;
视图,数据,结构分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作;虚拟 DOM:dom 操作是非常耗费性能的,不再使用原生的 dom 操作节点,极大解放 dom 操作,但具体操作的还是 dom 不过是换了另一种方式;
运行速度更快:相比较于 react 而言,同样是操作虚拟 dom,就性能而言, vue 存在很大的优势。
- assets 和 static 的区别
 
相同点: assets 和 static 两个都是存放静态资源文件。项目中所
需要的资源文件图片,字体图标,样式文件等都可以放在这两个文件
下,这是相同点
不相同点:assets 中存放的静态资源文件在项目打包时,也就是运
行 npm run build 时会将 assets 中放置的静态资源文件进行打包
上传,所谓打包简单点可以理解为压缩体积,代码格式化。而压缩后
的静态资源文件最终也都会放置在 static 文件中跟着 index.html
一同上传至服务器。static 中放置的静态资源文件就不会要走打包
压缩格式化等流程,而是直接进入打包好的目录,直接上传至服务器。
因为避免了压缩直接进行上传,在打包时会提高一定的效率,但是
static 中的资源文件由于没有进行压缩等操作,所以文件的体积也
就相对于 assets 中打包后的文件提交较大点。在服务器中就会占据
更大的空间。
建议: 将项目中 template 需要的样式文件 js 文件等都可以放置在
assets 中,走打包这一流程。减少体积。而项目中引入的第三方的
资源文件如 iconfoont.css 等文件可以放置在 static 中,因为这
些引入的第三方文件已经经过处理,不再需要处理,直接上传。
- Vue编译模板原理
 
vue 中的模板 template 无法被浏览器解析并渲染,因为这不属于浏
览器的标准,不是正确的 HTML 语法,所有需要将 template 转化成一
个 JavaScript 函数,这样浏览器就可以执行这一个函数并渲染出对
应的 HTML 元素,就可以让视图跑起来了,这一个转化的过程,就成
为模板编译。模板编译又分三个阶段,解析 parse,优化 optimize,
生成 generate,最终生成可执行函数 render。
解析阶段:使用大量的正则表达式对 template 字符串进行解析,将
标签、指令、属性等转化为抽象语法树 AST。
优化阶段:遍历 AST,找到其中的一些静态节点并进行标记,方便在
页面重渲染的时候进行 diff 比较时,直接跳过这一些静态节点,优
化 runtime 的性能。
生成阶段:将最终的 AST 转化为 render 函数字符串。
- Vue初始化页面闪动问题
 
{{message}} 可以给便签添加V-cloak指令,并且添加css
[v-cloak]{
display:none;
}
14.MVVM的优点
优点: 分离视图和模型,降低代码耦合,提高视图或者逻辑的重用性。
提高可测试性: VM可以帮助开发者更好地编写测试代码
自动更新DOM: 利用双向数据绑定
缺点:
bug很难被调试
花费内存高
对于大型项目,视图状态较多,VM构建和维护的成本就会比较高
15. v-if和v-for哪一个优先级更高?如果同时出现应如何优化?
v-for优先级更高,如果同时出现每次渲染都会先执行循环再判断条件,浪费性能
优化:可以使用计算属性提前过滤掉那些不需要显示的项
- Vue子组件和父组件执行顺序
 
加载渲染过程
1.父组件beforeCreate
2.父组件created
3.父beforeMount
4.子组件beforeCreate
5.子组件created
6.子组件beforeMounte
7.子组件Mounted
8.父组件Mounted
更新过程
 1. 父组件 beforeUpdate
 2.子组件 beforeUpdate
 3.子组件 updated
 4.父组件 updated
销毁过程
1. 父组件 beforeDestroy
2.子组件 beforeDestroy
3.子组件 destroyed
4.父组件 destoryed
17. v-model修饰符
.number 以parseFloat转成数字类型
.trim 去除首尾空白字符
.lazy 在失去焦点时触发更改而非inpput
18. 计算属性
计算属性有缓存,提高渲染性能
如果在页面上需要用到对现有数据进行加工处理的数据,则需要使用计算属性
普遍写法:
computed: {
        //属性名字(计算属性名称)
        //属性的值(计算属性处理函数)
        计算属性名1 () {
            // 对依赖的数据进行处理,且进行return
            return 
        }
完整写法:
computed: {
    "属性名": {
        set(值){
            
        },
        get() {
            return "值"
        }
    }
}
- 
Vue监听器 watch
watch 可以监听数据(data/computed)值的改变。
 
watch: {
    "要监听的属性名": {
        immediate: true, // 立即执行
        deep: true, // 深度监听复杂类型内变化
        handler (newVal, oldVal) {
            
        }
    }
}
- scoped实现组件的私有样式
 
<stype scoped>
  h2 {} // 样式只会在当前组件内生效\
</style>
- 
组件传值
- props emit
 - provide inject
 - $bus
 - vuex
 - event
 - prante
 
 - 
props校验
 
子组件在注册父组件传递过来的props可以进行类型和值的校验
props:{
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)\
    propA: Number,\
    // 多个可能的类型\
    propB: [String, Number],\
    // 必填的字符串\
    propC: {\
      type: String,\
      required: true\
    },\
    // 带有默认值的数字\
    propD: {\
      type: Number,\
      default: 100\
    },\
    // 带有默认值的对象\
    propE: {\
      type: Object,\
      // 对象或数组默认值必须从一个工厂函数获取\
      default: function () {\
        return { message: 'hello' }\
      }\
    },\
    // 自定义验证函数\
    propF: {\
      validator: function (value) {\
        // 这个值必须匹配下列字符串中的一个\
        return ['success', 'warning', 'danger'].indexOf(value) !== -1\
      }\
    }
}
- 动态组件
 
<component :is="comName"></component> //comName是变量,值为需要切换的几个组件名
- keep-alive组件
 
用keep-alive缓存组件,一般使用于多个组件频繁切换的时候,用于缓存,防止重复渲染dom
<keep-alive>\
    <!-- vue内置的组件component, 可以动态显示组件 -->\
    <component :is="comName"></component>\
</keep-alive>
include="组件1,组件2" 指定缓存组件 :include="['组件1','组件2']"
keep-alive中有两个生命周期 activated和deactivated, 当组件被换掉时,会被缓存到内存中,触发deactivated 当组件被切换回来时,会去缓存中找到这个组件,触发activated钩子函数 25. 具名插槽
定义:
<slot name="XXX">
使用:
<template #xxx></template>
<template v-slot:xxx></template>
- 自定义指令-传值和更新
 
自定义指令,获取原生dom,自定义操作 目标:使用自定义指令,传入一个值 需求:定义color指令-传入一个颜色,给标签设置文字颜色
directives: {\
  "color":{\
    inserted(el, binding){ // 插入时触发此函数\
      el.style.color = binding.value;\
    },\
    update(el, binding){ // 更新绑定的变量时触发此函数=》手动更新\
      el.style.color = binding.value;\
    }\
  }\
}
使用
<p v-color="theColor" @click="changeColor">使用v-color指令控制颜色, 点击变蓝</p>\
\
<script>\
  data() {\
    return {\
      theColor: "red",\
    };\
  },\
  methods: {\
    changeColor() {\
      this.theColor = 'blue';\
    },\
  },\
</script>
- created和mounted的区别
 
created: 在模板渲染成html前调用,即通常初始化某些属性值,然后渲染成视图 mounted:在模板渲染成html后调用,通常是初始化页面完成之后,再对html的dom节点进行一些需要的操作
一般建议再created中去发送异步请求,因为数据有已经初始化完成,这个时候发送请求能够较快得拿到后端数据。减少页面加载时间,用户体验更好。ssr中不支持beforeMount和mouted钩子函数,放在created中有组于一致性
- Vue3有什么更新
 
1.监听机制发生改变
用proxy取代Object.defineProperty 消除了defineroPerty存在的限制
defineroPerty带来的问题:
    只能监测属性,不能监测对象;不能监测到属性的添加和删除,数组长度和索引的变更; 不支持Map、Set、WeakMap、WeakSet
2.使用TS编写,支持类型推断。
3.新增comenpent api让Vue功能性代码逻辑能够提出hooks,便于代码维护和逻辑复用
4.支持多片段代码
5.使用Vue单例模式
6.优化tree shaking,提供了更多内置功能
- 
defineProperty和proxy的区别
defineProperty
拦截对象的属性,存在一下问题:添加和删除对象属性时,Vue监测不到;无法监测数组索引和长度改变; proxy是ES6提供的用来代理对象的API;proxy代理整个对象而非对象属性,这样就可以监听到对象属性新增和删除了;proxy可以监听到数组的变化。支持 Map,Set,WeakMap 和 WeakSet。
Proxy 实现的响应式原理与 Vue2 的实现原理相同,实现方式大同小异∶
get 收集依赖 Set、delete 等触发依赖 对于集合类型,就是对集合对象的方法做一层包装:原方法执行后执 行依赖相关的收集或触发逻辑。 - 
虚拟DOM的解析过程
 
分析DOM树结构,用JS对象表达出来,DOM片段插入到文档
状态发生改变,重新构建对象树,新树和旧树对比,记录差异,修改真是DOM
31. diff算法原理
在新老虚拟 DOM 对比时:
首先,对比节点本身,判断是否为同一节点,如果不为相同节点,则
删除该节点重新创建节点进行替换
如果为相同节点,进行 patchVnode,判断如何对该节点的子节点进
行处理,先判断一方有子节点一方没有子节点的情况(如果新的
children 没有子节点,将旧的子节点移除)
比较如果都有子节点,则进行 updateChildren,判断如何对这些新
老节点的子节点进行操作(
diff 核心)。
匹配时,找到相同的子节点,递归比较子节点
在 diff 中,只对同层的子节点进行比较,放弃跨级的节点比较,使
得时间复杂从 O(n3)降低值 O(n),也就是说,只有当新旧 children
都为多个子节点时才需要用核心的 Diff 算法进行同层级比较。
- 
Vue中key的作用
Vue中的key分两种情况来考虑:
 
1.v-if中使用的key。由于vue会尽可能高效地渲染元素,通常会复用已有元素而不是重头渲染。因此当使用v-if来实现元素切换的时候,如果切换前后元素含有相同类型的元素,那么这个元素就会被复用。用key的作用是用来标识一个独立的元素
2.v-for中使用key。用v-for更新已渲染过的元素列表时,他默认使用‘就地复用’的策略。如果数据项的顺序发生改变,Vue不会移动DOM元素来匹配数据项的顺序,而是简单复用此处的 每个元素。因此通过为每个列表提供一个key值,以便Vue来跟踪元素的身份,从而高效的实现复用,这个时候key的作用就是高效的更新渲染虚拟DOM
key是为Vue中Vnode的唯一标记,通过这个key,diff操作可以更准确、更快速
为什么更准确?因为带key就不是就地复用了,在sameNode函数a.key===b.key对比中可以避免就地复用的情况。所以会更加准确
为什么更快速?利用key的唯一性生成map对象来获取对应节点,比遍历更快
React部分
性能优化部分
- 懒加载的概念
 
懒加载也叫做延迟加载、按需加载。
在长网页或应用中,如果图片很多,所以图片都加载出来,而用户只能看到可视区域的那一部分图片数据,这样就浪费性能,
使用图片懒加载: 在屏幕滚动时才加载后面的几张图片,这样页面加载速度更快,减少了服务器的负载。
懒加载适用于图片较多或者长列表场景中
- 懒加载的特点
 
减少无用资源的加载
减少服务器压力和流量
减少了浏览器的负担
提升用户体验
防止加载过多图片而影响其他资源文件的加载
- 懒加载的实现原理
 
图片的加载是由 src 引起的,当对 src 赋值时,浏览器就会请求图片
资源。根据这个原理,我们使用 HTML5 的 data-xxx 属性来储存图片
的路径,在需要加载图片的时候,将 data-xxx 中图片的路径赋值给
src,这样就实现了图片的按需加载,即懒加载。
注意:data-xxx 中的 xxx 可以自定义,这里我们使用 data-src 来定
义。
懒加载的实现重点在于确定用户需要加载哪张图片,在浏览器中,可
视区域内的资源就是用户需要的资源。所以当图片出现在可视区域时,
获取图片的真实地址并赋值给图片即可。
使用原生 JavaScript 实现懒加载:
知识点:
window.innerHeight 是浏览器可视区的高度
document.body.scrollTop
105document.documentElement.scrollTop 是浏览器滚动的过的距离
imgs.offsetTop 是元素顶部距离文档顶部的高度(包括滚动条的距
离)
图 片 加 载 条 件 : img.offsetTop < window.innerHeight +
document.body.scrollTop;
- 
回流和重绘的概念及触发条件
- 回流(重排) 当元素尺寸、结构或者属性发生变化时,浏览器会重新渲染文档的过程称之为回流
 
下面这些操作会导致回流:
页面首次渲染
浏览器窗口大小发生改变
元素的内容发生改变
元素的尺寸或者位置发生改变
激活css伪类
查询某些属性或者调用某些方法
添加或者删除可见的DOM元素
- 重绘
 
当页面某些元素的样式发生改变,但是不会影响其在文档中的位置时,浏览器就会对元素进行重新绘制,这个过程就是重绘。
下面这些操作会导致重绘
color|background相关属性:background-color、background-image等
outline相关属性:outline-color 、 outline-width、text-decoration 、border-radius、visibility、box-shadow
注意:当触发回流时,一定会触发重绘,当时重绘不一定会引发回流
 - 
如何避免回流和重绘?
 
操作 DOM 时,尽量在低层级的 DOM 节点进行操作
不要使用 table 布局, 一个小的改动可能会使整个 table 进行重新
布局
使用 CSS 的表达式不要频繁操作元素的样式,对于静态页面,可以修改类名,而不是样
式。
使用 absolute 或者 fixed,使元素脱离文档流,这样他们发生变化
就不会影响其他元素
避免频繁操作 DOM,可以创建一个文档片段 documentFragment,在它
上面应用所有 DOM 操作,最后再把它添加到文档中
将元素先设置 display: none,操作结束后再把它显示出来。因为在
display 属性为 none 的元素上进行的 DOM 操作不会引发回流和重绘。
将 DOM 的多个读操作(或者写操作)放在一起,而不是读写操作穿插
着写。这得益于浏览器的渲染队列机制。
浏览器针对页面的回流与重绘,进行了自身的优化——渲染队列
浏览器会将所有的回流、重绘的操作放在一个队列中,当队列中的操
作到了一定的数量或者到了一定的时间间隔,浏览器就会对队列进行
批处理。这样就会让多次的回流、重绘变成一次回流重绘。
上面,将多个读操作(或者写操作)放在一起,就会等所有的读操作
进入队列之后执行,这样,原本应该是触发多次回流,变成了只触发
一次回流。
- 如何优化动画?
 
一般情况下,动画需要频繁的操作
DOM,就就会导致页面的性能问题,我们可以将动画的 position 属性
设置为 absolute 或者 fixed,将动画脱离文档流,这样他的回流就
不会影响到页面了
7.documentFragment 是什么?用它跟直接操作 DOM 的区别是
什么?
MDN 中对 documentFragment 的解释:
DocumentFragment,文档片段接口,一个没有父对象的最小文档对象。
它被作为一个轻量版的 Document 使用,就像标准的 document 一样,
存储由节点(nodes)组成的文档结构。与 document 相比,最大的区
别是 DocumentFragment 不是真实 DOM 树的一部分,它的变化不会触
发 DOM 树的重新渲染,且不会导致性能等问题。
当我们把一个 DocumentFragment 节点插入文档树时,插入的不是
DocumentFragment 自身,而是它的所有子孙节点。在频繁的 DOM 操
作时,我们就可以将 DOM 元素插入 DocumentFragment,之后一次性
的将所有的子孙节点插入文档中。和直接操作 DOM 相比,将
DocumentFragment 节点插入 DOM 树时,不会触发页面的重绘,这样
就大大提高了页面的性能
8.对节流和防抖的理解
防抖:指事件在一定时间后再执行回调,如果在这段时间内事件又被触发,则会重新计时。运用在点击请求的 事件上面,避免用户多次点击后向后端发送多次请求。按钮提交场景
function debounde(fn,t){
    let timer = null
    return function(){
        let context = this
        let args = [...arguments]
        if(timer){
            clearTimeout(timer)
            timer = null
        }
        
        timer = setTimeout(()=>{
            fn.apply(context,args)
        },t)
    }
}
节流:指在规定时间内指触发一次回调函数,如果在规定时间内被触发多次也只会执行一次。运用了页面scroll事件的监听上面,通过节流来降低事件调用的频率。拖拽场景、缩放场景、动画尝尽
//定时器版本
funtion throttle( fn, t ){
    let timer =null
    return funtion (){
         let context = this
         let args = [...arguments]
         
         if(!timer){
             timer = setTimeout(()=>{
                fn.apply(context,args)
                timer =null
               },t)
         }
          
    }
}
//时间戳版本
function throttle( fn, t ){
    let preTIme = Date.now()
    
    return function (){
        let context = this
        let args = [...arguments]
        let nowTime = Date.now()
        
        //如果两次时间间隔超过指定的时间,则执行函数
        if(nowTime-preTime>=t){
            fn.apply( context, args )
        }
    }
}
10.如何对项目中图片优化
1.不用图片。对于一些小图片可以用css或者是字体图标代替
2.使用CDN加载图片或者对象存储
3.小图片可以使用base64格式
4.雪碧图(精灵图)
5.选择正确的图片格式
尽量使用WebP格式。因为WebP格式具有更好的图像数据压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量,缺点就是兼容性并不好
小图使用png,其实大本分都可以使用SVG代替
照片使用JPEG
11. 常见的图片格式及使用场景
(1)BMP,是无损的、既支持索引色也支持直接色的点阵图。这种图
片格式几乎没有对数据进行压缩,所以 BMP 格式的图片通常是较大的
文件。
(2)GIF 是无损的、采用索引色的点阵图。采用 LZW 压缩算法进行
编码。文件小,是 GIF 格式的优点,同时,GIF 格式还具有支持动画
以及透明的优点。但是 GIF 格式仅支持 8bit 的索引色,所以 GIF 格
式适用于对色彩要求不高同时需要文件体积较小的场景。
(3)JPEG 是有损的、采用直接色的点阵图。JPEG 的图片的优点是采
用了直接色,得益于更丰富的色彩,JPEG 非常适合用来存储照片,
与 GIF 相比,JPEG 不适合用来存储企业 Logo、线框类的图。因为有
损压缩会导致图片模糊,而直接色的选用,又会导致图片文件较 GIF
更大。
(4)PNG-8 是无损的、使用索引色的点阵图。PNG 是一种比较新的图
片格式,PNG-8 是非常好的 GIF 格式替代者,在可能的情况下,应该
尽可能的使用 PNG-8 而不是 GIF,因为在相同的图片效果下,PNG-8
具有更小的文件体积。除此之外,PNG-8 还支持透明度的调节,而 GIF
并不支持。除非需要动画的支持,否则没有理由使用 GIF 而不是 PNG-8。
(5)PNG-24 是无损的、使用直接色的点阵图。PNG-24 的优点在于它
压缩了图片的数据,使得同样效果的图片,PNG-24 格式的文件大小
要比 BMP 小得多。当然,PNG24 的图片还是要比 JPEG、GIF、PNG-8
大得多。
(6)SVG 是无损的矢量图。SVG 是矢量图意味着 SVG 图片由直线和曲
线以及绘制它们的方法组成。当放大 SVG 图片时,看到的还是线和曲
线,而不会出现像素点。这意味着 SVG 图片在放大时,不会失真,所
以它非常适合用来绘制 Logo、Icon 等。
(7)WebP 是谷歌开发的一种新图片格式,WebP 是同时支持有损和无
损压缩的、使用直接色的点阵图。从名字就可以看出来它是为 Web 而
生的,什么叫为 Web 而生呢?就是说相同质量的图片,WebP 具有更
小的文件体积。现在网站上充满了大量的图片,如果能够降低每一个
图片的文件大小,那么将大大减少浏览器和服务器之间的数据传输量,
进而降低访问延迟,提升访问体验。目前只有 Chrome 浏览器和 Opera
浏览器支持 WebP 格式,兼容性不太好。
在无损压缩的情况下,相同质量的 WebP 图片,文件大小要比 PNG 小
26%;
在有损压缩的情况下,具有相同图片精度的 WebP 图片,文件大小要
比 JPEG 小 25%~34%;
WebP 图片格式支持图片透明度,一个无损压缩的 WebP 图片,如果要
支持透明度只需要 22%的格外文件大小。
- 
如何用webpack来优化前端性能?
1. 压缩代码:删除多余的代码、注释、简化代码的写法等等⽅式。可以 利⽤webpack的 UglifyJsPlugin 和 ParallelUglifyPlugin 来压缩JS⽂件, 利⽤ cssnano (css-loader?minimize)来压缩 css 2. 利⽤CDN 加速: 在构建过程中,将引⽤的静态资源路径修改为 CDN 上对应的路径。可以利⽤webpack 对于 output 参数和各 loader 的publicPath 参数来修改资源路径 3. Tree Shaking: 将代码中永远不会⾛到的⽚段删除掉。可以通过在启动 webpack 时追加参数 --optimize-minimize 来实现 4. Code Splitting: 将代码按路由维度或者组件分块(chunk),这样做到按需加载,同时可以充分利⽤浏览器缓存 5. 提取公共第三⽅库: SplitChunksPlugin 插件来进⾏公共模块抽取,利⽤浏览器缓存可以⻓期缓存这些⽆需频繁变动的公共代码 - 
如何提高webpack的构建速度?
- 多入口情况下,使用CommonsChunkPlugin来提取公共代码
 - 通过externals配置来提取常用库
 - 利⽤ DllPlugin 和 DllReferencePlugin 预编译资源模块 通过DllPlugin 来对那些我们引⽤但是绝对不会修改的 npm 包来进⾏预编译,再通过 DllReferencePlugin 将预编译的模块加载进来。
 - 使⽤ Happypack 实现多线程加速编译
 - 使⽤ webpack-uglify-parallel 来提升 uglifyPlugin 的压缩速度。原理上 webpack-uglify-parallel 采⽤了多核并⾏压缩来提升压缩速度
 - 使⽤ Tree-shaking 和 Scope Hoisting 来剔除多余代码
 
 
前端工程化部分
- webpack与grunt\gulp的不同
 
- 
Grunt、Gulp 是基于任务运⾏的⼯具: 它们会⾃动执⾏指定的任务,就像流⽔线,把资源放上去然后通过不同插件进⾏加⼯,它们包含活跃的社区,丰富的插件,能⽅便的打造各种⼯作流。
 - 
Webpack 是基于模块化打包的⼯具: ⾃动化处理模块,webpack 把⼀切当成模块,当 webpack 处理应⽤程序时,它会递归地构建⼀个依赖关系图 (dependency graph),其中包含应⽤程序需要的每个模块,然后将所有这些模块打包成⼀个或多个 bundle。
 - 
因此这是完全不同的两类⼯具,⽽现在主流的⽅式是⽤npm script 代替 Grunt、Gulp,npm script 同样可以打造任务流。
 
- webpack、rollup、parcel 优劣?
 
- 
webpack 适⽤于⼤型复杂的前端站点构建: webpack 有强⼤的 loader和插件⽣态,打包后的⽂件实际上就是⼀个⽴即执⾏函数,这个⽴即执⾏函数接收⼀个参数,这个参数是模块对象,键为各个模块的路径,值为模块内容。⽴即执⾏函数内部则处理模块之间的引⽤,执⾏模块等,这种情况更适合⽂件依赖复杂的应⽤开发
 - 
rollup 适⽤于基础库的打包,如 vue、d3 等: Rollup 就是将各个模块打包进⼀个⽂件中,并且通过 Tree-shaking 来删除⽆⽤的代码,可以最⼤程度上降低代码体积,但是rollup没有webpack如此多的的如代码分割、按需加载等⾼级功能,其更聚焦于库的打包,因此更适合库的开发。
 - 
parcel 适⽤于简单的实验性项⽬: 他可以满⾜低⻔槛的快速看到效果,但是⽣态差、报错信息不够全⾯都是他的硬伤,除了⼀些玩具项⽬或者实验项⽬不建议使⽤
 
3.常见的Loader
- 
file-loader: 把⽂件输出到⼀个⽂件夹中,在代码中通过相对 URL去引⽤输出的⽂件
 - 
url-loader:和 file-loader 类似,但是能在⽂件很⼩的情况下以base64 的⽅式把⽂件内容注⼊到代码中去
 - 
source-map-loader:加载额外的 Source Map ⽂件,以⽅便断点调试
 - 
image-loader:加载并且压缩图⽚⽂件
 - 
babel-loader:把 ES6 转换成 ES5
 - 
css-loader:加载 CSS,⽀持模块化、压缩、⽂件导⼊等特性
 - 
style-loader:把 CSS 代码注⼊到 JavaScript 中,通过 DOM 操作去加载 CSS。
 - 
eslint-loader:通过 ESLint 检查 JavaScript 代码
注意:在 Webpack 中,loader 的执行顺序是从右向左执行的。因为webpack 选择了 compose 这样的函数式编程方式,这种方式的表达式执行是从右向左的。
 
- 
有哪些常⻅的 Plugin?
- 
define-plugin:定义环境变量
 - 
html-webpack-plugin:简化 html⽂件创建
 - 
uglifyjs-webpack-plugin:通过 UglifyES 压缩 ES6 代码
 - 
webpack-parallel-uglify-plugin: 多核压缩,提⾼压缩速度
 - 
webpack-bundle-analyzer: 可视化 webpack 输出⽂件的体积
 - 
mini-css-extract-plugin: CSS 提取到单独的⽂件中,⽀持按需加载
 
 - 
 - 
bundle,chunk,module 是什么?
 
- 
bundle:是由 webpack 打包出来的⽂件;
 - 
chunk:代码块,⼀个 chunk 由多个模块组合⽽成,⽤于代码的合并和分割;
 - 
module:是开发中的单个模块,在 webpack 的世界,⼀切皆模块,⼀个模块对应⼀个⽂件,webpack 会从配置的 entry 中递归开始找出所有依赖的模块。
 
- Loader和Plugin的区别
 
- 
不同的作用
- 
Loader 直译为"加载器"。Webpack 将⼀切⽂件视为模块,但是 webpack原⽣是只能解析 js⽂件,如果想将其他⽂件也打包的话,就会⽤到loader 。 所以 Loader 的作⽤是让 webpack 拥有了加载和解析⾮JavaScript⽂件的能⼒。
 - 
Plugin 直译为"插件"。Plugin 可以扩展 webpack 的功能,让 webpack具有更多的灵活性。在 Webpack 运⾏的⽣命周期中会⼴播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的API 改变输出结果。
 
 - 
 - 
不同的用法
- 
Loader 在 module.rules 中配置,也就是说他作为模块的解析规则⽽存在。 类型为数组,每⼀项都是⼀个 Object ,⾥⾯描述了对于什么类型的⽂件( test ),使⽤什么加载( loader )和使⽤的参数( options )
 - 
Plugin在 plugins 中单独配置。类型为数组,每⼀项是⼀个 plugin的实例,参数都通过构造函数传⼊。
 
 - 
 
- webpack 热更新的实现原理?
 
⾸先要知道 server 端和 client 端都做了处理⼯作:
第⼀步,在 webpack 的 watch 模式下,⽂件系统中某⼀个⽂件发⽣修改,webpack 监听到⽂件变化,根据配置⽂件对模块重新编译打包,并将打包后的代码通过简单的 JavaScript对象保存在内存中。
第⼆步是 webpack-dev-server 和 webpack 之间的接⼝交互,⽽在这⼀步,主要是 dev-server 的中间件 webpack- dev-middleware和 webpack 之间的交互,webpack-dev-middleware 调⽤ webpack暴露的 API 对代码变化进⾏监 控,并且告诉 webpack,将代码打包到内存中。
第三步是 webpack-dev-server 对⽂件变化的⼀个监控,这⼀步不同于第⼀步,并不是监控代码变化重新打包。当我们在配置⽂件中配置了 devServer.watchContentBase 为 true 的时候,Server 会监听这些配置⽂件夹中静态⽂件的变化,变化后会通知浏览器端对应⽤进⾏ live reload。注意,这⼉是浏览器刷新,和 HMR 是两个概念。
第四步也是 webpack-dev-server 代码的⼯作,该步骤主要是通过sockjs(webpack-dev-server 的依赖)在浏览器端和服务端之间建⽴⼀个 websocket ⻓连接,将 webpack 编译打包的各个阶段的状态信息告知浏览器端,同时也包括
第三步中 Server 监听静态⽂件变化的信息。浏览器端根据这些 socket 消息进⾏不同的操作。当然服务端传递的最主要信息还是新模块的 hash 值,后⾯的步骤根据这⼀hash 值来进⾏模块热替换。webpack-dev-server/client 端并不能够请求更新的代码,也不会执⾏ 热 更 模 块 操 作 , ⽽ 把 这 些 ⼯ 作 ⼜ 交 回 给 了 webpack ,webpack/hot/dev-server就是根据webpack-dev-server/client 传给它的信息以及 dev-server 的配置决定是刷新浏览器呢还是进⾏模块热更新。当然如果仅仅是刷新浏览器,也就没有后⾯那些步骤HotModuleReplacement.runtime 是客户端 HMR 的中枢,它接收到上⼀ 步 传 递 给 他 的 新 模 块 的hash值 , 它 通 过JsonpMainTemplate.runtime 向 server 端发送 Ajax 请求,服务端返回⼀个 json,该 json 包含了所有要更新的模块的 hash 值,获取到更新列表后,该模块再次通过 jsonp 请求,获取到最新的模块代码。这就是上图中 7、8、9 步骤。⽽第 10 步是决定 HMR 成功与否的关键步骤,在该步骤中,
HotModulePlugin 将会对新旧模块进⾏对⽐,决定是否更新模块,在决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引⽤。
最后⼀步,当 HMR 失败后,回退到 live reload 操作,也就是进⾏
浏览器刷新来获取最新打包代码。
8.Babel 的原理是什么?
babel 的转译过程也分为三个阶段,这三步具体是:
解析 Parse: 将代码解析⽣成抽象语法树(AST),即词法分析与语法分析的过程;
转换 Transform: 对于 AST 进⾏变换⼀系列的操作,babel 接受得到 AST 并通过 babel-traverse 对其进⾏遍历,在此过程中进⾏添加、更新及移除等操作;
⽣成 Generate: 将变换后的 AST 再转换为 JS 代码, 使⽤到的模块是 babel-generator。
- git和svn的区别
 
git 和 svn 最大的区别在于 git 是分布式的,而 svn 是集中式的。
因此我们不能再离线的情况下使用 svn。如果服务器出现问题,就没有办法使用 svn 来提交代码。
svn 中的分支是整个版本库的复制的一份完整目录,而 git 的分支是指针指向某次提交,因此 git 的分支创建更加开销更小并且分支上的变化不会影响到其他人。svn 的分支变化会影响到所有的人。svn 的指令相对于 git 来说要简单一些,比 git 更容易上手。
GIT 把内容按元数据方式存储,而 SVN 是按文件:因为 git 目录是处于个人机器上的一个克隆版的版本库,它拥有中心版本库上所有的东西,例如标签,分支,版本记录等。
GIT 分支和 SVN 的分支不同:svn 会发生分支遗漏的情况,而 git 可以同一个工作目录下快速的在几个分支间切换,很容易发现未被合并的分支,简单而快捷的合并这些文件。
GIT 没有一个全局的版本号,而 SVN 有GIT 的内容完整性要优于 SVN:GIT 的内容存储使用的是 SHA-1 哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏
12.git rebase 和 git merge 的区别
git merge 和 git rebase 都是用于分支合并,关键在 commit 记录的处理上不同:
git merge 会新建一个新的 commit 对象,然后两个分支以前的commit 记录都指向这个新 commit 记录。这种方法会保留之前每个分支的 commit 历史。
git rebase 会先找到两个分支的第一个共同的 commit 祖先记录,然后将提取当前分支这之后的所有 commit 记录,然后将这个commit 记录添加到目标分支的最新提交后面。经过这个合并后,两个分支合并后的 commit 记录就变为了线性的记录了
浏览器部分
- 什么是XSS攻击?
 
XSS是跨站脚本攻击,是一种代码注入攻击。攻击者通过在网站注入恶意脚本,使之在用户的浏览器上运行,从而盗取用户信息如cookie等
XSS 的本质是因为网站没有对恶意代码进行过滤,与正常的代码混合在一起了,浏览器没有办法分辨哪些脚本是可信的,从而导致了恶意代码的执行。
攻击者可以通过这种方式进行如下操作:
获取页面的数据,如DOM、cookie、localStorage
DOS攻击,发送合理请求,占用服务器资源,从而使与用户无法访问服务器
破坏页面结构
流量劫持(将连接指向某网站)
XSS 可以分为存储型、反射型和 DOM 型:
存储型指的是恶意脚本会存储在目标服务器上,当浏览器请求数据时,脚本从服务器传回并执行。
反射型指的是攻击者诱导用户访问一个带有恶意代码的 URL 后,服务器端接收数据后处理,然后把带有恶意代码的数据发送到浏览器端,浏览器端解析这段带有 XSS 代码的数据后当做脚本执行,最终完成XSS 攻击。
DOM 型指的通过修改页面的 DOM 节点形成的 XSS
2.如何防御XSS攻击
1. 使用纯前端的方式,不用ssr
2.对需要插入到html中的代码做好充分的转义
3.使用CSP,建立白名单,告诉浏览器哪些脚本是安全的
3. 什么是CSRF攻击
CSRF是跨站请求伪造攻击,利用cookie会在同源请求中携带发送服务器的特点,来实现用户的冒充
常见的 CSRF 攻击有三种:
GET 类型的 CSRF 攻击,比如在网站中的一个 img 标签里构建一个请求,当用户打开这个网站的时候就会自动发起提交。
POST 类型的 CSRF 攻击,比如构建一个表单,然后隐藏它,当用户进入页面时,自动提交这个表单。
链接类型的 CSRF 攻击,比如在 a 标签的 href 属性里构建一个请求,然后诱导用户去点击
如何防止CSRF
**进行同源检测**,服务器根据 http 请求头中 origin 或者 referer信息来判断请求是否为允许访问的站点,从而对请求进行过滤。当origin 或者 referer 信息都不存在的时候,直接阻止请求。这种方式的缺点是有些情况下 referer 可以被伪造,同时还会把搜索引擎的链接也给屏蔽了。所以一般网站会允许搜索引擎的页面请求,但是相应的页面请求这种请求方式也可能被攻击者给利用。(Referer 字段会告诉服务器该网页是从哪个页面链接过来的)
**使用 CSRF Token 进行验证**,服务器向用户返回一个随机数 Token ,当网站再次发起请求时,在请求参数中加入服务器端返回的 token ,然后服务器对这个 token 进行验证。这种方法解决了使用 cookie单一验证方式时,可能会被冒用的问题,但是这种方法存在一个缺点就是,我们需要给网站中的所有请求都添加上这个 token,操作比较繁琐。还有一个问题是一般不会只有一台网站服务器,如果请求经过负载平衡转移到了其他的服务器,但是这个服务器的 session 中没有保留这个 token 的话,就没有办法验证了。这种情况可以通过改变 token 的构建方式来解决。
对 Cookie 进行双重验证,服务器在用户访问网站页面时,向请求域
名注入一个 Cookie,内容为随机字符串,然后当用户再次向服务器
发送请求的时候,从 cookie 中取出这个字符串,添加到 URL 参数
中,然后服务器通过对 cookie 中的数据和参数中的数据进行比较,
来进行验证。使用这种方式是利用了攻击者只能利用 cookie,但是
不能访问获取 cookie 的特点。并且这种方法比 CSRF Token 的方法
更加方便,并且不涉及到分布式访问的问题。这种方法的缺点是如果
网站存在 XSS 漏洞的,那么这种方式会失效。同时这种方式不能做
到子域名的隔离。
在设置 cookie 属性的时候设置 Samesite ,限制 cookie 不能作为
被第三方使用,从而可以避免被攻击者利用。Samesite 一共有两种
模式,一种是严格模式,在严格模式下 cookie 在任何情况下都不可
能作为第三方 Cookie 使用,在宽松模式下,cookie 可以被请求是
GET 请求,且会发生页面跳转的请求所使用。
5.有哪些可能引起前端安全的问题?
跨站脚本 (Cross-Site Scripting, XSS): ⼀种代码注⼊⽅式, 为了
与 CSS 区分所以被称作 XSS。早期常⻅于⽹络论坛, 起因是⽹站没
有对⽤户的输⼊进⾏严格的限制, 使得攻击者可以将脚本上传到帖
⼦让其他⼈浏览到有恶意脚本的⻚⾯, 其注⼊⽅式很简单包括但不
限于 JavaScript / CSS / Flash 等;
iframe 的滥⽤: iframe 中的内容是由第三⽅来提供的,默认情况下
他们不受控制,他们可以在 iframe 中运⾏JavaScirpt 脚本、Flash
插件、弹出对话框等等,这可能会破坏前端⽤户体验;
跨站点请求伪造(
Cross-Site Request Forgeries,CSRF): 指攻击
者通过设置好的陷阱,强制对已完成认证的⽤户进⾏⾮预期的个⼈信
息或设定信息等某些状态更新,属于被动攻击
恶意第三⽅库: ⽆论是后端服务器应⽤还是前端应⽤开发,绝⼤多数
时候都是在借助开发框架和各种类库进⾏快速开发,⼀旦第三⽅库被
植⼊恶意代码很容易引起安全问题。
6.网络劫持有哪几种,如何防范
⽹络劫持分为两种:
 (1)DNS 劫持: (输⼊京东被强制跳转到淘宝这就属于 dns 劫持)DNS 强制解析: 通过修改运营商的本地 DNS 记录,来引导⽤户流量到缓存服务器
 302 跳转的⽅式: 通过监控⽹络出⼝的流量,分析判断哪些内容是可以进⾏劫持处理的,再对劫持的内存发起 302 跳转的回复,引导⽤户获取内容
(2)HTTP 劫持: (访问⾕歌但是⼀直有贪玩蓝⽉的⼴告),由于 http明⽂传输,运营商会修改你的 http 响应内容(即加⼴告)
(3)DNS 劫持由于涉嫌违法,已经被监管起来,现在很少会有 DNS劫持,⽽http 劫持依然⾮常盛⾏,最有效的办法就是全站 HTTPS,将HTTP 加密,这使得运营商⽆法获取明⽂,就⽆法劫持你的响应内容。
7. 浏览器渲染进程的线程有哪些
浏览器的渲染进程的线程总共有五种
1. GUI 渲染线程
负责渲染浏览器页面,解析 HTML、CSS,构建 DOM 树、构建 CSSOM 树、
构建渲染树和绘制页面;当界面需要重绘或由于某种操作引发回流时,该线程就会执行。
注意:GUI 渲染线程和 JS 引擎线程是互斥的,当 JS 引擎执行时 GUI线程会被挂起,GUI 更新会被保存在一个队列中等到 JS 引擎空闲时立即被执行。
2. JS 引擎线程
JS 引擎线程也称为 JS 内核,负责处理 Javascript 脚本程序,解析Javascript 脚本,运行代码;JS 引擎线程一直等待着任务队列中任务的到来,然后加以处理,一个 Tab 页中无论什么时候都只有一个JS 引擎线程在运行 JS 程序;
注意:GUI 渲染线程与 JS 引擎线程的互斥关系,所以如果 JS 执行的时间过长,会造成页面的渲染不连贯,导致页面渲染加载阻塞
3.事件触发线程
时间触发线程属于浏览器而不是 JS 引擎,用来控制事件循环;当 JS引擎执行代码块如 setTimeOut 时(也可是来自浏览器内核的其他线程,如鼠标点击、AJAX 异步请求等),会将对应任务添加到事件触发线程中;当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待 JS 引擎的处理;
注意:由于 JS 的单线程关系,所以这些待处理队列中的事件都得排队等待 JS 引擎处理(当 JS 引擎空闲时才会去执行)
4.定时器触发进程
定时器触发进程即 setInterval 与 setTimeout 所在线程;浏览器定时计数器并不是由 JS 引擎计数的,因为 JS 引擎是单线程的,如果处于阻塞线程状态就会影响记计时的准确性;因此使用单独线程来计时并触发定时器,计时完毕后,添加到事件队列中,等待 JS 引擎空闲后执行,所以定时器中的任务在设定的时间点不一定能够准时执行,定时器只是在指定时间点将任务添加到事件队列中;
注意:W3C 在 HTML 标准中规定,定时器的定时时间不能小于 4ms,如果是小于 4ms,则默认为 4ms。
5.异步 http 请求线程
XMLHttpRequest 连接后通过浏览器新开一个线程请求;
检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将回调函数放入事件队列中,等待 JS 引擎空闲后执行;
8. 僵尸进程和孤儿进程是什么?
 孤儿进程:父进程退出了,而它的一个或多个进程还在运行,那这些子进程都会成为孤儿进程。孤儿进程将被 init 进程(进程号为 1)所收养,并由 init 进程对它们完成状态收集工作。
 僵尸进程:子进程比父进程先结束,而父进程又没有释放子进程占用的资源,那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵死进程。
 
9. 如何实现浏览器内多个标签页之间的通信?
实现多个标签页之间的通信,本质上都是通过中介者模式来实现的。
因为标签页之间没有办法直接通信,因此我们可以找一个中介者,让
标签页和中介者进行通信,然后让这个中介者来进行消息的转发。通
信方法如下:
使用 websocket 协议,因为 websocket 协议可以实现服务器推送,
所以服务器就可以用来当做这个中介者。标签页通过向服务器发送数
据,然后由服务器向其他标签页推送转发。
使用 ShareWorker 的方式,shareWorker 会在页面存在的生命周期
内创建一个唯一的线程,并且开启多个页面也只会使用同一个线程。
这个时候共享线程就可以充当中介者的角色。标签页间通过共享一个
线程,然后通过这个共享的线程来实现数据的交换。
使用localStorage 的方式,我们可以在一个标签页对 localStorage
的变化事件进行监听,然后当另一个标签页修改数据的时候,我们就
可以通过这个监听事件来获取到数据。这个时候 localStorage 对象
就是充当的中介者的角色。
使用 postMessage 方法,如果我们能够获得对应标签页的引用,就
可以使用 postMessage 方法,进行通信
10. 浏览器的缓存机制
浏览器缓存的全过程:
浏览器第一次加载资源,服务器返回 200,浏览器从服务器下载资源文件,并缓存资源文件与 response header,以供下次加载时对比使用;
下一次加载资源时,由于强制缓存优先级较高,先比较当前时间与上一次返回 200 时的时间差,如果没有超过 cache-control 设置的 max-age,则没有过期,并命中强缓存,直接从本地读取资源。如果浏览器不支持 HTTP1.1,则使用 expires 头判断是否过期;
如果资源已过期,则表明强制缓存没有被命中,则开始协商缓存,向
服务器发送带有 If-None-Match 和 If-Modified-Since 的请求;
服务器收到请求后,优先根据 Etag 的值判断被请求的文件有没有做修改,Etag 值一致则没有修改,命中协商缓存,返回 304;如果不一致则有改动,直接返回新的资源文件带上新的 Etag 值并返回 200;
如果服务器收到的请求没有 Etag 值,则将 If-Modified-Since 和被请求文件的最后修改时间做比对,一致则命中协商缓存,返回 304;不一致则返回新的 last-modified 和文件并返回 200;
很多网站的资源后面都加了版本号,这样做的目的是:每次升级了 JS或 CSS 文件后,为了防止浏览器进行缓存,强制改变版本号,客户端浏览器就会重新下载新的 JS 或 CSS 文件 ,以保证用户能够及时获得网站的最新更新。
11.协商缓存和强制缓存的区别
1. 强缓存
使用强制缓存策略时,如果缓存资源有效,则直接使用缓存资源,不必向服务器发送请求
强缓存策略可以通过两种方式来设置,分别是 http 头信息中的Expires 属性和 Cache-Control 属性
(1)服务器通过在响应头中添加 Expires 属性,来指定资源的过期时间。在过期时间以内,该资源可以被缓存使用,不必再向服务器发送请求。这个时间是一个绝对时间,它是服务器的时间,因此可能存在这样的问题,就是客户端的时间和服务器端的时间不一致,或者用户可以对客户端时间进行修改的情况,这样就可能会影响缓存命中的结果。
(2)Expires 是 http1.0 中的方式,因为它的一些缺点,在 HTTP1.1 中提出了一个新的头部属性就是 Cache-Control 属性,它提供了对资源的缓存的更精确的控制。它有很多不同的值,Cache-Control 可设置的字段:
public:设置了该字段值的资源表示可以被任何对象(包括:发送请求的客户端、代理服务器等等)缓存。这个字段值不常用,一般还是使用 max-age=来精确控制;
 private:设置了该字段值的资源只能被用户浏览器缓存,不允许任何代理服务器缓存。在实际开发当中,对于一些含有用户信息的HTML,通常都要设置这个字段值,避免代理服务器(CDN)缓存;
no-cache:设置了该字段需要先和服务端确认返回的资源是否发生了变化,如果资源未发生变化,则直接使用缓存好的资源;
no-store:设置了该字段表示禁止任何缓存,每次都会向服务端发起新的请求,拉取最新的资源;
max-age=:设置缓存的最大有效期,单位为秒;
s-maxage=:优先级高于 max-age=,仅适用于共享缓存(CDN),优先级高于 max-age 或者 Expires 头;
max-stale[=]:设置了该字段表明客户端愿意接收已经过期的资源,但是不能超过给定的时间限制。
一般来说只需要设置其中一种方式就可以实现强缓存策略,当两种方式一起使用时,Cache-Control 的优先级要高于 Expires。
no-cache 和 no-store 很容易混淆:
no-cache 是指先要和服务器确认是否有资源更新,在进行判断。也就是说没有强缓存,但是会有协商缓存;
no-store 是指不使用任何缓存,每次请求都直接从服务器获取资源。
(2)协商缓存
如果命中强制缓存,我们无需发起新的请求,直接使用缓存内容,如果没有命中强制缓存,如果设置了协商缓存,这个时候协商缓存就会发挥作用了。
上面已经说到了,命中协商缓存的条件有两个:
max-age=xxx 过期了
值为 no-store
使用协商缓存策略时,会先向服务器发送一个请求,如果资源没有发生修改,则返回一个 304 状态,让浏览器使用本地的缓存副本。如果资源发生了修改,则返回修改后的资源。
协商缓存也可以通过两种方式来设置,分别是 http 头信息中的Etag 和 Last-Modified 属性。
(1)服务器通过在响应头中添加 Last-Modified 属性来指出资源最
后一次修改的时间,当浏览器下一次发起请求时,会在请求头中添加
一个 If-Modified-Since 的属性,属性值为上一次资源返回时的Last-Modified 的值。当请求发送到服务器后服务器会通过这个属性来和资源的最后一次的修改时间来进行比较,以此来判断资源是否做了修改。如果资源没有修改,那么返回 304 状态,让客户端使用本地的缓存。如果资源已经被修改了,则返回修改后的资源。使用这种方法有一个缺点,就是 Last-Modified 标注的最后修改时间只能精确到秒级,如果某些文件在 1 秒钟以内,被修改多次的话,那么文件已将改变了但是 Last-Modified 却没有改变,这样会造成缓存命中的不准确。
(2)因为 Last-Modified 的这种可能发生的不准确性,http 中提供了另外一种方式,那就是 Etag 属性。服务器在返回资源的时候,在头信息中添加了 Etag 属性,这个属性是资源生成的唯一标识符,当资源发生改变的时候,这个值也会发生改变。在下一次资源请求时,浏览器会在请求头中添加一个 If-None-Match 属性,这个属性的值就是上次返回的资源的 Etag 的值。服务接收到请求后会根据这个值来和资源当前的 Etag 的值来进行比较,以此来判断资源是否发生改变,是否需要返回资源。通过这种方式,比 Last-Modified 的方式更加精确。
当 Last-Modified 和 Etag 属性同时出现的时候,Etag 的优先级更高。使用协商缓存的时候,服务器需要考虑负载平衡的问题,因此多个服务器上资源的 Last-Modified 应该保持一致,因为每个服务器上 Etag 的值都不一样,因此在考虑负载平衡时,最好不要设置 Etag属性。
总结:
强缓存策略和协商缓存策略在缓存命中时都会直接使用本地的缓存副本,区别只在于协商缓存会向服务器发送一次请求。它们缓存不命中时,都会向服务器发送请求来获取资源。在实际的缓存机制中,强缓存策略和协商缓存策略是一起合作使用的。浏览器首先会根据请求的信息判断,强缓存是否命中,如果命中则直接使用资源。如果不命中则根据头信息向服务器发起请求,使用协商缓存,如果协商缓存命中的话,则服务器不返回资源,浏览器直接使用本地资源的副本,如果协商缓存不命中,则浏览器返回最新的资源给浏览器。
12. 点击刷新按钮或者按 F5、按 Ctrl+F5 (强制刷新)、地址栏回车有什么区别?
点击刷新按钮或者按 F5:浏览器直接对本地的缓存文件过期,但是会带上 If-Modifed-Since,If-None-Match,这就意味着服务器会对文件检查新鲜度,返回结果可能是 304,也有可能是 200。
用户按 Ctrl+F5(强制刷新):浏览器不仅会对本地文件过期,而且不会带上 If-Modifed-Since,If-None-Match,相当于之前从来没有请求过,返回结果是 200。
地址栏回车: 浏览器发起请求,按照正常流程,本地检查是否过期,然后服务器检查新鲜度,最后返回内容
16. 前端储存的⽅式有哪些?
cookies: 在 HTML5 标准前本地储存的主要⽅式,优点是兼容性好,请求头⾃带 cookie⽅便,缺点是⼤⼩只有 4k,⾃动请求头加⼊cookie浪费流量,每个 domain 限制 20 个 cookie,使⽤起来麻烦,需要⾃⾏封装;
localStorage:HTML5 加⼊的以键值对(Key-Value)为标准的⽅式,优点是操作⽅便,永久性储存(除⾮⼿动删除),⼤⼩为 5M,兼容IE8+ ;
sessionStorage:与 localStorage 基本类似,区别是 sessionStorage当⻚⾯关闭后会被清理,⽽且与 cookie、localStorage 不同,他不能在所有同源窗⼝中共享,是会话级别的储存⽅式;
Web SQL:2010 年被 W3C 废弃的本地数据库数据存储⽅案,但是主流浏览器(⽕狐除外)都已经有了相关的实现,web sql 类似于 SQLite,是真正意义上的关系型数据库,⽤sql进⾏操作,当我们⽤JavaScript时要进⾏转换,较为繁琐;
IndexedDB:是被正式纳⼊HTML5 标准的数据库储存⽅案,它是 NoSQL数据库,⽤键值对进⾏储存,可以进⾏快速读取操作,⾮常适合 web场景,同时⽤JavaScript 进⾏操作会⾮常便。
18. 事件循环的理解
Event Loop 执行顺序如下所示:
首先执行同步代码,这属于宏任务
当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行
执行所有微任务
当执行完所有微任务后,如有必要会渲染页面
然后开始下一轮 Event Loop,执行宏任务中的异步代码
计算机网络部分
- 
GET和POST请求的区别
Post 和 Get 是 HTTP 请求的两种方法,其区别如下:
应用场景:GET 请求是一个幂等的请求,一般 Get 请求用于对服务器资源不会产生影响的场景,比如说请求一个网页的资源。而 Post不是一个幂等的请求,一般用于对服务器资源会产生影响的情景,比如注册用户这一类的操作。
是否缓存:因为两者应用场景不同,浏览器一般会对 Get 请求缓存,但很少对 Post 请求缓存。
发送的报文格式:Get 请求的报文中实体部分为空,Post 请求的报文中实体部分一般为向服务器发送的数据。
安全性:Get 请求可以将请求的参数放入 url 中向服务器发送,这样的做法相对于 Post 请求来说是不太安全的,因为请求的 url 会被保留在历史记录中。
请求长度:浏览器由于对 url 长度的限制,所以会影响 get 请求发送数据时的长度。这个限制是浏览器规定的,并不是 RFC 规定的。
参数类型:post 的参数传递支持更多的数据类型。
 - 
POST 和 PUT 请求的区别
PUT 请求是向服务器端发送数据,从而修改数据的内容,但是不会增加数据的种类等,也就是说无论进行多少次 PUT 操作,其结果并没有不同。(可以理解为时更新数据)
POST 请求是向服务器端发送数据,该请求会改变数据的种类等资源,它会创建新的内容。(可以理解为是创建数据)
 - 
常见的 HTTP 请求头和响应头
HTTP Request Header 常见的请求头: Accept:浏览器能够处理的内容类型 Accept-Charset:浏览器能够显示的字符集 Accept-Encoding:浏览器能够处理的压缩编码 Accept-Language:浏览器当前设置的语言 Connection:浏览器与服务器之间连接的类型 Cookie:当前页面设置的任何 Cookie Host:发出请求的页面所在的域 Referer:发出请求的页面的 URL User-Agent:浏览器的用户代理字符串 HTTP Responses Header 常见的响应头: Date:表示消息发送的时间,时间的描述格式由 rfc822 定义 server:服务器名称 Connection:浏览器与服务器之间连接的类型 Cache-Control:控制 HTTP 缓存 content-type:表示后面的文档属于什么 MIME 类型 常见的 Content-Type 属性值有以下四种: (1)application/x-www-form-urlencoded:浏览器的原生 form 表单 , 如 果 不 设 置enctype 属 性 , 那 么 最 终 就 会 以application/x-www-form-urlencoded 方式提交数据。该种方式提交的数据放在 body 里面,数据按照 key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL 转码。 (2)multipart/form-data:该种方式也是一个常见的 POST 提交方式,通常表单上传文件时使用该种方式。 (3)application/json:服务器消息主体是序列化后的 JSON 字符串。 (4)text/xml:该种方式主要用来提交 XML 格式的数据 - 
常见的 HTTP 请求方法
GET: 向服务器获取数据; POST:将实体提交到指定的资源,通常会造成服务器资源的修改; PUT:上传文件,更新数据; DELETE:删除服务器上的对象; HEAD:获取报文首部,与 GET 相比,不返回报文主体部分; OPTIONS:询问支持的请求方法,用来跨域请求; CONNECT:要求在与代理服务器通信时建立隧道,使用隧道进行 TCP通信; TRACE: 回显服务器收到的请求,主要⽤于测试或诊断 - 
HTTP 和 HTTPS 协议的区别
HTTP 和 HTTPS 协议的主要区别如下: HTTPS 协议需要 CA 证书,费用较高;而 HTTP 协议不需要; HTTP 协议是超文本传输协议,信息是明文传输的,HTTPS 则是具有安全性的 SSL 加密传输协议; 使用不同的连接方式,端口也不同,HTTP 协议端口是 80,HTTPS 协议端口是 443; HTTP 协议连接很简单,是无状态的;HTTPS 协议是有 SSL 和 HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 更加安全。 - 
什么是 HTTPS 协议?
超文本传输安全协议(Hypertext Transfer Protocol Secure,简称:HTTPS)是一种通过计算机网络进行安全通信的传输协议。HTTPS 经由 HTTP 进行通信,利用 SSL/TLS 来加密数据包。HTTPS 的主要目的是提供对网站服务器的身份认证,保护交换数据的隐私与完整性。 HTTP 协议采用明文传输信息,存在信息窃听、信息篡改和信息劫持的风险,而协议 TLS/SSL 具有身份验证、信息加密和完整性校验的功能,可以避免此类问题发生。安全层的主要职责就是对发起的 HTTP 请求的数据进行加密操作 和对接收到的 HTTP 的内容进行解密操作 - 
HTTPS 通信(握手)过程
HTTPS 的通信过程如下:
1.客户端向服务器发起请求,请求中包含使用的协议版本号、生成的 一个随机数、以及客户端支持的加密方法。 2.服务器端接收到请求后,确认双方使用的加密方法、并给出服务器的证书、以及一个服务器生成的随机数。 3.客户端确认服务器证书有效后,生成一个新的随机数,并使用数字证书中的公钥,加密这个随机数,然后发给服 务器。并且还会提供一个前面所有内容的 hash 的值,用来供服务器检验。 4.服务器使用自己的私钥,来解密客户端发送过来的随机数。并提供前面所有内容的 hash 值来供客户端检验。 5.客户端和服务器端根据约定的加密方法使用前面的三个随机数,生 成对话秘钥,以后的对话过程都使用这个秘钥来加密信息 - 
DNS 完整的查询过程
DNS 服务器解析域名的过程: 首先会在浏览器的缓存中查找对应的 IP 地址,如果查找到直接返回, 若找不到继续下一步 将请求发送给本地 DNS 服务器,在本地域名服务器缓存中查询,如果 查找到,就直接将查找结果返回,若找不到继续下一步 本地 DNS 服务器向根域名服务器发送请求,根域名服务器会返回一个 所查询域的顶级域名服务器地址 本地 DNS 服务器向顶级域名服务器发送请求,接受请求的服务器查询 自己的缓存,如果有记录,就返回查询结果,如果没有就返回相关的 下一级的权威域名服务器的地址 本地 DNS 服务器向权威域名服务器发送请求,域名服务器返回对应的 结果 本地 DNS 服务器将返回结果保存在缓存中,便于下次使用 本地 DNS 服务器将返回结果返回给浏览器 比如要查询 www.baidu.com 的 IP 地址,首先会在浏览器的缓存中 查找是否有该域名的缓存,如果不存在就将请求发送到本地的 DNS 服务器中,本地 DNS 服务器会判断是否存在该域名的缓存,如果不存 在,则向根域名服务器发送一个请求,根域名服务器返回负责 .com 的顶级域名服务器的 IP 地址的列表。然后本地 DNS 服务器再向其 中一个负责 .com 的顶级域名服务器发送一个请求,负责 .com 的顶 级域名服务器返回负责 .baidu 的权威域名服务器的 IP 地址列表。 然后本地 DNS 服务器再向其中一个权威域名服务器发送一个请求, 最后权威域名服务器返回一个对应的主机名的 IP 地址列表 
HTML
- 
对 HTML 语义化的理解
语义化是指根据内容的结构化(内容语义化),选择合适的标签(代码语义化)。通俗来讲就是用正确的标签做正确的事情。 语义化的优点如下: 对机器友好,带有语义的文字表现力丰富,更适合搜索引擎的爬虫爬取有效信息,有利于 SEO。除此之外,语义类还支持读屏软件,根据文章可以自动生成目录; 对开发者友好,使用语义类标签增强了可读性,结构更加清晰,开发者能清晰的看出网页的结构,便于团队的开发与维护。 常见的语义化标签 
- 
DOCTYPE(⽂档类型) 的作⽤
DOCTYPE 是 HTML5 中一种标准通用标记语言的文档类型声明,它的目的是告诉浏览器(解析器)应该以什么样(html 或 xhtml)的文档类型定义来解析文档,不同的渲染模式会影响浏览器对 CSS 代码甚⾄JavaScript 脚本的解析。它必须声明在 HTML⽂档的第⼀⾏。 浏览器渲染页面的两种模式(可通过 document.compatMode 获取,比如,语雀官网的文档类型是 CSS1Compat):163 CSS1Compat:标准模式( Strick mode),默认模式,浏览器使用 W3C 的标准解析渲染页面。在标准模式中,浏览器以其支持的最高标准呈 现页面。 BackCompat:怪异模式(混杂模式)(Quick mode),浏览器使用自己的 怪异模式解析渲染页面。在怪异模式中,页面以一种比较宽松的向后 兼容的方式显示 - 
script 标签中 defer 和 async 的区别
defer 和 async 属性都是去异步加载外部的 JS 脚本文件,它们都不会阻塞页面的解析,其区别如下: 执行顺序:多个带 async 属性的标签,不能保证加载的顺序;多个带 defer 属性的标签,按照加载顺序执行; 脚本是否并行执行:async 属性,表示后续文档的加载和执行与 js 脚本的加载和执行是并行进行的,即异步执行;defer 属性,加载后 续文档的过程和 js 脚本的加载(此时仅加载不执行)是并行进行的164 (异步),js 脚本需要等到文档所有元素解析完成之后才执行, DOMContentLoaded 事件触发执行之前 - 
行内元素有哪些?块级元素有哪些? 空(void)元素有那些?
行内元素有:a b span img input select strong; 块级元素有:div ul ol li dl dt dd h1 h2 h3 h4 h5 h6 p; 空元素,即没有内容的 HTML 元素。空元素是在开始标签中关闭的,也就是空元素没有闭合标签: 常见的有:<br>、<hr>、<img>、<input>、<link>、<meta>; 鲜见的有: <area>、<base>、<col>、<colgroup>、<command>、<embed>、<keygen>、<param>、<source>、<track>、<wbr>。 - 
浏览器是如何对 HTML5 的离线储存资源进行管理和加载?
在线的情况下,浏览器发现 html 头部有 manifest 属性,它会请求manifest 文件,如果是第一次访问页面 ,那么浏览器就会根据manifest 文件的内容下载相应的资源并且进行离线存储。如果已经访问过页面并且资源已经进行离线存储了,那么浏览器就会使用离线的资源加载页面,然后浏览器会对比新的 manifest 文件与旧的manifest 文件,如果文件没有发生改变,就不做任何操作,如果文件改变了,就会重新下载文件中的资源并进行离线存储。 离线的情况下,浏览器会直接使用离线存储的资源。