MVVM是Model-View-ViewModel的缩写。MVVM是一种设计思想。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 来统一管理。
mvc和mvvm其实区别并不大。都是一种设计思想。主要就是mvc中Controller演变成mvvm中的viewModel。mvvm主要解决了mvc中大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。
区别:vue数据驱动,通过数据来显示视图层而不是节点操作。
场景:数据操作比较多的场景,更加便捷
· 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
· 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
· 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
· 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
1.父组件与子组件传值
在父组件中给子组件标签上绑定一个属性, 属性上挂载需要传递的值
在子组件通过props:{“自定义属性名”}来接收数据
子组件传给父组件
在父组件中给子组件标签绑定一个自定义事件,给这个事件挂载需要调用的方法
在子组件的方法通过this. e m i t ( ‘ 自 定 义 事 件 名 ’ ) 来 调 用 这 个 方 法 2. 非 父 子 组 件 间 的 数 据 传 递 , 兄 弟 组 件 传 值 创 建 一 个 空 的 v u e 实 例 b u s 通 过 b u s . emit(‘自定义事件名’)来调用这个方法 2.非父子组件间的数据传递,兄弟组件传值 创建一个空的vue实例bus 通过bus. emit(‘自定义事件名’)来调用这个方法2.非父子组件间的数据传递,兄弟组件传值创建一个空的vue实例bus通过bus.emit(‘事件名’)传到空的vue实例中
通过bus.$on(‘事件名’,(参数)=>{挂载从子1传来的数据})来接收
最常见的判断方法:typeof
判断已知对象类型的方法: instanceof
根据对象的constructor判断: constructor
无敌万能的方法:jquery.type()
在router目录下的index.js文件中,对path属性加上/:id。 使用router对象的params.id
获取:this.$route.params.id
active-class是vue-router模块的router-link组件中的属性,用来做选中样式的切换。
我们在 VueRouter 的参数中使用 children 配置,这样就可以很好的实现路由嵌套。
示例代码:
Vue 实例从创建到销毁的过程,就是生命周期。从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期。
1.beforeCreate
在数据观测和初始化事件还未开始,VUE实例的挂载元素 $ el 和数据对象都为undefined
可以进行的事件:加loading事件
2.created
完成数据观测,属性和方法的运算,初始化事件,$ el 属性还没有显示出来
可以进行的事件:结束loading,请求数据为mounted作准备
3.beforeMount
在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。($el和data都已经初始化,但仍是虚拟DOM节点)
4.Mounted
e l 被 新 创 建 的 v m . el被新创建的vm.el被新创建的vm.el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。(VUE实例挂载完成,data.filter成功渲染)
可以进行的事件:配合路由钩子使用
5.beforeUpdate
在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。
6.Update
在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
7.beforeDestroy
在实例销毁之前调用。实例仍然完全可用。
8.destroyed
在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。
会触发 下面这几个beforeCreate, created, beforeMount, mounted 。
DOM 渲染在 mounted 中就已经完成了。
vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
实现过程
我们已经知道如何实现数据的双向绑定了, 那么首先要对数据进行劫持监听,所以我们首先要设置一个监听器Observer,用来监听所有的属性,当属性变化时,就需要通知订阅者Watcher,看是否需要更新.因为属性可能是多个,所以会有多个订阅者,故我们需要一个消息订阅器Dep来专门收集这些订阅者,并在监听器Observer和订阅者Watcher之间进行统一的管理.以为在节点元素上可能存在一些指令,所以我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令初始化成一个订阅者Watcher,并替换模板数据并绑定相应的函数,这时候当订阅者Watcher接受到相应属性的变化,就会执行相对应的更新函数,从而更新视图.
整理上面的思路,我们需要实现三个步骤,来完成双向绑定:
1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器
vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 { {}}),最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。
js实现简单的双向绑定
<body>
<div id="app">
<input type="text" id="txt">
<p id="show">p>
div>
body>
<script type="text/javascript">
var obj = {
}
Object.defineProperty(obj, 'txt', {
get: function () {
return obj
},
set: function (newValue) {
document.getElementById('txt').value = newValue
document.getElementById('show').innerHTML = newValue
}
})
document.addEventListener('keyup', function (e) {
obj.txt = e.target.value
})
script>
v-for 数据循环
v-on 绑定事件
v-model 实现双向数据绑定
v-text 渲染字符串
v-html 渲染html节点及字符串
v-if 判断
v-show 显示/隐藏
v-bind 绑定属性
vue文件的一个加载器,将template/js/style转换成js模块。
需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点。
作用主要是为了高效的更新虚拟DOM。
assets文件夹是放静态资源;
components是放组件;
router是定义路由相关的配置;
app.vue是一个应用主组件;
main.js是入口文件。
computed:
当一个属性受多个属性影响的时候就需要用到computed
最典型的栗子: 购物车商品结算的时候
watch:
当一条数据影响多条数据的时候就需要用watch
栗子:搜索数据
当你修改了data的值然后马上获取这个dom元素的值,是不能获取到更新后的值,你需要使用nextTick这个回调,让修改后的data值渲染更新到dom元素之后在获取,才能成功。
处理数据动态变化后,dom还未及时更新的问题。nexttick就可以获取到数据更新后最新的dom变化
$nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 DOM
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。(官网解释)
解决的问题:有些时候在改变数据后立即要对dom进行操作,此时获取到的dom仍是获取到的是数据刷新前的dom,无法满足需要,这个时候就用到了$nextTick。
因为JavaScript的特性所导致,在component中,data必须以函数的形式存在,不可以是对象。
组建中的data写成一个函数,数据以函数返回值的形式定义,这样每次复用组件的时候,都会返回一份新的data,相当于每个组件实例都有自己私有的数据空间,它们只负责各自维护的数据,不会造成混乱。而单纯的写成对象形式,就是所有的组件实例共用了一个data,这样改一个全都改了。
因为一个组件是可以共享的,但他们的data是私有的,所以每个组件都要return一个新的data对象,返回一个唯一的对象。
主张最少;可以根据不同的需求选择不同的层级;
说白话,就是框架分层。:最核心的是视图层渲染,然后往外是组件机制,在此基础上再加入路由机制,再加入状态管理,最外层是构建工具,vue和react都是如此。
单页面应用(SPA)
它是指只有一个主页面的应用,浏览器y一开始加载所有的html ,js 以及css,这些所用页面的内容都包含在这个主页面中,但是在手写中,他们还是分开来书写的,在交互的时候,由路由程序动态载入,单页面的页面跳转,是整页刷新的。
组成:是由一个外壳和多个页面片段组成
单页面的优点:
1.用户的体验好,快,内容的改变不需要重新加载整个页面,由于这点,SPA它对服务器的压力小
2前后端分离
3.页面效果比较炫酷
单页面的缺点:
1.不利于seo优化
2.导航不可用
3.初次加载时耗时多
4.页面的复杂程度提高了很多
多页面开发的优缺点;
优点:多个页面之间跳转,用户可以清晰的了解整个网站的内容构成;页面结构的分布也不会很拥挤;实现简单,开发成本低;适用于对搜索引擎支持较高,页面跳转较少,数据传递较少的项目中开发。
缺点:页面跳转时,要重新执行css,js等文件,页面片段之间跳转较慢,需要用到页面跨页面传值的几种方法;实现转场动画比较困难。维护成本高。
hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;
特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。
hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
history模式:history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。
history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”
(版本在不断更新,以下的区别有可能不是很正确。我工作中只用到vue,对angular和react不怎么熟)
1.与AngularJS的区别
相同点:
都支持指令:内置指令和自定义指令;都支持过滤器:内置过滤器和自定义过滤器;都支持双向数据绑定;都不支持低端浏览器。
不同点:
AngularJS的学习成本高,比如增加了Dependency Injection特性,而Vue.js本身提供的API都比较简单、直观;在性能上,AngularJS依赖对数据做脏检查,所以Watcher越多越慢;Vue.js使用基于依赖追踪的观察并且使用异步队列更新,所有的数据都是独立触发的。
2.与React的区别
相同点:
React采用特殊的JSX语法,Vue.js在组件开发中也推崇编写.vue特殊文件格式,对文件内容都有一些约定,两者都需要编译后使用;中心思想相同:一切都是组件,组件实例之间可以嵌套;都提供合理的钩子函数,可以让开发者定制化地去处理需求;都不内置列数AJAX,Route等功能到核心包,而是以插件的方式加载;在组件开发中都支持mixins的特性。
不同点:
React采用的Virtual DOM会对渲染出来的结果做脏检查;Vue.js在模板中提供了指令,过滤器等,可以非常方便,快捷地操作Virtual DOM。
vuex是一个专为vue.js应用程序开发的状态管理器,采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
解决的问题:
有了vue-router,可以将一个组件视为一个页面来使用。由于组件只维护自身的状态(data),组件创建时或者说进入路由时它们被初始化,切换至其他的组件页时当前页自然要被销毁,从而导致data也随之销毁。
页面与页面之间总会产生各种需要的共享变量,如果通过 r o u t e r . p a r a m 或 者 router.param或者 router.param或者router.meta来传递是远远不够的,很多情况下不得不采用window来保存一些全局的共享变量。
这样出现的问题:vue是不会维护window的这些共享变量的。对于组件来讲,这些变量都存在于组件作用域以外,组件并不会自动维护,这样就违背了js编程规范或者风格规范的一条基本准则:全局变量是毒瘤,是具有极高副作用的。
当我们将window内的对象绑定到不同的自定义组件内,一旦要对window内的变量进行修改,会发现所有以对象方式绑定的自定义组件,当对象内的某个属性发生改变时将不会执行自动刷新,所有的计算属性也同时失效!更诡异的是这种情况并不是绝对出现的,当页面元素相对简单的时候一切都显得很正常,一旦页面元素增多,对应的交互操作增多时,这种奇怪的现象就会发送。
vuex就是专门解决页面与页面之间需要的共享变量的创建、维护、变更问题的。
它有以下几个核心部分组成:
在main.js引入store,注入。新建一个目录store,…… export 。
场景有:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车
main.js:
import store from './store'
new Vue({
el:'#app',
store
})
1.可维护性会下降,想修改数据要维护三个地方;
2.可读性会下降,因为一个组件里的数据,根本就看不出来是从哪来的;
3.增加耦合,大量的上传派发,会让耦合性大大增加,本来Vue用Component就是为了减少耦合,现在这么用,和组件化的初衷相背。
的作用是什么?
包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。
1)采用ES6的import … from …语法或CommonJS的require()方法引入组件
2)对组件进行注册,代码如下
// 注册
Vue.component('my-component', {
template: '<div>A custom component!div>'
})
3)使用组件
预处理css,把css当前函数编写,定义变量,嵌套。 先装css-loader、node-loader、sass-loader等加载器模块,在webpack-base.config.js配置文件中加多一个拓展:extenstion,再加多一个模块:module里面test、loader
基于vue的前端组件库。npm安装,然后import样式和js,vue.use(mintUi)全局引入。
在单个组件局部引入:import {Toast} from ‘mint-ui’。
组件一:Toast(‘登录成功’);
组件二:mint-header;
组件三:mint-swiper
vue用来写路由一个插件。router-link、router-view
简而言之,就是先转化成AST树,再得到的render函数返回VNode(Vue的虚拟DOM节点)
详情步骤:
首先,通过compile编译器把template编译成AST语法树(abstract syntax tree 抽象语法树 即 源代码的抽象语法结构的树状表现形式),compile是createCompiler的返回值,createCompiler是用以创建编译器的。另外compile还负责合并option。
然后,AST会经过generate(将AST语法树转化成render funtion字符串的过程)得到render函数,render的返回值是VNode,VNode是Vue的虚拟DOM节点,里面有(标签名、子节点、文本等等)
数据驱动:ViewModel,保证数据和视图的一致性。
组件系统: 应用类UI可以看作全部是由组件树构成的。
Vue.directive('自定义指令名', {
inserted: function (当前节点) {
// 对节点的操作
}
})
directives:{
color:{
bind:function(el,binding){
if(binding.arg){
el.style.color=binding.arg
}else{
el.style.color='red'
}
}
}
}
bind:只调用一次,指令第一次绑定到元素时调用
inserted:被绑定元素插入父节点时调用
update:所在组件的虚拟节点更新时调用
componentUpdated:所在组件的虚拟节点及子虚拟节点全部更新后调用
unbind:只调用一次,指令与元素解绑时调用
特性:重用性、可指定性、互操作性、高内聚性、低耦合度
好处:组件可以扩展HTML元素、封装可重用代码
过滤器:是对即将显示的数据做进一步的筛选处理,然后显示,过滤器并没有改变原来的数据,只是在原数v据的基础上产生新的数据
全局:
vue.filter(‘过滤器名’,funciton(val){
//逻辑代码
})
局部:
filter:{过滤器名:funciton(参数){
//逻辑代码
}
}
首先,组件可以提升整个项目的开发效率。能够把页面抽象成多个相对独立的模块,解决了我们传统项目开发:效率低、难维护、复用性等问题。
然后,使用Vue.extend方法创建一个组件,然后使用Vue.component方法注册组件。子组件需要数据,可以在props中接受定义。而子组件修改好数据后,想把数据传递给父组件。可以采用emit方法。
全局守卫:
beforeEach((to, from, next) => {调用next来resolve这个钩子} ),
beforeResolve((to, from, next) => {} ),
afterEach((to, from) => {} )
路由独享守卫:
beforeEnter((to, from, next) => {} ),
组件内部守卫:
beforeRouteEnter((to, from, next) => {next可以传回调,回调里面可以用vm访问实例} ), 内部没有this,因为路由还没confirm
beforeRouteUpdate((to, from, next) => {}),可以拿到this
beforeRouteLeave((to, from, next) => {})
他们执行顺序:全局》路由》组件
keep-alive:组件缓存,只会执行一次,不会被销毁。被keep-alive包裹的组件,有activated和deactivated方法。
当 keep-alive 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们,它自身不会渲染一个DOM元素,也不会出现在父组件中,主要用于保留组件状态或避免重新渲染
能够解决插值表达式闪烁问题,需要在style中设置样式[v-clock]{display:none}
1.用法 params要用name引入
this. r o u t e . p a r a m s . n a m e q u e r y 要 用 p a t h 引 入 t h i s . route.params.name query要用path引入 this. route.params.namequery要用path引入this.route.query.name
2.Url地址显示
query类似于ajax的get传参,浏览器地址栏中显示参数
params类似于post浏览器地址栏中不显示参数
共同点:两个都是存放静态资源文件的
区别:
assets:存放的静态资源文件在项目打包时,压缩后的静态资源文件最终也会放在static文件中跟着index.html一同上传
static:static中 放置的静态资源文件就不会走打包压缩格式化等流程,而是直接进入打包好的目录,直接上传至服务器
① to:(route路由对象)即将要进入的目标路由对象
② from (route路由对象)当前导航正要离开的路由
③ next:(funciton函数)一定要调用该方法来resolve这个钩子
拦截器:动态拦截action调动的对象
拦截点:共有四个拦截点:
request(发送一个请求)
1、 success (成功)
2、 fall (失败)
response(服务器响应)
1、 success (成功)
2、 fall (失败)
Webpack 的核心概念是一个 模块打包工具 ,它的主要目标是将js文件打包在一起,打包后的文件用于在浏览器中使用。
entry: 入口,定义整个编译过程的起点
output: 输出,定义整个编译过程的终点
module: 定义模块module的处理方式
plugins:插件,对编译完成后的内容进行二度加工
resolve.alias 定义模块的别名
1.渲染过程顺序:
父组件beforeCreate() -> 父组件created() -> 父组件beforeMount() -> 子组件beforeCreate() ->子组件created() -> 子组件beforeMount() -> 子组件mounted() -> 父组件mounted()
2.更新过程顺序:
父组件更新过程:
父组件beforeUpdate() -> 父组件updated()
子组件更新过程:
父组件beforeUpdate() -> 子组件beforeUpdate() -> 子组件updated() -> 父组件updated()
3.销毁过程
父组件beforeDestroy() -> 子组件beforeDestroy() -> 子组件destroyed() -> 父组件destroyed()
// 这里是父组件
<template>
<child
@hook:mounted="getChildMounted"
/>
template>
<script>
method: {
getChildMounted () {
// 这里可以获取到子组件mounted的信息
}
}
script>
能,对于有返回值的数据,watch就能监听到,比如数组的pop,push, unshift,map等行为。
watch: {
obj: {
handler: function(val) {
},
deep: true // 深度监听
}
}
(1)修改对象属性后,页面未重新渲染:
this.$set(对象名称, '属性名', '属性值')
(2)使用this.$forceUpdate()方法可重新渲染页面
防止组件与组件之间变量的局部污染
通过异步的方式来加载对应的路由组件,提高页面的加载速度
SPA是单页面应用程序,vue react angular 都是,整个项目只有一个页面
{
// 会匹配所有路径
// 含有通配符的路由应该放在最后
path: '*',
name: '404',
component: () => import('../views/404.vue')
}
beforeRouteLeave (to, from, next) {
if(用户已经输入信息){
//出现弹窗提醒保存草稿,或者自动后台为其保存
}else{
next(true);//用户离开
}
}
vue-cli 配置了babel,webpack打包时会将高级语法转为ES5,所以上线的时候没有问题。但是脚手架只是配置了一些默认常见的用法,像装饰器还需另做配置。 (可以根据babel官网配置一些尚在草案中的语法)
使用JS来模拟DOM树结构
由于每次查询DOM都会遍历整颗DOM树,相当消耗计算资源。
通过JS以对象嵌套的方式表示DOM树,每次DOM的更改就变成了JS对象属性的修改,这样就会减少性能的开销。
1.errorCaptured
当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:
1.错误对象
2.发生错误的组件实例
3.包含错误来源信息的字符串。
此钩子可以返回 false 以阻止该错误继续向上传播。
注意:只能在父组件中处理子组件的错误,没法直接在Vue的主实例中使用它。
errorCaptured(err,vm,info) {
console.log(`cat EC: ${err.toString()}\ninfo: ${info}`);
return false;
}
2.errorHandler
Vue.config.errorHandler = function(err, vm, info) {
console.log(`Error: ${err.toString()}\nInfo: ${info}`);
}
err:error对象
info:是一个Vue特有的字符串
vm:Vue应用本身。
在一个页面你可以有多个Vue应用。这个error handler作用到所有的应用。
因为如果不采用异步更新,那么每次更新数据都会对当前组件进行重新渲染.所以为了性能考虑。 Vue 会在本轮数据更新后,再去异步更新视图!
1.路由、图片懒加载
2.DNS预解析dns-prefetch
3.CDN分发
4.按需加载三方资源
5.webpack开启gzip压缩
理解:
1.在created的时候,视图中的 dom 并没有渲染出来,所以此时如果直接去操 dom 节点,无法找到相 关的元素
2.在mounted中,由于此时 dom 已经渲染出来了,所以可以直接操作 dom 节点
一般情况下都放到 mounted 中,保证逻辑的统一性,因为生命周期是同步执行的, ajax 是异步执行的
服务端渲染不支持mounted方法,所以在服务端渲染的情况下统一放到created中
(1)引入生产环境的 Vue 文件
(2)使用单文件组件预编译模板
(3)提取组件的 CSS 到单独到文件
(4)利用Object.freeze()提升性能
(5)扁平化 Store 数据结构
(6)合理使用持久化 Store 数据
(7)组件懒加载
(1)服务端渲染 / 预渲染
(2)组件懒加载
vue(整体架构) + vuex(状态管理) + vue-router(路由) + vue_resource || axios(ajax请求) + mint-UI(移动端UI框架库) || antd-vue(PC端UI框架库)
v-for的优先级比v-if高,导致每循环一次就会去v-if一次,而v-if是通过创建和销毁dom元素来控制元素的显示与隐藏,所以就会不停的去创建和销毁元素,造成页面卡顿,性能下降。
解决办法:
1.在v-for的外层包裹一个标签来使用v-if
2将需要的判断在computed里处理,然后在放到v-for里
beforedestoryed是组件销毁之前执行的一个生命周期,在这个生命周期里,我们可以进行回调函数或定时器的清除,数据初始化等
在vue2.2.0 中新增provide和inject属性
使用的方式很简单:
父组件通过provide提供数据,其他组价可以使用inject注入数据
注意
不推荐直接用于应用程序代码中。一般使用的场景是自定义组件库的时候,底层组件之间需要通信的时候使用。
格式
provide 选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的属性。
inject 选项应该是:
一个字符串数组
或 一个对象,对象的 key 是本地的绑定名(自定义的一个名字),value 是:
在provide传过来的值(字符串或 Symbol),或
一个对象,该对象的:
from 属性是provide传过来的 (字符串或 Symbol)
default 属性是降级情况下使用的 value
例子:
父组件
<template>
<div>
<h1>HelloWorldh1>
<One>One>
div>
template>
<script>
import One from "./One";
export default {
components: {
One },
// provide: {
// form: "这是父组件的provide"
// }
provide() {
return {
form: "这是父组件的provide"
};
}
};
script>
子组件1:
<template>
<div>
<h2>childOne组件h2>
{
{demo}}
<Two>Two>
div>
template>
<script>
import Two from "./Two.vue";
export default {
name: "One",
// inject: ["form"],
inject: {
form: {
default: () => ({
})
}
},
data() {
return {
demo: this.form
};
},
components: {
Two
}
};
script>
子组件2:
<template>
<div>
<h2>childtwo组件h2>
{
{demo}}
div>
template>
<script>
export default {
name: "Two",
// inject: ["form"],
inject: {
for: {
default: () => ({
})
}
},
data() {
return {
demo: this.form
// demo: "childTwo"
};
}
};
script>
在子组件上定义ref属性来获取子组件的属性和方法
// 这里是父组件
<templete>
<child ref="child"/>
templete>
<script>
method: {
getChild () {
this.$refs.child.属性名(方法名)
}
}
script>
watch:监听数据变化,并执行一个回调函数
computed:对已有的数据进行加工,具有缓存功能,只有数据发生改时,才会重新计算
能,通过@hook:进行监听代码如下:
// 这里是父组件
<template>
<child
@hook:mounted="getChildMounted"
/>
template>
<script>
method: {
getChildMounted () {
// 这里可以获取到子组件mounted的信息
}
}
script>
.stop: 阻止事件冒泡
.native: 绑定原生事件
.once: 事件只执行一次
.self:将事件绑定在自身身上,相当于阻止事件冒泡
.prevent: 阻止默认事件
.caption: 用于事件捕获
防止组件与组件之间变量的局部污染
1.errorCaptured
当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:
1.错误对象
2.发生错误的组件实例
3.包含错误来源信息的字符串。
此钩子可以返回 false 以阻止该错误继续向上传播。
注意:只能在父组件中处理子组件的错误,没法直接在Vue的主实例中使用它。
errorCaptured(err,vm,info) {
console.log(`cat EC: ${err.toString()}\ninfo: ${info}`);
return false;
}
2.errorHandler
Vue.config.errorHandler = function(err, vm, info) {
console.log(`Error: ${err.toString()}\nInfo: ${info}`);
}
err:error对象
info:是一个Vue特有的字符串
vm:Vue应用本身。
在一个页面你可以有多个Vue应用。这个error handler作用到所有的应用。
批量更新,收集当前的改动一次性更新,节省diff开销
第一步:在components目录新建你的组件文件(indexPage.vue),script一定要export default {}
第二步:在需要用的页面(组件)中导入:import indexPage from ‘@/components/indexPage.vue’
第三步:注入到vue的子组件的components属性上面,components:{indexPage}
第四步:在template视图view中使用,
例如有indexPage命名,使用的时候则index-page
webpack中提供了require.ensure()来实现按需加载。以前引入路由是通过import 这样的方式引入,改为const定义的方式进行引入。
不进行页面按需加载引入方式:import home from ‘…/…/common/home.vue’
进行页面按需加载的引入方式:const home = r => require.ensure( [], () => r (require(’…/…/common/home.vue’)))
v-show指令是通过修改元素的display的CSS属性让其显示或者隐藏
v-if指令是直接销毁和重建DOM达到让元素显示和隐藏的效果
1.beforecreate : 可以在这加个loading事件,在加载实例时触发
2.created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用
3.mounted : 挂载元素,获取到DOM节点
4.updated : 如果对数据统一处理,在这里写上相应函数
5.beforeDestroy : 可以做一个确认停止事件的确认框
6.nextTick : 更新数据后立即操作dom
什么情况下使用$set?
由于 Vue 会在初始化实例时进行双向数据绑定,使用Object.defineProperty()对属性遍历添加 getter/setter 方法,所以属性必须在 data 对象上存在时才能进行上述过程 ,这样才能让它是响应的。如果要给对象添加新的属性,此时新属性没有进行过上述过程,不是响应式的,所以会出想数据变化,页面不变的情况。此时需要用到$set。
向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新属性,因为 Vue 无法探测普通的新增属性
(比如 this.myObject.newProperty = ‘hi’)(官方示例) 我自己的理解就是,在vue中对一个对象内部进行一些修改时,vue没有监听到变化无法触发视图的更新,此时来使用$set来触发更新,使视图更新为最新的数据。
由于 JavaScript 的限制,Vue 不能检测以下数组的变动:
当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength
建议放在created里
created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。
如果在mounted钩子函数中请求数据可能导致页面闪屏问题
其实就是加载时机问题,放在created里会比mounted触发早一点,如果在页面挂载完之前请求完成的话就不会看到闪屏了
由于javascript的限制,vue不能检测对象属性的添加和删除
对于已经创建的实例,vue不能动态添加根界别的响应式属性。但是可以使用Vue.set(object,key,value)方法向嵌套对象添加响应式属性。该钩子在服务器端渲染期间不被调用。
1、计算属性必须返回结果
2、计算属性是基于它的依赖缓存。一个计算属性所依赖的数据发生变化时,它才会重新取值。
3、使用计算属性还是methods取决于是否需要缓存,当遍历大数组和做大量计算时,应当使用计算属性。
4、计算属性是根据依赖自动执行的,method需要事件调用。
computed: 计算属性是基于它们的依赖进行缓存的,只有在它的相关依赖发生改变时才会重新求值
对于 method ,只要发生重新渲染,method 调用总会执行该函数
我们先简单说下v-model的机制:v-model会把它关联的响应式数据(如info.message),动态地绑定到表单元素的value属性上,然后监听表单元素的input事件:当v-model绑定的响应数据发生变化时,表单元素的value值也会同步变化;当表单元素接受用户的输入时,input事件会触发,input的回调逻辑会把表单元素value最新值同步赋值给v-model绑定的响应式数据。
导航被触发–>在失活的组件里调用离开守卫–>调用全局beforeEach守卫–>在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)–>在路由配置里调用beforeEnter–>解析异步路由组件–>在被激活的组件里调用beforeRouteEnter–>调用全局的beforeResole守卫(2.5+)–>导航被确认–>调用全局afterEach --> 触发DOM更新 --> 用创建好的实例调用beforeRouteEnter守卫中传给next的回调函数
当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。key的作用主要是为了高效的更新虚拟DOM。
key的作用是为了在diff算法执行时更快的找到对应的节点,提高diff速度
key具有唯一性
vue中循环需加 :key=“唯一标识” ,唯一标识可以使item里面id index 等,因为vue组件高度复用增加key可以标识组件的唯一性,为了更好地区别各个组件key的作用主要是为了高效的更新虚拟DOM
不带有key,并且使用简单的模板,基于这个前提下,可以更有效的复用节点,diff速度来看也是不带key更加快速的,因为带key在增删节点上有耗时。这就是vue文档所说的默认模式。但是这个并不是key作用,而是没有key的情况下可以对节点就地复用,提高性能。这种模式会带来一些隐藏的副作用,比如可能不会产生过渡效果,或者在某些节点有绑定数据(表单)状态,会出现状态错位。VUE文档也说明了。还有就是key的作用是为了在diff算法执行时更快的找到对应的节点,提高diff速度,但是这个含有争议。
1、vuex的状态存储是响应式的,当vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会相应的得到高效更新。
2、你不能直接改变store的状态。改变store中的状态的唯一途径就是显示提交(commit)mutation。这样使得我们可以方便的跟踪每一个状态的变化。
我觉得渐进式就是不必一开始就用Vue所有的全家桶,可以根据场景,按需使用想要的插件。也可以说就使用vue不需要太多的要求。
过滤器:能够对数据进行各种过滤处理,返回需要的结果,非常的方便和快捷。
有2种:全局定义过滤器和局部定义过滤器
过滤器要想获得我们的数据,要通过一个叫做 ”管道符 | “的来获取数据
传统的Diff算法通过循环递归对节点进行比较,然后判断每个节点的状态以及要做的操作(add,remove,change),最后 根据Virtual DOM进行DOM的渲染。
Vue的Diff算法的思路只比较同级的节点,若找不到与新节点类型相同的节点,则插入一个新节点,若有相同类型的节点则进行节点属性的更新,最后删除新节点列表中不包含的旧节点。
标签有 scoped 属性时,它的 CSS 只作用于当前组件中的元素。
created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。
看实际情况,一般在 created(或beforeRouter) 里面就可以,如果涉及到需要页面加载完成之后的话就用 mounted。
这里的路由就是SPA(单页应用)的路径管理器 。再通俗的说,vue-router就是WebApp的链接路径管理系统。 vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用。vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。传统的页面应用,是用一些超链接来实现页面切换和跳转的。在vue-router单页面应用中,则是路径之间的切换,也就是组件的切换。 路由模块的本质 就是建立起url和页面之间的映射关系 。 至于我们为啥不能用a标签,这是因为用Vue做的都是单页应用,就相当于只有一个主的index.html页面,所以你写的标签是不起作用的,你必须使用vue-router来进行管理。
1、beforeCreate:可以在这加loading事件,在加载实例时触发。
2、created:初始化完成时的事件写在这里,如在这里结束loading,异步请求也适合在这里调用。
3、mounted:挂载元素,获取到dom节点。
4、updated:如果对数据统一处理,在这里写上相应的函数。
5、beforeDestroy:可以做一个确定停止事件的确认框。
先转化成AST树,再得到的render函数返回VNode(Vue的虚拟DOM节点)
1、通过compile编译器把template编译成AST语法树(abstract syntax tree 抽象语法树 即 源代码的抽象语法结构的树状表现形式),compile是createCompiler的返回值,createCompiler是用以创建编译器的。另外compile还负责合并option。
2、AST会经过generate(将AST语法树转化成render funtion字符串的过程)得到render函数,render的返回值是VNode,VNode是Vue的虚拟DOM节点,里面有(标签名、子节点、文本等等)
引入lib-flexible
1、第一步:解析模板成render函数
template
2、第二步:响应式开始监听
object.defineProperty
data属性代理到vm上
3、第三步:首次渲染,显示页面,且绑定依赖
(1)为何要监听get,直接监听set不行吗?
①data中有很多属性,有些被用到,有些可能不被用到(data中没有人访问,就不会用get,如没有{ {aaa}}指的就是aaa没有被访问)
②被用到的会走到get,不被用到的不会走到get
③未走到get中的属性,set的时候也无需关心
④避免不必要的重复渲染
4、第四步:data属性变化,触发rerender
defineProperty, get, set
(1)修改属性,被响应式的set监听到
(2)set中执行updateComponent
(3)updateComponent重新执行vm._render()
(4)生成的vnode和prevVnode,通过Patch进行对比,渲染到html
1、当vue处理指令时,当v-for,v-if处于同一节点时,v-for比v-if具有更高的优先级。这意味着v-if将分别重复运行于每个v-for循环中。
2、解决:可以将v-if放在一个包装元素内。
axios是通过promise实现对ajax技术的一种封装
ajax:
1、本身是针对MVC编程,不符合前端MVVM的浪潮
2、基于原生XHR开发,XHR本身的架构不清晰,已经有了fetch的替代方案,jquery整个项目太大,单纯使用ajax却要引入整个jquery非常不合理(采取个性化打包方案又不能享受cdn服务)
3、ajax不支持浏览器的back按钮
4、安全问题ajax暴露了与服务器交互的细节
5、对搜索引擎的支持比较弱
6、破坏程序的异常机制
7、不容易调试
axios:
1、从node.js创建http请求
2、支持Promise API
3、客户端防止CSRF(网站恶意利用)
4、提供了一些并发请求的接口
使用location.href=’/url’来跳转,简单方便,但是刷新了页面。
然后使用router.push(’/url’)来跳转,使用了diff算法,实现了按需加载,减少了dom的消耗。
使用history无刷新页面,静态跳转