一、Vue知识点
1、基本使用
1.1 数据驱动
理念:当数据发生变化的时候,用户界面也会跟随变化,不用去手动修改dom
1.2 MVVM框架
MVVM框架主要包括:是一个架构设计模式,由Model、View、ViewModel三部分组成
Model:数据部分
View:视图部分
ViewModel:中间桥梁,连接View和Model(vue的实例)
作用:实现了数据与视图的分离功能,当数据Model发生改变,ViewModel会监听到变化并通知View也做出改变,当View视图的事件触发,ViewModel也能事件,让Model做出响应。
难点:MVVM的三要素——响应式、模板、渲染
2、模板语法
2.1 Class与Style动态绑定
Class:
Class对象语法
<div :class="{active: true}"></div> 拥有active这个类名
<div :class="{active: isActive===0}"></div>
后面的判断为true就拥有active类名
Class 数组语法
<div :class="[active?activeClass:'']"></div>
active为true就拥有activeClass类名,否则为空
Style:
Style对象语法
<div :style="{fontsize: ph+'px'}"></div>
<div :style="{color: active===item?'red':'pink'}"></div>
Style数组语法
<div :style="[styleColor,styleSize]"></div>
data() {
styleColor: {
color: 'red'
},
styleSize: {
font-size: '16px'
}
}
Style 对象语法::style="{ color: 'red', fontsize: ph + 'px' }"
变化: :class="{ color: select===item?'red':'pink'}"
判断select是否等于item,正red假pink
3、条件渲染v-if与v-show
v-if是真正的条件渲染,控制元素是否存在,如果为false,则元素是不存在的。
v-show是通过css属性display来控制显示与隐藏。
如果需要频繁控制元素显示与隐藏,使用v-show避免大量DOM操作,提高性能;某元素满足条件,变化比较少使用v-if。
4、计算属性computed
computed是一个对象,里面可以定义函数,但这个函数可以当作属性来直接使用。
优点:避免模板中出现过多的逻辑;具有缓存功能,不像methods每一次都会执行,他会调用缓存的数据,计算属性所依赖的数据发生变化,才会重新取值。
<div>{{fullName}}</div>
computed: {
fullName() {
return this.firstname+this.lastname
}
}
5、侦听器watch
侦听数据发生变化,如果数据发生变化,就执行相应的操作。允许在watch中处理异步操作
处理场景:异步操作或者开销较大的操作,可以开启深度监听,监听对象的属性,deep:true,但是不能获取到old value
watch与computed差异:
-
watch:适合一个值发生变化,对应要做一些其他事情,处理异步操作,一个值影响多个值
computed:由其依赖的值而来,依赖值发生变化,对应的也发生变化,适合多个值影响一个值
-
computed计算属性有缓存功能,能使用计算属性尽量使用这个
-
watch适合异步操作或者开销比较大的操作
watch: {
users: {
//立即执行, 如果没有这个第一次渲染的时候不会执行,添加的话初始化也会执行侦听器
immediate: true,
handler(newValue, oldValue) {
this.totalcount = newValue.length + '人数'
}
}
}
watch: {
// watch监听uname的变化,同时也改变了message的值,也可以发送其他的异步请求
uname(value) {
this.checkUserName(value)
this.message='在异步请求中' // 做了一些其他的异步操作
}
}
computed: {
fullName() {
return this.firstname+this.lastname // 2个值去得到1个值 一定要return
}
}
5.1、事件及参数
5.1.1、event参数
event是原生的、事件被挂载到当前元素
<button @click="add">+1</button>
<button @click="down(2, $event)">+2</button>
add(event){
console.log(event)// 不传参、输出原生event
}
down(val, event){ // 需要定义形参来接收
console.log(event) // 传参输出event
}
6、生命周期
6.1 三大阶段
- 挂载阶段(初始化相关属性,watch、methods):beforeCreate、created、beforeMount、mounted
- 更新阶段(元素或组件的变更操作):beforeUpdate、updated
- 销毁阶段(销毁相关属性):beforeDestroy、destroyed
beforeCreate():创建前,组件实例还没有创建
created():创建后,组件初始化完成,各种数据可以使用,可以使用ajax发送异步请求获取数据
beforeMount():挂载前,未执行渲染,更新,虚拟DOM完成,真实DOM未创建
mounted():挂载后,真实DOM创建完毕,可以访问dom元素发送异步请求获取数据
beforeUpdate():更新前,用于获取更新前各种数据状态
updated():更新后,所有数据完成更新
beforeDestroy():销毁前,用于处理一些定时器的清除
destroyed():销毁后,子实例被销毁,所有事件监听移除
7、组件化开发
7.1、组件描述
将一个页面拆分成多个组件,每一个组件都具有不同的功能。
特点:可复用性,可维护,可组合
7.2、基本使用
Vue.component('componentActive', {template: "<p>我的世界</p>"})
// 在调用时候,遇到驼峰命名的需要使用-来进行拼接
<component-active></component-active>
8、插槽的使用
8.1、基本插槽
在页面中使用组件,组件中是不能存在内容的,如果存在着即为插槽的内容
<div id="app">
<alert-box>我插入在slot位置</alert-box>
</div>
Vue.component("alert-box", {
template: `
<div>
<h1>我的世界</h1>
<slot>默认内容</slot> // 上面的组件使用若没有内容则使用默认内容
</div>
`
})
将子组件标签之内的内容传到对应的slot,然后把整体的子组件传过来
8.2、具名插槽
会查找对应name的插槽填充内容,没有name就不填充
<div slot="header">我的插入slot位置</div>
<slot name="header">插槽</slot>
注意:我们给template标签添加插槽名称,在其标签中嵌入多个其他标签,从而完成布局
<template slot="header">
<div>我是插入的内容一</div>
<div>我是插入的内容二</div>
</template>
8.3、作用域插槽
将子组件的数据暴露出去,为父组件根据数据指定不同的模板,更加灵活
render 渲染函数
Vue.set .set
Vue.delete .delete
$on $emit
$once $off
r e f s v m . refs vm. refsvm.refs ref
带参数的过滤器 祁东县
混入mixin
二、Vue-Router基础
1、路由简介
- 后端路由
- 概念:根据不同用户URL请求,返回不同的内容
- 本质:URL请求地址与服务器资源之间的对应关系
- SPA
- Ajax前端渲染,前端渲染提高性能
- SPA单页面应用程序,整个网页只有一个页面,通过AJAx实现局部更新
- 核心技术点:前端路由
- 原理之一:基于URL地址的hash
- 前端路由
- 概念:根据不同的触发用户事件,显示不同的内容
- 本质:用户事件与事件处理函数之间的对应关系
2、编程式导航
-
声明式导航:通过点击链接实现导航的方式,
-
编程式导航:通过调用API实现,
this.$router.push('/login')
// 字符串(路径名称)router.push('/home')router.push({ path: '/home' }) // 对象router.push({ name: '/user', params: {userId: 123}}) // 命名的路由(传递参数)router.push({path: '/register', query:{uname: 'lisi'}}) // 带参数查询 /register?uname=lisi
3、路由守卫
1、全局前置守卫
router.beforeEach((to, from, next) => {})
to:去哪个页面 from:来自哪个页面 next:继续执行
router.beforeEach
((to,from,next) =>
{if(to.path == '/login' || to.path == '/register')
{next()}
else {alert('请先登录,未登录') next('/login')}})
全局后置守卫(一般不提这个,全局守卫指前置)
router.afterEach((to,from) => {})
全局后置守卫不会接受next函数,也不会改变导航本身
2、独享守卫
beforeEnter(to, from, next) {} 只对添加的该路由起作用
const router = new VueRouter({ routes: [{ path: '/foo', component: Foo, beforeEnter: (to, from, next) => { if(window.isLogin) { next() } else { next('/login?redirect=' + to.fullPath ) } } }]})
3、组件内的守卫
//beforeRouteEnter 在渲染组件前调用,不能获取this,组件实例未被挂载
beforeRouteEnter(to, from, next) {if(window.isLogin) {next()}else {next('/login?redirect=' + to.fullPath)}}
// beforeRouteUpdata 在当前路由改变,但当前组件被复用时调用,可访问实例
thisbeforeRouteUpdate(to, from, next) {next()}
// beforeRouteLeave 离开组件对应的路由,被调用
beforeRouteLeave(to, from, next) {next()}
4、动态添加路由
语法:this.$router.addRoutes([])
5、路由组件缓存——keep-alive
<keep-alive include="goods">
// 只对goods这个组件起路由缓存作用,需要在组件添加name属性,这样找到更快
<router-view></router-view></keep-alive>
// keep-alive有2个钩子函数,activated, deactivated从其他页面进入keep-alive的缓存页面,会触发activated钩子函数,
//可以进行更新操作从缓存页面离开,会触发deactivated在缓存页面时候,点击刷新会触发:created -- mounted -- activated
6、Hash模式与History模式
-
前端路由中,路径发生变‘化是不会向服务器发生请求的,只有用到ajax才会向服务器发送请求
-
Hash模式
https://www.baidu.com/#/showlist?id=123
hash模式中路径带有#,#后面的内容作为路由地址,可以通过?携带参数
-
History模式
https://www.baidu.com/showlist/123
history模式是 一个正常的路径模式,需要服务端相应支持
-
Hash与History区别
Hash模式:基于锚点,以及onhashchange事件,通过锚点的值作为路由地址,地址发生变化触发onhashchange事件
History模式:基于HTML5中的History API,history.pushState()与history.replaceState()
7、History模式的使用
const router = new VueRouter({mode: "history",
// 默认使用hash
routes: [{path: '/login', component: Login}]})
8、Nginx服务器
- 代理服务器:局域网内部的机器通过代理服务器发送请求到互联网上的服务器。
- 反向代理服务器:在服务器端接收到客服端的请求,然后把请求分发给各个具体 的服务器进行处理,然后再讲结果反馈给客户端。
- 当单击浏览器中的刷新按钮后,会向服务器发送请求,服务器接收请求后,发现没有找到访问的文件,由于配置了一些 try_files ,所有会将html目录下面的index.html页面的内容返回给浏览器,浏览器根据路由来处理,查找相对于组件进行渲染。
三、Vue-Router原理
1、分析Hash与History工作原理
- Hash:URL中#后面作为路径地址,当地址改变并不会向服务器发送请求,而是触发
hashchange
事件,记录下当前路由地址,根据当前路由地址找到对应组件,重新渲染 - History:通过history.pushState()方法改变地址栏,并且当前地址记录到浏览器历史记录中,并不会向服务器发送请求,监听popstate事件,发现浏览器历史操作的变化,记录改变后的地址,根据路由地址找到对应组件渲染。
模式基本类似:将window.location.hash()直接进行赋值window.location.replate()改为调用了window.location.history.pushState()和window.location.replateState()方法,而History中添加对修改浏览器地址栏的监听popstate是在构造函数进行的。
2、路由配置(动态路由、懒加载)
routes: [ {path: '/user/:id', components: User, redirect: '/index'}, {path: '/feedback', component: () => import('../components/Feedback')}]
四、Vue响应式原理
1、数据驱动
MVVM
2、响应式的核心原理
Vue2.x
监听对象,监听数组、复杂对象,深度监听、缺点
缺点:1:深度监听,需要递归到底,一次性计算量大
2:无法监听新增属性/删除属性(Vue.set/Vue.delete API去做事)
3:无法原生监听数组,需要特殊处理
-
defineProperty
// Vue2.x是通过Object.defineProperty数据劫持来改变 单个属性let data={ msg: 'hello' } // data数据let vm={} // vue实例// 数据劫持Object.defineProperty(vm,'msg',{ enumerable: true, configurable: true, get(){ // 获取 return data.msg }, set(newValue){ if(newValue===data.msg){ return } data.msg=newValue document.querySelector('#app').textContent=data.msg }})vm.msg='abc'console.log(vm.msg)
-
defineProperty 监听的是对象中的属性
let data={msg: 'hello', count: 10} // (多个属性)let vm={} // 实例proxyData(data) // 调用function.proxyData(data) { Object.keys(data).forEach(key => { // Object.keys()将对象转为数组,进行循环遍历 Object.defineProperty(vm, key, { enumerable: true, configurable: true, get() { console.log('get', key, data[key]) // 读取数据方式不一样 return data[key] }, set(newValue) { console.log('set', key, newValue) if(newValue===data[key]) { return } data[key] = newValue document.querySelect('#app').textContent=data[key] } }) })}vm.msg='hello word' // 先触发setconsole.log(vm.msg) // 后触发get
Vue3.x
// Vue3.x是通过Proxylet data = { msg: 'hello', count: 0}let vm = new Proxy(data, { get(target, key) { console.log('get key', key, target[key]) return target[key] } set(target, key, newValye) { console.log('set key', key, newValue) if(target[key] === newValue) { return } target[key]=newValue document.querySelector('#app').textContent = target[key] }})vm.msg = 'abc'console.log(vm.msg)
3、发布订阅者模式
// 订阅者、发布者、信号中心let eventHub = new Vue()addTodo:function() { eventHub.$emit('add-todo') // 发布}create:function() { eventHub.$on('add-todo',) // 订阅}
4、组件化
5、vdom和diff
- vdom-用 js 模拟DOM结构,计算出最小变量,操作DOM
- diff算法是vdom的最核心、最关键的部分
- diff算法能在日常使用,比如key
6、模板编译
7、渲染过程
8、前端路由
五、vue面试基础知识
1、computed和watch
- computed有缓存,所依赖的data没有发生变化,不会重新计算
- watch监听引用类型(对象),获取不到oldValue,已经指向newValue
- watch第一次加载不会更新,需要设置属性
immediate:true
2、v-for
-
v-for与v-if不能一起使用,v-for的优先级高于v-if,会先执行循环才会去判断,不合理
<li v-if="flag" v-for="item in lists" :keys="lists.id"></li>
3、父子组件通信-兄弟组件通信
父子通信:props– e m i t 调 用 父 组 件 ‘ t h i s . emit 调用父组件`this. emit调用父组件‘this.emit`
兄弟通信: o n 定 义 / on定义/ on定义/emit调用 定义
event.$on('onAdd', this.addtitle)
调用
event.$emit('onAdd', this.title)
addtitle(title){}
在beforeDestroy里及时销毁自定义事件
event.$off('onAdd',this.addtitle)
// event 是一个new Vue实例import Vue from 'vue'export defalute new Vue{}import event from './event' // 引入
4、声明周期
5、$nextTick Vue是异步渲染
Vue是异步渲染的,data改变之后,DOM节点不会立刻去渲染,$nextTick会在DOM渲染之后触发,以获取最新的DOM节点
addItem(){ this.list.push(data.name) thislist.push(data.name) // const ulElem = this.$refs.ul1 // console.log(ulElem.childNodes.length) // Vue是异步渲染的,data发生变化,DOM不会立刻渲染,需要使用$nextTick(),它会等DOM渲染完成再进行回调 this.$nextTick(() => { const ulElem = this.$refs.ul1 console.log(ulElem.childNodes.length) })}
2、页面渲染的时候会将data做一些整合处理,将多出的data修改进行一次渲染
6、自定义v-model数据双向绑定
v-model本质是一个v-bind和v-on语法糖,value+input方法的语法糖,通过model属性的
props
+event
属性来进行自定义的
<input type="text" :value="text1" @input="$emit('change1', $event.target.value)">export default{ model: { props: 'text1', event: 'change1' }, props: { text1: String, default() {return ''} }}
7、slot
-
基本使用 :
父组件使用子组件,子组件标签之中通常不给内容,给的话会代替子组件里面的slot标签内容,不给就会默认使用子组件slot标签内容
// father.vue<son> {{user.name}} </son>// son.vue<template> <a> <slot>默认内容</slot> </a></template>
-
作用域插槽:
作用域插槽:父组件可以使用子组件中定义的data数据,通过在子组件中v-bind定义数据,在父组件中v-slot定义使用名称接收,引用时:
slotFather.slotname.name
// father.vue<son> <template v-slot="slotFather"> {{slotFather.slotname.name}}// jack </template></son>// son.vue<template> <a> <slot :slotname="user">默认内容</slot> </a></template>data(){ return{ user:{name: 'jack', age: 18} }}
-
具名插槽:
具名一样的才会互相插槽
8、动态组件(少)
:is="val.type"
:用来渲染动态组件
<div v-for="(val,key) in list"> <component :is="val.type"></div>data(){ return{ list:{1:{type:'xx'}, 2:{type:'jj'}} }}
9、异步组件(多) 我感觉是懒加载指的路由
import()函数
按需加载,异步加载大组件 什么时候用什么时候加载
// 正常引入import NextTick from './NextTick.vue'// 正常组件引入components: { NextTick, slotDemo}// 使用import引入NextTick: () => import('../NectTick.vue')
10、keep-alive
- 缓存组件(商品详情页、商品列表页)(tab切换可以使用keep-alive )
- 频繁切换,不需要重复渲染(频繁切换会渲染销毁,浪费资源),可以使用keep-alive包裹
- Vue性能优化
11、mixin
- 多个组件有相同的逻辑,抽离出来
- mixin并不是完美的解决方法,存在问题
- 变量来源不明确,不利于阅读
- 命名容易重复冲突
Vue通信方式
-
props/$emit
父组件A通过props方式向子组件B传值,B通过$emit方式向A传值
-
e m i t / emit/ emit/on
触发事件、监听事件
var Event = new Vue()Event.$emit(事件名, 数据)Event.$on(事件名, data => {})
-
Vuex
是vue的状态管理器,存储的数据是响应式的,并不会保存,需要与localStorage配合
-
a t t r s / attrs/ attrs/listenners
多级组件嵌套传递数据通常使用vuex,但是仅仅传递数据,不做中间处理,vuex有点大材小用,可以使用——
$attrs/$listeners
。a t t r s : 包 含 父 作 用 域 中 不 被 p r o p 所 识 别 的 特 性 绑 定 , 通 过 v − b i n d = “ attrs:包含父作用域中不被prop所识别的特性绑定,通过v-bind=“ attrs:包含父作用域中不被prop所识别的特性绑定,通过v−bind=“attrs”传入内部组件
l i s t e n e r s : 包 含 了 父 作 用 域 中 的 v − o n 事 件 监 听 器 , 通 过 v − o n = “ listeners:包含了父作用域中的v-on 事件监听器,通过v-on=“ listeners:包含了父作用域中的v−on事件监听器,通过v−on=“listeners”
-
provide/inject
祖先组件通过provide来提供变量,子组件通过inject来注入变量,provide/inject主要解决了跨域组件间的通信问题
A.vue通过provide:name,将name变量提供给它的所有子组件,B.vue通过inject注入name变量,就可以通过this.name访问变量,但是并不是响应的,A.vue中的值改变,B中的不会变化
-
p a r e n t / parent/ parent/children与ref
ref: 如果在普通的DOM元素上使用,引用指向就是DOM元素,在子组件上引用就是指向组件实例
$parent / $children:访问父子实例
无法跨级或在兄弟间
一、微信小程序
1、基础语法使用
- 在data中声明之后,页面中使用都需要{{}} mastauch语法,与vue不同,:src=“data” sec="{{data}}"
路由原理
路由怎么去设置的
子路由、路由懒加载 路由守卫
路由模式怎么设定的
哈希模式和history 的区别
什么时候用哈希什么时候用history
computed和watch 的区别
es6语法:update: function() {} === update() { }