订阅发布模式和观察者模式
订阅发布模式
希望接收通知的对象:基于一个主题通过自定义事件订阅主题,
发布事件的对象:通过发布主题事件的方式通知各个订阅该主题的对象
消息发送方:发送者
消息接收方:订阅者
订阅者与发布者不知道对方,需要第三方组件叫做信息中介。
观察者模式
一个对象(subject)维持一系列依赖于它的对象(observer),将有关状态的任何变更自动通知给观察者
比较(一到北地)
- Observer模式要求观察者必须订阅内容改变的事件,定义了一个一对多的依赖关系;
- Publish/Subscribe模式使用了一个主题/事件通道,这个通道介于订阅着与发布者之间;
- 观察者模式里面观察者「被迫」执行内容改变事件(subject内容事件);发布/订阅模式中,订阅着可以自定义事件处理程序;
- 观察者模式两个对象之间有很强的依赖关系;发布/订阅模式两个对象之间的耦合读底。
MVVM、MVC、MVP区别
时三种常见的软件架构设计模式,通过分离关注点的方式来组织代码结构,优化开发效率。
- MVC即模型、视图、控制器(Model View Controller)
- 简单来说就是通过controller的控制去操作model层的数据,并且返回给view层展示。controller在这里执行的的就是别墅里面的“管家”的作用。
- View 接受用户交互请求
- View 将请求转交给Controller处理
- Controller 操作Model进行数据更新保存
- 数据更新保存之后,Model会通知View更新
- View 更新变化数据使用户得到反馈
MVVM
- MVVM即模型、视图、视图模型(Model-View-ViewModel),ViewModel是MVVM这栋别墅的“管家”。
- 将其中的 View 的状态和行为抽象化,让我们可以将UI和业务逻辑分开。
- MVVM的优点是低耦合、可重用性、独立开发。
- View 接收用户交互请求
- View 将请求转交给ViewModel
- ViewModel 操作Model数据更新
- Model 更新完数据,通知ViewModel数据发生变化
- ViewModel 更新View数据
MVP
与mvc唯一不同在于presenter和controller,
mvc,model改变,通知view更新,逻辑混乱,controller只知道model的接口,无法控制view的更新
presenter可以更新view
slot
插槽
- 默认插槽
- 匿名插槽,slot没有指定name时默认插槽,一个组件只能由一个
- 组件
<slot></slot>
使用组件
<Category title="美食" > <img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">//放入插槽 </Category>
- 组件
- 匿名插槽,slot没有指定name时默认插槽,一个组件只能由一个
- 具名插槽
- 带有name属性的slot,一个组件可以出现多个
- 组件
<slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
使用组件
<Category title="美食" > <img slot="center" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt=""> <a slot="footer" href="http://www.atguigu.com">更多美食</a> </Category>
- 作用域插槽(子给父亲)
- 在子组件渲染作用域插槽时可以将内部数据传递过去
- 组件
<slot :games="games" msg="hello">我是默认的一些内容</slot> data() { return { games:['红色警戒','穿越火线','劲舞团','超级玛丽'], } },
使用组件
<Category title="游戏"> <template scope="atguigu"> <ul> <li v-for="(g,index) in atguigu.games" :key="index">{{g}}</li> </ul> </template> </Category>
过滤器
过滤数据是一个函数,他会把表达式中的值始终当作函数的第一个参数,
过滤器用在插值表达式{{}},v-bind中放在’|‘后显示
如何保存页面当前的状态
- 前组件会被卸载
- 存储在local/session storage中
- 前组件不会被卸载
- 单页面渲染,要切换的组件作为子组件全屏渲染,父组件正常存储页面状态
- keep-alive
常见的修饰符
.stop 阻止冒泡
.prevent 阻止默认行为
.capture 事件捕获
.once 只触发一次
data为什么是函数而不是对象
js中的对象是引用类型数据,当多个实例引用一个对象时,只要夜歌实例对这个对象进行操作,其他实例中的数据也会改变
复用组件时,每个组件都要有自己的数据,互不干扰
所以组件的数据不能写成对象,要写成函数,已返回值的形式定义,每次复用都会返回一个新的data
$nextTick原理及作用
本质时对js执行原理eventloop的一种应用
本质是为了利用js的这些异步回调任务队列来实现vue框架中自己的异步回调队列。
vue中数据更新与dom渲染是异步的,数据更新,会把任务交给一个异步队列,当当前任务空闲时,才会进行dom渲染,渲染后立即调用。
执行顺序
- Call Stack清空
- 执行当前的微任务
- 尝试DOM渲染
- 触发Event Loop
应用场景:
- 数据变化后执行的某个操作,而这个操作需要使用随数据变化而变化的dom结构时
- 在vue生命周期中,如果在created的狗子中进行dom操作,要放在nextTick的回调函数中
vue中给data新添加一个属性
新添加的属性,并不是响应式属性,不会触发视图更新,需要使用vue的全局api,$set()
this.$set(target, key , value)
单页面应用于多页面应用
- 单页面(spa,一句)
- 只有一个主页面的应用,一开始只需要加载一次js,css等相关资源,所有内容都包含在主页面,每一个功能组件化,单页面应用跳转只切换相关组件,刷新局部资源
- 多页面(MPA,重整)
- 每个页面都要重复加载js、css等资源,刷新整个网页
区别
template到render的过程
template->ast->render函数
vue data中的一个属性改变后,视图是否会立即重新渲染
不会立即渲染,
vue实现响应式布局吧并不是数据变化后DOM立即变化,而是按照一定策略对dom更新。
vue在更新,dom时是异步执行的。
vue优点(请数组时需快)
轻量级框架:只关注视图层,大小很小
双向数据绑定:数据操作简单
组件化:在构建单页面应用时有很大优势
视图,数据,结构分离:是数据的更改更为简单。
虚拟dom:不使用原生dom,提高性能
运行速度快:相较于react vue运行更快
vue模板编译原理
vue中的template无法被浏览器直接解析,需要将template转为一个js函数(render).浏览器执行这个函数,并渲染出相对应的html元素。这个转化过程被称为模板编译。有三个阶段
- 解析阶段
- 使用大量的正则表达式,对template字符串进行解析,将标签,指令,属性转化为AST抽象语法树
- 优化阶段
- 遍历AST找到其中的一些静态节点并标记,方便再次渲染时进行diff比较时,直接跳过着一些静态节点,优化runtime性能
- 生成阶段
- 将AST转化为render函数字符串
对ssr的理解
服务器端渲染,将vue在客户端把标签渲染成HTML的工作放在服务器端完成,然后再把html直接返回给客户端
优势
更好的seo
首屏加载速度更快
缺点
开发阶段受限制,服务器端渲染支支持beforeCreate和created两个钩子
更多的服务器负载
vue性能优化
- 编码阶段(换位需防迪兰)
- 减少data中的数据
- 使用路由懒加载、图片懒加载
- 第三方模块按需导入
- 防抖节流
- spa页面使用keep-alive缓存组件
- key值唯一
- v-if替代v-show
- v-for和v-if不连用
- SEO优化
- 预渲染
- SSR
- 打包优化
- 压缩代码
- 使用cdn加载静态资源
- tree shaking
- 多线程打包happypack
对spa单页面的理解
在web页面初始化时加载html,js,css。一旦加载完成,用户的操作不会使得页面重新加载或跳转。取而代之的是通过路由实现页面的内容的局部刷新。
优点
用户体验好,快。页面内容不需要整个重新刷新,避免了不必要的跳转和重复渲染
spa对服务器压力小
前后端分离,架构清晰。前端进行交互逻辑,后端负责数据处理
缺点
初次加载耗时多
seo难度大
不能使用浏览器的前进后退
assets与static的区别
同:
都是用于存放静态资源文件
异:
assets中存放的静态资源文件在项目打包时,会将assets中防止的静态资源文件进行打包上传。压缩后的静态资源文件,最终也会放置在static中跟着index.html一同上传到服务器。
static中的静态资源不走打包压缩格式化等流程,而是直接进入打包好的目录,直接上传。体积更大
MVVM优缺
优点(分梯子)
- 分离视图和模型,降低代码耦合。提高试图或者逻辑的重用性
- 提高可测试性:viewmodel可以帮助开发者更好的开发测试代码
- 自动更新dom:利用双向绑定,数据更新后视图自动更新
缺点(对于大型图形应用,视图状态多,vm构建和维护成本高)
- 大的模块中mode也很大,长期使用造成内存浪费
- 对大型图形应用的应用程序,视图状态多,vm的构建和维护成本高
生命周期
- beforeCreate(创建前):啥也没
- created(创建后) :数据,函数可以。节点大咩
- beforeMount(挂载前):有html无节点。
- mounted(挂载后):可以调用dom。
- beforeUpdate(更新前):数据可以,节点打灭。
- updated(更新后) :可以了。
- beforeDestroy(销毁前):实例销毁之前调用。这一步,实例仍然完全可用,
this
仍能获取到实例。 - destroyed(销毁后):实例销毁后调用,调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用。
在那个生命周期请求异步数据
created - beforeMount - mounted
推荐在created钩子函数中调用异步请求,
在created钩子函数中调用异步请求的优点
- 更快获取服务器端数据,减少页面加载时间
- ssr不支持beforeMount,mounted函数 ,有助于提高一致性
keep-alive的生命周期
是vue的内置组件,用来对组件进行缓存,在组件切换过程成将保持状态存在内存中
如果一个组件包裹了keep-alive那么他会多出两个生命周期
deactivated:切换走
,activated:切换来
同时beforeDestroy和destroy就不会再触发了
组件通信
(1)父子组件间通信
- 子组件通过 props 属性来接受父组件的数据,然后父组件在子组件上注册监听事件,子组件通过 emit 触发事件来向父组件发送数据。
- 父:<son :list='list'></son>
- 子:props:[ 'list' ]
父
this.$refs.student.$on('atguigu',this.getStudentName)
getStudentName(name,...params){
console.log('App收到了学生名:',name,params)
this.studentName = name
},
子
this.$emit('atguigu',this.name,666,888,900)
(2)兄弟组件间通信
- 使用 eventBus 的方法,它的本质是通过创建一个空的 Vue 实例来作为消息传递的对象,通信的组件引入这个实例,通信的组件通过在这个实例上监听和触发事件,来实现消息的传递。在
- 安装
- vue上beforeCreate(){ Vue.prototype.$bus=this }
发送
this.$bus.$emit('hello',this.name)
接受
this.$bus.$on('hello',(data)=>{
console.log('我是School组件,收到了数据',data)
})
(3)任意组件之间
- 使用 eventBus ,其实就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。
路由
路由懒加载的实现
非懒加载
import List from '@/components/list.vue'
const router = new VueRouter({
routes: [
{ path: '/list', component: List }
]
})
懒加载
使用箭头函数+import动态加载(const list =箭头函数包裹import)
const List = () => import('@/components/list.vue')
const router = new VueRouter({
routes: [
{ path: '/list', component: List }
]
})
箭头函数+require动态加载(无第一行 require做箭头函数返回值)
const router = new Router({
routes: [
{
path: '/list',
component: resolve => require(['@/components/list'], resolve)
}
]
})
路由hash和history的区别
hash
- hash值会出现再url中,不会出现在http请求中,对后端没有影响。
- 改变hash值不会重新发起请求
- 浏览器支持度好
把前端路由的路径拼接在url的#后边。
#后的路径发生变化时,并不会发生请求,而是出发onhashchage事件,但可以页面跳转(哈希路径在前端自生自灭)
history模式
- 用户输入url,服务器会接受这个请求,并解析这个url
- historyAPI
- 切换历史状态
- 切换状态:forward,back,go
- 修改历史状态:对历史记录进行更改,replaceState(),pushState()方法
- 切换历史状态
路由传参
query参数
路由定义
正常定义
路由跳转
<router-link :to="{path:'/profile',query:{name:'why',age:28,height:188}}">档案</router-link>
接受参数
$route.query
- 传递后形成的路径:
/route?id=123
params参数
路由定义
{ path: '/user/:userid', component: User, },
:userid用来占位
路由跳转
<router-link :to="{ name: 'users', params: { uname: wade }}">按钮</router-link>
接受:$route.params
vue-router路由钩子在生命周期中的体现
路由实现登陆权限验证。
全局路由钩子(一起)
- router.beforeEach 全局前置守卫, 进入路由之前
- router.beforeResolve 全局解析守卫 在beforeRouterEnter调用之后调用
- router.afterEach 全局后置守卫,进入路由之后
路由独享钩子
- beforeEnter
组件内钩子(3b)
- beforeRouteUpdate 当前地址改变,且组件被复用时触发
- beforeRouteEnter 进入组件前触发
- beforeRouteLeave 离开调用
router.beforeEach
beforeEnter
beforeRouteEnter
router.beforeResolve
router.afterEach
vue-router跳转和location.href有什么区别
- location.href跳转刷新了页面
- history.pushState无刷新,静态跳转
- 引入router使用router.push使用了diff实现按需加载。
对前端路由的理解
前端路由可以帮助我们在仅有一个页面的的情况下,记住当前用户走到了哪一步。
用户前进后退触发的新内容都会映射到不同的url中,此时即使刷新页面,有url可确定所在位置,内容也不会丢失。
vuex
当多个组件依赖同一状态
来自不同组件行为需要变更到同一状态
dispatch:调用action的方法 dispatch(functionAction , data)
action:操作处理模块,处理交互行为,可以支持异步操作。其中的操作会调用commit
commit:执行mutation的方法 commit(functionMutation , data)
mutations:状态操作改变方法,真正修改state中数据的操作
state:用于存储数据
getters:state对象读取方法,用于数据加工
vuex的几种属性
state、getters、mutations、actions、modules
vuex和单纯的全局对象有什么区别
vuex的存储状态是响应式的,当vue从store中读取状态的时候,若store中的数据发生变化,组件也会更新
不能直接改变store中的状态,可以改变store中的状态唯一途径就是提交
虚拟dom
虚拟dom是对dom的抽象,本质上来说,虚拟dom是js的一个对象。将页面的状态抽象为js对象形式,通过diff算法,计算最小变化,根据变化更新dom,将多次dom修改的结果一次性更新到页面上,从而减少页面渲染的次数,减少dom重绘回流次数,提高渲染性能。
虚拟DOM解析过程
- 首先对要插入文档中的DOM树结构进行分析,使用js对象将其表示出来。将这个js对象树保存下来,最后再将dom片段插入到文档中
- 当页面的状态发生改变,需要对页面的dom结构进行调整的时候,首先根据变更的状态重新构建起一颗对象树,然年后将这颗新的对象树和旧的对象树进行比较,记录两颗树的差异
- 最后将记录的有差异的地方应用到真正的dom树中
为什么要使用虚拟dom
(1)保证性能下限,再不优化的前提下提供过得去的性能,
页面渲染流程
解析html-->生成dom->生成cssom-》layout->paint->complier
重绘回流性能小号比
- 真实dom:生成html字符串+重建所有的DOM元素
- 虚拟DOM:生成vNode+DOMdiff+必要的dom更新
(2)跨平台