接上篇,这里继续知识点的总结
Vue部分:
Vue和Jquery相比的优缺点:
- Vue适用的场景:复杂数据操作的后台页面,表单填写及验证页面;
- Vue侧重数据绑定;(框架)
- jQuery适用的场景:html5的动画页面,需要js来操作页面样式的页面;
- jQuery侧重样式操作以及动画效果等。(类库)
Vue和React的区别:
- 监听数据变化的实现原理不同:
Vue通过getter/setter以及数据劫持来实现监听数据的变化 React则是通过比较引用(diff算法)来实现,如果不优化可能导致大量不必要的VDOM的重新渲染。 两者设计理念不同,Vue使用的是可变的数据,React则强调数据的不可变,Vue更加简单,React构建大型应用的时候更加鲁棒。 - 数据流向不同
Vue2.0中组件与DOM可以通过v-model双向绑定,2.0中不再支持父子组件之间通过props双向绑定,不鼓励组件对props进行修改,会警告。 React提倡单向数据流,使用Redux进行数据流状态管理 - 组件间的通信方式不同
Vue的组件通信由三种方式:父组件通过props向子组件传递数据或者回到,子组件通过事件想父组件传递消息,通过2.2.0中新增的provide/inject实现父组件向子组件注入数据,可以跨越多个层级。 React的组件通信有三种方式:父组件通过props可以向子组件传递数据或者回调,可以通过context进行跨层级的通信,这其实和provide/inject起到的作用差不多,React本身不支持自定义时间,React中子组件向父组件传递消息使用回调函数。 - 模板渲染方式不同
React通过JSX渲染模板,Vue通过拓展的HTML语法进行渲染。 React是在组件的js代码中,通过原生js来实现模板中的常见语法,比如插值,条件,循环等等,更加纯粹原生,而Vue是在和组件js代码分离的单独的模板中通过指令来实现的。 - 渲染过程不同
Vue可以更快计算出虚拟DOM的差异,因为渲染过程中,Vue会收集依赖,然后根据注册的组件渲染对应的DOM,不需要重新渲染每一个组件 React在应用的状态改变时,全部子组件都会重新渲染。 - 框架本质不同
Vue本质是MVVM框架,由MVC发展而来; React是前端组件化框架,由后端组件化发展而来。 - Vuex和Redux的区别
Redux使用的是不可变数据,而Vuex的数据是可变的,因此,Redux每次都是用新state替换旧state,而Vuex是直接修改。Redux在检测数据变化的时候,是通过diff的方式比较差异的,而Vuex其实和Vue的原理一样,是通过getter/setter来比较的,这两点的区别,也是因为React和Vue的设计理念不同。React更偏向于构建稳定大型的应用,非常的科班化。相比之下,Vue更偏向于简单迅速的解决问题,更灵活,不那么严格遵循条条框框。
组件间的通信:
父组件向子组件传值总结:
- 子组件在props中创建一个属性,用以接收父组件传过来的值
- 父组件中注册子组件
- 在子组件标签中添加子组件props中创建的属性
- 把需要传给子组件的值赋给该属性
子组件向父组件传值总结:
- 子组件中需要以某种方式例如点击事件的方法来触发一个自定义事件
- 将需要传的值作为$emit的第二个参数,该值将作为实参传给响应自定义事件的方法
- 在父组件中注册子组件并在子组件标签上绑定对自定义事件的监听
在通信中,无论是子组件向父组件传值还是父组件向子组件传值,他们都有一个共同点就是有中间介质,子向父的介质是自定义事件,父向子的介质是props中的属性。抓准这两点对于父子通信就好理解了
vue兄弟组件之间的传值:
- 创建一个事件总线,例如demo中的eventBus.js,new一个vue对象并向外暴露,用它作为通信桥梁
2.在需要传值的组件中引入eventBus.js,用
bus.$emit触发一个自定义事件,并传递参数 - 在需要接收数据的组件中引入eventBus.js,在mounted() 钩子函数(挂载实例) 中用
bus.$on监听自定义事件,并在回调函数中处理传递过来的参数
Vue常用指令
v-once:能执行一次性地插值,当数据改变时,插值处的内容不会更新。v-show:和v-if一样,区别是if是将代码注释掉,v-show是给一个display:none的属性 让它不显示。v-if 有更高的切换消耗而 v-show 有更高的初始渲染消耗。因此,如果需要频繁切换使用 v-show 较好,如果在运行时条件不大可能改变则使用 v-if 较好。v-for:基于数据渲染一个列表,类似于JS中的遍历。其数据类型可以是 Array | Object | number | string。v-html:双大括号会将数据解释为普通文本,而非HTML 代码。为了输出真正的 HTML,你需要使用 v-html 而且给一个标签加了v-html 里面包含的标签都会被覆盖。v-text:给一个便签加了v-text 会覆盖标签内部原先的内容v-bind:动态属性绑定,缩写:v-on:动态事件绑定,缩写@v-model:双向数据绑定,限制在<input>、<select>、<textarea>、components中使用
Vue的自定义指令
在组件中定义自定义指令,使用directives属性
全局自定义指定
钩子函数
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。unbind:只调用一次,指令与元素解绑时调用。
指令钩子函数会被传入以下参数:
el:指令所绑定的元素,可以用来直接操作 DOM 。binding:一个对象,包含以下属性:name:指令名,不包括 v- 前缀。value:指令的绑定值,例如:v-my-directive=“1 + 1” 中,绑定值为 2。oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。expression:字符串形式的指令表达式。例如 v-my-directive=“1 + 1” 中,表达式为 “1 + 1”。arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 “foo”。modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。vnode: Vue编译生成的虚拟节点。移步 VNode API 来了解更多详情。oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
Vue的生命周期
一个组件从开始到最后消亡所经历的各种状态,就是一个组件的生命周期
- beforeCreate()
说明:在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用
注意:此时,无法获取 data中的数据、methods中的方法 - created()
说明:这是一个常用的生命周期,可以调用methods中的方法、改变data中的数据 - beforeMounted()
说明:在挂载开始之前被调用,此时无法获取到el中的DOM元素 - mounted()
说明:此时,vue实例已经挂载到页面中,可以获取到el中的DOM元素,进行DOM操作 - beforeUpdated()
说明:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
注意:此处获取的数据是更新后的数据,但是获取页面中的DOM元素是更新之前的 - updated()
说明:组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作 - beforeDestroy()
说明:实例销毁之前调用。在这一步,实例仍然完全可用。
使用场景:实例销毁之前,执行清理任务,比如:清除定时器等 - destroyed()
说明:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
Vue父子组件生命周期执行顺序
- 加载渲染过程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted - 子组件更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated - 父组件更新过程
父beforeUpdate->父updated - 销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
Vuex是什么?怎么使用?那种功能场景使用它
只要来读取的状态集中放在store中;改变状态的方式就是提交mutations。这是个同步的实物;异步逻辑应该封装中action中。
- state:Vuex 使用单一状态树,既每个应用将仅仅包含一个store实例,单单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据。可以通过this.$store.state调用state中的参数。
- mutations:mutations定义的方法动态修改Vuex的store中的状态或数据。Mutatison中必须是同步函数。可以通过this.$store.commit('increment',args)来调用并更改state中的参数状态
- getters:类似vue的计算属性,主要用来过滤一些数据。
- action:action可以理解为通过mutations里面处理数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。通过this.$store.dispatch('increment')来调用action方法。Action提交的是mutation,而不是直接改变装填,action可以包含异步操作 Modeules:项目特别复杂的时候,可以让每一个模块拥有自己的state,mutation,action,getters,使得结构非常清晰,方便管理
Vuex中的数据流向:
- vue组件先调用dispatch 来触发Actions做些异步处理或批量的同步操作,紧接着Actions通过提交commit来调用Mutations , Mutations 中放的是一个一个同步的对state的修改,只有通过mutations才能改变公用数据的值。最后通过this.$store.state.id的方法将值映射到页面上。
- 如果逻辑简单,vue 组件也可以略过actions, 让组件直接调用mutations来修改state的公用数据的值
vue-router的概念与使用:
vue-router路由管理器
vue router和vue.js的核心深度集成,可以方便的用于spa的应用程序开发
它的功能有:
支持HTML5 history模式,和hash模式;支持嵌套路由;支持路由参数,支持编程式路由,支持命名路由。
路由的进阶,导航守卫,路由元信息,过渡效果,数据获取,滚动行为,路由懒加载。
vue-router的基本使用
基本使用步骤:
- 第一步,引入相关的库文件,
<script src="./lib/vue-routerxxx.js"></script> - 第二步,添加路由连接,
<router-link to="/user">User</router-link> - 第三步,添加路由填充位,
<router-view></router-view> - 第四步,定义路由组件,
var User = {template: '<div>user</div>'} - 第五步,配置路由规则并创建路由实例,
路由重定向
路由重定向值的是,用户在访问地址a的时候,强制用户跳转到地址c,从而展示特定的组件页面,通过路由规则的redirect属性,指定一个新的路由地址,可以方便地设置路由的重定向。
var router = new VueRouter({
routers: [
// 其中,path表示需要被重定向的原地址,redirect表示将要被重定向的新地址
{path;'/', redirect: '/user'},
{path:'/user', component: User},
{path:'/register', component:Register}
}
})
vue-router动态路由匹配(传参)
什么是动态路由匹配,为啥要动态路由匹配?即通过路由传参。
路由传参的三种方式:
- 直接调用router.push 中path携带的参数。在子组件中可以使用来获取传递的参数值。
getDescribe(id) {
this.$router.push({
path: `/describe/${id}`,
})
{
path: '/describe/:id',
name: 'Describe',
component: Describe
}
//对应子组件: 这样来获取参数 this.$route.params.id
- 父组件中:通过路由属性中的name来确定匹配的路由,通过params来传递参数。
this.$router.push({
name: 'Describe',
params: {
id: id
}
})
{
path: '/describe',
name: 'Describe',
component: Describe
}
//对应子组件: 这样来获取参数 this.$route.params.id
- 父组件:使用path来匹配路由,然后通过query来传递参数,这种情况下 query传递的参数会显示在url后面?id=?
this.$router.push({
path: '/describe',
query: {
id: id
}
})
//对应子组件: 这样来获取参数 this.$route.query.id
什么叫做命名路由
路由的name可以指定命名名称,不用写path。命名路由的配置规则
// 路由导航
const router = new VueRouter({
routes: [
{
path: '/user/id',
name: 'user',
component: User
}
]
})
<router-link :to="{name:'user', params: {id:1} }">dada</router-link>
router.push({name:'user', params: {id:1} }}
vue-router 路由导航守卫
- 全局守卫
使用router.beforeEach 注册一个全局的路由守卫
当一个导航守卫触发时,全局前置守卫按照创建顺序调用,守卫是异步解析执行,此时导航在所有守卫resolve完成之前一直处于等待状态中
全局后置钩子 router.afterEach((to,from)=){})
全局后置钩子就是在路由跳转之后执行的钩子函数
页面独享的守卫
如下可以在路由的配置上面定义beforeEnter()
- 组件内的守卫
通过meta属性实现要监听的路由元信息
- 设置页面是否被缓存
钩子函数的执行顺序
- 不使用keep-alive
beforeRouteEnter --> created --> mounted --> destroyed - 使用keep-alive
beforeRouteEnter --> created --> mounted --> activated --> deactivated
再次进入缓存的页面,只会触发beforeRouteEnter -->activated --> deactivated 。created和mounted不会再执行。因为这个页面需要缓存。只有第一次进入时才会执行created和mounted方法,再次进入就不执行了。而activated每次进入都执行,所以在这个钩子函数中获取数据。
Vue-router中的两种导航模式
- hash模式。
#后面的hash的变化,不会导致浏览器向服务器发送请求,浏览器不发出请求,也就不会刷新页面,每次hash值的变化,会触发hashchange这个时间,通过这个时间我们可以知道hash值发生了什么变化,可以监听hashchange来实现更新页面部分内容的操作,这就是前端路由的实现原理。 而且hash发生变化的url会被浏览器记录下来,所以浏览器的前进后退等功能都可以用了。页面状态和url关联起来
特点:hash值虽然出现在url中,但是不会被包括在http请求中,对后端完全没有影响,所以改变hash不会重现加载页面 - History模式
Url以/分割,单页面没有跳转,这种模式需要服务端支持,服务端在接收到所有请求后,都指向同一个html文件,不然404,单页面应用只有一个html,整个网站的内容都在html中,因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问www.yongcun.wang/tclass就会返回 404,这就不好看了。这需要在后台配置匹配不到资源的时候返回主页面。
History模式利用了html5中新增的pushState()和replaceState()方法,这两个方法可以改变url且不会发送请求,不仅可以读取历史记录栈,还可以对浏览器的历史记录栈进行修改,以上两个方法用于修改历史状态,还有back,froward,go三个方法用来切换历史状态。
Vue默认使用hash模式,可以设置成history模式
区别:
hashchange,你只能改变#后面的url片段。而pushState设置的新URL可以是与当前URL同源的任意URL
history模式则会将URL修改得就和正常请求后端的URL一样,如后端没有配置对应/user/id的路由处理,则会返回404错误
route的区别
- route.query
和this.$route.params获取`中的动态参数 - router.push(url)
方法,返回上一个history也要使用$router.go(-1)`方法
MVVM和MVC
Vue 最独特的特性
- MVVM是model-view-viewmodel的缩写
- Model代表数据模型,可以在model中定义数据的修改和操作的业务逻辑
- View代表UI组件,负责将数据模型转化成UI展示出来
- ViewModel监听模型数据的改变和控制视图行为,处理用户交互,简单理解就是一个同步View和Model的对象。
- 在MVVM架构下,model和view并没有直接的联系,而是通过VIewModel进行交互,model和viewmodel之间的交互是双向的,因此view的数据变化会同步到model中,而model的数据变化也会反映到view上
观察者模式与发布订阅模式
观察者模式:
定义了对象间一种一对多的关系,当目标对象Subject的状态发生改变时,所有依赖他的对象observer都会得到通知
模式特征:
- 一个目标者对象Subject,拥有方法:添加/删除/通知Observer
- 对个观察者对象Observer,拥有方法:接收Subject状态变更通知并处理
- 目标对象Subject状态变更时,通知所有Observer
- Subject添加一系列Observer,Subject负责维护这些Observer之间的联系。
优点:目标者与观察者,功能上的关联性降低,专注自身的功能逻辑,观察者被动接收更新,时间上的关联性降低,实时接收目标更新状态
缺点:观察者模式虽然减少了对象间的依赖关系,但却不能对事件通知进行细分管控,如筛选通知,指定主题事件通知。
发布订阅模式:
- 基于一个事件通道,希望接收通知的对象Subscriber通过各自定义一个事件来订阅对应的主题,被激活事件的对象Publisher通过发布主题事件的方式通知各个订阅该主题的Subscriber对象。
- 发布订阅模式与观察者模式不同,存在事件中心这个模块,目标对象的变化不是直接通知观察者,而是通过事件中心来对订阅了该对象的观察进行通知派发,而且要先订阅后发布时才会通知。
观察者模式是为了实现对象间的解耦,是松耦合的,而发布订阅模式则是完全解耦的。
Vue2.0双向数据绑定的原理
Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。
那么,Vue是如何实现的呢?
vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。当把一个普通的js对象传给vue实例来作为它的data选项时,vue将遍历它的属性,用object.defineproperty()将它们转化为getter/setter,用户看不到getter/setter,但是内部它们让vue追踪依赖,并在属性被访问和修改时通知变化
整体思路
通过Object.defineProperty()来实现对属性的劫持,达到监听数据变动的目的
要实现mvvm的双向绑定,就必须要实现以下几点:
- 实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
- 实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
- 实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
- mvvm入口函数,整合以上三者
Vue响应式原理
总共分为三步骤:
- init 阶段: Vue 的 data的属性都会被reactive化,也就是加上 setter/getter函数。其中这里的Dep就是一个观察者类,相当于发布订阅模式中的事件中心这个模块,每一个data的属性都会有一个dep对象。当getter调用的时候,去dep里注册函数。setter的时候,就是去通知执行刚刚注册的函数。
- mount 阶段:mount 阶段的时候,会创建一个Watcher类的对象。这个Watcher实际上是连接Vue组件与Dep的桥梁。每一个Watcher对应一个vue component。
new watcher的时候会在watch中生成一个updateComponent函数,这个函数会调用render函数来重新渲染对应的component。
当render一个vue组件的时候,如果这个组件用到了某个属性title,那么这个组件对应的watcher对象会被注册到这个属性title的dep对象中,这就是收集依赖,收集完属性所有的依赖组件之后,当这个属性title发生改变之后,就会找到dep中注册过的所有的watcher对象,通知他们执行updateComponent函数来更新对应的组件
在组件更新的时候,render函数里,会访问他所依赖的data的getter函数。 - 更新阶段:
当某个属性title发生改变的时候,就去调用Dep的notify函数,然后通知所有的Watcher调用updateComponent函数更新。
总结
第一步:组件初始化的时候,先给每一个data属性都注册getter,setter,也就是reactive化。然后再new 一个自己的Watcher对象,此时watcher会立即调用组件的render函数去生成虚拟DOM。在调用render的时候,就会需要用到data的属性值,此时会触发getter函数,将当前的Watcher函数注册进dep里。
第二步:当data属性发生改变之后,就会遍历dep里所有的watcher对象,通知它们去重新渲染组件。
Vue diff算法原理
vue-diff 是什么?
提到vue的diff算法就不得不提一个名词 虚拟dom(Virtual DOM) ,什么是虚拟dom,我的理解是使用js对象来描述dom节点,是js和html的中间层,是一层对真实 DOM 的抽象。以前传统的开发直接使用js操作dom,现在使用js操作js对象(虚拟dom),在合适的时机将虚拟dom转化为真实dom节点
为什么需要虚拟dom?
为什么需要虚拟dom,直接操作dom不是更方便吗?实际上真正的 DOM 元素是非常庞大的,里面有非常多的属性,当我们频繁的去做 DOM 更新,会产生一定的性能问题。而虚拟dom使用js对象来描述dom节点,所以它比创建一个 DOM 的代价要小很多。
虚拟DOM的最终目标是将虚拟节点渲染到视图上。但是如果直接使用虚拟节点覆盖旧节点的话,会有很多不必要的DOM操作。例如,在一个dom元素非常多的页面你只改里其中的一个地方,这种情况下如果使用新的虚拟节点覆盖旧节点的话,造成了性能上的浪费,这个时候就需要比较计算新老虚拟节点的不同,差量的更新。所以这个时候vue-diff 就出现了。
下面说说 Diff 的比较逻辑
- 能不移动,尽量不移动
- 没得办法,只好移动
- 实在不行,新建或删除
比较处理流程是下面这样
在新旧节点中 - 先找到 不需要移动的相同节点,消耗最小
- 再找相同但是需要移动的节点,消耗第二小
- 最后找不到,才会去新建删除节点,保底处理
比较是为了修改DOM树
其实这里存在三种树,一个是页面DOM树,一个是旧VNode树,一个是新Vnode树
页面DOM树和旧VNode树节点一一对应的
而新Vnode树则是表示更新后页面DOM树该有的样子
这里把旧Vnode树和新Vnode树进行比较的过程中
不会对这两棵Vode树进行修改,而是以比较的结果直接对真实DOM 进行修改,比如说,在旧Vnode树同一层中,找到和新Vnode树中一样但位置不一样节点,此时需要移动这个节点,但是不是移动旧Vnode树中的节点,而是直接移动DOM,总的来说,新旧Vnode树是拿来比较的,页面DOM 树是拿来根据比较结果修改的
Vue computed和watch的区别
计算属性computed
- 支持缓存,只有依赖数据发生改变,才会重新进行计算
- 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
- computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值
- 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
- 如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。
侦听属性watch
- 不支持缓存,数据变,直接会触发相应的操作;
- watch支持异步;
- 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
- 当一个属性发生变化时,需要执行对应的操作;一对多;
- 监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数,
immediate:组件加载立即触发回调函数执行,
deep: 深度监听,为了发现对象内部值的变化,复杂类型的数据时使用,例如数组中的对象内容的改变,注意监听数组的变动不需要这么做。注意:deep无法监听到数组的变动和对象的新增,参考vue数组变异,只有以响应式的方式触发才会被监听到。
总结:
computed特性
- 是计算值,不支持异步操作,单纯的返回一个值
- 应用:就是简化tempalte里面{{}}计算和处理props或$emit的传值
- 具有缓存性,页面重新渲染值不变化;计算属性会立即返回之前的计算结果,而不必再次执行函数,只有依赖数据发生改变,才会重新进行计算
watch特性
- 是观察的动作,支持异步操作,可以进行一系列异步操作,
- 应用:监听props,$emit或本组件的值执行异步操作
- 无缓存性,页面重新渲染时值不变化也会执行
vue.nextTick()方法
定义:在下次DOM更新循环结束之后执行延迟回调,在修改数据之后立即使用这个方法,获取更新后的DOM。
理解:nextTick(),是将回调函数延迟在下一次dom更新数据后调用,就是当数据更新之后,并且在dom中渲染之后,自动执行该函数。
什么时候要用到?
- created()钩子函数中的DOM操作一定要放在this.nextTick()的回调函数中,因为在created()时虽然数据可以调用,但是DOM并没有进行更新,所以操作DOM是无效的。
- 当项目中要对改变之后的DOM元素做一定的操作时,将操作放在this.nextTick()的回调函数中
- 在使用某些第三方插件时,想要在vue生成的某些DOM发生变化后重新应用该插件时,需要在this.nextTick()的回调函数中重新执行该插件的方法
Vue 3.0新特性
Vue主要的特点有三个方面,响应式机制,模板,以及对象式组件声明语法。
- 响应式
2.x的响应式是基于Object.defineProperty实现的代理,能够监听数据对象的变化,但是监听不到对象属性的增删,数组元素和长度的变化,同时会在Vue初始化的时候把所有的Observer建立,实现数据视图与数据的响应式更新,底层需要Observer的支持,所以需要Observer都建立好,才能观察到数据对象的变化。
3.0中使用es6中的Proxy来代替Object.defineProperty,在目标对象之上架了一层拦截,代理的是对象而不是对象的属性。这样可以将原本对对象属性的操作变为对整个对象的操作,可以监听到对象属性的增删和数组长度的变化,还可以监听Map,Set,WeakSet,WeakMap等,同时实现了惰性监听,即不会再初始化的时候创建所有的observer,而是会在用到的时候才去监听。
proxy在目标对象的外层搭建了一层拦截,外界对目标对象的某些操作,必须通过这层拦截
new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为
Proxy 实例也可以作为其他对象的原型对象
Proxy的作用
对于代理模式 Proxy 的作用主要体现在三个方面
1.拦截和监视外部对对象的访问
2.降低函数或类的复杂度
3.在复杂操作前对操作进行校验或对所需资源进行管理
Proxy所能代理的范围--handler
实际上 handler 本身就是ES6所新设计的一个对象.它的作用就是用来 自定义代理对象的各种可代理操作 。它本身一共有13中方法,每种方法都可以代理一种操作.其13种方法如下
Proxy场景:实现私有变量,抽离校验模块,访问日志,预警和拦截,过滤操作,中断代理
- 模板
模板方面没有大的变更,只改了作用域插槽,2.x 的机制导致作用域插槽变了,父组件会重新渲染,而 3.0 把作用域插槽改成了函数的方式,这样只会影响子组件的重新渲染,提升了渲染的性能。
同时,对于 render 函数的方面,vue3.0 也会进行一系列更改来方便习惯直接使用 api 来生成 vdom 。 - 对象式的组件声明方式
vue2.x 中的组件是通过声明的方式传入一系列 option,和 TypeScript 的结合需要通过一些装饰器的方式来做,虽然能实现功能,但是比较麻烦。
3.0 修改了组件的声明方式,改成了类式的写法,这样使得和 TypeScript 的结合变得很容易。
此外,vue 的源码也改用了 TypeScript 来写。其实当代码的功能复杂之后,必须有一个静态类型系统来做一些辅助管理。
现在 vue3.0 也全面改用 TypeScript 来重写了,更是使得对外暴露的 api 更容易结合 TypeScript。静态类型系统对于复杂代码的维护确实很有必要。 - 其它方面的更改
vue3.0 的改变是全面的,上面只涉及到主要的 3 个方面,还有一些其他的更改:
支持自定义渲染器,从而使得 weex 可以通过自定义渲染器的方式来扩展,而不是直接 fork 源码来改的方式。
支持 Fragment(多个根节点)和 Protal(在 dom 其他部分渲染组建内容)组件,针对一些特殊的场景做了处理。
基于 treeshaking 优化,提供了更多的内置功能
Webpack部分:
Webpack配置模块:
Webpack常见配置:
webpack的打包原理:
- 识别入口文件
- 通过逐层识别模块依赖,commonjs,amd,import等进行分析,获取代码依赖
- Webpack做的就是分析代码,转换代码,编译代码,输出代码
- 最终形成打包后的代码
Loader和plugin的区别:
- Loader:文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译压缩等,最终一起打包到指定的文件中
处理一个文件可以使用多个loader,loader的执行顺序和配置中的顺序是相反的
第一个loader接收源文件内容作为参数,其他loader接收前一个执行的loader的返回值作为参数,最后一个执行的loader会返回此模块的js源码 - Plugin:在webpack运行的生命周期中会广播很多的时间,plugin可以监听这些事件,在合适的时机通过webpack提供的API改变输出的结果,比如设置项目的html首页,启用热更新机制等。
- Loader和plugin的区别:
Loader是一个转换器,对A文件进行编译形成B文件,这里操作的是文件,例如将A.scss转换为A.css,单纯的文件转换过程。
Plugin是一个扩展器,丰富了webpack本身,针对的是loader结束后,webpack打包的整个过程,它不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些结点,执行对应的任务。
Babel:
Babel是一个JavaScript编译器。他把最新版的javascript编译成当下可以执行的版本,简言之,利用babel就可以让我们在当前的项目中随意的使用这些新最新的es6,甚至es7的语法。
Babel运行原理:
- 解析:将代码字符串解析成抽象语法树(AST)
分为两个阶段:词法分析,语法分析
词法分析:将字符串形式的代码转换为令牌流
语法分析:使用令牌中的信息将它们转换成一个AST表述的结构 - 转换:接收AST并对其进行遍历,对结点进行添加更新移除等操作。
Babel提供了@babel/traverse(遍历)方法维护这AST树的整体状态,并且可完成对其的替换,删除或者增加节点,这个方法的参数为原始AST和自定义的转换规则,返回结果为转换后的AST。 - 生成:把经过一系列转换后的AST重新转换成字符串形式的代码,同时还会创建源码映射。
本质其实是深度优先遍历整个AST,然后构建可以表示转换后的代码的字符串,Babel使用@babel/generator将修改后的AST转换成代码,生成过程可以对是否压缩以及是否删除注释等进行配置,并且支持sourceMap。
Vue中配置babelrc
Git部分:
git常用命令:
git常用命令:
git add# 将工作区的修改提交到暂存区git commit# 将暂存区的修改提交到当前分支git reset# 回退到某一个版本git stash# 保存某次修改git pull# 从远程更新代码git push# 将本地代码更新到远程分支上git reflog# 查看历史命令git status# 查看当前仓库的状态git diff# 查看修改git log# 查看提交历史git revert# 回退某个修改
git add相关
git add .将文件的修改,新建添加到暂存区git add -u将文件的修改,删除添加到暂存区git add -A将文件的修改,新建,删除添加到暂存区
git commit 相关
git commit -m “本次提交描述”该命令会将git add . 存入暂存区修改内容提交至本地仓库中,若文件未添加至暂存区,则提交时不会提交任何修改git commit -a相当于运行git add -u把所有当前目录下的文件加入缓存区域再运行git commit,注意新增文件并没有被commitgit commit -am“本次提交描述” 或git commit -a -m “本次提交描述”,等同于git commit -m和git commit -agit commit -amend修改最近的一次提交,提交注释书写有误或者漏提文件的时候使用,注意漏提的文件要先用git add添加到缓存区之后,git commit -amend才能追加到
git stash用法
git stash将所有未提交的修改保存起来,用于后续恢复当前的工作目录git stash save “name”给每个stash添加一个message,用于记录版本git stash pop/git stash apply恢复最新缓存的工作目录,并恢复缓存堆栈中的那个stash删除(pop),apply则只恢复不删除git stash list查看现有的所有stashgit stash drop移除最新的stash,也可以指定名字
git reset用法
git reset HEAD^ 回退版本,一个^表示一个版本git reset HEAD~n回退n个版本git reset commit-id回退到指定版本git reset命令有三个参数,git reset --soft HEAD~1--soft:软回退,表示将本地版本库头指针重置到指定版本,且将这次提交之后的所有变更都移动到暂存区--mixed:默认参数,可不写,将本地版本库的头指针重置到指定版本,且重置暂存区,即这次提交之后所有变更都移动到工作区--hard:回退一个版本,将本地版本库的头指针重置到指定版本,重置暂存区,并将工作区代码清空。
git reset HEAD filename回退指定文件
git reflog用法
git reflog查看所有分支的操作记录,包括commit 和reset的操作,包括已经被删除的commit操作,git log则不能查看已经删除commit 记录
git checkout用法
git checkout [<commit>] [--] <paths>用于拿暂存区的文件覆盖工作区的文件,或者用指定提交中的文件覆盖暂存区和工作区中对应的文件,带commit参数则是从暂存区检出文件覆盖工作区,不带commit参数则用--path中的文件覆盖暂存区和工作区的文件。git checkout <branch>用于切换分支git checkout -b <new_branch> [<start_point>]用于创建并切换分支,-b 选项表示创建新分支。
git revert用法
git revert反转提交,撤销一个提交的同时会创建一个新的提交,相当于用一个新提交来消除一个历史提交所做出的修改git revert commit-id反转提交指定一个commitgit revert HEAD~3反转提交第四个commitgit revert --abort终止此次revert,防止产生冲突
git branch用法
git branch -d <branchname>删除分支,前提要切换到其他分支
git push用法
git push <远程主机名> <本地分支名>:<远程分支名>将本地分支的更新,推送到远程主机。git push origin master将本地的master分支推送到origin主机的master分支。如果master不存在,则会被新建。git push origin :master等同于git push origin --delete master省略本地分支名,则表示删除指定远程分支。如果本地分支和远程分支之间存在追踪关系,则直接git push origin,如果远程仓库只有一个分支,则git pushgit push -u origin master使用-u命令建立当前分支与某个主机的追踪关系,可以不加参数使用git push
git pull用法
git pull <远程主机名> <远程分支名>:<本地分支名>从另一个存储库或者本地分支获取并整合,取回远程主机某个分支的更新,再与本地的指定分支合并git pull origin master:test取回origin主机的master分支与本地的test分支合并,如果是与本地的master分支合并,则:test可省略git pull = git fetch + git mergegit pull --rebase = git fetch + git rebasegit中fetch命令是将远程分支的最新内容拉到了本地,但是fetch后看不到变化,因为此时本地多了一个FETCH_HEAD指针,checkout改指针后可以查看远程分支的最新内容,然后checkout到master分支,执行merge,选中FETCH_HEAD指针,合并后并解决冲突后,可以commit。
计算机网络部分:
TCP/IP模型结构及协议
数据链路层:实现了网卡接口的网络驱动程序,以处理数据在物理媒介上的传输
- ARP协议(地址解析协议):实现了IP地址和物理机器地址之间的转换。网络层使用IP地址寻址到一台机器,而数据链路层使用物理地址寻址一台机器,因此网络层必须先将目标机器的Ip地址转化成物理地址,才能使用数据链路层提供的服务,这就是ARP协议的用途。
- RARP协议(逆地址解析协议):仅用于网络上的某些无盘工作站,因为缺乏存储设备,无盘工作站无法记住自己的IP地址,但他们可以利用网上的物理地址来向网络管理者查询自己的IP地址。
网络层:实现数据包的选路和转发,广域网通常使用众多分级的路由器来连接分散的主机,因此通信的两台主机一般是通过多个中间节点连接的,网络层的任务就是选择这些中间节点,以确定两台主机之前的通信路径 - IP协议(因特网协议):根据数据包的目的IP地址来决定如何投递,如果数据包不能直接发送给目标主机,那么IP协议就为它寻找下一个合适的路由器,并将数据包交付给路由器进行转发,多次重复,知道数据包达到目的主机或因发送失败而丢弃,IP协议使用逐跳的方式来确定通信路径
- ICMP协议(因特网报文控制协议):是IP协议的重要补充,主要用于检测网络连接,ICMP报文分为差错报文和查询报文,使用16位检验和字段对整个报文进行循环冗余校验,检验报文在传输过程中是否损坏。
传输层:为两台主机上的应用程序提供端到端的通信,与网络层使用逐跳通信的方式不同,传输层只关心通信的起始端和目的端,而不在乎数据包的中转过程。 - TCP协议
- UDP协议
应用层:负责处理应用程序的逻辑,应用层在用户空间实现,因为它负责处理众多的逻辑,比如文件传输,名称查询和网络管理等等。 - Ping:是应用程序,而不是协议,它利用ICMP报文检测网络连接,是调试网络环境的必备工具。
- Telnet协议:是一种远程登录协议,它使我们能在本地完成远程任务
- OSPF协议(开放最短路径优先):是一种动态路由更新协议,由于路由器之间的通信,以告知对方各自的路由信息
- DNS协议:提供机器域名到IP地址的转换
TCP和UDP的区别:
- TCP是面向连接的,UDP是无连接的即发送数据前不需要先建立链接。
- TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付。 并且因为TCP可靠,面向连接,不会丢失数据因此适合大数据量的交换。
- TCP是面向字节流,UDP面向报文,并且网络出现拥塞不会使得发送速率降低,因此会出现丢包,对实时的应用比如IP电话和视频会议等。其中字节是散乱的数据,报文是添加了标记,封装后的数据,字节流是由字节组成的。
- TCP只能是1对1的,UDP支持1对1,1对多。
- TCP的 首部较大为20字节,而UDP只有8字节。
- TCP是面向连接的可靠性传输,而UDP是不可靠的。
- TCP用于在传输层有必要实现可靠传输的情况,比如文件下载,UDP主要用于哪些对高速传输和实时性有较高要求的通信或广播通信,比如实时通话
tcp三次握手与四次挥手:
TCP报文包含了哪些内容?
- TCP报头中的源端口号和目的端口号同IP数据报中的源IP与目的IP唯一确定一条TCP连接,TCP在发送数据前必须在彼此间建立连接,这里连接的意思是:双方需要内保存对方的信息(如IP,Port)
- 报文主要段的意思:
- 序号:表示发送的数据字节流,确保TCP传输有序,对每个字节编号。
- 确认序号:发送方期待接收的下一序列号,接收成功后数据字节序列号加1,只有ACK=1时才有效。
- ACK:确认序号的标志,ACK=1表示确认号有效,ACK=0表示报文不含确认序号信息。
- SYN:连接请求序号标志,用于建立连接,SYN=1表示请求连接。
- FIN:结束标志,用于释放连接,为1表示关闭本方数据流。
三次握手过程:
- 第一次握手:客户端发送初始序号hx和syn=1请求标志。
- 第二次握手:服务器发送请求标志syn,发送确认标志ACK,发送自己的序号seq=y,发送客户端的确认序号,ack=x+1
- 第三次握手:客户端发送ACK确认信号,发送自己的序号seq=x+1,发送对方的确认号ack=y+1
三次握手过程分析:
- 第一次:客户端发送请求到服务器,服务器知道客户端发送,自己接收正常。SYN=1,seq=x
- 第二次:服务器发给客户端,客户端知道自己发送、接收正常,服务器接收、发送正常。ACK=1,ack=x+1,SYN=1,seq=y
- 第三次:客户端发给服务器:服务器知道客户端发送,接收正常,自己接收,发送也正常.seq=x+1,ACK=1,ack=y+1
为什么要有三次握手的过程?
因为只有经过三次,服务器和客户端才可以互相知道对方和自己发送接收都正常,经过两次握手的话服务端无法知道客户端是否具备接收以及自己是否具备发送的能力;并且两次握手的话如果首次握手的报文超时客户端再重发之后,服务端接收到之前的那个报文也会直接建立连接,浪费资源;
四次挥手过程:
- 第一次挥手:客户端发出释放FIN=1,自己序列号seq=u,进入FIN-WAIT-1状态
- 第二次挥手:服务器收到客户端的后,发出ACK=1确认标志和客户端的确认号ack=u+1,自己的序列号seq=v,进入CLOSE-WAIT状态
- 第三次挥手:客户端收到服务器确认结果后,进入FIN-WAIT-2状态。此时服务器发送释放FIN=1信号,确认标志ACK=1,确认序号ack=u+1,自己序号seq=w,服务器进入LAST-ACK(最后确认态)
- 第四次挥手:客户端收到回复后,发送确认ACK=1,ack=w+1,自己的seq=u+1,客户端进入TIME-WAIT(时间等待)。客户端经过2个最长报文段寿命后,客户端CLOSE;服务器收到确认后,立刻进入CLOSE状态。、
四次挥手过程分析:
- 第一次:客户端请求断开FIN,seq=u
- 第二次:服务器确认客户端的断开请求ACK,ack=u+1,seq=v
- 第三次:服务器处理完数据之后请求断开FIN,seq=w,ACK,ack=u+1
- 第四次:客户端确认服务器的断开ACK,ack=w+1,seq=u+1
为什么挥手要进行四次?
因为服务器端发送确认客户端断开的ACK之后,还要进行数据处理,处理完数据之后才可以发送自己的断开请求。这里不能合并成一个请求过程。
PS:以上知识点的内容是楼主根据自己的理解和其他大神的博客内容整理出来的,可能有一些内容是原文,如果侵犯到了哪位大大的版权,请联系我及时删掉对应的内容。