GithubHelp home page GithubHelp logo

blog's People

Watchers

 avatar

blog's Issues

ts

接触ts,断断续续也有两三年了。也想写个总结,记录一下与ts的”爱恨情仇“~

我希望能讲一些不太一样的,能给出一套团队上ts的解决方案出来。

前言

个人是非常推荐上ts的,现在ts的生态已经比我18年刚接触的时候好多了,webpack,rollup,jest的配置文件啊等等都已经直接支持ts了,绝大部分常用的包都可以找到类型定义,而且学习资料也比较多。

一两年前我也觉得小项目的话没啥必要上ts,现在的话,上手成本已经降下来不少了。很多人用习惯了ts会感觉回不到js了,基本上大小项目都可以直接上了(包括vue项目,后面会讲到如何在vue2.x里顺畅地使用ts)

简介

ts就可以简单理解为加了类型的js。2020年的github统计中ts已经排名第四了。可见其在开源社区的流行度。
image

ts解决了哪些js的痛点?

  • 写代码IDE没有提示的痛点。好的、符合业务逻辑的类型甚至还能指引我们写代码。写起来有种java的感觉,很提高生产力。
  • 静态类型系统大大增强了代码的可读性和可维护性,尤其适合多人合作的大型一些的项目,重构代码也会更有信心。
  • 编译期类型检查可以避免很多的运行时空指针问题,提高代码质量。据我观察我们Sentry里最多的报错就是undefined这种。

入门

建议大家直接从官方文档入手即可,之前对ts不太了解的话可以先从typescript for javascript developer开始。然后直接去typescript handbook,从基础入门到泛型即可。大概两小时左右的首次学习成本,上手难度不高。这里也有中文站。在掘金上搜索相关关键字学习也可以。

一般而言,泛型那里是一个比较难的点,需要重点看看。class相关的我们用的比较少,继承等功能可以后面再看。

构建工具

实际项目中更建议使用babel而不是ts-loader等去编译ts,Babel有丰富的插件,包括自动polyfill等。不过有几个使用率比较低的特性babel并不支持,需要注意一下

框架相关

主流三个框架,react和angular对ts支持都很不错。vue要多花一些心思。

在react项目上写ts之前,非常建议大家先去查看一下前人总结的cheatsheet,react + ts的绝大部分常用场景都会覆盖到。比较长,大家可以先看Getting Start部分。

vue的话,能用3.x版本是最好的,如果迁移3.x比较困难,也可以使用vue2.x + composition api插件。将逻辑都写到组合式api中即可。抛弃options那一套。直接参考官方ts文档即可,2.x,3.x通用。如果坚持使用options api,感觉上ts就没什么必要性了。

tsconfig.json

tsconfig.json是项目跟目录下的一个ts配置文件,可以设定类型检查的严格程度等。首先分享一下我比较喜欢的一套配置。建议大家内部商量一个通用的ts配置出来,时间充足的话,强制类型检查等建议全部开启。

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */

    /* Basic Options */
    // "incremental": true,                   /* Enable incremental compilation */
    "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
     "lib": ["DOM", "ESNext"],                             /* Specify library files to be included in the compilation. */
    // "allowJs": true,                       /* Allow javascript files to be compiled. */
    // "checkJs": true,                       /* Report errors in .js files. */
     "jsx": "preserve",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
    // "declaration": true,                   /* Generates corresponding '.d.ts' file. */
    // "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */
    // "sourceMap": true,                     /* Generates corresponding '.map' file. */
    // "outFile": "./",                       /* Concatenate and emit output to single file. */
    // "outDir": "./",                        /* Redirect output structure to the directory. */
    // "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    // "composite": true,                     /* Enable project compilation */
    // "tsBuildInfoFile": "./",               /* Specify file to store incremental compilation information */
    // "removeComments": true,                /* Do not emit comments to output. */
     "noEmit": true,                         /* Do not emit outputs. */
    // "importHelpers": true,                 /* Import emit helpers from 'tslib'. */
    // "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
    // "isolatedModules": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */

    /* Strict Type-Checking Options */
     "strict": true,                           /* Enable all strict type-checking options. */
     "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */
     "strictNullChecks": true,              /* Enable strict null checks. */
     "strictFunctionTypes": true,           /* Enable strict checking of function types. */
     "strictBindCallApply": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
     "strictPropertyInitialization": true,  /* Enable strict checking of property initialization in classes. */
     "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */
     "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */

    /* Additional Checks */
    // "noUnusedLocals": true,                /* Report errors on unused locals. */
    // "noUnusedParameters": true,            /* Report errors on unused parameters. */
    // "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
    // "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */
    // "noUncheckedIndexedAccess": true,      /* Include 'undefined' in index signature results */

    /* Module Resolution Options */
    // "moduleResolution": "node",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    // "baseUrl": "./",                       /* Base directory to resolve non-absolute module names. */
    // "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
    // "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
    // "typeRoots": [],                       /* List of folders to include type definitions from. */
    // "types": [],                           /* Type declaration files to be included in compilation. */
    "allowSyntheticDefaultImports": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
    //  "esModuleInterop": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */
    // "allowUmdGlobalAccess": true,          /* Allow accessing UMD globals from modules. */

    /* Source Map Options */
    // "sourceRoot": "",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */
    // "mapRoot": "",                         /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */
    // "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */

    /* Experimental Options */
    // "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */
    // "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */

    /* Advanced Options */
