mvvm理解
MVVM是Model-View-ViewModel的缩写。Model 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑;View 代表UI 组件,它负责将数据模型转化成UI 展现出来,ViewModel 是一个同步View 和 Model的对象。
在MVVM架构下,View 和 Model 没有直接的联系,通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
vue.js核心
数据驱动:ViewModel,保证数据和视图的?致性
组件化:应?类UI可以看做全部是由组件树构成的
vue优点
低耦合:视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
可复用性:可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
可测试:界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
轻量级的框架、双向数据绑定、组件化开发、单页面路由、学习成本低、虚拟dom、渐进式框架、数据和结构的分离、运行速度快、插件化
vue组件
封装组件:为了解耦,提高代码复用率
组件是html、css和js的聚合体,相当于库,把一些能在项目里或者不同项目里可以复用的代码进行需求性的封装
组件中的data是一个函数,是因为让每个返回的实例都可以维护一份被返回对象的独立的拷贝
多个组件有相同逻辑,应该使用mixin对公共部分的逻辑进行抽离
组件之间传值
使用vue-router通过跳转链接带参数传参。
使用本地缓存localStorge。
使用vuex数据管理传值
封装组件
1、Vue.extend()创建一个组件
2、使用Vue.component()方法注册组件
3、如果子组件需要数据,可以在props中接受定义
4、子组件修改好数据之后,想把数据传递给父组件,可以使用emit()方法
可复用组件
Props 允许外部环境传递数据给组件
Events 允许组件触发外部环境的副作用 $emit
Slots 允许外部环境将额外的内容组合在组件中。
尽量减少组件对外部条件的依赖
数据扁平化:每一个prop应该是一个简单类型的数据,这样组件的接口清晰,props校验方便
vueCli使用自定义组件
1、在 components 目录新建组件文件
2、在需要用到的页面import中导入
3、使用component注册
4、在 template 视图中使用组件标签
插件使用步骤
采用ES6的import ... from ...语法或CommonJS的require()方法引入插件。
使用全局方法Vue.use( plugin )使用插件,可以传入一个选项对象Vue.use(MyPlugin, { someOption: true })。
axios
拦截器
请求拦截器:axios.interceptors.request.use
响应拦截器:axios.interceptors.response.use
数组的常用方法
push() 向数组中添加数据,直接改变原数组
pop() 删除数组中的最后一个数据,直接改变原数组
unshift() 向数组中最前面添加数据,直接改变原数组
shift() 删除数组中的最前面数据
concat() 对数组进行拼接
splice() 对数组进行截取,改变原数组
arr.splice(2,3,5);//表示从下标为2的位置开始,删除3个项,在下标为2的位置添加一个数字为5的新项
slice() 截取数组中的值
slice(start,end):从start开始截取到end但是不包括end;原始的数组不会发生变化
reverse() 颠倒数组中元素的顺序
sort() 数组排序
join() 把数组链接成一个字符串
new操作具体
在堆空间中创建一个给定构造函数的实例对象
new 会让this指向这个新的对象
执行构造函数 目的:给这个新对象加属性和方法
new会返回这个新对象
堆栈
堆是用来存放引用数据类型,例如对象,数组,函数
栈是用来存放基本数据类型,变量和引用数据类型的地址值
组件通信方式
父子通信
在父组件中子组件标签中绑定属性
子组件中 props: { 子组件标签自定义的属性名: { type: , default} }
父组件要给子组件传值,在子组件上定义一个 ref 属性,这样通过父组件的 $refs 属性就可以获取子组件的值了
子父通信
在嵌套组件中,父组件中的[子组件标签] ,自定义事件@fn=“”,
在子组件里用$emit向父组件触发一个事件,父组件监听这个事件就行了。
直接在子组件中通过this.$parent.event来调用父组件的方法。
兄弟通信
创建一个事件总线:$eventBus;或者使用vuex
vue中的key
key为每个节点提供了唯一标识,当添加或删除节点时,通过对比数据前后的变化,只用操作某个变化的节点,不需要重新渲染所有数据,提高了性能
html
v-bind就是用于绑定数据和元素属性的
v-on:方法绑定,@click:xx
v-show 可以操作display属性
v-if 销毁和创建元素
v-for要用key
快速查找到节点,减少渲染次数,提升渲染性能
v-show和v-if
v-show通过css display控制显示和隐藏
v-if组件真正的渲染和销毁,而不是显示和隐藏
v-for和v-if
不能一起使用,原因是v-for的优先级高于v-if,因此无论v-if判断的结果是什么,都会通过v-for创建节点,然后再通过v-if判断是否显示,这样浪费了性能
如果要判断的话,可以用template包裹外层v-if,先进行判断,或者提前用计算属性过滤调不需要显示的项。
双向数据绑定v-model原理
vue采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty劫持data属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
es6的新增特性
箭头函数,let const 块级作用域,模块化(moudle),promise,对象属性的简写,解构赋值,模板字符串,类(类的继承),扩展运算符
vue生命周期
8个阶段:beforeCreate(创建前)
created(创建后)
beforeMount(载入前,挂载)
mounted(载入后)
beforeUpdate(更新前)
updated(更新后)
beforeDestroy(销毁前)
destroyed(销毁后)
单个页面
挂载: beforeCreate => created => beforeMount => mounted
更新: beforeUpdate => updated
销毁: beforeDestroy => destroyed
父子组件生命周期
挂载: parent beforeCreate => parent created => parent beforeMount => child beforeCreate => child created => child beforeMount => child mounted => parent mounted
更新: parent beforeUpdate => child beforeUpdate => child updated => parent updated
销毁: parent beforeDestroy => child beforeDestroy => child destroyed => parent destroyed
周期使用场景
beforecreate : 可以在这加个loading事件,在加载实例时触发
created : 初始化完成时的事件,如在这结束loading事件,异步请求也可以在这调?
mounted : 挂载元素,获取到DOM节点;ajax请求(因为js是单线程,ajax异步获取数据)
updated : 如果对数据统?处理,在这?写上相应函数
beforeDestroy : 可以做?个确认停?事件的确认框
nextTick : 更新数据后?即操作dom
页面加载过程
开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载
computed和watch的区别
computed :计算属性是基于它们的依赖进行缓存的,只有在它的相关依赖发生改变时才会重新求值。节约性能,计算属性,不支持异步
watch:只会监听数据的值是否改变,而不会监听数据的地址是否改变,要深度监听需要配合deep:true属性使用;immediate:true 页面首次加载的时候做一次监听;支持异步
computed擅长处理的场景:一个数据受多个数据影响;watch擅长处理的场景:一个数据影响多个数据。
功能上:computed是计算属性,watch是监听一个值的变化,然后执行对应的回调。
是否调用缓存:computed支持缓存,只有依赖数据发生改变,才会重新进行计算;而watch不支持缓存,数据变,直接会触发相应的操作。
是否调用return:computed中的函数必须要用return返回,watch中的函数不是必须要用return。
computed不支持异步 ,当computed内有异步操作时无效,无法监听数据的变化;而watch支持异步。
computed默认第一次加载的时候就开始监听;watch默认第一次加载不做监听,如果需要第一次加载做监听,添加immediate属性,设置为true(immediate:true)
keep-alive
作用:包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。
vuex
vuex 是一个状态管理工具,主要是为了解决多组间之间状态共享问题。强调的是集中式管理,(组件与组件之间的关系变成了组件与仓库之间的关系)
vuex 的核心
五种属性:state(存放状态)、mutations(同步的更改状态)、actions(发送异步请求,拿到数据)、getters(根据之前的状态派发新的状态)、modules(模块划分)
执行过程:state 发布一条新的数据,在 getters 里面根据状态派发新的状态,actions 发送异步请求获取数据,然后在 mutations 里面同步的更改数据
应用场景
单页应用中,组件之间的状态,音乐播放、登录状态等。
state特性
Vuex就是一个仓库,仓库里面放了很多对象。其中state就是数据源存放地,对应于一般Vue对象里面的data。
state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新。
通过mapState和mapGetters把全局 state 和 getters 映射到当前组件的 computed 计算属性中。
getter特性
getters 可以对State进行计算操作,它就是Store的计算属性。
虽然在组件内也可以做计算属性,但是getters 可以在多组件之间复用。
如果一个状态只在一个组件内使用,可以不用getters。
mutation特性
是唯一一种方式来修改state中的状态的;
在组件的自定义方法中,使用this.$store.commit(‘对应mutations中的方法’, 新的值)方法,把新的值提交给mutations中相对应的方法,mutations属性中的每个方法中有两个参数,分比为state和payload;state其实就是vuex中的state属性,payload叫做mutations的载荷,其实就是传过来的值。一般payload传的是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读
action和mutation
角色定位
Mutation:专注于修改State,理论上是修改State的唯一途径。
Action:业务代码、异步请求。
限制
Mutation:必须同步执行。
Action:可以异步,但不能直接操作State。
不用vuex会怎样
可维护性会下降,想修改数据要维护三个地方
可读性会下降,因为一个组件里的数据,根本就看不出来是从哪来的;
增加耦合,大量的上传派发,会让耦合性大大增加,Vue用Component本意就是为了减少耦合,现在这么用,和组件化的初衷相背。
diff算法
过程就是调用名为 patch 的函数,比较新旧节点。一边比较一边给真实的 DOM 打补丁。patch 函数接收两个参数 oldVnode 和 Vnode,它们分别代表新的节点和之前的旧节点
采用异步渲染
考虑性能问题,vue会在本轮数据更新之后,再去异步更新视图
$nextTick
将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。
ref作用
获取dom元素 :this.$refs.box
获取子组件中的data :this.$refs.box.msg
调用子组件中的方法 :this.$refs.box.open()
vnode与虚拟dom
vnode:vue在页面上渲染的节点,及其子节点
虚拟dom:vue组件树建立起来的整个vnode树的称呼
key
作用:key是虚拟DOM对象的标识,当状态中的数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】的差异比较
用index作为key可能会引发的问题:
若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ===> 界面效果没问题,但效率低
如果结构中还包含输入类的DOM:会产生错误DOM更新 ===> 界面有问题
key的选择
最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值
vue的响应式系统
当页面渲染时(render),触发data里面的数据(getter),watcher(观察者)会把接触过的数据记录为依赖(该过程为依赖收集)。每个数据都有getter和setter,当你修改这个数据的时候,依赖的setter触发,然后通知到watcher,从而使关联这个数据的组件重新渲染。
注意点
任何一个vue Component都有一个与之对应的watcher实例
Vue如何实现按需加载配合webpack设置
webpack中提供了require.ensure( )来实现按需加载。以前引入路由是通过import这样的方式引入,改为const定义方式进行引入。
不进行页面按需加载引入方式 import home from ../../common/home.vue
进行页面按需加载的引入方式:const home = r =>require.ensure([],() =>require('../../common/home.vue)))
性能优化
合理使用v-if和v-show
合理使用computed
v-for加key
自定义事件,dom事件及时销毁
合理使用异步组件和keep-alive
前端通用性能优化(如图片懒加载/减少 HTTP请求数/合理设置 HTTP缓存/资源合并与压缩/合并 CSS图片/将 CSS放在 head中/避免重复的资源请求/切分到多个域名),
使用ssr
首屏优化
css模块化加载,对应模块下的css交给js或jsonp请求返回
js懒执行,有交互才执行
图片在其他屏(非首屏)都采用懒加载的模式,这样既能节省流量,也能减少请求数或延迟请求数。
解决非工程化项目初始化页面闪动问题
使?vue开发时,在vue初始化之前,由于div是不归vue管的,所以代码在还没有解析的情况下容易出现花屏现象,看到类似于{{message}}的字样。
?先:在css?加上[v-cloak]{display:none;},如果没有彻底解决问题,则在根元素加上style=“display:none;” :style="{display:block}"
Vuex页面刷新数据丢失
问题:F5页面刷新,页面销毁之前的资源,重新请求,因此写在生命周期里的vuex数据是重新初始化,无法获取的,这也就是为什么会打印出空的原因。
解决1:使用Localstorage sessionStorage 或cookie
解决2:插件vuex-persistedstate
vue弹窗后如何禁止滚动条滚动
弹出框出现:调用禁止滚动方法stopScroll()
弹出框去掉:调取允许滚动方法canScroll()
vue-loader
解析.vue文件的一个加载器。
用途:js可以写es6、style样式可以scss或less、template可以加jade等。
$route和 $router
$router为VueRouter的实例,是一个全局路由对象,包含了路由跳转的方法、钩子函数等。
$route 是路由信息对象或跳转的路由对象,每一个路由都会有一个route对象,是一个局部对象,包含path,params,hash,query,fullPath,matched,name等路由信息参数。
路由跳转的几种方式
第一种方式:router-link (声明式路由)
第二种方式:router.push(编程式路由)
第三种方式:this.$router.push() (函数里面调用)
第四种方式:this.$router.replace() (用法同上,push)
第五种方式:this.$router.go(n)
Vue-router跳转和location.href有什么区别
vue-router使用pushState进行路由更新,静态跳转,页面不会重新加载;location.href会触发浏览器,页面重新加载一次
vue-router使用diff算法,实现按需加载,减少dom操作
vue-router是路由跳转或同一个页面跳转;location.href是不同页面间跳转;
vue-router是异步加载this.$nextTick(()=>{获取url});location.href是同步加载
路由:params和query的区别
?法:query要?path来引?,params要?name来引?,接收参数都是类似的,分别是this. $router.query.name 和 this.$router.params.name。url地址显?:query更加类似于我们ajax中get传参,params则类似于post,前者在浏览器地址栏中显?参数,后者则不显?
注意点:query刷新不会丢失query??的数据params刷新会丢失params??的数据
delete和Vue.delete删除数组的区别
delete只是被删除的元素变成了empty/undefined其他的元素的键值还是不变。
Vue.delete直接删除了数组 改变了数组的键值
路由守卫
全局守卫
router.beforeEach(to, from, next),全局前置守卫(进入之前触发
router.beforeResolve(to, from, next),全局的解析守卫
router.afterEach(to, from ,next) 全局的后置守卫(进入之后触发
独享守卫(单个路由里面的钩子)
beforeEnter(to, from, next):读取路由信息
只有前置没有后置
组件内守卫:
只有前置没有后置,直接写在.vue文件中
beforeRouterEnter(to, from, next):进入组件之前触发,在Created前面。(该守卫不能访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。
beforeRouterUpdated (to, from, next):路由更新但是内容不会改变
beforeRouterLeave (to, from, next):离开之前触发,在beforeDestory之前触发
上述守卫三个参数含义
to: Route: 即将要进入的目标路由对象(to是一个对象,是将要进入的路由对象,可以用to.path调用路由对象中的属性)
from: Route: 当前导航正要离开的路由
next: Function: 这是一个必须需要调用的方法,执行效果依赖 next 方法的调用参数。
url解析到显示页面过程
首先浏览器主进程接管,开了一个下载线程。
然后进行HTTP请求(DNS查询、IP寻址等等),中间会有三次握手,等待响应,开始下载响应报文。
将下载完的内容转交给Renderer进程管理。
Renderer进程开始解析css rule tree和dom tree,这两个过程是并行的,所以一般我会把link标签放在页面顶部。
解析绘制过程中,当浏览器遇到link标签或者script、img等标签,浏览器会去下载这些内容,遇到时候缓存的使用缓存,不适用缓存的重新下载资源。
css rule tree和dom tree生成完了之后,开始合成render tree,这个时候浏览器会进行layout,开始计算每一个节点的位置,然后进行绘制。
绘制结束后,关闭TCP连接,过程有四次挥手
vueCli项目文件及其作用
assets?件夹是放静态资源;components是放组件;router是定义路由相关的配置;view是视图;app.vue是?个应?主组件;main.js是???件
常用修饰符
stop:等统?JavaScript中的event.stopPropagation(),防?事件冒泡
.prevent:等同于JavaScript中的event。preventDefault(),防?执?预设的?为(如果事件可取消,则取消该事件,?不停?事件的进?步
传播);
.capture:与事件冒泡的?向相反,事件捕获由外到内
.self 只当在 event.target 是当前元素自身时触发处理函数
.passive 告诉浏览器你不想阻止事件的默认行为
.once:只会触发?次。
|