- 简单介绍一下你对vue框架的理解?
1.1 vue中MVVM的理解
- 描述:
M:模型(Model):数据模型;负责数据存储。泛指后端进行的各种业务逻辑处理和数据操控,主要围绕数据库系统展开。
V:View 视图: 负责页面展示,也就是用户界面。主要由 HTML 和 CSS 来构建
VM:视图模型(View-Model): 负责业务逻辑处理(比如Ajax请求等),对数据进行加工后交给视图展示
通过vue类创建的对象叫Vue实例化对象,这个对象就是MVVM模式中的VM层,模型通过它可以将数据绑定到页面上,视图可以通过它将数据映射到模型上 - 优点:
1.低耦合。视图(View)可以独立于Model变化和修改,
2.可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑
3.前后端分离,开发人员可以专注于业务逻辑(ViewModel)和数据的开发,设计人员可以专注于页面设计
1.2 渐进式: 为什么说VUE是一个渐进式的javascript框架, 渐进式是什么意思?
- VUE允许你将一个网页分割成可复用的组件,每个组件都包含属于自己的HTML、CSS、JAVASCRIPT以用来渲染网页中相应的地方。对于VUE的使用可大可小,它都会有相应的方式来整合到你的项目中。所以说它是一个渐进式的框架。VUE是响应式的(reactive)这是VUE最独特的特性,也就是说当我们的数据变更时,VUE会帮你更新所有网页中用到它的地方。
- vue生命周期:
- beforeCreate(创建前) :组件实例被创建之初,组件的属性生效之前 //beforeCreate生命周期执行的时候,data和methods中的数据都还没有初始化。不能在这个阶段使用data中的数据和methods中的方法
- created(创建后) :组件实例已经完全创建,属性也绑定,但真实 dom 还没有生成,
$el
还不可用 // data 和 methods都已经被初始化好了,如果要调用 methods 中的方法,或者操作 data 中的数据,最早可以在这个阶段中操作 - beforeMount(挂载前) :在挂载开始之前被调用:相关的 render 函数首次被调用
//执行到这个钩子的时候,在内存中已经编译好了模板了,但是还没有挂载到页面中,此时,页面还是旧的 - mounted(挂载后) :在el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子
//到mounted周期的时候,Vue实例已经初始化完成了。此时组件脱离了创建阶段,进入到了运行阶段。 如果我们想要通过插件操作页面上的DOM节点,最早可以在和这个阶段中进行 - beforeUpdate(更新前) :组件数据更新之前调用,真实DOM还没被渲染
// 当执行这个钩子时,页面中的显示的数据还是旧的,data中的数据是更新后的,页面还没有和最新的数据保持同步 - update(更新后) :组件数据更新之后 //页面显示的数据和data中的数据已经保持同步了,都是最新的
- activated(激活前) :keep-alive专属,组件被激活时调用 //当组件被切回来时,再去缓存里找这个组件、触发 activated钩子函数。
- deactivated(激活后) :keep-alive专属,组件被销毁时调用 //当组件被换掉时,会被缓存到内存中、触发 deactivated 生命周期
- beforeDestory(销毁前) :组件销毁前调用//Vue实例从运行阶段进入到了销毁阶段,这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于可用状态。还没有真正被销毁
- destoryed(销毁后) :组件销毁前调用 //这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于不可用状态。组件已经被销毁了。
指令:
v-el 作用是什么: 提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。可以是 CSS 选择器,也可以是一个 HTMLElement 实例。$nextTick原理及运用
-
nextTick是啥?
- Vue.nextTick( [callback, context] ):在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
-
为什么需要它呢?
- Vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个事件循环 (event loop)
- 当中观察到数据变化的 watcher 推送进这个队列。如果这个watcher被触发多次,只会被推送到队列一次。
- 这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和DOm操作。而在下一个事件循环时,Vue会清空队列,并进行必要的DOM更新。
- 假使你设置 vm.someData = 'new value',DOM 并不会马上更新,而是在异步队列被清除,也就是下一个事件循环
- 开始时执行更新时才会进行必要的DOM更新。如果此时你想要根据更新的 DOM 状态去做某些事情,就会出现问题。
- 为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。
这样回调函数在 DOM 更新完成后就会调用。
-
我在什么地方用它呢?
- 1、在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中。
原因是在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,
所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted钩子函数,
因为该钩子函数执行时所有的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操作都不会有问题 。 - 2、在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候
(譬如v-if/v-show根据字段变化显隐),这个操作都应该放进Vue.nextTick()的回调函数中。
- 1、在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中。
- Vue实现数据双向绑定的原理:Object.defineProperty()
- vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。
- vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}),最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。
- 动态给vue的data添加一个新的属性时为什么不刷新?怎样解决?
- 如果为对象添加少量的新属性,可以直接采用Vue.set()
- 如果需要为新对象添加大量的新属性,则通过Object.assign()创建新对象
- 如果你实在不知道怎么操作时,可采取$forceUpdate()进行强制刷新 (不建议)
- PS:vue3是用过proxy实现数据响应式的,直接动态添加新属性仍可以实现数据响应式
- vue中key的原理及其必要性
- vue中key作用:key是给每一个vnode的唯一id,也是diff的一种优化策略,可以根据key,更准确, 更快的找到对应的vnode节点
- key是每一个节点的唯一标识
- 必要性:当我们对数据进行更新的时候,譬如在数组中插入、移除数据时,设置的key值能让vue底层高效的对新旧vnode进行diff,然后将比对出的结果用来更新真实的DOM
vue基础语法: 修饰符
- Vue常用的修饰符及其使用
修饰符是用于限定类型以及类型成员的声明的一种符号, 常见修饰符种类:
- 表单修饰符
//lazy、trim、number
//光标离开标签的时候,才会将值赋予给value
<input type="text" v-model.lazy="value">
//过滤用户输入的首尾空格字符,注意,如果是中间的空格则不会过滤
<input type="text" v-model.trim="value">
//自动将用户的输入值转为数值类型,但如果这个值无法被parseFloat解析,则会返回原来的值
<input v-model.number="age" type="number">
- 事件修饰符
//stop、prevent、self、once、capture、passive、native
//阻止了事件冒泡
<button @click.stop="btn()">ok</button>
//阻止了事件的默认行为
<form v-on:submit.prevent="onSubmit"></form>
//只当在 event.target 是当前元素自身时触发处理函数
<div v-on:click.self="doSome">...</div>
//事件只能触发一次
<button @click.once="btn()">ok</button>
//事件触发从包含这个元素的顶层开始往下触发 //输出结构: 1 2
<div @click.capture="btn(1)">
1
<div @click.capture="btn(2)">
2
</div>
</div>
/* 在移动端,当我们在监听元素滚动事件的时候,会一直触发onscroll事件会让我们的网页变卡,
因此我们使用这个修饰符的时候,相当于给onscroll事件整了一个.lazy修饰符*/
<div v-on:scroll.passive="onScroll">...</div>
/*让组件变成像html内置标签那样监听根元素的原生事件,否则组件上使用 v-on 只会监听自定义事件
注意:使用.native修饰符来操作普通HTML标签是会令事件失效的*/
<my-component v-on:click.native="doSomething"></my-component>
- 鼠标按键修饰符
//left 左键点击、right 右键点击、middle 中键点击
<button @click.left="btn('left')">ok</button>
<button @click.right="btn('right')">ok</button>
<button @click.middle="btn('middle')">ok</button>
- 键值修饰符
//onkeyup,onkeydown,后面需要跟keycode编码名或者按键编码
<input type="text" @keyup.keyCode="btn()">
- v-bind修饰符
//async、prop、camel
//能对props进行一个双向绑定
//父组件
<comp :myMessage.sync="bar"></comp>
//子组件
this.$emit('update:myMessage',params);
//上面代码与以下代码是等同的,实际上就是实现了获取子组件信息处理并回传
//父亲组件
<comp :myMessage="bar" @update:myMessage="func"></comp>
func(e){
this.bar = e;
}
//子组件js
func2(){
this.$emit('update:myMessage',params);
}
注意:
1、使用sync的时候,子组件传递的事件名格式必须为update:value,其中value必须与子组件中props中
声明的名称完全一致
2、注意带有 .sync 修饰符的 v-bind 不能和表达式一起使用
3、将 v-bind.sync 用在一个字面量的对象上,例如 v-bind.sync=”{ title: doc.title }”,是无法正常工作的
//设置自定义标签属性,避免暴露数据,防止污染HTML结构
<input id="uid" title="title1" value="1" :index.prop="index">
//将命名变为驼峰命名法,如将view-Box属性名转换为 viewBox
<svg :view-Box.camel="viewBox"></svg>
- computed与watch的区别
- computed擅长处理的场景:一个数据受多个数据影响;watch擅长处理的场景:一个数据影响多个数据。
- 功能上:computed是计算属性,watch是监听一个值的变化,然后执行对应的回调。
- 是否调用缓存:computed支持缓存,只有依赖数据发生改变,才会重新进行计算;而watch不支持缓存,数据变,直接会触发相应的操作。
- 是否调用return:computed中的函数必须要用return返回,watch中的函数不是必须要用return。
- computed不支持异步 ,当computed内有异步操作时无效,无法监听数据的变化;而watch支持异步。
- computed默认第一次加载的时候就开始监听;watch默认第一次加载不做监听,如果需要第一次加载做监听,添加immediate属性,设置为true(immediate:true)
虚拟Dom
- 虚拟DOM中key的作用:
- key是虚拟DOM对象的标识,当状态中的数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】的差异比较,比较规则如下:
- key的对比规则:
- 旧虚拟DOM中找到了与新虚拟DOM相同的key:
- 若虚拟DOM中内容没变,直接使用之前的真实DOM
- 若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
- 旧虚拟DOM中未找到与新虚拟DOM相同的key: 创建新的真实DOM,随后渲染到页面
- 用index作为key可能会引发的问题:
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ===> 界面效果没问底,但效率低
- 如果结构中还包含输入类的DOM:会产生错误DOM更新 ===> 界面有问题
- 开发中如何选择key?
- 最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值
- 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的
组件通讯
- Vue子组件和父组件执行顺序
- 加载渲染过程:beforeCreate(父) —> created(父)—>beforeMount(父)—>beforeCreate(子)—>created(子)—>beforeMount(子)—>mounted(子)—>mounted(父)
- 更新过程:beforeUpdate(父) —> beforeUpdate(子) —> update(子) —> update(父)
- 父组件更新:beforeUpdate(父) —> updated(父)
- 销毁过程:beforeDestory(父) —> beforeDestory(子) —> destoryed(子) —> destoryed(父)
- 非父子组件如何通讯?
- 在非父子组件通信时,可以使用通常的bus或者使用vuex,但是一些小项目上功能不是太复杂,
而使用上面两个又有点繁琐。这时,observable就是一个很好的选择
- Vue.observable
- Vue.observable,让一个对象变成响应式数据。Vue 内部会用它来处理 data 函数返回的对象返回的对象可以直接用于渲染函数和计算属性内,并且会在发生变更时触发相应的更新。也可以作为最小化的跨组件状态存储器
- Vue组件间的参数传递
- 父组件传给子组件:子组件通过props方法接受数据;
- 子组件传给父组件:$emit方法传递参数
- 非父子组件间的数据传递,兄弟组件传值借用eventBus,就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。发送数据使用 on 接收
- provide和inject使用
- provide和inject是用来实现父组件向深层的子组件传值和接收的语法
// 祖先组件
provide(){
return {
// keyName: { name: this.name }, // value 是对象才能实现响应式,也就是引用类型
keyName: this.changeValue // 通过函数的方式也可以[注意,这里是把函数作为value,而不是this.changeValue()]
// keyName: 'test' value 如果是基本类型,就无法实现响应式
}
},
data(){
return {
msg:'初始mesg'
}
},
methods: {
changeValue(){
this.msg= '改变后的msg'
}
}
// 后代组件
inject:['keyName']
create(){
console.log(this.keyName) // 改变后的msg
}
- mixins
mixins是一种分发Vue组件中可复用功能的一种灵活方式。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
mixins是一个JavaScript对象,可以包含组件中的任意选项,比如Vue实例中生命周期的各个钩子函数,也可以是data、components、methods或directives等
- 运用:
//mixin文件
export const myMixin={
data(){
return{
msg:1
}
},
created(){
console.log('myMixin')
},
methods:{
Fn(){
console.log('myMixin')
}
}
}
//引入
<template>
<div>运用mixin的组件</div>
</template>
<script>
import {myMixin} from'目标文件路径'
export default{
mixins:[myMixin]
}
</script>
- 特点:
1、在组件A对混入的数据做出更改后组件B获取到的仍是混入初始设置的数据,组件间操作互不污染。
2、值为对象的如methods,components等,选项会被合并,组件会覆盖混入对象的方法。
比如混入对象里有个方法A,组件里也有方法A,这时候在组件里调用的话,执行的是组件里的A方法。
3、created,mounted等,就会被合并调用,混合对象里的钩子函数在组件里的钩子函数之前调用,
同一个钩子函数里,会先执行混入对象的东西,再执行本组件的。
4、在mixins里面包含异步请求函数的时候,通过直接调用异步函数获取返回数据
- 运用场景区别:
vuex:用来做状态管理,可以看做全局变量,里面定义的变量在每个组件中均可以使用和修改,在任一组件中修改此变量的值之后,其他组件中此变量的值也会随之修改。
mixins:可以定义共用的变量,在每个组件中使用,引入组件中之后,各个变量是相互独立的,值的修改在组件中不会相互影响。
父子组件:父子组件相对来说比较独立,只是父组件将一部分使用子组件,而mixins更像是对于组件的拓展,并且组件可以对于混入的数据和方法进行多样化操作。
vue中路由
- Vue的路由实现:hash模式 和 history模式
- hash模式: 在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;
- 工作原理: 监听网页的hash值变化 —> onhashchange事件, 获取location.hash
- 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。
- 会给用户好像跳转了网页一样的感觉, 但是实际上没有跳转
- 主要用在单页面应用(SPA)
- 特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。
- history模式: history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。
- 工作原理: 主要利用 history.pushState() API 来改变URL, 而不刷新页面.
- 其实一共有五种模式可以实现改变URL, 而不刷新页面.
- 需要后台配置支持, 如果输入一个并不存在的url, 需要后端配置做 “兜底配置”, 不是粗暴的返回404, 而是返回首页
- vue中的路由传参:
//通过 params 传参
this.$router.push({
name: '目标组件名',
params: {
id: id
}
})
//接收:
this.$route.params
//通过 query 传参
this.$router.push({
path: '目标组件路径',
query: {
id: id
}
})
//接收
this.$route.query
//区别:query使用path来引入,params使用name来引入,接收方式是this.$route.query.name和this.$route.params.name,值得注意的是query传递的参数会显示在url后面以?id=?形式展示。
//动态路由传参
//直接调用$router.push 实现携带参数的跳转
this.$router.push({
path: `/particulars/${id}`,
})
//通过this.$route.params.id接收,可以看到,和上面传参不一样的是我们直接把动态参数加在路径后面实现动态路由
- Vue 路由跳转的几种方式
- router-link (声明式路由)
- router.push(编程式路由)
- this.$router.push() (函数里面调用)
- this.$router.replace() (用法同上,push)
- this.$router.go(n)
vuex
- vuex是什么?怎么使用?哪种功能场景使用它?
- 是什么: vue框架中状态管理。在main.js引入store注入。新建一个目录store 。场景有:单页应用中,组件之间的状态,
音乐播放、登录状态、加入购物车等。 - 属性:State、 Getter、Mutation 、Action、 Module
- State: state是数据源存放地,对应于一般Vue对象里面的data。state里面存放的数据是响应式的,
Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新
需要通过mapState把全局 state 和 getters 映射到当前组件的 computed 计算属性中。 - Getter: getters 可以对State进行计算操作,在多个组件间复用
- Mutation 、Action: Action 类似于 mutation,不同在于Action 提交的是 mutation,而不是直接变更状态;Action 可以包含任意异步操作。
- Module: Vuex允许我们将store分隔成模块(module),每个模块拥有自己的state,mutation,action,getter,甚至是嵌套子模块
- 使用场景: 一句话,不要为了使用vuex而去使用vuex,推荐组件间数据复用,记录登录及其它状态值数据,一些需要缓存的数据使用vuex都能达到很好的管理
- 是什么: vue框架中状态管理。在main.js引入store注入。新建一个目录store 。场景有:单页应用中,组件之间的状态,
- vuex是什么?怎么使用?哪种功能场景使用它?
- vue框架中状态管理。在main.js引入store注入。新建一个目录store 。场景有:单页应用中,组件之间的状态,音乐播放、登录状态、加入购物车等。
3)vuex有哪几种属性? : 有五种,分别是 State、 Getter、Mutation 、Action、 Module。
- vuex的State特性:
- Vuex就是一个仓库,仓库里面放了很多对象。其中state就是数据源存放地,对应于一般Vue对象里面的data。
- state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新。
- 通过mapState把全局 state 和 getters 映射到当前组件的 computed 计算属性中。
- vuex的Getter特性
- getters 可以对State进行计算操作,它就是Store的计算属性。
- 虽然在组件内也可以做计算属性,但是getters 可以在多组件之间复用。
- 如果一个状态只在一个组件内使用,可以不用getters。
- vuex的Mutation特性
- Action 类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态;Action 可以包含任意异步操作。
- 不用Vuex会带来什么问题?
- 可维护性会下降,想修改数据要维护三个地方;
- 可读性会下降,因为一个组件里的数据,根本就看不出来是从哪来的;
- 增加耦合,大量的上传派发,会让耦合性大大增加,Vue用Component本意就是为了减少耦合,现在这么用,和组件化的初衷相背。
综合:
- SSR
什么是ssr:Server-Side Rendering 我们称其为SSR,意为服务端渲染,展开说就是通过服务侧完成页面的 HTML 结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程
ssr作用:
seo:搜索引擎优先爬取页面HTML结构,使用ssr时,服务端已经生成了和业务想关联的HTML,有利于seo
首屏呈现渲染:用户无需等待页面所有js加载完成就可以看到页面视图(压力来到了服务器,所以需要权衡哪些用服务端渲染,哪些交给客户端)
缺点:
项目复杂度高
需要库的支持性,代码兼容
服务器负载变大,相对于前后端分离务器只需要提供静态资源来说,服务器负载更大
- JS事件循环