vue
-
vue中v-if和v-for的优先级那个更高,应该怎么优化提升性能
1、在vue2.x中v-for的优先级高
2、可以将v-if写到v-for的外面,也可以将数据在computed计算属性中进行过滤,在循环计算方法就可以了
3、在vue3.x中v-if的优先级更高 -
vue中data为什么必须是函数,而vue根实例是对象
image.png
1、组件中data要不是一个函数的话,那么会进行报错
2、在多实例的时候,保护状态不被污染,干扰
3、函数的话,可以让我们每次调用组件都会创建一个新的data实例
4、要是对象的话,组件调用多次,改变一个,都会进行改变触发,导致状态会被污染,干扰
5、根实例跟组件不一样,是单例,每次new vue只会创建一次 -
vue中key的作用和工作原理
image.png
1、key的作用主要是高效的更新虚拟dom,通过key可以精准的判断两个节点是否是同一个,要是没有加key的话,会认为是相同的,从而进行强制的更新,导致多做一个dom的更新,消耗性能
2、不加的话,会触发一些隐蔽的bug,还有不加的话,会报错
3、vue中使用相同标签名元素过渡切换的时候,也会使用到key的属性,目的就是为了让vue可以区分他们,否则vue只会替换内部属性,而不会触发过渡效果 -
你怎么理解vue中的diff算法(需要看看课程中,对diff算法更深的理解)
1、必要性,为了降低组件的watcher,一个组件一个watcher,所以要是用diff
2、深度优先,同层比较(先从根找,有孩子往下找,有的话,继续,没有的话,同层比较,之后在看父节点的,另一边有没有子节点,有往下找,没有同层比较) -
vue组件化的理解
1、高内聚、低耦合,提升代码的健壮性
2、有属性props,自定义事件,插槽等,用于组件通信,扩展等
3、可复用性
4、全局组件和局部组件 -
MVVM
1、解决model和view耦合的问题
2、model发生改变会自动更新view,减少dom操作,提高开发效率,可读性还保持了优越的性能表现 -
vue性能优化有哪些?
1、路由懒加载
2、keep-alive进行缓存页面
3、v-show和v-if合理的使用
4、大数据列表,使用虚拟滚动列表(vue-virtual-scroll,vue-virtual-scroll-list)
5、有定时器的,组件页面销毁前进行清除
6、图片的懒加载
7、第三方UI组件库的按需引入
8、将无状态的UI组件可以标记为函数式组件,在template标签上添加functional就可以了
9、组件中的方法进行调用多次的时候,使用变量将方法进行保存,这样调用多次的时候,我们只用了这一次的方法 -
vue3.0的新特性了解
1、更快:虚拟dom的重写,优化slots的生成,静态树的提升,静态属性的提升,proxy响应式系统
2、更小:摇树优化
3、更容易维护:ts+模块化
4、composition Api -
vuex的使用及其理解
1、模块state mutations getters action modlues(namespace)
-
vue 组件之间的通信方式
1、props
2、on
3、parent
4、provide/inject
5、listeners
6、ref
7、$root
8、eventbus
9、 vuex-
vue-router保护路由的安全
1、获取组件实例只有组件内守卫可以进行获取到,全局守卫,路由独享都是获取不到的
image.png
2、触发顺序
image.png
-
-
vue响应式的理解
1、通过数据响应式加上虚拟DOM和patch算法,可以使我们只需要操作数据就可以达到更新视图,完全不需要操作dom,从而大大提升开发效率,降低开发难度
2、vue2中的数据响应式机制会根据类型来进行不同的处理,如果是对象的话,使用Obiect.defineProperty()的方式定义数据的拦截和响应,如果是数组的话,通过覆盖数组原型的方法,扩展他的7个变更方法,使这些方法额外的做更新通知,可以很好的解决了数据响应式的问题,但是也有一些缺点:比如初始化时递归 遍历时会造成性能损失,新增或者是删除属性时需要使用vue.set/delete这样的api才能生效,对于es6的新增map,,set结构不支持等问题
3、为解决这些问题,vue3中利用es6的proxy机制代理响应化的数据,不在需要使用vue.set/delete等api,初始化性能和内存消耗都得到了大幅改善,另外将响应式的代码单独抽离为独立的reactivity包,可以让我们更加灵活的使用他,我们甚至不要要引入vue就可以进行体验 -
vue的双向数据绑定原理
1、首先通过object.defineProperty()来设置和读取数据
2、通过监听器Observe监听所有的属性,如果属性发生变化了,就需要告诉订阅者是否更新,因为订阅者有很多,此时我们需要Dep专门进行收集订阅者,然后对监听器Objserve和watcher之间进行统一的管理
3、当watcher接收到相应的属性变化,就会进行更新对应的函数
es6的语法总结
-
深浅拷贝
1、引用类型的数据直接赋值就是浅拷贝
2、基本数据类型直接赋值是深拷贝
3、使用Object.prototype.toString.call()可以检测到当前数据类型,适用于所有数据类型的检测,返回'[objec Object]','[objec Array]','[objec String]' 等
4、也可以使用Object.getPrototypeOf()进行检测出当前的数据是对象还是数组
5、使用Object.create(),创建出的对象或者是数组,都是将对方的属性,方法放到了原型上,没有这个属性可以直接从原型上获取,有的话就不会再使用原型中的属性值
6、如果是对象的话,可以使用Object.assign()进行克隆,但是,当前的对象有多层,也就是说对象中还有对象或者是数组,那么里面的对象或者数组就是浅拷贝
7、扩展运算符跟6是一样的,单层是深拷贝,多层嵌套就是浅拷贝了
8、如果是数组的话,可以使用数组中的方法concat,slice等方法,也可以使用Object.assign(),扩展运算符,情况跟6一样 -
函数的变量提升
1、函数的变量提升优先于变量的提升
function test(a,b){
console.log(a);//function a(){}
var a=10;
function a(){}
console.log(a);//10
function c(){}
console.log(b);//undefined
var b=function b(){}
console.log(b)//function b(){}
}
test(1);
var a=121;
console.log(a);//121
/**
* 函数的变量提升:
* 1、函数的变量提升比变量的提升优先级高,在变量的前面
* 2、找形参和变量,相同的只需要一个就可以了,值为undefined
* a:undefined b:undefined
* 3、将实参和形参统一
* a:1 b:没有传递实参,值为undefined
* 4、在函数中找函数,将值赋值给函数体
* a:function a(){} b:由于函数中的b是字面量函数,所以值还是undefined
* 5、在函数的内部开始运行
* a:function a(){}----10
* b:undefined-----function b(){}
*/
var a = 10 ;
function a(){
console.log(a);//function a(){}
a=20;
function a(){}
console.log(a)//20
}
a();
console.log(a)//10
a = 20;
var a = 10;
console.log(a);//10 本来a是全局变量20,然后下面有声明了一下,a=10;所以最终输出为10
-
字符串模板
1、可以进行简单的运算
2、比字符串拼接好使
// todo 字符串模板的使用
const a = 1;
const b = 2;
const c = `${a+b}`
console.log(c);//3
const d = 18;
const e = `小明今年${d}岁`
const f = '小明今年'+d+'岁'
console.log(e);//小明今年18岁
console.log(f);//小明今年18岁
- 判断当前是否有这个字符串
// 判断当前是否有这个字符串
const str = '我现在正在练习es6';
const str1 = '现在'
const str2 = '我'
const str3 = 'es6'
if(str.indexOf(str1)>-1) console.log('indexOf有返回当前下标,没有返回-1---',str.indexOf(str1));//indexOf有返回当前下标,没有返回-1
if(str.includes(str1)) console.log('includes有返回true,没有返回false---',str.includes(str1));//includes有返回true,没有返回false
if(str.startsWith(str2)) console.log('startsWith头部有返回true,没有返回false---',str.startsWith(str2));//startsWith头部有返回true,没有返回false
if(str.endsWith(str3)) console.log('endsWith尾部有返回true,没有返回false---',str.endsWith(str3));//endsWith尾部有返回true,没有返回false
- 字符串重复多次的写法(复制字符串)
// 字符串重复多次
const str = '重复我10次'
console.log(str.repeat(10));//重复我10次重复我10次重复我10次重复我10次重复我10次重复我10次重复我10次重复我10次重复我10次重复我10次
let str1 = ''
for (let i = 0; i < 10; i++) {
str1+=str;
}
console.log(str1);
-
Array.from()
1、可以将对象转化为数组
// 使用Array.from(),将对象转化为数组
const obj = {
0:'key要是用数字',
1:'必须有length这个属性,否则转化不了',
length:2
}
2、可以将伪数组转化为数组
3、可以将set,map,string等数据结构转化为数组
4、第二个参数是一个回调函数,相当于数组的map方法,返回一个新的数组,将第一个参数中的数据进行处理
-
Array.of
1、这个方法不管你是什么数据和类型,都会创建一个新的数组
2、当我们使用new Array(10);进行声明的时候,这个时候数组的长度是10,在进行传递参数的时候,就会产生一些问题
const arr = new Array(10);
console.log(arr);//[empty × 10]
arr.push(20);
console.log(arr);//[empty × 10, 20]
3、此时代码中的数组长度就是11了,在添加的时候,前面都是10个空的
4、而我们使用Aarray.of就可以解决上面的问题了
const arr = Array.of(10);
console.log(arr);//[10]
arr.push(20);
console.log(arr);//[10, 20]
- Array.fill()填充
let arr =[1,2,4,5,6,7];
arr.fill(10);//一个参数默认将数组中的数据全部改变为10
//第一个参数是填充的参数,第二个参数是填充的位置,第三个下标是填充的个数,从下标0开始算起
// 只能是数组内的长度,不能超过数组内的下标或者是长度,
arr.fill('3',0,1);//["3", 2, 4, 5, 6, 7]
arr.fill('3',1,2);//----[1, "3", 4, 5, 6, 7]
arr.fill('3',1,4);//---- [1, "3", "3", "3", 6, 7],第三个参数是4个但是从下标1开始填充,又是从0开始计算
console.log(arr);
-
判断对象或者是数组中是否有某个值
1、对象中的检测某个属性使用in或者是object.hasOwnProperty()都可以检测,但是in是从对象的原型和原型链上查找,不太推荐使用
let obj = {
id:1,
title:'1111'
}
console.log('id' in obj);
console.log(obj.hasOwnProperty('id'));
let arr =[1,2,4,5,6,7];
console.log( 1 in arr);
console.log(arr.indexOf(1)>-1);
console.log(arr.includes(1));
const index = arr.findIndex(item=>item===1)
console.log(index!==-1)
....
- object.is()对象比较
let obj = {id:1,title:'1111'};
let obj2 = {id:1,title:'2222'};
console.log(obj.id === obj2.id);//true
console.log(Object.is(obj.id,obj2.id));//true
-
objec.is和===区别
1、===为同值相等,is()为严格相等
console.log(+0 === -0); //true
console.log(NaN === NaN ); //false
console.log(Object.is(+0,-0)); //false
console.log(Object.is(NaN,NaN)); //true
- reduce简单的计算及数组去重
//reduce 简单的计算,第一个参数是我们传递的初始值,莫有的话,默认是数组的第一个数
//reduce 第二个参数是数组中的每一项
//reduce 第三个参数是数组本身
let arr = [1,1,1,1,2,2,3,4,5,5,6,7,8,9];
// 计算出现的次数
let arr1 = arr.reduce((pre,next)=>{
if(next in pre){//可以将in替换为hasOwnProperty()
pre[next]+=1;
}else{
pre[next] = 1;
}
return pre
},{})
console.log(arr1);//返回的是对象
arr1.length = 10;//给当前的对象添加一个length属性,可以转化为数组
console.log(Array.from(arr1));
// 数组去重
let arr = [1,1,1,1,2,2,3,4,5,5,6,7,8,9];
// 使用双循环进行判断,当前的数值跟下一个数值进行比较,是否相同,相同的话进行去重
for (let i = 0; i < arr.length; i++) {
for (let j = i+1; j < arr.length; j++) {
if(arr[i]===arr[j]){
arr.splice(i,1);
i--;
j--;
}
}
}
console.log(arr);
const arr1 = [...new Set(arr)];//使用new Set 进行去重
console.log(arr1);
let arr2 = arr.reduce((pre,next)=>{
// if(!pre.includes(next)){//当数组中没有这个值的时候进行添加就好
// pre.push(next);
// }
// indexOf()有的话返回下标,没有的话返回-1,所以没有的时候进行添加
if(pre.indexOf(next)===-1){
pre.push(next);
}
return pre;
},[])
console.log(arr2);
// 简单的计算数组中的和
let arr = [1,2,3,4,5,6,7];
let s = arr.reduce((pre,next)=>{
return pre + next
},0)
console.log(s);
- new Proxy()简单的代理
let obj = {
id:1,
title:'练习proxy代理'
}
let obj2 = new Proxy(obj,{
get(target,key){
console.log(`你正在读取obj中的${key}属性`);
// return target[key];//必须要有返回值
return Reflect.get(target,key);//一般我们推荐使用这种映射的方式进行返回
},
set(target,key,val){
console.log(`你正在对obj的${key}进行设置`);
// target[key] = val;
// return target[key];
return Reflect.set(target,key,val);
},
has(target,key){
console.log(`obj中是否有这个${key}属性`);
return Reflect.has(target,key);
},
ownKeys(target){
return Reflect.ownKeys(target);
},
})
console.log(obj2.id);
obj2.name = '张三';
obj2.id = 333;//设置属性
console.log(obj2.name);
console.log(obj2.id);//获取属性
console.log('id' in obj2);//这种方法进行调用has方法
console.log(Object.keys(obj2))//返回所有属性
-
web安全攻击手段有哪些?
1、XSS跨站脚本攻击- 定义:就是攻击者可以在我们的网站中嵌入恶意脚本,在进行分享出去,当用户进行浏览的时候,会被攻击或者是泄漏隐私
-
解决办法:设置httpOnly,防止获取我们的cookie信息。可以将我们页面中的input框等可输入的进行代码,特殊符号的转义,还有就是长度进行限制
2、CSRF跨站请求伪造 -
定义:就是我们使用localhost:3000进行访问,攻击者可以在localhost:3000进行访问我们的数据及恶意的攻击
-
解决办法:使用token进行验证。http头中自定义属性进行验证
-
TCP的三次连接
-
直接举例说明
-
小明——客户端,小红——服务端
1、小明给小红打电话,接通了之后,小明说喂,能听到吗,这就是相当于连接建立了
2、小红给小明回应,能听到,你能听到我说话吗,这就相当于是请求响应
3、小明听到小红的回应后,好的,这相当于是连接确认,在这之后小明和小红就可以通话/交换信息了 -
TCP的四次挥手
-
直接举例说明
1、小明对小红说,我所有的东西说完了,我要挂电话了
2、小红说,收到,我这边还有一些东西没说
3、经过若干秒后,小红也说完了,小红说,我说完了,现在可以挂断了
4、小明收到消息后,又等了若干时间后,挂断了电话 -
地址栏输入URL发生了什么
-
首先输入URL之后,去查找域名是否被本地DNS缓存,解析域名,tcp的连接(三次握手,四次挥手),请求服务器,服务器响应,HTML进行重绘和回流,渲染页面
template 和 JSX的区别
- 首先他们两个都是render的一种表现形式,最终都会编译成render进行渲染
- template更加符合我们的视图,结构分离
- JSX相对于template更加的灵活
SPA单页面的优缺点
- 优点:
1、用户体验好
2、快
3、内容的改变不用重新加载整个页面,避免了不必要的跳转到渲染 - 缺点:
1、首屏加载慢(loading进行解决/骨架屏)
2、对seo不友好
Vue中的data中某一个属性值发生改变,视图会立即更新渲染吗?
- 不会立即同步更新渲染
- vue在更新dom的时候是异步执行的,只要侦听到数据的变化,vue将开启一个队列,并缓冲在同一个事件循环中发生的所有数据的变更
- 如果同一个watcher被多次进行触发,只会被推入到队列中一次。(这种在缓冲时去除重复的数据对于避免不重要的计算和dom操作是非常重要的)
- 然后在下一次的事件循环tick中,vue刷新队列并执行实际的工作(已经进行去重的数据)
vue中的watch,computed和methods的区别
- 首先methods没有缓存,只要调用就会进行触发
- computed 有缓存,只有里面的数据发生了改变,才会进行更新,不支持异步,只支持data,props
- watch没有缓存,支持异步的数据,只要数据发生变化就会触发,有两个参数新老值
如何减少重绘和回流
- 重绘:就是页面的颜色啥的进行改变,不会影响页面的布局
- 回流:就是页面的布局发生改变,需要重新进行计算和渲染
- 使用transfrom替代top等
- 使用visibility替换display:none;(因为前者只会引起重绘,后者会触发回流)
url输入到渲染的过程?
- url解析(是否是合法的http)
- DNS查询(将http解析为ip地址)
- tcp链接(三次挥手,4次握手)
- 向服务器发送请求
- 服务器响应
- 页面进行渲染:
1、解析html,构建dom树
2、解析css,生成css树
3、计算html,css元素的尺寸,位置
4、进行绘制
rsa加密是非对称加密
- 非对称加密:就是需要我们知道公钥和私钥,然后通过算法进行加密
- 对称加密:就是我们只知道公钥,私钥是我们请求的时候进行传递过来的,但是要是被劫持了,那么就会有泄露的风险
- 总结:一般都是使用非对称加密,安全
文件的大小
- 文件的大小我们可以通过file文件中的属性size进行指导,在通过slice进行截取切割,
- 也可以进行判断文件的大小
- 要是文件要判断是不是png图片,使用二进制流读取文件前六位,每个文件都是不同的
Vue组件模板会在某些情况下受到HTML的限制,如在table表格中使用组件是无效的,常见的还有在ul,ol,select中使用组件也是会被限制的
- 我们为了解决在table表格标签中写上我们的组件,使用is属性来进行挂载
- 我们也可以使用is这个属性,进行动态的渲染组件,写一个公共的部分,通过绑定is来实现不同的地方进行渲染组件
<table><thead is="组件名"></thead></table>
插槽
-
默认插槽:
1、就是在组件中可以进行写入一些标签,可以在组建的内部进行渲染
2、直接在组件的内部使用<slot></slot>就可以了 -
具名插槽:
1、就是我们要在不同的地方,显示不同数据,这个时候就要显示了
2、使用name在slot标签上进行起不同的名字
3、在使用组件插槽的时候,在标签上显示不同的名字就行slot='名字' -
作用域插槽:
1、就是子组件传递数据给父组件,然后在子组件中进行显示
2、在子组件的slot标签上动态的绑定我们需要的数据,或者是静态的传递
3、在父组件的子组件标签中使用template标签进行渲染,在template标签上使用v-slot=“随便起一个名字(aa)”,在里面就是aa.传递过来的数据就可以了 -
this.$slot.default 是获取默认的插槽数据
-
this.$slot.head 获取头部slot的数据
vue3.0的语法的一些总结
vue2.0 ======================= vue3.0
beforeCreatec ================ setup
created ====================== setup
beforeMount ================== onBeforeMount
mounted ====================== onMounted
beforeUpdate ================= onBeforeUpdate
updated ====================== onUpdated
beoreDestory ================= onBeforeUnmount
destory ====================== onUmmounted
flex弹性盒模型布局
- display:flex; //申明弹性盒模型,默认水平排列
- flex-direction:row | row-reverse | column | column-reverse //排列的方向,水平,水平倒序, 垂直, 垂直倒序
- flex-wrap; nowrap | wrap | wrap-reverse //默认不换行, 换行, 换行倒序
- flex-flow: <flex-direction> || <flex-wrap> //这是排列和换行的简写 row nowrap 默认水平不换行
- justify-content: flex-start | flex-end | center | space-between | space-around //水平的对齐方式,头部(左边)对齐,尾部(右边)对齐 中间 两端 均匀对齐
- align-item: flex-start | flex-end | center | baseline | stretch //垂直对齐方式, 顶部(上) 尾部 (下) 中间 文字对齐 上下占满空间(没有设置高度或auto,占满上下空间)
- align-content: flex-start | flex-end | center | space-between | space-around | stretch ///这个属性是对多个盒子进行垂直对齐的方式
- order: 1 - 9999999 //数值越小,排列顺序越往前
- flex-grow:0-1 //填充,默认是0,有剩余空间,也不放大
- flex-shrink: 1 // 缩放默认为1,空间不足,可以进行缩小,如果其他都是1,当前为0,其他进行缩小,当前不变
- flex-basis: auto //默认auto 本来的大小, 分配多余空间之前占据的大小
- flex: <flex-grow> || <flex-shrink> || <flex-basis> //三者简写
箭头函数和普通函数的区别
1、写法不同,箭头函数使用()=>{} ,普通函数使用function(){}
2、箭头函数都是匿名函数,普通函数可以是匿名函数也可以是命名函数
3、箭头函数不能使用构造函数,使用new进行声明
4、箭头函数本身是没有this的,声明时可以捕获上下文的this供自己使用,普通函数的this总是指向调用它的对象
5、箭头函数没有arguments对象,普通函数有
6、箭头函数的this永远指向其上下文的this,任何方法都改变不了它的指向,如call,apply,bind都不行
vue和react的相同点和不同点
- 相同点
1、使用虚拟dom
2、都是响应式数据和组件化 - 不同点
1、vue是双向数据绑定
2、react 是单向数据流
3、vue内置dalailng
原生的上拉加载和下拉刷新
-
上拉加载
image.png - 看到上面的图片,大致的意思就是scrollTop+clientHeight >= scrollHeight-触底的距离
let clientHeight = document.documentElement.clientHeight; //浏览器高度
let scrollHeight = document.body.scrollHeight;
let scrollTop = document.documentElement.scrollTop;
let distance = 50; //距离视窗还用50的时候,开始触发;
if ((scrollTop + clientHeight) >= (scrollHeight - distance)) {
console.log("开始加载数据");
}
- 下拉刷新
- 首先要经历3个过程,touchstart,touchmove,touchend,确定用户开始触发时候的y轴距离,移动的距离-开始的距离,结束的时候,在进行请求或者是做一些其他的操作
<main>
<p class="refreshText"></p>
<ul id="refreshContainer">
<li>111</li>
<li>222</li>
<li>333</li>
<li>444</li>
<li>555</li>
...
</ul>
</main>
- touchstart
var _element = document.getElementById('refreshContainer'),
_refreshText = document.querySelector('.refreshText'),
_startPos = 0, // 初始的值
_transitionHeight = 0; // 移动的距离
_element.addEventListener('touchstart', function(e) {
_startPos = e.touches[0].pageY; // 记录初始位置
_element.style.position = 'relative';
_element.style.transition = 'transform 0s';
}, false);
- touchmove
_element.addEventListener('touchmove', function(e) {
// e.touches[0].pageY 当前位置
_transitionHeight = e.touches[0].pageY - _startPos; // 记录差值
if (_transitionHeight > 0 && _transitionHeight < 60) {
_refreshText.innerText = '下拉刷新';
_element.style.transform = 'translateY('+_transitionHeight+'px)';
if (_transitionHeight > 55) {
_refreshText.innerText = '释放更新';
}
}
}, false);
- touchend
_element.addEventListener('touchend', function(e) {
_element.style.transition = 'transform 0.5s ease 1s';
_element.style.transform = 'translateY(0px)';
_refreshText.innerText = '更新中...';
// todo...
}, false);
- vue中父组件监听子组件的生命周期
- 方法一
// Parent.vue
<Child @mounted="doSomething"/>
// Child.vue
mounted() {
this.$emit("mounted");
}
- 方法二
// Parent.vue
<Child @hook:mounted="doSomething" ></Child>
doSomething() {
console.log('父组件监听到 mounted 钩子函数 ...');
},
// Child.vue
mounted(){
console.log('子组件触发 mounted 钩子函数 ...');
},
// 以上输出顺序为:
// 子组件触发 mounted 钩子函数 ...
// 父组件监听到 mounted 钩子函数 ...
- 可以在mounted中监听到beforeDistory生命周期进行清除定时器
- 参考链接https://blog.csdn.net/T_shiyi/article/details/108668263
- 重绘和回流
- 重绘:一般页面中的背景,颜色等变化
- 回流:页面的宽度,高度等发生变化,需要进行重新计算,渲染
- 如何减少回流:
1、用translate替代top改变
2、使用opacity代替visibility
3、不要使用table布局,一个小的改动都需要进行重新计算渲染 - this的指向问题
- 关于这个this的指向问题,要靠自己的理解,不用死记硬背,但是要多注意看题
网友评论