Vue
1. v-model
v-model是一个语法糖, 实现在表单及元素上创建双向数据绑定, 真正靠的是:
1. v-bind绑定响应式数据
2. 触发@input事件并传递数据
2. 双向绑定原理
发布-订阅者模式 :它定义了对象间的一种一对多的依赖关系,只要当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
Vue2实现:
通过defineProperty劫持data的getter/setter
Get时,创建一个watcher
Set时,通知使用的watcher改变DOM
Vue2无法检测到对象属性的添加或删除,和数组的变化,这时候可用 vm.$set (object, propertyName, value)
Vue3实现:
用Proxy这个对象来代替object.defineProperty实现数据的双向绑定,通过Proxy代理对象的方式来劫持数据,当数据发生变化时发出通知。
Proxy优势:
直接监听对象而非属性
直接监听数组的变化
拦截方式较多(有13种方式)
Proxy作为新标准将受到浏览器厂商重点持续的性能优化
3. VueX
vuex是一种状态管理机制,将全局组件的共享状态抽取出来为一个store
state: state是存储的单一状态,是存储的基本数据。
Getters: 是store的计算属性,对state的加工, 类似computed,只有当它的依赖值发生改变才会被重新计算。
Mutations:提交更改数据,使用store.commit方法更改state存储的状态。
Actions:store.dispath像一个装饰器,提交mutation,而不是直接变更状态。建议把异步的操作放到这里来,mutation只做单纯的commit。
Module:每个模块拥有自己的state、getters、mutations、actions。
Vuex提供了mapState、MapGetters、MapActions、mapMutations等辅助函数。
4. Router
导航守卫:
路由跳转过程中的一些钩子函数。路由跳转是一个大的过程,这个大的过程分为跳转前中后等等细小的过程,在每一个过程中都有一函数,这个函数能让你操作一些其他的事儿的时机,这就是导航守卫。
router.beforeEach
router.afterEach
登录判断示例:
router.beforeEach((to, from, next) => { if (to.matched.some(record => record.meta.requireAuth)){ // 判断该路由是否需要登录权限 if(!sessionStorage.getItem('token') && !localStorage.getItem('token')){ next({ path: '/login', query: {redirect: to.fullPath} // 将跳转的路由path作为参数,登录成功后跳转到该路由 }) }else{ next(); } }else { next(); } }); export default new Router({ routes: [ { path: '/', name: 'home', redirect: '/home' }, { path: '/home', component: Home, meta: { title: '', requireAuth: true, // 添加该字段,表示进入这个路由是需要登录的 } } ] })
单独路由守卫:beforeEnter
const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, beforeEnter: (to, from, next) => { } } ] })
路由懒加载:
Webpack: 使用import
const Foo = () => import('./Foo.vue')
const router = new VueRouter({
routes: [
{ path: '/foo', component: Foo }
]
})
Vite: 使用defineAsyncComponent
import { defineAsyncComponent } from 'vue'
const _import = (path) => defineAsyncComponent(
() => import(`../views/${path}.vue`)
);
const routes = [
{
path: '/async-component',
name: 'asyncComponent',
component: _import('home'),
}
];
HashRouter 和 HistoryRouter:
原理:
hash路由:hash模式的工作原理是hashchange事件。当URL改变的时候,页面不会重新加载, 也就是单页应用了,当#后面的hash发生变化,不会导致浏览器向服务器发出请求, 通过触发hashChange事件, 监听hash值变化来实现更新页面部分内容。可以用window.onhashchange监听hash的变化。
对于hash模式会创建hashHistory对象,在访问不同的路由的时候,会发生两件事:
HashHistory.push()将新的路由添加到浏览器访问的历史的栈顶
HasHistory.replace()替换到当前栈顶的路由
history路由:
主要使用HTML5的pushState和replaceState这两个api结合window.popstate事件(监听浏览器前进后退)来实现的
pushState可以改变url地址且不会发送请求
replaceState可以读取历史记录栈,还可以对浏览器记录进行修改
使用方法:
// 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 })
区别:
hash模式较丑,history模式较优雅
pushState通过stateObject可添加任意类型的数据到记录中,而hash只可添加短字符串
pushState可额外设置title属性供后续使用
hash兼容IE8以上,history兼容IE10以上
history模式需要服务器支持,需要后端配合将所有访问都指向index.html,否则用户刷新页面,会导致404
5. 组件间通信
父子:props/$emit
爷孙:provide/inject
兄弟:vuex/eventBus
6. Keep-alive
keep-alive就是保持路由组件活跃,不会被destroy销毁掉,组件没有被销毁掉的话,组件上挂载的数据就还存在,所以状态就可以保留。
使用方式:
常用的两个属性include/exclude,允许组件有条件的进行缓存。
两个生命周期activated/deactivated,用来得知当前组件是否处于活跃状态
应用场景:
查看表格某条数据详情页,返回还是之前的状态,比如还是之前的筛选结果,页数等
填写的表单的内容路由跳转返回还在,比如input框、下选择拉框、开关切换等用户输入了一大把东西,跳转再回来不能清空啊,不能让用户再写一遍
7. 生命周期
beforeCreate: 当前实例创建之前,一般用于加载动画,比如创建一个菊花旋转。
Created: 表示当前实例创建完成,组件、属性等初始化完成,一般封装一个方法,比如从网络请求数据,触发回调,关闭beforeCreated中的菊花加载动画。
beforeMount: 表示将要挂载,页面仍未显示
mounted: 此时可以操作dom
beforeUpdate与update: 保持数据与页面的同步,会频繁触发,一般不要进行操作
beforeDestroy: 表示组件即将销毁,一般用于取消计时器等。
8. Ref
ref 被用来给元素或子组件注册引用信息, 引用信息将会注册在父组件的 $refs 对象上。
ref 需要在dom渲染完成后才会有,要在生命周期 mounted(){} 后调用,或者在 this.$nextTick(()=>{}) 中调用。
1. ref 加在普通的元素上,用this.ref.name 获取到的是dom元素
<div ref="englishAnalysis" class="usageChart"></div>
console.log(this.$refs. englishAnalysis);
2. ref 加在子组件上,用this.ref.name 获取到的是组件实例,可以使用组件的所有方法
<page-summary title="练习" ref="outsideComponentRef"></page-summary>
console.log(this.$refs.outsideComponentRef);
9. 如何动态引入静态资源图片
通用:
1. 将图片转base64格式:小图片
2. 使用import引入图片:要约定好图片名称,会有很多v-if判断,更适合单个文件资源
Webpack:
使用require(src)动态加载
Vite:
1. 使用import.meta.url:本地可以,打包后路径会丢失,需要将资源放到public目录
new URL(`../assets/images/${url}`, import.meta.url).href
2. 使用import.meta.glob(懒加载)// 不知道为啥不能用了
const getAssetsFile = (url: string) => { const path = `../assets/images/home/${url}`; const modules = import.meta.glob("../assets/images/home/*"); return modules[path].default; }
10. Vue3 与 Vue2 区别
1. 生命周期:vue3没有created、beforeCreated,vue3 onUnmouted代替destoryed
2. Vue3 支持多个根节点
3. Vue3组合式API,可将同一逻辑的内容写到一起。Setup语法糖,书写更自由。
4. Vue3 提供 Suspense 组件,fallback里可以加载前做些事情,比如loading
5. Vue3 提供 Teleport 组件可将部分 DOM 移动到 Vue app 之外的位置。比如项目中常见的 Dialog 弹窗。
6. Vue2 响应式原理基础是 Object.defineProperty;Vue3 响应式原理基础是 Proxy。可以监听对象或数组
7. vue3虚拟DOM的diff算法增加静态标记,通过建立新旧节点的关系映射找到最长递增序列。一定程度地减少节点本身及其属性的比对。
8. Vue3在第一次渲染后可以缓存事件。
9. ts的支持和打包优化等
11. 异步组件Suspense
<Suspense>
<template #default><router-view /></template>
<template #fallback><div class="loading"></div></template>
</Suspense>
使用场景:错误处理、路由加载、异步组件加载。
12. 虚拟DOM和Diff算法
虚拟dom:
通过JS的Object对象模拟DOM中的节点,然后再通过特定的render方法将其渲染成真实的DOM节点。比操作真实dom减少性能开销。
diff算法:
Diff 算法会对比新老虚拟DOM,记录下他们之间的变化,然后将变化的部分更新到视图。
VUE和REACT的diff:
相同:两者都只比较同一级节点,复杂度为O(n),用key去区分追踪每个节点。
React: 从左到右,先删除,再移动节点
Vue2:对旧节点做个映射表,然后遍历新节点剩余,先找到不需要移动的相同节点,再找要移动的节点,然后移动过去,最后找不到相同的,就去创建新的节点,并删除多余的节点。是两边向中间比较,最后再统一删除。
Vue3: 通过打静态标记,对于不参与更新的元素,只会创建一次,在渲染时直接复用。建立新旧关系映射数组,然后找最长递增子序列的方式。
浙公网安备 33010602011771号