数据类型来分有基本数据类型和引用数据类型:
基本数据类型:String、Number、Boolean、Null、Undefined、Symbol、BigInt
引用数据类型:Object【Object是个大类,function函数、array数组、date日期...等都归属于Object】
什么是双向绑定
Object.defineProperty(obj, prop, descriptor) 方法会直接在一个对象上定义一个新属性
原理是通过数据劫持+发布订阅模式相结合的方式来是实现的,简单来说就是数据层发生变化的时候,可同布更新视图层,当视图层发生变化的时候,同步更新数据层
1、vue的传参方式;vue混入。
答:
vue的传参方式
1、利用路由的name属性来传参,通过“$route.name”来接收参数;
2、通过router-link中的to属性来传参;
3、使用path来匹配路由组件,采用url来传参。
vue混入。
1.什么是混入
- 混入 (mixin) : 是一种分发Vue组件中可复用功能的非常灵活的一种方式。混入对象(mixins)是一个js对象,它可以包含我们组件中script项中的任意功能选项,如data、components、created、methods 、computed、watch等等。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
2.创建Mixins
- 在src目录下创建一个mixins文件夹,文件夹下新建自己要定义的混入对象js文件。使用对象的形式来定义混入对象,在对象中可定义与vue组件一样的data、components、created、methods 、computed、watch等属性,并通过export导出该对象.
2、css的计算属性
calc(百分比 - 像素)
注意加空格百分比和像素和减号之间
3、js计算宽高
- innerWidth/innerHeight 获取窗口的宽度和高度 出现滚动条时也是计算在内的,窗口指的是window,所以一般时window.innerWidth/innerHeight。
- clientWidth/clientHeight 获取文档的可视区宽高,不计算滚动条,不计算border线,文档一般指的是documet.documentElement(html).clientWidth/clientHeight,但是也可以用在一般元素上
clientWidth = padding(左、右) + width
clientHeight = padding(上、下) + height
4、路由传参,怎么调整响应式布局;
@media screen and (max-width: 800px) 媒体查询的最简单用法
把涉及到宽度的div的width,全部改成百分比最外围的div的宽度设置为width:100%; 比如left:30%;right:60%;
5、forEach map那个性能好;forEach可以用break跳出循环
- 如果只谈性能,显然是 for > forEach > map
- forEach:函数签名中包含了参数和上下文,性能低于 for 循环。
- map:map会返回一个新的数组,数组的创建和赋值会导致分配内存空间,因此会带来较大的性能开销。
js 的 forEach里是不能使用break。
forEach 、map通过抛出异常的方式跳出循环 通过return跳过当次循环
var arr = [1,2,3]
var newArr = []
arr.forEach((item,index)=>{
try{
if(index > 1) {
throw new Error('文本小于2')
}
newArr.push(item)
}catch (e){
// throw e
}
})
console.log(newArr) [1,2]
6、promise.all方法;
const databasePromise = connectDatabase();
const booksPromise = databasePromise
.then(findAllBooks);
const userPromise = databasePromise
.then(getCurrentUser);
Promise.all([
booksPromise,
userPromise
])
.then(([books, user]) => pickTopRecommendations(books, user));
上面代码中,booksPromise
和 userPromise
是两个异步操作,只有等到它们的结果都返回了,才会触发 pickTopRecommendations
这个回调函数。
7、新语言es6新增了那些语法;
- 新增声明变量方式let和const
- 新增解构赋值
- 新增了一些数组方法 字符串方法 正则表达的方法 函数的一些写法 对象的方法
- Promise
- Async await
- class以及继承
- 模块化
- 新的数据类型 set类型,map类型,symbol类型
- 箭头函数
- for in;for of
- 模板字符串 `${ }`
8、数据可视化;
echarts
9、跨域问题,js vue 获取dom的方法;
当一个请求url的协议,域名,端口三者之间任意一个与当前的url不同都即为跨域
JSONP,代理服务器
10、防抖节流的区别
- 防抖触发高频率事件时n秒后只会执行一次,如果n秒内再次触发,则会重新计算。
简单概括:每次触发时都会取消之前的延时调用。
- 节流是高频事件触发,每次触发事件时设置一个延迟调用方法,并且取消之前延时调用的方法。
简单概括:每次触发事件时都会判断是否等待执行的延时函数。
防抖和节流本质是不一样的。防抖是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段事件执行
11、echarts在create渲染需要做什么
12、typeof和instanceof的区别
- typeof
- 用于判断数据类型,返回值有number、string、boolean、function、undefined、object 六个。
- instanceof
- instance的意思是实例,因此可以知道instanceof的作用就是判断该对象是谁的实例,我们也就知道了instanceof是对象运算符。
总之,typeof 和 instanceof 都是用来判断变量类型的,区别在于:
1、typeof判断所有变量的类型,返回值有number、string、boolean、function、object、undefined。
2、typeof对于丰富的对象实例,只能返回object,导致有时候得不到真实的数据类型。
3、instanceof用来判断对象,代码形式(obj1 instanceof obj2)(判断obj1是否为obj2的实例),obj2必须为对象,否则会报错。返回的是布尔值。
4、instanceof可以对不同的实例对象进行判断,判断方法是根据对象的原型链依次向下查询,如果obj2的原型属性存在于obj1的原型链上,(obj1 instanceof obj2)值为true。
13、vue3的问题,pc兼容问题
优雅降级
-ms-:IE浏览器私有属性
——Trident(MSHTML)内核:由微软开发的排版引擎。
-moz-: 火狐浏览器私有属性
——Gecko内核,moz代表的是Firefox的开发商Mozilla。开源内核,代码完全公开,可开发程度很高。
-webkit-:苹果、微软和谷歌浏览器前私有属性
——Webkit内核:开源内核。Chrome内核原型,是苹果公司自己的内核。
-o-:欧朋(Opera)浏览器私有属性
——Presto内核(已废弃):Opera前内核,在2003年的Opera中首次被使用至Opera12.17前
——Blink内核:是一个由Google和Opera Software开发的浏览器排版引擎,在Chrome(28及以后版本)、Opera(15及以后版本)和Yandex浏览器中使用。
14、es7
是ECMAScript 7
15、watch监听、flex布局、父子传值、兄弟传值、计算属性和监听的区别
- watch监听
- watch监听属性监听的是data中已经存在的变量,只有当变量值发生改变时,会触发watch监听属性中的相应的方法。
- 当需要在数据变化时执行异步或开销较大的操作时,这时watch是非常有用的
- watch只能监听简单数据类型,当监听对象、数组等复杂数据类型时,其中的元素值改变也不会触发watch中监听对象的方法。
- 但是可以采用深度监听来监听对象的变化,设置deep: true,就可以解决特点3的问题来监听某个对象的属性,但是当对象嵌套的属性太深时开销太大。
<!DOCTYPE html>
<html lang="en">
<meta charset="utf-8">
<head>
<title>侦听器</title>
</head>
<body>
<div id="app">
<div>
<span>名:</span>
<span><input type="text" name="" v-model="lastname" @input="handle"></span>
</div>
<div>
<span>性:</span>
<span><input type="text" name="" v-model="firstname" @input="handle"></span>
</div>
<div>
{{getFullname}}
</div>
<div>{{obj}}</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vue = new Vue({
el: "#app",
data: {
fullname: '',
firstname: '',
lastname: '',
obj: {
name: "wh",
age: 23
}
},
methods: {
handle: function () {
this.fullname = this.firstname + this.lastname;
// 每次输入都让obj对象的age自增,对象的属性值的变化是不能触发obj监听方法的
this.obj.age++
// 注意!!!!!!!!只有当这个obj整个对象值改变时才会触发obj的watch监听方法
// this.obj = { mes: "值已经改变" }
},
},
//加入监听器,监听器会一直监听属性的变化,一旦变化,就会执行对应的方法
watch: {
//val表示firstname的当前的最新值,只要值发生变化就会触发这个方法
firstname: function (val) {
this.fullname = val + ' ' + this.lastname;
},
lastname: function (val) {
this.fullname = this.firstname + ' ' + val;
},
// 不采用深度监听不能够监听到对象属性值的改变
// obj: function (val) {
// console.log(val);
// },
// 采用深度监听来监听对象属性值的改变
obj: {
// handler为固定属性,不能改变名字
handler: function (val) {
console.log(oldval);
console.log(newVal);
},
// immediate: true代表在watch对象中声明了obj这个监听属性后会立即执行handler方法,在正常情况下只有当obj对象属性值改变时才会触发handler
immediate: true,
// 是否深度监听,默认值为false
deep: true
},
},
//添加计算属性实现
computed: {
getFullname: function () {
return this.firstname + this.lastname;
}
},
});
</script>
</body>
</html>
- computed属性
- computed具有缓存功能,当与computed变量相关的变量值不发生改变时,一直用的是缓存中的值,只有当依赖变量值发生改变时,computed计算属性值才会发生更新;
- computed变量不在data中定义,而是在computed对象中定义;
flex布局
display: flex;
display: inline-flex;
- flex-direction
row | row-reverse | column | column-reverse;
- flex-wrap
nowrap | wrap | wrap-reverse;
- flex-flow
<flex-direction> || <flex-wrap>;
- justify-content
flex-start | flex-end | center | space-between | space-around;
- align-items
flex-start | flex-end | center | baseline | stretch;
- align-content
flex-start | flex-end | center | space-between | space-around | stretch;
父子传值
- props
子传父
- $emit(事件名,参数)
父组件调用子组件事件
- <child ref="childfn"></child>
- this.$refs.childfn.childMethod()
兄弟传值
- 新建文件bus.js引入后使用
- bus.$emit('name', data)
- bus.$on('name', (data) => {console.log(data, '上车成功');})
- this.$bus.$off('name');
16、前置守卫和后置守卫、请求拦截器
//全局前置守卫
router.beforeEach((to,from,next) =>{
//第一个参数to,包含的内容是切换后的路由对象,也就是跳转后的路由对象
//第二个参数from,包含的内容的是切换前的路由对象,也就是跳转前的路由对象
//第三个参数next(),是否往下执行,执行的话,如果不写的话路由就不会跳转,操作将会终止
})
//全局后置守卫
router.afterEach((to,from) =>{
//第一个参数to,包含的内容是切换后的路由对象,也就是跳转后的路由对象
//第二个参数from,包含的内容的是切换前的路由对象,也就是跳转前的路由对象
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter(to,from,next){
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave(to,from,next){
}
请求拦截器
// 添加请求拦截器
// 所有的请求都会到这里来,会去执行第一个参数,同时自动传入config
axios.interceptors.request.use(function (config) {
const tokenStr = localStorage.getItem('tokenStr')
if (tokenStr) { // 如果本地有token,给它加上请求头
config.headers.Authorization = `Bearer ${tokenStr}`
}
return config
}, function (error) {
// Do something with request error
return Promise.reject(error)
})
// 响应拦截器
request.interceptors.response.use(function (response) {
console.log('响应拦截器', response)
return response
}, function (err) {
console.dir(err)
return Promise.reject(err) //抛出错误,把错误抛给谁? 谁调用它发请求就给谁
})
17、vue父子组件生命周期执行顺序
vue组件的生命周期
beforeCreate->created ->beforeMount ->mounted ->beforeUpdate ->updated -> beforeDestroy ->destroyed
1、挂载阶段
执行顺序为:
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
2、更新阶段
执行顺序为:
父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated
3、销毁阶段
执行顺序为:
父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed
18、import require 区别 v-if v-show webpack vite 区别
node编程中最重要的思想就是模块化,import 和 require 都是被模块化所使用。在 ES6 当中,用 export 导出接口,用 import 引入模块。但是在 node 模块中,使用module.exports导出接口,使用 require 引入模块。
两者的区别如下:
遵循规范:
- require 是 AMD 规范引入方式
- import是 ES6 的一个语法标准,如果要兼容浏览器的话必须转化成 ES5 的语法
调用时间:
- require是运行时调用,所以require理论上可以运用在代码的任何地方
- import是编译时调用,所以必须放在文件开头
v-if v-show区别
- v-show指令设置隐藏是给绑定的DOM元素添加CSS样式:display:none,但是DOM元素仍然存在;
- v-if指令设置隐藏是将DOM元素整个删除,此时DOM元素不再存在。
webpack vite 区别
- vite服务器启动速度比webpack快,由于vite启动的时候不需要打包,也就无需分析模块依赖、编译,所以启动速度非常快。当浏览器请求需要的模块时,再对模块进行编译,这种按需动态编译的模式,极大缩短了编译时间,当项目越大,文件越多时,vite的开发时优势越明显。vite热更新比webpack快,vite在HRM方面,当某个模块内容改变时,让浏览器去重新请求该模块即可,而不是像webpack重新将该模块的所有依赖重新编译。
- Vite的使用简单,只需执行初始化命令,就可以得到一个预设好的开发环境,开箱即获得一堆功能,包括:CSS预处理、html预处理、异步加载、分包、压缩、HMR等。使用复杂度介于Parcel和Webpack的中间,只是暴露了极少数的配置项和plugin接口,既不会像Parcel一样配置不灵活,又不会像Webpack一样需要了解庞大的loader、plugin生态,灵活适中、复杂度适中。
19、业务开发流程,项目中遇到过什么问题,权限控制怎么做的,用过vue3吗,vue组件通信
权限控制
1 、菜单控制
- 权限数据需要在多组件之间共享,因此采用vuex。
- 防止刷新界面权限数据丢失,所以需要存储在sessionStorage,并且保证两者同步。
2、 界面控制
- 路由的导航守卫可以防止跳过登录界面。
- 动态路由可以让不具备权限的界面路由就不存在。
3 、按钮控制
- 路由规则中可以增加路由的原数据meta
- 通过路由对象可以得到当前的路由规则,以及存储在此规则中的meta数据
- 自定义指令可以很方便的实现按钮控制
4 、请求和响应控制
- 请求拦截器和响应拦截器的使用
- 请求方式的约定restful
20、mvvm,传参方式,query和params的区别
- mvvm
- Model —— MVVM 中的 model 层和 MVC 中的 model 层非常相似,包含了供应用正常运转所需的基本数据。
- View —— view 层是用户和设计模式之间的图形界面,类似于 MVC 中的 view 层。用来展示处理后的数据。
- View-Model —— view-model 既是视图层的抽象,又提供了要访问的模型数据的包装。 也就是说,它包含一个可以被转换为视图的模型,并且还包含了一些命令,视图层可以使用这些命令更改模型。
2.query和params的区别
- 两者中query在刷新页面的时候参数不会消失 但params在刷新页面的时候参数会消失 可以考虑本地存储解决此问题
- query传过来的参数会显示到地址栏中 而params传过来的参数不会显示到地址栏中 直白的来说 query相当于get请求,而params相当于post请求
21、vue计算属性,能不能给计算赋值?数据结构的,堆栈,二叉树一类的,宏任务,深浅拷贝的区别,页面传值。
嘉兴:js数据类型,null和undefined的区别,深浅拷贝,闭包,原型,原型链,箭头函数和普通函数的区别,==和===有什么区别,vue3和vue2的区别,判断数据类型的方法,跨域的原因,解决方法,一个页面从输入url到页面加载显示完成,这个过程都发生了什么,路由,权限管理,小程序
数组的常用方法
// 该方法可以将数组里的元素,通过指定的分隔符,以字符串的形式连接起来。
// 返回值:返回一个新的字符串
arr.join()
// 该方法是用过指定的分隔符,将字符串分割成数组。
// 返回值:返回一个新的数组
arr.split()
arr.push(值)
arr.pop()
arr.unshift(值)
arr.shift()
arr.reserse()
arr.sort()
arr1.concat(arr2)
arr.slice(0,3)
arr.splice(start,deletedCount)
arr.indexOf(元素)
arr. lastIndexOf()
// ES5新增的遍历数组方法
forEach( )
map( )
filter( )
some()
every()
arr4.find()
arr4.findIndex()
- join (原数组不受影响)
该方法可以将数组里的元素,通过指定的分隔符,以字符串的形式连接起来。
返回值:返回一个新的字符串
//将数组用 - 符号连接起来
let arr = [1,2,3,4,5];
let str = arr.join('-');
console.log(str)//str = 1-2-3-4-5;
- split (原数组不受影响)
该方法是用过指定的分隔符,将字符串分割成数组。
返回值:返回一个新的数组
let str = wqz-ttj;
let arr = str.split('-');
console.log(arr);// arr=['wqz','ttj'];
数组的增删操作(直接改变原数组)
- push
该方法可以在数组的最后面,添加一个或者多个元素
结构: arr.push(值)
返回值:返回的是添加元素后数组的长度.
- pop
该方法可以在数组的最后面,删除一个元素
结构: arr.pop()
返回值:返回的是刚才删除的元素.
- unshift
该方法可以在数组的最前面,添加一个或者几个元素
结构: arr.unshift(值)
返回值: 返回的是添加元素后数组的长度
- shift
该方法可以在数组的最前面,删除一个元素
结构: arr.shift()
返回值: 返回的是刚才删除的元素.
数组的翻转和排序(改变数组)
- reverse 翻转数组
结构:arr.reserse()
- sort
该方法可以对数组进行排序.
let arr = [1,3,5,2,4,23,122,34];
//没有参数:时按照首字符的先后排序
arr.sort()//arr=[1,122,2,23,3,34,4,5];
//有参数
arr.sort(function(a,b){
return a-b;//从小到大排序
return b-a;//从大到小排序
})
数组的拼接与截取(原数组不受影响)
- concat
该方法可以把两个数组里的元素拼接成一个新的数组
返回值: 返回拼接后的新数组
let arr1 = [1,2,3];
let arr2 = [4,5,6];
let arr = arr1.concat(arr2);//arr = [1,2,3,4,5,6];
arr1.push(arr2);//arr1 = [1,2,3,[4,5,6]];
该方法和push的区别: push是直接把后一个元素原封不动的添加到第一个数组的后面;
- slice 截取 出来
该方法可以从数组中截取指定的字段,返回出来
返回值:返回截取出来的字段,放到新的数组中,不改变原数组
结构1:arr.slice(start,end) ;从start下标开始截取,一直到end结束,不包括end
let arr = [0,1,2,3,4,5,6,7];
let newArr = arr.slice(0,3)//newArr = [0,1,2];
结构2:
arr.slice(start) ;从start下标开始截取,一直到最后
let arr = [0,1,2,3,4,5,6,7];
let newArr = arr.slice(2)//newArr = [2,3,4,5,6,7];
结构3:
arr.slice( ) ;全部截取
let arr = [0,1,2,3,4,5,6,7];
let newArr = arr.slice(2)//newArr = [0,1,2,3,4,5,6,7];
删除或增加元素(任意在任何位置,直接改变原数组,没有返回值)
- splice
结构1: arr.splice(start,deletedCount) 纯删除
从start下标开始,删除几个
结构2: arr.splice(start,deletedCount,item) 替换
从start下标开始,删除几个,并在该位置添加item
结构3: arr.splice(start,0,item) 纯添加
从start下标开始,删除0个,并在该位置添加item,start开始全部往后移动
let arr = [1,2,6,7,8];
arr.splice(2,0,3,4,5);//arr = [1,2,3,4,5,6,7,8];
查找元素在数组中出现的位置
- indexOf
该方法用来查找元素在数组中第一次出现的位置
结构:
arr.indexOf(元素)
特殊用法:
(1) arr.indexOf (ele,fromIndex),从fromIndex这个下标开始,元素第一次出现的位置
用来判断元素是否存在于数组中!
if (arr.indexOf(ele) === -1){//说明元素不存在!!
console.log('元素不存在!)
} else {
console.log(' 元素存在! ')
}
- lastIndexOf
该方法用来查找元素最后一次在数组中出现的位置
ES5新增的遍历数组方法
- forEach( )
该方法等同于for循环,没有返回值
用法:
arr.forEach(function(item,index,arr){
//里面的function是一个回调函数,
//item: 数组中的每一项;
//index:item 对应的下标索引值
//arr: 就是调用该方法的数组本身
})
- map( )
映射,该方法使用和forEach大致相同,但是该方法有返回值,返回一个新数组,新数组的长度和原数组长度相等
//里面的function是一个回调函数,
//item: 数组中的每一项;
//index:item 对应的下标索引值
//arr: 就是调用该方法的数组本身
用法:
let arr = [1,32,54,6,543];
let res = arr.map(function(item,index,arr){
return item*2;
})
- filter( )
filter方法: 有返回值, 过滤出符合条件的元素
let arr = [1, 3, 5, 2, 4, 6];
let res3 = arr.filter(function(item, index) {
return item % 2 === 0;
});
console.log(res3);
过滤出布尔类型为true的项
let arr2 = [0, "", false, 1, 3, 4];
let res4 = arr2.filter(function(item, index) {
return item;
});
console.log(res4);
- some
判断数组中有没有符合条件的项(只要有,就返回true),如果一个都没有,才返回false
let arr3 = [
{ name: "zs", age: 18, done: "notYet" },
{ name: "ls", age: 20, done: true },
{ name: "ww", age: 22, done: true }
];
let res5 = arr3.some(function(item) {
return item.done;
});
console.log(res5);
- every
判断数组中所有的项是否满足要求,如果全都满足,才返回true,否则返回false
let res6 = arr3.every(function(item) {
return item.done;
});
console.log(res6);
- find
找到符合条件的项,并且返回第一项
let arr4 = [
{ id: 3, name: "ls", done: false },
{ id: 1, name: "zs", done: true },
{ id: 2, name: "ww", done: true }
];
// var res7 = arr4.find(function(item) {
// return item.done;
// });
// console.log(res7);
- findIndex
找到符合条件的项的下标,并且返回第一个
var res8 = arr4.findIndex(function(item) {
return item.done;
});
console.log(res8);
- reduce
1.求和计算
*第一次:pre–>1 next–>2 index–>1
pre+next=1+2=3
*第二次:pre–>3 next–>3 index–>2
pre+next=3+3=6
*第三次:pre–>6 next–>4 index–>3
pre+next=6+4=10
*第四次:pre–>10 next–>5 index–>4
var arr1 = [1,2,3,4,5] ;
var new1 = arr1.reduce(function(pre,next,index){
return pre+next ;
//pre+next=10+5=15
})
console.log(new1);
- 扁平化数组(拼接数组)
var arr2 = [[1,2,3],[4,5],[6,7]] ;
var new2 = arr2.reduce(function(pre,next,index){
return pre.concat(next); //前数组拼接后数组 .concat()
})
console.log(new2);
- 对象数组叠加计算
var arr3 = [
{price:10,count:1},
{price:15,count:2},
{price:10,count:3}
];
var new3 = arr3.reduce(function(pre,next,index){
return pre+next.price*next.count;
},0) //在原数组第一项添加为0,不改变原数组,则可不操作第一项
console.log(new3);
应用 : 计算数组中每个元素出现的次数
投票板
var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
var countedNames = names.reduce(function (allNames, name) {
// console.log(allNames, '| ' + name);
if (name in allNames) {
allNames[name]++;
} else {
allNames[name] = 1;
}
return allNames;
}, {});
console.log(countedNames);