微信号:FrontDev

介绍:分享 Web 前端相关的技术文章、工具资源、精选课程、热点资讯

从 Angular 1 升级到 Angular 2 需要准备的步骤

2016-07-18 20:16 伯乐专栏\/段昕理

(点击上方公众号,可快速关注)

原文:Oren Farhi

译文:伯乐在线 - 段昕理

链接:http://web.jobbole.com/86930/


我最近在试玩 Angular 2。刚开始感觉很奇怪,和我们钟爱的第 1 版完全不同。第 1 版是用 ES5 标准的纯 javascript 编写,而第 2 版采用了 typescript 和 es 2015。不过,你已经可以采取一些步骤,让你的 Angular 1 代码(或用 Angular 1 创建的新项目)更加接近 Angular 2。


我为什么要为 Angular 1 迁移到 Angular 2 做准备


首先,当时机成熟了,你打算用 Angular 2 作为框架时,肯定想让代码迁移更容易些。目前,Angular 小组已经提供了一些迁移策略,你可以混合使用 Angular 1 和 Angular 2 组件,但目标是要将代码库统一,最终只使用一个框架。


其次,在 Angular 2 中更多的是写纯 javascript,然后才是使用专有的框架代码。


再次,社区和浏览器厂商将逐步拥抱 Ecmascript 的最新标准,所以,坚持使用标准编码,尽可能让代码库可复用,而不管选择的框架是什么。


迁移到 Angular 2 的步骤


采取这些策略可以让你的代码更加接近 Angular 2,使转换变得容易。


1. 开始用 Ecmascript 2015


Angular 2 使用 Typescript 编写,Typescript 是 Ecmascript 2015 的超集,带有更多的特性。不过,如果你不喜欢 Typescript, 也可以只用 Ecmascript 2015 编写 angular 2。 目前,代码最终都会编译成 Ecmascript 5。所以实际上你也可以用 Ecmascript 5 来编写 Angular 2。


但是,在我看来,使用 Ecmascript 2015 的新特性,可以减少代码量(有些时候…)、增强代码可读性、用上令人兴奋的特性,如解构。


如果想使用 Ecmascript 2015 的特性,你需要一个转换器来编译代码。目前最流行的转换器是 babel。babel 在很多流行的构建脚本中都可以配置,如 gulp、webpack、browserify 及其它。


// 对象属性增强

var exports = {

    search: search,

    setType: setType,

    setDuration: setDuration

};

// 可以写成这样

var exports = {

    search,

    setType,

    setDuration

};

 

/////////////

// 使用“胖箭头” =>  可以简化代码并增强可读性

var videoIds = response.data.items.map(function(video){

    return video.id[idPropertyName[activeType]];

}).join(',');

 

// 使用了胖箭头符号

var videoIds = response.data.items.map((video) => {

    return video.id[idPropertyName[activeType]];

}).join(',');


2. 使用 “angular.service” 替换 “angular.factory”


使用 Ecmascript 2015 意味着我们可以用新的 “class” 关键字来创建新对象甚至扩展其它对象。我曾经写过,比起继承我更热衷于组合,所以我看不出用 “extend” 实现继承有什么用处,不过通过 class 的特性的确可以为创建对象增加好用的语法糖(简化代码)。


在 angular 1 中的 “service” 和 “factory” 的区别是实例化方法:


“service” 使用 “new” 关键字调用(仅一次)


“factory” 使用普通函数调用 — 不需要 “new” 关键字。


在 Angular 2 中,Services 使用了 Ecmascript 2015 类编写。这会导致你需要将 Angular 1 代码中的 factories 转化成 services,并且使用 “class” 替代 function。


例如在我的开源项目-Echoes Player 中,我使用了“class” 和 Angular“service” 编写 youtube api 服务(我以前用的是 factory):


