文章目录
- Vue.js 是什么,它有什么有点和缺点
- vue响应式原理
- 谈谈你对MVVM的理解?
- 谈谈你对Vue中响应式数据的理解?
- 为什么说Vue是一个渐进式框架?
- Vue跟React的异同点?
- Vue和JQuery的区别在哪?为什么放弃JQuery用Vue?
- 为什么data是个函数并且返回一个对象呢?
- 使用过哪些Vue的修饰符呢?
- vue的虚拟dom和真实dom的区别?
- 使用过哪些Vue的内部指令呢?
- 路由有哪些模式呢?又有什么不同呢?
- 如何设置动态class,动态style?
- v-if和v-show有何区别?
- computed和watch有何区别?
- vue生命周期函数
- vue父子组件的生命周期
- 为什么v-if和v-for不建议用在同一标签?
- 你对组件的理解
- vuex的有哪些属性?用处是什么?
- 不需要响应式的数据应该怎么处理?
- watch有哪些属性,分别有什么用?
- 对象新属性无法更新视图,删除属性无法更新视图,为什么?怎么办?
- 直接arr[index] = xxx无法更新视图怎么办?为什么?怎么办?
- 说说vue的自定义指令
- 插槽的使用以及原理?
- 说说nextTick的用处?
- vue中使用v-for时为什么要用到key?为什么不能用index作为key?
- Vue的SSR是什么?有什么好处?
- Vue的el属性和$mount优先级?
- 4.vue的组件通信
Vue.js 是什么,它有什么有点和缺点
Vue.js 是一个轻巧、高性能、可组件化的 MVVM 库,拥有非常容易上手的 API,是一个构建数据驱动的 Web 界面的库。
优点
- 渐进式:通俗点讲就是,你想用啥你就用啥,咱也不强求你。你想用component就用,不用也行,你想用vuex就用,不用也可以。
- 组件化
- 轻量级
- 虚拟dom
- 响应式设计
- 单页面路由
- 数据与视图分开
- 双向数据绑定减少了 DOM 操作,将更多精力放在数据和业务逻辑上
缺点
- 单页面不利于seo
- 不支持IE8以下
- 首屏加载时间长
vue响应式原理
1.响应式原理
在生成vue实例时,为对传入的data进行遍历,使用Object.defineProperty把这些属性转为getter/setter。
Object.defineProperty 不支持 IE8 以及更低版本浏览器的原因。
每个vue实例都有一个watcher实例,它会在实例渲染时记录这些属性,并在setter触发时重新渲染。
Vue 无法检测到对象属性的添加或删除
Vue 不允许动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, propertyName, value)方法向嵌套对象添加响应式属性。
2.声明响应式属性
由于 Vue 不允许动态添加根级响应式属性,所以你必须在初始化实例前声明所有根级响应式属性,哪怕只是一个空值。
如果你未在 data 选项中声明 message,Vue 将警告你渲染函数正在试图访问不存在的属性。
3.异步更新队列
数据变化、更新是在主线程中同步执行的;在侦听到数据变化时,watcher将数据变更存储到异步队列中,当本次数据变化,即主线成任务执行完毕,异步队列中的任务才会被执行(已去重)。
如果你在js中更新数据后立即去操作DOM,这时候DOM还未更新;vue提供了nextTick接口来处理这样的情况,它的参数是一个回调函数,会在本次DOM更新完成后被调用。
使用方法:
- 1.在组件内使用 vm.$nextTick() 实例方法特别方便,因为它不需要全局 Vue,并且回调函数中的 this 将自动绑定到当前的 Vue 实例上:
Vue.component('example', {
template: '<span>{{ message }}</span>',
data: function () {
return {
message: '未更新'
}
},
methods: {
updateMessage: function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
this.$nextTick(function () {
console.log(this.$el.textContent) // => '已更新'
})
}
}
})
- 2.因为 $nextTick() 返回一个 Promise 对象,所以你可以使用新的 ES2016 async/await 语法完成相同的事情:
methods: {
updateMessage: async function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
await this.$nextTick()
console.log(this.$el.textContent) // => '已更新'
}
}
谈谈你对MVVM的理解?
谈谈你对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变更双向绑定效果。
为什么说Vue是一个渐进式框架?
渐进式:通俗点讲就是,你想用啥你就用啥,咱也不强求你。你想用component就用,不用也行,你想用vuex就用,不用也可以
Vue跟React的异同点?
相同点:
1.都使用了虚拟dom
2.组件化开发
3.都是单向数据流(父子组件之间,不建议子修改父传下来的数据)
4.都支持服务端渲染
不同点:
1.React的JSX,Vue的template
2.数据变化,React手动(setState),Vue自动(初始化已响应式处理,Object.defineProperty)
3.React单向绑定,Vue双向绑定
4.React的Redux,Vue的Vuex
Vue和JQuery的区别在哪?为什么放弃JQuery用Vue?
.jQuery是直接操作DOM,Vue不直接操作DOM,Vue的数据与视图是分开的,Vue只需要操作数据即可
2.在操作DOM频繁的场景里,jQuery的操作DOM行为是频繁的,而Vue利用虚拟DOM的技术,大大提高了更新DOM时的性能
3.Vue中不倡导直接操作DOM,开发者只需要把大部分精力放在数据层面上
4.Vue集成的一些库,大大提高开发效率,比如Vuex,Router等
为什么data是个函数并且返回一个对象呢?
data之所以只一个函数,是因为一个组件可能会多处调用,而每一次调用就会执行data函数并返回新的数据对象,这样,可以避免多处调用之间的数据污染。
通俗来说就是:如果你不用函数,只用对象保存数据时,所有组件将引用一个公共的obj。一个属性改动,其他组件上全部都更改了。用函数来返回就完美解决这个问题。
使用过哪些Vue的修饰符呢?
- .lazy:改变输入框的值时value不会改变,当光标离开输入框时,v-model绑定的值value才会改变
- .trim 类似于js的tram()方法,作用事把v-model值得空格去掉
- .number 将值转化为数字,但是先输入字符串和先输入数字,是两种情况,先输入数字的话只取前面的部分,先输入字母的话,number修饰符无效
- .stop 阻止猫跑
- .self 只有点击事件绑定的本身才会触发
- .once 事件只执行一次
- .prevent 阻止默认事件(例如a标签跳转)
- .native 加在自定义组件的事件上,保证事件能执行
- .left .rihgt .middle 这三个修饰符是鼠标的左中右案件触发的事件
- .sync 父子组件船只,子组件想更新这个值,使用此修饰符可简写。
vue的虚拟dom和真实dom的区别?
1、虚拟DOM不会进行排版与重绘操作
2、虚拟DOM进行频繁修改,然后一次性比较并修改真实DOM中需要改的部分(注意!),最后并在真实DOM中进行排版与重绘,减少过多DOM节点排版与重绘损耗
3、真实DOM频繁排版与重绘的效率是相当低的
4、虚拟DOM有效降低大面积(真实DOM节点)的重绘与排版,因为最终与真实DOM比较差异,可以只渲染局部
总之,一切为了减弱频繁的大面积重绘引发的性能问题,不同框架不一定需要虚拟DOM,关键看框架是否频繁会引发大面积的DOM操作
使用过哪些Vue的内部指令呢?
- v-text更新元素的textContent
- v-html更新元素的innerHtml
- v-show更改显示隐藏,当条件变化时会触发过度效果
- v-if v-else v-else-if
- v-for列表循环渲染
- v-on 缩写是on绑定事件
- v-bind 缩写是:动态绑定数据
- v-molde 双向数据绑定
- v-slot缩写是#插槽名
- v-once元素组件只渲染一次
- v-pre跳过这个元素和子元素编译过程,可以用来显示原始mustache标签。跳过大量没有指令的节点加快编译
路由有哪些模式呢?又有什么不同呢?
hash模式:
1、url路径会出现 # 字符
2、hash值不包括在 HTTP 请求中,它是交由前端路由处理,所以改变hash值时不会刷新页面,也不会向服务器发送请求
3、hash值的改变会触发hashchange事件
history模式:
1、整个地址重新加载,可以保存历史记录,方便前进后退
2、使用 HTML5 API(旧浏览器不支持)和 HTTP服务端配置,没有后台配置的话,页面刷新时会出现404
如何设置动态class,动态style?
- 动态class对象:
- 动态class数组:
- 动态style对象:
- 动态style数组:
v-if和v-show有何区别?
- 1.v-if是通过控制dom元素的删除和生成来实现显隐,每一次显隐都会使组件重新跑一遍生命周期,因为显隐决定了组件的生成和销毁
- 2.v-show是通过控制dom元素的css样式来实现显隐,不会销毁
- 3.频繁或者大数量显隐使用v-show,否则使用v-if
computed和watch有何区别?
vue生命周期函数
beforeCreate( 创建前 ) 实例了vue但还没进行数据的初始化与响应式处理
created(创建后)数据已被初始化和响应式处理,在这里可以访问数据,也可以使用数据
beforeMount(挂在前)render函数在这里被调用,生成虚拟DOM但是还没转成真是DOM并替换到el
mounted(挂载后)真实DOM挂载完毕
beforeUpdate(数据更新前调用)数据更新后新的DOM生成但还没跟旧虚拟DOM对比打补丁
updated(更新后)新旧DOM打补丁后进行真实DOM更新
beforeDestroy(实例销毁前)实例销毁前调用在这一步依然可以访问数据
destroyed(实例销毁后) 实例销毁后该钩子被调用,对应的vue事件被解绑所有的监听事件被移除所有的子实例被销毁
activated(被 keep-alive 缓存的组件激活时调用)
decactived(被 keep-alive 缓存的组件停用时调用)
vue父子组件的生命周期
1.加载渲染过程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
2.子组件更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
3.父组件更新过程
4.销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
为什么v-if和v-for不建议用在同一标签?
在Vue2中,v-for优先级是高于v-if的,咱们来看例子
<div v-for="item in [1, 2, 3, 4, 5, 6, 7]" v-if="item !== 3">
{{item}}
</div>
上面的写法是v-for和v-if同时存在,会先把7个元素都遍历出来,然后再一个个判断是否为3,并把3给隐藏掉,这样的坏处就是,渲染了无用的3节点,增加无用的dom操作,建议使用computed来解决这个问题:
<div v-for="item in list">
{{item}}
</div>
computed() {
list() {
return [1, 2, 3, 4, 5, 6, 7].filter(item => item !== 3)
}
}
你对组件的理解
可组合,可复用,可维护,可测试
vuex的有哪些属性?用处是什么?
- State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。
- Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
- Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。
- Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。
- Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。
不需要响应式的数据应该怎么处理?
在我们的Vue开发中,会有一些数据,从始至终都未曾改变过,这种死数据,既然不改变,那也就不需要对他做响应式处理了,不然只会做一些无用功消耗性能,比如一些写死的下拉框,写死的表格数据,这些数据量大的死数据,如果都进行响应式处理,那会消耗大量性能。
//方法一:将数据定义在data之外
data () {
let abc = 123
return {}
}
// 方法二:Object.freeze()
data(){
return{
abc:Object.freeze({})
}
}
watch有哪些属性,分别有什么用?
watch: {
value () {
// do something
}
}
watch: {
obj: {
handler () { // 执行回调
// do something
},
deep: true, // 是否进行深度监听
immediate: true // immediate用于首次监听,默认监听只会在属性发生改变时才会触发,当immediate属性为true时,数据第一次渲染也会被触发监听属性。
}
}
参考:Vue.js中 watch 用法详解(立即执行,深度监听)
-----------------------------------------------------------------------------------------------
对象新属性无法更新视图,删除属性无法更新视图,为什么?怎么办?
因:Object.defineProperty没有对对象的新属性进行属性劫持
对象新属性无法更新视图:使用Vue.$set(obj, key, value),组件中this.$set(obj, key, value)
删除属性无法更新视图:使用Vue.$delete(obj, key),组件中this.$delete(obj, key)
直接arr[index] = xxx无法更新视图怎么办?为什么?怎么办?
原因:Vue没有对数组进行Object.defineProperty的属性劫持,所以直接arr[index] = xxx是无法更新视图的
使用数组的splice方法,arr.splice(index, 1, item)
使用Vue.$set(arr, index, value)
说说vue的自定义指令
稍后更新
插槽的使用以及原理?
稍后更新
说说nextTick的用处?
稍后更新
-----------------------------------------------------------------------------------------------
vue中使用v-for时为什么要用到key?为什么不能用index作为key?
参考:vue中使用v-for时为什么要用到key?为什么不能用index作为key?
Vue的SSR是什么?有什么好处?
- SSR就是服务端渲染
- 基于nodejs serve服务环境开发,所有html代码在服务端渲染
- 数据返回给前端,然后前端进行“激活”,即可成为浏览器识别的html代码
- SSR首次加载更快,有更好的用户体验,有更好的seo优化,因为爬虫能看到整个页面的内容,如果是vue项目,由于数据还要经过解析,这就造成爬虫并不会等待你的数据加载完成,所以其实Vue项目的seo体验并不是很好
Vue的el属性和$mount优先级?
下面这种情况会先渲染哪个
new Vue({
router,
store,
el: '#app',
render: h => h(App)
}).$mount('#ggg')
这是官方的一张图,可以看出el和$mount同时存在时,el优先级 > $mount
4.vue的组件通信
1.方法一:props和$emit
父组件通过props向下传递数据给子组件,子组件通过event给父组件发送消息,实际上就是子组件把自己的数据发送给父组件。
方法二:
a
t
t
r
s
和
attrs和
attrs和listeners
第一种方式处理父子组件之间的数据传输有一个问题:如果父组件A下面有子组件B,组件B下面有组件C,这时如果组件A想传递数据给组件C怎么办呢? 如果采用第一种方法,我们必须让组件A通过prop传递消息给组件B,组件B在通过prop传递消息给组件C;要是组件A和组件C之间有更多的组件,那采用这种方式就很复杂了。Vue 2.4开始提供了
a
t
t
r
s
和
attrs和
attrs和listeners来解决这个问题,能够让组件A之间传递消息给组件C。
<!--组件C-->
<template>
<div>
<input type="text" v-model="$attrs.messagec" @input="passCData($attrs.messagec)">
</div>
</template>
<script>
export default {
name: 'C',
data () {
return {}
},
methods:{
passCData(val){
//触发父组件A中的事件,把从A得到的数据hello C还给A组件
this.$emit('getCData',val)
}
}
}
</script>
<!--组件B-->
<template>
<div>
<input type="text" v-model="mymessage" @input="passData(mymessage)">
<!-- C组件中能直接触发getCData的原因在于 B组件调用C组件时 使用 v-on 绑定了$listeners 属性 -->
<!-- 通过v-bind 绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的) -->
<C v-bind="$attrs" v-on="$listeners"></C>
</div>
</template>
<script>
export default {
name: 'B',
props:[
message:{
type: String,
default: null
}
],//得到父组件A传递过来的数据,hello B
methods:{
passData(val){
//触发父组件中的事件,把message给组件A
this.$emit('getChildData',val)
}
}
}
</script>
<!--组件A-->
<template>
<div>
<p>this is parent compoent!</p>
<B
:messagec="messagec"
:message="message"
:getCData="getCData"
:getChildData="getChildData"></B>
</div>
</template>
<script>
export default {
name: 'A',
data(){
return {
message:'hello B',
messagec:'hello C' //传递给c组件的数据
}
},
methods:{
getChildData(val){
console.log('这是来自B组件的数据',val) //打印hello B
},
//执行C子组件触发的事件
getCData(val){
console.log("这是来自C组件的数据:", val) //打印hello C
}
}
}
</script>
3.方法三:provide和 inject
在 Vue.js 的 2.2.0+ 版本中添加加了 provide 和 inject 选项。他们成对出现,用于父级组件向下传递数据。
父组件中通过provide来提供变量,然后在子组件中通过inject来注入变量。不论子组件有多深,只要调用了inject那么就可以注入provide中的数据。而不是局限于只能从当前父组件的prop属性来获取数据,只要在父组件的生命周期内,子组件都可以调用。
<!--父组件-->
<template>
<div>
<p>this is parent compoent!</p>
<child></child>
</div>
</template>
<script>
export default {
name: 'parent',
provide: {
forChidrenData:'这是要个子组件的数据'
},
data(){
return {
message:'hello'
}
}
}
</script>
<!--子组件-->
<template>
<div>
<input type="text" v-model="mymessage">
</div>
</template>
<script>
export default {
name: 'child',
inject:['forChidrenData'], //得到父组件传递过来的数据
data(){
return {
mymessage:this.forChidrenData
}
}
}
</script>
4.方法四:vuex处理组件之间的数据交互
如果业务逻辑复杂,很多组件之间需要同时处理一些公共的数据,这个时候才有上面这一些方法可能不利于项目的维护,vuex的做法就是将这一些公共的数据抽离出来,然后其他组件就可以对这个公共数据进行读写操作,这样达到了解耦的目的。
5.方法五:中央事件总线
如果两个组件不是父子关系呢?这种情况下可以使用中央事件总线的方式。新建一个Vue事件bus对象,然后通过bus.
e
m
i
t
触
发
事
件
,
b
u
s
.
emit触发事件,bus.
emit触发事件,bus.on监听触发的事件。
公共事件总线eventBus的实质就是创建一个vue实例,通过一个空的vue实例作为桥梁实现vue组件间的通信。它是实现非父子组件通信的一种解决方案。
<!--兄弟组件A-->
<template>
<div>
<p>this is brother1 compoent!</p>
<input type="text" v-model="mymessage" @input="passData(mymessage)">
</div>
</template>
<script>
export default {
name: 'brother1',
data(){
return {
mymessage:'hello brother1'
}
},
methods:{
passData(val){
//触发全局事件globalEvent
bus.$emit('globalEvent', val)
}
}
}
</script>
<!--兄弟组件B-->
<template>
<div>
<p>this is brother2 compoent!</p>
<p>brother1传递过来的数据:{{brothermessage}}</p>
</div>
</template>
<script>
export default {
name: 'brother2',
data(){
return {
brothermessage:''
}
},
mounted(){
//绑定全局事件globalEvent
bus.$on('globalEvent',(val)=>{
this.brothermessage = val;
})
}
}
</script>
//中央事件总线
var bus=new Vue();
var app=new Vue({
el:'#app',
template:`
<div>
<brother1></brother1>
<brother2></brother2>
</div>
`,
beforeDestroy(){
sd bus.$off('globalEvent')
}
})
6.方法六: p a r e n t 和 parent和 parent和children
Vue.component('child',{
props:{
value:String, //v-model会自动传递一个字段为value的prop属性
},
data(){
return {
mymessage:this.value
}
},
methods:{
changeValue(){
this.$parent.message = this.mymessage;//通过如此调用可以改变父组件的值
}
},
template:`
<div>
<input type="text" v-model="mymessage" @change="changeValue">
</div>`
})
Vue.component('parent',{
template:`
<div>
<p>this is parent compoent!</p>
<button @click="changeChildValue">test</button >
<child></child>
</div>
`,
methods:{
changeChildValue(){
this.$children[0].mymessage = 'hello';
}
},
data(){
return {
message:'hello'
}
}
})
var app=new Vue({
el:'#app',
template:`
<div>
<parent></parent>
</div>
`
})