MVVM是Model-View-ViewModel的简写。微软的WPF带来了新的技术体验。本文主要和大家分享vue实践小结之mvvm学习,希望能帮助到大家。
1 mvvm 学习
1.1 实现原理
mvvm类框架的实现原理不复杂,大致如下:
模板分析得到依赖的属性
通过某种变动监测手段监测这些依赖的属性
当属性变动的时候,触发相应的directive的处理逻辑即可
实际上,directive的处理逻辑不一定是对view进行操作,比如上报。但是,在mv的思想下,建议对view的操作都集中在directive里实现
从最核心上看,mv思想仅仅是一个观察者模式的具体应用于延展而已
1.2 核心技术点
1.2.1 模板分析
模板分析是比较基础的,凡是和view相关的基本都会涉及模板,这是原始资料,这里的关键点是模板来源的问题,实际上,它应该可以是任何字符串
这里暗示了框架需要一个模板解析器,不管这个解析器复杂还是简单,它都处于一个模式:【输入 –> 模板引擎 –> 输出】
于是,mvvm的模板解析器特点如下:
输入:任何符合规则的字符串
输出:需要监听的data.attr,directive,filter
在设计一个框架的时候,如果想要有更好的可扩展性,则
输入应该足够灵活,从来源上来说,模板可以是someDomHere.html(),也可以是动态输入,那就更有可适用性;从内容上来说,如果引擎可以识别更高级的语法,那就更有功能性
输出应该足够收敛,收敛的意思是有限并规则,像mvvm框架,最后出来的只是directive和filter,具体的处理都集中在这两个概念中,仅扩展这两个概念,即可对系统进行扩展
1.2.2 变动监测
在众多mvvm类框架中,实现变动监测有3种:
门面方法setter,getter:比如knockout,q。限定可以变动的入口,并且让入口使用权放给用户来决定。
利用defineProperty:比如vue,avalon。本质上也是setter,getter,但是没有把入口使用权放给用户来决定。
dirty check:比如angular。对angular的研究够多了,这里也不赘述了。
1 2 3 4 5 6 7 8 9 10 11 |
<span class="comment">//方式1 vs. 方式2</span> <span class="comment">//方式1:</span> vm.<span class="variable">$set</span>(aaa, <span class="number">1</span>); <span class="comment">//会触发变动逻辑</span> vm._data.aaa = <span class="number">2</span>; <span class="comment">//不会触发变动逻辑,不过这不是框架希望的操作,可以被hack</span> vm.<span class="variable">$get</span>(aaa); <span class="comment">//2</span> <span class="comment">//方式2:</span> vm.aaa = <span class="number">1</span>; <span class="comment">//一定会触发变动逻辑</span> vm._data.aaa = <span class="number">2</span>; <span class="comment">//也可以找到内部的data进行修改,但是没用</span> vm.aaa; <span class="comment">//1</span> |
1.2.3 小结与延伸
对一类复杂并且常见的问题进行分析,解耦,抽象,在实践的过程中获得广泛的认可,那就形成了一种模式,mvvm也是一种模式,它不一定叫mvvm模式,这也不是笔者能决定的
对于这个模式的核心,笔者理解如下:系统根据配置得到了对某些数据源的某些处理规则,当数据源变动时就会引发相应的处理规则。模式的扩展是双向性,这由系统实现来决定,当符合某些规则的时候,可以对数据源进行更新。
我们跳出view的概念禁锢,联想实现一个监控系统,其实这个模式非常适合用在监控系统上面。
一般的监控系统的处理逻辑是:由收集源对监控数据进行收集整理,然后存储到数据库中,监控系统实时监控数据源,绘制实时的图线(反馈),当数据源发生了符合某些规则的变动时,就会触发相应的动作,比如报警。
如何实现这个系统,让系统具有更高的扩展性?参考mvvm模式,可以这样:
收集系统独立于监控系统,各不相同,暂且不论。监控系统通过某些配置文件取得需要监控的数据源与相应的处理逻辑规则,当数据源发生变动时触发相应的处理。
按照mvvm模式,进行一些抽象。
数据源不一定限定在数据库中,他可以在任何地方,只需要系统可以通过某些可配置的规则获取得到
处理规则进行抽象,让它更容易被扩展,比如发邮件,发短信,发微信,发qq消息等等
对应前端的mvvm框架,模板就是配置文件,directive就是处理规则,data对应数据源。
当系统需要新增一个数据源的时候,只需要更新配置文件,让系统读取即可启动数据监控
当需要新增一个处理规则的时候,可以通过一个热插拔的处理规则插件系统,扩展一个新的处理规则,再更新配置文件,系统即可接受新的处理规则
2 vue实践
vue介绍就不用了,太多资源了。这里讲述一下vue实践过程中的一些收获
2.1 组织结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
+ src +-- common +-- vue +-- coms +-- directives +-- filters +-- vue.js +-- vue.ext.js +-- pages +-- index +-- index.js +-- vue.ext.js +-- xxx.mixin.js |
2.2 Vue扩展
vue的扩展非常方便,与vue相关的资源都放置在src/common/vue/下面,比如coms(组件),directive,filter
src/common/vue/vue.ext.js是对vue进行全局公共的扩展,对于所有页面共有的扩展放在这个文件下面,内容如下:
可以看到,扩展vue库本身有4个扩展点:
扩展Vue库的全局方法/属性,方式:Vue.xxx = …
扩展Vue实例的方法/属性,方式:Vue.prototype = …
扩展directive,方式:Vue.directive(‘directiveName’, options);
扩展filter,方式:Vue.filter(‘filterName’, function(){});
对于页面单独需要的扩展,集中在src/pages/pageName/vue.ext.js里面,形式与全局的vue.ext.js一样
在实例化Vue的过程中也有许多可以扩展与优化的地方,在实践过程中只是应用了mixin功能,其他的可以慢慢深入
mixin的作用是在实例化Vue的时候混入一些功能,它可以混入许多特性,格式与实例化Vue时用到的option格式一样,比如index页面的mixin.js的内容如下:
这个mixin混入了两个方法,多个Vue实例共享的options可以放置到mixin中,从而避免了代码重,比如在实例化Vue的时候这样使用mixin:
可以看到mixin是个数组,因此可以同时使用多个mixin
实际上这里的mixin主要不是为了避免代码重复(实践的时候只是这样用),mixin是一种模式,一个mixin内聚了实现一项功能的方法/属性集合,在定义/生成实例的时候,通过混入mixin就可以让该实例拥有某项功能,归根结底是组合vs继承问题的产物
2.3 vue组件插入问题
2.3.1 首屏
对于首屏的vue组件,直接把模板放在主页面中即可,初始化的时候只需要把el参数传入,Vue就会用el的html作为模板来初始化Vue实例:
这里需要注意的是在模板中不能使用{{}},否则在还没初始化之前,页面会显示奇怪的东西,比如:
1 2 3 4 5 6 7 |
<p>hello, {{name}}</p> <!--初始化前,页面会直接展示hello, {{name}}--> <img src=<span class="s tring">"{{imgSrc}}"</span> /> <!--初始化前,会报错,can not find http:<span class="comment">//xxx.com/{{imgSrc}}--></span> <!--正确的写法:--> <p v-text=<span class="string">"'hello, '+name"</span>>hello</p> <img v-attr=<span class="string">"src: imgSrc"</span> />
|
{{}} 只是一个语法糖,不建议使用
2.3.2 非首屏
对于非首屏的组件,使用vue的方式和原始方式差不多,先生成节点,然后append,譬如:
el参数可以接收query string,也可以直接是一个dom节点,如果是dom节点则直接编译dom的内容。如果dom节点不在文档树中,则利用vueObj.$appendTo方法将vue实例的根节点插入到文档树中
上面这种方式是在页面中没有组件的【坑】的情况下使用的,如果页面为组件留了【坑】,比如:
1 2 |
0510d38451755f656b967a56ae76d68aclass54bdf357c58b8a65c66d7c19c8e4d114=e7b5d65d577098ec09191daa17885c87"hotRecord"54bdf357c58b8a65c66d7c19c8e4d114 id=e7b5d65d577098ec09191daa17885c87"js-hotRecord"54bdf357c58b8a65c66d7c19c8e4d114>4d7ab0de9a42de71c682b0860bad1410
|
那么,我们可以这样初始化vue实例:
利用template参数传入模板,并指定el,那么vue实例在初始化之后就会自动把内容插入到el中
通过vue实现组件的主要核心也就这些,更方便的组件写法也只是对这些进行封装
2.4 自定义 directive
在vue中自定义directive是非常简单明了的,要自定义一个directive,可以注册3个钩子函数:
bind:仅调用一次,当指令第一次绑定元素的时候。
update:第一次调用是在 bind之后,用的是初始值;以后每当绑定的值发生变化就会被调用,新值与旧值作为参数。
unbind:仅调用一次,当指令解绑元素的时候。
下面简单介绍一个自定义directive——lazyload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<span class="keyword">function</span> addSrc(){} <span class="keyword">function</span> load(){} module.exports = { bind: <span class="keyword">function</span>() { <span class="keyword">if</span> (!hasBind) { <span class="comment">//全局事件只绑定一次</span> hasBind = <span class="keyword">true</span>; (document.querySelector(<span class="string">'.z-scroller'</span>) || window).addEventListener(<span class="string">'scroll'</span>, T.debounce(load, <span class="number">100</span>), <span class="keyword">false</span>); } <span class="comment">//这里也可以使用data属性来获取</span> <span class="keyword">var</span> defaultSrc = <span class="keyword">this</span>.el.getAttribute(<span class="string">'data-defaultsrc'</span>); <span class="keyword">if</span> (defaultSrc) addSrc(<span class="keyword">this</span>.el, defaultSrc); <span class="comment">//先使用默认图片</span> }, update: <span class="keyword">function</span>(src) { <span class="comment">//directive初始化时,会调用一次bind和update,bind没有传入src,只有update才会传入src</span> <span class="comment">//因此只能在update这里拿到需要lazyload的src</span> <span class="comment">//lazyload不允许修改src,这里限制只会执行一次update,防止src被修改造成的影响</span> <span class="comment">//注:接受src改变可以实现,只是需要一些复杂的处理,这里为了简单起见不让src改变</span> <span class="keyword">if</span> (<span class="keyword">this</span>.init) <span class="keyword">return</span>; <span class="keyword">this</span>.init = <span class="keyword">true</span>; <span class="comment">//如果图片已经加载了,就不需要注册了,这里也可以使用data属性来区分</span> <span class="keyword">var</span> isLoad = parseInt(<span class="keyword">this</span>.el.getAttribute(<span class="string">'data-isload'</span>)); <span class="keyword">if</span> (isLoad) <span class="keyword">return</span>; <span class="comment">//注册需要lazyload的图片</span> <span class="keyword">list</span>[index++] = <span class="keyword">this</span>; <span class="keyword">list</span>[index++] = src; } <span class="comment">//这里有一个最大的问题:由于有local的存在,会创建两个一模一样的lazyload directive</span> <span class="comment">//按理说应该定义一个unbind,但是在unbind中找到并除掉local创建出来的lazyload directive会比较麻烦</span> <span class="comment">//因此在load函数里面做了一个处理:如果发现需要lazyload的节点不在文档树中,则剔除掉这个lazyload</span> <span class="comment">//通过这个直接省掉了unbind函数</span> };
|
自定义filter也很简单,只是定义一个处理函数而已,这里就不多介绍了
2.5 实践过程中的痛点与小技巧
2.5.1 没有事件代理
用习惯了事件代理,突然没有了会有点不习惯,但是回头想想,事件代理真的很重要吗?还是说我们只是习惯了事件代理而已?
通过vue注册相同的事件并不费事。另一个问题,只要事件不多,大约不超过50,100,也不至于耗掉很大的内存,因此有时候还真不需要事件代理。如果真的需要,也只是实现一个contain方法而已
2.5.2 没有if-else的奇怪
最初看到下面的代码真的会觉得很奇怪
1 2 3 |
af5c2ece8ea7a096914f90670424bcf6if54bdf357c58b8a65c66d7c19c8e4d114=e7b5d65d577098ec09191daa17885c87"hasTitle"54bdf357c58b8a65c66d7c19c8e4d114>xxx39528cedfa926ea0c01e69ef5b2ea9b0 06a275819bf51ee1d38350f8e7d3f19aif54bdf357c58b8a65c66d7c19c8e4d114=e7b5d65d577098ec09191daa17885c87"!hasTitle"54bdf357c58b8a65c66d7c19c8e4d114>xxx94b3e26ee717c64999d7867364b1b4a3
|
2.5.3 单值
虽然vue有语法解析器,可以在directive的值中使用表达式,但是当出现一个复杂的表达式时,会污染模板,让代码可读性变得很差,又或者,表达式完成不了这个任务的时候。
因此,在mvvm实践的过程中,深深地发现,利用单值(最多只用一个?:表达式)来写模板会让代码变得很清晰,更加可读,增加代码的可维护性,而且这也更符合mvvm的核心思想:f(state) = view
有些库连语法解析器都没有,比如q,但也能很好的工作。
那么,复杂的操作放在哪里呢?
对于不会变的值来说,也就是常量,要在初始化之前完成处理
对于会变的值来说,把复杂的操作放在filter里面,在filter里面不仅可以进行复杂处理,甚至可以同时应用到其他字段,这不完全等同于computed attribute
2.5.4 替代 $(document).on
用jquery/zepto的时候,习惯了用$(document).on来充当一个全局的事件代理,在使用vue的时候,需要抛弃zepto,因此需要解决这个问题
因为vue实例本身就有event功能,因此这里解决的办法是创建一个全局的空vue对象,把它作为全局的事件代理:
1 2 3 4 5 6 7 8 9 |
e63d03b1fe1ef417bff512a2da2d7305//common/vue/vue.ext.js 回头看前面对该文件的介绍可以看到这句54bdf357c58b8a65c66d7c19c8e4d114 Vue.noopVue = 9b8f193a100df443bb5f942579a9d04cnew54bdf357c58b8a65c66d7c19c8e4d114 Vue({});
e63d03b1fe1ef417bff512a2da2d7305//a.js54bdf357c58b8a65c66d7c19c8e4d114 Vue.noopVue.5a4944fb39e8a6d067595fa969ab7502$on54bdf357c58b8a65c66d7c19c8e4d114(e7b5d65d577098ec09191daa17885c87'someEvent'54bdf357c58b8a65c66d7c19c8e4d114, 9b8f193a100df443bb5f942579a9d04cfunction54bdf357c58b8a65c66d7c19c8e4d114() {});
e63d03b1fe1ef417bff512a2da2d7305//b.js54bdf357c58b8a65c66d7c19c8e4d114 Vue.noopVue.5a4944fb39e8a6d067595fa969ab7502$emit54bdf357c58b8a65c66d7c19c8e4d114(e7b5d65d577098ec09191daa17885c87'someEvent'54bdf357c58b8a65c66d7c19c8e4d114, [opts]); |
3 总结
虽然,最后在付出产出比权衡中放弃了对现有项目的vue改造,但是这并不妨碍我们研究mvvm类框架
mvvm模式还是值得我们去深入学习的,而在实践中,我们也能学习到许多
用一种不一样的思想和思维去开发的体验也会令我们在看待问题,处理问题的道路上有所收获。
相关推荐:
以上是vue实践小结之mvvm学习的详细内容。更多信息请关注PHP中文网其他相关文章!

随着Web应用程序的快速发展,越来越多的开发者将目光投向了各种新兴的Web开发框架和架构设计模式。其中一个备受瞩目的设计模式就是MVVM(ModelViewViewModel)架构模式。MVVM采用了一种现代化的设计模式,通过将UI和业务逻辑相分离,使得开发人员能够更好地管理和维护应用程序。此外,MVVM减少了不必要的耦合,提高了代码的可重用性和灵活性,

作为一名Java开发者,学习和使用Spring框架已经是一项必不可少的技能。而随着云计算和微服务的盛行,学习和使用SpringCloud成为了另一个必须要掌握的技能。SpringCloud是一个基于SpringBoot的用于快速构建分布式系统的开发工具集。它为开发者提供了一系列的组件,包括服务注册与发现、配置中心、负载均衡和断路器等,使得开发者在构建微

win7系统自带有备份还原系统的功能,如果之前有给win7系统备份的话,当电脑出现系统故障的时候,我们可以尝试通过win7还原系统修复。那么win7怎么还原系统呢?下面小编就教下大家如何还原win7系统。具体的步骤如下:1、开机在进入Windows系统启动画面之前按下F8键,然后出现系统启动菜单,选择安全模式登陆即可进入。2、进入安全模式之后,点击“开始”→“所有程序”→“附件”→“系统工具”→“系统还原”。3、最后只要选择最近手动设置过的还原点以及其他自动的还原点都可以,但是最好下一步之前点击

随着Web应用程序的需求越来越高,PHP技术在开发领域中变得越来越重要。在PHP开发方面,测试是一个必要的步骤,它可以帮助开发者确保他们创建的代码在各种情况下都可靠和实用。在PHP中,一个流行的测试框架是PHPUnit。PHPUnit是一个基于Junit的测试框架,其目的是创建高质量、可维护和可重复的代码。下面是一些学习使用PHPUnit框架的基础知识和步骤

随着win10系统的成熟,微软停止win7的更新和支持,越来越多人选择win10系统使用,打算将自己的win7升级win10系统。不过很多小伙伴不知道win7如何升级win10系统,找不到升级的按键。下面小编教大家一个简单的win7升级win10系统的方法。我们可以借助工具轻松实现win7升级安装win10的方法,具体的操作步骤如下:1、先在电脑上下载安装小鱼一键重装系统工具并打开,关闭电脑的杀毒软件,备份c盘重要资料。然后选择需要安装的win10系统点击安装此系统。2、这个界面选择想要安装的软

香港中文大学(深圳)吴保元教授课题组和浙江大学秦湛教授课题组联合发表了一篇后门防御领域的文章,已顺利被ICLR2022接收。近年来,后门问题受到人们的广泛关注。随着后门攻击的不断提出,提出针对一般化后门攻击的防御方法变得愈加困难。该论文提出了一个基于分割后门训练过程的后门防御方法。本文揭示了后门攻击就是一个将后门投影到特征空间的端到端监督训练方法。在此基础上,本文分割训练过程来避免后门攻击。该方法与其他后门防御方法进行了对比实验,证明了该方法的有效性。收录会议:ICLR2022文章链接:http

Laravel是一个基于PHP的开源Web应用程序框架,是当今最受欢迎的框架之一。它的设计思想是以简单、优雅的方式解决复杂的问题,为开发Web应用程序提供了丰富的工具和资源。如果你想在PHP中学习Laravel框架,下面是几个关键步骤:第一步:安装和配置Laravel在开始使用Laravel之前,您需要安装PHP和Composer。Composer是一个PH

这个问题涉及到许多实际上并不了解核心技术并希望在SeleniumAutomation领域发展职业生涯的专业人士。编码这个术语让非程序员有点害怕,甚至不敢从自动化之类的东西开始。人们认为非程序员无法在自动化方面表现出色,但这只是在头脑中。许多值得和有能力的手动测试人员回避Selenium,只是认为它需要一些特殊技能。Selenium脚本是用多种语言设计的,例如Python、Ruby、C#、JavaScript和Java就是其中之一他们当中就有这样的人。了解了Java的受欢迎程度和未来前景,现在更倾


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

Dreamweaver CS6
视觉化网页开发工具

禅工作室 13.0.1
功能强大的PHP集成开发环境

EditPlus 中文破解版
体积小,语法高亮,不支持代码提示功能

SublimeText3 英文版
推荐:为Win版本,支持代码提示!

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境