vue的特性
1.数据驱动试图: 数据发生变化会驱动视图自动更新
2.双向数据绑定: 在页面或者data里面修改数据都会相对应的改变
vue指令
数据绑定指令(内容渲染指令)
- v-text 的缺点:会覆盖元素内部的内容
- v-html 的作用:可以把带标签的指令渲染到页面上
- {{}} 开发中经常用这个
- v-model 输入框的双向数据绑定指令
属性指令
- v-bind
- 简写形式
:
(英文输入法模式哦)
事件绑定指令
- v-on
- 简写形式
@
条件指令
- v-show
- v-if
循环指令
- v-for(item in 数据源)
computed 计算属性
计算属性默认只能获取而不能修改,需要修改的话就需要通过setter来改变
读取就使用: getter 修改的话: setter
filter 过滤器属性
只能用在 双花括号插值 和 v-bind 这两个地方
watch 监听属性
键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象
生命周期函数
- beforeCreate
- 此时data 和 methods之类的都没创建出来,在加载实例时触发
- created
- 此时实例创建完成,可以访问 data methods computed等方法和数据,但是没有挂载DOM,一般都在这个阶段发送 axios 请求数据
- beforeMount
- 这个阶段已经完成了 模板的编译,但是还没有渲染到页面上
- mounted
- 这个阶段 页面已经渲染完成,页面可以看到演示了
- beforeUPdate
- 这个阶段 页面的某些data数据发生变化,是一种最新的状态,但是,页面的数据还是旧的,没有渲染上去。 (有时候我们希望某些数据渲染延迟一会,这个时候就可以用**nextTick()**方法来进行延迟更新)
- updated
- 这个阶段 data中的数据和页面上的数据都是最新的了,页面重新渲染完成
- beforeDestroy
- 这个阶段,VUE开始销毁了,但是,此时还没销毁,实例依旧可以使用,this仍然可以获取实例
- destroyed
- 这个阶段,实例销毁完成this获取不到实例,所有事件监听都会移除
- 这个阶段,实例销毁完成this获取不到实例,所有事件监听都会移除
vue修饰符
-
lazy
- 作用: 改变输入框的值时 value不会改变,但是,一旦输入框失去焦点,v-model.lazy绑定的值就会改变
<input type="text" v-model.lazy="value"> <div>{{value}}</div> data(){ return{ value:'222' } }
-
trim
- 作用:去掉首位的空格
<input type="text" v-model.trim="value"> <div>{{value}}</div> data(){ return{ value:'222' } }
-
number
- 将值转换成数字类型。注意:先输入数字的话,只取前面数字部分/先输入字母的话,number将会无效
-
stop
- 作用:阻止冒泡
<div @click="clickEvent(2)" style="width:300px;height:100px;background:red"> <button @click.stop="clickEvent(1)">点击</button> </div> methods: { clickEvent(num) { 不加 stop 点击按钮输出 1 2 加了 stop 点击按钮输出 1 console.log(num) } }
-
self
- 作用:只有点击事件绑定的本身才会触发事件
<div @click.self="clickEvent(2)" style="width:300px;height:100px;background:red"> <button @click="clickEvent(1)">点击</button> </div> methods: { clickEvent(num) { 不加 self 点击按钮输出 1 2 加了 self 点击按钮输出 1 点击div才会输出 2 console.log(num) } }
-
once
- 作用:事件只执行一次
<div @click.once="clickEvent(2)" style="width:300px;height:100px;background:red"> <button @click="clickEvent(1)">点击</button> </div> methods: { clickEvent(num) { 不加 once 多次点击按钮输出 1 加了 once 多次点击按钮只会输出一次 1 console.log(num) } }
-
proevent
- 作用: 阻止默认事件
<a href="#" @click.prevent="clickEvent(1)">点我</a> methods: { clickEvent(num) { 不加 prevent 点击a标签 先跳转然后输出 1 加了 prevent 点击a标签 不会跳转只会输出 1 console.log(num) } }
Vuex的原理
含义:vuex是一个专门为vuex.js应用程序开发的状态管理模式,每一个Vuex应用的核心是store(仓库),“store”基本上就是一个容器,它包含着应用中大部分状态。
- Vuex的状态存储是响应式的,当Vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会相应的更新
- 改变store中的状态的唯一途径就是通过Mutation。这样使得我们可以方便地跟踪每一个状态的变化
- 主要包括一下几个模块:
- State:定义了应用状态的数据结构,可以在这里设置默认初始状态
- Mutation: 这是我们修改State中数据状态的唯一路径
- Action:在这里面一些异步操作,然后提交到Mutation中,再由其修改State中的数据
- Getter:允许组件从Store中获取数据,
- Module:允许将单一的Store拆分成多个Store且同时保存在单一的状态树中
VueX的使用
- 安装:npm i vuex --save
- 新建一个store文件夹,写入一下基本代码格式
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state:{
//这里面写 Vue实例里 data数据一样
},
mutations:{
//这里面写修改 state 里面数据的方法
}
})
- 在 入口函数main.js引入Store
原理:
通俗讲:利用了 全局混入Mixin,将我们所创建的store对象,混入到每一个Vue实例中。
vuex数据持久化方法
这个还没写
面试题部分
Vue的优点和缺点
- 优点:
- 渐进式,
- 组件化,可以重复利用
- 简单易学,中文文档,不存在语言障碍,易于学习和理解
- 轻量级,只关注视图层,是一个构建数据的试图集合
- 虚拟dom,
- 双向数据绑定,
- 单页面路由,
- 数据和试图是分开的
- 缺点:首屏加载时间长
Vue的虚拟DOM有什么好处
- 虚拟dom比真实dom体积小,操作是相对来说消耗性能少,如果说在页面上删除某个dom,会引起页面的重绘,影响后边的布局
- 虚拟dom进行频繁的修改,然后一次性笔记并修改真实DOM中需要修改的部分,最后并在真实DOM中进行回流和重绘,减少过多DOM节点的回流和重绘,
- 真实DOM频繁的回流1与重绘会降低效率,
MVVM是什么?
- MVVM的理解:
- MVVM由Model、View、ViewModel三部分构成,Model层代表数据模型,可以在Model中定义数据修改和操作的业务逻辑;View代表UL组件,它负责将数据模型转换成UL展现出来;ViewModel是一个同步View和Model的对象;ViewModel通过双向数据绑定把View层和Model层连接起来,它们之间的同步工作完全是自动的,无需人为干涉,开发者只需要关注业务逻辑,不需要手动操作DOM,不需要关注数据状态的同步问题,数据状态维护完全由MVVM来统一管理
为什么VUE组件中的data必须是一个函数,而Vue实例却可以是个对象
因为组件是用来复用的,且JS里对象是引用关系,如果组件中data是一个对象,那么这样作用域没有隔离,子组件中的data属性值会被互相影响,如果组件中dara选项是一个函数,使用的是data()函数,data()函数中的this指向的是当前实例本身,就不会相互影响了,组件实例之间的data属性值不会互相影响。而new Vue实例 是不会被复用的,因此不存在引用对象的问题
SPA是什么?对它的理解,它的优缺点
- 是什么?
- SPA仅在Web页面初始化时加载相应的HTML、js和CSS
- 一旦页面加载完成,SPA不会因为用户的操作进行页面的重新加载或跳转
- 页面的变化是利用路由机制实现HTML内容的变换,避免了页面的重新加载
- 优点
- 用户体验好,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染
- 相对减轻了服务器的压力
- 缺点
- 初次加载消耗的时间多
- 不能使用浏览器的前进后退功能,由与单页应用在一个页面中显示所有的内容,所有无法前进后退
- 不利于搜索引擎检索:由于所有内容在一个页面替换显示,所有在SEO上有着天然弱势
SPA首屏加载速度慢怎么解决?
首屏时间☞浏览器从响应用户输入网址到首屏内容渲染完成的时间,此时整个网页不一定要全部渲染完成,但需要展示当前视窗需要的内容。
- 加载慢的原因:
- 网络延时问题
- 资源文件体积是否过大
- 资源是否重复发送请求去加载了
- 加载脚本的时候,渲染内容堵塞了
- 常见的几种首屏优化方式:
- 减小入口文件体积
- 路由懒加载,把不同路由对应的组件分割成不同的代码块,待路由请求的时候单独打包路由,使得入口文件变小
- 在配置路由的时候,采用动态路由加载路由的形式
- 静态资源本地缓存
- 合理利用localStorage,把一些静态资源缓存到本地,
- UL框架按需加载
- 图片资源的压缩
- 对页面上使用道德icon,可以使用在线字体图标,图片之类的可以使用雪碧图,将众多小图标合并到同一张图上
Vue数据双向绑定原理
Vue的数据双向绑定是采用数据劫持和发布订阅来实现的。就是对象内部使用object.defineproperty将各个已经存在的属性添加**setter(),getter()**来进行劫持和监听,当监听的对象属性有变化,就拿到最新的值并且通知订阅者,订阅者拿到最新的值来根据指令模板替换需要修改的数据部分,以及绑定相应的更新函数,从而更新视图
为什么只对对象劫持,而数组就直接进行方法重写?
因为对象最多也就几十个属性,拦截起来数量不多,但数组就不同了,数组可能会有几百上千组数据,拦截起来非常消耗性能,所以直接重写数组原型上的方法是比较省性能的方法
描述下vue从初始化页面—修改数据–刷新页面UL的过程
当Vue进入初始化阶段时,一方面Vue会遍历data中的属性,并用Object.defindProperty将它劫持监听成 get/set 的形式,另一方面Vue的指令编辑器 Compiler会对元素节点的各个指令进行解析,初始化视图,并订阅Watcher来更新视图,此时Watcher会将自己添加到消息订阅器Dep中,此时初始化完毕。
当数据发生变化时,触发Observer中的setter方法,立即调用Dep.notify(),Dep这个数组开始遍历所i有的订阅者,并调用其update方法,Vue内部再通过dif算法,patch相应的更新完成对订阅者视图的改变
组件之间的传值方式
- 父组件传值给子组件使用自定义属性,子组件使用props进行接收
// 父组件:
<div :sisui="xfq"></div>
data(){
return{
xfq:{
这里写要传给子组件的数据
}
}
}
// 子组件
<p>{{sisuiL}}</p>
props:{
sisuiL{
default:这里是写一些默认值的
type:这里写传过来的数据类型
requires:这里写 true/false 是用来确定这个是不是必须填的数据
}
}
- 子组件传值给父组件使用自定义事件,子组件使用**$emit + 事件**对父组件进行传值
// 子组件
methods:{
add(){
this.$emit('sisui',要传的数据)
}
}
// 父组件
<div @sisui="getSisui"></div>
methods:{
getSisui(val){
console.log(val)
}
}
- 使用**$refs**获取组件实例,从实例里面获取数据
- 使用Vuex进行状态管理
- 使用全局事件总线eventBus。使用步骤: 创建eventBus.js模块,向外暴露 vue的实例对象。数据发送方通过:bus. e m i t ( ′ 事 件 名 称 ′ , 要 发 送 的 数 据 ) ∗ ∗ 。 数 据 接 收 方 通 过 : ∗ ∗ b u s . emit('事件名称',要发送的数据)**。数据接收方通过:**bus. emit(′事件名称′,要发送的数据)∗∗。数据接收方通过:∗∗bus.on(‘传过来的事件名称’,事件处理函数)
// 1.创建文件(eventBus )里面一个 空的Vue实例 向外共享实例对象
import Vue from 'vue'
export default new Vue() //向外共享实例对象
// 数据发送方
// 导入 空的Vue实例
import bus from '@/eventBus.js'
methods:{
add(){
bus.#emit('sisui',要发送的数据)
}
}
// 数据接收方
// 导入 空的Vue实例
import bus from '@/eventBus.js'
methods:{
add(){
bus.$on('sisui'val=>{
这里要记得使用 箭头函数 哦
接收的数据赋给本组件的某个
})
}
}
为啥不可以修改父组件传递的Prop?要是想要强行修改该咋办
- 为什么不能修改:
- Vue提倡单向数据流,即父级props的更新会流向子组件,但是反过来不行,这是为了防止意外的改变父组件状态,使得应用的数据流变得难以理解。如果这样做了,Vue会在浏览器的控制台中发出警告
- 修改的方法:
- .sync修饰符
路由的两种模式:hash模式和history模式
- has :通过 #号 后面的内容更改触发hashchange事件实现路由切换
- history :通过 pushState 和 replaceState 这两个API可以改变 url,但是不会发送请求
Vue中如何使用自定义组件
-
- 在components目录中新建组件文件
-
- 在需要使用组件的页面中导入
-
- 在需要用组件的页面的compontents属性上注册这个导入的组件
-
- 在template视图上像使用标签一样使用组件
v-if 和 v-show 的区别
- v-if 是通过控制dom元素的删除和生成来实现隐藏的,每一次显示和隐藏都会使组件重新跑一遍生命周期,因此,决定了组件的生成和销毁
- v-show 是通过控制dom元素的css样式来实现显示和隐藏的,不会被销毁
- 频繁或者大数量的显示和隐藏使用v-show,否则就使用v-if
computed 和 watch 有什么区别
computed适合在模板渲染中,某个值是依赖了其它响应式对象甚至是计算属性计算而来,而watch适合监听某个值的变化去完成一段复杂的业务逻辑
- computed 是依赖已有的变量来计算一个木匾变量,大多数情况都是多个变量凑在一起计算出一个变量,并且computed具有缓存,依赖值不变的情况下会之间读取缓存进行复用,但是,computed不能进行异步操作
- watch 是监听某一个变量的变化,并执行相应的回调函数,通常时一个变量的变化决定多个变量的变化,watch可以进行异步操作
computed 和 methods 的区别
- 相同点:
- 如果做为模板的数据显示,二者能实现响应的功能,唯一不同的是methods定义的方法需要执行
- 不同点:
- computed 会基于响应数据缓存, methods 不会缓存
- computed 是属性调用,而 methods 是函数调用
为什么 v-for 和 v-if 不建议在同一标签使用?而我就是想要使用在一起,有什么好方法
当v-for和v-if在同一节点使用时,v-for的优先级比v-if更高,意味着v-if将分别重复运行于每个v-for循环中,如果要遍历的数组很大,而真正要展现的数据很少是,这样会造成很大的性能浪费
<div v-for="item in [1,2,3,4,5,6,7]" v-if="item !== 3">{{item}}</div>
上面的写法会将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)
}
}
不需要响应式的数据应该怎样处理
在开发中有些数据,从始至终都未曾改变过,这种死数据,那也就不需要对其做响应式处理,不然只会做一些无用功消耗,,会消耗大量性能
- 方法一:将数据定义在 data 之外
data(){
//不需要响应式的数据,比如一些表单校验之类的
// 写在外面的话别忘了 this 这个玩意,不然会获取不到的哈
this.数据名 = {数据值}
this.数据名 = {数据值}
this.数据名 = {数据值}
return{
//需要响应式的数据
}
}
- 方法二: 在data里面写 object.freeze()
data(){
return{
数据名:Object.freeze(数据值)
数据名:Object.freeze(数据值)
数据名:Object.freeze(数据值)
数据名:Object.freeze(数据值)
}
}
watch有哪些属性
watch:{
add(){
handler(){
//执行回调
},
deep:true //是否进行深度监听
immediate: true // 是否页面一加载就执行 handler的回调函数
}
}
父组件 和 子组件 生命周期钩子执行顺序是什么
- 渲染过程
父组件 挂载完成一定是在等 子组件 都挂载完成后,才算父组件挂载完成,所以父组件的mounted在子组件moutend之后
顺序:父beforeCreate --> 父created --> 父beforeMount --> 子beforeCreate --> 子created --> 子beforeMounte --> 子mounted -->父mounted - 子组件更新过程:
-
- 影响到父组件:父beforeUpdate -->子beforeUpdate --> 子updated --> 父updated
-
- 不影响父组件:子beforeUpdate --> 子updated
-
- 父组件更新过程
-
- 影响到的子组件:父beforeUpdate -->子beforeUpdate --> 子updated --> 父updated
-
- 不影响道德子组件:父beforeUpdate --> 父updated
-
对象新属性无法更新视图,删除属性无法更新视图,为什么?怎么办?
- 原因:object.defineProperty在对数据进行劫持的时候没有把属性一起劫持
- 解决办法:
- 对象新属性无法更新视图:使用Vue. s e t ( o b j , k e y , v a l u e ) ∗ ∗ , 组 件 中 ∗ ∗ t h i s . set(obj,key,value)**,组件中**this. set(obj,key,value)∗∗,组件中∗∗this.set(obj,key,value)
- 删除属性无法更新视图:使用Vue. d e l e t e ( o b j , k e y ) ∗ ∗ , 组 件 中 ∗ ∗ t h i s . delete(obj,key)**,组件中**this. delete(obj,key)∗∗,组件中∗∗this.delete(obj,key)
自定义指令
通过 vue.directive()方法注册组件
指令定义函数提供了几个钩子函数:
- bind: 只调用一次,指令第一次绑定到元素时调用,(这个时候还没插入dom),在这里可以进行初始化设置(一般进行样式之类的操作)
- inserted:被绑定元素插入父节点时调用(一般进行js相关的操作)
- update所在组件Vnode更新时调用,但是可能发生在其子元素的Vnode更新之前
- computedUpdate:所在组件的Vnode及其子元素的Vnode全部更新之前
- unbind:只调用一次,指令与元素解绑的时候调用
插槽的使用以及原理
插槽(Slot)是Vue为组件的封装者提供的能力,允许开发者在封装组件的时候,把不确定的、希望由用户指定的部分定义为插槽
- 默认插槽
- 没有设置 name属性,默认使用 default
- 具名插槽
- 设置了一个name属性
- 作用域插槽
- 在组件上的属性,可以在组件元素内使用
说说nexTick的用处
在下次DOM更新循环结束之后执行延迟回调(相当于原生js的setTimeout延时定时器方法)。在修改数据后立即调用这个方法,获取更新后的DON。
获取更新后的DOM言外之意是指什么操作需要用到更新后的DOM而不能使用更新前的DOM会出问题,
什么时候需要用到**nextTick()
- 由于created()钩子函数执行的时候,DOM其实还没有进行任何渲染,生命周期钩子还没走到mounted 钩子, **而偏偏此时你需要对 DOM 进行操作,此时就可以用*nextTick()*来执行,这样回调函数在 DOM 更新完成后就会调用
route 和 router 的区别
- router是Router的实例,相当于一个全局的路由器对象,里面包含很多的属性和子对象,经常用来跳转链接之类的
- route相当于当前正在跳转的路由对象,可以从里面获取到路由的基本信息,携带的参数等
动态路由
路径参数使用“:”标记,冒号后面填我们传入的参数,可以通过this.$router.params查看得到路由参数数据。
嵌套路由
一层路由里面套着子路由,子路由通过children来包含着
const router = new VueRouter({
routes:[
{
path:'路径',
component:()=>import('文件路径'),
children:[
{
// 在这写子路由
}
]
}
]
})
路由重定向
通过redirect来对页面进行重新定向(比如一个组件有两个子路由页面,当我们进入这个组件页面的时候,通过redirect来设置一个默认展示路由页面)
路由的几种钩子函数
- 前置路由守卫
- **beforeEach(to,from,next)**在路由跳转的时候触发,
router.beforeEach(to,from,next) =>{ // to 代表要去的路由 // from 代表从哪个路径跳转过来的,相当于上一个路由 // next 守卫通过next控制下一步的跳转 }
- 后置路由守卫
- 路由独享的导航钩子
- 组件内守卫钩子
项目优化性能系列
代码层面的优化
- 1.1 v-if 和 v-show 区分使用场景
- 1.2 computed 和 watch 区分使用场景
- 1.3 v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
- 1.4 图片资源懒加载
- 做法:通过 vue-lazyload插件
// 第一步 安装插件 npm install vue-lazyload --save-dev // 第二步 在入口文件 man.js 中引入并使用 import VueLazyload from 'vue-lazyload' Vue.use(VueLazyload) // 第三步 在vue文件中将 img 标签的 src 属性直接改为 v-lazy ,从而将图片显示方式更改为懒加载方式显示 <img v-lazy="图片路径">
- 1.5 路由懒加载
- 1.6 第三方插件的按需导入
Vue项目如何解决兼容性问题
- 安装 babel-polyfill 模块
- 在 main.js中引入
- 在 webpack.base.comf.js 中配置
- 在index.html中强制浏览器默认渲染最高版本内核
// 1. 安装 babel-polyfill 模块
npm install -- save babel-polyfill
// 2. 在 main.js中引入
import 'babel-polyfill'
// 3. 在 webpack.base.comf.js 中配置
module.exports = {
....
entry:{
app:["babel-polyfill","./src/main.js"]
}
....
}
// 4. 在index.html中强制浏览器默认渲染最高版本内核
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
导入
Vue项目如何解决兼容性问题
- 安装 babel-polyfill 模块
- 在 main.js中引入
- 在 webpack.base.comf.js 中配置
- 在index.html中强制浏览器默认渲染最高版本内核
// 1. 安装 babel-polyfill 模块
npm install -- save babel-polyfill
// 2. 在 main.js中引入
import 'babel-polyfill'
// 3. 在 webpack.base.comf.js 中配置
module.exports = {
....
entry:{
app:["babel-polyfill","./src/main.js"]
}
....
}
// 4. 在index.html中强制浏览器默认渲染最高版本内核
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">