//    "skipLibCheck": true,                     /* Skip type checking of declaration files. */
//    "forceConsistentCasingInFileNames": true  /* Disallow inconsistently-cased references to the same file. */
  }
}

主要打开了严格模式,同时allowSyntheticDefaultImports为true,就可以import React from 'react'了,不需要import * as React from 'react'了,减少上手成本。

如果需要大量引用js代码,建议设置allowJS为true。noImplicitAny为false。这样引入js代码就不会再报错了,类型自动是any

需要注意的是一般我们使用babel去转译ts,babel压根就不会去读tsconfig文件,所以tsconfig里只设置一些类型检查相关的选项即可。设置target, module, sourcemap等字段完全不会生效

迁移指南

首先按照上述在项目根目录加一个tsconfig.json文件,然后在webpack中配置让babel编译ts文件。
image
image
image
先在webpack的resolve选项中加入ts,然后webpack中配置让babel编译ts文件,最后使用babel-preset-typescript。一般而言就足够了。
接下来两种迁移方案:

  1. 直接把ts的严格类型检查关了,允许隐式any,这样ts其实和js就区别不大了。直接把js文件后缀改成ts就可以开始写了,后续编写ts注意写类型即可。这样迁移会非常简单,风险也极低,但是由于关闭了类型检查,写起来跟js没差,一般而言效果不是很好。
  2. 开启ts的严格类型检查,同时为所有公共的js文件写d.ts类型标注。后续新文件直接用ts写,老js文件尽量不做大的改动,可以配合jsdoc在js中引入ts的类型检查。参考文档

ts进阶

进阶的话,非常建议大家先把官网的handbook看一遍,泛型推导我个人认为是ts中比较复杂的一个地方,也是最灵活的地方。ts的类型系统是图灵完备的,也就是说任何编程语言能实现的功能,理论上他的类型系统都能实现。比如可以用它的类型系统去写算法题,比如这个仓库就展示了如何用ts的类型系统写快排,归并排序等算法。

当你有了一定的使用经验后,可以去尝试一下type-challenges,里面有很多比较复杂的类型,可以挑战一下自己,能否不看答案写出正确的类型。
image

虽然也写了有段时间的ts,但是上面这些challenges大部分我也不会写。我个人认为做工程的话,一般也用不到这么复杂的类型。就到泛型就够了。

项目实战

对于ts来说,比较麻烦的点就是管理一些自定义类型,和外部api的数据类型了。

  • 自定义类型。有些时候我们需要去修改一些全局上的数据的类型,或者我们发现依赖里的类型写的有问题。我们可以在src下新建一个.d.ts结尾的文件,写一些类型去做覆盖。以下是一个简单的示例。
    image
    由于axios有个拦截器自动返回data,这里我们改变了axios的返回类型。还改变了process.env.NODE_ENV的类型。

  • 管理后端api数据类型。建议在一个文件里统一去管理所有api的返回数据类型。这样合代码的时候会经常冲突,文件也会比较长,不过胜在简单实用。也可以参考这种方案去拆分管理。如果团队中使用swagger等管理api,也可以考虑阿里巴巴开源的pont,专门针对这些场景做了些优化。下面举个简单场景的例子。
    image
    image
    定义好每个请求的返回类型(这些类型不要自己手写,把后端返回的json复制粘贴到quickType里,再把生成的类型粘贴过来,偶尔会需要改一改),用typeof拿到类型即可。这不是最佳的方案,但实施起来非常简单。

没有银弹

