mvvm :Model-View-ViewModel的缩写,
model数据层
,数据模型,仅仅关注数据本身
View视图层
是用户操作界面 也可称为,当ViewModel对Model进行更新的时候,会通过数据绑定更新到view
viewmodel 业务逻辑层 view需要什么数据 ViewModel要提供这个数据,view有哪些操作,ViewModel就要响应哪些操作,所以也可以说 它是Model for View
为什么会出现MVVM 前端开发中暴露出了三个痛点问题:
总结:MVVM模式简化了界面与业务的一览,解决了数据频繁更新,MVVM在使用当中,利用双向绑定技术,使得Model变化时,ViewModel会自动更新,而,ViewModel变化时,View也会自动变化。在MVVM的框架下视图和模型是不能直接通信的,并没有直接的联系,它们通过ViewModel来通信。Model 和 ViewModel 之间的交互是双向的, 因此 View 数据的变化会同步到 Model 中,而 Model 数据的变化也会立即反应到View 上。ViewModel 通过双向数据绑定把 View 和 Model 连接了起来,而 View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
MVC
一共8个阶段 1、beforeCreate(创建前) 2、created(创建后) 3、beforeMount(载入前) 4、mounted(载入后) 5、beforeUpdate(更新前) 6、updated(更新后) 7、beforeDestroy(销毁前) 8、destroyed(销毁后)
image.png
beforeCreate(创建前) 在数据观测和初始化事件还未开始created(创建后) 完成数据观测,属性和方法的运算,初始化事件,el属性还没有显示出来beforeMount(载入前) 在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。mounted(载入后) 在el 被新创建的 vm.el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。此过程中进行ajax交互。beforeUpdate(更新前) 在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。updated(更新后) 在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。beforeDestroy(销毁前) 在实例销毁之前调用。实例仍然完全可用。destroyed(销毁后) 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
https://www.cnblogs.com/yuliangbin/p/9348156.html
加载渲染过程 父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子组件更新过程 父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程 父beforeUpdate->父updated
销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
jsx可以像我们写HTML文件一样写业务代码,借助于babel,会将jsx语法转换成render语法,没有什么副作用。
vue template语法简单明了,数据操作与视图分离,开发体验友好。但是在某些特定场合中,会限制一些功能的扩展,如动态使用过滤器、解析字符串类型的模板文件等。以上功能的实现可以借助vue的render语法,render语法比template更偏底层,允许在HTML中使用js语法,可以极大的扩展HTML的能力。render函数注入了一个参数createElement,用来创建我们所需要的标签内容,有三个参数:HTML标签(elementTag),标签属性(option),子元素(children);从createElement的参数列表里面可以看出,如果组件内部结构嵌套比较深,render的语法写起来会比较繁琐,需要不断的调用createElement,对于想偷懒的我,还是想想有没有什么比较简易的写法,jsx无疑是一种很好的选择,区别在于jsx可以像我们写HTML文件一样写业务代码,借助于babel,会将jsx语法转换成render语法,没有什么副作用。
虚拟DOM概念随着react的诞生而诞生,由facebook提出,其卓越的性能很快得到广大开发者的认可;继react之后vue2.0也在其核心引入了虚拟DOM的概念
什么是虚拟DOM? vdom可以看作是一个使用javascript模拟了DOM结构的树形结构,这个树结构包含整个DOM结构的信息.。dom 树对应的虚拟 dom 对象( js 对象),如下图:
image.png
可见左边的DOM结构,不论是标签名称还是标签的属性或标签的子集,都会对应在右边的树结构里。
为什么要使用虚拟DOM?
虚拟 dom 是相对于浏览器所渲染出来的真实 dom 的,在react,vue等技术出现之前,我们要改变页面展示的内容只能通过遍历查询 dom 树的方式找到需要修改的 dom 然后修改样式行为或者结构,来达到更新 ui 的目的。这种方式相当消耗计算资源
,因为每次查询 dom 几乎都需要遍历整颗 dom 树
,如果建立一个与 dom 树对应的虚拟 dom 对象( js 对象),以对象嵌套的方式来表示 dom 树,那么每次 dom 的更改
就变成了 js 对象的属性的更改
,这样一来就能查找 js 对象的属性变化要比查询 dom 树的 性能开销小
。
其实并不是查询 dom 树性能开销大而是 dom 树的实现模块和 js 模块是分开的这些跨模块的通讯增加了成本,以及 dom 操作引起的浏览器的回流和重绘,使得性能开销巨大,原本在 pc 端是没有性能问题的,因为 pc 的计算能力强,但是随着移动端的发展,越来越多的网页在智能手机上运行,而手机的性能参差不齐,会有性能问题。
他们的思想是每次更新 dom 都尽量避免刷新整个页面,而是有针对性的 去刷新那被更改的一部分
,来释放掉被无效渲染占用的 gpu,cup 性能。
angular
angular 采用的机制是 脏值检测查机制
所有使用了 ng 指令的 scope data 和 {{}} 语法的 scope data 都会被加入脏检测的队列
vue
vue 采用的是虚拟dom通过重写 setter , getter
实现观察者
监听 data 属性的变化生成新的虚拟 dom 通过 h 函数创建真实 dom 替换掉dom树上对应的旧 dom。
react react 也是通过虚拟 dom 和 setState 更改 data 生成新的虚拟 dom 以及 diff 算法来计算和生成需要替换的 dom 做到局部更新的。
diff算法 深度优先遍历 diff的过程就是调用patch函数,就像打补丁一样修改真实dom
总之,一切为了减弱频繁的大面积重绘引发的性能问题,不同框架不一定需要虚拟DOM,关键看框架是否频繁会引发大面积的DOM操作
你的知道浏览器的虚拟DOM与真实DOM的区别(注意:需不需要虚拟DOM,其实与框架的DOM操作机制有关): 虚拟DOM不会进行排版与重绘操作 虚拟DOM进行频繁修改,然后一次性比较并修改真实DOM中需要改的部分(注意!),最后并在真实DOM中进行排版与重绘,减少过多DOM节点排版与重绘损耗 真实DOM频繁排版与重绘的效率是相当低的 虚拟DOM有效降低大面积(真实DOM节点)的重绘与排版,因为最终与真实DOM比较差异,可以只渲染局部(同2) 使用虚拟DOM的损耗计算: 总损耗=虚拟DOM增删改+(与Diff算法效率有关)真实DOM差异增删改+(较少的节点)排版与重绘 直接使用真实DOM的损耗计算: 总损耗=真实DOM完全增删改+(可能较多的节点)排版与重绘 总之,一切为了减弱频繁的大面积重绘引发的性能问题,不同框架不一定需要虚拟DOM,关键看框架是否频繁会引发大面积的DOM操作
实际的标签中可能存在两个一模一样的两个节点,但是在virtual Dom中无法体现这个区别,另一方面为了加快diff算法的速度,一个区别节点的变量的需求变得非常必要。virtual Dom中需要给每个节点一个标识,作为判断是同一个节点的依据。所以这也是 Vue 和 React 中官方推荐列表里的节点使用唯一的 key 来保证性能。其中在diff算法中,大量使用了利用tagName和key组合判断节点之间差异的逻辑代码
https://www.jianshu.com/p/7f9495b1c8ab
Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。
1.在Vue生命周期的created()钩子函数进行DOM操作一定要放到Vue.nextTick()的回调函数中。
在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted()钩子函数,因为该钩子函数执行时所有的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操作都不会有问题。
2.在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作都应该放进Vue.nextTick()的回调函数中。
Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作上非常重要。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部尝试对异步队列使用原生的Promise.then和MessageChannel,如果执行环境不支持,会采用setTimeout(fn, 0)代替。
例如,当你设置vm.someData = ‘new value’,该组件不会立即重新渲染。当刷新队列时,组件会在事件循环队列清空时的下一个“tick”更新。多数情况我们不需要关心这个过程,但是如果你想在 DOM 状态更新后做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员沿着“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们确实要这么做。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用Vue.nextTick(callback)。这样回调函数在 DOM 更新完成后就会调用。
子组件: 通过
emit(“listenToChildEvent”, deliver) 父组件进行绑定事件的,调用方法! v-on:listenToChildEvent=”showMsgFromChild”
https://www.jianshu.com/p/92d0e78c9906
1) React和Vue有许多相似之处,它们都有:
使用 Virtual DOM 提供了响应式(Reactive)和组件化(Composable)的视图组件。 将注意力集中保持在核心库,伴随于此,有配套的路由和负责处理全局状态管理的库。
2) 性能: 到目前为止,针对现实情况的测试中,Vue的性能是优于React的
3) 生态圈 Vue.js: ES6+Webpack+unit/e2e+Vue+vue一router+单文件组件+vuex+iVew React: ES6+Webpack+Enzyme+React+React一router+Redux
4) 什么时候选择Vue.js 如果你喜欢用(或希望能够用)模板搭建应用,请使用Vue 如果你喜欢简单和”能用就行”的东西,请使用Vue 如果你的应用需要尽可能的小和快,请使用Vue 如果你计划构建一个大型应用程序,请使用React 如果你想要一个同时适用于Web端和原生App的框架,请选择React 如果你想要最大的生态圈,请使用React
image.png
配置相关路径
可以通过vue-router实例来配置路由规则列表,指定路径path与组件component的对应关系。可以通过mode这一参数控制路由的实现模式,默认值是hash
,基于hash的实现方式,如果显示设置为history
,则会设为基于history API的实现方式,如果浏览器不支持,可以设置fallback来控制是否需要回滚为’hash’模式。另外,如果是非浏览器端运行(如nodejs中),会将mode强制设为’abstract’模式。
vue-router支持路由嵌套、动态路由的配置、重定向及别名等,可参看官方文档。
前端路由的实现方式 在HTML5的 history API出现之前,前端路由主要是通过 hash 来实现的,hash能兼容低版本的浏览器。下面分别来介绍这2种方式。
方法一:基于hash(location.hash+hashchange事件) 我们知道location.hash的值是url中#后面的内容,如http://www.163.com#netease此网址中,location.hash=’#netease’。hash满足以下几个特性,才使得其可以实现前端路由:
url中hash值的变化并不会重新加载页面,因为hash是用来指导浏览器行为的,对服务端是无用的,所以不会包括在http请求中。 hash值的改变,都会在浏览器的访问历史中增加一个记录,也就是能通过浏览器的回退、前进按钮控制hash的切换 我们可以通过hashchange事件,监听到hash值的变化,从而响应不同路径的逻辑处理。 window.addEventListener(“hashchange”, funcRef, false) 如此一来,我们就可以在hashchange事件里,根据hash值来更新对应的视图,但不会去重新请求页面,同时呢,也在history里增加了一条访问记录,用户也仍然可以通过前进后退键实现UI的切换。
触发hash值的变化有2种方法:
一种是通过a标签,设置href属性,当标签点击之后,地址栏会改变,同时会触发hashchange事件
<a href=”#kaola”>to KAOLA</a> 另一种是通过js直接赋值给location.hash,也会改变url,触发hashchange事件。
location.hash=”#kaola”
route是一条路由,是将一个URL路径和一个处理函数相关联,是一条url和函数的映射规则,如上面代码中通过原型上的route可以设置一条路由规则,将一个path和其callback关联起来。
而router则更像是一个容器,或者说一种机制,它管理了一组route。简单来说,route只是进行了URL和函数的映射,而在当接收到一个URL之后,去路由映射表中查找相应的函数,这个过程是由router来处理的,如上面代码,Router管理传入的route,并且在hash改变的时候,根据当前的url响应其对应的函数。
方法二:基于History新API(history.pushState()+popState事件) HTML5中history对象上新的API,同样能实现前端的路由。通过pushState()方法或replaceState()方法可以修改url的地址,并在popstate事件中能监听地址的改变,不同的是,手动的进行pushState()并不会触发popstate事件。
先认识下两个新增的API:history.pushState和 history.replaceState,这两个API都接收三个参数:
window.history.pushState(null, null, “http://www.163.com“); 状态对象(state object),一个JavaScript对象,与用pushState()方法创建的新历史记录条目关联。无论何时用户导航到新创建的状态,会触发popstate事件,并能在事件中使用该对象。 标题(title) :传入一个短标题给当前state。现在大多数浏览器不支持或者会忽略此参数,最好传入null代替; 地址(URL):新的历史记录条目的地址。浏览器不会在调用pushState()方法后加载该地址,但之后,可能会试图加载,例如用户重启浏览器。新的URL不一定是绝对路径;如果是相对路径,它将以当前URL为基准;传入的URL与当前URL应该是同源的,否则,pushState()会抛出异常。该参数是可选的;不指定的话则为文档当前URL。 这两个API的相同之处是都会操作浏览器的历史记录,而不会引起页面的刷新。不同之处在于,pushState会增加一条新的历史记录,而replaceState则会替换当前的历史记录。这两个api,加上state改变触发的popstate事件,提供了单页应该的另一种路由方式。
当我们在历史记录中切换时就会触发 popstate 事件,可以在事件中还原当前state对应的UI。对于触发popstate 事件的方式,各浏览器实现也有差异,我们可以根据不同浏览器做兼容处理。
两种方式对比,基于Hash的路由,兼容性更好;基于History API的路由,则更正式,可以设置与当前URL同源的任意URL,路径更直观。另外,基于Hash的路由不需要对服务器做改动,基于History API的路由需要对服务器做一些改造,配置不同的路由都返回相同的页面。
路由的匹配规则是按照书写的顺序执行的,第一条匹配成功则不去匹配下一条,利用这一特性,可以在所有匹配路由的下面拦截匹配所有路由:
//创建路由对象并配置路由规则
let router = new VueRouter({
routes:[
{path:'/',redirect:{name:"home"}}, // 重定向到主页
{name:'home',path:'/home',component:Home},
{name:'login',path:'/login',component:Login},
{path:'*',component:NotFound},//全不匹配的情况下,匹配NotFound组件,路由按顺序从上到下,依次匹配。最后一个*能匹配全部,
]
});
原理同方法2,只不过在匹配到*时,重定向到根路径:
//创建路由对象并配置路由规则
let router = new VueRouter({
routes:[
{path:'/',redirect:{name:"home"}}, // 重定向到主页
{name:'home',path:'/home',component:Home},
{name:'login',path:'/login',component:Login},
{path:'*',redirect:'/'},//路由按顺序从上到下,依次匹配。最后一个*能匹配全部,然后重定向到主页面
]
});
hash前端路由,无刷新 history 会去请求接口
vue 是渐进式前端开发框架,为了实现 SPA ,需要引入前端路由系统(vue-router)。前端路由的核心是:改变视图的同时不会向后端发出请求。
为了达到这一目的,浏览器提供了 hash 和 history 两种模式。
因此可以说, hash 模式和 history 模式都属于浏览器自身的属性,vue-router 只是利用了这两个特性(通过调用浏览器提供的接口)来实现路由。
实现的原理
pushState()、replaceState() 方法接收三个参数:stateObj、title、url。 // 设置状态
history.pushState({color: "red"}, "red", "red");
// 监听状态window.onpopstate = function(event){
console.log(event.state);
if(event.state && event.state.color === "red"){
document.body.style.color = "red";
}} // 改变状态history.back();history.forward();复制代码
应用场景
通过 pushState 把页面的状态保存在 state 对象中,当页面的 url 再变回到这个 url 时,可以通过 event.state 取到这个 state 对象,从而可以对页面状态进行还原,如页面滚动条的位置、阅读进度、组件的开关等。
调用 history.pushState() 比使用 hash 存在的优势:
劣势:
写在前面:通常 SPA 中前端路由有2种实现方式:
下面就来介绍下这两种方式具体怎么实现的
一.history
1.history基本介绍
window.history 对象包含浏览器的历史,window.history 对象在编写时可不使用 window 这个前缀。history是实现SPA前端路由是一种主流方法,它有几个原始方法:
在HTML5,history对象提出了 pushState() 方法和 replaceState() 方法,这两个方法可以用来向历史栈中添加数据,就好像 url 变化了一样(过去只有 url 变化历史栈才会变化),这样就可以很好的模拟浏览历史和前进后退了,现在的前端路由也是基于这个原理实现的。
2.history.pushState
pushState(stateObj, title, url) 方法向历史栈中写入数据,其第一个参数是要写入的数据对象(不大于640kB),第二个参数是页面的 title, 第三个参数是 url (相对路径)。
关于pushState,有几个值得注意的地方:
pushState方法不会触发页面刷新,只是导致history对象发生变化,地址栏会有反应,只有当触发前进后退等事件(back()和forward()等)时浏览器才会刷新
这里的 url 是受到同源策略限制的,防止恶意脚本模仿其他网站 url 用来欺骗用户,所以当违背同源策略时将会报错
3.history.replaceState
replaceState(stateObj, title, url) 和pushState的区别就在于它不是写入而是替换修改浏览历史中当前纪录,其余和 pushState一模一样
4.popstate事件
定义:每当同一个文档的浏览历史(即history对象)出现变化时,就会触发popstate事件。
注意:仅仅调用pushState方法或replaceState方法 ,并不会触发该事件,只有用户点击浏览器倒退按钮和前进按钮,或者使用JavaScript调用back、forward、go方法时才会触发。另外,该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件也不会触发。
用法:使用的时候,可以为popstate事件指定回调函数。这个回调函数的参数是一个event事件对象,它的state属性指向pushState和replaceState方法为当前URL所提供的状态对象(即这两个方法的第一个参数)。
二.Hash
1.Hash基本介绍
url 中可以带有一个 hash http://localhost:9000/#/a.html
window 对象中有一个事件是 onhashchange,以下几种情况都会触发这个事件:
router官网中进阶部分提供了导航守卫的功能
,可以通过设置全局前置守卫
或者路由独享守卫
来做用户登陆权限的判别。
需要在 VueRouter 的参数中使用 children 配置,这样就可以很好的实现路由嵌套。
在将数组处理成响应式数据后,如果使用数组原始方法改变数组时,数组值会发生变化,但是并不会触发数组的setter来通知所有依赖该数组的地方进行更新,为此,vue通过重写数组的某些方法来监听数组变化,重写后的方法中会手动触发通知
该数组的所有依赖进行更新
。
我们知道通过Object.defineProperty()劫持数组为其设置getter和setter后,调用的数组的push、splice、pop等方法改变数组元素时并不会触发数组的setter
,这就会造成使用上述方法改变数组后,页面上并不能及时体现这些变化,也就是数组数据变化不是响应式的(对上述不了解的可以参考这篇文章)。但实际用vue开发时,对于响应式数组,使用push、splice、pop等方法改变数组时,页面会及时体现这种变化,那么vue中是如何实现的呢?通过vue源码可以看出,vue重写了数组的push、splice、pop等方法
因为监听的数组带来的代价和一些问题,Vue使用了重写原型的方案
代替。拦截了数组的一些方法,在这个过程中再去做通知变化等操作。
https://blog.csdn.net/weixin_38500689/article/details/88227985
原理 vue数据双向绑定通过‘数据劫持’ + 订阅发布模式实现
数据劫持 指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果
典型的有 1.Object.defineProperty() 2.es6中Proxy对象
vue2.x使用Object.defineProperty(); vue3.x使用Proxy;
订阅发布模式 定义:对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知 订阅发布模式中事件统一由处理中心处理,订阅者发布者互不干扰。 优点:实现更多的控制,做权限处理,节流控制之类,例如:发布了很多消息,但是不是所有订阅者都要接收
// 实现一个处理中心
let event = {
clientList: {}, // 订阅事件列表
// 订阅
on(key, fn){
// 如果这个事件没有被订阅,那么创建一个列表用来存放事件
if(!this.clientList[key]) {
this.clientList[key] = []
}
// 将事件放入已有的事件列表中
this.clientList[key].push(fn);
},
// 发布
trigger(type, args){
let fns = this.clientList[type] // 拿到这个事件的所有监听
if(!fns || fns.length === 0){ // 如果没有这条消息的订阅者
return false
}
// 如果存在这个事件的订阅,那么遍历事件列表,触发对应监听
fns.forEach(fn => {
// 可以在此处添加过滤等处理
fn(args)
})
}
}
vue中如何实现 利用Object.defineProperty();把内部解耦为三部分 Observer: 递归的监听对象上的所有属性,当属性改变时触发对应的watcher watcher(观察者):当蒋婷的数据值修改时,执行相应的回调函数,更新模板内容 dep:链接observer和watcher,每一个observer对应一个dep,内部维护一个数组,保存与该observer相关的watcher
proxy实现观察者模式 观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行
const person = observable({
name: '张三',
age: 20
});
function print() {
console.log(`${person.name}, ${person.age}`)
}
observe(print);
person.name = '李四';
// 输出
// 李四, 20
代码中。对象person是观察目标,函数print是观察者。一旦数据发生变化,print就会自动执行
使用proxy实现一个最简单观察者模式,即实现observable和observe这两个函数。 思路是observable函数返回一个原始对象的proxy代理,拦截复制操作。触发充当观察者的各个函数
const queue = new Set();
const observe = fn => queue.add(fn);
const observable = obj => new Proxy(obj, {set});
function set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
queue.forEach(observer => observer());
return result;
}
上面代码中,先定义了一个Set集合,所有观察者函数都放进这个集合,然后,observable函数返回原始对象的代理,拦截赋值操作。 拦截函数set中,自动执行所有观察者
关于Vue 3.0有幸看过尤大的关于3.0版本的RFC Vue Function-based API RFC。 大致说了三个点,第一个是关于提出的新API setup()
函数,第二个说了对于Typescript的支持,最后说了关于替换Object.defineProperty为Proxy的支持。
详细说了下关于Proxy代替带来的性能上的提升,因为传统的原型链拦截的方法,无法检测对象及数组的一些更新操作,但使用Proxy又带来了浏览器兼容问题。
1、速度方面:
通过更新 runtime-core 与 runtime-dom ,支持了包括 Fragments、Portals与Suspense w/ async setup() 等(似乎有点越来越像 react),支持 Composition API 、Options API 和 typings,配合 Proxy 的引入,极大程度上提高了响应式的能力。
Composition API 与 Proxy 的运用使得组件化更加灵活,逻辑业务组件的编写与UI组件的多样化能够更好的实现。
2、体积方面:
runtime-core 体积压缩成了 约10kb
3、维护性:
代码迁移成 TypeScript (还没学的抓紧啦)
编译器(Compiler)优化,以下借用尤大大的特性更新图翻译一下:
使用模块化架构
优化 “Block tree”
更激进的 static tree hoisting 功能
支持 Source map
内置标识符前缀(又名 “stripWith”)
内置整齐打印功能
移除 source map 和标识符前缀功能后,使用 Brotli 压缩的浏览器版本精简了大约 10KB
可见,编译器更多从数据结构入手,优化架构层级,降低使用成本。
4、更接近原生开发
5、让你更轻松开发
其次是 GitHub 上的源码目录:
reactivity 目录:数据响应式系统,主要使用 Proxy。 runtime-core 目录:支持 v-dom 与 Vue 原生的各种 API 在浏览器上的调用,调用 Vue 特性的相关代码。 runtime-dom 目录: 调用各类浏览器原生 DOM 事件的相关代码。 runtime-test 目录: 测试用的runtime的相关代码。 server-renderer 目录: SSR 相关代码。 compiler-core 目录: 支持 Vue 原生编写的 编译器特性,以及开发者编写的编译器特性的相关代码。 compiler-dom 目录: 调用浏览器的编译器的相关代码。 shared 目录: 无内置API。 vue 目录: 实现 vue 构建与编译的相关代码。
在官方文档中,有简单介绍一点,是基于 Object.defineProperty
去实现的;但是有一个要求,就是在 实例化 Vue
构造函数前,所有熬进行双向绑定的数据,都是要在 data
里面去做初始化的,哪怕是一个空值;因为在每次实例化的时候,Vue
会去检测 data
,查看并把存在属性用 Object.defineProperty
进行监听;在每一个需要判断或者展示当前响应式监听的数据时,例如:
<div>{{name}}</div>
复制代码
new Vue({
data(){
return {
name:'zhangsan'
}
}
})
复制代码
初始化的时候,我绑定了一个 name
属性,它在一个 div
里面做了展示;当我在 div
里面添加 name
展示的时候,其实在模板编译的时候,获取了一下 name
属性;因为前面有提到,我给当前的属性绑定了 Object.defineProperty
,所以在获取的时候,我会调用到 get
方法;
在这之前,我有实例化一个 dep
队列,把每次获取 name
属性的地方,做一个 push
;
当我接下来要做数据修改的时候,比如把 zhangsan
变成了 lisi
,那么在 set
方法里,把我当前属性的队列有监听当前属性的位置,全部更新一遍;最后把 data
对象里面的属性值做修改;
注:这是一个面试的回答,但这个不够详细,如果想很详细的去了解,具体都做了什么,请跳转:Vue 源码解析(实例化前) – 响应式数据的实现原理
https://www.jianshu.com/p/2df6dcddb0d7
Proxy的优势:
可以直接监听对象而非属性
Object.defineProperty的优势如下:
1、vuex是什么 公共状态管理 解决多个非父子组件传值问题。 多个页面都能用Vuex中store公共的数据 a、并不是所有的数据都要放在Vuex中,只有各个组件公用的一些数据会放在Vuex当中 b、Vuex是一个公共状态管理模式 并不是数据库 所以不可能持久保存一些数据,当用户刷新浏览器的时候那么数据就有可能消失 c、Vuex主要应用在大型的单页面开发中 2、vuex的特点 1、遵循单向数据流 2、Vuex中的数据是响应式的 3、vuex的流程图:
image.png
总结下vuex数据传输流程
1、通过new Vuex.Store()创建一个仓库 state是公共的状态,state—>components渲染页面 2、在组件内部通过this.$store.state.属性 来调用公共状态中的state,进行页面的渲染。 3、当组件需要修改数据的时候,必须遵循单向数据流。组件里在methods中扩展mapActions,调用store里的actions里的方法。 4、actions中的每个方法都会接受一个对象 这个对象里面有一个commit方法,用来触发mutations里面的方法 5、mutations里面的方法用来修改state中的数据 mutations里面的方法都会接收到2个参数 一个是store中的state, 另外一个是需要传递到参数 6、当mutations中的方法执行完毕后state会发生改变,因为vuex的数据是响应式的 所以组件的状态也会发生改变
一、vue-router——路由管理
经过自己的学习加项目总结,针对路由这块内容说下自己的理解:
路由配置主要有三个关键字:route routes router
route(单数)表示一条路由,它是一个对象由两部分构成(path——路径;component——组件)
例如:homeBtn——homeContent(点击homeBtn按钮页面切换到homeContent组件);sideBtn——sideContent;这是两条路由(route)
如果路由多,这样一条一条表示显然不合理。
由此出现routes(复数)表示一组路由(路由的数组),上面的两条路由就可以这样表示:
image
具体写法:const routes = [{path:’/home’,component:homeContent},{path:’/sidebar’,component:sidebarContent}]
router表示路由机制(即路由的管理者),实现原理:点击homeBtn按钮——到routes里去查找到‘/home’对应得组件homeContent,并显示页面。
写法:const router = new VueRouter({routes:routes}),简写:const router = new VueRouter({routes})
最后将router实例注入到vue根实例中,便可以使用路由
配置好路由再定义好路由的出口router-view就可以使用路由实现页面跳转了。
ps:router-link相当于a标签。
二、vuex——数据状态管理
简单说下vuex的作用:主要用于组件直接通讯。比如A和B和C为兄弟组件,组件中都用到name这个字段,A组件中如果对name字段进行更改,那该如何通知B和C组件name字段已经发生变化了呢?这个时候就可以使用Vuex来进行通讯了。先将name存起来(state管理变量初始状态的),A组件中通过触发mutation来通知state中的name发生改变。那么B和C组件就可以从vuex获取state中的name。大概就是这么个意思。
另外说一下:vuex中的几个关键要素:state、mutation、action以及vuex中自带的函数:mapState、mapMutation、mapAction、mapGetter(都是个人的理解,如有不当请指出。)
state主要定义一些初始化的变量;
mutation定义修改State的方法,有两个参数(state、value)
action定义触发mutation行为的方法;语法:方法名(context)、通过context.commit来触发mutation的方法;
getter定义获取state的方法,参数state;
上面内容主要说如何定义这些变量,下面说下如果在组件中使用这些方法:
第一种情况:设置/修改变量的两种方式:
a、在计算属性中或方法中调用mutation方法:this.$store.commit(‘方法名’,参数值)
b、使用action触发mutation方法:this.$store.dispatch(mutaion的方法)
官方不建议使用a,建议使用b。
第二种情况:获取参数
两种方式:1、this.
store.getter.方法名
使用this.$store.state等这些书写方式太长。所以可以通过vuex的辅助函数:mapState等来简化书写
辅助函数的使用方法:
1、mapGetters/apState放在computed属性中。
书写方式:a、…mapGetters([‘Getter定义的方法名’]) b、参数如果是count:…mapState({state=>state.count}) 2、mapMutation和mapAction,一般放在method中写:…mapMutation([‘mutation定义的方法名’]
https://blog.csdn.net/brave_haha/article/details/81000633
http://www.fly63.com/article/detial/996
watch computed区别
1、methods,computed的区别
例子:
var vm = new Vue({ el: ‘#app’, data: { msg: ‘nihao’, text: ‘byebye’ }, computed: { getmsg: function () { return this.msg } }, methods:{ gettext:function () { return this.msg } } }) <p>msg:{{getmsg}}</p> <p>text:{{gettext()}}</p> 在这个例子中,只要msg值发生变化,getmsg方法就会触发,而text的值发生变化,只要没有调用gettext方法,显示的值不会动态改变。就是说,methods中就是普通的方法,通过调用执行,computed中的方法会监听自己的依赖,依赖发生变化方法会自动执行。
默认加载的时候先computed再watch,不执行methods;等触发某一事件后,则是:先methods再watch。
watch适合处理的场景是,侦听一个数的变化,当该数据变化,来处理其他与之相关数据的变化(该数据影响别的多个数据)==当数据发生变化时,执行异步操作或较大开销操作的情况。如图所示:
image.png
computed适合处理的场景是,一个数据属性在它所依赖的属性发生变化时,也要发生变化,这种情况下,我们最好使用计算属性。如图所示:
image.png
react fiber架构的理解 https://juejin.im/post/5c92f499f265da612647b754#heading-3
RESTful架构优点:
RESTful架构设计原则(不同公司具体细节可能不同):
image.png
总结:优秀的RESTful接口设计,能够根据请求的路径及请求方法就能看出这个接口主要是对具体某个资源进行什么方法的操作以及返回数据的规则等等。
https://www.jianshu.com/p/b5966899c435
js 是更倾向于函数式编程,js是弱类型语言,也可以叫解释型语言,开始创建js时倾向于函数式编程。随着js的发展,加入了面向对象的东西,但它是函数式编程。 https://www.cnblogs.com/Darlietoothpaste/p/10633550.html
资深前端
会vue基础使用不值钱 组件化 + element-ui源码
做一些更高端和通用的事情,组件化,框架,框架源码里,有大量的最佳实践。vue源码里,大量的工程化,设计模式,代码规范的最佳实践。除了经验丰富外,视野更高,解决诡异的bug,架构的设计