汇总了一些前端常见的面试题。
@[toc]javascript
用正确的标签作正确的事情。
html 语义化让页面的内容结构化,结构更清晰,便于对浏览器、搜索引擎解析;即便在没有样式 CSS 状况下也以一种文档格式显示,而且是容易阅读的;
搜索引擎的爬虫也依赖于 HTML 标记来肯定上下文和各个关键字的权重,利于 SEO;
使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解。
alt 是给搜索引擎识别,在图像没法显示时的替代文本;
title 是关于元素的注释信息,主要是给用户解读。
当鼠标放到文字或是图片上时有 title 文字显示。(由于 IE 不标准)在 IE 浏览器中 alt 起到了 title 的做用,变成文字提示。
在定义 img 对象时,将 alt 和 title 属性写全,能够保证在各类浏览器中都能正常使用。
优势:css
- 解决加载缓慢的第三方内容如图标和广告等的加载问题
- Security sandbox
- 并行加载脚本
缺点:html
- iframe会阻塞主页面的Onload事件
- 即时内容为空,加载也须要时间
- 没有语意
- href (Hypertext Reference)指定网络资源的位置,从而在当前元素或者当前文档和由当前属性定义的须要的锚点或资源之间定义一个连接或者关系。(目的不是为了引用资源,而是为了创建联系,让当前标签可以连接到目标地址。)
- src source(缩写),指向外部资源的位置,指向的内容将会应用到文档中当前标签所在位置。
href与src的区别前端
- 一、请求资源类型不一样:href 指向网络资源所在位置,创建和当前元素(锚点)或当前文档(连接)之间的联系。在请求 src 资源时会将其指向的资源下载并应用到文档中,好比 JavaScript 脚本,img 图片;
- 二、做用结果不一样:href 用于在当前文档和引用资源之间确立联系;src 用于替换当前内容;
- 三、浏览器解析方式不一样:当浏览器解析到src ,会暂停其余资源的下载和处理,直到将该资源加载、编译、执行完毕,图片和框架等也如此,相似于将所指向资源应用到当前内容。这也是为何建议把 js 脚本放在底部而不是头部的缘由。
有两种, IE 盒子模型、W3C 盒子模型;
盒模型: 内容(content)、填充(padding)、边界(margin)、 边框(border);
区 别: IE 的 content 部分把 border 和 padding 计算了进去;
!important > 行内样式(比重1000)> ID 选择器(比重100) > 类选择器(比重10) > 标签(比重1) > 通配符 > 继承 > 浏览器默认属性
单行文本: line-height = height
图片: vertical-align: middle;
absolute 定位: top: 50%;left: 50%;transform: translate(-50%, -50%);
flex: display:flex;margin:auto
link 是 XHTML 标签,除了加载CSS外,还能够定义 RSS 等其余事务;@import 属于 CSS 范畴,只能加载 CSS。
link 引用 CSS 时,在页面载入时同时加载;@import 须要页面网页彻底载入之后加载。
link 是 XHTML 标签,无兼容问题;@import 是在 CSS2.1 提出的,低版本的浏览器不支持。
link 支持使用 Javascript 控制 DOM 去改变样式;而@import不支持。
opacity 会继承父元素的 opacity 属性,而 RGBA 设置的元素的后代元素不会继承不透明属性。
display:none 隐藏对应的元素,在文档布局中再也不给它分配空间,它各边的元素会合拢,就当他历来不存在。
visibility:hidden 隐藏对应的元素,可是在文档布局中仍保留原来的空间。
relative:相对定位,相对于本身自己在正常文档流中的位置进行定位。
absolute:生成绝对定位,相对于最近一级定位不为static的父元素进行定位。
fixed: (老版本IE不支持)生成绝对定位,相对于浏览器窗口或者frame进行定位。
static:默认值,没有定位,元素出如今正常的文档流中。
sticky:生成粘性定位的元素,容器的位置根据正常文档流计算得出。
HTML5vue
新的语义标签html5
- article 独立的内容。
- aside 侧边栏。
- header 头部。
- nav 导航。
- section 文档中的节。
- footer 页脚。
- 画布(Canvas) API
- 地理(Geolocation) API
- 本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;
sessionStorage 的数据在浏览器关闭后自动删除- 新的技术webworker, websocket, Geolocation
- 拖拽释放(Drag and drop) API
- 音频、视频API(audio,video)
- 表单控件,calendar、date、time、email、url、searc
CSS3java
- 2d,3d变换
- Transition, animation
- 媒体查询
- 新的单位(rem, vw,vh 等)
- 圆角(border-radius),阴影(box-shadow),对文字加特效(text-shadow),线性渐变(gradient),旋转(transform)transform:rotate(9deg) scale(0.85,0.90) translate(0px,-30px) skew(-9deg,0deg);//旋转,缩放,定位,倾斜
- rgba
BFC 即 Block Formatting Contexts (块级格式化上下文),它属于普通流,即:元素按照其在 HTML 中的前后位置至上而下布局,在这个过程当中,行内元素水平排列,直到当行被占满而后换行,块级元素则会被渲染为完整的一个新行,除非另外指定,不然全部元素默认都是普通流定位,也能够说,普通流中元素的位置由该元素在 HTML 文档中的位置决定。
能够把 BFC 理解为一个封闭的大箱子,箱子内部的元素不管如何翻江倒海,都不会影响到外部。
只要元素知足下面任一条件便可触发 BFC 特性node
- body 根元素
- 浮动元素:float 除 none 之外的值
- 绝对定位元素:position (absolute、fixed)
- display 为 inline-block、table-cells、flex
- overflow 除了 visible 之外的值 (hidden、auto、scroll)
- 浏览器默认的margin和padding不一样。解决方案是加一个全局的*{margin:0;padding:0;}来统一。
- Chrome 中文界面下默认会将小于 12px 的文本强制按照 12px 显示,
可经过加入 CSS 属性 -webkit-text-size-adjust: none; 解决.
数据类型主要包括两部分:css3
- 基本数据类型: Undefined、Null、Boolean、Number 和 String
- 引用数据类型: Object (包括 Object 、Array 、Function)
- ECMAScript 2015 新增:Symbol(建立后独一无二且不可变的数据类型 )
- typeof 运算符
- instanceof 运算符
- Object.prototype.toString 方法
null 表示一个对象被定义了,值为“空值”;
undefined 表示不存在这个值。
(1)变量被声明了,但没有赋值时,就等于undefined。 (2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。 (3)对象没有赋值的属性,该属性的值为undefined。 (4)函数没有返回值时,默认返回undefined。
arr instanceof Array
arr.constructor == Array
Object.protype.toString.call(arr) == '[Object Array]'
==,当且仅当两个运算数相等时,它返回 true,即不检查数据类型
===,只有在无需类型转换运算数就相等的状况下,才返回 true,须要检查数据类型
它的功能是把对应的字符串解析成 JS 代码并运行;
应该避免使用 eval,不安全,很是耗性能(2次,一次解析成 js 语句,一次执行)。
不须要function关键字来建立函数
省略return关键字
改变this指向
var 存在变量提高。
let 只能在块级做用域内访问。
const 用来定义常量,必须初始化,不能修改(对象特殊)
一、建立一个空对象,而且 this 变量引用该对象,同时还继承了该函数的原型。
二、属性和方法被加入到 this 引用的对象中。
三、新建立的对象由 this 所引用,而且最后隐式的返回 this 。
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。
它是基于JavaScript的一个子集。数据格式简单, 易于读写, 占用带宽小
{'age':'12', 'name':'back'}
document.write 只能重绘整个页面
innerHTML 能够重绘页面的一部分
(1)建立XMLHttpRequest对象,也就是建立一个异步调用对象.
(2)建立一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
(3)设置响应HTTP请求状态变化的函数.
(4)发送HTTP请求.
(5)获取异步调用返回的数据.
(6)使用JavaScript和DOM实现局部刷新.
概念:同源策略是客户端脚本(尤为是Netscape Navigator2.0,其目的是防止某个文档或脚本从多个不一样源装载。
这里的同源策略指的是:协议,域名,端口相同,同源策略是一种安全协议。
指一段脚本只能读取来自同一来源的窗口和文档的属性。
- 闭包是指有权访问另外一个函数做用域中的变量的函数,建立闭包常见方式,就是在一个函数的内部建立另外一个函数
- 使用闭包主要为了设计私有的方法和变量,闭包的优势是能够避免变量的污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易形成内存泄露。在js中,函数即闭包,只有函数才会产生做用域的概念。
闭包有三个特性:git
- 函数嵌套函数
函数内部能够引用外部的参数和变量
- 参数和变量不会被垃圾回收机制回收
- 应用场景,设置私有变量的方法
- 不适用场景:返回闭包的函数是个很是大的函数
- 闭包的缺点就是常驻内存,会增大内存使用量,使用不当会形成内存泄漏
- 垃圾回收器会每隔一段时间找出那些再也不使用的内存,而后为其释放内存
- 通常使用标记清除方法(mark and sweep), 当变量进入环境标记为进入环境,离开环境标记为离开环境
垃圾回收器会在运行的时候给存储在内存中的全部变量加上标记,而后去掉环境中的变量以及被环境中变量所引用的变量(闭包),在这些完成以后仍存在标记的就是要删除的变量了
- 还有引用计数方法(reference counting), 在低版本IE中常常会出现内存泄露,不少时候就是由于其采用引用计数方式进行垃圾回收。引用计数的策略是跟踪记录每一个值被使用的次数,当声明了一个 变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1,若是该变量的值变成了另一个,则这个值得引用次数减1,当这个值的引用次数变为0的时 候,说明没有变量在使用,这个值无法被访问了,所以能够将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为0的值占用的空间。
- 在IE中虽然JavaScript对象经过标记清除的方式进行垃圾回收,但BOM与DOM对象倒是经过引用计数回收垃圾的, 也就是说只要涉及BOM及DOM就会出现循环引用问题。
- 每一个对象都会在其内部初始化一个属性,就是prototype(原型),当咱们访问一个对象的属性时,
若是这个对象内部不存在这个属性,那么他就会去prototype里找这个属性,这个prototype又会有本身的prototype,
因而就这样一直找下去,也就是咱们平时所说的原型链的概念。
- 关系:instance.constructor.prototype = instance.__proto__
- 特色:
JavaScript对象是经过引用来传递的,咱们建立的每一个新对象实体中并无一份属于本身的原型副本。当咱们修改原型时,与之相关的对象也会继承这一改变。
MVVM分为Model、View、ViewModel三者。
Model 表明数据模型,数据和业务逻辑都在Model层中定义;
View 表明UI视图,负责数据的展现;
ViewModel 负责监听 Model 中数据的改变而且控制视图的更新,处理用户交互操做;
Model 和 View 并没有直接关联,而是经过 ViewModel 来进行联系的,Model 和 ViewModel 之间有着双向数据绑定的联系。所以当 Model 中的数据改变时会触发 View 层的刷新,View 中因为用户交互操做而改变的数据也会在 Model 中同步。
这种模式实现了 Model 和 View 的数据自动同步,所以开发者只须要专一对数据的维护操做便可,而不须要本身操做 dom。
- v-if 是真正的条件渲染,会控制这个 DOM 节点的存在与否。由于它会确保在切换过程当中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:若是在初始渲染时条件为假,则什么也不作——直到条件第一次变为真时,才会开始渲染条件块。
- v-show 就简单得多——无论初始条件是什么,元素老是会被渲染,而且只是简单地基于 CSS 的 “display” 属性进行切换。
- 当咱们须要常常切换某个元素的显示/隐藏时,使用v-show会更加节省性能上的开销;当只须要一次显示或隐藏时,使用v-if更加合理。
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
- (1)Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地获得高效更新。
- (2)改变 store 中的状态的惟一途径就是显式地提交 (commit) mutation。这样使得咱们能够方便地跟踪每个状态的变化。
---
主要包括如下几个模块:
- State => 基本数据,定义了应用状态的数据结构,能够在这里设置默认的初始状态。
- Getter => 从基本数据派生的数据,容许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
- Mutation => 是惟一更改 store 中状态的方法,且必须是同步函数。
- Action => 像一个装饰器,包裹mutations,使之能够异步。用于提交 mutation,而不是直接变动状态,能够包含任意异步操做。
- Module => 模块化Vuex,容许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。
SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页> 面加载完成,SPA 不会由于用户的操做而进行页面的从新加载或跳转;取而代之的是利用路由机制实现 > HTML 内容的变换,UI 与用户的交互,避免页面的从新加载。
- 优势:
用户体验好、快,内容的改变不须要从新加载整个页面,避免了没必要要的跳转和重复渲染;
基于上面一点,SPA 相对对服务器压力小;
先后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
- 缺点:
初次加载耗时多:为实现单页 Web 应用功能及显示效果,须要在加载页面的时候将 JavaScript、CSS 统一> 加载,部分页面按需加载;
前进后退路由管理:因为单页应用在一个页面中显示全部的内容,因此不能使用浏览器的前进后退功能,所> 有的页面切换须要本身创建堆栈管理;
SEO 难度较大:因为全部的内容都在一个页面中动态替换显示,因此在 SEO 上其有着自然的弱势。
Class 能够经过对象语法和数组语法进行动态绑定:
<div v-bind:class="{ active: isActive, 'text-danger': hasError }"></div> data: { isActive: true, hasError: false }
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div> data: { activeClass: 'active', errorClass: 'text-danger' }
Style 也能够经过对象语法和数组语法进行动态绑定:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div> data: { activeColor: 'red', fontSize: 30 }
<div v-bind:style="[styleColor, styleSize]"></div> data: { styleColor: { color: 'red' }, styleSize:{ fontSize:'23px' } }
全部的 prop 都使得其父子 prop 之间造成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,可是反过来则不行。
这样会防止从子组件意外改变父级组件的状态,从而致使你的应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中全部的 prop 都将会刷新为最新的值。
这意味着你不该该在一个子组件内部改变 prop。若是你这样作了,Vue 会在浏览器的控制台中发出警告。
子组件想修改时,只能经过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。
- computed: 是计算属性,依赖其它属性值,而且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会从新计算 computed 的值;
- watch: 更多的是「观察」的做用,相似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操做;
运用场景:
- 当咱们须要进行数值计算,而且依赖于其它数据时,应该使用 computed,由于能够利用 computed 的缓存特性,避免每次获取值时,都要从新计算;
- 当咱们须要在数据变化时执行异步或开销较大的操做时,应该使用 watch,使用 watch 选项容许咱们执行异步操做 ( 访问一个 API ),限制咱们执行该操做的频率,并在咱们获得最终结果前,设置中间状态。这些都是计算属性没法作到的。
因为 JavaScript 的限制,Vue 不能检测到如下数组的变更:
- 当你利用索引直接设置一个数组项时,例如:
vm.items[indexOfItem] = newValue
- 当你修改数组的长度时,例如:
vm.items.length = newLength
// Vue.set Vue.set(vm.items, indexOfItem, newValue) // vm.$set,Vue.set的一个别名 vm.$set(vm.items, indexOfItem, newValue) // Array.prototype.splice vm.items.splice(indexOfItem, 1, newValue)
// Array.prototype.splice vm.items.splice(newLength)
Vue 实例有一个完整的生命周期,也就是从开始建立、初始化数据、编译模版、挂载 Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,咱们称这是 Vue 的生命周期。
生命周期 描述 beforeCreate 组件实例被建立之初,组件的属性生效以前 created 组件实例已经彻底建立,属性也绑定,但真实 dom 尚未生成,$el 还不可用 beforeMount 在挂载开始以前被调用:相关的 render 函数首次被调用 mounted el 被新建立的 vm.$el 替换,并挂载到实例上去以后调用该钩子 beforeUpdate 组件数据更新以前调用,发生在虚拟 DOM 打补丁以前 updated 组件数据更新以后 activited keep-alive 专属,组件被激活时调用 deadctivated keep-alive 专属,组件被销毁时调用 beforeDestory 组件销毁前调用 destoryed 组件销毁后调用
Vue 的父组件和子组件生命周期钩子函数执行顺序能够归类为如下 4 部分:
- 加载渲染过程 :
父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
- 子组件更新过程 :
父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
- 父组件更新过程 :
父 beforeUpdate -> 父 updated
- 销毁过程 :
父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
好比有父组件 Parent 和子组件 Child,若是父组件监听到子组件挂载 mounted 就作一些逻辑处理,能够经过如下写法实现:
// Parent.vue <Child @mounted="doSomething"/> // Child.vue mounted() { this.$emit("mounted"); }
以上须要手动经过 $emit 触发父组件的事件,更简单的方式能够在父组件引用子组件时经过 @hook 来监听便可,以下所示:
// Parent.vue <Child @hook:mounted="doSomething" ></Child> doSomething() { console.log('父组件监听到 mounted 钩子函数 ...'); }, // Child.vue mounted(){ console.log('子组件触发 mounted 钩子函数 ...'); }, // 以上输出顺序为: // 子组件触发 mounted 钩子函数 ... // 父组件监听到 mounted 钩子函数 ...
固然 @hook 方法不只仅是能够监听 mounted,其它的生命周期事件,例如:created,updated 等均可以监听。
keep-alive 是 Vue 内置的一个组件,可使被包含的组件保留状态,避免从新渲染 ,其有如下特性:
- 通常结合路由和动态组件一块儿使用,用于缓存组件;
- 提供 include 和 exclude 属性,二者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高;
- 对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated。
- 由于组件是用来复用的,且 JS 里对象是引用关系,若是组件中 data 是一个对象,那么这样做用域没有隔离,子组件中的 data 属性值会相互影响,
- 若是组件中 data 选项是一个函数,那么每一个实例能够维护一份被返回对象的独立的拷贝,组件实例之间的 data 属性值不会互相影响;而 new Vue 的实例,是不会被复用的,所以不存在引用对象的问题。
咱们在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上建立双向数据绑定,咱们知道 v-model 本质上不过是语法糖,v-model 在内部为不一样的输入元素使用不一样的属性并抛出不一样的事件:
- text 和 textarea 元素使用 value 属性和 input 事件;
- checkbox 和 radio 使用 checked 属性和 change 事件;
- select 字段将 value 做为 prop 并将 change 做为事件。
<input v-model='something'>
至关于
<input v-bind:value="something" v-on:input="something = $event.target.value">
若是在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,以下所示:
父组件: <ModelChild v-model="message"></ModelChild> 子组件: <div>{{value}}</div> props:{ value: String }, methods: { test1(){ this.$emit('input', '小红') }, },
Vue 组件间通讯是面试常考的知识点之一,这题有点相似于开放题,你回答出越多方法固然越加分,代表你对 Vue 掌握的越熟练。
Vue 组件间通讯只要指如下 3 类通讯:父子组件通讯、隔代组件通讯、兄弟组件通讯,下面咱们分别介绍每种通讯方式且会说明此种方法可适用于哪类组件间通讯。
(1)props / $emit
适用 父子组件通讯
- 这种方法是 Vue 组件的基础,相信大部分同窗耳闻能详,因此此处就不举例展开介绍。
(2)
ref
与$parent / $children
适用 父子组件通讯
ref
:若是在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;若是用在子组件上,引用就指向组件实例$parent / $children
:访问父 / 子实例(3)
EventBus ($emit / $on)
适用于 父子、隔代、兄弟组件通讯
- 这种方法经过一个空的 Vue 实例做为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通讯,包括父子、隔代、兄弟组件。
(4)
$attrs/$listeners
适用于 隔代组件通讯
$attrs
:包含了父做用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时,这里会包含全部父做用域的绑定 ( class 和 style 除外 ),而且能够经过v-bind="$attrs"
传入内部组件。一般配合 inheritAttrs 选项一块儿使用。$listeners
:包含了父做用域中的 (不含 .native 修饰器的) v-on 事件监听器。它能够经过v-on="$listeners"
传入内部组件(5)
provide / inject
适用于 隔代组件通讯
- 祖先组件中经过 provider 来提供变量,而后在子孙组件中经过 inject 来注入变量。
provide / inject API
主要解决了跨级组件间的通讯问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间创建了一种主动提供与依赖注入的关系。(6)
Vuex
适用于 父子、隔代、兄弟组件通讯
- Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地获得高效更新。
- 改变 store 中的状态的惟一途径就是显式地提交 (commit) mutation。这样使得咱们能够方便地跟踪每个状态的变化。
- Vue.js 是构建客户端应用程序的框架。默认状况下,能够在浏览器中输出 Vue 组件,进行生成 DOM 和操做 DOM。然而,也能够将同一个组件渲染为服务端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上彻底可交互的应用程序。
- 即:SSR大体的意思就是vue在客户端将标签渲染成的整个 html 片断的工做在服务端完成,服务端造成的html 片断直接返回给客户端这个过程就叫作服务端渲染。
服务端渲染 SSR 的优缺点以下:
(1)服务端渲染的优势:
- 更好的 SEO:由于 SPA 页面的内容是经过 Ajax 获取,而搜索引擎爬取工具并不会等待 Ajax 异步完成后再抓取页面内容,因此在 SPA 中是抓取不到页面经过 Ajax 获取到的内容;而 SSR 是直接由服务端返回已经渲染好的页面(数据已经包含在页面中),因此搜索引擎爬取工具能够抓取渲染好的页面;
- 更快的内容到达时间(首屏加载更快):SPA 会等待全部 Vue 编译后的 js 文件都下载完成后,才开始进行页面的渲染,文件下载等须要必定的时间等,因此首屏渲染须要必定的时间;SSR 直接由服务端渲染好页面直接返回显示,无需等待下载 js 文件及再去渲染等,因此 SSR 有更快的内容到达时间;
(2) 服务端渲染的缺点:
- 更多的开发条件限制:例如服务端渲染只支持 beforCreate 和 created 两个钩子函数,这会致使一些外部扩展库须要特殊处理,才能在服务端渲染应用程序中运行;而且与能够部署在任何静态文件服务器上的彻底静态单页面应用程序 SPA 不一样,服务端渲染应用程序,须要处于 Node.js server 运行环境;
- 更多的服务器负载:在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用CPU 资源 (CPU-intensive - CPU 密集),所以若是你预料在高流量环境 ( high traffic ) 下使用,请准备相应的服务器负载,并明智地采用缓存策略。
vue-router 有 3 种路由模式:hash、history、abstract,对应的源码以下所示:
switch (mode) { case 'history': this.history = new HTML5History(this, options.base) break case 'hash': this.history = new HashHistory(this, options.base, this.fallback) break case 'abstract': this.history = new AbstractHistory(this, options.base) break default: if (process.env.NODE_ENV !== 'production') { assert(false, `invalid mode: ${mode}`) } }
其中,3 种路由模式的说明以下:
- hash: 使用 URL hash 值来做路由。支持全部浏览器,包括不支持 HTML5 History Api 的浏览器;
- history : 依赖 HTML5 History API 和服务器配置。具体能够查看 HTML5 History 模式;
- abstract : 支持全部 JavaScript 运行环境,如 Node.js 服务器端。若是发现没有浏览器的 API,路由会自动强制进入这个模式.
(1)hash 模式的实现原理
早期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简单,location.hash 的值就是 URL 中 # 后面的内容。好比下面这个网站,它的 location.hash 的值为 '#search':https://www.word.com#search
hash 路由模式的实现主要是基于下面几个特性:
- URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送;
- hash 值的改变,都会在浏览器的访问历史中增长一个记录。所以咱们能经过浏览器的回退、前进按钮控制hash 的切换;
- 能够经过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 的 hash 值会发生改变;或者使用 JavaScript 来对 loaction.hash 进行赋值,改变 URL 的 hash 值;
- 咱们可使用 hashchange 事件来监听 hash 值的变化,从而对页面进行跳转(渲染)。
(2)history 模式的实现原理
HTML5 提供了 History API 来实现 URL 的变化。其中作最主要的 API 有如下两个:history.pushState() 和 history.repalceState()。这两个 API 能够在不进行刷新的状况下,操做浏览器的历史纪录。
惟一不一样的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,以下所示:window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);
history 路由模式的实现主要基于存在下面几个特性:
- pushState 和 repalceState 两个 API 来操做实现 URL 的变化 ;
- 咱们可使用 popstate 事件来监听 url 的变化,从而对页面进行跳转(渲染);
- history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时咱们须要手动触发页面跳转(渲染)。
Vue 数据双向绑定主要是指:数据变化更新视图,视图变化更新数据。
即:
- 输入框内容变化时,Data 中的数据同步变化。即 View => Data 的变化。
- Data 中的数据变化时,文本节点的内容同步变化。即 Data => View 的变化。
其中,View 变化更新 Data ,能够经过事件监听的方式来实现,因此 Vue 的数据双向绑定的工做主要是如何根据 Data 变化更新 View。
Vue 主要经过如下 4 个步骤来实现数据双向绑定的:
- 实现一个监听器 Observer:对数据对象进行遍历,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。
- 实现一个解析器 Compile:解析 Vue 模板指令,将模板中的变量都替换成数据,而后初始化渲染页面视图,并将每一个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变更,收到通知,调用更新函数进行数据更新。
- 实现一个订阅者 Watcher:Watcher 订阅者是 Observer 和 Compile 之间通讯的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。
- 实现一个订阅器 Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理。
若是被问到 Vue 怎么实现数据双向绑定,你们确定都会回答 经过 Object.defineProperty() 对数据进行劫持,可是 Object.defineProperty() 只能对属性进行数据劫持,不能对整个对象进行劫持。
同理没法对数组进行劫持,可是咱们在使用 Vue 框架中都知道,Vue 能检测到对象和数组(部分方法的操做)的变化,那它是怎么实现的呢?咱们查看相关代码以下:
/** * Observe a list of Array items. */ observeArray (items: Array<any>) { for (let i = 0, l = items.length; i < l; i++) { observe(items[i]) // observe 功能为监测数据的变化 } } /** * 对属性进行递归遍历 */ let childOb = !shallow && observe(val) // observe 功能为监测数据的变化
经过以上 Vue 源码部分查看,咱们就能知道 Vue 框架是经过遍历数组 和递归遍历对象,从而达到利用 Object.defineProperty() 也能对对象和数组(部分方法的操做)进行监听。
受现代 JavaScript 的限制 ,Vue 没法检测到对象属性的添加或删除。
因为 Vue 会在初始化实例时对属性执行 getter/setter 转化,因此属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。
可是 Vue 提供了Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)
来实现为对象添加响应式属性,那框架自己是如何实现的呢?
- 咱们查看对应的 Vue 源码:
vue/src/core/instance/index.js
export function set (target: Array<any> | Object, key: any, val: any): any { // target 为数组 if (Array.isArray(target) && isValidArrayIndex(key)) { // 修改数组的长度, 避免索引>数组长度致使splcie()执行有误 target.length = Math.max(target.length, key) // 利用数组的splice变异方法触发响应式 target.splice(key, 1, val) return val } // key 已经存在,直接修改属性值 if (key in target && !(key in Object.prototype)) { target[key] = val return val } const ob = (target: any).__ob__ // target 自己就不是响应式数据, 直接赋值 if (!ob) { target[key] = val return val } // 对属性进行响应式处理 defineReactive(ob.value, key, val) ob.dep.notify() return val }
咱们阅读以上源码可知,vm.$set 的实现原理是:
- 若是目标是数组,直接使用数组的 splice 方法触发相应式;
- 若是目标是对象,会先判读属性是否存在、对象是不是响应式,最终若是要对属性进行响应式处理,则是经过调用 defineReactive 方法进行响应式处理( defineReactive 方法就是 Vue 在初始化对象时,给对象属性采用 Object.defineProperty 动态添加 getter 和 setter 的功能所调用的方法)
优势:
- 保证性能下限: 框架的虚拟 DOM 须要适配任何上层 API 可能产生的操做,它的一些 DOM 操做的实现必须是普适的,因此它的性能并非最优的;可是比起粗暴的 DOM 操做性能要好不少,所以框架的虚拟 DOM 至少能够保证在你不须要手动优化的状况下,依然能够提供还不错的性能,即保证性能的下限;
- 无需手动操做 DOM: 咱们再也不须要手动去操做 DOM,只须要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮咱们以可预期的方式更新视图,极大提升咱们的开发效率;
- 跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 能够进行更方便地跨平台操做,例如服务器渲染、weex 开发等等。
缺点:
- 没法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 没法进行针对性的极致优化。
虚拟 DOM 的实现原理主要包括如下 3 部分:
- 用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
- diff 算法 — 比较两棵虚拟 DOM 树的差别;
- pach 算法 — 将两个虚拟 DOM 对象的差别应用到真正的 DOM 树。
key 是为 Vue 中 vnode 的惟一标记,经过这个 key,咱们的 diff 操做能够更准确、更快速。
Vue 的 diff 过程能够归纳为:oldCh 和 newCh 各有两个头尾的变量 oldStartIndex、oldEndIndex 和 newStartIndex、newEndIndex,它们会新节点和旧节点会进行两两对比,即一共有4种比较方式:newStartIndex 和oldStartIndex 、newEndIndex 和 oldEndIndex 、newStartIndex 和 oldEndIndex 、newEndIndex 和 oldStartIndex,若是以上 4 种比较都没匹配,若是设置了key,就会用 key 再进行比较,在比较的过程当中,遍历会往中间靠,一旦 StartIdx > EndIdx 代表 oldCh 和 newCh 至少有一个已经遍历完了,就会结束比较。
因此 Vue 中 key 的做用是:key 是为 Vue 中 vnode 的惟一标记,经过这个 key,咱们的 diff 操做能够更准确、更快速!
- 更准确:由于带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中能够避免就地复用的状况。因此会更加准确。
- 更快速:利用 key 的惟一性生成 map 对象来获取对应节点,比遍历方式更快,源码以下:
function createKeyToOldIdx (children, beginIdx, endIdx) { let i, key const map = {} for (i = beginIdx; i <= endIdx; ++i) { key = children[i].key if (isDef(key)) map[key] = i } return map }
(1)代码层面的优化
- v-if 和 v-show 区分使用场景
- computed 和 watch 区分使用场景
- v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
- 长列表性能优化
- 事件的销毁
- 图片资源懒加载
- 路由懒加载
- 第三方插件的按需引入
- 优化无限列表性能
- 服务端渲染 SSR or 预渲染
(2)Webpack 层面的优化
- Webpack 对图片进行压缩
- 减小 ES6 转为 ES5 的冗余代码
- 提取公共代码
- 模板预编译
- 提取组件的 CSS
- 优化 SourceMap
- 构建结果输出分析
- Vue 项目的编译优化
(3)基础的 Web 技术的优化
- 开启 gzip 压缩
- 浏览器缓存
- CDN 的使用
- 使用 Chrome Performance 查找性能瓶颈
Vue 3.0 的目标是让 Vue 核心变得更小、更快、更强大,所以 Vue 3.0 增长如下这些新特性:
(1)监测机制的改变
3.0 将带来基于代理 Proxy 的 observer 实现,提供全语言覆盖的反应性跟踪。这消除了 Vue 2 当中基于 Object.defineProperty 的实现所存在的不少限制:
- 只能监测属性,不能监测对象
- 检测属性的添加和删除;
- 检测数组索引和长度的变动;
- 支持 Map、Set、WeakMap 和 WeakSet。
新的 observer 还提供了如下特性:
- 用于建立 observable 的公开 API。这为中小规模场景提供了简单轻量级的跨组件状态管理解决方案。
- 默认采用惰性观察。在 2.x 中,无论反应式数据有多大,都会在启动时被观察到。若是你的数据集很大,这可能会在应用启动时带来明显的开销。在 3.x 中,只观察用于渲染应用程序最初可见部分的数据。
- 更精确的变动通知。在 2.x 中,经过 Vue.set 强制添加新属性将致使依赖于该对象的 watcher 收到变动通知。在 3.x 中,只有依赖于特定属性的 watcher 才会收到通知。
- 不可变的 observable:咱们能够建立值的“不可变”版本(即便是嵌套属性),除非系统在内部暂时将其“解禁”。这个机制可用于冻结 prop 传递或 Vuex 状态树之外的变化。
- 更好的调试功能:咱们可使用新的 renderTracked 和 renderTriggered 钩子精确地跟踪组件在何时以及为何从新渲染。
(2)模板
模板方面没有大的变动,只改了做用域插槽,2.x 的机制致使做用域插槽变了,父组件会从新渲染,而 3.0 把做用域插槽改为了函数的方式,这样只会影响子组件的从新渲染,提高了渲染的性能。
同时,对于 render 函数的方面,vue3.0 也会进行一系列更改来方便习惯直接使用 api 来生成 vdom 。(3)对象式的组件声明方式
vue2.x 中的组件是经过声明的方式传入一系列 option,和 TypeScript 的结合须要经过一些装饰器的方式来作,虽然能实现功能,可是比较麻烦。
3.0 修改了组件的声明方式,改为了类式的写法,这样使得和 TypeScript 的结合变得很容易。
此外,vue 的源码也改用了 TypeScript 来写。其实当代码的功能复杂以后,必须有一个静态类型系统来作一些辅助管理。
如今 vue3.0 也全面改用 TypeScript 来重写了,更是使得对外暴露的 api 更容易结合 TypeScript。静态类型系统对于复杂代码的维护确实颇有必要。(4)其它方面的更改
vue3.0 的改变是全面的,上面只涉及到主要的 3 个方面,还有一些其余的更改:
- 支持自定义渲染器,从而使得 weex 能够经过自定义渲染器的方式来扩展,而不是直接 fork 源码来改的方式。
- 支持 Fragment(多个根节点)和 Protal(在 dom 其余部分渲染组建内容)组件,针对一些特殊的场景作了处理。
- 基于 treeshaking 优化,提供了更多的内置功能。
主要分红两部分:渲染引擎(layout engineer或Rendering Engine)和JS引擎。
- 渲染引擎:负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入CSS等),以及计算网页的显示方式,而后会输出至显示器或打印机。浏览器的内核的不一样对于网页的语法解释会有不一样,因此渲染的效果也不相同。全部网页浏览器、电子邮件客户端以及其它须要编辑、显示网络内容的应用程序都须要内核。
- JS引擎则:解析和执行javascript来实现网页的动态效果。
- 最开始渲染引擎和JS引擎并无区分的很明确,后来JS引擎愈来愈独立,内核就倾向于只指渲染引擎。
常见内核
- Trident 内核:IE, MaxThon, TT, The World, 360, 搜狗浏览器等。[又称 MSHTML]
- Gecko 内核:Netscape6 及以上版本,FF, MozillaSuite / SeaMonkey 等
- Presto 内核:Opera7 及以上。 [Opera内核原为:Presto,现为:Blink;]
- Webkit 内核:Safari, Chrome等。 [ Chrome的:Blink(WebKit 的分支)]
1.压缩 css, js, 图片
2.减小 http 请求次数, 合并 css、js 、合并图片(雪碧图)
3.使用 CDN
4.减小 dom 元素数量
5.图片懒加载
6.静态资源另外用无 cookie 的域名
7.减小 dom 的访问(缓存 dom)
8.巧用事件委托
9.样式表置顶、脚本置低
大体能够分为以下7步:
- 输入网址;
- 发送到DNS服务器,并获取域名对应的web服务器对应的ip地址;
- 与web服务器创建TCP链接;
- 浏览器向web服务器发送http请求;
- web服务器响应请求,并返回指定url的数据(或错误信息,或重定向的新的url地址);
- 浏览器下载web服务器返回的数据及解析html源文件;
- 生成DOM树,解析css和js,渲染页面,直至显示完成;
- 一个程序至少有一个进程,一个进程至少有一个线程.
- 线程的划分尺度小于进程,使得多线程程序的并发性高。
- 另外,进程在执行过程当中拥有独立的内存单元,而多个线程共享内存,从而极大地提升了程序的运行效率。
- 线程在执行过程当中与进程仍是有区别的。每一个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。可是线程不可以独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
- 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分能够同时执行。但操做系统并无将多个线程看作多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
100 Continue 继续,通常在发送post请求时,已发送了http header以后服务端将返回此信息,表示确认,以后发送具体参数信息
200 OK 正常返回信息
201 Created 请求成功而且服务器建立了新的资源
202 Accepted 服务器已接受请求,但还没有处理
301 Moved Permanently 请求的网页已永久移动到新位置。
302 Found 临时性重定向。
303 See Other 临时性重定向,且老是使用 GET 请求新的 URI。
304 Not Modified 自从上次请求后,请求的网页未修改过。
400 Bad Request 服务器没法理解请求的格式,客户端不该当尝试再次使用相同的内容发起请求。
401 Unauthorized 请求未受权。
403 Forbidden 禁止访问。
404 Not Found 找不到如何与 URI 相匹配的资源。
500 Internal Server Error 最多见的服务器端错误。
503 Service Unavailable 服务器端暂时没法处理请求(多是过载或维护)。
当页面滚动的时间被触发 -> 执行加载图片操做 -> 判断图片是否在可视区域内 -> 在,则动态将data-src的值赋予该图片
- 尽可能使用css3动画,开启硬件加速
- 适当使用touch时间代替click时间
- 避免使用css3渐变阴影效果
- 能够用transform: translateZ(0) 来开启硬件加速
- 不滥用float。float在渲染时计算量比较大,尽可能减小使用
- 不滥用web字体。web字体须要下载,解析,重绘当前页面
- 合理使用requestAnimationFrame动画代替setTimeout
- css中的属性(css3 transitions、css3 3D transforms、opacity、webGL、video)会触发GUP渲染,耗电
- 三次握手:
为了准确无误地吧数据送达目标处,TCP协议采用了三次握手策略。用TCP协议把数据包送出去后,TCP不会对传送后的状况置之不理,他必定会向对方确认是否送达,握手过程当中使用TCP的标志:SYN和ACK
- 发送端首先发送一个带SYN的标志的数据包给对方
- 接收端收到后,回传一个带有SYN/ACK标志的数据包以示传达确认信息
- 最后,发送端再回传一个带ACK的标志的数据包,表明“握手”结束
- 如在握手过程当中某个阶段莫明中断,TCP协议会再次以相同的顺序发送相同的数据包
断开一个TCP链接须要“四次挥手”
- 第一次挥手:主动关闭方发送一个FIN,用来关注主动方到被动关闭方的数据传送,也便是主动关闭方告诫被动关闭方:我已经不会再给你发数据了(在FIN包以前发送的数据,若是没有收到对应的ACK确认报文,主动关闭方依然会重发这些数据)。可是,此时主动关闭方还能够接受数据
- 第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号收到序号 +1(与SYN相同,一个 FIN占用一个序号)
- 第三次挥手:被动关闭方发送一个 FIN。用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,个人数据也发送完了,不会给你发送数据了
- 第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手
- HTTP协议一般承载与 TCP协议之上,在HTTP和TCP之间添加一个安全协议层(SSL或TSL),这个时候,就成了咱们常说的HTTPS
- 默认HTTP的端口号为80,HTTPS的端口号为443
- 由于网络请求须要中间有不少的服务器路由的转发,中间的节点均可能篡改信息,而若是使用HTTPS,密钥在你和终点站才有,https之全部说比http安全,是由于他利用ssl/tls协议传输。包含证书,流量转发,负载均衡,页面适配,浏览器适配,refer传递等,保障了传输过程的安全性
注意:用心找本身作的项目中本身感受最拿出来手的(复杂度最高,用的技术最多的项目),描述的时候尽量往里面添加一些技术名词
布局咱们用html5+css3
咱们会用reset.css重置浏览器的默认样式
JS框架的话咱们选用的是jQuery(也多是Zepto)
咱们用版本控制工具git来协同开发
咱们会基于gulp搭建的前端自动化工程来开发(里面包含有咱们的项目结构、咱们须要引用的第三方库等一些信息,咱们还实现了sass编译、CSS3加前缀等的自动化)
咱们的项目中还用到了表单验证validate插件、图片懒加载Lazyload插件
前端是最贴近用户的程序员,比后端、数据库、产品经理、运营、安全都近。
一、实现界面交互 二、提高用户体验 三、有了Node.js,前端能够实现服务端的一些事情
前端是最贴近用户的程序员,前端的能力就是能让产品从 90分进化到 100 分,甚至更好,
参与项目,快速高质量完成实现效果图,精确到1px;
与团队成员,UI设计,产品经理的沟通;
作好的页面结构,页面重构和用户体验;
处理hack,兼容、写出优美的代码格式;
针对服务器的优化、拥抱最新前端技术。