目录
1.你怎么理解vue?它是属于什么模式?
3.vue响应式原理和双向绑定原理
Object.defineProperty详解
vue3.x中Proxy(代理)
4.说一说你对vue响应式理解?
5.讲一下vue框架的原理?
6.虚拟DOM和diff算法
7.说说你对虚拟DOM的理解?
8.你了解diff算法吗?
10.什么是vue生命周期
11.vue的生命周期与不同阶段的作用
12.第一次页面加载会触发哪几个钩子
13.一般在哪个生命周期请求异步数据
14.DOM 渲染在哪个周期就已经完成
15.多组件(父子组件)中生命周期的调用顺序说一下
16.vue组件之间的传值方式
1、父子组件传值方式
2、兄弟组件传值方式
17.hods、computed 和 watch 的区别和运用的场景?
1、methods、computed的区别
2、computed和watch的区别
18.vue组件watch中deep和immediate的作用
19.watch会不会立即执行 ?
20.Vue3中watch与wacthEffect有什么区别?
21.在哪个生命周期内调用异步请求?
22.对 keep-alive 的了解?
23.keep-alive的实现
24.组件中 data 为什么是一个函数?
25.Vue 中的 key 有什么作用?
26.vue中如何动态的设置class、style?
27.为什么v-if和v-for不建议用在同一标签?
28.不需要响应式的数据应该怎么处理?
29.watch有哪些属性,分别有什么用?
1、正常监听
2、immediate
3、deep
30.this.$router和this.$route的区别
31.active-class 是哪个组件的属性?
32.路由传参的方式与动态路由匹配
33.怎么定义动态路由?怎么获取传过来的动态参数?
34.我们如何处理404 Not Found路由
35.路由组件传参
36.vue-router 有哪几种导航钩子(导航守卫)?
37.vue-router历史模式和hash模式的区别?
38.动态路由添加
39.vue-router怎么重定向的?
40.vue-router实现路由懒加载
41.怎么实现路由懒加载呢
42.Vue-router跳转和location.href有什么区别?
43.怎么配置404页面?
44.切换路由时需要保存草稿的功能,怎么实现?
45.Vue-router 路由模式有几种
46.切换到新路由时,页面要滚动到顶部或保持原先的滚动位置怎么做呢?
47.说说vue-router完整的导航解析流程是什么?(选)
48. 什么是MVVM它和其它框架(jquery)的区别是什
么?哪些场景适合?
49.MVVM的优缺点:
50.组件中的data为什么是一个函数?
51.为什么v-for和v-if不建议用在一起
52.vuex的State特性是?
53.介绍一下Vue的响应式系统
54.组件之间是怎么通信的
55.Vue 组件间通信有哪几种方式?
56.什么是Vue的计算属性?
57.Vue路由的钩子函数
58.Vue.cli中怎样使用自定义的组件?有遇到过哪些问题吗?
59.Vue如何实现按需加载配合webpack设置
60.简单描述每个周期具体适合哪些场景
61.scss是什么?在Vue.cli中的安装使用步骤是?有哪几大特性?
62.聊聊你对Vue.js的template编译的理解?
63.Vue与Angular以及React的区别?
64.Vue 路由跳转的几种方式
65.Vue的路由实现:hash模式和history模式
66. 说一下 Vue 的优点
响应式编程
组件化开发
虚拟 DOM
67.vue 中组件和插件有什么区别
1. 组件是什么
2. 插件是什么
3. 两者的区别
3.1 编写形式
编写组件
编写插件
3.2 注册形式
组件注册
插件注册
4. 使用场景
68.在Vue中使用插件的步骤
69.页面刷新后Vuex 状态丢失怎么解决?
70.mixin
71.nextTick 的作用是什么?他的实现原理是什么?
72.vue 如何快速定位那个组件出现性能问题的
73. vuex 是什么?怎么使用它?什么场景下我们会使用到 vuex
「vuex 是什么」
「为什么需要 vuex」
「使用方法」
「什么场景下会使用到 vuex」
74.Vuex 和 localStorage 的区别
75.简单说一说你对vuex理解
76.vuex有什么缺点吗?你在开发过程中有遇到什么问题吗?
77.怎么监听vuex数据的变化
78.sessionstorage localstorage区别
生命周期
localStorage和sessionStorage都保存在客户端,不与服务器进行交互通信
存储内容类型
79.Vue3.0 响应式方面做了哪些改变
80.Vue 3.0 所采用的 Composition Api 与 Vue 2.x使用的Options Api 有什么区别?
Options Api
composition Api
81.Proxy 相对于 Object.defineProperty 有哪些优点?
82.Vue3.0 在编译方面有哪些优化?
73.Vue3.0 响应式api如何实现的
1. reactive
2. effect
3. track
4.trigger
84.Vue2.0 和 Vue3.0 有什么区别?
85.Composition API 和 React Hook 很像,请问他们的区别是什么
86.vue3带来了什么改变?
1.性能的提升
2.源码的升级
3.拥抱TypeScript
4.新的特性
4.1、Composition API(组合API)
4.2、新的内置组件
4.3、其他改变
4.4.vue3还有哪些其他改变?
87.你知道哪些vue3新特性
88.vue3响应式数据的判断?
89.Vue complier 实现
90.开发中常用的指令有哪些
91.vue常用指令有哪些?
92.v-model的作用是什么?
93v-model是什么?怎么使用?vue中标签怎么绑定事件?
94.v-model的实现原理?
95.说一下vue2.x中如何监测数组变化
96.你的接口请求一般放在哪个生命周期中
97.delete和Vue.delete删除数组的区别
98.请说出vue.cli项目中src目录每个文件夹和文件的用法
99.vue-loader 是什么?使用它的用途有哪些?
100.vue常⽤的修饰符
101.对Vue项目你做过哪些性能优化
102.v-for和v-if放在一起用好吗
103.怎么减少页面加载时间
104.介绍知道的状态码,状态码301、302区别
105.content-type作用
106.package.json package.lock.json 区别
107.export和import的区别和作用
108.Vue实例挂载的过程中发生了什么?
109.说说从 template 到 render 处理过程
110.实际工作中,你总结的vue最佳实践有哪些
111.action和mutation的区别是什么?为什么要区分它们?
112.assets和static的区别
113.异步组件是什么?使用场景有哪些?
114.Vue 子组件和父组件创建和挂载顺序
115.vue父子组件挂载顺序是什么?
116.怎么缓存当前的组件?缓存后怎么更新?
117.从0到1自己构架一个vue项目,说说有哪些步骤、哪些重要插件、目录结构你会怎么组织
118.如果让你从零开始写一个vue路由,说说你的思路
119.new Vue后整个的流程
120.双向绑定实现原理
121.如何避免vue组件的样式污染?
122.Vue如何给一个对象添加新的属性?
123.process.nextTick和Vue.nextTick的区别?
124.讲讲slot?有什么作用?
125.React、Vue和JQuery在不同场景下怎么选型?
126.vue全家桶包含哪些
127.vue嵌套路由怎么定义?
128.iframe的优缺点?
129.为什么会出现vue修改数据后页面没有刷新这个问题?
130.请说下封装 vue 组件的过程?
vue.js是一款渐进式、多途径、高性能JavaScript框架,易用、灵活、高效
属于MVVM(Model-View-ViewModel)模式,也是就model(模型层)、ViewModel(视图驱动层)、view(视图层),数据在传递的时候双向传递
2.v-if和v-show的区别
相同点:v-if与v-show都可以动态控制dom元素显示隐藏
不同点:v-if显示隐藏是将dom元素整个添加或删除,而v-show隐藏则是为该元素添加css–display:none,dom元素还在。
性能角度上:v-if有更高的切换消耗;v-show有更高的初始渲染消耗;
使用场景:v-if适合运营条件不大可能改变;v-show适合频繁切换。
答案2
v-show 与 v-if 的作用效果是相同的(不含v-else),都能控制元素在页面是否显示,在用法上也是相同的
- 区别
控制手段不同
编译过程不同
编译条件不同
控制手段:v-show隐藏则是为该元素添加css--display:none,dom元素依旧还在。v-if显示隐藏是将dom元素整个添加或删除
编译过程:v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show只是简单的基于css切换
编译条件:v-if是真正的条件渲染,它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。只有渲染条件为假时,并不做操作,直到为真才渲染
v-show 由false变为true的时候不会触发组件的生命周期
v-if由false变为true的时候,触发组件的beforeCreate、create、beforeMount、mounted钩子,由true变为false的时候触发组件的beforeDestory、destoryed方法
性能消耗:v-if有更高的切换消耗;v-show有更高的初始渲染消耗
在了解该知识点前,先做一个只是铺垫,详细介绍下Object.defineProperty的用法
Object.defineProperty是es5的方法,作用是直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象(只能用在对象上,不能用在数组上)
1、语法
Object.defineProperty(obj, prop, descriptor)
2、参数
obj:必需。目标对象
prop:必需。需定义或修改的属性的名字
descriptor:必需。目标属性所拥有的特性
3、属性用法
修改某个属性的值时,给这个属性添加一些特性。
let person = {};
Object.defineProperty(person, 'name',
writable: true || false,
configurable: true || false,
enumerable: true || false,
value:'gjf'
});
属性详解:
writable:是否可以被重写,true可以重写,false不能重写,默认为false。
enumerable:是否可以被枚举(使用for…in或Object.keys())。设置为true可以被枚举;设置为false,不能被枚举。默认为false。
value:值可以使任意类型的值,默认为undefined
configurable:是否可以删除目标属性或是否可以再次修改属性的特性(writable, configurable, enumerable)。设置为true可以被删除或可以重新设置特性;设置为false,不能被可以被删除或不可以重新设置特性。默认为false。
vue响应式原理(vue双向绑定的原理)
vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty() 来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty() 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue追踪依赖,在属性被访问和修改时通知变化。
vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}),最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。
vue2.x中object.defineProperty是属性级别的拦截,会在set、get的方法中进行拦截操作
当把一个普通的js对象传入vue实列作为data选项时,vue会遍历此对象的所有的property,并使用object.defineProperty,把这些property全部转换为getter/setter。然后watcher会监听setter的变化,每当setter变化后,就会进行视图层的重新渲染,达到响应式效果
在这里主要我改动了data中的某一个值,那么整个响应式的状态又会重新开始进行setter、监听,相对来说浪费资源
hellow
若是data中有多个值,则会进行循环输出,来监听每一个值的变化
对象级别的拦截
改变语法原有的规则,可以自定义其规则(元编程)
target:代理处理的目标对象
handler: 代理处理的方法
let obj =new Proxy(target,handler)
1
自定义对象属性的获取、赋值、枚举、函数调用等功能
hellow
\这里去监听每一个属性的时候不需要用到循环的方法,简单的说就是谁改变就去监听谁,在原用的基础上提升了性能
回答思路啥是响应式?
为什么vue需要响应式?
它能给我们带来什么好处?
vue的响应式是怎么实现的?有哪些优缺点?
vue3中的响应式的新变化
所谓数据响应式就是能够使数据变化可以被检测并对这种变化做出响应的机制。
MVVM框架中要解决的一个核心问题是连接数据层和视图层,通过数据驱动应用,数据变化,视图更新,要做到这点的就需要对数据做响应式处理,这样一旦数据发生变化就可以立即做出更新处理。
以vue为例说明,通过数据响应式加上虚拟DOM和patch算法,开发人员只需要操作数据,关心业务,完全不用接触繁琐的DOM操作,从而大大提升开发效率,降低开发难度。
vue2中的数据响应式会根据数据类型来做不同处理,如果是对象则采用Object.defineProperty()的方式定义数据拦截,当数据被访问或发生变化时,我们感知并作出响应;如果是数组则通过覆盖数组对象原型的7个变更方法,使这些方法可以额外的做更新通知,从而作出响应。这种机制很好的解决了数据响应化的问题,但在实际使用中也存在一些缺点:比如初始化时的递归遍历会造成性能损失;新增或删除属性时需要用户使用Vue.set/delete这样特殊的api才能生效;对于es6中新产生的Map、Set这些数据结构不支持等问题。
为了解决这些问题,vue3重新编写了这一部分的实现:利用ES6的Proxy代理要响应化的数据,它有很多好处,编程体验是一致的,不需要使用特殊api,初始化性能和内存消耗都得到了大幅改善;另外由于响应化的实现代码抽取为独立的reactivity包,使得我们可以更灵活的使用它,第三方的扩展开发起来更加灵活了。
我们使用Vue开发应用,实际上是编写若干Vue组件,实现模板、data、生命周期钩子等,然后执行new Vue(),将根组件挂载到指定的DOM节点上面,当我们编写的组件中生命周期钩子里面的或者在模板的元素事件中改变数据时候,视图会响应地更新。这样就实现了应用。
那么Vue是如何实现上面的效果的呢?
new Vue()之后,Vue会从根组件开始,遍历整个组件树,对每个组件进行处理。
对于一个Vue组件,Vue首先会进行模板编译,将模板编译为render函数,render函数返回虚拟DOM,如果遇到子组件,也对子组件做同样操作,最终形成一个虚拟DOM树。
Vue会把虚拟DOM映射到真实DOM并渲染到指定节点上,这样就实现了视图的渲染。
Vue在组件初始化时候还会设置数据为响应式,并将依赖于数据的渲染方法、computed、watch收集起来。
当数据改变后,Vue会根据初始化时候收集的依赖,更新视图,这时候我们就看到最新的界面了。
1、认识虚拟DOM
没有虚拟DOM之前: 数据改变=>操作DOM=>视图更新
使用虚拟DOM后:数据改变=>虚拟DOM(计算出变更)=>操作DOM=>视图更新
虚拟DOM:用js模拟DOM结构
如下图,左边是实际DOM元素,而右边是js模拟的DOM结构
2、虚拟DOM的好处
每当dom改变的时候,不会全部都进行DOM改变,而是被改动的地方进行改变
这里就是用js代码去模拟DOM结构
用虚拟DOM计算出最小的变化,然后再去更新变化的实际DOM
3、认识diff算法基本概念
是两个虚拟DOM之间的一种比较
1、虚拟DOM之间会平级别对比 ,深度遍历进行对比
2、对比的同时会通过key值进行判断,判断元素是仅仅位置发生改变还是需要整个替换或删除
3、如果不是元素发生改变的话,再对内容进行对比,如果是内容发生改变的话,就直接修改内容
详细的算法介绍就需要另外研究了
vdom是什么
引入vdom的好处
vdom如何生成,又如何成为dom
在后续的diff中的作用
虚拟dom顾名思义就是虚拟的dom对象,它本身就是一个 JavaScript 对象,只不过它是通过不同的属性去描述一个视图结构。
通过引入vdom我们可以获得如下好处:
将真实元素节点抽象成 VNode,有效减少直接操作 dom 次数,从而提高程序性能
方便实现跨平台
同一 VNode 节点可以渲染成不同平台上的对应的内容,比如:渲染在浏览器是 dom 元素节点,渲染在 Native( iOS、Android) 变为对应的控件、可以实现 SSR 、渲染到 WebGL 中等等
Vue3 中允许开发者基于 VNode 实现自定义渲染器(renderer),以便于针对不同平台进行渲染。
直接操作 dom 是有限制的,比如:diff、clone 等操作,一个真实元素上有许多的内容,如果直接对其进行 diff 操作,会去额外 diff 一些没有必要的内容;同样的,如果需要进行 clone 那么需要将其全部内容进行复制,这也是没必要的。但是,如果将这些操作转移到 JavaScript 对象上,那么就会变得简单了。
操作 dom 是比较昂贵的操作,频繁的dom操作容易引起页面的重绘和回流,但是通过抽象 VNode 进行中间处理,可以有效减少直接操作dom的次数,从而减少页面重绘和回流。
vdom如何生成?在vue中我们常常会为组件编写模板 - template, 这个模板会被编译器 - compiler编译为渲染函数,在接下来的挂载(mount)过程中会调用render函数,返回的对象就是虚拟dom。但它们还不是真正的dom,所以会在后续的patch过程中进一步转化为dom。
图片
image-20220209153820845
挂载过程结束后,vue程序进入更新流程。如果某些响应式数据发生变化,将会引起组件重新render,此时就会生成新的vdom,和上一次的渲染结果diff就能得到变化的地方,从而转换为最小量的dom操作,高效更新视图。
diff算法是干什么的
它的必要性
它何时执行
具体执行方式
拔高:说一下vue3中的优化
1.Vue中的diff算法称为patching算法,它由Snabbdom修改而来,虚拟DOM要想转化为真实DOM就需要通过patch方法转换。
2.最初Vue1.x视图中每个依赖均有更新函数对应,可以做到精准更新,因此并不需要虚拟DOM和patching算法支持,但是这样粒度过细导致Vue1.x无法承载较大应用;Vue 2.x中为了降低Watcher粒度,每个组件只有一个Watcher与之对应,此时就需要引入patching算法才能精确找到发生变化的地方并高效更新。
3.vue中diff执行的时刻是组件内响应式数据变更触发实例执行其更新函数时,更新函数会再次执行render函数获得最新的虚拟DOM,然后执行patch函数,并传入新旧两次虚拟DOM,通过比对两者找到变化的地方,最后将其转化为对应的DOM操作。
4.patch过程是一个递归过程,遵循深度优先、同层比较的策略;以vue3的patch为例:
首先判断两个节点是否为相同同类节点,不同则删除重新创建
如果双方都是文本则更新文本内容
如果双方都是元素节点则递归更新子元素,同时更新元素属性
更新子节点时又分了几种情况:
新的子节点是文本,老的子节点是数组则清空,并设置文本;
新的子节点是文本,老的子节点是文本则直接更新文本;
新的子节点是数组,老的子节点是文本则清空文本,并创建新子节点数组中的子元素;
新的子节点是数组,老的子节点也是数组,那么比较两组子节点,更新细节blabla
vue3中引入的更新策略:编译期优化patchFlags、block等
9.你怎么理解Vue中的diff算法?
在js中,渲染真实DOM的开销是非常大的, 比如我们修改了某个数据,如果直接渲染到真实DOM, 会引起整个dom树的重绘和重排。那么有没有可能实现只更新我们修改的那一小块dom而不要更新整个dom呢?此时我们就需要先根据真实dom生成虚拟dom, 当虚拟dom某个节点的数据改变后会生成有一个新的Vnode, 然后新的Vnode和旧的Vnode作比较,发现有不一样的地方就直接修改在真实DOM上,然后使旧的Vnode的值为新的Vnode。
diff的过程就是调用patch函数,比较新旧节点,一边比较一边给真实的DOM打补丁。在采取diff算法比较新旧节点的时候,比较只会在同层级进行。在patch方法中,首先进行树级别的比较 new Vnode不存在就删除 old Vnodeold Vnode 不存在就增加新的Vnode 都存在就执行diff更新 当确定需要执行diff算法时,比较两个Vnode,包括三种类型操作:属性更新,文本更新,子节点更新 新老节点均有子节点,则对子节点进行diff操作,调用updatechidren 如果老节点没有子节点而新节点有子节点,先清空老节点的文本内容,然后为其新增子节点 如果新节点没有子节点,而老节点有子节点的时候,则移除该节点的所有子节点 老新老节点都没有子节点的时候,进行文本的替换
updateChildren 将Vnode的子节点Vch和oldVnode的子节点oldCh提取出来。oldCh和vCh各有两个头尾的变量StartIdx和EndIdx,它们的2个变量相互比较,一共有4种比较方式。如果4种比较都没匹配,如果设置了key,就会用key进行比较,在比较的过程中,变量会往中间靠,一旦StartIdx>EndIdx表明oldCh和vCh至少有一个已经遍历完了,就会结束比较。
对于 vue 来讲,生命周期就是一个 vue 实例从创建到销毁的过程。
vue的生命周期有:beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed
作用:
beforeCreate:可以加载loading事件,在加载实列的时候被触发 此时data和methods中的数据都还没有初始化
created:初始化完成时的事件在这里,这里也结束loading事件,异步请求也适合在这里被调用
(创建完毕,data中有值,未挂载)
mounted:可以挂载元素,过去DOM节点
updated:对数据统一处理,这里写上对应的函数即可
beforeDestroy:可以确定一个停止事件的确认框
nextTick:更新数据后,立即操作dom
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
create阶段:vue实例被创建
beforeCreate: 创建前,此时data和methods中的数据都还没有初始化
created:创建完毕,data中有值,未挂载
mount阶段:vue实例被挂载到真实DOM节点
beforeMount:可以发起服务端请求,去数据
mounted: 此时可以操作DOM
update阶段:当vue实例里面的data数据变化时,触发组件的重新渲染
beforeUpdate :更新前
updated:更新后
destroy阶段:vue实例被销毁
beforeDestroy:实例被销毁前,此时可以手动销毁一些方法
destroyed:销毁后
Vue 2中的生命周期钩子 |
Vue 3选项式API的生命周期选项 |
Vue 3 组合API中生命周期钩子 |
描述 |
beforeCreate |
beforeCreate |
setup() |
创建前,此时data和 methods的数据都还没有初始化 |
created |
created |
setup() |
创建后,data中有值,尚未挂载,可以进行一些Ajax请求 |
beforeMount |
beforeMount |
onBeforeMount |
挂载前,会找到虚拟DOM,编译成Render |
mounted |
mounted |
onMounted |
挂载后,DOM已创建,可用于获取访问数据和DOM元素 |
beforeUpdate |
beforeUpdate |
onBeforeUpdate |
更新前,可用于获取更新前各种状态 |
updated |
updated |
onUpdated |
更新后,所有状态已是最新 |
beforeDestroy |
beforeUnmount |
onBeforeUnmount |
销毁前,可用于一些定时器或订阅的取消 |
destroyed |
unmounted |
onUnmounted |
销毁后,可用于一些定时器或订阅的取消 |
activated |
activated |
onActivated |
keep-alive缓存的组件激活时 |
deactivated |
deactivated |
onDeactivated |
keep-alive缓存的组件停用时 |
errorCaptured |
errorCaptured |
onErrorCaptured |
捕获一个来自子孙组件的错误时调用 |
— |
renderTracked |
onRenderTracked |
调试钩子,响应式依赖被收集时调用 |
— |
renderTriggered |
onRenderTriggered |
调试钩子,响应式依赖被触发时调用 |
— |
serverPrefetch |
onServerPrefetch |
组件实例在服务器上被渲染前调用 |
会触发 4 个钩子,分别是:beforeCreate、created、beforeMount、mounted
我们可以在钩子函数 created、beforeMount、mounted 中进行调用, 因为在这三个钩子函数中 data 已经创建,可以将服务端端返回的数据进行赋值。推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:
2、SSR 不支持 beforeMount 、mounted 钩子函数,放在 created 中有助于一致性
DOM 渲染是在 mounted 阶段完成,此阶段真实的 DOM 挂载完毕,数据完成双向绑定,可以访问到 DOM 节点。
组件的调用顺序都是先父后子,渲染完成的顺序是先子后父。组件的销毁操作是先父后子,销毁完成的顺序是先子后父。
加载渲染过程:父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount- >子mounted->父mounted
子组件更新过程:父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程:父 beforeUpdate -> 父 updated
销毁过程:父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
父组件向子组件传值
1、传值前现在父组件中导入需要的子组件,然后在父组件中传入值、
2、子组件需要在props:{}中定义传过来的值,以及类型,然后就可以在其他地方使用
父组件:
子组件:
子组件向父组件传值
就是在子组件中通过$emit(传向父组件事件,传向父组件值)向父组件传入值,父组件接受改变即可
子组件:
{{ sendData }}
1、bus总线方法
1、定义一个bus.js ,内容如下
import Vue from "vue";
const Bus = new Vue();
export default Bus;
2、然后在main.js中添加如下代码
import bus from "./components/bus";
Vue.use(bus);
Vue.prototype.$bus = bus;
3、在需要传值的兄弟组件中,通过b u s . bus.bus.emit(传入方法,传入值),方式传入
child1
4、在接收的组件中在mouted生命周期中,用b u s . bus.bus.on(传过来的方法名,回调函数),来进行接收
child2
{{ cmsg }}
2、 可以子组件先传入父组件,然后再由父组件再传给另一个子组件
在页面进行渲染的时候,methos中所有的方法都会被重新调用,而computed,只会在其被依赖项改变的时候才会重新计算
答案2
场景不同,computed是计算属性,用在渲染或者其他获取计算属性值的地方,method是定义的方法,用在生命周期、交互回调等地方。
使用方式不同,computed是直接取值,method是调用方法。
computed方法不应该有副作用,method则无此要求。
computed是惰性求值,只有依赖的数据发生改变,并且其他地方使用到了这个值,才会调用方法进行计算;method是每次调用会立刻执行。
答案1计算属性可以从组件数据派生出新数据,最常见的使用方式是设置一个函数,返回计算之后的结果,computed和methods的差异是它具备缓存性,如果依赖项不变时不会重新计算。侦听器可以侦测某个响应式数据的变化并执行副作用,常见用法是传递一个函数,执行副作用,watch没有返回值,但可以执行异步操作等复杂逻辑。
计算属性常用场景是简化行内模板中的复杂表达式,模板中出现太多逻辑会是模板变得臃肿不易维护。侦听器常用场景是状态变化之后做一些额外的DOM操作或者异步操作。选择采用何用方案时首先看是否需要派生出新值,基本能用计算属性实现的方式首选计算属性。
使用过程中有一些细节,比如计算属性也是可以传递对象,成为既可读又可写的计算属性。watch可以传递对象,设置deep、immediate等选项。
vue3中watch选项发生了一些变化,例如不再能侦测一个点操作符之外的字符串形式的表达式;reactivity API中新出现了watch、watchEffect可以完全替代目前的watch选项,且功能更加强大。
答案2
应用场景不同
computed用在根据data属性或者其他computed计算得到一个新值的情况,computed的值一般被用在渲染中。
watch用在监听数据变化,然后做一些有副作用的操作的场景。
执行过程不同
在依赖的data属性变化后,computed并不会重新计算新的值,而是等到访问的时候再判断,如果依赖的data有改动则重新计算并返回结果,如果依赖的data没有改动,就不计算,直接返回当前结果。
依赖的数据变化后就会执行watch的回调。
computed是计算属性,有如下特点:
1、计算属性定义的属性可以不用在data中定义,在DOM中直接使用。
2、支持缓存,只有在数据依赖项发生改变的时候,才会重新计算。
3、不支持异步操作,在computed中异步操作无效,无法监听数据的变化
4、computed 属性是函数时,都有 get 和 set 方法,默认走 get 方法,get 必须有返回值(return)
computed 是多对一(监听属性依赖于其他属性)
watch是监听属性,特点如下:
1、不支持缓存,只要数据变化,就会直接触发响应的操作
2、在watch内支持异步操作
3、监听的函数接受两个参数,第一个是参数最新的值,第二个是参数输入之前的值
4、watch 是一对多(监听某一个值变化,执行对应操作)
总结:
如果一个数据需要经过复杂计算就用 computed
如果一个数据需要被监听并且对数据做一些操作就用 watch
watch有两个选项:deep和immediate,deep决定是否深度监听,即是否监听多层对象数据,immediate决定是否立即执行,如果为true,会在绑定时候(初始时候)立即执行,如果为false,只在监听的值变更时候执行。默认为false。
不会立即执行,在监听到数据变化了才会改变,
你可以认为他们是同一个功能的两种不同形态,底层的实现是一样的。
1、watch-显式指定的依赖源,依赖源更新时执行回调函数。
2、watchEffect-自动收集依赖源,依赖源更新时重新执行自身。
在created、beforeMount、mounted都可以发送异步请求,因为可以将服务端端返回的数据进行赋值。但是最常用的是在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求
有两个优点:
第一点:能更快获取到服务端数据,减少页面 loading 时间;
第二点:放在 created 中有助于一致性,因为ssr 不支持 beforeMount 、mounted 钩子函数。
keep-alive的实现
作用:实现组件缓存,保持这些组件的状态,以避免反复渲染导致的性能问题。需要缓存组件 频繁切换,不需要重复渲染
场景:tabs标签页 后台导航,vue性能优化
原理:Vue.js内部将DOM节点抽象成了一个个的VNode节点,keep-alive组件的缓存也是基于VNode节点的而不是直接存储DOM结构。它将满足条件(pruneCache与pruneCache)的组件在cache对象中缓存起来,在需要重新渲染的时候再将vnode节点从cache对象中取出并渲染。
可以设置以下属性:
① include:字符串或正则,只有名称匹配的组件会被缓存。
② exclude:字符串或正则,任何名称匹配的组件都不会被缓存。
③ max:数字,最多可以缓存多少组件实例。
匹配首先检查组件的name选项,如果name选项不可用,则匹配它的局部注册名称(父组件 components选项的键值),匿名组件不能被匹配。
设置了keep-alive缓存的组件,会多出两个生命周期钩子:activated、deactivated。
首次进入组件时:beforeCreate --> created --> beforeMount --> mounted --> activated --> beforeUpdate --> updated --> deactivated
再次进入组件时:activated --> beforeUpdate --> updated --> deactivated
keep-alive是Vue的内置组件,实现组件缓存。当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁。keep-alive是系统自带的一个组件,用来缓存组件,避免多次加载相同的组件,减少性能消耗,提高用户体验。例如我们可以在列表页进入详情页使用。如果在列表页点击的都是相同的 ,详情页就不用请求多次了,直接缓存起来就行了,如果点击的不同,则需要重新请求数据
因为vue的组件可能在不同的页面中会被调用,是一个函数的话每一次的调用就会执行data函数并返回新的数据,这样就可以避免多处调用之间的数据污染
我们可以知道,通常我们在使用v-for的时候会去标明key值,但是不建议去使用其数组的下标index作为key值。
key的作用是为了在diff算法执行时更快的找到对应的节点,提高diff速度,更高效的更新虚拟DOM;
vue和react都是采用diff算法来对比新旧虚拟节点,从而更新节点。在vue的diff函数中,会根据新节点的key去对比旧节点数组中的key,从而找到相应旧节点。如果没找到就认为是一个新增节点。而如果没有key,那么就会采用遍历查找的方式去找到对应的旧节点。一种一个map映射,另一种是遍历查找。相比而言。map映射的速度更快。
在上面中已经简单的介绍的虚拟DOM、diff算法,key值主要是为了diff算法服务的,给每一个循环出的item绑定一个key值,做唯一标识
好处如下:
1、判断新旧VDOM节点在逻辑上是不是同一个对象
2、准确。因为key的特性是唯一性。所以如果不加key,采用index的标记法,那么vue会选择复用同类型节点(Vue的就地更新策略),导致之前节点的状态被保留下来,会产生一系列的bug.
3、快速。在元素list一直变化的情况下,key值设置唯一时,能很精确找到/找不到变更元素,若使用index标记,要遍历vnode,时间长。
Vue的策略是不对dom直接更新,而是先比较oldDOM和VDOM 之间的区别,通过比较差别并且复用没有改变的dom,来快速的构建新的dom并渲染到页面
//动态class对象
//动态style对象
//动态class数组
//动态style数组
因为v-for的优先级高于v-if,程序会先执行v-for的操作,然后再去执行v-if,每次都会先去循环再进行条件判断,就会带来性能上的浪费
// 方法一:将数据定义在data之外
data () {
this.list1 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
this.list2 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
this.list3 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
this.list4 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
this.list5 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
return {}
}
// 方法二:Object.freeze()
data () {
return {
list1: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
list2: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
list3: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
list4: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
list5: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
}
}
new Vue({
el:"#app",
data:{
brand:'nike',
},
watch:{
cityname(newbrand,oldbrand){
//...
}
}
})
watch时有一个特点,就是当值第一次绑定的时候,不会执行监听函数,只有值发生改变才会执行。如果我们需要在最初绑定值的时候也执行函数,则就需要用到immediate属性。
普通的监听时候写的请示写的函数其实就是在写这个handler方法。
immediate表示在watch中首次绑定的时候,是否执行handler,值为true则表示在watch中声明的时候,就立即执行handler方法,值为false,则和一般使用watch一样,在数据发生变化的时候才执行handler。
深度监听,当需要监听一个对象的改变时,普通的watch方法无法监听到对象内部属性的改变,只有data中的数据才能够监听到变化,此时就需要deep属性对对象进行深度监听。
new Vue({
el:"#app",
data:{
brand:(name:'nike',id:"no1"),
},
watch:{
cityname:{
handler:(newbrand,oldbrand){
//...
},
deep:true,
immediate:true
}
}
})
this.$router:是全局路由器的router的实列,可以在任何组件内进行访问,就是跳转路由的方法,里面有很多的方法比如
this.$router.push()
this.$router.replace()
this.$router.go()
his.$route:包含当前激活的路由状态信息、url解析得到的信息
1.$route.path**
字符串,对应当前路由的路径,总是解析为绝对路径,如 "/foo/bar"。
2.$route.params**
一个 key/value 对象,包含了 动态片段 和 全匹配片段,
如果没有路由参数,就是一个空对象。
3.$route.query**
一个 key/value 对象,表示 URL 查询参数。
例如,对于路径 /foo?user=1,则有 $route.query.user == 1,
如果没有查询参数,则是个空对象。
4.$route.hash**
当前路由的 hash 值 (不带 #) ,如果没有 hash 值,则为空字符串。锚点
5.$route.fullPath**
完成解析后的 URL,包含查询参数和 hash 的完整路径。
6.$route.matched**
数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。
7.$route.name 当前路径名字**
8.$route.meta 路由元信息
$route 对象出现在多个地方:
组件内的 this.$route 和 route watcher 回调(监测变化处理)
router.match(location) 的返回值
scrollBehavior 方法的参数
导航钩子的参数,例如 router.beforeEach 导航守卫的钩子函数中,to 和 from 都是这个路由信息对象。
$router 对象是全局路由的实例,是 router 构造方法的实例。
$router 对象常用的方法有:
push:向 history 栈添加一个新的记录
go:页面路由跳转前进或者后退
replace:替换当前的页面,不会向 history 栈添加一个新的记录
active-class是vue-router模块的router-link组件中的属性,用来做选中样式的切换;
用法:
1、直接在路由js文件中配置linkActiveClass
export default new Router({
linkActiveClass: 'active'
})
2、在router-link中写入active-class
这里这个路由的样式可以直接在style中去写对应的类名就好了
如果两个路由都是用一样的样式,比如如下情况:
这样跳转后两个router-link都会有显示的样式,可能是因为 to=“/” 引起的,active-class选择样式时根据路由中的路径去匹配,然后显示,例如在my页面中,路由为localhost:8080/#/my,那么to=“/”和to=”/my"都可以匹配到,所有都会激活选中样式
解决办法
a.直接在路由js文件中配置linkActiveClass
export default new Router({
linkExactActiveClass: 'active',
})
b.在router-link中写入exact
介绍前我们先了路由的两中形式
编程式的导航 router.push
声明式的导航
对于参数的传递主要是用对象的方式去写的,可分为命名路由、查询参数
命名路由
使用前提:在注册路由的地方需要给路由命名
{
path: "/",
name: "Home",
component: Home,
},
命名路由搭配着params来进行传递,如下
this.$router.push({ name: 'Home', params: { userId: 123 }})
在目标页面接收
this。$route.params.userId
1查询参数
查询参数其实就是在路由地址后面带上参数和传统的url参数一致的,也就是传递的参数会出现在url地址栏上
注意:name、path可以和query搭配,params只能和name搭配
用法:
this.$router.push({ path: 'Home', query: { userId: 123 }});
接收:
this.$route.query.userId
动态路由匹配
常见场景:当我们有一个通用组件,对应不同ID共和不相同的客户,都要使用这个组件来渲染,我们就可以使用路由中的 动态路参数来达到这个效果,一个路径参数使用:进行标记,每当匹配到一个路由,参数值就会被设置
可以用this.$route.params去接收参数的值
注意:当使用该方法时,原组件的实列会被复用,但是组件的生命周期钩子不会被调用,简单列子如下
写跳转的组件:
路由写法:
{
path: "/about/:id",
name: "About",
component: () => import("../views/About.vue"),
},
在目标组件用 {{ this.$route.params.id }}进行参数接收即可
关于在watch中$route(to, from)不生效
这里我想监听组件的路由变化,使用watch去监听$route(to, from)发现不生效,解决办法参考这篇文章
https://blog.csdn.net/u010227042/article/details/107961248
很多时候,我们需要将给定匹配模式的路由映射到同一个组件,这种情况就需要定义动态路由。
例如,我们可能有一个 User 组件,它应该对所有用户进行渲染,但用户 ID 不同。在 Vue Router 中,我们可以在路径中使用一个动态字段来实现,例如:{ path: '/users/:id', component: User },其中:id就是路径参数
路径参数 用冒号 : 表示。当一个路由被匹配时,它的 params 的值将在每个组件中以 this.$route.params 的形式暴露出来。
参数还可以有多个,例如/users/:username/posts/:postId;除了 $route.params 之外,$route 对象还公开了其他有用的信息,如 $route.query、$route.hash 等。
常规参数只匹配 url 片段之间的字符,用 / 分隔。如果我们想匹配任意路径,我们可以使用自定义的 路径参数 正则表达式,在 路径参数 后面的括号中加入 正则表达式 :
js
const routes = [
// 将匹配所有内容并将其放在 `$route.params.pathMatch` 下
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
// 将匹配以 `/user-` 开头的所有内容,并将其放在 `$route.params.afterUser` 下
{ path: '/user-:afterUser(.*)', component: UserGeneric },
]
在这个特定的场景中,我们在括号之间使用了自定义正则表达式,并将pathMatch 参数标记为可选可重复。这样做是为了让我们在需要的时候,可以通过将 path 拆分成一个数组,直接导航到路由:
js
this.$router.push({
name: 'NotFound',
// 保留当前路径并删除第一个字符,以避免目标 URL 以 `//` 开头。
params: { pathMatch: this.$route.path.substring(1).split('/') },
// 保留现有的查询和 hash 值,如果有的话
query: this.$route.query,
hash: this.$route.hash,
})
当组件中使用 $route 会与路由紧密耦合,这限制了组件的灵活性,因为它只能用于特定的 URL。虽然这不一定是件坏事,但我们可以通过 props 配置来解除这种行为:
1、如果 props 被设置为 true,route.params 将会被设置为组件属性
这里用动态路由匹配做列子,同样传入的是id
在目标跳转的路由中:
{
path: "/about/:id",
name: "About",
props: true,
component: () => import("../views/About.vue"),
},
目标路由中props接收然后直接用:
export default {
name: "About",
props: ["id"],
};
2、对象模式
{
//对象模式
path:'/about/chilrenRoute2/:id',
component:chilrenRoute2,
props: { userName: 'userName'}
}
export default {
props:['userName']
}
获取:
通过 props传递——对象 , 获取路由组件传递参数值 /about/chilrenRoute2/:id 路由的id值: {{ this.userName }}
3、函数模式
函数模式的路由配置中,props属性是函数,这个函数返回一个对象。
在函数模式中,可以有一个参数,这个参数就是route对象,里面存储的是路由的相关携带信息。
{
//函数模式
path:'/about/chilrenRoute2/:id',
component:chilrenRoute2,
props: function (route){
//route 是 $route对象
return {
userName:'userName',
querys: route.params.id
}
}
}
js代码:
export default {
// props:['id']
// props:['userName']
props:['userName','querys']
}
获取:
通过 props传递——函数模式 , 获取路由组件传递参数值 /about/chilrenRoute2/:id 路由的id值: {{ this.userName }} , {{ this.querys }} , {{ $route.params }}
1. 全局导航钩子 主要有两种钩子:前置守卫、后置钩子
router.beforeEach 全局前置守卫 进入路由之前
router.afterEach 全局后置钩子 进入路由之后
router.beforeResolve 全局解析守卫(2.5.0+) 在beforeRouteEnter调用之后调用
2. 路由独享的钩子
beforeEnter: (to, from, next) => {}
3. 路由组件内的导航钩子
beforeRouteEnter 进入路由前
beforeRouteUpdate (2.2) 路由复用同一个组件时
beforeRouteLeave 离开当前路由时
三个参数 to 、from 、next 分别的作用:
to 和 from 是将要进入和将要离开的 路由对象,
路由对象指的是平时通过 this.$route获取到的路由对象。
next:Function 这个参数是个函数,且必须调用,否则不能进入路由(页面空白)。
beforeRouteEnter
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
虽然无法直接获取组件实力
但是我们可以通过next参数的回调函数获取到当前实例进行操作
beforeRouteEnter: (to, from, next) => {
next((vm) => {
//vm就是当前组件实例
});
}
beforeRouteUpdate
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
beforeRouteLeave
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
hash模式:
url里面带有#号,开发中默认的就是hash模式,hash虽然出现在URL中,但是不会被包括在HTTP请求中,所以改变hash不会重新刷新页面
路由的哈希模式就是利用了window.onhashchange事件,也就是url中的hash值(#号后面的值)如果有变化,就会自动调用hashchange的监听事件,在hashchange的监听事件内可以得到改变后的url,这样就能找到对应页面进行加载
window.addEventListener('hashchange', () => {
// 把改变后的url地址栏的url赋值给data的响应式数据current,调用router-view去加载对应的页面
this.data.current = window.location.hash.substr(1)
})
历史模式:
利用了H5新增的pushState()、replaceState()方法。当这两个方法执行时,只能改变当前地址栏的URL,但是浏览器不会像后端发起请求,也不会触发popstate事件的执行。
也就是说,完成URL跳转而无需重新加载页面,由于vue是单页面的形式,当后台没有正确配置的时候,需要我们自己去配置404页面
很多时候,我们需要将给定匹配模式的路由映射到同一个组件,这种情况就需要定义动态路由。
例如,我们有一个 User组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用动态路径参数(dynamic segment)来达到这个效果:{path: '/user/:id', compenent: User},其中:id就是动态路径参数。
用redirect即可定位到相对位置
const routes = [
{
path: '/users/:id/posts',
redirect: to => {
// 方法接收目标路由作为参数
// return 重定向的字符串路径/路径对象
},
},
]
路由懒加载也就是延迟加载没在需要的时候记加载,随用随载,也是前端优化的一种手段
懒加载的实现方法:
1、ES6标准语法import()
const Foo = () => import('../components/Foo')
const Aoo = () => import('../components/Aoo')
export default new Router({
routes: [
{
path: '/Foo',
name: 'Foo',
component: Foo
},
{
path: '/Aoo',
name: 'Aoo',
component: Aoo
}
]
})
就是定义一个变量,变量就是路由文件,然后在路由中随用随导入
2、Vue异步加载技术
1:vue-router配置路由,使用vue的异步组件技术,可以实现懒加载,此时一个组件会生成一个js文件。
2:component: resolve => require(['放入需要加载的路由地址'], resolve)
{
path: '/problem',
name: 'problem',
component: resolve => require(['../pages/home/problemList'], resolve)
}
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。利用路由懒加载我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样会更加高效,是一种优化手段
一般来说,对所有的路由都使用动态导入是个好主意
给component选项配置一个返回 Promise 组件的函数就可以定义懒加载路由。例如:{ path: '/users/:id', component: () => import('./views/UserDetails') }
结合注释 () => import(/* webpackChunkName: "group-user" */ './UserDetails.vue') 可以做webpack代码分块
1、vue-router使用pushState进行路由更新,静态跳转,页面不会重新加载;location.href会触发浏览器,页面重新加载一次
2、vue-router使用diff算法,实现按需加载,减少dom操作
3、vue-router是路由跳转或同一个页面跳转;location.href是不同页面间跳转;
4、vue-router是异步加载this.$nextTick(()=>{获取url});location.href是同步加载
在router.js中 路由是从上到下执行的 只需要在最后一行把path写成 * 并且指定一个404.vue页面即可
如何触发404页面,比如你的域名是http://localhost:8080/,当你进入一个没有声明/匹配的路由页面时就会跳到404页面,
比如访问了http://localhost:8080/无此页面,就会跳到404页面,如果没有声明一个404页面,那就会跳到一个空白页面
{
path: '*',
name: '/404',
component: resolve => require(['@/components/404.vue'], resolve),
}
1、用组件内的守卫中的:beforeRouteLeave去实现
keep-alivebeforeRouteLeave (to, from, next) {
if(用户已经输入信息){
//出现弹窗提醒保存草稿,或者自动后台为其保存
}else{
next(true);//用户离开
}
}
2、使用keep-alive去缓存组件
vue-router 有 3 种路由模式:hash、history、abstract,对应的源码如下所示
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract':
this.history = new AbstractHistory(this, options.base)
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid mode: ${mode}`)
}
}
其中,3 种路由模式的说明如下:
hash: 使用 URL hash 值来作路由,支持所有浏览器
history : 依赖 HTML5 History API 和服务器配置
abstract : 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.
1、使用afterEach
router.afterEach((to,from,next) => {
window.scrollTo(0,0);
});
2、该功能只能在H5 history下去使用
注意: 这个功能只在 html5 history 模式下可用。
const router = new vueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
// return 期望滚动到哪个的位置
}
})
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}
1.导航被触发
2.在即将离开的组件里调用beforeRouteLeave守卫
3.调用全局前置守卫beforeEach守卫
4.在重用的组件里调用beforeRouteUpdate守卫 / 调用路由配置的beforeEnter守卫
5.解析异步路由组件
6.在被激活的组件里调用beforeRouteEnter
7.调用全局的beforeResolve守卫
8.导航被确认
9.调用全局的 afterEach 钩子
10.触发DOM更新
11.用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。
视图模型双向绑定,是Model-View-ViewModel的缩写,也就是把MVC中的Controller演变成ViewModel。Model层代表数据模型,View代表UI组件,ViewModel是View和Model层的桥梁,数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据。以前是操作DOM结构更新视图,现在是数据驱动视图。
区别:vue数据驱动,通过数据来显示视图层而不是节点操作,而 jq更多的是操作 DOM 还有动画
场景:用于数据操作比较多的场景,更加便捷
优点
1.低耦合。视图(View)可以独立于Model变化和修改,一个Model可以绑定到不同的View上,当View变化的时候Model可以不变化,当Model变化的时候View也可以不变;
2.可重用性。你可以把一些视图逻辑放在一个Model里面,让很多View重用这段视图逻辑。
3.独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
4.可测试。
缺点:
Bug 很难被调试: 因为使⽤双向绑定的模式,当你看到界⾯异常了, 有可能是你 View 的代码有 Bug,也可能是 Model 的代码有问题。数据绑定使得⼀个位置的 Bug 被快速传递到别的位置,要定位出问题的原始地⽅就变得不那么容易了。另外,数据绑定的声明是指令式地写在 View 的模版当中的,这些内容是没办法去打 debug 的。
内存花费高:⼀个⼤的模块中 model 也会很⼤,虽然使⽤⽅便了也很容易保证了数据的⼀致性。但需要⻓期持有 model ,不释放内存就造成了花费更多的内存。
对于⼤型的图形应⽤程序,视图状态较多,ViewModel 的构建和维护的成本都会⽐较⾼
1.一个组件被复用多次的话,也就会创建多个实例。本质上,这些实例用的都是同一个构造函数。2.如果data是对象的话,对象属于引用类型,会影响到所有的实例。所以为了保证组件不同的实例之间data不冲突,data必须是一个函数。
1.当 v-for 和 v-if 处于同一个节点时,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。如果要遍历的数组很大,而真正要展示的数据很少时,这将造成很大的性能浪费
2.这种场景建议使用 computed,先对数据进行过滤
注意:3.x 版本中 v-if 总是优先于 v-for 生效。由于语法上存在歧义,建议避免在同一元素上同时使用两者。比起在模板层面管理相关逻辑,更好的办法是通过创建计算属性筛选出列表,并以此创建可见元素。
解惑传送门 ☞ # v-if 与 v-for 的优先级对比非兼容
Vuex就是一个仓库,仓库里面放了很多对象。其中state就是数据源存放地,对应于与一般Vue对象里面的data
state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新
它通过mapState把全局的 state 和 getters 映射到当前组件的 computed 计算属性中
答案2
state属性是Vuex中用于存放组件之间共享的数据;也就是说,我们把一些组件之间共享的状态主要存放在state属性中;它采用的是单一状态树——用一个对象就包含了全部的应用层级状态。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。
在vue2.0和vue3.0版本中的底层实现有所不同,简单了来说就是处理属性的getter/setter部分从Object.defineProperty替换成了Proxy(不过vue3也保留了Object.defineProperty方式用于支持IE浏览器)
vue2.0实现原理
当一个普通的javascript对象传入vue实例作为data选项时,vue将遍历data的所有属性,并使用Object.defineProperty重写这些属性的getter/setter方法,这些属性的getter/setter对于用户不可见,但是vue可以利用他们来追踪依赖,在属性值被访问和修改时通知变更。每个组件实例都对应一个watcher实例,它会在组件渲染的过程中访问过的属性设置为依赖。之后当属性的setter触发时,会通知watcher对关联的组件进行重新渲染。
vue的响应式系统是基于数据拦截+发布订阅的模式,主要包含三个部分:
Observer:通过Object.defineProperty拦截data属性的setter/getter方法,从而使每个属性都拥有一个Dep,当触发getter时收集依赖(使用该属性的watcher),当触发setter时通知更新;
Dep:依赖收集器,用于维护依赖data属性的所有Watcher,并分发更新;
Watcher:将视图依赖的属性绑定到Dep中,当数据修改时触发setter,调用Dep的notify方法,通知所有依赖该属性的Watcher进行update更新视图,使属性值与视图绑定起来;
Compile:模板指令解析器,对模板每个元素节点的指令进行扫描解析,根据指令模板替换属性数据,同时注入Watcher更新数据的回调方法。(本文不涉及Compile的源码部分)
vue3.0实现原理
当一个普通的javascript对象传入vue实例作为data选项时,vue会将其转化为Proxy。首次渲染后,组件将跟踪在渲染过程中被访问的属性,组件就成了这些属性的订阅者。当proxy拦截到set操作时,将通知所有订阅了该属性的组件进行重新渲染。
答案2
任何一个 Vue Component 都有一个与之对应的 Watcher 实例
2、Vue 的 data 上的属性会被添加 getter 和 setter 属性
3、当 Vue Component render 函数被执行的时候, data 上会被 触碰(touch), 即被读, getter 方法会被调用, 此时 Vue 会去记录此 Vue component 所依赖的所有 data。(这一过程被称为依赖收集)
4、data 被改动时(主要是用户操作), 即被写, setter 方法会被调用, 此时 Vue 会去通知所有依赖于此 data 的组件去调用他们的 render 函数进行更新
组件之间通信主要分为三种:父子传参,子父传参,兄弟传参。
1.父子传参:父组件通过自定义属性的方式传参,通过props属性给子组件传参,子组件通过props属性去接收参数
2.子父传参:子组件通过自定义事件的方式传参,通过$emit去进行传参。
答案2
vue中8种常规的通信方案
通过 props 传递
通过 $emit 触发自定义事件
使用 ref
EventBus
$parent 或$root
attrs 与 listeners
Provide 与 Inject
Vuex
组件间通信的分类可以分成以下
父子关系的组件数据传递选择 props 与 $emit进行传递,也可选择ref
兄弟关系的组件数据传递可选择$bus,其次可以选择$parent进行传递
祖先与后代组件数据传递可选择attrs与listeners或者 Provide与 Inject
复杂关系的组件数据传递可以通过vuex存放共享的变量
Vue 组件间通信是面试常考的知识点之一,这题有点类似于开放题,你回答出越多方法当然越加分,表明你对 Vue 掌握的越熟练。Vue 组件间通信只要指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信,下面我们分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信。
(1)props / $emit 适用 父子组件通信
这种方法是 Vue 组件的基础,相信大部分同学耳闻能详,所以此处就不举例展开介绍。
(2)ref 与 $parent / $children 适用 父子组件通信
ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
$parent / $children:访问父 / 子实例
(3)EventBus ($emit / $on) 适用于 父子、隔代、兄弟组件通信
这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件。
(4)$attrs/$listeners 适用于 隔代组件通信
$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 inheritAttrs 选项一起使用。
$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
(5)provide / inject 适用于 隔代组件通信
祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。 provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
(6)Vuex 适用于 父子、隔代、兄弟组件通信
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。
computed是vue的计算属性,是根据依赖关系进行缓存的计算,只有在它的相关依赖发生改变时才会进行更新。
通过getter和setter实现对属性数据的显示和监视,计算属性存在缓存, 多次读取只执行一次getter计算。
首页可以控制导航跳转,beforeEach,afterEach等,一般用于页面title的修改。一些需要登录才能调整页面的重定向功能。
beforeEach主要有3个参数to,from,next。
to:route即将进入的目标路由对象。
from:route当前导航正要离开的路由。
next:function一定要调用该方法resolve这个钩子。执行效果依赖next方法的调用参数。可以控制网页的跳转
1.在 components(专门放组件的文件夹)下创建一个 xxx.vue文件
2.在 xxx.vue文件里使用组件
import headerInfo from "../common/headerInfo"; //引入组件
components: { //声明成为自己的组件
headerInfo
},
问题:
组件命名为 headerInfo ,使用的时候则 header-info。
答案2
vue.cli自定义组件
//第一步:在components目录新建你的组件文件indexPage.vue,script一定要
export defalt{}
//第二步:在需要用的页面或组件中导入:
import indexPage from '@/components/indexPage.vue'
//第三步:注入到Vue的子组件components属性上面
components:{indexPage}
//第四步:在templaate视图view中使用,例如有indexPage命名,使用index-page等
答案3
在 components 目录新建组件文件
在需要用到的页面import中导入
使用component注册
在 template 视图中使用组件标签
webpack中提供了require.ensure()来实现按需加载。以前引入路由是通过import 这样的方式引入,改为const定义的方式进行引入。
不进行页面按需加载引入方式:import home from '../../common/home.vue' 进行页面按需加载的引入方式:const home = r => require.ensure( [], () => r (require('../../common/home.vue')))
在音乐app中使用的路由懒加载方式为:
const Recommend = (resolve) => { import('components/recommend/recommend').then((module) => { resolve(module) }) }
const Singer = (resolve) => { import('components/singer/singer').then((module) => { resolve(module) }) }
beforeCreate:可以加Loading事件。
create:初始化完成时的事件写在这,异步请求也适宜在这里调用(请求不宜过多,避免白屏时间太长)。
可以在这里结束loading事件,还做一些初始化,或者实现函数的自执行。此时未挂载DOM,若在此阶段进行DOM操作一定要放在Vue.nextTick()的回调函数中。
mounted:此时完成挂载DOM和渲染,需要操作DOM的方法可以放在这里,也可以在这发起后端请求,拿回数据,配合路由钩子做一些事情。
beforeupdate:可以更新前访问现有的DOM,如手动移出添加的事件监听器。
updated:组件DOM已完成更新,可执行依赖的DOM操作。
注意:不要再此函数中操作数据(修改属性),会陷入死循环。
activated:在使用vue-router时有时需要使用
deactivated:
beforestory:销毁前,可以做一些删除提示,如:你确定删除XX吗?
destory:销毁后,这时组件已经没有了,无法操作里面的任何东西了。
答案2
beforecreate : 可以在这加个loading事件,在加载实例时触发
created : 初始化完成时的事件写在这⾥,如在这结束loading事件,异步请求也适宜在这⾥调⽤
mounted : 挂载元素,获取到DOM节点
updated : 如果对数据统⼀处理,在这⾥写上相应函数
beforeDestroy : 可以做⼀个确认停⽌事件的确认框
nextTick : 更新数据后⽴即操作dom
css的预编译语言。
使用步骤:
第一步:先装css-loader、node-loader、sass-loader等加载器模块;
第二步:在build目录找到webpack.base.config.js,在extends属性中加一个拓展.scss;
第三步:在同一个文件,配置一个module属性;
第四步:然后在组件的style标签加上lang属性 ,例如:lang=”scss”;
特性:
可以用变量,例如($变量名称=值);
可以用混合器;
可以嵌套
先转化成AST树,再得到render函数返回VNode (Vue的虚拟DOM节点)
详细步骤:首先通过compile编译器把template编译出AST语法树,然后AST会经过generate(将
AST语法树转化成render function字符串的过程)得到render函数,render的返回值是VNode
VNode是Vue的虚拟DOM节点,里面有标签名,子节点,文本等
Vue与AngularJS的区别
Angular采用TypeScript开发, 而Vue可以使用javascript也可以使用TypeScript
AngularJS依赖对数据做脏检查,所以Watcher越多越慢;Vue.js使用基于依赖追踪的观察并且使用异步队列更新,所有的数据都是独立触发的。
AngularJS社区完善, Vue的学习成本较小
Vue与React的区别
vue组件分为全局注册和局部注册,在react中都是通过import相应组件,然后模版中引用;
props是可以动态变化的,子组件也实时更新,在react中官方建议props要像纯函数那样,输入输出一致对应,而且不太建议通过props来更改视图;
子组件一般要显示地调用props选项来声明它期待获得的数据。而在react中不必需,另两者都有props校验机制;
每个Vue实例都实现了事件接口,方便父子组件通信,小型项目中不需要引入状态管理机制,而react必需自己实现;
使用插槽分发内容,使得可以混合父组件的内容与子组件自己的模板;
多了指令系统,让模版可以实现更丰富的功能,而React只能使用JSX语法;
Vue增加的语法糖computed和watch,而在React中需要自己写一套逻辑来实现;
react的思路是all in js,通过js来生成html,所以设计了jsx,还有通过js来操作css,社区的styled-component、jss等;而 vue是把html,css,js组合到一起,用各自的处理方式,vue有单文件组件,可以把html、css、js写到一个文件中,html提供了模板引擎来处理。
react做的事情很少,很多都交给社区去做,vue很多东西都是内置的,写起来确实方便一些, 比如 redux的combineReducer就对应vuex的modules, 比如reselect就对应vuex的getter和vue组件的computed, vuex的mutation是直接改变的原始数据,而redux的reducer是返回一个全新的state,所以redux结合immutable来优化性能,vue不需要。
react是整体的思路的就是函数式,所以推崇纯组件,数据不可变,单向数据流,当然需要双向的地方也可以做到,比如结合redux-form,组件的横向拆分一般是通过高阶组件。而vue是数据可变的,双向绑定,声明式的写法,vue组件的横向拆分很多情况下用mixin
答案2
基本概念
Angular 是一个应用设计框架与开发平台,用于创建高效、复杂、精致的单页面应用。
React 是一个用于构建用户界面的 JavaScript 库
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
三者比较
相同点
1.都是基于javascript/typescript的前端开发库,为前端开发提供高效、复用性高的开发方式
2.都有组件和模板的开发思想
3.各自的组件都有生命周期,不用的组件可以卸载,不占用资源
4.都支持指令,如样式、事件等的指令
不同点
1.创始和发行不同:Angular是由googl提供支持的,初始发行于 2016年9月;React由Facebook维护,初始发行于 2013年3月;Vue是由前google人员创建,初始发行于2014年2月
2.应用类型不同:Angular支持开发native应用程序、SPA单页应用程序、混合应用程序和web应用程序;React支持开发SPA和移动应用程序;Vue支持开发高级SPA,开始支持native应用程序
3.模型不同:angular基于MVC(模型-视图-控制器)架构;react和vue是基于Virtual DOM(虚拟的文档对象模型)
4、数据流流向不同:Angular使用的是双向数据绑定,React用的是单数据流的,而Vue则支持两者。
5. 对微应用和微服务的支持不同:Angular使用的是TypeScript,因此它更适合于单页Web应用(single page web application,SPA),而非微服务。相反,React和Vue的灵活性更适合微应用和微服务的开发。
6. 对原生应用的支持不同:React Native为iOS和Android开发原生应用;Angular的NativeScript已被原生应用所采用,特别是Ionic框架已经被广泛地运用在制作混合应用等方面;Vue的Weex平台正在开发之中,尚无下一步使之成为全面跨平台的计划。
7. 框架和库:Angular 是一个框架而不是一个库,因为它提供了关于如何构建应用程序的强有力的约束,并且还提供了更多开箱即用的功能。React 和 Vue 是是一种库,可以和各种包搭配。
答案3
=> 相同点:
1. 数据驱动页面,提供响应式的试图组件
2. 都有virtual DOM,组件化的开发,通过props参数进行父子之间组件传递数据,都实现了webComponents规范
3. 数据流动单向,都支持服务器的渲染SSR
4. 都有支持native的方法,react有React native, vue有wexx
=> 不同点:
1.数据绑定:Vue实现了双向的数据绑定,react数据流动是单向的
2.数据渲染:大规模的数据渲染,react更快
3.使用场景:React配合Redux架构适合大规模多人协作复杂项目,Vue适合小快的项目
4.开发风格:react推荐做法jsx + inline style把html和css都写在js了
答案4
React和Vue在框架设计上有很多相同点
使用 Virtual DOM
提供了响应式 (Reactive) 和组件化 (Composable) 的视图组件。
将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库。
性能、包体积大小也都不是决定因素。
它们在编码风格和使用细节上存在差别:
React的API简洁,Vue的API更多,因此使用Vue编写代码可能更少。
Vue更容易上手(因为帮开发者做了很多事情,computed、双向绑定等),React要实现相同功能需要用户手操作,例如需要通过受控组件来实现表单的数据同步,不如Vue的v-model语法糖更便捷。而且Vue的模板语法更贴近原生,因而更容易接受和理解。
React虽然API简单,但也因此在某些场景需要一些实践来优化,如shouldComponentUpdate,Vue使用数据劫持,能够做到改变了才触发渲染,更精准。
模板语法 VS JSX语法,其实Vue也可以支持JSX,JSX表达能力更强,模板语法更直观。
React有一些主张,例如纯函数等,对编写项目有一定的限制,有的观点认为React规范能尽可能保证项目少出bug,所以更适合大型项目。
react社区较大,Vue及周边都主要是官方在维护,更稳定一些。
超大规模的首屏渲染上React有一些优势,因为Vue需要做一些初始化工作。
React组件是继承React.Component,Vue是对象,因此React可以实现基于继承的组件复用(虽然并不推荐),而Vue不行。
在响应式原理上也存在差别:
React和Vue在响应式的原理上有所不同,Vue是通过数据劫持,实现当数据变化时候响应式更新,React是在setState时候diff组件树。
第一种方式:router-link (声明式路由)
第二种方式:router.push(编程式路由)
第三种方式:this.$router.push() (函数里面调用)
第四种方式:this.$router.replace() (用法同上,push)
第五种方式:this.$router.go(n)
答案2
1. router-link
2. this.$router.push() (函数里面调用)
3. this.$router.replace() (用法同push)
不带参
1.1 router-link
// 注意:router-link中链接如果是'/'开始就是从根路由开始,如果开始不带'/',则从当前路由开始。
1.2 this.$router.push()
this.$router.push('/home')
this.$router.push({name:'home'})
this.$router.push({path:'/home'})
1.3 this.$router.replace() (用法同push)
二、带参
2.1 router-link
// params传参数 (类似post)
// 路由配置 path: "/home/:id" 或者 path: "/home:id"
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参 $route.params.id
// script 取参 this.$route.params.id
// query传参数 (类似get,url后面会显示参数)
// 路由可不配置
// html 取参 $route.query.id
// script 取参 this.$route.query.id
2.2 this.$router.push(query传参
this.$router.push({name:'home',query: {id:'1'}})
this.$router.push({path:'/home',query: {id:'1'}})
// html 取参 $route.query.id
// script 取参 this.$route.query.id
params传参
this.$router.push({name:'home',params: {id:'1'}}) // 只能用 name
// 路由配置 path: "/home/:id" 或者 path: "/home:id" ,
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参 $route.params.id
// script 取参 this.$route.params.id
2.3 this.$router.replace() (用法同push)
this.$router.go(n)向前或者向后跳转n个页面,n可为正整数或负整数
区别:
this.$router.push 跳转到指定url路径,并想history栈中添加一个记录,点击后退会返回到上一个页面
this.$router.replace 跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面 (就是直接替换了当前页面)
this.$router.go(n) 向前或者向后跳转n个页面,n可为正整数或负整数
后面 hash 值的变化,不会导致浏览器向服务器发出请求,浏览器不发出请求,就不会刷新页面;通过监听 hashchange 事件可以知道 hash 发生了哪些变化,然后根据 hash 变化来实现更新页面部分内容的操作。
hash虽然在URL中,但不被包括在HTTP请求中
history 模式的实现,主要是 HTML5 标准发布的两个 API,pushState 和 replaceState,这两个 API 可以在改变 URL,但是不会发送请求。这样就可以监听 url 变化来实现更新页面部分内容的操作。
两种模式的区别:
首先是在 URL 的展示上,hash 模式有“#”,history 模式没有
刷新页面时,hash 模式可以正常加载到 hash 值对应的页面,而 history 没有处理的话,会返回 404,一般需要后端将所有页面都配置重定向到首页路由
在兼容性上,hash 可以支持低版本浏览器和 IE
答案2
1、Hash模式:vue-router 默认 hash 模式,使用 URL 的 hash 来模拟一个完整的 URL
因此改变 hash 不会重新加载页面;同时每一次改变#后的部分
,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮
,就可以回到上一个位置。所以说 Hash模式通过锚点值的改变
,根据不同的值,渲染指定 DOM 位置的不同数据。hash模式的原理是 onhashchange 事件(监测hash值变化),可以在 window对象上监听这个事件
2、History模式:由于hash模式会在url中自带#,如果不想要很丑的 hash,我们可以用路由的 history 模式
只需要在配置路由规则时,加入"mode: 'history'"。history采用 HTML5的新特性,且提供了两个新方法: pushState(),replaceState() 可以对浏览器历史记录栈进行修改
在当前已有的 back、forward、go 基础之上,它们提供了对历史记录修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求
history模式的问题:
在 history下,你可以自由的修改 path,但当刷新页面时,如果服务器中没有相应的响应或者资源就会返回 404
所以你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源
,则应该返回同一个 index.html页面,这个页面就是你 app依赖的页面。
{ path: "*", redirect: "/index" } //重定向
低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变
可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑
可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写
Vue 是一个构建数据驱动的 Web 界面的渐进式框架。 Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。核心是一个响应的数据绑定系统。 关于 Vue 的优点,主要有响应式编程、组件化开发、虚拟 DOM
这里的响应式不是 @media 媒体查询中的响应式布局,而是指 Vue 会自动对页面中某些数据的变化做出响应。这也就是 Vue 最大的优点,通过 MVVM 思想实现数据的双向绑定,让开发者不用再操作 DOM 对象,有更多的时间去思考业务逻辑。
Vue 通过组件,把一个单页应用中的各种模块拆分到一个一个单独的组件(component)中,我们只要先在父级应用中写好各种组件标签(占坑),并且在组件标签中写好要传入组件的参数(就像给函数传入参数一样,这个参数叫做组件的属性),然后再分别写好各种组件的实现(填坑),然后整个应用就算做完了。 组件化开发的优点:提高开发效率、方便重复使用、简化调试步骤、提升整个项目的可维护性、便于协同开发。
在传统开发中,用 JQuery 或者原生的 JavaScript DOM 操作函数对 DOM 进行频繁操作的时候,浏览器要不停的渲染新的 DOM 树,导致在性能上面的开销特别的高。 而 Virtual DOM 则是虚拟 DOM 的英文,简单来说,他就是一种可以预先通过 JavaScript 进行各种计算,把最终的 DOM 操作计算出来并优化,由于这个 DOM 操作属于预处理操作,并没有真实的操作 DOM,所以叫做虚拟 DOM。最后在计算完毕才真正将 DOM 操作提交,将 DOM 操作变化反映到 DOM 树上。
组件就是把图形、非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式,在 Vue 中每一个.vue 文件都可以视为一个组件
组件的优势
降低整个系统的耦合度,在保持接口不变的情况下,我们可以替换不同的组件快速完成需求,例如输入框,可以替换为日历、时间、范围等组件作具体的实现
调试方便,由于整个系统是通过组件组合起来的,在出现问题的时候,可以用排除法直接移除组件,或者根据报错的组件快速定位问题,之所以能够快速定位,是因为每个组件之间低耦合,职责单一,所以逻辑会比分析整个系统要简单
提高可维护性,由于每个组件的职责单一,并且组件在系统中是被复用的,所以对代码进行优化可获得系统的整体升级
插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:
添加全局方法或者属性。如: vue-custom-element
添加全局资源:指令/过滤器/过渡等。如 vue-touch
通过全局混入来添加一些组件选项。如vue-router
添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如vue-router
两者的区别主要表现在以下几个方面:
编写形式
注册形式
使用场景
编写一个组件,可以有很多方式,我们最常见的就是 vue 单文件的这种格式,每一个.vue文件我们都可以看成是一个组件
vue 文件标准格式
export default{
...
}
我们还可以通过template属性来编写一个组件,如果组件内容多,我们可以在外部定义template组件内容,如果组件内容并不多,我们可直接写在template属性上
// 组件显示的内容
Vue.component('componentA',{ template: '#testComponent' template: `
` // 组件内容少可以通过这种形式 })
vue插件的实现应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象
MyPlugin.install = function (Vue, options) {
// 1. 添加全局方法或 property
Vue.myGlobalMethod = function () {
// 逻辑...
}
// 2. 添加全局资源
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// 逻辑...
}
...
})
// 3. 注入组件选项
Vue.mixin({
created: function () {
// 逻辑...
}
...
})
// 4. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
// 逻辑...
}
}
vue 组件注册主要分为全局注册与局部注册
全局注册通过Vue.component方法,第一个参数为组件的名称,第二个参数为传入的配置项
Vue.component("my-component-name", {
/* ... */
});
局部注册只需在用到的地方通过components属性注册一个组件
const component1 = {...} // 定义一个组件
export default {
components:{
component1 // 局部注册
}
}
插件的注册通过Vue.use()的方式进行注册(安装),第一个参数为插件的名字,第二个参数是可选择的配置项
Vue.use(插件名字, {
/* ... */
});
注意的是:
注册插件的时候,需要在调用 new Vue() 启动应用之前完成
Vue.use会自动阻止多次注册相同插件,只会注册一次
组件 (Component) 是用来构成你的 App 的业务模块,它的目标是 App.vue
插件 (Plugin) 是用来增强你的技术栈的功能模块,它的目标是 Vue 本身
简单来说,插件就是指对Vue的功能的增强或补充
采用ES6的import ... from ...语法或CommonJS的require()方法引入插件
使用全局方法Vue.use( plugin )使用插件,可以传入一个选项对象Vue.use(MyPlugin, { someOption: true })
Vuex 只是在内存中保存状态,刷新后就会丢失,如果要持久化就需要保存起来。
localStorage就很合适,提交mutation的时候同时存入localStorage,在store中把值取出来作为state的初始值即可。
也可以使用第三方插件,推荐使用vuex-persist插件,它是为 Vuex 持久化储存而生的一个插件,不需要你手动存取storage,而是直接将状态保存至 cookie 或者 localStorage中。
mixin(混入), 它提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。
使用场景:不同组件中经常会用到一些相同或相似的代码,这些代码的功能相对独立。可以通过mixin 将相同或相似的代码提出来。
缺点:
变量来源不明确
多 mixin 可能会造成命名冲突(解决方式:Vue 3的组合API)
mixin 和组件出现多对多的关系,使项目复杂度变高。
答案1实现原理
nextTick是等待下一次 DOM 更新刷新的工具方法。
Vue有个异步更新策略,意思是如果数据变化,Vue不会立刻更新DOM,而是开启一个队列,把组件更新函数保存在队列中,在同一事件循环中发生的所有数据变更会异步的批量更新。这一策略导致我们对数据的修改不会立刻体现在DOM上,此时如果想要获取更新后的DOM状态,就需要使用nextTick。
开发时,有两个场景我们会用到nextTick:
created中想要获取DOM时;
响应式数据变化后获取DOM更新后的状态,比如希望获取列表更新后的高度。
nextTick签名如下:function nextTick(callback?: () => void): Promise
所以我们只需要在传入的回调函数中访问最新DOM状态即可,或者我们可以await nextTick()方法返回的Promise之后做这件事。
在Vue内部,nextTick之所以能够让我们看到DOM更新后的结果,是因为我们传入的callback会被添加到队列刷新函数(flushSchedulerQueue)的后面,这样等队列内部的更新函数都执行完毕,所有DOM操作也就结束了,callback自然能够获取到最新的DOM值。
答案2
作用:vue 更新 DOM 是异步更新的,数据变化,DOM 的更新不会马上完成,nextTick 的回调是在下次 DOM 更新循环结束之后执行的延迟回调。
实现原理:nextTick 主要使用了宏任务和微任务。根据执行环境分别尝试采用
Promise:可以将函数延迟到当前函数调用栈最末端
MutationObserver :是 H5 新加的一个功能,其功能是监听 DOM 节点的变动,在所有 DOM 变动完成后,执行回调函数
setImmediate:用于中断长时间运行的操作,并在浏览器完成其他操作(如事件和显示更新)后立即运行回调函数
如果以上都不行则采用 setTimeout 把函数延迟到 DOM 更新之后再使用,原因是宏任务消耗大于微任务,优先使用微任务,最后使用消耗最大的宏任务。
(官方对其的定义
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM
什么意思呢?
我们可以理解成,Vue 在更新 DOM 时是异步执行的。当数据发生变化,Vue将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新
Vue 的异步更新机制的核心是利用了浏览器的异步任务队列来实现的,首选微任务队列,宏任务队列次之。
当响应式数据更新后,会调用 dep.notify 方法,通知 dep 中收集的 watcher 去执行 update 方法,watcher.update 将 watcher 自己放入一个 watcher 队列(全局的 queue 数组)。
然后通过 nextTick 方法将一个刷新 watcher 队列的方法(flushSchedulerQueue)放入一个全局的 callbacks 数组中。
如果此时浏览器的异步任务队列中没有一个叫 flushCallbacks 的函数,则执行 timerFunc 函数,将 flushCallbacks 函数放入异步任务队列。如果异步任务队列中已经存在 flushCallbacks 函数,等待其执行完成以后再放入下一个 flushCallbacks 函数。
flushCallbacks 函数负责执行 callbacks 数组中的所有 flushSchedulerQueue 函数。
flushSchedulerQueue 函数负责刷新 watcher 队列,即执行 queue 数组中每一个 watcher 的 run 方法,从而进入更新阶段,比如执行组件更新函数或者执行用户 watch 的回调函数
⽤ timeline ⼯具。 通过 timeline 来查看每个函数的调⽤时常,定位出哪个函数的问题,从⽽能判断哪个组件出了问题。
vuex 是一个专为 Vue 应用程序开发的状态管理器,采用集中式存储管理应用的所有组件的状态。每一个 vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着应用中大部分的状态 (state)。
由于组件只维护自身的状态(data),组件创建时或者路由切换时,组件会被初始化,从而导致 data 也随之销毁。
在 main.js 引入 store,注入。只用来读取的状态集中放在 store 中, 改变状态的方式是提交 mutations,这是个同步的事物,异步逻辑应该封装在 action 中。
如果是 vue 的小型应用,那么没有必要使用 vuex,这个时候使用 vuex 反而会带来负担。组件之间的状态传递使用 props、自定义事件来传递即可。
但是如果涉及到 vue 的大型应用,那么就需要类似于 vuex 这样的集中管理状态的状态机来管理所有组件的状态。例如登录状态、加入购物车、音乐播放等,总之只要是开发 vue 的大型应用,都推荐使用 vuex 来管理所有组件状态。
state:Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据。
mutations:mutations定义的方法动态修改Vuex 的 store 中的状态或数据
getters:类似vue的计算属性,主要用来过滤一些数据。
action:actions可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。view 层通过 store.dispath 来分发 action
modules:项目特别复杂的时候,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理
最重要的区别-存储位置 vuex 存储在内存中,localstorage 则以文件的方式存储在本地,只能存储字符串类型的数据,存储对象需要 JSON 的 stringify 和 parse 方法进行处理。读取内存比读取硬盘速度要快。
应用场景 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。vuex 用于组件之间的传值。localstorage 是本地存储,是将数据存储到浏览器的方法,一般是在跨页面传递数据时使用 。Vuex 能做到数据的响应式,localstorage 不能。
数据时效性 刷新页面时 vuex 存储的值会丢失,localstorage 不会。注意:对于不变的数据确实可以用 localstorage 可以代替 vuex,但是当两个组件共用一个数据源(对象或数组)时,如果其中一个组件改变了该数据源,希望另一个组件响应该变化时,localstorage 无法做到。
Vuex 是一个专为 Vue.js 应用开发的状态管理模式 + 库。它采用集中式存储,管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
我们期待以一种简单的“单向数据流”的方式管理应用,即状态 -> 视图 -> 操作单向循环的方式。但当我们的应用遇到多个组件共享状态时,比如:多个视图依赖于同一状态或者来自不同视图的行为需要变更同一状态。此时单向数据流的简洁性很容易被破坏。因此,我们有必要把组件的共享状态抽取出来,以一个全局单例模式管理。通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。这是vuex存在的必要性,它和react生态中的redux之类是一个概念。
Vuex 解决状态管理的同时引入了不少概念:例如state、mutation、action等,是否需要引入还需要根据应用的实际情况衡量一下:如果不打算开发大型单页应用,使用 Vuex 反而是繁琐冗余的,一个简单 的 store 模式就足够了。但是,如果要构建一个中大型单页应用,Vuex 基本是标配。
我在使用vuex过程中感受到一些blabla
刷新浏览器,vuex中的state会重新变为初始状态
解决方案-插件vuex-persistedstate
可以通过watch选项或者watch方法监听状态
可以使用vuex提供的API:store.subscribe()
watch选项方式,可以以字符串形式监听$store.state.xx;subscribe方式,可以调用store.subscribe(cb),回调函数接收mutation对象和state对象,这样可以进一步判断mutation.type是否是期待的那个,从而进一步做后续处理。
watch方式简单好用,且能获取变化前后值,首选;subscribe方法会被所有commit行为触发,因此还需要判断mutation.type,用起来略繁琐,一般用于vuex插件中
watch方式
const app = createApp({
watch: {
'$store.state.counter'() {
console.log('counter change!');
}
}
})
subscribe方式:
store.subscribe((mutation, state) => {
if (mutation.type === 'add') {
console.log('counter change in subscribe()!');
}
})
localStorage的生命周期是永久的,关闭页面或浏览器之后localStorage中的数据也不会消失。localStorage除非主动删除数据,否则数据永远不会消失。
sessionStorage的生命周期是仅在当前会话下有效。sessionStorage引入了一个“浏览器窗口”的概念,sessionStorage是在同源的窗口中始终存在的数据。只要这个浏览器窗口没有关闭,即使刷新页面或者进入同源另一个页面,数据依然存在。但是sessionStorage在关闭了浏览器窗口后就会被销毁。同时独立的打开同一个窗口同一个页面,sessionStorage也是不一样的。
存储大小
localStorage和sessionStorage的存储数据大小一般都是:5MB
存储位置
localStorage和sessionStorage只能存储字符串类型,对于复杂的对象可以使用ECMAScript提供的JSON对象的stringify和parse来处理
获取方式
localStorage:window.localStorage
sessionStorage:window.sessionStorage
应用场景
localStorage:常用于长期登录(+判断用户是否已登录),适合长期保存在本地的数据
sessionStorage:敏感账号一次性登录
vue2在初始化的时候,对data中的每个属性使用definepropery调用getter和setter使之变为响应式对象。如果属性值为对象,还会递归调用defineproperty使之变为响应式对象。
vue3使用proxy对象重写响应式。proxy的性能本来比defineproperty好,proxy可以拦截属性的访问、赋值、删除等操作,不需要初始化的时候遍历所有属性,另外有多层属性嵌套的话,只有访问某个属性的时候,才会递归处理下一级的属性。
优势:
可以监听动态新增的属性;
可以监听删除的属性 ;
可以监听数组的索引和 length 属性;
在逻辑组织和逻辑复用方面,Composition API是优于Options API
因为Composition API几乎是函数,会有更好的类型推断。
Composition API对 tree-shaking 友好,代码也更容易压缩
Composition API中见不到this的使用,减少了this指向不明的情况
如果是小型组件,可以继续使用Options API,也是十分友好的
包含一个描述组件选项(data、methods、props等)的对象 options;
API开发复杂组件,同一个功能逻辑的代码被拆分到不同选项 ;
使用mixin重用公用代码,也有问题:命名冲突,数据来源不清晰;
vue3 新增的一组 api,它是基于函数的 api,可以更灵活的组织组件的逻辑。
解决options api在大型项目中,options api不好拆分和重用的问题。
proxy的性能本来比defineproperty好,proxy可以拦截属性的访问、赋值、删除等操作,不需要初始化的时候遍历所有属性,另外有多层属性嵌套的话,只有访问某个属性的时候,才会递归处理下一级的属性。
Object.defineProperty() 的问题主要有三个:
不能监听数组的变化
必须遍历对象的每个属性
必须深层遍历嵌套的对象
Proxy 拥有以下优势
操作时不是对原对象操作,是 new Proxy 返回的一个新对象
Proxy 的第二个参数可以有 13 种拦截方法,这比起 Object.defineProperty() 要更加丰富
Proxy 作为新标准受到浏览器厂商的重点关注和性能优化,相比之下 Object.defineProperty() 是一个已有的老方法。
针对对象:针对整个对象,而不是对象的某个属性,所以也就不需要对 keys 进行遍历。这解决了上述 Object.defineProperty() 第二个问题
支持数组:Proxy 不需要对数组的方法进行重载,省去了众多 hack,减少代码量等于减少了维护成本,而且标准的就是最好的。
vue.js 3.x中标记和提升所有的静态节点,diff的时候只需要对比动态节点内容;
Fragments(升级vetur插件): template中不需要唯一根节点,可以直接放文本或者同级标签
静态提升(hoistStatic),当使用 hoistStatic 时,所有静态的节点都被提升到 render 方法之外.只会在应用启动的时候被创建一次,之后使用只需要应用提取的静态节点,随着每次的渲染被不停的复用。
patch flag, 在动态标签末尾加上相应的标记,只能带 patchFlag 的节点才被认为是动态的元素,会被追踪属性的修改,能快速的找到动态节点,而不用逐个逐层遍历,提高了虚拟dom diff的性能。
缓存事件处理函数cacheHandler,避免每次触发都要重新生成全新的function去更新之前的函数
tree shaking 通过摇树优化核心库体积,减少不必要的代码.
设置对象为响应式对象。接收一个参数,判断这参数是否是对象。不是对象则直接返回这个参数,不做响应式处理。
创建拦截器handerler,设置get/set/deleteproperty。
「get」
收集依赖(track);
如果当前 key 的值是对象,则为当前 key 的对象创建拦截器 handler, 设置 get/set/deleteProperty;
如果当前的 key 的值不是对象,则返回当前 key 的值。
「set」
设置的新值和老值不相等时,更新为新值,并触发更新(trigger)。
「deleteProperty」
当前对象有这个 key 的时候,删除这个 key 并触发更新(trigger)。
接收一个函数作为参数。作用是:访问响应式对象属性时去收集依赖
接收两个参数:target 和 key
-如果没有 activeEffect,则说明没有创建 effect 依赖
-如果有 activeEffect,则去判断 WeakMap 集合中是否有 target 属性
-WeakMap 集合中没有 target 属性,则 set(target, (depsMap = new Map()))
-WeakMap 集合中有 target 属性,则判断 target 属性的 map 值的 depsMap 中是否有 key 属性
-depsMap 中没有 key 属性,则 set(key, (dep = new Set()))
-depsMap 中有 key 属性,则添加这个 activeEffect
判断 WeakMap 中是否有 target 属性,WeakMap 中有 target 属性,则判断 target 属性的 map 值中是否有 key 属性,有的话循环触发收集的 effect()。
响应式系统的重新配置,使用代理替换对象.define属性,使用代理优势:
可直接监控阵列类型的数据变化
监听的目标是对象本身,不需要像Object.defineProperty那样遍历每个属性,有一定的性能提升
可拦截应用、拥有密钥、有等13种方法,以及Object.define属性没有办法
直接添加对象属性/删除
新增组合API,更好的逻辑重用和代码组织
重构虚拟 DOM
模板编译时间优化,将一些静态节点编译成常量
slot优化,采取槽编译成懒人功能,拿槽渲染的决定留给子组件
在模板中提取和重用内联事件(最初,每次渲染时都会重新生成内联函数)
代码结构调整,更方便树摇动,使其更小
使用打字脚本替换流
答案2
1. 响应式系统的重新配置,使用proxy替换Object.defineProperty
2. typescript支持
3. 新增组合API,更好的逻辑重用和代码组织
4. v-if和v-for的优先级
5. 静态元素提升
6. 虚拟节点静态标记
7. 生命周期变化
8. 打包体积优化
9. ssr渲染性能提升
10. 支持多个根节点
Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
* `beforeDestroy`改名为 `beforeUnmount`
* `destroyed`改名为 `unmounted`
* Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
* `beforeCreate`===>`setup()`
* `created`=======>`setup()`
* `beforeMount` ===>`onBeforeMount`
* `mounted`=======>`onMounted`
* `beforeUpdate`===>`onBeforeUpdate`
* `updated` =======>`onUpdated`
* `beforeUnmount` ==>`onBeforeUnmount`
* `unmounted` =====>`onUnmounted`
Vue 实例有⼀个完整的⽣命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载 等⼀系列过程,称这是Vue的⽣命周期。
1、beforeCreate(创建前) :数据观测和初始化事件还未开始,此时 data 的响应式追踪、event/watcher 都还没有被设置,也就是说不能访问到data、computed、watch、methods上的方法和数据。
2、created(创建后) :实例创建完成,实例上配置的 options 包括 data、computed、watch、methods 等都配置完成,但是此时渲染得节点还未挂载到 DOM,所以不能访问到 `$el` 属性。
3、beforeMount(挂载前) :在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。此时还没有挂载html到页面上。
4、mounted(挂载后) :在el被新创建的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html 页面中。此过程中进行ajax交互。
5、beforeUpdate(更新前) :响应式数据更新时调用,此时虽然响应式数据更新了,但是对应的真实 DOM 还没有被渲染。
6、updated(更新后):在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。此时 DOM 已经根据响应式数据的变化更新了。调用时,组件 DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
7、beforeDestroy(销毁前) :实例销毁之前调用。这一步,实例仍然完全可用,`this` 仍能获取到实例。
8、destroyed(销毁后) :实例销毁后调用,调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用。
从 React Hook 从实现的角度来看,React Hook 是基于 useState 的调用顺序来确定下一个 re 渲染时间状态从哪个 useState 开始,所以有以下几个限制
* 不在循环中、条件、调用嵌套函数 Hook
* 你必须确保它总是在你这边 React Top level 调用函数 Hook
* 使用效果、使用备忘录 依赖关系必须手动确定
和 Composition API 是基于 Vue 的响应系统,和 React Hook 相比
* 在设置函数中,一个组件实例只调用一次设置,而 React Hook 每次重新渲染时,都需要调用 Hook,给 React 带来的 GC 比 Vue 更大的压力,性能也相对 Vue 对我来说也比较慢
* Compositon API 你不必担心调用的顺序,它也可以在循环中、条件、在嵌套函数中使用
* 响应式系统自动实现依赖关系收集,而且组件的性能优化是由 Vue 内部完成的,而 React Hook 的依赖关系需要手动传递,并且依赖关系的顺序必须得到保证,让路 useEffect、useMemo 等等,否则组件性能会因为依赖关系不正确而下降。
虽然Compoliton API看起来像React Hook来使用,但它的设计思路也是React Hook的参考。
打包大小减少41%
初次渲染快55%, 更新渲染快133%
内存减少54%
......
使用Proxy代替defineProperty实现响应式
重写虚拟DOM的实现和Tree-Shaking
......
Vue3可以更好的支持TypeScript
setup配置
ref与reactive
watch与watchEffect
provide与inject
Fragment
Teleport
Suspense
新的生命周期钩子
data 选项应始终被声明为一个函数
移除keyCode支持作为 v-on 的修饰符
data选项应始终被声明为一个函数。
过度类名的更改:
Vue2.x写法
.v-enter,
.v-leave-to {
opacity: 0;
}
.v-leave,
.v-enter-to {
opacity: 1;
}
Vue3.x写法
.v-enter-from,
.v-leave-to {
opacity: 0;
}
.v-leave-from,
.v-enter-to {
opacity: 1;
}
「移除」keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes
「移除」v-on.native修饰符
父组件中绑定事件
v-on:close="handleComponentEvent" v-on:click="handleNativeClickEvent" /> 子组件中声明自定义事件 export default { emits: ['close'] } 「移除」过滤器(filter) api层面Vue3新特性主要包括:Composition API、SFC Composition API语法糖、Teleport传送门、Fragments 片段、Emits选项、自定义渲染器、SFC CSS变量、Suspense 另外,Vue3.0在框架层面也有很多亮眼的改进: 更快 虚拟DOM重写 编译器优化:静态提升、patchFlags、block等 基于Proxy的响应式系统 更小:更好的摇树优化 更容易维护:TypeScript + 模块化 更容易扩展 独立的响应化模块 自定义渲染器 isRef: 检查一个值是否为一个 ref 对象 isReactive: 检查一个对象是否是由 reactive 创建的响应式代理 isReadonly: 检查一个对象是否是由 readonly 创建的只读代理 isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理 模板解析这种事,本质是将数据转化为一段 html ,最开始出现在后端,经过各种处理吐给前端。随着各种 mv* 的兴起,模板解析交由前端处理。 总的来说,Vue complier 是将 template 转化成一个 render 字符串。 可以简单理解成以下步骤: parse 过程,将 template 利用正则转化成AST 抽象语法树。 optimize 过程,标记静态节点,后 diff 过程跳过静态节点,提升性能。 generate 过程,生成 render 字符串 v-model :一般用在表达输入,很轻松的实现表单控件和数据的双向绑定 v-html: 更新元素的 innerHTML v-show 与 v-if: 条件渲染, 注意二者区别 使用了v-if的时候,如果值为false,那么页面将不会有这个html标签生成。v-show则是不管值为true还是false,html元素都会存在,只是CSS中的display显示或隐藏 v-on : click: 可以简写为@click,@绑定一个事件。如果事件触发了,就可以指定事件的处理函数 v-for:基于源数据多次渲染元素或模板块 v-bind: 当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM 语法:v-bind:title="msg"简写::title="msg" v-show // 控制元素是否展示 这个指令用于给Vue组件实现双向绑定,状态的改变会同步到组件上,组件的值的变化也会同步到状态上。 答案:v-model可以实现双向绑定,指令(v-bind:class、v-for、v-if、v-show、v-on)。vue的model层的data属性。绑定事件: vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。 v-model的实现具体步骤共分为四步: 第一步: 需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter和getter 这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化 第二步:compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图 第三步:Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是: 1、在自身实例化时往属性订阅器(dep)里面添加自己 2、自身必须有一个update()方法 3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。 第四步:MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。 使用了函数劫持的方式,重写了数组的方法,Vue将data中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样当调用数组api时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样就实现了监测数组变化。 接口请求一般放在mounted中,但需要注意的是服务端渲染时不支持mounted,需要放到created中 delete只是被删除的元素变成了 empty/undefined 其他的元素的键值还是不变。 Vue.delete直接删除了数组 改变了数组的键值。 assets文件夹是放静态资源; components是放组件; router是定义路由相关的配置; view视图; app.vue是一个应用主组件; main.js是入口文件 loader Eg:(陋得) vue-loader 是一个 webpack 的 loader,可以将单文件组件转换为 JavaScript 模块 解析.vue文件的一个加载器,跟template/js/style转换成js模块。 用途:js可以写es6、style样式可以scss或less、template可以加jade等 .stop:等统⼀JavaScript中的event.stopPropagation(),防⽌事件冒泡 .prevent:等同于JavaScript中的event。preventDefault(),防⽌执⾏预设的⾏为(如果事件可取消,则取消该事件,⽽不停⽌事件的进⼀步 capture:与事件冒泡的⽅向相反,事件捕获由外到内 self:只会触发⾃⼰范围内的事件,不包含⼦元素; once:只会触发⼀次。 1`v-if`和`v-show` * 频繁切换时使用`v-show`,利用其缓存特性 * 首屏渲染时使用`v-if`,如果为`false`则不进行渲染 2、`v-for`的`key` * 列表变化时,循环时使用唯一不变的`key`,借助其本地复用策略 * 列表只进行一次渲染时,`key`可以采用循环的`index` 3、侦听器和计算属性 * 侦听器`watch`用于数据变化时引起其他行为 * 多使用`compouter`计算属性顾名思义就是新计算而来的属性,如果依赖的数据未发生变化,不会触发重新计算 4、合理使用生命周期 * 在`destroyed`阶段进行绑定事件或者定时器的销毁 * 使用动态组件的时候通过`keep-alive`包裹进行缓存处理,相关的操作可以在`actived`阶段激活 5、数据响应式处理 * 不需要响应式处理的数据可以通过`Object.freeze`处理,或者直接通过`this.xxx = xxx`的方式进行定义 * 需要响应式处理的属性可以通过`this.$set`的方式处理,而不是`JSON.parse(JSON.stringify(XXX))`的方式 6、路由加载方式 * 页面组件可以采用异步加载的方式 7、插件引入 * 第三方插件可以采用按需加载的方式,比如`element-ui`。 8、减少代码量 * 采用`mixin`的方式抽离公共方法 * 抽离公共组件 * 定义公共方法至公共`js`中 * 抽离公共`css` 9、编译方式 * 如果线上需要`template`的编译,可以采用完成版`vue.esm.js` * 如果线上无需`template`的编译,可采用运行时版本`vue.runtime.esm.js`,相比完整版体积要小大约`30%` 10、渲染方式 * 服务端渲染,如果是需要`SEO`的网站可以采用服务端渲染的方式 * 前端渲染,一些企业内部使用的后端管理系统可以采用前端渲染的方式 11、字体图标的使用 答案2 1)编码阶段 尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher v-if和v-for不能连用 如果需要使用v-for给每项元素绑定事件时使用事件代理 SPA 页面采用keep-alive缓存组件 在更多的情况下,使用v-if替代v-show key保证唯一 使用路由懒加载、异步组件 防抖、节流 第三方模块按需导入 长列表滚动到可视区域动态加载 图片懒加载 (2)SEO优化 预渲染 服务端渲染SSR (3)打包优化 压缩代码 Tree Shaking/Scope Hoisting 使用cdn加载第三方模块 多线程打包happypack splitChunks抽离公共文件 sourceMap优化 (4)用户体验 骨架屏 PWA 还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。 v-if和v-for不要同时使用,因为会在每次渲染时候都要遍历列表并判断是否需要渲染,这个遍历操作其实是有一部分冗余或者完全不必要的。 应该用以下方式替换v-if和v-for同时使用的方案: 如果是为了过滤一个列表中的项目(v-for循环,v-if过滤条件),可以将列表作为计算属性,在computed中过滤出需要渲染的列表,再进行渲染。这样避免了每次渲染都计算(只在computed依赖的属性变化时候才计算),同时渲染列表是过滤了的,那么循环的次数也可能减少。 如果是为了控制整个列表的展示和隐藏(v-for循环,v-if判断整个列表是否需要展示),可以将判断条件放到父元素(ul、ol)上。这样展示和隐藏的判断只需要执行一次(在列表最开始)。 Vue3修改了v-if和v-for的优先级,v-if没有权限访问v-for的变量,这个需要注意。 1、压缩css、js文件 2、合并js、css文件,减少http请求 3、外部js、css文件放在最底下 4、减少dom操作,尽可能用变量替代不必要的dom操作 5、优化图像(格式选择、懒加载等等) 6、使用CDN 7、使用缓存 8、减少 http 请求(合并文件,合并图片) 9、标明高度和宽度 10、网址后加斜杠 11、优化TCP协议 有些图片图标尽可能使用字体图标 状态码分为5类: 1** 信息,服务器收到请求,需要请求者继续执行操作 2** 成功,操作被成功接收并处理 3** 重定向,需要进一步的操作以完成请求 4** 客户端错误,请求包含语法错误或无法完成请求 5** 服务器错误,服务器在处理请求的过程中发生了错误 301:(永久移动)请求的网页已被永久移动到新位置。服务器返回此响应(作为对GET或HEAD请求的响应)时,会自动将请求者转到新位置。 302:(临时移动)服务器目前正从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。此代码与响应GET和HEAD请求的301代码类似,会自动将请求者转到不同的位置。 HTTP状态码301与302的区别: 1、它们之间关键区别在,资源是否存在有效性; 2、301资源还在只是换了一个位置,返回的是新位置的内容; 3、302资源暂时失效,返回的是一个临时的代替页上 Content-Type 实体头部用于指示资源的MIME类型 media type 。 在响应中,Content-Type标头告诉客户端实际返回的内容的内容类型。浏览器会在某些情况下进行MIME查找,并不一定遵循此标题的值;为了防止这种行为,可以将标题 X-Content-Type-Options 设置为 nosniff。 在请求中 (如POST 或 PUT),客户端告诉服务器实际发送的数据类型。 Content-Type 的值类型: 1.1 application/json:消息主体是序列化后的 JSON 字符串 1.2 application/x-www-form-urlencoded:数据被编码为名称/值对。这是标准的编码格式 1.3 multipart/form-data: 需要在表单中进行文件上传时,就需要使用该格式。常见的媒体格式是上传文件之时使用的 1.4 text/plain:数据以纯文本形式(text/json/xml/html)进行编码,其中不含任何控件或格式字符 package.json Node.js 项目遵循模块化的架构,当我们创建了一个 Node.js 项目,意味着创建了一个模块,这个模块的描述文件,被称为 package.json。 属性说明: name - 包名; version - 包的版本号; description - 包的描述; homepage - 包的官网URL; author - 包的作者,它的值是你在 https://npmjs.org 网站的有效账户名,遵循“账户名<邮件>”的规则,例如:zhangsan contributors - 包的其他贡献者; dependencies / devDependencies - 生产/开发环境依赖包列表。它们将会被安装在 node_module 目录下; repository - 包代码的 Repo 信息,包括 type 和 URL,type 可以是 git 或 svn,URL 则是包的 Repo 地址; main - main 字段指定了程序的主入口文件,这个字段的默认值是模块根目录下面的 index.js; keywords - 关键字 devDependencies、dependencies、peerDependencies的区别 devDependencies是只会在开发环境下依赖的模块,生产环境不会被打入包内。安装时,加上--save-dev dependencies依赖的包不仅开发环境能使用,生产环境也能使用。安装时,加上--save peerDependencies是用来发布npm插件时指定所需要兼容的宿主包的版本。 package.lock.json 当我们安装依赖时,package-lock.json 文件会自动生成。 package-lock.json 文件中记录了下载源地址,可以加快我们的 npm install 速度。 在 es6 之前,模块加载的方案主要是 CommonJS 和 AMD 两种,前者用于服务器,后者用于浏览器;而在 es6,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。 模块功能主要由两个命令构成:export 和 import。 export 命令用于规定模块的对外接口;import 命令用于输入其他模块提供的功能。 export 的作用:导出功能。 import 的作用:导入功能。 export 既可以导出变量,还可以导出函数、对象或类(class),导出变量时还可以使用 as 关键字对变量指定别名。 import 接受一对大括号,里面指定要从其他模块导入的变量名,也可以使用 as 关键字 对导入的变量指定别名。 挂载过程指的是app.mount()过程,这个过程中整体上做了两件事:初始化和建立更新机制 初始化会创建组件实例、初始化组件状态,创建各种响应式数据 建立更新机制这一步会立即执行一次组件更新函数,这会首次执行组件渲染函数并执行patch将前面获得vnode转换为dom;同时首次执行渲染函数会创建它内部响应式数据之间和组件更新函数之间的依赖关系,这使得以后数据变化时会执行对应的更新函数。 Vue中有个独特的编译器模块,称为“compiler”,它的主要作用是将用户编写的template编译为js中可执行的render函数。 之所以需要这个编译过程是为了便于前端程序员能高效的编写视图模板。相比而言,我们还是更愿意用HTML来编写视图,直观且高效。手写render函数不仅效率底下,而且失去了编译期的优化能力。 在Vue中编译器会先对template进行解析,这一步称为parse,结束之后会得到一个JS对象,我们成为抽象语法树AST,然后是对AST进行深加工的转换过程,这一步成为transform,最后将前面得到的AST生成为JS代码,也就是render函数。 编码风格方面: 命名组件时使用“多词”风格避免和HTML元素冲突 使用“细节化”方式定义属性而不是只有一个属性名 属性名声明时使用“驼峰命名”,模板或jsx中使用“肉串命名” 使用v-for时务必加上key,且不要跟v-if写在一起 性能方面: 路由懒加载减少应用尺寸 利用SSR减少首屏加载时间 利用v-once渲染那些不需要更新的内容 一些长列表可以利用虚拟滚动技术避免内存过度占用 对于深层嵌套对象的大数组可以使用shallowRef或shallowReactive降低开销 避免不必要的组件抽象 安全: 不使用不可信模板,例如使用用户输入拼接模板:template: 87.你知道哪些vue3新特性
88.vue3响应式数据的判断?
89.Vue complier 实现
90.开发中常用的指令有哪些
91.vue常用指令有哪些?
v-if、v-else-if、v-else // if表达式控制元素展示
v-for // 循环渲染
v-bind // 绑定数据
v-model // 双向绑定
v-on // 绑定事件92.v-model的作用是什么?
93v-model是什么?怎么使用?vue中标签怎么绑定事件?
94.v-model的实现原理?
95.说一下vue2.x中如何监测数组变化
96.你的接口请求一般放在哪个生命周期中
97.delete和Vue.delete删除数组的区别
98.请说出vue.cli项目中src目录每个文件夹和文件的用法
99.vue-loader 是什么?使用它的用途有哪些?
100.vue常⽤的修饰符
101.对Vue项目你做过哪些性能优化
102.v-for和v-if放在一起用好吗
103.怎么减少页面加载时间
104.介绍知道的状态码,状态码301、302区别
105.content-type作用
106.package.json package.lock.json 区别
107.export和import的区别和作用
108.Vue实例挂载的过程中发生了什么?
109.说说从 template 到 render 处理过程
110.实际工作中,你总结的vue最佳实践有哪些
小心使用v-html,:url,:style等,避免html、url、样式等注入
action中处理异步,mutation不可以
mutation做原子操作
action可以整合多个mutation的集合
mutation 是同步更新数据(内部会进行是否为异步方式更新数据的检测) $watch 严格模式下会报错
action 异步操作,可以获取数据后调佣 mutation 提交最终数据
流程顺序:“相应视图—>修改State”拆分成两部分,视图触发Action,Action再触发Mutation`。
基于流程顺序,二者扮演不同的角色:Mutation:专注于修改State,理论上是修改State的唯一途径。Action:业务代码、异步请求
角色不同,二者有不同的限制:Mutation:必须同步执行。Action:可以异步,但不能直接操作State
相同点: assets 和 static 两个都是存放静态资源文件。项目中所需要的资源文件图片,字体图标,样式文件等都可以放在这两个文件下,这是相同点
不相同点:assets 中存放的静态资源文件在项目打包时,也就是运行 npm run build 时会将 assets 中放置的静态资源文件进行打包上传,所谓打包简单点可以理解为压缩体积,代码格式化。而压缩后的静态资源文件最终也都会放置在 static 文件中跟着 index.html 一同上传至服务器。static 中放置的静态资源文件就不会要走打包压缩格式化等流程,而是直接进入打包好的目录,直接上传至服务器。因为避免了压缩直接进行上传,在打包时会提高一定的效率,但是 static 中的资源文件由于没有进行压缩等操作,所以文件的体积也就相对于 assets 中打包后的文件提交较大点。在服务器中就会占据更大的空间。
建议: 将项目中 template需要的样式文件js文件等都可以放置在 assets 中,走打包这一流程。减少体积。而项目中引入的第三方的资源文件如iconfoont.css 等文件可以放置在 static 中,因为这些引入的第三方文件已经经过处理,不再需要处理,直接上传。
体验
大型应用中,我们需要分割应用为更小的块,并且在需要组件时再加载它们
import { defineAsyncComponent } from 'vue'
// defineAsyncComponent定义异步组件,返回一个包装组件。包装组件根据加载器的状态决定渲染什么内容
const AsyncComp = defineAsyncComponent(() => {
// 加载函数返回Promise
return new Promise((resolve, reject) => {
// ...可以从服务器加载组件
resolve(/* loaded component */)
})
})
// 借助打包工具实现ES模块动态导入
const AsyncComp = defineAsyncComponent(() =>
import('./components/MyComponent.vue')
)
回答范例
在大型应用中,我们需要分割应用为更小的块,并且在需要组件时再加载它们。
我们不仅可以在路由切换时懒加载组件,还可以在页面组件中继续使用异步组件,从而实现更细的分割粒度。
使用异步组件最简单的方式是直接给defineAsyncComponent指定一个loader函数,结合ES模块动态导入函数import可以快速实现。我们甚至可以指定loadingComponent和errorComponent选项从而给用户一个很好的加载反馈。另外Vue3中还可以结合Suspense组件使用异步组件。
异步组件容易和路由懒加载混淆,实际上不是一个东西。异步组件不能被用于定义懒加载路由上,处理它的是vue框架,处理路由组件加载的是vue-router。但是可以在懒加载的路由组件中使用异步组件
创建过程自上而下,挂载过程自下而上;即:
parent created
child created
child mounted
parent mounted
之所以会这样是因为Vue创建过程是一个递归过程,先创建父组件,有子组件就会创建子组件,因此创建时先有父组件再有子组件;子组件首次创建时会添加mounted钩子到队列,等到patch结束再执行它们,可见子组件的mounted钩子是先进入到队列中的,因此等到patch结束执行这些钩子时也先执行。
加载渲染过程
父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created ->子 beforeMount -> 子 mounted -> 父 mounted
更新过程
父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
销毁过程
父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
开发中缓存组件使用keep-alive组件,keep-alive是vue内置组件,keep-alive包裹动态组件component时,会缓存不活动的组件实例,而不是销毁它们,这样在组件切换过程中将状态保留在内存中,防止重复渲染DOM。
结合属性include和exclude可以明确指定缓存哪些组件或排除缓存指定组件。vue3中结合vue-router时变化较大,之前是keep-alive包裹router-view,现在需要反过来用router-view包裹keep-alive:
缓存后如果要获取数据,解决方案可以有以下两种:
beforeRouteEnter:在有vue-router的项目,每次进入路由的时候,都会执行beforeRouteEnter
beforeRouteEnter(to, from, next){
next(vm=>{
console.log(vm)
// 每次进入路由执行
vm.getData() // 获取数据
})
},
actived:在keep-alive缓存的组件被激活的时候,都会执行actived钩子
activated(){
this.getData() // 获取数据
},
keep-alive是一个通用组件,它内部定义了一个map,缓存创建过的组件实例,它返回的渲染函数内部会查找内嵌的component组件对应组件的vnode,如果该组件在map中存在就直接返回它。由于component的is属性是个响应式数据,因此只要它变化,keep-alive的render函数就会重新执行。
从0创建一个项目我大致会做以下事情:项目构建、引入必要插件、代码规范、提交规范、常用库和组件
目前vue3项目我会用vite或者create-vue创建项目
接下来引入必要插件:路由插件vue-router、状态管理vuex/pinia、ui库我比较喜欢element-plus和antd-vue、http工具我会选axios
其他比较常用的库有vueuse,nprogress,图标可以使用vite-svg-loader
下面是代码规范:结合prettier和eslint即可
最后是提交规范,可以使用husky,lint-staged,commitlint
目录结构我有如下习惯:.vscode:用来放项目中的 vscode 配置
plugins:用来放 vite 插件的 plugin 配置
public:用来放一些诸如 页头icon 之类的公共文件,会被打包到dist根目录下
src:用来放项目代码文件
api:用来放http的一些接口配置
assets:用来放一些 CSS 之类的静态资源
components:用来放项目通用组件
layout:用来放项目的布局
router:用来放项目的路由配置
store:用来放状态管理Pinia的配置
utils:用来放项目中的工具方法类
views:用来放项目的页面文件
一个SPA应用的路由需要解决的问题是页面跳转内容改变同时不刷新,同时路由还需要以插件形式存在,所以:
首先我会定义一个createRouter函数,返回路由器实例,实例内部做几件事:
保存用户传入的配置项
监听hash或者popstate事件
回调里根据path匹配对应路由
将router定义成一个Vue插件,即实现install方法,内部做两件事:
实现两个全局组件:router-link和router-view,分别实现页面跳转和内容显示
定义两个全局变量:
和
router,组件内可以访问当前路由和路由器实例
initProxy:作用域代理,拦截组件内访问其它组件的数据。initLifecycle:建立父子组件关系,在当前组件实例上添加一些属性和生命周期标识。如[Math Processing Error]parent,parent,refs,$children,_isMounted等。initEvents:对父组件传入的事件添加监听,事件是谁创建谁监听,子组件创建事件子组件监听 initRender:声明[Math Processing Error]slots和slots和createElement()等。initInjections:注入数据,初始化inject,一般用于组件更深层次之间的通信。initState:重要)数据响应式:初始化状态。很多选项初始化的汇总:data,methods,props,computed和watch。initProvide:提供数据注入。思考:为什么先注入再提供呢??
1、首先来自祖辈的数据要和当前实例的data,等判重,相结合,所以注入数据的initInjections一定要在InitState的上面。
从上面注入进来的东西在当前组件中转了一下又提供给后代了,所以注入数据也一定要在上面。
当一个Vue实例创建时,Vue会遍历data选项的属性,用 Object.defineProperty 将它们转为 getter/setter并且在内部追踪相关依赖,在属性被访问和修改时通知变化。每个组件实例都有相应的 watcher 程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher重新计算,从而致使它关联的组件得以更新。
在style标签中添加“scoped”属性,可以避免当前组件的CSS污染全局。
添加了这个属性后,vue-loader会给组件的每个元素添加一个data属性,并且将CSS代码编译,添加这个data属性的选择器。
使用Vue.set(object, propertyName, value)或者vm.$set((object, propertyName, value)。这两个方法相同。
Vue.nextTick是将任务加入到宏任务队列或者微任务队列。
process.nextTick并未将任务加入到宏任务队列或者微任务队列,它是将任务加到下次事件循环之前。
插槽是Vue 实现的一套内容分发的 API,它支持在渲染一个自定义组件时候,向它“插入”一些内容。这个自定义组件会将起始标签和结束标签之间的内容渲染到自己模板中“slot”组件所在的位置上。
Vue的插槽和React的render props的作用相同,React也会将自定义组件开始和闭合标签之间的内容作为children属性挂载到props上,从而在自定义组件中渲染处理。
插槽(和render props)提供了更灵活的组件组合和复用方式,在布局等应用场景让代码更直观和易于维护。
首先,Vue和React在很多地方都是相似的,对于大部分场景,并没有React能做到而Vue不能做到,或者Vue能做到,而React不能做到的事情。所以决定Vue和React之间的选型的因素更多是团队成员的熟练程度、老项目的情况、整个公司的环境、团队的沉淀、甚至领导的个人偏好。
至于JQuery,一般已经不使用了。
vue全家桶:
vue(整体架构) + vuex(状态管理) + vue-router(路由) + vue_resource || axios(ajax请求) + mint-UI(移动端UI框架库) || antd-vue(PC端UI框架库)
react全家桶:
react(整体架构) + redux || mobx(状态管理) + react-router(路由) + axios(ajax请求) + antd || react-material || antd-mobile(UI框架库)
ndex.html,只有一个路由出口
main.js,路由的重定向,就会在页面一加载的时候,就会将home组件显示出来,因为重定向指向了home组件,redirect的指向与path的必须一致。children里面是子路由,当然子路由里面还可以继续嵌套子路由。
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
//引入两个组件
import home from "./home.vue"
import game from "./game.vue"
//定义路由
const routes = [
{ path: "/", redirect: "/home" },//重定向,指向了home组件
{
path: "/home", component: home,
children: [
{ path: "/home/game", component: game }
]
}
]
//创建路由实例
const router = new VueRouter({routes})
new Vue({
el: '#app',
data: {
},
methods: {
},
router
})
home.vue,点击显示就会将子路由显示在出来,子路由的出口必须在父路由里面,否则子路由无法显示。
game.vue
现在的game组件就是home组件的子级路由了。如果还要嵌套继续在game中加children就可以了。
答案:iframe也称作嵌入式框架,嵌入式框架和框架网页类似,它可以把一个网页的框架和内容嵌入在现有的网页中。
优点:
解决加载缓慢的第三方内容如图标和广告等的加载问题
Security sandbox
并行加载脚本
方便制作导航栏
缺点:
iframe会阻塞主页面的Onload事件
即使内容为空,加载也需要时间
没有语意
受 ES5 的限制,Vue.js 不能检测到对象属性的添加或删除。因为 Vue.js 在初始化实例时将属性转为 getter/setter,所以属性必须在 data 对象上才能让 Vue.js 转换它,才能让它是响应的。
首先,组件可以提升整个项目的开发效率。能够把页面抽象成多个相对独立的模块,解决了我们传统项目开发:效率低、难维护、复用性等问题。
创建一个组件,然后使用Vue.component方法注册组件。子组件需要数据,可以在props中接受定义。而子组件修改好数据后,想把数据传递给父组件,可以采用emit方法。