简单理解为:仅仅在web页面初始化时加载相应的HTML、JavaScript、CSS,一旦页面加载完成了,SPA不会因为用户的操作而进行页面的重新加载或跳转。取⽽代之的是利⽤路由机制实现 HTML 内容的变换,UI 与⽤户的交互,避免⻚⾯的重新加载。
1、优点:
(1)⽤户体验好、快,内容的改变不需要重新加载整个⻚⾯,避免了不必要的跳转和重复渲染;
(2)SPA 相对对服务器压⼒⼩;
(3)前后端职责分离,架构清晰,前端进⾏交互逻辑,后端负责数据处理;
2、缺点:
(1)⾸屏(初次)加载慢:为实现单⻚ Web 应⽤功能及显示效果,需要在加载⻚⾯的时候将JavaScript、CSS 统⼀加载,部分⻚⾯按需加载;
(2)不利于 SEO:由于所有的内容都在⼀个⻚⾯中动态替换显示,所以在 SEO 上其有着天然的弱势。
vue每个组件都是独立的,每个组件都有一个属于它的生命周期,从一个组件创建、数据初始化、挂载、更新、销毁,这就是一个组件所谓的生命周期
总共分为8个阶段:创建前/后,载入前/后,更新前/后,销毁前/后。
1、创建前/后:
说明:在当前阶段data、methods、computed以及watch上的数据和方法都不能被访问。
说明:可以做一些初始数据的获取,在当前阶段无法与Dom进行交互,如果非要想,可以通过vm.$nextTick来访问Dom。
2、载入前/后:
说明:当前阶段虚拟Dom已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发updated。
说明:在当前阶段,真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点,使用$refs属性对Dom进行操作。
3、更新前/后:
说明:可以在当前阶段进行更改数据,不会造成重渲染。
说明:当前阶段组件Dom已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新。
4、销毁前/后:
说明:在当前阶段实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器。
说明:当前阶段组件已被拆解,数据绑定被卸除,监听被移出,子实例也统统被销毁。
补充回答:
第一次页面加载时会触发:beforeCreate, created, beforeMount, mounted。
created 实例已经创建完成,因为它是最早触发的原因可以进行一些数据,资源的请求。(服务器渲染支持created方法)
mounted 实例已经挂载完成,可以进行一些DOM操作。(接口请求)
MVVM分为Model、View、ViewModel三者。
Model 代表数据模型,数据和业务逻辑都在Model层中定义;
View 代表UI视图,负责数据的展示;
ViewModel 负责监听 Model 中数据的改变并且控制视图的更新,处理用户交互操作;
Model 和 View 并无直接关联,而是通过 ViewModel 来进行联系的,Model 和 ViewModel 之间有着双向数据绑定的联系。因此当 Model 中的数据改变时会触发 View 层的刷新,View 中由于用户交互操作而改变的数据也会在 Model 中同步。
这种模式实现了 Model 和 View 的数据自动同步,因此开发者只需要专注对数据的维护操作即可,而不需要自己操作 dom。
不会刷新
原因在于在Vue实例创建时,obj.attr 并未声明,因此就没有被Vue转换为响应式的属性,自然就不会触发视图的更新,这时就需要使用Vue的全局api—— $set()
2.x 原理:Object.defineProperty(),然后重新定义了get()、set()
3.0 采用es6的proxy,(Proxy用于修改某些操作的默认行为,也可以理解为在目标对象之前架设一层拦截,外部所有的访问都必须先通过这层拦截,因此提供了一种机制,可以对外部的访问进行过滤和修改。)取代之前的object.defineProperty()
Vue 的响应式原理核心是通过 ES5 的保护对象的 Object.defindeProperty 中的访问器属性中的 get 和 set 方法,data 中声明的属性都被添加了访问器属性,当读取 data 中的数据时自动调用 get 方法,当修改 data 中的数据时,自动调用 set 方法,检测到数据的变化,会通知观察者 Wacher,观察者 Wacher自动触发重新渲染 当前组件(子组件不会重新渲染),生成新的虚拟 DOM 树,Vue 框架会遍历并对比新虚拟 DOM 树和旧虚拟 DOM 树中每个节点的差别,并记录下来,最后加载操作,将所有记录的不同点,局部修改到真实 DOM 树上。
1、监听器Observer:用来劫持并监听所有属性,如果有变动的,就通知订阅者。
2、订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图
使用v-for更新已渲染的元素列表时,默认用就地复用策略;列表数据修改的时候,他会根据key值去判断某个值是否修改,如果修改,则重新渲染这一项,否则复用之前的元素;
key的作用主要是为了高效的更新虚拟DOM。
我们经常会使用index(即数组的下标)来作为key,但其实这是不推荐的一种使用方法;
另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。
局部注册
1.import 导入
2.components注册
3.以标签的形式,渲染在页面里
全局注册
1.在main.js中import导入组件,
2.使用Vue.component(标签名,组件模块)全局注册
区别:
1.局部注册只可以在对应引用.vue文件中使用。
2.全局注册可以在任意地方使用…
为什么Vue中的v-if和v-for不建议一起用?
原因:v-for优先级比v-if高
注意:
1、永远不要把 v-if 和 v-for 同时用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)
2、如果避免出现这种情况,则在外层嵌套template(页面渲染不生成dom节点),在这一层进行v-if判断,然后在内部进行v-for循环
3、如果条件出现在循环内部,可通过计算属性computed提前过滤掉那些不需要显示的项
computed: {
items: function() {
return this.list.filter(function (item) {
return item.isShow
})
}
}
父子组件通讯方式:
1.父组件向子组件传值
在父组件的子组件中通过属性值方式传值
子组件通过props接收
2.子组件向父组件传值
父组件通过@符号向子组件传递一个事件方法
子组件使用this.$emit(事件方法,数据,数据)
父组件通过methods方法中的形参接收数据。
vue父子组件 渲染过程
同步引入 例子: import Page from ‘@/components/page’
1.加载渲染过程
父beforeCreated—父created----父beforeMount — 子beforeCreated—子created----子beforeMount — 子mounted — 父mounted
2.子组件更新过程
父beforeUpdate—子beforeUpdate----子updated—父updated
3.销毁过程
父beforeDestroy—子beforeDestroy----子destroyed—父destroyed
异步引入 例子:const Page = () => import(’@/components/page’)
或者: const Page = resolve => require([’@/components/page’], page)
1.加载渲染过程
父组件的beforeCreate、created、beforeMount、mounted --> 子组件的beforeCreate、created、beforeMount、mounted
2.子组件更新过
父beforeUpdate—父updated —子beforeUpdate----子updated
3.销毁过程
父beforeDestroy—父destroyed—子beforeDestroy----子destroyed
1:目录结构不一样
vuecli2.0有build目录 有static目录
vuecli3.0没有build目录 有pulic目录
2:webpack的配置文件不一样
vuecli2.0有build目录 放置的是对应的 webpack的配置文件
vuecli3.0已经内嵌了webpack得配置 ,如果有修改需要新建一个
vue.config.js文件
3:创建项目的命令不一样
4:vuecli3.0有对应的可视化的界面
1、编码阶段
尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher;
如果需要使用v-for给每项元素绑定事件时使用事件代理;
SPA 页面采用keep-alive缓存组件;
在更多的情况下,使用v-if替代v-show;
key保证唯一;
使用路由懒加载、异步组件;
防抖、节流;
第三方模块按需导入;
长列表滚动到可视区域动态加载;
图片懒加载;
2、用户体验:
骨架屏;
PWA;
还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。
3、SEO优化
预渲染;
服务端渲染SSR;
4、打包优化
压缩代码;
Tree Shaking/Scope Hoisting;
使用cdn加载第三方模块;
多线程打包happypack;
splitChunks抽离公共文件;
sourceMap优化;
Dom 和 虚拟dom
虚拟dom javascript中的一个内置对象,用来描述真实的dom
vue和react框架中,采用的都是虚拟dom
在页面第一次渲染完成后,会生成一个js对象,用来描述真实的dom,之后当数据有更新或者变化的时候,
会在生成一个虚拟dom,然后新的虚拟dom和旧的dom进行对比(同级对比,采用的是diff算法),找出差异,进行更新,
生成新的虚拟dom,然后和真实的dom对比,修改真实dom中有变化的那一块
回流:当渲染树中的一部分或者全部因为元素的尺寸、布局、隐藏等改变而需要重新构建的时候,这时候就会发生回流
重绘:完成回流以后,浏览器会重新绘制受到影响的部分元素到屏幕中
1、添加或者删除可见的DOM元素的时候
2、元素的位置发生改变
3、元素尺寸改变
4、内容改变
5、页面第一次渲染的时候
直接操作dom的时候,引起页面回流和重会的次数很多,而虚拟的dom是不会发生回流和重会的,
它会把要渲染的先统一放到一个对象中,然后通过document.fragment一次性比较并修改真实DOM中需要改的部分,
可以只渲染局部,大大减少了dom的操作,提高了性能
activated和deactivated两个生命周期函数
1.activated:当组件激活时,钩子触发的顺序是created->mounted->activated
2.deactivated: 组件停用时会触发deactivated,当再次前进或者后退的时候只触发activated
computed当做属性使用,可以依赖其他 computed,甚至是其他组件的 data必须有返回值,不能与 data 中的属性重复;有缓存的功能;不能接受参数;
watch可以监听data中的属性,也可以监听vuex状态; 监听的属性必须是存在的;允许异步;可接受两个参数
总结:
当有一些数据需要随着另外一些数据变化时,建议使用 computed
当有一个通用的响应数据变化的时候,要执行一些业务逻辑或异步操作的时候建议使用 watch
let const var 区别
1.var定义的变量,作用域是整个封闭函数,是全域的;let定义的变量,作用域是在块级或者字块中;
2.变量提升:不论通过var声明的变量处于当前作用于的第几行,都会提升到作用域的最顶部。
而let声明的变量不会在顶部初始化,凡是在let声明之前使用该变量都会报错(引用错误ReferenceError);
3,只要块级作用域内存在let,它所声明的变量就会绑定在这个区域;
4,let不允许在相同作用域内重复声明(报错同时使用var和let,两个let)
const用来专门声明一个常量,它跟let一样作用于块级作用域,没有变量提升,重复声明会报错,不同的是const声明的常量不可改变,声明时必须初始化(赋值)
① let const 两者都有块级作用域 ② 箭头函数 ③ 模板字符串 ④ 解构赋值
⑤ for of 循环 ⑥ import 、export 导入导出 ⑦ set 数据结构 ⑧ …展开运算符
⑨ 修饰器 @ ⑩ class 类继承 ⑪ async、await ⑫ promise ⑬ Symbol
⑭ Proxy 代理
ES5:concat 、join 、push、pop、shift、unshift、slice、splice、substring 和 substr 、sort、 reverse、indexOf 和 lastIndexOf 、every、some、filter、map、forEach、reduce
ES6:find、findIndex、fill、copyWithin、Array.from、Array.of、entries、values、key、includes
super
1.作为函数使用
代表的是父类的构造函数,返回的是子类的实例,即super内部的this指的是子类的实例,因此super()在这里相当于父类.prototype.constructor.call(this)。
2.super作为对象时,在普通方法中,指向父类的原型对象(A.prototype.p());在静态方法中,指向父类。
1、全局导航守卫
前置守卫
router.beforeEach((to, from, next) => {
// do someting
});
后置钩子(没有 next 参数)
router.afterEach((to, from) => {
// do someting
});
2、路由独享守卫
cont router = new VueRouter({
routes: [
{
path: '/file',
component: File,
beforeEnter: (to, from ,next) => {
// do someting
}
}
]
});
3、 组件内的导航钩子
组件内的导航钩子主要有这三种:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave。他们是直接在路由组件内部直接进行定义的
运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时
只会在进入当前这个路由时候才会走 component
1、vue异步组件实现懒加载:component:resolve=>(require([‘需要加载的路由的地址’]),resolve)
2、webpack提供的require.ensure()
3、ES6 提出的import方法:const HelloWorld = ()=>import(‘需要加载的模块地址’)