VUE
事件修饰符
- .prevent 阻止默认行为(例如:阻止a链接的挑战)
- .stop 阻止事件冒泡
- self 只当在 event.target 是当前元素自身时触发处理函数,相当于阻止事件冒泡
- once 绑定了事件以后只能触发一次,第二次就不会触发
- caption 用于事件捕获
v-model 表单修饰符
- .number 自动将用户输入值转为数值类型
- .trim 自动过滤用户输入的收尾空白字符
- .lazy 在“change”时而非“input”更新
v-bind 修饰符
- sync 对 props 进行一个双向绑定
- prop 设置之定义标签属性,避免暴露数据,防止污染 HTML 结构
- camel 驼峰化
sync 注意点: - 使用 sync 的时候,子组件传递事件名格式必须为 Update:value, 其中 value 必须与子组件中的 props 中的声明名称完全一致
- 注意带有 .sync 修饰符的 v-bind 不能和表达式一起使用
// 父组件
<comp :myMessage.sync="bar"></comp>
// 子组件
this.$emit('update:myMessage',params);
v-if 和 v-show
v-if 有更高的切换开销,v-show 有更高的初始渲染开销
- 如果有频繁的切换,用v-show
- 如果在运行条件很少时,用v-if
v-show 原理:不管初始条件是什么,元素总是会被渲染
v-if 原理: render 函数通过表达式的值来决定是否生成 DOM
过滤器
过滤器的注意点
- 要定义到 filters 节点下,本质是一个函数
- 在过滤器函数中,一定要有 return 值
- 在过滤器的形参中,可以获取到“管道符 [ | ]”前面待处理的那个值
- 如果全局过滤器和私有过滤器名字一致,此时按照“就近原则”,调用的是”私有过滤器“
- 一个表达式可以使用多个过滤器。过滤器之间需要用管道符 “ | ”隔开。其执行顺序从左往右
应用场景:
单位转换、数字打点、文本格式化、时间格式化
原理: - 在编译阶段通过 parseFilters 将过滤器编译成函数调用(串联过滤器则是一个嵌套的函数调用,前一个过滤器执行的结果是后一个过滤器函数的参数)
- 编译后通过调用 resolveFilters 函数找到对应过滤器并返回结果
- 执行结果作为参数传递给 toString 函数,而 toString 执行后,其结果会保存在 Vnode 的 text 属性中,渲染到视图
watch 侦听器
侦听器的格式
- 方法格式的侦听器
- 缺点1:无法在刚进入页面的时候,自动触发!!!
- 缺点2:如果侦听的是一个对象,如果对象中的属性发生了变化,不会触发侦听器!!!
- 对象格式的侦听器
- 好处1:可以通过 immediate 选项,让侦听器自动触发!!!
- 好处2:可以通过 deep 选项,让侦听器深度监听对象中每个属性的变化!!!
计算属性
特点:
- 定义的时候,要被定义为“方法”
- 在使用计算属性的时候,当普通的属性使用即可
好处:
- 实现了代码的复用
- 只要计算属性中依赖的数据源变化了,则计算属性会自动重新求值!
异步渲染 ( 批量的 )
数据发生变化是,vue 并不会立即去更新 Dom ,而是将修改数据的操作放在了一个异步操作队列中,如果我们一直修改相同数据,异步操作队列还会进行去重,等待同一事件循环中的所有数据变化完成之后,会将队列中的时间拿来进行处理,进行 DOM 更新
$nextTick 下一次 DOM ( 渲染 ) 更新周期之后执行
插槽 v-solt
- 默认插槽
- 作用域插槽 ( 父组件给子组件插槽传值 )
- 具名插槽
总结: - v-slot 属性只能在 < template > 上使用,但是只有默认插槽
- 默认插槽名为default,可以省略default直接写v-slot
- 缩写为#时不能不写参数,写成#default
- 可以通过解构获取v-slot={user},还可以重命名v-slot=“{user: newName}“和定义默认值v-slot=”{user = ‘默认值’}”
原理:
slot本质上是返回VNode的函数,一般情况下,Vue中的组件要渲染到页面上需要经过template -> render function -> VNode -> DOM 过程
组件之间传值
- 父子 关系
- 兄弟 关系
1. 父子关系
1). props/$emit => 父传子,可以通过 自定义属性, 子传父,可以通过 自定义事件
2). provide/inject => 父传子,子传孙, provide 将数据以对象的形式传出去, 在孙组件中 inject 接收
3). $attrs/$listeners => 父传子, 子传孙,类似与第一种,给中间的子组件加上 v-on="$listeners"
<sun v-bind="$attrs" v-on="$listeners"></sun>
2. 兄弟组件
EventBus => $emit/$on
1). 创建 eventBus.js 模块, 并向外共享一个 Vue 的实例对象
2). 在数据发送方, 调用 bus.$emit ('事件名称', 要发送的数据) 方法触发自定义事件
3). 在数据接收方,调用 bus.$on('事件名称',事件处理函数) 方法注册一个自定义事件
3. 全局
vuex
4. ref
ref 引用
ref 用来获取 DOM 元素或者 组件 的引用
生命周期
生命周期 | 描述 |
---|---|
beforeCreate | 组件实例被创建之初,组件的属性生效之前 |
created | 组件实例已经完全创建,data 数据初始化,属性也绑定,但真实的 DOM 还没生成,$el( 虚拟 DOM ) 还不可用 |
beforeMount | 在挂在之前被调用,$el 初始化:相关的 render 函数首次被调用 |
mounted | el 被新创建的 vm.$el 替换,完成挂载 |
beforeUpdate | 组件数据更新之前调用,发生在虚拟 DOM 打补丁之前 |
update | 组件数据更新之后 |
activited | keep-alive 专属,组件被激活时调用 |
deactivated | keep-alive 专属,组件被销毁时调用 |
beforeDestory | 组件销毁前调用 |
destoryed | 组件销毁后调用 |
初始化顺序: props、methods、data
父子组件的加载的生命周期:父 beforeCreate => 父 create => 父 beforeMount => 子 beforeCreate => 子 create => 子 beforeMount => 子 mounted => 父 mounted
在哪个生命周期内调用异步请求
created、beforeMount 、mounted
推荐 created 中请求异步
- 能更快的获取到服务端数据,减少页面的 loading 时间
- ssr 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性
- 请求在 mounted 中有可能会导致页面闪动
SSR
SSR 服务端渲染,在服务端将标签渲染成整个 html 片段,直接返回给客户端
优点:
1). 更好的SEO( 搜索引擎优化 )
2). 首屏加载更快,不需要下载 Vue 编译后的 js 和 css
缺点:
1). 更多的开发条件限制:例如服务端渲染只支持 beforeCreate 和 created 这两个钩子函数
2). 更多的服务器负载: 在服务器中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用 CPU 资源
说说你对 VUE 的理解?
Vue.js 是一个用于创建用户界面的开源 JavaScript 框架,也是一个创建单页面(SPA)的 web 应用框架
Vue 的核心特征:
- 数据驱动 (MVVM)
- 组件化:把图形、非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式
- 指令系统
组件化的优势:
- 降低整个系统的耦合度,在保持接口不变的情况下,我们可以替换不同的组件快速完成需求,例如输入框,我们可以替换为日历、时间、范围等组件作具体的实现
- 调试方便,由于整个系统是通过组件组合起来的,在出现问题的时候,可以排除法直接移除组件、或者根据报错的组件快速定位问题,是因为每个组件之间低耦合,职责单一,所以逻辑分析会比整个系统要简单
- 提高可维护性,由于每个组件的职责单一,并且组件在系统是被复用的,所以对代码进行优化可获得系统的整体升级
指令系统:
指令是带有 v- 前缀的特殊属性作用:当表达式的值改变时,将其产生的连带影响,响应式的作用于 DOM 。v-if 、v-for、v-bind、v-model
Vue 和 React 对比
相同点:
- 都有组件化思想
- 都支持服务器端渲染
- 都有 Virtual DOM (虚拟 DOM)
- 数据驱动视图
- 都有支持 native 的方案:Vue 的 weex、React 的React native
- 都有自己的构建工具:Vue 的 vue-cli、React 的 create React App
区别
- 数据变化的实现原理不同。React 使用的是不可变数据,而 Vue 使用的是可变数据
- 组件化通信的不同。React 中我们通过使用回调函数进行通信,而 Vue 中子组件向父组件传递消息方式有两种方式:事件和回调函
- diff 算法不同。React 主要使用 diff 队列保存需要更新哪些 DOM,得到 patch 树,再统一操作批量更新 DOM。Vue 使用双指针,边对比,边更新 DOM
说说对双向绑定的理解
MVVM
VM (ViewModel)的主要职责是:
- 数据变化后更新视图
- 视图变化后更新数据
流程:
- new Vue() 首先执行初始化,对 data 执行响应化处理,这个过程发生在 Observe 中
- 同时对模版执行编译,找到其中动态绑定的数据,从 data 中初始化视图,这个过程发生在 Complie 中
- 同时定义一个更新函数和 Watcher ,将来对应数据变化时 Watcher 会调用更新函数
- 由于 data 的某个 key 在视图中可能出现多次,所以每个 key 都需要一个管家 Dep 来管理多个 Watcher
- 将来 data 中的数据一旦发生变化,会首先找到对应的 Dep ,通知所有的 Watcher 执行更新函数
说说对 SPA 单页应用 的理解
SPA 是一种 网络应用程序或者网站的模型
优点:
- 具有桌面应用的即时性、网站的可移植性和可访问性
- 用户体验号、快,内容的改变不需要更新加载整个页面
- 良好的前后端分离,分工更明确
缺点: - 不利于搜索引擎的抓取
- 首次渲染速度相对较慢
单页面应用 (SPA) | 多页面应用(MPA) | |
---|---|---|
组成 | 一个页面或者多个页面片段 | 多个页面 |
刷新方式 | 局部刷新 | 整页刷新 |
URL 模式 | 哈希模式 | 历史模式 |
SEO 搜索引擎化 | 难实现,可使用 SSR方式改善 | 容易实现 |
数据传递 | 容易 | 通过 URL、cookie、localStorage等传递 |
页面切换 | 速度快,用户体验号 | 切换加载资源,速度慢,用户体验差 |
维护成本 | 相对容易 | 相对复杂 |
如何给 SPA 做 SEO
- SSR 服务端渲染
- 静态化:1)通过程序将动态页面抓取并保存为静态页面,这样的页面实际存在于服务器硬盘中;2)Web 服务器的 URL Rewrite 的方式,原理是通过 Web 服务器内部模块按照一定的规则将外部的 URL 请求转化为内部的文件地址,一句话来说就是把请求的静态地址转化为动态页面地址,而静态页面实际是不存在的
- 使用 Phantomjs 针对爬虫处理
Vue 实例挂载的过程中发生了什么?
- new Vue 的时候会调用 _init 方法
* 定义 $ set 、$ get、 $ delete 、$watch 等方法
* 定义 $ on、$ off 、$ emit 、等事件
* 定义 _update、$ forceUpdate、$ destroy 等生命周期 - 调用 $ mount 进行页面的挂载
- 挂载的时候主要通过 mountComponent 方法
- 定义 updateComponment 更新函数
- 执行 render 生成虚拟 DOm
- _update 将虚拟 DOM 生成真实 DOM 结构,并且渲染到页面中
SPA 首屏加载速度慢怎么解决
先计算 通过 DOMContentLoad 或者 performance 计算出首屏时间
// 方案一:
document.addEventListener('DOMContentLoaded', (event) => {
console.log('first contentful painting');
});
// 方案二:
performance.getEntriesByName("first-contentful-paint")[0].startTime
// performance.getEntriesByName("first-contentful-paint")[0]
// 会返回一个 PerformancePaintTiming的实例,结构如下:
{
name: "first-contentful-paint",
entryType: "paint",
startTime: 507.80000002123415,
duration: 0,
};
加载慢的原因:
- 网络延迟问题
- 资源文件体积是否过大
- 资源是否重复发送请求去加载了
- 加载脚本的时候,渲染内容堵塞了
解决方案:
几种常用的 SPA 首屏优化方式:
- 减小入口文件体积
- 静态资源本地缓存:强缓存、协商缓存、Service Worker 离线缓存
- UI 架构按需加载:
- 图片资源的压缩:矢量字体、精灵图
- 组件重新打包
- 开启 GZip 压缩:使用 compression-webpack-plugin
- 使用 SSR
减小入口文件体积:
- 路由懒加载,配置路由采用动态路由
- externals 加载外部 CDN 资源
v-if 和 v-for 不建议一起用
在 Vue 2.0 中 v-for 的优先级比 v-if 高 ,作用域同一个元素上,带来性能的浪费(每次渲染都会先循环在进行条件判断)
单在 Vue 3.0 中 v-if 的优先级永远比 v-for 高
为什么 data 属性是一个函数而不是一个对象
是为了防止同一个组件多次创建实例对象共用一个 data,产生数据污染。采用函数的形式,initData 是会将其作为工厂函数都会返回全新 data 对象
Vue 中给对象添加新属性界面不刷新?
- Vue.set( target: Object | Array ,proprertyName/index : String| number, value: Number)
- Object.assign()
- $forcecUpdated()
小结: - 如果为对象添加少量的新属性,可以直接采用 Vue.set()
- 如果需要为新对象添加大量的新属性,则通过 Object.assign() 创建对象
- 如果实在是不知道怎么操作时候,可采用 $forceUpdate() 进行强制刷新(不建议)
Vue 中组件和插件有什么区别
插件:
- 添加全局方法或者属性。如:vue-custom-element
- 添加全局资源:指令/ 过滤器 / 过渡 等。如 vue-touch
- 通过全局混入来添加一些组件选项。如 vue-router
- 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现
- 一个库,提供自己的 API,同时提供上面提到的一个或者多个功能。如 vue-router
组件 | 插件 | |
---|---|---|
编写形式 | vue 单文件,每一个 .vue 文件都可以看成一个组件 | vue 插件的实现应该暴露一个 install 方法,这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象 |
注册形式 | 全局注册和局部注册 | 通过 Vue.use() 注册 |
使用场景 | 用来构成你的 App 的业务模块 | 对 Vue 的功能的增强或者补充 |
说说对 vue 的 mixin 的理解,有什么应用场景?
局部混入 和 全局混入
合并策略:
- 替换型:props、methods、inject、computed
- 合并型:data,通过 set 方法进行合并和重新赋值
- 队列型:全部生命周期和 watch,原理是将函数存入一个数组,然后正序遍历执行
- 叠加型:component、directives、filters 通过原型链进行层层的叠加
SSR 解决了什么问题?
解决:
- 利于 SEO
- 首屏呈现渲染,提高首屏加载速度
缺点: - 复杂度:整个项目的复杂度
- 库的支持性,代码兼容
- 性能问题:内存消耗变大、缓存、降级
- 服务器负载变大
过程:
- 使用 ssr 不存在单例模式,每次用户请求都会创建一个新的 vue 实例
- 使用 ssr 需要实现服务端首屏渲染和客户端激活
- 服务端异步获取数据 asyncData 可以分为首屏异步获取和切换组件获取
- 首屏异步获取数据,在服务端预渲染的时候就应该已经完成
- 切换组件通过 mixin 混入,在 beforeMount 钩子完成数据获取
Vue.observable
Observable 翻译过来我们可以理解成可观察的
Vue.observable,让一个对象变成响应式数据。Vue 内部会用它来处理 data 函数返回的对象
返回的对象可以直接用于渲染函数和计算属性内,并且会在发生变更时触发相应的更新。也可以作为最小化的跨组件状态存储器
Vue 中 key 的原理
使用场景:
- 当我们在使用 v-for 时,需要给单元加上 key
- 用 +new Date() 生成的时间戳作为 key ,手动强制触发重新渲染
key的作用:
key 是给每一个 vnode 的 唯一 id,也是 diff 的一种优化策略,可以根据 key,更准确,更快的找到对应的 vnode 节点
keep-alive
keep-alive 是 vue 中的内置组件,能在组件切换过程中将状态仍保留在内存中,防止重复渲染 DOM
props 属性:
- include - 字符串或正则表达式,只有名称匹配的组件会被缓存
- exclude - 字符串或者正则表达式,任何名字匹配的组件都不会被缓存
- max - 数字,最多可以缓存多少组件实例
自定义指令
指令系统:vue 中提供了一套为数据驱动视图更为方便的操作
注册指令:全局注册 和 局部注册 (directive)
应用场景:
- 防抖
- 图片懒加载
- 一键 Copy 功能
- 权限校验
Vue 项目中有封装过 axios ?
axios 是一个轻量的 HTTP 客户端,基于 XMLHTTPRequest 服务来执行 HTTP 请求
特性:
- 从浏览器中创建 XMLHttpRequest
- 从 node.js 创建 http 请求
- 支持 Promise APL
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换 JSON 数据
- 客户端支持 防御 XSRF
如何封装:
- 封装的同时,你需要和后端协商好一些约定,请求头、状态码、请求超时时间…
- 设置接口请求前缀:根据开发、测试、生产环境的不同,前缀需要加以区分
- 请求头:来实现一些具体的业务,也必须携带一些参数才可以请求
- 状态码:根据不同接口返回的不同 status,来执行不同的业务
- 请求方法:根据 get 、post 等方法进行一个再次封装
- 请求拦截器:更加请求的请求头设定,来决定哪些请求可以访问
- 响应拦截器:根据后端返回的状态码进行判定执行不同的业务
Vue 项目的目录结构
基本原则:
- 文件夹和文件夹内部语义一致性
- 单一入口/出口
- 就近原则,紧耦合的文件应该放到一起,且应以相对路径引用
- 公共的文件应该以绝对路径的方式从根目录引用
- ./src 外的文件不应该被引入
Vue 做权限管理
前端权限控制可以分为四个方面:
- 接口权限 JWT
- 按钮权限
- 菜单权限
- 路由权限
vue 项目如何部署,404 问题
为什么在 history 模式下有问题?没有刷新
为什么在 hash 模式下没有问题?
解决方案: nginx 重定向
如何处理 Vue 项目中的错误的
主要错误来源:
- 后端接口错误: axios 的 interceptor 实现网络请求的 response 先进行一层拦截
- 代码本身逻辑的错误:全局设置错误处理