软件工程没有银弹。虽然typescript能够大幅度提升代码的质量、可读性,可维护性,但是使用之前也要考虑到一些可能存在的问题。

  1. typescript的代码量,通常会比纯js的代码量更多一些。根据我的经验,大概会多出五分之一到四分之一的代码量,用于定义、使用类型等。熟练了之后,虽然代码量更多了,但是有自动补全写起来更快,效率基本没差。前期的话还是有点降低效率的。
  2. 推行初期,会出现相当多的anyscript情况,需要付出额外的code review、eslint、CI等精力来纠正。

FAQ

如何避免代码中出现大量的any,ts变成了anyscript?

  1. 降低写类型的成本,尤其是外部数据源的类型。可以使用一些自动类型生成工具,例如quicktype等。类型生成完了,直接复制进代码里即可。
  2. code review的时候更加关注类型,不是万不得已不写any。
  3. eslint禁止显式any(但是可以用注释disable掉,毕竟有些场景真的需要any),配合husky commit的时候进行检查。

有些类型是真的不会写怎么办?

不用担心,这是正常了,哪怕是用了很久的人也会遇见这种情况。优先看文档,其次问问有经验的同事,实在不行就any或者@ts-ignore吧。随着越来越熟练,这种情况会越来越少的。

ts报错can not found module or it's type declarations。怎么办?

一般而言是找不到这个包的类型定义文件,尝试npm install @types/yourPackageName --save-dev,如果报错404找不到这个包,说明这个包没有类型文件。你需要手动给这个包写类型定义,或者直接@ts-ignore忽略这个报错。以下是一个手动写类型定义的例子。
随便新建一个.d.ts结尾的文件,声明模块内导出的类型即可。
image
image

如何快速给后端的api数据标注类型?

quicktype中把后端的json粘贴进去,自动生成ts类型,再粘贴到项目的api类型管理的地方。

tailwind

用了tailwind有半年了吧,依稀记得那个时候tailwind就有20几k的star了,现在回过头一看已经40k+了,最近流行度最高的css框架非tailwind莫属了。想详细的写一下实践经验。

前言

个人认为tailwind非常适合一些后台管理页面的场景。后台管理对样式要求一般不高,使用tailwind的情况下几乎不需要写css了。
其实自tailwind v2.1引入了jit模式之后,面向C端的场景也可以轻松使用tailwind了。不过我还没有机会尝试,暂时按下不表。

tailwind是什么?

tailwind是一个css框架,提供了一系列的工具class类名。写起来的体验像是在写inline style一样,写样式非常高效,也比较自由。

tailwind相比scss等有哪些优势,或者说解决了哪些痛点?

  • 起名问题,css起名字是个很头疼的事,尤其是嵌套层次比较多的那种HTML,各种wrapper,child。有时候可能就加一个marginTop属性,还要去起个名字,比较麻烦。使用tailwind不存在起名的问题。
  • 嵌套问题,很多时候我们css嵌套的结构是和HTML的结构耦合在一起的。这就导致一个问题,HTML的结构变了,CSS的结构也要跟着变。尤其在HTML结构较复杂的场景,改动成本略高。使用tailwind后不存在嵌套、css选择器的问题。
  • 可读性提升。使用tailwind后看看HTML就可以大概知道页面的样式了,面对传统的rate-list-footer-amount这种类名。几乎没法知道它对应的css属性。
  • 几乎不再需要手写css,得益于tailwind2.1引入的jit,现在哪怕是3px,5px这种尺寸也可以精确表达了。除非需要覆盖组件本身的样式,绝大部分场景下都不再需要手写css了,用工具class组合即可。
  • 针对常见的css需求,提供了大量的工具方法,直接用即可。这些常见的css需求,手写当然没问题,但是用tailwind更快,更方便。例如文本ellipsis,常用的阴影效果等,一个line-clamp-3 shadow-sm 类名即可。
  • 预设了一些颜色,间距等等。没有设计师,直接手写css的情况下也可以轻松做出来起码看上去不丑的页面,适合没有任何设计稿的后台管理型项目。
  • 在传统的vue开发场景下,很多人会把模板 js css写到一个.vue文件中,当这个.vue文件越来越长的时候,我们不得不来回滚动屏幕查看或者编辑模板元素对应的css。即便把css文件抽取到另一个文件了,很多时候也需要开双屏才好舒服的开发。使用tailwind可以快速开发,写静态页的时候屏幕甚至都不需要离开模板区域。

说了这么多,我个人认为它最大的优势还是快速开发。熟练以后配合热更新,写css的正反馈周期极短,写起来很效率。

快速上手

安装

