vue2生命周期
beforeCreate(){}#初始化之后、
created(){}#实例创建完成后被立即调用
beforeMount(){}#在挂载开始之前被调用
mounted(){}#被挂载后调用
beforeUpdate#数据更新时调用
updated(){}#组件 DOM 已经更新
beforeDestroy(){}#实例销毁之前调用
destroyed(){}#实例销毁后调用
activated(){}#被keep-alive 缓存的组件激活时调用
deactivated(){}#被keep-alive 缓存的组件停用时调用
errorCaptured(){}#捕获一个来自子孙组件的错误时被调用
补充:不要在updated中修改data数据,很容易造成死循环
#vue2 data设计原理
-
原理:闭包设计:闭包可以保证每个组件有自己的私有作用域,而data是对象存在有干扰
-
作用(data为什么是一个函数):保证组件的独立性,有私有作用域
-
原因:如果data是对象的话,多个vue实例对应多个data,data是对象都会引用同一个内存地址;区分不同实例的data,只能是函数通过闭包的形式,实现data私有作用域
#vue2响应式原理
数据劫持+**发布订阅模式,**通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调
- 原理:发布订阅模式 + 数据劫持
// 发布订阅
let Dep = {
container: {},//容器
// 添加订阅
listen: function (key, fn) {
if (!this.container[key]) {//如果没有容器为空,就给它设置默认值
this.container[key] = [];
}
this.container[key].push(fn)
},
// 发布订阅
trigger: function () {
let key = Array.prototype.shift.call(arguments),//类数组转换(arguments本身就是类数组)
fns = this.container[key];//获取打的容器内容
if (!fns || fns.length === 0) { //容器或容器内容为空直接结束
return false
}
for (let i = 0, fn; fn = fns[i++];) {//通过遍历吧每个信息单独存起来
fn.apply(this, arguments)//改变this的指向
}
}
}
// 数据劫持
let dataHijack = function ({ data, tag, tagkey, selector }) {//data数据,tag目标值,tagkey目标的键值,selector选择的元素
let value = '',
el = document.querySelector(selector);
//双向数据绑定
Object.defineProperty(data, tagkey, {
get: function () {
console.log('取值')
return value;
},
set: function (val) {
console.log('设置值')
value = val
Dep.trigger(tag, val)//调用发布
}
})
// 绑定订阅(必须先订阅才能出发)
Dep.listen(tag, function (text) {
el.innerHTML = text
})
}
- 双向数据绑定/v-modle:初始化:设置输入目标值给dom,更新时:监听dom,dom变化就修改目标值
- 除了v-modle还可以怎么实现双向数据绑定:complute/watch
#MVC/MVVM/MVP
软件架构设计模式
- MVC
# 组成:模型—视图—控制器
# 特点:Model 频繁发生变化,需要主动操作dom更新到View;
- MVVM
# 组成:Model、View、ViewModel(View 和 Model双向绑定);
# 特点:Model 和 ViewModel 之间的交互是双向的;不需要手动操作DOM;使view也具有控制功能
# 缺点:1.虽然使用Model方便了保证数据一致性,但是大的模块中长期不释放内存就会内存压力
# 缺点:2.数据双向绑定不利于View层的代码复用
- MVP
# 组成:模型—视图—控制器(Presenter)
# 特点:view和Model的通信是通过Presenter来进行的,所有的交互都发生在Presenter内部
#单页面/多页面区别
补充区别:单页面只有一个index,多页面有多个index;
#SPA缺点优化
- seo优化:prerender-spa-plugin、vue-meta-info
# 安装预渲染插件 prerender-spa-plugin
# 解决title、描述、关键词插件: vue-meta-info
- 首屏加载优化
补充:首屏变白的原因:第一次返回空的html,数据好了再返回真实页面数据
为什么做ssr:1、vue的首屏加载慢、2、不利于seo优化(代码都在js里面)
# 首屏SSR(服务端预渲染-骨架屏):通过nuxt.js;首屏服务端渲染,其他交互采用客服端渲染
#SSR
ssr:服务端预渲染;将Vue在客户端把标签渲染成HTML的工作放在服务端完成,然后再把html直接返回客户端
- 优点:利于seo、首屏加载优化
- 缺点:只支持beforeCreate和created两个钩子;
# 1.组件化
# 2.数据驱动:没有虚拟dom的时候一个li变化,整个都更新,造成性能浪费
#diff中patch
两个阶段:初始化、更新
- 初始化:虚拟dom转真实dom
// 1.需要三要素:虚拟节点、属性、子节点
// 2.创建dom、添加属性、创建目标节点,子元素加入到目标上(递归)
function vnode_to_dom(vnode) {
// 三要素
let tag = vnode.tag
let attrs = vnode.attrs
let children = vnode.children
if (!tag) return null
// 创建目标节点
let dom = document.createElement()
// 目标节点添加属性
for (let attrsName in attrs) {
if (attrs.hasOwnProperty(attrsName)) dom.setAttribute(attrsName, attrs[attrsName])
}
// 子元素加入目标
children.forEach((childNode) => {
dom.appendChild(vnode_to_dom(childNode))
});
return dom
}
- 更新时:对比,老的虚拟节点-vdom、新的虚拟节点-vdom,当目标不一样就替换
#vue过滤器
- 作用:常用于文本的格式化
filters:{
upper(input){
return input.slice(0,1).toUpperCase()+input.slice(1)
}
}
#保存当前页面状态
- LocalStorage / SessionStorage
- 路由传参
- vuex
- keep-alive来缓存页面:当组件在keep-alive内被切换时组件的activated,deactivated
#事件修饰符
- .stop :等同于js中的event.stopPropagation(),防止事件冒泡;
- .capture:与事件冒泡的方向相反,事件捕获由外到内;
- .self:只会触发自己范围内的事件,不包含子元素;
- .prevent:等同于js中的event.preventDefault(),防止执行预设的行为;
- .once:只会触发—次。
#v-for/key
优先级;v-for>v-if;所以不能同时用
- v-for为什么要有key
# key是虚拟DOM对象的标识,vue在patch过程中通过key判断是否是同一个dom,使patch过程更快/更高效
- v-for用index作为key可能会引发的问题
# 1 不管数组顺序如何改变,index都是从0、1、2..这样排列;会导致操作错误节点
# 2 同级元素也使用v-for以index作为索引会报错
- vue2中v-for优先于v-if被解析,如果同时出现,每次渲染都会先执行循环再判断条件
#keep-alive缓存组件
- 是什么:keep-alive包裹动态组件时,会缓存不活动的组件实例。
# 3个props属性:
include - 字符串或正则表达式。只有名称匹配的组件会被缓存
exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存
max - 数字。最多可以缓存多少组件实例
# 2个生命周期函数
activated()# 激活时,会重新调render;
deactivated()# 当组件被换掉时,会被缓存到内存中、触发deactivated生命周期
- 如何使用
<keep-alive include="a,b">
<component :is="view"></component>
</keep-alive>
<!-- 正则表达式 (使用 `v-bind`) -->
<keep-alive :include="/a|b/">
<component :is="view"></component>
</keep-alive>
<!-- 数组 (使用 `v-bind`) -->
<keep-alive :include="['a', 'b']">
<component :is="view"></component>
</keep-alive>
- keep-alive为什么不能缓存 Iframe 中的 dom 结构
# 1 Vue的keep-alive缓存也是基于VNode节点而不是直接存储DOM节点。
# iframe页里的内容并不属于节点的信息,keep-alive依然会重新渲染iframe内的内容。
- 缓存什么:虚拟节点vnode实例,缓存时进行对比
# 注意:路由表中的mate属性 配置哪些组件需要缓存;
meta:{ noCache: true //如果设置为true,则不会被 <keep-alive> 缓存(默认 false) }
- LRU(least recently used)缓存策略:最近被访问,那将来被访问几率更高
#nextTick
nextTick:dom更新之后再延迟回调
- 原理:基于事件循环,在主线程执行任务之前,先将任务放到一个消息队列里,等待主线程执行完毕后,取出执行
- 应用场景:获取更新后的dom;this.$nextTick,场景操作页面,获取操作后的结果再操作
#Vue自定义指令
vue内置指令(v-show、v-text等..),不满足开发需要时,需要自定义指令;没有自定义过指令
#assets/static区别
-
打包前后区别
-
相同点:都是存放静态资源的
-
assets打包后到了dist的static中--进行了压缩、代码格式化
#delete/Vue.delete删除数组区别
- delete:没有删除键,只是值变为undefined/null
- Vue.delete:直接删除了数组
#vue初始化页面闪动
原因:页面一开始还没解析时加载到插值表达式;
解决办法:在插件表达式的div加,v-cloak (隐藏元素,内存中替换)
出现场景:插值表达式
- 解决办法:v-cloak渲染指令(解决屏幕闪动)
<p class="#app" v-cloak>
<p>{{value.name}}</p>
</p>
<style>
[v-cloak] {
display: none;
}
</style>
v-cloak原理:隐藏元素、在内存中进行值替换,完成后v-cloak 属性会被自动去除。
#extend
作用:**扩展组件生成—个构浩器,**通常会与$mount一起使用。
概念:基于Vue构造器创建一个Vue子类,对Vue构造器进行扩展,通常会与$mount一起使用。
#父/子组件执行顺序
- 加载渲染过程:
1.父组件 beforeCreate2.父组件created3.父组件 beforeMount4.子组件 beforeCreate5.子组件created6.子组件 beforeMount
7.子组件 mounted8.父组件mounted
- 更新过程:
1.父组件beforeUpdate2.子组件beforeUpdate3.子组件updated4.父组件updated
- 销毁过程:
1.父组件beforeDestroy2.子组件 beforeDestroy3.子组件destroyed4.父组件destoryed
更新和销毁相似
#created/mounted区别
- created 模板渲染html前调用
- mounted 模板渲染成html后调用。通常是确定dom已经生成,再操作dom,例如echarts画图
#什么生命周期请求异步数据
- 在created、beforeMount、 mounted 此时的data已经创建;
- 推荐created异步请求数据,SSR只支持beforeCreated、created,方便统一建议created,
#组件传参
- 父子传参:props、$emit
# 父传子:v-ind绑定一个值,子组件通过props接收
# 子传父:$emit('xxx',value),父组件通过@方法,接收传来的变量xxx
- 任意传参:事件总线eventBus($emit / $on)
# 两页面都引入EventBus
# 发:EventBus.$emit("addition',xxx)
# 收:EventBus.$on("addition',function(xxx){})
- 祖孙传参:(provide/ inject)
// 爷
provide() {
return {
num: "xxx"
}
};
// 孙
inject:['num']
- vuex:略
# 存:store.commit('theme/setTheme', obj)
# 调:store.state.theme.theme
#vue-router路由
- vue-router原理
# hash原理:onhashchange 事件(监测hash值变化),可以在 window 对象上监听这个事件。
# history原理:pushState() 和 replaceState() 方法
-
路由模式三种:hash、history
-
使用路由模块来实现页面跳转
# 1 直接修改地址栏
# 2 router.push(‘路由地址’)
# 3 router-link
- Vue-Router的懒加载如何实现
# 使用箭头函数+import动态加载
# 使用箭头函数+require动态加载
- 路由的hash和history模式的区别
hash改变不会请求服务器、history需要正确配置nginx
# hash: 会有#,又称前端路由
# hash原理:onhashchange()事件,hash值发生变化时无需向后端发起请求
# history:没有#
# history模式需要nginx配置支持。如果nginx没有正确配置,访问时会返回404。
history.go(-2);//后退两次
history.go(2);//前进两次
history.back(); //后退
hsitory.forward(); //前进
- 如何获取页面的hash变化
# 方法1:监听$route属性;onhashchange
# 方法2:window.location.hash 读取hash值
- $route和$router的区别
# router路由实例:router.push路由跳转的方法
# route路由信息对象:包含路由信息query、params、path、name
router.push({path:'/home', params:{id:'12'}}) # 用来跳转传参
route.query.id route.params.id # 用来获取参数信息
- 如何定义动态路由?如何获取传过来的动态参数?
params类似post,地址栏不显示参数,query类似get,地址栏出现参数
param传参
(1) param方式
# 配置路由格式:/router/:id
# 传递的方式:在path后面跟上对应的值传递后形成的路径:/router/123
(2)路由跳转
# to可以是name或者path
# router.push({path:'/home', params:{id:'12'}}) router.push({path:'/home/:1')
query传参
(1) query方式
# 配置路由格式:/router 正常配置
# 传递的方式:在path后面跟上对应的值传递后形成的路径:/router?id=123
(2)路由跳转
# to可以是name或者path
# router.push({path:'/home', query:{id:'12'}}) router.push({path:'/home/:1')
- vue-router路由钩子在生命周期的体现
三个全局路由钩子:前置路由、解析路由、后置路由
# router.beforeEach 前置路由
# router.beforeResolve 解析路由 beforeRouteEnter调用之后调用
# router.afterEach 后置路由
三参数:to、from、next()
# to 去哪
# from 来着哪
# next() 允许从to到from
- vue-router跳转和location.href有什么区别
localtion.href
# 会刷新页面(场景:跳外链)
vue-router
# 1.按需加载,减少dom操作(场景:内部页面)
# 2.js原生,history.pushState,无需刷新页面、静态跳转
- vue-router导航守卫有哪些
# 全局三钩子:beforeEach、beforeResolve、afterEach
# 路由独享的守卫: beforeEnter
# 组件内的守卫: beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
#vuex
- Vuex中action和mutation的区别
action 异步操作、不能直接改变状态,需要通过上下文提交mutation;场景:多个任务时的异步操作修改状态
# store.dispatch('user/sum_actions', sum) // 含有异步操作,数据提交至 actions;
mutation 同步操作,是改变状态的唯一方法
# store.commit('theme/setTheme', obj) // 同步操作,数据提交至 mutations;修改store的state的值
- vuex和localStorage的区别
# 1 vuex存在内存中,刷新页面消失;localStorage存在浏览器中,一直存储;但是读取内存比读硬盘快
# 2 vuex能做响应式数据,localStorage不能
- vuex有哪几种属性?
state # 基本数据
getters # 基于基础数据派生的数据
muations # 改变状态的唯一方法;commit
actions # 异步操作、调muations
modules # 模块化
- Vuex和单纯的全局对象有什么区别
# 主区别:状态管理(vuex由统一的方法修改数据,全局变量可以任意修改)、响应式数据
- 为什么Vuex的mutation中不能做异步操作:方便跟踪状态
- Vuex的严格模式是什么,有什么作用,如何开启?
概念:状态变更且不是由 mutation 函数引起的,将会抛出错误
const store = new Vuex.Store({
strict :true,
})
- Vuex 区分 State 外部修改、内部修改(commit)
修改state唯一渠道就是commit;监听_committing 是否为true
#vuex/pinia区别
# 1 抛弃了Vuex中的Mutation
# 2 pinia中action支持同步和异步,Vuex的action是异步
# 3 支持服务端渲染ssr
#Updated可以修改节点嘛
能,但不推荐在updated钩子函数中直接修改DOM节点;更新dom或数据,对于不定的数据会触发死循环;
补充:不要在update beforeUpdate修改不定数据,否则会引起死循环
#vue限制传递数据的类型
在props中限制传递类型type、值是否必须required、default默认值
props: {
test: {
type: Number, // 数字类型
required: true // 是否必须
default: 99 // 默认值
},
},
#attribute/property区别
-
property是DOM中的属性,是js对象;
-
attribute是HTML标签上的特性,它的值只能够是字符串;
下面获取/修改值的区别:以获取class为例
let div1 = document.getElementById('div1');// 获取div节点
# attribute取值:div1.getAttribute('class');
# attribute修改值:div1.setAttribute('class', 'xxx')
# property直接取值:div1.xx
# property直接修改值:div1.xx = 'xxx'