{"action":"opened","issue":{"url":"https://api.github.com/repos/naraku666/blog/issues/2","repository_url":"https://api.github.com/repos/naraku666/blog","labels_url":"https://api.github.com/repos/naraku666/blog/issues/2/labels{/name}","comments_url":"https://api.github.com/repos/naraku666/blog/issues/2/comments","events_url":"https://api.github.com/repos/naraku666/blog/issues/2/events","html_url":"https://github.com/naraku666/blog/issues/2","id":165738337,"number":2,"title":"AngularJS 最佳实践","user":{"login":"naraku666","id":11005827,"avatar_url":"https://avatars.githubusercontent.com/u/11005827?v=3","gravatar_id":"","url":"https://api.github.com/users/naraku666","html_url":"https://github.com/naraku666","followers_url":"https://api.github.com/users/naraku666/followers","following_url":"https://api.github.com/users/naraku666/following{/other_user}","gists_url":"https://api.github.com/users/naraku666/gists{/gist_id}","starred_url":"https://api.github.com/users/naraku666/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/naraku666/subscriptions","organizations_url":"https://api.github.com/users/naraku666/orgs","repos_url":"https://api.github.com/users/naraku666/repos","events_url":"https://api.github.com/users/naraku666/events{/privacy}","received_events_url":"https://api.github.com/users/naraku666/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/naraku666/blog/labels/AngularJS","name":"AngularJS","color":"d93f0b"}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2016-07-15T08:42:35Z","updated_at":"2016-07-15T08:42:35Z","closed_at":null,"body":"> 最近在整理公司的[前端规范](https://github.com/naraku666/code-spec)，顺便也整理一份angular的[最佳实践](https://github.com/naraku666/code-spec/blob/master/angular-code-spec.md)：  \r\n\r\n## <a name=\"name\">项目结构</a>\r\n- 标准结构(适用小型项目)\r\n```javascript\r\napp/\r\n----- controllers/\r\n---------- mainController.js\r\n---------- otherController.js\r\n----- directives/\r\n---------- mainDirective.js\r\n---------- otherDirective.js\r\n----- services/\r\n---------- userService.js\r\n---------- itemService.js\r\n----- js/\r\n---------- bootstrap.js\r\n---------- jquery.js\r\n----- app.js\r\nviews/\r\n----- mainView.html\r\n----- otherView.html\r\n----- index.html    \r\n```\r\n这是比较经典的项目结构，以类型分类，好处就是一看目录就知道里面是什么文件，当文件不多的时候也是容易查找阅读。但是，当每一个分类的文件都比较多的时候，不仅混乱难查找，也更难维护，理解修改成本较大。\r\n\r\n- 更好的结构(适用中大型项目)\r\n```javascript\r\napp/\r\n----- shared/   // acts as reusable components or partials of our site\r\n---------- sidebar/\r\n--------------- sidebarDirective.js\r\n--------------- sidebarView.html\r\n---------- article/\r\n--------------- articleDirective.js\r\n--------------- articleView.html\r\n----- components/   // each component is treated as a mini Angular app\r\n---------- home/\r\n--------------- homeController.js\r\n--------------- homeService.js\r\n--------------- homeView.html\r\n---------- blog/\r\n--------------- blogController.js\r\n--------------- blogService.js\r\n--------------- blogView.html\r\n----- app.module.js\r\n----- app.routes.js\r\nassets/\r\n----- img/      // Images and icons for your app\r\n----- css/      // All styles and style related files (SCSS or LESS files)\r\n----- js/       // JavaScript files written for your app that are not for angular\r\n----- libs/     // Third-party libraries such as jQuery, Moment, Underscore, etc.\r\nindex.html\r\n```\r\n这是以模块划分，组件化的概念的项目结构。同一个功能模块的相关文件都在一个文件夹内，一幕了然，职责清晰。这种结构可能对新手来说难理解一点，但随着对项目的熟悉，这种结构也会带来更大的工作效率。\r\n\r\n## <a name=\"beset\">最佳实践、规范</a>\r\n请阅读：[Angular规范](https://github.com/johnpapa/angular-styleguide/blob/master/a1/i18n/zh-CN.md#%E6%89%8B%E5%8A%A8%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5)\r\n## <a name=\"optimize\">优化</a>\r\n- ### 减少监控值的个数  \r\n    由于在html模板中绑定的数据，angular都会为其创建相应的监听器，绑定的数据越多，监听器越多，那么angular在进行脏值检测的时候遍历的监听器也就越多，性能就会降低。所以当我们的页面渲染完成后不需要更新。则采用单次绑定，仅在初始化的时候绑定：\r\n    单个值：\r\n    ```javascript\r\n    <div>{{::value}}</div>\r\n    ```\r\n    列表：\r\n    ```javascript\r\n    <div ng-repeat=\"item in ::list\"></div>\r\n    ```\r\n\r\n- ### 进行数据绑定时，使用对象字面量或者使用`controllerAS`的方式代替直接往`$scope`中添加属性。\r\n\r\n    ```javascript\r\n    使用\r\n    <input type=\"text\" ng-model=\"obj.prop\">\r\n    来替代\r\n    <input type=\"text\" ng-model=\"obj.prop\">\r\n    ```\r\n\r\n- ### 使用track by 提升索引的性能\r\n\r\n    在angular中，在数据变更时DOM也会相应的更新，所以，DOM和数据需要建立关联，即需要一个映射关系，映射关系需要唯一的索引，angular默认对简单类型使用自身当索引，当出现重复的时候，就会出错了。如果指定$index，也就是元素在数组中的下标为索引，就可以避免这个问题。\r\n    简单数组：\r\n\r\n    ```javascript\r\n    this.arr = [1, 3, 5, 3];\r\n    ```\r\n    ```javascript\r\n    <!-- 报错 -->\r\n    <ul>\r\n        <li ng-repeat=\"num in arr\">{{num}}</li>\r\n    </ul\r\n    ```\r\n    ```javascript\r\n    <!-- 指定$index为索引 -->\r\n    <ul>\r\n        <li ng-repeat=\"num in arr track by $index\">{{num}}</li>\r\n    </ul\r\n    ```\r\n\r\n    对象数组：\r\n    ```javascript\r\n    this.arr = [{\r\n        id: 1,\r\n        name: 'naraku666'\r\n    }, {\r\n        id: 2,\r\n        name: 'ljs'\r\n    }, ...];\r\n    ```\r\n    ```javascript\r\n    <!-- 性能较差 -->\r\n    <ul>\r\n        <li ng-repeat=\"item in arr\">{{item}}</li>\r\n    </ul\r\n    ```\r\n    ```javascript\r\n    <!-- 指定唯一值id为索引 -->\r\n    <ul>\r\n        <li ng-repeat=\"item in arr track by item.id\">{{item.name}}</li>\r\n    </ul\r\n    ```\r\n\r\n- ### 使用`angular`已有的功能。\r\n    \r\n    比如：  \r\n    1. 可以不需要引入`jquery`, 因为`angular`已经内置了`jQLite`。\r\n    2. 熟悉内置的过滤器和指令，避免重复造轮子。\r\n\r\n- ### 使用`ng-cloak`/`ng-bind`指令来代替`{{}}`避免在页面初始化的时候闪烁。  \r\n\r\n    ```html\r\n     <div id=\"template1\" ng-cloak>hello</div>\r\n    ```\r\n\r\n- ### 离开页面时注销不需要的监听器、定时器和事件绑定。\r\n    因为这些操作可能会在页面跳转后继续执行，影响性能\r\n    \r\n    ```javascript\r\n    var destroyNameWatch = $scope.$watch('user.name', function(){\r\n        ....\r\n    });\r\n    var timer = $interval(function(){\r\n        ....\r\n    });\r\n\r\n    // 监听页面离开事件\r\n    $scope.$on('$destroy', function(){\r\n        // 注销监听器\r\n        destroyNameWatch();\r\n        // 清除定时器\r\n        $interval(timer);\r\n    });\r\n    ```\r\n\r\n- ### 总是在指令的`compile`函数或`postLink`函数中进行DOM的操作。\r\n    Angular指令编译过程：\r\n    -------------------\r\n    1.首先加载angularjs库，查找到ng-app指令，从而找到应用的边界;\r\n    2.根据ng-app划定的作用域来调用$compile服务进行编译;\r\n    3.angularjs会遍历整个HTML文档，并根据js中指令的定义来处理在页面上声明的各个指令;\r\n    4.按照指令的优先级(priority)排列，根据指令中的配置参数(template，place，transclude等)转换DOM;\r\n    5.然后就开始按顺序执行各指令的compile函数（如果指令上有定义compile函数）对模板自身进行转换;\r\n    `注意：此处的compile函数是我们指令中配置的，跟上面说的$compile服务不一样。`\r\n    6.每个compile函数执行完后都会返回一个link函数，所有的link函数会合成一个大的link函数;\r\n    7.然后这个大的link函数就会被执行，主要做数据绑定，通过在DOM上注册监听器来动态修改scope中的数据，或者是使用$watchs监听 scope中的变量来修改DOM，从而建立双向绑定等等;\r\n    8.若我们的指令中没有配置compile函数，那我们配置的link函数就会运行，\r\n    她做的事情大致跟上面complie返回之后所有的link函数合成的的大的link函数差不多\r\n    所以：在指令中compile与link选项是互斥的，如果同时设置了这两个选项，\r\n    那么就会把compile所返回的函数当做是链接函数，而link选项本身就会被忽略掉\r\n\r\n- ### 保持业务逻辑与界面分离。\r\n\r\n    `ViewMode`(controller)应该总是专注于监听来自`view`(模板/directive)行为，然后调用相应的`Model`(service)中的方法。避免在`controller`中进行`DOM`的操作，而是应该根据`数据驱动`的模式，通过改变数据来更新`DOM`, 或者将复杂的DOM操作封装成指令。\r\n\r\n- ### 避免在`$rootScope`中添加函数、业务逻辑。\r\n    \r\n    公共，可复用的函数应该写成`service`。`$rootScope`应该用来保存共享的数据。"}}
{"actor":{"display_login":"naraku666"}}