请参考官方文档安装。不同的脚手架可能安装方式不太相同。但是原理上都是一样的。让webpack用postcss去处理css文件,然后在postcss.config.js中加上tailwind和autoprefixer插件即可。
安装完成后请确保tailwindcss版本大于v2.1

IDE插件

强烈建议先安装编辑器插件,这样写的时候会有css类名的提示,vscode用户需要安装官方tailwind插件。webstorm用户需要安装官方插件,最新版本的webstorm已经自带了这个插件,一般不需要手动安装。

配置文件

项目根目录下创建文件tailwind.config.js。由于tailwind v2.1后支持了JIT模式,以前的很多配置项就不太需要了。这里贴一份简单的配置。一般而言,配置文件设置这几个就足够了。
image
新项目的话可以删掉prefix字段,这个主要用于避免class名字冲突,老项目引入的话还是很建议加个前缀的,避免冲突。
purge中将所有的.vue .jsx .tsx等文件加进去即可。

全局CSS文件

新建一个.css文件。将以下内容复制粘贴进去即可。
image
如果是老项目建议把第一行的base删掉。这个base跟PureCSS类似,会修改一些HTML的默认样式,可能会造成潜在的问题。
如果没有安装对应的tailwind编辑器插件,上述代码并不符合css语法,所以编辑器会报错,但是不影响使用。

然后在APP.tsx里引入这个css文件即可。

创建tsx文件

image
注意这里装了插件应该有代码提示,具体类名对应的css属性请参考官方文档。打开浏览器样式生效了,说明安装配置完成。

上手体验

初期的上手体验有点像写行内style,一堆class名字记不住。大概需要一到两天去熟悉这些类名,装了插件有代码提示,熟悉了之后,配合react-fast-refresh,反馈周期很短,可以体验到写页面行云流水一般的感觉。几个flex grid my-3等类名,页面就大致画好了。

熟练使用后一般而言你的项目代码大概长这样~ 可以看到样式全部由工具类名组合出来。可以看到class比较长,初期可能会有些不适应,习惯就好了。
image

核心特性

  • 响应式布局,工具类名前面加个前缀即可适配多种屏幕大小。需要响应式布局的场景不多,用到的时候再关注一下。
  • 支持伪元素伪类,比如需要在hover的时候加深背景色,我们可以这样写<div className="bg-red-500 hover:bg-red-700"></div>。几乎所有的伪元素伪类都支持,可以自己试一试~
  • 夜间模式。比如手机开启了夜间模式,但是大部分的h5依然是白色的,很刺眼,没有适配夜间模式。tailwind原生支持夜间模式,在配置文件中打开夜间模式,然后加dark前缀即可。举个例子<div className="bg-white dark:bg-gray-800"></div>手机打开夜间模式就会展示黑色的背景了。
  • 提取组件。这个一般来说用的比较少。主要场景就是很多样式写起来很相似。比如button之类的,重复的去粘贴很麻烦,所以可以把重复的部分,抽取出来成为一个单独的工具类名。
    其实还有些@layer @Variants等更高级的特性,但是实际中基本上不会用到,建议大家可以先跳过~

使用体验

使用了半年多,也在一些项目中实际使用过。总的来说体验还是比较正面的。

  • 好的体验

    • 写样式飞快,配合react fast refresh,写布局样式等非常快。之前margin-top: 12px这种样式还得给它起个名字,然后切到另一个css文件去写,很是麻烦。用了tailwind之后,完全不用考虑起名,css module等一些问题。而且写的时候不再需要到处切文件了,写样式效率很快。
    • 我后台管理写的比较多,对CSS都不太熟悉了,甚至有时候记不清boxshadow的一堆属性,怎么实现文本超过三行就显示...等等CSS常见技巧都不太记得清楚,每次都要查Google。用了tailwind后直接.shadow-md .shadow-sm .line-clamp-3即可,不需要去关注实现的细节。还有一些简单的动画效果等,也是内置好的,拿来即用。
    • 相比于直接写scss等,tailwind只有1/5到1/10的代码量。
  • 不太好的体验

    • 我主要用webstorm。webstorm的tailwind插件有点慢,代码提示不给力,经常性的需要手写。vscode的那个插件是tailwind官方出的,应该会好一些吧。
    • 对一些比较精细化的grid布局,tailwind比较鸡肋了就,一般我会选择inline style去写稍微复杂一点的grid布局。
    • 常用的一些样式,因为用的比较多记得比较清楚,有些不太常用的比如transform相关的,经常感觉记不住,要翻翻文档才行,有的时候就比较懒,直接写inlien style了。
    • tailwind很难去覆盖组件内部的样式,尤其是组件本身没有暴露出来对应的classNames时。只能手写css去覆盖了。这种场景很少,所以问题不大。
    • 公共组件还是建议使用scss等去开发。方便外部使用的时候通过类名覆盖样式。

