Vue 实例从创建到销毁的过程,就是生命周期。从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期。
它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。
它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后。
会触发 下面这几个beforeCreate, created, beforeMount, mounted 。
DOM 渲染在 mounted 中就已经完成了。
一般 created/beforeMount/mounted 皆可.
比如如果你要操作 DOM , 那肯定 mounted 时候才能操作.
答:created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。
答:使用vue开发时,在vue初始化之前,由于div是不归vue管的,所以我们写的代码在还没有解析的情况下会容易出现花屏现象,看到类似于{ {message}}的字样,虽然一般情况下这个时间很短暂,但是我们还是有必要让解决这个问题的。
css样式里:
[v-cloak] {
display: none;
}。
如果没有彻底解决问题,则在根元素加上
style="display: none;" :style="{display: 'block'}"
使用@click.native。原因:router-link会阻止click事件,.native指直接监听一个原生事件。
Vue路由在Android机上有问题,babel问题,安装babel polypill插件解决。
根据vue-cli脚手架规范,一个js文件,一个CSS文件。
简单来说,假如父组件需要在子组件内放一些DOM,那么这些DOM是显示、不显示、在哪个地方显示、如何显示,就是slot分发负责的活。
ref=“domName” 用法:this.$refs.domName
vue文件的一个加载器,将template/js/style转换成js模块。
用途:js可以写es6、style样式可以scss或less;template可以加jade等;
assets文件夹是放静态资源;
components是放组件;
router是定义路由相关的配置;
app.vue是一个应用主组件;
main.js是入口文件。
使用location.href=’/url’来跳转,简单方便,但是刷新了页面;
使用history.pushState(’/url’),无刷新页面,静态跳转
引进router,然后使用router.push(’/url’)来跳转,使用了diff算法,实现了按需加载,减少了dom的消耗。
其实使用router跳转和使用history.pushState()没什么差别的,因为vue-router就是用了history.pushState(),尤其是在history模式下。
数据劫持 结合 发布订阅模式
VUE实现双向数据绑定的原理就是利用了 Object.defineProperty() 这个方法重新定义了对象获取属性值(get)和设置属性值(set)的操作来实现数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变;
beforeCreate(创建前)
是new Vue()之后触发的第一个钩子,在当前阶段data,methods,computed以及watch上的数据和方法都不能被访问
created(创建后)
在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发update函数。可以做一些初始数据的获取。在当前阶段无法与DOM进行交互,如果非要想,可以通过vm.$nextTick来访问dom
beforeMount(载入前)
发生在挂载之前,在这之前template模板已导入渲染函数编译。而当前阶段虚拟DOM已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发updated
mounted(载入后)
在挂载完成后发生,在当前阶段,真实的DOM挂载完毕,数据完成双向绑定,可以访问到DOM节点,使用$refs属性对DOM进行操作
beforeUpdate(更新前)
发生在更新之前,也就是响应式数据发生更换,虚拟DOM重新渲染之前被触发,可以在当前阶段进行更改数据,不会造成重渲染。
updated(更新后)
发生在更新完成之后,当前阶段组件DOM已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新。
beforeDestroy(销毁前)
在实例销毁之前调用。实例仍然完全可用。
destroyed(销毁后)
在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
接口请求一般放在mounted中,但需要注意的是服务端渲染时不支持mounted,需要放到created中
Computed本质是一个具备缓存的watcher,依赖的属性发生变化就会更新视图。适用于计算比较消耗性能的计算场景。当表达式过于复杂时,在模板中放入过多逻辑会让模板难以维护,可以将复杂的逻辑放入计算属性中处理。(购物车商品结算的时候)
Watch没有缓存,更多的是观察的作用,可以监听某些数据进行回调。当我们需要深度监听对象中的属性时,可以打开deep:true选项,这样便会对对象中的每一项进行监听。这样会带来性能问题,优化的话可以使用字符串形式监听,如果没有写到组件中,不要忘记使用unWatch手动注销。,当一条数据影响多条数据的时候就需要用watch(搜索数据)
一个组件被复用多次的话,也就会创建多个实例。本质上,这些实例用的都是同一个构造函数。如果data是对象的话,对象属于引用类型,会影响到所有的实例。所以为了保证组件不同的实例之间data不冲突,data必须是一个函数。
v-model用于表单数据的双向绑定,其实它就是一个语法糖,这个背后就做了两个操作:
v-bind绑定一个value属性;
v-on指令给当前元素绑定input事件。
// 等同于
//自html5开始,input每次输入都会触发oninput事件,所以输入时input的内容会绑定到sth中,于是sth的值就被改变;
//$event 指代当前触发的事件对象;
//$event.target 指代当前触发的事件对象的dom;
//$event.target.value 就是当前dom的value值;
//在@input方法中,value => sth;
//在:value中,sth => value;
原生事件绑定是通过assEventListener绑定给真实元素的,组件事件绑定是通过Vue自定义的$on实现的(可以用event来触发)。
想在组件监听原生事件怎么办呢?可以,在绑定原生事件的时候告诉vue,它是原生事件
由于在浏览器中操作DOM是很昂贵的。频繁的操作DOM,会产生一定的性能问题。这就是虚拟DOM的产生原因。
虚拟DOM本质就是用一个原生的JS对象去描述一个DOM节点。是对真实DOM的一层抽象
虚拟DOM映射到真实DOM要经历VNode的create.diff,patch等阶段
{
{text}}
这里如果text发生改变,整个元素会发生更新,因为当text改变时,这个元素的key属性就发生了改变,在渲染更新时,Vue会认为这里新产生了一个元素,而老的元素由于key不存在了,所以会被删除,从而触发了过渡。
总的来说:
Model代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。
View 代表UI 组件,它负责将数据模型转化成UI 展现出来。
ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步View 和 Model的对象,连接Model和View。
在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model和 ViewModel 之间的交互是双向的 , 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上
。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
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 错误
beforeEach主要有3个参数to,from,next:
to:route即将进入的目标路由对象,
from:route当前导航正要离开的路由
next:function一定要调用该方法resolve这个钩子。执行效果依赖next方法的调用参数。可以控制网页的跳转。
在 router 目录下的 index.js 文件中,对 path 属性加上 /:id,使用 router 对象的params.id获取。
单页面应用(SPA),通俗一点说就是指只有一个主页面的应用,浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。但在写的时候,还是会分开写(页面片段),然后在交互的时候由路由程序动态载入,单页面的页面跳转,仅刷新局部资源。多应用于pc端。
多页面(MPA),就是指一个应用中有多个页面,页面跳转时是整页刷新
单页面的优点:
单页面应用优点:Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件,核心是一个响应的数据绑定系统。MVVM、数据驱动、组件化、轻量、简洁、高效、快速、模块友好。
缺点:不支持低版本的浏览器,最低只支持到IE9;不利于SEO的优化(如果要支持SEO,建议通过服务端来进行渲染组件);第一次加载首页耗时相对长一些;不可以使用浏览器的导航按钮需要自行实现前进、后退。
模板中放入太多的逻辑会让模板过重且难以维护,在需要对数据进行复杂处理,且可能多次使用的情况下,尽量采取计算属性的方式
①使得数据处理结构清晰;
②依赖于数据,数据更新,处理结果自动更新;
③计算属性内部this指向vm实例;
④在template调用时,直接写计算属性名即可;
⑤常用的是getter方法,获取数据,也可以使用set方法改变数据;
⑥相较于methods,不管依赖的数据变不变,methods都会重新计算,但是依赖数据不变的时候computed从缓存中获取,不会重新计算。
一般情况下,组件进行切换的时候,默认会进行销毁,如果有需求,某个组件切换后不进行销毁,而是保存之前的状态,防止重复渲染DOM。那么就可以利用keep-alive来实现
进行到这一步,就是所有的路由组件都被缓存了,严重浪费性能,而且也不符合需求,我们现在只想缓存我想缓存的组件,那么就有以下两种方法
具体有两种方法
// 组件
export default {
name: 'test-keep-alive',
data () {
return {
includedComponents: "test-keep-alive"
}
}
}
{
path: '/',
name: 'home',
meta:{
keepAlive:true
},
component: Home
}
keep-alive代码可以结合v-if进行包裹,如果meta中的keepAlive为true进行缓存,否侧不进行缓存,这样可以更灵活一些
这样组件的缓存是实现了,但是还是会有一些问题,就是因为组件被缓存,并没有被销毁,所以组件在切换的时候也就不会被重新创建,自然也就不会调用created等生命周期函数,所以此时要使用activated与deactivated来获取当前组件是否处于活动状态
在其中一个组件中组件里面写入了activated与deactivated生命周期函数
activated(){
console.log("哎呀看见我了")
console.log("----------activated--------")
},
deactivated(){
console.log("讨厌!!你又走了")
console.log("----------deactivated--------")
}
在style标签中写入scoped即可 例如:
数据驱动、组件系统
数据驱动:ViewModel,保证数据和视图的一致性。
组件系统:应用类UI可以看作全部是由组件树构成的。
相同点:
assets和static两个都是存放静态资源文件。项目中所需要的资源文件图片,字体图标,样式文件等都可以放在这两个文件下
不相同点:
assets中存放的静态资源文件在项目打包时,也就是运行npm run build时会将assets中放置的静态资源文件进行打包上传,所谓打包简单点可以理解为压缩体积,代码格式化。而压缩后的静态资源文件最终也都会放置在static文件中跟着index.html一同上传至服务器。
static中放置的静态资源文件就不会要走打包压缩格式化等流程,而是直接进入打包好的目录,直接上传至服务器。因为避免了压缩直接进行上传,在打包时会提高一定的效率
但是static中的资源文件由于没有进行压缩等操作,所以文件的体积也就相对于assets中打包后的文件提交较大点。在服务器中就会占据更大的空间。
建议:将项目中template需要的样式文件js文件等都可以放置在assets中,走打包这一流程。减少体积。而项目中引入的第三方的资源文件如iconfoont.css等文件可以放置在static中,因为这些引入的第三方文件已经经过处理,我们不再需要处理,直接上传。
.prevent: 提交事件不再重载页面;
.stop: 阻止单击事件冒泡;
.self: 当事件发生在该元素本身而不是子元素的时候会触发
.capture: 事件侦听,事件发生的时候会调用
可以
例子:
。
它默认用“就地复用”策略。
如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,
而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。
key的作用:
①Diff算法就可以正确的识别此节点,主要是为了高效的更新虚拟DOM。
②给渲染出来的每一项数据一个唯一标识
vue用来写路由一个插件。router-link、router-view
用法:query要用path来引入,params要用name来引入,接收参数都是类似的,分别是this. r o u t e . q u e r y . n a m e 和 t h i s . route.query.name和this. route.query.name和this.route.params.name。
url地址显示:query更加类似于我们ajax中get传参,params则类似于post,说的再简单一点,前者在浏览器地址栏中显示参数,后者则不显示
注意点:query刷新不会丢失query里面的数据
params刷新 会 丢失 params里面的数据。
query:
//query传参,使用path跳转
this.$router.push({
path:'second',
query: {
queryId:'20180822',
queryName: 'query'
}
})
//query传参接收
this.queryName = this.$route.query.queryName;
this.queryId = this.$route.query.queryId;
params:
//params传参 使用name
this.$router.push({
name:'second',
params: {
id:'20180822',
name: 'query'
}
})
//params接收参数
this.id = this.$route.params.id ;
this.name = this.$route.params.name ;
mvc和mvvm其实区别并不大。都是一种设计思想。主要就是mvc中Controller演变成mvvm中的viewModel。mvvm主要解决了mvc中大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。
区别:vue数据驱动,通过数据来显示视图层而不是节点操作
场景:数据操作比较多的场景,更加便捷
轻量级框架:只关注视图层,是一个构建数据的视图集合,大小只有几十kb;
简单易学:国人开发,中文文档,不存在语言障碍 ,易于理解和学习;
双向数据绑定:保留了angular的特点,在数据操作方面更为简单;
组件化:保留了react的优点,实现了html的封装和重用,在构建单页面应用方面有着独特的优势;
单页面应用:视图,数据,结构分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作;
虚拟DOM:dom操作是非常耗费性能的, 不再使用原生的dom操作节点,极大解放dom操作,但具体操作的还是dom不过是换了另一种方式;
运行速度更快:相比较与react而言,同样是操作虚拟dom,就性能而言,vue存在很大的优势。
很缺乏高阶教程与文档
VUE不支持IE8
生态环境差不如angular和react
社区不大
如果有问题可以读源码。功能仅限于 view 层,Ajax 等功能需要额外
的库。对开发人员要求较高。开发的话,需要 webpack,不然很难用,最好配合 es6。不过Vue-cli把webpakc也隔离的差不多了
webpack中提供了require.ensure()来实现按需加载。以前引入路由是通过import 这样的方式引入,改为const定义的方式进行引入。
不进行页面按需加载引入方式:import home from ‘…/…/common/home.vue’
进行页面按需加载的引入方式:const home = r => require.ensure( [], () => r (require(’…/…/common/home.vue’)))
vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过
Object.defineProperty()
来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 { {}}),最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。
含义:Vue在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。
所以,$nextTick就像是一个延迟机制一样。来保证数据永远最新
所以,如果直接获取的话就会报错,此时应该用$nextTick
思路:点击按钮出现input框,并且主动获取焦点
data(){
return{
isShowInput:false
}
}
methods:{
showInput(){
this.isShowInput=true,
//此时如果直接运行就会报错
this.$refs.ipt.focus()
应该用$nextTick来运行
this.$nextTick(()=>{
this.$refs.ipt.focus()
})
}
}
默认插槽:当父组件中和子组件的插槽中都有值时,则默认使用父组件自己的
具名插槽(有名字的插槽)
含义:格式化变量内容的输出
简单通俗的来说:(日期格式化,字母大小写,数值再计算)
{
{message}}
//讲message传给toUpper方法
{
{message | toUpper}}
// |是管道符 打印结果:HELLO WELCOME TO MY HOME
现在vue的学习进度是:{
{count}} ({
{count | topercentage}})
//0.3(30%)
data(){
return{
message:"hello welcome to my home",
count:0.3
}
}
filters:{
//将所有字符全部转为大写
toUpper:function(value){ //value为上面的message
return value.toUpperCase()
}
//将所有数字转为百分号
topercentage:function(value){
return value*100+"%"
}
}
el 指令所绑定的元素,可以用来直接操作 DOM
binding 一个对象
vnodeVue 编译生成的虚拟节点
oldVnode上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用
核心函数是Object.defineProperty,
核心是给对象的属性做一些配置,只不过里面的set和get实现了响应式
缺陷是只能监听一个属性,所以要想监听一个对象,vue2的内部做了一个for in处理
用proxy代替了Object.defineProperty,proxy可以监听整个对象。省去for in提升效率,并且可以监听数组,不用再去单独的对数组做特异性操作