(function() {

    'use strict';

 

    /* @ngInject */

    class YoutubePlayerApi {

 

        /* @ngInject */

        constructor ($window, $q) {

            /*jshint validthis: true */

            this.deferred = $q.defer();

            //当 API 准备好时,Youtube 回调

            $window.onYouTubeIframeAPIReady = () => {

                this.deferred.resolve()

            };

        }

 

        // 注入 YouTube 的 iFrame API

        load () {

            let validProtocols = ['http:', 'https:'];

            let url = '//www.youtube.com/iframe_api';

 

            // 我们愿意使用相关的 url 协议,但为避免协议不可用,还是回退到 ‘http:’

            if (validProtocols.indexOf(window.location.protocol) < 0) {

                url = 'http:' + url;

            }

            let tag = document.createElement('script');

            tag.src = url;

            let firstScriptTag = document.getElementsByTagName('script')[0];

            firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

            return this.deferred.promise;

        }

    }

 

    angular

       .module('youtube.player')

       .service('YoutubePlayerApi', YoutubePlayerApi);

})();


3. 编写 Controllers 时使用 “Class” 替换 “function”


这一步和上一步多少有些相似。Angular 1 中的 Controllers 总是不停被重建(或 “新建”)- 因为它们不是单例。


Angular 2 几乎不用 controllers。


反之,Angular 2 是基于组件的。每个组件都有一个简单的类(包含少量 es7 注解)来控制。如果你的 Angular 1 代码是用 web 组件方式来编写的,那么很可能每个指令(directive)都对应一个 controller 函数来控制。


有个很重的点必须意识到 - 指令的概念在 Angular 2 中更加简单:


  1. 使用了元素选择器的指令都是组件。

  2. 剩下的都是指令。


Angular 2 仍然会在内部初始化 services 和 controllers,不要自己去初始化,因为那样会导致代码很难测试。不过 Angular 2 还是易于测试并对 TDD (测试驱动开发) 和 BDD(行为驱动开发)友好。我还写过一篇文章,内容是讲为什么应当封装 “new” 关键字,从而写出更容易测试的代码。


例如把 controller 写成类,会使代码迁移到 angular 2 组件变得非常容易:


class DurationCtrl {

    /* @ngInject */

    constructor (YoutubeSearch) {

        this.YoutubeSearch = YoutubeSearch;

        this.durations = [

            'Any',

            'Short (less then 4 minutes)',

            'Medium (4-20 minutes)',

            'Long (longer than 20 minutes)'

        ];

        this.durationsMap = [

            '',

            'short',

            'medium',

            'long'

        ];

    }

 

    onDurationChange (duration, index) {

        this.YoutubeSearch.setType(this.YoutubeSearch.types.VIDEO);

        this.YoutubeSearch.setDuration(this.durationsMap[index]);

        this.YoutubeSearch.search();

    }

}

 

angular

    .module('echoes')

    .controller('DurationCtrl',  DurationCtrl);


4. 使用指令封装代码


在这一步,你需要重新思考代码,并且使用更好的架构。从 组件(components)/指令(directives) 的角度开始思考。千万不要在 index.html 或任何未关联指令的模板中编写任何 Angular 代码。例如:如果你在一段描述个人资料卡片的代码中使用了 ng-repeat, 你可以创建一个指令,“<person-profile-card>” 或者 “<profile-cards>” (作为一个列表)。


<div ng-repeat="person in vm.persons">

    <img ng-src="person.thumb">

    <h3>{{:: person.name }}</h3>

    <aside>{{:: person.moto }}</aside>

    <p>

        {{:: person.description }}

    </p>

</div>

<!-- 可以转换成一个组件   -->

<div ng-repeat="person in vm.persons">

    <person-profile-card model="person"></person-profile-card>

</div>

<!-- 可以成为另一个列表组件 -->

<profile-cards items="vm.persons"></profile-cards>


在即将推出的 Angular 1.5 版本里,你可以使用 ”angular.component“ 定义来创建组件,使得语法( 出自 todd motto 之手)比指令(directive) 更优美。


记住,组件搭配组件(或指令) 就是 Angular 2 的全部,从这个角度思考,将有助于你更好的重新组织代码,也更容易使用 Angular 2。


5. 使用 Angular2to1,ng-upgrade 或其它方法


