Hello
我是一个前端
我会在这里记录下我工作中碰到的问题和解决这些问题的过程
programmer-yang / blog Goto Github PK
View Code? Open in Web Editor NEWBlog
Blog
带添加...
带添加...
带添加...
带添加...
先写个标题,不然时间久了又不会写了
npm
中的参数传递我在使用package.json
的scripts
命令的时候遇到了一个参数传递问题
npm run build -type all
这条命令中的--type all
参数无法继承到npm run build
命令具体执行的那条命令上去
npm run build -- -type all
npm
命令中,--
后的内容会继承到真实的命令中去
这个其实比较自由,实现的方法很多,就像一个网站,你可以使用jquery、php、jsp、react、vue、ng中的任何一种来实现一样,用你觉得对的语法就好,当然,我这里还是要推荐下我觉得挺好的语法
const argv = yargs.option('t', {
alias: 'type',
demand: true,
describe: '类型',
type: 'string'
}).argv;
yargs
是个很强大的库,推荐给大家 链接
最近写个工具,用到了一些库,记录一下,同时也推荐给大家
const rimraf = require('rimraf');
const copydir = require('copy-dir');
const chalk = require('chalk');
copy-dir-参考
rimraf-参考
chalk-参考
rimraf
用于清空文件夹相当于rm -rf
,所以用起来小心一点,主要用于编译等操作之前清空目标文件
夹
rimraf.sync('./public/*');
copydir
复制文件夹,支持同步异步操作`
copydir.sync('a', 'b');
// a 源文件夹 b目标文件夹
chalk
可以在控制台输出不同颜色的信息,用于写工具提醒的提醒信息,告别单调的console.log('xxx')
吧
console.log(chalk.red('hello world'));
记录自己在使用koa的时候遇到的问题和解决办法
工作中有个需求需要自己实现一个文件上传来测试React的一个图片上传组件
参考资料:
官方关于文件上传的demo
官方关于跨域的解决方案
实现方式就是在官方文件上传的基础上修改几行代码实现跨域文件上传
官方demo中文件上传后是直接跳转了,这里我修改了返回json格式的响应内容,可把文件名放进去实现前端暂时上传成功的图片
// const stream = fs.createWriteStream(os.tmpdir(), Math.random().toString()));
const stream = fs.createWriteStream(path.join(`${__dirname}/public/tmp/`, file.name));
reader.pipe(stream);
console.log('uploading %s -> %s', file.name, stream.path);
// ctx.redirect('/');
ctx.response.body = { code: 0, filePath: `public/tmp/${file.name}` };
添加跨域的支持
添加在顶部即可
const cors = require('@koa/cors');
app.use(cors());
记录自己在升级dva到2.x中遇到的问题,如果发现写的不对的地方请不要笑,及时沟通,新手司机上路 ^_^
由于
history
版本变动,location不在包含有query对象,需要手动处理
yarn add query-string
import queryString from 'query-string';
history.listen((location) => {
const query = queryString.parse(location.search);
// console.log(query);
});
R4里修改了对于嵌套路由的实现方式
// old
<Router history={history}>
<Route path="/home" component={Container}>
<Route path="/home/page1" component={Page1} />
<Route path="/home/page2" component={Page2} />
</Route>
</Router>
// new
<Router history={history}>
<Route path="/home">
<Container>
<Route path="/home/page1" component={Page1} />
<Route path="/home/page2" component={Page2} />
</Container>
</Route>
</Router>
// or
<Router history={history}>
<Route path="/home" render={(props) => {
return (
<Container {...props}>
<Route path="/home/page1" component={Page1} />
<Route path="/home/page2" component={Page2} />
</Container>
);
}}>
</Route>
</Router>
最后一种写法主要用于
Container
组件中需要用到history
location
match
的情况
R4取消了关于
on****
钩子函数,相关实现可以在Container
容器组件的生命周期里来做
关于Redirect
R4 删除了
IndexRedirect
,需要重定向的时候使用Redirect
Redirect
的to
属性现在和Route
的path属性一样,适配所有能适配上的路径
// error
<Switch>
<Redirect from="/home" to="/home/page1" />
<Route path="/home/page1" component={Page1} />
<Route path="/home/page2" component={Page2} />
</Switch>
这样的写法会报错,因为form
的值会适配到/hom/page1 /home/page2
这样的所有子路径,就会出现理论上的递归跳转(会报错,不会递归,只是形容一下)
给Redirect
加上exact
属性就好了
<Switch>
<Redirect exact from="/home" to="/home/page1" />
<Route path="/home/page1" component={Page1} />
<Route path="/home/page2" component={Page2} />
</Switch>
关于Link
R4的Link
拆分成了两个:Link
NavLink
简单的跳转使用Link
特殊的跳转(有选中样式需求:activeClassName)使用NavLink
参考
有使用到
QueueAnim
给Link
添加动画的地方要修改为NavLink
在dva2.x中新增了effect返回promise的特性,现在写回调逻辑越来yue方便了
dispatch({ type: 'home/xxx' })
.then(({ data = {} }) => {
console.log(data);
})
.catch((e) => {
// error
console.log(e);
});
browserHistory
在之前的版本中,我们去掉path中的#的方式是
import {browserHistory} from 'dva/router';
const app = dva({
history: browserHistory,
});
在2.x版本中需要修改为:
yarn add history
import createHistory from 'history/createBrowserHistory';
const history = createHistory();
const app = dva({
history,
});
这里到将来可能还会有一些修改,但目前这么使用是可行的
webpack
插件html-webpack-plugin
dva之前的版本不支持文件hash,现在dva已经支持了这个功能 链接
使用方式是在.roadhog
中添加hash:true
的配置,默认约定在src下有一个index.ejs
的模板
Uncaught TypeError: Cannot read property '__reactInternalInstance$ufa3tpnn4i' of null
at Object.getClosestInstanceFromNode (8:113)
at findParent (
<script src="./index.js"></script>
<script type="text/javascript" src="/index.js?a6d74e09fa325aa523a2"></script>
问题就是引入了两次index.js
方法一:删除模板中的index.js
的引用
方法二:
module.exports = (webpackConfig) => {
const htmlWebpackPlugin = new HtmlWebpackPlugin({
filename: 'index.html',
template: `!!html!./src/${process.env.LOCAL_DIR}/index.html`,
hash: true,
inject: false,
minify: {
collapseBooleanAttributes: true,
},
});
webpackConfig.plugins.push(htmlWebpackPlugin);
return webpackConfig;
};
在webpack.config.js
的关于html-webpack-plugin
的配置内容里添加inject: false
参考
inject: false,
会使注入失效,如果需要hash请使用方法一
最近开源了一个组件、文档、测试、发布
解决方案,
是基于bisheng
写的,在这个过程中研究了一下GitHub Pages
这里分享一篇文章
但bisheng
目前发布到GitHub Pages
还存在一些问题,我提了个问题,可以在这里查看进度
后续看是否需要写一篇总结在这里
待续...
2018-01-08
目前问题已经解决,原来在bisheng
的config
中可以定义一个root
节点,这个节点就是来设置路由前缀的,因为git pages上所有的目录都是xxx.github.io/xxx/
这样的结构,所以需要设置一下路由的前缀。
像这样:
if (process.env.NODE_ENV) {
bishengConfig.htmlTemplateExtraData = {
root: '/component-factory/'
};
bishengConfig.root = '/component-factory/';
}
考虑到开发环境中是不需要设置
htmlTemplateExtraData
和root
节点的
如果使用的是create-react-app
的话,就需要手动在package.json
中配置homepage
节点。编译过程中,程序会解析你这个配置,解析出路由前缀和资源前缀,详情参考我上边儿分享的那篇文章
小程序现在功能越来越完善,从开发的角度虽然还有很多不足,但从业务的角度还是有必要学一下的。正巧身边有朋友想一起学习,而且基本是零基础,所以想把这个学习过程记录下来,供一些新手参考。
其实最主要的是我也没玩过小程序,所以一起学习啦 ^_^
因为好朋友是零基础开始,所以会讲很多基础的东西,如果是有基础的朋友看这个可以自己选择查看相应内容
好了,让我们开始吧,我们先列一个知识地图
我喜欢旅游,偏向自然风光的旅游,但我是一个程序员,到目前为止,大多数时间只能在办公室里度过,所以在我很累或者很闲的时候,我总是习惯去看看风景图片,遐想着有一天我能去那些地方旅游。
国内的网站都商业化比较严重,质量高又免费的比较难找,思来想去,还是自己做一个。正巧一个好朋友在学习瀑布流布局方式,然后我突发奇想说来一场比赛,看谁能更快的构建一个瀑布流网站,所以这个网站应运而生。
还是先说一下诉求,我希望做一个可以看高质量图片的网站,图片能自动更新,加载方式用瀑布流,网站整体要比较清爽、极简。好像就这么多,让我们开始吧。
因为这个网站的核心技术点是数据源跟瀑布流布局,所以这篇 blog 重点说这两个事情。
数据源的问题我找了比较久,最开始打算用 https://picsum.photos/ 这个数据源,但后来在使用过程中发现图片更新比较慢,而且如果用随机的方式的话要自己考虑去重问题,就放弃了。最后找来找去,还是选择用 https://unsplash.com/ ,主要是它的图片质量跟更新频率都很优秀,然后图片都是免费的,不会有版权问题。但伴随而来了一个新问题,图片读取速度。
因为 unsplash 的站点在国外,所以在国内打开会很慢,好像没有很好的解决这个问题的办法,我最终用了一个很蠢的办法,利用自己的 vps 做一个桥接。简单来说就是我在我的 vps 服务器上起了一个服务,通过这个服务器做数据流的桥接,这样图片访问速度就达到了一个可以接受的程度。核心代码如下:
app.get("/api/xxx", (req, res) => {
request.get(req.query.path).pipe(res)
})
看起来很简单,其实它确实很简单,使用 request 这个库就能轻松实现这个过程。
数据问题解决后就开始解决第二个核心问题,瀑布流。
用瀑布流关键字去搜索可以很容易的搜到一些解决方案,这些解决方案中最简单的应该算是 column-count 解决方案,利用 column-count
的特性,你可以很快的实现一个瀑布流,但在实践中我发现这个方案其实是有问题的。
.demo {
column-count: 3;
}
瀑布流的目的是在数据加载的时候用像瀑布的方式加载数据,又称砌砖布局方式,就像这样:
1 2 3
4 5 6
7 8 9
但 column-count
的机制确是这样:
1 (4) 7
2 5 8
3 6 9
当有新的数据后
1 5 9
2 6 10
3 7 11
(4) 8
你会发现这种排版方式其实不符合我们阅读习惯。再尝试了其他几种方式后,我最终选择了一个比较笨,但比较好理解的方式 - flex
布局方式。
flex 布局方式最大的好处就是简单、方便控制,只要把展示的图片分位 N 个数组,每个数组会记录当前数组渲染后的图片高度之和,然后在每次有新的图片的时候,优先添加早高度最小的数组里去,这样就实现了瀑布流。就像这样:
const oneLine = {
height: 100,
photos: [1, 4],
}
const twoLine = {
height: 180,
photos: [2, 5, 7, 9],
}
const threeLine = {
height: 150,
photos: [3, 6, 8],
}
当有新的图片10
的时候,通过便利当前所有数组,找出高度最小的数组,然后添加进去。然后页面上就很简单的使用 N 个 div
,然后纵向排列就好了。
看起来写的内容不是很多,其实做这个网站过程还是很曲折的,从最开始的数据源选择,到后来的瀑布流布局,都尝试了各种方法后才找到了合适的,似乎所有的事情都是这样?总要在实践中才能找到适合自己的东西。
由于没有美学细胞,这个网站的样式很简陋,所以我请了一个朋友帮忙设计一下这个网站,目前还在设计中,等有结果了我把改造过程再更新到这篇 blog 里。
假如一个页面有多个模态框,其中一部分长得很像,另一部分长得很像。都不能直接用ant design提供的简约版模态框,要用Modal组件。
请问这种情况下该怎么拆分页面的组件?是所有模态框放一个模态框组件里:
<div className={styles.root}
<Header />
<Table />
<Footer />
<MyModals />
</div>
还是所有模态框都放在页面里?:
<div className={styles.root}
<Header />
<Table />
<Footer />
<Modal1 />
<Modal2 />
<Modal3 />
</div>
代码片段可以让我们有更好的编码体验,但使用第三方代码片段总有一些自己不想要的副作用,所以我们何不自己创建一个符合自己编码习惯的代码片段库呢
我在写 React
跟 JavaScript
的时候喜欢用一些代码片段(Snippets
),因为这样可以让我的专注点集中在代码逻辑和业务逻辑上,而不用过多的去回忆各种语言的特效跟语法,并且这种事情往往都是重复工作。
比如最常见的语法:
我们在开发过程中往往需要多次敲击它来协助我们定位一些问题,虽然我没做明确的统计,但我觉得如果开发过程中这样类似的语法全部自己手写,那会浪费很多时间。
可能大多数人在这里会想到第三方代码片段库,其实我也是一样的。但我在使用了一段时间后发现,第三方库往往有一些不符合我的使用习惯的 Snippets 会影响我正常开发,比如这种情况:
我的意向是输入 p
标签,然后通过 tab
键来补全语法,但这些第三方库的副作用 Snippets
让我必须先按 esc
关闭这个提醒窗口后再按 tab
继续我的代码编写。
这导致我在日常编码中经常按错,耽误很多时间,对于我这种有轻度代码洁癖的人而言,这是一种煎熬,所以我一定要解决这个问题。
VSCode
给我们提供了一个代码片段的解决方案,可以在你本地配置属于你自己的代码片段。
配置起来很简单,只需要打开 首选项-用户代码片段
就可以开始配置了,比如我们上面提到的console.log
的语法,我们可以这样配置:
{
"consoleLog": {
"prefix": "clg",
"body": "console.log(${1:object});",
"description": "Displays a message in the console"
}
}
这样配置后,你只需要输入 clg
然后 tab
就可以补全 console.log
的全部语法,并且把光标停留在 ()
之间。
VSCode
还内置了一些函数跟语法来协助我们写出功能更强大的 Snippets
。比如我们可以使用$1 $2
来控制我们使用该 Snippets
后的光标停留位置跟顺序,我们还可以使用内置的CURRENT_DATE
变量得到当前的日期等。
更多内容请在参考连接里查看。
这些特性让我们编写适合自己的 Snippets
有了更多的可能性,在日积月累中,你的编码效率就会不知不觉的越来越高效。
当然,你也可以把你自己的 Snippets
分享出去,让它成为上文中提到的第三方库,哈哈。
https://code.visualstudio.com/docs/editor/userdefinedsnippets#_creating-your-own-snippets
https://code.visualstudio.com/api/language-extensions/snippet-guide
这个 Blog 我选了一个我很不熟悉的技术栈 Gatsby,这应该算是就是跳出舒适圈吧。
先说说我的诉求,我希望我的 Blog 可控性比较高,这样方便我做一些自己想做的事情。然后我希望它尽可能的简洁跟简单,个人有点极简主义。然后我希望 Blog 的内容使用 MarkDown 编写,最后我希望它可以自动部署,我 push 到 Github 就可以自动发布。
好吧,就这些,让我们开始吧。
Gatsby 是一个很优秀的基于 React 的开源架构,使用它,你可以很简单很快速构建你的网站,它已经帮你做了很多事情,比如静态化页面、统一的数据获取方式等。我很久前就有学习它的计划,但一直搁置,这次我选择使用它。
使用 Gatsby 跟我使用之前的 React 架构最大的区别应该是在数据资源层上,Gatsby 有一个内置的数据层,是基于 GraphQL 的,这个数据层包含了你开发过程需要的所有资源,比如,图片、视频、JSON 数据等。GraphQL 是一种查询语言,我在几年前就开始关注,但因为使用起来需要涉及到服务端的改造(原来的理解,但现在似乎也可以独立在展示层使用),就一直停留在只会基础的使用阶段,没有深入了解,我计划会在另一篇博客中在讲讲我的理解,这里就不明细说了。
使用 Gatsby 开发 Blog 系统,markdown 文件也是一种静态资源,我们需要使用 Gatsby 的插件把 markdown 文件加载到 Gatsby 的数据层,然后在应用就可以随意的使用了。
module.exports = {
plugins: [
{
resolve: "gatsby-source-filesystem",
options: {
name: "content",
path: `${__dirname}/src/content`,
},
},
"gatsby-transformer-remark",
],
}
我们先使用 gatsby-source-filesystem 插件把我们 markdown 文件添加到系统内,然后使用 gatsby-transformer-remark 插件把刚刚添加的内容转换为我们可以使用的数据。
当你启动了 Gatsby 的开发模式,它会自同时给你启动一个 graphql 服务,可以通过http://localhost:8001/___graphql
来访问。在使用了 gatsby-transformer-remark 插件后,你会发现你的数据源里多了 allMarkdownRemark
跟 markdownRemark
,一个是用来查询所有的 markdown 数据,一个是来获取单个 markdown 数据的详情
Gatsby 提供了很多类似生命周期的接口,你可以使用这些接口来完成你的业务,比如在我们这个场景下,我们要使用 Gatsby 的创建页面的接口,因为我的诉求是最终通过 markdown 的方式来写 blog,那么在我每次创建一个新的 markdown 文件后,都需要把这个文件转换为一个页面。
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const result = await graphql(`
query MyQuery {
allMarkdownRemark {
edges {
node {
frontmatter {
path
}
}
}
}
}
`)
result.data.allMarkdownRemark.edges.forEach(edge => {
const { node } = edge
createPage({
path: node.frontmatter.path,
component: path.resolve(`src/templates/post.js`),
})
})
}
上面的代码就是使用 Gatsby 给我们提供 graphql,来查询到所有的 markdown 数据,然后通过 createPage 方法创建对应的页面,这样我们就可以通过路由来访问我们的页面了,这里需要注意,在创建页面的时候,我们还需要指定一个模板文件,每个页面的具体内容,我们需要在模板里再通过 markdownRemark 去查询,查询条件就是路由 path,如下所示:
export const pageQuery = graphql`
query($path: String!) {
markdownRemark(frontmatter: { path: { eq: $path } }) {
html
frontmatter {
date(formatString: "YYYY MM DD")
title
path
}
}
}
`
然后我们就可以写我们最熟悉的组件代码了
const Templates = props => {
const { markdownRemark } = props.data
const { html, frontmatter } = markdownRemark
return (
<div>
<h1>{frontmatter.title}</h1>
<h2>{frontmatter.date}</h2>
<div dangerouslySetInnerHTML={{ __html: html }} />
</div>
)
}
开始处理自动发布需求,虽然网上有很多类似的架构跟教程,但我觉得既然底层原理很简单,还是自己实现一个,这样比较好控制。
自动发布的核心是 Git 的 Hook 机制,在 Github 里对应的是 Settings 页面的 Webhooks 选项,可以让用户填写一个 API,在你的库发生改变的时候就自动调用里设置的 API。
然后我用 node 的 Express 在服务端运行了一个 Web 程序,通过 shelljs 这个库来执行一个写好的脚本,在脚本里做安装依赖、编译、重启 Nginx 的操作,这样看起来一个简单的自动化发布系统就做好了,虽然很简陋。
到目前为止,我的需求都已经满足了,一个干净的用 Markdown 编写的可以自动发布的 Blog 系统。其实还有很多细节跟踩坑我没细说,主要是因为我不太会把控技术细节跟描述文字之间的界线,担心太多的技术内容导致阅读体验下降。
我比较喜欢兴趣式学习方式,所以我也用这种方式来学习前端技术,就像你看到的这个 Blog,因为兴趣,我在这个过程中学习了使用 Gatsby,学习了自动发布等工作中会遇到的一些技术,我很喜欢这种方式跟节奏。
umi 马上就要发布1.x版本,其中包含了很多现在前端的痛点解决方案,仔细分析后觉得很有必要好好学习。
后续的时间会陆续在这里更新我在学习和使用umi中遇到的问题。
相关:
官方网站
成哥的描述 强烈建议大家都去细细阅读,其中包含很多知识点
遇到的第一个问题是vscode
中eslint没有生效,我建立了一个issues在跟踪这个问题。
2-15 更新 eslint其实是没问题的,只是需要自己去配置具体的规则
比如这样:
{
"extends": "eslint-config-umi",
"rules": {
"semi": ["error", "never"]
}
}
今天尝试在umi中使用dva,通过官方的demo发现在umi中使用dva有两种方式,
区别在于是否使用umi-plugin-dva
插件。
待补充
umi-plugin-dva
插件首先要安装这个插件
yarn add umi-plugin-dva
然后在.umirc.js
中注册插件
export default {
plugins: [
'umi-plugin-dva'
]
}
然后重启项目
当前
1.0.0-rc.15
中还需要重启,以后可能会修复这个问题
现在umi会自动加载项目下models
文件夹里的文件
现在在你需要使用数据的页面通过connect
去取出数据就可以使用了
写这篇blog的原因在于我今天早上在看一篇文章的时候,忽然想通了一个很久前别人问我的关于redux的问题,想一想,觉得还是记录下来,然后顺便再捋一捋redux相关的内容
我现在的答案是:为了增加改变数据状态的成本
抽空来完善这个问题
最近接手了一个前端项目,由于之前前端开发人员比较多加上没有人很好的来约束管理,到我手上的时候已经变的比较恐怖,这里的恐怖主要指代码臃肿和体积庞大,随便改点东西,热加载都需要8秒左右的时间,在此基础上改需求已经异常难受,优化代码变的刻不容缓。
简单列一下优化任务清单:
DllPlugin
优化开发环境CommonsChunkPlugin
优化生产环境要完成这一步,我们要先分析一下当前代码的第三方组件使用情况,这里我推荐使用webpack-bundle-analyzer
yarn add webpack-bundle-analyzer
在***.prod.js
中添加
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
//...
plugins: [
new BundleAnalyzerPlugin(),
]
然后运行编译命令,编译结束后本地会启一个服务并在浏览器中弹出一个页面,页面中就是当前编译后代码的组件使用情况,从中找出没用用且很大的组件,然后从代码中移除或注解
很多人在写代码的时候不会注意,就是只要你
import
的组件,不管是否使用,都会打包进去,切记开启no-unused-vars
规则
这是个体力活儿,慢慢处理吧
当我完成这这一步的时候,我的项目已经从7mb的容量缩小到了3mb
我们代码已经比最开始清爽了很多,但我发现现在在开发的时候热加载还是4到5秒左右,还是不能忍受,所以我们现在要开始优化开发体验
webpack
的组件dllplugin
可以让你的热加载只编译业务部分,从而来大大提升热加载速度,这里我推荐使用autodll-webpack-plugin
yarn add autodll-webpack-plugin
然后在你的***.dev.js
配置文件中加上
const AutoDllPlugin = require('autodll-webpack-plugin'); // dll
//...
plugins: [
new AutoDllPlugin({
inject: true, // will inject the DLL bundles to index.html
filename: '[name].[hash].js',
path: 'static/js',
entry: {
vendor: [
'react',
'react-dom',
// 这里放步骤1中分析出来的比较大,但又必须使用的组件库
]
}
}),
]
完成这一步后这个项目的热加载速度已经降低到了3s以内,已经可以接受了
我的电脑年级比较大了,新电脑应该能到2s以内
我们来进行最后一步,优化此项目的线上表现
这里其实有几种思路,比如按需加载,但考虑到一些原因,这次的优化暂时不上按需,只是提取一下公共文件
这里我们要用到CommonsChunkPlugin
在***.prodj.js
中
//...
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: module => {
return module.resource && /node_modules/.test(module.resource)
}
}),
]
这里的意思是把
node_modules
下的所有包都提取到公共文件中
现在的webpack已经没有"boilerplate
和manifest
代码可能在每次编译时都会变化"的问题,所以不用单独提取
通过这3步,我在很短的时间内解决了当下这个项目的几个痛点,但优化的路还很长,而且优化永远是补救措施,更关键的还是从源头解决问题,比如整体团队的能力提升,整体代码质量的把控,都是需要很多精力去搞的事情,让我们一起加油吧
DllPlugin
CommonsChunkPlugin
autodll-webpack-plugin
https://www.erichain.me/2017/07/11/2017-07-11-speeding-up-webpack-with-dllplugin/
creeperyang/blog#37
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.