{"id":29926529,"url":"https://github.com/ecomfe/san-hot-loader","last_synced_at":"2026-01-20T16:47:45.339Z","repository":{"id":45600049,"uuid":"303972952","full_name":"ecomfe/san-hot-loader","owner":"ecomfe","description":"San-Hot-Loader 为基于 Webpack 构建的 San 项目提供热更新功能。能够让 San 项目在调试开发的时候变得更加方便。","archived":false,"fork":false,"pushed_at":"2022-11-09T07:05:31.000Z","size":48,"stargazers_count":0,"open_issues_count":0,"forks_count":3,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-06-30T11:11:35.905Z","etag":null,"topics":["loader","san","san-loader","webpack"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ecomfe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-10-14T09:58:31.000Z","updated_at":"2021-12-06T08:56:42.000Z","dependencies_parsed_at":"2022-09-07T17:51:52.414Z","dependency_job_id":null,"html_url":"https://github.com/ecomfe/san-hot-loader","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ecomfe/san-hot-loader","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ecomfe%2Fsan-hot-loader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ecomfe%2Fsan-hot-loader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ecomfe%2Fsan-hot-loader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ecomfe%2Fsan-hot-loader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ecomfe","download_url":"https://codeload.github.com/ecomfe/san-hot-loader/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ecomfe%2Fsan-hot-loader/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268348673,"owners_count":24236299,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["loader","san","san-loader","webpack"],"created_at":"2025-08-02T12:41:49.346Z","updated_at":"2026-01-20T16:47:45.312Z","avatar_url":"https://github.com/ecomfe.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# San-Hot-Loader\n\nSan-Hot-Loader 为基于 Webpack 构建的 [San](https://github.com/baidu/san) 项目提供热更新功能。能够让 San 项目在调试开发的时候变得更加方便。\n\n## 安装\n\n**代码1-1**\n```shell\n$ npm install --save-dev san-hot-loader\n```\n\n## 配置\n\n您需要在 Webpack 配置文件当中添加 san-hot-loader 相关配置信息。完整的配置信息可参考示例项目当中的 [webpack.config.js](./examples/component-hmr/webpack.config.js) 配置，其中与 san-hot-loader 有关的配置如下所示：\n\n**代码2-1**\n```js\nmodule.exports = {\n    // ... 其他的 webpack 配置信息\n\n    module: {\n        rules: [\n            {\n                test: /\\.js$/,\n                use: ['san-hot-loader']\n            }\n            // ... 其他的 loader 信息\n        ]\n    }\n}\n```\n\n这样，在启动 webpack 进行代码调试的时候，就自动实现了 San 组件与 San-Store 的热更新功能。\n\n\u003e **需要注意的是**，当项目代码使用了 ES7 及以上的语法时，通常需要 babel-loader 将代码进行转换成 ES5 语法，这个转换过程可能会带来额外的 Babel Helper、Polyfill 代码的注入，在这种情况下，san-hot-loader 同时也提供了 babel 插件来实现热更新代码注入：\n\n\n**代码2-2**\n```js\nmodule.exports = {\n    // ... 其他的 webpack 配置信息\n\n    module: {\n        rules: [\n            {\n                test: /\\.js$/,\n                use: [\n                    // 'san-hot-loader',\n                    {\n                        loader: 'babel-loader',\n                        options: {\n                            plugins: [\n                                // 通过 babel plugin 的形式添加\n                                require.resolve('san-hot-loader/lib/babel-plugin')\n                            ]\n                        }\n                    }\n                ]\n            }\n            // ... 其他的 loader 信息\n        ]\n    }\n}\n```\n\n## 使用\n\n在默认情况下，san-hot-loader 自动开启对 San 组件与 San-Store 模块的热更新代码注入，通过自动检测的手段来判断哪些是 San 组件，哪些是 San-Store 模块。\n\n### San 组件格式\n\n#### 常规 San 组件格式\n\n一个简单的常规 San 组件文件如下所示：\n\n**代码3-1**\n```js\n// app.js\nimport san from 'san';\n\nexport default san.defineComponent({\n    template: '\u003cp\u003eHello {{name}}\u003c/p\u003e',\n    initData: function () {\n        return {name: 'San'};\n    }\n});\n```\n\n也可以采用 class 的方式：\n\n**代码3-2**\n```js\nimport {Component} from 'san';\nexport default class App extends Component {\n    static template = '\u003cp\u003eHello {{name}}\u003c/p\u003e';\n    initData() {\n        return {name: 'San'};\n    }\n}\n```\n或者采用 ES5 Function Constructor 的方式来定义组件：\n\n**代码3-3**\n```js\nvar san = require('san');\n\nfunction App(options) {\n    san.Component(this, options);\n}\nsan.inherits(App, san.Component);\nApp.prototype.template = '\u003cp\u003eHello {{name}}\u003c/p\u003e';\nApp.prototype.initData = function () {\n    return {name: 'San'};\n};\nmodule.exports = App;\n```\n\n基于上述例子不难看出，一个功能完整的组件代码文件应包含以下特征：\n\n1. 文件引入 `san` 模块（`import`、`require`）；\n2. 使用 `san` 模块所提供的 API（defineComponent、Component）定义组件；\n3. 将定义好的组件作为默认模块导出（`export default`、`module.exports`）；\n\n以上就是 san-hot-loader 判断文件是否为普通 San 组件的方法。\n\n#### 结合 San-Store 的组件写法\n\n当项目使用 San-Store 时，根据相关文档的说明，需要使用 `san-store` 提供的 connect 方法将状态源与组件关联起来，如：\n\n**代码3-4**\n```js\nimport {defineComponent} from 'san';\nimport {connect} from 'san-store';\nimport './register-store-actions';\n\nconst App = defineComponent({\n    template: '\u003cp\u003eHello {{name}}\u003c/p\u003e',\n    initData: function () {\n        return {name: 'San'};\n    }\n});\n// connect 到默认 store\nconst NewApp = connect.san({name: 'name'})(App);\nexport default NewApp;\n```\n\n或者：\n\n**代码3-5**\n```js\nimport {defineComponent} from 'san';\nimport {connect} from 'san-store';\nimport store from './store';\n\nconst App = defineComponent({\n    template: '\u003cp\u003eHello {{name}}\u003c/p\u003e',\n    initData: function () {\n        return {name: 'San'};\n    }\n});\n// connect 到自定义 store\nconst connector = connect.createConnector(store);\nconst NewApp = connector({name: 'name'})(App);\nexport default NewApp;\n\n```\n\n因此在这种情况下，san-hot-loader 对于满足以下特征的文件，也同样能够识别为 San 组件：\n\n1. 文件引入 `san-store`（`import`、`require`）；\n2. 使用 `san-store` 提供的 `connect.san` 或 `connect.createConnector(store)` 得到的方法连接 store 与当前 San 组件获得新的组件；\n3. 将得到的新组件作为默认模块导出（`export default`、`module.exports`）；\n\n### San-Store 模块写法\n\nsan-hot-loader 为 san-store 提供了 action 的热更新功能，即在修改 san-store 注册 action 的文件时，store 与组件所保存的状态都不会丢失，并且在触发 action 时自动使用最新的 action。\n\nsan-store 提供了默认 store 和自定义 store 两种，因此在支持 san-hot-loader 自动识别的写法上也存在不同。\n\n#### 默认 store\n\nsan-store 提供了默认 store 实例，在使用上可以通过 `import {store} from 'san-store'` 获取该实例对象，调用其 `addAction` 方法可以完成对默认 store 的 action 注册。\n\n一个简单的对默认 store 实例注册 action 的代码如下所示：\n\n**代码3-6**\n```js\n// register-store-actions.js\nimport {store} from 'san-store';\nimport {builder} from 'san-update';\nstore.addAction('increase', function (num) {\n    builder().set('num', num + 1);\n});\n\nstore.addAction('decrease', function (num) {\n    builder().set('num', num - 1);\n});\n```\n\n这个文件会被 san-hot-loader 成功识别，并注入热更新代码。该文件可以参考前面的代码3-4 的方式引入到项目当中。\n\n它应具有以下特征：\n\n1. 文件引入 `san-store`；\n2. 使用 `san-store` 提供的 store.addAction 方法注册 action；\n3. 文件不存在任何模块导出（`export`、`export default`、`module.exports`、`exports.xxx`）；\n\n在这里需要解释一下特征 3。首先，在对默认 store 实例注册 action 之后，可以直接使用 `san-store` 提供的 `connect.san` 方法对 San 组件进行关联，因此无需对默认 store 实例做任何的模块导出；其次，热更新的本质是前端异步接收更新后的文件模块，并进行替换，在这里 san-hot-loader 只针对注册 action 的功能进行了替换处理，如果文件存在模块导出，san-hot-loader 无法去跟踪处理相应行为从而会导致热更新出错。所以要求文件不存在任何模块导出。\n\n#### 自定义 store\n\nsan-store 提供了自定义 store 的方法来满足开发者多 store 的状态管理需求。一个简单的自定义 store 代码如下所示：\n\n**代码3-7**\n```js\n// store.js\nimport {Store} from 'san-store';\nimport {builder} from 'san-update';\n\nexport default new Store({\n    initData: {\n        num: 0\n    },\n    actions: {\n        increase(num) {\n            return builder().set('num', num + 1);\n        },\n        decrease(num) {\n            return builder().set('num', num - 1);\n        }\n    }\n});\n```\n通过类似前面代码3-5 的写法使用自定义 store，同样地，在修改自定义 store 文件时，同样也能够获得热更新效果。\n\n需要注意的是，在这种自定义 Store 的情况下，只有在修改 `actions` 部分的代码会实现热更新，当 initData 部分出现改动时，则会直接进行页面重载。\n\n自定义 store 的文件应具有以下特征：\n\n1. 文件引入 `san-store`；\n2. 使用 `san-store` 提供的 `Store` 方法实例化自定义 store；\n3. 将自定义 store 以默认模块导出；\n\n### 特殊注释\n\n前面给出了符合 san-hot-loader 满足热更新自动检测的一些文件写法，在某些情况下可能会存在以下情况：\n\n1. 文件不希望被热更新；\n2. 文件希望被热更新，但是由于没有被自动检测到而热更新失效；\n\n针对这种情况，除了可以采用下文所提到的 san-hot-loader 配置指定之外，还可以直接在书写具体工程代码的时候，通过添加一些特殊的注释进行标记，san-hot-loader 会去检测这些特殊的注释去执行相应的操作。\n\n#### 禁止热更新\n\n在文件头部或尾部添加以下注释，即可达到禁止该文件被热更新的效果：\n\n```js\n/* san-hmr disable */\n\nimport san from 'san';\n\nexport default san.defineComponent({\n    template: '\u003cdiv\u003ehello world\u003c/div\u003e'\n});\n```\n\n#### 开启热更新\n\n热更新包括组件热更新和 San-Store 热更新。\n\n组件热更新需要满足的条件是：当前文件默认导出的模块组件对象。在满足该前提下，可以使用以下注释指定热更新：\n\n```js\n// ./component.js\nimport sanComponentWrapper from './wrapper'\nimport componentDescriptor from './descriptor';\n\nexport default sanComponentWrapper(componentDescriptor);\n\n/* san-hmr component */\n```\n\n其中：\n\n```js\n// ./wrapper.js\n\nimport san from 'san';\nexport default function (descriptor) {\n    return san.defineComponent(descriptor);\n}\n\n// ./descriptor.js\n\nexport default {\n    template: '\u003cdiv\u003ehello world\u003c/div\u003e'\n}\n```\n\n很明显在上述的示例代码当中 component.js 是无法被 san-hot-loader 自动检测判定为 San 组件文件，因此可以通过注释 `/* san-hmr component */` 主动告知对该文件执行热更新。\n\n同样，对于 San-Store 也可以使用 `/* san-hmr store */` 来实现对 Store 的热更新：\n\n```js\nimport store from './custom-store.js';\n\nstore.addAction('increase', )\n```\n\n```js\n// ./custom-action.js\nimport store from './custom-store';\nimport {builder} from 'san-update';\nstore.addAction('increase', function (num) {\n    builder().set('num', num + 1);\n});\n\nstore.addAction('decrease', function (num) {\n    builder().set('num', num - 1);\n});\n\n/* san-hmr store */\n ```\n\n其中：\n\n```js\n// .custom-store.js\nimport {Store} from 'san-store';\nexport default new Store({\n    initData: {\n        num: 0\n    }\n});\n```\n\n在这种情况下，san-hot-loader 无法通过自动检测手段判定 ./custom-action.js 需要执行热更新，因此可以通过 `/* san-hmr store */` 进行标记。\n\n## 配置\n\nsan-hot-loader 提供一系列配置，通过 webpack loader 的 `options` 配置项传入，如果使用的是 babel 插件时，在配置上也是一样的：\n\n\n```js\n// webpack loader 配置\nmodule.exports = {\n    // ...\n    module: {\n        rules: [\n            // ...\n            {\n                test: /\\.js$/,\n                use: [\n                    {\n                        loader: 'san-hot-loader',\n                        options: {\n                            enable: process.env.NODE_ENV === 'development',\n                            component: {\n                                patterns: [\n                                    /\\.san\\.js$/,\n                                    'auto'\n                                ]\n                            },\n                            store: {\n                                patterns: [\n                                    function (resourcePath) {\n                                        return /\\.store\\.js$/.test(resourcePath);\n                                    },\n                                    'auto'\n                                ]\n                            }\n                        }\n                    }\n                ]\n            }\n        ]\n    }\n}\n```\n\n```js\n// Babel Plugin 配置\nmodule.exports = {\n    // ...\n    module: {\n        rules: [\n            // ...\n            {\n                test: /\\.js$/,\n                use: [\n                    {\n                        loader: 'babel-loader',\n                        options: {\n                            plugins: [\n                                [\n                                    'san-hot-loader',\n                                    {\n                                        enable: process.env.NODE_ENV === 'development',\n                                        component: {\n                                            patterns: [\n                                                /\\.san\\.js$/,\n                                                'auto'\n                                            ]\n                                        },\n                                        store: {\n                                            patterns: [\n                                                function (resourcePath) {\n                                                    return /\\.store\\.js$/.test(resourcePath);\n                                                },\n                                                'auto'\n                                            ]\n                                        }\n                                    }\n                                ]\n                            ]\n                        }\n                    }\n                ]\n            }\n        ]\n    }\n}\n\n```\n\n接下来将详细介绍 san-hot-loader 配置项的使用方法。\n\n### enable\n\n- enable `{boolean}` 是否使能该 loader，默认值为 true。一般配合环境变量一起使用，如：\n\n```js\n// webpack.config.js\n{\n    loader: 'san-hot-loader',\n    options: {\n        enable: process.env.NODE_ENV !== 'production'\n    }\n}\n```\n\n### component.enable\n\n- component.enable `{boolean}` 是否开启 San 组件热更新，默认值为 true，即默认开启：\n\n```js\n{\n    loader: 'san-hot-loader',\n    options: {\n        component: {\n            enable: false // 关闭 San 组件热更新\n        }\n    }\n}\n```\n\n### component.patterns\n\n- component.patterns `{Array.\u003cObject\u003e}` 开启热更新的 San 组件的路径匹配模式，默认值为：`[{component: 'auto'}]`，即采取自动检测的模式。\n\n其中 `component` 的取值除了 `'auto'` 之外，还可以传入正则表达式和函数来对进行文件路径匹配，比如：\n\n```js\n{\n    loader: 'san-hot-loader',\n    options: {\n        component: {\n            patterns: [\n                /\\.san\\.js$/,\n                function (resourcePath) {\n                    // resourcePath 为当前资源的绝对路径\n                    // return true 则表示匹配成功\n                    return /\\.san\\.js$/.test(resourcePath);\n                },\n                'auto'\n            ]\n        }\n    }\n}\n```\n\nsan-hot-loader 会优先匹配到后缀为 `.san.js` 的文件时，会直接向该文件注入热更新代码，匹配失败后，再按顺序执行剩余的匹配规则。\n\n### store.enable\n\n- store.enable `boolean` 是否开启 San-Store 热更新，默认为 true，即默认开启：\n\n```js\n{\n    loader: 'san-hot-loader',\n    options: {\n        store: {\n            enable: false\n        }\n    }\n}\n```\n\n### store.patterns\n\n- store.patterns `{Array.\u003cObject\u003e}` 开启热更新的 San Store 模块路径匹配模式，默认值为：`['auto']`，即采取自动检测的模式。\n\n无论是使用 san-store 提供的全局 store 还是自定义 store，同样也可以通过正则表达式和函数的方式去指定文件，在规则上与 component 配置一致：\n\n```js\n{\n    loader: 'san-hot-loader',\n    options: {\n        store: {\n            patterns: [\n                /\\.store\\.js$/,\n                function (resourcePath) {\n                    return /\\.store\\.js$/.test(resourcePath);\n                },\n                'auto'\n            ]\n        }\n    }\n}\n```\n\n## 示例\n\n项目 examples 提供了一个简单但功能完整的[示例](./examples/loader)，可以将本项目 clone 下来，运行该示例，并尝试修改示例当中的代码来感受热更新为开发所带来的便利。\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fecomfe%2Fsan-hot-loader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fecomfe%2Fsan-hot-loader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fecomfe%2Fsan-hot-loader/lists"}