常见问题
1、vue2和vue3区别
1)组合式api (其实就是用函数的方式写vue组件)
2)语法糖 setup ( 组件实例创建时会立即调用一次 )
3)teleport组件( 用来将组件放到指定的dom元素内。 <Teleport>
挂载时,传送的 to
目标必须已经存在于 DOM 中。理想情况下,这应该是整个 Vue 应用 DOM 树外部的一个元素。如果目标元素也是由 Vue 渲染的,你需要确保在挂载 <Teleport>
之前先挂载该元素。)
4)fragments片段 (组件可以有多个根节点)
import { useAttrs } from 'vue'
const attrs = useAttrs()
<template> <header>...</header> <main v-bind="attrs">...</main> <footer>...</footer> </template>
5) >>>
and /deep/
弃用
:deep(.foo) {} //深度选择
:slotted(.foo){} //插槽选择
:global(.foo){} //全局
6)响应式
const count = ref(0) >>>>>>>> count.value
const obj = reactive({ a:1 }) >>>>>>>> obj.a++ //响应式对象(仅对对象类型有效(对象、数组和 Map
、Set
这样的集合类型),而对 string
、number
和 boolean
这样的 原始类型 无效。)
const count = ref({a:1}) >>>>>>>>>> count.value.a = 2 //响应式的,会触发更改
const count = shallowRef({a:1}) >>>>>>> count.value.a = 2 // 浅响应式 , 不会触发更改 >>>>>>> count.value = { a:2 } //会触发更改
const obj = shallowReactive({ >>>>>>> obj.a =2 //更改状态自身的属性是响应式的
a:1,
b:{ >>>>>obj.b.c = 3 //下层嵌套对象不会被转为响应式 不会触发更改
c:1
}
})
isRef(a) //判断 a 是不是ref
unRef(a) //如果参数是 ref,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val
计算的一个语法糖
toRef() //1、 可以将值、refs 或 getters 规范化为 refs 。2、也可以基于响应式对象上的一个属性,创建一个对应的 ref。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然。
toRef(1) //从非函数的值中创建普通的 ref 等同于 ref(1)
toRef(ref(1)) // 按原样返回现有的 ref 即 ref(1)
toRef(() => props.foo) // 创建一个只读的 ref,当访问 .value 时会调用此 getter 函数 类似于computed()
cosnt obj = reactive({ a:1, b:2 })
const aRef = toRef( obj, 'a' ) // 创建了一个ref 与 obj.a同步
/ /更改该 ref 会更新源属性
aRef.value =3
console.log(obj.a) // 3
// 更改源属性也会更新该 ref
obj.a= 4
console.log(aRef.value) // 4
toValue() //将值、refs 或 getters 规范化为值。这与 unref() 类似,不同的是此函数也会规范化 getter 函数。如果参数是一个 getter,它将会被调用并且返回它的返回值。
toValue(1) >>>>>>> 1
toValue(()=>1) >>>>>> 1
toValue(ref(1)) >>>>>> 1
toRefs() // 将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用 toRef()
创建的。(可以用来解构并不失去响应式)
// 当从组合式函数中返回响应式对象时,toRefs
相当有用。使用它,消费者组件可以解构/展开返回的对象而不会失去响应性:
function useFeatureX() {
const state = reactive({ foo: 1, bar: 2 })
// ...基于状态的操作逻辑 // 在返回时都转为 ref
return toRefs(state)
}
// 可以解构而不会失去响应性
const { foo, bar } = useFeatureX()
7)内置组件
<Teleport to="#app" :disabled="true"></Teleport> //将其插槽内容渲染到 DOM 中的另一个位置。(多用于页面弹窗)
to: 必填项。指定目标容器。
disabled: true 内容将保留在其原始位置,而不是移动到目标容器中
<Suspense> //用来在组件树中协调对异步依赖的处理。它让我们可以在组件树上层等待下层的多个嵌套异步依赖项解析完成,并可以在等待时渲染一个加载状态。 可以使用<Suspense>的场景
1> 组件有异步的setup() 两种方式
2> 异步组件 defineAsyncComponent 方法接收一个返回 Promise 的加载函数。这个 Promise 的 resolve 回调方法应该在从服务器获得组件定义时调用。你也可以调用 reject(reason) 表明加载失败。 import { defineAsyncComponent } from 'vue' const AsyncComp = defineAsyncComponent(() => { return new Promise((resolve, reject) => { // ...从服务器获取组件 resolve(/* 获取到的组件 */) }) }) // ... 像使用其他一般组件一样使用 `AsyncComp` defineAsyncComponent(() => import('./Foo.vue')) //简写 (ES 模块动态导入 ( 即 ()=>import('') 这种形式 ) 也会返回一个 Promise) const AsyncComp = defineAsyncComponent({ //完整属性 // 加载函数 loader: () => import('./Foo.vue'), // 加载异步组件时使用的组件 loadingComponent: LoadingComponent, // 展示加载组件前的延迟时间,默认为 200ms delay: 200, // 加载失败后展示的组件 errorComponent: ErrorComponent, // 如果提供了一个 timeout 时间限制,并超时了 // 也会显示这里配置的报错组件,默认值是:Infinity timeout: 3000 }) <RouterView v-slot="{ Component }"> <template v-if="Component"> <Transition mode="out-in"> <KeepAlive> //发生回退时,后备内容不会立即展示出来。相反,<Suspense> 在等待新内容和异步依赖完成时,会展示之前 #default 插槽的内容。这个行为可以通过一个 timeout prop 进行配置: //在等待渲染新内容耗时超过 timeout 之后,<Suspense> 将会切换为展示后备内容。若 timeout 值为 0 将导致在替换默认内容时立即显示后备内容。 <Suspense timeout="20"> <!-- 主要内容 --> <component :is="Component"></component> <!-- 加载中状态 --> <template #fallback> 正在加载... </template> </Suspense> </KeepAlive> </Transition> </template> </RouterView>
8)自定义指令 const myDirective = { // 在绑定元素的 attribute 前 // 或事件监听器应用前调用 created(el, binding, vnode, prevVnode) { // 下面会介绍各个参数的细节
// el 指令绑定到的元素。这可以用于直接操作 DOM const { value, oldValue, arg, modifiers, instance, dir } = binding
// value 传递给指令的值。例如在v-my-directive="1 + 1"
中,值是2
。
// oldValue 之前的值,仅在beforeUpdate
和updated
中可用。无论值是否更改,它都可用。
// arg 传递给指令的参数 (如果有的话)。例如在v-my-directive:foo
中,参数是"foo"
。
// modifiers 一个包含修饰符的对象 (如果有的话)。例如在v-my-directive.foo.bar
中,修饰符对象是{ foo: true, bar: true }
// instance 使用该指令的组件实例
// dir 指令的定义对象
// vnode 代表绑定元素的底层 VNode
// prevNode 之前的渲染中代表指令所绑定元素的 VNode。仅在beforeUpdate
和updated
钩子中可用。
}, // 在元素被插入到 DOM 前调用 beforeMount(el, binding, vnode, prevVnode) {}, // // 在绑定元素的父组件 // 及他自己的所有子节点都挂载完成后调用 mounted(el, binding, vnode, prevVnode) {}, // 绑定元素的父组件更新前调用 beforeUpdate(el, binding, vnode, prevVnode) {}, // 在绑定元素的父组件 // 及他自己的所有子节点都更新后调用 updated(el, binding, vnode, prevVnode) {}, // 绑定元素的父组件卸载前调用 beforeUnmount(el, binding, vnode, prevVnode) {}, // 绑定元素的父组件卸载后调用 unmounted(el, binding, vnode, prevVnode) {} }
9)特殊函数 defineOptions() 这个宏可以用来直接在 <script setup> 中声明组件选项,而不必使用单独的 <script> 块: <script setup> defineOptions({ inheritAttrs: false, }) </script> useSlots()和 useAttrs() 在 <script setup> 使用 slots 和 attrs 的情况应该是相对来说较为罕见的,因为可以在模板中直接通过 $slots 和 $attrs 来访问它们。在你的确需要使用它们的罕见场景中,可以分别用 useSlots 和 useAttrs 两个辅助函数: <script setup> import { useSlots, useAttrs } from 'vue' const slots = useSlots() const attrs = useAttrs() </script> 10)顶层await <script setup> 中可以使用顶层 await。结果代码会被编译成 async setup(): <script setup> const post = await fetch(`/api/post/1`).then((r) => r.json()) </script> async setup() 必须与 Suspense 内置组件组合使用,Suspense 目前还是处于实验阶段的特性,会在将来的版本中稳定。
2、Object.defineProperty 和 Proxy 区别
1) Proxy
可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。它不直接操作对象,而是像代理模式,通过对象的代理对象进行操作,在进行这些操作时,可以添加一些需要的额外操作。
2) Object.defineProperty
Object.defineproperty 其实与Proxy很相似,也可以对目标对象进行拦截操作,它是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性。
const proxyObj = new Proxy({ a:[] },{ get: function(target, propKey, receiver){ console.log('get>>>>>>>', target, propKey, receiver); return Reflect.get(target, propKey, receiver); }, set: function(target, propKey, value, receiver){ console.log('set>>>>>>>>', target, propKey, value, receiver) return Reflect.set(target, propKey, value, receiver); } }) setTimeout(()=>{ proxyObj['a'][0]=1 console.log(proxyObj) },1000) var book = { _year : [], edition : 0 } Object.defineProperty(book,"year",{ get: function(val){ console.log(11111,this, val) return this._year }, set: function(newYear){ console.log(2222,newYear) this._year = newYear this.edition = this._year.length } }) book['year'][0] = 2005; // 不起作用 book['year'] = [2005]; // 可用 console.log(book.edition); // 2 console.log(book._year); //2005
3、判断数组
Object.prototype.toString.call([]) 、 a instanceof Array、isArray、
4、防抖和节流
-
函数防抖(debounce):触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。
-
函数节流(throttle):高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率。
function debounce(fn,delay) { var timeout = null; // 创建一个标记用来存放定时器的返回值 return function (e) { // 每当用户输入的时候把前一个 setTimeout clear 掉 clearTimeout(timeout); // 然后又创建一个新的 setTimeout, 这样就能保证interval 间隔内如果时间持续触发,就不会执行 fn 函数 timeout = setTimeout(() => { fn.apply(this, arguments); }, delay); }; }
//节流throttle代码: function throttle(fn,delay) { let canRun = true; // 通过闭包保存一个标记 return function () { // 在函数开头判断标记是否为true,不为true则return if (!canRun) return; // 立即设置为false canRun = false; // 将外部传入的函数的执行放在setTimeout中 setTimeout(() => { // 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。 // 当定时器没有执行的时候标记永远是false,在开头被return掉 fn.apply(this, arguments); canRun = true; }, delay); }; }
5、事件循环
js是单线程的,当主线程运行时遇到异步任务时会将其加入的任务队列等待执行,主线程空闲时会执行异步任务。
异步任务分为微任务和宏任务(先执行微任务,在执行宏任务)
-
宏任务
setTimeout
浏览器 Node
setInterval
浏览器 Node
setImmediate
Node
requestAnimationFrame
浏览器 -
微任务
process.nextTick
Node
MutationObserver
浏览器
Promise.then catch finally
浏览器 Node
6、ajax、axios和 fetch区别
AJAX的原理就是通过XHR对象来向服务器发起异步请求,从服务器获得数据,然后用JS来操作DOM更新页面
axios
- 由浏览器端发起请求,在浏览器中创建XHR
- 支持promise API
- 监听请求和返回
- 更好的格式化,自动将数据转换为json数据
- 安全性更高,可抵御CSRF攻击
fetch是http请求数据的方式,它使用Promise,但不使用回调函数。fetch采用模块化设计,通过数据流处理数据,对于请求大文件或网速慢的情况相当有用。默认情况下fetch不会接收或发送cookies。
优点:
采用模块化思想,将输入、输出、状态跟踪分离
基于promise,返回一个promise对象
缺点:
过于底层,有很多状态码没有进行封装
无法阻断请求
兼容性差
无法检测请求进度
7、js冒泡排序和快速排序
冒泡排序
1.比较相邻的两个元素,如果前一个比后一个大,则交换位置。
2.第一轮的时候最后一个元素应该是最大的一个。
3.按照步骤一的方法进行相邻两个元素的比较,这个时候由于最后一个元素已经是最大的了,所以最后一个元素不用比较。
const arr = [9, 3, 2, 6, 5, 9, 7]; // 时间复杂度 n^2 function bubbleSort(arr) { let len = arr.length - 1; // 外层循环趟数 for(let i = 0; i < len; i++){ // 里层循环比较相邻两个数值大小 for(let j = 0; j < len - i; j++) { if(arr[j] > arr[j + 1]){ [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]] } } } return arr; } console.log(bubbleSort(arr)); // [2, 3, 5, 6, 7, 9, 9]
快速排序
快速排序是对冒泡排序的一种改进,第一趟排序时将数据分成两部分,一部分比另一部分的所有数据都要小。然后递归调用,在两边都实行快速排序。
1.选择数组中间数作为基数,并从数组中取出此基数;
2.准备两个数组容器,遍历数组,逐个与基数比对,较小的放左边容器,较大的放右边容器;
3.递归处理两个容器的元素,并将处理后的数据与基数按大小合并成一个数组,返回。
function quickSort(arr) { if (arr.length < 2) { return arr; } // 找到基数的索引 const index = Math.floor(arr.length / 2); // 拿到索引对应的值 const item = arr.splice(index, 1)[0]; const left = []; const right = []; for( let i = 0; i < arr.length; i++) { if(arr[i] < item) { left.push(arr[i]); } else { right.push(arr[i]) } } // return quickSort(left).concat([item], quickSort(right)) return [...quickSort(left), item, ...quickSort(right)] }
8、 reduce函数
var numbers = [1,2,3,4,5,5,6]; //数组去重 var val = numbers.reduce((prev,current,index,arr)=>{ console.log(index,arr) if(prev.indexOf(current) === -1){ prev.push(current) } return prev },[]) console.log(val)
9、Array.from 和Array.of 区别
Array.from 将类数组和 可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map) 变为真正的数组
Array.of 用于将一组值,转换为数组 Array.of(3, 11, 8) // [3,11,8]
10、apply、call、bind区别
三者都是可以改变this指向
apply(this, [ 1,2 ])
call(this, 1, 2)
bind(this)(1, 2)
11、amd和cmd区别
CMD (Common Module Definition), 是seajs推崇的规范,CMD则是依赖就近,用的时候再require
// 所有模块通过defined来定义 define(function(require,export,module){ //主要这里不同 // 通过require引入依赖 var $=require('jqurey'); var spinning=require('./spinning'); }) // 加载模块 seajs.use(['myModule.js'], function(my){ });
AMD是Asynchronous Module Definition的缩写,意思就是”异步模块定义”。 依赖前置 它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。
define(['package/lib'],function(lib){ //主要是这里不相同 function foo(){ lib.log("hello world"); }; return { foo:foo }; }) // 加载模块 require(['myModule'], function (my){ my.printName(); });