浏览器常驻的线程:
1.js 引擎线程(解释和执行js 代码, JS内核-V8引擎 ,js引擎用来解释执行js代码 )
2.GUI 线程(绘制用户界面, 与js 主线程是互斥的);
3.http 网络请求线程(处理用户的get, post 等请求等, 返回结果后讲回调函数推入到任务队列);
4.定时器触发线程(setTimeout, setInterval 等待时间结束后把执行函数推入到任务队列中);
5.浏览器事件处理线程(将click, mouse 等交互事件发生后将这些事件放入到事件队列中)。
代码预编译
javaScript从编译到执行的过程,大致可分为四步:
- 函数声明,整体提升
- 变量声明,声明提升
1. 词法分析
2. 语法分析:检查代码是否存在错误,若有错误,引擎会抛出语法错误。同时会构建一颗抽象语法树(`AST`)。
3. 预编译
4. 解释执行
函数预编译四部曲
1. 预编译开始,会建立`AO(Activation Object)`对象
2. 找形参和变量声明,使其作为`AO`的属性名,值赋予`undefined`
3. 实参和形参相统一(将实参值赋值给形参)
4. 找函数声明,函数名作为`AO`属性名,值赋予函数体
全局的预编译执行三部曲
1. 创建Global Object(以下简写为GO对象);
2. 找形参和变量声明,将变量声明和形参作为GO的属性名,值为undefined;
3. 在全局里找函数声明,将函数名作为GO对象的属性名,值赋予函数体。
js执行时会生成一个临时的对象(AO)
1 AO(activeObject) 存储所有的方法和局部变量,全局变量不会存储在这里
2 寻找var变量声明
3 形参实参赋值
4 寻找函数声明
5 执行代码
事件轮询event loop
调用栈
web APIS 异步的
callback Queue 回调队列
event loop
js是单线程的
所有js代码都是在主线程执行的
同步任务进入主线程即会执行
异步任务则会进入浏览器的管理模块 (有DOM事件管理模块、ajax请求管理模块、定时器管理模块等)
管理模块一直监视异步任务是否满足条件.如果满足条件则会将对应的回调放入回调队列中(callback queue)
主线程的同步任务执行完后会通过event loop(事件轮询机制)询问callback queue:
如果回调队列中有可执行的回调函数,则将会回调钩到主线程上执行。
如果没有则loop
1.所有的同步任务都是在主线程上执行,形成一个执行栈
2.主线程之外,还存在一个任务队列,只要存在异步任务,就会在任务队列里放置一个事件
3.一旦执行栈里边同步任务代码执行完毕,主线程就会去读取“任务队列",看任务队列有哪些对应的异步任务,结束等待状态,进入执行栈,开始执行
主线程不断重复上面3个步骤
主线程执行完毕以后,从事件队列中去读取任务队列的过程,我们称之为事件循环(Event Loop)
JS执行顺序
1.script主代码块放入执行栈,从上到下顺序执行
2.遇到宏任务,加入宏任务队列(如果是setTimeout,则到时间后加入)
3.遇到微任务,加入微任务队列
4.主代码执行完毕后,清空微任务队列
5.执行UI渲染(并不是每次都渲染,和浏览器都优化机制有关)
6.从宏任务队列中拿出一个任务放入执行栈执行
宏任务微任务
宏任务 setTimout ajax dom事件 在dom渲染之后触发
微任务 promise async/awit 在dom渲染之前触发
微任务比宏任务先执行
同步任务
微任务
dom渲染
宏任务
diff算法(key值的作用)
1.虚拟dom中key的作用
1.简单来说:key之虚拟dom对象的标识,在更新显示时key起着极其重要的作用
2.详细的说:当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,
随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
a.旧虚拟DOM中找到了与新虚拟DOM相同的key:
(1).若虚拟DOM中内容没变,直接使用之前的真实DOM
(2).若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
b.旧虚拟DOM中未找到与新虚拟DOM相同的key
根据数据创建新的真实DOM,随后渲染到到页面
2.川index作为key可能会引发的问题:
1。若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新==>界面效果没问题,但效率低。
2.如果结构中还包含输入类的DOM:
会产生错误DOM更新==>界面有问题。
3,注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,
仅用于渲染列表用于展示,使用index作为key是没有问题的。
3,开发中如何选择key? :
1.最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。
2.如果确定只是简单的展示数据,用index也是可以的。
vue的diff
vue更新有两条线 1.初始化 patch(container,vnode) 2.更新 updata(vnode,newVnode)
function createVueDiff(vnode) {
let tag = vnode.tag //目标对象
let attrs = vnode.attrs || {} //属性
let child = vnode.child || [] //子节点
if (!tag) {
return null
}
let elem = document.childElementCount(tag)
let attName
//给dom添加属性
for (attName in attrs) {
if (attrs.hasOwnProperty) {
elem.setAttribute(attName, attrs[attName])
}
}
// 将子元素追加到目标上
child.forEach((item) => {
elem.appendChild(createVueDiff(item))
})
return elem
}
function updata(vnode, newnode) {
let child = vnode.children || [] //现有节点
let newChild = newnode.children || []//新节点
child.forEach((item, index) => {
let newChildrenVnode = newChild[index]
//第一层没有变化
if (item.tag == newChildrenVnode.tag) {
updata(item, newChildrenVnode)
} else {
replace(item, newChildrenVnode)
}
})
}
虚拟dom
vue2.0才有了虚拟dom
虚拟dom本质是js对象=>夸平台
虚拟dom在vue那个做了什么
a.vue的渲染过程(html,css,js)
b.将真实dom传化为虚拟dom(js对象) 更新时用来在做对比
第一次渲染的多经过了一步 虚拟dom render()和虚拟dom会增加vue初始渲染时间
虚拟dom是如何提升vue的渲染效率的
1.组件化 2.数据驱动
1.局部更新(节点数据)
2.将直接操作dom的地方拿到两个js对象之中去做对比
http缓存
Expires:new Date('2022-4-2 15:26:30').toUTCString()
'Cache-Control':'public max-age=5'
一般用'Cache-Control', Expires是为了向下兼容
no-store 不缓存
no-cache 强制进行协商缓存
public 浏览器和代理服务器都能缓存
private 只能浏览器缓存
max-age 设置过期时间
s-maxage 设置代理服务器过期时间
last-modified 协商缓存
if-modified-since 协商缓存
协商缓存
const { mtime } = fs.statSync('./img/3.png') 获取修改时间
const ifmodeified = req.headers['if-modified-since'] 获取请求头时间
if (ifmodeified === mtime.toUTCString()) { 对比时间
console.log('缓存生效');
res.statusCode = 304 没有修改返回304用缓存的数据
res.end()
return
}
console.log('/img/3.png');
res.setHeader('last-modified', mtime.toUTCString()) 设置响应头 修改时间
res.setHeader('Cache-Control', 'no-cache') 设置响应头
const data = fs.readFileSync('./img/3.png')
res.end(data)
强制缓存
res.writeHead(200, {
// http1.1缓存 5秒
'Cache-Control': 'max-age=5'
http1.0的方法
Expires: new Date('2022-4-2 15:26:30').toUTCString()
一般用'Cache-Control', Expires是为了向下兼容
})
const data = fs.readFileSync('./img/1.png')
res.end(data)
ETag的协商缓存
const ifnodematch = req.headers['if-none-match']
const etagCont=etag(data)
if(ifnodematch==etagCont){
console.log('缓存生效');
res.statusCode = 304
res.end()
return
}
res.setHeader('etag',etagCont)
res.setHeader('Cache-Control','no-cache')
res.end(data)
闭包
1. 函数的执行,导致函数被定义
2. 闭包和函数的定义有关
3. this指向和函数的执行方式有关
闭包是什么
方法里面返回一个方法,闭包会常驻内存=>慎用闭包 vue的data是一个函数 每一个组件都有自己的私有作用域确保各个组件数据不会相互干扰
1、函数里面包含的子函数,子函数访问父函数的局部变量
2、通过return将子函数暴露在全局作用域,子函数就形成闭包
3、通过闭包,父函数的局部变量没有被销毁,可通过闭包去调用,但同时,这个局部变量也不会被全局变量
污染
闭包存在的意义
1.延长变量的生命周期
2.创建私有环境(变量)
闭包的优点和缺点
优点: 避免全局变量的污染,同时,局部变量没有被销毁,驻留在内存中,还可以被访问
缺点: 使用不当,会造成内存泄露
闭包使用场景
防抖节流
防抖
防抖(将多此变为一次)在固定的时间内事件只允许发生一次
<input type="text" placeholder="电话" class="inp">
function get(name) {
return document.querySelector(name)
}
// 防抖
get('.inp').addEventListener('input', antiShake(demo,2000))
function antiShake(fn, wait) {
let timeout = null
return args => {
if (timeout) {
clearTimeout(timeout)
}
timeout=setTimeout(fn,wait)
}
}
function demo(){
console.log('发起请求');
}
节流
一定时间只调用一次,应用场景 提交表单 高频的监听事件
function get(){
let time=new Date().getTime()
let t=null
return function(){
let cur=new Date().getTime();
clearTimeout(t);
if (cur - begin >= delay) {
fn.apply(_self, args);
begin = cur;
} else {
t = setTimeout(function () {
fn.apply(_self, args);
}, delay);
}
}
}
原型原型链
原型 prototype =>函数特有的
原型链 __ptoto__ => [[prototype]]
常规的对象和数组都是没有原型的
从当前实例属性查找,找到了返回,否则顺着原型链一层一层向上找
知道null为止,如果null也没找到报错
js相关问题
js出现的undefined情况
1 已声明未赋值
let o
console.log(o);
2对象某个属性不存在
let obj={}
console.log(obj.a);
3函数调用少了参数
function fn(a,b){
console.log(a,b);
}
fn(1)
4 常规函数函数的默认返回值
构造函数默认返回当前构造的对象
function fn() {
}
console.log(fn());
js出现的null情况
1.手动释放内存
let obj={}
obj=null
2.作为函数的参数(此处参数不是对象)
3.原型链的顶端
构造函数是同步执行的
let promise=new Promise((resolve)=>{
console.log('promise');
setTimeout(()=>{
console.log(2);
})
resolve()
console.log(4);
})
promise.then(()=>{
console.log(3);
})
console.log(1);
promise 4 1 3 2
vue相关问题
$nextTick
dom更新之后延迟回调
使用场景 在A组件中调用B组件中的方法,但要确保B组件已经加载出来
$nextTick({
this.$refs.b.dn()
})
单页面应用于多页面优缺点
单页面应用(SPA) 只有一个主页面应用
组件=>页面片段
vue页面跳转=>刷新页面
场景=> pc
优点
体验好,快
改动内容,不用加载整个页面
前后端分离
缺点
不利于优化SEO(优化)
初次比较慢
页面复杂度高
多页面应用
整页刷新
优点
不利于优化SEO(优化)
初次比较慢
页面复杂度高
缺点
体验好,快
改动内容,不用加载整个页面
前后端分离
vue-router与location.href区别
location.href:简单方便,刷新页面 (跳外链)
vue-router 实现了按需加载,减少dom消耗(内部页面)
vue-router =>js原生history
vue侦听器
<script type="module">
import {Observer} from './index.js'
let obj=new Observer({
name:'小明',
age:18,
demo:{
a:'aaa',
b:88
}
})
console.log(obj.value.name);
obj.value.age=20
console.log(obj.value.age);
</script>
export class Observer {
constructor(value) {
this.value = value
if (Array.isArray(value)) {
// 数组
} else {
// 对象
this.walk(value)
}
}
walk(obj) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj,keys[i])
}
}
}
//循环让对象的每一个属性都变成可观测的
function defineReactive(obj, key, val) {
if (arguments.length === 2) {
val = obj[key] //对象的某个值
}
if (typeof val === 'object') {
// 递归
new Observer(val)
}
Object.defineProperty(obj, key, {
enumerable: true, //可枚举
configurable: true , //可改变
get() {
console.log(`${key}属性被读取了`);
return val
},
set(newval) {
console.log(`${key}属性被修改了,新值${newval}`);
val=newval
}
})
}
vue2响应式实现
视图一<span class="box-1"></span>
<br>
视图二<span class="box-2"></span>
let obj={}
dataHi({
data:obj,
tag:'view-1',
datakey:'one',
selector:'.box-1'
})
dataHi({
data:obj,
tag:'view-2',
datakey:'two',
selector:'.box-2'
})
obj.one='这是视图一'
obj.two='这是视图二'
// 订阅器模型
let Dep = {
clientList: {}, //容器
// 添加订阅
listen: function (key, fn) {
// if(!this.clientList[key]){
// this.clientList[key]=[]
// }
// this.clientList[key].push(fn)
(this.clientList[key] || (this.clientList[key] = [])).push(fn)
},
// 发布
tigger: function () {
let key = Array.prototype.shift.call(arguments)
fns = this.clientList[key]
if (!fns || fns.length == 0) {
return
}
console.log(fns);
for (let i = 0, fn; fn = fns[i++];) {
fn.apply(this, arguments)
}
}
}
// data 要劫持的数据
// tag 具体的目标元素
// datakey 目标元素的key值
// selector 选择器
let dataHi = function ({ data, tag, datakey, selector }) {
let value = ''
let el = document.querySelector(selector)
Object.defineProperty(data, datakey, {
get() {
console.log(`属性被读取了`);
return value
},
set(newval) {
console.log(`新值`, newval);
val = newval
Dep.tigger(tag, val)
}
})
//订阅
Dep.listen(tag, function (text) {
el.innerHTML = text
})
}
为什么选着react
React函数式编程理念使代码更优雅和合理
严谨的单向数据流设计,方便构建大型复杂稳定的单页面应用
丰富的技术生态圈,拥有世界范围内各大技术社区支持
容易维护
更容易实现前端自动化测试
为什么选着vue
视图层(html/css)和逻辑层(javascript)分开
vue两大特点:响应式编程、组件化
vue的优势:轻量级框架、简单易学、双向数据绑定、组件化、视图、数据和结构的分离、
虚拟DOM、运行速度快
mvc和mvvm区别是什么
mvc和mvvm区别是:
1、处理业务的模式不同,MVC里,View是可以直接访问Model,而MVVM是将页面与数据逻
辑分离的模式,它把数据绑定工作放到一个JS里去实现;
2、处理数据操作不同,MVVM通过数据来显示视图层而不是节点操作
this的理解
1、this是Javascript语言的一个关键字,它代表函数运行时自动生成的一个内部对象,
只能在函数内部使用。this指的是调用函数的那个对象。his的指向在函数定义的时候是确
定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那
个调用它的对象
清除浮动
方法一:额外标签法
给谁清除浮动,就在其后额外添加一个空白标签 ,给其设置clear:both。
优点:通俗易懂,书写方便。
缺点:添加许多无意义的标签,结构化比较差。
方法二:父元素添加overflow:hidden
通过触发BFC方式,实现清除浮动
优点:代码简洁
缺点:内容增多的时候容易造成不会自动换行导致内容被隐藏掉,无法显示要溢出的元素。
方法三:使用after伪元素清除浮动
优点:符合闭合浮动思想,结构语义化正确。
缺点:ie6-7不支持伪元素:after,使用zoom:1触发hasLayout。
方法五:为父元素设置高度
缺点: 需要手动添加高度,如何后面的高度发生变化,还要再次修改高度,给后期的维
护带来麻烦。
优点: 简单粗暴直接有效,清除了浮动带来的影响。
重绘和回流的区别
1、 重绘:元素样式的改变(但宽高、大小、位置等不变)
2、 回流:元素的大小或者位置发生改变(当页面布局和几何信息发生改变的时候),触
发了重新布局导致渲染树重新计算布局和渲染
如添加或删除可见的DOM元素;
元素的位置发生变化;
元素的尺寸发生变化、
内容发生变化(如文本变化或图片被另一个不同尺寸的图片所代替);
页面一开始渲染的时候(无法避免)
注意:回流一定会触发重绘,而重绘不一定会回流
响应式布局有几种办法
一:媒体查询
使用@media媒体查询可以针对不同的媒体类型定义不同的样式,特别是响应式页面,可以
针对不同屏幕的大小,编写多套样式,从而达到自适应的效果。
二:百分比%
比如当浏览器的宽度或者高度发生变化时,通过百分比单位,通过百分比单位可以使得浏
览器中的组件的宽和高随着浏览器的变化而变化,从而实现响应式的效果。
三:vw/vh
css3中引入了一个新的单位vw/vh,与视图窗口有关,vw表示相对于视图窗口的宽度,vh
表示相对于视图窗口高度。 任意层级元素,在使用vw单位的情况下,1vw都等于视图宽度
的百分之一。
四:rem
rem单位是相对于字体大小的html元素,也称为根元素。 默认情况下,html元素的font-
size为16px。所以此时1rem = 16px。
五:flex弹性布局
弹性布局只需要依赖于CSS样式的实现响应式布局的方式,也是最多用到的一种实现响应式
的方法。
package.json文件
package.json是一个项目描述文件, 里面记录了当前项目的信息。eg: 项目名称、gitHub地址、当前项目依赖哪些第三方模块等。 使用npm安装第三方模块,是模块的相关信息会自动添加到package.json文件中
web前端优化策略
(1) 减少HTTP请求数
(2) 从设计实现层面简化页面
(3) 合理设置HTTP缓存
(4) 资源合并与压缩
(5) css sprite
(6) 网页内联图片
(7) 图片懒加载
从url到页面显示发生了什么
1. 构建请求
2. 网络传输
3. 构建响应
4. 网络传输
5. 浏览器渲染页面
1.DNS 解析域名 (找到要去哪里)
2. TCP 三次握手 (找到服务器之后要交流下,征求下服务器意见)
3. 浏览器发出请求 (服务器同意之后,发送请求)
4. 服务器处理请求并返回页面信息 (服务器收到请求之后,把请求内容返回)
5. 浏览器解析渲染 (拿到返回内容,开始渲染页面)
原文链接:https://blog.csdn.net/Hey_guy_struggle/article/details/119953269
1.1 dns(域名解析)解析转换成IP 先去浏览器缓存查找->再去找系统缓存->再去找路由器缓存->运营商缓存->根域->顶级域名服务器->祖域服务器
1.2在应用层封装http 请求报文;请求行,头,体,响应行头,体
1.3在传输层建立tcp链接(添加tcp报头 然后添加ip报头)
1.4 数据链路层,物理层
5.1 解析html
解码:unicode码
分词:把字节流(unicode码)分成短语 Token
解析:构建节点
建树:构建dom树
解析css
....
解析css会构建 render 树和renderLaryTree
解析js
回流和重绘
DNS
URL
udp---tcp--http
tcp和udp的区别有:1、udp是无连接的,tcp是面向连接的;2、udp是不可靠传输,tcp是
可靠传输;3、udp是面向报文传输,tcp是面向字节流传输。
udp是无连接协议
tcp建立可靠连接的协议
HTTPS:超文本传输协议(Hypertext Transfer Protocol,HTTP)
是一个简单的请求-响应协议,它通常运行在TCP之上。
它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。
请求和响应消息的头以ASCII形式给出;而消息内容则具有一个类似MIME的格式。
这个简单模型是早期Web成功的有功之臣,因为它使开发和部署非常地直截了当。
TCP与UDP区别总结:
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要
建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,
且按序到达;UDP尽最大努力交付,即不保 证可靠交付
3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有
用,如IP电话,实时视频会议等)
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP首部开销20字节;UDP的首部开销小,只有8个字节
6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
————————————————
版权声明:本文为CSDN博主「码城」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附
上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Li_Ning_/article/details/52117463
blob对象
二进制大对象(binary large object)存放二进制的容器
文件的下载
<a href="" download=""></a> 跳转的地址同源会进行下载
URl.createObjectURL将bolb对象变为url地址h5新增
<a id="btn">下载</a>
<input type="file" name="" id="inp">
function get(name) {
return document.querySelector(name)
}
var str='123'
var blob=new Blob([str],{
type:'text/plain'
})
blob.text().then((res)=>{
console.log(res);123
})
var str=`<div>
<p>123</p>
</div>`
var blob=new Blob([str],{
type:'text/html'
})
get('#btn').onclick=function(e){
this.setAttribute('download','123.html')
this.href=window.URL.createObjectURL(blob)
}
get('#inp').onchange = function (e) {
let file = e.target.files[0]
var a=document.createElement('a')
a.setAttribute('download','mybaidu.html')
a.href=window.URL.createObjectURL(file)
a.click()
}
get('#inp').onchange = function (e) {
let file = e.target.files[0]
console.log(file);
var img=new Image()
var filder=new FileReader()
document.body.appendChild(img)
filder.onload=function(){
img.src=filder.result
}
filder.readAsDataURL(file)
}
定义一个变量发生了什么
1.当JS引擎解析和渲染js代码的时候,提供一个运行环境,让JS代码执行全局作用域(scope)
2.在栈空间开辟一块内存空间,把10存储进去
3.在当前的作用域中,声明一个变量a (var,let ,const都是声明变量)
4.让声明的变量与存储的10进行关联(把存储的10赋值给了变量a ,赋值操作才叫定义变量)
将a的值进行了复制
将a的值在内存中开辟一块新空间存放a的值,这个a和b是没有关系的
let a=10
let b=a