VUE框架知识点总结
1. MVVM 的优点和缺点有哪些?
MVVM 是 web 前端一种非常流行的开发模式,利用 MVVM 可以使我们的代码更专注域处理业务逻辑而不用去关心 DOM 操作。
优点
- 双向数据绑定,当 Model 变化时, View-Model 会自动更新,View 也会自动变化,很好的做到数据的一致性。
- View 的功能被进一步强化,具有部分控制功能,很多的功能都可以写在视图中
- 由于 View 分担了控制器的很多功能,从而实现控制器功能的瘦身
缺点
- 调试难度增加,如果功能不正常,很可能是 View 的问题,也可能是 Model 的问题
- 双向数据绑定不利于代码的复用
2. Vue.js 的优点和缺点
优点
- 文档友好,中文文档更新及时,例子言简意赅
- 学习曲线平缓,上手快
- 声明式框架,开发速度快
- .vue 文件开发,模块化支持的很完美
缺点
- 代码的复用性不够强,.vue 文件中的代码只能给当前组件使用
- 单页面应用不利于 SEO
- 不兼容 IE8-(Object.observer()方法在 IE9+可以用)
3. vue 的生命周期函数具体每个干了啥、区别
-
beforeCreate 在实例初始化之火,数据观测 data observer 和 event/watcher 事件配置之前被调用
-
created 在实例创建完成后被立即调用。在这一步,实例已经完成以下配置 data observer 属性和方法运算,watch/event 事件回调,但是挂载还没开始,$el 属性不可用
-
beforeMount 在挂载开始之前被调用,相关的 render 函数首次被调用
-
mounted el 被新创建的 vm. e l 替 换 , 并 挂 载 到 实 例 上 去 之 后 调 用 该 钩 子 , 如 果 r o o t 实 例 挂 载 了 一 个 文 档 内 元 素 , 当 m o u n t e d 被 调 用 时 v m . el 替换,并挂载到实例上去之后调用该钩子,如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm. el替换,并挂载到实例上去之后调用该钩子,如果root实例挂载了一个文档内元素,当mounted被调用时vm.el 也在文档内;注意 mounted 不会承诺所有的子组件一起挂载,如果希望在整个视图渲染完毕后,可以使用 vm.$nextTick
-
beforeUpdate 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM ,比如手动移除已经添加的事件监听器
-
updated 由于数据更改导致虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子;当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM的操作。注意不会承诺所有的子组件也一起被重绘,如果你希望等到整个视图都重绘完毕,可以用 vm.$nextTick
-
activated keep-alive 组件被激活时调用
-
deactived keep-active 组件停用时调用
-
beforeDestroy 实例销毁之前调用,在这一步,实例仍然完全可用
-
destroyed Vue 实例销毁后,调用后,Vue 实例都会解绑,所有的事件监听器会被移除
4.常用的属性方法
- $set : 设置响应式数据
- $el : 挂载的元素
- $destroy : 销毁vue的实例;同时调用beforeDestroy destroyed
- $mount : 挂载真实DOM的方法
- $data : 响应式的对象
- $options: 是Vue的构造函数的参数
- $refs : 可以用来获取指定的元素对象 (非受控组件)
- $on : 订阅
- $emit : 发布
- $watch:监控
- …
5.vue SSR(服务端渲染) 和 prerender(预渲染)
- vue 一般写的是 SPA (单页面应用),不利于 SEO,解决这个问题可以有两种方式:SSR 和 Prerender;
- 使用 prerender 需要使用 webpack 的插件 prerender-spa-plugin
new PrerenderSPAPlugin({
staticDir: path.join(__dirname, ‘dist’),
routes: [ ‘/’, ‘/about’, ‘/contact’ ],
renderer: new Renderer({
inject: {
foo: 'bar'
},
headless: true,
renderAfterDocumentEvent: 'render-event'
})
})
])
- Vue-SSR 需要使用:vue-server-renderer 依赖包; 主要使用其中的createRenderer 方法创建 renderer,以及 renderer.renderToString() 把 vue 实例转换成 HTML 字符串;
meta: `<meta name="keywords" content="这是一段来自VUE-SSR练习生的SSR作品"> <meta name="description" content="这是一段描述">`
}
renderer.renderToString(app, context, (err, html) => {
if (err) return res.status(500).end('Internal Server Error')
})
res.send(html)
})
server.listen(8080, () => console.log('8080 is on'))
*1 组件的生命周期在 ssr 时只有 beforeCreate 和 created 会执行
*2 指令尽量少用,或者抽象问组件层面并运行在虚拟 DOM 层面或者使用服务端渲染版本的指令
*3 避免状态单例即不能使用一个 Vue 实例,应该使用一个工厂函数创建 Vue 实例;同理 VueRouter 和 Vuex 使用相同处理,否则会出现交叉请求状态污染
*
6.vuex核心及用法
- State
- state里面就是存放的我们上面所提到的状态
- Vuex 只能包含一个store(存储数据的库)
- Getter
- 是用来取数据,得到数据的
- Mutation
- 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
- Action
- Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
- Action 类似于 mutation,不同在于:
- Module
- 就是当用这个容器来装这些状态还是显得混乱的时候,我们就可以把容器分成几块,把状态和管理规则分类来装。这和我们创建js模块是一个目的,让代码结构更清晰。
7. vuex 原理
使用 vuex 托管应用的状态以后,我们需要通过 computed 计算属性或者
this.$store.state.xxx
访问 store 中的状态;其内部调用了一下applyMixin方法,该方法主要作用就是在所有组件的beforeCreate生命周期注入了设置this.$store这样一个对象,把 vuex 的 state 初始化到组件的 data 中,其本质就是将我们传入的 state 作为一个隐藏的 vue 组件的 data,当我们通过提交 mutation 或者 dispatch action 时提交的 mutation 会修改数据,而数据修改会自动通知 vue 的更新机制;vuex 中的store 本质就是没有 template 的隐藏着的 vue 组件;
8.vue 插件
- 添加全局的方法或者属性
- 添加全局资源:指令、过滤器、过度
- 添加 Vue 实例方法,通过把他们添加要 Vue.prototype 上实例
- 使用 Vue.use() 使用插件,他需要在调用 new Vue() 之前调用
- Vue 的插件是 install 方法的对象,该方法的第一个参数是 Vue,第二个是在 Vue.use(插件,配置) 时第二个参数的配置对象
9.vue 异步组件:
Vue.component('async-component', (resolve, reject) => {
setTimeout(() => {
resolve({
template: `<div>这是异步组件模板</div>`
})
}, 3000)
})
-
配合 webpack code-spliting
Vue.component(‘async-webpack-example’, function (resolve) {
// 这个特殊的require
语法将会告诉 webpack
// 自动将你的构建代码切割成多个包,这些包
// 会通过 Ajax 请求加载
require([’./my-async-component’], resolve)
}) -
使用 import() 方法
Vue.component(
'async-webpack-example',
// 这个 `import` 函数会返回一个 `Promise` 对象。
() => import('./my-async-component')
)
10.vue 3.0 新特性
- 用 proxy 改写数据观测,原来的使用
Object.defineProperty(obj, prop, configObj)
;改善了之前的一些问题,例如数组 .length 的操作不能支持等;惰性监测,原有 2.x 是在应用启动时就对所有的数据进行观测,而 3.x 会默认只对应用中可见的数据进行监测; - 内部模块化将 observer 抽离成单独模块,更加易于测试
- 编译器优化,树摇友好的输出,如果采用的是支持“树摇友好”的打包器,模板中使用到的那些可选特性,在生成代码中将通过 es 的模块语法导入,在打包后的文件,那些没有用到的可选特性将会被摇掉
- TS 的支持,3.0 的内核将会用 TS 改写,而 3.0 增强了对 3.0 的支持(所以不光要学 3.0 还要学 TS)
11.什么时候使用 keep-alive
当动态组件频繁切换,如果不使用 keep-alive 组将将会重复的销毁和重新渲染会引发性能问题。此外,组件销毁,其中的状态也会随之销毁。所以,为了避免重复渲染的性能问题或者需要动态组件中的状态得以保存就需要使用 keep-alive
12.怎么提高Vue.js架构页面第一次加载的速度?
- 路由懒加载
- 静态资源,如 css、js、img、iconfont 上 CDN
- webpack 开启 gzip
- 服务器开启 gzip
- 客户端静态资源文件缓存
13.导航守卫:to、from、next 这三个东西;to 是要去往的路由对象、from是从哪个路由跳转过来的、next 是控制权函数
- 全局导航守卫 beforeEach 在 配置完 router 实例的时候使用 router.beforeEach
- 组件内的导航守卫 在 *.vue 文件 导出的对象中配置 beforeRouteEnter 、beforeRouteUpdate、beforeRouteLeave
- 虚拟 DOM 和 DOM DIFF
- 为什么使用虚拟 DOM,因为传统的开发模式,原生 JS 或者 JQ 操作 DOM 时,浏览器会重新构建 DOM 树然后重新渲染;虽然说现在计算机的硬件已经有了很大的提升,但是频繁操作 DOM 仍然会导致页面卡顿,影响用户体验。
- 虚拟 DOM 的实现?
- DOM DIFF
let str = <div>
<p>Real DOM</p>
<div>cannot update</div>
<ul>
<li className="item">Item 1</li>
<li className="item">Item 2</li>
<li className="item">Item 3</li>
</ul>
</div>
14.双向数据绑定的实现原理
Object.defineProperty
- 实现的底层原理
//observer:观察者
function observer(obj) {
if (obj && typeof obj === 'object') {
for (let key in obj) {
if (!obj.hasOwnProperty(key)) break;
defineReactive(obj, key, obj[key]);
}
}
}
function defineReactive(obj, key, value) {
observer(value);
Object.defineProperty(obj, key, {
get() {
return value;
},
set(newValue) {
observer(newValue);
if (value === newValue) return;
value = newValue;
}
});
}
function $set(data, key, value) {
defineReactive(data, key, value);
}
Vue 主要通过以下 4 个步骤来实现数据双向绑定的:
- 实现一个监听器 Observer:对数据对象进行遍历,包括子属性对象的属性,利用
Object.defineProperty()
对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。 - 实现一个解析器 Compile:解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。
- 实现一个订阅者 Watcher:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。
- 实现一个订阅器 Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理。
15.VUE中的组件(component)
组件的特点是:
- 每个组件就是一个自定义标签
- 可复用
- 方便维护
- 方便拆分
- 每个组件作用域隔离(互不干扰)
- 有完整的生命周期
- 有自己的响应式数据和各种方法(事件)
- …
16.全局组件 & 组件的基本语法
在任何组件中可以直接使用(不需要引入,直接在组件模板中调用即可)
Vue.component(componentName,options)
- 组件名字中的一点规范
- kebab-case:只能调用
- PasalCase:既可类似前种方式调用,也可以方式调用
- 调用组件的细节规范
- 采用双闭合方式
- 单闭合方式不符合w3c规范(只能识别一个)
- template
- 每个组件只能有一个根元素
- 模板字符串方式
- template标记方式
- slot插槽处理
- 基础操作
- 多插槽的指定
- data必须是一个函数,保证不同组件之间的数据互不干扰(返回的对象中初始化数据)
- …
<my-component>
<template v-slot:xxx 或者 #xxx>
永怀善意
</template>
</my-component>
{
templete:`<div>
<slot name='xxx'></slot>
</div>`
}
17.局部组件
- 创建组件:let componenName={…}
- 基于components属性声明组件:想用哪个组件需要先声明
- 使用组件
18.组件信息通讯之父传子:props属性传递
- 父组件调用到的时候
<my-component aa='zhangzhang' :bb='xxx'></my-component>
- 子组件中基于props声明需要接收的属性值
Vue.component('my-component',{
props:['aa','bb'],
...
})
- props中声明的属性和data一样,是响应式数据,挂载到vm实例上,可控制视图渲染
- props中的一些细节问题
- 命名大小写:传递的是kebab-case格式,props中获取的是camelCase驼峰命名
- 指定属性的类型:props:{xxx:String,…}
- 指定属性的默认值:props:{xxx:{type:String,default:‘xxx’,required:true}}
- type如果是一个数组,意为指定的多类型皆可以
- default可以是一个函数,函数返回值是默认值
- validator自定义验证规则函数:必须符合函数中指定的规则,返回true/false
- 传递的属性值默认都是字符串格式,如果想要让传递的值是数字、布尔、数组、对象等格式,我们需要使用v-bind处理
- 样式和class自动合并问题
19.vue的单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。
Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分:
- 加载渲染过程:父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
- 子组件更新过程:父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
- 父组件更新过程:父 beforeUpdate -> 父 updated
- 销毁过程:父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。有两种常见的试图改变一个 prop 的情形 :
- 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。 在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值
- 这个 prop 以一种原始的值传入且需要进行转换。 在这种情况下,最好使用这个 prop 的值来定义一个计算属性
20.组件信息通讯之子改父:this.$emit
- 订阅自定义事件:调用组件的时候基于属性传递一个方法 (父)
<my-component @func='xxx'></my-component>
new Vue({
methods:{
xxx(value){
//=>value是this.$emit时候传递的第二个参数值
}
}
});
- 通知自定义事件执行 (子)
{
methods:{
xxx(){
this.$emit('func',10);
}
}
}
- 也可以基于此方法实现兄弟组件(父子组件、隔代组件)之间的信息通信
let eventBus=new Vue; //=>创建事件总线
//A组件
eventBus.$on('xxxA',this.func);
//B组件
eventBus.$emit('xxxA');
21.基于ref实现父子组件信息通信
- ref 如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,基于此可以快速获取和操作子组件中的数据
- p a r e n t 和 parent和 parent和children是获取组件和子组件的实例,只不过$children是一个数组集合,需要我们记住组件顺序才可以
22.基于provide和inject实现祖先与后代的通信
- 祖先组件基于provide注册需要供后代组件使用的数据
{
provide:{ //=>对象或者返回对象的函数都可以(属性值如果是data中的数据,则必须使用函数的方法进行处理)
name:'zhangzhang',
year:10
},
...
}
- 后代组件基于inject声明需要使用的数据并调取使用
{
inject:['name'],
methods:{
func(){
let name=this.name;
}
}
}
23.transition的基础使用
- v-enter:定义进入过渡的开始状态,在元素被插入之前生效,在元素被插入之后的下一帧移除
- v-enter-active:定义进入过渡生效时的状态,在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除
- v-enter-to: 定义进入过渡的结束状态,元素被插入之后下一帧生效 ,在过渡/动画完成之后移除
- v-leave: 定义离开过渡的开始状态,在离开过渡被触发时立刻生效,下一帧被移除
- v-leave-active:定义离开过渡生效时的状态,在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除
- v-leave-to: 定义离开过渡的结束状态,在离开过渡被触发之后下一帧生效,在过渡/动画完成之后移除
- 基于 transiton 标记中的 name 属性来进行动画样式区分
- .demo-enter 等
- 自定义过渡类名(例如:结合animate.css使用)
-
<transition
enter-active-class=“animated xxx” leave-active-class=“animated xxx”
-