虚拟dom:dom操作时非常消耗性能,不再使用原生的dom操作,极大的释放了dom操作,但本质还是操作dom,只是换了一种方式。
视图,数据,结构分离:使数据的修改变得更为简单。不需要修改逻辑代码只需要操作数据即可
组件化:将一个单页面的各种模块拆分到不同的组件中,便于开发以及后期的维护管理
model-view-viewmodel的缩写,mvvm把视图和逻辑代码区分开。
在mvvm的框架下,view和model没有直接的交互,是通过view-model进行数据的双向交互。
viewmodel通过双向数据绑定,将model和view连接起来,因此model和view的数据同步工作时完全自动的。
开发者只需要关注业务逻辑,不需要手动操作dom,也不需要关注数据的同步问题,这些由mvvm统一管理
1、简述MVVM和MVC
MVC:
Model(模型)
View(视图)
Controller(控制器)
简单的理解:视图请求数据,将请求发送至控制器,控制器再将请求发送给模型,模型去查找数据,找到之后传给控制器,控制器再传给视图进行渲染。
MVVM
Model 代表数据模型
View 代表UI视图
ViewModel 负责监听 Model 中数据的改变并且控制视图的更新(桥梁,可以理解成mvc中的控制器)
简单理解:视图请求数据,将请求发送至控制器,在控制器的两端具有监听机制,直接调用模型的数据,一端改变全部改变,利用数据劫持,结合订阅者和发布者模式,实现数据的双向绑定
在子组件中创建模板,在props中接受父组件传递的数据,在父组件中引入子组件,然后在components
中创建子组件,挂载到父组件的template
中
props 父传子使用 $emit(参1,参2)
子传父时使用 参1是自定义的事件名,参2是要传递的数据,在父组件的子组件中绑定自定义事件名,调用一个函数,函数的形参就是传递的数据
$children
获取的是一个数组 $parent
获取的是一个对象
this.$children[0].msg = "hello world" //父组件修改子组件data中的数据
this.$parent.msg //子组件拿到父组件data中的数据
eventBus 总线程
兄弟传参时使用 在传数据的一方引入bus,通过函数调用
this.emit(参1,参2)
,参1是自定义的事件名,参2是要传递的数据,
在接收的一方引入bus,在created中调用this.$on(自定义事件名,回调函数(data){})
data就是传递的数据
父传递子如何传递
(1)在父组件的子组件标签上绑定一个属性,挂载要传输的变量
(2)在子组件中通过props来接受数据,props
可以是数组也可以是对象,接受的数据可以直接使用 props:["属性 名"] props:{属性名:数据类型}
子传递父如何传递
(1)在父组件的子组件标签上自定义一个事件,然后调用需要的方法
(2)在子组件的方法中通过 this.$emit("事件")
来触发在父组件中定义的事件,数据是以参数的形式进行传递的
兄弟组件如何通信
(1)在src中新建一个Bus.js的文件,然后导出一个空的vue实例
(2)在传输数据的一方引入Bus.js 然后通过Bus.$emit(“事件名”,"参数")
来来派发事件,数据是以$emit()
的参数形式来传递
(3)在接受的数据的一方 引入 Bus.js 然后通过 Bus.$on("事件名",(data)=>{data是接受的数据})
ref/refs
区别在于ref绑定在dom元素上,引用的就是dom元素,refs在组件的实例化调用获取到ref传递的dom元素内容
$attrs / $listeners
将数据挂在到子组件的标签上去后,在子组件中使用this.$attrs直接获取到所有挂载的数据,返回的是一个对象
使用的原因是Vue是异步修改DOM的,并不鼓励开发者直接接触DOM
但有时需要对数据更改后的DOM元素做相应的处理,但获取的数据不是修改之后的,所以要使用this.$nextTick()
原理是Vue通过异步队列控制DOM的更新和nextTick回调函数的先后执行顺序
<button @click="change()">按钮</button>
<h1 ref="gss">{{msg}}</h1>//JS
export default{
name:"app",
data(){
return {
msg:"123"
}
},
methods:{
change(){
this.msg = "456";
console.log(this.refs["gss"].innerHTML)//123
this.$nextTick(function(){
console.log(this.refs["gss"].innerHTML)//456
})
}
}
}
beforeCreate(创建前):在此生命周期函数里,data 和 methods 的数据还没有初始化
created(创建后):在此生命周期内,data和methods中的数据已经初始化完毕,最早使用data数据的钩子函数
beforeMount(载入前):在此生命周期函数里,模板已经在内存中编译好了,但还没有挂载到页面,页面此时是旧的
mounted(载入后):此时页面已经渲染完毕,这个是最早可以操作dom的钩子函数
beforeUpdate(修改前):页面显示的数据旧的,data的数据是新的
updated(修改后):页面于data的数据已经同步
beforeDestroy(销毁前):该钩子函数执行的时候,数据还可以使用
destroyed(销毁后):数据已经销毁完毕
activated(keep-alive组件激活调用)
deactivated(keep-alive组件停用调用)
errorcapture(捕获来自子孙组件错误是调用)
虚拟dom就是用对象的方式区代真实的dom操作。
当页面打开时浏览器解析HTML元素,构建一个dom树,将状态保存起来,在内存中模拟dom操作,又会生成一个dom树,两个进行比较,根据diff算法找出不同的地方,之渲染一次不同的地方
虚拟dom是利用js描述元素与元素的关系。
好处:是可以快速的渲染和高效的更新元素,提高浏览器的性能
vue的双向数据绑定只要是采用数据劫持结合开发者和订阅者方式
vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式。
通过Object.defineProperty()
来劫持各个属性的setter
,getter
, 在数据变动时发布消息给订阅者,触发相应监听回调
vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer
,Compile
和Watcher
三者。 通过Observer
来监听自己的model
的数据变化,通过Compile
来解析编译模板指令。
最终利用watcher
搭起observer
和Compile
之间的通信桥梁,达到数据变化 —>视图更新;。
数据劫持:当我们访问或设置对象的属性的时候,都会触发相对应的函数,然后在这个函数里返回或设置属性的值。我们可以在触发函数的时候动一些手脚做点我们自己想做的事情,这也就是“劫持”操作
vue中的v-model可以实现双向绑定,其核心思想通过Object.definePropery
来对Vue的数据进行数据劫持,
主要分为三部分
observer
主要是负责对Vue数据进行数据劫持,使其数据拥有get和set方法
指令解析器负责绑定数据和指令,绑定试图更新方法
watcher
负责数据监听,当数据发生改变通知订阅者,调用视图更新函数更新视图
Proxy相比于defineProperty的优势 Vue3.0摒弃了Object.defineProperty
,改为基于Proxy的观察者机制探索
Object.defineProperty
无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实施响应。
Object.defineProperty
只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。 如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象才是更好的选择。
而要取代它的Proxy有以下两个优点: 可以劫持整个对象,并返回一个新对象。 有多种劫持操作(13种)
methods在重新渲染的时候每次都会被重新的调用
computed 是自动监听依赖值的变化,从而动态返回内容,主要目的是简化模板内的复杂运算
watch也可以影响数据的变化,当绑定的数据方法变化时触发响应的函数, 需要在数据变化时执行异步或开销较大的操作时使用watch。
virtual-dom(简称vdom)的概念得益于react的出现react这个框架的非常重要的特性之一。
相比于频繁的手动去操作dom而带来性能问题,vdom很好的将dom做了一层映射关系 进而将在我们本需要直接进行dom的一系列操作,映射到了操作vdom,而vdom上定义了 关于真实dom进行的创建节点,删除节点,添加节点等一系列复杂的dom操作, 并将这些操作放到vdom中进行,这样就通过操作vdom来提高直接操作的dom的效率和性能。
在vue的整个应用生命周期当中,每次需要更新视图的时候便会使用vdom
vdom算法是基于snabbdom算法所做的修改。
实现:
①用js对象构造一个虚拟的dom树,插入到文档中;
②状态变更时,记录新树和旧树的差异;
③把上面的差异构建到真正的dom中
单页面的跳转
hash
:使用URLhash 的值来作为路由
history
:依赖于HTML5 History API和服务器配置
abstract
:严格模式支持所有的JavaScript运行环境根据model参数来决定时那种方式
原理是更新视图但不重新请求页面
vuex是专门为vue.js应用程序开发的状态管理工具。
它采用集中式储存管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
优点: 当state中定义一个数据后,可以在所在项目的任何组件中进行获取,修改,修改可以得到全局的变更
运行机制:vue提供数据来驱动试图,听过dispath
派发actions
,通过commit
调用mutations
的方法,修改state的数据
①state:定义初始数据。
②mutations:更改Vuex的store
中的状态的唯一方法是提交mutation
③getters:可以对 state
进行计算操作,它就是 store
的计算属性虽然在组件内也可以做计算属性
④actions:异步操作初始数据,其实就是调用mutations
里面的方法。
⑤module:面对复杂的应用程序,当管理的状态比较多时;我们需要将vuex的store对象分割成模块(modules)。
Vuex的映射: state getters
需要映射在computed
实例中,mutations actions
等需要放在methods实例中
当vue进入初始化页面的时候会遍历data的属性,通过object.defineproperty
转换成getet/setter
形式,实现数据劫持,
compile对元素的指令进行解析,初始化视图,watcher
来更新视图,watcher
会添加到Dep
中,初始化完毕数据发生改变时,触发observer
的setter
方法,
调用Dep.notfiy()
,Dep数组开始遍历所有的订阅者,调用update
方法,在通过diff算法,完成视图的改变
Vue实例创建时,vue会遍历data选项的属性,用 Object.defineProperty
将它们转为 getter/setter
,在属性被访问和修改时通知变化。 每个组件实例都有相应的 watcher 程序实例,它会在组件渲染的过程中把属性记录作为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。
轻量级的框架组件化 和 响应式设计实现数据与结构的分离数据的双向绑定 优点是减少了dom操作数据驱动
插槽就是子组件中的提供给父组件使用的一个占位符,用
标签接收,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的标签。
子组件
<template>
<div>
<h1>商品列表</h1>
<solt></solt>
</div>
</template>
<script>
export default {
name:'child',
}
</script>
父组件
<template>
<div>
<h1>商品列表</h1>
<child>
<div>苹果,香蕉</div>
</child>
</div>
</template>
① 全局导航钩子:一般用来判断权限,以及页面丢失时需要执行的操作;
beforeEach()每次路由进入之前执行的函数。
afterEach()每次路由进入之后执行的函数。
beforeResolve()2.5新增
② 单个路由(实例钩子)
beforeEnter()
beforeLeave()
③ 组件路由钩子:
beforeRouteEnter()
beforeRouteLeave()
beforeRouteUpdate()
data是一个函数时,每个组件实例都有自己的作用域,每个实例相互独立,不会相互影响。
Object是引用数据类型,如果不用function返回,每个组件的data都是内存的同一个地址,一个数据改变了其他也改变了
在单页应用中,如果没有应用懒加载,运用webpack
打包后的文件很大,进入首页时,加载的内容过多,不利于用户体验。而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力。
原理:vue异步组件技术:异步加载,vue-router配置路由 , 使用vue的异步组件技术 , 实现按需加载
1
component:(resolve) => {
require(["@/components/HelloWorld"],resolve);
}
2
const info = () => import("@/components/info");
3
const info = (resolve) => {
import("@/components/info").then(modul => {
resolve(modul);
}
)}
4
const info = r => require.ensure([],() => r(
require("@/components/info")
),"info");
vue就是一个js库,并且无依赖别的js库。
vue的核心库只关注视图层,非常容易与其它库或已有项目整合。
Vue.js是一个轻巧、高性能、可组件化的MVVM库,同时拥有非常容易上手的API。
vue中的scoped通过在DOM结构以及css样式上加唯一不重复的标记:data-v-hash的方式,以保证唯一,达到样式私有模块化的目的。
scoped穿透:/deep/ >>>
asstes 静态资源文件
components 组件
router 路由的配置
view 视图
app.vue 应用主组件
main.js 入口文件
主要是为了高效的更新虚拟DOM。
使用Object.assign()
,vm.$data
可以获取当前状态下的data,
vm.$options.data
可以获取到组件初始化状态下的data。
Object.assign(this.$data, this.$options.data())
项目使用keep-alive
时,可搭配组件的name
进行缓存过滤。
DOM做递归组件时需要调用自身name
vue-devtools调试工具里显示的组件名称是由vue中组件name
决定的
route是“路由信息对象”,包括path
,params
,hash
,query
,fullPath
,matched
,name
等路由信息参数。router是“路由实例对象”,包括了路由的跳转方法(push、go)
,钩子函数等。
监听数据变化的实现原理不同:Vue通过getter/setter
以及函数的劫持,能快速的计算出Vdom的差异,这是它在渲染的过程中,会跟踪每个组件的依赖关系,不需要重新渲染组件树。react是默认通过比较的引用方式进行,如果不优化,每当应用的状态被改变时,所有的子组件页时重新渲染,导致大量的不必要的vdom重新渲染
数据流的不同:Vue默认双向绑定数据,组件和dom动过v-model双向绑定。但是父子组件之间,props在2x版本中时单项的数据流,react一直提倡单向数据流
模板渲染的方式不同:react是通过JSX渲染模板,vue是通过拓展HTML语法进行渲染
不长改变的库放到index.html中,用cdn引入然后找到 build/webpack.base.conf.js 文件,
在 module.exports = { } 中添加以下代码:
externals: {
'vue': 'Vue',
'vue-router': 'VueRouter',
'element-ui': 'ELEMENT',
}
路由懒加载
vue的组件尽量不要全局引入使用更轻量级的工具库
开启gzip压缩:这个优化是两方面的,前端将文件打包成.gz文件,然后通过nginx的配置,让浏览器直接解析.gz文件。
不生成map文件,找到config/index.js文件,修改为productionSourcceMap:false
新的API
setup()函数
关于Typescript的支持
替换Object.defineProperty
为 Proxy 的支持。关于Proxy代替带来的性能上的提升,因为传统的原型链拦截的方法,无法检测对象及数组的一些更新操作,但使用Proxy又带来了浏览器兼容问题。
vue-cli是基于 Vue.js 进行快速开发的完整系统,也可以理解成是很多 npm 包的集合。
.vue 文件 --> .js 文件
ES6 语法 --> ES5 语法
Sass,Less,Stylus --> CSS
对 jpg,png,font 等静态资源的处理
热更新
定义环境变量,区分 dev 和 production 模式
如果开发者需要补充或修改默认设置,需要在 package.json 同级下新建一个 vue.config.js 文件
v-for比v-if优先,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候。
使用babel-polyfill插件
<template>
<h1>{{arr}}</h1>
<button @click="change()">点击</button>
</template>
<script>
export default {
data(){
return {
arr:[1,2,3]
}
},
methods:{
change(){
this.arr[1]=0;
console.log(this.arr); //[1,0,3]
}
}
}
</script>
当我们点击按钮想要根据数组arr的下标改变其元素的时候,你会发现data中的数据改变了,但是页面中的数据并没有改变。这时候就需要$set出场了。
change(){
this.$set(this.list,1,0);
}
改变/添加 对象属性的时候:
this.$set(data实例,“属性名(添加的属性名)”,“属性值(添加的属性值)”)
改变/添加 数组属性的时候:
this.$set(data实例,数组下标,“改变后的元素(添加的元素)”)
vue在创建实例的时候把data深度遍历所有属性,并使用 Object.defineProperty
把这些属性全部转为 getter/setter
。
让 Vue 追踪依赖,在属性被访问和修改时通知变化。
所以属性必须在 data 对象上存在才能让 Vue 转换它,这样才能让它是响应的。
当你在对象上新加了一个属性newProperty,当前新加的这个属性并没有加入vue检测数据更新的机制,vue.$set
是能让vue知道你添加了属性, 它会给你做处理
dependencies 是生产环境
devDependencies 是开发测试环境
new Vue({
el: "#app",
data:{
},
methods:{
}
})
v-html 解析输出变量 能解析html
v-text 解析输出变量
v-bind 给标签绑定属性 可以简写为 :属性名=“变量”
v-on 给元素绑定事件 用法 v-on:事件名=“方法名” 可以简写为@事件名=“方法”
v-pre 跨过当前的标签不解析 用法 :<标签 v-pre></标签>
v-cloak 解决差值表达式闪烁的问题
v-model 实现数据的双向绑定 只能适用于表单元素
v-for 可以循环遍历数据
v-if 条件输出
v-show条件输出
.stop 阻止事件冒泡
.capture 设置事件捕获
.self 只有当事件作用在元素本身才会触发
.prevent 阻止默认事件,比如超链接跳转
.once 事件只能触发一次
.keyup.enter
.keyup.space
区别:
1、当条件为真的时候 没有区别 当条件为假的时候 v-if是不创建元素 v-show 渲染元素然后隐藏掉
2、v-if更适合数据的筛选和初始渲染 v-show更适合元素的切换
bind(){} 只调用一次,指令第一次绑定到元素时调用
inserted(){}被绑定元素插入父节点时调用
update(){}被绑定元素所在的模板更新时调用,而不论绑定值是否变化
componentUpdated(){}被绑定元素所在模板完成一次更新周期时调用
unbind(){}只调用一次, 指令与元素解绑时调用
Vue是一个MVVM的渐进式框架,,渐进式指的是先使用Vue的核心库,然后再根据需求逐渐增加所需要的插件,慢慢的组织成自己的项目,
我们在使用过程中主张最小化,Vue没有强主张,插件使用比较灵活,也就是没有做职责之外的事。