JS
数据双向绑定原理(响应式)
vue采用了发布-订阅模式。
内部结合了Object.defineProperty这个ES5的新特性(ie8浏览器不支持)
Object.defineProperty(obj, prop, descriptor)
方法会直接给对象定义一个新属性或者修改现有属性,并返回此对象。
处理过程:
- 对vue传入的数据,通过数据监听器Observer进行数据监听
- 为其动态添加get与set方法。当数据变化的时候,就会触发对应的set方法
- 随后触发watcher
- 接着进行虚拟dom对比
- 执行render,视图进行更新。
缺点:
- 需要对data对象的每一个属性进行遍历劫持,性能消耗大
- 由于 Vue 会在初始化实例时对 property 执行getter/setter 转化,所以property 必须在最开始时在 data 对象上存在才能让 Vue 将它转换为响应式的。
- vue不能检测到利用索引直接设置一个数组项, 例如:
vm.items[indexOfItem] = newValue
也不能检测到修改数组的长度,例如:vm.items.length = newLength
哪些数组的方法是响应式的:
- push pop shift unshift splice sort resourse 是响应式的,还有
$set()
拦截了数组的原型,重写了数组的原型 - 通过下标索引的方法,也可通过去实现响应式
this.letters.splice(0, 1, 'bbbbbb')
this.$set(this.letters, 0, 'bbbbbb') // set(要修改的对象, 索引值, 修改后的值)
补:
watcher
是用来订阅数据的变化,并执行相应操作,从而更新视图render
函数是把模板解析成Vnode(虚拟DOM)节点参数:obj:要定义属性的对象。 prop:要定义或修改的属性的名称,descriptor:要定义或修改的属性描述符get
: 负责获取值,它不带任何参数,set
: 负责设置值,在它的函数体中,return是无效的.- 实现数据监听器
Observer
,能够对数据对象的所有属性进行监听,如有变动将最新值通知订阅者
key的作用
1、主要是用在vue的虚拟Dom算法,新旧虚拟dom对比时作为唯一标识ID
2、vue为了高效的渲染元素,会恢复已有的元素(key值没有改变)而不是从头开始进行渲染,同理,改变某一个元素的key值会使该元素重新被渲染。
图片来源:尚硅谷bibililivue视频截图
当页面的数据发生变化时,Diff算法只会比较同一层级的节点:
- 如果节点类型不同,直接干掉前面的节点,再创建并插入新的节点,不会再比较这个节点以后的子节点了。
- 如果节点类型相同,则会重新设置该节点的属性,从而实现节点的更新。
补:
元素类型nodetype
:
- 元素
document.getElementById('div')
- 属性
document.getElementById('div').getAttributeNode('name')
- 文本
document.getElementByTagName('td')[0].firstChild
- 注释
- 文本
-
另一个版本(尚硅谷)
1。虚拟DOM中key的作用:key是虚拟DOM对象的标识,当数据发生变化,Vue会根据[新数据]生成[新的虚拟DOM]随后Vue进行[新虚拟DOM]与[旧虚拟DOM] 的差异比较
2.对比规则:
(1).旧虚拟DOM中找到了与与新虚拟DOM相同的key: a.若虚拟DOM中内容没变,直接使用之前的真实DOM b.若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM。 (2).旧虚拟DOM中未找到与新虚拟DOM相同的key创建新的真实DOM,随后渲染到到页面。
3。用index作为key可能会引发的问题:
1。若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==>界面效果没问题,但效率低。 2。如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。
4。开发中如何选择key?
1.最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。 2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示. 使用index作为key是没有问题的。
diff算法
- 首先比较两个虚拟dom是否在同层级
- 是否在同一层
- 是否有相同的父级
- diff是采用先序、优先深度遍历的方式进行节点比较的,即,当比较某个节点时,如果该节点存在子节点,那么会优先比较他的子节点,直到所有子节点全部比较完成,才会开始去比较改节点的下一个同层级节点。
遍历的顺序为:1、2、3、4、5、6、7、8、9
diff算法的优化策略:四种命中查找,四个指针
- 旧前与新前(先比开头,后插入和删除节点的这种情况)
- 旧后与新后(比结尾,前插入或删除的情况)
- 旧前与新后(头与尾比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧后之后)
- 旧后与新前(尾与头比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧前之前)
数据类型
数据类型&类型检测
- 基本数据类型:
number string undefined null? boolean
typeof()
- 引用(Object)数据类型:
function、Array、Date
Object.prototype.toString.call() && hasOwnPropertype() // 检测某个对象是否含有指定的自身属性
symbol(ES6新增):类似一种构造函数。
特点:
1.唯一性:两个symbol有相同值但不相等
2.隐藏性:for…in无法遍历
typeof
:对于null及数组、对象,typeof均检测出为object[1,2] instanceof Array
:可以左边放你要判断的内容,右边放类型来进行JS类型判断,只能用来判断复杂数据类型 instanceof 是用于检测构造函数(右边)的prototype属性是否出现在某个实例对象(左边)的原型链上。Object.prototype.toString.call(1)
:toString是Object原型对象上的一个方法,该方法默认返回其调用者的具体类型,基本上所有对象的类型都可以通过这个方法获取到。
引:
-
undefined和null的区别
null --------定义但为空 undefined ---应该有值,缺少值,但未定义
- undefined:( 4 ,变量与对象没有赋值,函数入参出参)
- 变量被声明了,但没有赋值时,就等于undefined。
- 对象中没有赋值的属性,该属性的值为undefined。
- 调用函数时,应该提供的参数没有提供,该参数等于undefined。
- 函数没有返回值时,默认返回undefined。
- null:(2)
- 作为函数的参数,表示该参数不是对象。
- 作为对象原型链的终点
当用Object.prototype._proto_取原型时,当不存在时需要用一个值去表示,undefined为不存在,也相当于原型就是undefined。所以用null来表示比较合适。
Object.getPrototypeOf(XXX)
返回对象的原型
- undefined:( 4 ,变量与对象没有赋值,函数入参出参)
引:
- 数据类型转化
- 隐式转换
- 隐式类型转换:
== != - * / % *= /= -= %=
先转数据类型再进行比较- 数字与字符串进行运算
- 加法运算:结果会转换为字符串。
- 除加法以外的运算转换为数字。
- 强制转换:
Number() parseInt() parseFloat()
- Number:
如果是undefined,返回NaN
如果是Boolean,分别转换为0,1
如果是null,返回0ParseInt&ParseFloat
从第一个字符开始解析每一个字符,一直解析到字符串的末尾,或者遇到一个无效的浮点数字字符为止。——字符串的第一个小数点有效,第二个小数点无效
栈和堆:
- 栈(stack):栈会自动分配内存空间,会自动释放,存放基本类型:String,Number,Boolean,Null,Undefined,BigInt,简单的数据段,占据固定大小的空间。
- 堆(heap):动态分配的内存,大小不定也不会自动释放,存放引用类型,(Function,Array,Object),指那些可能由多个值构成的对象,保存在堆内存中,包含引用类型的变量,实际上保存的不是变量本身,而是指向该对象的指针。
区别:
- 栈:所有在方法中定义的变量都是放在栈内存中,随着方法的执行结束,这个方法的内存栈也自然销毁。
- 优点:存取速度比堆快,仅次于直接位于CPU中的寄存器,数据可以共享;
- 缺点:存在栈中的数据大小与生存期是确定的,缺乏灵活性。
- 堆:堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(参数传递)。创建对象是为了反复利用,这个对象将被保存到运行时数据区。
深拷贝和浅拷贝的区别
https://juejin.cn/post/6844903929705136141
发生在引用类型中
- 浅拷贝(引用拷贝):它与原来的变量仍然指向同一个地址,两者之间相互影响
let obj1 = { person: {name: "kobe", age: 41},sports:'basketball' };
//Object.assign()是对象的静态方法,可以用来复制对象的可枚举属性到目标对象,利用这个特性可以实现对象属性的合并。
let obj2 = Object.assign({}, obj1);
let obj2= {... obj1} // 扩展运算符实现
- 深拷贝(值拷贝):拷贝的过程中,开辟了独立的空间,这个对象指向这个地址,与原来的对象互不干扰。
JSON.parse(JSON.stringify())
:有很大缺陷的,比如拷贝其他引用类型、拷贝函数、循环引用等情况。- for in 遍历 递归 *
function clone(target) { if (typeof target === 'object') { let cloneTarget = Array.isArray(target) ? [] : {} for (const key in target) { cloneTarget[key] = clone(target[key]) } return cloneTarget } else { return target } }
原型&原型链
提出这个概念(why):
- 同一个函数
new
出来的两个实例,都会开辟出一个新的堆区,当判断这两个实例方法时会发现不相等
function Person(name, age) {
this.name = name;
this.age = age;
this.eat = function() {
console.log(age + "岁的" + name + "在吃饭。");
}
}
let p1 = new Person("jsliang", 24);
let p2 = new Person("jsliang", 24);
console.log(p1.eat === p2.eat); // false
- 这样会导致内存不足,需要建立一个能够溯源的链条,类似共享库的对象;实例向上可以找到自己的构造函数
function Person(name) {
this.name = name;
}
// 通过构造函数的 Person 的 prototype 属性找到 Person 的原型对象
Person.prototype.eat = function() {
console.log("吃饭");
}
let p1 = new Person("大雄", 24);
let p2 = new Person("静香", 24);
console.log(p1.eat === p2.eat); // true
what
-
原型
- 所有引用类型都有(除null外)一个
__proto__
(隐式原型)属性,指向它构造函数的prototype
引:引用类型:
Array、Object、Date、Function、null(null值表示一个空对象指针,而这也是typeof检测null返回object的原因)var a = [1,2,3]; a.__proto__ === Array.prototype; // true
- 所有函数都有一个
prototype
(原型)属性,指向函数的原型对象
function Person(age) { this.age = age } Person.prototype.name = 'kavin' var person1 = new Person() var person2 = new Person() person1.name === person2.name === 'kavin'
prototype属性值都为一个普通对象
- 所有引用类型都有(除null外)一个
-
原型链
- 当访问一个对象的某个属性时,会先在对象本身属性上查找,如果没有找到,则会去它的
__proto__
上查找,即它的构造函数的prototype
,如果还没有找到就会在构造函数prototype
的__proto__
中查找,这样一层一层向上查找就会形成一个链式结构,称为原型链。
1.一直往上层查找,直到到null还没有找到,则返回undefined
引:null作为对象原型链的终点
2.
Object.prototype.__proto__ === null
(为了避免死循环而设置)
3.所有从原型或更高级原型中的得到、执行的方法,其中的this在执行时,指向当前这个触发事件执行的对象 - 当访问一个对象的某个属性时,会先在对象本身属性上查找,如果没有找到,则会去它的
this指向
- 构造函数(new)的this指向实例化后的那个对象
第一步:创建一个Object对实例。
第二步:将构造函数的执行对象赋给新生成的这个实例。
第三步:执行构造函数中的代码。
第四步:返回新生成的实例。引:
new时做了什么:
1. 创建一个新对象
2. this指向这个新对象
3. 执行代码给this赋值
4. return this
如果调用新对象没有的属性时,js会延原型链向上逐层查找function Person(age,name){ this.age = age console.log(this) // this分别指向Person的实例对象p } var p = new Person(18,"xiaoming")
2.普通函数的this指向调用该函数的那个对象
// 在ES6严格模式中禁止this指向全局对象
"use strict";
function f() {
console.log(this);
}
f() //log:undefined
window.f() //log:window{}
3.在事件中this指向发生事件的元素本身(btn)
4.箭头函数的this指向父作用域的this
- 箭头函数中的this永远都只看它所属的作用域的this,无法通过bind/call/apply来修改;不能做为构造函数
5.setTimeout()
调用的代码运行在与所在函数完全分离的执行环境上。这会导致这些代码中包含的this关键字会指向window(或全局)对象。
call、apply、bind区别
- call将第一个参数指向this,当为null、undefined时,默认指向window
- apply与call相似,但apply传递给方法的参数是数组的形式,只接受两个参数,一个是改变this的对象,另一个是参数数组
- call和apply在改变方法的this指向时,会同时执行方法;而bind不会立即执行方法,参数会传给返回的方法
es6
序号 | 具体 | 分类 |
---|---|---|
1 | let、const、symbol | 数据类型 |
2 | 模板字符串 | 字符串 |
3 | 扩展运算符 | 数组 |
4 | 解构赋值 | 对象 |
5 | 箭头函数 | 函数 |
6 | 取消变量提升,添加块级作用域 | 语法 |
7 | promise、async await、filter(),map(),forEach(),find() | 高阶函数等 |
引:
- 数组去重
[...new Set(arr)]
- 数组方法:
push()// 方法可向数组的末尾添加一个或多个元素,并返回新的长度。 pop()// 方法用于删除并返回数组的最后一个元素。 shift()// 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。 unshift()// 方法可向数组的开头添加一个或更多元素,并返回新的长度。 sort()//(排序) reverse() //(反转数组) concat()// 方法用于连接两个或多个数组 slice()//(数组截取) arr.slice(start,end); splice() //方法向/从数组中添加/删除项目,然后返回被删除的项目。(该方法会改变原始数组) arr.splice(index,howmany,item1,.....,itemX) forEach() //不返回、map()返回新数组、filter() reduce() Array.from() find() includes()
- map和forEach
- map会返回一个新数组,forEach返回undefined,都不对原数组产生影响 ,只是调用原数组,但在回调函数中原数据都可以被修改。
- map速度比forEach快(快70%)
- map()会分配内存空间存储新数组并返回,forEach()不会返回数据
使用场景:
- map适合改变数据值的时候。不仅更快,而且返回一个新数组。还可以组合filter(), reduce()等组合使用
- forEach适用于不改变数据的时候,而只是取值po
生命周期
vue的生命周期
就是Vue实例一生过程——从最开始的数据监听、模板编译、将实例挂载到DOM并在数据变化时更新DOM、销毁等。在这过程中有8个钩子的函数,在不同阶段可以添加自己的代码
- vue对象核心的四个阶段 八个钩子函数:
- 数据挂载:把传入data属性的内容,赋给vue对象
前后分别的钩子函数是:beforeCreate
、created
- 模板渲染:把vue对象中data渲染到dom对象上。
前后分别的钩子函数是:beforeMount
、mounted
- 组件更新:当渲染到DOM上的数据发生变化时,会触发组件的更新。
前后分别的钩子函数是:beforeUpdate
、updated
- 组件销毁:
前后分别的钩子函数是:beforeDestroy
、`destroyed
- 数据挂载:把传入data属性的内容,赋给vue对象
keep-alive标签的作用
keep-alive可以缓存组件的状态,避免了组件的频繁创建和销毁。
减少加载时间及性能消耗,提高用户体验性
- 特性:
- 一般结合路由和动态组件一起用于缓存组件
- 提供
include
和exclude
属性。两者都支持字符串和正则表达式,- include名称匹配的组件会被缓存,
- exclude表示任何名称匹配的组件都不会被缓存,其中
exclude
的优先级比include
高; - 对应两个钩子函数
activated
和deactivated
,当组件被激活时,触发钩子函数activated,当组件被移除时,触发钩子函数deactivated
- 钩子函数的执行顺序:
-
当使用keep-alive时:
钩子触发的顺序是:created -> mounted -> activated -> deactivated
-
当使用keep-alive,并且再次进入了缓存页面的情况:
activated -> deactivated
-
使用<keep-alive>
会将数据保留在内存中,
如果要在每次进入页面的时候获取最新的数据,需要在activated阶段获取数据,承担原来created钩子中获取数据的任务
Vue 的父组件和子组件生命周期钩子函数执行顺序
Vue 的父子组件钩子函数的执行顺序可以归类为4个部分:
-
第一部分:首次加载渲染
父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
-
第二部分:子组件更新
父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
-
第三部分:父组件更新过程
不会影响子组件父 beforeUpdate -> 父 updated
-
第四部分:销毁过程
父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
nextTick:
在下次 DOM 更新循环结束之后立即执行延迟回调。用来获取更新后的 DOM。
- 视图更新之后,基于新的视图进行操作。
- 第三方插件,在vue生成的某些dom动态发生变化时重新应用该插件。
promise
把复杂的异步代码改成同步——解决回调地狱
promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态) ;状态一旦改变,就不会再变。创造promise实例后,它会立即执行。
- 基本使用
- Promise的构造函数接收一个为函数的参数,有两个参数:resolve,reject,分别表示异步操作执行成功、异步执行失败后的回调函数。
- 当promise运行有了结果,再执行.then()中的异步操作。当执行失败时也可以通过promise.catch()调用;
- 方法:
- Promise.all():当所有的异步代码都执行完毕以后才会执行.then中的操作
- Promise.finally() : 不管 Promise 对象最后状态如何,都会执行的操作
- Promise.race():只要有一个promise执行完毕后就会执行.then操作
Promise、nextTick、setTimeout的执行顺序
执行顺序:同步代码 > nextTick > Promise > setTimeout
-
同步&异步:
- 同步:立即执行且必须执行。单位时间内,只会做一件事。如果中途出现错误,就会导致主线程阻塞,从而导致页面报错。
- 异步:并非是当下立即需要执行的,就会使用异步;比如当满足某个条件时才会执行。
-
宏任务&微任务: 微任务 > 宏任务
-
宏任务:script、setTimeout / setInterval
- setTimeOut并不是直接把回掉函数放进异步队列中,而是在定时器的时间到了之后,把回掉函数放到执行异步队列中。如果此时这个队列已经有很多任务了,那就排在他们的后面。这也就解释了setTimeOut为什么不能精准执行的问题
-
微任务:Promise、nextTick
- new Promise是同步任务,会被放到主进程中去立即执行。而.then()函数是异步任务,当promise状态结束时,就会立即放进异步队列中去
- nextTick会在一个事件循环结束之后,下一个事件循环开始之前进行回调的调用
- nextTick其实也是使用Promise来处理回调,但是会将 nextTick 中的回调全部塞到 Promise.then 的回调之前,而同级微任务的情况下,是按顺序来进行执行的,这就让 nextTick 永远都在 Promise.then 之前执行。
-
-
调用栈 & 任务队列:先入后出
- 当有函数需要执行而分配给浏览器引擎时,引擎会按一定顺序将函数进行排队
- 排到该函数后,该函数推入到 JavaScript 主线程中的调用栈
- 该函数执行完毕后,出栈
-
Event Loop事件循环:
当同步代码执行完后,就会开启Event Loop,不断去循环回调队列,看队列中是否有回调函数需要被执行
- 工作原理:
- 同步代码执行完后,开启 Event Loop,其中分为微任务队列和宏任务队列
- 检查微任务队列中是否有回调函数,且调用栈是否为空,若是则把该函数从队列中推入到调用栈执行
- 检查宏任务队列中是否有回调函数,且调用栈是否为空,若是则把该函数从队列中推入到调用栈执行
- 直到微任务和宏任务队列中均为空,则开启下一个循环,从第2步开始一直反复
- 工作原理:
概念
36.如何利用webpack来优化前端性能?(提高性能和体验)
https://juejin.cn/post/6844904071736852487
用webpack优化前端性能是指----优化webpack的输出结果,让打包的最终结果在浏览器运行快速高效。
- 压缩代码:删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的
UglifyJsPlugin
和ParallelUglifyPlugin
来压缩JS文件,利用cssnano(css-loader?minimize)来压缩css - 兼容高版本node
- 提取公共代码
- 利用CDN加速:在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对于output参数和各loader的publicPath参数来修改资源路径
- 删除死代码(
Tree Shaking
)。将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数--optimize-minimize
来实现
26.路由传参2种方式params与query
-
params: 取值
this.$route.params.id
- params比query严格只能用name不能用path,不然会直接无视掉params中的内容
- 需要写成动态路由
'/login/:username:age'
,不然在第一次点击实现跳转后再刷新页面是,不能取到传过来的值。但是这样就不会类似于 post 请求,他会把接收到的参数替换作为地址 - 假如传入参数为:
params: { username: 'admin'}
,那么最终访问的地址为:http://localhost:8080/#/home/admin
编程式:
data:{ username: '', age:'' }, login() { ... this.$router.push({ name: 'home', //注意使用 params 时,只能使用 name , 不能使用 path params: { username: this.username , age: this.age }, }) } // 动态路由 const routes = [ { path: '/home/:username/:age', name: '/home', component: Home }
声明式:
<router-link :to="{ name: 'home', params: { username: username } }">
-
query:取值
this.$route.query.id
- query 传参类似于网络请求中的 get 请求,query 传过去的参数会拼接在地址栏中(
http://localhost:8080/#/?name=xx
)。
编程式:
data:{ username: '' }, login() { this.$router.push({ path: '/home', query: { username: this.username }, }) }
申明式:
<router-link :to="{ path: '/home', query: { username: username } }">
- query 传参类似于网络请求中的 get 请求,query 传过去的参数会拼接在地址栏中(
28.内存泄漏
动态开辟的空间在使用完毕后未释放,结果导致一直占据该内存。
原因:
setInterval
定时器如果没有取消会一直存在与内存中- 不合理的使用闭包
- 单例,原因单例的静态特性使得其生命周期和应用的生命周期一样长
- 使用未声明的变量,使其成为全局变量会一直存在内存中
21.闭包
- 闭包就是能够读取父函数内部变量的函数
- 就是函数嵌套,子函数要在外部访问父函数的局部变量,通过在子函数中使用父元素的局部变量,将子函数作为父函数的返回值。
- 缺点:使变量始终保持在内存当中,增长变量生命周期,不会被垃圾回收机制回收;内存会消耗很大(内存泄漏)。
var fn = function(){
var count = 0;
return function(){
return count++;
}
}
引用:节流防抖
引:
垃圾回收机制:
1.工作原理:
是当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。
2.工作流程
- 垃圾回收器,在运行的时候会给存储在内存中的所有变量都加上标记。
- 去掉环境中的变量以及被环境中的变量引用的变量的标记。
- 再被加上标记的会被视为准备删除的变量。
- 垃圾回收器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。
防抖节流:
- 防抖:函数连续多次触发,只执行最后一次。-------防止抖动,以免把一次事件误认为多次 防抖重在清零
clearTimeout(timer)
- 应用
- 富文本编辑器自动保存,当无任何更改操作一秒后进行保存
- 登录、发送数据等按钮避免用户点击太快,以致于发送了多次请求,需要防
function debounce (f, wait) { let timer return (...args) => { clearTimeout(timer) // !!! timer = setTimeout(() => { f(...args) }, wait) } }
- 应用
- 节流:函数连续多次触发,在某个时间间隔内只执行一次 。 控制事件发生的频率,如控制为1s发生一次 节流重在开关锁 timer=null
- 应用
- 滚动事件中触发请求,不希望在滚动时一直发送请求,而是隔一定时间发起一次
- 调整浏览器窗口大小时,resize 次数过于频繁,此时需要一次到位
function throttle (f, wait) { let timer return (...args) => { if (timer) { return } timer = setTimeout(() => { f(...args) timer = null // !!! }, wait) } }
- 应用
为什么用闭包:如果把timer放在内部函数中,每次执行内部的函数,都会创建一个新的timer变量。
5.MVVM
- 将mvc中Controller演变成mvvm中的viewModel。
- mvvm主要解决了mvc模式下大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。
- mvvm:
View
(代表UI 组件)负责将数据模型转化成UI 展现出来,ViewModel
是一个同步View 和Model的桥梁(用来存放于页面相关的数据)
面向对象
面向对象就是一种思想,任何事物都可以看作是一个对象。
对象:对象是一个容器,封装了属性{key:value}和方法{function:()},属性是对象的状态,方法是对象的行为
- 主要思想:
- 封装:就是模块化,把不同的功能,封装在不同的模块里。把内部的实现隐藏起来,然后只暴露必要的方法让外部调用。
- 继承:在不修改原类的情况下,给现存的类添加新特性。
- 多态:相当于对于同一个接口类型,由多种不同的实现方式。
- 特点:
- 代码复用性强
- 代码开发模块化,便于维护。
- 代码的可靠性和灵活性
- 代码的可读性和可扩展性。
面向过程:按照我们分析好的步骤,按照步骤解决问题。(更注重过程实现) ++优点++:性能好,++缺点++:不易维护
面向对象:以对象功能来划分问题,而不是步骤。(关注对象的功能) ++优点++:低耦合,灵活,易于维护,++缺点++:性能比面向过程低
watch和computed的区别
- computer:通过属性计算而得来的属性 有缓存没异步
- 当它依赖的属性值发生改变,下一次获取computed的值时会重新计算,当没有改变时,会从缓存中读取
- computed中的函数必须用return返回
- 可以设置getter和setter
使用场景:多对一&&一对一,比如购物车商品的结算
- watch:属性监听 没缓存有异步
- watch是依赖data中的属性,当data中的属性发生改变的时候,watch中的函数就会立即执行
- watch中的函数有两个参数,前者是newVal,后者是oldVal。可以没有return
- watch想要监听引用类型数据的变化,需要进行深度监听(
deep:true
);首次加载做监听(immediate:true
)
使用场景:一对多,比如搜索框
浏览器工作原理
实现http协议的通讯
- 浏览器的主要组件:
- 用户界面 - 包括地址栏、 前进/后退按钮、 书签菜单等。除了浏览器主窗口显示的请求的页面外,其他显示的各个部分都属于用户界面。其他显示的各个部分都属于用户界面。
- 浏览器引擎 - 在用户界面和呈现引擎之间传送指令。
- 渲染引擎 - 负责显示请求的内容。 如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。
- 网络 - 用于网络调用,如 HTTP 请求。 其接口与平台无关,为所有平台提供底层实现。
- 用户界面后端 - 用于绘制基本的窗口小部件,比如组合框和窗口。 其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。
- JavaScript 解释器 - 用于解析和执行 JavaScript 代码。
- 数据存储 - 这是持久层。 浏览器需要在硬盘上保存各种数据,例如 Cookie。 新的 HTML 规范(HTML5) 定义了“网络数据库” ,这是一个完整的浏览器内数据库。
https://www.cnblogs.com/xiaohuochai/p/9174471.html
cookie和webstorage区别
- sessionStorage数据在浏览器关闭后自动删除
- localStorage长期存储数据;当对cookie做持久化处理时也可。
设置cookie持久化:
设置cookie的生命周期setMaxAge,并给出一个以秒为单位的时间。将最大时效设为0则为删除。
- sessionStorage和localStorage的存储空间更大(5M),cookie(4k);
- sessionStorage和localStorage有比cookie更多丰富易用的接口,和各自独立的存储空间;
- cookie在浏览器和服务器间来回传递,每次http请求都会携带cookie;sessionStorage和localStorage不会
data是个函数
引用类型如果不用function返回,每个组件的data都是内存的同一个地址,一个数据改变了其他也改变了;使用函数使每个实例维护一份独立的地址
什么是作用域链?
内部环境通过作用域链来访问外部环境的属性和方法,外部环境不能访问内部环境的任何属性和方法。只能通过定义函数来延长作用域。
CSS
css3新特性
- 新的选择器
- 属性选择器:
[title]
[class='demo']
- 结构伪类选择器
nth-child(n)
n可为数字、公式、关键字nth-of-type(n)
根据类型选择
- 伪类选择器
::before
::after
- 属性选择器:
- 转换transform
- 2D
- translate(x,y)
- rotate(45deg)
- scale(x,y)
- 3D
- translate3d(x,y,z)
- rotateX(45deg)
- rotateY(45deg)
- rotateZ(45deg)
- 2D
- 动画animation
- 浏览器私有前缀
-moz-
:firefox浏览器-ms-
:ie浏览器-webkit-
:safari、chrome浏览器-o-
:Opera
css盒模型
盒模型:分为内容(content)、内填充(padding)、外边界(margin)、边框(border)四个部分
通过 box-sizing 来改变盒子模型,它有两个选项
border-box
设置的width = 左右padding + 左右border + content真正宽度widthcontent-box
默认值 内容真正宽度 = 设置的宽度
flex布局:
- 采用Flex布局的元素,称为Flex容器(flex container)。它的所有子元素自动成为容器成员,称为Flex项目(flex item)
- 容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。
- 主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end;
- 交叉轴的开始位置叫做cross start,结束位置叫做cross end。项目默认沿主轴排列
- 元素的属性:
- flex-wrap:是否换行
- justify-content:主轴上的对齐方式
- align-items:交叉轴上的对齐方式
- flex-direction:设置主轴的方向
- column-reverse:主轴的起点位置
为元素设置默认样式
为了兼容不同了浏览器,不同浏览器的样式不同。
1.clientWidth
, offsetWidth
, scrollWidth
- offsetWidth
- 在
box-sizing:border
的时候:offsetWidth
其实就等于dom
元素的width
- 在
box-sizing:content
的时候:offsetWidth
= width + 左border + 右border + 左padding + 右padding
- 在
注意
width
指内容区(content
)宽度,随盒模型数据改变
- clientWidth ——内容区的宽度(不加border)
- 在
box-sizing:border
的时候 :clientWidth = width - 左border - 右border - 在
box-sizing:content
的时候:clientWidth = width - 左border - 右border + 左padding + 右padding
- 在
- scrollWidth
- 1.在内容区没有发生溢出的情况下,scrollWidth = clientWidth 因为它们都是代表内容区的宽度。
- 2.在内容区发生了溢出,并且设置了 overflow-scroll 之类的属性的情况下,
clientWidth 代表dom 当前状态下,实际上展示在可视区域的 内容区(content) 的宽度,而 scrollWidth 则代表了真实的内容区的宽度,包括了那些没有展现在用户面前的,需要滚动才可以看到的内容的宽度。这时候
scrollWidth= clientWidth + 溢出的内容区的宽度。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ryYjogeo-1678762567154)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/89dfb2a05d4340a2b55f8368c846213f~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp?)]
HTML
HTML & HTML5
- 指代上的区别:
- HTML指的是HTML4.01
- HTML值得是HTML的升级版,是公认的下一代Web语言,可以理解为第五代超文本标记语言标准。
- 文档类型的声明:在文档声明上,html有很长的一段代码,并且很难记住这段代码,而html5却不同,只有简简单单的声明,方便记忆。
<!-- HTMl --> < !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> < html xmlns="http://www.w3.org/1999/xhtml">
<!doctype html>
- HTML5有一些语义标签
<header/> <article/> <footer/> <nav/> <aside/> <audio/> <command/> …
- HTML5为input标签添加了一些属性
placeholder 、 required 、 pattern、 min/max 、 autofocus、 mulitiple
type="number" // 限制用户只能输入数字 type="tel" // 手机号 type="search" // 输入框
- 存储:
sessionStorage
localStorage
- 通信:
webSocket
v-if为什么不能与v-for一起使用
- 优先级不同;v-for的执行优先比v-if要高(vue 2中如此,在vue 3中正好相反)
- 不管循环多少次,都得用v-if去判断,比较耗费性能
- 优化
- 将v-if写在v-for的外面,从而提高v-if的优先级
- 用computed/filters去对v-for循环的数据进行处理
- 优化
v-if与v-show的区别
- 相同点:v-show和v-if都是控制dom元素的显示和隐藏的。
- 不同点:
- 原理:
- v-show是通过控制元素的样式属性display的值,来完成显示和隐藏;
- v-if是通过dom元素的添加和删除,完成显示和隐藏
- 使用场景:由原理(做法)得出使用场景的区别
- v-show:使用在dom元素频繁切换的场景
- v-if:当dom元素的切换不频繁,可以使用。特别是,首次元素处于隐藏的情况下。
- 原理: