1.DNS解析
2.TCP连接
3.发送HTTP请求
4.服务器处理请求并返回需要的数据
5.浏览器解析渲染页面
解析HTML,生成DOM树,解析CSS,生成CSSOM树
将DOM树和CSSOM树结合,生成渲染树(Render Tree)
Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
Display:将像素发送给GPU,展示在页面上
6.连接结束
表示临时响应并需要请求者继续执行操作的状态代码
表示成功处理了请求的状态码。
例:200 – 服务器成功返回网页
表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向
例:
301 (永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应时,会自动将请求者转到新位置。
302 (临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
304 (未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。
例:404 – 请求的网页不存在
这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。
例:
500 (服务器内部错误) 服务器遇到错误,无法完成请求。
503 (服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。
服务器解析 HTTP1.1 的请求时,必须不断地读入字节,直到遇到分隔符 CRLF 为止。而解析 HTTP2 的请求就不用这么麻烦,因为 HTTP2 是基于帧的协议,每个帧都有表示帧长度的字段。
多路复用 HTTP1.1 如果要同时发起多个请求,就得建立多个 TCP 连接,因为一个 TCP 连接同时只能处理一个 HTTP1.1 的请求。 在 HTTP2 上,多个请求可以共用一个 TCP 连接,这称为多路复用。同一个请求和响应用一个流来表示,并有唯一的流 ID 来标识。 多个请求和响应在 TCP 连接中可以乱序发送,到达目的地后再通过流 ID 重新组建。
首部压缩
HTTP2 提供了首部压缩功能。多请求请求时,有很多消息头都是重复的。如果可以把相同的首部存储起来,仅发送它们之间不同的部分,就可以节省不少的流量,加快请求的时间。
HTTP/2 在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键-值对,对于相同的数据,不再通过每次请求和响应发送。如果服务器收到了请求,它会照样创建一张表。 当客户端发送下一个请求的时候,如果首部相同,它可以直接发送这样的首部块:服务器会查找先前建立的表格,并把这些数字还原成索引对应的完整首部。
HTTP2 可以对比较紧急的请求设置一个较高的优先级,服务器在收到这样的请求后,可以优先处理。
由于一个 TCP 连接流量带宽(根据客户端到服务器的网络带宽而定)是固定的,当有多个请求并发时,一个请求占的流量多,另一个请求占的流量就会少。流量控制可以对不同的流的流量进行精确控制。
HTTP2 新增的一个强大的新功能,就是服务器可以对一个客户端请求发送多个响应。换句话说,除了对最初请求的响应外,服务器还可以额外向客户端推送资源,而无需客户端明确地请求。
单例模式:就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。在JavaScript里,单例作为一个命名空间提供者,从全局命名空间里提供一个唯一的访问点来访问该对象。
观察者模式: 观察者的使用场合就是:当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式。
工厂模式解决了重复实例化的问题,缺点,创建不同对象其中属性和方法都会重复建立,消耗内存;还有函数识别问题等等。
措施:
1、验证 HTTP Referer 字段
2、添加token验证
利用的是用户对恶意网站的权限,攻击者会在用户浏览某个网站时,通过伪装成一个受信任的网站发送请求,CSRF 通过利用用户的身份信息,从而让用户无意识地执行恶意操作。这些操作通常是由第三方发起的,而用户并不知情。
措施:
1、输入过滤
2、输出转义
3、使用 HttpOnly Cookie
引擎优化。是一种方式:利用搜索引擎的规则提高网站在有关搜索引擎内的自然排名。
1、SSR服务器渲染
2、预渲染prerender-spa-plugin
3、的title、description、keywords
4、化的HTML代码,符合W3C规范
5、饰性图片必须加alt
6、外链
7、向各大搜索引擎提交收录自己的站点
8、少用iframe:iframe中的内容是不会被抓取到的
先创建XHR对象即XMLHttpRequest()
然后open准备发送,open中有三个参数一是提交方式get和post,二是接口地址,三是同步和异步
第三步是用send发送
第四步再发送的过程中通过onreadystatechange来监听接收的回调函数,可以通过判断readyState==4和status==200来判断是否成功返回,然后通过responseText接收成功返回的数据
push,pop,unshift,shift,splice,join,concat,forEach,filter,map,sort,some,every
第一种:利用ES6的set来实现 例如:[...new Set(arr)]
第二种:借用临时对象的方式
JS事件代理就是通过给父级元素(例如:ul)绑定事件,不给子级元素(例如:li)绑定事件,然后当点击子级元素时,通过事件冒泡机制在其绑定的父元素上触发事件处理函数,主要目的是为了提升性能,因为我不用给每个子级元素绑定事件,只给父级元素绑定一次就好了,在原生js里面是通过event对象的targe属性实现
var ul = document.querySelector("ul");
ul.onclick = function(e){//e指event,事件对象
var target = e.target || e.srcElement; //target获取触发事件的目标(li)
if(target.nodeName.toLowerCase() == 'li'){//目标(li)节点名转小写字母,不转的话是大写字母
alert(target.innerHTML)
}
}
jq方式实现相对而言简单 $(“ul”).on(“click”,“li”,function(){//事件逻辑}) 其中第二个参数指的是触发事件的具体目标,特别是给动态添加的元素绑定事件,这个特别起作用
构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写。另外就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用。
每当定义一个函数数据类型(普通函数、类)时候,都会天生自带一个prototype属性,这个属性指向函数的原型对象,并且这个属性是一个对象数据类型的值。
当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。如果没有则去原型的原型中寻找,直到找到Object对象的原型,Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined。
浅拷贝可以理解为改变一个对象属性值,另一个对象属性也会发生改变,即互相影响, 对象深拷贝即就是说改变一个对象属性,另一个对象属性值不会发生改变,可以通过多种方法来实现对象深拷贝,
浅拷贝方法:
Object.assign
Array.prototype.slice()
, Array.prototype.concat()
深拷贝方法:
function deepClone(obj) {
let objClone = Array.isArray(obj) ? [] : {};
if(obj && typeof obj === 'object') {
for(key in obj) {
// 判断对象是否包含特定的自身(非继承)属性。
if(obj.hasOwnProperty(key)) {
// 判断obj子元素是否为对象,如果是,递归复制
if(obj[key] && typeof obj[key] === 'object') {
objClone[key] = deepClone(obj[key]);
} else {
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let arr = [1,2,[3,4],5];
let arr1 = arr;
let arr2 = deepClone(arr);
arr[2][0] = 9;
console.log(arr); // [1,2,[9,4],5]
console.log(arr1); // [1,2,[9,4],5] 可以看到改变 arr 后 arr1 的值也改变了;
console.log(arr2); // [1,2,[3,4],5]
核心思想:引用数据类型是有基本数据类型构成的,并且,基本数据类型是不存在深浅拷贝这一说的,那么我们只需要将引用数据类型的每一层次的基本数据类型赋值,并遍历到最深处的基本数据并赋值就可以完成深拷贝。
JS原生绑定事件主要为三种:
一是html事件处理程序
例如:点我
二是DOM0级事件处理程序
例如:var btn=document.getElementById(‘id元素’)
btn.οnclick=function() {
//要处理的事件逻辑
}
三是DOM2级事件处理程序
例如: var btn=document.getElementById(‘id元素’)
//绑定事件
btn.addEventListener(‘click’,绑定的事件处理函数名,false)
//移除事件
btn.removeEventListener(‘click’,要移除的事件处理函数名,false)
== 用于比较两者是否相等,忽略数据类型。
=== 用于更严谨的比较,值和值的数据类型都需要同时比较。
定位方式:普通流 (normal flow)、浮动 (float)、绝对定位 (absolute positioning)
BFC 即 Block Formatting Contexts (块级格式化上下文):具有 BFC 特性的元素可以看作是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,并且 BFC 具有普通容器所没有的一些特性。
要元素满足下面任一条件即可触发 BFC 特性:
浮动元素:float 除 none 以外的值
绝对定位元素:position (absolute、fixed)
display 为 inline-block、table-cells、flex
overflow 除了 visible 以外的值 (hidden、auto、scroll)
BFC特性:
同一个 BFC 下外边距会发生折叠
BFC 可以包含浮动的元素(清除浮动)
BFC 可以阻止元素被浮动元素覆盖
清除浮动解决的方法有很多,主要目的是让父级元素有高度
方法一:给父级元素设置绝对定位:position:absolute
方法二:给父级元素设置overflow:hidden;
方法三:通过伪对象来实现
.clearfix:after { content: " "; display: block; clear: both; height: 0; }
盒模型其实就是浏览器把一个个标签都看一个形象中的盒子,那每个盒子(即标签)都会有内容(width,height),边框(border),以及内容和边框中间的缝隙(即内间距padding),还有盒子与盒子之间的外间距(即margin)
盒模型包括两种:IE盒模型和w3c标准盒模型
IE盒模型总宽度即就是width宽度=border+padding+内容宽度
标准盒模型总宽度=border+padding+width
那如何在IE盒模型宽度和标准盒模型总宽度之间切换呢,可以通过box-sizing:border-box或设置成content-box来切换
其中:box-sizing:border-box //IE盒模型
box-sizing:content-box //w3c盒模型、
rem和em都是相对单位,主要参考的标签不同:
rem是相对于根字号,即相对于标签的font-size实现的,浏览器默认字号是font-size:16px
em:是相对于父元素标签的字号,和百分比%类似,%也是相对于父级的,只不过是%相对于父级宽度的,而em相对于父级字号的
百分比是相对于父元素标签的宽度和高度
vw和vh分别相对于屏幕宽度和屏幕高度的,1vw相当于屏幕宽度的1%,100vw相当于满屏宽度100%,
定位:
通过给div设置绝对定位,并且left,right,top,bottom设置为0,margin:auto即可以水平垂直居中
通过给div设置绝对定位,left为50%,top为50%,再给div设置距左是自身的一半即:margin-left:自身宽度/2,margin-top:自身高度/2。
通过给div设置绝对定位,left为50%,top为50%,再给div设置跨左和跟上是自身的一半:transform:translate3d(-50%,-50%,0)
flex布局: display: flex; justify-content: center; align-items: center;
在HTML中,每个元素都可以理解成一个盒子,在浏览器解析过程中,会涉及到回流与重绘:
回流:布局引擎会根据各种样式计算每个盒子在页面上的大小与位置
重绘:当计算好盒模型的位置、大小及其他属性后,浏览器根据每个盒子特性进行绘制
防抖和节流都是为了解决短时间内大量触发某函数而导致的性能问题,比如触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象
防抖:在事件被触发n秒后再执行回调函数,如果在这n秒内又被触发,则重新计时。
节流:规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。
防抖和节流的区别:
-- 效果:
函数防抖是某一段时间内只执行一次;而函数节流是间隔时间执行,不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数。
-- 原理:
防抖是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,都会清除当前的 timer 然后重新设置超时调用,即重新计时。这样一来,只有最后一次操作能被触发。
节流是通过判断是否到达一定时间来触发函数,若没到规定时间则使用计时器延后,而下一次事件则会重新设定计时器。
闭包说的通俗一点就是打通了一条在函数外部访问函数内部作用域的通道。正常情况下函数外部是访问不到函数内部作用域变量的,
表象判断是不是闭包:函数嵌套函数,内部函数被return 内部函数调用外层函数的局部变量
优点:可以隔离作用域,不造成全局污染
缺点:由于闭包长期驻留内存,则长期这样会导致内存泄露
如何解决内存泄露:将暴露全外部的闭包变量置为null
适用场景:封装组件,for循环和定时器结合使用,for循环和dom事件结合.可以在性能优化的过程中,节流防抖函数的使用,导航栏获取下标的使用
基于ajax同源策略,因为安全的考虑,ajax不允许访问不同域名下的资源
只存在前后端访问,后端访问后端不存在跨域问题。
产生跨域的情况有:不同协议,不同域名,不同端口以及域名和ip地址的访问都会产生跨域。
解决:
一、是jsonp
jsonp实现原理:主要是利用动态创建script标签请求后端接口地址,然后传递callback参数,后端接收callback,后端经过数据处理,返回callback函数调用的形式,callback中的参数就是json
二、 是代理(前端代理和后端代理)
前端代理我在vue中主要是通过vue脚手架中的config中的index文件来配置的,其中有个proxyTable来配置跨域的
三、是CORS
CORS全称叫跨域资源共享,主要是后台工程师设置后端代码来达到前端跨域请求的
vuex是一个状态管理工具,主要解决大中型复杂项目的数据共享问题,主要包括state,actions,mutations,getters和modules 5个要素,
主要流程:组件通过dispatch到 actions,actions是异步操作,再actions中通过commit到mutations,mutations再通过逻辑操作改变state,从而同步到组件,更新其数据状态
getters相当于组件的计算属性对,组件中获取到的数据做提前处理的.再说到辅助函数的作用.
因为vuex中的state是存储在内存中的,一刷新就没了,例如登录状态,常见解决方案有:
第一种:利用H5的本地存储(localStorage,sessionStorage)
第二种:利用第三方封装好的插件,例如:vuex-persistedstate
第三种:使用vue-cookie插件来做存储
同步即sync,形象的说就是代码一行行执行,前面代码和请求没有执行完,后面的代码和请求就不会被执行
异步:即async,形象的说就是代码可以在当前程序没有执行完,也可以执行后面的代码
异步解决方案主要有:
回调函数
promise
generator
async和await
回调地狱:为了实现某些逻辑经常会写出层层嵌套的回调函数,如果嵌套过多,会极大影响代码可读性和逻辑,这种情况也被成为回调地狱
promise的状态改变一次以后就不会再改变,链式调用:then返回的是一个Promise对象,所以能进行链式调用。
async/await其实是Promise的语法糖, async 会将其后的函数(函数表达式或 Lambda)的返回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,并将其 resolve 的结果返回出来。
减少前端资源数量,减小前端资源大小,减少dom操作 代码层面:注意事件的销毁、路由懒加载、提取公共代码、css放前面、组件懒加载、减少代码体积大小、字体图标代替图片、使用事件委托、使用查找表、不覆盖原生方法、减低css选择器复杂度、使用flex、使用 transform 和 opacity实现动画
交互优化:减少请求数量、使用 HTTP2(头部压缩、链路复用、解析快、可设置优先级、流量控制、服务器推送)、减少重绘重排(innerHTML代替DOM操作、避免使用动态属性、脱离文档流操作再合并)
加载优化:按需引入、使用CDN、使用服务端渲染、善用缓存、避免卡顿
打包优化:使用gzip、Webpack 对图片进行压缩、webpack 按需加载代码、提取第三库代码、webpack物理打包以及配置项打包优化
其他优化:图片优化(压缩、懒加载、用webp格式,延迟加载、响应式图片)、使用工作线程Web Workers、少用全局变量
ES6新增特性常用的主要有:let/const,箭头函数,模板字符串,解构赋值,模块的导入(import)和导出(export default/export),Promise,还有一些数组字符串的新方法,其实有很多,我平时常用的就这些
call,apply,bind主要作用都是改变this指向的,但使用上略有区别,说一下区别:
call和apply的主要区别是在传递参数上不同,call后面传递的参数是以逗号的形式分开的,apply传递的参数是数组形式、Apply是以A开头的,所以应该是跟Array(数组)形式的参数]
bind返回的是一个函数形式,如果要执行,则后面要再加一个小括号 例如:bind(obj,参数1,参数2,)(),bind只能以逗号分隔形式,不能是数组形式
var:存在变量提升,后面声明的变量会覆盖前面的变量声明、作用域不可控。
let:只能声明一次,可以重复赋值,形成暂时性死区
cosnt:只能声明一次,不能重复赋值,形成暂时性死区
Vue是单项数据流,数据双向绑定
单向数据流主要是vue 组件间传递数据是单向的,即数据总是由父组件传递给子组件,子组件在其内部维护自己的数据,但它无权修改父组件传递给它的数据,当开发者尝试这样做的时候,vue 将会报错。
单页应用SPA是一种网络应用程序或网站的模型,它通过动态重写当前页面来与用户交互,这种方法避免了页面之间切换打断用户体验在单页应用中,所有必要的代码(HTML、JavaScript和CSS)都通过单个页面的加载而检索
多页应用MPA(MultiPage-page application),翻译过来就是多页应用在MPA中,每个页面都是一个主页面,都是独立的当我们在访问另一个页面的时候,都需要重新加载html、css、js文件
我们熟知的JS框架如react,vue,angular,ember都属于SPA
单页应用与多页应用的区别
单页面应用(SPA) | 多页面应用(MPA) | |
---|---|---|
组成 | 一个主页面和多个页面片段 | 多个主页面 |
刷新方式 | 局部刷新 | 整页刷新 |
url模式 | 哈希模式 | 历史模式 |
SEO搜索引擎优化 | 难实现,可使用SSR方式改善 | 容易实现 |
数据传递 | 容易 | 通过url、cookie、localStorage等传递 |
页面切换 | 速度快,用户体验良好 | 切换加载资源,速度慢,用户体验差 |
维护成本 | 相对容易 | 相对复杂 |
单页应用优缺点
优点:
具有桌面应用的即时性、网站的可移植性和可访问性
用户体验好、快,内容的改变不需要重新加载整个页面
良好的前后端分离,分工更明确
缺点:
不利于搜索引擎的抓取
首次渲染速度相对较慢
MVVM 由 Model,View,ViewModel 三部分构成,Model 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑;View 代表UI 组件,它负责将数据模型转化成UI 展现出来,ViewModel 是一个同步View 和 Model的对象。
在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
作用域问题,为了保证互不干扰,因为一个组件是可以共享的,但他们的data是私有的,所以每个组件都要return一个新的data对象,返回一个唯一的对象,不要和其他组件共用一个对象。而且data也是一个闭包的经典使用常见。
this指向直接调用者,而非间接调用者
普通函数中的this:
在Vue所有的生命周期钩子方法(如created,mounted, updated以及destroyed)里 使用this,this指向调用它的Vue实例,即(new Vue)。
箭头函数中的this
箭头函数没有自己的this, 它的this是继承而来; 默认指向在定义它时所处的对象(宿主对象),而不是执行时的对象, 定义它的时候,可能环境是window; 箭头函数可以方便地让我们在 setTimeout ,setInterval中方便的使用this
vue生命周期即为一个组件从出生到死亡的一个完整周期,主要包括以下4个阶段:创建,挂载,更新,销毁
创建前:beforeCreate, 创建后:created(有data,无$el)
挂载前:beforeMount, 挂载后:mounted(有data,有$el)
更新前:beforeUpdate, 更新后:updated
销毁前:beforeDestroy, 销毁后:destroyed
新增了使用内置组件 keep-alive 来缓存实例,而不是频繁创建和销毁(开销大)
actived 实例激活(该钩子在服务器端渲染期间不被调用。)
deactived 实例失效 (该钩子在服务器端渲染期间不被调用。
前端路由实现原理主要通过以下两种技术实现的
第一种:利用H5的history API实现
主要通过history.pushState 和 history.replaceState来实现,不同之处在于,pushState会增加一条新的历史记录,而replaceState则会替换当前的历史记录[发布项目时,需要配置下apache]
第二种:利用url的hash实现
我们经常在 url 中看到 #,这个 # 有两种情况,一个是我们所谓的锚点,路由里的 # 不叫锚点,我们称之为 hash,我们说的就是hash,主要利用监听哈希值的变化来触发事件 —— hashchange 事件来做页面局部更新
第三种:abstract模式
适用于所有JavaScript环境,例如服务器端使用Node.js。如果没有浏览器API,路由器将自动被强制进入此模式。
总结:hash 方案兼容性好,而H5的history主要针对高级浏览器。
路由守卫:
第一种:全局路由钩子
beforeEach(to,from,next) { }
第二种:路由独享的钩子
beforeEnter(to,from,next) {}
第三种:组件内的钩子
beforeRouteEnter(to,from,next) {}
beforeRouteUpdate(to,from,next) {}
beforeRouteLeave(to,from,next) {}
适用场景:动态设置页面标题,判断用户登录权限等
vue路由懒加载
vue路由懒加载主要解决打包后文件过大的问题,事件触发才加载对应组件中的js
replace和push的区别
this.$router.replace方法,不计入history记录,this.$router.push计入历史
利用ES5中的Object.defineProperty结合观察者模式实现的,然后利用里面的getter和setter来实现双向数据绑定的、发布订阅模式,数据劫持
首先要对数据进行劫持监听,设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令(如v-model,v-on)对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。
异步更新队列:Vue在观察到数据变化时并不是直接更新DOM,而是开启一个队列,并缓冲在同一个事件循环中发生的所以数据改变。在缓冲时会去除重复数据,从而避免不必要的计算和DOM操作。然后,在下一个事件循环tick中,Vue刷新队列并执行实际(已去重的)工作。
因为Vue的异步更新队列,$nextTick是用来知道什么时候DOM更新完成的
vue中的nextTick主要用于处理数据动态变化后,DOM还未及时更新的问题,用nextTick就可以获取数据更新后最新DOM的变化
适用场景:
第一种:有时需要根据数据动态的为页面某些dom元素添加事件,这就要求在dom元素渲染完毕时去设置,但是created与mounted函数执行时一般dom并没有渲染完毕,所以就会出现获取不到,添加不了事件的问题,这回就要用到nextTick处理
第二种:在使用某个第三方插件时 ,希望在vue生成的某些dom动态发生变化时重新应用该插件,也会用到该方法,这时候就需要在 $nextTick 的回调函数中执行重新应用插件的方法,例如:应用滚动插件better-scroll时
第三种:数据改变后获取焦点
vue最大特点我感觉就是“组件化“和”数据驱动“
组件化就是可以将页面和页面中可复用的元素都看做成组件,写页面的过程,就是写组件,然后页面是由这些组件“拼接“起来的组件树
数据驱动就是让我们只关注数据层,只要数据变化,页面(即视图层)会自动更新,至于如何操作dom,完全交由vue去完成,咱们只关注数据,数据变了,页面自动同步变化了,很方便
jquery主要是玩dom操作的“神器“,强大的选择器,封装了好多好用的dom操作方法和如何获取ajax方法 例如:$.ajax()非常好用
vue:主要用于数据驱动和组件化,很少操作dom,当然vue可能通过ref来选择一个dom或组件
vue与react:
相同点:数据驱动视图、组件化、都使用 Virtual DOM
不同点:核心思想不同、组件写法差异、diff算法不同、响应式原理不同
特性:引入Tree-shaking,多出setup生命周期、响应式API、响应式侦听、Composition API(组合API)、Fragment、Teleport、Suspense等
v-on 指令常用修饰符:
.stop - 调用 event.stopPropagation(),禁止事件冒泡。
.prevent - 调用 event.preventDefault(),阻止事件默认行为。
.capture - 添加事件侦听器时使用 capture 模式。
.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
.{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
.native - 监听组件根元素的原生事件。
.once - 只触发一次回调。
.left - (2.2.0) 只当点击鼠标左键时触发。
.right - (2.2.0) 只当点击鼠标右键时触发。
.middle - (2.2.0) 只当点击鼠标中键时触发。
.passive - (2.3.0) 以 { passive: true } 模式添加侦听器
v-bind 指令常用修饰符:
.prop - 被用于绑定 DOM 属性 (property)。(差别在哪里?)
.camel - (2.1.0+) 将 kebab-case 特性名转换为 camelCase. (从 2.1.0 开始支持)
.sync (2.3.0+) 语法糖,会扩展成一个更新父组件绑定值的 v-on 侦听器。
v-model 指令常用修饰符:
.lazy - 取代 input 监听 change 事件
.number - 输入字符串转为数字
.trim - 输入首尾空格过滤
v-if和v-show都可以显示和隐藏一个元素,但有本质区别
v-if是惰性的,只是值为false就不会加载对应元素,为true才动态加载对应元素
v-show:是无论为true和为false都会加载对应html代码,但为false时用display:none隐藏不在页面显示,但为true时页面上用display:block显示其效果
适用场景:切换频繁的场合用v-show,切换不频繁的场合用v-if
v-for的优先级比v-if更高,这意味着 v-if将分别重复运行于每个 v-for循环中。当你想为仅有的一些项渲染节点时,这种优先级的机制会十分有用,vue3.x则v-if优先级更高
提前使用js的方式表示出dom结构树来.存储在内存里面.同样的循环.只会最终合并执行一次,大大的提高了性能.(这个地方有点儿像js中的createElementFragment文档碎片)
而在对比的过程中.通过diff算法进行比较差异.这个比较在我理解而言就是同层比较.降低了时间复杂度空间复杂度一些什么玩意儿.最终把差异同步到真实dom上去.这就是我理解的虚拟dom
在传统的jq中,操作的都是真实的DOM,.而一个真实dom的渲染过程,要经过渲染引擎构建DOM树.构建样式表.组建成render(渲染)树,的过程,要经过不断的重绘回流才能够展示给用户.
那么在直接js操作dom的过程中,比方说一个循环10次插入dom元素,其实每一次都会经历上面的过程..经历大量的重绘回流.代价特别大.性能低下.所以出现了虚拟dom
diff 算法是一种通过同层的树节点进行比较的高效算法
其有两个特点:
比较只会在同层级进行, 不会跨层级比较
在diff比较的过程中,循环从两边向中间比较
diff 算法在很多场景下都有应用,在 vue 中,作用于虚拟 dom 渲染成真实 dom 的新旧 VNode 节点比较
原理:当数据发生改变时,set方法会调用Dep.notify通知所有订阅者Watcher,订阅者就会调用patch给真实的DOM打补丁,更新相应的视图
第一种:父传子:主要通过props来实现的
具体实现:父组件通过import引入子组件,并注册,在子组件标签上添加要传递的属性,子组件通过props接收,接收有两种形式一是通过数组形式[‘要接收的属性’ ],二是通过对象形式{ }来接收,对象形式可以设置要传递的数据类型和默认值,而数组只是简单的接收
第二种:子传父:主要通过$emit来实现
具体实现:子组件通过通过绑定事件触发函数,在其中设置this.$emit(‘要派发的自定义事件’,要传递的值),$emit中有两个参数一是要派发的自定义事件,第二个参数是要传递的值
然后父组件中,在这个子组件身上@派发的自定义事件,绑定事件触发的methods中的方法接受的默认值,就是传递过来的参数
第三种:兄弟之间传值有两种方法:
方法一:通过event bus实现
具体实现:创建一个空的vue并暴露出去,这个作为公共的bus,即当作两个组件的桥梁,在两个兄弟组件中分别引入刚才创建的bus,在组件A中通过bus.$emit(’自定义事件名’,要发送的值)发送数据,在组件B中通过bus.$on(‘自定义事件名‘,function(v) { //v即为要接收的值 })接收数据
方法二:通过vuex实现
具体实现:vuex是一个状态管理工具,主要解决大中型复杂项目的数据共享问题,主要包括state,actions,mutations,getters和modules 5个要素,主要流程:组件通过dispatch到 actions,actions是异步操作,再actions中通过commit到mutations,mutations再通过逻辑操作改变state,从而同步到组件,更新其数据状态
方法三:使用全局的变量传递
在windons或document上声明或绑定数据,取值
computed是多对一的关系,而watch则是一对多的关系;
methods中都是封装好的函数,无论是否有变化只要触发就会执行
computed:是vue独有的特性计算属性,可以对data中的依赖项再重新计算,得到一个新值,应用到视图中,和methods本质区别是computed是可缓存的,也就是说computed中的依赖项没有变化,则computed中的值就不会重新计算,而methods中的函数是没有缓存的。Watch是监听data和计算属性中的新旧变化。
css没有局部样式的概念,vue脚手架通过实现了,即在style标签上添加scoped,
scoped的实现原理:vue通过postcss给每个dom元素添加一个以data-开头的随机自定义属性、然后css根据属性选择器添加样式
第三方库的样式穿透:
less/sass穿透问题 >>> /deep/
声明全局样式,样式加后加 !important
将样式impoimport至App的上方
成对出现:provide和inject
是成对出现的
作用:用于父组件向子孙组件传递数据
使用方法:provide
在父组件中返回要传给下级的数据,inject
在需要使用这个数据的子辈组件或者孙辈等下级组件中注入数据。
使用场景:由于vue
有$parent
属性可以让子组件访问父组件。但孙组件想要访问祖先组件就比较困难。通过provide/inject
可以轻松实现跨级访问父组件的数据
provider/inject
:简单的来说就是在父组件中通过provider
来提供变量,然后在子组件中通过inject
来注入变量
需要注意的是这里不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据。