AngularJs规范

简介

本风格指南的目的是展示AngularJS应用的最佳实践和风格指南。 这些最佳实践来自于:

  1. AngularJS项目源码
  2. 本人阅读过的源码和文章
  3. 本人的实践经历

说明1: 这只是风格指南的草案,主要目的是通过交流以消除分歧,进而被社区广泛采纳。
说明2: 本版本是翻译自英文原版,在遵循下面的指南之前请确认你看到的是比较新的版本。

在本指南中不会包含基本的JavaScript开发指南。这些基本的指南可以在下面的列表中找到:

  1. Google‘s JavaScript style guide
  2. Mozilla‘s JavaScript style guide
  3. GitHub‘s JavaScript style guide
  4. Douglas Crockford‘s JavaScript style guide
  5. Airbnb JavaScript style guide

对于AngularJS开发,推荐 Google‘s JavaScript style guide.

在AngularJS的Github wiki中有一个相似的章节 ProLoser, 你可以点击这里查看。

内容目录

  • 概览

    • 目录结构
    • 标记
    • 命名约定
    • 其他
  • 模块
  • 控制器
  • 指令
  • 过滤器
  • 服务
  • 模板
  • 路由
  • 国际化
  • 性能
  • 加入我们
  • 贡献者

概览

目录结构

由于一个大型的AngularJS应用有较多组成部分,所以最好通过分层的目录结构来组织。 有两个主流的组织方式:

  • 按照类型优先,业务功能其次的组织方式

这种方式的目录结构看起来如下:

.
├── app
│   ├── app.js
│   ├── controllers
│   │   ├── home
│   │   │   ├── FirstCtrl.js
│   │   │   └── SecondCtrl.js
│   │   └── about
│   │       └── ThirdCtrl.js
│   ├── directives
│   │   ├── home
│   │   │   └── directive1.js
│   │   └── about
│   │       ├── directive2.js
│   │       └── directive3.js
│   ├── filters
│   │   ├── home
│   │   └── about
│   └── services
│       ├── CommonService.js
│       ├── cache
│       │   ├── Cache1.js
│       │   └── Cache2.js
│       └── models
│           ├── Model1.js
│           └── Model2.js
├── partials
├── lib
└── test
  • 按照业务功能优先,类型其次的组织方式

如下:

.
├── app
│   ├── app.js
│   ├── common
│   │   ├── controllers
│   │   ├── directives
│   │   ├── filters
│   │   └── services
│   ├── home
│   │   ├── controllers
│   │   │   ├── FirstCtrl.js
│   │   │   └── SecondCtrl.js
│   │   ├── directives
│   │   │   └── directive1.js
│   │   ├── filters
│   │   │   ├── filter1.js
│   │   │   └── filter2.js
│   │   └── services
│   │       ├── service1.js
│   │       └── service2.js
│   └── about
│       ├── controllers
│       │   └── ThirdCtrl.js
│       ├── directives
│       │   ├── directive2.js
│       │   └── directive3.js
│       ├── filters
│       │   └── filter3.js
│       └── services
│           └── service3.js
├── partials
├── lib
└── test
  • 当目录里有多个单词时, 使用 lisp-case 语法:
app
 ├── app.js
 └── my-complex-module
     ├── controllers
     ├── directives
     ├── filters
     └── services
  • 在创建指令时,合适的做法是将相关的文件放到同一目录下 (如:模板文件, CSS/SASS 文件, JavaScript文件)。如果你在整个项目周期都选择这种组织方式,
app
└── directives
    ├── directive1
    │   ├── directive1.html
    │   ├── directive1.js
    │   └── directive1.sass
    └── directive2
        ├── directive2.html
        ├── directive2.js
        └── directive2.sass

那么,上述的两种目录结构均能适用。

  • 组件的单元测试应与组件放置在同一目录下下。在这种方式下,当改变组件时,更加容易找到对应的测试。同时,单元测试也充当了文档和示例。
services
├── cache
│   ├── cache1.js
│   └── cache1.spec.js
└── models
    ├── model1.js
    └── model1.spec.js
  • app.js文件包含路由定义、配置和启动说明(如果需要的话)。
  • 每一个 JavaScript 文件应该仅包含 一个组件 。文件名应该以组件名命名。
  • 使用 Angular 项目模板,如 Yeomanng-boilerplate.

