vue的基础知识
-
v-model作用
v-model本质:语法糖,可以使用v-model指令在表单元及元素上创建数据的双向绑定。
-
vue2.0双向绑定缺陷
vue2.0的数据响应采用数据劫持结合发布者-订阅者模式的方式,通过object.defineProperty()来劫持各个属性的setter、getter。 1、vue实例创建后,无法检测到对象属性的新增或者删除,仅可追踪到数据是否被修改。 解决:Vue.set(obj, propertName/index, value) 2、无法监听数组的变化,数组push、pop、shift、unshift、splice、sort、reverse方法可以监听。
-
vue3.0数据双向绑定
vue3.0通过proxy实现数据双向绑定。proxy为es6新增的特性(代理),proxy可以让我们能够以简洁易懂的方式控制外部对象的访问。proxy可理解为,在目标对象之前架设一层拦截,外界对对象进行访问,必须通过这层拦截,因此提供机制,可以对外界访问进行过来和改写。
-
vuex
专门为vuejs应用程序设计的状态管理工具,采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生改变。
router
- 全局路由守卫beforeEach(前置)、afterEach(后置)
import router from './router';
router.beforeEach((to, from, next)=>{
// if未登录todo
// next({path:"/login"})
// 已登录
// next()
});
router.afterEach((to, from)=>{
// todo
})
- 路由守卫
import Login from "./Login.vue"
const router = new VueRouter({
routes:[
{
path:"/login",
component:Login,
beforeEnter:(to, from, next)=>{
// todo
}
}
]
})
- 组件内守卫(与周期函数同级)
beforeRouteEnter(to, from, next){
// 渲染组件的对应路由被confirm前调用
// 不可以获取组件实例this
// 守卫执行时,组件并未创建
}
beforeRouteUpdate(to, from, next){
// 当前路由改变,该组件被重复调用
// 可以访问组件实例this
}
beforeRouteLeave(to, from, next){
// 导航离开组件对应的路由调用
// 可以访问组件实例this
}
路由懒加载
懒加载:打包构建应用,JavaScript包非常大,影响页面加载。把不同路由对应组件分割成不同的代码快,路由被访问时才载入对应组件。(提供效率)
const Home = ()=>import("./Home.vue")
const router = new VueRouter({
routes:[
{path:"/home", component:Home}
]
})
HashRouter 和 HistoryRouter 的区别和原理
-
HashRouter原理
hash模式的工作原理是hashChange事件,可在window监听hash的变化。在url后面添加#xxx触发事件。vue-router默认是hash模式-使用url的hash来模拟一个完整的url,当url改变的时候,页面不会重新载入,也就是单页面应用,当#后面的hash变化,不会导致浏览器向服务器发出请求,因此不刷新页面,并且会触发hashChange事件,通过监听hash值的改变实现更新页面部分内容的操作。
-
HistorRouter原理
主要使用html5的pushState()和replaceState()两个api结合window.popstate事件(监听浏览器前进后退)来实现。pushState可以改变url地址不会发送请求,replaceState可以读取历史记录栈,可以对浏览器记录进行修改。
-
区别
1、pushState设置新url可以与当前url一样,也会添加到记录栈,而hash设置的新值必须与原来的不一样才会添加到记录栈。 2、pushState通过stateObject可添加任意类型的数据到记录中,而hash仅可以添加短字符串。 3、pushState可额外设置title属性供后续使用。 4、hash兼容IE8以上,history兼容IE10以上。 5、history模式需要后端配合将所有访问指向index.html,否则用户刷新页面,会导致404错误。
// hash 路由原理
// 监听hashChange方法
window.addEventListener("hashChange",()=>{
div.innerHTML = location.hash.slice(1)
})
// history 路由原理
// 利用html5的history的pushState方法结合window.popstate事件(监听浏览器前进后退)
function routerChange(pathName){
history.pushState(null, null, pathName)
div.innerHTML = location.pathname
}
window.addEventListener("popstate", ()=>{
div.innerHTML = location.pathname
})
vue函数生命周期
-
beforeCreate
vue对象属性未绑定,如data属性,computed属性。
-
挂载数据(属性赋值)
包括data属性与computed的运算。
-
create函数
vue对象属性已绑定,DOM未生成,$el属性未存在。
-
beforeMount函数
模板编译(template)、数据挂载之前执行的函数。this.$el存在值,但数据未挂载到页面。
-
模板编译
使用vue对象的数据属性替换模板中页面内容。
-
mounted函数
模板编译完成,数据挂载完毕。
-
beforeUpdated函数
组件更新之前执行函数,数据更新后,调用beforeUpdated,注:更新数据需要是模板上出现的数据,否则不会触发。
-
updated函数
组件更新之后执行函数,vue对象对应的dom的内部(innerHTML)改变。
-
activated函数(keep-alive组件激活时调用)
-
deactivated函数(keep-alive组件停用时调用)
-
beforeDestroy (组件销毁之前)
-
destroyed(组件销毁之后)
keep-alive组件(实现组件缓存,vue.js的内置组件)
-
作用
1、能够将不活跃的组件示例保存在内存中,而不是直接销毁; 2、抽象组件,不会被渲染到真实dom中,也不会出现在父组件链。
-
使用方法
1、常用属性include/exclude,允许组件有条件的进行缓存; 2、两个生命周期activated/ deactivated,判断组件是否处于活跃状态; 3、运用LRU算法
-
原理
vue的缓存机制并不是直接存储dom结构,而是dom节点抽象成一个个VNode节点,因此keep-alive的缓存也是基于VNode节点的而不是直接存储dom结构。 将需要缓存的VNode节点保存在this.cache中,在render时,如果VNode的name符合在缓存条件,则会从this.cache中取出之前缓存的VNode实例进行渲染。
vue中的data为何可以是函数
JavaScript函数可以构成作用域,data是函数时,每个组件实例都有自己的作用域,每个实例相互独立,相互不会影响。
vue中$nextTick作用与原理
作用:可以获取更新后的DOM
由于vue DOM更新是异步执行,即修改数据,不会立即更新视图,而是监听数据变化,并缓存在同一事件中,等同一数据循环中的所有数据变化完成后,统一更新视图。确保获取更新后的DOM,因此设置了vue.nextTick().
原理:在下次DOM更新循环结束之后执行延迟回调,nextTick使用宏任务和微任务。根据执行环境分别尝试采用Promise、mutationObserver、setImmediate(若以上均不行,则采用setTimeout。
vue首屏白屏解决
1、路由懒加载
2、vue-cli开启打包压缩与后退配合gzip访问;
3、进行cdn加速;
4、开启vue服务渲染模式;
5、使用webpack的externals属性将不需要打包的库文件分离出去,减少打包后文件的大小;
6、生产环境中删除不必要的console.log;
7、添加loading效果,给予一种过渡效果。
// 添加删除 console.log
plugins:[
new webpack.optimize.UglifyJsPlugin({
compress:{
warnings:false,
drop_debugger:true,
drop_console:true
},
sourceMap:true
})
]
单页面优缺点
优点:体验好,快,内容的改变不需要重新加载整个页面,对服务器压力较小;前后端分离;页面效果可以比较酷炫。
缺点:不利于SEO;导航不可用,如果一定要导航需要自行实现前进、后退;初次加载耗时多;页面复杂度更高。
路由跳转与location.href区别
location.href简单方便,刷新页面;
路由跳转方式,无刷新页面,静态跳转。
<!-- 路由跳转 -->
<router-link :to="{name:'home'}">
<router-link :to="{path:'/home'}">
<!-- param参数 -->
<router-link :to="{name:'home', params:{id:0}}">
<router-link :to="{path:'/home:id', params:{id:0}}">
<!-- query参数 -->
<router-link :to="{name:'home', query:{id:0}}">
<router-link :to="{path:'/home', query:{id:0}}">
// 不带参数
this.$router.push("/home")
this.$router.push({name:"home"})
this.$router.push({path:"/home"})
// param参数 (name可用,path不可用)
this.$router.push({name:"home", params:{id:0}})
// 路由配置 path:"/home:id", 不配置path,刷新页面params参数消失
// query参数
this.$router.push({name:'home', query:{id:0}})
this.$router.push({path:'/home', query:{id:0}})
// this.$router.replace() 用法同push
// this.$router.go(n) 正数往前,负数往后
delete 与 vue.delete
delete删除数组的值,依然会占用内存中位置;
vue.delete已删除数组中的内存占位。
let arr = [1,2,3]
let arrV = [1,2,3]
delete arr[1]
this.$delete(arrV,2)
console.log(arr) //【1, empty, 3】
console.log(arrV) //【1,2】
vuex是什么,属性?
- 关键:state、mutations、getters、actions、module、store.commit、store.dispatch
- 解析:vuex集中管理项目的公共数据。
- state-存储公共管理的数据;
- mutations-定义改变state中数据的方法(不建议异步);
- getters-定义store的计算属性;
- getters的返回值根据所依赖的数据进行缓存,依赖值发生改变触发重新计算;
- action-提交mutation,而不是直接变更状态;
- module-将store分割成模块。
- 注释:mutations:通过store.commit调用;action:通过store.dispatch触发;getters:通过store.getters调用。
import Vue from 'vue'
import Vuex from 'vuex'
// 安装插件
Vue.use(Vuex)
// 创建对象
const store = new Vuex.Store({
state:{
name:'xzp'
},
mutations:{
updateState(state, payload){
state.name = payload
}
},
actions:{
asyncUpdate(context, payload){
return new Promise(()=>{
setTimeout(()=>{
context.commit('updateState')
})
})
}
},
getters:{},
modules:{}
})
// 暴露对象
export default store
Vue循环key
- 关键:性能优化、diff算法节点比对
- 解析:生成虚拟dom,更新dom时,使用diff算法对节点进行比对,举例:li元素需要插入li,若li是上没有key,将全部渲染一遍,若存在key,则比对li元素的key,创建新的li元素,插入,不对其他元素进行修改和渲染。存在问题:下标做key,可能会导致无效刷新,影响效率。操作数组靠前的元素,unshift元素。
- diff算法:虚拟dom即以对象的形式来模拟dom的树形结构,若数据发生改变,生成新的虚拟dom,同层级比对虚拟dom,若存在不一致,则重新渲染对应真实dom以及该dom的子元素。两端比较,比较头尾的key是否相同,再决定新增或删除元素。
vue2.0双向绑定的原理与缺陷
- 关键:Object.defineProperty、getter、setter
- 解析
- 响应式:组件的data发生改变,立即触发视图更新
- 原理:Vue采用数据劫持结合发布者-订阅者模式来实现数据响应式,通过Object.defineProperty劫持数据的setter与getter,数据发生改变时,发布消息给订阅者,订阅者受到消息后进行相应的处理。通过原生js提供的监听数据的API,数据发生改变时,在回调函数中修改dom。
- 核心API:Object.defineProperty,定义对象属性;特点:默认情况下定义数据属性不能修改,描述属性与存取属性不能同时使用
- 响应式原理:获取属性值触发getter方法,设置属性值触发setter方法,在setter方法中调用修改dom的方法。
- 注释:缺点
- 单次递归开销巨大,数据量过大,大量递归导致调用栈溢出
- 不能监听对象的新增属性和删除属性
- 无法正确监听数组的方法,不能通过监听数组下标来监听数据变化
vue.$set(data, key, value) // 新增属性
vue.$delete(data, key) // 删除属性
computed与watch
- 关键:computed存在缓存、触发条件依赖值的改变、watch无缓存、支持异步、监听数据变化
- 解析:computed缓存-多次使用到computed计算的属性,只执行一次,除非数据发生变化;不支持异步。
- 解析:watch不支持缓存,数据变化,即触发相应的操作;支持异步监听;接收两个参数,第一个参数:新的值,第二个参数:变化之前的值;immediate:立即触发回调函数;deep:深度监听,数据内部变化,复杂数据类型中使用,例如数组中的对象发生改变(无法监听到数组和对象内部的变化)。
vue -> $nextTick作用与原理
- 关键:异步渲染、获取dom、Promise
- 注解:vue更新dom为异步行为,修改数据后,视图不会立马更新,而是等同一件事循环中所有数据变化完成后,统一更新视图。
- nextTick作用:该方法会在当前渲染完成后执行,解决异步渲染获取不到更新后dom的问题。原理:本质是返回一个Promise,vue异步执行dom更新,数据改变,vue开启队列,同一个事件循环中观察到数据变化的watcher推送进队列。若watcher多次触发,仅会被推送到队列一次。这种缓冲行为可有限去掉重复数据改变造成的不必要计算和dom操作。
vue组件通信方式
- 关键:父子通信、自定义属性、props、$emit、eventBus、$on、vuex。
- 解析
- props/$emit(父子组件通信方式)
- ref/$refs
this.$emit('xxx', 1,2,3); this.$refs.child.xzp();
- eventBus事件总线($emit/$on)
// 入口文件 export const EventBus = new Vue(); // 发送 EventBus.$emit("xxxzzzppp",{a:1}) // 接收 EventBus.$on("xxxzzzppp",(data)=>{ console.log(data.a) // 1 })
- 依赖注入(provide/inject)
// 提供provide provide(){ return { name:"xzp", age:"22" } } // 接收(后代组件) inject:["name", "age"]
- $parent/$children
this.$parent.msg; this.$children[0].msg;
- $attrs/$listeners