vuejs+ts+webpack2框架的项目实践

YAZShawnee 7年前
   <h3>作者简介:王鹤,高级前端工程师,隶属于腾讯SNG增值产品部。主要负责QQ个性化业务的功能开发及技术优化。目前专注于框架的研究,致力于提升效能,解放生产力。</h3>    <h2>一、框架的选型</h2>    <p>没有什么框架是全能的,都有其适用场景。我们的最初的定位一定要围绕我们的业务来选择。我们个性化业务是基于移动端的多页面应用。我们综合考虑之后,决定使用vuejs+typescript+webpack2来作为现在和将来的核心主框架,未来的演变也基于此基础。</p>    <p>1、为什么使用vuejs</p>    <p>早些年,前端的MVVM框架呈现爆发式的增长,比如angular,react,vuejs,avalon,meteor。对比过这些框架,最终选择了vuejs作为我们业务的「核心引擎」。原因如下:</p>    <p>1)angular和react虽然火爆,但是学习曲线还是太陡了,需要理解很多东西,上手不易。vuejs上手非常容易,语法简单。我们试验过,一个有前端基础但没有接触过vuejs的同学,基本上一天就可以上手开发简单应用。几天之内学会模块组件的概念基本就可以完成中等复杂的业务。这是其它框架无法相比的。</p>    <p>2)vuejs的文档是非常友好的,当然现在其它框架的文档建设也在加强。这也告诉我们一个东西是否能普及开,核心有两点,一是本身足够好用,二是足够方便理解。vuejs做到了这点,所以它火了。</p>    <p>3)vuejs体积小适合移动端业务,vuejs在gzip压缩后的代码是react的一半。而且移动端基本没啥兼容性问题。PC的话兼容IE9+。如果是PC业务,其实我们现在也只是兼容IE9+。把时间浪费在兼容性问题上完全是浪费生命。所以现在的前端开发者,感谢这个时代吧。</p>    <p>4)vuejs最新的版本中也逐步借鉴学习了一些其它框架的优秀的思想,能学习和使用一种框架用到深处,我想是足够满足我们的业务需求的。</p>    <p>2、为什么使用typescript</p>    <p>1)数据流结构规范化的重要性</p>    <p>在业务需求的中级阶段,我们意识到数据流结构规范化的重要性。vuejs因为本质是MVVM框架,引入了数据流的概念。但JS是弱类型语言,数据流本身比较随意,比如一个Button的属性,基本属性有按钮文字(text),按钮状态(status),按钮进度(process)等三个基本数据属性。但是团队中不同人可能有自己的想法,关于一个Button的定义命名都有可能不一样。长期看来,注定无法维护。这时候数据结构的规范(接口,强类型)显得非常重要。引入这些概念,对基本组件的定义和规范在代码编写阶段自然就形成了约定(不遵守规范,编译都通不过),这比文档规范约束有效和方便得多。</p>    <p>typescript恰好就是为此类需求而诞生的,而且充分考虑到兼容性。对之前JS的代码完全兼容。</p>    <p>2)使用ES6/ES7特性,具有优秀的自编译能力</p>    <p>很多ES6/ES7项目的编译都是通过babel进行处理的,不熟悉的朋友可能整配置都要搞半天,而且babel还有babel5和babel6的区别,两者也并不太兼容。</p>    <p>typescript具有自编译的能力,不需要额外引入babel。只依赖tsconfig.json,将此文件放到项目的根目录,即可全局配置。</p>    <p>typescript不仅能满足babel的编译的功能,而且比babel做得更好。比如很重要的async/await语法,babel在使用的时候会引入相当大的一个文件:</p>    <p><img src="https://simg.open-open.com/show/1959921dde9e3c642870c44ed93b2835.jpg"></p>    <p>typescript则非常干净利落,就几十行代码:</p>    <p><img src="https://simg.open-open.com/show/61002b3123f0d8fe700ce9960e1205f5.jpg"></p>    <p>3)typescript2.0引入了@types,系统性地解决了绝大部分公共库的类型定义问题</p>    <p>WONDER迟迟没有在生产项目中使用typescript的一个很大的原因就是类型定义实在是太麻烦了。从DefinitelyTyped到typings,最后是@types。微软自己也发现有这么个问题,所以也在一直演进。目前来看,@types算是一个不错的方案。充分利用npm进行管理和维护,且绝大多数公共库都已经支持@types,比如@types/jquery、@types/node等。vue更先进一些,直接本身vue模块即支持typescript的类型定义。不需要额外的@types/vue。也就是`npm install vue`即可在typescript中正常使用。</p>    <p>3、为什么使用webpack2</p>    <p>使用webpack2最核心的地方就是使用tree-shaking特性,tree-shaking是大势所趋,符合代码极简主义,提高代码使用率。对于代码的精简还是挺高的,大概可以优化30%的代码体积。</p>    <p>要使用webpack2的tree-shaking,前提条件就是使用ES6的module,这是核心根本。所以意味着所有的代码要基于ES6的module来写。建议从新项目入手,代价较小。</p>    <p>阶段性小结:</p>    <p>1、使用vuejs,从数据驱动的角度来处理逻辑,操作DOM,可以完全抛弃zepto/jQuery</p>    <p>2、由于使用数据驱动,数据本身的结构由为重要,再加上方便优雅地使用ES6/ES7,我们引入了typescript作为主要开发语言</p>    <p>3、利用ES6的module的重构代码,通过webpack2的tree-shaking来达到简化代码体积,提高代码利用率的目的。</p>    <h2>二、项目实践</h2>    <p>从本章节起开始结合线上项目来讲讲框架的使用和细节。本次介绍的项目名叫「个性化体验卡」。基本上借助此项目从头搭建了一套基础底层,项目本身是多页面项目。</p>    <p>1、项目结构</p>    <p>首先是项目结构,基本上只需要一个公共库目录和项目本身目录即可。</p>    <p><img src="https://simg.open-open.com/show/e220573e83f6e11d8bf21c9b21a01f8c.jpg"></p>    <p>公共库目前搭基础,包含common和shim两个即可,这里特别指出的是mqq的库被重写后,从原先的 <strong>1400行</strong> ,减少到 <strong>200行</strong> 。去除了相当多的无用代码,极简风格。</p>    <p>项目本身目录也很好理解:</p>    <p>common——项目的公共方法目录,比如helper之类的</p>    <p>comp——项目的组件目录,核心目录。以后各个业务都是组件复用</p>    <p>css——CSS目录,发布时会inline到html中</p>    <p>html——入口文件目录,基本就是一个框架,如下图所示:</p>    <p><img src="https://simg.open-open.com/show/16622403682de98c035add5d143da7fb.jpg"></p>    <p>由于tree-shaking的因素,我们的首屏业务逻辑代码可以直接inline到页面当中,复杂的次屏逻辑可以以动态组件的形式加载。形成**直出+主内嵌JS+异步动态JS组件**的优雅的加载模式。</p>    <p>细节提示:</p>    <p>这里有个开发细节和大家讲解一下,我们在上图中,我把vuejs并没有以模块的形式打入到detail.entry.js里,因为一是额外增加了js的体积,二是我们的项目是多页面的项目,公共vuejs是可以也有必要进行缓存和复用的。</p>    <p>这里采用外链离线包+强缓存的形式是比较合理的。</p>    <p>2、配置文件</p>    <p>初学者在做配置的时候一般比较蒙逼,所以有脚手架这么个东西来帮助初学者快速搭好环境,但实际上每个人从事的项目不完全一样。脚手架并不能完全满足需求。所以了解一下配置的基本原理还是有相当必要的。</p>    <p>本文是基于vuejs、typescript、webpack2的框架,用gulp进行把几个东西串起来。</p>    <p>因为文中的脚手架结合了一些项目本身的各种功能,这里先讲几个核心需要注意的点。</p>    <p>1)npm install 几个最重要的模块</p>    <p>gulp、vue、vue-class-component、typescript、webpack、@types/node、ts-loader、text-loader</p>    <p>这几个装了以后,基本上项目用到的最核心模块都有了。</p>    <p>2)配置tsconfig.json</p>    <p><img src="https://simg.open-open.com/show/53b3bc1da652774a699ad2be6126f8a7.jpg"></p>    <p>这个配置非常非常关键,建议初学者直接Copy,每个配置都有用,我也写了注解,不要随意删减。</p>    <p>3)webpack.config.js配置</p>    <p><img src="https://simg.open-open.com/show/105eda1ab062a28bbc242a066048af2c.jpg"></p>    <p>细节提示:</p>    <p>注意红框部分的地方,一定不要配置错。</p>    <p>externals这样写的目的是不要把vue打包进来,一定要注意大小写。大小写不对就容易出错。不理解的初学者直接Copy。</p>    <p>rules就只配这一个就可以了,其实就是对于模板html文件的处理。由于我们的代码(IDEWebStorm自带的特性)在编写的时候就转换成了js,所以webpack不需要加ts-loader。但最理想的状态就是编译过程交给webpack或gulp进行,IDE不自动编译js文件,这样源代码比较纯粹。</p>    <p>3、vuejs的组件写法</p>    <p>vuejs其实是一个很灵活的框架,可以有很多种写法。vue的组件看官方文档也有很多写法。但在typescript中,写法和之前有很大变化(但其实和react、angularjs很像了)。如果不这么写,你会发现你的编辑器到处报错。。So,目前我们线上项目中的组件大概长这个样子:</p>    <p><img src="https://simg.open-open.com/show/2de87b777d711248ae0b8ef93a691008.jpg"></p>    <p>这就是一个组件,如何使用呢,我们看入口文件的JS。</p>    <p><img src="https://simg.open-open.com/show/f8ecf326cf6b74089deb506f96e5aa82.jpg"></p>    <p>更多细节可以参考官方DEMO:</p>    <p><a href="/misc/goto?guid=4959751388081625462" rel="nofollow,noindex">https://github.com/vuejs/vue-class-component/tree/master/example</a></p>    <p>这种写法是很优雅和可维护的,以及配合typescript的特性,感觉非常的愉快!</p>    <p>4、编辑器的选择</p>    <p>我们项目组的同学基本就两种编辑器,一种是Webstorm,一种是vscode。其实Webstorm除了卡,其它都比vscode好用。</p>    <p>这两种编辑器,Webstorm自带编译功能,所见即所得。配置好tsconfig.json,剩下都省心。vscode略复杂,需要建立一个task,然后在跑项目的时候执行build。也可以达到类似的效果。</p>    <p>5、环境搭建的坑</p>    <p>其实如果初学者前面不按照我说的一些细节来操作的话,很容易在搭环境上一堆编译报错,编辑器语法报错。会影响初学者的学习热情。所以WONDER这里会尽量在抽离一个相对普适的脚手架给各位使用,尽请期待。</p>    <p>其实初学者按照我上述的配置操作的话,一般问题也不是太大。有遇到任何编译报错或者语法报错,欢迎和WONDER交流,我也作下记录。我的微信号是:wonderhwang</p>    <p>6、兼容性问题</p>    <p>项目实践过程中,有些兼容性问题这里提出来。避免大家再踩。</p>    <p>1)首先关于Promise的兼容性实践证明如果你是基于手Q或微信的移动端业务,可以不需要引入Promise-Polyfill。PC端还是建议需要Promise-Polyfill。PC对文件大小不敏感,所以加下polyfill无所谓。移动端没有必要,主流都支持。目前线上业务也没有收到什么反馈页面功能由兼容性异常的。</p>    <p>2)有些写法尽量不要用,WONDER已知的就是不要使用ES6的模板字符串。</p>    <p>首先虽然我们配置了tsconfig.json,但是并不是所有的语法都转成了ES5,模板字符串就没有完全转义。在IOS8的系统里面兼容性有问题,模板会报错,程序会出问题。</p>    <p>3)不要使用`Object.assign`,这个也不会转义,在IOS8系统也有兼容问题。而且也不是很优雅。WONDER找到一个优雅的写法,就是使用ES6的三点解构符`...`,虽然这两者并不完全相同,且三点解构符其实也并不是用在这里的。只是结合我们的使用场景,比较巧妙。</p>    <p>我们使用最多的无非就是两个object对象参数的合并。如果`Object.assign`有兼容性问题,又不想写诸如`_.extend`。那么就试试这种写法,如下所示:</p>    <p><img src="https://simg.open-open.com/show/83fe4c1bc0ac713891f6d5f290400021.png"></p>    <p>我们看看typescript如何翻译变成这里的,对应的JS如下:</p>    <p><img src="https://simg.open-open.com/show/316a978653bddb7378d626358b0449d6.jpg"></p>    <p>我们注意到`__assign`方法,其实就是翻译了三点解构符。果然如此:</p>    <p><img src="https://simg.open-open.com/show/30e29d428dc6003668dd1a70a848b583.jpg"></p>    <p>7、数据接口</p>    <p>这个是项目实践过程中的细节了。interface这里指的是数据接口,也就是我们熟知的DAO层。JS之前过于灵活,现在有typescript辅助,数据结构的定义会更加清晰和规范。不符合规范会报错的。</p>    <p><img src="https://simg.open-open.com/show/846de2e0b208ab2cbe88ece97221663b.jpg"></p>    <p>8、TS中的DOM操作</p>    <p>document.querySelector('.group-qp')).style.display = 'none';</p>    <p>这么写TS是会报错的:</p>    <p><img src="https://simg.open-open.com/show/7a2c93a503b2a688eac21efedb2bfd66.png"></p>    <p>原因是document.querySelector返回的是Element对象,Element对象并没有style方法,只有继承的HTMLElement对象才有style方法,所以这里要写为:</p>    <p>(<HTMLElement>document.querySelector('.group-qp')).style.display = 'none';</p>    <p>9、采用事件来处理中等复杂程序的组件通信问题</p>    <p>中等复杂的组件通信建议直接使用eventbus的$on和$emit,复杂的就使用vuex。这里组件虽然是用事件进行信息传递,我们还是可以在模板中进行显示的声明,符合vue模板显示声明一贯的做法。如下图所示:</p>    <p><img src="https://simg.open-open.com/show/1d4980ebf57d2e17250363128233423d.jpg"></p>    <p>组件声明,v-event:xxx,xxx表示事件名称,后面是传递参数,非常直观。组件的具体监听是:</p>    <p><img src="https://simg.open-open.com/show/617094385b62daac45ca5a6d0c3083d7.png"></p>    <p><img src="https://simg.open-open.com/show/a524be1600e5090660981a7c948b0bc5.jpg"></p>    <p>所有的事件触发和监听都挂载在eventbus上面,eventbus名为「事件总线」,其实本质就是一个Vue的实例而已</p>    <pre>  import eventbus from 'lib/common/eventbus';</pre>    <pre>  <em>/**  <em> * Created by wonderwang on 2017/7/11. <em> */ <strong>import Vue <strong>from 'vue'; <strong>export default new Vue();</strong></strong></strong></em></em></em></pre>    <p>10、其它问题</p>    <p>1)标签不能用驼峰法,比如在vue模板中<rankDetail></rankDetail>是不会被正确识别的,应该写成<rank-detail></rank-detail></p>    <p>2)不能用关键字dialog,可能是vue本身的BUG。</p>    <p>原    文: <a href="https://mp.weixin.qq.com/s?__biz=MzI3NTM1MjExMg==&mid=2247484571&idx=1&sn=b8c54910f5e9b0dedbd7be24fb247304&chksm=eb075bc4dc70d2d281bd41f41f247d327cb5b47e96140b2445b463e21e80da25cdea34778976&mpshare=1&scene=1&srcid=08140lNGWT2e9mJKrPdJccNH#rd" rel="nofollow,noindex">小时光茶社</a></p>    <p>作    者:王鹤</p>    <p> </p>    <p>来自:https://sdk.cn/news/7338</p>    <p> </p>