组件命名的约定可以在每个组件中看到。

标记

太长慎读 把script标签放在文档底部。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>MyApp</title>
</head>
<body>
  <div ng-app="myApp">
    <div ng-view></div>
  </div>
  <script src="angular.js"></script>
  <script src="app.js"></script>
</body>
</html>

保持标签的简洁并把AngularJS的标签放在标准HTML属性后面。这样提高了代码可读性。标准HTML属性和AngularJS的属性没有混到一起,提高了代码的可维护性。

<form class="frm" ng-submit="login.authenticate()">
  <div>
    <input class="ipt" type="text" placeholder="name" require ng-model="user.name">
  </div>
</form>

其它的HTML标签应该遵循下面的指南的 建议

标记

下表展示了各个Angular元素的命名约定

元素 命名风格 实例 用途
Modules lowerCamelCase angularApp  
Controllers Functionality + ‘Ctrl‘ AdminCtrl  
Directives lowerCamelCase userInfo  
Filters lowerCamelCase userFilter  
Services UpperCamelCase User constructor
Services lowerCamelCase dataFactory others

其他

  • 使用:

    • $timeout替代 setTimeout
    • $interval instead of setInterval
    • $window替代 window
    • $document 替代 document
    • $http替代 $.ajax

这将使你更易于在测试时处理代码异常 (例如:你在 setTimeout 中忘记 $scope.$apply)

使用如下工具自动化你的工作流 * Yeoman * Gulp * Grunt * Bower

  • 使用 promise ($q) 而非回调。这将使你的代码更加优雅、直观,并且免于回调地狱。
  • 尽可能使用 $resource 而非 $http。更高的抽象可以避免冗余。
  • 使用AngularJS的预压缩版 (像 ngmin 或 ng-annotate) 避免在压缩之后出现问题。
  • 不要使用全局变量或函数。通过依赖注入解决所有依赖,这可以减少 bug ,规避很多测试时的麻烦。
  • 为避免使用全局变量或函数,可以借助 Grunt 或 Gulp 把你的代码放到一个立即执行的函数表达式(IIFE)中。可用的插件有 grunt-wrap 或 gulp-wrap。下面是 Gulp 的示例:
gulp.src("./src/*.js")
    .pipe(wrap(‘(function(){\n"use strict";\n<%= contents %>\n})();‘))
    .pipe(gulp.dest("./dist"));
  • 不要污染 $scope。仅添加与视图相关的函数和变量。
  • 使用 controllers 而非 ngInitngInit 只有在一种情况下的使用是合适的:用来给 ngRepeat的特殊属性赋予一个别名。除此之外, 你应该使用 controllers 而不是 ngInit 来初始化scope变量。ngInit 中的表达式会传递给 Angular 的$parse 服务,通过词法分析,语法分析,求值等过程。这会导致:
    • 对性能的巨大影响,因为解释器由 Javascript 写成
    • 多数情况下,$parse 服务中对表达式的缓存基本不起作用,因为 ngInit 表达式经常只有一次求值
    • 很容易出错,因为是模板中写字符串,没有针对表达式的语法高亮和进一步的编辑器支持
    • 不会抛出运行时错误
  • 不要使用 $ 前缀来命名变量, 属性和方法. 这种前缀是预留给 AngularJS 来使用的.
  • 当使用 DI 机制来解决依赖关系, 要根据他们的类型进行排序 - AngularJS 内建的依赖要优先, 之后才是你自定义的:
module.factory(‘Service‘, function ($rootScope, $timeout, MyCustomDependency1, MyCustomDependency2) {
  return {
    //Something
  };
});

模块

  • 模块应该用驼峰式命名。为表明模块 b 是模块 a 的子模块, 可以用点号连接: a.b 。

    有两种常见的组织模块的方式:

    1. 按照功能组织
    2. 按照组件类型组织

    当前并无太大差别,但前者更加清晰。同时,如果 lazy-loading modules 被实现的话 (当前并未列入 AngularJS 的路线图),这种方式将改善应用的性能。

