数据类型来分有基本数据类型和引用数据类型:

基本数据类型: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计算宽高

  1. innerWidth/innerHeight 获取窗口的宽度和高度 出现滚动条时也是计算在内的,窗口指的是window,所以一般时window.innerWidth/innerHeight。
  2. 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布局、父子传值、兄弟传值、计算属性和监听的区别

  1. 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>
  1. 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的区别

  1. 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);