dongtong / blog Goto Github PK
View Code? Open in Web Editor NEWCollect info
Collect info
Angular 2 will release recently, and many developers choose angular-cli to generate angular 2 application. I think that is not suitable for production environment. We could use webpack to build angular 2 application. So let's start it.
Enter your node project directory, and initialize
$ npm init
Add the dependencies and devDependencies options. The lastest angular 2 version is RC.6.
"dependencies": {
"@angular/common": "^2.0.0-rc.6",
"@angular/compiler": "^2.0.0-rc.6",
"@angular/core": "^2.0.0-rc.6",
"@angular/forms": "^2.0.0-rc.6",
"@angular/http": "^2.0.0-rc.6",
"@angular/platform-browser": "^2.0.0-rc.6",
"@angular/platform-browser-dynamic": "^2.0.0-rc.6",
"@angular/router": "^3.0.0-rc.2",
"core-js": "^2.4.1",
"jquery": "^3.1.0",
"reflect-metadata": "^0.1.8",
"rxjs": "^5.0.0-beta.11",
"zone.js": "^0.6.17"
},
"devDependencies": {
"css-loader": "^0.25.0",
"file-loader": "^0.9.0",
"html-loader": "^0.4.3",
"html-webpack-plugin": "^2.22.0",
"raw-loader": "^0.5.1",
"style-loader": "^0.13.1",
"template-url-loader": "^0.0.2",
"ts-loader": "^0.8.2",
"tslint": "^3.15.1",
"typescript": "^1.8.10",
"typings": "^1.3.3",
"url-loader": "^0.5.7",
"webpack": "^1.13.2",
"webpack-dev-server": "^1.15.1"
}
$ npm install
{
"compilerOptions": {
"declaration": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"mapRoot": "",
"module": "commonjs",
"moduleResolution": "node",
"noEmitOnError": true,
"noImplicitAny": false,
"outDir": "./dist/",
"rootDir": ".",
"sourceMap": true,
"target": "es5",
"inlineSources": true,
"suppressImplicitAnyIndexErrors": true
},
"exclude": [
"node_modules",
"typings/main",
"typings/main.d.ts"
]
}
$ typings install --global --save dt~core-js
var webpack = require('webpack');
var path = require('path');
var CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var autoprefixer = require('autoprefixer');
module.exports = {
entry: {
'main': './src/main.ts'
},
output: {
path: './',
filename: '[name].bundle.js',
sourceMapFilename: '[name].map',
chunkFilename: '[id].chunk.js'
},
postcss: [autoprefixer],
module: {
loaders: [{
test: /\.ts$/,
loader: 'ts'
},{
test: /\.css$/,
exclude: /src/,
loaders: ['style', 'css']
},{
test: /\.css$/,
include: /src/,
loader: 'raw!postcss'
},{
test: /\.scss$/,
exclude: /node_modules/,
loader: 'style-loader!css-loader!sass-loader'
},{
test: /\.(woff|woff2|ttf|eot|svg|png)$/,
loader: 'url?limit=10000'
},{ test: /\.html$/, loader: "html" }]
},
htmlLoader: {
minimize: true,
removeAttributeQuotes: false,
caseSensitive: true,
customAttrSurround: [
[/#/, /(?:)/],
[/\*/, /(?:)/],
[/\[?\(?/, /(?:)/]
],
customAttrAssign: [/\)?\]?=/]
},
resolve: {
extensions: ['', '.js', '.ts', '.json', '.scss', '.css', '.html']
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
]
};
var webpack = require('webpack');
var path = require('path');
var ProvidePlugin = require('webpack/lib/ProvidePlugin');
var CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var UglifyJsPlugin = require('webpack/lib/optimize/UglifyJsPlugin');
module.exports = {
/**
* Switch loaders to debug mode.
*
* See: http://webpack.github.io/docs/configuration.html#debug
*/
debug: false,
entry: {
'main': './src/main.ts'
},
output: {
path: './dist',
filename: '[name].bundle.js',
sourceMapFilename: '[name].map',
chunkFilename: '[id].chunk.js'
},
module: {
loaders: [{
test: /\.ts$/,
loader: 'ts'
},{
test: /\.css$/,
exclude: /src/,
loaders: ['style', 'css']
},{
test: /\.(woff(2)?|ttf|eot|svg|png)$/,
loader: 'url?limit=10000'
},{ test: /\.html$/, loader: "html" }]
},
htmlLoader: {
minimize: true,
removeAttributeQuotes: false,
caseSensitive: true,
customAttrSurround: [
[/#/, /(?:)/],
[/\*/, /(?:)/],
[/\[?\(?/, /(?:)/]
],
customAttrAssign: [/\)?\]?=/]
},
resolve: {
extensions: ['', '.js', '.ts', '.json', '.css', '.html']
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true
}
}),
new UglifyJsPlugin({
beautify: false,
mangle: { screw_ie8 : true },
compress: { screw_ie8: true },
comments: false
})
]
};
"scripts": {
"build": "webpack --inprogress --config webpack.config.prod.js",
"build:prod": "webpack -p --inprogress",
"server": "webpack-dev-server --inline --inprogress --config webpack.config.dev.js",
"postinstall": "typings install"
}
So, where is boilerplate code? please go to angular2-webpack-starter.
If you have any questions or suggestions, please tell me.
That's all.
在Ajax回调中使用window.open方法打开新的窗口时,会被浏览器拦截,无法打开目标窗口。
浏览器因为安全机制,window.open方法必须是浏览器自身事件触发,否则被认为是脚本注入。如下实现能正常打开窗口
<p onclick="window.open('www.baidu.com'); return false;">点击打开新窗口</p>
如果是在开发控制台执行以下代码,新的窗口被拦截。
window.open('www.baidu.com');
以下是Ajax伪代码实现,也是会被拦截:
$selector.on('click', function () {
$.ajax({
//...
}).done(function (res) {
//...
window.open(url);
});
});
原因是click回调是异步,你无法预测done什么时候执行,而浏览任务click是一个'long dead'事件, 所以不认为是浏览器自身事件触发的。
方法1:
提前定义一个window引用,在回调中调用方法
$selector.on('click', function () {
var newWin = window.open('');
$.ajax({
//...
}).done(function (res) {
//...
window.location = url;
});
});
方法2:
将ajax请求变成同步请求。
Sometimes, we don't want to use CoffeeScript engine in Rails application, we want to use JavaScript engine, How do that?
Edit application config in config/application.rb
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 5.1
# replace coffeescript with js
config.generators.javascript_engine :js
end
目前比较流行的预处理器主要有Sass, Less, Stylus等,它们出现的顺序从左到右,后者基本都增强了一些特性。帮助开发人员快速实现高效的CSS样式,避免重复劳动。
CSS框架主要有OOCSS, SMACSS, BEM等,比较流行的有SMACSS, BEM。但是Boostrap这样的CSS UI框架吸取了一些实践。
Basic: 系统基本样式,Reset, 字体大小等通用样式,一般不修改,除非重构系统。
Layout: 定义系统的一些布局样式,对于grid类型的加上l, 如l-grid。
Module: 定义系统一些组件模块的样式,比Layout元素粒度小。样式独立,互不冲突。推荐使用class选择器
State: 定义一些组件状态样式,一般以is开头,如is-active,使用class选择器,
可以通过JavaScript控制样式。在模块默认样式基础上加以补充。
Theme: 定义系统主题,局部或者整体,以theme-开头,如theme-blue。
使用CSS预处理器结合CSS框架,在实施前加以规划, 使CSS代码易懂,可维护, 可扩展。
移动Web在页面布局、网络请求、JavaScript性能方面有别于PC版本。
移动设备布局空间有限。一般使用Chrome开发者工具,结合一下viewport meta设置
<meta name="viewport" content="width=device-width, initial-scale=1 max-scale=1">
网络是基于基站的无线网络,而不是基于有线或者无线路由器。可以使用开发者工具中的"Network"模拟不同不网络请求下页面渲染情况。
移动设备的计算能力比PC较弱。可以使用http://emberperf.eviltrout.com测试。
为了提高移动设备上JavaScript性能:
推荐书籍 <<高性能JavaScript>>
减小bundle.js文件大小主要针对SPA(单页面应用程序)。有时候即使压缩bundle.js文件后,发现还是几百K。这对于PC端影响其实并不大,但是对于Mobile端用户来说,在网络环境不好的情况下会使页面加载变慢。
目前比较流行的JS模块方案是CommonJS规范,下面看两种方式加载模块后文件大小比较。
方式1:使用ES6的解构赋值语法
import { concat, sortBy, map, sample } from 'lodash';
// Example: sortBy
const users = [
{ 'user': 'fred', 'age': 48 },
{ 'user': 'barney', 'age': 36 },
{ 'user': 'fred', 'age': 42 },
{ 'user': 'barney', 'age': 34 }
];
const exampleSortBy = sortBy(users, function(o) { return o.user; });
console.log(exampleSortBy);
// Example: map
const exampleMap = map(users, 'user');
console.log(exampleMap);
// Example: concat
const array = [1];
const exampleConcat = concat(array, 2, [3], [[4]]);
console.log(exampleConcat);
// Example: sample
const exampleSample = sample([1, 2, 3, 4]);
console.log(exampleSample);
然后使用browserify编译压缩
browserify src/example.js -o dist/bundle.js -t [ babelify --presets [ es2015 ] ] -v -d -g uglifyify
方式2: 指定文件路径加载特定模块
import concat from 'lodash/concat';
import sortBy from 'lodash/sortBy';
import map from 'lodash/map';
import sample from 'lodash/sample';
// Example: sortBy
const users = [
{ 'user': 'fred', 'age': 48 },
{ 'user': 'barney', 'age': 36 },
{ 'user': 'fred', 'age': 42 },
{ 'user': 'barney', 'age': 34 }
];
const exampleSortBy = sortBy(users, function(o) { return o.user; });
console.log(exampleSortBy);
// Example: map
const exampleMap = map(users, 'user');
console.log(exampleMap);
// Example: concat
const array = [1];
const exampleConcat = concat(array, 2, [3], [[4]]);
console.log(exampleConcat);
// Example: sample
const exampleSample = sample([1, 2, 3, 4]);
console.log(exampleSample);
然后使用browserify编译压缩
browserify src/example-2.js -o dist/bundle-2.js -t [ babelify --presets [ es2015 ] ] -v -d -g uglifyify
经过比较比较bundle-2.js比bundle.js小,原因是方式2是按需加载特定模块,而不是加载整个模块,然后解构赋值。这种方式适用于所有node模块,以及使用CommonJS规范的模块(前提要保证每个子模块的内聚性,低耦合)。
When you access console in rails hang with no error messages and no response, how to solve it?
Stop spring:
$ spring stop
or
$ bin/spring stop
and then
$ spring
$ rails c
When we execute component business logic, and maybe there is error that we can not predicate, so we need to use componentDidCatch method to catch error in anywhere, includes self-component and children components.
If error occurs, the component will not rendered in DOM. Notice, in development environment, the error will still popup as modal dialog, it will describe the error detail info, but in production environment, this modal dialog will not appear.
import React, { PureComponent, Component } from 'react';
// import sendErr from './sendErr';
class CustomErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = {
hasErr: false
};
}
componentDidCatch(error, info) {
console.error(error);
console.log(info);
this.setState(state => ({
...state,
hasErr: true
}))
// sendErr(error, info);
}
render() {
if (this.state.hasErr) {
return (
<div> Oops, Error </div>
);
} else {
return this.props.children;
}
}
}
const Profile = (props) => (
<div>
Name: {props.user.name}
</div>
);
export default class App extends PureComponent {
constructor(props) {
super(props);
this.state = {
user: 'foobar'
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
...state,
user: null
}));
}
render() {
return (
<div>
<CustomErrorBoundary>
<Profile user={this.state.user} />
<button onClick={this.handleClick}>Trigger Error</button>
</CustomErrorBoundary>
</div>
);
}
}
Sometimes, you need to report error to server side, so use sendErr component to do it.
componentDidCatch just catch the error when con component render, it means that if component status is broken, the error will be caught by error boundary, but when you access server side, and error occurs, that error will not be caught.
componentDidCatch accepts two arguments, they are error internal information, you could log it to server side.
We could use high order component to create error boundary component.
最近visual studio code感觉是要火的样子。因为angular 2 , typescript这些带动了。
那么就说书如何在控制台直接使用visual studio code打开工程。而不需要先打开visual studio code, 然后导入项目工程。
F1(fn + f1 mac下)
然后搜索"shell command" 选择"在path中安装code命令"
安装完成后,重启visual studio code, 打开新的控制台,然后在工程目录下执行:
angular2-starter mac $ code
大功告成
This is my first issue article with English, bad grammar, pardon me.
When we upgrade Angular from beta to RC, we could load external template by using templateUrl annotation option. So browser will spend much time in loading them when in production mode, we could optimize it now.
Firstly, install dependency loaders:
npm i -D html-loader
npm i -g typings
Then, create typings config file
typings init
install require type definition
typings install dt~require --global --save
Lastly, replace templateUrl option with template option, for example:
templateUrl: ['../templates/about.component.html']
to
template: require('../templates/about.component.html')
Notice that you could add type definition for your typescript in tsconfig.json
"filesGlob": [
"./src/**/*.ts",
"!./node_modules/**/*.ts",
"!./typings/index",
"!./typings/index.d.ts"
]
Do not forget to config loaders in webpack.config.js
{ test: /\.html$/, loader: "html" }
Start server and check network request assets.
In ES6 we use arrow functions and refer these functions as stateless components. If you built your application using create react-app then there is no need of importing react into the functional/stateless components.
const search = ({input}) => {
//....
}
export default search
But if you do not use create-react-app to generate your react application, please remember to import react
import React from 'react'
当发现页面渲染变慢时,可以先观察页面使用的图片是否比较多,这些图片是否从远程服务器下载下来的。因为页面上使用的图片因为需求场景可能需要经常变更,在上传图片的时候没有经过压缩,也没有自动化压缩机制,所以导致图片过大,拖慢页面下载和渲染。
用户在上传图片的时候可以在后台处理图片时进行压缩。最终得到一个合理大小,密度适中,适合的图片格式。
有时候一张图片可能需要适配不同分辨率的设备,可以使用CSS3的Media-Query来检测设备特性
max-device-width
min-device-width
max-height
max-width
color
color-index
@media not | only mediatype and (media feature) {
CSS规则
}
例如:
@media screen and (max-width: 960px) {
#header {
background-image: url("small.png");
}
}
使用Media Query问题就是开发者无法预测所有设备的分辨率以及设备展示行为,设备是否是Retina显示屏等等,另外一个问题就是浏览器渲染图片延迟,因为一开始需要先检测设备参数,然后查找适合的CSS规则运用。
可以使用响应式图片(使用srcset属性),让浏览器渲染在运行时选择适合的图片。
<img src="default.png" srcset="small.png 320w, medium.png 640w, large.png 960w" />
在改变浏览器大小时会发起网络请求。
密度适中主要是关注分辨率密度,它是单位空间中有多少像素。通过这种方式可以针对不同的网络情况,使用不同分辨率的图片。可以使用srcset属性实现
<img src="default.png" srcset="normal.png 1x, retina.png 2x" />
图片的大小以及存储内容使用大小都是成倍增长(2x使用4倍大小和存储空间)。
使用适合的图片格式是因为不同的图片格式的压缩率,高保证不同。但是可以通过data uri实现图片内联。支持到IE8以上。
在生产环境中,需要准备多份不同版本的图片,如果有Retina设备的,还需要准备正常图片以及针对Retina设备的图片。
可以减少网络请求; 避免了图片重新上传,清理缓存的问题; 减少Round-Trip。但是无法重复利用。img虽然是一个请求数,但是加载完后,如果还有N次引用,那还1,而DataURI却必须是N次了;图片等资源转换为DataURI所用的Base64编码之后,文件大小增加了好多(通常50%-300%),如果服务器端不做Gzip压缩的话,压缩后倒是跟原本的差不多大小;加载、解码后渲染,消耗内存和CPU,手机上还得耗电池;浏览器支持到IE8。
mobify最新的测试数据:DataURI要比简单的外链资源要慢6倍
格式:
data:[<MIME type>][;charset=<charset>][;base64],<encoded data>
使用方式:
<img src="data:image/gif;base64,xxxxxx" />
所以sprite能用还得用啊,DataURI的方法要慎用,对于Webapp,还是研究各种缓存技术;个别不能sprite且质量小的图片可以用DataURI。
推荐一个转Base64的在线工具: Base64。直接将图片拖进去即可获得图片Base64编码。
俗称"雪碧图"。是一种将图片排列在一起的CSS手段。经常用在一个图片有多重状态,或者button之类零碎的图片当中。使用Sprite可以减少Round-Trip。
使用Sprite需要用到CSS中的background和background-position规则。
例如:
ul.sprite-image {
list-style-style: none;
}
ul.sprite-image li a {
height: 90px;
/*...*/
background: url("images/sprite.png") no repeat; /*一张整图作为所有需要用到元素的背景图*/
}
ul.sprite-image li a {
background-position: 0 0;
}
ul.sprite-image li a:hover {
background-position: 0 -98px;
}
有损压缩: 针对摄影图片,允许范围内的丢失像素图片格式是: WebP, JPEG-XR, JPEG 2000。用来去除一些图片中的冗余信息,在转换的过程中会丢失数据,但是在丢失数据力度以及转化后文件大小之间平衡。
无损压缩: PNG, GIF是比较常用的丢失较少数据的文件格式。有些格式例如TIFF, WebP都支持有损压缩和无损压缩。有些场景需要无损压缩,例如医疗,艺术,摄影。
GIF: 使用PNG8将GIF转化成PNG, PNG8支持透明,尽可能使文件达到最小,可以使用optiPNG工具转化。
JPEG 2000: 基于wavelet压缩技术的新的JPEG格式,更好的像素表现以及较小的文件大小。支持有损压缩和无损压缩。在Safari和iOS Safari上支持。
JPEG XR: JPEG扩展简称。是微软针对高清摄影图片开发的,支持有损和无损压缩,最好的压缩率达到30%以上,更好的透明颜色精准性(alpha通道)。支持在IE9, Edge上。
WebP: Google针对web设计的一种图片格式,支持有损和无损压缩。无损可以减少0~25%大小,有损减少 0 ~ 30%大小,在Chrome, Android Chrome, Android, Opera, Opera Mini上。iOS可以自定开发图片解码库。
如何衡量图片质量?
使用SSIM(Structural Similarity Index) : 在图片转化前后使用此技术观察图片转化效果。
现在Angular 2有一些像Vue,解决了一些问题。
谁在用[Vue](https://github.com/vuejs/vue/wiki/Projects- Using- Vue.js#interactive- experiences)
Ember是一个全栈的前端框架,提供了很多规约优于配置, 经验来自于Ruby on Rails框架。一旦熟悉它,将会很大程度提高你的生产力。但是同时它的学习曲线比较高,很难忍受。
是谷歌的开源项目,受Vue启发。两者风格很相似。最大的不同是Polymer构建在Web Components特性上,Web Component 在实际项目中还要借助polyfill工作,而Vue不需要任何依赖可以支持到IE9。
发布到生产中时,需要借助工具vulcanizer来打包。Vue可以借助Webpack工作流充分拥抱ES6和CSS预处理(SASS, LESS, PostCSS, Stylus)
在设计理念上两者互相融合与相似,Riot也被称作精简版的React。 但是Vue还是比Riot更加丰富一些,提供的功能比较多一些。
Sometimes, we need to connect to multiple databases in single rails application. The below is my practice in my application.
default: &default
adapter: mysql2
encoding: utf8
pool: 10
database: [the database your want to connect]
username: [username]
password: '[your db password]'
host: [your db host]
development:
<<: *default
host: [your db host]
production:
<<: *default
# define connection yaml file here
DEMO_DB = YAML.load_file(File.join(Rails.root, "config", "ext_databases", "demo.yml"))[Rails.env.to_s]
#encoding: utf-8
module Demo
class Base < ActiveRecord::Base
begin
self.abstract_class = true
establish_connection DEMO_DB
rescue Exception => e
Rails.logger.error(e.message)
Rails.logger.error(e.backtrace.join("\n"))
end
end
end
#encoding: utf-8
module Demo
class Post < Gridx::Base
self.table_name = "post"
end
end
In developing procedure, we often use styleUrls option to define component stylesheet. but we found that will make request in browser. We need to bundle related stylesheets in JavaScript file, and reduce browser request.
Install raw-loader, postcss-loader, style-loader, css-loader and sass-loader(if you use)
npm i -D raw-loader postcss-loader style-loader css-loader sass-loader
Define loader parser in webpack.config.js
{
test: /\.css$/,
exclude: /src/,
loaders: ['style', 'css']
},{
test: /\.css$/,
include: /src/,
loader: 'raw!postcss'
},{
test: /\.scss$/,
exclude: /node_modules/,
loader: 'style-loader!css-loader!sass-loader'
}
Out of our app stylesheet we use style-loader and css-loader to load them in style html tag. Our app stylesheet we use postcss-loader and raw-loader to parse them and bundle. If you use sass preprocessor, we could use sass-loader.
Then, we refactor our component
styles: [
require('../stylesheets/about.component.css')
]
If our app have public stylesheet, we could refer them in entry. such as app.component.ts :
styles: [require('./stylesheets/fonts.css')]
End-to-end testing is a methodology used to test whether the flow of an application is performing as designed from start to finish. The purpose of carrying out end-to-end tests is to identify system dependencies and to ensure that the right information is passed between various system components and systems.
End-to-end testing involves ensuring that that integrated components of an application function as expected. The entire application is tested in a real-world scenario such as communicating with the database, network, hardware and other applications.
For example, a simplified end-to-end testing of an email application might involve:
// user.js
module.exports = function (name, age) {
this.name = name;
this.age = age;
this.greet = function () {
console.log('I am ' + this.name + ' and I am ' + this.age + 'years old');
}
}
var User = require('./user');
var user = new User('foobar', 30);
user.greet();
//config.js
exports.version = '1.0.0';
module.exports = "foobar";
exports.greet = function () {
console.log('I am not existed')
}
# node install -g pm2
# pm2 startup centos
-> configure pm2
-> startup: configure startup script
-> centos: what type of system, available list is [ubuntu | ubuntu14 | ubuntu12 | centos | centos6 | arch | oracle | amazon | macos | darwin | freesd | systemd | systemv | upstart | launchd | rcd | openrc]
-> # Detect available init system, generate configuration and enable startup system
-> pm2 startup
-> sudo env PATH=$PATH:/home/jin/.nvm/versions/node/v6.9.1/bin /home/jin/.nvm/versions/node/v6.9.1/lib/node_modules/pm2/bin/pm2 startup upstart -u dong --hp /home/jin
-> the startup script to be executed under the certain user
NOTE : When updating nodejs, the pm2 binary path might change (it will necessarily change if you are using nvm). Therefore, we would advise you to run the startup command after any update.
root # useradd -s /bin/bash -m -d /home/dong dong
-> create a certain user
root # vim /etc/init.d/pm2-init.sh
dong # vim /etc/init.d/pm2-dong
-> check
-> USER=root
-> PM2_HOME="/home/root/.pm2"
-> modify USER and PM2_HOME according to PM2 user
Start server
dong # pm2 start server.js
-> Start standalone
# kill -p [pid]
-> You will find application is still online
# pm2 list
fork_mode is very useful because it enables a lot of possibilities. For example, you could launch multiple servers on pre-established ports which will then be load-balanced by HAProxy or Nginx.
# pm2 start index.js -i 4
The above command will launch 4 instances of index.js and let the cluster module handle load balancing. This is perfect for simulating a Production load-balanced environment all from your local machine.
You can also pass in 0 as the -i value which will automatically spawn as many workers as you have CPU cores.
If any of your workers happens to die, PM2 will restart them immediately so you don’t have to worry about that either.
$ pm2 unstartup upstart
When we use moment.js library to process the time format or parser, we actually do not need to include the locales part. So we could use the below configuration to reduce bundle file size about 301kb.
plugins: [
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
//...
]
That's all.
Action用来操作state。
Action触发可能是人为直接操作,例如用户点击按钮,也有可能来自网络请求(Ajax返回修改state)。
事件触发的过程也称作Action Creator。
Action 交互到Container重新渲染过程:
Action触发(人为或者网络)
\||/
调用Action Creator, Action Creator返回一个对象
function (return {type: SELECT_BOOK, book: {title: 'Redux'}})
\||/
Action会自动发送到所有Reducer, 查找符合要求的Reducer,
Reducer会根据action.type返回对应的action.book;
如果没有的话,就返回当前状态
\||/
所有 reducers处理完,返回一个新的状态,会通知container状态已经变更,container
接受到状态变更通知后,会重新渲染新的props
它分为强缓存和协商缓存:
1)浏览器在加载资源时,先根据这个资源的一些http header判断它是否命中强缓存,强缓存如果命中,浏览器直接从自己的缓存中读取资源,不会发请求到服务器。比如某个css文件,如果浏览器在加载它所在的网页时,这个css文件的缓存配置命中了强缓存,浏览器就直接从缓存中加载这个css,连请求都不会发送到网页所在服务器;
2)当强缓存没有命中的时候,浏览器一定会发送一个请求到服务器,通过服务器端依据资源的另外一些http header验证这个资源是否命中协商缓存,如果协商缓存命中,服务器会将这个请求返回,但是不会返回这个资源的数据,而是告诉客户端可以直接从缓存中加载这个资源,于是浏览器就又会从自己的缓存中去加载这个资源;
3)强缓存与协商缓存的共同点是:如果命中,都是从客户端缓存中加载资源,而不是从服务器加载资源数据;区别是:强缓存不发请求到服务器,协商缓存会发请求到服务器。
4)当协商缓存也没有命中的时候,浏览器直接从服务器加载资源数据。
当浏览器对某个资源的请求命中了强缓存时,返回的http状态为200。
直接用了浏览器缓存没有和服务器确认。
在chrome的开发者工具的network里面size会显示为from cache,比如京东的首页里就有很多静态资源配置了强缓存,用chrome打开几次,再用f12查看network,可以看到有不少请求就是从缓存中加载的:
强缓存是利用Expires或者Cache-Control这两个http response header实现的,它们都用来表示资源在客户端缓存的有效期。
Expires
Expires是http1.0提出的一个表示资源过期时间的header,它描述的是一个绝对时间,由服务器返回,用GMT格式的字符串表示,如:Expires:Thu, 31 Dec 2037 23:55:55 GMT,它的缓存原理是:
1)浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Expires的header,如:
2)浏览器在接收到这个资源后,会把这个资源连同所有response header一起缓存下来(所以缓存命中的请求返回的header并不是来自服务器,而是来自之前缓存的header);
3)浏览器再请求这个资源时,先从缓存中寻找,找到这个资源后,拿出它的Expires跟当前的请求时间比较,如果请求时间在Expires指定的时间之前,就能命中缓存,否则就不行。
4)如果缓存没有命中,浏览器直接从服务器加载资源时,Expires Header在重新加载的时候会被更新。
Expires是较老的强缓存管理header,由于它是服务器返回的一个绝对时间,在服务器时间与客户端时间相差较大时,缓存管理容易出现问题,比如随意修改下客户端时间,就能影响缓存命中的结果。
Cache-Control
这是一个相对时间,在配置缓存的时候,以秒为单位,用数值表示,如:Cache-Control:max-age=315360000,它的缓存原理是:
1)浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Cache-Control的header,如:
2)浏览器在接收到这个资源后,会把这个资源连同所有response header一起缓存下来;
3)浏览器再请求这个资源时,先从缓存中寻找,找到这个资源后,根据它第一次的请求时间和Cache-Control设定的有效期,计算出一个资源过期时间,再拿这个过期时间跟当前的请求时间比较,如果请求时间在过期时间之前,就能命中缓存,否则就不行。
4)如果缓存没有命中,浏览器直接从服务器加载资源时,Cache-Control Header在重新加载的时候会被更新。
Cache-Control描述的是一个相对时间,在进行缓存命中的时候,都是利用客户端时间进行判断,所以相比较Expires,Cache-Control的缓存管理更有效,安全一些。
这两个header可以只启用一个,也可以同时启用,当response header中,Expires和Cache-Control同时存在时,Cache-Control优先级高于Expires。
强缓存管理
强缓存是前端性能优化最有力的工具,没有之一,对于有大量静态资源的网页,一定要利用强缓存,提高响应速度。
通常的做法是,为这些静态资源全部配置一个超时时间超长的Expires或Cache-Control,这样用户在访问网页时,只会在第一次加载时从服务器请求静态资源,其它时候只要缓存没有失效并且用户没有强制刷新的条件下都会从自己的缓存中加载,比如前面提到过的京东首页缓存的资源,它的缓存过期时间都设置到了2026年。
然而这种缓存配置方式会带来一个新的问题,就是发布时资源更新的问题,比如某一张图片,在用户访问第一个版本的时候已经缓存到了用户的电脑上,当网站发布新版本,替换了这个图片时,已经访问过第一个版本的用户由于缓存的设置,导致在默认的情况下不会请求服务器最新的图片资源,除非他清掉或禁用缓存或者强制刷新,否则就看不到最新的图片效果。
当浏览器对某个资源的请求没有命中强缓存,就会发一个请求到服务器,验证协商缓存是否命中,如果协商缓存命中,请求响应返回的http状态为304并且会显示一个Not Modified的字符串。
查看单个请求的Response Header,也能看到304的状态码和Not Modified的字符串,只要看到这个就可说明这个资源是命中了协商缓存,然后从客户端缓存中加载的,而不是服务器最新的资源。
协商缓存是利用的是Last-Modified、If-Modified-Since 和ETag、If-None-Match这两对Header来管理的。
Last-Modified、If-Modified-Since
Last-Modified、If-Modified-Since控制缓存的原理是:
1)浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Last-Modified的header,这个header表示这个资源在服务器上的最后修改时间。
2)浏览器再次跟服务器请求这个资源时,在request的header上加上If-Modified-Since的header,这个header的值就是上一次请求时返回的Last-Modified的值。
3)服务器再次收到资源请求时,根据浏览器传过来If-Modified-Since和资源在服务器上的最后修改时间判断资源是否有变化,如果没有变化则返回304 Not Modified,但是不会返回资源内容;如果有变化,就正常返回资源内容。当服务器返回304 Not Modified的响应时,response header中不会再添加Last-Modified的header,因为既然资源没有变化,那么Last-Modified也就不会改变,这是服务器返回304时的response header:
4)浏览器收到304的响应后,就会从缓存中加载资源。
5)如果协商缓存没有命中,浏览器直接从服务器加载资源时,Last-Modified Header在重新加载的时候会被更新,下次请求时,If-Modified-Since会启用上次返回的Last-Modified值。
Last-Modified,If-Modified-Since都是根据服务器时间返回的header,一般来说,在没有调整服务器时间和篡改客户端缓存的情况下,这两个header配合起来管理协商缓存是非常可靠的,但是有时候也会服务器上资源其实有变化,但是最后修改时间却没有变化的情况,而这种问题又很不容易被定位出来,而当这种情况出现的时候,就会影响协商缓存的可靠性。
ETag、If-None-Match
ETag、If-None-Match的缓存管理的方式是:
1)浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上ETag的header,这个header是服务器根据当前请求的资源生成的一个唯一标识,这个唯一标识是一个字符串,只要资源有变化这个串就不同,跟最后修改时间没有关系,所以能很好的补充Last-Modified的问题.
2)浏览器再次跟服务器请求这个资源时,在request的header上加上If-None-Match的header,这个header的值就是上一次请求时返回的ETag的值.
3)服务器再次收到资源请求时,根据浏览器传过来If-None-Match和然后再根据资源生成一个新的ETag,如果这两个值相同就说明资源没有变化,否则就是有变化;如果没有变化则返回304 Not Modified,但是不会返回资源内容;如果有变化,就正常返回资源内容。与Last-Modified不一样的是,当服务器返回304 Not Modified的响应时,由于ETag重新生成过,response header中还会把这个ETag返回,即使这个ETag跟之前的没有变化.
4)浏览器收到304的响应后,就会从缓存中加载资源。
协商缓存的管理
协商缓存跟强缓存不一样,强缓存不发请求到服务器,所以有时候资源更新了浏览器还不知道,但是协商缓存会发请求到服务器,所以资源是否更新,服务器肯定知道。
大部分web服务器都默认开启协商缓存,而且是同时启用Last-Modified,If-Modified-Since和ETag、If-None-Match。
如果没有协商缓存,每个到服务器的请求,就都得返回资源内容,这样服务器的性能会极差。
Last-Modified,If-Modified-Since和ETag、If-None-Match一般都是同时启用,这是为了处理Last-Modified不可靠的情况。
$ gem list --local
$ rails _version_ new [application name]
such as
$ rails _5.0.1_ new blog
需要注意的是yahoo军规,其实并不是每一条都需要符合,在某些情况下,它们存在一种互斥关系。根据自己的站点选择需要优化点。运行Run Test测试网站得分。
可以测试站点在不同设备下的性能,不管是PC还是移动设备。运行测试获取得分,并提供通用解决方案。
使用chrome浏览器开发应用时,可以借助chrome自带开发工具,诊断站点的性能瓶颈
HTTP 1.1连接是等客户端发送请求到服务端,服务端有反馈后,然后进行下一个连接发送。是一个同步行为。导致客户端下载资源缓慢。在HTTP 1.1中使用资源合并,将资源合并在一个连接中,或者使用HTTP 2可以加速客户端资源下载。
将外联的资源引用直接内联在HTML中,减少Roud-Trip时间。可以定义一个HTML文件大小限制,比如14K(阿里优化限制HTML大小),小于14K使用以下方式。
图片: 使用data: image/png
CSS: 直接在HTML中使用<style>引用
JavaScript: 直接在HTML中使用<javascript>引用
优化步骤:
如果HTML页面内联样式后文件大小过大,可以使用外联策略。增加Round-Trip,将资源合并到
外部资源中,减小HTML文件大小。
<img href='image.png' />
<style href='style.css' />
<script src='app.js' />
具体html文件结构如下
<html>
<head>
<script src="app.js"></script>
<style id="style.css"></style>
<body>
<script id="main.js"></script>
</body>
</head>
</html>
app.js动态非阻塞加载style.css和main.js
(function(doc){
function loadCSS() {
doc.querySelector('#style.css').innerText = '...';
}
function loadJS() {
doc.querySelector('#main.js').setAttribute('src', '...');
}
doc.addEventListener('DOMContentLoaded', function() {
loadCSS();
loadJS();
});
}(document));
目前通过meta实现preload还处在草案阶段,可以通过iframe实现预加载方案。
预加载就是在前一页加载将来将要使用到的资源,可以通过侦听onload事件在iframe中加载资源。通常的做法是将预加载的资源存入localStorage(针对IE问题,可以在github上找到通用解决方案)。注意localStorage针对同一个域下有限制大小的,不同的浏览器大小不同,但是一般建议小于2.6M的内容可以存入。
(function(doc){
//通过iframe预加载下个页面使用到的资源,将它们存入localStorage
function preloadRes() {
var f = doc.createElement('iframe'),
iframe = doc.getElementsByTagName('head')[0].appendChild(f),
iframeDoc = iframe.contentWindow.document;
iframeDoc.open().write('<body id="addContent" onload="var d = document;d.getElementsByTagName(\"head\")[0].appendChild(d.createElement(\"script\").src=\"/nextPage.js\")"');
//iframe onload事件触发
iframe.close();
}
window.addEventListener('load', function() {
preloadRes();
});
}(document))
nextPage.js内容如下:
(function(d){
var obj = {
runOnce: false,
run: function(d) {
if(!this.runOnce) {
this.addCss(d);
this.addJs(d);
this.addToStorage(d);
this.runOnce = true;
}
},
addJs: function(d) {...},
addToStorage: function(d) {
var str = JSON.stringify(this, function(key, val) {
if (typeof val === 'function') {
return val.toString(); //将函数存储为String
}
if (key === 'runOnce') {
return false;
}
return val;
});
localStorage.setItem('nextPage.js', str);
},
addCss: function(d) {...}
}
obj.run(d);
}(document));
下一个页面的JS内容:
(function(d) {
d.addEventListener('DOMContentLoaded', function() {
var raw = localStorage.getItem('nextPage.js');
if(raw) {
var rs = JSON.parse(raw, function(key, val) {
var prefix = val.toString().substring(0, 8);
if(prefix === 'function') {
return eval('(' + val + ')');
}else{
return val;
}
});
rs.run(d);
}else{
var f = d.createElement('script');
f.setAttribute('type', 'text/javascript');
f.setAttribute('src', '/nextPage.js');
d.getElementsByTagName('head')[0].appendChild(f);
}
}, false);
}(document));
当加载第一个页面的时候,页面通过localStorage将下一个页面将要使用的nextPage.js已经通过iframe预加载到localStorage中,然后访问下一个页面的时候,直接从缓存中(localStorage)获取nextPage.js,加快页面的渲染。
IE7, IE8 浏览器中Array内建类不支持indexOf方法,当然可以使用万能的github上的polyfill,但是引入项目中比较大,下面实现一个CommonJS的简单地我自己都不好意思写出来的MonkeyPatching :)
/**
* @module Array
* @desc 扩展内建类Array
*/
'use strict';
module.exports = {
/**
* 兼容IE7/8数组索引方法
*
* @function indexOf
* @returns {Integer}
* @example
* array.indexOf(element)
*/
indexOf: function() {
Array.prototype.indexOf = function (target, index) {
for (var i = (index || 0), j = this.length; i < j; i++) {
if(this[i] === target) {
return i;
}
}
return -1;
}
}
}
I have met a special issue that typescript can not access webpack resolved alias variables, and I spent some time investigating. I have already solved it. The following is my procedures.
Step 1: Define webpack configuration file, my file name is webpack.config.dev.js
resolve: {
extensions: ['', '.js', '.ts', '.json', '.less', '.scss', '.css', '.html', ".config.dev.js"],
alias: {
config: path.join(__dirname, 'src/config/dev.ts')
}
}
Step 2: Define src/config/dev.ts. You could define global variables here.
// define special environment constants
export default {
ENV: "development"
}
Step 3: Typescript class import it and use it
import config from 'config';
console.log(config.ENV)
You should define different environment webpack configuration file, such webpack.config.dev.js, webpack.config.prod.js etc, and then refer to src/config/dev.ts, src/config/prod.ts etc.
That's it. :)
选择对应的平台下载安装,然后将图片拖进列表压缩。
使用gulp工作流创建任务压缩图片
在线制作雪碧图,What's sprite? Google or Baidu
在线制作雪碧图
使用ICON Font 代替小图片
制作自己的ICON Font
响应式图片polyfill, 可以针对不同的设备(包含retina)使用不同的图片.
腾讯制作良品,在线压缩图片
Angular 2 @component注解中可以指定组件使用模版。使用方式有两种,一种是内联(Inline),另外一种是外链模版文件(External Files)。
如何正确使用?
当项目比较小的时候,或者模版无法重用时,可以使用内联模版。当项目比较大,或者将来会变得比较复杂,模版也可能被重用,那么建议使用外链模版文件
@Component({
selector: 'app',
template: `
// inline html
`
})
@Component({
selector: 'app',
templateUrl: 'path/to/your/html file'
})
bluebird是一个功能丰富的,高性能的Promise库。
详细文档可查阅API
下面介绍几个比较典型的API
返回一个新函数,这个新函数返回一个promise, 而不是使用回调函数。使用在 “错误优先” 的回调函数中。
其实就是将回调函数包装成promise
var Promise = require('bluebird');
var promiseReadFile = Promise.promisify(require('fs').readFile);
promiseReadFile('someFile.txt', 'utf8')
.then(function(content) {
console.log('content:', content);
})
.catch(function(err) {
console.log('Error reading file: ', err);
});
可以将库中所有回调函数包装成promise,就不需要每一个接口一一包装。
var Promise = require('bluebird');
var fs = require('fs');
var promiseFs = Promise.promisifyAll(fs);
promiseFs.readFileAsync('someFile.txt', 'utf8')
.then(function(content) {
console.log(content);
})
.catch(function(err) {
console.log('Error reading file: ', err);
});
类似于Function.prototype.bind(), 针对this创建一个promise, 这个promise也可以再次绑定。
function Person(name, age) {
this.name = name;
this.age = age;
}
var foobar = new Person('foobar', 30);
Person.protortype.introMyself = function () {
promiseFs.readFileAsync('someFile.txt', 'utf8').bind(this)
.then(function (content) {
return this.name;
})
.then(function (name) {
console.log("This guy name is " + name + ' and age is ' + this.age + ' years old');
return this;
})
.catch(function (err) {
console.log(err);
});
}
foobar.introMyself();
all/spead
接受一个promise数组,数组中的每个promise几乎同时执行。当所有promise状态都为fulfilled时,才会执行resolve。 经常结合spread使用
Promise.all([findItems, findUsers, findPictures])
.spread(function (items, users, pictures) {
console.log("all users:", users);
// ...
});
类似于Promise.all, 接受一个promise集合。参数接受的时promises,而不是数组。性能更好。
Promise.join(findItems(), findUsers(), findPictures(), function (items, users, picutures) {
//....
});
在刚开始接触React的时候,可能大多数情况下,我们都会选择使用React.createClass这种定义方式。它使用的是ES5的标准语法。后面当ES2015(6)越来越新潮时,大家都使用ES2015的中的class定义方式,结合Babel降级到ES5。
下面介绍几种在代码中经常使用到的定义React组件方式。
var HelloWorld = React.createClass({
render: function() {
return (<h1>Hello World</h1>);
}
});
class HelloWord extends React.Component {
render() {
return (<h1>Hello World</h1>);
}
}
var HelloWorld = function(props) {
return (<h1>Hello World</h1>);
}
const HelloWorld = (props) => {
return (<h1>Hello World</h1>);
}
无状态定义组件,主要通过props,可以避免使用this。 不参与组件生命周期,自然效率比较高。同时测试起来也比较方便,基本都是一些功能小模块。主要适用于不会改变的组件,如果想重新渲染此组件,可以通过container组件props传递下来,刷新父类组件。
如果组件关注state, 生命周期,通过refs访问原生DOM,以及操作嵌套函数,那么定义有状态的组件,其他场景可以定义无状态组件。
还有其他几种非主流的定义方式
What is ES6?
ECMAScript 6 = ES6 = ES2015
ES6 was completed in mid-2015. It must be implemented by browser vendor, such Chrome, FireFox, IE etc, otherwise we need to downgrade the ES6 code, and let browser compile.
But V8(JavaScript Engine By Google and used in Chrome browser) achieved complete support for ES6 in mid-2016. V8 also used in Node.js. Node.js version 6.x supports full ES6 through V8 released.
So if we want to use ES6 in browser, we need to use Babel or other transpiler to compile ES6 code, but we could use ES6 in Node.js v6.x.
We could choose Node.js LTS.
The below is Node.js and V8 version.
Node.js Version | V8 Version |
---|---|
0.12.18 | 3.28.71.20 |
4.7.3 | 4.5.103.43 |
6.9.5 | 5.1.281.89 |
7.5.0 | 5.4.500.48 |
If you want to check whether the Node.js supports ES6 feature, you could see Node.Green
So, we could install Node.js v6.9.5, it is LTS version. We do not demostrate how to install Node.js here.
Arrow Function
Original:
function slicePizza(pizza, sliceNum) {
var slices = [];
for (var i = 0; i < sliceNum; i++) {
slices.push(pizza.slice());
}
return slices;
}
ES6:
var slicePizza = (pizza, sliceNum) => {
var slices = [];
for (var i = 0; i < sliceNum; i++) {
slices.push(pizza.slice());
}
return slices;
}
Original:
orderPizza('Cheese', function(pizza, totalCost) {
console.log('Ordered ' + pizza + ' for ' + totalCost);
});
ES6:
orderPizza('Cheese', (pizza, totalCost) => {
console.log('Ordered ' + pizza + ' for ' + totalCost);
});
// Improve if function has only one parameter
orderPizza('Cheese', pizza => {
console.log('Ordered ' + pizza + ' for ' + totalCost);
});
// Improve if function has only one parameter
orderPizza('Cheese', pizza => console.log('xxx'));
// If function returns value
orderPizza('Cheese', pizza => {
return bake(pizza);
});
// Improve with no return statement
orderPizza('Cheese', pizza => bake(pizza));
Default Function Parameter
Rest Parameters
Spread Syntax
Class
Assign variable with let and const
Array Destructing
Object Destructing
Shortened object property declaration
Object function property shorthand
Template strings and string interpolation
Promise
Keep values unique with Set
Drop the prototype with Map
Iterating with for...of
Generators
和Flux类似,是一种架构模式。它与Flux的区别点在于Redux集中管理程序状态,将程序状态存放在一个对象中。而Flux存储在不同的Store中。
Redux可以于不同的框架和库结合在一起架构,而不仅仅局限于React。它可以与Backbone, Angular, Vue, Riot等等结合,因为它主要用于管理程序状态,控制程序数据流。
Redux与React结合,那么Redux负责数据状态管理,React负责应用程序展示。用户与视图交互,通过调用Redux Dispatch,修改程序状态,近而改变视图的展示。
Reducer通俗来说就是一个function, 它返回程序的一块状态。所以一般应用会有多个reducer。每个reducer代表一个数据集或者叫做状态。
//使用reducer产生应用程序状态
{
books: [{title: 'React'}, {title: 'Redux'}], //Books Reducer
currentBook: {title: 'Learn React'} //currentBook Reducer
}
Redux中的container概念就是获取数据(从store中)然后通过props将数据传递給对应的子组件。
一般命名规则如:
book_container.js -> BookContainer -> Book
如果没有container, 那么用react创建的组件将负责获取数据以及展示它们。这样做其实没有什么错误的地方,只是没有充分发挥react优势,那就是
所以container只是负责获取数据,以及将合法的数据通过props传递給子组件。展示组件(Dumb Components)可以达到重用的目的。数据的传递通过props,而尽量少用state。
下面是一个container:
import React, { Component } from 'react';
import BookList from 'book_list';
export default class BookListContainer extends Component {
constructor() {
super();
this.state = {books: []};
}
componentDidMount() {
$.ajax({
url: 'books-list-url',
dataType: 'json',
success: function(res) {
this.setState({books: res.books});
}.bind(this);
});
}
render() {
return <BookList books={this.state.books} />;
}
}
container对应的组件:
import React, { Component } from 'react';
export default class BookList extends Component {
constructor(props) {
super(props);
}
renderBook() {
return this.props.books.map((book) => {
return <li key={book.title}>{book.title}</li>;
});
}
render() {
return <ul>{this.renderBook()}</ul>;
}
}
这样做数据获取以及展示分离,BookList可以重用。BookList可以通过PropTypes验证props。
参考: https://www.thoughtworks.com/insights/blog/bff-soundcloud
BFF模式(Backends for Frontends)
以前大家暴露系统API给Web, App, 第三方服务。这是一个常用的混搭模式(Mashup Pattern),很多系统都采用这种方式提供接口服务。
在早期系统都是单一的,提供API给 iOS, Android, Web以及其他的混搭模式。所有接入API接口的终端都是对等的。但是有时考虑到服务端API只针对特定API接入者进行更新,例如更新一个接口只针对App接入者,而Web接口保持不变,或者有新的终端接入,例如TV,那么API都要按需提供。
方案:
将单一系统API拆分成为微服务(Micro-Service)接口,针对不同断点提供不同的API以及版本控制。
.
&yet发布了用于构建JavaScript应用的“不像框架”的框架Ampersand.js。
Ampersand.js 是一个高度模块化,松耦合,用于构建先进的 JavaScript 应用程序的框架。通过良好定义的方法,结合了一系列微小的 CommonJS 模块。条理清晰,没有多余的冗余代码。
其灵感主要来自Backbone.js,但与Backbone.js的主要不同之处在于,Ampersand.js更加模块化,并添加了更多的(完全可选的)特性。
Ampersand.js中最重要的模块(它们的文档或许也是最完善的)包括:
When we integrate rails application by iframe, the browser console will report "X-Frame-Options to SAMEORIGIN by default" error, so we should let rails application ignore iframe check in controller.
before_action :allow_iframe_requests
private
def allow_iframe_requests
response.headers.delete('X-Frame-Options')
end
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.