vue
1.什么是动态组件
- 在一个挂载点可以挂载多个组件,并进行切换。
- 在vue中有两种方法动态切换组
component is='active'></component>使用v-if 切换
以上两种方式均可做到组件的动态切换,但是组件切换时每次都会将上一个组件销毁,然后渲染下一个组件,有时候这并不是我们所想的,对于频繁切换的时候会很消耗性能,所以我们可以通过 keep-alive 对组件进行缓存对于不显示的组件不是销毁他,而是使他处于不被激活状态 - 和keep-alive 相关的两个生命周期 activated deactivated
- keep-alive的两个属性
include 表示只能允许匹配到的组件生效
exclude 则相反,除了匹配到的组件之外有效
<!-- 字符串 逗号分隔字符串, a,b 分别为组件名 -->
<keep-alive include="a,b">
<component :is="currentView"></component>
</keep-alive>
<!-- 正则表达式 -->
<keep-alive :include="/a|b/">
<component :is="currentView"></component>
</keep-alive>
<!-- 数组 -->
<keep-alive :include="['a', 'b']">
<component :is="currentView"></component>
</keep-alive>
<keep-alive>
<component :is="currentView"></component>
</keep-alive>
import a from "./a";
import b from "./b";
data(){
return{
currentView:'a'
}
}
method:{
activated() {
console.log("a 页面被添加");// 被缓存的组件激活时触发
},
deactivated() {
console.log("a 页面被移除"); // 被切换到其他组件时触发
}
}
2.函数式组件
Vue 提供了一种称为函数式组件的组件类型,用来定义那些没有响应数据,也不需要有任何生命周期的场景,
它只接受一些props 来显示组件。
3.vue中的路由钩子
1.全局路由钩子
这类钩子主要作用于全局,一般用来判断权限,以及以及页面丢失时候需要执行的操作
-
router.beforeEach
此钩子有三个参数 to from next -
router.afterEach
`此钩子没有next,不能改变导航`
2.路由独享的钩子
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
3.组件内的钩子
-
beforeRouteEnter
不能访问 this,因为钩子在导航确认前被调用,因此即将登场的新组件还没被创建。 不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
- beforeRouteUpdate
- beforeRouteLeave
可以访问this
4.路由传参方式
1.直接通过$router.push来传递参数
此种情况需要在对应的路由下配置参数
{
path: '/about/:id',
name: 'Describe',
component: Describe
}
this.$router.push(`/about/${id}`)
2.通过params传参
此种情况刷新参数会消失,不会在地址栏看到,并且只能通过name来跳转,
this.$router.push({
name:'about',
params:{
id:id
}
})
3.通过query传参
此种情况刷新参数不会消失,并且会在地址栏看到,可以通过name,或者path跳转
this.$router.push({
path:'/about',
query:{
id:id
}
})
5.computed和watch区别
1.computed
a.支持缓存,只有依赖的数据发生了变化才会重新计算。
b.不支持异步,在computed中的异步操作无效,无法监听数据的变化。
c.computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,
也就是基于data中声明过的数据通过计算得到的。
2.watch
a.不支持缓存,数据变,直接会触发相应的操作。
b.支持异步。
c.监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值。
d.深度监听 deep。
e.使用watch时有一个特点,就是当值第一次绑定的时候,不会执行监听函数,只有值发生改变才会执行。
如果我们需要在最初绑定值的时候也执行函数,则就需要用到immediate属性。
6.vue子组件如何获取父组件的异步数据
1.可以在父组件中通过v-if控制,只有当数组成功拿到后再渲染子组件
2.通过在子组件中使用watch监听传过来的值
7.生命周期
1.beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed
2.keep-alive 有自己独立的钩子函数 activated 和 deactivated
JS
1.promise
主要解决异步问题,传统方法一般为回调函数解决异步问题
1.三种状态
- 进行中(pending)
- 成功(fulfilled)
- 失败(rejected)
2.优点
- 有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
- Promise 对象提供统一的接口,使得控制异步操作更加容易。
3.缺点
- 无法取消 Promise,一旦新建它就会立即执行,无法中途取消。
- 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
- 当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
4.promise.all()
Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。但是只要有一个失败了,就会返回失败,并且为第一个失败的promise的结果。
5.promise.race()
Promise.race([p1, p2,p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
2.this 问题
1.this是什么
this是指包含它的函数作为方法被调用时所属的对象。
谁特么的调用了我,我就是谁的对象,谁特么的最后调用了我,我就是谁的对象。
function showName(){
console.log(this); //window
}
showName();
function testFun () {
var name="hello this!";
console.log(this.name);
}
testFun();
/**
*此时打印不出,因为此时的this指向的是window
*/
var name = "hey";
function testFun() {
var name = "hello this!";
console.log(this.name);//hey
}
testFun();
var obj={
a:"哈哈哈",
b:function(){
var a="嘿嘿嘿";
console.log(this.a);
}
};
obj.b();
/**
*因为此时是obj调用的b 所以此时this属于obj 打印 '哈哈哈'
*/
2.箭头函数中的this
- 箭头函数中的this是在定义函数时绑定,普通函数是在执行函数时绑定,
- 箭头函数的this绑定看的是this所在的函数定义在哪个对象下,绑定到哪个对象则this就指向哪个对象
- 如果有对象嵌套的情况,则this绑定到最近的一层对象上
3.原型与原型链
- 所有引用类型(数组、函数、对象)都可以自由扩展属性(null除外)
- 所有的引用类型都有一个 _ proto _ 属性(隐式原型)
- 所有的函数都有一个 prototype 属性(显示原型)
- 所有的引用类型的 _ proto _ 属性指向他的构造函数的prototype属性
- 当寻找一个对象的某个属性时,如果这个对象本身不存在该属性,则会在该对象的 _ _ proto _ _ 属性中去查找(也是该对象的构造函数的prototype中查找)
4.继承
- 原型继承
子类的原型对象指向父类的实例;即将父类挂载到子类的原型上。 优点: 可以继承父类所有属性和方法 缺点: 1.子类无法向父类传参; 2.如果要给子类的原型上新增属性和方法,就必须放在Child.prototype = new Parent()这样的语句后面; 3.父类原型链上的属性会被多个实例共享,这样会造成一个实例修改了原型,其他的也会改变。 - 构造函数继承
在子类构造函数内部使用call/apply来调用父类构造函数。 * 通过call()、apply()或者bind()方法直接指定this的绑定对象, 如foo.call(obj) * 使用.call()或者.apply()的函数是会直接执行的 * 而bind()是创建一个新的函数,需要手动调用才会执行 * call()和.apply()用法基本类似,不过call接收若干个参数,而apply接收的是一个数组 function Child () { Parent.call(this, ...arguments) } 优点: 1.保证了原型链中引用类型值的独立,不再被所有实例共享; 2.子类可以向父类传递参数 缺点:构造函数继承只能继承父类的属性和方法,无法继承父类的原型。 - 组合继承
使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承. function Parent(name, sex){ this.name = name; this.sex = sex; this.hobby = ['游泳','跑步']; this.desc = '我是描述'; console.log('我是parent') } Parent.prototype.getInfo=function(){ console.log(this.name, this.sex) } function Child (name, sex){ //继承父类构造函数的属性和方法 Parent.call(this, name, sex) } //继承父类的原型 Child.prototype = new Parent(); var lisa = new Child('lisa','girl'); console.log(lisa) lisa.getInfo() //lisa girl 优点: 1.可以继承父类实例属性和方法,也能够继承父类原型属性和方法; 2.弥补了原型链继承中引用属性共享的问题; 3.可传参,可复用 缺点: 1.使用组合继承时,父类的构造函数被调用了两次。 2.生成了两个实例,子类实例中的属性和方法会覆盖子类原型(父类实例)上的属性和方法, 所以增加了不必要的内存 - 寄生组合式继承
原理: 使用Object.create(proto, [propertiesObject])方法创建一个新的对象。 proto参数必须,代表新建对象的原型对象, function Person(desc){ this.color = ['red']; this.desc = desc; console.log('哈哈') } Person.prototype.getName = function(){ console.log(this.name); } Child.prototype = Object.create(Person.prototype); function Child(name, age, desc) { this.name = name; this.age = age; Person.call(this,desc); } const Jack = new Child('Jack', 23, '我是Jack'); Jack.color.push('pink') const Iric = new Child('Iric', 20, '我是Iric'); Iric.color.push('orange') console.log(Jack); Jack.getName(); console.log(Iric); Iric.getName() 优点: 1.公有的写在原型; 2.私有的写在构造函数; 3.可以向父类传递参数; 4.不会重复调用父类 缺点:需要手动绑定 constructor (如果重写 prototype) - ES6继承
Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多 主要利用class配合extends与super实现继承; class Person{ constructor(name, age){ this.name = name; this.age = age; this.color = ['red']; } getName(){ console.log(this.name); } } class Child extends Person{ constructor(name, age){ super(name, age) } } const Jack = new Child('Jack',20); const Iric = new Child('Iric',23); Jack.color.push('pink'); Iric.color.push('orange'); Jack.getName(); Iric.getName(); console.log(Jack); console.log(Iric);
5.new 做了些啥
1.new命令的作用执行构造函数,返回一个实例对象。
2.使用new命令经历的步骤
(1).创建一个空对象,作为将要返回的对象实例;
(2).将这个空对象的原型指向构造函数的prototype属性;
(3).绑定this指向(将构造函数的作用域赋给新对象,因此 this 就指向了这个新对象);
(4).执行构造函数(为这个新对象添加属性);
(5).返回新对象(确保返回的是对象).
6.for循环中的var和let声明
for(var i=0,i<5,i++){
setTimeout(()=>{console.log(i)},0) // 5,5,5,5,5
}
for(let i=0,i<5,i++){
setTimeout(()=>{console.log(i)},0) // 0,1,2,3,4
}
之所以会这样,因为在退出循环时,迭代变量保存的是导致循环退出的值:5;在之后执行超时逻辑时,所有的i都是同一个变量。 而在使用let声明迭代变量时,JavaScript引擎在后台会给每个迭代循环声明一个新的迭代变量,每个setTimeout引用的都是不同的变量实例
7.防抖、节流
1.防抖
防止抖动,单位时间内事件触发会被重置,避免事件被误伤触发多次。 防抖重在清零 clearTimeout(timer)
场景:
- 登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖
- 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖
- 文本编辑器实时保存,当无任何更改操作一秒后进行保存
function debounce (f, wait) {
let timer
return (...args) => {
clearTimeout(timer)
timer = setTimeout(() => {
f(...args)
}, wait)
}
}
2.节流
控制流量,单位时间内事件只能触发一次,与服务器端的限流 (Rate Limit) 类似。 节流重在加锁
场景:
- scroll 事件,每隔一秒计算一次位置信息等
- 浏览器播放事件,每个一秒计算一次进度信息等
- input 框实时搜索并发送请求展示下拉列表,每隔一秒发送一次请求 (也可做防抖)
function throttle (f, wait) {
let timer
return (...args) => {
if (timer) { return }
timer = setTimeout(() => {
f(...args)
timer = null
}, wait)
}
}
8.执行上下文
执行上下文类型:
1.全局执行上下文; 2.函数执行上下文; 3.eval函数执行上下文
this绑定
在全局执行上下文中,this 的值指向全局对象。(在浏览器中,this引用 Window 对象)。 在函数执行上下文中,this 的值取决于该函数是如何被调用的。如果它被一个引用对象调用,那么this会被设置成那个对象,否则this的值被设置为全局对象或者 undefined(在严格模式下)
9.闭包
闭包是一个可以访问外部作用域的内部函数,即使这个外部作用域已经执行结束。
当外部作用域执行完毕后,内部函数还存活(仍在其他地方被引用)时,闭包才真正发挥其作用。
应用场景
- setTimeout
原生的setTimeout传递的第一个函数不能带参数,通过闭包可以实现传参效果
//通过闭包可以实现传参效果
function func(param){
return function(){
alert(param)
}
}
var f1 = func(1);
setTimeout(f1,1000);
- 回调
我们定义行为,然后把它关联到某个用户事件上(点击或者按键)。我们的代码通常会作为一个回调(事件触发时调用的函数)绑定到事件上
<body>
<p>哈哈哈哈哈哈</p>
<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>
<script>
function changeSize(size){
return function(){
document.body.style.fontSize = size + 'px';
};
}
var size12 = changeSize(12);
var size14 = changeSize(14);
var size16 = changeSize(16);
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
</script>
</body>
- 封装变量
如创建一个计数器
在返回的对象中,实现了一个闭包,该闭包携带了局部变量x,并且,从外部代码根本无法访问到变量x。换句话说, 闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来。
function create_counter(initial) {
var x = initial || 0;
return {
inc: function () {
x += 1;
return x;
}
}
}
var c1 = create_counter();
c1.inc(); // 1
c1.inc(); // 2
c1.inc(); // 3
var c2 = create_counter(10);
c2.inc(); // 11
c2.inc(); // 12
c2.inc(); // 13
10.拷贝
浅拷贝----只复制引用,而未复制真正的值;深拷贝----对目标的完全拷贝,不像浅拷贝那样只是复制了一层引用,就连值也都复制了。
- 复制运算符 属于浅拷贝
- JavaScript 中数组和对象自带的拷贝方法都是“首层浅拷贝”;如对象的
JSON.stringify/parse,数组有两个方法concat 和 slice以及...扩展运算符 JSON.stringify/parse,对于对象中存在undefined、function、symbol会在转换过程中被忽略。- 真正的深拷贝请用递归
function deepClone(source){
const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
for(let keys in source){ // 遍历目标
if(source.hasOwnProperty(keys)){
if(source[keys] && typeof source[keys] === 'object'){ // 如果值是对象,就递归一下
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
}else{ // 如果不是,就直接赋值
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}
11.原生操作dom和bom
1.dom
document.createElement(tagName) 创建元素
paranetElement.appendChild(child) 添加到节点,子节点的最后
paranetElement.insertBefore(newElement, Element) 添加到节点的前面
Element.innerHTML 修改DOM的文案
paranetElement.removeChild(element)删除DOM
2.bom
window.alert
prompt() 显示可提示用户输入的对话框
confirm() 显示一个带有提示信息、“确定”和“取消”按钮的对话框
open() 打开一个新的浏览器窗口,加载给定URL所指定的文档
location对象等
12.es6中的class
浏览器
1.输入url 发生了什么
- 首先进行url解析,根据dns系统进行IP查找;
- 根据IP找到服务器,然后浏览器和服务器进行三次握手建立连接,如果是https还会建立TLS连接以及协商加密算法;
- 连接建立后浏览器开始发送请求获取文件(后面步骤当作不走缓存)
- 首先获取html文件,构建dom树,这个过程是边下载边解析;
- 解析HTML头部的时候(当作css放头部,js放尾部),发现有css文件,此时下载css文件(css也是边下载边解析),构建cssom树,当dom树和cssom树全部构建完成之后,浏览器会将dom树和cssom树构建成渲染树(包含样式计算:主要为DOM树上的节点找到对应的样式);
- 构建布局树,样式计算完成之后就开始构建布局树,主要是为DOM树上的节点找到页面上对应位置以及一些"display:none"元素的隐藏;
- 构建分层树,主要为了满足滚动条,z-index,position这些复杂的分层操作;
- 将分层树图块化,利用光栅找到视图窗口下的对应的位图。主要是因为一个页面可能很长,一下渲染出来比较浪费,所以浏览器会找到视图窗口对应的图块,将这部分的图块进行渲染;
- 最终渲染进程将整个页面渲染出来,在渲染过程中还会出现重排和重绘。
2.浏览器如何管理内存,清除变量,垃圾回收
网络
1.304
该状态码表示客户端发送附带条件的请求时,服务器端允许请求访问资源,但未满足条件的情况。304 状态码返回时,不包含任何响应的主体部分。304 虽然被划分在 3XX 类别中,但是和重定向没有关系。
状态码304并不是一种错误,而是告诉客户端有缓存,直接使用缓存中的数据。返回页面的只有头部信息,是没有内容部分的,这样在一定程度上提高了网页的性能。
1.强缓存
强缓存的控制字段:HTTP1.0:Expires;HTTP1.1 :Cache-Control
判断过程:请求再次发起 -> 浏览器根据 expires 和 cache-control 判断目标资源是否命中"强缓存" -> 若命中,直接从缓存获取资源,不再与服务器发生通讯。
Cache-Control取值:
public:所有内容都将被缓存(客户端和代理服务器都可缓存)
private:所有内容只有客户端可以缓存,Cache-Control的默认取值
no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定
no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
max-age=xxx (xxx is numeric):缓存内容将在xxx秒后失效
must-revalidate: 强制浏览器严格遵守你设置的cache规则
proxy-revalidate: 强制proxy严格遵守你设置的cache规则
2.协商缓存
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况:
协商缓存生效,返回304
协商缓存失效,返回200和请求结果。
协商缓存的控制字段:
HTTP1.0:Last-Modified && If-Modified-Since
HTTP1.1 :Etag && If-None-Match
2.http和https
1.http
超文本传输协议,是一个基于TCP实现的应用层协议。HTTP由请求和响应构成,是一个标准的客户端服务器模型(B/S)。HTTP协议永远都是客户端发起请求,服务器回送响应。HTTP是一个无状态的协议。无状态是指客户机(Web浏览器)和服务器之间不需要建立持久的连接,这意味着当一个客户端向服务器端发出请求,然后服务器返回响应(response),连接就被关闭了,在服务器端不保留连接的有关信息.HTTP遵循请求(Request)/应答(Response)模型。客户机(浏览器)向服务器发送请求,服务器处理请求并返回适当的应答。所有HTTP连接都被构造成一套请求和应答。
2.https
超文本传输安全协议。HTTPS是在HTTP上建立SSL加密层,并对传输数据进行加密,是HTTP协议的安全版。
主要作用: 1.对数据进行加密,并建立一个信息安全通道,来保证传输过程中的数据安全;2.对网站服务器进行真实身份认证
区别:
项目优化
1.使用webpack对项目进行的优化
CSS
1.清除浮动
- 结尾出添加空div,{clear:both;overflow:hidden;height:0}
- 伪类同上 zoom:1 解决ie问题
- 父级定义高度
- 结尾添加 br
2.rem原理
等比缩放
3.有哪些写动画的方式
- animation
- Lottie 动画
在Web 端其本质是基于 SVG 或 Canvas,使用 JS 进行开发的动画实现 - transition