高级前端软件工程师知识整理之Vue技术栈篇
1.什么是MVVM?MVVM是Model-View-ViewModel的简写,它本质上就是MVC的改进版。MVVM就是将View的状态和行为抽象化,将视图UI和业务逻辑分开,View和Model之间并没有直接的联系,而是通过ViewModel进行交互,它们之间的交互是双向的。Model -代表数据模型,定义了视图的状态或行为。View -代表视图,负责将数据模型转化成可视化界...
1. 什么是MVVM?
MVVM是Model-View-ViewModel的简写,它本质上就是MVC的改进版。MVVM就是将View的状态和行为抽象化,将视图UI和业务逻辑分开,View和Model之间并没有直接的联系,而是通过ViewModel进行交互,它们之间的交互是双向的。
- Model - 代表数据模型,定义了视图的状态或行为。
- View - 代表视图,负责将数据模型转化成可视化界面。
- ViewModel - 负责监听模型数据和视图状态或行为的变化,同步了View和Model的对象,即实现了双向绑定。
以下是Vue技术栈中一个双向绑定的典型例子:
<div id="app6">
<p>{{message}}</p>
<input type="text" v-model="message" />
</div>
<script>
var app6 = new Vue({
el: "#app",
data: {
message: "HelloWorld"
}
});
</script>
示例中当修改input标签中的内容时,p标签的内容也会同步修改。
2. Vue的生命周期函数有哪些?
Vue生命周期:
名称 | 描述 |
---|---|
beforeCreate | 实例刚被创建,vue所有属性都还不存在 |
created | 实例创建完成,但$el还不存在 |
beforeMount | 挂载之前 |
mounted | 挂载之后,即data中的数值已经被渲染到元素中 |
beforeUpdate | 更新之前 |
updated | 更新之后 |
activated | <keep-alive>组件被激活时 |
deactivated | <keep-alive>组件移除时 |
beforeDestroy | 实例被销毁前 |
destroyed | 实例被销毁后 |
示例:https://blog.csdn.net/zeping891103/article/details/78135698
3. Vue实现数据双向绑定的原理是什么?
双向绑定的原理是利用Object.defineProperty()进行数据劫持,然后采用结合发布者-订阅者模式监听数据的变化。当把一个 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。如第一题中的双向绑定的示例代码,它的数据劫持方式为:
var data = { message: 'HelloWorld' }
Object.defineProperty(data, 'message', {
get: function () {
return data.message;
},
set: function (newValue) {
data.message = 'newValue';
// data-v-xxxxx为Vue实例在初始化时给dom元素添加的唯一属性值
// 当message数据发生变化时,通知相关v-model绑定的元素也发生变化
document.getElementByAttr('input','data-v-xxxxx','').value = newValue;
}
})
另外,当input元素被v-modal绑定时,Vue实例为该元素添加了一个input事件,当内容发生变化时,触发this.$emit('input',' ')函数发出事件告诉Vue实例 data.message发生变化了,实现数据同步。更多有关Object.defineProperty的用法可以参考本系列的基础篇(四)中有介绍。
发布者-订阅者模式的设计思想是发布者管理所有相依于它的订阅者,并且在发布者本身的状态改变时主动发出通知,订阅者接到通知后开始响应变化并刷新。
4.Vue组件间的参数通过什么方法传递?
1.父组件与子组件传参
父组件传给子组件:子组件通过props方法接受参数
子组件传给父组件:$emit方法传递参数
示例:父组件向子组件mycomp传递参数message,并添加对子组件的cbmsg事件监听
<template>
<div>
<p>{{callback_msg}}</p>
<mycomp :message="message" @cbmsg="cbmsgHandle" />
</div>
</template>
<script>
import mycomp from './vmcomp.vue';
export default {
name: '我是父组件',
data() {
return {
message:'父组件向子组件传参',
callback_msg:''
}
},
methods: {
cbmsgHandle(response){
this.callback_msg = response;
}
},
components: {
mycomp
}
}
</script>
子组件通过props属性接收父组件传入的参数,并通过触发cbmsg事件向父组件传递参数
<template>
<div>
<p>{{message}}</p>
<button @click="clickHandle">click</button>
</div>
</template>
<script>
export default {
props: {
message: {
type: String,
default: ''
}
},
methods: {
clickHandle() {
this.$emit('cbmsg', '子组件向父组件传参');
}
}
}
</script>
2.非父子组件间的数据传递,兄弟组件传值
使用事件总线EventBus,创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。
示例:创建Bus
// vue-bus.js
const install = function(Vue) {
const Bus = new Vue({
methods: {
emit(event, ...args) {
this.$emit(event, ...args);
},
on(event, callback) {
this.$on(event, callback);
},
off(event, callback) {
this.$off(event, callback);
}
}
});
Vue.prototype.$bus = Bus;
}
export default install;
// Vue首次创建实例时一次性引入
import VueBus from '@/assets/js/vue-bus';
Vue.use(VueBus);
new Vue({
el: '#app',
...
})
// 组件间使用
this.$bus.on('eventName',function(value){})
this.$bus.emit('eventName', value)
使用Vuex,父组件与子组件共享相同的状态值,配合...mapGetters实现数据同步更新,这时会触发变化组件的updated钩子函数,但并不会重新挂载。这里要加个题外话,在Vuex中状态是存储在一个共享对象里的,所以当有相同getters的时候将会报错,针对这种情况,可以使用命名空间namespaced: true,添加这个属性后,无论是存值、取值还是执行函数都需要加上前缀命名空间,详细可参考:https://blog.csdn.net/weixin_39015132/article/details/84143962。
5. Vue的路由实现:hash模式 和 history模式
hash模式:在浏览器显示的URL中带有符号“#”,#以及#后面的字符称之为hash,通过hashChange事件来监听URL的改变,用window.location.hash读取。
特点:hash虽然在URL中,但不被包括在HTTP请求中,因此hash不会重加载页面。hash内容是用来指导浏览器动作,对服务端安全无用。
history模式:在浏览器显示的URL中以“ / ”分割,没有#。这种模式采用HTML5的新特性且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。
特点:前端的 URL 必须和实际向后端发起请求的 URL 一致,如http://www.xxx.com/items/id,后端如果缺少对 /items/id 的路由处理,将返回 404 错误。解决办法:当访问不到页面时,重定向到index.html页面,即app依赖的页面。
6. Vue路由的钩子函数有哪些?
主要有两个:beforeEach,afterEach。用于对路由导航跳转前、后的监听,如跳转前检查是否包括有效Token,没有则返回登录页面。
beforeEach有3个参数to,from,next:
- to:route即将进入的目标路由对象。
- from:route当前正要离开的路由对象。
- next :一定要调用该方法完成路由跳转,默认情况不设参数,跳转至to指定的路由对象;也可以设置把路由对象作为入参传入,跳转到指定页面。
示例:
// Router跳转页面时,验证token
export const SetRouterTransition = function(router, store) {
/* router before */
router.beforeEach((to, from, next) => {
// check this router need auth
if(to.meta.requireAuth) {
if(store.state.app.token || getLocalStorage('api_token')) {
next();
} else {
alert('没有找到模拟TOKEN值')
next({
path: '/',
query: {
redirect: to.fullPath
}
})
}
} else {
next();
}
});
/* router after */
router.afterEach((transition) => {
let title = transition.name;
document.title = title;
});
}
// 在入口文件添加参数router和store
SetRouterTransition(router, store);
7. Vuex是什么?
Vuex是一种用来实现状态管理的机制,它的应用核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex主要有四个模块组成:
- state:用来存储组件状态。Vuex使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据。
- mutations:用于同步修改store中的状态。使用this.$store.commit(mutationsType, ...param);触发。
- getters:类似Vue的计算属性,主要用来过滤一些数据或者直接返回某个状态值。使用this.$store.getters.xxx获取。
- action:用于同步修改store中的状态。使用this.$store.dispatch(actionType, ...param) 触发。
8. vue-cli如何新增自定义指令?
自定义指令使用Vue.directive来定义,简单而言,就是从指令赋值中获取对添加该指令元素的操作说明,通过对操作的解析后按要求初始化该元素。
定义指令有5个钩子函数:
- bind:只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行的初始化动作。
- inserted:被绑定元素插入父节点时调用。
- update:所在组件的 VNode 更新时调用,指令的值可能发生了改变也可能没有。
- componentUpdated:所在组件的 VNode 及其孩子的 VNode 全部更新时调用。
- unbind:只调用一次,指令与元素解绑时调用。
这些钩子函数具有相同的回调参数:
- el:指令所绑定的元素,可以用来直接操作 DOM 。
- binding: 获取外界数值的对象
- vnode:vue 编译生成的虚拟节点
- oldVnode:上一个虚拟节点
全局指令:
Vue.directive('mydir', {
inserted(el) {
console.log(el);
}
})
局部指令:
var app = new Vue({
el: '#app',
// 创建指令(可以多个)
directives: {
// 指令名称
mydir: {
inserted(el) {
console.log(el);
}
}
}
})
示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script src="../js/libs/vue/2.4.2/vue.js"></script>
</head>
<body>
<div id="app">
<!-- v-myorder:arg.a=value -->
<!-- arg用于对该指令区别类别 .a表示对该指令类别的修饰符,value类似于值对入参value -->
<!--如果你不需要arg.a,也可以直接写成 v-myorder=value -->
<p v-didemo:foo.a="msg">{{msg}}</p>
<button @click="change">改变msg</button>
</div>
</body>
<script>
Vue.directive("didemo", {
bind: function(el, binding, vnode) {
console.log(1, el, binding);
el.style.color = "red"; //设置文本字体颜色为红色
},
inserted: function(el, binding, vnode) {
console.log(2, el, binding);
},
update: function(el, binding, vnode) {
console.log(3, el, binding);
},
componentUpdated: function(el, binding, vnode) {
console.log(4, el, binding);
},
unbind: function(el, binding, vnode) {
console.log(5, el, binding);
}
});
var app = new Vue({
el: "#app",
data: {
msg: "hello world"
},
methods: {
change: function() {
this.msg = "i am change";
}
}
});
</script>
</html>
指令内容更新前:
指令内容更新后:
9. vue如何自定义一个过滤器?
通过vue实例的 filters 属性或 Vue.filter() 函数创建过滤器,在值运算中用竖杠 | 表示过滤:
<div id="app">
<input type="text" v-model="msg" />
{{ msg | capitalize }}
</div>
局部过滤器:
var vm=new Vue({
el:"#app",
data:{
msg:''
},
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
})
全局过滤器:
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
10. 对keep-alive 的了解?
keep-alive是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。在vue 2.1.0 版本之后,keep-alive新加入了两个属性: include(包含的组件缓存) 与 exclude(排除的组件不缓存,优先级大于include) 。
<!-- 逗号分隔字符串,只有组件a与b被缓存。 -->
<keep-alive include="a,b">
<component></component>
</keep-alive>
该组件也常用于路由导航是否缓存页面内容:
<keep-alive>
<router-view/>
</keep-alive>
这时,在进入导航页面时将不会重新渲染页面,如果需要更新页面数据可以在生命周期钩子函数 activated 里操作。
11. 什么是事件修饰符?
事件修饰符是指与事件触发相关的修饰符。如要阻止冒泡事件需要用到event.stopPropagation(),然而使用事件修饰符,仅需@click.stop即可。
事件修饰符有以下几种,以click事件为例:
- @click.stop 阻止继续冒泡
- @click.capture 由默认冒泡模式转为捕获模式
- @click.prevent 提交事件不再重载页面
- @click.self 只当事件在该元素本身触发时触发回调
- @click.once 仅执行一次
修饰符间可以串联使用,如用 @click.stop.once,表示阻止冒泡并仅执行一次。
12. 什么是按键修饰符?
按键修饰符是指与键盘事件相关的修饰符。如对Enter键的监听@keyup.enter,快捷组合件的监听等。按键修饰符一般与input标签配合使用,有以下几种:
- .enter
- .tab
- .delete (捕获 “删除” 和 “退格” 键)
- .esc
- .space
- .up
- .down
- .left
- .right
vue2.x版本后新增了下面几种修饰符:
- .ctrl
- .alt
- .shift
- .meta
13. Vue中Methods与计算属性computed的区别?
Methods里的函数每次调用都会从头开始执行一遍;computed里的函数只要入参相同,只在首次执行计算过程,其它的会从缓存中直接返回已经计算过的值,不会重新计算。
更多推荐
所有评论(0)