├── .github ├── dependabot.yml ├── labeler.yml └── workflows │ ├── gh-pages.yaml │ └── label.yml ├── .gitignore ├── .hugo_build.lock ├── LICENSE ├── README.md ├── booklist.md ├── content ├── _index.md └── docs │ ├── _index.md │ ├── framework │ ├── _index.md │ ├── angular │ │ ├── 01_overview.md │ │ ├── 02_component.md │ │ ├── 03_communication.md │ │ ├── 04_lifecycle.md │ │ ├── 05_directive.md │ │ ├── 06_pipe.md │ │ ├── 07_di.md │ │ ├── 08_service.md │ │ ├── 09_form.md │ │ ├── 10_custom_form.md │ │ ├── 11_router.md │ │ ├── 12_http.md │ │ ├── 13_other.md │ │ └── _index.md │ ├── rxjs │ │ ├── 01_overview.md │ │ ├── 02_operators.md │ │ ├── 03_subject.md │ │ ├── 04_practice.md │ │ └── _index.md │ └── vue │ │ ├── _index.md │ │ ├── basic │ │ ├── README.md │ │ ├── command.md │ │ ├── component.md │ │ ├── component_pack.md │ │ ├── computed.md │ │ ├── custom_command.md │ │ ├── event.md │ │ ├── form.md │ │ ├── mixin.md │ │ ├── plugin.md │ │ ├── render.md │ │ ├── vue_router.md │ │ └── vuex.md │ │ ├── deep │ │ ├── README.md │ │ ├── responsive.md │ │ └── ssr.md │ │ └── impl │ │ └── README.md │ ├── guide │ ├── _index.md │ ├── html │ │ ├── 01_tags.md │ │ ├── 02_css.md │ │ ├── 03_layout.md │ │ ├── 04_animation.md │ │ ├── 05_other.md │ │ ├── 06_selector.md │ │ ├── 07_responsive.md │ │ ├── 08_svg.md │ │ ├── 09_chrome.md │ │ └── _index.md │ ├── javascript │ │ ├── _index.md │ │ ├── decorator.md │ │ ├── dom.md │ │ ├── event_loop.md │ │ ├── model.md │ │ └── web.md │ ├── performance │ │ └── _index.md │ ├── security │ │ ├── _index.md │ │ └── referer.md │ └── typescript │ │ ├── 01_js_c.md │ │ ├── 02_env.md │ │ ├── 03_base_type.md │ │ ├── 04_complex_type.md │ │ ├── 05_module.md │ │ ├── 06_namespace.md │ │ ├── 07_lint.md │ │ └── _index.md │ ├── node │ ├── _index.md │ └── npm.md │ └── practice │ ├── _index.md │ ├── css │ └── _index.md │ ├── cypress │ └── _index.md │ ├── less │ └── 01_getting_started.md │ ├── vite │ └── _index.md │ ├── vuecli3 │ ├── README.md │ ├── _index.md │ ├── env.md │ ├── expand.md │ ├── guide.md │ ├── intergration.md │ ├── multi_pages.md │ ├── multi_router.md │ ├── npm_package.md │ ├── spa_config.md │ └── webpack.md │ └── webpack │ └── _index.md ├── deploy.sh ├── development.md ├── examples ├── css │ ├── animation │ │ ├── demo1 │ │ │ ├── demo.html │ │ │ └── luoxuanwan.png │ │ └── demo2 │ │ │ ├── demo.html │ │ │ └── luoxuanwan.png │ ├── filter │ │ ├── background.jpeg │ │ └── demo.html │ ├── flex │ │ └── demo.html │ ├── grid │ │ ├── demo1 │ │ │ └── index.html │ │ ├── demo2 │ │ │ └── index.html │ │ ├── demo3 │ │ │ └── index.html │ │ ├── demo4 │ │ │ └── index.html │ │ ├── demo5 │ │ │ └── index.html │ │ └── demo6 │ │ │ └── index.html │ ├── nav │ │ ├── demo │ │ │ └── index.html │ │ └── learn │ │ │ └── index.html │ ├── overflow │ │ └── hidden │ │ │ ├── demo1 │ │ │ └── index.html │ │ │ ├── demo2 │ │ │ └── index.html │ │ │ ├── demo3 │ │ │ └── index.html │ │ │ ├── demo4 │ │ │ ├── book.jpeg │ │ │ └── index.html │ │ │ └── demo5 │ │ │ └── index.html │ ├── override │ │ ├── index.html │ │ └── style.css │ ├── position │ │ ├── absolute │ │ │ └── demo1 │ │ │ │ └── index.html │ │ └── sticky │ │ │ ├── demo1 │ │ │ └── index.html │ │ │ └── demo2 │ │ │ └── index.html │ ├── responsive │ │ ├── demo1 │ │ │ └── index.html │ │ ├── demo2 │ │ │ └── index.html │ │ ├── demo3 │ │ │ └── index.html │ │ ├── demo4 │ │ │ └── index.html │ │ ├── demo5 │ │ │ └── index.html │ │ ├── demo6 │ │ │ └── index.html │ │ ├── demo7 │ │ │ └── index.html │ │ └── demo8 │ │ │ ├── index.html │ │ │ ├── large-1920.jpg │ │ │ ├── medium-1280.jpg │ │ │ └── small-640.jpg │ ├── selector │ │ ├── demo1 │ │ │ └── index.html │ │ ├── demo2 │ │ │ └── index.html │ │ ├── demo3 │ │ │ └── index.html │ │ ├── demo4 │ │ │ └── index.html │ │ ├── demo5 │ │ │ └── index.html │ │ └── demo6 │ │ │ └── index.html │ ├── shadow │ │ ├── chrome.webp │ │ └── index.html │ └── transform │ │ ├── demo1 │ │ └── index.html │ │ ├── demo2 │ │ └── index.html │ │ ├── demo3 │ │ └── index.html │ │ ├── demo4 │ │ └── index.html │ │ └── demo5 │ │ └── index.html ├── es6 │ ├── .babelrc │ ├── decorator │ │ ├── attr_decorator.js │ │ ├── class_decorator.js │ │ ├── function_decorator.js │ │ └── order_decorator.js │ └── index.js ├── mvue │ ├── README.md │ ├── index.html │ └── js │ │ ├── compile.js │ │ ├── mVue.js │ │ ├── observer.js │ │ └── watcher.js ├── ts-base │ ├── package.json │ ├── src │ │ ├── 01_datatype.ts │ │ ├── 02_enum.ts │ │ ├── 03_interface.ts │ │ ├── 04_function.ts │ │ ├── 05_class.ts │ │ ├── 06_class_interface.ts │ │ ├── 07_generics.ts │ │ ├── 08_inference.ts │ │ ├── 09_compatible.ts │ │ ├── 10_guard.ts │ │ ├── 11_advance.ts │ │ ├── index.ts │ │ └── tmpl │ │ │ └── index.html │ └── tsconfig.json └── ts-project │ ├── .eslintrc.json │ ├── build │ ├── webpack.base.config.js │ ├── webpack.config.js │ ├── webpack.dev.config.js │ └── webpack.pro.config.js │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── es6 │ │ ├── a.ts │ │ ├── b.ts │ │ ├── c.ts │ │ └── d.ts │ ├── index.ts │ ├── libs │ │ ├── global.lib.d.ts │ │ ├── global.lib.js │ │ ├── index.ts │ │ ├── module.lib.d.ts │ │ ├── module.lib.js │ │ ├── umd.lib.d.ts │ │ └── umd.lib.js │ ├── merge │ │ └── merge.ts │ ├── namespace │ │ ├── a.js │ │ ├── a.ts │ │ ├── b.js │ │ └── b.ts │ ├── node │ │ ├── a.ts │ │ ├── b.ts │ │ └── c.ts │ ├── reference │ │ ├── new │ │ │ ├── src │ │ │ │ ├── client │ │ │ │ │ ├── index.ts │ │ │ │ │ └── tsconfig.json │ │ │ │ ├── common │ │ │ │ │ ├── index.ts │ │ │ │ │ └── tsconfig.json │ │ │ │ └── server │ │ │ │ │ ├── index.ts │ │ │ │ │ └── tsconfig.json │ │ │ ├── test │ │ │ │ ├── client.test.ts │ │ │ │ ├── server.test.ts │ │ │ │ └── tsconfig.json │ │ │ └── tsconfig.json │ │ └── old │ │ │ ├── src │ │ │ ├── client │ │ │ │ └── index.ts │ │ │ ├── common │ │ │ │ └── index.ts │ │ │ └── server │ │ │ │ └── index.ts │ │ │ ├── test │ │ │ ├── client.test.ts │ │ │ └── server.test.ts │ │ │ └── tsconfig.json │ ├── rxjs │ │ ├── concat-map.ts │ │ ├── concat.ts │ │ ├── delay.ts │ │ ├── every.ts │ │ ├── exhaust-all.ts │ │ ├── filter.ts │ │ ├── last.ts │ │ ├── map.ts │ │ ├── merge-map.ts │ │ ├── merge.ts │ │ ├── range.ts │ │ ├── reduce.ts │ │ ├── retry.ts │ │ ├── skip.ts │ │ ├── switch-map.ts │ │ ├── switch.ts │ │ └── take.ts │ └── tmpl │ │ └── index.html │ └── tsconfig.json ├── go.mod ├── go.sum ├── hugo.yaml ├── i18n └── zh-cn.yaml └── layouts └── partials └── custom └── head-end.html /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | assignees: 5 | - "shipengqi" 6 | directory: "/" 7 | schedule: 8 | interval: "daily" 9 | time: "08:00" 10 | labels: 11 | - "dependencies" 12 | commit-message: 13 | prefix: "chore" 14 | include: "scope" -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | # Works with https://github.com/actions/labeler/ 2 | # Below this line, the keys are labels to be applied, and the values are the file globs to match against. 3 | 4 | framework: 5 | - changed-files: 6 | - any-glob-to-any-file: content/docs/framework/* 7 | 8 | basic: 9 | - changed-files: 10 | - any-glob-to-any-file: content/docs/guide/* 11 | 12 | practice: 13 | - changed-files: 14 | - any-glob-to-any-file: "content/docs/practice/*" 15 | 16 | node: 17 | - changed-files: 18 | - any-glob-to-any-file: content/docs/node/* 19 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yaml: -------------------------------------------------------------------------------- 1 | name: GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - master # Set a branch to deploy 7 | paths: 8 | - 'content/**' 9 | pull_request: 10 | paths: 11 | - 'content/**' 12 | jobs: 13 | deploy: 14 | runs-on: ubuntu-latest 15 | concurrency: 16 | group: ${{ github.workflow }}-${{ github.ref }} 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | submodules: true # Fetch Hugo themes (true OR recursive) 21 | fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod 22 | 23 | - name: Setup Hugo 24 | uses: peaceiris/actions-hugo@v3 25 | with: 26 | hugo-version: '0.139.0' 27 | extended: true 28 | 29 | - name: Build 30 | run: hugo --minify 31 | 32 | - name: Deploy 33 | uses: peaceiris/actions-gh-pages@v4 34 | if: github.ref == 'refs/heads/master' 35 | with: 36 | github_token: ${{ secrets.PAT }} 37 | publish_dir: ./public 38 | -------------------------------------------------------------------------------- /.github/workflows/label.yml: -------------------------------------------------------------------------------- 1 | # This workflow will triage pull requests and apply a label based on the 2 | # paths that are modified in the pull request. 3 | # 4 | # To use this workflow, you will need to set up a .github/labeler.yml 5 | # file with configuration. For more information, see: 6 | # https://github.com/actions/labeler 7 | 8 | name: Labeler 9 | on: [pull_request_target] 10 | 11 | jobs: 12 | label: 13 | 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: read 17 | pull-requests: write 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | - uses: actions/labeler@v5 22 | with: 23 | repo-token: "${{ secrets.PAT }}" 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # idea 2 | .idea 3 | 4 | # vs code 5 | .vscode 6 | # logs 7 | *.log 8 | 9 | # node_modules 10 | node_modules 11 | 12 | # dist 13 | docs2/.vuepress/dist 14 | 15 | public 16 | resources 17 | -------------------------------------------------------------------------------- /.hugo_build.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipengqi/frontend-learn/73b0f3852f0f5ab837ac6a46b78ce6c828c48a4e/.hugo_build.lock -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Shi PengQi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # frontend-learn 2 | 3 | Frontend learning ... 4 | 5 | ## 主要内容 6 | 7 | ### 🍚 前端基础 8 | 9 | HTML,CSS,Javasript,Typescript。 10 | 11 | ### ⚡ 前端框架 12 | 13 | Vue,Angular 等。 14 | 15 | ### 🛠️ 前端工程化实践 16 | 17 | 记录了 webpack,vite 等工具的使用和原理。 18 | 19 | ### 🔍 Node.js 底层原理 20 | Node.js 底层内存管理,GC,调度器的实现原理。浏览器的原理。 21 | 22 | ## 开始阅读 23 | 24 | - [GitHub 在线](https://shipengqi.github.io/frontend-learn) -------------------------------------------------------------------------------- /booklist.md: -------------------------------------------------------------------------------- 1 | # Book List 2 | 3 | ## Basic 4 | 5 | - [SVG](https://www.youtube.com/watch?v=plvn3hw4uGQ&list=PLtKLDYAuxSmPKGycCOafdmK73iM8pqlx-&index=64) 6 | - 7 | - 8 | - 极客时间 重学前端 9 | - [performance](https://github.com/barretlee/performance-column) 10 | - [web前端面试 - 面试官系列](https://vue3js.cn/interview/) 11 | - https://github.com/stephentian/33-js-concepts 12 | - https://github.com/javascript-tutorial/zh.javascript.info -------------------------------------------------------------------------------- /content/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Frontend Learning 3 | --- 4 | 5 | ## 主要内容 6 | 7 | {{< cards cols="2" >}} 8 | {{< card link="/frontend-learn/docs/guide" title="前端基础" icon="code" subtitle="HTML,CSS,Javascript,Typescript。" >}} 9 | {{< card link="/frontend-learn/docs/framework" title="前端框架" icon="color-swatch" subtitle="Vue,Angular 等。" >}} 10 | {{< card link="/frontend-learn/docs/practice" title="前端工程实践" icon="cube" subtitle="记录 Vite,Cypress 等工具的使用和原理。" >}} 11 | {{< card link="/frontend-learn/docs/node" title="Node.js 底层原理" icon="beaker" subtitle="Node.js 底层原理、浏览器的原理。" >}} 12 | {{< /cards >}} 13 | 14 | ## 互动与勘误 15 | 16 | 阅读过程中遇到任何问题 (包括但不限于语法语病、错别字、文章结构、配图等),可以直接向仓库提交 PR 或 Issues, 我会尽量在第一时间处理。 17 | 18 | 该文档仅供学习使用,如有侵权请联系我删除。感谢!😄 19 | -------------------------------------------------------------------------------- /content/docs/_index.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /content/docs/framework/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ⚡ 前端框架 3 | weight: 2 4 | --- 5 | -------------------------------------------------------------------------------- /content/docs/framework/angular/01_overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 概述 3 | weight: 1 4 | --- 5 | 6 | ## NgModule 7 | 8 | Angular 应用是由一个个模块组成的,称作 **NgModule**。 9 | 10 | NgModule 是一组相关功能的集合,专注于某个应用领域,可以将组件和一组相关代码关联起来,是应用组织代码结构的一种方式。 11 | 12 | NgModule 可以从其它 NgModule 中导入功能,并允许导出它们自己的功能供其它 NgModule 使用。 13 | 14 | NgModule 是由 NgModule 装饰器函数装饰的类。 15 | 16 | ```javascript 17 | import { BrowserModule } from '@angular/platform-browser'; 18 | import { NgModule } from '@angular/core'; 19 | 20 | @NgModule({ 21 | imports: [ 22 | BrowserModule 23 | ] 24 | }) 25 | export class AppModule { } 26 | ``` 27 | 28 | ### `@NgModule` 元数据 29 | 30 | NgModule 是一个带有 `@NgModule()` 装饰器的类。`@NgModule()` 元数据对象比较重要的属性: 31 | 32 | - `declarations` 属于当前 NgModule 的组件、指令、管道。 33 | - `exports` 导出当前 NgModule 的组件、指令、管道的列表。 34 | - `imports` 导入的 NgModule、组件、指令、管道的列表。 35 | - `providers` 当前 NgModule 所需的服务。 36 | - `bootstrap` 应用的主视图,称为根组件。它是应用中所有其它视图的宿主。只有 **root module** 才应该设置这个 `bootstrap` 属性。 37 | 38 | ## 组件 39 | 40 | 组件用来描述用户界面,它由三部分组成,组件类、组件模板、组件样式,它们可以被集成在组件类文件中,也可以是三个不同的文件。 41 | 42 | 组件类用来编写和组件直接相关的界面逻辑,在组件类中要关联该组件的组件模板和组件样式。 43 | 44 | 组件模板用来编写组件的 HTML 结构,通过数据绑定标记将应用中数据和 DOM 进行关联。 45 | 46 | 组件样式用来编写组件的组件的外观,组件样式可以采用 CSS、LESS、SCSS、Stylus 47 | 48 | 在 Angular 应用中至少要有一个根组件,用于应用程序的启动。 49 | 50 | 组件类是由 Component 装饰器函数装饰的类。 51 | 52 | ```javascript 53 | import { Component } from "@angular/core" 54 | 55 | @Component({ 56 | selector: "app-root", 57 | templateUrl: "./app.component.html", 58 | styleUrls: ["./app.component.css"] 59 | }) 60 | export class AppComponent { 61 | title = "angular-test" 62 | } 63 | ``` 64 | 65 | ## 服务 66 | 67 | 服务用于放置和特定组件无关并希望跨组件共享的数据或逻辑。 68 | 69 | 服务出现的目的在于解耦组件类中的代码,是组件类中的代码干净整洁。 70 | 71 | 服务是由 `Injectable` 装饰器装饰的类。 72 | 73 | ```javascript 74 | import { Injectable } from '@angular/core'; 75 | 76 | @Injectable({}) 77 | export class AppService { } 78 | ``` 79 | 80 | 服务的实例对象由 Angular 框架中内置的依赖注入系统创建和维护。服务是依赖需要被注入到组件中。 81 | 82 | 在组件中需要通过 `constructor` 构造函数的参数来获取服务的实例对象。 83 | 84 | 在组件中获取服务实例对象,写法如下。 85 | 86 | ```javascript 87 | import { AppService } from "./AppService" 88 | 89 | export class AppComponent { 90 | constructor ( 91 | private appService: AppService 92 | ) {} 93 | } 94 | ``` 95 | 96 | Angular 会根据你指定的服务的类型来传递你想要使用的服务实例对象。 97 | 98 | 在 Angular 中服务被设计为单例模式,这也正是为什么服务可以被用来在组件之间共享数据和逻辑的原因。 99 | -------------------------------------------------------------------------------- /content/docs/framework/angular/05_directive.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 指令 3 | weight: 5 4 | --- 5 | 6 | 指令是 Angular 提供的操作 DOM 的途径。指令分为属性指令和结构指令。 7 | 8 | 属性指令:修改现有元素的外观或行为,`selector` 使用 `[]` 包裹。 9 | 10 | 结构指令:增加、删除 DOM 节点以修改布局,使用 `*` 作为指令前缀。 11 | 12 | ## 内置指令 13 | 14 | ### *ngIf 15 | 16 | 根据条件渲染 DOM 节点或移除 DOM 节点。 17 | 18 | ```html 19 |
没有更多数据
20 | ``` 21 | 22 | ```html 23 |
24 | 课程列表 25 | 没有更多数据 26 | ``` 27 | 28 | ### [hidden] 29 | 30 | 根据条件显示 DOM 节点或隐藏 DOM 节点。 31 | 32 | ```html 33 |
课程列表
34 |
没有更多数据
35 | ``` 36 | 37 | ### *ngFor 38 | 39 | 遍历数据生成HTML结构 40 | 41 | ```javascript 42 | interface List { 43 | id: number 44 | name: string 45 | age: number 46 | } 47 | 48 | list: List[] = [ 49 | { id: 1, name: "张三", age: 20 }, 50 | { id: 2, name: "李四", age: 30 } 51 | ] 52 | ``` 53 | 54 | ```html 55 |
  • 65 |
  • 66 | ``` 67 | 68 | #### trackBy 69 | 70 | 在 Angular 中遍历数组时,会用到 `ngFor` 指令,如果数组中的数据改变了(新数组替换旧数组),Angular 会删除与数据关联的所有 DOM 元素,然后再次创建它们。这意味着将有很多 DOM 操作。 71 | 72 | 使用 `*ngFor` 的 `trackBy` 属性,Angular 可以仅更改和重新渲染那些已更改的条目,而不是重新加载整个条目列表。 73 | 74 | ```typescript 75 | trackByItems(index: number, item: Item): number { 76 | return item.id; // 只需要返回一个唯一标识就好了 77 | } 78 | ``` 79 | 80 | ```html 81 |
    82 | ({{ item.id }}) {{ item.name }} 83 |
    84 | ``` 85 | 86 | 使用 `trackBy` 的好处是自定义返回跟踪结果,以比对上次的跟踪结果,如果不一样,那么就刷新变化的页面实例(减少不必要的 Dom 刷新而带来性能的提升)。 87 | 88 | ### 自定义指令 89 | 90 | 需求:为元素设置默认背景颜色,鼠标移入时的背景颜色以及移出时的背景颜色。 91 | 92 | ```html 93 |
    Hello Angular
    94 | ``` 95 | 96 | ```javascript 97 | import { AfterViewInit, Directive, ElementRef, HostListener, Input } from "@angular/core" 98 | 99 | // 接收参的数类型 100 | interface Options { 101 | bgColor?: string 102 | } 103 | 104 | @Directive({ 105 | selector: "[appHover]" 106 | }) 107 | export class HoverDirective implements AfterViewInit { 108 | // 接收参数 109 | @Input("appHover") appHover: Options = {} 110 | // 要操作的 DOM 节点 111 | element: HTMLElement 112 | // 获取要操作的 DOM 节点 113 | constructor(private elementRef: ElementRef) { 114 | this.element = this.elementRef.nativeElement 115 | } 116 | // 组件模板初始完成后设置元素的背景颜色 117 | ngAfterViewInit() { 118 | this.element.style.backgroundColor = this.appHover.bgColor || "skyblue" 119 | } 120 | // 为元素添加鼠标移入事件 121 | @HostListener("mouseenter") enter() { 122 | this.element.style.backgroundColor = "pink" 123 | } 124 | // 为元素添加鼠标移出事件 125 | @HostListener("mouseleave") leave() { 126 | this.element.style.backgroundColor = "skyblue" 127 | } 128 | } 129 | ``` 130 | -------------------------------------------------------------------------------- /content/docs/framework/angular/06_pipe.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 管道 3 | weight: 6 4 | --- 5 | 6 | 管道的作用是转换组件模板数据。要在 HTML 模板中指定值的转换方式,使用管道操作符 `|`。 7 | 8 | ## 内置管道 9 | - `date` 日期格式化 10 | - `currency` 货币格式化 11 | - `uppercase` 转大写 12 | - `lowercase` 转小写 13 | - `json` 格式化 json 数据 14 | 15 | ```html 16 | {{ date | date: "yyyy-MM-dd" }} 17 | {{ num | currency: "¥" }} // ¥{num} 18 | ``` 19 | 20 | ## 自定义管道 21 | 22 | 需求:指定字符串不能超过规定的长度 23 | 24 | ```javascript 25 | // summary.pipe.ts 26 | import { Pipe, PipeTransform } from '@angular/core'; 27 | 28 | @Pipe({ 29 | name: 'summary' 30 | }); 31 | export class SummaryPipe implements PipeTransform { 32 | transform (value: string, limit?: number) { 33 | if (!value) return null; 34 | let actualLimit = (limit) ? limit : 50; 35 | return value.substr(0, actualLimit) + '...'; 36 | } 37 | } 38 | ``` 39 | 40 | ```typescript 41 | // app.module.ts 42 | import { SummaryPipe } from './summary.pipe' 43 | @NgModule({ 44 | declarations: [ 45 | SummaryPipe 46 | ] 47 | }); 48 | ``` 49 | 50 | 使用管道: 51 | 52 | ```html 53 |
    {{ 'test' | summary: 100 }}
    54 | ``` 55 | 56 | 管道传参,使用 `:` 分隔,如果有多个参数,每个参数之间用 `:` 分隔(`| summary: 100: 200`)。 57 | -------------------------------------------------------------------------------- /content/docs/framework/angular/08_service.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 服务 3 | weight: 8 4 | --- 5 | 6 | ## 创建服务 7 | 8 | ```javascript 9 | import { Injectable } from '@angular/core'; 10 | 11 | @Injectable({ 12 | providedIn: 'root' 13 | }) 14 | export class TestService { } 15 | ``` 16 | 17 | 使用: 18 | 19 | ```javascript 20 | export class AppComponent { 21 | // 这里的 TestService 实际上就是获取示例对象的 Token(唯一标识) 22 | constructor (private _test: TestService) {} 23 | } 24 | ``` 25 | 26 | ## 服务的作用域 27 | 28 | 使用服务可以轻松实现跨模块跨组件共享数据,这取决于服务的作用域。 29 | 30 | 1. 在根注入器中注册服务,所有模块使用同一个服务实例对象。 31 | 32 | ```javascript 33 | import { Injectable } from '@angular/core'; 34 | 35 | @Injectable({ 36 | providedIn: 'root' 37 | }) 38 | 39 | export class CarListService { 40 | } 41 | ``` 42 | 43 | 2. 在模块级别注册服务,该模块中的所有组件使用同一个服务实例对象。 44 | 45 | 新语法: 46 | ```javascript 47 | import { Injectable } from '@angular/core'; 48 | import { CarModule } from './car.module'; 49 | 50 | @Injectable({ 51 | providedIn: CarModule, 52 | }) 53 | 54 | export class CarListService { 55 | } 56 | ``` 57 | 58 | 老语法: 59 | ```javascript 60 | import { CarListService } from './car-list.service'; 61 | 62 | @NgModule({ 63 | providers: [CarListService], 64 | }) 65 | export class CarModule { 66 | } 67 | ``` 68 | 69 | 3. 在组件级别注册服务,该组件及其子组件使用同一个服务实例对象。 70 | 71 | ```javascript 72 | import { Component } from '@angular/core'; 73 | import { CarListService } from '../car-list.service.ts' 74 | 75 | @Component({ 76 | selector: 'app-car-list', 77 | templateUrl: './car-list.component.html', 78 | providers: [CarListService] 79 | }) 80 | ``` 81 | 82 | {{< callout type="info" >}} 83 | `providedIn: 'root'` 只是指明服务是全局单例的,这意味着整个应用中只有一个服务实例,通常用于跨多个组件和服务共享的场景。 84 | 但是,`providedIn: 'root'` 仍然可以在组件或模块的 `providers` 数组中重新提供该服务,这样它在该组件或模块中会有一个单独的实例, 85 | 其他地方仍然会使用全局单例实例。 86 | {{< /callout >}} 87 | 88 | -------------------------------------------------------------------------------- /content/docs/framework/angular/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Angular 3 | weight: 5 4 | --- 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /content/docs/framework/rxjs/04_practice.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 实践 3 | weight: 4 4 | --- 5 | 6 | ## 优雅的资源释放 7 | 8 | 对于无限值必须要取消订阅,反之可以不需要。例如监听 DOM 元素的事件: 9 | 10 | ```typescript 11 | Observable.fromEvent(node, 'input') 12 | .subscribe(value => { 13 | console.log(value); 14 | }); 15 | ``` 16 | 17 | 因为如果不取消订阅,事件所关联的方法会一直被占用,导致内存泄露。 18 | 19 | ### 传统方式 20 | 21 | ```typescript 22 | @Component({ 23 | selector: 'app-demo', 24 | template: ` 25 |
    Hello, world!
    26 | ` 27 | }) 28 | export class GeneralComponent implements OnDestroy { 29 | private readonly _destroy$ = new Subject(); 30 | 31 | private timer; 32 | 33 | constructor() { 34 | this.timer = interval(1000).pipe(takeUntil(this._destroy$)).subscribe(console.log); 35 | } 36 | 37 | ngOnDestroy() { 38 | this._destroy$.next(); 39 | this._destroy$.complete(); 40 | } 41 | } 42 | ``` 43 | 44 | 上面的在组件中定义了一个私有变量 `_destroy$`,是一个 `Subject` 对象,用于在组件销毁时发出信号以释放资源。通过 `takeUntil(this._destroy$)` 操作符来限制 `Observable` 的生命周期,在 `_destroy$` 发出信号时停止发出值。 45 | 46 | 这种方式虽然使用了 `takeUntil` 来限制 `Observable` 的生命周期,但是仍然需要在 `ngOnDestroy` 钩子中手动调用 `_destroy$.next()` 和 `_destroy$.complete()` 来确保释放资源。容易遗漏而引发错误。 47 | 48 | 49 | ### 继承方式 50 | 51 | ```typescript 52 | @Directive() 53 | export class BaseComponent implements OnDestroy { 54 | // protected, not private 55 | protected readonly _destroy$ = new Subject(); 56 | 57 | ngOnDestroy() { 58 | this._destroy$.next(); 59 | this._destroy$.complete(); 60 | } 61 | } 62 | 63 | @Component({ 64 | selector: 'app-demo', 65 | template: ` 66 |
    Hello, world!
    67 | ` 68 | }) 69 | export class GeneralComponent extends BaseComponent { 70 | constructor() { 71 | super(); 72 | 73 | interval(1000).pipe(takeUntil(this._destroy$)).subscribe(console.log); 74 | } 75 | } 76 | ``` 77 | 78 | 继承方式可以减少了在每个组件中手动管理资源释放的重复性工作。但是导致了派生组件与基类紧密耦合。一个派生类只能继承自一个基类。如果要在不同的组件中共享不同的基础逻辑,就会受到继承单一基类的限制。而且继承方式导致代码的可读性和可维护性下降。 79 | 80 | ### DestroyRef 机制 81 | 82 | ```typescript 83 | function destroyRefFactory() { 84 | const destroy$ = new Subject(); 85 | const destroyRef = inject(DestroyRef); 86 | 87 | destroyRef.onDestroy(() => { 88 | destroy$.next(); 89 | destroy$.complete(); 90 | }) 91 | 92 | return destroy$; 93 | } 94 | 95 | @Component({ 96 | selector: 'app-demo', 97 | template: ` 98 |
    Hello, world!
    99 | ` 100 | }) 101 | export class GeneralComponent implements OnDestroy { 102 | private readonly _destroy$ = destroyRefFactory(); 103 | 104 | constructor() { 105 | interval(1000).pipe(takeUntil(this._destroy$)).subscribe(console.log) 106 | } 107 | } 108 | ``` 109 | 110 | 基于 `DestroyRef` 机制,不需要在组件中手动释放资源。而且不仅限于单一订阅场景,它在多个订阅场景中同样适用。 111 | 112 | ### 自定义操作符 113 | 114 | 基于 `DestroyRef` 机制的实现方式简洁灵活,但是仍然需要在组件中声明 `_destroy$`。通过自定义操作符可以将释放资源的逻辑封装在操作符内部,让代码更加整洁,使资源释放与业务逻辑解耦。 115 | 116 | ```typescript 117 | function takeUntilDestroyed(destroyRef?: DestroyRef): MonoTypeOperatorFunction { 118 | if (!destroyRef) { 119 | destroyRef = inject(DestroyRef); 120 | } 121 | 122 | const destroy$ = new Observable(observer => { 123 | return destroyRef!.onDestroy(() => { 124 | observer.next(); 125 | observer.complete(); 126 | }); 127 | }) 128 | 129 | return (source: Observable) => { 130 | return source.pipe(takeUntil(destroy$)) 131 | } 132 | } 133 | 134 | @Component({ 135 | selector: 'app-demo', 136 | template: ` 137 |
    Hello, world!
    138 | ` 139 | }) 140 | export class GeneralComponent implements OnDestroy { 141 | constructor() { 142 | interval(1000).pipe(takeUntilDestroyed()).subscribe(console.log) 143 | } 144 | } 145 | ``` 146 | 147 | `@angular/core/rxjs-interop` 中已经提供了 `takeUntilDestroyed` 操作符。 148 | 149 | -------------------------------------------------------------------------------- /content/docs/framework/rxjs/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: RxJS 3 | weight: 4 4 | --- 5 | -------------------------------------------------------------------------------- /content/docs/framework/vue/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Vue 3 | weight: 6 4 | draft: true 5 | --- 6 | 7 | # Vue 8 | -------------------------------------------------------------------------------- /content/docs/framework/vue/basic/component_pack.md: -------------------------------------------------------------------------------- 1 | # Vue 如何封装组件 2 | 3 | 封装通用组件必须具备高性能、低耦合的特性,如何封装一个好的组件? 4 | 5 | ## 数据从父组件传入 6 | 7 | 1. 为了低耦合,子组件就应该是无状态的。即使本身会生成数据,也只能在内部使用,不能传递出去。 8 | 2. 应该对`props`传递的参数应该添加一些验证规则 9 | 3. `props`传入的参数,不建议修改,如果必须要修改,建议定义子组件的`data`的一个属性来接收`prop`传入的值。 10 | 如果`prop`只以一种原始的值传入且需要进行转换,可以使用计算属性,比如把`prop`传入的值转成小写`return this.size.trim().toLowerCase()`。 11 | 12 | ## 在父组件处理事件 13 | 14 | 1. 事件的处理方法应当尽量放到父组件中,通用组件本身只作为一个中转,降低耦合性,保证了通用组件中的数据不被污染 15 | 2. 组件内部的一些交互行为,或者处理的数据只在组件内部传递,这时候就不需要用`$emit`了 16 | 17 | ## 留一个`slot` 18 | 19 | 1. 一个通用组件,往往不能够完美的适应所有应用场景,留一个`slot`来让父组件实现一些功能。 20 | 2. 开发通用组件的时候,只要不是独立性很高的组件,建议都留一个`slot`,即使还没想好用来干什么。 21 | 22 | ## 不要依赖 Vuex 23 | 24 | 1. Vuex 用来做组件状态管理的,类似一个全局变量,会一直占用内存,除非刷新页面,不建议用来非父子组件通信。 25 | 2. Vuex 在写入数据庞大的 state 的时候,就会产生内存泄露 26 | 3. 如果刷新页面时需要保留数据,可以通过 web storage 保存。 27 | 28 | ## 合理运用 scoped 29 | 在编写组件的时候,可以在` 34 | 35 | 36 |
    37 | 38 |
    39 | 40 | 51 | 52 | -------------------------------------------------------------------------------- /examples/css/animation/demo1/luoxuanwan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipengqi/frontend-learn/73b0f3852f0f5ab837ac6a46b78ce6c828c48a4e/examples/css/animation/demo1/luoxuanwan.png -------------------------------------------------------------------------------- /examples/css/animation/demo2/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Animation Demo 6 | 123 | 124 | 125 |
    126 | 127 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /examples/css/animation/demo2/luoxuanwan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipengqi/frontend-learn/73b0f3852f0f5ab837ac6a46b78ce6c828c48a4e/examples/css/animation/demo2/luoxuanwan.png -------------------------------------------------------------------------------- /examples/css/filter/background.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipengqi/frontend-learn/73b0f3852f0f5ab837ac6a46b78ce6c828c48a4e/examples/css/filter/background.jpeg -------------------------------------------------------------------------------- /examples/css/filter/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Filter And Backdrop Filter 7 | 37 | 38 | 39 |
    40 |
    Normal
    41 |
    Filter
    42 |
    Backdrop Filter
    43 |
    44 | 45 | -------------------------------------------------------------------------------- /examples/css/flex/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Flex Layout Demo 6 | 31 | 32 | 33 |
    34 |
    35 |
    36 |
    37 |
    38 | 39 | 40 | -------------------------------------------------------------------------------- /examples/css/grid/demo1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Grid Demo 7 | 25 | 26 | 27 |
    28 | 29 | -------------------------------------------------------------------------------- /examples/css/grid/demo2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Grid Demo 7 | 24 | 25 | 26 |
    27 |
    111
    28 |
    222
    29 |
    333
    30 |
    444
    31 |
    32 | 33 | -------------------------------------------------------------------------------- /examples/css/grid/demo3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Grid Sort Demo 7 | 51 | 52 | 53 |
    54 |
    1
    55 |
    2
    56 |
    3
    57 |
    4
    58 |
    5
    59 |
    6
    60 |
    7
    61 |
    8
    62 |
    9
    63 |
    64 | 65 | 66 | -------------------------------------------------------------------------------- /examples/css/grid/demo4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Grid Sort Demo 7 | 56 | 57 | 58 |
    59 |
    1
    60 |
    2
    61 |
    3
    62 |
    4
    63 |
    5
    64 |
    6
    65 |
    7
    66 |
    8
    67 |
    9
    68 |
    69 | 70 | 71 | -------------------------------------------------------------------------------- /examples/css/grid/demo5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Grid Sort Demo 7 | 52 | 53 | 54 |
    55 |
    1
    56 |
    2
    57 |
    3
    58 |
    4
    59 |
    5
    60 |
    6
    61 |
    7
    62 |
    8
    63 |
    9
    64 |
    65 | 66 | 67 | -------------------------------------------------------------------------------- /examples/css/grid/demo6/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Grid Sort Demo 7 | 56 | 57 | 58 |
    59 |
    1
    60 |
    2
    61 |
    3
    62 | 63 | 64 | 65 | 66 | 67 | 68 |
    69 | 70 | 71 | -------------------------------------------------------------------------------- /examples/css/nav/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Nav Learn Demo 6 | 93 | 94 | 95 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /examples/css/nav/learn/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Nav Learn Demo 6 | 54 | 55 | 56 |
    57 |
    58 |
    59 | 60 | 61 | -------------------------------------------------------------------------------- /examples/css/overflow/hidden/demo1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Overflow Demo 7 | 35 | 36 | 37 |
    38 |
    39 |
    40 |
    41 |
    42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/css/overflow/hidden/demo2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Overflow Hidden Demo 7 | 34 | 35 | 36 |
    37 |
    38 |
    39 |
    40 |
    41 | 42 | 43 | -------------------------------------------------------------------------------- /examples/css/overflow/hidden/demo3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Overflow Hidden Cut Demo 7 | 43 | 44 | 45 |
    46 |
    47 |
    48 |
    49 |
    50 | 51 | 52 | -------------------------------------------------------------------------------- /examples/css/overflow/hidden/demo4/book.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipengqi/frontend-learn/73b0f3852f0f5ab837ac6a46b78ce6c828c48a4e/examples/css/overflow/hidden/demo4/book.jpeg -------------------------------------------------------------------------------- /examples/css/overflow/hidden/demo4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Overflow background-attachment Demo 7 | 49 | 50 | 51 |

    scroll

    52 |
    The background-attachment CSS property sets whether a background image's position is fixed within the viewport, or scrolls with its containing block.
    53 | 54 |

    fixed

    55 |
    The background-attachment CSS property sets whether a background image's position is fixed within the viewport, or scrolls with its containing block.
    56 | 57 |

    local

    58 |
    The background-attachment CSS property sets whether a background image's position is fixed within the viewport, or scrolls with its containing block.
    59 | 60 | 61 | -------------------------------------------------------------------------------- /examples/css/overflow/hidden/demo5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Overflow Align Demo 7 | 54 | 55 | 56 |
    57 |
    Item 1
    58 |
    Item 2
    59 |
    Item 3
    60 |
    Item 4
    61 |
    Item 5
    62 |
    63 | 64 | 65 | -------------------------------------------------------------------------------- /examples/css/override/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CSS Override Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 |
    13 | hello 14 |
    15 |
    16 | world 17 |
    18 | 19 | -------------------------------------------------------------------------------- /examples/css/override/style.css: -------------------------------------------------------------------------------- 1 | .override1 { 2 | width: 200px; 3 | height: 200px; 4 | background-color: skyblue; 5 | border: 4px solid #ff0000; 6 | border-style: dashed; 7 | } 8 | 9 | .override2 { 10 | width: 200px; 11 | height: 200px; 12 | background-color: skyblue; 13 | border-style: dashed; 14 | border: 4px solid red; 15 | } -------------------------------------------------------------------------------- /examples/css/position/absolute/demo1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Position Absolute-Flex Demo 6 | 33 | 34 | 35 |
    36 |
    37 | 38 |
    39 |
    40 | 41 |
    42 |
    43 | 44 |
    45 |
    46 | 47 |
    48 |
    49 |
    50 |
    51 | 52 |
    53 |
    54 | 55 |
    56 |
    57 | 58 |
    59 |
    60 | 61 |
    62 |
    63 | 64 | 65 | -------------------------------------------------------------------------------- /examples/css/position/sticky/demo1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Position Sticky Section Demo 6 | 34 | 35 | 36 |
    37 |
    38 |
    39 |
    40 |
    41 |
    42 |
    43 |
    44 | 45 | 46 | -------------------------------------------------------------------------------- /examples/css/position/sticky/demo2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Position Sticky Section Demo 6 | 35 | 36 | 37 |
    38 |
    39 |
    1
    40 |
    内容一
    41 |
    42 |
    43 |
    2
    44 |
    内容二
    45 |
    46 |
    47 |
    3
    48 |
    内容三
    49 |
    50 |
    51 |
    4
    52 |
    内容四
    53 |
    54 |
    55 | 56 | 57 | -------------------------------------------------------------------------------- /examples/css/responsive/demo1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Responsive Size Demo 7 | 19 | 20 | 21 |
    22 |
    23 |
    24 | 25 | -------------------------------------------------------------------------------- /examples/css/responsive/demo2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Responsive Flex Sub Element Demo 7 | 27 | 28 | 29 |
    30 |
    31 | 32 |
    33 |
    34 | 35 |
    36 |
    37 | 38 | -------------------------------------------------------------------------------- /examples/css/responsive/demo3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Responsive Grid Sub Element Demo 7 | 22 | 23 | 24 |
    25 |
    26 | 27 |
    28 |
    29 | 30 |
    31 |
    32 | 33 |
    34 |
    35 | 36 | -------------------------------------------------------------------------------- /examples/css/responsive/demo4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Media Queries Demo 7 | 30 | 31 | 32 |
    33 | 34 |
    35 | 36 | -------------------------------------------------------------------------------- /examples/css/responsive/demo5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Media Queries Mobile First Demo 7 | 44 | 45 | 46 |
    47 |
    1
    48 |
    2
    49 |
    3
    50 |
    4
    51 |
    5
    52 |
    6
    53 |
    7
    54 |
    8
    55 |
    9
    56 |
    10
    57 |
    11
    58 |
    12
    59 |
    13
    60 |
    14
    61 |
    15
    62 |
    16
    63 |
    17
    64 |
    18
    65 |
    19
    66 |
    20
    67 |
    68 | 69 | -------------------------------------------------------------------------------- /examples/css/responsive/demo6/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Media Queries PC First Demo 7 | 44 | 45 | 46 |
    47 |
    1
    48 |
    2
    49 |
    3
    50 |
    4
    51 |
    5
    52 |
    6
    53 |
    7
    54 |
    8
    55 |
    9
    56 |
    10
    57 |
    11
    58 |
    12
    59 |
    13
    60 |
    14
    61 |
    15
    62 |
    16
    63 |
    17
    64 |
    18
    65 |
    19
    66 |
    20
    67 |
    68 | 69 | -------------------------------------------------------------------------------- /examples/css/responsive/demo7/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Container Queries Demo 7 | 34 | 35 | 36 |
    37 |
    1
    38 |
    39 | 40 | 41 | -------------------------------------------------------------------------------- /examples/css/responsive/demo8/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Images Demo 7 | 40 | 41 | 42 |
    43 | 52 | small 53 | medium 54 | large 55 |
    56 | 57 | 58 | -------------------------------------------------------------------------------- /examples/css/responsive/demo8/large-1920.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipengqi/frontend-learn/73b0f3852f0f5ab837ac6a46b78ce6c828c48a4e/examples/css/responsive/demo8/large-1920.jpg -------------------------------------------------------------------------------- /examples/css/responsive/demo8/medium-1280.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipengqi/frontend-learn/73b0f3852f0f5ab837ac6a46b78ce6c828c48a4e/examples/css/responsive/demo8/medium-1280.jpg -------------------------------------------------------------------------------- /examples/css/responsive/demo8/small-640.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipengqi/frontend-learn/73b0f3852f0f5ab837ac6a46b78ce6c828c48a4e/examples/css/responsive/demo8/small-640.jpg -------------------------------------------------------------------------------- /examples/css/selector/demo1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Pseudo Elements Underline Demo 6 | 53 | 54 | 55 |
    56 |
    57 | Hello World 58 |
    59 |
    60 | 61 | 62 | -------------------------------------------------------------------------------- /examples/css/selector/demo2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Pseudo Elements Demo 6 | 36 | 37 | 38 |
    39 |
    40 | DTM is a distributed transaction framework which provides cross-service eventual data consistency. It provides saga, tcc, xa, 2-phase message, outbox, workflow patterns for a variety of application scenarios. 41 |
    42 | 43 |
    44 | 45 | 46 | -------------------------------------------------------------------------------- /examples/css/selector/demo3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Pseudo Elements Switch Button/Toggle Demo 6 | 40 | 41 | 42 |
    43 |
    44 | 45 | 46 | -------------------------------------------------------------------------------- /examples/css/selector/demo4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Check Light Demo 6 | 86 | 87 | 88 |
    89 | 90 | 91 |
    92 |
    93 |
    94 |
    95 |
    96 | 97 | 98 | -------------------------------------------------------------------------------- /examples/css/selector/demo5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hover Light Demo 6 | 69 | 70 | 71 |
    72 |
    73 | 74 |
    75 |
    76 |
    77 |
    78 |
    79 |
    80 | 81 | 82 | -------------------------------------------------------------------------------- /examples/css/selector/demo6/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tab Demo 6 | 70 | 71 | 72 |
    73 | 74 | 75 |
    76 | Application list 77 |
    78 | 79 | 80 |
    81 | Demo List 82 |
    83 | 84 | 85 |
    86 | Code 87 |
    88 |
    89 | 90 | 91 | -------------------------------------------------------------------------------- /examples/css/shadow/chrome.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipengqi/frontend-learn/73b0f3852f0f5ab837ac6a46b78ce6c828c48a4e/examples/css/shadow/chrome.webp -------------------------------------------------------------------------------- /examples/css/shadow/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Shadow Demo 7 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /examples/css/transform/demo1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Transform Demo - Progress Bar 6 | 46 | 47 | 48 |
    49 |
    50 | Hello 51 |
    52 |
    53 | World 54 |
    55 |
    56 | 57 | 58 | -------------------------------------------------------------------------------- /examples/css/transform/demo2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Transform Demo - Loading 6 | 40 | 41 | 42 |
    43 |
    44 | 45 |
    46 |
    47 | 48 | 49 | -------------------------------------------------------------------------------- /examples/css/transform/demo3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Transform Demo - Loading 6 | 64 | 65 | 66 |
    67 |
    68 |
    69 |
    70 | 71 | 72 | -------------------------------------------------------------------------------- /examples/css/transform/demo4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Transform Demo - Perspective 6 | 40 | 41 | 42 |
    43 |
    44 | 1 45 |
    46 |
    47 | 2 48 |
    49 |
    50 | 51 | 52 | -------------------------------------------------------------------------------- /examples/css/transform/demo5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Transform Demo - Cube 6 | 76 | 77 | 78 |
    79 |
    1
    80 |
    2
    81 |
    3
    82 |
    4
    83 |
    5
    84 |
    6
    85 |
    86 | 87 | 88 | -------------------------------------------------------------------------------- /examples/es6/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | [ 4 | "@babel/plugin-proposal-decorators", 5 | { 6 | "legacy": true 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /examples/es6/decorator/attr_decorator.js: -------------------------------------------------------------------------------- 1 | function readonly(target, name, descriptor){ 2 | // descriptor 对象原来的值如下 3 | // { 4 | // value: specifiedFunction, 5 | // enumerable: false, 6 | // configurable: true, 7 | // writable: true 8 | // }; 9 | descriptor.writable = false; 10 | return descriptor; 11 | } 12 | 13 | // readonly(ExampleClassAttrDecorator.prototype, 'name', descriptor); 14 | // 类似于 15 | // Object.defineProperty(ExampleClassAttrDecorator.prototype, 'name', descriptor); 16 | // 装饰器第一个参数是类的原型对象,上例是 ExampleClassAttrDecorator.prototype,装饰器的本意是要“装饰”类的实例,但是这个时候实例还没生成,所以只能去 17 | // 装饰原型(这不同于类的装饰,那种情况时target参数指的是类本身);第二个参数是所要装饰的属性名,第三个参数是该属性的描述对象。 18 | 19 | function log(target, name, descriptor) { 20 | var oldValue = descriptor.value; 21 | 22 | descriptor.value = function() { 23 | console.log(`Calling ${name} with`, arguments); 24 | return oldValue.apply(this, arguments); 25 | }; 26 | 27 | return descriptor; 28 | } 29 | 30 | class ExampleClassAttrDecorator { 31 | @readonly 32 | @log 33 | name() { return `${this.first} ${this.last}` } 34 | 35 | @log 36 | add(a, b) { 37 | return a + b; 38 | } 39 | } 40 | 41 | const attre = new ExampleClassAttrDecorator(); 42 | 43 | attre.name() // Calling name with [Arguments] {} 44 | attre.add(2, 4); // Calling add with [Arguments] { '0': 2, '1': 4 } 45 | -------------------------------------------------------------------------------- /examples/es6/decorator/class_decorator.js: -------------------------------------------------------------------------------- 1 | function testable(target) { 2 | target.testable = true; 3 | } 4 | 5 | @testable 6 | class ExampleClassDecorator {} 7 | 8 | // 等同于 9 | // class ExampleClassDecorator {} 10 | // ExampleClassDecorator = testable(ExampleClassDecorator) || ExampleClassDecorator; 11 | 12 | console.log(ExampleClassDecorator.testable) // true 13 | 14 | function testableWrap(isTestable) { 15 | return function(target) { 16 | target.testable = isTestable; 17 | } 18 | } 19 | 20 | @testableWrap(true) 21 | class ExampleClassDecoratorWrap {} 22 | 23 | console.log(ExampleClassDecoratorWrap.testable) // true 24 | 25 | // 装饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,装饰器能在编译阶段运行代码。 26 | // 装饰器本质就是编译时执行的函数。 27 | 28 | // 前面的例子是为类添加一个静态属性,如果想添加实例属性,可以通过目标类的 prototype 对象操作 29 | 30 | function testableInstance(target) { 31 | target.prototype.testable = true; 32 | } 33 | 34 | @testableInstance 35 | class ExampleClassDecoratorInstance {} 36 | let obj = new ExampleClassDecoratorInstance(); 37 | 38 | console.log(ExampleClassDecoratorInstance.testable) // undefined 39 | console.log(obj.testable) // true 40 | -------------------------------------------------------------------------------- /examples/es6/decorator/function_decorator.js: -------------------------------------------------------------------------------- 1 | // 装饰器不能用于函数 2 | // 因为存在函数提升 3 | 4 | var counter = 0; 5 | 6 | var add = function () { 7 | counter++; 8 | }; 9 | 10 | @add 11 | function foo() { 12 | } 13 | 14 | // 意图是执行后 counter 等于 1,但是实际上结果是 counter 等于 0 15 | 16 | // 因为函数提升,使得实际执行的代码是下面这样 17 | // var counter; 18 | // var add; 19 | // 20 | // @add 21 | // function foo() { 22 | // } 23 | // 24 | // counter = 0; 25 | // 26 | // add = function () { 27 | // counter++; 28 | // }; 29 | -------------------------------------------------------------------------------- /examples/es6/decorator/order_decorator.js: -------------------------------------------------------------------------------- 1 | function dec(id) { 2 | console.log('evaluated', id); 3 | return (target, property, descriptor) => console.log('executed', id); 4 | } 5 | 6 | function dec2(target, property, descriptor) { 7 | console.log('executed dec2'); 8 | } 9 | 10 | function dec3(target, property, descriptor) { 11 | console.log('executed dec3'); 12 | } 13 | 14 | class ExampleDecoratorsOrder { 15 | @dec(1) 16 | @dec(2) 17 | method() { 18 | } 19 | 20 | @dec2 21 | @dec3 22 | method2() { 23 | } 24 | } 25 | 26 | // evaluated 1 27 | // evaluated 2 28 | // executed 2 29 | // executed 1 30 | // executed dec3 31 | // executed dec2 32 | -------------------------------------------------------------------------------- /examples/es6/index.js: -------------------------------------------------------------------------------- 1 | require('@babel/register'); 2 | require('core-js'); 3 | require('regenerator-runtime/runtime'); 4 | // require('./decorator/class_decorator.js'); 5 | // require('./decorator/attr_decorator.js'); 6 | require('./decorator/order_decorator.js'); 7 | -------------------------------------------------------------------------------- /examples/mvue/README.md: -------------------------------------------------------------------------------- 1 | # mVue 2 | A simple implementation for MVVM of Vue.js, and deep into Vue.js. 3 | 4 | ## 开始 5 | 6 | - `src`目录下是对`Vue`双向绑定简化版的源码。 7 | - `vue_src`目录是`Vue`的源码,加了学习源码的注释。 8 | - `vue_router_src`目录是`vue-router`的源码。 9 | - `vuex_src`目录是`vuex`的源码。 10 | - `docs`目录下是`Vue`的学习笔记。也可以直接访问[这里](https://shipengqi.github.io/frontend-learn)。 11 | 12 | ## 简单介绍 13 | Vue.js 在做 2.0 重构时,引入了 Flow 做静态类型检查,选择 Flow,主要是因为 Babel 和 ESLint 都有对应的 Flow 插件以支持语法, 14 | 可以完全沿用现有的构建配置,非常小成本的改动就可以拥有静态类型检查的能力。 15 | 16 | ### Flow 17 | Flow 两个重要的特性: 18 | - 类型推断,通过变量的使用上下文来推断出变量类型,并检查类型。 19 | - 类型注释,基于注释来判断类型。 20 | 21 | ### Vue 源码目录 22 | `src`目录下我们能看到: 23 | ``` 24 | src 25 | ├── compiler # 编译相关 26 | ├── core # 核心代码 27 | ├── platforms # 不同平台的支持 28 | ├── server # 服务端渲染 29 | ├── sfc # .vue 文件解析 30 | ├── shared # 共享代码 31 | ``` 32 | 33 | - compiler,所有编译相关的代码。它包括把模板解析成 ast 语法树,ast 语法树优化,代码生成等功能。 34 | - core,核心代码,包括内置组件、全局 API 封装,Vue 实例化、观察者、虚拟 DOM、工具函数等等。 35 | - platform,Vue 是一个跨平台的框架,可以跑在 web 上,也可以配合 weex 跑在 native 客户端上,platform 是 Vue.js 的入口, 36 | `web`和`weex`目录,分别打包成运行在 web 上和 weex 上的 Vue.js。 37 | - server,所有服务端渲染相关的逻辑都在这个目录下。 38 | - sfc,目录下的代码逻辑会把 .vue 文件内容解析成一个 JavaScript 的对象。 39 | - shared,定义一些工具方法,被浏览器端的 Vue.js 和服务端的 Vue.js 所共享。 40 | 41 | ### Vue 源码构建 42 | Vue 通过 Rollup 构建,构建脚本在`package.json`中: 43 | ```js 44 | { 45 | "script": { 46 | "build": "node scripts/build.js", 47 | "build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer", 48 | "build:weex": "npm run build --weex" 49 | } 50 | } 51 | ``` 52 | 53 | 打开`scripts/build.js`: 54 | ```js 55 | let builds = require('./config').getAllBuilds() 56 | 57 | // filter builds via command line arg 58 | if (process.argv[2]) { 59 | const filters = process.argv[2].split(',') 60 | builds = builds.filter(b => { 61 | return filters.some(f => b.output.file.indexOf(f) > -1 || b._name.indexOf(f) > -1) 62 | }) 63 | } else { 64 | // filter out weex builds by default 65 | builds = builds.filter(b => { 66 | return b.output.file.indexOf('weex') === -1 67 | }) 68 | } 69 | 70 | build(builds) 71 | ``` 72 | 先从配置文件读取配置,再通过命令行参数对构建配置做过滤,这样就可以构建出不同用途的 Vue 了. -------------------------------------------------------------------------------- /examples/mvue/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | mVue demo 6 | 7 | 8 |
    9 | 10 |

    {{ message }}

    11 | 12 |
    13 | 14 | 15 | 16 | 17 | 18 | 31 | 32 | -------------------------------------------------------------------------------- /examples/mvue/js/compile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * User: shipengqi (pooky.shipengqi@gmail.com) 3 | * Date: 2018/5/6 4 | * Time: 15:45 5 | * 6 | */ 7 | function Compile(el, vm) { 8 | this.vm = vm; 9 | this.el = document.querySelector(el); 10 | this.fragment = null; 11 | this.init(); 12 | } 13 | 14 | Compile.prototype = { 15 | init: function () { 16 | if (this.el) { 17 | this.fragment = this.nodeToFragment(this.el); 18 | this.compileElement(this.fragment); 19 | this.el.appendChild(this.fragment); 20 | } else { 21 | console.log('Cannot find the element.'); 22 | } 23 | }, 24 | nodeToFragment: function (el) { 25 | var fragment = document.createDocumentFragment(); 26 | var child = el.firstChild; 27 | // 将Dom节点拷贝到fragment中 28 | while (child) { 29 | fragment.appendChild(child); 30 | child = el.firstChild 31 | } 32 | return fragment; 33 | }, 34 | compileElement: function (el) { 35 | var childNodes = el.childNodes; 36 | var self = this; 37 | [].slice.call(childNodes).forEach(function(node) { 38 | var reg = /\{\{(.*)\}\}/; 39 | var text = node.textContent; 40 | // 按元素节点方式编译 41 | if (self.isElementNode(node)) { 42 | self.compile(node); 43 | } else if (self.isTextNode(node) && reg.test(text)) { // 判断是否是符合这种形式{{}}的指令 44 | self.compileText(node, reg.exec(text)[1]); 45 | } 46 | // 遍历编译子节点 47 | if (node.childNodes && node.childNodes.length) { 48 | self.compileElement(node); 49 | } 50 | }); 51 | }, 52 | compile: function(node) { 53 | var nodeAttrs = node.attributes; 54 | var self = this; 55 | Array.prototype.forEach.call(nodeAttrs, function(attr) { 56 | var attrName = attr.name; 57 | if (self.isDirective(attrName)) { 58 | var exp = attr.value; 59 | var dir = attrName.substring(2); 60 | if (self.isEventDirective(dir)) { // 事件指令 61 | self.compileEvent(node, self.vm, exp, dir); 62 | } else { // v-model 指令 63 | self.compileModel(node, self.vm, exp, dir); 64 | } 65 | node.removeAttribute(attrName); 66 | } 67 | }); 68 | }, 69 | compileText: function(node, exp) { 70 | var self = this; 71 | exp = exp ? exp.trim() : ""; 72 | var initText = this.vm[exp]; 73 | this.updateText(node, initText); 74 | new Watcher(this.vm, exp, function (value) { 75 | self.updateText(node, value); 76 | }); 77 | }, 78 | compileEvent: function (node, vm, exp, dir) { 79 | var eventType = dir.split(':')[1]; 80 | var cb = vm.methods && vm.methods[exp]; 81 | 82 | if (eventType && cb) { 83 | node.addEventListener(eventType, cb.bind(vm), false); 84 | } 85 | }, 86 | compileModel: function (node, vm, exp, dir) { 87 | var self = this; 88 | var val = this.vm[exp]; 89 | this.modelUpdater(node, val); 90 | new Watcher(this.vm, exp, function (value) { 91 | self.modelUpdater(node, value); 92 | }); 93 | 94 | node.addEventListener('input', function(e) { 95 | var newValue = e.target.value; 96 | if (val === newValue) { 97 | return; 98 | } 99 | self.vm[exp] = newValue; 100 | val = newValue; 101 | }); 102 | }, 103 | updateText: function (node, value) { 104 | node.textContent = typeof value === 'undefined' ? '' : value; 105 | }, 106 | modelUpdater: function(node, value, oldValue) { 107 | node.value = typeof value === 'undefined' ? '' : value; 108 | }, 109 | isDirective: function(attr) { 110 | return attr.indexOf('v-') === 0; 111 | }, 112 | isEventDirective: function(dir) { 113 | return dir.indexOf('on:') === 0; 114 | }, 115 | isElementNode: function (node) { 116 | return node.nodeType === 1; 117 | }, 118 | isTextNode: function(node) { 119 | return node.nodeType === 3; 120 | } 121 | }; -------------------------------------------------------------------------------- /examples/mvue/js/mVue.js: -------------------------------------------------------------------------------- 1 | /** 2 | * User: shipengqi (pooky.shipengqi@gmail.com) 3 | * Date: 2018/5/6 4 | * Time: 15:45 5 | * 6 | */ 7 | 8 | function mVue (options) { 9 | var self = this; 10 | this.data = options.data; 11 | this.methods = options.methods; 12 | var mounted = options.mounted || function(){}; 13 | Object.keys(this.data).forEach(function(key) { 14 | self.proxyKeys(key); 15 | }); 16 | 17 | observe(this.data); 18 | new Compile(options.el, this); 19 | mounted.call(this); // 所有事情处理好后执行mounted函数 20 | } 21 | 22 | mVue.prototype = { 23 | proxyKeys: function (key) { 24 | var self = this; 25 | Object.defineProperty(this, key, { 26 | enumerable: false, 27 | configurable: true, 28 | get: function getter () { 29 | return self.data[key]; 30 | }, 31 | set: function setter (newVal) { 32 | self.data[key] = newVal; 33 | } 34 | }); 35 | } 36 | }; -------------------------------------------------------------------------------- /examples/mvue/js/observer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * User: shipengqi (pooky.shipengqi@gmail.com) 3 | * Date: 2018/5/6 4 | * Time: 15:46 5 | * 6 | */ 7 | 8 | function Observer(data) { 9 | this.data = data; 10 | this.walk(data); 11 | } 12 | 13 | Observer.prototype = { 14 | walk: function(data) { 15 | var self = this; 16 | if (!data || typeof data !== 'object') { 17 | return; 18 | } 19 | 20 | // 遍历data所有属性 21 | Object.keys(data).forEach(function(key) { 22 | self.defineReactive(data, key, data[key]); 23 | }); 24 | }, 25 | defineReactive: function(data, key, value) { 26 | var dep = new Dep(); 27 | //递归遍历 28 | observe(value); 29 | 30 | //添加 setter和getter 31 | Object.defineProperty(data, key, { 32 | enumerable: true, 33 | configurable: false, 34 | get: function getter () { 35 | if (Dep.target) { 36 | dep.addSub(Dep.target); 37 | } 38 | return value; 39 | }, 40 | set: function setter (newValue) { 41 | if (newValue === value) { 42 | return; 43 | } 44 | value = newValue; 45 | 46 | // 通知所有订阅者 47 | dep.notify(); 48 | } 49 | }); 50 | } 51 | }; 52 | 53 | function observe(value) { 54 | if (!value || typeof value !== 'object') { 55 | return; 56 | } 57 | return new Observer(value); 58 | } 59 | 60 | 61 | function Dep () { 62 | this.subs = []; 63 | } 64 | Dep.prototype = { 65 | addSub: function(sub) { 66 | this.subs.push(sub); 67 | }, 68 | notify: function() { 69 | this.subs.forEach(function(sub) { 70 | sub.update(); 71 | }); 72 | } 73 | }; 74 | Dep.target = null; -------------------------------------------------------------------------------- /examples/mvue/js/watcher.js: -------------------------------------------------------------------------------- 1 | /** 2 | * User: shipengqi (pooky.shipengqi@gmail.com) 3 | * Date: 2018/5/6 4 | * Time: 15:46 5 | * 6 | */ 7 | 8 | function Watcher(vm, exp, cb) { 9 | this.cb = cb; 10 | this.vm = vm; 11 | this.exp = exp; 12 | this.value = this.get(); // 将自己添加到订阅器的操作 13 | } 14 | 15 | Watcher.prototype = { 16 | update: function() { 17 | // 收到属性值变化的通知 18 | this.run(); 19 | }, 20 | run: function() { 21 | var value = this.get();// 取到最新值 22 | var oldVal = this.value; 23 | if (value !== oldVal) { 24 | this.value = value; 25 | this.cb.call(this.vm, value, oldVal); 26 | } 27 | }, 28 | get: function() { 29 | Dep.target = this; // 缓存自己 30 | var value = this.vm.data[this.exp]; // 强制触发getter,添加自己到属性订阅器中 31 | Dep.target = null; // 释放自己 32 | return value; 33 | } 34 | }; -------------------------------------------------------------------------------- /examples/ts-base/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-base-examples", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "./src/index.ts", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode=development --config=./build/webpack.config.js", 8 | "build": "webpack --mode=production --config=./build/webpack.config.js", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "clean-webpack-plugin": "^4.0.0", 15 | "html-webpack-plugin": "^5.5.0", 16 | "ts-loader": "^9.3.0", 17 | "typescript": "^4.6.4", 18 | "webpack": "^5.72.0", 19 | "webpack-cli": "^4.9.2", 20 | "webpack-dev-server": "^4.9.0", 21 | "webpack-merge": "^5.8.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/ts-base/src/01_datatype.ts: -------------------------------------------------------------------------------- 1 | // 原始类型 2 | let bool: boolean = true 3 | let num: number = 100 4 | let str: string = 'hello' 5 | // str = 123 // Type 'number' is not assignable to type 'string'.ts(2322) 6 | 7 | // 数组 8 | let arr1: number[] = [1, 2, 3] 9 | let arr2: Array = [1, 2, 3] 10 | // let arr3: Array = [1, 2, 3, '4'] // Type 'string' is not assignable to type 'number'.ts(2322) 11 | // 联合类型 12 | let arr4: Array = [1, 2, 3, '4'] 13 | 14 | // 元组 特殊的数组 限制了元素的类型和个数 15 | let tuple: [number, string] = [0, '1'] 16 | // let tuple2: [number, string] = ['1', '1'] // Type 'string' is not assignable to type 'number'.ts(2322) 17 | // let tuple3: [number, string] = [0, '1', 2] // Type '[number, string, number]' is not assignable to type '[number, string]'. Source has 3 element(s) but target allows only 2.ts(2322) 18 | // 元组的越界问题 19 | tuple.push(2) 20 | console.log(tuple) // [0, "1", 2] 21 | // tuple[2] // Tuple type '[number, string]' of length '2' has no element at index '2'.ts(2493) 22 | // 所以 ts 允许向元组插入元素,但是不能访问 23 | 24 | 25 | // 函数 26 | let add = (x: number, y: number): number => x + y 27 | let add2 = (x: number, y: number) => x + y // 利用了 ts 的类型推断功能,省略了返回值的类型 28 | 29 | // 定义了函数类型 compute 30 | let compute: (x: number, y: number) => number 31 | // 实现 compute,不需要再指定具体的类型 32 | compute = (a, b) => a + b; 33 | 34 | // 对象 35 | let obj: object = {x: 1, y: 2} 36 | // obj.x = 1 // Property 'x' does not exist on type 'object'.ts(2339) 37 | // 上面只是指定了 obj 的类型是 object,没有具体定义包含的属性 38 | // 正确的的定义 39 | let obj2: {x: number, y: number} = {x: 1, y: 2} 40 | obj2.x = 3 41 | 42 | // symbol 具有唯一的值 43 | let s1: symbol = Symbol() 44 | let s2 = Symbol() 45 | console.log(s1 === s2) // false 46 | 47 | // undefined, null 48 | let un: undefined = undefined 49 | // let nu2: undefined = 3 // Type '3' is not assignable to type 'undefined'.ts(2322) 50 | // undefined 类型只能被赋值为 undefined 51 | // num = undefined // Type 'undefined' is not assignable to type 'number'.ts(2322) 52 | // num = null // Type 'null' is not assignable to type 'number'.ts(2322) 53 | 54 | // undefined 和 null 是任意类型的子类型,要修复这个报错,需要修改 tsconfig.json,将 "strictNullChecks": true, 改为 false 55 | // 这里也可以使用联合类型来修复报错 56 | // let num: number | undefined | null = 2 57 | 58 | 59 | let nu: null = null 60 | 61 | // void 没有返回值的类型,可以让任何表达式返回 undefined,例如 void 0 62 | // 在 ts 中 undefined 不是保留字,是可以被变量覆盖的,如 var undefined = 0 63 | // 使用 void 可以确保返回值是 undefined 64 | let noReturn = () => {} 65 | 66 | // any,不指定变量类型,就是默认的 any 类型。any 类型就和 js 中没有区别,可以赋值任意类型 67 | let x 68 | // x = 1 69 | // x= [] 70 | // x = "" 71 | 72 | // never 永远不会有返回值的类型 73 | let error = () => { 74 | throw new Error('error') 75 | } 76 | 77 | let endless = () => { 78 | while(true) {} 79 | } 80 | 81 | // 因为接口 Box 里面 name 被定义为可空的值,但是实际情况是不为空的,那么我们就可以 82 | // 通过在 BoxA 里面使用 ! 重新强调 name 不为空值 83 | class BoxA implements Box { 84 | name!: string 85 | } 86 | 87 | interface Box { 88 | name?: string 89 | } 90 | 91 | // 这里 Error 对象定义的 stack 是可选参数,如果这样写的话编译器会提示 92 | // TS2532: Object is possibly 'undefined'. 93 | // new Error().stack.split('\n'); 94 | 95 | // 假如我们确信这个字段一定存在,那么就可以添加 ! 强调这个字段一定存在 96 | // 就不会报错了 97 | new Error().stack!.split('\n'); 98 | -------------------------------------------------------------------------------- /examples/ts-base/src/02_enum.ts: -------------------------------------------------------------------------------- 1 | // 数字枚举 2 | 3 | enum Role { 4 | Repoter = 1, 5 | Developer, 6 | Maintainer, 7 | Owner, 8 | Guest 9 | } 10 | // 会被编译成下面的 js 11 | // "use strict"; 12 | // var Role; 13 | // (function (Role) { 14 | // Role[Role["Repoter"] = 1] = "Repoter"; 15 | // Role[Role["Developer"] = 2] = "Developer"; 16 | // Role[Role["Maintainer"] = 3] = "Maintainer"; 17 | // Role[Role["Owner"] = 4] = "Owner"; 18 | // Role[Role["Guest"] = 5] = "Guest"; 19 | // })(Role || (Role = {})); 20 | // 这是一个双向映射 21 | 22 | console.log(Role.Repoter) // 1 23 | console.log(Role) 24 | // { 25 | // "1": "Repoter", 26 | // "2": "Developer", 27 | // "3": "Maintainer", 28 | // "4": "Owner", 29 | // "5": "Guest", 30 | // "Repoter": 1, 31 | // "Developer": 2, 32 | // "Maintainer": 3, 33 | // "Owner": 4, 34 | // "Guest": 5 35 | // } 36 | // 枚举是被编译成了一个对象,枚举对象既可以使用枚举成员的名来索引,也可以用值来索引 37 | console.log(Role[1]) // Repoter 38 | 39 | 40 | // 字符串枚举 41 | enum Message { 42 | Success = '成功了', 43 | Fail = '失败了' 44 | } 45 | // "use strict"; 46 | // var Message; 47 | // (function (Message) { 48 | // Message["Success"] = "\u6210\u529F\u4E86"; 49 | // Message["Fail"] = "\u5931\u8D25\u4E86"; 50 | // })(Message || (Message = {})); 51 | // 只有枚举成员的名称作为了 key,没有进行反向映射 52 | 53 | // 异构枚举 54 | enum Answer { 55 | N, 56 | Y = 'Yes' 57 | } 58 | 59 | // 枚举成员 60 | 61 | // Role.Repoter = 2 // Cannot assign to 'Repoter' because it is a read-only property.ts(2540) 62 | // 枚举成员的值是只读的 63 | 64 | // 枚举成员有三种类型 65 | // 1. const 常量类型,会在编译时计算结果,运行时以常量形式出现 66 | // 2. computed 计算类型,非常量的表达式,在运行时计算 67 | enum Char { 68 | a, // const 69 | b = Char.a, // const 对已有常量成员的引用 70 | c = 1 + 3, // const 常量表达式 71 | d = Math.random(), // computed 72 | e = '123'.length, // computed 73 | // f, Enum member must have initializer.ts(1061) 74 | f2 = 4 75 | } 76 | // "use strict"; 77 | // var Char; 78 | // (function (Char) { 79 | // Char[Char["a"] = 0] = "a"; 80 | // Char[Char["b"] = 0] = "b"; 81 | // Char[Char["c"] = 4] = "c"; 82 | // Char[Char["d"] = Math.random()] = "d"; 83 | // Char[Char["e"] = '123'.length] = "e"; 84 | // })(Char || (Char = {})); 85 | 86 | // 常量枚举 用 const 声明的枚举 87 | const enum Month { 88 | Jan, 89 | Feb, 90 | Mar, 91 | Apr = Month.Mar + 1, 92 | // May = () => 5 93 | } 94 | // 常量枚举在编译阶段会被删除,下面是编译后的代码 95 | // "use strict"; 96 | 97 | let mouth = [Month.Jan, Month.Feb] 98 | // "use strict"; 99 | // let mouth = [0 /* Jan */, 1 /* Feb */]; 100 | // 枚举被直接替换成了常量 101 | 102 | 103 | // 枚举类型 枚举和枚举成员可以作为单独的类型 104 | enum E { a, b } // 成员没有初始值 105 | enum F { a = 0, b = 1 } // 所有成员都是数值 106 | enum G { a = 'apple', b = 'banana' } // 所有成员都是字符串 107 | 108 | let e: E = 3 109 | let f: F = 3 110 | // console.log(e === f) This condition will always return 'false' since the types 'E' and 'F' have no overlap.ts(2367) 111 | // 不同的枚举类型不能比较 112 | 113 | let e1: E.a = 3 114 | let e2: E.b = 3 115 | let e3: E.a = 3 116 | // console.log(e1 === e2) This condition will always return 'false' since the types 'E.a' and 'E.b' have no overlap.ts(2367) 117 | // 不同的枚举成员不能比较 118 | console.log(e1 === e3) // true 119 | 120 | let f1: F.a = 3 121 | let f2: F.b = 3 122 | let f3: F.a = 3 123 | console.log(f1 === f3) 124 | 125 | // 字符串枚举的取值只能是枚举成员的类型 126 | let g1: G = G.a 127 | let g2: G.a = G.a -------------------------------------------------------------------------------- /examples/ts-base/src/03_interface.ts: -------------------------------------------------------------------------------- 1 | interface List { 2 | readonly id: number; // 只读属性,不可修改 3 | // id: number; 4 | name: string; 5 | age?: number; // 可选属性 6 | } 7 | 8 | interface Result { 9 | data: List[] 10 | } 11 | 12 | function render(result: Result) { 13 | result.data.forEach((value) => { 14 | console.log(value.id, value.name) 15 | // 1 'a' 16 | // 2 'b' 17 | if (value.age) { // 如果 List 不加上 age: number 会报错 Property 'age' does not exist on type 'List'.ts(2339) 18 | console.log(value.age) 19 | } 20 | }) 21 | } 22 | 23 | let result = { 24 | data: [ 25 | {id: 1, name: "a"}, 26 | {id: 2, name: "b", age: 10}, 27 | {id: 3, name: "c", sex: 'male'}, // 有多出的字段,不会报错 28 | ] 29 | } 30 | 31 | render(result) 32 | 33 | // 如果直接传入对象字面量,ts 就会对对象进行检查 34 | // render( { 35 | // data: [ 36 | // {id: 1, name: "a"}, 37 | // {id: 2, name: "b"}, 38 | // {id: 3, name: "c", sex: 'male'}, // Type '{ id: number; name: string; sex: string; }' is not assignable to type 'List'. Object literal may only specify known properties, and 'sex' does not exist in type 'List'.ts(2322) 39 | // ] 40 | // }) 41 | // 避免上面的问题 除了赋值给一个变量外,还可以使用类型断言,明确的告诉编译器传入的对象就是 Result 类型,编译器会跳过类型检查 42 | render({ 43 | data: [ 44 | {id: 1, name: "a"}, 45 | {id: 2, name: "b"}, 46 | {id: 3, name: "c", sex: 'male'}, 47 | ] 48 | } as Result) 49 | render({ // 不建议 在 react 中会产生歧义 50 | data: [ 51 | {id: 1, name: "a"}, 52 | {id: 2, name: "b"}, 53 | {id: 3, name: "c", sex: 'male'}, 54 | ] 55 | }) 56 | // 两种类型断言是等价的 57 | // 第三种方法,使用字符串索引签名 58 | interface List2 { 59 | id: number; 60 | name: string; 61 | [x: string]: any; // List 可以支持多个属性了 62 | } 63 | interface Result2 { 64 | data: List2[] 65 | } 66 | function render2(result: Result2) { 67 | result.data.forEach((value) => { 68 | console.log(value.id, value.name) 69 | // 1 'a' 70 | // 2 'b' 71 | }) 72 | } 73 | render2({ 74 | data: [ 75 | {id: 1, name: "a"}, 76 | {id: 2, name: "b"}, 77 | {id: 3, name: "c", sex: 'male'}, 78 | ] 79 | }) 80 | 81 | interface StringArray { 82 | [index: number]: string; 83 | } 84 | 85 | let chars: StringArray = ['a', 'b'] 86 | 87 | interface Names { 88 | [x: string]: string; 89 | // number 成员就不能再声明 90 | // y: number; // Property 'y' of type 'number' is not assignable to string index type 'string'.ts(2411) 91 | [z: number]: string; 92 | // [z: number]: number; 不兼容 93 | } 94 | 95 | // 接口定义函数 96 | // let add: (x: number, y: number) => number 等价于下面的接口 97 | // interface Add { 98 | // (x: number, y: number): number 99 | // } 100 | 101 | // 也可以使用类型别名 102 | type Add = (x: number, y: number) => number 103 | 104 | let add3: Add = (a, b) => a + b 105 | 106 | // 混合类型接口 107 | interface Lib { 108 | (): void; 109 | version: string; 110 | doSometing(): void; 111 | } 112 | 113 | // let lib: Lib = () => {} 114 | let lib: Lib = (() => {}) as Lib 115 | // 添加缺少的属性,否则会报错 Type '() => void' is missing the following properties from type 'Lib': version, doSometingts(2739) 116 | lib.version = '1.0.0'; 117 | lib.doSometing = () => {} 118 | // 添加属性后,但是还是报错 Type '() => void' is missing the following properties from type 'Lib': version, doSometing 119 | // 这个使用类型断言 as Lib 120 | 121 | // 可以封装一个工厂函数 122 | function NewLib() { 123 | let lib: Lib = (() => {}) as Lib 124 | lib.version = '1.0.0'; 125 | lib.doSometing = () => {} 126 | return lib 127 | } 128 | 129 | let lib1 = NewLib(); 130 | lib1(); 131 | lib1.doSometing(); -------------------------------------------------------------------------------- /examples/ts-base/src/04_function.ts: -------------------------------------------------------------------------------- 1 | // 函数定义的四种方式 2 | 3 | function plus1(x: number, y: number) { 4 | return x + y 5 | } 6 | 7 | // 下面的三种方式只是函数类型的定义,并没有具体实现 8 | let plus2: (x: number, y: number) => number 9 | 10 | type plus3 = (x: number, y: number) => number 11 | 12 | interface plus4 { 13 | (x: number, y: number): number 14 | } 15 | 16 | // plus1(1, 2, 3) // Expected 2 arguments, but got 3. 17 | 18 | // y 是可选参数 19 | function plus5(x: number, y?: number) { 20 | return y ? x + y: x; 21 | } 22 | 23 | // function plus6(x: number, y?: number, z: number) { // A required parameter cannot follow an optional parameter.ts(1016) 24 | // return y ? x + y: x; 25 | // } 26 | 27 | plus5(1) 28 | 29 | // 参数默认值 30 | function plus7(x: number, y = 0, z: number, q: number = 1) { 31 | return x + y + z + q 32 | } 33 | 34 | // 如果可选参数后面有必选参数,那么不可以省略,可以传入 undefined 来获取默认值,最后的可选参数可以省略 35 | console.log('可选参数:', plus7(1, undefined, 3)) // 可选参数: 5 36 | 37 | // rest 参数 38 | function plus8(x: number, ...rest: number[]) { 39 | return x + rest.reduce((pre, cur) => pre + cur); 40 | } 41 | console.log('rest 参数:', plus8(1, 2, 3, 4, 5)) // rest 参数: 15 42 | 43 | // 函数重载,两个函数名称相同,但是函数参数的个数或者类型不同 44 | // ts 编译器处理重载时,会去查询重载的列表,逐个尝试,所以应该把最常用的重载函数定义在最前面 45 | 46 | // 定义名称相同函数声明 47 | function plus9(...rest: number[]): number; 48 | function plus9(...rest: string[]): string; 49 | // 实现重载 50 | function plus9(...rest: any[]): any { 51 | let first = rest[0]; 52 | if (typeof first === 'number') { 53 | return rest.reduce((pre, cur) => pre + cur); 54 | } 55 | if (typeof first === 'string') { 56 | return rest.join(''); 57 | } 58 | } 59 | 60 | console.log('函数重载 number:', plus9(1, 2)) // 函数重载 number: 3 61 | console.log('函数重载 string:', plus9('a', 'b', 'c')) // 函数重载 string: abc -------------------------------------------------------------------------------- /examples/ts-base/src/05_class.ts: -------------------------------------------------------------------------------- 1 | // ts 和 es6 都是实例属性和实例方法 2 | // 类的所有属性默认都是 public,也可以显示声明,如 public name: string 3 | // 私有属性员只能被类的本身调用,不能被类的实例和子类调用 4 | class Dog { 5 | constructor(name: string) { 6 | this.name = name 7 | } 8 | // 如果在 constructor 加 private,那么这个类不能被继承和实例化 9 | // Constructor of class 'Dog' is private and only accessible within the class declaration.ts(2673) 10 | // Cannot extend a class 'Dog'. Class constructor is marked as private.ts(2675) 11 | // private constructor(name: string) { 12 | // this.name = name 13 | // } 14 | 15 | // 如果在 constructor 加 protected,那么这个类可以被继承,但是不能被实例化 16 | // Constructor of class 'Dog' is protected and only accessible within the class declaration.ts(2674) 17 | // protected constructor(name: string) { 18 | // this.name = name 19 | // } 20 | 21 | public name: string // 实例的属性必须有初始值 否则会报错 Property 'name' has no initializer and is not definitely assigned in the constructor.ts(2564) 22 | // 可以在 constructor 中初始化,也可以使用下面的两种方式 23 | // name: string = 'dog' 24 | // name?: string = 'dog' 25 | run() {} 26 | private pri() {} // 私有属性 只能被类的本身调用,不能被类的实例和子类调用 27 | protected pro() {} // 受保护属性只能在类和子类中访问,不能被实例访问 28 | readonly legs: number = 4 // 只读属性,必须初始化 29 | static food: string = 'bones' // 静态属性, 只能通过类名来调用 30 | } 31 | 32 | console.log(Dog.prototype) // {constructor: ƒ, run: ƒ} 33 | console.log(Dog.food) // bones 34 | 35 | let dog = new Dog('wangwnag') 36 | console.log(dog) // Dog{name: 'wangwnag'} name 属性在实例上 37 | 38 | // 私有成员不能被类的实例调用 39 | // dog.pri() // Property 'pri' is private and only accessible within class 'Dog'.ts(2341) 40 | // 受保护成员只能在类和子类中访问,不能被实例访问 41 | // dog.pro() // Property 'pro' is protected and only accessible within class 'Dog' and its subclasses.ts(2445) 42 | 43 | // 类的继承 44 | class Huskey extends Dog { 45 | // 在构造函数中的参数前添加修饰符,会将参数变成实例属性,不需要额外声明 46 | constructor(name: string, public color: string) { // Constructors for derived classes must contain a 'super' call.ts(2377) 47 | super(name) 48 | this.color = color; 49 | // 私有成员不能被类的子类调用 50 | // this.pri(); // Property 'pri' is private and only accessible within class 'Dog'.ts(2341) 51 | // 受保护成员可以在子类中访问 52 | this.pro(); 53 | } 54 | 55 | // public color: string // 参数中的 color 添加了修饰符,因此这里不需要声明,否则报错 Duplicate identifier 'color'.ts(2300) 56 | } 57 | 58 | // 类的静态属性 可以被子类继承 59 | console.log(Huskey.food) // bones 60 | 61 | let huskey = new Huskey('wangwnag', 'black') 62 | 63 | 64 | console.log(huskey) // Dog {name: 'wangwnag'} name 属性在实例上 65 | 66 | // 抽象类和多态 67 | // es6 中没有抽象类的概念,抽象类表示只能被继承不能被实例化的类 68 | // 抽象类的好处是可以抽离一些代码的共性,有利于代码的复用和扩展 69 | // 抽象类还可以实现多态,抽象方法由不同的子类实现,程序运行时不同的对象调用不同的方法 70 | 71 | // 抽象类 Animal 72 | abstract class Animal { 73 | eat() { // 抽象类中实现的方法,子类可以不用实现,这样可以实现方法的复用 74 | console.log('eating') 75 | } 76 | abstract sleep(): void // 抽象方法不需要实现,由子类实现 77 | } 78 | 79 | // 抽象类不能被实例化 80 | // let animal = new Animal() // Cannot create an instance of an abstract class.ts(2511) 81 | 82 | class Dog2 extends Animal { 83 | constructor(name: string) { 84 | super() 85 | this.name = name 86 | } 87 | name: string 88 | run() {} 89 | sleep() { 90 | console.log('sleeping') 91 | } 92 | } 93 | 94 | let dog2 = new Dog2('wangwang') 95 | dog2.eat() // eating 96 | 97 | class Cat extends Animal { 98 | sleep() { 99 | console.log('cat sleeping') 100 | } 101 | } 102 | 103 | let cat = new Cat() 104 | 105 | // 多态的使用 106 | let animals: Animal[] = [dog2, cat] 107 | 108 | animals.forEach(i => { 109 | i.sleep() 110 | }) 111 | 112 | 113 | // 链式调用 114 | class WorkFlow { 115 | step1() { 116 | return this; 117 | } 118 | 119 | step2() { 120 | return this; 121 | } 122 | } 123 | 124 | new WorkFlow().step1().step2() 125 | 126 | // this 可以调用子类和父类的属性 127 | class SubFlow extends WorkFlow { 128 | next() { 129 | return this; 130 | } 131 | } 132 | 133 | new SubFlow().next().step1().next() 134 | -------------------------------------------------------------------------------- /examples/ts-base/src/06_class_interface.ts: -------------------------------------------------------------------------------- 1 | // 类与接口 2 | 3 | // 定义一个接口来约束类的 公共属性 4 | interface Human { 5 | name: string; 6 | eat(): void; 7 | } 8 | 9 | // implements 实现接口 10 | // 类实现接口,必须实现接口的声明的所有属性 11 | class Asian implements Human { 12 | constructor(name: string) { 13 | this.name = name; 14 | } 15 | name: string 16 | eat() {} 17 | } 18 | 19 | // 接口可以继承接口 20 | interface Man extends Human { 21 | run(): void; 22 | } 23 | 24 | interface Child { 25 | cry(): void; 26 | } 27 | 28 | // 继承多个接口 29 | interface Boy extends Man, Child { 30 | 31 | } 32 | 33 | let boy: Boy = { // missing the following properties from type 'Boy': run, name, eat, cry 34 | name: '', 35 | run() {}, 36 | eat() {}, 37 | cry() {} 38 | } 39 | 40 | // 接口可以继承类,相当于把类的属性抽象了,只有类的属性结构,但是没有实现 41 | // 注意 接口不仅抽离了类的公共属性,还抽离了私有属性和受保护属性 42 | class Auto { 43 | state = 1 44 | // private state2 = 2 // Class 'C' incorrectly implements interface 'AutoInterface'. Property 'state2' is missing in type 'C' but required in type 'Auto'.ts(2420) 45 | } 46 | 47 | // 接口继承类,AutoInterface 就包含了 Auto 类的属性 48 | interface AutoInterface extends Auto { 49 | 50 | } 51 | 52 | // C 实现 AutoInterface,只需要包含 state 属性 53 | class C implements AutoInterface { 54 | state = 1 55 | } 56 | 57 | // Auto 的子类也可以实现 AutoInterface 接口 58 | // Bus 是 Auto 的子类,就不需要实现 state 属性,因为从父类 Auto 继承了 59 | class Bus extends Auto implements AutoInterface { 60 | 61 | } 62 | 63 | -------------------------------------------------------------------------------- /examples/ts-base/src/07_generics.ts: -------------------------------------------------------------------------------- 1 | function log(value: T): T { 2 | console.log(value); 3 | return value; 4 | } 5 | 6 | // 调用泛型函数 7 | log(['a', 'b', 'c']) // ['a', 'b', 'c'] 8 | log(['a', 'b']) // ['a', 'b'] 9 | 10 | // 类型别名 定义泛型函数类型 11 | type Log = (value: T) => T 12 | let myLog: Log = log 13 | 14 | // 接口 定义泛型函数类型 15 | interface Log2 { 16 | (value: T): T 17 | } 18 | 19 | // let myLog2: Log2 = log // Generic type 'Log2' requires 1 type argument(s).ts(2314) 20 | 21 | let myLog2: Log2 = log 22 | 23 | // 也可以不指定类型,在接口中指定默认类型 24 | interface Log3 { 25 | (value: T): T 26 | } 27 | let myLog3: Log3 = log 28 | 29 | // 泛型类 30 | class Log4 { 31 | // 泛型不能应用于类的静态成员 32 | // static run(value: T) { // Static members cannot reference class type parameters. 33 | // console.log(value); 34 | // return value; 35 | // } 36 | run(value: T) { 37 | console.log(value); 38 | return value; 39 | } 40 | } 41 | 42 | // 实例化泛型类,可以显示的传入 T 的类型 43 | let log4 = new Log4() 44 | // run 的参数受到泛型的约束 45 | // log4.run('a') // Argument of type 'string' is not assignable to parameter of type 'number'.ts(2345) 46 | log4.run(5) 47 | 48 | // 实例化泛型类,不传入 T 的类型,value 的类型就可以是任意类型 49 | let log41 = new Log4() 50 | log41.run(5) 51 | log41.run('a') 52 | 53 | 54 | // 泛型的类型约束 55 | interface Length { 56 | length: number 57 | } 58 | 59 | // T 继承了 Length 接口,就不能传入任意类型,参数必须具有 length 属性 60 | function log5(value: T): T { 61 | console.log(value, value.length); 62 | return value; 63 | } 64 | // 数组 字符串 都具有 length 属性 65 | log5([1]) 66 | log5('123') 67 | log5({length: 1}) -------------------------------------------------------------------------------- /examples/ts-base/src/08_inference.ts: -------------------------------------------------------------------------------- 1 | let a = 1 // a: number 2 | let b = [1] // b: number[] 3 | let b2 = [1, null] // b2: (number | null)[] 这是因为 number 和 null 是不兼容的类型 4 | // 把 tsconfig.json 中的 "strictNullChecks" 改为 false,那么 number 和 null 就可以兼容,类型推断为 b2: number[] 5 | 6 | let c = (x = 1) => { // c: (x?: number) => number 7 | return x + 1 8 | } 9 | 10 | // 上面的类型推断都是从右向左的,根据右边的值 推断类型 11 | 12 | // 上下文类型推断是从左向右的,例如 window.onkeydown 推断出 event 是 KeyboardEvent 类型 13 | window.onkeydown = (event) => { // (parameter) event: KeyboardEvent 14 | console.log(event.BUBBLING_PHASE) 15 | } 16 | 17 | // 类型断言 18 | // 有时候 ts 的类型推断不是你想要的,就可以使用类型断言覆盖类型推断 19 | 20 | interface Foo { 21 | bar: number 22 | } 23 | 24 | // let foo = {} 25 | // foo.bar = 1 // Property 'bar' does not exist on type '{}'.ts(2339) 26 | 27 | // 使用类型断言 28 | let foo = {} as Foo 29 | foo.bar = 1 30 | 31 | // 类型断言不要随便使用,如果上面的代码没有给 foo 添加 bar 属性,但是由于类型断言,并不会报, 32 | // 那么后面使用时,就可能出现错误。建议在声明时 直接指定类型 33 | let foo2: Foo = {bar: 1} -------------------------------------------------------------------------------- /examples/ts-base/src/09_compatible.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * X(目标类型) = Y(源类型),X 兼容 Y 3 | * 源类型具有目标类型的必要属性,也就是属性少的,兼容属性多的 4 | */ 5 | 6 | 7 | 8 | let s: string = 'a' 9 | 10 | // s = null // 把 tsconfig.json 中的 "strictNullChecks" 改为 false,那么 string 和 null 就可以兼容, 11 | // null 是 string 的子类型 12 | 13 | // 接口兼容性 14 | interface X { 15 | a: any; 16 | b: any; 17 | } 18 | interface Y { 19 | a: any; 20 | b: any; 21 | c: any; 22 | } 23 | let x2: X = {a: 1, b: 2} 24 | let y2: Y = {a: 1, b: 2, c: 3} 25 | 26 | x2 = y2 // Y 接口具有 X 接口的所有属性,所以 X 可以兼容 Y 类型 27 | // y2 = x2 // Property 'c' is missing in type 'X' but required in type 'Y'.ts(2741) 28 | 29 | // 函数兼容性的条件 30 | // 1. 参数个数 31 | // 对于固定参数的函数:目标函数的参数个数,要多于源函数的参数个数 32 | // 对于可选参数和 rest 参数: 33 | // 固定参数可以兼容可选参数和 rest 参数 34 | // 可选参数不兼容固定参数和 rest 参数 35 | // rest 参数可以兼容可选参数和固定参数 36 | // 2. 参数类型 37 | // 3. 返回值类型 38 | 39 | type Handler = (a: number, b: number) => void 40 | function hof(handler: Handler) { 41 | return handler 42 | } 43 | 44 | // 目标函数的参数个数,多于源函数的参数个数 45 | let handler1 = (a: number) => {} 46 | 47 | hof(handler1) 48 | 49 | let handler2 = (a: number, b: number, c: number) => {} 50 | 51 | // hof(handler2) // Argument of type '(a: number, b: number, c: number) => void' is not assignable to parameter of type 'Handler'. 52 | 53 | // 可选参数和 rest 参数 54 | let af = (p1: number, p2: number) => {} 55 | let bf = (p1?: number, p2?: number) => {} 56 | let cf = (...args: number[]) => {} 57 | 58 | // 固定参数可以兼容可选参数和 rest 参数 59 | af = cf 60 | af = bf 61 | 62 | // 可选参数不兼容固定参数和 rest 参数 63 | // 把 tsconfig.json 中的 "strictFunctionTypes" 改为 false,就可以兼容了 64 | // bf = af 65 | // bf = cf 66 | 67 | // rest 参数可以兼容可选参数和固定参数 68 | cf = af 69 | cf = bf 70 | 71 | // 参数类型不兼容 72 | let handler3 = (s: string) => {} 73 | 74 | // hof(handler3) // Types of parameters 's' and 'a' are incompatible. 75 | 76 | 77 | interface Point3D { 78 | x: number; 79 | y: number; 80 | z: number; 81 | } 82 | interface Point2D { 83 | x: number; 84 | y: number; 85 | } 86 | 87 | let p3d = (point: Point3D) => {} 88 | let p2d = (point: Point2D) => {} 89 | 90 | // 这里的接口的兼容性正好相反,接口是属性少的,兼容属性多的 91 | // 这里可以把接口的属性看做函数的参数,那就是参数多的兼容参数少的,与函数的兼容性一致 92 | p3d = p2d 93 | 94 | // p2d = p3d // Property 'z' is missing in type 'Point2D' but required in type 'Point3D'. 95 | // 把 tsconfig.json 中的 "strictFunctionTypes" 改为 false,就可以兼容了 96 | 97 | 98 | // 返回值类型 99 | let ff = () => ({name: 'Alice'}) 100 | let gf = () => ({name: 'Alice', location: 'Beijing'}) 101 | 102 | // 属性少的兼容属性多的 103 | ff = gf 104 | // gf = ff 105 | 106 | 107 | // 函数重载 108 | function overload(a: number, b: number): number 109 | function overload(a: string, b: string): string 110 | function overload(a: any, b: any): any {} 111 | 112 | // 参数个数不兼容 113 | // function overload(a: any): any {} 114 | // function overload(a: any, b: any, c: any): any {} 115 | 116 | // 参数返回值不兼容 117 | // function overload(a: any, b: any) {} 118 | 119 | 120 | // 枚举兼容性 121 | enum Fruit { Apple, Banana } 122 | enum Color { Red, Yellow } 123 | // 枚举和 number 是可以相互兼容的 124 | let fruit: Fruit.Apple = 1 125 | let no: number = Fruit.Apple 126 | 127 | // 枚举类型之间是不兼容的 128 | // let color: Color.Red = Fruit.Apple // Type 'Fruit.Apple' is not assignable to type 'Color.Red'. 129 | 130 | // 类的兼容性 和 接口的兼容性类似 131 | // 不过类的构造函数和静态属性是不参与比较的 132 | // 并且类中如果有私有属性,那么类也是不兼容的,除了父类和子类之间是兼容的 133 | class AC { 134 | constructor(p: number, q: number) {} 135 | id: number = 1 136 | private name: string = '' 137 | } 138 | class BC { 139 | static s = 1 140 | constructor(p: number) {} 141 | id: number = 2 142 | private name: string = '' 143 | } 144 | class CC extends AC {} 145 | let ac = new AC(1, 2) 146 | let bc = new BC(1) 147 | // ac = bc 148 | // bc = ac 149 | 150 | // 类中如果有私有属性,那么类也是不兼容的,除了父类和子类之间是兼容的 151 | let cc = new CC(1, 2) 152 | ac = cc 153 | cc = ac 154 | 155 | 156 | 157 | // 泛型兼容性 158 | interface Empty { 159 | // value: T 160 | } 161 | let objt1: Empty = {}; 162 | let objt2: Empty = {}; 163 | 164 | // 如果泛型接口没有属性,那么是兼容的 165 | objt1 = objt2 166 | 167 | // 泛型函数的兼容性 168 | let log1 = (x: T): T => { 169 | console.log('x') 170 | return x 171 | } 172 | let log2 = (y: U): U => { 173 | console.log('y') 174 | return y 175 | } 176 | 177 | // 泛型函数如果定义相同,那么也是可以兼容的 178 | log1 = log2 179 | 180 | -------------------------------------------------------------------------------- /examples/ts-base/src/10_guard.ts: -------------------------------------------------------------------------------- 1 | enum Type { Strong, Week } 2 | 3 | class Java { 4 | helloJava() { 5 | console.log('Hello Java') 6 | } 7 | java: any 8 | } 9 | 10 | class JavaScript { 11 | helloJavaScript() { 12 | console.log('Hello JavaScript') 13 | } 14 | js: any 15 | } 16 | 17 | function getLang(t: Type) { 18 | let lang = t === Type.Strong ? new Java() : new JavaScript() 19 | 20 | // 这里需要类型断言,否则 ts 无法判断类型 21 | if ((lang as Java).helloJava) { 22 | (lang as Java).helloJava() 23 | } else { 24 | (lang as JavaScript).helloJavaScript() 25 | } 26 | 27 | return lang 28 | } 29 | 30 | getLang(Type.Strong) // Hello Java 31 | 32 | // 上面的 getLang 方法,由于不知道程序运行时,会传入什么样的类型,就需要在每一处加上类型断言,这使代码不容易维护。 33 | // 类型保护就可以用来解决这个问题 34 | // 1. 使用 instanceof 35 | 36 | function getLang2(t: Type) { 37 | let lang = t === Type.Strong ? new Java() : new JavaScript() 38 | 39 | if (lang instanceof Java) { 40 | lang.helloJava() 41 | } else { 42 | lang.helloJavaScript() 43 | } 44 | } 45 | 46 | // 2. in 判断对象是否包含某个属性 47 | 48 | function getLang3(t: Type) { 49 | let lang = t === Type.Strong ? new Java() : new JavaScript() 50 | 51 | if ('java' in lang) { 52 | lang.helloJava() 53 | } else { 54 | lang.helloJavaScript() 55 | } 56 | } 57 | 58 | // 3. typeof 判断出类型,就可以使用类型的方法或属性 59 | function getLang4(t: Type, x: string | number) { 60 | let lang = t === Type.Strong ? new Java() : new JavaScript() 61 | 62 | if (typeof x === 'string') { 63 | x.length 64 | } else { 65 | x.toFixed() 66 | } 67 | } 68 | 69 | // 4. 创建类型保护函数 70 | function isJava(lang: Java | JavaScript): lang is Java { 71 | return (lang as Java).helloJava !== undefined 72 | } 73 | function getLang5(t: Type) { 74 | let lang = t === Type.Strong ? new Java() : new JavaScript() 75 | 76 | if (isJava(lang)) { 77 | lang.helloJava() 78 | } else { 79 | lang.helloJavaScript() 80 | } 81 | } -------------------------------------------------------------------------------- /examples/ts-base/src/index.ts: -------------------------------------------------------------------------------- 1 | import './01_datatype' 2 | import './02_enum' 3 | import './03_interface' 4 | import './04_function' 5 | import './05_class' 6 | import './06_class_interface' 7 | import './07_generics' 8 | import './10_guard' 9 | import './11_advance' 10 | 11 | let hello: string = 'Hello TypeScript' 12 | document.querySelectorAll('#app')[0].innerHTML = hello -------------------------------------------------------------------------------- /examples/ts-base/src/tmpl/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | TypeScript Examples 8 | 9 | 10 |
    11 | 12 | -------------------------------------------------------------------------------- /examples/ts-project/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "plugins": ["@typescript-eslint"], 4 | "parserOptions": { 5 | "project": "./tsconfig.json" 6 | }, 7 | "extends": [ 8 | "plugin:@typescript-eslint/recommended" 9 | ], 10 | "rules": { 11 | "@typescript-eslint/no-inferrable-types": "off" 12 | } 13 | } -------------------------------------------------------------------------------- /examples/ts-project/build/webpack.base.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | const path = require("path"); 3 | 4 | module.exports = { 5 | entry: './src/index.ts', // 入口文件 6 | output: { 7 | filename: 'app.js', // 输出文件 8 | path: path.resolve(__dirname, "../dist") // https://github.com/johnagan/clean-webpack-plugin/issues/194 9 | }, 10 | resolve: { 11 | extensions: ['.js', '.ts', '.tsx'] 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/i, 17 | use: [{ 18 | loader: 'ts-loader' // ts 文件的 loader 19 | }], 20 | exclude: /node_modules/ // 排除 node_modules 下的文件 21 | } 22 | ] 23 | }, 24 | plugins: [ 25 | new HtmlWebpackPlugin({ // 自动嵌入 app.js 26 | template: './src/tmpl/index.html' 27 | }) 28 | ] 29 | } -------------------------------------------------------------------------------- /examples/ts-project/build/webpack.config.js: -------------------------------------------------------------------------------- 1 | const { merge } = require('webpack-merge'); 2 | const baseConfig = require('./webpack.base.config') 3 | const devConfig = require('./webpack.dev.config') 4 | const proConfig = require('./webpack.pro.config') 5 | 6 | module.exports = (env, argv) => { 7 | let config = argv.mode === 'development' ? devConfig : proConfig; 8 | return merge(baseConfig, config); 9 | }; -------------------------------------------------------------------------------- /examples/ts-project/build/webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | // cheap: 忽略文件的列信息,module: 定位到 ts 源码,eval-source-map: 打包 source-map 文件 4 | // https://webpack.js.org/configuration/devtool/ 5 | // console.log(path.resolve(__dirname, '../src/namespace')); 6 | module.exports = { 7 | devtool: 'eval-cheap-module-source-map', 8 | // 这里需要配置一下静态文件目录,否则 index.html 中引用 js 文件时会报错 404 not found 9 | // https://webpack.docschina.org/configuration/dev-server/#directory 10 | devServer: { 11 | static: [ 12 | { 13 | directory: path.resolve(__dirname, '../src/namespace') 14 | }, 15 | { 16 | directory: path.resolve(__dirname, '../src/libs') 17 | }, 18 | ] 19 | }, 20 | } -------------------------------------------------------------------------------- /examples/ts-project/build/webpack.pro.config.js: -------------------------------------------------------------------------------- 1 | const { CleanWebpackPlugin } = require('clean-webpack-plugin') 2 | 3 | module.exports = { 4 | plugins: [ 5 | new CleanWebpackPlugin() // 成功构建之后清空 dist 目录 6 | ] 7 | } -------------------------------------------------------------------------------- /examples/ts-project/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-project-examples", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "./src/index.ts", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode=development --config=./build/webpack.config.js", 8 | "build": "webpack --mode=production --config=./build/webpack.config.js", 9 | "lint": "eslint src --ext .js,.ts", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "", 13 | "license": "ISC", 14 | "devDependencies": { 15 | "clean-webpack-plugin": "^4.0.0", 16 | "html-webpack-plugin": "^5.5.0", 17 | "ts-loader": "^9.3.0", 18 | "typescript": "^4.6.4", 19 | "webpack": "^5.72.1", 20 | "webpack-cli": "^4.9.2", 21 | "webpack-dev-server": "^4.9.0", 22 | "webpack-merge": "^5.8.0" 23 | }, 24 | "dependencies": { 25 | "@types/jquery": "^3.5.14", 26 | "@typescript-eslint/eslint-plugin": "^5.23.0", 27 | "@typescript-eslint/parser": "^5.23.0", 28 | "eslint": "^8.15.0", 29 | "jquery": "^3.6.0", 30 | "moment": "^2.29.3", 31 | "rxjs": "^7.8.1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/ts-project/src/es6/a.ts: -------------------------------------------------------------------------------- 1 | // 单独导出 2 | export let a = 1 3 | 4 | // 批量导出 5 | let b = 2 6 | let c = 3 7 | export { b, c } 8 | 9 | // 导出接口 10 | export interface P { 11 | x: number; 12 | y: number; 13 | } 14 | 15 | // 导出函数 16 | export function f() {} 17 | 18 | // 导出时起别名 19 | function g() {} 20 | export { g as G } 21 | 22 | // 默认导出,无需函数名,给了也没有用 23 | export default function () { 24 | console.log("I'm default") 25 | } 26 | 27 | // 引入外部模块,重新导出 28 | export { str as hello } from './b' -------------------------------------------------------------------------------- /examples/ts-project/src/es6/b.ts: -------------------------------------------------------------------------------- 1 | // 导出常量 2 | export const str = 'Hello' -------------------------------------------------------------------------------- /examples/ts-project/src/es6/c.ts: -------------------------------------------------------------------------------- 1 | import { a, b, c } from './a'; // 批量导入 2 | import { P } from './a'; // 导入接口 3 | import { f as F } from './a'; // 导入时起别名 4 | import * as All from './a'; // 导入模块中的所有成员,绑定在 All 上 5 | import myFunction from './a'; // 不加{},导入默认 6 | import defaultName from './a' 7 | 8 | console.log(a, b, c) // 1 2 3 9 | 10 | let p: P = { 11 | x: 1, 12 | y: 1 13 | } 14 | 15 | console.log(All) 16 | 17 | myFunction() // {a: 1, b: 2, c: 3, f: ƒ, G: ƒ, …} 18 | defaultName() // I'm default -------------------------------------------------------------------------------- /examples/ts-project/src/es6/d.ts: -------------------------------------------------------------------------------- 1 | // ts 提供的一种兼容 commonjs 的语法 2 | // 下面这个相当于 module.exports = ... 是顶级导出 3 | export = function () { 4 | console.log("I'm default") 5 | } 6 | 7 | // export let a = 3 // An export assignment cannot be used in a module with other exported elements.ts(2309) 8 | // 如果有多个成员需要导出,就合并到一个对象 -------------------------------------------------------------------------------- /examples/ts-project/src/index.ts: -------------------------------------------------------------------------------- 1 | // import './merge/merge' 2 | import './libs/index' 3 | 4 | let hello: string = 'Hello TypeScript Project Examples' 5 | document.querySelectorAll('#app')[0].innerHTML = hello -------------------------------------------------------------------------------- /examples/ts-project/src/libs/global.lib.d.ts: -------------------------------------------------------------------------------- 1 | declare function globalLib(options: globalLib.Options): void; 2 | 3 | // 利用了命名空间的声明合并 4 | declare namespace globalLib { 5 | const version: string; 6 | function doSomething(): void; 7 | // interface Options 可以不暴露给全局 8 | interface Options { 9 | [key: string]: any 10 | } 11 | } -------------------------------------------------------------------------------- /examples/ts-project/src/libs/global.lib.js: -------------------------------------------------------------------------------- 1 | function globalLib(options) { 2 | console.log(options); 3 | } 4 | 5 | globalLib.version = '1.0.0'; 6 | 7 | globalLib.doSomething = function() { 8 | console.log('globalLib do something'); 9 | }; 10 | 11 | // 全局类库要使用,先在 index.html 中引用 12 | // 要在 ts 中使用还需要编写声明文件 -------------------------------------------------------------------------------- /examples/ts-project/src/libs/index.ts: -------------------------------------------------------------------------------- 1 | // install @types/jquery,就不会报错 cannot find jquery 2 | import $ from 'jquery' 3 | 4 | $('#app').css('color', 'red') 5 | 6 | globalLib({x: 1}) // {x: 1} 7 | globalLib.doSomething() // globalLib do something 8 | 9 | import moduleLib from './module.lib' 10 | moduleLib({y: 2}) // {y: 2} 11 | moduleLib.doSomething() // moduleLib do something 12 | 13 | 14 | // import umdLib from './umd.lib' // 如果注释掉,会报错:'umdLib' refers to a UMD global, but the current file is a module. Consider adding an import 15 | // 将 tsconfig 中的 "allowUmdGlobalAccess": true, 打开就不回报错了 16 | // 然后可以再 index.html 中全局引用 17 | // umdLib.doSomething() // umdLib do something 18 | 19 | // 模块插件 20 | // 可以给一些类库添加自定义的方法 21 | import m from 'moment'; 22 | // 如果不声明会报错:Property 'myFunction' does not exist on type 'typeof moment'.ts(2339) 23 | declare module 'moment' { 24 | export function myFunction(): void; 25 | } 26 | m.myFunction = () => {} 27 | 28 | // 全局插件 29 | // 给全局变量添加一些自定义方法,不过会对全局的命名空间造成污染 30 | declare global { 31 | namespace globalLib { 32 | function doAnyting(): void 33 | } 34 | } 35 | globalLib.doAnyting = () => {} 36 | 37 | // 如果一个类库很大,那么声明文件可能会根据模块划分多个文件 38 | // 例如 @type/jquery 的 index.d.ts 39 | // /// // types 是模块依赖,ts 会在 @types 目录下寻找模块 40 | // /// // path 是路径依赖,相对路径 41 | // /// 42 | // /// 43 | // /// 44 | 45 | // export = jQuery; -------------------------------------------------------------------------------- /examples/ts-project/src/libs/module.lib.d.ts: -------------------------------------------------------------------------------- 1 | declare function moduleLib(options: Options): void 2 | 3 | interface Options { 4 | [key: string]: any 5 | } 6 | 7 | declare namespace moduleLib { 8 | const version: string 9 | function doSomething(): void 10 | } 11 | 12 | export = moduleLib -------------------------------------------------------------------------------- /examples/ts-project/src/libs/module.lib.js: -------------------------------------------------------------------------------- 1 | const version = '1.0.0'; 2 | 3 | function doSomething() { 4 | console.log('moduleLib do something'); 5 | } 6 | 7 | function moduleLib(options) { 8 | console.log(options); 9 | } 10 | 11 | moduleLib.version = version; 12 | moduleLib.doSomething = doSomething; 13 | 14 | module.exports = moduleLib -------------------------------------------------------------------------------- /examples/ts-project/src/libs/umd.lib.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace umdLib { 2 | const version: string 3 | function doSomething(): void 4 | } 5 | 6 | // export as namespace 专为 umd 类库设置的语句 7 | export as namespace umdLib 8 | 9 | export = umdLib -------------------------------------------------------------------------------- /examples/ts-project/src/libs/umd.lib.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof define === "function" && define.amd) { 3 | define(factory); 4 | } else if (typeof module === "object" && module.exports) { 5 | module.exports = factory(); 6 | } else { 7 | root.umdLib = factory(); 8 | } 9 | }(this, function() { 10 | return { 11 | version: '1.0.0', 12 | doSomething() { 13 | console.log('umdLib do something'); 14 | } 15 | } 16 | })); -------------------------------------------------------------------------------- /examples/ts-project/src/merge/merge.ts: -------------------------------------------------------------------------------- 1 | // 接口的声明合并 2 | interface A { 3 | x: number; 4 | 5 | // 非函数的属性要保证唯一性,如果不唯一,类型必须相同。如 y 的类型必须相同 6 | // y: string; // Subsequent property declarations must have the same type. Property 'y' must be of type 'string', but here has type 'number'.ts(2717) 7 | 8 | // 函数属性 每一个函数都会被声明为函数重载 9 | foo(bar: number): number; // 5 10 | foo(bar: 'a'): string; // 2 11 | } 12 | 13 | interface A { 14 | y: number; 15 | 16 | // 接口合并时,函数重载的顺序 17 | // 在接口内部,按照函数定义的顺序 18 | // 接口只之间,后面定义的接口,会排在前面 19 | // 如果函数的参数是字符串字面量,声明就会提升到最顶端 20 | foo(bar: string): string; // 3 21 | foo(bar: string[]): string[]; // 4 22 | foo(bar: 'b'): string; // 1 23 | } 24 | 25 | // 上面的两个接口 A 就会合并成一个接口(不同一个文件中,也会合并) 26 | // am 需要有两个接口 A 的所有属性 27 | let am: A = { 28 | x: 1, 29 | y: 2, 30 | // 实现函数重载 31 | foo(bar: any) { 32 | return bar 33 | } 34 | } 35 | 36 | 37 | // 命名空间的合并 38 | // 命名空间必须在 类 函数 的声明之后,枚举没有要求 39 | 40 | // 命名空间和类的合并 41 | class C {} 42 | namespace C { // 相当于给 C 类增加了静态属性 state 43 | export let state = 1 44 | } 45 | console.log(C.state) // 1 46 | 47 | // 命名空间和函数的合并 48 | function Lib() {} 49 | namespace Lib { // 相当于给 Lib 函数增加了 version 属性 50 | export let version = '1.0' 51 | } 52 | console.log(Lib.version) // 1.0 53 | 54 | // 命名空间和枚举的合并 55 | enum Color { 56 | Red, 57 | Yellow, 58 | Blue 59 | } 60 | namespace Color { // 相当于给枚举 Color 增加了一个方法 mix 61 | export function mix() {} 62 | } 63 | console.log(Color) // {0: 'Red', 1: 'Yellow', 2: 'Blue', Red: 0, Yellow: 1, Blue: 2, mix: ƒ} -------------------------------------------------------------------------------- /examples/ts-project/src/namespace/a.js: -------------------------------------------------------------------------------- 1 | // 在 namespace 声明的变量,只在当前 namespace 中可见,如果想要某个成员全局可见,就要使用 export 导出 2 | var Shape; 3 | (function (Shape) { 4 | var pi = Math.PI; 5 | function cricle(r) { 6 | return pi * Math.pow(r, 2); 7 | } 8 | Shape.cricle = cricle; 9 | })(Shape || (Shape = {})); 10 | -------------------------------------------------------------------------------- /examples/ts-project/src/namespace/a.ts: -------------------------------------------------------------------------------- 1 | // 在 namespace 声明的变量,只在当前 namespace 中可见,如果想要某个成员全局可见,就要使用 export 导出 2 | // 注意 namespace 中导出的成员不能重复定义 3 | namespace Shape { 4 | const pi = Math.PI 5 | export function cricle(r: number) { 6 | return pi * r ** 2 7 | } 8 | // 注意 namespace 中导出的成员不能重复定义 9 | // export function square(x: number) { 10 | // return x * x 11 | // } 12 | } -------------------------------------------------------------------------------- /examples/ts-project/src/namespace/b.js: -------------------------------------------------------------------------------- 1 | // namespace 可以拆分,分布在多个文件中,并且共享同一个 namespace 2 | /// 3 | var Shape; 4 | (function (Shape) { 5 | function square(x) { 6 | return x * x; 7 | } 8 | Shape.square = square; 9 | })(Shape || (Shape = {})); 10 | console.log(Shape.cricle(2)); 11 | console.log(Shape.square(2)); 12 | var cricle = Shape.cricle; 13 | console.log(cricle(2)); 14 | // 需要编译成 js 文件,并在 html 文件中引用 15 | -------------------------------------------------------------------------------- /examples/ts-project/src/namespace/b.ts: -------------------------------------------------------------------------------- 1 | // namespace 可以拆分,分布在多个文件中,并且共享同一个 namespace 2 | 3 | /// 4 | namespace Shape { 5 | export function square(x: number) { 6 | return x * x 7 | } 8 | } 9 | 10 | console.log(Shape.cricle(2)) 11 | console.log(Shape.square(2)) 12 | 13 | import cricle = Shape.cricle 14 | console.log(cricle(2)) 15 | 16 | 17 | // 需要编译成 js 文件,并在 html 文件中引用 18 | // tsc ./b.ts -------------------------------------------------------------------------------- /examples/ts-project/src/node/a.ts: -------------------------------------------------------------------------------- 1 | let a = { 2 | x: 1, 3 | y: 2 4 | } 5 | 6 | // 整体导出 7 | module.exports = a -------------------------------------------------------------------------------- /examples/ts-project/src/node/b.ts: -------------------------------------------------------------------------------- 1 | // exports === module.exports 2 | // 导出多个变量 3 | // module.exports = {} 4 | exports.c = 3 5 | exports.d = 4 -------------------------------------------------------------------------------- /examples/ts-project/src/node/c.ts: -------------------------------------------------------------------------------- 1 | let c1 = require('./a') 2 | let c2 = require('./b') 3 | 4 | // 不要混用 commonjs 和 es6 的导入导出,例如下面用 es6 的 export default,和 commonjs 的 require 5 | let c3 = require('../es6/a') 6 | 7 | // 使用这种方式可以导入 es6 模块 8 | // import c4 = require('../es6/d') // Import may be converted to a default import.ts(80003) 9 | // 也可以使用下面 es6 的方式直接导入 10 | import c4 from '../es6/a' 11 | 12 | console.log(c1) 13 | console.log(c2) 14 | // c3() 直接运行会报错 c3 is not a fucntion 15 | console.log(c3) // {a: 1, b: 2, c: 3, f: ƒ, G: ƒ, …} c3 是一个对象 16 | c3.default() // I'm default 17 | 18 | c4() // I'm default -------------------------------------------------------------------------------- /examples/ts-project/src/reference/new/src/client/index.ts: -------------------------------------------------------------------------------- 1 | import { getTime } from '../common' 2 | 3 | console.log(`Client Time: ${getTime()}`) 4 | 5 | class Client {} 6 | 7 | export = Client -------------------------------------------------------------------------------- /examples/ts-project/src/reference/new/src/client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/client", 5 | }, 6 | "references": [ 7 | { "path": "../common" } 8 | ] 9 | } -------------------------------------------------------------------------------- /examples/ts-project/src/reference/new/src/common/index.ts: -------------------------------------------------------------------------------- 1 | export function getTime() { 2 | let time = new Date(); 3 | return `${time.getFullYear()}-${time.getMonth() + 1}-${time.getDate()}` 4 | } -------------------------------------------------------------------------------- /examples/ts-project/src/reference/new/src/common/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/common", 5 | } 6 | } -------------------------------------------------------------------------------- /examples/ts-project/src/reference/new/src/server/index.ts: -------------------------------------------------------------------------------- 1 | import { getTime } from '../common' 2 | 3 | console.log(`Server Time: ${getTime()}`) 4 | 5 | class Server {} 6 | 7 | export = Server -------------------------------------------------------------------------------- /examples/ts-project/src/reference/new/src/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/server", 5 | }, 6 | "references": [ 7 | { "path": "../common" } 8 | ] 9 | } -------------------------------------------------------------------------------- /examples/ts-project/src/reference/new/test/client.test.ts: -------------------------------------------------------------------------------- 1 | import Client = require('../src/client') 2 | 3 | let c = new Client() 4 | 5 | // do test -------------------------------------------------------------------------------- /examples/ts-project/src/reference/new/test/server.test.ts: -------------------------------------------------------------------------------- 1 | import Server = require('../src/server') 2 | 3 | let s = new Server() 4 | 5 | // do test -------------------------------------------------------------------------------- /examples/ts-project/src/reference/new/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "references": [ 4 | { "path": "../src/client" }, 5 | { "path": "../src/server" } 6 | ] 7 | } -------------------------------------------------------------------------------- /examples/ts-project/src/reference/new/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016", 4 | "module": "commonjs", 5 | "strict": true, 6 | "composite": true, 7 | "declaration": true 8 | } 9 | } -------------------------------------------------------------------------------- /examples/ts-project/src/reference/old/src/client/index.ts: -------------------------------------------------------------------------------- 1 | import { getTime } from '../common' 2 | 3 | console.log(`Client Time: ${getTime()}`) 4 | 5 | class Client {} 6 | 7 | export = Client -------------------------------------------------------------------------------- /examples/ts-project/src/reference/old/src/common/index.ts: -------------------------------------------------------------------------------- 1 | export function getTime() { 2 | let time = new Date(); 3 | return `${time.getFullYear()}-${time.getMonth() + 1}-${time.getDate()}` 4 | } -------------------------------------------------------------------------------- /examples/ts-project/src/reference/old/src/server/index.ts: -------------------------------------------------------------------------------- 1 | import { getTime } from '../common' 2 | 3 | console.log(`Server Time: ${getTime()}`) 4 | 5 | class Server {} 6 | 7 | export = Server -------------------------------------------------------------------------------- /examples/ts-project/src/reference/old/test/client.test.ts: -------------------------------------------------------------------------------- 1 | import Client = require('../src/client') 2 | 3 | let c = new Client() 4 | 5 | // do test -------------------------------------------------------------------------------- /examples/ts-project/src/reference/old/test/server.test.ts: -------------------------------------------------------------------------------- 1 | import Server = require('../src/server') 2 | 3 | let s = new Server() 4 | 5 | // do test -------------------------------------------------------------------------------- /examples/ts-project/src/reference/old/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "strict": true, 6 | "outDir": "./dist" 7 | } 8 | } -------------------------------------------------------------------------------- /examples/ts-project/src/rxjs/concat-map.ts: -------------------------------------------------------------------------------- 1 | import {concatMap, interval, Observable, Subscriber, take} from 'rxjs'; 2 | 3 | const fakeHttp = { 4 | get: function (url: string) { 5 | return new Observable(function subscribe(subscriber: Subscriber) { 6 | const id = setTimeout(() => { 7 | subscriber.next('success'); 8 | subscriber.complete(); 9 | }, 1000); 10 | }); 11 | } 12 | } 13 | 14 | const observable = new Observable(function subscribe(subscriber: Subscriber) { 15 | interval(1000).pipe(take(5)).subscribe(() => { 16 | subscriber.next('https://fakeurl.com'); 17 | }) 18 | }); 19 | 20 | // concatMap 21 | const stream$ = observable.pipe( 22 | concatMap((url) => fakeHttp.get(url)) 23 | ); 24 | 25 | stream$.subscribe((res) => { 26 | console.log(res); 27 | }) 28 | 29 | // Output: 30 | // success 31 | // success 32 | // success 33 | // success 34 | // success 35 | -------------------------------------------------------------------------------- /examples/ts-project/src/rxjs/concat.ts: -------------------------------------------------------------------------------- 1 | import {concat, range, interval, take, map} from "rxjs"; 2 | 3 | // concat(range(1, 5), range(6, 5)).subscribe(console.log); 4 | 5 | // Output: 6 | // 1 7 | // 2 8 | // 3 9 | // 4 10 | // 5 11 | // 6 12 | // 7 13 | // 8 14 | // 9 15 | // 10 16 | 17 | // const timer = interval(1000).pipe(take(4), map(x => `${x}s`)); 18 | // const sequence = range(1, 10); 19 | // const result = concat(timer, sequence); 20 | // result.subscribe(x => console.log(x)); 21 | 22 | // Output: 23 | // 0s-> 1s-> 2s -> 3s -> 1 -> 2 ... -> 10 24 | 25 | const timer = interval(1000).pipe(take(2)); 26 | 27 | concat(timer, timer) // concatenating the same Observable! 28 | .subscribe({ 29 | next: value => console.log(value), 30 | complete: () => console.log('...and it is done!') 31 | }); 32 | 33 | // Logs: 34 | // 0 after 1s 35 | // 1 after 2s 36 | // 0 after 3s 37 | // 1 after 4s 38 | // '...and it is done!' also after 4s 39 | -------------------------------------------------------------------------------- /examples/ts-project/src/rxjs/delay.ts: -------------------------------------------------------------------------------- 1 | import {interval, take} from "rxjs" 2 | import {delay} from "rxjs/operators" 3 | 4 | const start = new Date(); 5 | interval(500) 6 | .pipe( 7 | take(3), 8 | delay(3000) 9 | ) 10 | .subscribe((v) => { 11 | console.log('val', v); 12 | // @ts-ignore 13 | console.log(new Date() - start); 14 | }) 15 | -------------------------------------------------------------------------------- /examples/ts-project/src/rxjs/every.ts: -------------------------------------------------------------------------------- 1 | import { range } from "rxjs" 2 | import { every } from "rxjs/operators" 3 | 4 | range(1, 9) 5 | .pipe( 6 | every(n => n > 0) 7 | ) 8 | .subscribe(console.log) 9 | -------------------------------------------------------------------------------- /examples/ts-project/src/rxjs/exhaust-all.ts: -------------------------------------------------------------------------------- 1 | import { of, interval } from 'rxjs'; 2 | import { exhaustAll, map, take } from 'rxjs/operators'; 3 | 4 | // 模拟内部 Observable(例如 HTTP 请求) 5 | const simulateAsyncTask = (id: number) => { 6 | return of(`Request ${id} completed`).pipe( 7 | // 模拟任务延迟 8 | map(val => val), 9 | take(1) // 模拟每个请求只会发出一个值,然后完成 10 | ); 11 | }; 12 | 13 | // 创建一个源 Observable,发出多个 Observable 14 | const source$ = of( 15 | simulateAsyncTask(1), 16 | simulateAsyncTask(2), 17 | simulateAsyncTask(3), 18 | simulateAsyncTask(4) 19 | ); 20 | 21 | // 使用 exhaustAll 来处理内部 Observable 22 | const result$ = source$.pipe( 23 | exhaustAll() // 忽略当前任务未完成时的其他任务 24 | ); 25 | 26 | // 订阅并打印输出 27 | result$.subscribe(value => console.log(value)); 28 | -------------------------------------------------------------------------------- /examples/ts-project/src/rxjs/filter.ts: -------------------------------------------------------------------------------- 1 | import {range} from "rxjs"; 2 | import {filter} from "rxjs/operators"; 3 | 4 | // filter 对数据流进行过滤 5 | range(1, 10).pipe(filter((n) => n > 5)) 6 | .subscribe((even) => console.log(even)); 7 | 8 | // Output: 9 | // 6 10 | // 7 11 | // 8 12 | // 9 13 | // 10 14 | -------------------------------------------------------------------------------- /examples/ts-project/src/rxjs/last.ts: -------------------------------------------------------------------------------- 1 | import {interval} from "rxjs" 2 | import {last} from "rxjs/operators" 3 | 4 | interval(1000).pipe(last()).subscribe(console.log) 5 | -------------------------------------------------------------------------------- /examples/ts-project/src/rxjs/map.ts: -------------------------------------------------------------------------------- 1 | import {map, interval, take} from "rxjs"; 2 | 3 | const timer = interval(1000).pipe(take(5)); 4 | 5 | // map 对数据流进行转换,基于原有值进行转换。 6 | const positions = timer.pipe(map(ev => ev * 10)); 7 | positions.subscribe(x => console.log(x)); 8 | 9 | // Output: 10 | // 0 11 | // 10 12 | // 20 13 | // 30 14 | // 40 15 | -------------------------------------------------------------------------------- /examples/ts-project/src/rxjs/merge-map.ts: -------------------------------------------------------------------------------- 1 | import { of } from 'rxjs'; 2 | import { mergeAll } from 'rxjs/operators'; 3 | 4 | // 创建一个包含多个内部 Observables 的高阶 Observable 5 | const source$ = of( 6 | of('A', 'B', 'C'), 7 | of('D', 'E'), 8 | of('F', 'G') 9 | ); 10 | 11 | // 使用 mergeAll 将这些内部 Observables 合并为一个单一的流 12 | const result$ = source$.pipe(mergeAll()); 13 | 14 | // 订阅并打印输出 15 | result$.subscribe(value => console.log(value)); 16 | -------------------------------------------------------------------------------- /examples/ts-project/src/rxjs/merge.ts: -------------------------------------------------------------------------------- 1 | import {merge, interval, take, map} from "rxjs"; 2 | 3 | const timer = interval(1000).pipe(take(3), map(v => 'timer1: ' + v + 's')); 4 | const timer2 = interval(1000).pipe(take(3), map(v => 'timer2: ' + v + 's')); 5 | 6 | // 合并数据流,多个参数一起发出数据流,按照时间线进行交叉合并 7 | merge(timer, timer2).subscribe(console.log); 8 | 9 | // Output: 10 | // timer1: 0s 11 | // timer2: 0s 12 | // timer1: 1s 13 | // timer2: 1s 14 | // timer1: 2s 15 | // timer2: 2s 16 | 17 | -------------------------------------------------------------------------------- /examples/ts-project/src/rxjs/range.ts: -------------------------------------------------------------------------------- 1 | import { range } from "rxjs"; 2 | 3 | range(1, 10).subscribe((n) => console.log(n)); 4 | 5 | // 0 6 | // 1 7 | // 2 8 | // 3 9 | // 4 10 | -------------------------------------------------------------------------------- /examples/ts-project/src/rxjs/reduce.ts: -------------------------------------------------------------------------------- 1 | import {interval} from "rxjs" 2 | import {take, reduce} from "rxjs/operators" 3 | 4 | interval(100) 5 | .pipe( 6 | take(5), 7 | reduce((acc, value) => acc += value, 0) 8 | ) 9 | .subscribe(console.log) 10 | -------------------------------------------------------------------------------- /examples/ts-project/src/rxjs/retry.ts: -------------------------------------------------------------------------------- 1 | import {of, throwError, timer} from 'rxjs'; 2 | import {delay, retry} from 'rxjs/operators'; 3 | 4 | // 模拟一个会发出错误的 Observable 5 | const source$ = throwError(() => new Error('Unknown Error')); 6 | 7 | // 定义一个控制延迟的 Observable 8 | // const notify$ = timer(1000); // 每次重试之前等待 1 秒 9 | 10 | // 动态计算每次重试的延迟:指数退避(Exponential Backoff) 11 | // const retryDelay = (attempt: number) => timer(Math.pow(2, attempt) * 1000); // 每次重试间隔 2^attempt 秒 12 | 13 | // 条件重试 14 | const retryDelay = (err: any) => { 15 | if (err.message === 'Network Error') { 16 | // 如果是网络错误,延迟 1 秒 17 | console.log('Network Error: Retrying...'); 18 | return of(err).pipe(delay(1000)); 19 | } else if (err.message === 'Timeout Error') { 20 | // 如果是超时错误,延迟 2 秒 21 | console.log('Timeout Error: Retrying...'); 22 | return of(err).pipe(delay(2000)); 23 | } else { 24 | // 其他错误直接抛出 25 | return throwError(() => err); 26 | } 27 | } 28 | 29 | // 使用 retry 操作符并指定 delay 属性 30 | const retried$ = source$.pipe( 31 | retry({ 32 | count: 3, 33 | // delay: () => notify$ // 每次重试之间延迟 1 秒 34 | delay: (error, attempt) => retryDelay(error) 35 | }) 36 | ); 37 | 38 | // 订阅并打印输出 39 | retried$.subscribe({ 40 | next: value => console.log(value), 41 | error: err => console.error('Error:', err.message), 42 | complete: () => console.log('Completed!') 43 | }); 44 | -------------------------------------------------------------------------------- /examples/ts-project/src/rxjs/skip.ts: -------------------------------------------------------------------------------- 1 | import {range, timer, interval} from "rxjs" 2 | import {skipWhile, skipUntil} from "rxjs/operators" 3 | 4 | // range(1, 10) 5 | // .pipe(skipWhile(n => n < 5)) 6 | // .subscribe(console.log) 7 | 8 | interval(500) 9 | .pipe(skipUntil(timer(2000))) 10 | .subscribe(console.log) 11 | -------------------------------------------------------------------------------- /examples/ts-project/src/rxjs/switch-map.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipengqi/frontend-learn/73b0f3852f0f5ab837ac6a46b78ce6c828c48a4e/examples/ts-project/src/rxjs/switch-map.ts -------------------------------------------------------------------------------- /examples/ts-project/src/rxjs/switch.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipengqi/frontend-learn/73b0f3852f0f5ab837ac6a46b78ce6c828c48a4e/examples/ts-project/src/rxjs/switch.ts -------------------------------------------------------------------------------- /examples/ts-project/src/rxjs/take.ts: -------------------------------------------------------------------------------- 1 | import {range, interval, timer} from "rxjs" 2 | import {take, takeWhile, takeUntil} from "rxjs/operators" 3 | 4 | // range(1, 10).pipe(take(3)).subscribe(console.log) 5 | 6 | // range(1, 10) 7 | // .pipe(takeWhile(n => n < 8)) 8 | // .subscribe(console.log) 9 | 10 | interval(1000) 11 | .pipe(takeUntil(timer(5000))) 12 | .subscribe(console.log) 13 | -------------------------------------------------------------------------------- /examples/ts-project/src/tmpl/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | TypeScript Examples 8 | 9 | 10 |
    11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/shipengqi/frontend-learn 2 | 3 | go 1.23.1 4 | 5 | require ( 6 | github.com/imfing/hextra v0.9.0 // indirect 7 | ) 8 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/imfing/hextra v0.9.0 h1:1UyLZgS1eayce2ETCOjAQssXpkRz3HDrIs/fljH0lkU= 2 | github.com/imfing/hextra v0.9.0/go.mod h1:cEfel3lU/bSx7lTE/+uuR4GJaphyOyiwNR3PTqFTXpI= 3 | -------------------------------------------------------------------------------- /hugo.yaml: -------------------------------------------------------------------------------- 1 | baseURL: "https://shipengqi.github.io/frontend-learn" 2 | title: "Frontend Learning" 3 | 4 | module: 5 | imports: 6 | - path: github.com/imfing/hextra 7 | 8 | enableRobotsTXT: true 9 | enableGitInfo: true 10 | # enableEmoji: false 11 | hasCJKLanguage: true 12 | 13 | # services: 14 | # googleAnalytics: 15 | # ID: G-MEASUREMENT_ID 16 | 17 | outputs: 18 | home: [HTML] 19 | page: [HTML] 20 | section: [HTML, RSS] 21 | 22 | defaultContentLanguage: zh-cn 23 | languages: 24 | # en: 25 | # languageName: English 26 | # weight: 1 27 | # title: Hextra 28 | zh-cn: 29 | languageName: 简体中文 30 | languageCode: zh-CN 31 | weight: 2 32 | title: Frontend Learning 33 | 34 | markup: 35 | goldmark: 36 | renderer: 37 | unsafe: true 38 | highlight: 39 | noClasses: false 40 | 41 | enableInlineShortcodes: true 42 | 43 | 44 | menu: 45 | # masthead navigation 46 | main: 47 | - identifier: documentation 48 | name: Documentation 49 | pageRef: /docs/guide 50 | weight: 1 51 | - identifier: blog 52 | name: Blog 53 | url: "https://shipengqi.github.io" 54 | weight: 2 55 | - name: Search 56 | weight: 3 57 | params: 58 | type: search 59 | - name: GitHub 60 | weight: 4 61 | url: "https://github.com/shipengqi/frontend-learn" 62 | params: 63 | icon: github 64 | # documentation side navigation 65 | # sidebar: 66 | # - identifier: guide 67 | # name: Guide 68 | # params: 69 | # type: separator 70 | # weight: 1 71 | # - identifier: framework 72 | # name: "Framework" 73 | # weight: 2 74 | 75 | params: 76 | description: Modern, responsive, batteries-included Hugo theme for creating beautiful static websites. 77 | 78 | navbar: 79 | displayTitle: true 80 | displayLogo: true 81 | logo: 82 | path: images/logo.svg 83 | dark: images/logo-dark.svg 84 | # width: 40 85 | # height: 20 86 | # link: / 87 | width: wide 88 | 89 | page: 90 | # full (100%), wide (90rem), normal (1280px) 91 | width: normal 92 | 93 | theme: 94 | # light | dark | system 95 | default: system 96 | displayToggle: true 97 | 98 | footer: 99 | enable: true 100 | displayCopyright: true 101 | displayPoweredBy: true 102 | width: normal 103 | 104 | displayUpdatedDate: true 105 | dateFormat: "January 2, 2006" 106 | 107 | # Search 108 | # flexsearch is enabled by default 109 | search: 110 | enable: true 111 | type: flexsearch 112 | 113 | flexsearch: 114 | # index page by: content | summary | heading | title 115 | index: content 116 | # full | forward | reverse | strict 117 | # https://github.com/nextapps-de/flexsearch/#tokenizer-prefix-search 118 | tokenize: forward 119 | 120 | editURL: 121 | enable: true 122 | base: "https://github.com/shipengqi/frontend-learn/tree/master/content" 123 | 124 | blog: 125 | list: 126 | displayTags: true 127 | # date | lastmod | publishDate | title | weight 128 | sortBy: date 129 | sortOrder: desc # or "asc" 130 | 131 | highlight: 132 | copy: 133 | enable: true 134 | # hover | always 135 | display: hover 136 | 137 | # comments disabled 138 | comments: 139 | enable: false 140 | type: giscus 141 | 142 | # https://giscus.app/ 143 | giscus: 144 | repo: shipengqi/frontend-learn 145 | repoId: R_kgDOJ9fJag 146 | category: General 147 | categoryId: DIC_kwDOJ9fJas4CY7gW 148 | # mapping: pathname 149 | # strict: 0 150 | # reactionsEnabled: 1 151 | # emitMetadata: 0 152 | # inputPosition: top 153 | # lang: en 154 | -------------------------------------------------------------------------------- /i18n/zh-cn.yaml: -------------------------------------------------------------------------------- 1 | copyright: "© 2025 PengQi Shi." 2 | -------------------------------------------------------------------------------- /layouts/partials/custom/head-end.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | --------------------------------------------------------------------------------