vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
具体步骤:
第一步:需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter和getter
这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
第二步:compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
第三步:Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
1、在自身实例化时往属性订阅器(dep)里面添加自己
2、自身必须有一个update()方法
3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
第四步:MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后
说一下每一个阶段可以做的事情
beforeCreate, created, beforeMount, mounted 这几个钩子
三种
// 父组件
data(){
return{
toChild: '大儿子',
fromChild: ''
}
},
methods: {
getFromChild(val){
this.fromChild=val
}
}
// 子组件
{{data}}
props:[data],
methods: {
toParent(){
this.$emit('send', '给父亲')
}
}
v-model其实是props,emit的语法糖,v-model默认会解析成名为value的prop和名为input的事件
// 父组件
{{msg}}
data(){
return{
msg:'model'
}
}
// 子组件
props: ['value'],
methods: {
toInput(e){
this.$emit('input', e.target.value)
}
}
// 父组件
data(){
return {
msg: '父组件数据'
}
},
methods: {
test(){
console.log('我是父组件的方法,被执行')
}
},
mounted(){
console.log(this.$children[0].child_msg); // 执行子组件方法
}
// 子组件
{{$parent.msg}}
data(){
return{
child_msg: '子组件数据'
}
},
mounted(){
// 子组件执行父组件方法
this.$parent.test();
}
// 父组件
data(){
return {
num: 0
}
}
// 子组件
add
data(){
return {
counter: this.count
}
},
props: ["count"],
methods: {
handleAdd(){
this.$emit('update:count', ++this.counter)
}
}
二、兄弟组件通信:可以通过查找父组件中的子组件实现,
this.$parent.$children
在$children中可以通过组件name查询到需要的组件实例,然后进行通信
跨多层次组件通信
可以使用provide/inject,虽然文档中不推荐直接使用在业务中。
假设有父组件A,然后有一个跨多层次的子组件B
// 父组件A
export default{
provide: {
data: 1
}
}
// 子组件B
export default{
inject: ['data'],
mounted(){
// 无论跨几层都能获取父组件的data属性
console.log(this.data); // 1
}
}
三、任意组件
可以用Vuex或Event Bus解决
eventBus的使用
1.新建一个bus.js文件
import Vue from 'vue';
export default new Vue();
2.使用它
添加
import Bus from 'bus.js';
export default{
methods: {
addCart(event){
Bus.$emit('getTarget', event.target)
}
}
}
// 另一组件
export default{
created(){
Bus.$on('getTarget', target =>{
console.log(target)
})
}
}
答:在模板中放入太多的逻辑会让模板过重且难以维护,在需要对数据进行复杂处理,且可能多次使用的情况下,尽量采取计算属性的方式。
好处:
①使得数据处理结构清晰;
②依赖于数据,数据更新,处理结果自动更新;
③计算属性内部this指向vm实例;
④在template调用时,直接写计算属性名即可;
⑤常用的是getter方法,获取数据,也可以使用set方法改变数据;
⑥相较于methods,不管依赖的数据变不变,methods都会重新计算,但是依赖数据不变的时候computed从缓存中获取,不会重新计算。
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 依赖的页面。”
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。
首页可以控制导航跳转,beforeEach,afterEach等,一般用于页面title的修改。一些需要登录才能调整页面的重定向功能。
beforeEach主要有3个参数to,from,next:
to:route即将进入的目标路由对象,
from:route当前导航正要离开的路由
next:function一定要调用该方法resolve这个钩子。执行效果依赖next方法的调用参数。可以控制网页的跳转。
只用来读取的状态集中放在store中; 改变状态的方式是提交mutations,这是个同步的事物; 异步逻辑应该封装在action中。
在main.js引入store,注入。新建了一个目录store,….. export 。
场景有:单页应用中,组件之间的状态、音乐播放、登录状态、加入购物车
state
Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据。
mutations
mutations定义的方法动态修改Vuex 的 store 中的状态或数据。
getters
类似vue的计算属性,主要用来过滤一些数据。
action
actions可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。view 层通过 store.dispath 来分发 action。
modules
项目特别复杂的时候,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
应用场景:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车。
组件复用时所有组件实例都会共享data,如果data是对象就会造成一个组件修改data以后会影响到其他所有组件,所以需要将data写成函数,每次用到就调用一次函数获得新的数据
当我们使用new Vue()的方式的时候,无论我们将data设置为对象还是函数都是可以的,因为new Vue()的方式是生成一个根组件,该组件不会复用,也就不存在共享data的情况
在router目录下的index.js文件中,对path属性加上/:id。 使用router对象的params.id
页面配置 :to="路由/ :id=123" 获取参数使用 this.$route.params.id
也可以使用 Vue的get取值 页面配置 :to="路由?id=123" 获取参数 this.$route.query.id
watch: {
obj: {
handler (newValue, oldValue) {
console.log('obj changed')
},
deep: true
}
}
// deep属性表示深层遍历,但是这么写会监控obj的所有属性变化,并不是想要结果,修改如下:
watch: {
'obj.a': {
handler (newName, oldName) {
console.log('obj.a changed')
}
}
}
// 还有一种方法,可以通过computed来实现,只需要
computed: {
a1 () {
return this.obj.a
}
}
//利用计算属性的特性来实现,当依赖改变时,便会重新计算一个新值。
1.减少 HTTP 请求数量
在浏览器与服务器进行通信时,主要是通过 HTTP 进行通信。浏览器与服务器需要经过三次握手,每次握手需要花费大量时间。而且不同浏览器对资源文件并发请求数量有限(不同浏览器允许并发数), 一旦 HTTP 请求数量达到一定数量,资源请求就存在等待状态,这是很致命的,因此减少 HTTP 的请求数量可以很大程度上对网站性能进行优化。
2、CSS Sprites:国内俗称 CSS 精灵,
这是将多张图片合并成一张图片达到减少 HTTP 请求的一种解决方案,可以通过 CSS background 属性来访问图片内容。这种方案同时还可以减少图片总字节数。
3、.合并 CSS 和 JS 文件:现在前端有很多工程化打包工具,
如:grunt、gulp、webpack等。为了减少 HTTP 请求数量,可以通过这些工具再发布前将多个 CSS 或者 多个 JS 合并成一个文件。
4、.采用 lazyLoad:俗称懒加载,可以控制网页上的内容在一开始无需加载,
不需要发请求,等到用户操作真正需要的时候立即加载出内容。这样就控制了网页资源一次性请求数量。
5、 控制资源文件加载优先级
浏览器在加载 HTML 内容时,是将 HTML 内容从上至下依次解析,
解析到 link 或者 script 标签就会加载 href 或者 src 对应链接内容,
为了第一时间展示页面给用户,就需要将 CSS 提前加载,
不要受 JS 加载影响。一般情况下都是 CSS 在头部,JS 在底部。
6、.利用浏览器缓存
浏览器缓存是将网络资源存储在本地,等待下次请求该资源时,
如果资源已经存在就不需要到服务器重新请求该资源,直接在本地读取该资源。
7、.减少重排(Reflow)
基本原理:重排是 DOM 的变化影响到了元素的几何属性(宽和高),
浏览器会重新计算元素的几何属性,会使渲染树中受到影响的部分失效,
浏览器会验证 DOM 树上的所有其它结点的 visibility 属性,
这也是 Reflow 低效的原因。如果 Reflow 的过于频繁,CPU 使用率就会急剧上升。
8、.减少 Reflow,
如果需要在 DOM 操作时添加样式,
尽量使用 增加 class 属性,而不是通过 style 操作样式。
减少 DOM 操作,图标使用 IconFont 替换
大致可以分为如下7步:
1.输入网址;
2.发送到DNS服务器,并获取域名对应的web服务器对应的ip地址;
3.与web服务器建立TCP连接;
4.浏览器向web服务器发送http请求;
5.web服务器响应请求,并返回指定url的数据(或错误信息,或重定向的新的url地址);
6.浏览器下载web服务器返回的数据及解析html源文件;
7.生成DOM树,解析css和js,渲染页面,直至显示完成;
mvc和mvvm其实区别并不大。都是一种设计思想。主要就是mvc中Controller演变成mvvm中的viewModel。mvvm主要解决了mvc中大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。
区别:vue数据驱动,通过数据来显示视图层而不是节点操作。
场景:数据操作比较多的场景,更加便捷
一、低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
二、可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
三、独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
四、可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
第一步:在components目录新建你的组件文件(如:indexPage.vue),script一定要export default {}
第二步:在需要用的页面(组件)中导入:import indexPage from '@/components/indexPage.vue'
第三步:注入到vue的子组件的components属性上面,components:{indexPage}
第四步:在template视图view中使用,
例如有indexPage命名,使用的时候则index-page
0 - (未初始化)还没有调用send()方法
1 - (载入)已调用send()方法,正在发送请求
2 - (载入完成)send()方法执行完成,已经接收到全部响应内容
3 - (交互)正在解析响应内容
4 - (完成)响应内容解析完成,可以在客户端调用了