Angular 2 采用了一个简单漂亮的语法来定义组件(指令)。为了在 es5 代码中体验 Angular 2 的组件语法,我创建了一个 npm 模块 “angular2to1”。示例,你可以在 Angular 1 应用中使用 Angular 2 的 es5 标准语法来定义一个指令:


var myApp = ng

    .Component({

        selector: 'youtube-videos'

        providers: [

            'core.services'

        ],

        bindings: {

            videos: '@'

        }

    })

    .View({

        templateUrl: 'app/youtube-videos/youtube-videos.tpl.html'

    })

    .Class({

        constructor: 'YoutubeVideosCtrl'

    })


这和定义一个 Angular 1 指令等效:


angular

        .module('youtube-videos', [

            'core.services'

        ])

        .directive('youtubeVideos', youtubeVideos);

 

    /* @ngInject */

    function youtubeVideos () {

        var directive = {

            controller: 'YoutubeVideosCtrl',

            controllerAs: 'vm',

            restrict: 'E',

            replace: true,

            template: 'app/youtube-videos/youtube-videos.tpl.html',

            bindToController: true,

            scope: {

                videos: '@'

            }

        };

        return directive;

    }


有大量的选项可供使用。


ng-upgraders 是一个代码仓库,包含了 Angular 2 升级策略的资源链接。里面有一些有趣的项目,有的项目提供了在 Angular 1 中使用 Angular 2 的 typescript 注解及 Ecmascript 2015 的可能性,这样几乎可以完全用 Angular 2 的语法来写 Angular 1 了。


另外,有很多在 Angular 1 中使用 Ecmascript 2015 的解决方案,无论是从软件架构还是 Angular 推荐架构的角度来看,都坚持了最佳实践和严格准则。


我比较喜欢的一个项目是: NG6-Starter , 项目包含使用 Ecmascrpipt 2015 编写 Angular 1 应用的骨架代码,组件生成器,测试配置和更多内容。


6. (彩蛋)使用模块加载器 system.js、webpack、browserify 或其它工具


所有 Angular 2 例子都依赖于 System.js 库的组件懒加载机制。System.js 既允许我们使用懒加载,也可以编译成压缩好的单一文件用于生产环境。这样你就可以在不同的文件中编写组件(components)和服务(services),不管构建还是开发,都使用构建脚本来解决依赖关系。


In addition, if you rather use gulp.js, webpack or browserify – that’s a no brainer and can be easily configured and integrated.


另外,如果你就是喜欢用 gulp.js、webpack 或 browserify - 完全没理由。没关系,配置和移植都很简单。


总结


我倾向于遵守标准。我认为 Ecmascript 2015 最终会成为了 javascript 语言下一代正式标准,所以,有理由去使用它(我之前也写过)并且拥抱变化。


如果其它的框架、平台和库拥抱了 Ecmascript 2015 标准 ,所有人都会受益。大家可以用更灵活的方式编码,同时在不同的库和项目中共享代码。


译者简介 ( 点击 → 加入专栏作者 


段昕理:因为iPod而喜欢上苹果的一系列产品,非常认同他们追求极致的精神。工作之余,喜欢前端的开源项目,Github(https://github.com/sandywalker)


【今日微信公号推荐↓】

更多推荐请看值得关注的技术和设计公众号


其中推荐了包括技术设计极客 和 IT相亲相关的热门公众号。技术涵盖:Python、Web前端、Java、安卓、iOS、PHP、C/C++、.NET、Linux、数据库、运维、大数据、算法、IT职场等。点击《值得关注的技术和设计公众号》,发现精彩!


 
前端大全 更多文章 静态网站生成器将是下一个大事件 来看看机智的前端童鞋怎么防盗 为什么说人人都应该学习Markdown? Medium 的 CSS 方案简直完美 前端开发者不得不知的ES6十大特性
猜您喜欢 在线视频直播!FreeBuf联手GeekPwn(极棒)带来极客盛宴 SaltStack中Syndic和Proxy介绍与使用 R中的数据结构——数组 React Native 高质量学习资料汇总 冰山一角:物联网之生活应用