donglee0504 / blogs Goto Github PK
View Code? Open in Web Editor NEW前端技术总结,项目经验总结。
前端技术总结,项目经验总结。
[toc]
建议使用 === 而不是 == 来判断是否相等
// 如果没有妥善处理的话,可能会出现和预想不一样的结果
0 == false; // true
0 === false; // false
2 == "2"; // true
2 === "2"; // false
const value = "500";
if (value === 500) {
// 不会执行
console.log(value);
}
if (value === "500") {
// 会执行
console.log(value);
}
坏
let daysSLV = 10;
let y = new Date().getFullYear();
let ok;
if (user.age > 30) {
ok = true;
}
好
const MAX_AGE = 30;
let daysSinceLastVisit = 10;
let currentYear = new Date().getFullYear();
...
const isUserOlderThanAllowed = user.age > MAX_AGE;
不要使用多余的无意义的单词来组合命名
坏
let nameValue;
let theProduct;
好
let name;
let product;
不要使用无意义的字符/单词来命名,增加额外的记忆负担
坏
const users = ["John", "Marco", "Peter"];
users.forEach(u => {
doSomething();
doSomethingElse();
// ...
// ...
// ...
// ...
// 这里u到底指代什么?
register(u);
});
好
const users = ["John", "Marco", "Peter"];
users.forEach(user => {
doSomething();
doSomethingElse();
// ...
// ...
// ...
// ...
register(user);
});
在某些环境下,不用添加冗余的单词来组合命名。比如一个对象叫user,那么其中的一个名字的属性直接用name,不需要再使用username了。
坏
const user = {
userName: "John",
userSurname: "Doe",
userAge: "28"
};
好
const user = {
name: "John",
surname: "Doe",
age: "28"
};
请使用完整的声明式的名字来给函数命名。比如一个函数实现了某个行为,那么函数名可以是一个动词或则一个动词加上其行为的被作用者。名字就应该表达出函数要表达的行为。
坏
function notif(user) {
// implementation
}
好
function notifyUser(emailAddress) {
// implementation
}
避免使用过多参数。最好一个函数只有 2 个甚至更少的参数。参数越少,越容易做测试。
坏
function getUsers(fields, fromDate, toDate) {
// implementation
}
好
function getUsers({ fields, fromDate, toDate }) {
// implementation
}
getUsers({
fields: ["name", "surname", "email"],
fromDate: "2019-01-01",
toDate: "2019-01-18"
});
为函数参数设置默认值,而不是在代码中通过条件判断来赋值。
坏
function createShape(type) {
const shapeType = type || "cube";
// ...
}
好
function createShape(type = "cube") {
// ...
}
使用Objecg.assign来设置默认对象值。
坏
const shapeConfig = {
type: "cube",
width: 200,
height: null
};
function createShape(config) {
config.type = config.type || "cube";
config.width = config.width || 250;
config.height = config.width || 250;
}
createShape(shapeConfig);
好
const shapeConfig = {
type: "cube",
width: 200
// Exclude the 'height' key
};
function createShape(config) {
config = Object.assign(
{
type: "cube",
width: 250,
height: 250
},
config
);
...
}
createShape(shapeConfig);
不要使用 true/false 的标签(flag),因为它实际上让函数做了超出它本身的事情。
坏
function createFile(name, isPublic) {
if (isPublic) {
fs.create(`./public/${name}`);
} else {
fs.create(name);
}
}
好
function createFile(name) {
fs.create(name);
}
function createPublicFile(name) {
createFile(`./public/${name}`);
}
不要污染全局。如果你需要对现有的对象进行扩展,不要在对象的原型链上定义函数。请使用 ES 的类和继承。
坏
Array.prototype.myFunc = function myFunc() {
// implementation
};
好
class SuperArray extends Array {
myFunc() {
// implementation
}
}
// Good:
const menuConfig = {
title: 'Order',
// 不包含 body
buttonText: 'Send',
cancellable: true
};
function createMenu(config) {
config = Object.assign({
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true
}, config);
// config : {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
// ...
}
createMenu(menuConfig);
// Bad:
Array.prototype.diff = function diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
};
// Good:
class SuperArray extends Array {
diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
}
}
// Bad:
get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', (requestErr, response) => {
if (requestErr) {
console.error(requestErr);
} else {
writeFile('article.html', response.body, (writeErr) => {
if (writeErr) {
console.error(writeErr);
} else {
console.log('File written');
}
});
}
});
// Good:
get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin')
.then((response) => {
return writeFile('article.html', response);
})
.then(() => {
console.log('File written');
})
.catch((err) => {
console.error(err);
});
// perfect:
async function getCleanCodeArticle() {
try {
const response = await get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin');
await writeFile('article.html', response);
console.log('File written');
} catch(err) {
console.error(err);
}
}
坏
const Person = function(name) {
if (!(this instanceof Person)) {
throw new Error("Instantiate Person with `new` keyword");
}
this.name = name;
};
Person.prototype.sayHello = function sayHello() {
/**/
};
const Student = function(name, school) {
if (!(this instanceof Student)) {
throw new Error("Instantiate Student with `new` keyword");
}
Person.call(this, name);
this.school = school;
};
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Student.prototype.printSchoolName = function printSchoolName() {
/**/
};
好
class Person {
constructor(name) {
this.name = name;
}
sayHello() {
/* ... */
}
}
class Student extends Person {
constructor(name, school) {
super(name);
this.school = school;
}
printSchoolName() {
/* ... */
}
}
使用函数调用链。像 jQuery,Lodash 都使用这个模式。你只需要在每一个函数的末尾返回this,之后的代码会更加的简洁。
坏
class Person {
constructor(name) {
this.name = name;
}
setSurname(surname) {
this.surname = surname;
}
setAge(age) {
this.age = age;
}
save() {
console.log(this.name, this.surname, this.age);
}
}
const person = new Person("John");
person.setSurname("Doe");
person.setAge(29);
person.save();
好
class Person {
constructor(name) {
this.name = name;
}
setSurname(surname) {
this.surname = surname;
// Return this for chaining
return this;
}
setAge(age) {
this.age = age;
// Return this for chaining
return this;
}
save() {
console.log(this.name, this.surname, this.age);
// Return this for chaining
return this;
}
}
const person = new Person("John")
.setSurname("Doe")
.setAge(29)
.save();
作者:Fundebug
链接:https://juejin.im/post/5cff04cbf265da1bd3054f31
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
/*! @preserve https://github.com/wusfen */
function wutpl(tpl){
var code = tpl
.replace(/<%=(.*?)%>/g, '\f_html_+= $1\f') // <%= %>
.replace(/<%(.*?)%>/g, '\f$1\f') // <% %>
.replace(/(^|\f)([\s\S]*?)(\f|$)/g, function($, $1, $2, $3){ // \f html \f
return '\n_html_+= "' + $2
.replace(/\\/g, '\\\\') // \ -> '\\'
.replace(/\r?\n/g, '\\n') // \n -> '\\n'
.replace(/"/g, '\\"') // " -> '\\"'
+ '"\n'
})
return Function('_data_', 'var _html_="";with(_data_){'+code+'}return _html_')
}
// ========== 示例与解析 ==========
// 模板
var tpl = `
<ul>
<% for(var i=0; i<list.length; i++){ %>
<li>
<%= list[i].name %>
</li>
<% } %>
</ul>
`
// 编译模板
var render = wutpl(tpl)
// 以上模板实际上编译成了以下代码 (render函数)
function anonymous(_data_) {
var _html_ = "";
with (_data_) {
_html_ += "\n<ul>\n "
for (var i = 0; i < list.length; i++) {
_html_ += "\n <li>\n "
_html_ += list[i].name
_html_ += "\n </li>\n "
}
_html_ += "\n</ul>\n"
}
return _html_
}
// 渲染数据
var html = render({
list:[
{id:1, name:'wsf'},
{id:2, name:'Tom'}
]
})
// 输出结果
console.log(html)
/*
<ul>
<li>
wsf
</li>
<li>
Tom
</li>
</ul>
*/
// 复以上内容到控制台运行即看到结果
vue cli 2.x 打包后字体路径出错
此问题是webpack的bug
解决方案
目录 build/utils.js
原代码
if (options.extract) {
return ExtractTextPlugin.extract({
use: sourceLoader,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader', sourceLoader].join('!')
}
修改后
if (options.extract) {
return ExtractTextPlugin.extract({
use: sourceLoader,
fallback: 'vue-style-loader',
publicPath: '../../'
})
} else {
return ['vue-style-loader', sourceLoader].join('!')
}
let res = {
name: 'lidong',
age: 18,
sports: 'baseketball'
},
obj,
arr = ['name'];
obj = arr.reduce((p, n) => {
if(n in res) {
p[n] = res[n]
};
return p
},{})
console.log(obj); // {name: "lidong"}
计算出一个字符串中每个字符出现的次数
let str = 'hello world';
let obj2 = str.split('').reduce((acc, item) => {
acc[item] = (acc[item] || 0) + 1;
return acc
}, {});
console.log(obj2) // {h: 1, e: 1, l: 3, o: 2, " ": 1, …}
之前在写前端代码时,在图片路径的设置那里经常会遇到一个问题.比方说,我
(1)在根目录下面新建了个"images"文夹,里面放了张图片top.gif
(2)在根目录下另外新建了两个文件夹"CSS"和"JS"专门用来存放用到的.css文件和.js文件(假设我们用到的为"test.css"和"test.js")
假设在根目录下有个"test.html"文件,里面分别引用了"test.css"以及"test.js"
Just for test!
这两者的区别很重要,当我们要引用一个图片时,在js文件中要以引用它的html的路径为准,而在css文件中,要以改css路径为准.
ant design pro 项目菜单自定义icon
步骤:
1、将svg 上传到 iconfont
2、在iconfont -- 我的项目里
3、在antd pro -- src -- defaultSettings
4、router.config.js 贴上自己图标
参考文献
(https://zhuanlan.zhihu.com/p/57998143)
(https://link.zhihu.com/?target=https%3A//github.com/ant-design/ant-design-pro/pull/3517)
<text>1</text>
<text>
2
</text>
输出
1
2
<text>1</text>
<text>2</text>
输出
12
总结:text的闭合标签注意
React 中 setState 什么时候是同步的,什么时候是异步的?
在 React 中,如果是由 React 引发的事件处理(比如通过 onClick 引发的事件处理),调用 setState 不会同步更新 this.state,除此之外的 setState 调用会同步执行 this.state。所谓“除此之外”,指的是绕过 React 通过 addEventListener 直接添加的事件处理函数,还有通过 setTimeout/setInterval 产生的异步调用。
**原因:**在 React 的 setState 函数实现中,会根据一个变量 isBatchingUpdates 判断是直接更新 this.state 还是放到队列中回头再说,而 isBatchingUpdates 默认是 false,也就表示 setState 会同步更新 this.state,但是,有一个函数 batchedUpdates,这个函数会把 isBatchingUpdates 修改为t rue,而当 React 在调用事件处理函数之前就会调用这个 batchedUpdates,造成的后果就是由 React 控制的事件处理过程 setState 不会同步更新 this.state。
Advanced-Frontend/Daily-Interview-Question#17
需求:用户需在连续点击才能看到某些信息
思路:类似js的节流,点击时记录当前时间,然后与上次时间进行对比。借鉴 https://github.com/jrainlau/rhyke
源码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<button>点击</button>
</body>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script>
var obj = {
tabTime: 0,
tabStart: 0,
tabTime: 0,
userRhythm: [],
isTimeout: false,
timer: null,
options: {
el: 'body',
rhythm: '...',
dashTime: 400,
timeout: 2000,
tabEvent: false,
matching: () => {},
matched: () => {},
unmatched: () => {},
onTimeout: () => {}
},
init: function () {
this.addEvent({
el: "button",
dashTime: 400,
timeout: 2000,
rhythm: '.....',
matching(arr) {
console.log(arr)
},
matched() {
alert('Monster awoke!!')
}
});
},
addEvent: function (options) {
var that = this;
that.options = Object.assign(that.options, options)
$(options.el).click(function () {
that.tabStartEvent = options.tabEvent ? 'touchstart' : 'mousedown';
that.tabEndEvent = options.tabEvent ? 'touchend' : 'mouseup';
that.tabStartFunc();
that.tabEndFunc();
})
},
tabStartFunc() {
this.stopTimer()
this.tabStart = new Date().getTime()
},
stopTimer() {
clearTimeout(this.timer)
},
tabEndFunc() {
this.tabTime = new Date().getTime() - this.tabStart
if (!this.isTimeout) {
this.tabTime < this.options.dashTime ? this.userRhythm.push('.') : this.userRhythm.push('-')
this.options.matching(this.userRhythm)
this.matchRhythem(this.userRhythm)
this.startTimer()
} else {
this.reset()
}
},
matchRhythem(userRhythm) {
const rhythm = this.options.rhythm
const testRhythm = userRhythm.join('')
if (testRhythm.length === rhythm.length && testRhythm === rhythm) {
this.options.matched()
this.reset()
} else if (testRhythm.length === rhythm.length && testRhythm !== rhythm) {
this.options.unmatched()
this.reset()
}
},
reset() {
this.userRhythm = []
this.isTimeout = false
this.timeoutStart = 0
this.timeout = 0
},
startTimer() {
this.timer = setTimeout(() => {
this.isTimeout = true
this.reset()
this.options.onTimeout()
}, this.options.timeout)
}
}
obj.init();
</script>
</html>
<div class="swiper-container swiper-container2" id="banner2">
<div class="swiper-wrapper">
<div class="swiper-slide">
<div class="comment">
<div class="comment_user">180*****123<span class="comment_date">12-10</span></div>
<div class="comment_content">前两天看内参推荐买了钢铁,今天暴跌及时空仓,居然还有盈余,真是太惊险了</div>
</div>
</div>
<div class="swiper-slide">
<div class="comment">
<div class="comment_user">158*****314<span class="comment_date">6-10</span></div>
<div class="comment_content">老师说得很对,抄底还是要抄估值底的龙头,我准备听老师的,买消费+医药</div>
</div>
</div>
<div class="swiper-slide">
<div class="comment">
<div class="comment_user">187*****227<span class="comment_date">11-19</span></div>
<div class="comment_content">老师很实在,从不推荐所谓的妖股,稳中求胜,跟我的风格很合。</div>
</div>
</div>
<div class="swiper-slide">
<div class="comment">
<div class="comment_user">136*****188<span class="comment_date">7-14</span></div>
<div class="comment_content">我文化水平不高也能看懂,很好</div>
</div>
</div>
<div class="swiper-slide">
<div class="comment">
<div class="comment_user">150*****233<span class="comment_date">8-19</span></div>
<div class="comment_content">之前对这种内参持怀疑态度,观察了一周,服了,说跌就跌,说反弹还真反弹了,真是准!</div>
</div>
</div>
<div class="swiper-slide">
<div class="comment">
<div class="comment_user">188*****667<span class="comment_date">10-02</span></div>
<div class="comment_content">老师说还要下跌,让多看少动,我手痒没听,抄到半山腰了,真是后悔</div>
</div>
</div>
<div class="swiper-slide">
<div class="comment">
<div class="comment_user">155*****229<span class="comment_date">7-09</span></div>
<div class="comment_content">我文化水平不高也能看懂,很好</div>
</div>
</div>
<div class="swiper-slide">
<div class="comment">
<div class="comment_user">132*****687<span class="comment_date">9-19</span></div>
<div class="comment_content">完这个,对各个行业的把握更全面了,选股不会再盲目了</div>
</div>
</div>
<div class="swiper-slide">
<div class="comment">
<div class="comment_user">158*****145<span class="comment_date">12-19</span></div>
<div class="comment_content">之前对这种内参持怀疑态度,观察了一周,服了,说跌就跌,说反弹还真反弹了,真是准!</div>
</div>
</div>
<div class="swiper-slide">
<div class="comment">
<div class="comment_user">153*****663<span class="comment_date">11-29</span></div>
<div class="comment_content">贴合行情,依托研报推出策略,有理有据,我信服</div>
</div>
</div>
<div class="swiper-slide">
<div class="comment">
<div class="comment_user">156*****324<span class="comment_date">12-18</span></div>
<div class="comment_content">紧跟老师步伐,我随时在准备抄底了!</div>
</div>
</div>
<div class="swiper-slide">
<div class="comment">
<div class="comment_user">186*****996<span class="comment_date">12-18</span></div>
<div class="comment_content">前两天看内参推荐买了钢铁,今天暴跌及时空仓,居然还有盈余,真是太惊险了</div>
</div>
</div>
</div>
</div>
js部分
new Swiper('#banner2', {
loop: true,
direction: 'vertical',
slidesPerView: '5',
autoplay: 3800,
noSwiping: true,
noSwipingClass: 'swiper-slide',
spaceBetween: 10
});
注意:每个slide之间的间距用css控制会出现slide滚动不完全,采用swiper的配置项spaceBetween即可
比如2018-08-08 24:00:00 应写成 2018/08/09 00:00:00
外层元素display:table,里面元素:display:table-cell;vertical-align:middle;
减少http请求
function getWeekOfYear (time) {
var thisDay;
if (time) {
thisDay = new Date(time)
} else {
thisDay = new Date()
}
var firstDay = new Date(thisDay.getFullYear(),0, 1); //本年的第一天,Js月份从0开始记!0就是1月啦。
var dayWeek = thisDay.getDay(); //今天周几
if (dayWeek == 0) {
dayWeek = 7;
}
startWeek = firstDay.getDay(); //本年第一天周几
if (startWeek == 0) {
startWeek = 7;
}
//第几周
return ((thisDay.getTime() - firstDay.getTime()) / (24 * 60 * 60 * 1000) + startWeek - dayWeek) / 7 + 1
};
body.modal-open {
position: fixed;
width: 100%;
}
var ModalHelper = (function(bodyCls) {
var scrollTop;
return {
afterOpen: function() {
scrollTop = document.scrollingElement.scrollTop;
document.body.classList.add(bodyCls);
document.body.style.top = -scrollTop + 'px';
},
beforeClose: function() {
document.body.classList.remove(bodyCls);
// scrollTop lost after set position:fixed, restore it back.
document.scrollingElement.scrollTop = scrollTop;
}
};
})('modal-open');
在modalOpen时调用ModalHelper.afterOpen();
在modalClose时调用ModalHelper.beforeClose();
网址链接:https://uedsky.com/2016-06/mobile-modal-scroll/ https://segmentfault.com/a/1190000005617307
if let url = URL(string: "https://www.reddit.com") {
if #available(iOS 10.0, *) {
UIApplication.shared.open(url, options: [:])
} else {
UIApplication.shared.openURL(url)
}
}
function getFirstDayOfWeek (date) {
var day = date.getDay() || 7;
return new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1 - day);
};
function lazyload() {
var seeHeight = document.body.clientHeight; //可见区域高度
var scrollTop = $(window).scrollTop(); //滚动条距离顶部高度;
for (var i = 0; i < num; i++) {
if (((gif.eq(i).offset().top) < seeHeight + scrollTop) && (gif.eq(i).offset().top >= scrollTop)) { // GIF图进入可视区
if (!gifInit[i]._init) { // GIF进入
var src = gif.eq(i).attr('data-src');
gif.eq(i).attr('src', src);
gifInit[i]._init = true
console.log(gifInit[i]._init, 'true')
} else {
return
}
} else {
gifInit[i]._init = false //GIF离开
console.log(gifInit[i]._init, 'false')
}
}
};
$(window).scroll(lazyload);
})
1、在keyup事件外层定义一个延时器
2、在keyup事件触发时清除掉定时
$(".stock_code_put").on("keyup", function () {
clearTimeout(that.timer);
that.timer = setTimeout(function () {
that.searchCode = $(".stock_code_put").val();
if (that.searchCode) {
that.ajaxSearch();
} else {
$(".search_list").addClass("none");
}
}, 500);
//函数防抖
function debounce(method,time){
var timer = null;
return function(){
console.log('被触发')
var context = this;
//在函数执行的时候先清除timer定时器;
console.log(timer)
clearTimeout(timer);
timer = setTimeout(function(){
method.call(context);
},time);
}
}
//防抖给人的感受是执行后等待一定时间才会被执行
//如果在等待的时间内又被触发,以最后的为准 因为上次的被clear掉了
//总之一定会等待规定时间秒才会被执行
// 节流
function throttle (func,delay){
var prev = Date.now();
return function(){
console.log('被触发')
var context = this;
var args = arguments;
var now = Date.now();
if(now-prev>=delay){
func.apply(context,args);
prev = Date.now();
}
}
}
// 函数节流给人的感受是上次执行时间和第二次执行时间相隔一定秒。否则不会被执行真正的函数
// 上面函数返回是一个闭包,prev不会被清除掉,并且每次prev都会更新为最后一次执行的时间
// 要么不会执行,要么就立即执行不会等待
a、请求后台时返回promise对象,然后then回调
ajax1: function(){
return new promise(resolve) {
// 请求数据
resolve(data)
}
}
ajax2: function(){
return new promise(resolve) {
// 请求数据
resolve(data)
}
}
ajax1().then().then(ajax2())
b、采用async await
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value) // timeout函数执行完毕后才会打印
}
asyncPrint('hello world', 50);
//点击评论框 var bfscrolltop = 0;//获取软键盘唤起前浏览器滚动部分的高度 $('input[type="text"],textarea').focus(function() { //给个延迟 bfscrolltop = document.body.scrollTop;//获取软键盘唤起前浏览器滚动部分的高度 interval = setInterval(function() { document.body.scrollTop = document.body.scrollHeight}, 100 ); window.addEventListener('touchmove', fn, false); }).blur(function(){ clearInterval(interval); }); //如果你的页面中使用了iscorll插件,这时候你需要处理评论框的滑动事件————拒绝滑动 function fn(ev) { var _target = ev.target || ev.srcElement; if(_target.nodeName != 'TEXTAREA') { $('.pinglun_footerr_text').blur(); } };
--------------------- 本文来自 风中乘凉 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/qq_37231097/article/details/76614702?utm_source=copy
<button class="cs_button" open-type="contact" session-from="weapp">
<image class="cs_image" src="../images/cs.png"></image>
</button>
.cs_button {
background-color: #fff;
border: 0px;
height: 100rpx;
position: fixed;
margin-left: 240rpx;
}
// 这里是关键css 否则button有边框
.cs_button:after {
border: 0px;
}
.cs_image {
width: 100rpx;
height: 100rpx;
}
本文来自 Afanbaby 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/Afanbaby/article/details/78796968?utm_source=copy
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.