控制器

  • 不要在控制器里操作 DOM,这会让你的控制器难以测试,而且违背了关注点分离原则。应该通过指令操作 DOM。
  • 通过控制器完成的功能命名控制器 (如:购物卡,主页,控制板),并以字符串Ctrl结尾。
  • 控制器是纯 Javascript 构造函数,所以应该用首字母大写的驼峰命名法(HomePageCtrlShoppingCartCtrl,AdminPanelCtrl, 等等)。
  • 控制器不应该在全局中定义 (尽管 AngularJS 允许,但污染全局命名空间是个糟糕的实践)。
  • 使用以下语法定义控制器:
    function MyCtrl(dependency1, dependency2, ..., dependencyn) {
      // ...
    }
    module.controller(‘MyCtrl‘, MyCtrl);

    为了避免在压缩代码时产生问题,你可以使用工具自动生成标准的数组定义式语法,如:ng-annotate (还有 grunt 任务grunt-ng-annotate

  • 使用 controller as 语法:
    <div ng-controller="MainCtrl as main">
       {{ main.title }}
    </div>
    
    app.controller(‘MainCtrl‘, MainCtrl);
    
    function MainCtrl () {
      this.title = ‘Some title‘;
    }

    使用 controller as 主要的优点是:

    • 创建了一个“独立”的组件——绑定的属性不属于 $scope 原型链。这是一个很好的实践,因为 $scope 原型继承有一些重要的缺点(这可能是为什么它在 Angular 2 中被移除了):

      • Scope值的改变会在你不注意的地方有影响。
      • 难以重构。
      • dot rule‘.
    • 当你不需要做必须由 $scope 完成的操作(比如$scope.$broadcast)时,移除掉了 $scope,就是为 Angular2 做好准备。
    • 语法上更接近于普通的 JavaScript 构造函数。

    想深入了解 controller as ,请看: digging-into-angulars-controller-as-syntax

  • 如果使用数组定义语法声明控制器,使用控制器依赖的原名。这将提高代码的可读性:
    function MyCtrl(s) {
      // ...
    }
    
    module.controller(‘MyCtrl‘, [‘$scope‘, MyCtrl]);

    下面的代码更易理解

    function MyCtrl($scope) {
      // ...
    }
    module.controller(‘MyCtrl‘, [‘$scope‘, MyCtrl]);

    对于包含大量代码的需要上下滚动的文件尤其适用。这可能使你忘记某一变量是对应哪一个依赖。

  • 尽可能的精简控制器。将通用函数抽象为独立的服务。
  • 不要再控制器中写业务逻辑。把业务逻辑交给模型层的服务。 举个例子:
    // 这是把业务逻辑放在控制器的常见做法
    angular.module(‘Store‘, [])
    .controller(‘OrderCtrl‘, function ($scope) {
    
      $scope.items = [];
    
      $scope.addToOrder = function (item) {
        $scope.items.push(item);//-->控制器中的业务逻辑
      };
    
      $scope.removeFromOrder = function (item) {
        $scope.items.splice($scope.items.indexOf(item), 1);//-->控制器中的业务逻辑
      };
    
      $scope.totalPrice = function () {
        return $scope.items.reduce(function (memo, item) {
          return memo + (item.qty * item.price);//-->控制器中的业务逻辑
        }, 0);
      };
    });

    当你把业务逻辑交给模型层的服务,控制器看起来就会想这样:(关于 service-model 的实现,参看 ‘use services as your Model‘):

    // Order 在此作为一个 ‘model‘
    angular.module(‘Store‘, [])
    .controller(‘OrderCtrl‘, function (Order) {
    
      $scope.items = Order.items;
    
      $scope.addToOrder = function (item) {
        Order.addToOrder(item);
      };
    
      $scope.removeFromOrder = function (item) {
        Order.removeFromOrder(item);
      };
    
      $scope.totalPrice = function () {
        return Order.total();
      };
    });

    为什么控制器不应该包含业务逻辑和应用状态?

    • 控制器会在每个视图中被实例化,在视图被销毁时也要同时销毁
    • 控制器是不可重用的——它与视图有耦合
    • Controllers are not meant to be injected
  • 需要进行跨控制器通讯时,通过方法引用(通常是子控制器到父控制器的通讯)或者 $emit$broadcast 及 $on 方法。发送或广播的消息应该限定在最小的作用域。
  • 制定一个通过 $emit$broadcast 发送的消息列表并且仔细的管理以防命名冲突和bug。

    Example:

    // app.js
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    Custom events:
     - ‘authorization-message‘ - description of the message
       - { user, role, action } - data format
         - user - a string, which contains the username
         - role - an ID of the role the user has
         - action - specific ation the user tries to perform
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  • 在需要格式化数据时将格式化逻辑封装成 过滤器 并将其声明为依赖:
    function myFormat() {
     return function () {
       // ...
     };
    }
    module.filter(‘myFormat‘, myFormat);
    
    function MyCtrl($scope, myFormatFilter) {
     // ...
    }
    
    module.controller(‘MyCtrl‘, MyCtrl);
  • 有内嵌的控制器时使用 "内嵌作用域" ( controllerAs 语法):

    app.js

    module.config(function ($routeProvider) {
     $routeProvider
       .when(‘/route‘, {
         templateUrl: ‘partials/template.html‘,
         controller: ‘HomeCtrl‘,
         controllerAs: ‘home‘
       });
    });

    HomeCtrl

    function HomeCtrl() {
     this.bindingValue = 42;
    }

    template.html

    <div ng-bind="home.bindingValue"></div>
    

指令

  • 使用小写字母开头的驼峰法命名指令。
  • 在 link function 中使用 scope 而非 $scope。在 compile 中, 你已经定义参数的 post/pre link functions 将在函数被执行时传递, 你无法通过依赖注入改变他们。这种方式同样应用在 AngularJS 项目中。
  • 为你的指令添加自定义前缀以免与第三方指令冲突。
  • 不要使用 ng 或 ui 前缀,因为这些备用于 AngularJS 和 AngularJS UI。
  • DOM 操作只通过指令完成。
  • 为你开发的可复用组件创建独立作用域。
  • 以属性和元素形式使用指令,而不是注释和 class。这会使你的代码可读性更高。
  • 使用 scope.$on(‘$destroy‘, fn) 来清除。这点在使用第三方指令的时候特别有用。
  • 处理不可信的数据时,不要忘记使用 $sce 。

过滤器

  • 使用小写字母开头的驼峰法命名过滤器。
  • 尽可能使过滤器精简。过滤器在 $digest loop 中被频繁调用,过于复杂的过滤器将使得整个应用缓慢。
  • 在过滤器中只做一件事。更加复杂的操作可以用 pipe 串联多个过滤器来实现。

服务

这个部分包含了 AngularJS 服务组件的相关信息。下面提到的东西与定义服务的具体方式(.provider.factory,.service 等)无关,除非有特别提到。

  • 用驼峰法命名服务。

    • 用首字母大写的驼峰法命名你自己的服务, 把服务写成构造函数的形式,例如:

      function MainCtrl($scope, User) {
        $scope.user = new User(‘foo‘, 42);
      }
      
      module.controller(‘MainCtrl‘, MainCtrl);
      
      function User(name, age) {
        this.name = name;
        this.age = age;
      }
      
      module.factory(‘User‘, function () {
        return User;
      });
    • 用首字母小写的驼峰法命名其它所有的服务。
  • 把业务逻辑封装到服务中,把业务逻辑抽象为服务作为你的 model。例如:
    //Order is the ‘model‘
    angular.module(‘Store‘)
    .factory(‘Order‘, function () {
        var add = function (item) {
          this.items.push (item);
        };
    
        var remove = function (item) {
          if (this.items.indexOf(item) > -1) {
            this.items.splice(this.items.indexOf(item), 1);
          }
        };
    
        var total = function () {
          return this.items.reduce(function (memo, item) {
            return memo + (item.qty * item.price);
          }, 0);
        };
    
        return {
          items: [],
          addToOrder: add,
          removeFromOrder: remove,
          totalPrice: total
        };
    });

    如果需要例子展现如何在控制器中使用服务,请参考 ‘Avoid writing business logic inside controllers‘。

  • 将业务逻辑封装成 service 而非 factory,这样我们可以更容易在服务间实现“经典式”继承:
    function Human() {
      //body
    }
    Human.prototype.talk = function () {
      return "I‘m talking";
    };
    
    function Developer() {
      //body
    }
    Developer.prototype = Object.create(Human.prototype);
    Developer.prototype.code = function () {
      return "I‘m coding";
    };
    
    myModule.service(‘human‘, Human);
    myModule.service(‘developer‘, Developer);
    
  • 使用 $cacheFactory 进行会话级别的缓存,缓存网络请求或复杂运算的结果。
  • 如果给定的服务需要配置,把配置相关代码放在 config 回调里,就像这样:
    angular.module(‘demo‘, [])
    .config(function ($provide) {
      $provide.provider(‘sample‘, function () {
        var foo = 42;
        return {
          setFoo: function (f) {
            foo = f;
          },
          $get: function () {
            return {
              foo: foo
            };
          }
        };
      });
    });
    
    var demo = angular.module(‘demo‘);
    
    demo.config(function (sampleProvider) {
      sampleProvider.setFoo(41);
    });

模板

  • 使用 ng-bind 或者 ng-cloak 而非简单的 {{ }} 以防止页面渲染时的闪烁。
  • 避免在模板中使用复杂的表达式。
  • 当需要动态设置  的 src 时使用 ng-src 而非 src 中嵌套 {{}} 的模板。
  • 当需要动态设置的 href 时使用 ng-href 而非 href 中嵌套 {{ }} 的模板。
  • 通过 ng-style 指令配合对象式参数和 scope 变量来动态设置元素样式,而不是将 scope 变量作为字符串通过 {{ }} 用于 style 属性。
<script>
...
$scope.divStyle = {
  width: 200,
  position: ‘relative‘
};
...
</script>

<div ng-style="divStyle">my beautifully styled div which will work in IE</div>;

路由

  • 在视图展示之前通过 resolve 解决依赖。
  • 不要在 resolve 回调函数中显式使用RESTful调用。将所有请求放在合适的服务中。这样你就可以使用缓存和遵循关注点分离原则。

国际化

  • 在较新版本的 Angular(>=1.4.0)下,使用内置的 i18n 工具,在较老版本下(<1.4.0),使用 angular-translate

性能

    • 优化 digest cycle

      • 只监听必要的变量。仅在必要时显式调用 $digest 循环(例如:在进行实时通讯时,不要在每次接收到消息时触发$digest 循环)。
      • 对于那些只初始化一次并不再改变的内容, 使用一次性 watcher bindonce (对于早期的 AngularJS)。如果是 AngularJS >=1.3.0 的版本,应使用Angular内置的一次性数据绑定(One-time bindings).
      • 尽可能使 $watch 中的运算简单。在单个 $watch 中进行繁杂的运算将使得整个应用变慢(由于JavaScript的单线程特性,$digest loop 只能在单一线程进行)
      • 当监听集合时, 如果不是必要的话不要深度监听. 最好使用 $watchCollection, 对监听的表达式和之前表达式的值进行浅层的检测.
      • 当没有变量被 $timeout 回调函数所影响时,在 $timeout 设置第三个参数为 false 来跳过 $digest 循环.
      • 当面对超大不太改变的集合, 使用 immutable data structures.
    • 用打包、缓存html模板文件到你的主js文件中,减少网络请求, 可以用 grunt-html2js / gulp-html2js. 详见 这里 和 这里 。 在项目有很多小html模板并可以放进主js文件中时(通过minify和gzip压缩),这个办法是很有用的。
时间: 2024-10-19 19:46:46

AngularJs规范的相关文章

AngularJS模块规范和性能优化

模块化规范 概念:根据我们写的不同的功能按照不同的类型划分,分工协作 作用:辅助的作用,能将我们编写好的js分为模块,是我们代码的编写更加专业 come on js 引入模块是同步进行的:前面记载以后,后面才能使用,所以不适合前端,主要是适用于后台 nodejs用的最多 amd 概念:a异步,m模块,d定义(异步模块化规范),引入模块可以异步加载 jquery和bootstrap都支持amd requirejs运用的最多 require js 主模块引入:data-main="url"

Angularjs书写规范

文件命名原则: 遵循以描述组件功能,然后是类型(可选)的方式来给所有的组件提供统一的命名 命名:feature.type.js. 测试文件名(feature.type.spec.js) 大多数文件都有2个名字: 文件名 (avengers.controller.js) 带有Angular的注册组件名 (AvengersController) 测试文件名(avengers.controller.spec.js) 规则 一个文件只定义一个组件 每一个文件都需要使用JavaScript闭包 使用mod

angularJs项目实战!01:模块划分和目录组织

近日来我有幸主导了一个典型的web app开发.该项目从产品层次来说是个典型的CRUD应用,故而我毫不犹豫地采用了grunt + boilerplate + angularjs + bootstrap + D3 + requirejs 的架构来实现它.angularjs早在去年6月份我就有所接触,将它应用在实验室项目的个别页面中,11月份在新浪的时候也将其推荐给了所在云事业部项目组.项目组老大程辉等人都是很有技术敏感性的人,大胆地采纳了我的建议,将之应用于原本使用dojo开发的项目前端模块上.然

AngularJS in Action读书笔记5(实战篇)——在directive中引入D3饼状图显示

前言: "宁肯像种子一样等待  也不愿像疲惫的陀螺  旋转得那样勉强" 这是前几天在查资料无意间看到的一位园友的签名,看完后又读了两遍,觉得很有味道.后来一寻根究底才知这是出资大诗人汪国真之口,出处<她>.且抛开上下文,单从这短短几句,正恰如其分的折射出有一群人,他们穿着不那么fashion,言辞不那么犀利,但是内心某一块地方像是躁动的火山,拥有无尽的动力和激情,矢志不渝种子般投身到技术研究和心得分享当中. 或许每一次的生长都是那么悄无声息,但是无数次的坚持只是为了破土那日

一步步构建自己的AngularJS(2)——scope之$watch及$digest

在上一节项目初始化中,我们最终得到了一个可以运行的基础代码库,它的基本结构如下: 其中node_modules文件夹存放项目中的第三方依赖模块,src存放我们的项目代码源文件,test存放测试用例文件,.jshintrc是jshint插件的配置文件,karma.conf.js是karma的配置文件,package.json是npm的配置文件,结构其实很简单.从本节开始,会在这个代码库的基础上进行我们自己Angular的实现. 首先,在写代码之前,在命令行中输入npm test命令,让我们的测试用

angularjs的部分总结

就在这个星期,我们学习了一个神奇的框架叫:"Angular js",它的神奇之处不是它的功能有多强,甚至它的功能还是很简陋的,但是它的那种思想是非常牛逼的;他完全抛弃了我们现在所常用的.先取得对象,然后再对这个对象去绑定或者是处理.它完全的放弃了dom操作,化繁为简.但是我们所说的万物有好处,就会有不理想的地方,比如说它的适应性就不是很好,需要大量dom操作的时候,就不是它的用武之地了,比如说游戏,而且由于与我们常规的做法相违背,所以对于初学者来说不是很友好,有的人说用着很爽,而有的人

对于angularJS的一点思考

已经找好工作近两周了,入职基本上还算顺利,自己两年来的挑灯夜战也算是有了收获,于是这两周基本上是按部就班的工作,没有学习什么新技术.在上个公司的时候,同事在项目中使用angularJs,之前他也没有接触过angularJs,一边学习一边做项目,结果给项目团队造成了很大的困扰.angularJS他研究的不透彻,在使用的过程中造成了很大的弊端,比如标签闪烁,暴露出未解析的源代码等.在商业的项目上尝试使用新技术是十分危险的,轻则项目留坑,给维护留下巨大的安全隐患,重则项目流产,商业计划因此而毁于一旦,

如何用angularjs制作一个完整的表格之一__创建简单表格

初步接手人生的第一个项目,需要用angularjs制作表格和实现各种功能,因此遇到了各种问题和以前不熟悉的知识点,在此记录下来,以供大家学习交流,解决方式可能并不完善或符合规范,如果大家有更好的方式欢迎指出,由于这个表格功能的制作是一点点添加上去的,因此我也分成几个部分介绍,日后如增加了新的功能也会不时更新的 首先,表格采用的是BootStrap样式编辑的,主要使用的是angularjs,为了方便也有jQuery的方法,在测试时需自行引入bootstrap,angularjs和jq的文件. 正文

AngularJS开发下一代Web应用笔记(一)

一.写在最前 AngularJS是Google推出的一款Web应用开发框架.它提供了一系列兼容性良好并且可扩展的服务,包括数据绑定.DOM操作.MVC设计模式和模块加载等. 现在网上JS框架茫茫多,真不知道是到底要学习什么框架,学习了这个框架,说不定还没用在项目,又有更好的js框架出来了.前端就是不断折腾新东西,生命在于折腾~ 是的,这么多的前端框架,是不是真的都要学习??答案当然不是,看所要完成的项目.这是带我的前端前辈告诉我,具体使用什么框架还是不用框架, 看项目需求,一切都以按时按质完成作