SPA单页 Web 应用 (single-page application)
简单理解为:仅仅在web页面初始化时加载相应的HTML、JavaScript、CSS,一旦页面加载完成了,SPA不会因为用户的操作而进行页面的重新加载或跳转,而是利用 JavaScript 动态的变换HTML的内(采用的是div切换显示和隐藏),从而实现UI与用户的交互。
SPA的优缺点
1、优点:
(1)由于避免了页面的重新加载,SPA 可以提供较为流畅的用户体验。得益于ajax,我们可以实现无跳转刷新,又多亏了浏览器的histroy机制,我们用hash的变化从而可以实现推动界面变化。
(2)只要使用支持HTML5和CSS3的浏览器就可以执行复杂的SPA,让开发SPA网页程序的入门和使用门槛降低不少。
2、缺点:
以SPA方式开发的网站不容易管理也不够安全。因为没了一页一页的网页给搜索引擎的爬虫来爬,所以,在搜索引擎最佳化(SEO)的工作上,需要花费额外的功夫。因为没有换页,需要自定义状态来取代传统网页程序以网址来做判断。
vue 生命周期
vue每个组件都是独立的,每个组件都有一个属于它的生命周期,从一个组件创建、数据初始化、挂载、更新、销毁,这就是一个组件所谓的生命周期
beforeCreate:实例组件刚创建,元素DOM和数据都还没有初始化,暂时不能在这个周期里面进行生命操作
created:数据data已经初始化完成,方法也已经可以调用,但是DOM未渲染。
beforeMount:DOM未完成挂载,数据也初始化完成,但是数据的双向绑定还是显示{{}}
mounted:数据和DOM都完成挂载,在上一个周期占位的数据把值给渲染进去。可以在这边请求,不过created请求会更好一些。这个周期适合执行初始化需要操作DOM的方法。
beforeUpdate:只要是页面数据改变了都会触发,数据更新之前,页面数据还是原来的数据,当你请求赋值一个数据的时候会执行这个周期,如果没有数据改变不执行。
updated:只要是页面数据改变了都会触发,数据更新完毕,页面的数据是更新完成的。
beforeDestroy:钩子函数在实例销毁之前调用。在这一步,实例仍然完全可用。
destroyed:钩子函数在Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
MVVM框架设计理念
MVVM分为Model、View、ViewModel三者。
Model 代表数据模型,数据和业务逻辑都在Model层中定义;
View 代表UI视图,负责数据的展示;
ViewModel 负责监听 Model 中数据的改变并且控制视图的更新,处理用户交互操作;
Model 和 View 并无直接关联,而是通过 ViewModel 来进行联系的,Model 和 ViewModel 之间有着双向数据绑定的联系。因此当 Model 中的数据改变时会触发 View 层的刷新,View 中由于用户交互操作而改变的数据也会在 Model 中同步。
这种模式实现了 Model 和 View 的数据自动同步,因此开发者只需要专注对数据的维护操作即可,而不需要自己操作 dom。
vue data中声明一个空对象,obj.attr = 5视图会刷新吗?
不会刷新
原因在于在Vue实例创建时,obj.attr 并未声明,因此就没有被Vue转换为响应式的属性,自然就不会触发视图的更新,这时就需要使用Vue的全局api—— $set()
Vue双向绑定实现原理
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
使用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.全局注册可以在任意地方使用..
父子组件通讯方式:
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
vuecli2.0 和 vue3.0 区别
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有对应的可视化的界面
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的操作,提高了性能
跟keep-alive有关的生命周期是哪些?描述下这些生命周期
activated和deactivated两个生命周期函数
1.activated:当组件激活时,钩子触发的顺序是created->mounted->activated
2.deactivated: 组件停用时会触发deactivated,当再次前进或者后退的时候只触发activated
computed、watch的区别
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声明的常量不可改变,声明时必须初始化(赋值)
es6 的特有的类型, 常用的操作数组的方法都有哪些?
① 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('需要加载的模块地址')