{"id":15235705,"url":"https://github.com/wscats/omil","last_synced_at":"2026-03-04T15:01:09.365Z","repository":{"id":34806249,"uuid":"183772912","full_name":"Wscats/omil","owner":"Wscats","description":"📝Webpack loader for Omi.js React.js and Rax.js components 基于 Omi.js，React.js 和 Rax.js 单文件组件的 Webpack 模块加载器","archived":false,"fork":false,"pushed_at":"2023-01-06T06:44:43.000Z","size":3716,"stargazers_count":181,"open_issues_count":23,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-10-21T12:00:04.509Z","etag":null,"topics":["eno-loader","htm","jsx","loader","omi","omil","preact","rax","react","shadow-dom","single-file-component","webpack"],"latest_commit_sha":null,"homepage":"https://wscats.github.io/omil","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/Wscats.png","metadata":{"files":{"readme":"README.CN.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":"2019-04-27T12:56:07.000Z","updated_at":"2025-02-23T08:23:47.000Z","dependencies_parsed_at":"2023-01-15T09:30:53.544Z","dependency_job_id":null,"html_url":"https://github.com/Wscats/omil","commit_stats":null,"previous_names":["wscats/eno-loader"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Wscats/omil","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wscats%2Fomil","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wscats%2Fomil/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wscats%2Fomil/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wscats%2Fomil/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Wscats","download_url":"https://codeload.github.com/Wscats/omil/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wscats%2Fomil/sbom","scorecard":{"id":152491,"data":{"date":"2025-08-11","repo":{"name":"github.com/Wscats/omil","commit":"2f78804f573ce8a2af6e83a93b65ea06e47d4581"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 0/15 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 15 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"79 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-phwq-j96m-2c2q","Warn: Project is vulnerable to: GHSA-ghr5-ch3p-vcr6","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-8r6j-v8pm-fqw3","Warn: Project is vulnerable to: MAL-2023-462","Warn: Project is vulnerable to: GHSA-q54r-r9pr-w7qv","Warn: Project is vulnerable to: GHSA-7wwv-vh3v-89cq","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-6c8f-qphg-qjgp","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-wx77-rp39-c6vg","Warn: Project is vulnerable to: GHSA-ch52-vgq2-943f","Warn: Project is vulnerable to: GHSA-5v2h-r2cx-5xgj","Warn: Project is vulnerable to: GHSA-rrrm-qjm4-v8hf","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-8hfj-j24r-96c4","Warn: Project is vulnerable to: GHSA-wc69-rhjr-hc9g","Warn: Project is vulnerable to: GHSA-56x4-j7p9-fcf9","Warn: Project is vulnerable to: GHSA-v78c-4p63-2j6c","Warn: Project is vulnerable to: GHSA-9v62-24cr-58cx","Warn: Project is vulnerable to: GHSA-r8f7-9pfq-mjmv","Warn: Project is vulnerable to: GHSA-rp65-9cf3-cjxr","Warn: Project is vulnerable to: GHSA-x77j-w7wf-fjmw","Warn: Project is vulnerable to: GHSA-76c9-3jph-rj3q","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-566m-qj78-rww5","Warn: Project is vulnerable to: GHSA-7fh5-64p2-3v2j","Warn: Project is vulnerable to: GHSA-hwj9-h5mp-3pm3","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-7mwh-4pqv-wmr8","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-m6fv-jmcg-4jfg","Warn: Project is vulnerable to: GHSA-cm22-4g7w-348p","Warn: Project is vulnerable to: GHSA-qxg5-2qff-p49r","Warn: Project is vulnerable to: GHSA-2rq5-699j-x7p6","Warn: Project is vulnerable to: GHSA-3jfq-g458-7qm9","Warn: Project is vulnerable to: GHSA-5955-9wpr-37jh","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-r628-mhmh-qjhw","Warn: Project is vulnerable to: GHSA-9r2w-394v-53qc","Warn: Project is vulnerable to: GHSA-qq89-hq3f-393p","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-7p7h-4mm5-852v","Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp","Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-x9w5-v3q2-3rhw","Warn: Project is vulnerable to: GHSA-vh7m-p724-62c2","Warn: Project is vulnerable to: GHSA-r9p9-mrjm-926w","Warn: Project is vulnerable to: GHSA-434g-2637-qmqr","Warn: Project is vulnerable to: GHSA-49q7-c7j4-3p7m","Warn: Project is vulnerable to: GHSA-977x-g7h5-7qgw","Warn: Project is vulnerable to: GHSA-f7q4-pwc6-w24p","Warn: Project is vulnerable to: GHSA-fc9h-whq2-v747","Warn: Project is vulnerable to: GHSA-vjh7-7g9h-fjfh","Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-76p3-8jx3-jpfq","Warn: Project is vulnerable to: GHSA-3rfm-jhwj-7488","Warn: Project is vulnerable to: GHSA-hhq3-ff78-jv3g","Warn: Project is vulnerable to: GHSA-h7cp-r72f-jxh6","Warn: Project is vulnerable to: GHSA-v62p-rq8g-8h59","Warn: Project is vulnerable to: GHSA-hxcc-f52p-wc94","Warn: Project is vulnerable to: GHSA-4wf5-vphf-c2xc"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-16T11:04:43.818Z","repository_id":34806249,"created_at":"2025-08-16T11:04:43.818Z","updated_at":"2025-08-16T11:04:43.818Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30084685,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-04T13:22:36.021Z","status":"ssl_error","status_checked_at":"2026-03-04T13:20:45.750Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["eno-loader","htm","jsx","loader","omi","omil","preact","rax","react","shadow-dom","single-file-component","webpack"],"created_at":"2024-09-29T08:05:53.668Z","updated_at":"2026-03-04T15:01:09.343Z","avatar_url":"https://github.com/Wscats.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[English](./README.EN.md)  | 简体中文\n\n# Omil 是什么？\n`omil`是一个 webpack 的 loader，它允许你以一种名为单文件组件`(SFCs)`的格式撰写 Omi 组件：\n\n```html\n\u003ctemplate lang=\"html\" name=\"component-name\"\u003e\n  \u003cheader onClick=\"${this.test}\"\u003e${this.data.title}\u003c/header\u003e\n\u003c/template\u003e\n\u003cscript\u003e\nexport default class {\n  test(){ console.log('Hello Eno!') }\n  install() {\n    this.data = { title: 'Omi' }\n  }\n}\n\u003c/script\u003e\n\u003cstyle\u003e\nheader { color: #58bc58; }\n\u003c/style\u003e\n```\n\nOmil 还提供了很多酷炫的特性：\n\n- 允许为 Omi 组件的每个部分使用其它的 webpack loader，例如在`\u003cstyle\u003e`的部分使用 Sass 和在`\u003ctemplate\u003e`的部分使用 jsx；\n- 允许在一个 .omi 文件中使用自定义块，并对其运用自定义的 loader 链；\n- 使用 webpack loader 将`\u003cstyle\u003e`和`\u003ctemplate\u003e`中引用的资源当作模块依赖来处理；\n- 在开发过程中使用热重载来保持状态。\n\n简而言之，webpack 和 Omi Loader 的结合为你提供了一个现代、灵活且极其强大的前端工作流，来帮助撰写 Omi.js 应用。\n\n# 起步\n\n## Omi CLI\n如果你不想手动设置 webpack，我们推荐使用 [Omi CLI](https://github.com/Tencent/omi/tree/master/packages/omi-cli) 直接创建一个项目的脚手架。通过 Omi CLI 创建的项目会针对多数常见的开发需求进行预先配置，做到开箱即用。\n\n如果`Omi CLI`提供的内建没有满足你的需求，或者你乐于从零开始创建你自己的 webpack 配置，那么请继续阅读这篇指南。\n\n# 手动设置\n## 安装\n\n首先先安装好`Omil`\n```bash\nnpm install -D omil\n```\n如果你使用的是 [Visual Studio Code](https://code.visualstudio.com/) 进行开发，强烈建议下载 [Omi Snippets](https://marketplace.visualstudio.com/items?itemName=Wscats.omi-snippets) 扩展，它会提供给你语法高亮，局部编译等功能。您可以在 VSC 扩展界面里面搜索 omi 这个关键词出现`Omi Snippets`点击安装即可，稍等片刻，当它安装成功后会提醒你需要重新加载编辑工具，点击重新加载即可使用。\n\n\u003cimg src=\"../images/omi-snippets.png\"\u003e\n\n每个`Omil`包的新版本发布时，一个相应版本的`Omi Snippets`也会随之发布。\n\n## webpack 配置\n\nOmi Loader 的配置和其它的 loader 基本一样。\n```js\n// webpack.config.js\nmodule.exports = {\n  module: {\n    rules: [\n      // ... 其它规则\n      {\n        test: /\\.omi|eno$/,\n        loader: 'omil'\n      }\n    ]\n  }\n}\n```\n一个更完整的 webpack 配置示例看起来像这样：\n```js\nmodule.exports = {\n  mode: 'development',\n  module: {\n    rules: [{\n      test: /\\.omi|eno$/,\n      use: [{\n        loader: require.resolve('omil'),\n        options: {\n          // Use in development, You should remove in production\n          sourceMaps: 'both',\n          // Config babel plugins for async, await and other many features\n          plugins: [\n            [\n              \"@babel/plugin-transform-runtime\",\n              {\n                \"absoluteRuntime\": false,\n                \"corejs\": false,\n                \"helpers\": true,\n                \"regenerator\": true,\n                \"useESModules\": false\n              }\n            ]\n          ]\n        }\n      }],\n      // Or you can use eno-loader or omil directly\n      // use: ['eno-loader']\n      // use: ['omil']\n    }]\n  }\n}\n```\n\n# Omi Snippets\n\n在配置完 Omil 之后，我们可以在 VS Code 上同时安装好 [Omi Snippets](https://marketplace.visualstudio.com/items?itemName=Wscats.omi-snippets) 扩展，这个插件可以方便的让你把 .omi 和 .eno 后缀文件在未经过 webpack 处理前转化为 .js 文件，让你可以直观了解到单文件组件经过 omil 转化后的 JS 文件内容，这相当于局部编译减轻 webpack 处理单文件时候的不必要消耗。\n\n## 目录结构\n\n例如你在 webpack 的入口文件夹中有一个 .omi 的后缀文件，当你新建并经过编辑保存之后，Omi Snippets扩展会在同级目录下新建一份同名但不同后缀的 .js 文件\n\n- src\n  - Hello.omi\n  - Hello.js\n\n|Hello.omi|开发中你需要编写的单文件组件|\n|-|-|\n|Hello.js|修改或者保存文件`Hello.omi`后经过插件转化的js文件|\n\n如下图，左边的代码是我们编写的 .omi 后缀的单文件组件，右边是经过 Omi Snippets 生成的 .js 后缀文件。\n\n\u003cimg src=\"../images/transfer.png\" /\u003e\n\n\n## 示例代码\n\n上图的示例代码如下\n\n- `\u003ctemplate\u003e` 标签负责放 JSX 的内容，属性`name=\"my-test\"`为该组件的名字，后面可以在 JSX 中用`\u003cmy-text\u003e`使用该组件;\n- `\u003cscript\u003e` 标签负责放入组件的逻辑文件，固定的结构为 `export default class { // 你的代码 }`或者为`export default HOC(class { // 你的代码 })`两种形式，第一种是定义类组件，第二种用来定义高阶组件，你的代码部分可以放入生命周期，函数等;\n- `\u003cstyle\u003e` 标签负责定义该组件的局部样式\n\n```html\n\u003ctemplate name=\"my-test\"\u003e\n  \u003cdiv class=\"example\"\u003e\n    { this.data.msg }\n  \u003c/div\u003e\n\u003c/template\u003e\n\n\u003cscript\u003e\nexport default class {\n  install () {\n    this.data = {\n      msg: 'Hello world!'\n    }\n  }\n}\n\u003c/script\u003e\n\n\u003cstyle\u003e\n.example {\n  color: red;\n}\n\u003c/style\u003e\n```\n以下代码就是经过 Omi Snippets 生成的 .js 后缀文件，可以用于在你没有 omil 模块下，主逻辑文件或者其他组件引入调用。\n```js\nimport { WeElement, define, h } from \"omi\";\nclass MyTest extends WeElement {\n  render() {\n    return h(\n      \"div\",\n      {\n        class: \"example\"\n      },\n      this.data.msg\n    );\n  }\n  install() {\n    this.data = {\n      msg: \"Hello world!\"\n    };\n  }\n}\nMyTest.css = `\n.example {\n  color: red;\n}\n`;\ndefine(\"my-test\", MyTest);\n```\n\n# 配合 React 开发\n\n安装 React 脚手架和一些必要模块。\n```bash\nnpm install create-react-app\n# 初始化项目\ncreate-react-app my-project\n# 进入项目文件夹目录\ncd my-project\n# 安装项目依赖\nnpm install\n# 安装 styled-components 这个务必得安装 用于处理 React 单文件组件局部样式\nnpm install styled-components --save\n# 安装 omil 处理React单文件组件，把 .omi 或者 .eno 后缀文件处理为 JS\nnpm install omil --save-dev\n```\n\n在配置完 Omil 之后，我们可以在 VS Code 上同时安装好 [Omi Snippets](https://marketplace.visualstudio.com/items?itemName=Wscats.omi-snippets) 扩展，这个插件可以方便的让你把 .omi 和 .eno 后缀文件在未经过 webpack 处理前转化为 .js 文件，让你可以直观了解到单文件组件经过 omil 转化后的 JS 文件内容，这相当于局部编译减轻 webpack 处理单文件时候的不必要消耗。\n\n\n# 编写第一个组件\n\n现在你可以使用单文件组件来编写 React 组件，默认生成类组件。\n\n- name属性值是组件名要满足 React 框架的组件名字定义规范，首字母必须大写字母;\n- `\u003ctemplate\u003e`模板中不能有`\u003cscript\u003e`和`\u003cstyle\u003e`代码片段。\n\n```html\n\u003ctemplate name=\"Component-name\"\u003e\n    \u003cdiv\u003e\n        \u003cp\u003e{this.state.title}\u003c/p\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\nexport default class {\n    constructor(props) {\n        super(props)\n        this.state = {\n            title: \"react\"\n        }\n    }\n    componentDidMount(){\n        console.log('生命周期')\n    }\n}\n\n\u003c/script\u003e\n\u003cstyle\u003e\np {color: #58bc58};\n\u003c/style\u003e\n```\n以上文件经过 Omil 处理后将会转化为以下代码：\n\n```js\nimport { Component as WeElement, createElement as h } from \"react\";\nimport styled from \"styled-components\";\nconst StyledComponents = styled.div`\n  /* CSS */\n  p {\n    color: #58bc58;\n  }\n`;\n\nclass ComponentName extends WeElement {\n  render() {\n    return h(\n      StyledComponents,\n      null,\n      h(\"div\", null, h(\"p\", null, this.state.title))\n    );\n  }\n\n  constructor(props) {\n    super(props);\n    this.state = {\n      title: \"react\"\n    };\n  }\n\n  componentDidMount() {\n    console.log(\"生命周期\");\n  }\n}\n\nComponentName.css = `\n/* CSS */\np {color: #58bc58};\n`;\nexport default ComponentName;\n```\n\n# 语言块规范\n\n## 简介\n\n.omi 文件是一个自定义的文件类型，用类 HTML 语法描述一个 Omi 组件。每个 .omi 文件包含三种类型的顶级语言块 `\u003ctemplate\u003e`、`\u003cscript\u003e` 和 `\u003cstyle\u003e`:\n\n```html\n\u003ctemplate name=\"my-test\"\u003e\n  \u003cdiv class=\"example\"\u003e\n    { this.data.msg }\n  \u003c/div\u003e\n\u003c/template\u003e\n\n\u003cscript\u003e\nexport default class {\n  install () {\n    this.data = {\n      msg: 'Hello world!'\n    }\n  }\n}\n\u003c/script\u003e\n\n\u003cstyle\u003e\n.example {\n  color: red;\n}\n\u003c/style\u003e\n```\n\nOmil 会解析文件，提取每个语言块，如有必要会通过其它 loader 处理，最后将他们组装成一个 ES Module，它的默认导出是一个 Omi.js 组件定义好的自定义标签对象。\n\nOmil 支持使用非默认语言，比如 CSS 预处理器，预编译的 HTML 模版语言，通过设置语言块的 lang 属性。例如，你可以像下面这样使用 Sass 语法编写样式：\n\n```html\n\u003cstyle lang=\"sass\"\u003e\n  /* write Sass! */\n\u003c/style\u003e\n```\n\n# 语言块\n\n## `\u003ctemplate\u003e`模板\n\n每个 .omi 文件最多包含一个 `\u003ctemplate\u003e` 块。\n\n内容将被提取，如果是 JSX 会编译为函数片段，如果为 html 会编译为字符串，并最终注入到从`\u003cscript\u003e`导出的组件 render 函数中。\n\n### 属性`name = \"xxx-xxx\"`(Omi组件)\n\n定义`name=\"xxx-xxx\"`可以给组件定义一个名字，这个名字会自动调用 omi 框架的 `define('xxx-xxx', xxxXxx)` 方法来注册组件，你就可以在页面中用这个属性名`\u003cxxx-xxx\u003e\u003c/xxx-xxx\u003e`来使用该组件\n\n**注意：** \n- name属性值是组件名要满足 omi 框架的组件名字定义规范，首字母不能用大写字母，并且中间必须有`-`字符;\n- `\u003ctemplate\u003e`模板中不能有`\u003cscript\u003e`和`\u003cstyle\u003e`代码片段。\n\n```html\n\u003ctemplate name=\"my-test\"\u003e\n  \u003cdiv class=\"example\"\u003e\n    { this.data.msg }\n  \u003c/div\u003e\n\u003c/template\u003e\n```\n在页面容器中如此使用\n```html\n\u003cmy-test/\u003e\n\u003cmy-test\u003e\u003c/my-test\u003e\n```\n\n### 属性`name = \"XxxXxx\"`(React组件)\n\n定义`name=\"XxxXxx\"`可以给组件定义一个名字，这个名字会自动调用 React 框架的 `React.Component` 方法来定义类组件，你就可以在页面中用这个属性名`\u003cXxxXxx\u003e\u003c/XxxXxx\u003e`来使用该组件\n\n**注意：** \n- name属性值是组件名要满足 React 框架的组件名字定义规范，首字母必须大写字母;\n- `\u003ctemplate\u003e`模板中不能有`\u003cscript\u003e`和`\u003cstyle\u003e`代码片段。\n\n```html\n\u003ctemplate name=\"MyTest\"\u003e\n  \u003cdiv class=\"example\"\u003e\n    { this.data.msg }\n  \u003c/div\u003e\n\u003c/template\u003e\n```\n在页面容器中如此使用\n```html\n\u003cMyTest/\u003e\n\u003cMyTest\u003e\u003c/MyTest\u003e\n```\n\n### 属性`lang = \"html\"`(仅支持Omi)\n\n默认情况下，我们的`\u003ctemplate\u003e`模板是使用 JSX 语法，如果我们增加属性`lang = \"html\"`，就可以支持编写html格式的字符串模板，你可以使用 ES6 的语法来编写 html 模板`\u003cdiv\u003e${ this.data.msg }\u003cdiv\u003e`，Omil 和 Omi-Snippets 会自动帮你引入`Omi.html()`方法帮你在客户端进行处理，会有一定的性能损耗，一般情况下不建议使用。\n\n```html\n\u003ctemplate name=\"my-test\" lang=\"html\"\u003e\n  \u003cdiv class=\"example\"\u003e\n    ${ this.data.msg }\n  \u003c/div\u003e\n\u003c/template\u003e\n```\n\n## `\u003cscript\u003e`脚本\n\n每个 .omi 文件最多包含一个 `\u003cscript\u003e` 块。\n\n### 类组件\n\n如果我们使用过 react 我们会了解到组件通常有两种定义方式，一种是函数组件，一种是类组件，Omil 默认是帮你创建类组件，我们在`export default class { // 你的代码 }`或者`module.exports = class { // 你的代码 }`片段中写入你的组件逻辑代码，\n\n**注意：** \n- 定义类组件必须是`export default class { // 你的代码 }`这种写法，`class MyText {} ; export default MyText`这种写法不可以，因为 Omil 和 Omil Snippets 只识别连续的`export default class`这段字符串\n\n\n|`export default class { // 你的代码 }`|可以|建议使用|\n|-|-|-|\n|`module.exports = class { // 你的代码 }`|可以|支持|\n|`class MyText { // 你的代码 }`\u003cbr/\u003e`export default MyText`|不可以|不支持|\n|`class MyText { // 你的代码 }`\u003cbr/\u003e`module.export = MyText`|不可以|不支持|\n\n```html\n\u003cscript\u003e\nexport default class {\n  install () {\n    this.data = {\n      msg: 'Hello world!'\n    }\n  }\n}\n\u003c/script\u003e\n```\n\n### 高阶组件(仅支持React)\n\n有时候我们可以使用高阶组件拓展组件本身的一些功能，高阶组件跟类组件一样，只支持下面规定的写法。\n\n|`export default HOC(class { // 你的代码 })`|可以|建议使用|\n|-|-|-|\n|`module.exports = HOC(class { // 你的代码 })`|可以|支持|\n|`class MyText { // 你的代码 }`\u003cbr/\u003e`export default HOC(MyText)`|不可以|不支持|\n|`class MyText { // 你的代码 }`\u003cbr/\u003e`module.export = HOC(MyText)`|不可以|不支持|\n\n```html\n\u003cscript\u003e\nexport default HOC(class {\n  install () {\n    this.data = {\n      msg: 'Hello world!'\n    }\n  }\n})\n\u003c/script\u003e\n```\n下面是一个高阶组件的详细参考例子\n```html\n\u003ctemplate name=\"MyTest\"\u003e\n    \u003cdiv\u003e\u003cp\u003e{this.state.title}\u003c/p\u003e\u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\n// 高阶函数\nconst HOC = (props) =\u003e {\n    return (WraooedComponent) =\u003e {\n        return class HOC extends WeElement {\n            render() {\n                return (\u003cdiv\u003e\u003cWraooedComponent name={{ ...this.props }} /\u003e\u003c/div\u003e)\n            }\n        }\n    }\n}\nexport default HOC({\n    age: 18\n})(class {\n    install () {\n        this.data = {\n            msg: 'Hello world!'\n        }\n    }\n})\n\u003c/script\u003e\n\u003cstyle lang=\"scss\"\u003e\np { color: #58bc58; }\n\u003c/style\u003e\n```\n或者你可以这样写\n```html\n\u003ctemplate name=\"MyTest\"\u003e\n    {HOC(\u003cdiv\u003e\u003cp\u003e{this.state.title}\u003c/p\u003e\u003c/div\u003e)}\n\u003c/template\u003e\n\u003cscript\u003e\n// 高阶函数\nconst HOC = (props) =\u003e {\n    return (WraooedComponent) =\u003e {\n        return class HOC extends WeElement {\n            render() {\n                return (\u003cdiv\u003e\u003cWraooedComponent name={{ ...this.props }} /\u003e\u003c/div\u003e)\n            }\n        }\n    }\n}\nexport default class {\n    install () {\n        this.data = {\n            msg: 'Hello world!'\n        }\n    }\n}\n\u003c/script\u003e\n\u003cstyle lang=\"scss\"\u003e\np { color: #58bc58; }\n\u003c/style\u003e\n```\n\n### 属性`type=\"text/babel\"`\n\n通常情况下，你可以在代码中使用ES6的语法，甚至一些新特性，例如：`static`，某些情况下我们需要转化为ES5做兼容，我们可以添加属性`type=\"text/babel\"`\n```html\n\u003cscript\u003e\nexport default class {\n  static name = 'Eno Yao'\n  install () {\n    this.data = {\n      msg: 'Hello world!'\n    }\n  }\n}\n\u003c/script\u003e\n```\n\n## `\u003cstyle\u003e`样式\n\n一个 .omi 文件可以包含一个`\u003cstyle\u003e`标签。\n\n`\u003cstyle\u003e`标签的样式本身具有局部样式的特性，这取决于 Omi 的设计是 Web Components，这有点类似于 Vue 的 scoped 属性。\n\n```html\n\u003cstyle\u003e\n.example {\n  color: red;\n}\n\u003c/style\u003e\n```\n\n### 属性`lang = \"scss\"`\n\n我们还可以使用`lang = \"scss\"`来书写 scss 样式，它会自动帮我们编译为 css 格式内容\n\n```html\n\u003cstyle lang = \"scss\"\u003e\n$color: red;\n.example {\n  color: $color;\n}\n\u003c/style\u003e\n```\n\n# 语法高亮\n\n建议使用 VS Code 配合 Omi Snippets （该扩展支持语法高亮）扩展开发 Omi 项目，当然你可以把 .omi 文件当作 HTML 对待。\n\n# 注释\n\n在语言块中使用该语言块对应的注释语法 (HTML、CSS、JavaScript 等)。\n\n|JSX 注释语法|`{/* comment contents here */}`|\n|-|-|\n|HTML 注释语法|`\u003c!-- comment contents here --\u003e`|\n\n# JSX 简介\n\n观察下面这段代码模板：\n```html\n\u003ctemplate name=\"component-name\"\u003e\n  \u003cheader onClick={this.test}\u003e{this.data.title}\u003c/header\u003e\n\u003c/template\u003e\n```\n这个有趣的标签语法既不是字符串也不是 HTML。\n\n它被称为 JSX，是一个 JavaScript 的语法扩展。我们建议在 Omi 中配合使用 JSX，JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式。JSX 可能会使人联想到模版语言，但它具有 JavaScript 的全部功能。\n\n上面的代码事实上会自动编译为下面这份 js 代码\n```js\nimport { WeElement, define, h } from \"omi\";\nclass ComponentName extends WeElement {\n  render() {\n    return h(\n      \"div\",\n      {\n        onClick: this.testClick\n      },\n      this.data.title\n    );\n  }\n}\ndefine(\"component-name\", ComponentName);\n```\n\n## 为什么使用 JSX？\n\nOmi 和 React 不强制要求使用 JSX，但是大多数人发现，在 JavaScript 代码中将 JSX 和 UI 放在一起时，会在视觉上有辅助作用。\n\n# Omi 和 React 在使用 Omil 和 Omi Snippets 的区别\n\nOmil和Omi Snippets都支持编译Omi和React，编译的区别取决于`\u003ctemplate\u003e`的`name`属性值，React的组件名必须首字母大写，Omi的组件首字母不能大写，并且名字中间必须有`-`符号连接。\n\n|React|Omi|\n|-|-|\n|`\u003ctemplate name=\"ComponentName\"\u003e`|`\u003ctemplate name=\"component-name\"\u003e`|\n|组件名必须首字母大写|组件首字母不能大写，并且名字中间必须有`-`符号连接|\n\n## 在 JSX 中嵌入表达式\n\n在下面的例子中，我们声明了一个名为 title 的变量，然后在 JSX 中使用它，并将它包裹在大括号中：\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cdiv\u003e\n        {this.data.title}\n    \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\n    export default class {\n        install() {\n            this.data = {\n                title: \"Eno Yao !\"\n            }\n        }\n    }\n\u003c/script\u003e\n```\n\n在 JSX 语法中，你可以在大括号内放置任何有效的 JavaScript 表达式。例如，2 + 2，user.firstName 或 formatName(user) 都是有效的 JavaScript 表达式。\n\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cdiv\u003e\n        \u003cp\u003eName: {this.formatName(user)}\u003c/p\u003e\n        \u003cp\u003eAge: {9+9}\u003c/p\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\n    const user = {\n        firstName: 'Eno',\n        lastName: 'Yao'\n    };\n    export default class {\n        formatName(user) {\n            return user.firstName + ' ' + user.lastName;\n        }\n    }\n\u003c/script\u003e\n```\n\n二元和三元表达式\n\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cdiv\u003e\n        { !0 ? '真' : \u003cp\u003e假\u003c/p\u003e }\n        \u003ch1\u003e{ user.age \u003e 18 \u0026\u0026 \u003cdiv\u003e成年\u003c/div\u003e }\u003ch1\u003e\u003c/h1\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n```\n\n数组渲染成列表\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cul\u003e\n        {\n            ['a','b','c'].map((item,index) =\u003e {\n                return \u003cli key={index}\u003e{item}\u003c/li\u003e\n            })\n        }\n    \u003c/ul\u003e\n\u003c/template\u003e\n```\n\n\n## JSX 也是一个表达式\n\n在编译之后，JSX 表达式会被转为普通 JavaScript 函数调用，并且对其取值后得到 JavaScript 对象。\n\n也就是说，你可以在 if 语句和 for 循环的代码块中使用 JSX，将 JSX 赋值给变量，把 JSX 当作参数传入，以及从函数中返回 JSX：\n\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cdiv\u003e\n        \u003cp\u003e{this.getGreeting(user)}\u003c/p\u003e\n        \u003cp\u003e{this.getGreeting()}\u003c/p\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\n    const user = {\n        firstName: 'Eno',\n        lastName: 'Yao'\n    };\n    export default class {\n        formatName(user) {\n            return user.firstName + ' ' + user.lastName;\n        }\n        getGreeting(user) {\n            if (user) {\n                return \u003ch1\u003eHello, {this.formatName(user)}!\u003c/h1\u003e;\n            }\n            return \u003ch1\u003eHello, Stranger.\u003c/h1\u003e;\n        }\n    }\n\u003c/script\u003e\n```\n\n## JSX 特定属性\n\n你可以通过使用引号，来将属性值指定为字符串字面量\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cdiv tabIndex=\"0\"\u003e\u003c/div\u003e\n\u003c/template\u003e\n```\n\n也可以使用大括号，来在属性值中插入一个 JavaScript 表达式：\n\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cdiv tabIndex=\"0\"\u003e\n        \u003cimg src={this.data.avatarUrl} /\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\n    export default class {\n        install() {\n            this.data = {\n                avatarUrl: 'https://avatars1.githubusercontent.com/u/17243165?s=460\u0026v=4'\n            }\n        }\n    }\n\u003c/script\u003e\n```\n\nHTML 和 JSX 的一些区别\n\n|HTML|JSX|\n|-|-|\n|`\u003cdiv class\u003e`|`\u003cdiv className\u003e`|\n|`\u003clabel for\u003e`|`\u003clabel htmlFor\u003e`|\n|`\u003cdiv tabindex\u003e`|`\u003cdiv tabIndex\u003e`|\n\n\n在属性中嵌入 JavaScript 表达式时，不要在大括号外面加上引号。你应该仅使用引号（对于字符串值）或大括号（对于表达式）中的一个，对于同一属性不能同时使用这两种符号。\n\n\u003e 警告：\n\n\u003e 因为 JSX 语法上更接近 JavaScript 而不是 HTML，所以 React DOM 使用 camelCase（小驼峰命名）来定义属性的名称，而不使用 HTML 属性名称的命名约定。\n\n\u003e 例如，JSX 里的 class 变成了 className，而 tabindex 则变为 tabIndex。\n\n\n## 使用 JSX 指定子元素\n\n假如一个标签里面没有内容，你可以使用 `/\u003e` 来闭合标签，就像 XML 语法一样：\n\n```html\n\u003cimg src={this.data.avatarUrl} /\u003e\n\u003cinput onChange={this.getInputValue.bind(this)} /\u003e\n```\nJSX 标签里能够包含很多子元素:\n\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cdiv\u003e{this.data.element}\u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\n    export default class {\n        install() {\n            this.data = {\n                element: (\n                    \u003cdiv\u003e\n                        \u003ch1\u003eHello!\u003c/h1\u003e\n                        \u003ch2\u003eGood to see you here.\u003c/h2\u003e\n                    \u003c/div\u003e\n                )\n            }\n        }\n    }\n\u003c/script\u003e\n```\n\n## JSX 表示对象\n\nBabel 会把 JSX 转译成一个名为 `h()` 函数调用。\n\n以下两种示例代码完全等效：\n```js\nconst element = \u003cdiv\u003e\n    \u003ch1 className=\"greeting\"\u003e\n        Hello, world!\n    \u003c/h1\u003e\n\u003c/div\u003e\n```\n```js\nconst element = h(\n  \"div\",\n  null,\n  h(\n    \"h1\",\n    {\n      className: \"greeting\"\n    },\n    \"Hello, world!\"\n  )\n);\n```\n\n`h()` 会预先执行一些检查，以帮助你编写无错代码，但实际上它创建了一个这样的对象：\n\n```js\n// 注意：这是简化过的结构\nconst element = {\n  children: [{\n    attributes: {className: \"greeting\"},\n    children: [\"Hello, world!\"],\n    nodeName: \"h1\",\n  }],\n  nodeName: \"div\"\n}\n```\n\n这些对象它们描述了你希望在屏幕上看到的内容。Omi 通过读取这些对象，然后使用它们来构建 DOM 以及保持随时更新。\n\n# Props\n\n我们可以在组件的属性上传入属性值，通过传入属性值让组件接受外部的数据而更改自身的状态。\n\n```html\n\u003ccomponent-name myObj={{ name: 'Eno Yao' }} /\u003e\n```\n\n组件内部通过props接受即可：\n\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cp\u003e{props.myObj.name}\u003c/p\u003e\n\u003c/template\u003e\n```\n\n我们还可以通过`static defaultProps`设置默认的props值和通过`static propTypes`设置默认的props类型。\n\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cdiv\u003e\n        \u003cp\u003e{props.name}\u003c/p\u003e\n        \u003cp\u003e{props.age}\u003c/p\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\n    export default class {\n        static defaultProps = {\n            name: 'Omi',\n            age: 18\n        }\n\n        static propTypes = {\n            name: String,\n            age: Number\n        }\n    }\n\u003c/script\u003e\n```\n\n# 事件处理\n\nOmi 元素的事件处理和 React 一样和 DOM 元素的很相似，但是有一点语法上的不同:\n\n- Omi 事件的命名采用小驼峰式（camelCase），而不是纯小写。\n- 使用 JSX 语法时你需要传入一个函数作为事件处理函数，而不是一个字符串。\n\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cdiv\u003e\n        \u003cbutton onClick={this.onClick}\u003eHello Omi!\u003c/button\u003e\n        \u003cbutton onClick={(evt)=\u003e {alert('Hello Omi!')}}\u003eHello Omi!\u003c/button\u003e\n        \u003cbutton onClick={onClick}\u003eHello Omi!\u003c/button\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\n    const onClick = (evt) =\u003e {\n        alert('Hello Omi!')\n    }\n    export default class {\n        onClick(evt) {\n            alert('Hello Omi!')\n        }\n    }\n\u003c/script\u003e\n```\n\n## 事件中的this\n\n你必须谨慎对待 JSX 回调函数中的 this，在 JavaScript 中，class 的方法默认不会绑定 this。如果你忘记绑定 this.handleClick 并把它传入了 onClick，当你调用这个函数的时候 this 的值为 undefined。\n\n这并不是 React 特有的行为；这其实与 JavaScript 函数工作原理有关。通常情况下，如果你没有在方法后面添加 ()，例如 onClick={this.handleClick}，你应该为这个方法绑定 this。\n\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cdiv\u003e\n        \u003cbutton onClick={this.onClick.bind(this)}\u003e{this.data.title}\u003c/button\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\n    export default class {\n        install() {\n            this.data = { title: 'Hello Omi!' }\n        }\n        onClick() {\n            this.data.title = 'Hi Eno!'\n            this.update()\n        }\n    }\n\u003c/script\u003e\n```\n\n## 向事件处理程序传递参数\n\n在循环中，通常我们会为事件处理函数传递额外的参数。例如，若 id 是你要删除那一行的 ID，以下两种方式都可以向事件处理函数传递参数：\n\n```html\n\u003cbutton onClick={(e) =\u003e this.deleteRow(id, e)}\u003eDelete Row\u003c/button\u003e\n\u003cbutton onClick={this.deleteRow.bind(this, id)}\u003eDelete Row\u003c/button\u003e\n```\n\n上述两种方式是等价的，分别通过箭头函数和 Function.prototype.bind 来实现。\n\n在这两种情况下，React 的事件对象 e 会被作为第二个参数传递。如果通过箭头函数的方式，事件对象必须显式的进行传递，而通过 bind 的方式，事件对象以及更多的参数将会被隐式的进行传递。\n\n# 生命周期\n\n以下表格是 Omi 的生命周期：\n\n|生命周期钩子|描述|\n|-|-|\n|install|组件挂载到 DOM 前|\n|installed|组件挂载到 DOM 后|\n|uninstall|组件从 DOM 中移除前|\n|beforeUpdate|update 更新前|\n|updated|update 更新后|\n|beforeRender|render() 之前|\n|receiveProps|父元素重新渲染触发|\n\n举个例子：\n\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cdiv\u003eSeconds: {this.data.seconds}\u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\n    export default class {\n        data = {\n            seconds: 0\n        }\n        tick() {\n            this.data.seconds++\n            this.update()\n        }\n        install() {\n            this.interval = setInterval(() =\u003e this.tick(), 1000)\n        }\n        uninstall() {\n            clearInterval(this.interval)\n        }\n    }\n\u003c/script\u003e\n```\n\n# Update\n\nupdate 方法是内置的重要核心方法，用于更新组件自身。比如:\n\n```js\nthis.update()\n```\n\n也可以传递参数，决定是否在 html 模式下忽略 attributes，强行更新:\n\n```js\nthis.update(true)\n```\n\n当我们组件的 data 值发生变化，我们可以使用`this.update()`更新视图\n\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cdiv\u003e\n        \u003cbutton onClick={this.toggle.bind(this)}\u003eUpdate\u003c/button\u003e\n        \u003cp style={{display:this.data.bool?'block':'none'}}\u003e显示或者隐藏\u003c/p\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\n    export default class {\n        data = {\n            bool: !0\n        }\n        toggle() {\n            this.data.bool = !this.data.bool\n            this.update()\n        }\n    }\n\u003c/script\u003e\n```\n\n# Ref\n\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cdiv\u003e\n        \u003ch1 ref={e=\u003e { this.h1 = e }} onClick={this.onClick}\u003eHello, world!\u003c/h1\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\n    export default class {\n        onClick = (evt) =\u003e {\n            console.log(this.h1)\n        }\n    }\n\u003c/script\u003e\n```\n\n在元素上添加 `ref={e =\u003e { this.anyNameYouWant = e }}` ，然后你就可以 JS 代码里使用 `this.anyNameYouWant` 访问该元素。你可以使用两种方式来提高 update 的性能：\n\n- 提前赋值\n- createRef\n\n\n## 提前赋值\n\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cdiv\u003e\n        \u003ch1 ref={e=\u003e { this.myRef = e }} onClick={this.onClick}\u003eHello, world!\u003c/h1\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\n    export default class {\n        myRef = e =\u003e { this.h1 = e }\n        onClick = (evt) =\u003e {\n            console.log(this.h1)\n        }\n    }\n\u003c/script\u003e\n```\n\n## createRef\n\n你也可以使用 `createRef` 来得到更高的性能，使用前需要引用 `import { createRef } from \"omi\"`:\n\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cdiv\u003e\n        \u003ch1 ref={this.myRef} onClick={this.onClick}\u003eHello, world!\u003c/h1\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\n    import { createRef } from \"omi\";\n    export default class {\n        myRef = createRef()\n        onClick = (evt) =\u003e {\n            console.log(this.myRef.current)\n        }\n    }\n\u003c/script\u003e\n```\n\n# Store 是什么？\n\nStore 是 Omi 内置的中心化数据仓库，他解决和提供了下面问题和能力：\n\n组件树数据共享\n数据变更按需更新依赖的组件\n\n\u003cimg src=\"../images/store.cn.jpg\" /\u003e\n\n# 两份代码完全上手 Store\n\n`path/elements/app/index.omi`下的根组件\n```html\n\u003ctemplate name=\"my-app\"\u003e\n    \u003cdiv\u003e\n        \u003cp\u003e\n            Clicked: {this.use.count} times\n            {' '}\n            \u003cbutton onClick={this.add}\u003e+\u003c/button\u003e\n            {' '}\n            \u003cbutton onClick={this.sub}\u003e-\u003c/button\u003e\n            {' '}\n            \u003cbutton onClick={this.addIfOdd}\u003e\n                Add if odd\n            \u003c/button\u003e\n            {' '}\n            \u003cbutton onClick={this.addAsync}\u003e\n                Add async\n            \u003c/button\u003e\n        \u003c/p\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\n    export default class {\n        static use = [\n            { count: 'count' }\n        ]\n\n        add = () =\u003e this.store.add()\n        sub = () =\u003e this.store.sub()\n\n        addIfOdd = () =\u003e {\n            if (this.use.count % 2 !== 0) {\n                this.store.add()\n            }\n        }\n\n        addAsync = () =\u003e {\n            setTimeout(() =\u003e this.store.add(), 1000)\n        }\n    }\n\u003c/script\u003e\n\u003cstyle lang=\"scss\"\u003e\n    /* CSS */\n    p {\n        color: #58bc58\n    };\n\u003c/style\u003e\n```\n`path/src/index.js`全局的入口文件代码\n```js\nimport { render } from 'omi'\nimport './elements/app'\n\nrender(\u003cmy-app /\u003e, '#root', {\n    data: {\n        count: 0\n    },\n    sub() {\n        this.data.count--\n    },\n    add() {\n        this.data.count++\n    },\n})\n```\n\n- 通过 `static use` 声明依赖的 path\n- store 通过 render 的第三个参数从根节点注入到所有组件。\n\n\nStore 里的 data:\n```js\n{\n  count: 0,\n  arr: ['china', 'tencent'],\n  motto: 'I love omi.',\n  userInfo: {\n    firstName: 'dnt',\n    lastName: 'zhang',\n    age: 18\n  }\n}\n```\n\n下面举一个复杂的 `use` 例子：\n```jsx\nstatic use = [\n  'count', //直接字符串，JSX 里可通过 this.use[0] 访问\n  'arr[0]', //也支持 path，JSX 里可通过 this.use[1] 访问\n  //支持 json\n  {\n    //alias，JSX 里可通过 this.use.reverseMotto 访问\n    reverseMotto: [\n      'motto', //path\n      target =\u003e target.split('').reverse().join('')  //computed\n    ]\n  },\n  { name: 'arr[1]' }, //{ alias: path }，JSX 里可通过 this.use.name 访问\n  {\n    //alias，JSX 里可通过 this.use.fullName 访问\n    fullName: [\n      ['userInfo.firstName', 'userInfo.lastName'], //path array\n      (firstName, lastName) =\u003e firstName + lastName //computed\n    ]\n  },\n]\n```\n\n下面看看 JSX 中使用:\n\n```jsx\n...\n...\n\u003ctemplate\u003e\n    \u003cdiv\u003e\n      \u003cbutton onClick={this.sub}\u003e-\u003c/button\u003e\n      \u003cspan\u003e{this.use[0]}\u003c/span\u003e\n      \u003cbutton onClick={this.add}\u003e+\u003c/button\u003e\n      \u003cdiv\u003e\n        \u003cspan\u003e{this.use[1]}\u003c/span\u003e\n        \u003cbutton onClick={this.rename}\u003erename\u003c/button\u003e\n      \u003c/div\u003e\n      \u003cdiv\u003e{this.use.reverseMotto}\u003c/div\u003e\u003cbutton onClick={this.changeMotto}\u003echange motto\u003c/button\u003e\n      \u003cdiv\u003e{this.use.name}\u003c/div\u003e\n      \u003cdiv\u003e{this.use[3]}\u003c/div\u003e\n      \u003cdiv\u003e\n        {this.use.fullName}\n        \u003cbutton onClick={this.changeFirstName}\u003echange first name\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n...\n...\n```\n\n如果不带有 alias ，你也可以直接通过 `this.store.data.xxx` 访问。\n\n\n当 `store.data` 发生变化，依赖变更数据的组件会进行更新，举例说明 Path 命中规则:\n\n| Proxy Path(由数据更改产生) | static use 中的 path | 是否更新 |\n| ---------- | ---------- | -------- |\n| abc        | abc        | 更新     |\n| abc[1]     | abc        | 更新     |\n| abc.a      | abc        | 更新     |\n| abc        | abc.a      | 不更新   |\n| abc        | abc[1]     | 不更新   |\n| abc        | abc[1].c   | 不更新   |\n| abc.b      | abc.b      | 更新     |\n\n以上只要命中一个条件就可以进行更新！\n\n总结： 只要注入组件的 path 等于 use 里声明 或者在 use 里声明的其中 path 子节点下就会进行更新！\n\n# CSS\n\n这里说的是 props 的 css，而不是 static css，它提供了修改 shadow dom 内部 scoped style 的能力。\n\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cdiv\u003e\n        \u003ch1\u003eLook at my color!\u003c/h1\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\n    export default class {\n        static css = `h1{\n            color: red;\n        }`\n    }\n\u003c/script\u003e\n```\n\n上面的 `my-element` 的 h1 标签颜色是红色。有什么办法修改吗？\n\n```html\n\u003ctemplate name=\"component-name\"\u003e\n    \u003cdiv onClick={this.onClick}\u003e\n        \u003cmy-element css={this.myCSS} /\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\n    export default class {\n        myCSS = `\n            h1{\n                color: green;\n            }\n        `\n        onClick = () =\u003e {\n            //动态修改\n            this.myCSS = `\n                h1{\n                    color: blue;\n                }\n            `\n            this.update()\n        }\n    }\n\u003c/script\u003e\n```\n\n而且还可以通过下面的方式保证一定能够修改：\n\n```css\ncolor: blue!important;\n```\n\n# 高阶组件\n\n如果您用过 React，相信对高阶组件肯定不陌生，高阶组件（HOC）是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分，它是一种基于 React 的组合特性而形成的设计模式。\n\n具体而言，高阶组件是参数为组件，返回值为新组件的函数。\n```js\nconst EnhancedComponent = higherOrderComponent(WrappedComponent);\n```\n组件是将 props 转换为 UI，而高阶组件是将组件转换为另一个组件。\n\nHOC 在 React 的第三方库中很常见，例如 Redux 的 connect。\n\n下面这个例子是是在组件中使用 Redux 高阶组件\n```html\n\u003ctemplate name=\"Component-name\"\u003e\n    \u003cdiv\u003e\u003cp\u003e{this.state.title}\u003c/p\u003e\u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\n    import { connect } from 'react-redux';\n    export default connect((state) =\u003e {\n        return state\n    })(class {\n        constructor(props) {\n            super(props)\n            this.state = {\n                title: \"react\"\n            }\n        }\n    })\n\u003c/script\u003e\n\u003cstyle\u003e\n    p {color: #58bc58;}\n\u003c/style\u003e\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwscats%2Fomil","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwscats%2Fomil","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwscats%2Fomil/lists"}