多个CSS解决方案的主观使用体验

  • tailwind: 写css效率极高,看着HTML就知道页面大概长啥样了,而且几乎不需要写多少代码。最好的代码就是没有代码。喜欢CSS in JS的话也可以看看twin。tailwind版本的css in js。
  • 常见的css in js方案(styled-components等): 用的不多不好评价。
  • css module。基于css loader的一个解决方案,跟Vue的scoped基本没差,写起来就是在写scss。
  • BEM: 用的不多,见别人用过,感觉class名字特别长~
  • scss: scss也挺好的,这个的主要问题是很容易出现无脑嵌套的问题。嵌套过程中,重复了一遍HTML的嵌套逻辑,后续改HTML结构也要改scss嵌套。然后因为嵌套的比较深,可能会出现相当多的.text .img这种classname,然后要去找上一级class去确定位置。

相关生态

  • twin tailwind版本的css in js写法。喜欢css in js的小伙伴可以尝试以下。
  • windicss 和tailwind类似,但是更加激进,功能更多。Jit这一套是windicss先搞的,后来tailwind开发者觉得不错,后面反正也有了这个功能。。。windicss的作者是antfu,在Vue生态中挺有影响力的。也是我崇拜的一个人。

V2.1之前版本的原理

大体上的原理就是通过postcss在编译期间根据tailwind的配置文件生成大量的utility class。默认配置下生成的css文件体积会超过10MB,没错是10MB。然后打包的时候通过正则匹配等去除99%以上的没有用到的类名。

V2.1之前版本存在的问题

根据上述的原理,我们不难看出v2.1前的版本可能存在的问题。

  • 生成如此大量的utility class会拖累webpack的打包速度,大概会慢个二十秒左右。
  • 大量的utility class对开发环境的浏览器性能也是个挑战,有点吃电脑的性能。
  • 开发和生产环境的utility class不一致,通过正则匹配等去除无用的class可能会出问题,导致生产环境下有些样式丢失了。
  • 很难去做一些精细化的样式,比如margin-top: 3px在之前的版本是很麻烦的。所以并不太适合C端应用。

为了减少开发环境下的css体积,我们可能会去手动关闭一些hover之类的效果,完全用不到的话还好,用到的话就会很麻烦。

JIT

针对上述存在的一些问题。tailwind在2.1版本引入了jit模式,这是一个里程碑式的功能。
JIT全称just in time。是一个更快,更加按需加载的一个引擎。

举个例子作为对比。
<div className="bg-white"></div>
在没有JIT的时候,tailwind会生成超过10MB的css文件,其中有个class就叫bg-white,所以bg-white这个样式能够生效,会产生巨大的性能浪费。

在JIT模式下,tailwind会检查所有purge选项下的文件,按需生成需要的class,在这个场景中生成的css文件只包含bg-white一个类
名,约0.x kb的体积,对比之前的10MB大幅度的提高了开发环境下的编译效率。

之前我们为了减少开发环境下的css体积,会去手动关闭一些hover之类的效果。现在是完全按需加载了,不再需要去关闭这些hover效果。

最大的特点就是现在支持自定义的css属性了。以往在tailwind中很难实现margin-top: 3px的效果,现在可以直接用方括号语法写成这样<div className="mt-[3px]"></div>,同理设计图上花花绿绿的颜色也可以实现了<div className="bg-[#189015]"></div>,tailwind会动态生成这些类名对应的css

还有一些小的优化

  • 方便的!important,有时候我们覆盖一些组件上的样式,但是优先级不够,会比较麻烦。此时只能在配置文件中加important提高优先级,这样会影响到所有生成的样式,进一步的提升开发环境的css尺寸。现在可以在工具类名前面加一个!强制!important。

举个例子,之前在tailwind中想要改变antd Button组件的文本颜色还有点麻烦,因为css优先级没有antd的高,会被覆盖掉。
现在只需要加个感叹号前缀生成的css文件就会自带!important。
<Button className="!text-[#189015]"></Button>

同时由于按需生成css,生产环境和开发环境生成的css一致了。而且按需生成起码减少了99%的体积,对打包速度几乎没有影响。

我个人觉得基于JIT的tailwind也可以用于C端项目了。C端项目花在CSS上的时间通常会比较多一些,能用tailwind加速这一过程,应该还是比较提效的。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.