1

ng-hot-loader

前言

webpack-dev-server自带支持模块热替换特性(HMR),不刷新页面实现代码局部更新,使用HMR可以大幅提升开发效率。

实现目标

  • [x] 样式热替换 -- style-loader本身即支持HMR,建议依赖库css直接解析出文件link,降低热替换成本

  • [x] 模板热替换

  • [x] 服务热替换

  • [x] 过滤器热替换 -- expression 变动才会重新实例化过滤器

  • [x] 指令热替换 -- 目前low level的支持

  • [x] 控制器热替换

实现策略

合理划分angular模块,将angular模块划分为主模块,业务模块。路由, 业务服务全局服务合理分散到主模块与业务模块中。参照angular组件单一职责,单个模块(文件)仅承担有限职责,。

HMR关键核心为组件声明 => 组件定位 => 组件更新

  • 文件命名建议按照官方styleguide

  • 关联filter, service, directive在单一模块中声明。

  • 关键核心依旧在模块,路由声明。路由声明中,模板与控制器统一采用ES6引入方案,template: templateVariable,控制器声明统一采用pure function的方式,而不是字符串的方式。

  • 路由声明与组件声明在*.module.js统一文件内部声明。

  • 控制器暂时不考虑通过$controllerProvider注册的类型。

  • 路由不考虑热更新。

  • 所有模板文件,都通过ES6 default import导入,JS文件建议全部通过destruct的方式引用。

/**
 * @description - application level router config.
 * @author bornkiller <hjj491229492@hotmail.com>
 */

'use strict';

// layout module dependency
import layoutAuthorizeTemplate from './authority/authorize.html';
import { AuthorizeController } from './authority/authorize.controller';

import layoutNavbarTemplate from './flow/navbar.html';
import layoutSidebarTemplate from './flow/sidebar.html';
import layoutCoreTemplate from './flow/core.html';
import { SidebarController } from './flow/sidebar.controller';


// layout module name
const LAYOUT_MODULE = 'app.layout';

// layout module router
const LayoutRoute = [
  {
    name: 'authorize',
    url: '/authorize',
    views: {
      'core': {
        template: layoutAuthorizeTemplate,
        controller: AuthorizeController,
        controllerAs: 'vm'
      }
    }
  },
  {
    name: 'application',
    url: '/application',
    views: {
      'navbar': {
        template: layoutNavbarTemplate
      },
      'sidebar': {
        template: layoutSidebarTemplate,
        controller: SidebarController,
        controllerAs: 'vm'
      },
      'core': {
        template: layoutCoreTemplate
      }
    }
  }
];

angular.module(LAYOUT_MODULE, [])
  // eslint-disable-next-line angular/di
  .config(['$stateProvider', function ($stateProvider) {
    LayoutRoute.forEach((route) => {
      $stateProvider.state(route);
    });
  }]);

export { LAYOUT_MODULE };

模块划分

通过划分模块实现最终目标:

  • ng-hot-analyzer - 分析组件的声明与定位,主要包括importregister token的分析。最终汇总,实现模块定位 <--> 模块导出实例 <--> NG组件注册的图谱。

  • ng-hot-loader - 汇聚子模块操作,基于分析结果与对应模板,生成对应HMR代码。

  • ng-hmr - 热更新具体实现。

组件替换约定

服务在angularjs内部属于单例,实例声明通过factory的方式,声明literal object服务。

过滤器仅支持ng-bind使用。

模板热替换目前采用较为粗放的容器定位策略,基于最近的ui-router view进行局部替换。

指令热替换同样采用较为粗放的容器定位策略,通过路由模板替换实现。

控制器统一使用ES6 Class声明,字段更新策略如下:

  • [x] 删除字段

  • [x] 依赖注入服务,直接override

  • [x] 函数字段,直接override

  • [x] 通过控制器内声明shouldFieldUpdate方法来精确控制对应字段是否更新。

  • [x] 如果控制器未声明shouldFieldUpdate方法,默认判定策略是类型变化,类型出现变化即直接override

HMR示例

if (module.hot) {
  module.hot.accept(['${descriptor.location}'], function () {
    ${translateImportType(descriptor)}
    $hmr.hmrOnChange('${capitalize(descriptor.category)}', '${descriptor.token}', ${descriptor.name});
    $hmr.hmrDoActive('${capitalize(descriptor.category)}', '${descriptor.token}', ${descriptor.name});
  });
};

项目实现

TODO

  • 支持字符串声明控制器

  • 调整模板scope定位方式


怀疑真爱的流浪者jason
923 声望62 粉丝

For every single second in life, I want to fight with the monster deep within my heart , and I want to win.........