I am a frontEnd web developer from China.
huyuee / blog Goto Github PK
View Code? Open in Web Editor NEW📖 个人博客
Home Page: https://www.jianshu.com/u/2b10ebdd9c0f
License: MIT License
📖 个人博客
Home Page: https://www.jianshu.com/u/2b10ebdd9c0f
License: MIT License
I am a frontEnd web developer from China.
分配每个人统计各个组件在不同框架中的样式,详细至链接,或者截图
通过查看各个不同样式的,来分析出各个组件的最小版本,需要的各个功能
抽离出最小的组件样式,给出默认样式
以后随着不同项目组的不同的需求,提炼出多个高阶模板函数,多种不同的设计风格和场景,针对于类似的设计风格,可以在现有高阶模板函数的基础上进行拓展;否则就建立新的高阶模板组件
步骤如下:
将下面的脚本中的三处xxxx的位置替换相应的数据
注意:第三处ObjectPath的xxx处的替换为你想要刷新缓存的域名,比如我这里是:http://design.yonyoucloud.com/
然后再该python文件目录下运行python xx.py
#!/usr/bin/python
# -*- coding:utf-8 -*-
import sys,os
import urllib, urllib2
import base64
import hmac
import hashlib
from hashlib import sha1
import time
import uuid
class pushAliCdn:
def __init__(self):
self.cdn_server_address = 'http://cdn.aliyuncs.com'
self.access_key_id = 'xxxxxxxxx'
self.access_key_secret = 'xxxxxxxxxxxxxxxx'
def percent_encode(self, str):
res = urllib.quote(str.decode(sys.stdin.encoding).encode('utf8'), '')
res = res.replace('+', '%20')
res = res.replace('*', '%2A')
res = res.replace('%7E', '~')
return res
def compute_signature(self, parameters, access_key_secret):
sortedParameters = sorted(parameters.items(), key=lambda parameters: parameters[0])
canonicalizedQueryString = ''
for (k,v) in sortedParameters:
canonicalizedQueryString += '&' + self.percent_encode(k) + '=' + self.percent_encode(v)
stringToSign = 'GET&%2F&' + self.percent_encode(canonicalizedQueryString[1:])
h = hmac.new(access_key_secret + "&", stringToSign, sha1)
signature = base64.encodestring(h.digest()).strip()
return signature
def compose_url(self, user_params):
timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
parameters = { \
'Format' : 'JSON', \
'Version' : '2014-11-11', \
'AccessKeyId' : self.access_key_id, \
'SignatureVersion' : '1.0', \
'SignatureMethod' : 'HMAC-SHA1', \
'SignatureNonce' : str(uuid.uuid1()), \
'TimeStamp' : timestamp, \
}
for key in user_params.keys():
parameters[key] = user_params[key]
signature = self.compute_signature(parameters, self.access_key_secret)
parameters['Signature'] = signature
url = self.cdn_server_address + "/?" + urllib.urlencode(parameters)
return url
def make_request(self, user_params, quiet=False):
url = self.compose_url(user_params)
#print url
#刷新url
try:
req = urllib2.Request(url)
res_data = urllib2.urlopen(req)
res = res_data.read()
return res
except:
return user_params['ObjectPath'] + ' refresh failed!'
if __name__ == '__main__':
f = pushAliCdn()
params = {'Action': 'RefreshObjectCaches', 'ObjectPath': 'http://xxxxxxxxxx/', 'ObjectType': 'Directory'}
res = f.make_request(params)
print res
大概的**就是:先拆分,然后反转,然后拼接,然后使用match已三位数截断,然后用逗号拼接,再拆分,然后反转,最后拼接!ok!
下面是源码,可以在codepen中打开,地址
//保留两位小数并且整数部分三位一个逗号分隔符的数字金钱标准表示法:
//这里假设我们即不知道输入数字的整数位数,也不知道小数位数
/*将100000转为100,000.00形式*/
var dealNumber = function(money){
if(money && money!=null){
money = String(money);
var left=money.split('.')[0],right=money.split('.')[1];
right = right ? (right.length>=2 ? '.'+right.substr(0,2) : '.'+right+'0') : '.00';
var temp = left.split('').reverse().join('').match(/(\d{1,3})/g);
return (Number(money)<0?"-":"") + temp.join(',').split('').reverse().join('')+right;
}else if(money===0){ //注意===在这里的使用,如果传入的money为0,if中会将其判定为boolean类型,故而要另外做===判断
return '0.00';
}else{
return "";
}
};
/*将100,000.00转为100000形式*/
var undoNubmer = function(money){
if(money && money!=null){
money = String(money);
var group = money.split('.');
var left = group[0].split(',').join('');
return Number(left+"."+group[1]);
}else{
return "";
}
};
更快init出package.json
npm init -y
枚举可用的脚本
1.cat package.json
2.使用ntl工具,npm install -g ntl,然后再项目中使用ntl命令选择脚本
枚举已经安装的包
npm ls --depth 0
访问项目的github主页
npm repo
访问项目的homepage
npm home
访问项目的readme
npm docs
修改包的版本
npm version patch
npm version minor
npm version major
给vue开发者和爱好者发送点福利!
卡片上一共117个api,方便查阅
网址: https://vuejs-tips.github.io/cheatsheet
Github: https://github.com/vuejs-tips/cheatsheet
PDF: https://vuejs-tips.github.io/cheatsheet/vuejs-cheatsheet.pdf
原文地址:链接----自寻梯子
开篇我声明,是写入门教程的套路,并不是所有的教程都是如此。不喜勿喷..
我总结了下,入门教程要想写的好(被人点的赞多),首先我们得清楚看这个入门教程的人想知道什么。其实,无非就是三点:
入门教程第一个标题一般就是,什么是xx?那么回答这个问题,核心描述一定得抓住。
核心要浅显易懂,要直入主题,一定不要搞一些复杂的专有名词(个人认为就是装X),不要说的花里胡哨。最好就是一句话。
这里我举了两个例子,比如:
MobX只做一件事,解决 state 到 view 的数据更新问题。
还有一个例子:
MobX 是一个简单、方便扩展、久经考验的状态管理解决方案。这个教程旨在十分钟内向你介绍 MobX 的一些重要概念。MobX 是一个独立的苦,不过大多数人都把它和 React 一起使用,所以本教程也就着眼于这个组合展开。
这里很清楚、很明显的能看到这两个回答的对比之处。
第二个例子太含糊其词了,状态管理,什么状态,如何管理的。到底是什么东西,这个到底有什么,用户还是不太明白你再说什么。所以入门教程最重要的就是尽量用不专业的名词,来解释专业名词。通俗的说就是入门教程的内容尽量要用小孩能听懂的词语来解释一个比较专业的东西,这是最重要的一点
这里我们需要交代这个框架或者这个技术他主要涉及到的技术点有什么内容,一定要配合图片
比如
核心理念
mobx 引入了几个概念,
Observable state
,Derivations
和Reactions
。可以拿 Excel 表格做个比喻,
Observable state
是单元格,Derivations
是计算公式,单元格的修改会触发公司的重新计算,并返回值,而最终公式的计算结果需要显示在屏幕上(比如通过图表的方式),这是Reactions
。
作者通过一个图片来展示,主要涉及的就是三块,一个state,一个derivations,一个Reactions。并且通过Excel表格的比喻来解释这个核心理念,就更加通俗易懂了。
所以图片和通俗易懂的比喻更加能让读者明白
怎么使用?当然就是一段能够快速上手的简单示例或者demo了。
没错,确实是这样。但是不仅仅是demo,而且一定要有可以在线调试的地址(方便读者可以快速练手或者快速改动代码看效果)。不明白?没关系,看看别的大神是怎么做的。如下图:
如上图所示,一个在线调试的地址和demo代码片段同样重要
规范有很多,但是可以参考下面规范👇
基于开发共识,结合实际项目,坚持制定好的代码规范。确保同一团队以及不同的项目间,都能够实现代码风格的一致性,提高可维护性、可读性。
全部采用小写方式, 以下划线分隔。
例:my_project_name
参照项目命名规则;
有复数结构时,要采用复数命名法。
例:scripts, styles, images, data_models
参照项目命名规则。
例:account_model.js
参照项目命名规则。
例:retina_sprites.scss
参照项目命名规则。
例:error_report.html
│ .babelrc
│ .gitignore
│ LICENSE
│ package.json
│ postcss.config.js
│ README.md
│ uba.config.js
│ uba.mock.js
│
├─dist
│ ├─assets
│ │ ├─css
│ │ ├─js
│ │ ├─images
│ │
│ │ index.html
├─mock
│ └─index.json
└─src
│ constant.js
│ index.html
│ index.js
│ index.less
│
├─assets
│ README.md
│
├─components
│ │ index.js
│ ├─LoadingTable
│ index.css
│ index.jsx
│
├─pages
│ │ index.js
│ │
│ ├─App
│ index.css
│ index.jsx
│
└─api
index.js
</li>
和 </body>
。<!DOCTYPE html>
<html>
<head>
<title>Page title</title>
</head>
<body>
<img src="images/company_logo.png" alt="Company">
<h1 class="hello-world">Hello, world!</h1>
</body>
</html>
<!DOCTYPE html>
<html>
...
</html>
应在html标签上加上lang属性。这会给语音工具和翻译工具帮助,告诉它们应当怎么去发音和翻译。
但 sitepoint
只是给出了语言的大类,例如中文只给出了zh,但是没有区分香港,**,大陆。而微软给出了一份更加详细的语言列表,其中细分了 zh-cn, zh-hk, zh-tw
。
<!DOCTYPE html>
<html lang="en-us">
...
</html>
通过声明一个明确的字符编码,让浏览器轻松、快速的确定适合网页内容的渲染方式,通常指定为'UTF-8'。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
...
</html>
用 <meta>
标签可以指定页面应该用什么版本的IE来渲染;
如果你想要了解更多,请点击这里;
不同 doctype
在不同浏览器下会触发不同的渲染模式(这篇文章总结的很到位)。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
</head>
...
</html>
根据HTML5规范, 通常在引入CSS和JS时不需要指明 type,因为 text/css 和 text/javascript 分别是他们的默认值。
HTML5 规范链接:
<!-- External CSS -->
<link rel="stylesheet" href="code_guide.css">
<!-- In-document CSS -->
<style>
...
</style>
<!-- External JS -->
<script src="code_guide.js"></script>
<!-- In-document JS -->
<script>
...
</script>
属性应该按照特定的顺序出现以保证易读性:
class是为高可复用组件设计的,所以应处在第一位;
id更加具体且应该尽量少使用,所以将它放在第二位。
<a class="..." id="..." data-modal="toggle" href="#">Example link</a>
<input class="form-control" type="text">
<img src="..." alt="...">
boolean属性指不需要声明取值的属性,XHTML需要每个属性声明取值,但是HTML5并不需要;
更多内容可以参考 WhatWG section on boolean attributes:
boolean属性的存在表示取值为true,不存在则表示取值为false。
<input type="text" disabled>
<input type="checkbox" value="1" checked>
<select>
<option value="1" selected>1</option>
</select>
在JS文件中生成标签让内容变得更难查找,更难编辑,性能更差。应该尽量避免这种情况的出现。
减少标签数量
在编写HTML代码时,需要尽量避免多余的父节点;
很多时候,需要通过迭代和重构来使HTML变得更少。
<!-- Not well -->
<span class="avatar">
<img src="...">
</span>
<!-- Better -->
<img class="avatar" src="...">
实用高于完美
尽量遵循HTML标准和语义,但是不应该以浪费实用性作为代价;
任何时候都要用尽量小的复杂度和尽量少的标签来解决问题。
以下为前端开发团队遵循和约定的 CSS 编码规范。
代码应该符合 CSS 语法有效性,可以使用 W3C CSS validator 工具来验证。
ID 和 Class 应该按照元素功能命名,不应该按照元素表现命名,命名应该含义清晰。
/* bad: 含义不清 */
#yee-1901 {}
/* bad: 表现化 */
.button-green {}
.clear {}
/* good: 功能化 */
#gallery {}
#login {}
.video {}
ID 和 Class 命名应该在保持含义清晰的前提下尽可能简短。
/* bad */
#navigation {}
.atr {}
/* good */
#nav {}
.author {}
ID 和 Class 命名中单词应该全部小写,单词之间使用 -
作为分隔符。
/* bad */
#videoId {}
.demoimage {}
.error_status {}
/* good */
#video-id {}
.ads-sample {}
不能「MUST NOT」把 ID 和 Class 选择符作为类型选择符的限定符,这样做没必要,反而还影响性能。
/* bad */
ul#example {}
div.error {}
/* good */
#example {}
.error {}
CSS 属性应该尽可能使用简化方式书写,需注意简写时默认值的副作用,详细参考 Shorthand properties。
/* bad */
border-top-style: none;
font-family: palatino, georgia, serif;
font-size: 100%;
line-height: 1.6;
padding-bottom: 2em;
padding-left: 1em;
padding-right: 1em;
padding-top: 0;
/* good */
border-top: 0;
font: 100%/1.6 palatino, georgia, serif;
padding: 0 1em 2em;
CSS 属性中的 0
值不应该带单位。
/* bad */
margin: 0px;
padding: 0px;
/* good */
margin: 0;
padding: 0;
CSS 属性中数值介于-1到1之间的小数应该忽略开头的 0
。
/* bad */
font-size: 0.8em;
/* good */
font-size: .8em;
CSS 的色值应该尽可能使用简化写法。
/* bad */
color: #eebbcc;
/* good */
color: #ebc;
必须采用 4 个空格为一次缩进。
CSS 属性声明应该按字母升序排列。
/* good */
background: fuchsia;
border: 1px solid;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
color: black;
text-align: center;
text-indent: 2em;
CSS 每个代码块相对于父代码库必须有缩进。
/*good*/
@media screen, projection {
html {
background: #fff;
color: #444;
}
}
CSS 属性声明必须以分号结尾。
CSS 属性名冒号后必须有一个空格。
/* bad */
color:#eebbcc;
/* good */
color: #ebc;
CSS 中的属性名建议按照字母顺序排列,可以使用 Sublime Text 的 F5 命令来自动格式化。
最后的选择符与 {
之间必须有一个空格。
/* bad */
#video{
margin-top: 1em;
}
.author
{
margin-top: 1em;
}
/* good */
#video {
margin-top: 1em;
}
多个并列的选择符必须换行。
/* bad */
a:focus, a:active {
position: relative; top: 1px;
}
/* good */
h1,
h2,
h3 {
font-weight: normal;
line-height: 1.2;
}
CSS 规则之间必须以空白行分隔。
/* good */
html {
background: #fff;
}
body {
margin: auto;
width: 50%;
}
CSS 属性值中所有使用到引号的位置必须使用单引号。
/* bad */
@import url("//www.google.com/css/maia.css");
html {
font-family: "open sans", arial, sans-serif;
}
/* good */
@import url('//www.google.com/css/maia.css');
html {
font-family: 'open sans', arial, sans-serif;
}
CSS规则段落之前应该添加注释说明。
/* good */
/* Header */
#adw-header {}
/* Footer */
#adw-footer {}
/* Gallery */
.adw-gallery {}
以下内容前端开发团队遵循和约定的 JavaScript 编码规范。
使用soft tab(4个空格)。
var x = 1,
y = 1;
if (x < y) {
x += 10;
} else {
x += 1;
}
不要超过 80,但如果编辑器开启 word wrap
可以不考虑单行长度。
原始值: 相当于传值
string
number
boolean
null
undefined
var foo = 1,
bar = foo;
bar = 9;
console.log(foo, bar); // => 1, 9
复杂类型: 相当于传引用
object
array
function
var foo = [1, 2],
bar = foo;
bar[0] = 9;
console.log(foo[0], bar[0]); // => 9, 9
使用字面值创建对象
// bad
var item = new Object();
// good
var item = {};
不要使用保留字 reserved words 作为键
// bad
var superman = {
class: 'superhero',
default: { clark: 'kent' },
private: true
};
// good
var superman = {
klass: 'superhero',
defaults: { clark: 'kent' },
hidden: true
};
使用字面值创建数组
// bad
var items = new Array();
// good
var items = [];
如果你不知道数组的长度,使用push
var someStack = [];
// bad
someStack[someStack.length] = 'abracadabra';
// good
someStack.push('abracadabra');
当你需要拷贝数组时使用slice
var len = items.length,
itemsCopy = [],
i;
// bad
for (i = 0; i < len; i++) {
itemsCopy[i] = items[i];
}
// good
itemsCopy = items.slice();
使用slice将类数组的对象转成数组.
function trigger() {
var args = Array.prototype.slice.call(arguments);
...
}
对字符串使用单引号 ''
// bad
var name = "Bob Parr";
// good
var name = 'Bob Parr';
// bad
var fullName = "Bob " + this.lastName;
// good
var fullName = 'Bob ' + this.lastName;
超过80个字符的字符串应该使用字符串连接换行
// bad
var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';
// bad
var errorMessage = 'This is a super long error that \
was thrown because of Batman. \
When you stop to think about \
how Batman had anything to do \
with this, you would get nowhere \
fast.';
// good
var errorMessage = 'This is a super long error that ' +
'was thrown because of Batman.' +
'When you stop to think about ' +
'how Batman had anything to do ' +
'with this, you would get nowhere ' +
'fast.';
编程时使用join而不是字符串连接来构建字符串,特别是IE
var items,
messages,
length, i;
messages = [{
state: 'success',
message: 'This one worked.'
},{
state: 'success',
message: 'This one worked as well.'
},{
state: 'error',
message: 'This one did not work.'
}];
length = messages.length;
// bad
function inbox(messages) {
items = '<ul>';
for (i = 0; i < length; i++) {
items += '<li>' + messages[i].message + '</li>';
}
return items + '</ul>';
}
// good
function inbox(messages) {
items = [];
for (i = 0; i < length; i++) {
items[i] = messages[i].message;
}
return '<ul><li>' + items.join('</li><li>') + '</li></ul>';
}
函数表达式:
// 匿名函数表达式
var anonymous = function() {
return true;
};
// 有名函数表达式
var named = function named() {
return true;
};
// 立即调用函数表达式
(function() {
console.log('Welcome to the Internet. Please follow me.');
})();
绝对不要在一个非函数块里声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但是它们解析不同。
块
定义为一组语句,函数声明不是一个语句。阅读ECMA-262对这个问题的说明.// bad
if (currentUser) {
function test() {
console.log('Nope.');
}
}
// good
if (currentUser) {
var test = function test() {
console.log('Yup.');
};
}
arguments
, 这将会逾越函数作用域内传过来的 arguments
对象.// bad
function nope(name, options, arguments) {
// ...stuff...
}
// good
function yup(name, options, args) {
// ...stuff...
}
当使用变量访问属性时使用中括号.
var luke = {
jedi: true,
age: 28
};
function getProp(prop) {
return luke[prop];
}
var isJedi = getProp('jedi');
总是使用 var
来声明变量,如果不这么做将导致产生全局变量,我们要避免污染全局命名空间。
// bad
superPower = new SuperPower();
// good
var superPower = new SuperPower();
使用一个 var
以及新行声明多个变量,缩进4个空格。
// bad
var items = getItems();
var goSportsTeam = true;
var dragonball = 'z';
// good
var items = getItems(),
goSportsTeam = true,
dragonball = 'z';
// bad
var i, len, dragonball,
items = getItems(),
goSportsTeam = true;
// bad
var i, items = getItems(),
dragonball,
goSportsTeam = true,
len;
// good
var items = getItems(),
goSportsTeam = true,
dragonball,
length,
i;
// bad
function() {
test();
console.log('doing stuff..');
//..other stuff..
var name = getName();
if (name === 'test') {
return false;
}
return name;
}
// good
function() {
var name = getName();
test();
console.log('doing stuff..');
//..other stuff..
if (name === 'test') {
return false;
}
return name;
}
// bad
function() {
var name = getName();
if (!arguments.length) {
return false;
}
return true;
}
// good
function() {
if (!arguments.length) {
return false;
}
var name = getName();
return true;
}
适当使用 ===
和 !==
以及 ==
和 !=
.
条件表达式的强制类型转换遵循以下规则:
''
则被计算为 false, 否则为 trueif ([0]) {
// true
// An array is an object, objects evaluate to true
}
使用快捷方式.
// bad
if (name !== '') {
// ...stuff...
}
// good
if (name) {
// ...stuff...
}
// bad
if (collection.length > 0) {
// ...stuff...
}
// good
if (collection.length) {
// ...stuff...
}
给所有多行的块使用大括号
// bad
if (test)
return false;
// good
if (test) return false;
// good
if (test) {
return false;
}
// bad
function() { return false; }
// good
function() {
return false;
}
使用 /** ... */
进行多行注释,包括描述,指定类型以及参数值和返回值
// bad
// make() returns a new element
// based on the passed in tag name
//
// @param <String> tag
// @return <Element> element
function make(tag) {
// ...stuff...
return element;
}
// good
/**
* make() returns a new element
* based on the passed in tag name
* @param <String> tag
* @return <Element> element
*/
function make(tag) {
// ...stuff...
return element;
}
使用 //
进行单行注释,在评论对象的上面进行单行注释,注释前放一个空行.
// bad
var active = true; // is current tab
// good
// is current tab
var active = true;
// bad
function getType() {
console.log('fetching type...');
// set the default type to 'no type'
var type = this._type || 'no type';
return type;
}
// good
function getType() {
console.log('fetching type...');
// set the default type to 'no type'
var type = this._type || 'no type';
return type;
}
FIXME
或 TODO
帮助其他人迅速理解function Calculator() {
// FIXME: shouldn't use a global here
total = 0;
return this;
}
function Calculator() {
// TODO: total should be configurable by an options param
this.total = 0;
return this;
}
将tab设为4个空格
// bad
function() {
∙∙var name;
}
// bad
function() {
∙var name;
}
// good
function() {
∙∙∙∙var name;
}
大括号前放一个空格
// bad
function test(){
console.log('test');
}
// good
function test() {
console.log('test');
}
// bad
dog.set('attr',{
age: '1 year',
breed: 'Bernese Mountain Dog'
});
// good
dog.set('attr', {
age: '1 year',
breed: 'Bernese Mountain Dog'
});
// bad
$('#items').find('.selected').highlight().end().find('.open').updateCount();
// good
$('#items')
.find('.selected')
.highlight()
.end()
.find('.open')
.updateCount();
// bad
var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true)
.attr('width', (radius + margin) * 2).append('svg:g')
.attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
.call(tron.led);
// good
var leds = stage.selectAll('.led')
.data(data)
.enter().append('svg:svg')
.class('led', true)
.attr('width', (radius + margin) * 2)
.append('svg:g')
.attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
.call(tron.led);
不要将逗号放前面
// bad
var once
, upon
, aTime;
// good
var once,
upon,
aTime;
// bad
var hero = {
firstName: 'Bob'
, lastName: 'Parr'
, heroName: 'Mr. Incredible'
, superPower: 'strength'
};
// good
var hero = {
firstName: 'Bob',
lastName: 'Parr',
heroName: 'Mr. Incredible',
superPower: 'strength'
};
不要加多余的逗号,这可能会在IE下引起错误,同时如果多一个逗号某些ES3的实现会计算多数组的长度。
// bad
var hero = {
firstName: 'Kevin',
lastName: 'Flynn',
};
var heroes = [
'Batman',
'Superman',
];
// good
var hero = {
firstName: 'Kevin',
lastName: 'Flynn'
};
var heroes = [
'Batman',
'Superman'
];
语句结束一定要加分号
// bad
(function() {
var name = 'Skywalker'
return name
})()
// good
(function() {
var name = 'Skywalker';
return name;
})();
// good
;(function() {
var name = 'Skywalker';
return name;
})();
在语句的开始执行类型转换.
字符串:
// => this.reviewScore = 9;
// bad
var totalScore = this.reviewScore + '';
// good
var totalScore = '' + this.reviewScore;
// bad
var totalScore = '' + this.reviewScore + ' total score';
// good
var totalScore = this.reviewScore + ' total score';
对数字使用 parseInt
并且总是带上类型转换的基数.
var inputValue = '4';
// bad
var val = new Number(inputValue);
// bad
var val = +inputValue;
// bad
var val = inputValue >> 0;
// bad
var val = parseInt(inputValue);
// good
var val = Number(inputValue);
// good
var val = parseInt(inputValue, 10);
// good
/**
* parseInt was the reason my code was slow.
* Bitshifting the String to coerce it to a
* Number made it a lot faster.
*/
var val = inputValue >> 0;
布尔值:
var age = 0;
// bad
var hasAge = new Boolean(age);
// good
var hasAge = Boolean(age);
// good
var hasAge = !!age;
// bad
function q() {
// ...stuff...
}
// good
function query() {
// ..stuff..
}
// bad
var OBJEcttsssss = {};
var this_is_my_object = {};
var this-is-my-object = {};
function c() {};
var u = new user({
name: 'Bob Parr'
});
// good
var thisIsMyObject = {};
function thisIsMyFunction() {};
var user = new User({
name: 'Bob Parr'
});
当命名构造函数或类时使用驼峰式大写
```javascript
// bad
function user(options) {
this.name = options.name;
}
var bad = new user({
name: 'nope'
});
// good
function User(options) {
this.name = options.name;
}
var good = new User({
name: 'yup'
});
```
命名私有属性时前面加个下划线 _
// bad
this.__firstName__ = 'Panda';
this.firstName_ = 'Panda';
// good
this._firstName = 'Panda';
当保存对 this
的引用时使用 _this
.
```javascript
// bad
function() {
var self = this;
return function() {
console.log(self);
};
}
// bad
function() {
var that = this;
return function() {
console.log(that);
};
}
// good
function() {
var _this = this;
return function() {
console.log(_this);
};
}
```
// bad
dragon.age();
// good
dragon.getAge();
// bad
dragon.age(25);
// good
dragon.setAge(25);
// bad
if (!dragon.age()) {
return false;
}
// good
if (!dragon.hasAge()) {
return false;
}
function Jedi(options) {
options || (options = {});
var lightsaber = options.lightsaber || 'blue';
this.set('lightsaber', lightsaber);
}
Jedi.prototype.set = function(key, val) {
this[key] = val;
};
Jedi.prototype.get = function(key) {
return this[key];
};
给对象原型分配方法,而不是用一个新的对象覆盖原型,覆盖原型会使继承出现问题。
function Jedi() {
console.log('new jedi');
}
// bad
Jedi.prototype = {
fight: function fight() {
console.log('fighting');
},
block: function block() {
console.log('blocking');
}
};
// good
Jedi.prototype.fight = function fight() {
console.log('fighting');
};
Jedi.prototype.block = function block() {
console.log('blocking');
};
方法可以返回 this
帮助方法可链。
// bad
Jedi.prototype.jump = function() {
this.jumping = true;
return true;
};
Jedi.prototype.setHeight = function(height) {
this.height = height;
};
var luke = new Jedi();
luke.jump(); // => true
luke.setHeight(20) // => undefined
// good
Jedi.prototype.jump = function() {
this.jumping = true;
return this;
};
Jedi.prototype.setHeight = function(height) {
this.height = height;
return this;
};
var luke = new Jedi();
luke.jump()
.setHeight(20);
可以写一个自定义的toString()方法,但是确保它工作正常并且不会有副作用。
function Jedi(options) {
options || (options = {});
this.name = options.name || 'no name';
}
Jedi.prototype.getName = function getName() {
return this.name;
};
Jedi.prototype.toString = function toString() {
return 'Jedi - ' + this.getName();
};
当给事件附加数据时,传入一个哈希而不是原始值,这可以让后面的贡献者加入更多数据到事件数据里而不用找出并更新那个事件的事件处理器
// bad
$(this).trigger('listingUpdated', listing.id);
...
$(this).on('listingUpdated', function(e, listingId) {
// do something with listingId
});
更好:
// good
$(this).trigger('listingUpdated', { listingId : listing.id });
...
$(this).on('listingUpdated', function(e, data) {
// do something with data.listingId
});
!
开始,这保证了如果一个有问题的模块忘记包含最后的分号在合并后不会出现错误'use strict';
// fancyInput/fancyInput.js
!function(global) {
'use strict';
var previousFancyInput = global.FancyInput;
function FancyInput(options) {
this.options = options || {};
}
FancyInput.noConflict = function noConflict() {
global.FancyInput = previousFancyInput;
return FancyInput;
};
global.FancyInput = FancyInput;
}(this);
// bad
function setSidebar() {
$('.sidebar').hide();
// ...stuff...
$('.sidebar').css({
'background-color': 'pink'
});
}
// good
function setSidebar() {
var $sidebar = $('.sidebar');
$sidebar.hide();
// ...stuff...
$sidebar.css({
'background-color': 'pink'
});
}
$('.sidebar ul')
或 $('.sidebar ul')
find
// bad
$('.sidebar', 'ul').hide();
// bad
$('.sidebar').find('ul').hide();
// good
$('.sidebar ul').hide();
// good
$('.sidebar > ul').hide();
// good (slower)
$sidebar.find('ul');
// good (faster)
$($sidebar[0]).find('ul');
要求编码规范,接口定义规范,组件结构自由给予用户充分定制能力。
使用es6开发,尽量使用常用的ES6语法,(ES6语法参考)[http://es6.ruanyifeng.com/]
使用jsx语法
组件仓库命名为小写和“-”连接,如button、button-group
组件文件命名使用大驼峰, ComponentDemo
带命名空间的组件,如果一个组件包含只有自身使用的子组件,以该组件为命名空间编写组件,例如Table,Table.Head
不使用displayName命名
自定义属性使用data-
使用propTypes进行props类型校验
使用defaultProps定义默认参数
定义props避开react关键字及保留字,常用的props及state定义可参考下表
尽量少或者不使用ref获取和操作dom节点,使用state和prop进行控制dom
事件调用使用在元素上onClick调用
注意,react和html的表单元素的差异
使用es6后,不支持mixin,使用decorator进行扩展,(babel?需要增加解析器)和高阶组件方式扩展。
尽量不使用比较大的第三方js库
组件方法定义顺序 constructor --> 声明周期方法(componentWillMount,componentDidMount,
componentWillUpdate,componentDidUpdate,componentWillUnmount)
尽量多而有用的代码注释,方法用块级注释,结构如下例。
有必要需要些组件的销毁方法,比如 定时器,需要用销毁方法销毁定时器
...others 没有必要 勿用
自身定义的props属性应避免与react的关键字相同
代码规范使用 airbnb规范
clsPrefix="yourPre"
const clsPrefix = 'u-select';
const class1 = {
[`${clsPrefix}-item`]: true,
[`${clsPrefix}-item-last`]: stepLast,
[`${clsPrefix}-status-${status}`]: true,
[`${clsPrefix}-custom`]: icon
};
const class2 = [`${clsPrefix}-submit`, `${clsPrefix}-item`];
const classString = classNames('hide', class1, class2);
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
size | 尺寸 | string | medium |
color | 颜色 | string | '' |
shape | 形状 | string | '' |
disabled | 是否禁用(disabled 或 true false ) |
bool | false |
className | 增加额外的类名 | string | '' |
htmlType | html dom 的 type 属性 | string | '' |
style | 内联样式 | object | '' |
clsPrefix | 自定义样式前缀 | string | '' |
对于方法的传递,外部使用onClick传入事件,内部使用handleClick进行接收使用
当你的组件包含一些文字时,在src目录下,创建i18n.js
文件,写下对应的hash值。内容如下:
module.exports = {
'zh-cn': {
'ok': '确定',
'cancel': '取消',
'isee': '知道了'
},
'en-us': {
'ok': 'ok',
'cancel': 'cancel',
'isee': 'ok'
}
}
组件内这么使用:
import i18n from './i18n';
import React from 'react';
Example.defaultProps = {
locale: 'zh-cn'
};
class Example extends React.Component {
constructor(props) {
super(props);
}
render() {
const {locale} = this.props;
const locale = i18n[locale];
const buttons= [
<Button>
{locale['ok']}
</Button>,
<Button>
{locale['cancel']}
</Button>
];
return (
<div>
{ buttons }
</div>
)
}
}
//引入依赖
import React from 'react';
import ReactDOM from'react-dom';
import classnames from 'classnames';
//定义prop检验
const propTypes = {
//每一个props都要写注释
}
//定义默认参数
const defaultProps = {
}
/**
* 定义组件
*/
class Button extends React.Component {
constructor (props) {
super(props);
//定义state
this.state = {
//每一个state要写注释
}
// 事先声明方法绑定
this.MyEvent = this.MyEvent.bind(this);
}
//自定义函数方法,及注释
MyEvent () {
}
//组件生命周期方法
render () {
return (
// <div onClick={this.MyEvent}></div>
)
}
}
Button.propTypes = propTypes;
Button.defaultProps = defaultProps;
export default Button;
var warning = require('warning');
var ShouldBeTrue = false;
warning(
ShouldBeTrue,
'This thing should be true but you set to false. No soup for you!'
);
参考链接
https://github.com/react-component/react-component.github.io/blob/master/docs/zh-cn/code-style/js.md
.editorconfig
文件:
## http://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
max_line_length = 80
trim_trailing_whitespace = true
[*.md]
max_line_length = 0
trim_trailing_whitespace = false
[COMMIT_EDITMSG]
max_line_length = 0
可采用开源方案 eslint 和 csslint ,将其集成在 webpack、gulp 等构建工作流中。后续 uba 将会在工具层结合规范进行封装。
资源:
以下为前端性能优化相关的方案指导,具体优化实践应结合项目具体情况进行,不可为了优化而优化(性能优化和开发效率、用户体验以及实现成本等是相结合的)。性能优化的实现手段多样,关注领域复杂,从前到后,自上而下,方方面面都是性能优化的地方。
优化图像
Inline images
image Maps
优化css sptite,合成雪碧图
不在html中缩放图片
使用小且可缓存defavicon.ico
这是我读的第一本路遥的小说,但是就在我读完这本书的第一章之后。我就深深的被路遥细腻的文笔所吸引了,这也使我兴奋了很久。那细腻的文笔让人能很清晰的在脑海中绘画出,那文笔中透露出来的画面。这种文笔让我觉得哪怕他的小说的情节再烂,也有一种让读者读下去的欲望,更何况情节很曲折动人(虽然我还没有看完)。
1.贫穷的少平捡残汤剩饭情节:
“他直起身子来,眼睛不由地朝三只空荡荡的菜盆里瞥了一眼。他瞧见乙菜盆的底子上还有一点残汤剩水。房上的檐水滴答下来,盆底上的菜汤四处飞溅。他扭头瞧了瞧:雨雪迷蒙的大院坝里空无一人。他很快蹲下来,慌得如同偷窃一般,用勺子把盆底上混合着雨水的剩菜汤往自己的碗里舀。铁勺刮盆底的嘶啦声象炸弹的爆炸声一样令人惊心。血涌上了他黄瘦的脸。一滴很大的檐水落在盆底,溅了他一脸菜汤。他闭住眼,紧接着,就见两颗泪珠慢慢地从脸颊上滑落了下来——唉,我们姑且就认为这是他眼中溅进了辣子汤吧!” 选自《平凡的世界》第一章
这一段中通过一些人物细微的动作瞥
,扭
,还有通过描写铁勺在盆底舀菜汤的动作,生动的描写了一个自尊心很强的,贫穷的农村小伙子捡残汤剩饭,害怕被别的同学发现的丰富的内心纠葛,将主人公的紧张恐惧的心里表现的淋漓尽致
2.作为大学生的晓霞再见到少平后对时代的感慨:
”是的,他在我们的时代属于这样的青年:有文化,但没有幸运地进入大学或参加工作,因此似乎没有充分的条件直接参与�到目前社会发展的主潮之中。而另外一方面,他们又不甘心把自己局限在狭小的生活天地里。因此,他们往往带着一种悲壮的激情,在一条最为艰难的道路上进行人生的搏斗。他们顾不得高谈阔论或愤世嫉俗地忧患人类的命运。他们首先得改变自己的生存条件,同时也放弃最主要的精神追求;他们既不鄙视普通人的世俗生活,但又竭力使自己对生活的认识达到更深的层次“
这段话在现在的社会何尝不是这样呢,每个人基本顾不得忧患人类的命运,都是在思考自己怎么去改变自己的生存条件。自己何时才能买得起一套房,一辆车。更有甚者,沦为车奴,房奴。但是现在社会已较当时的社会现状好了很多,现在至少农民的孩子能通过自己的努力能够获得成功,能让自己获得财富。所以让我们整理好思绪再出发。
加油,2017!
出发,2017!
内容很简单,可以直接下载项目代码进行查看。https://github.com/HuYuee/NodeSpider
一直想整出一个个人博客出来玩玩,于是就查了些资料,发现在域名绑定个人博客这一块的资料比较杂,试了很多次才成功,所以写出篇文章供大家更方便的操作。
打开你的电脑的命令行工具,ping你的github地址,忽略“/”后面的路径,比如我的github pages地址是huyuee.github.io/blog,那么我需要ping的地址就是huyuee.github.io,如下图:
我得到了我的github pages的ip地址:151.101.100.133
进入你的阿里云的解析域名列表,选择你想要解析的域名,点击后面的解析。如下图所示:
然后点击添加解析,因为我的ip地址是151.101.100.133,所以我添加了两条解析记录。如下图所示:
进入你的github pages的仓库,然后在设置里面将的你的域名的地址,添加到custom domain中,然后保存即可。如下图所示:
设置到这个地方,你现在访问你的域名地址,比如我的是www.huyuee.com。就能看到你的github pages了!
with这种语法现如今应该已经无人问津了,但是还是想来说说这个在JavaScript中的用法和缺点
with语句的作用是将代码的作用域设置到一个特定的对象中。
利:with语句可以在不造成性能损失的情况下,减少变量的长度。很多情况下,也可以不使用with语句,而是使用一个临时变量来保存指针,来达到同样的效果。
弊:with语句使得程序在查找该语句块中的所有的变量值时,都是先在该with语句指定的对象下面先寻找一遍,然后再去外面的作用域去寻找。所以尽量不要在该语句块中去使用一些不属于该对象中的变量
var x = {
name : "古朋",
nick_name : "gupeng"
};
with(x){
console.log(name+'的小名是'+nick_name);
}
可以替换为:
var x = {
name : "古朋",
nick_name : "gupeng"
};
/*
*这里将x对象赋值到当前局部变量中,减少不必要的指针路径解析运算
*一般用于在在方法中将this对象局部化,比如:var this_ = this;
*/
var x_ = x;
console.log(x_.name+'的小名是'+x_nick_name);
1.堆排序
先使用最大堆的算法排出顺序,然后取出根节点继续递归使用最大堆算法算出第二大的数,以此类推。
1.1 最大堆算法
form表单提交
标签的属性enctype设置以何种编码方式提交表单数据。可选的值有三个:优秀的本质
--来自俞敏洪的《开讲啦》
有编程开发经验的都知道函数有作用域这种东西,JavaScript中的函数中的亦是如此。但是想要更改该函数的作用域,最方便的方式就是通过apply和call方法
apply和call在功能上是相同的,但是唯一的不同之处在于提供参数的方式。
apply使用参数数组而不是一组参数列表
window.color = "red";
var a = {
color : "blue"
};
var x = function(){
alert(this.color+"--"+arguments.length);
}
x();//red is 0
x.apply(window);//red is 0
x.apply(a,[1,2,3]);//blue is 3
call使用时参数列表
window.color = "red";
var a = {
color : "blue"
};
var x = function(){
alert(this.color+" is "+arguments.length);
}
x();//red is 0
x.call(window);//red is 0
x.call(a,1,2,3);//blue is 3
fun.apply(thisArg[, argsArray])
thisArg
在 fun 函数运行时指定的 this
值。
需要注意的是,指定的 this
值并不一定是该函数执行时真正的 this
值,如果这个函数处于非严格模式下,则指定为 null
或 undefined
时会自动指向
全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的 this
会指向该原始值的自动包装对象。
argsArray
一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 fun
函数。如果该参数的值为null
或 {{jsxref("Global_Objects/undefined", "undefined")}},则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。浏览器兼容性请参阅本文底部内容。
fun.call(thisArg[, arg1[, arg2[, ...]]])
thisArg
在fun函数运行时指定的this
值*。*需要注意的是,指定的this
值并不一定是该函数执行时真正的this
值,如果这个函数处于非严格模式下,则指定为null
和undefined
的this值会自动指向
全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this
会指向该原始值的自动包装对象。
arg1, arg2, ...
指定的参数列表。
React事件处理和DOM元素很相似。但是语法上有点不一样:
在组件中,必须谨慎对待JSX回调函数中的this,类的方法默认是不会绑定this的。
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
//如果不在此绑定this,那么this在handleClick方法中将是undefined.也可以使用箭头函数语法,就不用在此绑定了👍
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
直接可以使用if表达式进行判断,然后再加载不同的组件。不能直接在render()方法中书写。在线地址
####与运算符 &&
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById('root')
);
总结:可以直接在render中书写,如果条件是 true
,&&
右侧的元素就会被渲染,如果是 false
,React 会忽略并跳过它。
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
);
}
总结:可以通过return null来实现。很暴力!😜😜😜
注意:阻止组件渲染,并不会影响该组件生命周期方法的回调。例如,componentWillUpdate
和 componentDidUpdate
依然可以被调用。
list元素中必须包括一个特殊的key属性。
key在DOM中的某些元素被增加或删除的时候帮助React识别哪些元素发生了变化。key最好是该元素在列表中独一无二。通常使用id,没有id时候也可以使用序列号索引index作为key。但是如果列表可以重新排序,不见使用索引。这会导致渲染变得很慢。因为在操作的时候,React依赖于启发式算法。不稳定的key将使得组件实例和DOM节点进行不必要的重建,使得性能下降并丢失子组件的状态。
注意:key会作为给React的提示,但不会传递给你的组件。
当在多个输入的解决方法,你可以通过给每个元素添加一个name属性,然后让state中添加name的值,来动态的判断是何组件
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
也就是说通过修改state的状态来动态的渲染ui和更新数据
<input type="text" value={this.state.value} onChange={this.handleChange} />
不需要为每个状态更新编写事件处理程序,你可以 使用 ref 从 DOM 获取表单值。
//通过this.input.value来取到value
<input type="text" ref={(input) => this.input = input} />
总结:这两者差别这篇关于受控和非受控的表单输入
通常,状态都是首先添加在需要渲染数据的组件中。此时,如果另一个组件也需要这些数据,你可以将数据提升至离它们最近的父组件中。你应该在应用中保持 自上而下的数据流,而不是尝试在不同组件中同步状态。
好处:可以更快地寻找和定位bug的工作
可以使用children属性将子元素直接传递到输出。props.children
也可以使用自己约定的属性而不是children,比如下面例子的left和right:
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>
);
}
function App() {
return (
<SplitPane
left={
<Contacts />
}
right={
<Chat />
} />
);
}
使用 React 一起创建一个可搜索的产品数据表格,并向你展示我们的思考过程。地址
总结:这篇文章从一个具体的需求来给读者展示,一个需求功能如何优雅的通过react完成。
HTML/HTML5基础:
高健壮性CSS
深入学习JS
跨终端
工具
性能
HTTP及TCP协议族
安全性
最近一直在学习并研究mobx相关的,看了官方文档,也看到了阿华的日报里面推荐了相关的文章链接。但是要说mobx入门教程,这篇文章着实让人比较容易明白。所以迫切的想分享给各位,从下个段落开始就是那篇文章了,原文地址。但是我事后好好思考了一番,为什么别的大神(大牛)写的入门教程为什么这么通俗易懂呢。于是我总结了一个写入门教程的好一点的方式和套路。地址:如何写好一篇入门教程?
mobx 只做一件事,解决 state 到 view 的数据更新问题。
mobx 是一个库 (library),不是一个框架 (framework)。他不限制如何组织代码,在哪里保存 state 、如何处理事件,怎么发异步请求等等。我们可以回归到 Vanilla JavaScript,可以和任意类库组合使用。
mobx 引入了几个概念,Observable state
, Derivations
和 Reactions
。
可以拿 Excel 表格做个比喻,Observable state
是单元格,Derivations
是计算公式,单元格的修改会触发公司的重新计算,并返回值,而最终公式的计算结果需要显示在屏幕上(比如通过图表的方式),这是 Reactions
。
下面通过代码理解下这些概念,以 mobx 和 react 的组合使用为例:(Open Demo on jsfiddle)
import { observable, computed } from 'mobx';
import { observer } from 'mobx-react';
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
////////////////////
// Store
class TodoStore {
@observable todos = [];
@computed get completedTodosCount() {
return this.todos.filter(todo => todo.completed === true).length;
}
addTodo(task) {
this.todos.push({ task, completed: false });
}
}
////////////////////
// Components
@observer
class TodoList extends Component {
render() {
const { todoStore } = this.props;
return (
<div>
{ todoStore.todos.map((todo, index) => <Todo todo={todo} key={index} />) }
Progress: { todoStore.completedTodosCount }
</div>
);
}
}
@observer
class Todo extends Component {
render() {
const { todo } = this.props;
return (
<li onDoubleClick={this.onRename}>
<input
type="checkbox"
checked={ todo.completed }
onChange={ this.onToggleCompleted }
/>
{ todo.task }
</li>
);
}
onToggleCompleted = () => {
const todo = this.props.todo;
todo.completed = !todo.completed;
}
onRename = () => {
const todo = this.props.todo;
todo.task = prompt('Task name', todo.task) || "";
}
}
////////////////////
// Init
const todoStore = new TodoStore();
todoStore.addTodo('foo');
todoStore.addTodo('bar');
ReactDOM.render(
<TodoList todoStore={todoStore} />,
document.getElementById('mount')
);
这里通过 @observable
定义 Observable state
,通过 @computed
定义 Derivations
,通过 @observer
封装了 React Component 的 render 方法,这是 Reactions
。
mobx 官网 罗列了不少区分与 flux 框架的优点,这里摘录一些比较打动我的。
简单
没有 connect,没有 cursor,没有 Immutable Data ... 总之感觉会少很多代码。同时概念也更少。
可以用 class 来组织和修改数据
对于组织复杂的领域模型比较适用。
可以用 JavaScript 引用来组织和修改数据
比如,可以直接
todo.completed = true;
而不需要
return todos.map((todo) => { if (todo.id === action.payload.id) { return {...todo, {completed: true}} else { return todo; } });
同时也不需要引入额外的 immutable.js。
性能相比 redux 有优势
mobx 会建立虚拟推导图 (virtual derivation graph),保证最少的推导依赖。dan_abramov 亲自操刀为 todoMVC 做了极致的优化才和 mobx 打成平手。链接
参考之前 redux + redux-saga 的方案,这里的一些点可能会成为你我不用他的原因。
浏览器兼容性,不支持 IE8
由于用了 reactive arrays, objects with reactive properties (getters) 这些 ES5 特性,而且这些特性不能通过 es5-shim 解决。兼容列表可参考:http://kangax.github.io/compat-table/es5/
缺少最佳实践
这部分不在 mobx 的范围之内,需要自己探索一套最佳实践。比如如何触发 action,如何组织 store,如何组织业务逻辑,如何发异步请求,如何在 React Component 之间传递数据等等。
热替换 (Hot Module Replacement)
用过 HMR,就不愿再回到手动刷页面的时代。mobx 支持 [react-transform] 的热替换方式,但是否支持 webpack 原生热替换情况下对 store 进行替换,还有待探索。
mobx 简单高效,在用吐了 redux 之后,对 mobx 简直爱不释手。除了不支持 IE8 这个硬伤,其他缺点都还是可以接受的。我会在后面的小项目中应用它,并尝试探索一套最佳实践。
原型,其实已经是前端知识中老生常谈的内容了。很多初学者和工作者其实都觉得这个概念其实跟你使用JavaScript没有太大的联系(因为我刚开始其实就是这样)。但是当你深入到代码中,一些架构中的时候,你就会发现巧妙的运用原型,能让你的代码写的既简洁又优美
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象
举个栗子:
function Person{
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true
如下图图一所示,针对于上面的“栗子”,Person
是一个函数,那么在 JavaScript中就会为这个函数创建一个prototype的属性,这个prototype属性指向该函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor属性,这个属性指向该函数。
如上图图一所示:当调用构造函数创建一个新实例(person1和person2)后,该实例的内部将包含一个指针,指向构造函数的原型对象(Person Prototype)。
查找机制是这样的:首先会先在实例上面搜索属性,如果找到了直接返回,否则就继续去原型上面寻找。
说个形象一点的故事,这样好理解一点。假设:实例就是你,原型就是你的父亲。你的有些东西是继承自你的父亲。比如你父亲在北京有一套价值1000W的别墅,他作为不动产留给了你。你自己也通过自己的努力,积攒了300W的积蓄。这个时候,你急需要用钱,你改怎么办?首先你会看你自己有没有这个符合条件的积蓄,如果有,那么就用掉自己的积蓄,如果没有,那么就只能用父亲的房子作抵押给别人了。
先来个栗子:
function Person(){
}
Person.prototype.money = "1000W";
var person1 = new Person();
var person2 = new Person();
person1.money = "300W";
alert(person1.money); //"300W"----来自实例,是自己的钱
alert(person2.money); //"1000W"----来自原型,是父亲的钱
从上面的例子我们能发现,当在alert()中访问person1.name时,他就会去实例上面搜索一个名为name的属性。这个属性在person1实例中找到了,直接返回。同理在person2的实例中寻找name属性时,没有找到,这时就需要继续去原型寻找,这个时候找到了,于是返回原型上面的值。
在了解完上面的知识之后,有的人就会问了,那我在写代码的时候,如何去判断属性值是来自实例的,还是来自原型对象上面的?就是说我想知道那个钱,到底我自己的积蓄,还是用的我父亲的房子。这个时候我就可以借助方法hasOwnProperty(),当属性值是来自实例,也就是说是自己的钱,那么返回true,否则返回false
来段代码来看看:
function Person(){
}
Person.prototype.money = "1000W";
var person1 = new Person();
var person2 = new Person();
person1.money = "300W";
alert(person1.hasOwnProperty(money)); //true----来自实例,是自己的钱
alert(person2.hasOwnProperty(money)); //false----来自原型,是父亲的钱
在看了前面栗子之后,你们可能注意到了,每添加一个属性和方法就要敲一遍Person.prototype。为了使代码简洁美观,最常见的做法是用一个包含所有属性的方法的对象字面量来重写整个原型对象。
来看个栗子:
function Person(){
}
Person.prototype = {
name : "Lee",
age : 20
}
但是上面的这种更简单的写法有几个问题:
1.上面的写法本质上完全重写了默认的prototype对象,因此使得原型中的constructor属性不再指向Person了。这种情况的时候如果constructor属性很重要,可以像下面这样特意将他设置回适当的值
function Person(){
}
Person.prototype = {
constructor : Person,//设置回适当的值
name : "Lee",
age : 20
}
2.上面的写法在重写了默认的prototype对象,切断了现有原型与任何之前已经存在的对象实例之间的联系,他们引用的仍然是最初的原型。
下面两个栗子来对比这个问题:
1)没有重写原型对象的栗子:
function Person(){
}
var lee = new Person();
Person.prototype.name = 'Lee';
alert(lee.name);//'Lee'
2)重写原型对象的栗子
function Person(){
}
var lee = new Person();
Person.prototype = {
constructor : Person,//设置回适当的值
name : "Lee",
age : 20
}
alert(lee.name);//undefined
原型中所有属性是被很多实例共享的,这种共享对于函数非常合适。对于那些包含基本值的属性也说得过去,毕竟(上面例子中所示),通过在实例上添加一个同名属性,可以隐藏原型中的对应属性。然而,对于包含引用类型值的属性就有问题了。
举个栗子:
function Person(){
}
Person.prototype = {
constructor : Person,//设置回适当的值
name : "Lee",
age : 20,
friends : ["Wang","Tang"]
}
var p1 = new Person();
var p2 = new Person();
p1.friends.push("Zhang");
alert(p1.friends);//"Wang,Tang,Zhang"
alert(p2.friends);//"Wang,Tang,Zhang"
alert(p1.friends === p2.friends);//true
在上面的例子中,Person.prototype对象有一个名为friends的属性,该属性包含一个字符串数组。这个属性就是包含引用类型值属性。因为该属性是保存的对这个数组的引用,相当于就是说所有的实例都是公用的同一个这个数组,只要一个人对这个数组进行了修改,其他实例就都会改变。
sysOS=`uname -s`
if [ $sysOS == "Darwin" ];then
echo "I'm MacOS"
elif [ $sysOS == "Linux" ];then
echo "I'm Linux"
else
echo "Other OS: $sysOS"
fi
在html中属性的值通过双引号(字符串数据)或者大括号(表达式)
//双引号
const a = <img src="1.jpg" />
//大括号
const x = "1.jpg"
const a = <img src={x} />
//有子元素的情况,都只能包含在同一个元素内,与vue语法保持一致
const a = (
<div>
<h1>标题</h1>
<span>gogogo</span>
</div>
)
注意:react对于jsx中的变量都会进行转义,以免xss攻击
通过reactDOM.render方法来渲染dom节点,并且react元素一旦创建就是不可变的,唯一的办法就是重新创建个新的元素然后重新通过ReacDOM.render去渲染,就是这么cool!😝😝😝。但是每次更新,不是统一将整个元素都更新,都只会修改更新了的元素
function tick(){
const ele = (
<div>
<h1>现在的时间是:{new Date().toLocaleTimeString()}</h1>
</div>
);
ReactDOM.render(
ele,
document.getElementById('root')
)
}
setInterval(tick,1000);
当用户看到用户自定义的组件的时候,就会将jsx的属性作为单个对象(props)传递给此组件
function Aiv(props){
return <h1>hi,{props.name}</h1>
}
const e = <Aiv name = "haha" />;
ReactDOM.render(
e,
document.getElementById('root')
)
注意:自定义的组件的首字母必须为大写,否则不能识别
react虽然是非常灵活的,但是有一条严格的规范:props是只读的,虽然也可以改变,但是不推荐。😝😝😝
比如:
//合格的形式
function Aiv (x, y){
return x+y;
}
//不合格的形式,传参x被修改了
function Aiv (x, y){
x = x + y;
}
注意事项:
class
类,并且继承自React.Component
class Aiv extends React.Component{
render(){
return <h1>hi,{this.props.name}</h1>
}
}
PS:相比于函数组件,类组件还多了本地状态和生命周期挂钩。👍👍👍
将render中的this.props替换为this.state
class Aiv extends React.Component{
render(){
return <h1>hi,{this.state.name}</h1>
}
}
在类组件中增加一个constructor方法来初始化this.state
class Aiv extends React.Component{
constructor(props){
super(props);
this.state = {name:'hy'};
}
render(){
return <h1>hi,{this.state.name}</h1>
}
}
此系列方法是为了拓展ReacDOM的渲染的不可变性。使用此方法可以动态的去改变dom,让我们拭目以待。
class Aiv extends React.Component{
constructor(props){
super(props);
this.state = {name:'hy'};
this.i = 1;
}
componentDidMount() {
this.timerID = setInterval(()=>this.setName(),1000);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
setName(){
this.i ++;
this.setState({name:"hy"+this.i})
}
render(){
return <h1>hi,{this.state.name}</h1>
}
}
ReactDOM.render(
<Aiv />,
document.getElementById('root')
); ;
componentDidMount
方法是在ReacDOM渲染dom节点之后执行componentWillUnmount
方法是在该dom节点被删除的时候执行//wrong
this.state.name = "hy1";
//correct
this.setState({
name: "hy1"
})
React为了性能,支持单次更新中可以批量执行多个setState
,所以你不能在setState中使用this.state这种方式来操作state属性。因为this.state可能是之前的属性,还没来得及改变。如下面的例子对比:
//假设count = 1
this.setState({
count:this.state.count+1
})
this.setState({
count:this.state.count+1
})
//最后count = 2
但是,setState
方法可以除了接收object
对象,还可以接收function
参数。传入function完美解决异步问题😎😎😎。使用方法:setState( function( prevState,[props] ){} )。
this.setState(function(x){
return {
count: x.count+1
}
})
this.setState(function(x){
return {
count: x.count+1
}
})
this.state = {
posts: [],
comments: []
};
this.setState({
posts: ['hy']
})
直接看例子:
<FormattedDate date={this.state.date} />
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}
等价于下面:
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
此样式表基于对当前UA实践的广泛研究,描述了所有HTML4元素的典型格式。鼓励开发人员在其实现中将其用作默认样式表。
以下样式表供大家去查看:
html, address,
blockquote,
body, dd, div,
dl, dt, fieldset, form,
frame, frameset,
h1, h2, h3, h4,
h5, h6, noframes,
ol, p, ul, center,
dir, hr, menu, pre { display: block; unicode-bidi: embed }
li { display: list-item }
head { display: none }
table { display: table }
tr { display: table-row }
thead { display: table-header-group }
tbody { display: table-row-group }
tfoot { display: table-footer-group }
col { display: table-column }
colgroup { display: table-column-group }
td, th { display: table-cell }
caption { display: table-caption }
th { font-weight: bolder; text-align: center }
caption { text-align: center }
body { margin: 8px }
h1 { font-size: 2em; margin: .67em 0 }
h2 { font-size: 1.5em; margin: .75em 0 }
h3 { font-size: 1.17em; margin: .83em 0 }
h4, p,
blockquote, ul,
fieldset, form,
ol, dl, dir,
menu { margin: 1.12em 0 }
h5 { font-size: .83em; margin: 1.5em 0 }
h6 { font-size: .75em; margin: 1.67em 0 }
h1, h2, h3, h4,
h5, h6, b,
strong { font-weight: bolder }
blockquote { margin-left: 40px; margin-right: 40px }
i, cite, em,
var, address { font-style: italic }
pre, tt, code,
kbd, samp { font-family: monospace }
pre { white-space: pre }
button, textarea,
input, select { display: inline-block }
big { font-size: 1.17em }
small, sub, sup { font-size: .83em }
sub { vertical-align: sub }
sup { vertical-align: super }
table { border-spacing: 2px; }
thead, tbody,
tfoot { vertical-align: middle }
td, th, tr { vertical-align: inherit }
s, strike, del { text-decoration: line-through }
hr { border: 1px inset }
ol, ul, dir,
menu, dd { margin-left: 40px }
ol { list-style-type: decimal }
ol ul, ul ol,
ul ul, ol ol { margin-top: 0; margin-bottom: 0 }
u, ins { text-decoration: underline }
br:before { content: "\A"; white-space: pre-line }
center { text-align: center }
:link, :visited { text-decoration: underline }
:focus { outline: thin dotted invert }
/* Begin bidirectionality settings (do not change) */
BDO[DIR="ltr"] { direction: ltr; unicode-bidi: bidi-override }
BDO[DIR="rtl"] { direction: rtl; unicode-bidi: bidi-override }
*[DIR="ltr"] { direction: ltr; unicode-bidi: embed }
*[DIR="rtl"] { direction: rtl; unicode-bidi: embed }
@media print {
h1 { page-break-before: always }
h1, h2, h3,
h4, h5, h6 { page-break-after: avoid }
ul, ol, dl { page-break-before: avoid }
}
reset 的目的,是将所有的浏览器的自带样式重置掉,这样更易于保持各浏览器渲染的一致性。
这里提供链接给小伙伴去了解各类人士的cssreset文,里面包含YUI等等——cssreset
国内的大牛们也很多自己去制定属于自己的CSS Rest。但是在玉伯写完第一版之后,在总结文章的结尾还是千叮咛,万嘱咐说:
请记住:永远不存在万能解决方案,永远没有银弹。 因此我的建议和 Eric 是一样的:请根据具体需求,适量裁剪和修改后再使用。
reset 的目的,是将所有的浏览器的自带样式重置掉,这样更易于保持各浏览器渲染的一致性。
而normalize 的理念则是尽量保留浏览器的默认样式,不进行太多的重置。——normalize
前面讲到CSS Reset
的核心作用就是清零,而且过于暴力;那么作为后者Normalize.css
,到底有什么优势可以完全取代前者呢?
1.Normalize.css 保护了有价值的默认值
Reset通过为几乎所有的元素施加默认样式,强行使得元素有相同的视觉效果。 相比之下,Normalize.css保持了许多默认的浏览器样式。 这就意味着你不用再为所有公共的排版元素重新设置样式。 当一个元素在不同的浏览器中有不同的默认值时,Normalize.css
会力求让这些样式保持一致并尽可能与现代标准相符合。
2.Normalize.css 修复了浏览器的bug
它修复了常见的桌面端和移动端浏览器的bug。这往往超出了Reset所能做到的范畴。 关于这一点,Normalize.css
修复的问题包含了HTML5元素的显示设置、预格式化文字的font-size
问题、在IE9中SVG
的溢出、许多出现在各浏览器和操作系统中的与表单相关的bug。
3.Normalize.css 修复了浏览器的bug
使用Reset最让人困扰的地方莫过于在浏览器调试工具中大段大段的继承链。在Normalize.css
中就不会有这样的问题,因为在我们的准则中对多选择器的使用时非常谨慎的,我们仅会有目的地对目标元素设置样式。
4.Normalize.css 是模块化的
这个项目已经被拆分为多个相关却又独立的部分,这使得你能够很容易也很清楚地知道哪些元素被设置了特定的值。因此这能让你自己选择性地移除掉某些永远不会用到部分(比如表单的一般化)。
5.Normalize.css 拥有详细的文档
Normalize.css
的代码基于详细而全面的跨浏览器研究与测试。这个文件中拥有详细的代码说明并在Github Wiki中有进一步的说明。这意味着你可以找到每一行代码具体完成了什么工作、为什么要写这句代码、浏览器之间的差异,并且你可以更容易地进行自己的测试。
这个项目的目标是帮助人们了解浏览器默认是如何渲染元素的,同时也让人们很容易地明白如何改进浏览器渲染。
还是要根据自身网站系统的情况来考虑之后,自己去修改出一份最适合自己的css-reset。
最后还是引用张鑫旭的一句话来总结下:
最少的CSS代码,最少的渲染,最少的重置就是最好的CSS样式代码,这反应了您的CSS层次。说句不好听的话,CSS reset是用来让那些CSS菜鸟,对CSS不太了解的人准备的。
参考地址
文档的每个元素被构造成文档布局内的一个矩形盒子,盒子每层的大小都可以使用一些特定的CSS属性调整。相关属性如下:
width和height
width和height设置内容盒(content box)盒子的宽度和高度。内容盒是盒子内容显示的区域 — 包括盒子内的文本内容,以及表示嵌套子元素的其它盒子。注意: 还有其他属性可以更巧妙地处理内容的大小 — 设置大小约束而不是绝对的大小。这些属性包括min-width
、max-width
、min-height
和 max-height
。
padding
padding表示一个 CSS 盒子的内边距 — 这一层位于内容盒的外边缘与边框的内边缘之间。该层的大小可以通过简写属性padding
一次设置所有四个边,或用 padding-top
、padding-right
、padding-bottom
和 padding-left
属性一次设置一个边。
border
CSS 盒的边框(border)是一个很明显的层,位于内边距的外边缘以及外边距的内边缘之间。边框的默认大小为 0 — 从而让它不可见 — 不过我们可以设置边框的厚度、风格和颜色让它出现。 border
简写属性可以让我们一次设置所有四个边,例如 border: 1px solid black
。This can be broken down into numerous different longhand properties for more specific styling needs:
border-top
,border-right
,border-bottom
,border-left
:设置厚度,款式和边境一侧的颜色。border-width
,border-style
,border-color
:设置仅厚,样式或颜色独立,但边界的所有四个侧面。您还可以设置单独的边框的单面的三个属性之一,使用border-top-width
,border-top-style
,border-top-color
,等。
margin
外边距(margin)代表 CSS 盒子周围的外部区域,在布局中推开其它 CSS 盒子。其表现与与 padding 很相似;简写属性为 margin
,单个属性分别为 margin-top
、margin-right
、margin-bottom
和 margin-left
。
**box-sizing **属性用于更改用于计算元素宽度和高度的默认的 CSS 盒子模型。可以使用此属性来模拟不正确支持CSS盒子模型规范的浏览器的行为。
content-box
默认值,标准盒子模型。width和height只包括内容的宽和高, 不包括边框(border),内边距(padding),外边距(margin)。
**注意: 内边距, 边框 & 外边距 都在这个盒子的外部。 **
比如. 如果 .box {width: 350px}; 而且 {border: 10px solid black;} 那么在浏览器中的渲染的实际宽度将是370px;
尺寸计算公式:width = 内容的宽度,height = 内容的高度。
border-box
width
和 height
属性包括内容,内边距和边框,但不包括外边距。
这是当文档处于 Quirks模式 时Internet Explorer使用的盒模型。padding和border将属于盒子的一部分
例如, .box {width: 350px; border: 10px solid black;}
导致在浏览器中呈现的宽度为350px的盒子。这个例子虽然没有设置高度,但是因为设置了border,所以高度自动就成了20px。
尺寸计算公式:width = border + padding + 内容的 width*,height = border + padding + 内容的 height。
str.replace(regexp|substr, newSubStr|function)
regexp
(pattern)
一个RegExp对象或者其字面量。该正则所匹配的内容会被第二个参数的返回值替换掉。
substr
(pattern)
一个要被 newSubStr
替换的
{{jsxref("String","字符串")}}。其被视为一整个字符串,而不是一个正则表达式。仅仅是第一个匹配会被替换。
newSubStr
(replacement)
用于替换掉第一个参数在原字符串中的匹配部分的 {{jsxref("String", "字符串")}}
function
(replacement)
一个用来创建新子字符串的函数,该函数的返回值将替换掉第一个参数匹配到的结果。
一个部分或全部匹配由替代模式所取代的新的字符串。
上面语法的含义是说:一共两个参数,但是两个参数可以分别传输不同的类型的值。
可能你看上面BB半天了也没看明白,没关系,我们直接来上代码:
基本用法
var x = "abcda";
//两个参数都是字符串
var y = x.replace('a','x');//'xbcda'
进阶用法
var x = "abcda";
//前面参数使用正则,后面使用字符串
var y = x.replace(/a/g,'x');//'xbcdx'
复杂用法
var x = 'abcda';
//前面参数使用正则,后面使用字符串
var y = x.replace(/a/g,function(match){
return match.toUpperCase();
});//'AbcdA'
这里只讲复杂用法中的应用场景实际使用,比如有个需求:你需要html文件中的标签之间的所有
标签中的字符a
替换为字符b
。当然如果是正则大神的话就可以跳过这里了,主要以下方式比较容易理解,更适合正则新手。
解决办法:
var allData = "<body>"+'\r\n'+
"<div>aaa</div>"+'\r\n'+
"<p>aaa</p>"+'\r\n'+
"</body>";
allData = allData.replace(/\<body\>([\s\S]*?)\<\/body\>/g, function(match) {
return match.replace(/\<p\>(.*?)\<\/p\>/g, function(match1) {
return match1.replace(/a/g, "b")
})
});
PS:在进行全局的搜索替换时,正则表达式需包含 g
标志。
如果对以上有什么疑问,可以在评论区发表,方便大家进行探讨
看到毕业一年的技术已经如此了得!发现自己还是进步的太慢了!共勉吧
地址:jawil/blog#22
在本地仓库陆续执行以下命令:
git init
git add .
git commit -m ‘提交说明’
git remote add origin 远程仓库地址
git pull origin master --allow-unrelated-histories
git branch --set-upstream-to=origin/master master
git push
顺序执行完即可正常操作
在本文中我们将展示一种新的使用仿CSS选择器的语法来快速开发HTML的方法。
我使用的是atom编辑器——由 Github 打造的编程开发利器,他自带这个解析功能。当然其他的如sublime,webstorm等都会自带这个功能,或者使用相关的插件即可。
你在写HTML代码(包括所有标签、属性、引用、大括号等)上花费多少时间?如果你的编辑器有代码提示功能,你编写的时候就会容易些,但即便如此你还是要手动敲入很多代码。
比如,你这么写,按下tab
键:
div#content>h1+p
然后就看到了这样的输出:
<div id="content">
<h1></h1>
<p></p>
</div>
这里是一个支持的属性和操作符的列表:
E
元素名称(div
, p
等);
E#id
使用id的元素(div#content
, p#intro
, span#error
);
E.class
使用类的元素(div.header
, p.error.critial
). 你也可以联合使用class和idID: div#content.column.width
;
E>N
子代元素(div>p
, div#footer>p>span
);
E+N
兄弟元素(h1+p
, div#header+div#content+div#footer
);
E*N
元素倍增(ul#nav>li*5>a
);
E$*N
条目编号 (ul#nav>li.item-$*5
);
这里就针对于倍增和条目编号来举例子吧。
比如你写个li*4>a
,就会生成以下HTML代码:
<li><a href=""></a></li>
<li><a href=""></a></li>
<li><a href=""></a></li>
<li><a href=""></a></li>
假设你想生成class为item1
、item2
和item3
的3个<div>
元素。你可以写成这样的缩写,div.item$*3
:
<div class="item1"></div>
<div class="item2"></div>
<div class="item3"></div>
简单吧,赶紧打开你的编辑器操练起来吧!
简单说来就是让机器可以读懂内容。
语义化的优点:
搜索引擎的爬虫依赖于标记来确定上下文和各个关键字的权重,利于 SEO
去掉或样式丢失的时候能让页面呈现清晰的结构。
使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解。
PS:一般的爬虫框架流程为----从互联网海量页面中先抓取一些高质量页面,抽取其中所包含的url,将这些URL放入待抓取队列中,爬虫依次读取该队列中的url,通过DNS解析,将这些url转化成对应网站的IP地址,网页下载器则通过IP地址下载页面所有内容。
header 元素代表“网页”或“section”的页眉。
通常包含h1-h6
元素或hgroup
,作为整个页面或者一个内容块的标题。也可以包裹一节的目录部分,一个搜索框,一个nav
,或者任何相关logo。
整个页面没有限制header元素的个数,可以拥有多个,可以为每个内容块增加一个header元素
<header>
<hgroup>
<h1>xxx</h1>
</hgroup>
<nav></nav>
</header>
footer
元素代表“网页”或“section”的页脚,通常含有该节的一些基本信息,譬如:作者,相关文档链接,版权资料。如果footer
元素包含了整个节,那么它们就代表附录,索引,提拔,许可协议,标签,类别等一些其他类似信息。
<footer>
Copyright © 2017 ...
</footer>
nav
元素代表页面的导航链接区域,用在整个页面主要导航部分上,不合适就不要用nav元素
<nav>
<ul>
<li>首页</li>
<li>xxx</li>
...
</ul>
</nav>
section
元素代表文档中的“节”或“段”,“段”可以是指一篇文章里按照主题的分段;“节”可以是指一个页面里的分组。
<section>
<h1>section是啥?</h1>
<article>
<h2>关于section</h1>
<p>section的介绍</p>
<section>
<h3>关于其他</h3>
<p>关于其他section的介绍</p>
</section>
</article>
</section>
article
代表一个在文档,页面或者网站中自成一体的内容,其目的是为了让开发者独立开发或重用。譬如论坛的帖子,博客上的文章,一篇用户的评论,一个互动的widget小工具。并不是说只有是文章内容才能使用,而是说只要是自成一体的内容就可以用这个标签(特殊的section)
<article>
<h1>一篇文章</h1>
<p>文章内容..</p>
<footer>
<p><small>版权:html5jscss网所属,作者:小北</small></p>
</footer>
</article>
aside
元素被包含在article元素中作为主要内容的附属信息部分,其中的内容可以是与当前文章有关的相关资料、标签、名次解释等。(特殊的section)
<article>
<p>内容</p>
<aside>
<h1>作者简介</h1>
<p>小北,前端一枚</p>
</aside>
</article>
参考地址:
先来个流程图,让大家心里有个底:
从上图,我们能看到这几点:
有下面的HTML示例如下:
<html>
<body>
<div class =“err” id =“div1”>
<p>
这是一个
<span class =“big”>大错误</span>
这也是一个
<span class =“big”>非常大的错误</span>错误
</p>
</div>
<div class =“err” id =“div2”>另一个错误</div>
</body>
</html>
然后我们的CSS文档是这样的:
/*1.*/ div {margin:5px; color:black}
/*2.*/ .err {color:red}
/*3.*/ .big {margin-top:3px}
/*4.*/ div span {margin-bottom:4px}
/*5.*/ #div1 {color:蓝色}
/*6.*/ #div2 {color:green}
于是我们的CSS Rule Tree是这样:
注意:CSS匹配HTML元素是一个相当复杂和有性能问题的事情。所以,你就会在N多地方看到很多人都告诉你,DOM树要小,CSS尽量用id和class,千万不要过渡层叠下去,……
通过这两个树,我们可以得到一个叫Style Context Tree,也就是下面这样(把CSS Rule结点Attach到DOM Tree上):
这个时候就有必要说下这个CSS选择器特性了
CSS选择器由CSS2规范定义如下:
连接四个数字abcd(在具有大基数的数字系统中)具有特异性。
这个四个数字保持的优先级是:a>b>c>d
您需要使用的数字基数由您在其中一个类别中的最高数量定义。
例如,如果a = 14,您可以使用十六进制基数。在不太可能的情况下,您将需要一个17位数的基数。后来的情况可能会发生在这样的选择器:html body div div p ...(你的选择器中的17个标签不太可能)。
一些例子:
* {} / * a = 0 b = 0 c = 0 d = 0 - > specificity = 0,0,0,0 * /
li {} / * a = 0 b = 0 c = 0 d = 1 - >specificity= 0,0,0,1 * /
li:first-line {} / * a = 0 b = 0 c = 0 d = 2 - > specificity = 0,0,0,2 * /
ul li {} / * a = 0 b = 0 c = 0 d = 2 - > specificity = 0,0,0,2 * /
ul ol + li {} / * a = 0 b = 0 c = 0 d = 3 - > specificity = 0 ,0,0,3 * /
h1 + * [rel = up] {} / * a = 0 b = 0 c = 1 d = 1 - > specificity = 0,0,1,1 * /
ul ol li.red {} / * a = 0 b = 0 c = 1 d = 3 - > specificity = 0,0,1,3 * /
li.red.level {} / * a = 0 b = 0 c = 2 d = 1 - > specificity = 0,0,2,1 * /
#test {} / * a = 0 b = 1 c = 0 d = 0 - > specificity = 0,1,0,0 * /
style =“”/ * a = 1 b = 0 c = 0 d = 0 - > specificity = 1,0,0,0 * /
我在JavaScript中如何拷贝一个对象?这是一个简单的问题,但是答案确不是很简单。
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>Did you ever wanted to create a deep copy of an object in JavaScript? There is a way, but you are not gonna like it...
— Surma (@DasSurma) 2018年1月22日
I feel like we need something better 🤔 pic.twitter.com/IDazhB8BKJ
JavaScript通过引用来传递所有的值。如果你不知道这是什么意思,下面有个例子👇:
function mutate(obj) {
obj.a = true;
}
const obj = {a: false};
mutate(obj)
console.log(obj.a); // prints true
mutate
方法改变了作为参数传递进来的这个对象。在值调用
环境中,这个函数式传递的这个值,所以相关于这个函数是执行了一个拷贝。这个函数使这个对象对外是不可见的。但是在像js的这种引用调用
的环境,将会得到这个真实的对象。所以最后控制台输出的为true
。
不过,你想要保持你的原始的对象,其他函数只是创建了这个对象的拷贝。
在下面就介绍几种深度拷贝的方式
第一种最古老的方式就是通过将对象转换为JSON字符串格式,然后将其转换为对象。
let obj = { name : "huyue" };
let copy = JSON.parse(JSON.stringify(obj));
obj.name = 'hy';
console.log(copy);//'huyue'
但是这种方式有些问题
问题一:当对象中出现循环引用的时候会报错。尽管你可能认为你不会如此使用,但是那些还是会很容易发生。比如当你构建了树状类型的数据机构的时候,其中一个节点引用了父级的某个节点,这样就出现了这种场景。
const x = {};
const y = {x};
x.y = y; // Cycle: x.y.x.y.x.y.x.y.x...
const copy = JSON.parse(JSON.stringify(x)); // throws!
问题二:这种方式只支持基础类型,像Map,Set,RegExp,Date,ArrayBuffer,函数对象等都会在序列化的时候弄丢
var source = { name:function(){console.log(1);}, child:{ name:"child" } }
var target = JSON.parse(JSON.stringify(source));
console.log(target.name); //undefined
注:JSON对象是ES5中引入的新的类型(支持的浏览器为IE8+),浏览器支持情况
结构化克隆是一个现有算法,它是被用来把一个领域的值传递到另一个。比如,你调用postMessage去发送一个消息给另一个窗口或WebWorker。结构化很好的地方就是他能处理循环对象,并且支持多种内置类型。
我们通过MessageChannel创建一个新的消息通道,并通过它的两个MessagePort属性来发送数据和获取数据。我们接受到的这条信息就是会包含原始数据的结构化克隆对象。但是这种方式是异步情况,所以下面例子使用的async awit实现了的,也可参见在线地址
function structuralClone(obj) {
return new Promise(resolve => {
const {port1, port2} = new MessageChannel();
port2.onmessage = ev => resolve(ev.data);
port1.postMessage(obj);
});
}
const obj = /* ... */;
const clone = await structuralClone(obj);
注:浏览器支持IE10+,浏览器支持力度情况
如果你曾经使用过history.pushState()
去构建一个SPA(单页应用),你应该会知道能提供一个状态对象去保存这URL。这个状态对象就是结构化克隆,并且还是同步的。我们一定要小心,避免在使用这个状态对象的时候去混淆任何程序逻辑,所以我们需要在我们克隆了之后去恢复这个原始的状态对象。为了防止发生任何事件,请使用history.replaceState()而不是history.pushState()。replaceState和pushState区别详情
function structuralClone(obj) {
const oldState = history.state;
history.replaceState(obj, document.title);
const copy = history.state;
history.replaceState(oldState, document.title);//就是为了恢复原始状态对象,避免干扰
return copy;
}
const obj = /* ... */;
const clone = structuralClone(obj);
为了复制一个对象,使用浏览器的引擎感觉有些笨拙。不过你还是可以这么做,有些事情还是得注意,因为Safari浏览器会限制30秒内调用relaceState的次数上限为100次
注:浏览器支持IE10+,浏览器支持力度情况
这种方式由Jeremy Banks建议,通知接口用于向用户配置和显示桌面通知,这个消息通知的api有一个与它们相关的数据对象被克隆。看到这,可能有的人表示有点不是很明白,那么可以点击在线示例
function structuralClone(obj) {
return new Notification('', {data: obj, silent: true}).data;
}
const obj = /* ... */;
const clone = structuralClone(obj);
它基本触犯了浏览器内的权限机制,所以怀疑这个可能会非常慢。出于某种原因,Safari浏览器总是返回undefined
。可以使用在线示例
注:浏览器不支持IE,浏览器支持力度情况
对上面几种方式进行性能测试看哪种方式性能最高。刚开始尝试时,我拿一个小JSON对象,并通过这些克隆对象一千次的不同方式来进行测试。幸运的是, Mathias Bynens告诉我在给一个对象增加属性的时候V8是有缓存。为了确保不走缓存,所以我写了一个[函数](a function that generates objects of given depth and width using random key names),使用随机键名称生成给定深度和宽度的对象,并重新运行测试示例
MessageChannel
是最好的选择。(ie10+)特别声明:如果你的工作内容是基于es2015(也就是es6),并且不需要代码分割,也不需要[模块热替换(HMR)][https://webpack.js.org/concepts/hot-module-replacement/],那么你可以很愉快的使用rollup进行开发了。否则,你可以绕行webpack了。
rollup已经原生支持多入口多出口,可以查看英文文档
轩枫阁这个博客网址做的不错
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.