We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HTML
CSS
JS
Vue
小程序
浏览器和网络
webpack
后端
算法
计算机基础
设计模式
调试
mysql
其他
技术外
是指示 web 浏览器关于页面使用哪个 HTML 版本进行编写的指令。不加会用怪异模式解析,浏览器根据自己的方式解析,展示不同的样式。
相同点:都会异步去加载JS,JS下载时不干扰DOM解析 区别:
收到请求 -> 字节流解码 -> 输入流预处理(可能会有document.write写入) -> token化 -> DOM树
内容分区 文本内容 图片和多媒体 表单 表格
article aside div p a b i button input img
preload优先级更高,解析到这行代码就去加载,但不执行,真正用的时候再执行,页面关闭停止请求资源 prefetch未来可能用到,闲时加载资源,页面关闭依然请求
disabled作用于所有表单元素,禁止元素一切行为,表单提交时不会传递数据 readonly作用于input textarea输入元素,可以获取焦点,只是设置为只读,表单提交可以传递数据
标准盒模型:width = content
怪异盒模型:width = content + padding + border
width: 100px; padding: 10px; border: 1px; margin: 10px; 标准盒模型占位:100px + 20px + 2px + 20px = 142px 怪异盒模型占位:100px + 20px = 120px
1、绝对定位已知宽度、绝对定位+margin反向偏移
<div class="wrap"> <div class="example2"></div> </div>
.wrap { position: relative; width: 300px; height: 300px; background: blue; } .example2 { position: absolute; top: 50%; left: 50%; width: 100px; height: 100px; margin: -50px 0 0 -50px; background: red; }
2、绝对定位宽度未知、transform 上面的方法把margin换成transform: translate(-50%, -50%) 3、flex布局
.wrap { position: flex; width: 300px; height: 300px; background: blue; just-content: center; align-item: center } .example2 { width: 100px; height: 100px; background: red; }
4、绝对定位,margin: auto,top等值设为相同
.wrap { position: relative; width: 300px; height: 300px; background: blue; } .example2 { position: absolute; top: 0; left: 0; right: 0; bottom: 0; width: 100px; height: 100px; margin: auto; background-color: red; }
5、子元素相对定位,margin和transform来实现
.wrap { position: relative; width: 300px; height: 300px; background: blue; } .example2 { position: relative; top: 50%; width: 100px; height: 100px; margin: 0 auto; transform: translateY(-50%); background-color: red; }
1、text-align: center 2、定宽+margin
width: 200px; margin: 0 auto;
3、table+margin
display: table; margin: 0 auto;
4、子元素设置inline-block
.parent { text-align: center } .child { display: inline-block }
5、flex
display: flex; justify-content: center;
6、绝对定位+负margin 7、绝对定位+transform
1、float+margin。缺点主要内容不能最先加载
<div class="container"> <div class="left"></div> <div class="right"></div> <div class="main"></div> </div> .left { float: left; width: 100px; } .right { float: right; width: 100px; } .main { margin: 0px 100px; }
2、BFC(BFC区域不会和浮动元素重叠)。缺点:主要内容不能最先加载
.left { float: left; width: 100px; } .right: { float: right; width: 100px; } .main { overflow: hidden; }
3、圣杯。主体内容优先加载
<div class="container"> <div class="main"></div> <div class="left"></div> <div class="right"></div> </div> .container { margin: 0 100px; } .main { float: left; width: 100%; } .left { float: left; width: 100px; margin-left: -100%; position: relative; left: -100px; } .right { float: left; width: 100px; margin-left: -100px; position: relative; right: -100px; }
4、双飞翼。主体内容可以优先加载
<div class="content"> <div class="main"></div> </div> <div class="left"></div> <div class="right"></div> .content { float: left; width: 100%; } .main { margin: 0 100px; } .left { float: left; width: 100px; margin-left: -100%; } .right { float: right; width: 100px; margin-left: -100px; }
5、flex布局
<div class="container"> <div class="main"></div> <div class="left"></div> <div class="right"></div> </div> .container { display: flex; } .main { flex-grow: 1; } .left { order: -1; flex: 0 1 100px; } .right { flex: 0 1 100px; }
6、table布局
<div class="container"> <div class="left"></div> <div class="main"></div> <div class="right"></div> </div> .container { display: table; } .left, .main, .right { display: table-cell; } .left, .right { width: 100px; }
7、绝对定位
<div class="container"> <div class="main"></div> <div class="left"></div> <div class="right"></div> </div> .container { position: relative; } .main { margin: 0 100px; } .left { position: absolute; left: 0px; top: 0px; width: 100px; } .right { position: absolute; right: 0px; top: 0px; width: 100px; }
基本同上
flex:flex-grow flex-shrink flex-basis
1、浮动元素后加clear:both 2、加overflow:hidden触发BFC清除浮动 3、浮动元素容器after伪元素添加clear:both
1、grid布局 2、flex布局 3、inline-block
BFC:块级格式化上下文 触发:
特点: *
rem,相对单位,相对的只是HTML根元素font-size的值
假设设计稿是宽750px来做的,书写css方便计算考虑,根节点的font-size假定为100px,得出设备宽为7.5rem。设计稿中标注的任何px数值都可以换算成px/100的rem值。
就是说,每一个设备的宽度都定为7.5个rem,然后宽度非750px的设备里,就需要用JS对font-size做动态计算。
计算rem的单位的方式可通过以下方式: document.documentElement.style.fontSize = document.documentElement.clientWidth*(window.devicePixelRatio||1) / 7.5 + 'px'; 需要考虑到dpr,即一倍屏两倍屏的问题。
rem基于根元素字体大小展现,可以html font-size设置为62.5% em基于父元素 vw浏览器可视窗口宽度百分比 vh浏览器可视窗口高度百分比
BlockElementModifier其实是块(block)、元素(element)、修饰符(modifier)。 这三个部分使用__ 与--连接 优点:更具有语义化、方便多人协作开发、减少了class层级提高了性能(最好别超过3层)
如果从左往右会进行大量的共用样式重复匹配(遍历到最后才知道是不是匹配到),反过来减少了公用样式的匹配。从右往左匹配的全部是DOM节点的父元素,从左往右则是匹配的DOM节点的子元素,这样避免了HTML和CSS还没下载完需要等待的情况。
IFC:内联格式化上下文
float布局 image设置 align='right'或者其他
align-items属性定义项目在交叉轴上如何对齐。 justify-content属性定义了项目在主轴上的对齐方式。 flex-direction属性决定主轴的方向(即项目的排列方向)。
prefetch 闲时加载资源,不一定加载 preload 一定需要这些资源,一定加载
引入样式表位置盖过默认的 class优先级 vue scope+:deep深度选择器 react :global(xxxxx)不会被应用到CSS Module react是CSS Module
link rel="alternative stylesheet" 替换样式文件,默认是备选,所以不显示,通过disabled来切换 sass变量换肤 修改class名
rem 媒体查询
当前执行上下文的一个属性,根据不同的执行上下文,this代表不同的值。
创建它的上下文已经毁了,但是它依然存在
基于原型链继承
postMessage和onMessage
深拷贝:拷贝到具体的基本类型,彻底改变引用地址 浅拷贝:只是引用改变
for in 数组索引,对象的可枚举属性 for of 数组元素值,只用于可迭代对象 forEach 不能break或者return 空数据跳过回调函数
前者是调用对象的方法 后者是判断对象 this的指向不同
暂时性死区 块级作用域 const let 不会绑定到window上 变量提升 const 只是只读引用
主要在于大部分都是基于对象原型做的修改,所以可以判断类型,而正常toString只是当前方法的
require是运行时加载、值的拷贝 import是静态编译、值的引用
是根据定义时生成的作用域,创建作用域链时会有一个outer指向他的上一级,这个上一级是根据定义时生成的
__proto__指向原型,形成的原型链,访问对象属性的时候会沿着原型链往上查找
浮点运算精度问题导致的 1、转成整数然后运算完再回去 2、使用toFixed运算 3、转成字符串大数相加计算
Array.prototype.reduce = function(fn, defaultVal) { let acc, i; if (defaultVal === undefined) { acc = this[0] i = 1; } else { acc = defaultVal; i = 0; } for (; i < this.length; i++) { acc = fn(acc, this[i], i, this); } return acc; };
主线程 -> 所有微任务 -> 一个宏任务 -> 渲染(不一定每次)
优先级:主任务 > setImmediate > MessageChannel > setTimeout/setInterval
优先级:process.nextTick > Promise.then > MutationObserver
function debounce(fn, wait) { let timeout; return function() { const args = [...arguments]; clearTimeout(timeout); timeout = setTimeout(() => { fn.apply(this, args); }); } } function throttle(fn, wait) { let timeout; return function() { let args = [...arguments]; if (!timeout) { timeout = setTimeout(() => { fn.apply(this, args); clearTimeout(timeout); }, wait) } } }
应用场景:
class EventEmitter { constructor() { this.events = Object.create(null); } on(key, fn) { if (!this.events[key]) { this.events[key] = []; } this.events[key].push(fn); } off(key, fn) { if (!this.events[key]) { return; } if (!fn) { this.events[key] = null; } this.events[key] = this.events[key].filter(item => item != fn); } once(key, fn) { const func = (args) => { this.off(key, fn) fn.apply(this, args) } this.on(key, func) } emit(key, ...args) { if (!this.events[key]) { return; } this.events[key].forEach((item) => { item.apply(this, args); }) } }
主要通过队列来进行请求控制
function multiRequest(urls, maxNum) { const len = urls.length; let count = 0; while(count < maxNum) { next(); } function next() { count++ if (count > len) return; setTimeout(() => { if (count < len) { next() } }, 2000) } }
JS是词法作用域,在函数定义时确定的。
执行上下文包含了变量环境和词法环境。通过变量环境实现了函数作用域,通过词法环境实现了块级作用域。
var a = 10; function b() { console.log(a) } function c() { var a = 5; b(); } c() // 10
let、const:创建提升,虽然在内存中,但是不能访问是因为V8虚拟机做了限制。 var:创建和初始化被提升。 function:创建、初始化、赋值提升。
let 和 const在全局作用域中声明是不会挂载到window上的。由[[script]]包裹
var a = 1; function b() { (function (){ a = 2; })() } b() // a = 2
Function.prototype.bind = function(context) { var args = [...arguments].slice(1); var _this = this; var fBound = function() { var _args = [...arguments]; return _this.apply(this instanceof fBound ? this : context, [...args, ..._args]) } fBount.prototype = Object.create(this.prototype); return fBound } Function.prototype.apply = function(context) { const args = arguments[1]; context['_fn'] = this; const result = context._fn(...args); delete context['_fn']; return result; } Function.prototype.call = function(context) { const args = [...arguments].slice(1); context['_fn'] = this; const result = context._fn(...args); delete context['_fn']; return result; }
function curry(fn, arg) { const length = fn.length; const args = arg || []; return function() { const _args = [...arguments]; for(let i = 0; i < _args.length; i++) { args.push(_args[i]) } if (args.length === length) { return fn.apply(this, args) } else { return curry(fn, args) } } }
主要方法:
function flat(arr) { return arr.reduce((acc, item) => acc.concat(Array.isArray(item) ? flat(item) : item), []) }
function unique(arr) { const obj = {}; return array.filter(function(item, index, array){ console.log(typeof item + JSON.stringify(item)) return obj.hasOwnProperty(typeof item + JSON.stringify(item)) ? false : (obj[typeof item + JSON.stringify(item)] = true) }) }
不断查找__proto__是否等于当前的prototype
function instanceof(left, right) { left = left.__proto__ while(left) { if (left === right.prototype) { return true } left = left.__proto__ } return false; }
只有对象这个结构有继承。每个实例对象都有一个私有属性(proto)执向它的构造函数的原型对象(prototype),该原型对象也有自己的原型对象,层层向上直到原型对象为null。
谁调用指向谁。new时指向实例。
1、原型链继承
function Parent() {} function Child() {} Child.prototype = new Parent(); Child.prototype.constructor = Child;
缺点: 2、借用构造函数继承
function Parent() {} function Child(name) { Parent.call(this, name) }
缺点 父类型原型上的属性和方法不能继承。每次都调用构造函数创建新的方法和属性 3、组合继承
function Parent() {} function Child(name) { Parent.call(this, name) } Child.prototype = new Parent(); Child.prototype.constructor = Child;
缺点:调用两次构造函数,在prototype上添加了不必要的属性 4、寄生组合继承
function Parent() {} function Child(name) { Parent.call(this, name) } Child.prototype = Object.create(Parent.prototype, { constructor: { value: Child } })
优点:减少一次构造函数的调用,减少在原型上添加不必要的属性 5、class
1、创建新对象 2、将构造函数的作用域赋值给这个对象 3、执行构造函数 4、返回新对象
function new() { const args = [...arguments]; const _constructor = args.shift(); const obj = Object.create(_constructor.prototype) const result = _constructor.apply(obj, args) return typeof result === 'object' ? result : obj }
深拷贝: 1、JSON.stringify()。循环引用、函数不能拷贝 2、
function deepCopy(target, source) { for(let key in source) { if (Object.prototype.toString.call(source[key]) === '[object Object]' || Array.isArray(source[key])) { if (Object.prototype.toString.call(source[key]) === '[object Object]' && Object.prototype.toString.call(target[key]) !== '[object Object]') { target[key] = {} } if (Array.isArray(source[key]) && !Array.isArray(target[key])) { target[key] = [] } deepCopy(target[key], source[key]) } else if (source[key] !== undefined) { target[key] = source[key] } } }
浅拷贝: Object.assign ...扩展运算符
基本类型:undefined、null、string、number、boolean、symbol 引用类型:object 基本包装类型:number、string、boolean(通过new来创建)
1 === Number(1) typeof Number(1) === number
1.也是数字
所以1..toString才可以
() > . > [...]需计算的成员 > new ...(...)带参数 > 函数调用 > new ...()不带参数 > ! ~ + - ++前置 --前置 typeof(这一层从右到左)
虚拟列表,transform滑动页面。因为单独的层,走合成线程更快 外面一层监听滚动 里面分两层 一层是占位(计算每项高度*数量) 一层是窗口展示多少个DOM,然后用start,end记录进入视口的索引,start,end通过外层监听滚动去计算出来 https://segmentfault.com/a/1190000041812905 https://juejin.cn/post/6844903982742110216#heading-4
箭头函数:
利用script标签的src属性可以跨域,又由于src属性的限制,只能是get请求 客户端将参数拼到url上、并传一个函数名callback。后端将结果callback(JSON.stringify(data))返回
function jsonp(src, callbackName, callback) { const element = document.querySelector('script'); const container = document.querySelector('body'); element.setAttribute("type","text/javascript"); element.src = src; document.body.appendChild(element); window[callbackName] = function(res) { callback && callback(res); container.removeChild(element) delete window[callbackName]; } }
完整实现
观察者模式,then中收集回调,然后状态改变后逐一触发
new Promise((resolve, reject) => { fetch('').then((res) => { resolve(res) }).catch((e) => { reject(e) }) }).then((res) => { return fetch('').then(res => res) }).then((res) => { return fetch('').then(res => res) }).catch((e) => { console.log(e) })
因为异步的操作主要还是操作DOM。设计成单线程让语言更简单。
好处:增加代码可读性和可维护性
map和object的区别:
简单实现:(底层是邻接链表实现)
class Map { constructor() { this.init(); this.len = 8; this.bucket = []; } init() { for(let i = 0; i < this.len; i++) { this.bucket[i] = { next: null } } } makeHash(key) { let newHash = 0; let keyStr = ''; if (typeof key !== 'string') { keyStr = (key).toString(); // undefined、null没有toString() } for(let i = 0; i < key.length; i++) { newHash += key[i].charCodeAt(); } return newHash } getList(key) { let hash = this.makeHash(key); return this.bucket[hash % this.len]; } set(key, val) { let list = this.getList(); let nodeNext = list; while(nodeNext.next) { if (nodeNext.key === key) { nodeNext.val = val; return; } else { nodeNext = nodeNext.next; } } nodeNext.next = { key, val, next: null } return nodeNext.next } get(key) { let list = this.getList(); let nodeNext = list; while(nodeNext.next) { if (nodeNext.key === key) { return nodeNext.val; } else { nodeNext = nodeNext.next; } } return; } has(key) { let list = this.getList(); let nodeNext = list; while(nodeNext.next) { if (nodeNext.key === key) { return true } else { nodeNext = nodeNext.next; } } return false; } delete(key) { let list = this.getList(); let nodeNext = list; hile(nodeNext.next) { if (nodeNext.next.key === key) { nodeNext.next = nodeNext.next.next return true } else { nodeNext = nodeNext.next; } } return false; } clear() { this.init(); } }
TimSort排序 小于10个则是插入排序 大于则是快排
继承自对象,分快慢数组 快数组底层是数组 慢数组底层是哈希表
快数组转换成慢数组 新容量 >= 3 * 扩容后的容量 * 2 则转变成慢数组或者加入的index > 1024 前面有1025个空值 慢数组转换成快数组 V8的启发式算法那检查空间占用量(慢数组不再比快数组节省50%空间时),转换为快数组
快数组是空间换时间 慢数组是时间换空间
属性不超过128个时,用Search Cache 属性为较连续数字,则是数组。 其他情况用哈希表
JS类图
https://zhuanlan.zhihu.com/p/26169639
TS是JS的超集,集合了一些JS语法糖 执行环境层面:浏览器和Node可以直接运行JS,TS不可以 时序层面:TS执行前会转义成JS,然后解释执行
vue门槛低,文档清晰。
通过数据劫持和发布订阅者模式实现。 通过Object.defineProperty给数据添加getter(依赖收集)和setter(派发更新)实现数据劫持。 通过get进行依赖收集
收集过程:当实例化渲染一个watcher的时候,首先进入watcher的构造函数逻辑,执行get方法,get函数中把Dep.target赋值为当前渲染watcher并压入栈。执行渲染vm._render方法,生成渲染VNode时,对vm上的数据进行访问,此时触发数据对象的getter方法(执行Dep.target.addDep(this)方法)将watcher订阅到这个数据持有的dep的subs中,然后递归遍历添加所有的子项getter
通过set观察者更新视图
派发过程:当我们组件中对响应的数据做了修改,就会触发setter的逻辑,最后调用dep.notify方法(遍历subs里的依赖),调用每一个wathcer的update方法,update方法中有个queueWatcher的方法引入了队列的概念(是派发更新时优化的一个点,并不会每次数据变化都触发watcher回调,而是把这些watcher添加到一个队列中,然后在nextTick后执行watcher的run函数)。
队列排序(queueWatcher中有个根据id的sort排序)的保证: 1、组件更新由父到子,父组件创建早于子组件,所以watcher创建也是 2、用户自定义的watcher早于渲染watcher执行,用户自定义的watcher早于渲染watcher创建 3、父组件watcher期间被销毁,对应的watcher都会被跳过,所以父组件watcher先执行
run函数解析:通过this.get()得到当前的值,然后做判断,满足新旧值不等,新值是对象类型,deep模式任何一个条件时,执行watcher回调(回调执行时会把新旧值当参数传入,也是自定义watcher时新旧值的来源)。对于渲染watcher执行this.get()时,会执行getter方法,触发组件重新执行patch重新渲染。
1、无法监听数组变化 2、只能劫持对象的属性,如果属性还是对象,则需要深度遍历去劫持
vue3为啥用proxy 1、Proxy可以监听数组变化 2、proxy监听对象而非属性,在访问目标对象前架设了一层拦截,所以可以对外界的访问进行过滤和该写。可以劫持整个对象并返回新的对象
proxy缺点:兼容性问题,无法用polyfill实现
不能检测数组和对象的变化 对象:无法检测属性的添加和移除,可以通过Vue.set或vm.$set来添加响应式属性 数组:无法检测通过下标直接设置数组项、修改数组长度时。通过vm.$set或者是splice数组方法
重写数组的方法
抽象语法树,本质就是一个JS对象 先将模板语法变成AST,然后解析成HTML结构
由于DOM元素属性很多,性能有问题。所以虚拟DOM就是用JS对象去描述一个DOM节点,核心就是标签名、数据、子节点、键值等
vue识别节点的一个机制,虚拟DOM的VNode节点的唯一id,是diff的一种优化策略(判断是否是相同节点时需要,不添加则增多了比较次数),可根据key更准确、更快地找到VNode节点。 比较相同节点时先用的key值。 暴力破解时也是使用的key值匹配。
不用下标作为index原因,数组变化会导致下标变化,不稳定性导致性能浪费,无法关联上次的数据
同层树节点的比较 特点: 只在同层级比较,不会跨层级比较 在diff比较的过程中,循环从两边向中间比较 策略:深度优先、同层比较
过程: 新节点存在,旧节点不存在——新增 新节点不存在,旧节点存在——删除 新旧节点都存在——判断是否是相同节点 通过patchVNode进行对比: 判断新旧虚拟DOM是否全等,是则结束 更新节点的属性 判断新节点是不是文本节点,是则更新文本,否则继续比较, 判断新旧节点是否有孩子节点: 两个都有孩子节点——updateChildren比较孩子节点 新的有,旧的没有——新增 新的没有,旧的有——删除 新旧都没有,判断文本节点是否有内容,有则清空 新旧节点都不存在——初始化页面,createElm
updateChildren时 新旧两个数组,声明四个指针,分别指向两个数组的首尾 两个开始指针比较,相同则往后移一个 不相同则两个结尾指针比较,相同前移 不相同则旧开和新尾对比,相同则各自移动 不相同则新开和旧尾对比,相同则各自移动 如果都不行,则遍历旧节点寻找(将数组转成对象,然后通过键值对key查找),找到则设为undefined。后续遇到undefined则跳过,当新旧各自出现指针重叠的时候,则是处理另一方多余节点的时候
(new时,对象的话共用内存地址)防止多个组件实例之间共用一个data,产生数据污染。采用函数,在initData时会作为工厂函数返回全新的data对象
Promise.then mutationObserver setImmediate setTimeout
如果没有nextTick机制,每次数据更新都触发视图更新。有nextTick机制,只需要更新一次。性能更好
dom更新是实时的,只是操作DOM比较昂贵,所以一次性渲染DOM
v-if 将DOM元素删除或添加。 会触发子组件的生命周期 相应的事件监听等会销毁或新建(Watcher的newDepIds和depIds) v-show: display:none,DOM元素依然存在。 简单的CSS切换,不触发生命周期
slot:父组件编译和渲染阶段生成vnodes(作用域是父组件实例的)子组件渲染直接拿渲染好的vnodes scpoe-slot:父组件渲染和编译的时候并不直接生成vnodes,而是再vnode的data中保留一个scopedSlot对象,存储不同名称的插槽以及他们的渲染函数,在编译和渲染子组件的时候才执行渲染函数生成vnodes
props:父->子 provide-inject:祖->孙 vuex eventBus:兄弟 $parent $root:兄弟 attrs或者listeners:祖->孙 $emit:子->父 ref:父->子
不是
hash模式监听hashChange hisitory模式监听popState事件,只有浏览器动作的时候后才会触发popState,pushState和popState并不会触发
beforeRouterLeave->beforeEach->beforeEnter->beforeRouteEnter->beforeResolve->afterEach->进入组件声明生命周期beforeCreate->created->beforeMount->mounted->beforeRouteEnter
不一定,如果是不依赖于子组件状态或临时DOM状态的列表渲染 不加是高效的,可以直接复用
是,键名是key 键值为组件, 设置abstract为true 在keep-alive渲染函数中判断vnode是否有缓存,有则取出,没有则缓存 超过设定的缓存长度,则进行LRU清理(最近用的留下,远的删除)
动态设置html title标签内容和是否keepalive缓存
修改数据后,立刻得到更新后的DOM结构
mixin调用顺序:父beforeCreate->父created->父beforeMounted->子beforeCreate->子created->子beforeMount->子mounted->父mounted 更新过程:父beforeUpdate-->子beforeUpdate-->子updated-->父updated; 销毁过程:父beforeDestroy-->子beforeDestroy-->子destroyed-->父destroyed;
mixin是对Vue类的options进行混入,所有的vue实例对象都具备混入进来的配置行为 extend是产生一个继承自vue类的子类,只会影响这个子类的实例对象,不会对vue类本身以及vue类的实例对象产生影响
###浏览器
请求方法:get、post、put、delete、options、trace等
为了确保接收到了信号。第四次客户端回复ACK是为了防止由于网络的因素导致服务端没有接收到确认信号而不能关闭。服务端没收到会重新发送FIN信号。
防止产生混乱,因为当服务端发送FIN到客户端,可能还伴随其他的信息,如果客户端关闭的话,客户端的端口被新的应用占用,其他应用接收到这些数据,可能造成数据混乱
get和post区别: get:产生一个TCP数据包。把header和data一起发送出去 post:产生两个TCP数据包。先发送header,服务器响应100 continue。浏览器再发送data服务器响应200
1、是否是原生模块,内存中有则直接加载, 没有则加载模块并缓存模块 2、是否是文件模块,根据路径查找文件模块并缓存模块 3、自定义模块,逐级找node_modules。通过package.json的main字段找入口。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
HTML
CSS
JS
Vue
小程序
浏览器和网络
webpack
后端
算法
计算机基础
设计模式
调试
mysql
其他
技术外
答案
HTML
doctype不加会怎么样
是指示 web 浏览器关于页面使用哪个 HTML 版本进行编写的指令。不加会用怪异模式解析,浏览器根据自己的方式解析,展示不同的样式。
script加defer和async区别
相同点:都会异步去加载JS,JS下载时不干扰DOM解析
区别:
HTML解析流程
收到请求 -> 字节流解码 -> 输入流预处理(可能会有document.write写入) -> token化 -> DOM树
标签分类
内容分区
文本内容
图片和多媒体
表单
表格
块元素和行内元素
article aside div p
a b i button input img
preload和prefetch
preload优先级更高,解析到这行代码就去加载,但不执行,真正用的时候再执行,页面关闭停止请求资源
prefetch未来可能用到,闲时加载资源,页面关闭依然请求
disabled和readonly区别
disabled作用于所有表单元素,禁止元素一切行为,表单提交时不会传递数据
readonly作用于input textarea输入元素,可以获取焦点,只是设置为只读,表单提交可以传递数据
CSS
盒模型
标准盒模型:width = content
怪异盒模型:width = content + padding + border
box-sizing属性
position属性和作用
垂直水平居中
1、绝对定位已知宽度、绝对定位+margin反向偏移
2、绝对定位宽度未知、transform
上面的方法把margin换成transform: translate(-50%, -50%)
3、flex布局
4、绝对定位,margin: auto,top等值设为相同
5、子元素相对定位,margin和transform来实现
水平居中
1、text-align: center
2、定宽+margin
3、table+margin
4、子元素设置inline-block
5、flex
6、绝对定位+负margin
7、绝对定位+transform
三栏布局
1、float+margin。缺点主要内容不能最先加载
2、BFC(BFC区域不会和浮动元素重叠)。缺点:主要内容不能最先加载
3、圣杯。主体内容优先加载
4、双飞翼。主体内容可以优先加载
5、flex布局
6、table布局
7、绝对定位
两栏布局
基本同上
flex属性
flex:flex-grow flex-shrink flex-basis
清除浮动
1、浮动元素后加clear:both
2、加overflow:hidden触发BFC清除浮动
3、浮动元素容器after伪元素添加clear:both
实现十字窗
1、grid布局
2、flex布局
3、inline-block
BFC、IFC
BFC:块级格式化上下文
触发:
特点:
*
rem
rem,相对单位,相对的只是HTML根元素font-size的值
假设设计稿是宽750px来做的,书写css方便计算考虑,根节点的font-size假定为100px,得出设备宽为7.5rem。设计稿中标注的任何px数值都可以换算成px/100的rem值。
就是说,每一个设备的宽度都定为7.5个rem,然后宽度非750px的设备里,就需要用JS对font-size做动态计算。
计算rem的单位的方式可通过以下方式:
document.documentElement.style.fontSize = document.documentElement.clientWidth*(window.devicePixelRatio||1) / 7.5 + 'px';
需要考虑到dpr,即一倍屏两倍屏的问题。
rem em px的区别
rem基于根元素字体大小展现,可以html font-size设置为62.5%
em基于父元素
vw浏览器可视窗口宽度百分比
vh浏览器可视窗口高度百分比
bem的优缺点
BlockElementModifier其实是块(block)、元素(element)、修饰符(modifier)。
这三个部分使用__ 与--连接
优点:更具有语义化、方便多人协作开发、减少了class层级提高了性能(最好别超过3层)
css选择符解析顺序
如果从左往右会进行大量的共用样式重复匹配(遍历到最后才知道是不是匹配到),反过来减少了公用样式的匹配。从右往左匹配的全部是DOM节点的父元素,从左往右则是匹配的DOM节点的子元素,这样避免了HTML和CSS还没下载完需要等待的情况。
IFC:内联格式化上下文
css实现环绕布局(京东自营标签和标题)
float布局
image设置 align='right'或者其他
css移动端布局和兼容性注意问题
css flex align-item justify-content代表什么 direction代表什么
align-items属性定义项目在交叉轴上如何对齐。
justify-content属性定义了项目在主轴上的对齐方式。
flex-direction属性决定主轴的方向(即项目的排列方向)。
css pre-fetch pre-load区别
prefetch 闲时加载资源,不一定加载
preload 一定需要这些资源,一定加载
覆盖框架中的样式
引入样式表位置盖过默认的
class优先级
vue scope+:deep深度选择器
react :global(xxxxx)不会被应用到CSS Module react是CSS Module
换肤
link rel="alternative stylesheet" 替换样式文件,默认是备选,所以不显示,通过disabled来切换
sass变量换肤
修改class名
三个div实现顶部居中
响应式布局
rem
媒体查询
JS
this代表什么
当前执行上下文的一个属性,根据不同的执行上下文,this代表不同的值。
闭包
创建它的上下文已经毁了,但是它依然存在
JS继承
基于原型链继承
webworker通信
postMessage和onMessage
深拷贝浅拷贝
深拷贝:拷贝到具体的基本类型,彻底改变引用地址
浅拷贝:只是引用改变
for in for of forEach区别
for in 数组索引,对象的可枚举属性
for of 数组元素值,只用于可迭代对象
forEach 不能break或者return 空数据跳过回调函数
Object.prototype.toString和Object.prototype.toString.call区别
前者是调用对象的方法
后者是判断对象
this的指向不同
let const var 区别
暂时性死区
块级作用域
const let 不会绑定到window上
变量提升
const 只是只读引用
正常对象的toString和Object.prototype.toString.call不同
主要在于大部分都是基于对象原型做的修改,所以可以判断类型,而正常toString只是当前方法的
require和import的区别
require是运行时加载、值的拷贝
import是静态编译、值的引用
词法作用域
是根据定义时生成的作用域,创建作用域链时会有一个outer指向他的上一级,这个上一级是根据定义时生成的
原型链
__proto__指向原型,形成的原型链,访问对象属性的时候会沿着原型链往上查找
处理0.1+0.2 !== 0.3的问题
浮点运算精度问题导致的
1、转成整数然后运算完再回去
2、使用toFixed运算
3、转成字符串大数相加计算
reduce的实现
事件循环
主线程 -> 所有微任务 -> 一个宏任务 -> 渲染(不一定每次)
setTimeout、setInterval、setImmediate、MessageChannel
优先级:主任务 > setImmediate > MessageChannel > setTimeout/setInterval
Promise().then()、process.nextTick、MutationObserver、async中await后的代码
优先级:process.nextTick > Promise.then > MutationObserver
防抖和节流
应用场景:
EventEmitter
控制异步请求并发
主要通过队列来进行请求控制
作用域
JS是词法作用域,在函数定义时确定的。
执行上下文包含了变量环境和词法环境。通过变量环境实现了函数作用域,通过词法环境实现了块级作用域。
let const var function
let、const:创建提升,虽然在内存中,但是不能访问是因为V8虚拟机做了限制。
var:创建和初始化被提升。
function:创建、初始化、赋值提升。
let 和 const在全局作用域中声明是不会挂载到window上的。由[[script]]包裹
匿名自执行函数
bind、call、apply
函数柯里化
数组扁平化
主要方法:
数组去重
主要方法:
类型判断
instanceof
不断查找__proto__是否等于当前的prototype
原型链
只有对象这个结构有继承。每个实例对象都有一个私有属性(proto)执向它的构造函数的原型对象(prototype),该原型对象也有自己的原型对象,层层向上直到原型对象为null。
this的指向
谁调用指向谁。new时指向实例。
继承
1、原型链继承
缺点:
2、借用构造函数继承
缺点 父类型原型上的属性和方法不能继承。每次都调用构造函数创建新的方法和属性
3、组合继承
缺点:调用两次构造函数,在prototype上添加了不必要的属性
4、寄生组合继承
优点:减少一次构造函数的调用,减少在原型上添加不必要的属性
5、class
new
1、创建新对象
2、将构造函数的作用域赋值给这个对象
3、执行构造函数
4、返回新对象
深浅拷贝
深拷贝:
1、JSON.stringify()。循环引用、函数不能拷贝
2、
浅拷贝:
Object.assign
...扩展运算符
基本类型、基本包装类型、引用类型
基本类型:undefined、null、string、number、boolean、symbol
引用类型:object
基本包装类型:number、string、boolean(通过new来创建)
1.toString
所以1..toString才可以
运算符优先级
() > . > [...]需计算的成员 > new ...(...)带参数 > 函数调用 > new ...()不带参数 > ! ~ + - ++前置 --前置 typeof(这一层从右到左)
长列表优化
虚拟列表,transform滑动页面。因为单独的层,走合成线程更快
外面一层监听滚动
里面分两层
一层是占位(计算每项高度*数量)
一层是窗口展示多少个DOM,然后用start,end记录进入视口的索引,start,end通过外层监听滚动去计算出来
https://segmentfault.com/a/1190000041812905
https://juejin.cn/post/6844903982742110216#heading-4
箭头函数和普通函数的区别
箭头函数:
JSONP原理
利用script标签的src属性可以跨域,又由于src属性的限制,只能是get请求
客户端将参数拼到url上、并传一个函数名callback。后端将结果callback(JSON.stringify(data))返回
完整实现
Promise的实现原理
观察者模式,then中收集回调,然后状态改变后逐一触发
Promise的方法
Promise解决回调地狱
JS为什么单线程
因为异步的操作主要还是操作DOM。设计成单线程让语言更简单。
TS
好处:增加代码可读性和可维护性
Map的实现
map和object的区别:
简单实现:(底层是邻接链表实现)
V8引擎sort的实现
TimSort排序 小于10个则是插入排序 大于则是快排
数组的底层实现
继承自对象,分快慢数组
快数组底层是数组
慢数组底层是哈希表
快数组转换成慢数组
新容量 >= 3 * 扩容后的容量 * 2 则转变成慢数组或者加入的index > 1024 前面有1025个空值
慢数组转换成快数组
V8的启发式算法那检查空间占用量(慢数组不再比快数组节省50%空间时),转换为快数组
快数组是空间换时间
慢数组是时间换空间
对象的底层实现
属性不超过128个时,用Search Cache
属性为较连续数字,则是数组。
其他情况用哈希表
JS类图
https://zhuanlan.zhihu.com/p/26169639
A(), B(), C() var c = new C(),实现c访问A、B的方法
TS和JS区别
TS是JS的超集,集合了一些JS语法糖
执行环境层面:浏览器和Node可以直接运行JS,TS不可以
时序层面:TS执行前会转义成JS,然后解释执行
TS装饰器
Vue
Vue优势、为什么用Vue
vue门槛低,文档清晰。
生命周期
双向绑定
通过数据劫持和发布订阅者模式实现。
通过Object.defineProperty给数据添加getter(依赖收集)和setter(派发更新)实现数据劫持。
通过get进行依赖收集
收集过程:当实例化渲染一个watcher的时候,首先进入watcher的构造函数逻辑,执行get方法,get函数中把Dep.target赋值为当前渲染watcher并压入栈。执行渲染vm._render方法,生成渲染VNode时,对vm上的数据进行访问,此时触发数据对象的getter方法(执行Dep.target.addDep(this)方法)将watcher订阅到这个数据持有的dep的subs中,然后递归遍历添加所有的子项getter
通过set观察者更新视图
派发过程:当我们组件中对响应的数据做了修改,就会触发setter的逻辑,最后调用dep.notify方法(遍历subs里的依赖),调用每一个wathcer的update方法,update方法中有个queueWatcher的方法引入了队列的概念(是派发更新时优化的一个点,并不会每次数据变化都触发watcher回调,而是把这些watcher添加到一个队列中,然后在nextTick后执行watcher的run函数)。
队列排序(queueWatcher中有个根据id的sort排序)的保证:
1、组件更新由父到子,父组件创建早于子组件,所以watcher创建也是
2、用户自定义的watcher早于渲染watcher执行,用户自定义的watcher早于渲染watcher创建
3、父组件watcher期间被销毁,对应的watcher都会被跳过,所以父组件watcher先执行
run函数解析:通过this.get()得到当前的值,然后做判断,满足新旧值不等,新值是对象类型,deep模式任何一个条件时,执行watcher回调(回调执行时会把新旧值当参数传入,也是自定义watcher时新旧值的来源)。对于渲染watcher执行this.get()时,会执行getter方法,触发组件重新执行patch重新渲染。
Object.defineProperty缺点
1、无法监听数组变化
2、只能劫持对象的属性,如果属性还是对象,则需要深度遍历去劫持
vue3为啥用proxy
1、Proxy可以监听数组变化
2、proxy监听对象而非属性,在访问目标对象前架设了一层拦截,所以可以对外界的访问进行过滤和该写。可以劫持整个对象并返回新的对象
proxy缺点:兼容性问题,无法用polyfill实现
哪些监控不了,如何解决
不能检测数组和对象的变化
对象:无法检测属性的添加和移除,可以通过Vue.set或vm.$set来添加响应式属性
数组:无法检测通过下标直接设置数组项、修改数组长度时。通过vm.$set或者是splice数组方法
数组如何监控
重写数组的方法
3.0有什么
vue和react区别
AST
抽象语法树,本质就是一个JS对象
先将模板语法变成AST,然后解析成HTML结构
虚拟DOM
由于DOM元素属性很多,性能有问题。所以虚拟DOM就是用JS对象去描述一个DOM节点,核心就是标签名、数据、子节点、键值等
模板编译
v-key作用]
vue识别节点的一个机制,虚拟DOM的VNode节点的唯一id,是diff的一种优化策略(判断是否是相同节点时需要,不添加则增多了比较次数),可根据key更准确、更快地找到VNode节点。
比较相同节点时先用的key值。
暴力破解时也是使用的key值匹配。
不用下标作为index原因,数组变化会导致下标变化,不稳定性导致性能浪费,无法关联上次的数据
diff算法
同层树节点的比较
特点:
只在同层级比较,不会跨层级比较
在diff比较的过程中,循环从两边向中间比较
策略:深度优先、同层比较
过程:
新节点存在,旧节点不存在——新增
新节点不存在,旧节点存在——删除
新旧节点都存在——判断是否是相同节点
通过patchVNode进行对比:
判断新旧虚拟DOM是否全等,是则结束
更新节点的属性
判断新节点是不是文本节点,是则更新文本,否则继续比较,
判断新旧节点是否有孩子节点:
两个都有孩子节点——updateChildren比较孩子节点
新的有,旧的没有——新增
新的没有,旧的有——删除
新旧都没有,判断文本节点是否有内容,有则清空
新旧节点都不存在——初始化页面,createElm
updateChildren时
新旧两个数组,声明四个指针,分别指向两个数组的首尾
两个开始指针比较,相同则往后移一个
不相同则两个结尾指针比较,相同前移
不相同则旧开和新尾对比,相同则各自移动
不相同则新开和旧尾对比,相同则各自移动
如果都不行,则遍历旧节点寻找(将数组转成对象,然后通过键值对key查找),找到则设为undefined。后续遇到undefined则跳过,当新旧各自出现指针重叠的时候,则是处理另一方多余节点的时候
data为什么是函数
(new时,对象的话共用内存地址)防止多个组件实例之间共用一个data,产生数据污染。采用函数,在initData时会作为工厂函数返回全新的data对象
nextTick的实现
Promise.then mutationObserver setImmediate setTimeout
为什么异步渲染
如果没有nextTick机制,每次数据更新都触发视图更新。有nextTick机制,只需要更新一次。性能更好
nextTick为什么可以拿到DOM
dom更新是实时的,只是操作DOM比较昂贵,所以一次性渲染DOM
AST上都有啥
computed和watcher的区别
v-if和v-show区别
v-if
将DOM元素删除或添加。
会触发子组件的生命周期
相应的事件监听等会销毁或新建(Watcher的newDepIds和depIds)
v-show:
display:none,DOM元素依然存在。
简单的CSS切换,不触发生命周期
slot和scope-slot,实现原理
slot:父组件编译和渲染阶段生成vnodes(作用域是父组件实例的)子组件渲染直接拿渲染好的vnodes
scpoe-slot:父组件渲染和编译的时候并不直接生成vnodes,而是再vnode的data中保留一个scopedSlot对象,存储不同名称的插槽以及他们的渲染函数,在编译和渲染子组件的时候才执行渲染函数生成vnodes
组件通信方式
props:父->子
provide-inject:祖->孙
vuex
eventBus:兄弟
$parent $root:兄弟
attrs或者listeners:祖->孙
$emit:子->父
ref:父->子
vuex的原理,怎么实现每个组件实例都有Store
vuex数据流
action和mutation区别
provide和inject是响应式的么
不是
eventBus实现
封装组件应该注意什么
router的实现原理
hash模式监听hashChange
hisitory模式监听popState事件,只有浏览器动作的时候后才会触发popState,pushState和popState并不会触发
router的导航守卫
beforeRouterLeave->beforeEach->beforeEnter->beforeRouteEnter->beforeResolve->afterEach->进入组件声明生命周期beforeCreate->created->beforeMount->mounted->beforeRouteEnter
加v-key和不加区别,哪个效率高
不一定,如果是不依赖于子组件状态或临时DOM状态的列表渲染 不加是高效的,可以直接复用
如何写全局组件
如何实现的异步加载
Vue的设计思想、React的设计思想
keep-alive缓存的是整个组件么
是,键名是key 键值为组件, 设置abstract为true
在keep-alive渲染函数中判断vnode是否有缓存,有则取出,没有则缓存
超过设定的缓存长度,则进行LRU清理(最近用的留下,远的删除)
keep-alive如果a->b然后a->c此时不想缓存a了
vue-router的meta是做什么的
动态设置html title标签内容和是否keepalive缓存
nextTick的应用场景
修改数据后,立刻得到更新后的DOM结构
mixin和extend的区别
mixin调用顺序:父beforeCreate->父created->父beforeMounted->子beforeCreate->子created->子beforeMount->子mounted->父mounted
更新过程:父beforeUpdate-->子beforeUpdate-->子updated-->父updated;
销毁过程:父beforeDestroy-->子beforeDestroy-->子destroyed-->父destroyed;
mixin是对Vue类的options进行混入,所有的vue实例对象都具备混入进来的配置行为
extend是产生一个继承自vue类的子类,只会影响这个子类的实例对象,不会对vue类本身以及vue类的实例对象产生影响
template是如何转成AST
转换的那个插件是怎么实现的
###浏览器
请求方法,get和post区别
请求方法:get、post、put、delete、options、trace等
为什么要四次挥手
为了确保接收到了信号。第四次客户端回复ACK是为了防止由于网络的因素导致服务端没有接收到确认信号而不能关闭。服务端没收到会重新发送FIN信号。
为什么四次挥手客户端要等待
防止产生混乱,因为当服务端发送FIN到客户端,可能还伴随其他的信息,如果客户端关闭的话,客户端的端口被新的应用占用,其他应用接收到这些数据,可能造成数据混乱
get和post区别:
get:产生一个TCP数据包。把header和data一起发送出去
post:产生两个TCP数据包。先发送header,服务器响应100 continue。浏览器再发送data服务器响应200
Node
Node require
1、是否是原生模块,内存中有则直接加载, 没有则加载模块并缓存模块
2、是否是文件模块,根据路径查找文件模块并缓存模块
3、自定义模块,逐级找node_modules。通过package.json的main字段找入口。
The text was updated successfully, but these errors were encountered: