(1)防抖:就是将一段时间内连续的多次触发转化为一次触发。
(2)节流:减少一段时间内触发的频率
区别:两者区别在于函数节流是固定时间做某一件事,比如每隔1秒发一次请求。而函数防抖是在频繁触发后,只执行一次(两者的前提都是频繁触发)
场景:
(1)节流:一般是onrize,onscroll等这些频繁触发的函数,比如你想获取滚动条的位置,然后执行下一步动作;鼠标不断点击触发,mousedown(单位时间内只触发一次)…
(2)防抖:频繁操作点赞和取消点赞,因此需要获取最后一次操作结果并发送给服务器;search搜索联想,用户在不断输入值时…
生命周期函数 又叫钩子函数 是到了某一个时间点会自动触发 我们讨论的是 vue 实例的钩子函数 从 vue 实例创建到销毁的过程中 到了一定的时间节点就会触发
创建阶段
beforeCreate 实例创建之前 这个时候什么都没有 data methods 都不能用 也没有 this
created 创建之后 也是最早的可以使用 data 和 methods 的钩子函数 这个时候有 this 了
beforeMount 组件挂载之前
mounted 组件挂载之后 这个时候能拿到 dom 节点使用
运行阶段 只要修改 data 就会触发
beforeUpdate 数据变了 视图还没变
updated 数据变了 视图也变了
销毁阶段
beforeDestory 组件销毁之前 是最后一个能使用 data 和 methods 的钩子函数
destoryed 组件销毁之后
除了这八个 还有 三个 如果用 keep-alive 缓存了组件 就会有 actived deactived 这两个钩子函数 activated 组件激活 deactivated 组件停用 errorCaptured 子组件出错的时候会触发这个钩子函数
我在写效果的过程中 会在 created 或者 mounted 中发送 http 请求
created 和 mounted 的区别 就是 created 比 mounted 执行的时间更早所以会更多的在 created 中请求 mounted 的特点是可以操作 dom 节点
还会用 created 钩子函数 获取 本地存储的数据
自己试一试 把顺序写一下
页面—beforeCreate
页面—created
页面—beforeMount
组件—beforeCreate
组件—created
组件—beforeMount
组件—mounted
页面—mounted
vuex 是 vue 的状态管理工具 管理项目中的公共数据 能够在所有的组件中使用 一共有五大核心 state 存放公共数据的地方 通过 this.$store.state.xxx调用 mutations 修改 state 的地方 只有这里能修改 通过this.$store.commit 调用 getters 相当于是之前的计算属性 通过 this.$store.getters 调用 actions 执行异步操作的地方 通过 this.$store.dispatch 调用 modules 模块化
vuex 缺点就是刷新数据会丢失 我们可以保存本地存储 或者 安装 vuex 持久化插件 vuex-persist 去实现自动本地存储
我在项⽬当中如果要改变 state 的状态,我们⼀般是在组件⾥⾯调⽤ this.$store.dispatch ⽅式来触发 actions ⾥⾯的⽅法,在 actions ⾥⾯的⽅法通过 commit 来调⽤ mutations ⾥⾯定义的⽅法来改变 state,同时这也是 vuex 的执⾏机制
strict: true, 跟 state 同级的地方设置这个属性 开启完严格模式后 如果不是通过 mutation 修改的 state 就会报错 strict 严格模式只适合开启在开发环境下
把仓库里的数据分模块管理
每个模块里又有四个核心 state mutations getters actions
然后引入仓库 并且在 modules 下注册模块 在定义的 modules 里开启一个命名 namespaced:true
export default { computed: { arr() { return this.$store.state.a.arr; //使用 模块a里的state数据arr }, }, methods: { add() { this.$store.commit("a/add"); //调用a下面的add方法 }, }, };
⾯试官您好,接下来我给您介绍⼀下 vue 的路由模式,vue 的路由模式⼀共有两种,分别是哈希和 history.他们的区别是 hash 模式不会包含在 http 请求当中,并且 hash 不会重新加载⻚⾯,⽽使⽤ history 模式的话,如果前端的 url 和后端发起请求的 url 不⼀致的话,会报 404 错误,所以使⽤ history 模块的话我们需要和后端进⾏配合. history 的原理就是利⽤ ht
路由的导航守卫 又叫做路由的钩子函数(生命周期函数) 就是在跳转页面的 时候把 路由拦下来 做一些操作 再放行 一共有三种
第一种是全局守卫 beforeEach 路由进入之前 afterEach 路由进入之后
第二种 组件内守卫 beforeRouteEnter 路由进入之前 beforeRouteUpdate 路由更新之前 beforeRouteLeave 路由离开之前
第三种 独享守卫 beforeEnter 路由进入之前
有三个参数 to from next next 这个参数 在路由 3.x 版本的时候 是必须的 但是到了路由 4.x 版本的时候 next 参数变成可选的了
一般来说 vue2 搭配 3.x 的路由 vue3 搭配 4.x 的路由
比如说 购物车页面只有登陆的才能访问 我们可以用组件级守卫守卫购物车页面 如果已经登陆存的有 token 的话 就继续访问这个页面 如果没有登陆的话就会跳转到登陆页面
beforeRouteEnter(to, from, next) { if (localStorage.getItem("token")) { next(); } else { next("/login"); }
next 参数 除了括号里可以是空和路径之外 还可以是一个回调函数(回调函数就是一个被作为参数传递的函数) 比如说 token 被存进 vuex 里就能这么写:
beforeRouteEnter(to, from, next) { // if (localStorage.getItem("token")) { // next(); // } else { // next("/login"); // } next((vm) => { // vm就是 这个组件实例 就是this if (vm.$store.state.token) { next(); } else { next("/login"); } }); },
watch 监听路由的变化 监听路由的话就是监听 $router
watch: { $route: { handler(newval, oldval) { console.log(newval, oldval); }, }, },
watch 监听 数据的变化 watch 有三个参数 deep immediate handler 当我们不需要 deep 和 immediate 的时候 就可以简写为一个函数 这个函数就是 handler
watch: { num: { deep: true, //深度监听 immediate: true, //进入页面立刻监听 handler(newval) { console.log(newval); }, //执行函数 参数是newval 和 oldval }, // num() {}, // 之前写的只有一个函数的情况 是handler的简写 不需要 deep 和 immediate的时候可以简写 },
父传子
首先在使用子组件的标签上 通过自定义属性传递变量 在子组件中 通过 props 接受 在接收的时候有两种接收方式 数组形式 和 对象形式 对象形式可以规定传来的变量的数据类型(type)默认值(default)以及是否必填(required)
子传父
首先在使用子组件的标签上定义一个自定义事件 在子组件里通过 this.$emit 去调用这个自定义事件 $emit 方法的第一个参数是自定义事件的名字 第二个参数是就是子组件要传递给父组件的变量 最后在父组件接收使用就可以了
兄弟组件 利用中央事件总线 eventbus
在 main.js 中 把一个空的 vue 实例挂载在 vue 的原型上起名叫$bus 传数据的时候用this.$bus.$emit传 在要接受数据的子组件 在 created钩子函数中 用$on 方法接收
这是暂时给大家总结的三种 后期还有 另外几种扩展的 后边再总结
在 dom 更新之后执行的延迟回调 因为 vue 的 dom 更新是异步的 所以 当 dom 还未更新的时候我们无法拿到最新的 dom 来用 放在 nextTick 里的代码 就会在 dom 更新之后执行 就能拿到最新的 dom 来用了
.trim 去除⾸尾多余的空格 .stop 阻⽌事件冒泡 .once 只渲染⼀次 .self 事件只作⽤在元素本身 .number 将值转化为 number 类型 .capter 组件之间捕获 .prevent 阻⽌元素的默认⾏为 .native 事件穿透,让我们可以在⾃定义组件上触发原生的事件
keep-alive 是 vue 内置的⼀个组件,⽽这个组件的作⽤就是能够缓存不活动的组件,我们能够知道,⼀般情况下,组件进⾏切 换的时候,默认会进⾏销毁,如果有需求,某个组件切换后不进⾏销毁,⽽是保存之前的状态,⽐如说刚刚填好的表单数 据。那么就可以利⽤ keep-alive 来实现 被缓存的组件 切换的时候不会触发创建和销毁的钩子函数 而是会触发跟 keep-alive 相关的 activated(激活) deactivated(停用)这两个钩子函数
keep-alive 标签 有 include 属性和 exclude 属性 include 属性 定义了要缓存谁 exclude 属性 定义了不缓存谁 还可以利用 路由的 meta 信息去规定缓存谁不缓存谁
我在写项目的过程中 可以使用 keep-alive 缓存刚刚填好的表单数据 还可以记录页面滚动条的距离,离开再回来滚动条还在这个位置
所谓的 vue 过滤器就是将数据进⾏⼆次处理,得到我们想要的结果数据 vue 的过滤器分为两种,第⼀种是全局过滤器,通过 vue.filter 来进⾏定义,第⼆种是局部过滤器,需要定义在组件内部,用 filters 过滤 项⽬中我们通过过滤器将后台返回的状态 0 和 1 转化为⽀付或者未⽀付 还有对时间格式进行过滤
vue3 中没有过滤器了
⾸先呢,所谓的插槽就是⼀个占位符,将⾃定义组件的内容展示出来.我们知道⾃定义的组件⾥⾯如果写内容的话,⻚⾯是不会显 示出来的,如果我们想让⾃定义组件⾥⾯的内容显示出来,我们就需要使⽤ slot 的插槽. ⽽插槽分别具名插槽和匿名插槽、以及作⽤域插槽. 我们⽤的⽐较多的具名插槽和匿名插槽,具名插槽需要所有 slot 标签上指定 name 属性,⽽在对应标签上添加# 属性指定名字. 在项⽬中我们⼀般在进⾏组件封装的时候会使⽤插槽,以上就是我对插槽的理解.
作用域插槽 是把子组件里的数据传到父组件的插槽里使用
什么是跨域 跨域是浏览器的跨域 不符合 域名 协议 端口号一样(同源策略) 的请求都会出现跨域的问题 跨域的解决方法 最多的情况是后端处理跨域 前端的跨域 服务器代理 jsonp vue 中是 proxy 代理实现跨域
vue 的跨域 用 proxy 实现
先创建 utils 文件夹
创建 request.js
引入 axios
配置 基本路径和超时时间
配置请求拦截和响应拦截
在请求拦截里可以放 loading 和 token
在响应拦截中 可以 清除 loading 还有处理错误编码字典
最后把我们封装的 axios 实例 导出
拦截器有两种 一个是请求拦截一个是响应拦截 拦截器不需要手动调用而是每次发送 http 请求的时候都会自动触发 我们一般在请求拦截中 放全局的 loading 和 token 在响应拦截中关闭全局的 loading 和对 token 进行过期处理 还可以处理错误编码字典
⾸先呢 jquery 他是⽤ js 封装的⼀个类库,主要是为了⽅便操作 dom 元素,⽽ vue 他是⼀个框架,并且呢,他会从真实 dom 构建出⼀个 虚拟的 dom 树,通过 di!算法渲染只发⽣改变的 dom 元素,其他的相同的 dom 元素不⽤在重新渲染. ⽽使⽤ jquery 去改变 dom 元素 的时候,即使有相同的 dom 元素也会重新渲染, jq 重点操作 dom,而 vue 重点操作数据。以上就是我对 vue 和 jquery 区别的理解.
在 vue2 中 vue 实例的 data 数据是响应式 的 就是数据变了 视图也会跟着变,如果给某一个 data 新添加了一个字段 这个新添加的字段因为 js 的限制不响应,需要使用 this.$set 方法代替原本的普通添加方法 就能实现响应,这个方法的三个参数 是给谁添加 添加的字段 初始值 如果不是在组件中 用这个方法 那么就用 Vue.set
在 vue3 中数据响应的原理从 definedproperty 变成了 proxy 所以不会存在数据变了视图不变的问题
说⼀下什么是 mvvm 模式?(70%) MVVM 是把 MVC 的 Controller 和 MVP 的 Presenter 改成了 ViewModel 。 View 的变化会⾃动更新到 ViewModel , ViewModel 的变化也会⾃动同步到 View 上显示。这种⾃动 同步是因为 ViewModel 中的属性实现了 Observer ,当属性变更时都能触发对应的操作
2.MVVM模式的优点以及与MVC模式的区别?
MVVM 模式的优点: 1、低耦合: 视图(View)可以独⽴于 Model 变化和修改,⼀个 ViewModel 可以绑定到不同的"View"上,当 View 变化的时候 Model 可以不变,当 Model 变化的时候 View 也可以不变。 2、可重⽤性: 你可以把⼀些视图逻辑放在⼀个 ViewModel ⾥⾯,让很多 view 重⽤这段视图逻辑。 3、独⽴开发: 开发⼈员可以专注于业务逻辑和数据的开发(ViewModel),设计⼈员可以专注于⻚⾯设计。 4、可测试: 界⾯素来是⽐较难于测试的,⽽现在测试可以针对 ViewModel 来写。 MVVM 和 MVC 的区别: mvc 和 mvvm 其实区别并不⼤。都是⼀种设计思想。 主要区别 mvc 中 Controller 演变成 mvvm 中的 viewModel, mvvm 通过数据来显示视图层⽽不是节点操作。 mvvm 主要解决了: mvc 中⼤量的 DOM 操作使⻚⾯渲染性能降低,加载速度变慢,影响⽤户体验。
虚拟 dom 是利用 js 描述元素与元素的关系,用 js 对象来表示真实的 DOM 树结构,创建一个虚拟 DOM 对象 由于在浏览器中操作 DOM 是很昂贵的。频繁的操作 DOM,会产⽣⼀定的性能问题. 在组件渲染的时候会调用 render 函数,这个函数会生成一个虚拟 dom,再根据这个虚拟 dom 生成真实的 dom,然后这个真实的 dom 会挂载到我们的页面中。 如果只是渲染一个页面后期不改动的话 那么虚拟 dom 其实成本更高 因为 都要渲染成真实的 dom 如果组件内有响应的数据,数据发生改变的时候 render 函数会生成一个新的虚拟 dom 新的虚拟 dom 树和旧的虚拟 dom 树进行对比,找到要要修改的虚拟 dom 的部分,去修改相对应部分的真实 dom
diff 算法就是对虚拟 dom 进行对比,并返回一个 patch 对象,这个对象的作用是存储两个节点不同的地方,最后用 patch 里记录的信息去局部更新真实的 dom
diff 算法的步骤 1.js 对象表示真实的 dom 结构,就是我们说的生成一个虚拟 dom,再用虚拟 dom 构建一个真的 dom 树,放到页面中。 2.状态改变的时候生成一个新的虚拟 dom 跟旧的进行对比,这个对比的过程就是 diff 算法,通过 patch 对象记录差异 3.把记录的差异用在第一个虚拟 dom 构建的真实的 dom 上,视图就更新了
Vue 的 diff 算法是平级⽐较,不考虑跨级⽐较的情况。内部采⽤深度递归的⽅式+双指针⽅式⽐较
增加 name 属性,可以实现组件递归调⽤自身,调用的时候用的就是 name 名字 可以表示组件的具体名称,⽅便调试和查找对应的组件 比如说 keep-alive 的 include 和 exclude 就是通过组件的 name 属性区分谁缓存谁不缓存的
vue.use 是用来安装 Vue.js 插件。这个插件可以是一个组件也可以是一个函数,插件里要有一个 install 方法,install 方法调用时,他的第一个参数就是 Vue 在调用 vue.use 方法的时候就会执行 Vue.install 方法
该方法需要在调用 new Vue() 之前被调用。
混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”,出现冲突的时候以组件优先 比如,生命周期函数就会合并 但是如果组件和混入有相同的变量的时候 会以组件的优先
全局混入 混入也可以进行全局注册。使用时格外小心!一旦使用全局混入,所有的实例都会触发
使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。 可以理解为创建子组件的 可以用 mount 或者 el 指定这个子组件挂载到哪里
我理解的导航守卫 就是一座房子的保安 只有保安允许了才能访问页面
之前说三种六个 还有一个用的不是很多的全局守卫 叫做全局解析守卫 beforeResolve
导航守卫一共有三种 全局的守卫 守卫所有的页面 beforeEach 路由前置守卫 beforeResolve 路由解析之前 afterEach 路由离开
全局守卫 beforeEach 和 beforeResolve 他们两个都会在路由跳转前就执行 参数都是 to from next 他们两个的区别 执行的时机不一样 beforeEach 是在路由规则被循环之前执行 beforResolve 是在组件被解析之后调用 beforeEach 要比 beforResolve 先执行
组件守卫 守卫单个组件 beforeRouteEnter 路由进入 beforeRouteUpdate 路由更新 beforeRouteLeave 路由离开
独享守卫 守卫单个页面 beforeEnter 路由进入之前
作用范围不同 全局作用于所有页面 组件作用于单个组件 独享也是守卫单个页面
代码写的位置不一样 全局守卫写在 router/index.js 中 组件守卫写在组件里 跟 data 同级 独享守卫写在路由规则里
执行顺序有区别 执行的顺序要记下来 beforeEach 前置守卫 beforeEnter 独享守卫 beforeRouteEnter 组件守卫 beforeResolve 解析守卫 afterEach 路由后置守卫
父传子
首先在使用子组件的标签上 通过自定义属性传递变量 在子组件中 通过 props 接受 在接收的时候有两种接收方式 数组形式 和 对象形式 对象形式可以规定传来的变量的数据类型(type)默认值(default)以及是否必填(required)
子传父
首先在使用子组件的标签上定义一个自定义事件 在子组件里通过 this.$emit 去调用这个自定义事件 $emit 方法的第一个参数是自定义事件的名字 第二个参数是就是子组件要传递给父组件的变量 最后在父组件接收使用就可以了
兄弟组件
利用中央事件总线 eventbus 在 main.js 中 把一个空的 vue 实例挂载在 vue 的原型上起名叫$bus 传数据的时候用this.$bus.$emit传 在要接受数据的子组件 在 created钩子函数中 用$on 方法接收
利用 vuex 进行组件通信 把公共的数据存在 vuex 里就可以实现组件之间都能使用这个数据了
其实 v-model 也能实现组件通信 因为 v-model 就是 :value 和 @input 事件的合写 如果在一个子组件上使用 v-model 也能实现父子组件之间的通信
$attr+$listener 如果父组件 A 下面有子组件 B 子组件 B 下面又有子组件 C 如果 a 组件的变量和方法想要传给组件 C 的时候 就用到这个方法 适用于多级组件传值 在 B 组件中给 C 组件绑定 v-bind="$attrs" v-on="$listeners" 然后在 C 组件中就可以直接使用 a 传来的属性和方法了 (简单来说:$attrs与$listeners 是两个对象,$attrs 里存放的是父组件中绑定的非 Props 属性,$listeners 里存放的是父组件中绑定的非原生事件。)
provide 和 inject 父组件通过通过 provide 提供变量 子组件中通过 inject 注入变量,不论嵌套了几层子组件 都能通过 inject 来调用 provide 的数据
这种写法传的数据是不响应的
$parent和$children 在子组件内可以直接通过$parent对父组件进行操作,在父组件内可以直接通过$children 对子组件进行操作 在父组件调用子组件时候要加下标也就是$children 是一个数组 因为可以有很多个子组件
也能用本地存储 来 完成组件通信
一般分为开发环境和生产环境
development 开发 production 生产(上线) 怎么配置多环境变量 首先建立两个文件 在根目录 .env.dev 里面写上 NODE_ENV="development" .env.prod 里面写上 NODE_ENV="production"
这样我们可以通过 process.env.NODE_ENV 这个变量来判断当前的环境 比如我们可以通过判断不同的环境 在 axios 请求的时候设置不同的 baseURL 在开发的时候使用测试接口 在上线的时候使用真实接口
npm run serve 开启的就是开发环境 npm run build 开启的就是生产环境
数据总是从⽗组件传到⼦组件,⼦组件没有权利修改⽗组件传过来的数据,只能请求⽗组件对原始数据进⾏修改。这样会防⽌从 ⼦组件意外改变⽗级组件的状态,从⽽导致你的应⽤的数据流向难以理解。 注意:在⼦组件直接⽤ v-model 绑定⽗组件传过来的 prop 这样是不规范的写法 开发环境会报警告 如果实在要改变⽗组件的 prop 值 可以再 data ⾥⾯定义⼀个变量 并⽤ prop 的值初始化它 之后⽤$emit 通知⽗组件去修改
避免 dom 元素重复渲染. 我⻔⼀般在设置 key 的时候⾸先尽量会设置为 id,或者 index 下表.
vue.js 则是采⽤ 数据劫持 结合 发布者-订阅者 模式的⽅式, 通过 Object.defineProperty() 来劫持各个属性的 setter , getter , 在数据变动时发布消息给订阅者,触发相应的监听回调。 这个时候就可以实现数据的双向绑定
v-if v-show v-html v-text v-on v-bind v-model v-for
⾯试官您好,接下来我给您介绍⼀下 v-if 和 v-show 的区别? ⾸先 v-if 和 v-show 都是控制元素的显示与隐藏, 不过 v-if 控制元素的显 示和隐藏的时候会删除对⽤的 dom 元素,当每⼀个显示的时候,都会重新创建 dom 和渲染. ⽽ v-show 则是通过 css 的 display:none 和 display:block 来控制元素的显示与隐藏. v-if ⽐较耗费性能,所以我们涉及到频繁的显示隐藏操作我们建议使⽤ v-show,如果不 是频繁操作的话,我们可以 v-if 在项⽬中我会经常使⽤ v-if 和 v-show,⽐如我们在搜索功能的时候,他有⼀个历史记录,这个时候我们根据是否有搜索的结果来判 断历史记录的显示与隐藏,这块我就可以使⽤ v-if ,当然⽤ v-show 也可以. 以上就是我对 v-if 和 v-show 的理解.
v-for 的优先级⾼. 因为 v-for 的时候我们才开始渲染 dom 元素,这个 v-if 还⽆法进⾏判断. v-for 和 v-if 不能同时使⽤,我们可以通过标签,⽐如 div 或者 template 标签来进⾏包裹,把 v-if 写到包裹的标签上⾯(写到 v-for 外⾯)
双向数据绑定原理发生了改变,使用 proxy 替换 Object.defineProerty,使用 Proxy 的优势: 可直接监听数组类型的数据变化 监听的目标为对象本身,不需要像 Object.defineProperty 一样遍历每个属性,有一定的性能提升 可直接实现对象属性的新增/删除
默认使用懒加载 在 2.x 版本里。不管数据多大,都会在一开始就为其创建观察者,在数据很大时,就会造成性能的问题。在 3.x 中,只会对渲染出来的数据创建观察者,而且 3.x 的观察者更高效。
3.0 新加入了 TypeScript 以及 PWA 支持
重构 Virtual DOM 模板编译时的优化,将一些静态节点编译成常量 Slot 优化,将 slot 编译为 lazy 函数,将 slot 的渲染的决定权交给子组织
生命周期有了一定的区别 Vue2--------------vue3 beforeCreate -> setup() 开始创建组件之前,创建的是 data 和 method created -> setup() beforeMount -> onBeforeMount 组件挂载到节点上之前执行的函数。 mounted -> onMounted 组件挂载完成后执行的函数 beforeUpdate -> onBeforeUpdate 组件更新之前执行的函数。 updated -> onUpdated 组件更新完成之后执行的函数。 beforeDestroy -> onBeforeUnmount 组件挂载到节点上之前执行的函数。 destroyed -> onUnmounted 组件卸载之前执行的函数。 activated -> onActivated 组件卸载完成后执行的函数 deactivated -> onDeactivated
首先安装按需引入的插件,在 babel.config.js 中添加按需引入的配置,创建一个 plugin 文件夹,定义一个 js 文件用来存放按需引入的代码,之后在建好的 js 文件中首先导入 vue,再导入需要的 vant-ui 插件,通过 vue.use()全局注入。 修改样式可以用样式穿透 /deep/
响应系统的变动 由原来的 Object.defineProperty 的 getter 和 setter,改变成为了 ES2015 Proxy 作为其观察机制。 Proxy 的优势:速度加倍,节省了⼀半的内存开销。
虚拟 DOM 重写(Virtual DOM Rewrite) 用更有效的代码来创建虚拟节点。
组件渲染的优化(优化插槽⽣成) Vue2 当中在⽗组件渲染同时,⼦组件也会渲染。 Vue3 就可以单独渲染⽗组件、⼦组件。
总体来说:1. 更快 2. 更⼩ 3. 更容易维护 4. 更加友好 5. 更容易使⽤
defineProperty API 的局限性是只有 data 中定义过的属性才会做出响应。 Proxy 可以理解成,在⽬标对象之前架设⼀层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此 data 中新添加的属性也会响应。
响应式是惰性的 在 Vue.js 2.x 中,对于⼀个深层属性嵌套的对象,执⾏ Object.defineProperty 在一开始就把每⼀层对象数据都变成响应式的,会有很⼤的性能消耗。 在 Vue.js 3.0 中,使⽤ Proxy API 是真正访问到的内部属性才会变成响应式,简单的可以说是按需实现响应式,减少性能消耗。
proxy 的性能本来⽐ defineproperty 好,proxy 可以拦截属性操作,不需要初始化的时候遍历所有属性,并且新添加的 data 属性也是响应式的 不存在数据变了视图不变的情况 另外有多层属性嵌套的话,响应式是惰性的
Options Api 选项 在一个对象中包含了各种 data methods computed 等组件选项 这种叫做选项 api A 同⼀个功能逻辑的代码被拆分到不同选项 ;(使⽤ mixin 重⽤公⽤代码)也有问题:命名冲突,数据来源不清晰;
composition Api 组合 api vue3 新增的⼀组 api,它是基于函数的 api,把所有的数据 方法等都放在 setup 函数里, 解决在⼤型项⽬中,选项 api 不好拆分和重⽤的问题。
是 ECMAScript 的第六个版本 在 es5 的基础上新增加了一些语法 js 分成三部分 dom(文档对象模型) bom(浏览器对象模型) ECMAScript(js 语法)
promise 是 es6 提供的一种异步解决方案,在我的理解中 promise 就是把异步操作换了一种写法 从之前的嵌套回调函数 变成了链式的写法 promise 本身其实就是一个容器 里面放异步的代码 这样就可以让这个异步的代码执行.then .catch 的操作
1.1 说⼀下你对 promise 的理解?(必问) promise 是 es6 提供的一种异步解决方案,在我的理解中 promise 就是把异步操作换了一种写法 从之前的嵌套回调函数 变成了链式的写法
1.2 什么是 promise?通过 promise 能够解决什么问题?
promise 是 es6 提供的一种异步解决方案, promise 可以解决回调地狱的问题 回调地狱就是拿到 a 接口的数据后才能根据 a 返回的数据请求 b 接口 这样回调函数嵌套回调函数的写法就叫回调地狱 这种写法会让代码臃肿 并且后期难以维护 除了 promise 之外 async 是解决回调地狱的最终方案
1.3 说⼀下 promise 的特点?也就是三种状态? promise ⼀共有三个状态,分别是进⾏中,成功或者失败 如何成功的话可以通过 resolve ⽅法将正确结 果返回出去,通过.then 的⽅式进⾏接受,失败的话可以通过 reject 的⽅式将失败的结果返回出去,通 过.catch 的⽅式进⾏接受,pending 状态是进⾏中,⼀旦进⾏之后,他的状态是不可逆的
1.4 说⼀下 promise 怎么⽤? 如果要使⽤ promise,我们需要对 promise 进⾏实例化,在实例化的构造函数⾥⾯有⼀个回调函数,这个 回调函数⾥⾯有两个参数,分别是 resolve 和 reject,我们可以通过 promise 的实例化对象调⽤.then 或 者.catch ⽅式接受结果
1.5 我们之前遇到的哪些东西本身就是一个 promise 对象? jq 的 ajax 请求本身就是一个 promise 对象 所以可以直接用.then axios 也是一个 promise 对象
原生的 ajax 以及 小程序的 wx.request 请求都不是 promise 对象 不能用。then 的写法,如果想用链式写法 就要自己封装一个 promise
1.6. promise 的 all 和 race 方法
promise 还给我们提供了.all 和 race, 其中 all ⽅法的作⽤是将多个请求合并成⼀个请求, ⽐如当⾸⻚ 要请求 10 个接⼝,我们可以 promise.all 进⾏合并,.race 的作⽤也可以将多个请求合并成⼀个请求,不过 是谁先请求成功就先返回谁.
⾸先 async 和 await 是解决异步的终极⽅案,async 和 await ⼀般配和使⽤,当我 们给函数前⾯加上关键字 async,这个时候,这个函数的返回值就是⼀个 promise. ⽽ await 是⼀个同步 的操作,await 只能配合 async 只能,不然会报错,await 后⾯可以是表达式,也可以是⼀个 promise,在 await 下⾯的代码必须得等待 await 执⾏完之后才能在执⾏ 他们和 promise 的区别就是在写法上更加的简洁. 以上就是我对 async 和 await 的理解.
generator 函数也是 es6 的处理异步回调的一种写法 但是这种写法现在被 async 取代了 generator 函数 用*表示 跟 yield 搭配使用
let var 是声明变量的 const 是声明常量的
var 声明的变量会声明提升 但是 const 和 let 不会 (声明提升就是声明的变量会把声明语句提到作用域的顶端 但是赋值语句留在原地)
var 在全局作⽤域下声明变量会导致变量挂载在 window 上,其他两者不会
let 和 const 声明的是块级作用域 var 声明的是函数作用域
块级作用域:只要是大括号就能区分作用域 函数作用域: 只有函数才能区分作用域
在 es6 中,提供了⼀种简洁的函数写法,我们称作“箭头函数”。 写法:函数名=(形参)=>{……} 当函数体中只有⼀个表达式时,{}和 return 可以省略,当函数体中形 参只有⼀个时,()可以省略。 特点:箭头函数中的 this 始终指向箭头函数定义时的离 this 最近的⼀个函数,如果没有最近的函数 就指向 window。 区别:
箭头函数不能⽤于构造函数,不能使⽤ new** ⽽普通函数可以
在普通函数中,this 总是指向调⽤它的对象,如果⽤作构造函数,this 指向创建的对象实例, ⽽箭头函数指向箭头函数定义时的离 this 最近的⼀个函数,如果没有最近的函数就指向 window。
For in 可以遍历对象 ⽽ for of 遍历对象会报错
for in 遍历数组得到的数组的下表 ⽽ for of 遍历得到的时候数组⾥⾯的每⼀个元素
es5 的面向对象是通过 new 一个构造函数实现的 es6 的面向对象是通过 class 实现的 es6 的 class 就是面向对象的语法糖(实现同样的功能 但是代码更少 更加简洁)
面向对象是一个概念或者编程思想,面向对象是相对于面向过程来讲的,面向对象方法,把相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统建模,更贴近事物的自然运行模式
(在 js 中一切皆对象 比如说数组 object number string 等等 这些都是内置对象
那么怎么自定义对象 es5 的时候没有像后台语言一样的 class 这个概念 所以我们用函数当作构造函数来创建对象)
答:在 JavaScript 中, new 操作符⽤于创建⼀个给定构造函数的实例对象 new 的实例可以访问构造函数函数里的属性 也能访问 原型上的方法
创建⼀个新的对象 obj 将对象与构建函数通过原型链连接起来 将构建函数中的 this 绑定到新建的对象 obj 上 根据构建函数返回类型作判断,如果是原始值则被忽略,如果是返回对象,需要正常处理
prototype 显式原型 每个函数都有一个 prototype 属性,被称为显式原型,里面包含了这个构造函数公共的方法
_ proto 隐式原型 每个实例对象都会有_ proto 属性,其被称为隐式原型 每一个实例对象的隐式原型 proto _属性指向自身构造函数的显式原型 prototype
constructor 每个 prototype 原型都有一个 constructor 属性,指向它关联的构造函数。
原型链 获取对象属性或者方法的时候,如果对象本身没有这个属性,那就会去他的原型_ proto 上去找,如果还查不到,就去找原型的原型,一直找到最顶层(Object.prototype)为止。Object.prototype 对象也有 proto _属性值为 null。
答:JS 继承实现⽅式也很多,主要分 ES5 和 ES6 继承的实现
先说⼀下 ES5 是如何实现继承的 ES5 实现继承主要是基于 prototype 来实现的,具体有三种⽅法 ⼀是实例继承:即 B.prototype=new A() 让 b 的原型等于 a 的实例
⼆是借⽤构造函数继承(call 或者 apply 的⽅式继承) function B(name,age) { A.call(ths,name,age) } 三是组合继承 组合继承是结合第⼀种和第⼆种⽅式
再说⼀下 ES6 是如何实现继承的 ES6 继承是⽬前⽐较新,并且主流的继承⽅式,⽤ class 定义类,⽤ extends 继承类,⽤ super()表示 ⽗类,【下⾯代码部分只是熟悉,不⽤说课】 例如:创建 A 类 class A { constructor() { //构造器代码,new 时⾃动执⾏ } ⽅法 1( ) { //A 类的⽅法 } ⽅法 2( ) { //A 类的⽅法 } } 创建 B 类并继承 A 类 class B extends A { constructor() { super() //表示⽗类 } } 实例化 B 类: var b1=new B( ) b1.⽅法 1( )
传统的 javascript 中只有对象,没有类的概念。它是基于原型的面向对象语言。Es5 的时候通过构造函数来创建类 到了 es6 我们就可以通过 class 关键字来创建类,跟传统的面向对象的语言更加接近。 ES5 中如果要生成一个对象实例,需要先定义一个构造函数,然后通过 new 操作符来创建实例。
ES6 中的类 ES6 引入了 class(类)这个概念,通过 class 关键字可以定义类。该关键字的出现使得 javascript 在对象写法上更加清晰,更像是一种面向对象的语言。 注意项: 1.在类中声明方法的时候,千万不要给该方法加上 function 关键字 2.方法之间不要用逗号分隔,否则会报错
ES5 中的继承 (组合继承:原型链继承 + 借用构造函数) 原型链继承: 父类的实例作为子类的原型 借用构造函数继承: 在子类内,使用 call()调用父类方法,并将父类的 this 修改为子类的 this.相当于是把父类的实例属性复制了一份放到子类的函数内. 组合继承: 既能调用父类实例属性,又能调用父类原型属性
Es6 的继承 Class cat extends 父类 然后在 constructor 中定义 super 可以设置继承父类的哪些属性
是 es6 新增加的两种数据结构 数据结构不是数据类型
set 这种数据结构的特点是值是唯一的 所以可以用来数组去重 let arr = [1, 2, 3, 4, 4, 3, 5]; let data = [...new Set(arr)];
map 这种数据结构 跟对象相似放的是一个键值对形式的数据 跟对象的区别是他的 key 值可以是任何的数据类型
新增加的 symbol 才是数据类型 // symbol 是一种新的数据类型 // symbol 的值永远都是唯一的 let a = Symbol("aa"); let b = Symbol("aa"); console.log(a == b);
通过 object.keys ⽅法, 返回值数组,数组⾥⾯包含的是所有属性名 Object.hasOwnProperty() 使⽤ for in 的⽅式
es5 : concat for 循环 Es6: 扩展运算符 map ⽅法
使用 ES6 中的 set 是最简单的去重方法 let arr = [1, 2, 3, 4, 5, 5, 4, 3]; let res = [...new Set(arr)];
利用 Map 数据结构去重 创建一个空 Map 数据结构,遍历需要去重的数组,把数组的每一个元素作为 key 存到 Map 中。由于 Map 中不会出现相同的 key 值,所以最终得到的就是去重后的结果。 3 递归去重 4.forEach+indexOf 定义一个空数组,通过 forEach 循环,indexOf 判断值是否是-1,如果是 push 到新的数组中
1.push() 可以添加一个或多个参数到数组的尾部,添加之后原来的数组会发生改变,返回的是添加后的数组的长度 2.pop() 从数组尾部删除一个元素,原数组会发生改变,返回数组中被删除的元素 3.unshift() 可以添加一个或多个参数到数组的头部,添加后原来的数组会发生改变,返回的是添加后的数组的长度 4.shift() 从数组头部删除一个元素,原数组会发生改变,返回数组中被删除的元素 5.slice() 从数组中截取,如果不传参,会返回原数组。返回截取的值,两个参数 一个是开始的位置,第二个是结束的位置,原数组不变,最多两个参数 6.splice()截取类 没有参数,返回空数组,原数组不变;一个参数,从该参数表示的索引位开始截取,直至数组结束,返回截取的 数组,原数组改变;两个参数,第一个参数表示开始截取的索引位,第二个参数表示截取的长度,返回截取的 数组,原数组改变;三个或者更多参数,第三个及以后的参数表示要从截取位插入的值。 7.reverse() 数组翻转 8.sort() 数组排序 9.join() 数组拼接 10.isArray() 判断是否是数组 11.toString() 数组转字符串 12.concat( ) 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组
遍历数组的方法 它们的参数都是回调函数 forEach 循环 没有 return 对原数组发生改变 filter 返回所有满足条件的新数组 返回值就是新数组 map 遍历数组,可重新构建一个新的数组 返回值就是新数组 find 查找数组中满足条件的第一个数组项 返回这个数组项 findIndex 查找数组中满足条件的第一个数组项的索引 返回这个数组项的索引或者-1 some 遍历数组中,数组中至少有一个满足条件的数组项,返回 true 否则 false every 遍历数组 所有的数组项都满足条件时 返回 ture 否则 false
解构赋值就是从目标对象或数组中提取自己想要的变量。最常用的场景是:element-ui,vant-ui 按需引入,请求接口返回数据,提取想要数据。
常见的几种方式有
默认值
交换变量
Let a=10,b=20; {b,a}={a,b} 3. 将剩余数组赋给一个变量 当结构一个数组时,可以使用剩余模式,将数组剩余部分赋值给一个变量 Let [a,...b]=[1,2,3,4] 4. 给新的变量名赋值 给对象解构的时候可以给变量起一个新的名字 用: 比如:给 age 起别名 let { name, age: userage, tel = "13711111111", } = { name: "小明", age: 18, };
1.JavaScript(以下简称 js)的数据类型分为两种:原始类型(即基本数据类型)和对象类型(即引用数据类型); 两种类型的区别是:存储位置不同: 基本数据类型存储在栈中 引⽤类型的对象存储于堆中
2.js 常用的基本数据类型包括 undefined - - (未定义)、null- - (空的)、number - - (数字)、boolean- - (布尔值)、string- - (字符串)、Symbol - - (符号);
3.js 的引用数据类型也就是对象类型 Object- - (对象),比如:array - - (数组)、function - - (函数)、data - - (时间)以及 es6 新增加的 map 和 set
1.作用域: 作用域就是一个变量可以使用的范围,主要分为全局作用域和函数作用域
全局作用域就是 Js 中最外层的作用域 函数作用域是 js 通过函数创建的一个独立作用域,函数可以嵌套,所以作用域也可以嵌套 Es6 中新增了块级作用域(由大括号包裹,比如:if(){},for(){}等)
2.自由变量: 当前作用域外的变量都是自由变量,作用域链就是 自己没有这个变量就从上一级作用域查找,直到找到为止,直到找到顶层 window,没有的话就报错 3.变量提升: 每个 var 声明的变量,function 声明的函数存在变量提升。变量和函数都有声明提升,let const 不存在变量提升 会把声明语句提到代码的最顶端,赋值语句留在原地等待执行 Var a=10 var a 会被提升 a=10 会留在原地
(在 js 中声明之前未定义,会在 js 的最上方会形成一个预解析池,用来存储声明了但没有先定义的变量名)
闭包: 简单的理解就是函数中套了一个函数,内层函数可以访问外层函数中的变量 有时候需要用到函数内的局部变量,在正常情况下是不能读取到的,这个时候就需要用到闭包。 闭包可以封装对象的私有属性和方法,vue 中的 data 就是一种闭包的形式。 闭包作为回调函数,可以实现函数的复用
优点:闭包因为长期驻扎在内存中。可以重复使用变量,不会造成变量污染 缺点:闭包会使函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,可能会导致内存泄露。解决方法是在退出函数之前,将不使用的变量全部删除。
在 js 中 this 不是固定不变的,它会随着执行环境的改变而改变。要注意的是 this 取什么值,是在执行时确认的,定义时无法确认。 this 的调用大概分为五种场景: 1.浏览器里,在全局范围内的 this 指向 window 对象; 2.在函数中,this 永远指向最后调用他的那个对象; 3.构造函数中,this 指向 new 出来的那个新的对象; 4.箭头函数中 this 比较特殊,箭头函数 this 为父作用域的 this,不是调用时的 this.要知道前四种方式,都是调用时确定,也就是动态的,而箭头函数的 this 指向是静态的,声明的时候就确定了下来;
有三个方法改变 this:
call:参数是单个使用的, apply:参数是一个数组 call 和 apply 都会立刻调用这个函数 bind:只改变 this 不会让函数立刻调用
数据类型判断大概有四种 typeof、instanceof、constructor、Object.prototype.toString.call() 比如说检测 num=10 的数据类型 1.Type: typeof 检测数据类型会返回对应的数据类型小写字符。 引用数据类型中的:Array,Object,Date,RegExp。不可以用 typeof 检测。都会返回小写的 object console.log(typeof num);
2 . instanceof 除了使用 typeof 来判断,还可以使用 instanceof。instanceof 运算符需要指定一个构造函数,或者说指定一个特定的类型,它用来判断这个构造函数的原型是否在给定对象的原型链上。 console.log(arr instanceof Array);
3.constructor constructor 是 prototype 对象上的属性,指向构造函数。我们可以用实例的隐式原型去找到构造函数的值。 console.log(num.proto.constructor);
4 . 使用 Object.prototype.toString.call()检测对象类型 可以通过 toString() 来获取每个对象的类型。每个对象都能通过 Object.prototype.toString() 来检测 console.log(Object.prototype.toString.call(num));
深拷贝拷贝的是值 浅拷贝拷贝的是地址 深拷贝和浅拷贝的区别
浅拷贝: 将原对象的引用直接赋给新对象,新对象只是原对象的一个引用,而不复制对象本身,新旧对象还是共享同一块内存
深拷贝: 创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”,新对象跟原对象不共享内存,修改新对象不会改到原对象
基本数据类型存储在栈中 引⽤类型的对象存储于堆中
数组 深拷贝的方法
可以用 JSON.parse(JSON.stringify())
concat
展开运算符
对象的深拷贝
可以用 JSON.parse(JSON.stringify())
用递归实现
字符串的方法 charAt( ) 方法从一个字符串中返回某个下标上的字符 concat( ) 方法将一个或多个字符串与原字符串连接合并,形成一个新的字符串并返回。
search( ) 获取某个字符或者字符串片段首次出现的位置 match( ) 检索字符串或者正则表达式 replace( ) 替换 replace(参数 1,参数 2); 参数 1:替换谁 参数 2:替换值 split( 参数 1,参数 2 ) 字符串切割,切割后返回数组
slice( ) 和 substring( ) ( 开始位置,结束位置 ) 获取两个索引值之间的字符串片段 从下标 2 开始截取,到下标 4 结束,但不包含 4 substr ( 开始位置,截取的长度 ) 获取两个索引值之间的字符串片段
indexOf( ) 获取某个字符或者字符串首次出现的位置,找到则返回索引值,找不到则返回-1 lastIndexOf( ) 获取某个字符或者字符串最后出现的位置,找到则返回索引值,找不到则返回-1
跟 this 指向的答案背一样就行
1.浏览器里,在全局范围内的 this 指向 window 对象; 2.在函数中,this 永远指向最后调用他的那个对象; 3.构造函数中,this 指向 new 出来的那个新的对象; 4.Call、apply、bind 中的 this 被强绑定在指定的那个对象上; 5.箭头函数中 this 比较特殊,箭头函数 this 为父作用域的 this,不是调用时的 this.要知道前四种方式,都是调用时确定,也就是动态的,而箭头函数的 this 指向是静态的,声明的时候就确定了下来; 6.apply、call、bind 都是 js 给函数内置的一些 API,调用他们可以为函数指定 this 的执行,同时也可以传参。
冒泡排序法 将数组中的相邻两个元素进行比较,将比较大(较小)的数通过两两比较移动到数组末尾(开始),执行一遍内层循环,确定一个最大(最小)的数,外层循环从数组末尾(开始)遍历到开始(末尾)
插入排序法(插队排序) 将要排序的数组分成两部分,每次从后面的部分取出索引最小的元素插入到前一部分的适当位置 ·从第一个元素开始,该元素可以认为已经被排序; ·取出下一个元素,在已经排序的元素序列中从后向前扫描; ·如果该元素(已排序)大于新元素,将该元素移到下一位置; ·重复步骤 3,直到找到已排序的元素小于或者等于新元素的位置; ·将新元素插入到该位置后; ·重复步骤 2~5。
快速排序法 实现思路是,将一个数组的排序问题看成是两个小数组的排序问题,以一个数为基准(中间的数),比基准小的放到左边,比基准大的放到右边,而每个小的数组又可以继续看成更小的两个数组,一直递归下去,直到数组长度大小最大为 2。
数组扁平化就是将一个多维数组转换为一个一维数组,比如 [[1,2,3],3,[2,3]] =>[1,2,3,3,2,3] 实现基本方式 1、对数组的每一项进行遍历。 2、判断该项是否是数组。 3、如果该项不是数组则将其直接放进新数组。 4、是数组则回到 1,继续迭代。 5、当数组遍历完成,返回这个新数组。
垃圾回收: 浏览器的 js 具有自动垃圾回收机制,垃圾收集器会定期的找出那些不在继续使用的变量,然后释放内存。垃圾回收器会按照固定的时间间隔周期性的执行。 js 中最常用的垃圾回收方式就是标记清除。例如,在函数中声明一个变量,就将这个变量标记为“进入环境”。而当变量离开环境时,则将其标记为“离开环境”。 引用计数的含义是跟踪记录每个值被引用的次数。当这个值的引用次数变成 0 时,则说明没有办法再访问这个值了,就把它回收掉。
内存泄漏:
循环引用 一个很简单的例子:一个 DOM 对象被同一个多个 Javascript 对象引用,这个 DOM 对象可能会引发内存泄露。要想破坏循环引用,需要赋值为 null。
闭包 在闭包中引入闭包外部的变量时,当闭包结束时此对象无法被垃圾回收(GC)。
DOM 泄露 当原有的 DOM 被移除时,子节点引用没有被移除则无法回收
Times 计时器泄露
1.创建 xhr 核心对象 var xhr=new XMLHttpRequest();
2.调用 open 准备发送 (参数一:请求方式 参数二: 请求地址 参数三:true 异步,false 同步 xhr.open('post','http://www.baidu.com/api/search',true) )
3.如果是 post 请求,必须设置请求头。
4.调用 send 发送请求 (有参数就写在括号里,) xhr.send()
可以用 onload 事件 触发请求成功的操作 或者也可以用 (监听异步回调 onreadystatechange 判断 readyState 为 4 表示请求完成 判断 status 状态码 为 200 表示接口请求成功 responeseText 为相应数据。字符串类型。)
备注:如果是 post 请求,想要传 json 格式参数。
设置请求头的时候设置为 json 格式
1.xhr.setRequestHeader('Content-Type', 'application/json')
open 发送数据 2.xhr.open({_id:xxx,user:xxxx,age:xxxx})
JSONP 原理:
ajax 请求受同源策略影响,不允许进行请求,我们利用 script 标签的 src 属性不受同源策略的约束,利用这个特性 jsonp 需要以下步骤:
动态创建(document.createElement('script'))
设置 src 属性,(src 中要包含参数 callback=fn)进行跨域请求
将 添加到页面中执行 (body.appendChild('script'))
页面要提前定义好 callback。
后端会返回回调函数执行并包裹参数 callback(data) 备注: 服务端不再返回 JSON 格式的数据,而是返回回调函数包裹数据(fn({name:'tom',age:18}),在 src 中进行了调用,这样实现了跨域。
本地存储分为 cookie、localStorage、sessionStorage, Cookie Cookie 设计初衷是用来和服务器通讯,而不是本地存储,他只是被‘借用’到本地存储。 Cookie 有一些缺点:存储空间小,最大 4k、用的时候比较麻烦
localStorage、sessionStorage 在 HTML5 中,新加入了一个 localStorage/sessionStorage 特性,这个特性主要是用来作为本地存储来使用的,解决了 cookie 存储空间不足的问题(cookie 中每条 cookie 的存储空间为 4k),localStorage 中一般浏览器支持的是 5M 大小。 优点:HTML5 专门为存储而设计,最大可存 5M、API 简单易用 setItem getItem、不会随着 http 请求被发送出去
localStorage、sessionStorage.cookie 不同点:
Cookie 可以设置过期时间 存储容量比较小 用起来没有方便的方法 localStorage 数据会永久存储,除非代码或手动删除 sessionStroage 数据只存在于当前会话,浏览器关闭则清空 一般用 localStorage 会更多一些
注意:localStorage 只支持 string 类型的存储。
js 中的一个机制,就是遇到宏任务,先执行宏任务,将宏任务放入 eventqueue,然后在执行微任务。
宏任务和微任务都包含一些事件 宏任务:setTimeout,setInterval,Ajax,DOM 事件 微任务:Promise async/await
js 是单线程的,只能做一件事情,如果碰到异步任务就会把异步任务放在任务队列里等待执行,等到同步任务执行完成了,而且异步任务准备好了就会被放进主线程等待依次执行。异步的特点不会阻塞后面代码的执行。也就是请求处理过程中,你不能闲着,会产生异步的请求。回头再处理,然后继续执行下面的请求。 异步和单线程是相辅相成的,js 是一门单线程脚本语言,所以需要异步来辅助
DOM 是网页中用来表示文档中对象的标准模型,他是由节点和对象组成的结构集合。在浏览器解析 HTML 标签时,会构建一个 DOM 树结构。 由此呢 js 也提供了一些 dom 的操作 一、dom 元素获取 1.document.getElementById(id 的值) 通过 id 来获取元素的对象,返回值是一个对象 2.document.getElementsByName(name) 通过 name 属性来获取对象的,返回值是一个数组,与 getElementById()方法类似,但他是查询的 name 元素,而不是 id 属性 3.document.getElementsByTagName() 通过标签来获取元素的对象, 返回值是一个数组 4.document.getElementsByClassName() 通过 class 类名来获取的对象,返回值是一个数组 5.document.querySelector() css 选择器,返回与该模式匹配的第一个元素,结果为一个元素;如果没找到匹配的元素,则返回 null 6.document.querySelectorAll() css 选择器,返回与该模式匹配的所有元素,结果为一个类数组二、dom 创建 二、dom 操作
创建:新的标签(元素节点) = document.createElement("标签名")
删除:父节点.removeChild(子节点);
插入:insertBefore(新插入的节点,参照物节点) 往某个节点的前面插入一个新的节点
追加:appendChild(新的节点的名) 当前对象追加一个子节点
事件绑定的方法 一是直接在标签内直接添加执行语句,二是绑定函数。第三种 是事件监听(addEventListener)
DOM 事件两种类型 (事件传播也背这个答案) 事件类型分两种:事件捕获、事件冒泡。 事件捕获就是由外往内,从事件发生的顶点开始,逐级往下查找,一直到目标元素。 事件冒泡就是由内往外,从具体的目标节点元素触发,逐级向上传递,直到根节点。
怎么阻止事件冒泡:vue 里是 stop 原生 js 可以 return false 还有 stopPropagation
事件委托,又名事件代理。事件委托就是利用事件冒泡,就是把子元素的事件都绑定到父元素上。如果子元素阻止了事件冒泡,那么委托也就没法实现了
好处:提高性能,减少了事件绑定,从而减少内存占用 比如:瀑布流:无限上拉列表中,如果给每一个图片绑定点击事件,非常繁琐且消耗内存。所以我们可以把每张图片上的点击事件委托给共同的父元素。
我们在封装这个函数的时候可以用事件监听来实现 ,封装的函数有三个参数,第一个是要绑定事件的元素,第二个是要绑定的事件类型,第三个是事件的执行函数。 调用这个函数 就可以实现给某个元素绑定一个事件了。
Typescript 是⼀个强类型的 JavaScript 超集,⽀持 ES6 语法,⽀持⾯向对象编程的概念,如类、接⼝、继承、泛型等。 Typescript 并不直接在浏览器上运⾏,需要编译器编译成纯 Javascript 来运⾏。
增加了静态类型,可以在开发⼈员编写脚本时检测错误,使得代码质量更好,更健壮。 优势:
杜绝⼿误导致的变量名写错;
类型可以⼀定程度上充当⽂档;
IDE ⾃动填充,⾃动联想;
为编程阶段还不清楚类型的变量指定⼀个类型。 这种情况下,我们不希望类型检查器对这些值进⾏检查⽽是直接让它们通过编译阶段的检查。
any : 动态的变量类型(失去了类型检查的作⽤)。 never : 永不存在的值的类型。例如:never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的 返回值类型。 unknown : 任何类型的值都可以赋给 unknown 类型,但是 unknown 类型的值只能赋给 unknown 本身和 any 类型。 null & undefined : 默认情况下 null 和 undefined 是所有类型的⼦类型。 就是说你可以把 null 和 undefined 赋值给 number 类型的变量。当你指定了 --strictNullChecks 标记,null 和 undefined 只能赋值给 void 和它们各⾃。 void`: 没有任何类型。例如:⼀个函数如果没有返回值,那么返回值可以定义为 void。
相同点:
都可以描述 '对象' 或者 '函数'的结构
都允许拓展(extends) 不同点:
type 可以声明基本类型,联合类型,元组
type 可以使⽤ typeof 获取实例的类型进⾏赋值
多个相同的 interface 声明可以⾃动合并 使⽤ interface 描述‘数据结构’,使⽤ type 描述‘类型关系’
WXML :微信⾃⼰定义的⼀套组件 WXSS : ⽤于描述 WXML 的组件样式 js : 逻辑处理 json : ⼩程序⻚⾯配置
在 ⻚⾯标签上通过 绑定 data-key = value , 然后绑定点击通过 e.currentTarget.dataset.key 来获取标签上 绑定的值。
wxss 背景图⽚只能引⼊外链,不能使⽤本地图⽚ ⼩程序样式使⽤ @import 引⼊ 外联样式⽂件,地址为相对路径。 尺⼨单位为 rpx , rpx 是响应式像素,可以根据屏幕宽度进⾏⾃适应。
⼩程序 直接使⽤ this.data.key = value 是 不能更新到视图当中的。 必须使⽤ this.setData({ key :value }) 来更新值。
onLoad : ⻚⾯加载时触发。⼀个⻚⾯只会调⽤⼀次,可以在 onLoad 的参数中获取打开当前⻚⾯路径中的参数 onShow : ⻚⾯显示 / 切⼊前台时触发调⽤。 onReady : ⻚⾯初次渲染完成时触发,⼀个⻚⾯只会调⽤⼀次。 onHide : ⻚⾯隐藏 / 切⼊后台时触发,如 navigateTo 或底部 tab 切换到其他⻚⾯,⼩程序切⼊后台等 onUnload : ⻚⾯卸载时触发。如 redirectTo 或 navigateBack 到其他⻚⾯时.
两种⽅案 ⽅案 ⼀ : 通过在 app.json 中, 将 "enablePullDownRefresh": true, 开启全局下拉刷新。 或者通过在 组件 .json , 将 "enablePullDownRefresh": true, 单组件下拉刷新。 ⽅案⼆: scroll-view :使⽤该滚动组件 ⾃定义刷新,通过 bindscrolltoupper 属性, 当滚动到顶部/左边,会触发 scrolltoupper 事件,所以我们可以利⽤这个属性,来实现下拉刷新功能。
相同点: 都是点击事件 不同点: bindtap 不会阻⽌冒泡, catchtap 可以阻⽌冒泡。
使⽤全局变量 在 app.js 中的 this.globalData = { } 中放⼊要存储的数据。 在 组件.js 中, 头部 引⼊ const app = getApp(); 获取到全局变量 直接使⽤ app.globalData.key 来进⾏赋值和获取值。
使⽤ 路由 wx.navigateTo 和 wx.redirectTo 时,可以通过在 url 后 拼接 + 变量, 然后在 ⽬标⻚⾯ 通过在 onLoad 周期 中,通过参数来获取传递过来的值。
使⽤本地缓存
wx.navigateTo() : 保留当前⻚⾯,跳转到应⽤内的某个⻚⾯。但是不能跳到 tabbar ⻚⾯ wx.redirectTo() : 关闭当前⻚⾯,跳转到应⽤内的某个⻚⾯。但是不允许跳转到 tabbar ⻚⾯ wx.switchTab() : 跳转到 TabBar ⻚⾯,并关闭其他所有⾮ tabBar ⻚⾯ wx.navigateBack() : 关闭当前⻚⾯,返回上⼀⻚⾯或多级⻚⾯。可通过 getCurrentPages() 获取当前的⻚⾯栈,决 定需要返回⼏层 wx.reLaunch() : 关闭所有⻚⾯,打开到应⽤的某个⻚⾯。
wx:if : 有更⾼的切换消耗。 hidden : 有更⾼的初始渲染消耗。 使⽤ 频繁切换使⽤ hidden , 运⾏时条件变化使⽤ wx: if
pages : ⽤于存放当前⼩程序的所有⻚⾯路径 window : ⼩程序所有⻚⾯的顶部背景颜⾊,⽂字颜⾊配置。 tabBar : ⼩程序底部的 Tab ,最多 5 个,最少 2 个。
封装 wx.request 请求传递需要的参数( url , data , method , success 成功回调 , fail 失败回调 ) , 封装常⽤⽅ 法 POST , GET , DELETE , PUT .... 最后导出这些⽅法 然后新建⼀个 api.js ⽂件,导⼊封装好的⽅法,然后调取相应的⽅法,传递数据。
热启动 :假如⽤户已经打开了某个⼩程序,在⼀定时间内再次打开⼩程序的话,这个时候我们就不再需要重新启动了,这 需要把我们的后台打开的⼩程序切换到前台来使⽤。 冷启动 :⽤户⾸次打开⼩程序或被微信主动销毁再次打开的情况,此时⼩程序需要重新加载启动。
⼩程序在进⼊后台之后,客户端会帮我们在⼀定时间内维持我们的⼀个状态,超过五分钟后,会被微信主动销毁. 官⽅也没 有明确说明 什么时候销毁, 在不同机型表现也不⼀样, 2019 年开发时:时间官⽅⽂档没有说明,但是经过询问⼀般指 5 分 钟内 2020 年开发时:时间官⽅⽂档没有说明,实测安卓没有固定时间,内存⾜够情况下,有时候⼀天了还在,有时候⼏分钟 就没了
传参是传参 跳转是跳转 传参就是我们之前背的 query params 动态路由传参
路由跳转: 1.router-link 标签跳转 2.编程式导航 this.$router.push() 3.this.$router.replace({path:‘/’ } ) (replace 跟 push 类似,只不过跳转之后不存历史记录)
在路由跳转的时候可以携带参数 可以在这里说我们之前背的路由的三种传参
路由的配置: 在 router/index.js 中配置路由规则 路由规则中的 path 属性代表路径,name 属性是名字,component 属性代表在这个路径下加载什么组件,还可以用 children 属性配置子路由。还可以用 meta 定义路由的源信息。
动态路由: 动态路由是指路由器能够自动的建立自己的路由表,并且能够根据实际情况的变化实时地进行调整。用:开头,:后面跟的值是不确定的。这个值是我们要传递的参数 嵌套路由: vue 项目中,界面通常由多个嵌套的组件构成,用 children 实现嵌套路由
自定义指令: 使用位置:写在在标签里面 以 v-开头
使用场景:需要对普通 DOM 元素进行操作,这时候就会用到自定义指令 比如 我们可以使用自定义指令让一个元素进入页面就获得焦点,拖拽
// 注册一个全局自定义指令 `v-focus` Vue.directive("focus", { // 当被绑定的元素插入到 DOM 中时…… inserted: function (el) { // 聚焦元素 el.focus(); }, });
自定义指令的钩子函数 一个指令定义对象可以提供如下几个钩子函数 (均为可选):
bind:指令第一次绑定到元素时调用。
inserted:被绑定元素插入父节点时调用
update:所在组件的 虚拟 dom 更新时调用,
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
钩子函数的参数 即 el、binding el 指令所绑定的元素 binding:一个对象,包含这个自定义指令的一些信息
在很多时候,你可能想在 bind 和 update 时触发相同行为,而不关心其它的钩子就可以简写为一个函数
过滤器: 使用位置:双花括号插值 和 v-bind 表达式 使用场景 :处理数据格式的 比如说 我们可以用过滤器过滤时间格式
两个重点题 一个 BFC 一个说⼀下 localStorage、sessionStorage 和 cookie 的区别?(必问)(必须要背)
1.2 BFC 的理解? 答: 1.⾸先 html5 增加了 header,footer,nav,aside,section 等语义 化标签, 2.在表单⽅⾯,为了增强表单,为 input 增加了 color,email,data ,range 等类型, 3.在存储⽅⾯,提供了 sessionStorage,localStorage,和离线存储 4.在多媒体⽅⾯规定了⾳频和视频元素 audio 和 vedio 5.css3 新增特性: CSS3 边框如 border-radius,box-shadow 等; CSS3 2D,3D 转换如 transform 等; CSS3 动画如 animation 等
1.BFC(Block Formatting Context),即块级格式化上下⽂,它是⻚⾯中的⼀块渲染区域,并且有 ⼀套属于⾃⼰的渲染规则: BFC ⽬的是形成⼀个相对于外界完全独⽴的空间,让内部的⼦元素不会影响到外部的元素
2.触发 BFC 的条件包含不限于:
根元素,即 HTML 元素
浮动元素:float 值为 left、right
overflow 值不为 visible,为 auto、scroll、hidden
display 的值为 inline-block、inltable-cell、table-caption、table、inline-table、 flex、inline-flex、grid、inline-grid
position 的值为 absolute 或 fixed 3.利⽤ BFC 的特性,我们将BFC
应⽤在以下 防止 margin 塌陷 清浮动
实现⽔平垂直居中的⽅法有: 利⽤定位+margin:auto 利⽤定位+transform 利⽤定位+margin:负值 flex 布局
圣杯布局 双飞翼布局 (这两种基本都是靠 浮动定位和负的 margin 实现的) 两栏 flex
利用伪元素 after 内容设为空 然后给边框一个宽度 把其他方位都设置为透明的 就出现了一个三角形
答:em/px/rem/vh/vw 区别如下: px:绝对单位,⻚⾯按精确像素展示 em:相对单位,基准点为⽗节点字体的⼤⼩,如果⾃身定义了 font-size 按⾃身来计算,整个⻚⾯ 内 1em 不是⼀个固定的值 rem:相对单位,可理解为 root em , 相对根节点 html 的字体⼤⼩来计算 vh、vw:主要⽤于⻚⾯视⼝⼤⼩布局,在⻚⾯布局上更加⽅便简单
从输⼊ URL 到回⻋后发⽣的⾏为如下: URL 解析 DNS 查询 TCP 连接 HTTP 请求 响应请求 ⻚⾯渲染
GET 请求只能进⾏ url 编码,⽽ POST ⽀持多种编码⽅式。 GET 请求在 URL 中传送的参数是有⻓度限制的,⽽ POST 没有。 GET ⽐ POST 更不安全,因为参数直接暴露在 URL 上,所以不能⽤来传递敏感信息。 GET 参数通过 URL 传递,POST 放在 Request body 中 提交⽅式除了 GET 和 POST 外,还有 put 和 delete
1.HTTP (HyperText Transfer Protocol),即超⽂本运输协议,是实现⽹络通信的⼀种规范 https 可以理解为是加密的 http 协议,HTTPS 需要 SSL,SSL 证书需要钱,功能越强⼤的证书费⽤越⾼
1 表示消息 2 表示成功 3 表示重定向 4 表示请求错误 5 表示服务器错误
200(成功):请求已成功, 404(未找到): 服务器找不到请求的⽹⻚ 500(服务器内部错误):服务器遇到错误,⽆法完成请求