├── .gitignore ├── README.md ├── build └── webpack.config.js ├── docs ├── .vuepress │ ├── config.js │ └── public │ │ └── images │ │ ├── composition API VS Option API_r.jpg │ │ ├── favicon.ico │ │ ├── lifecycle_2.png │ │ ├── lifecycle_3.png │ │ ├── logo.png │ │ ├── option API.gif │ │ ├── option API2.gif │ │ ├── ts.png │ │ ├── ts_v3.png │ │ ├── v3_ts.JPG │ │ ├── vue3_logo.png │ │ ├── 基于函数组合的API.gif │ │ └── 基于函数组合的API2.gif ├── 00_课程介绍.md ├── README.md ├── chapter1 │ ├── 01_初识TS.md │ ├── 02_安装TS.md │ ├── 03_HelloWorld.md │ └── 04_webpack打包.md ├── chapter2 │ ├── 1_type.md │ ├── 2_interface.md │ ├── 3_class.md │ ├── 4_function.md │ ├── 5_generic.md │ └── 6_other.md ├── chapter3 │ ├── 01_认识Vue3.md │ └── 02_创建vue3项目.md ├── chapter4 │ ├── 01_Composition API_常用部分.md │ ├── 02_Composition API_其它部分.md │ ├── 03_手写组合API.md │ └── 04_Composition VS Option.md ├── chapter5 │ ├── 01_新组件.md │ └── 02_其他新API.md ├── chapter6 │ └── README.md └── chapter7 │ └── 快速搭建在线文档.md ├── package-lock.json ├── package.json ├── public └── index.html ├── src └── main.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 笔记 2 | ## 使用VuePress搭建github在线blog 3 | - 创建一个远程仓库 xxx_ts_study 4 | - 修改docs/.vuepress/config.js: base: '/xxx_ts_study/' 5 | - 打包文档: npm run doc:build 6 | - 将项目推送到github 7 | - git init 8 | - git add . 9 | - git commit -m "init" 10 | - git push origin master 11 | - 发布文档: npm run doc:deploy 12 | - 访问在线文档: https://24kcs.github.io/xxx_ts_study/ (可能要等待一定的时间) -------------------------------------------------------------------------------- /build/webpack.config.js: -------------------------------------------------------------------------------- 1 | const {CleanWebpackPlugin} = require('clean-webpack-plugin') 2 | const HtmlWebpackPlugin = require('html-webpack-plugin') 3 | const path = require('path') 4 | 5 | const isProd = process.env.NODE_ENV === 'production' // 是否生产环境 6 | 7 | function resolve (dir) { 8 | return path.resolve(__dirname, '..', dir) 9 | } 10 | 11 | module.exports = { 12 | mode: isProd ? 'production' : 'development', 13 | entry: { 14 | app: './src/main.ts' 15 | }, 16 | 17 | output: { 18 | filename: '[name].[contenthash:8].js' 19 | }, 20 | 21 | module: { 22 | rules: [ 23 | { 24 | test: /\.tsx?$/, 25 | use: 'ts-loader', 26 | include: [resolve('src')] 27 | } 28 | ] 29 | }, 30 | 31 | plugins: [ 32 | new CleanWebpackPlugin({ 33 | }), 34 | 35 | new HtmlWebpackPlugin({ 36 | template: './public/index.html' 37 | }) 38 | ], 39 | 40 | resolve: { 41 | extensions: ['.ts', '.tsx', '.js'] 42 | }, 43 | 44 | devtool: isProd ? 'cheap-module-source-map' : 'cheap-module-eval-source-map', 45 | 46 | devServer: { 47 | contentBase: './dist', // 服务器加载资源的基础路径 48 | host: 'localhost', // 主机名 49 | stats: 'errors-only', // 打包日志输出输出错误信息 50 | port: 8081, 51 | open: true 52 | }, 53 | } -------------------------------------------------------------------------------- /docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | base: '/vue3_study/', /* 基础虚拟路径 */ 3 | dest: 'docs/dist', /* 打包文件基础路径, 在命令所在目录下 */ 4 | title: 'Vue3+TS 快速上手', // 标题 5 | description: '尚硅谷前端研究院', // 标题下的描述 6 | themeConfig: { // 主题配置 7 | logo: '/images/logo.png', 8 | nav: [ 9 | { text: '官网', link: 'http://www.atguigu.com' }, 10 | { text: '谷粒学院', link: 'http://www.gulixueyuan.com/' }, 11 | { 12 | text: '学习路线', 13 | items: [ 14 | { text: '前端', link: 'http://www.atguigu.com/web/' }, 15 | { text: 'Java', link: 'http://www.atguigu.com/kecheng.shtml' }, 16 | { text: '大数据', link: 'http://www.atguigu.com/bigdata/' } 17 | ] 18 | }, 19 | { text: '全套视频资料', link: 'http://www.gulixueyuan.com/' }, 20 | ], 21 | sidebar: [ // 左侧导航 22 | '00_课程介绍', 23 | { 24 | title: '一.TypeScript快速上手', 25 | collapsable: false, 26 | children: [ 27 | { 28 | title: '初识 TypeScript', // 标题 29 | children: [ // 下级列表 30 | 'chapter1/01_初识TS', 31 | 'chapter1/02_安装TS', 32 | 'chapter1/03_HelloWorld', 33 | 'chapter1/04_webpack打包', 34 | ] 35 | }, 36 | { 37 | title: 'TypeScript 常用语法', 38 | children: [ 39 | 'chapter2/1_type', 40 | 'chapter2/2_interface', 41 | 'chapter2/3_class', 42 | 'chapter2/4_function', 43 | 'chapter2/5_generic', 44 | 'chapter2/6_other', 45 | ] 46 | }, 47 | ] 48 | }, 49 | 50 | { 51 | title: '二.Vue3快速上手', 52 | collapsable: false, 53 | children: [ 54 | 'chapter3/01_认识Vue3', 55 | 'chapter3/02_创建vue3项目', 56 | ] 57 | }, 58 | { 59 | title: '三.Composition API', 60 | collapsable: false, 61 | children: [ 62 | 'chapter4/01_Composition API_常用部分', 63 | 'chapter4/02_Composition API_其它部分', 64 | 'chapter4/03_手写组合API', 65 | 'chapter4/04_Composition VS Option', 66 | ] 67 | }, 68 | { 69 | title: '四.其它新组合和API', 70 | collapsable: false, 71 | children: [ 72 | 'chapter5/01_新组件', 73 | 'chapter5/02_其他新API', 74 | ] 75 | }, 76 | { 77 | title: '五.Vue3综合案例', 78 | collapsable: false, 79 | children: [ 80 | 'chapter6/', 81 | ] 82 | }, 83 | 'chapter7/快速搭建在线文档' 84 | ] 85 | }, 86 | 87 | head: [ 88 | ['link', { rel: 'shortcut icon', type: "image/x-icon", href: `./images/favicon.ico` }] 89 | ] 90 | } -------------------------------------------------------------------------------- /docs/.vuepress/public/images/composition API VS Option API_r.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/24kcs/vue3_study/c868e9d3d714631b2b314bb7f1a9d09b15ca66f6/docs/.vuepress/public/images/composition API VS Option API_r.jpg -------------------------------------------------------------------------------- /docs/.vuepress/public/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/24kcs/vue3_study/c868e9d3d714631b2b314bb7f1a9d09b15ca66f6/docs/.vuepress/public/images/favicon.ico -------------------------------------------------------------------------------- /docs/.vuepress/public/images/lifecycle_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/24kcs/vue3_study/c868e9d3d714631b2b314bb7f1a9d09b15ca66f6/docs/.vuepress/public/images/lifecycle_2.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/lifecycle_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/24kcs/vue3_study/c868e9d3d714631b2b314bb7f1a9d09b15ca66f6/docs/.vuepress/public/images/lifecycle_3.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/24kcs/vue3_study/c868e9d3d714631b2b314bb7f1a9d09b15ca66f6/docs/.vuepress/public/images/logo.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/option API.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/24kcs/vue3_study/c868e9d3d714631b2b314bb7f1a9d09b15ca66f6/docs/.vuepress/public/images/option API.gif -------------------------------------------------------------------------------- /docs/.vuepress/public/images/option API2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/24kcs/vue3_study/c868e9d3d714631b2b314bb7f1a9d09b15ca66f6/docs/.vuepress/public/images/option API2.gif -------------------------------------------------------------------------------- /docs/.vuepress/public/images/ts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/24kcs/vue3_study/c868e9d3d714631b2b314bb7f1a9d09b15ca66f6/docs/.vuepress/public/images/ts.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/ts_v3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/24kcs/vue3_study/c868e9d3d714631b2b314bb7f1a9d09b15ca66f6/docs/.vuepress/public/images/ts_v3.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/v3_ts.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/24kcs/vue3_study/c868e9d3d714631b2b314bb7f1a9d09b15ca66f6/docs/.vuepress/public/images/v3_ts.JPG -------------------------------------------------------------------------------- /docs/.vuepress/public/images/vue3_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/24kcs/vue3_study/c868e9d3d714631b2b314bb7f1a9d09b15ca66f6/docs/.vuepress/public/images/vue3_logo.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/基于函数组合的API.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/24kcs/vue3_study/c868e9d3d714631b2b314bb7f1a9d09b15ca66f6/docs/.vuepress/public/images/基于函数组合的API.gif -------------------------------------------------------------------------------- /docs/.vuepress/public/images/基于函数组合的API2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/24kcs/vue3_study/c868e9d3d714631b2b314bb7f1a9d09b15ca66f6/docs/.vuepress/public/images/基于函数组合的API2.gif -------------------------------------------------------------------------------- /docs/00_课程介绍.md: -------------------------------------------------------------------------------- 1 | # 课程指南 2 | v3 3 | 4 | 5 | 6 | 课程介绍:Vue是一套用于构建用户界面的**渐进式框架**。Vue.js 3.0 "One Piece" 正式版在2020年9月份发布,经过了2年多开发, 100+位贡献者, 2600+次提交, 600+次PR,同时Vue3也支持Vue2的大多数特性,且,更好的支持了TypeScript,也增加了很多的新特性,如:Composition API,新组件(Fragment/Teleport/Suspense)等等. 7 | 课程内容如下: 8 | 9 | #### 1.TypeScript 快速上手 10 | 11 | #### 2.Vue3快速上手 12 | 13 | #### 3.Vue3新特性 14 | 15 | #### 4.Vue3综合案例 16 | 17 | #### 5.Vue3 企业级项目(待发布) 18 | 19 | 注:由于Vue3中可以更好的支持TypeScript内容,且,课程内容中涉及到TS的内容,鉴于部分学员对于TS并不是很了解,所以,课程内容先从TS开始讲解 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | #首页 3 | home: true 4 | # 图标 5 | heroImage: /images/vue3_logo.png 6 | # 按钮文本 7 | actionText: 开始学习 → 8 | # 按钮点击跳转路径 9 | actionLink: 00_课程介绍.md 10 | --- -------------------------------------------------------------------------------- /docs/chapter1/01_初识TS.md: -------------------------------------------------------------------------------- 1 | # 1. 初识 TypeScript 2 | 3 | ![TS与JS.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/28ca61cc160c417c8497a00defdca5f0~tplv-k3u1fbpfcp-watermark.image) 4 | 5 | ## TypeScript 的介绍 6 | 7 | TypeScript是一种由微软开发的开源、跨平台的编程语言。它是JavaScript的超集,最终会被编译为JavaScript代码。 8 | 9 | 2012年10月,微软发布了首个公开版本的TypeScript,2013年6月19日,在经历了一个预览版之后微软正式发布了正式版TypeScript 10 | 11 | TypeScript的作者是安德斯·海尔斯伯格,C#的首席架构师。它是开源和跨平台的编程语言。 12 | 13 | TypeScript扩展了JavaScript的语法,所以任何现有的JavaScript程序可以运行在TypeScript环境中。 14 | 15 | TypeScript是为大型应用的开发而设计,并且可以编译为JavaScript。 16 | 17 | TypeScript 是 JavaScript 的一个超集,主要提供了类型系统和对 ES6+ 的支持**,它由 Microsoft 开发,代码开源于 GitHub 上 18 | 19 | **TypeScript 是 JavaScript 的一个超集**,主要提供了**类型系统**和**对 ES6+ 的支持**,它由 Microsoft 开发,代码[开源于 GitHub](https://github.com/Microsoft/TypeScript) 上 20 | 21 | ## TypeScript 的特点 22 | 23 | TypeScript 主要有 3 大特点: 24 | 25 | - **始于JavaScript,归于JavaScript** 26 | 27 | TypeScript 可以编译出纯净、 简洁的 JavaScript 代码,并且可以运行在任何浏览器上、Node.js 环境中和任何支持 ECMAScript 3(或更高版本)的JavaScript 引擎中。 28 | 29 | - **强大的类型系统** 30 | 31 | **类型系统**允许 JavaScript 开发者在开发 JavaScript 应用程序时使用高效的开发工具和常用操作比如静态检查和代码重构。 32 | 33 | - **先进的 JavaScript** 34 | 35 | TypeScript 提供最新的和不断发展的 JavaScript 特性,包括那些来自 2015 年的 ECMAScript 和未来的提案中的特性,比如异步功能和 Decorators,以帮助建立健壮的组件。 36 | 37 | ## 总结 38 | 39 | TypeScript 在社区的流行度越来越高,它非常适用于一些大型项目,也非常适用于一些基础库,极大地帮助我们提升了开发效率和体验。 40 | -------------------------------------------------------------------------------- /docs/chapter1/02_安装TS.md: -------------------------------------------------------------------------------- 1 | # 2. 安装 TypeScript 2 | 3 | 命令行运行如下命令,全局安装 TypeScript: 4 | 5 | ```bash 6 | npm install -g typescript 7 | ``` 8 | 9 | 安装完成后,在控制台运行如下命令,检查安装是否成功(3.x): 10 | 11 | ```bash 12 | tsc -V 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/chapter1/03_HelloWorld.md: -------------------------------------------------------------------------------- 1 | # 3. 第一个 TypeScript 程序 2 | 3 | ## 编写 TS 程序 4 | src/helloworld.ts 5 | 6 | ```typescript 7 | function greeter (person) { 8 | return 'Hello, ' + person 9 | } 10 | 11 | let user = 'Yee' 12 | 13 | console.log(greeter(user)) 14 | ``` 15 | 16 | ## 手动编译代码 17 | 18 | 我们使用了 `.ts` 扩展名,但是这段代码仅仅是 JavaScript 而已。 19 | 20 | 在命令行上,运行 TypeScript 编译器: 21 | 22 | ```bash 23 | tsc helloworld.ts 24 | ``` 25 | 26 | 输出结果为一个 `helloworld.js` 文件,它包含了和输入文件中相同的 JavsScript 代码。 27 | 28 | 在命令行上,通过 Node.js 运行这段代码: 29 | 30 | ```bash 31 | node helloworld.js 32 | ``` 33 | 34 | 控制台输出: 35 | 36 | ``` 37 | Hello, Yee 38 | ``` 39 | 40 | ## vscode自动编译 41 | 42 | 1). 生成配置文件tsconfig.json 43 | tsc --init 44 | 2). 修改tsconfig.json配置 45 | "outDir": "./js", 46 | "strict": false, 47 | 3). 启动监视任务: 48 | 终端 -> 运行任务 -> 监视tsconfig.json 49 | 50 | ## 类型注解 51 | 52 | 接下来让我们看看 TypeScript 工具带来的高级功能。 给 `person` 函数的参数添加 `: string` 类型注解,如下: 53 | 54 | ```typescript 55 | function greeter (person: string) { 56 | return 'Hello, ' + person 57 | } 58 | 59 | let user = 'Yee' 60 | 61 | console.log(greeter(user)) 62 | ``` 63 | 64 | TypeScript 里的类型注解是一种轻量级的为函数或变量添加约束的方式。 在这个例子里,我们希望 `greeter` 函数接收一个字符串参数。 然后尝试把 `greeter` 的调用改成传入一个数组: 65 | 66 | ```typescript 67 | function greeter (person: string) { 68 | return 'Hello, ' + person 69 | } 70 | 71 | let user = [0, 1, 2] 72 | 73 | console.log(greeter(user)) 74 | ``` 75 | 76 | 重新编译,你会看到产生了一个错误: 77 | 78 | ``` 79 | error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'. 80 | ``` 81 | 82 | 类似地,尝试删除 `greeter` 调用的所有参数。 TypeScript 会告诉你使用了非期望个数的参数调用了这个函数。 在这两种情况中,TypeScript提供了静态的代码分析,它可以分析代码结构和提供的类型注解。 83 | 84 | 要注意的是尽管有错误,`greeter.js` 文件还是被创建了。 就算你的代码里有错误,你仍然可以使用 TypeScript。但在这种情况下,TypeScript 会警告你代码可能不会按预期执行。 85 | 86 | ## 接口 87 | 88 | 让我们继续扩展这个示例应用。这里我们使用接口来描述一个拥有 `firstName` 和 `lastName` 字段的对象。 在 `TypeScript` 里,只在两个类型内部的结构兼容,那么这两个类型就是兼容的。 这就允许我们在实现接口时候只要保证包含了接口要求的结构就可以,而不必明确地使用 `implements` 语句。 89 | 90 | ```typescript 91 | interface Person { 92 | firstName: string 93 | lastName: string 94 | } 95 | 96 | function greeter (person: Person) { 97 | return 'Hello, ' + person.firstName + ' ' + person.lastName 98 | } 99 | 100 | let user = { 101 | firstName: 'Yee', 102 | lastName: 'Huang' 103 | } 104 | 105 | console.log(greeter(user)) 106 | ``` 107 | 108 | ## 类 109 | 110 | 最后,让我们使用类来改写这个例子。 TypeScript 支持 JavaScript 的新特性,比如支持基于类的面向对象编程。 111 | 112 | 让我们创建一个 `User` 类,它带有一个构造函数和一些公共字段。因为类的字段包含了接口所需要的字段,所以他们能很好的兼容。 113 | 114 | 还要注意的是,我在类的声明上会注明所有的成员变量,这样比较一目了然。 115 | 116 | ```typescript 117 | class User { 118 | fullName: string 119 | firstName: string 120 | lastName: string 121 | 122 | constructor (firstName: string, lastName: string) { 123 | this.firstName = firstName 124 | this.lastName = lastName 125 | this.fullName = firstName + ' ' + lastName 126 | } 127 | } 128 | 129 | interface Person { 130 | firstName: string 131 | lastName: string 132 | } 133 | 134 | function greeter (person: Person) { 135 | return 'Hello, ' + person.firstName + ' ' + person.lastName 136 | } 137 | 138 | let user = new User('Yee', 'Huang') 139 | 140 | console.log(greeter(user)) 141 | ``` 142 | 143 | 重新运行 `tsc greeter.ts`,你会看到 TypeScript 里的类只是一个语法糖,本质上还是 `JavaScript` 函数的实现。 144 | 145 | ## 总结 146 | 147 | 到这里,你已经对 TypeScript 有了一个大致的印象,那么下一章让我们来一起学习 TypeScript 的一些常用语法吧。 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /docs/chapter1/04_webpack打包.md: -------------------------------------------------------------------------------- 1 | # 4. 使用webpack打包TS 2 | 3 | ## 下载依赖 4 | 5 | ``` 6 | yarn add -D typescript 7 | yarn add -D webpack webpack-cli 8 | yarn add -D webpack-dev-server 9 | yarn add -D html-webpack-plugin clean-webpack-plugin 10 | yarn add -D ts-loader 11 | yarn add -D cross-env 12 | ``` 13 | 14 | ## 入口JS: src/main.ts 15 | 16 | ```typescript 17 | // import './01_helloworld' 18 | 19 | document.write('Hello Webpack TS!') 20 | ``` 21 | 22 | ## index页面: public/index.html 23 | 24 | ```html 25 | 26 | 27 | 28 | 29 | 30 | 31 | webpack & TS 32 | 33 | 34 | 35 | 36 | 37 | ``` 38 | 39 | ## build/webpack.config.js 40 | 41 | ```javascript 42 | const {CleanWebpackPlugin} = require('clean-webpack-plugin') 43 | const HtmlWebpackPlugin = require('html-webpack-plugin') 44 | const path = require('path') 45 | 46 | const isProd = process.env.NODE_ENV === 'production' // 是否生产环境 47 | 48 | function resolve (dir) { 49 | return path.resolve(__dirname, '..', dir) 50 | } 51 | 52 | module.exports = { 53 | mode: isProd ? 'production' : 'development', 54 | entry: { 55 | app: './src/main.ts' 56 | }, 57 | 58 | output: { 59 | path: resolve('dist'), 60 | filename: '[name].[contenthash:8].js' 61 | }, 62 | 63 | module: { 64 | rules: [ 65 | { 66 | test: /\.tsx?$/, 67 | use: 'ts-loader', 68 | include: [resolve('src')] 69 | } 70 | ] 71 | }, 72 | 73 | plugins: [ 74 | new CleanWebpackPlugin({ 75 | }), 76 | 77 | new HtmlWebpackPlugin({ 78 | template: './public/index.html' 79 | }) 80 | ], 81 | 82 | resolve: { 83 | extensions: ['.ts', '.tsx', '.js'] 84 | }, 85 | 86 | devtool: isProd ? 'cheap-module-source-map' : 'cheap-module-eval-source-map', 87 | 88 | devServer: { 89 | host: 'localhost', // 主机名 90 | stats: 'errors-only', // 打包日志输出输出错误信息 91 | port: 8081, 92 | open: true 93 | }, 94 | } 95 | ``` 96 | 97 | ## 配置打包命令 98 | 99 | ``` 100 | "dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.js", 101 | "build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js" 102 | ``` 103 | 104 | ## 运行与打包 105 | 106 | ``` 107 | yarn dev 108 | yarn build 109 | ``` 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /docs/chapter2/1_type.md: -------------------------------------------------------------------------------- 1 | # 1. 基础类型 2 | 3 | TypeScript 支持与 JavaScript 几乎相同的数据类型,此外还提供了实用的枚举类型方便我们使用。 4 | 5 | ## 布尔值 6 | 7 | 最基本的数据类型就是简单的 true/false 值,在JavaScript 和 TypeScript 里叫做 `boolean`(其它语言中也一样)。 8 | 9 | ```typescript 10 | let isDone: boolean = false; 11 | isDone = true; 12 | // isDone = 2 // error 13 | ``` 14 | 15 | ## 数字 16 | 17 | 和 JavaScript 一样,TypeScript 里的所有数字都是浮点数。 这些浮点数的类型是 number。 除了支持十进制和十六进制字面量,TypeScript 还支持 ECMAScript 2015中引入的二进制和八进制字面量。 18 | 19 | ```typescript 20 | let a1: number = 10 // 十进制 21 | let a2: number = 0b1010 // 二进制 22 | let a3: number = 0o12 // 八进制 23 | let a4: number = 0xa // 十六进制 24 | ``` 25 | 26 | ## 字符串 27 | 28 | JavaScript 程序的另一项基本操作是处理网页或服务器端的文本数据。 像其它语言里一样,我们使用 `string` 表示文本数据类型。 和 JavaScript 一样,可以使用双引号(`"`)或单引号(`'`)表示字符串。 29 | 30 | ```typescript 31 | let name:string = 'tom' 32 | name = 'jack' 33 | // name = 12 // error 34 | let age:number = 12 35 | const info = `My name is ${name}, I am ${age} years old!` 36 | ``` 37 | 38 | ## undefined 和 null 39 | 40 | TypeScript 里,`undefined` 和 `null` 两者各自有自己的类型分别叫做 `undefined` 和 `null`。 它们的本身的类型用处不是很大: 41 | 42 | ```typescript 43 | let u: undefined = undefined 44 | let n: null = null 45 | ``` 46 | 47 | 默认情况下 `null` 和 `undefined` 是所有类型的子类型。 就是说你可以把 `null` 和 `undefined` 赋值给 `number` 类型的变量。 48 | 49 | 50 | ## 数组 51 | 52 | TypeScript 像 JavaScript 一样可以操作数组元素。 有两种方式可以定义数组。 第一种,可以在`元素类型后面接上[]`,表示由此类型元素组成的一个数组: 53 | 54 | ```typescript 55 | let list1: number[] = [1, 2, 3] 56 | ``` 57 | 58 | 第二种方式是使用数组泛型,`Array<元素类型>`: 59 | 60 | ```typescript 61 | let list2: Array = [1, 2, 3] 62 | ``` 63 | 64 | ## 元组 Tuple 65 | 66 | 元组类型允许表示一个已知元素数量和类型的数组,`各元素的类型不必相同`。 比如,你可以定义一对值分别为 `string` 和 `number` 类型的元组。 67 | 68 | ```typescript 69 | let t1: [string, number] 70 | t1 = ['hello', 10] // OK 71 | t1 = [10, 'hello'] // Error 72 | ``` 73 | 74 | 当访问一个已知索引的元素,会得到正确的类型: 75 | 76 | ```typescript 77 | console.log(t1[0].substring(1)) // OK 78 | console.log(t1[1].substring(1)) // Error, 'number' 不存在 'substring' 方法 79 | ``` 80 | 81 | ## 枚举 82 | 83 | `enum` 类型是对 JavaScript 标准数据类型的一个补充。 使用枚举类型可以`为一组数值赋予友好的名字`。 84 | 85 | ```typescript 86 | enum Color { 87 | Red, 88 | Green, 89 | Blue 90 | } 91 | 92 | // 枚举数值默认从0开始依次递增 93 | // 根据特定的名称得到对应的枚举数值 94 | let myColor: Color = Color.Green // 0 95 | console.log(myColor, Color.Red, Color.Blue) 96 | ``` 97 | 98 | 默认情况下,从 `0` 开始为元素编号。 你也可以手动的指定成员的数值。 例如,我们将上面的例子改成从 `1` 开始编号: 99 | 100 | ```typescript 101 | enum Color {Red = 1, Green, Blue} 102 | let c: Color = Color.Green 103 | ``` 104 | 105 | 或者,全部都采用手动赋值: 106 | 107 | ```typescript 108 | enum Color {Red = 1, Green = 2, Blue = 4} 109 | let c: Color = Color.Green 110 | ``` 111 | 112 | 枚举类型提供的一个便利是你可以由枚举的值得到它的名字。 例如,我们知道数值为 2,但是不确定它映射到 Color 里的哪个名字,我们可以查找相应的名字: 113 | 114 | ```typescript 115 | enum Color {Red = 1, Green, Blue} 116 | let colorName: string = Color[2] 117 | 118 | console.log(colorName) // 'Green' 119 | ``` 120 | 121 | ## any 122 | 123 | 有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 那么我们可以使用 `any` 类型来标记这些变量: 124 | 125 | ```typescript 126 | let notSure: any = 4 127 | notSure = 'maybe a string' 128 | notSure = false // 也可以是个 boolean 129 | ``` 130 | 131 | 在对现有代码进行改写的时候,`any` 类型是十分有用的,它允许你在编译时可选择地包含或移除类型检查。并且当你只知道一部分数据的类型时,`any` 类型也是有用的。 比如,你有一个数组,它包含了不同的类型的数据: 132 | 133 | ```typescript 134 | let list: any[] = [1, true, 'free'] 135 | 136 | list[1] = 100 137 | ``` 138 | 139 | ## void 140 | 141 | 某种程度上来说,`void` 类型像是与 `any` 类型相反,它`表示没有任何类型`。 当一个函数没有返回值时,你通常会见到其返回值类型是 `void`: 142 | 143 | ```typescript 144 | /* 表示没有任何类型, 一般用来说明函数的返回值不能是undefined和null之外的值 */ 145 | function fn(): void { 146 | console.log('fn()') 147 | // return undefined 148 | // return null 149 | // return 1 // error 150 | } 151 | ``` 152 | 153 | 声明一个 `void` 类型的变量没有什么大用,因为你只能为它赋予 `undefined` 和 `null`: 154 | 155 | ```typescript 156 | let unusable: void = undefined 157 | ``` 158 | 159 | ## object 160 | 161 | `object` 表示非原始类型,也就是除 `number`,`string`,`boolean`之外的类型。 162 | 163 | 使用 `object` 类型,就可以更好的表示像 `Object.create` 这样的 `API`。例如: 164 | 165 | ```typescript 166 | function fn2(obj:object):object { 167 | console.log('fn2()', obj) 168 | return {} 169 | // return undefined 170 | // return null 171 | } 172 | console.log(fn2(new String('abc'))) 173 | // console.log(fn2('abc') // error 174 | console.log(fn2(String)) 175 | ``` 176 | 177 | ## 联合类型 178 | 179 | 联合类型(Union Types)表示取值可以为多种类型中的一种 180 | 需求1: 定义一个一个函数得到一个数字或字符串值的字符串形式值 181 | 182 | ```typescript 183 | function toString2(x: number | string) : string { 184 | return x.toString() 185 | } 186 | ``` 187 | 188 | 需求2: 定义一个一个函数得到一个数字或字符串值的长度 189 | 190 | ```typescript 191 | function getLength(x: number | string) { 192 | 193 | // return x.length // error 194 | 195 | if (x.length) { // error 196 | return x.length 197 | } else { 198 | return x.toString().length 199 | } 200 | } 201 | ``` 202 | 203 | ## 类型断言 204 | 205 | 通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript 会假设你,程序员,已经进行了必须的检查。 206 | 207 | 类型断言有两种形式。 其一是“尖括号”语法, 另一个为 `as` 语法 208 | 209 | ```typescript 210 | /* 211 | 类型断言(Type Assertion): 可以用来手动指定一个值的类型 212 | 语法: 213 | 方式一: <类型>值 214 | 方式二: 值 as 类型 tsx中只能用这种方式 215 | */ 216 | 217 | /* 需求: 定义一个函数得到一个字符串或者数值数据的长度 */ 218 | function getLength(x: number | string) { 219 | if ((x).length) { 220 | return (x as string).length 221 | } else { 222 | return x.toString().length 223 | } 224 | } 225 | console.log(getLength('abcd'), getLength(1234)) 226 | ``` 227 | 228 | ## 类型推断 229 | 230 | 类型推断: TS会在没有明确的指定类型的时候推测出一个类型 231 | 有下面2种情况: 1. 定义变量时赋值了, 推断为对应的类型. 2. 定义变量时没有赋值, 推断为any类型 232 | 233 | ```typescript 234 | /* 定义变量时赋值了, 推断为对应的类型 */ 235 | let b9 = 123 // number 236 | // b9 = 'abc' // error 237 | 238 | /* 定义变量时没有赋值, 推断为any类型 */ 239 | let b10 // any类型 240 | b10 = 123 241 | b10 = 'abc' 242 | ``` 243 | 244 | -------------------------------------------------------------------------------- /docs/chapter2/2_interface.md: -------------------------------------------------------------------------------- 1 | # 2. 接口 2 | 3 | TypeScript 的核心原则之一是对值所具有的结构进行类型检查。我们使用接口(Interfaces)来定义对象的类型。`接口是对象的状态(属性)和行为(方法)的抽象(描述)` 4 | 5 | 6 | ## 接口初探 7 | 需求: 创建人的对象, 需要对人的属性进行一定的约束 8 | ``` 9 | id是number类型, 必须有, 只读的 10 | name是string类型, 必须有 11 | age是number类型, 必须有 12 | sex是string类型, 可以没有 13 | ``` 14 | 下面通过一个简单示例来观察接口是如何工作的: 15 | 16 | ```typescript 17 | /* 18 | 在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型 19 | 接口: 是对象的状态(属性)和行为(方法)的抽象(描述) 20 | 接口类型的对象 21 | 多了或者少了属性是不允许的 22 | 可选属性: ? 23 | 只读属性: readonly 24 | */ 25 | 26 | /* 27 | 需求: 创建人的对象, 需要对人的属性进行一定的约束 28 | id是number类型, 必须有, 只读的 29 | name是string类型, 必须有 30 | age是number类型, 必须有 31 | sex是string类型, 可以没有 32 | */ 33 | 34 | // 定义人的接口 35 | interface IPerson { 36 | id: number 37 | name: string 38 | age: number 39 | sex: string 40 | } 41 | 42 | const person1: IPerson = { 43 | id: 1, 44 | name: 'tom', 45 | age: 20, 46 | sex: '男' 47 | } 48 | ``` 49 | 50 | 类型检查器会查看对象内部的属性是否与IPerson接口描述一致, 如果不一致就会提示类型错误。 51 | 52 | ## 可选属性 53 | 54 | 接口里的属性不全都是必需的。 有些是只在某些条件下存在,或者根本不存在。 55 | 56 | ```typescript 57 | interface IPerson { 58 | id: number 59 | name: string 60 | age: number 61 | sex?: string 62 | } 63 | ``` 64 | 65 | 带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个 `?` 符号。 66 | 67 | 可选属性的好处之一是可以对可能存在的属性进行预定义,好处之二是可以捕获引用了不存在的属性时的错误。 68 | 69 | ```typescript 70 | const person2: IPerson = { 71 | id: 1, 72 | name: 'tom', 73 | age: 20, 74 | // sex: '男' // 可以没有 75 | } 76 | ``` 77 | 78 | ## 只读属性 79 | 80 | 一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 `readonly` 来指定只读属性: 81 | 82 | ```typescript 83 | interface IPerson { 84 | readonly id: number 85 | name: string 86 | age: number 87 | sex?: string 88 | } 89 | ``` 90 | 91 | 一旦赋值后再也不能被改变了。 92 | 93 | ```typescript 94 | const person2: IPerson = { 95 | id: 2, 96 | name: 'tom', 97 | age: 20, 98 | // sex: '男' // 可以没有 99 | // xxx: 12 // error 没有在接口中定义, 不能有 100 | } 101 | person2.id = 2 // error 102 | ``` 103 | 104 | ### readonly vs const 105 | 106 | 最简单判断该用 `readonly` 还是 `const` 的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 `const`,若做为属性则使用 `readonly`。 107 | 108 | ## 函数类型 109 | 110 | 接口能够描述 JavaScript 中对象拥有的各种各样的外形。 除了描述带有属性的普通对象外,接口也可以描述函数类型。 111 | 112 | 为了使用接口表示函数类型,我们需要给接口定义一个调用签名。它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。 113 | 114 | ```typescript 115 | /* 116 | 接口可以描述函数类型(参数的类型与返回的类型) 117 | */ 118 | 119 | interface SearchFunc { 120 | (source: string, subString: string): boolean 121 | } 122 | ``` 123 | 124 | 这样定义后,我们可以像使用其它接口一样使用这个函数类型的接口。 下例展示了如何创建一个函数类型的变量,并将一个同类型的函数赋值给这个变量。 125 | 126 | ```typescript 127 | const mySearch: SearchFunc = function (source: string, sub: string): boolean { 128 | return source.search(sub) > -1 129 | } 130 | 131 | console.log(mySearch('abcd', 'bc')) 132 | ``` 133 | 134 | ## 类类型 135 | 136 | ### 类实现接口 137 | 138 | 与 C# 或 Java 里接口的基本作用一样,TypeScript 也能够用它来明确的强制一个类去符合某种契约。 139 | 140 | ```typescript 141 | /* 142 | 类类型: 实现接口 143 | 1. 一个类可以实现多个接口 144 | 2. 一个接口可以继承多个接口 145 | */ 146 | 147 | interface Alarm { 148 | alert(): any; 149 | } 150 | 151 | interface Light { 152 | lightOn(): void; 153 | lightOff(): void; 154 | } 155 | 156 | class Car implements Alarm { 157 | alert() { 158 | console.log('Car alert'); 159 | } 160 | } 161 | ``` 162 | 163 | ## 一个类可以实现多个接口 164 | 165 | ```typescript 166 | class Car2 implements Alarm, Light { 167 | alert() { 168 | console.log('Car alert'); 169 | } 170 | lightOn() { 171 | console.log('Car light on'); 172 | } 173 | lightOff() { 174 | console.log('Car light off'); 175 | } 176 | } 177 | ``` 178 | 179 | ## 接口继承接口 180 | 181 | 和类一样,接口也可以相互继承。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。 182 | 183 | ```typescript 184 | interface LightableAlarm extends Alarm, Light { 185 | 186 | } 187 | ``` -------------------------------------------------------------------------------- /docs/chapter2/3_class.md: -------------------------------------------------------------------------------- 1 | # 3. 类 2 | 3 | 对于传统的 JavaScript 程序我们会使用`函数`和`基于原型的继承`来创建可重用的组件,但对于熟悉使用面向对象方式的程序员使用这些语法就有些棘手,因为他们用的是`基于类的继承`并且对象是由类构建出来的。 从 ECMAScript 2015,也就是 ES6 开始, JavaScript 程序员将能够使用基于类的面向对象的方式。 使用 TypeScript,我们允许开发者现在就使用这些特性,并且编译后的 JavaScript 可以在所有主流浏览器和平台上运行,而不需要等到下个 JavaScript 版本。 4 | 5 | ## 基本示例 6 | 7 | 下面看一个使用类的例子: 8 | 9 | ```typescript 10 | /* 11 | 类的基本定义与使用 12 | */ 13 | 14 | class Greeter { 15 | // 声明属性 16 | message: string 17 | 18 | // 构造方法 19 | constructor (message: string) { 20 | this.message = message 21 | } 22 | 23 | // 一般方法 24 | greet (): string { 25 | return 'Hello ' + this.message 26 | } 27 | } 28 | 29 | // 创建类的实例 30 | const greeter = new Greeter('world') 31 | // 调用实例的方法 32 | console.log(greeter.greet()) 33 | ``` 34 | 35 | 如果你使用过 C# 或 Java,你会对这种语法非常熟悉。 我们声明一个 `Greeter` 类。这个类有 3 个成员:一个叫做 `message` 的属性,一个构造函数和一个 `greet` 方法。 36 | 37 | 你会注意到,我们在引用任何一个类成员的时候都用了 `this`。 它表示我们访问的是类的成员。 38 | 39 | 后面一行,我们使用 `new` 构造了 `Greeter` 类的一个实例。它会调用之前定义的构造函数,创建一个 `Greeter` 类型的新对象,并执行构造函数初始化它。 40 | 41 | 最后一行通过 `greeter` 对象调用其 `greet` 方法 42 | 43 | ## 继承 44 | 45 | 在 TypeScript 里,我们可以使用常用的面向对象模式。 基于类的程序设计中一种最基本的模式是允许使用继承来扩展现有的类。 46 | 47 | 看下面的例子: 48 | 49 | ```typescript 50 | /* 51 | 类的继承 52 | */ 53 | 54 | class Animal { 55 | run (distance: number) { 56 | console.log(`Animal run ${distance}m`) 57 | } 58 | } 59 | 60 | class Dog extends Animal { 61 | cry () { 62 | console.log('wang! wang!') 63 | } 64 | } 65 | 66 | const dog = new Dog() 67 | dog.cry() 68 | dog.run(100) // 可以调用从父中继承得到的方法 69 | ``` 70 | 71 | 这个例子展示了最基本的继承:类从基类中继承了属性和方法。 这里,`Dog` 是一个 派生类,它派生自 `Animal` 基类,通过 `extends` 关键字。 派生类通常被称作*子类*,基类通常被称作*超类*。 72 | 73 | 因为 `Dog` 继承了 `Animal` 的功能,因此我们可以创建一个 `Dog` 的实例,它能够 `cry()` 和 `run()`。 74 | 75 | 下面我们来看个更加复杂的例子。 76 | 77 | ```typescript 78 | class Animal { 79 | name: string 80 | 81 | constructor (name: string) { 82 | this.name = name 83 | } 84 | 85 | run (distance: number=0) { 86 | console.log(`${this.name} run ${distance}m`) 87 | } 88 | 89 | } 90 | 91 | class Snake extends Animal { 92 | constructor (name: string) { 93 | // 调用父类型构造方法 94 | super(name) 95 | } 96 | 97 | // 重写父类型的方法 98 | run (distance: number=5) { 99 | console.log('sliding...') 100 | super.run(distance) 101 | } 102 | } 103 | 104 | class Horse extends Animal { 105 | constructor (name: string) { 106 | // 调用父类型构造方法 107 | super(name) 108 | } 109 | 110 | // 重写父类型的方法 111 | run (distance: number=50) { 112 | console.log('dashing...') 113 | // 调用父类型的一般方法 114 | super.run(distance) 115 | } 116 | 117 | xxx () { 118 | console.log('xxx()') 119 | } 120 | } 121 | 122 | const snake = new Snake('sn') 123 | snake.run() 124 | 125 | const horse = new Horse('ho') 126 | horse.run() 127 | 128 | // 父类型引用指向子类型的实例 ==> 多态 129 | const tom: Animal = new Horse('ho22') 130 | tom.run() 131 | 132 | /* 如果子类型没有扩展的方法, 可以让子类型引用指向父类型的实例 */ 133 | const tom3: Snake = new Animal('tom3') 134 | tom3.run() 135 | /* 如果子类型有扩展的方法, 不能让子类型引用指向父类型的实例 */ 136 | // const tom2: Horse = new Animal('tom2') 137 | // tom2.run() 138 | ``` 139 | 140 | 这个例子展示了一些上面没有提到的特性。 这一次,我们使用 `extends` 关键字创建了 Animal的两个子类:`Horse` 和 `Snake`。 141 | 142 | 与前一个例子的不同点是,派生类包含了一个构造函数,它 必须调用 `super()`,它会执行基类的构造函数。 而且,在构造函数里访问 `this` 的属性之前,我们 一定要调用 `super()`。 这个是 TypeScript 强制执行的一条重要规则。 143 | 144 | 这个例子演示了如何在子类里可以重写父类的方法。`Snake`类和 `Horse` 类都创建了 `run` 方法,它们重写了从 `Animal` 继承来的 `run` 方法,使得 `run` 方法根据不同的类而具有不同的功能。注意,即使 `tom` 被声明为 `Animal` 类型,但因为它的值是 `Horse`,调用 `tom.run(34)` 时,它会调用 `Horse` 里重写的方法。 145 | 146 | ``` 147 | sliding... 148 | sn run 5m 149 | dashing... 150 | ho run 50m 151 | ``` 152 | 153 | ## 公共,私有与受保护的修饰符 154 | 155 | ### 默认为 public 156 | 157 | 在上面的例子里,我们可以自由的访问程序里定义的成员。 如果你对其它语言中的类比较了解,就会注意到我们在之前的代码里并没有使用 `public` 来做修饰;例如,C# 要求必须明确地使用 `public` 指定成员是可见的。 在 TypeScript 里,成员都默认为 `public`。 158 | 159 | 你也可以明确的将一个成员标记成 `public`。 我们可以用下面的方式来重写上面的 `Animal` 类: 160 | 161 | ### 理解 private 162 | 163 | 当成员被标记成 `private` 时,它就不能在声明它的类的外部访问。 164 | 165 | ### 理解 protected 166 | 167 | `protected` 修饰符与 `private` 修饰符的行为很相似,但有一点不同,`protected`成员在派生类中仍然可以访问。例如: 168 | 169 | ```typescript 170 | /* 171 | 访问修饰符: 用来描述类内部的属性/方法的可访问性 172 | public: 默认值, 公开的外部也可以访问 173 | private: 只能类内部可以访问 174 | protected: 类内部和子类可以访问 175 | */ 176 | 177 | class Animal { 178 | public name: string 179 | 180 | public constructor (name: string) { 181 | this.name = name 182 | } 183 | 184 | public run (distance: number=0) { 185 | console.log(`${this.name} run ${distance}m`) 186 | } 187 | } 188 | 189 | class Person extends Animal { 190 | private age: number = 18 191 | protected sex: string = '男' 192 | 193 | run (distance: number=5) { 194 | console.log('Person jumping...') 195 | super.run(distance) 196 | } 197 | } 198 | 199 | class Student extends Person { 200 | run (distance: number=6) { 201 | console.log('Student jumping...') 202 | 203 | console.log(this.sex) // 子类能看到父类中受保护的成员 204 | // console.log(this.age) // 子类看不到父类中私有的成员 205 | 206 | super.run(distance) 207 | } 208 | } 209 | 210 | console.log(new Person('abc').name) // 公开的可见 211 | // console.log(new Person('abc').sex) // 受保护的不可见 212 | // console.log(new Person('abc').age) // 私有的不可见 213 | ``` 214 | 215 | ## readonly 修饰符 216 | 217 | 你可以使用 `readonly` 关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。 218 | 219 | ```typescript 220 | class Person { 221 | readonly name: string = 'abc' 222 | constructor(name: string) { 223 | this.name = name 224 | } 225 | } 226 | 227 | let john = new Person('John') 228 | // john.name = 'peter' // error 229 | ``` 230 | 231 | ### 参数属性 232 | 233 | 在上面的例子中,我们必须在 `Person` 类里定义一个只读成员 `name` 和一个参数为 `name` 的构造函数,并且立刻将 `name` 的值赋给 `this.name`,这种情况经常会遇到。 参数属性可以方便地让我们在一个地方定义并初始化一个成员。 下面的例子是对之前 `Person` 类的修改版,使用了参数属性: 234 | 235 | ```typescript 236 | class Person2 { 237 | constructor(readonly name: string) { 238 | } 239 | } 240 | 241 | const p = new Person2('jack') 242 | console.log(p.name) 243 | ``` 244 | 245 | 注意看我们是如何舍弃参数 `name`,仅在构造函数里使用 `readonly name: string` 参数来创建和初始化 `name` 成员。 我们把声明和赋值合并至一处。 246 | 247 | 参数属性通过给构造函数参数前面添加一个访问限定符来声明。使用 `private` 限定一个参数属性会声明并初始化一个私有成员;对于 `public` 和 `protected` 来说也是一样。 248 | 249 | ## 存取器 250 | 251 | `TypeScript` 支持通过 `getters/setters` 来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。 252 | 253 | 下面来看如何把一个简单的类改写成使用 `get` 和 `set`。 首先,我们从一个没有使用存取器的例子开始。 254 | 255 | ```typescript 256 | class Person { 257 | firstName: string = 'A' 258 | lastName: string = 'B' 259 | get fullName () { 260 | return this.firstName + '-' + this.lastName 261 | } 262 | set fullName (value) { 263 | const names = value.split('-') 264 | this.firstName = names[0] 265 | this.lastName = names[1] 266 | } 267 | } 268 | 269 | const p = new Person() 270 | console.log(p.fullName) 271 | 272 | p.firstName = 'C' 273 | p.lastName = 'D' 274 | console.log(p.fullName) 275 | 276 | p.fullName = 'E-F' 277 | console.log(p.firstName, p.lastName) 278 | ``` 279 | 280 | ## 静态属性 281 | 282 | 到目前为止,我们只讨论了类的实例成员,那些仅当类被实例化的时候才会被初始化的属性。 我们也可以创建类的静态成员,这些属性存在于类本身上面而不是类的实例上。 在这个例子里,我们使用 `static` 定义 `origin`,因为它是所有网格都会用到的属性。 每个实例想要访问这个属性的时候,都要在 `origin` 前面加上类名。 如同在实例属性上使用 `this.xxx` 来访问属性一样,这里我们使用 `Grid.xxx` 来访问静态属性。 283 | 284 | ```typescript 285 | /* 286 | 静态属性, 是类对象的属性 287 | 非静态属性, 是类的实例对象的属性 288 | */ 289 | 290 | class Person { 291 | name1: string = 'A' 292 | static name2: string = 'B' 293 | } 294 | 295 | console.log(Person.name2) 296 | console.log(new Person().name1) 297 | ``` 298 | 299 | ## 抽象类 300 | 301 | 抽象类做为其它派生类的基类使用。 它们不能被实例化。不同于接口,抽象类可以包含成员的实现细节。 `abstract` 关键字是用于定义抽象类和在抽象类内部定义抽象方法。 302 | 303 | ```typescript 304 | /* 305 | 抽象类 306 | 不能创建实例对象, 只有实现类才能创建实例 307 | 可以包含未实现的抽象方法 308 | */ 309 | 310 | abstract class Animal { 311 | 312 | abstract cry () 313 | 314 | run () { 315 | console.log('run()') 316 | } 317 | } 318 | 319 | class Dog extends Animal { 320 | cry () { 321 | console.log(' Dog cry()') 322 | } 323 | } 324 | 325 | const dog = new Dog() 326 | dog.cry() 327 | dog.run() 328 | ``` 329 | -------------------------------------------------------------------------------- /docs/chapter2/4_function.md: -------------------------------------------------------------------------------- 1 | # 4. 函数 2 | 3 | 函数是 JavaScript 应用程序的基础,它帮助你实现抽象层,模拟类,信息隐藏和模块。在 TypeScript 里,虽然已经支持类,命名空间和模块,但函数仍然是主要的定义行为的地方。TypeScript 为 JavaScript 函数添加了额外的功能,让我们可以更容易地使用。 4 | 5 | ## 基本示例 6 | 7 | 和 JavaScript 一样,TypeScript 函数可以创建有名字的函数和匿名函数。你可以随意选择适合应用程序的方式,不论是定义一系列 API 函数还是只使用一次的函数。 8 | 9 | 通过下面的例子可以迅速回想起这两种 JavaScript 中的函数: 10 | 11 | ```javascript 12 | // 命名函数 13 | function add(x, y) { 14 | return x + y 15 | } 16 | 17 | // 匿名函数 18 | let myAdd = function(x, y) { 19 | return x + y; 20 | } 21 | ``` 22 | 23 | ## 函数类型 24 | 25 | ### 为函数定义类型 26 | 27 | 让我们为上面那个函数添加类型: 28 | 29 | ```typescript 30 | function add(x: number, y: number): number { 31 | return x + y 32 | } 33 | 34 | let myAdd = function(x: number, y: number): number { 35 | return x + y 36 | } 37 | ``` 38 | 39 | 我们可以给每个参数添加类型之后再为函数本身添加返回值类型。TypeScript 能够根据返回语句自动推断出返回值类型。 40 | 41 | ### 书写完整函数类型 42 | 43 | 现在我们已经为函数指定了类型,下面让我们写出函数的完整类型。 44 | 45 | ```typescript 46 | let myAdd2: (x: number, y: number) => number = 47 | function(x: number, y: number): number { 48 | return x + y 49 | } 50 | 51 | ``` 52 | 53 | ## 可选参数和默认参数 54 | 55 | TypeScript 里的每个函数参数都是必须的。 这不是指不能传递 `null` 或 `undefined` 作为参数,而是说编译器检查用户是否为每个参数都传入了值。编译器还会假设只有这些参数会被传递进函数。 简短地说,传递给一个函数的参数个数必须与函数期望的参数个数一致。 56 | 57 | JavaScript 里,每个参数都是可选的,可传可不传。 没传参的时候,它的值就是 `undefined`。 在TypeScript 里我们可以在参数名旁使用 `?` 实现可选参数的功能。 比如,我们想让 `lastName` 是可选的: 58 | 59 | 在 TypeScript 里,我们也可以为参数提供一个默认值当用户没有传递这个参数或传递的值是 `undefined` 时。 它们叫做有默认初始化值的参数。 让我们修改上例,把`firstName` 的默认值设置为 `"A"`。 60 | 61 | 62 | ```typescript 63 | function buildName(firstName: string='A', lastName?: string): string { 64 | if (lastName) { 65 | return firstName + '-' + lastName 66 | } else { 67 | return firstName 68 | } 69 | } 70 | 71 | console.log(buildName('C', 'D')) 72 | console.log(buildName('C')) 73 | console.log(buildName()) 74 | ``` 75 | 76 | ### 剩余参数 77 | 78 | 必要参数,默认参数和可选参数有个共同点:它们表示某一个参数。 有时,你想同时操作多个参数,或者你并不知道会有多少参数传递进来。 在 JavaScript 里,你可以使用 `arguments` 来访问所有传入的参数。 79 | 80 | 在 TypeScript 里,你可以把所有参数收集到一个变量里: 81 | 剩余参数会被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意个。 编译器创建参数数组,名字是你在省略号( `...`)后面给定的名字,你可以在函数体内使用这个数组。 82 | 83 | ```typescript 84 | function info(x: string, ...args: string[]) { 85 | console.log(x, args) 86 | } 87 | info('abc', 'c', 'b', 'a') 88 | ``` 89 | 90 | ## 函数重载 91 | 92 | 函数重载: 函数名相同, 而形参不同的多个函数 93 | 在JS中, 由于弱类型的特点和形参与实参可以不匹配, 是没有函数重载这一说的 94 | 但在TS中, 与其它面向对象的语言(如Java)就存在此语法 95 | 96 | ```typescript 97 | /* 98 | 函数重载: 函数名相同, 而形参不同的多个函数 99 | 需求: 我们有一个add函数,它可以接收2个string类型的参数进行拼接,也可以接收2个number类型的参数进行相加 100 | */ 101 | 102 | // 重载函数声明 103 | function add (x: string, y: string): string 104 | function add (x: number, y: number): number 105 | 106 | // 定义函数实现 107 | function add(x: string | number, y: string | number): string | number { 108 | // 在实现上我们要注意严格判断两个参数的类型是否相等,而不能简单的写一个 x + y 109 | if (typeof x === 'string' && typeof y === 'string') { 110 | return x + y 111 | } else if (typeof x === 'number' && typeof y === 'number') { 112 | return x + y 113 | } 114 | } 115 | 116 | console.log(add(1, 2)) 117 | console.log(add('a', 'b')) 118 | // console.log(add(1, 'a')) // error 119 | ``` 120 | -------------------------------------------------------------------------------- /docs/chapter2/5_generic.md: -------------------------------------------------------------------------------- 1 | # 5. 泛型 2 | 3 | 指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性。 4 | 5 | ## 引入 6 | 下面创建一个函数, 实现功能: 根据指定的数量 `count` 和数据 `value` , 创建一个包含 `count` 个 `value` 的数组 7 | 不用泛型的话,这个函数可能是下面这样: 8 | 9 | ```typescript 10 | function createArray(value: any, count: number): any[] { 11 | const arr: any[] = [] 12 | for (let index = 0; index < count; index++) { 13 | arr.push(value) 14 | } 15 | return arr 16 | } 17 | 18 | const arr1 = createArray(11, 3) 19 | const arr2 = createArray('aa', 3) 20 | console.log(arr1[0].toFixed(), arr2[0].split('')) 21 | ``` 22 | 23 | ## 使用函数泛型 24 | 25 | ```typescript 26 | function createArray2 (value: T, count: number) { 27 | const arr: Array = [] 28 | for (let index = 0; index < count; index++) { 29 | arr.push(value) 30 | } 31 | return arr 32 | } 33 | const arr3 = createArray2(11, 3) 34 | console.log(arr3[0].toFixed()) 35 | // console.log(arr3[0].split('')) // error 36 | const arr4 = createArray2('aa', 3) 37 | console.log(arr4[0].split('')) 38 | // console.log(arr4[0].toFixed()) // error 39 | ``` 40 | 41 | ## 多个泛型参数的函数 42 | 43 | 一个函数可以定义多个泛型参数 44 | 45 | ```typescript 46 | function swap (a: K, b: V): [K, V] { 47 | return [a, b] 48 | } 49 | const result = swap('abc', 123) 50 | console.log(result[0].length, result[1].toFixed()) 51 | ``` 52 | 53 | 54 | ## 泛型接口 55 | 56 | 在定义接口时, 为接口中的属性或方法定义泛型类型 57 | 在使用接口时, 再指定具体的泛型类型 58 | 59 | ```typescript 60 | interface IbaseCRUD { 61 | data: T[] 62 | add: (t: T) => void 63 | getById: (id: number) => T 64 | } 65 | 66 | class User { 67 | id?: number; //id主键自增 68 | name: string; //姓名 69 | age: number; //年龄 70 | 71 | constructor (name, age) { 72 | this.name = name 73 | this.age = age 74 | } 75 | } 76 | 77 | class UserCRUD implements IbaseCRUD { 78 | data: User[] = [] 79 | 80 | add(user: User): void { 81 | user = {...user, id: Date.now()} 82 | this.data.push(user) 83 | console.log('保存user', user.id) 84 | } 85 | 86 | getById(id: number): User { 87 | return this.data.find(item => item.id===id) 88 | } 89 | } 90 | 91 | 92 | const userCRUD = new UserCRUD() 93 | userCRUD.add(new User('tom', 12)) 94 | userCRUD.add(new User('tom2', 13)) 95 | console.log(userCRUD.data) 96 | ``` 97 | 98 | ## 泛型类 99 | 100 | 在定义类时, 为类中的属性或方法定义泛型类型 101 | 在创建类的实例时, 再指定特定的泛型类型 102 | 103 | ```typescript 104 | class GenericNumber { 105 | zeroValue: T 106 | add: (x: T, y: T) => T 107 | } 108 | 109 | let myGenericNumber = new GenericNumber() 110 | myGenericNumber.zeroValue = 0 111 | myGenericNumber.add = function(x, y) { 112 | return x + y 113 | } 114 | 115 | let myGenericString = new GenericNumber() 116 | myGenericString.zeroValue = 'abc' 117 | myGenericString.add = function(x, y) { 118 | return x + y 119 | } 120 | 121 | console.log(myGenericString.add(myGenericString.zeroValue, 'test')) 122 | console.log(myGenericNumber.add(myGenericNumber.zeroValue, 12)) 123 | ``` 124 | 125 | ## 泛型约束 126 | 127 | 如果我们直接对一个泛型参数取 `length` 属性, 会报错, 因为这个泛型根本就不知道它有这个属性 128 | 129 | ```typescript 130 | // 没有泛型约束 131 | function fn (x: T): void { 132 | // console.log(x.length) // error 133 | } 134 | ``` 135 | 136 | 我们可以使用泛型约束来实现 137 | 138 | ```typescript 139 | interface Lengthwise { 140 | length: number; 141 | } 142 | 143 | // 指定泛型约束 144 | function fn2 (x: T): void { 145 | console.log(x.length) 146 | } 147 | ``` 148 | 149 | 我们需要传入符合约束类型的值,必须包含必须 `length` 属性: 150 | 151 | ```typescript 152 | fn2('abc') 153 | // fn2(123) // error number没有length属性 154 | ``` -------------------------------------------------------------------------------- /docs/chapter2/6_other.md: -------------------------------------------------------------------------------- 1 | # 6. 其它 2 | 3 | ## 声明文件 4 | 5 | 当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能 6 | 7 | 什么是声明语句 8 | 9 | 假如我们想使用第三方库 jQuery,一种常见的方式是在 html 中通过 ` 68 | ``` 69 | 70 | ## 3) reactive 71 | 72 | - 作用: 定义多个数据的响应式 73 | - const proxy = reactive(obj): 接收一个普通对象然后返回该普通对象的响应式代理器对象 74 | - 响应式转换是“深层的”:会影响对象内部所有嵌套的属性 75 | - 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据都是响应式的 76 | 77 | ```vue 78 | 85 | 86 | 126 | ``` 127 | 128 | 129 | 130 | ## 4) 比较Vue2与Vue3的响应式(重要) 131 | 132 | ## vue2的响应式 133 | 134 | - 核心: 135 | - 对象: 通过defineProperty对对象的已有属性值的读取和修改进行劫持(监视/拦截) 136 | - 数组: 通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持 137 | 138 | ```js 139 | Object.defineProperty(data, 'count', { 140 | get () {}, 141 | set () {} 142 | }) 143 | ``` 144 | 145 | - 问题 146 | - 对象直接新添加的属性或删除已有属性, 界面不会自动更新 147 | - 直接通过下标替换元素或更新length, 界面不会自动更新 arr[1] = {} 148 | 149 | ## Vue3的响应式 150 | 151 | - 核心: 152 | - 通过Proxy(代理): 拦截对data任意属性的任意(13种)操作, 包括属性值的读写, 属性的添加, 属性的删除等... 153 | - 通过 Reflect(反射): 动态对被代理对象的相应属性进行特定的操作 154 | - 文档: 155 | - https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy 156 | - https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect 157 | 158 | ```js 159 | new Proxy(data, { 160 | // 拦截读取属性值 161 | get (target, prop) { 162 | return Reflect.get(target, prop) 163 | }, 164 | // 拦截设置属性值或添加新属性 165 | set (target, prop, value) { 166 | return Reflect.set(target, prop, value) 167 | }, 168 | // 拦截删除属性 169 | deleteProperty (target, prop) { 170 | return Reflect.deleteProperty(target, prop) 171 | } 172 | }) 173 | 174 | proxy.name = 'tom' 175 | ``` 176 | 177 | 178 | 179 | ```html 180 | 181 | 182 | 183 | 184 | 185 | Proxy 与 Reflect 186 | 187 | 188 | 230 | 231 | 232 | ``` 233 | 234 | 235 | 236 | ## 5) setup细节 237 | 238 | - setup执行的时机 239 | - 在beforeCreate之前执行(一次), 此时组件对象还没有创建 240 | - this是undefined, 不能通过this来访问data/computed/methods / props 241 | - 其实所有的composition API相关回调函数中也都不可以 242 | 243 | - setup的返回值 244 | - 一般都返回一个对象: 为模板提供数据, 也就是模板中可以直接使用此对象中的所有属性/方法 245 | - 返回对象中的属性会与data函数返回对象的属性合并成为组件对象的属性 246 | - 返回对象中的方法会与methods中的方法合并成功组件对象的方法 247 | - 如果有重名, setup优先 248 | - 注意: 249 | - 一般不要混合使用: methods中可以访问setup提供的属性和方法, 但在setup方法中不能访问data和methods 250 | - setup不能是一个async函数: 因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性数据 251 | 252 | - setup的参数 253 | - setup(props, context) / setup(props, {attrs, slots, emit}) 254 | - props: 包含props配置声明且传入了的所有属性的对象 255 | - attrs: 包含没有在props配置中声明的属性的对象, 相当于 this.$attrs 256 | - slots: 包含所有传入的插槽内容的对象, 相当于 this.$slots 257 | - emit: 用来分发自定义事件的函数, 相当于 this.$emit 258 | 259 | ```vue 260 | 267 | 268 | 294 | ``` 295 | 296 | ```vue 297 | 310 | 311 | 372 | ``` 373 | 374 | 375 | 376 | ## 6) reactive与ref-细节 377 | 378 | - 是Vue3的 composition API中2个最重要的响应式API 379 | - ref用来处理基本类型数据, reactive用来处理对象(递归深度响应式) 380 | - 如果用ref对象/数组, 内部会自动将对象/数组转换为reactive的代理对象 381 | - ref内部: 通过给value属性添加getter/setter来实现对数据的劫持 382 | - reactive内部: 通过使用Proxy来实现对对象内部所有数据的劫持, 并通过Reflect操作对象内部数据 383 | - ref的数据操作: 在js中要.value, 在模板中不需要(内部解析模板时会自动添加.value) 384 | 385 | ```vue 386 | 393 | 394 | 430 | 431 | ``` 432 | 433 | 434 | 435 | 436 | ## 7) 计算属性与监视 437 | 438 | - computed函数: 439 | - 与computed配置功能一致 440 | - 只有getter 441 | - 有getter和setter 442 | 443 | - watch函数 444 | - 与watch配置功能一致 445 | - 监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调 446 | - 默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次 447 | - 通过配置deep为true, 来指定深度监视 448 | 449 | - watchEffect函数 450 | - 不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据 451 | - 默认初始时就会执行第一次, 从而可以收集需要监视的数据 452 | - 监视数据发生变化时回调 453 | 454 | ```vue 455 | 464 | 465 | 574 | 575 | ``` 576 | 577 | 578 | 579 | ## 8) 生命周期 580 | 581 | **vue2.x的生命周期** 582 | 583 | ![lifecycle_2](https://vipkshttps3.wiz.cn/ks/note/view/49c30824-dcdf-4bd0-af2a-708f490b44a1/10311b3b-496c-41f1-8df3-c87572008080/index_files/1604629129730-y1h.png) 584 | 585 | **vue3的生命周期** 586 | 587 | ![lifecycle_3](https://vipkshttps3.wiz.cn/ks/note/view/49c30824-dcdf-4bd0-af2a-708f490b44a1/10311b3b-496c-41f1-8df3-c87572008080/index_files/1604629129585-tqn.png) 588 | 589 | **与 2.x 版本生命周期相对应的组合式 API** 590 | 591 | - `beforeCreate` -> 使用 `setup()` 592 | - `created` -> 使用 `setup()` 593 | - `beforeMount` -> `onBeforeMount` 594 | - `mounted` -> `onMounted` 595 | - `beforeUpdate` -> `onBeforeUpdate` 596 | - `updated` -> `onUpdated` 597 | - `beforeDestroy` -> `onBeforeUnmount` 598 | - `destroyed` -> `onUnmounted` 599 | - `errorCaptured` -> `onErrorCaptured` 600 | 601 | **新增的钩子函数** 602 | 603 | 组合式 API 还提供了以下调试钩子函数: 604 | 605 | - onRenderTracked 606 | - onRenderTriggered 607 | 608 | ```vue 609 | 616 | 617 | 701 | 702 | ``` 703 | 704 | ```vue 705 | 711 | 712 | 727 | 728 | ``` 729 | 730 | 731 | 732 | ## 09) 自定义hook函数 733 | 734 | 735 | 736 | - 使用Vue3的组合API封装的可复用的功能函数 737 | 738 | - 自定义hook的作用类似于vue2中的mixin技术 739 | 740 | - 自定义Hook的优势: 很清楚复用功能代码的来源, 更清楚易懂 741 | 742 | - 需求1: 收集用户鼠标点击的页面坐标 743 | 744 | hooks/useMousePosition.ts 745 | 746 | ```js 747 | import { ref, onMounted, onUnmounted } from 'vue' 748 | /* 749 | 收集用户鼠标点击的页面坐标 750 | */ 751 | export default function useMousePosition () { 752 | // 初始化坐标数据 753 | const x = ref(-1) 754 | const y = ref(-1) 755 | 756 | // 用于收集点击事件坐标的函数 757 | const updatePosition = (e: MouseEvent) => { 758 | x.value = e.pageX 759 | y.value = e.pageY 760 | } 761 | 762 | // 挂载后绑定点击监听 763 | onMounted(() => { 764 | document.addEventListener('click', updatePosition) 765 | }) 766 | 767 | // 卸载前解绑点击监听 768 | onUnmounted(() => { 769 | document.removeEventListener('click', updatePosition) 770 | }) 771 | 772 | return {x, y} 773 | } 774 | ``` 775 | 776 | 777 | 778 | ```vue 779 | 784 | 785 | 809 | ``` 810 | 811 | 812 | 813 | - 利用TS泛型强化类型检查 814 | 815 | - 需求2: 封装发ajax请求的hook函数 816 | 817 | hooks/useRequest.ts 818 | 819 | ```ts 820 | import { ref } from 'vue' 821 | import axios from 'axios' 822 | 823 | /* 824 | 使用axios发送异步ajax请求 825 | */ 826 | export default function useUrlLoader(url: string) { 827 | 828 | const result = ref(null) 829 | const loading = ref(true) 830 | const errorMsg = ref(null) 831 | 832 | axios.get(url) 833 | .then(response => { 834 | loading.value = false 835 | result.value = response.data 836 | }) 837 | .catch(e => { 838 | loading.value = false 839 | errorMsg.value = e.message || '未知错误' 840 | }) 841 | 842 | return { 843 | loading, 844 | result, 845 | errorMsg, 846 | } 847 | } 848 | ``` 849 | 850 | 851 | 852 | ```vue 853 | 871 | 872 | 912 | 913 | ``` 914 | 915 | 916 | 917 | ## 10) toRefs 918 | 919 | 把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref 920 | 921 | 应用: 当从合成函数返回响应式对象时,toRefs 非常有用,这样消费组件就可以在不丢失响应式的情况下对返回的对象进行分解使用 922 | 923 | 问题: reactive 对象取出的所有属性值都是非响应式的 924 | 925 | 解决: 利用 toRefs 可以将一个响应式 reactive 对象的所有原始属性转换为响应式的 ref 属性 926 | 927 | ```vue 928 | 937 | 938 | 988 | 989 | ``` 990 | 991 | 992 | 993 | ## 11) ref获取元素 994 | 995 | 利用ref函数获取组件中的标签元素 996 | 997 | 功能需求: 让输入框自动获取焦点 998 | 999 | ```vue 1000 | 1005 | 1006 | 1026 | ``` 1027 | -------------------------------------------------------------------------------- /docs/chapter4/02_Composition API_其它部分.md: -------------------------------------------------------------------------------- 1 | # 2. Composition API(其它部分) 2 | 3 | ## 1) shallowReactive 与 shallowRef 4 | 5 | - shallowReactive : 只处理了对象内最外层属性的响应式(也就是浅响应式) 6 | - shallowRef: 只处理了value的响应式, 不进行对象的reactive处理 7 | 8 | - 什么时候用浅响应式呢? 9 | - 一般情况下使用ref和reactive即可 10 | - 如果有一个对象数据, 结构比较深, 但变化时只是外层属性变化 ===> shallowReactive 11 | - 如果有一个对象数据, 后面会产生新的对象来替换 ===> shallowRef 12 | 13 | ```vue 14 | 24 | 25 | 67 | 68 | ``` 69 | 70 | 71 | 72 | ## 2) readonly 与 shallowReadonly 73 | 74 | - readonly: 75 | - 深度只读数据 76 | - 获取一个对象 (响应式或纯对象) 或 ref 并返回原始代理的只读代理。 77 | - 只读代理是深层的:访问的任何嵌套 property 也是只读的。 78 | - shallowReadonly 79 | - 浅只读数据 80 | - 创建一个代理,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换 81 | - 应用场景: 82 | - 在某些特定情况下, 我们可能不希望对数据进行更新的操作, 那就可以包装生成一个只读代理对象来读取数据, 而不能修改或删除 83 | 84 | ```vue 85 | 90 | 91 | 132 | ``` 133 | 134 | 135 | 136 | 137 | 138 | ## 3) toRaw 与 markRaw 139 | 140 | - toRaw 141 | - 返回由 `reactive` 或 `readonly` 方法转换成响应式代理的普通对象。 142 | - 这是一个还原方法,可用于临时读取,访问不会被代理/跟踪,写入时也不会触发界面更新。 143 | - markRaw 144 | - 标记一个对象,使其永远不会转换为代理。返回对象本身 145 | - 应用场景: 146 | - 有些值不应被设置为响应式的,例如复杂的第三方类实例或 Vue 组件对象。 147 | - 当渲染具有不可变数据源的大列表时,跳过代理转换可以提高性能。 148 | 149 | ```vue 150 | 155 | 156 | 194 | ``` 195 | 196 | 197 | 198 | ## 4) toRef 199 | 200 | - 为源响应式对象上的某个属性创建一个 ref对象, 二者内部操作的是同一个数据值, 更新时二者是同步的 201 | - 区别ref: 拷贝了一份新的数据值单独操作, 更新时相互不影响 202 | - 应用: 当要将 某个prop 的 ref 传递给复合函数时,toRef 很有用 203 | 204 | ```vue 205 | 215 | 216 | 262 | 263 | ``` 264 | 265 | ```vue 266 | 271 | 272 | 300 | 301 | ``` 302 | 303 | 304 | 305 | ## 5) customRef 306 | 307 | - 创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制 308 | - 需求: 使用 customRef 实现 debounce 的示例 309 | 310 | ```vue 311 | 316 | 317 | 367 | ``` 368 | 369 | 370 | 371 | 372 | ## 6) provide 与 inject 373 | 374 | - provide` 和 `inject` 提供依赖注入,功能类似 2.x 的 `provide/inject 375 | 376 | - 实现跨层级组件(祖孙)间通信 377 | 378 | ```vue 379 | 389 | 390 | 415 | ``` 416 | 417 | ```vue 418 | 425 | 426 | 434 | ``` 435 | 436 | ```vue 437 | 441 | 442 | 454 | ``` 455 | 456 | 457 | 458 | ## 7) 响应式数据的判断 459 | 460 | - isRef: 检查一个值是否为一个 ref 对象 461 | - isReactive: 检查一个对象是否是由 `reactive` 创建的响应式代理 462 | - isReadonly: 检查一个对象是否是由 `readonly` 创建的只读代理 463 | - isProxy: 检查一个对象是否是由 `reactive` 或者 `readonly` 方法创建的代理 464 | 465 | -------------------------------------------------------------------------------- /docs/chapter4/03_手写组合API.md: -------------------------------------------------------------------------------- 1 | # 3. 手写组合API 2 | 3 | ## 1) shallowReactive 与 reactive 4 | 5 | ```js 6 | const reactiveHandler = { 7 | get (target, key) { 8 | 9 | if (key==='_is_reactive') return true 10 | 11 | return Reflect.get(target, key) 12 | }, 13 | 14 | set (target, key, value) { 15 | const result = Reflect.set(target, key, value) 16 | console.log('数据已更新, 去更新界面') 17 | return result 18 | }, 19 | 20 | deleteProperty (target, key) { 21 | const result = Reflect.deleteProperty(target, key) 22 | console.log('数据已删除, 去更新界面') 23 | return result 24 | }, 25 | } 26 | 27 | /* 28 | 自定义shallowReactive 29 | */ 30 | function shallowReactive(obj) { 31 | return new Proxy(obj, reactiveHandler) 32 | } 33 | 34 | /* 35 | 自定义reactive 36 | */ 37 | function reactive (target) { 38 | if (target && typeof target==='object') { 39 | if (target instanceof Array) { // 数组 40 | target.forEach((item, index) => { 41 | target[index] = reactive(item) 42 | }) 43 | } else { // 对象 44 | Object.keys(target).forEach(key => { 45 | target[key] = reactive(target[key]) 46 | }) 47 | } 48 | 49 | const proxy = new Proxy(target, reactiveHandler) 50 | return proxy 51 | } 52 | 53 | return target 54 | } 55 | 56 | 57 | /* 测试自定义shallowReactive */ 58 | const proxy = shallowReactive({ 59 | a: { 60 | b: 3 61 | } 62 | }) 63 | 64 | proxy.a = {b: 4} // 劫持到了 65 | proxy.a.b = 5 // 没有劫持到 66 | 67 | 68 | /* 测试自定义reactive */ 69 | const obj = { 70 | a: 'abc', 71 | b: [{x: 1}], 72 | c: {x: [11]}, 73 | } 74 | 75 | const proxy = reactive(obj) 76 | console.log(proxy) 77 | proxy.b[0].x += 1 78 | proxy.c.x[0] += 1 79 | ``` 80 | 81 | 82 | 83 | ## 2) shallowRef 与 ref 84 | 85 | ```js 86 | /* 87 | 自定义shallowRef 88 | */ 89 | function shallowRef(target) { 90 | const result = { 91 | _value: target, // 用来保存数据的内部属性 92 | _is_ref: true, // 用来标识是ref对象 93 | get value () { 94 | return this._value 95 | }, 96 | set value (val) { 97 | this._value = val 98 | console.log('set value 数据已更新, 去更新界面') 99 | } 100 | } 101 | 102 | return result 103 | } 104 | 105 | /* 106 | 自定义ref 107 | */ 108 | function ref(target) { 109 | if (target && typeof target==='object') { 110 | target = reactive(target) 111 | } 112 | 113 | const result = { 114 | _value: target, // 用来保存数据的内部属性 115 | _is_ref: true, // 用来标识是ref对象 116 | get value () { 117 | return this._value 118 | }, 119 | set value (val) { 120 | this._value = val 121 | console.log('set value 数据已更新, 去更新界面') 122 | } 123 | } 124 | 125 | return result 126 | } 127 | 128 | /* 测试自定义shallowRef */ 129 | const ref3 = shallowRef({ 130 | a: 'abc', 131 | }) 132 | ref3.value = 'xxx' 133 | ref3.value.a = 'yyy' 134 | 135 | 136 | /* 测试自定义ref */ 137 | const ref1 = ref(0) 138 | const ref2 = ref({ 139 | a: 'abc', 140 | b: [{x: 1}], 141 | c: {x: [11]}, 142 | }) 143 | ref1.value++ 144 | ref2.value.b[0].x++ 145 | console.log(ref1, ref2) 146 | ``` 147 | 148 | 149 | 150 | ## 3) shallowReadonly 与 readonly 151 | 152 | ```js 153 | const readonlyHandler = { 154 | get (target, key) { 155 | if (key==='_is_readonly') return true 156 | 157 | return Reflect.get(target, key) 158 | }, 159 | 160 | set () { 161 | console.warn('只读的, 不能修改') 162 | return true 163 | }, 164 | 165 | deleteProperty () { 166 | console.warn('只读的, 不能删除') 167 | return true 168 | }, 169 | } 170 | 171 | /* 172 | 自定义shallowReadonly 173 | */ 174 | function shallowReadonly(obj) { 175 | return new Proxy(obj, readonlyHandler) 176 | } 177 | 178 | /* 179 | 自定义readonly 180 | */ 181 | function readonly(target) { 182 | if (target && typeof target==='object') { 183 | if (target instanceof Array) { // 数组 184 | target.forEach((item, index) => { 185 | target[index] = readonly(item) 186 | }) 187 | } else { // 对象 188 | Object.keys(target).forEach(key => { 189 | target[key] = readonly(target[key]) 190 | }) 191 | } 192 | const proxy = new Proxy(target, readonlyHandler) 193 | 194 | return proxy 195 | } 196 | 197 | return target 198 | } 199 | 200 | /* 测试自定义readonly */ 201 | /* 测试自定义shallowReadonly */ 202 | const objReadOnly = readonly({ 203 | a: { 204 | b: 1 205 | } 206 | }) 207 | const objReadOnly2 = shallowReadonly({ 208 | a: { 209 | b: 1 210 | } 211 | }) 212 | 213 | objReadOnly.a = 1 214 | objReadOnly.a.b = 2 215 | objReadOnly2.a = 1 216 | objReadOnly2.a.b = 2 217 | ``` 218 | 219 | 220 | 221 | ## 4) isRef, isReactive 与 isReadonly 222 | 223 | ```js 224 | /* 225 | 判断是否是ref对象 226 | */ 227 | function isRef(obj) { 228 | return obj && obj._is_ref 229 | } 230 | 231 | /* 232 | 判断是否是reactive对象 233 | */ 234 | function isReactive(obj) { 235 | return obj && obj._is_reactive 236 | } 237 | 238 | /* 239 | 判断是否是readonly对象 240 | */ 241 | function isReadonly(obj) { 242 | return obj && obj._is_readonly 243 | } 244 | 245 | /* 246 | 是否是reactive或readonly产生的代理对象 247 | */ 248 | function isProxy (obj) { 249 | return isReactive(obj) || isReadonly(obj) 250 | } 251 | 252 | 253 | /* 测试判断函数 */ 254 | console.log(isReactive(reactive({}))) 255 | console.log(isRef(ref({}))) 256 | console.log(isReadonly(readonly({}))) 257 | console.log(isProxy(reactive({}))) 258 | console.log(isProxy(readonly({}))) 259 | ``` 260 | -------------------------------------------------------------------------------- /docs/chapter4/04_Composition VS Option.md: -------------------------------------------------------------------------------- 1 | # 4. Composition API VS Option API 2 | 3 | ## 1) Option API的问题 4 | 5 | - 在传统的Vue OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 ,滚动条反复上下移动 6 | 7 | 8 | 9 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f84e4e2c02424d9a99862ade0a2e4114~tplv-k3u1fbpfcp-watermark.image) 10 | 11 | ![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e5ac7e20d1784887a826f6360768a368~tplv-k3u1fbpfcp-watermark.image) 12 | 13 | ## 2) 使用Compisition API 14 | 15 | 我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起 16 | 17 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bc0be8211fc54b6c941c036791ba4efe~tplv-k3u1fbpfcp-watermark.image) 18 | 19 | ![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6cc55165c0e34069a75fe36f8712eb80~tplv-k3u1fbpfcp-watermark.image) 20 | 21 | 22 | 23 | ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2c421e5392504ecc94c222057dba338a~tplv-k3u1fbpfcp-watermark.image) -------------------------------------------------------------------------------- /docs/chapter5/01_新组件.md: -------------------------------------------------------------------------------- 1 | # 1. 新组件 2 | 3 | ## 1) Fragment(片断) 4 | 5 | - 在Vue2中: 组件必须有一个根标签 6 | - 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中 7 | - 好处: 减少标签层级, 减小内存占用 8 | 9 | ```vue 10 | 14 | ``` 15 | 16 | 17 | 18 | ## 2) Teleport(瞬移) 19 | 20 | - Teleport 提供了一种干净的方法, 让组件的html在父组件界面外的特定标签(很可能是body)下插入显示 21 | 22 | ModalButton.vue 23 | 24 | ```vue 25 | 42 | 43 | 55 | 56 | 57 | 79 | ``` 80 | 81 | App.vue 82 | 83 | ```vue 84 | 88 | 89 | 103 | ``` 104 | 105 | 106 | 107 | ## 3) Suspense(不确定的) 108 | 109 | - 它们允许我们的应用程序在等待异步组件时渲染一些后备内容,可以让我们创建一个平滑的用户体验 110 | 111 | ```vue 112 | 124 | 125 | 146 | ``` 147 | 148 | - AsyncComp.vue 149 | 150 | ```vue 151 | 155 | 156 | 174 | ``` 175 | 176 | - AsyncAddress.vue 177 | 178 | ```vue 179 | 182 | 183 | 194 | ``` 195 | -------------------------------------------------------------------------------- /docs/chapter5/02_其他新API.md: -------------------------------------------------------------------------------- 1 | # 2. 其他新的API 2 | ## 全新的全局API 3 | - createApp() 4 | - defineProperty() 5 | - defineAsyncComponent() 6 | - nextTick() 7 | 8 | ## 将原来的全局API转移到应用对象 9 | - app.component() 10 | - app.config() 11 | - app.directive() 12 | - app.mount() 13 | - app.unmount() 14 | - app.use() 15 | 16 | ## 模板语法变化 17 | - v-model的本质变化 18 | - prop:value -> modelValue; 19 | - event:input -> update:modelValue; 20 | - .sync修改符已移除, 由v-model代替 21 | - 22 | - v-if优先v-for解析 -------------------------------------------------------------------------------- /docs/chapter6/README.md: -------------------------------------------------------------------------------- 1 | # TODO LIST -------------------------------------------------------------------------------- /docs/chapter7/快速搭建在线文档.md: -------------------------------------------------------------------------------- 1 | # 使用VuePress搭建在线文档网站 2 | ## 0. 在线文档 3 | 4 | [VuePress官方在线文档](https://vuepress.vuejs.org/zh/) 5 | 6 | ## 1. 搭建基本环境 7 | 8 | ```bash 9 | # 将 VuePress 作为一个本地依赖安装 10 | npm install -D vuepress 11 | 12 | # 新建一个 docs 文件夹 13 | mkdir docs 14 | 15 | # 新建一个文件: docs/README.md 16 | echo '# Hello VuePress!' > docs/README.md 17 | 18 | # 启动文档项目 19 | npx vuepress dev docs 20 | 21 | # 构建静态文件 22 | npx vuepress build docs 23 | |-- docs 24 | |-- .vuepress 25 | |-- config.js 26 | |-- README.md 27 | ``` 28 | 29 | ## 2. 配置ts教程文档 30 | 31 | 1. 整体结构 32 | 33 | ``` 34 | |-- dist 35 | |-- dics 36 | |-- .vuepress 37 | |-- public 38 | |-- ts-logo.png 39 | |-- config.js 40 | |-- chapter1 41 | |-- 01_初识TS.md 42 | |-- 02_安装TS.md 43 | |-- 03_HelloWorld.md 44 | |-- chapter2 45 | |-- 1_type.md 46 | |-- 2_interface.md 47 | |-- 3_class.md 48 | |-- 4_function.md 49 | |-- 5_generic.md 50 | |-- 6_other.md 51 | |-- chapter3 52 | |-- 01_react.md 53 | |-- 02_vue.md 54 | |-- chapter4 55 | |-- README.md 56 | |-- README.md 57 | |-- package.json 58 | ``` 59 | 60 | 1. docs/.vuepress/config.js 61 | 62 | ```javacript 63 | // 注意: base的值为github仓库的名称 64 | module.exports = { 65 | base: '/ts-study/', /* 基础虚拟路径: */ 66 | dest: 'dist', /* 打包文件基础路径, 在命令所在目录下 */ 67 | title: 'TypeScript 入门', // 标题 68 | description: '学习使用 TypeScript', // 标题下的描述 69 | themeConfig: { // 主题配置 70 | sidebar: [ // 左侧导航 71 | { 72 | title: '初识 TypeScript', // 标题 73 | collapsable: false, // 下级列表不可折叠 74 | children: [ // 下级列表 75 | 'chapter1/01_初识TS', 76 | 'chapter1/02_安装TS', 77 | 'chapter1/03_HelloWorld' 78 | ] 79 | }, 80 | { 81 | title: 'TypeScript 常用语法', 82 | collapsable: false, 83 | children: [ 84 | 'chapter2/1_type', 85 | 'chapter2/2_interface', 86 | 'chapter2/3_class', 87 | 'chapter2/4_function', 88 | 'chapter2/5_generic', 89 | ] 90 | }, 91 | ] 92 | } 93 | } 94 | ``` 95 | 96 | 3. docs/README.md 97 | 98 | ```bash 99 | --- 100 | #首页 101 | home: true 102 | # 图标 103 | heroImage: /ts-logo.png 104 | # 按钮文本 105 | actionText: 开始学习 → 106 | # 按钮点击跳转路径 107 | actionLink: /chapter1/01_初识TS 108 | --- 109 | ``` 110 | 111 | 4. package.json 112 | 113 | ```json 114 | "scripts": { 115 | "doc:dev": "vuepress dev docs", 116 | "doc:build": "vuepress build docs", 117 | "doc:deploy": "gh-pages -d docs/dist" 118 | } 119 | ``` 120 | 121 | ## 3. 发布到gitpage 122 | 123 | 1. 使用git管理当前项目 124 | 125 | 2. 将打包的项目推送到gitpage 126 | ```bash 127 | # 下载工具包 128 | yarn add -D gh-pages 129 | # 执行打包命令 130 | yarn doc:build 131 | # 执行部署命令 132 | yarn doc:deploy 133 | ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue3-docs", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.js", 8 | "build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js", 9 | "doc:dev": "vuepress dev docs", 10 | "doc:build": "vuepress build docs", 11 | "doc:deploy": "gh-pages -d docs/dist" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "devDependencies": { 17 | "@types/jquery": "^3.5.1", 18 | "clean-webpack-plugin": "^3.0.0", 19 | "cross-env": "^7.0.0", 20 | "gh-pages": "^2.2.0", 21 | "html-webpack-plugin": "^3.2.0", 22 | "ts-loader": "^6.2.1", 23 | "typescript": "^3.7.5", 24 | "webpack": "^4.41.5", 25 | "webpack-cli": "^3.3.10", 26 | "webpack-dev-server": "^3.10.2", 27 | "vuepress": "1.3.0" 28 | }, 29 | "dependencies": {} 30 | } 31 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | TS App 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | document.write('Hello Vue3') -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | // "incremental": true, /* Enable incremental compilation */ 5 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 6 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 7 | // "lib": [], /* Specify library files to be included in the compilation. */ 8 | // "allowJs": true, /* Allow javascript files to be compiled. */ 9 | // "checkJs": true, /* Report errors in .js files. */ 10 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 11 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 12 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 13 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 14 | // "outFile": "./", /* Concatenate and emit output to single file. */ 15 | "outDir": "./js", /* Redirect output structure to the directory. */ 16 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 17 | // "composite": true, /* Enable project compilation */ 18 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 19 | // "removeComments": true, /* Do not emit comments to output. */ 20 | // "noEmit": true, /* Do not emit outputs. */ 21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 22 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 23 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 24 | 25 | /* Strict Type-Checking Options */ 26 | "strict": false, /* Enable all strict type-checking options. */ 27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | // "strictNullChecks": true, /* Enable strict null checks. */ 29 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 30 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 31 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 32 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 33 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 34 | 35 | /* Additional Checks */ 36 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | 41 | /* Module Resolution Options */ 42 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 43 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 44 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 50 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 51 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 52 | 53 | /* Source Map Options */ 54 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 55 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 56 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 57 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 58 | 59 | /* Experimental Options */ 60 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 61 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 62 | 63 | /* Advanced Options */ 64 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 65 | } 66 | } 67 | --------------------------------------------------------------------------------