React+Redux 单界面应用简单实现

之前的项目需要做一个后端管理的web,既然要做,就挑战一下自己吧,打算使用react+redux 做一个单界面的应用。为什么要用这个组合去做呢,React我就不解释了,这是最近非常火的一个前端框架,但是他还是有一些缺点的,比如他在各组件之间的数据交互相对来说比较麻烦,而Redux的作用就是用来管理数据流,管理状态,所以选择用React和Redux结合起来做。

这里还要使用一些其他的框架,使用 react-redux 去连接React和Redux;使用 react-routerreact-router-redux 去做前端路由;使用 redux-thunk 去支持异步action等。

下面了解一下Redux的数据流(按顺序进行):

首先触发action:action 可以理解为应用向 store 传递的数据信息(一般为用户交互信息)。在实际应用中,传递的信息可以约定一个固定的数据格式,简单形式如下

1
2
3
function getCourse(data) {
return {type: 'Get_Courses', data};
}

dispatch(action):这是一个同步的过程:执行 reducer 更新 state -> 调用 store 的监听处理函数。如果需要在 dispatch 时执行一些异步操作可以通过引入 Middleware(redux-thunk) 解决。

最后reducer 改变状态:reducer的作用就是根据现在的state和action来得到新的state,也就是说这有在这一步state树才会被改变。

下面结合我的代码去说明:(这里以获取课程信息为例)

首先是前端目录结构:

1
2
3
4
5
6
7
8
9
10
11
admin/  
├── actions/ 用于存放action文件
│ └── course.js
├── components/ 能够复用的React组件
├── containers/ 界面
| └── course.js
├── reducers/ 存放reducers处理文件
| └── course.js
├── store/ 将reducer整合在一起
| └── rootStore.js
└── index.js 入口文件

action/course.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function getCourse(data) {
return {type: 'Get_Courses', data};
}
export function getCoursesAction(data) {
return dispatch=> {
fetch(`/admin/course/list?search=${data.search}&page=${data. page}`, {credentials: 'include'})
.then((response) => response.text())
.then((responseText) => {
var data = JSON.parse(responseText);
data.data = JSON.parse(data.data)
dispatch(getCourse(data))
})
.catch((error) => {
console.warn(error);
});

}
}

因为action函数是不支持异步的,所以这里将异步操作放到getCoursesAction函数里面,当异步获取成功数据之后通过dispacth调用getCourse函数将数据提交给reducer处理。


reducers/course.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var defaultFields = {
data: [],
total:0,
page:1,
count:10,
search: '',
};

export function courses(state = defaultFields, action) {
switch (action.type) {
case 'Get_Courses':
return action.data
default:
return state;
}
}

其实这里的参数action就是我们在action/course.js中返回的数据

1
return {type: 'Get_Courses', data};

所以我们通过action.type获取我们约定的操作,这里我们如果type是“Get_Courses”就会根据action里面的data更新state。


store/rootStore.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { createStore, applyMiddleware,compose ,combineReducers} from 'redux';
import {routerReducer} from 'react-router-redux';
import thunk from 'redux-thunk';

import * as course from '../reducers/course.js';

//将拆分的reducer组合起来
const reducer = combineReducers({
...course,
routing: routerReducer
});

//添加中间件
const createStoreWithMiddleware = compose(
applyMiddleware(
thunk
),
window.devToolsExtension ? window.devToolsExtension() : f => f
)(createStore)

//生成store
export default createStoreWithMiddleware(reducer)

containers/course.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mport React, { Component } from 'react';
import { connect } from 'react-redux';
class Course extends React.Component {
...
render() {
//获取到courses数据
var data = this.props.data;
return(...);
}
}

//将store中的course信息绑定到Course组件的props上
function show(state) {
return {
data: state.courses,
fields:state.fields
};
}

export default connect(show)(Course);


index.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

import { createStore, applyMiddleware,compose ,combineReducers} from 'redux';
import { Provider } from 'react-redux';
import Share from './components/share.js';

import User from './containers/user.js';
import Field from './containers/field.js';
import Course from './containers/course.js';
import Section from './containers/section.js';
import Lesson from './containers/lesson.js';

import store from './store/rootStore.js';

import {routerReducer, syncHistoryWithStore } from 'react-router-redux';
import { Route, Router ,browserHistory, IndexRoute ,hashHistory } from 'react-router';

import {createHistory } from 'history'

let history = syncHistoryWithStore(hashHistory, store)
let rootElement = document.getElementById('contain');
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<Route path="/" component={Share}>
<Route path="user" component={User}/>
<Route path="field" component={Field}/>
<Route path="course" component={Course}/>
<Route path="section/:id" component={Section}/>
<Route path="lesson/:id" component={Lesson}/>
</Route>
</Router>
</Provider>,
rootElement
);

index.js的作用主要是把所有的东西连在一起,这里使用了react-router中的 syncHistoryWithStore 函数去做前端路由,这样我的单页面应用就建起来了.

  

React Native 界面跳转

先放上 app.js 中的主要代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import List from './ListView';

const defaultRoute = {
component: List,
name:'List'
};

class RnDemo extends Component {
_renderScene(route, navigator) {
let Component = route.component;
return (
<Component {...route.params} navigator={navigator} />
);
}
render() {
return (
<Navigator
initialRoute={defaultRoute}
renderScene={this._renderScene}/>
);
}
}

  • 第一行是引用了一个文件,listview中就是我们打开app想要展示的第一个界面的内容,那么现在的 List 就是这个界面的组件的名字,详细后面再说,我们继续看

  • 第三行我们定义了一个对象,这个对象中有两个属性,name和component,component我们用 List 去赋值,这是个什么东西呢? 其实,这里的component也就是 List 中的class就是我们将要在下面的component中实例化的组件

  • 我们直接看17行,这是一个Navigator标签,他就是我们的导航器标签,我们看他的属性,initialRoute 是什么呢,其实这里才是设置app打开要显示的默认界面是什么,所以我们就可以把上面的 defaultRoute 放进去了

  • 19行这个就是很重要的了,reanderScene 是什么呢,我们发现给他赋值的是一个叫renderScene的方法,那我们回去看第9行。我们发现这里有两个参数route、 navigator,这又是什么鬼呢,其实route里就是我们上面传递的name和component这两个货,也就是我们将要在navigator里面push的东西,navigator是一个Navigator的对象,那我们在看函数里面,第10行,我们取出router里面的component,根据上面我们知道,这个component其实就是我们引用的ListView里面的那个class,所以下面我们可以用component生成一个标签,其实就是实例化了一个ListView里面的类,我们再来看属性,…route.params 其实就是给下一个界面传参,这个我们下面介绍。然后navigator作为props传递给了这个component,这样下一个界面就可以使用这个navigator来进行界面返回等其他操作了

下面我们看一下ListView里面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

import Detail from './Detail';

class List extends Component {
constructor(props) {
super(props);
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
var list = [];
for (var i = 0; i < 20; i++) {
list[i]='listItem'+i;
}
this.state = {
dataSource: ds.cloneWithRows(list),
};
}
itemClick(e, dataData){
this.props.navigator.push({
name: 'Detail',
component: Detail,
params: {
data: dataData
}
})
}
render() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={(rowData) =>
<TouchableHighlight onPress={(event) => this.itemClick(event, rowData)} underlayColor='lightgray'>
<View style={style.tableItem} >
<View ><Image style={style.img} source={require('../imgs/img.jpg')}></Image></View>
<View ><Text style={style.title}>{rowData}</Text></View>
</View>
</TouchableHighlight >

}
/>
);
}
}

export default List;

我们就看一下主要的吧,在这里我定义了一个List的组件,并通过export暴露出去,这样在 app.js 我们就可以使用他来实例化标签了

另一个重要的地方是16行,由于这个界面是一个list列表,所以 我就定义了一个 itemClick 方法,去响应每一个item的点击
由于我们之前吧把navigator对象作为组件的属性传递下来了,所以我们这里当然就可以使用 this.props.navigator 来获取到他,并进行界面跳转操作,在这里我们 push 一个新界面 component 就是我们我们要打开的界面的组件类,然后我们其实想要让下面的界面知道我是点击那个item进入的呀,所以我想把item的内容当做参数传递下去,!!!重点来了,可以发现我这里写了一个叫 params 的对象在这个对象里面我写入了我要传递的数据,那为什么写到这里就会传递下去呢? 如果你还没忘的话,其实我们在上面已经写了

1
2
3
4
5
6
_renderScene(route, navigator) {
let Component = route.component;
return (
<Component {...route.params} navigator={navigator} />
);
}

就是这里,app.js 中我们已经设置了把路由中的params拿出来作为新组件的属性,这样我们就可以在新组件里面通过 this.props 获取了,下面我们去detail中看一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Detail extends React.Component {
constructor(props){
super(props);
this.state = {
data : this.props.data,
};
}
BackClick(){
const { navigator } = this.props;
if(navigator) {
navigator.pop();
}
}
render() {
return (
<View >
<TouchableHighlight onPress={(event) => this.BackClick(event)} underlayColor='lightgray'>
<Text>{this.state.data}</Text>
</TouchableHighlight>
</View>

);
}
}
export default Detail;

刚刚已经介绍了,在这里我们可以通过this.props去获取上一个界面传递下来的数据

我们再来看一下界面返回,第九行,其实就是获取到navigator后,如果navigator存在就pop();这样就可以成功返回上一个界面了

其实,做界面跳转还可以使用 React-Router 等,这个我们下回再介绍吧^-^

Simple-Calendar开发文档

获取Simple-Calendar

从GitHub上下载 链接

引入资源

使用日历插件前首先要引用资源:JS CSS

1
2
3
<link rel="stylesheet" type="text/css" href="css/simple-calendar.css">

<script type="text/jacascrip" src="js/simple-calendar.js"></script>

初始化一个日历

首先为calendar准备一个容器,可以设置大小,也可以在options中设置,不设置的话自动设为默认

1
<div id='container'></div>

1
2
3
<script>
var myCalendar = new SimpleCalendar('#container');
</script>

这样一个日历就诞生了 ^-^

配置项说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var options = {
width: '500px',
height: '500px',
language: 'CH', //语言
showLunarCalendar: true, //阴历
showHoliday: true, //休假
showFestival: true, //节日
showLunarFestival: true, //农历节日
showSolarTerm: true, //节气
showMark: true, //标记
timeRange: {
startYear: 1900,
endYear: 2049
},
mark: {
'2016-5-5': '上学'
},
theme: {
changeAble: false,
weeks: {
backgroundColor: '#FBEC9C',
fontColor: '#4A4A4A',
fontSize: '20px',
},
days: {
backgroundColor: '#ffffff',
fontColor: '#565555',
fontSize: '24px'
},
todaycolor: 'orange',
activeSelectColor: 'orange',
}
}

国际化

language:
语言的话目前只支持中文和英文,分别对应’CH’,’EN’

如果想要加更多的语言或者更改现在的显示,可以直接更改languageData内容

节日显示配置

1
2
3
4
5
6
showLunarCalendar: true, //是否显示阴历日期
showHoliday: true, //是否显示休假休假
showFestival: true, //是否显示国际节日
showLunarFestival: true, //是否显示农历节日
showSolarTerm: true, //是否显示节气
showMark: true, //是否显示标记日期

时间范围

这个时间范围设置的是下拉框中的年数范围

1
2
3
4
timeRange: {
startYear: 1900,
endYear: 2049
}

标记日期

标记日期配置只有在 showMark 为 true 时才会生效

1
2
3
mark: {
'2016-5-5': '上学'
}

这样就会在日历的对应日期上面添加标记,当鼠标停留时显示输入的信息

主题配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
theme: {
changeAble: false,
weeks: {
backgroundColor: '#FBEC9C',
fontColor: '#4A4A4A',
fontSize: '20px',
},
days: {
backgroundColor: '#ffffff',
fontColor: '#565555',
fontSize: '24px'
},
todaycolor: 'orange',
activeSelectColor: 'orange',
}

主题配置只有在changeAble 为 true 时才会生效
weeks 设置的是星期一栏的主题,分别对应背景颜色,字体颜色,字体大小
days 设置的是日期的主题,参数同上
todaycolor 设置的是当天的日期背景颜色
activeSelectColor 设置的是鼠标滑过事件对应日期的边框颜色

事件接口说明

1
2
3
4
5
6
7
8
9
myCalendar.updateSize('500px', '500px');
myCalendar.addMark('2016-3-7', 'test');
myCalendar.setLanguage('EN')
myCalendar.showFestival(false);
myCalendar.showLunarCalendar(false);
myCalendar.showHoliday(false);
myCalendar.showSolarTerm(false);
myCalendar.showLunarFestival(false);
myCalendar.showMark(false);

updateSize(width,height)

调整日历大小,会自动根据大小调整对应的样式

addMark(day, info)

day 是一个可以确定一个日期的字符串
info 是要显示的信息

setLenguage(languageStr)

设置语言,目前支持的语言有’CH’,’EN’
如果想要增加语言,请在languageData中修改

其他

1
2
3
4
5
6
7
8
9
10
11
12
//关闭或者显示国际节日
showFestival(false);
//关闭或者显示阴历日期
showLunarCalendar(false);
//关闭或者显示假期
showHoliday(false);
//关闭或者显示二十四节气
showSolarTerm(false);
//关闭或者显示阴历节日
showLunarFestival(false);
//关闭或者显示标记
showMark(false)

js 原型方法 类方法 对象方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var people=function (name) {
this.name=name;
//对象方法
this.say=function () {
console.log(this.name);
}
}
//类方法
people.ssay=function () {
console.log('this:'+this);
}
var p1=new people('p1');
var p2=new people('p2');
//原型方法
people.prototype.psay=function () {
console.log('this:'+this.name);
}

p1.say();
p2.say();
p1.psay();
p2.psay();
people.ssay();

结果:

1
2
3
4
5
6
7
8
9
10
11
p1
p2
this:p1
this:p2
this:function (name) {
this.name=name;
//对象方法
this.say=function () {
console.log(this.name);
}
}

1. 对象方法

对象方法就是每个对象都会包括的一个方法,每个类的实例都可以调用

2. 类方法

每个类都相当于一个Object,类方法指的是这个Object里面的方法,只能通过类的名字调用,this指向这个类,不能通过实例调用

3. 原型方法

原型方法主要是用来对对已有的对象进行扩展的,看上面的代码,在对象已经实例化的时候添加原型方法,这时候每个实例都可以调用

AnjularJS学习笔记

以前也接触过Angularjs,也用过一些,如今又回来重新看看,想理解的更深刻一些,写的可能不是很详细,但却是都是我学习的历程。

1. 安装

安装方式可以自己选择,可以去网上下载,下载地址:
http://angularjs.org;

也可以直接引用:

1
<script src="http://code.angularjs.org/angular-1.0.1.min.js"></script>

我是用的bower:

bower install angularjs -save

1
<script src='bower_components/angular/angular.js'></script>

2. HelloWold

1
2
3
<body ng-app>
hello {{'world!'}}
</body>

这样浏览器中就会打印出hello world!啦!

3. ng-app

ng-app指令标记了AngularJS脚本的作用域,在<html>中添加ng-app属性即说明整个<html>都是AngularJS脚本作用域。也可以在局部使用ng-app指令,像上面的例子<body ng-app>,则AngularJS脚本仅在该<body>中运行。
注:全局仅可以有一个ng-app

4. ng-controller

什么是controller,这就涉及到模型-视图-控制器(MVC)模式
ng-controller命令就是绑定controller的命令,而他的作用域也只是在绑定的标签之下
看下面一段代码:

1
2
3
4
5
6
7
8
9
<body ng-app='myApp'>
hello {{'world!'}}
<div ng-controller='myCtrl'>
<div> {{myinfo.name}} </div>
</div>
<div ng-controller='youCtrl'>
<div> {{youinfo.name}} </div>
</div>
</body>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var app = angular.module('myApp', []);
app.controller('myCtrl', ['$scope',
function($scope) {
var people = new Object();
people.name='lxc';
people.age=22;
$scope.myinfo=people;
}
]);
app.controller('youCtrl', ['$scope',
function($scope) {
var people = new Object();
people.name = 'you';
people.age = 2;
$scope.youinfo = people;
}
]);

我在js中首先定义了一个myApp,在myApp下又定义了两个controller,myCtrlyouCtrl,在控制器中分别初始化两个people;
在html中我分别将两个控制器绑定div,在其中打印出name
这时看看正确结果:

hello world!
lxc
you

下面我们来试验一下一个controller中的值可不可以在绑定的表情外部访问或者在其他controller中访问:
js不动,更改html代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
<body ng-app='myApp'>
hello {{'world!'}}
<div ng-controller='myCtrl'>
<div> {{myinfo.name}} </div>
<div>test: {{youinfo.name}} </div>
</div>
<div ng-controller='youCtrl'>
<div> {{youinfo.name}} </div>
<div>test: {{myinfo.name}} </div>
</div>
<div>test: {{youinfo.name}} </div>
<div>test: {{youinfo.name}} </div>
</body>

结果如下:

hello world!
lxc
test:
you
test:
test:
test:

我们发现一个controller中的元素只能在自己的作用域中访问,那我们要想访问另一个controller中的元素怎么办呢?
答案是:

  • 利用作用域继承的原理,子控制器访问父级控制器中的内容。
  • 使用angularJS中的事件,也就是使用$on,$emit,$broadcast进行消息传递
  • 使用angularJS中的服务

5. 一些指令

(1)ng-repeat

这是一个循环指令
下面看个例子:

1
2
3
4
5
var list=['lxc1','lxc2','lxc3'];
$scope.list=list;
$scope.changelist=function(){
list.push('lxc4');
}

1
2
3
4
5

<li ng-repeat='i in list'>
{{i}}
</li>
<button ng-click='changelist()'>changelist()</button>

界面显示如下

lxc1
lxc2
lxc3

这时我们点击changelist()看看发生了什么:

lxc1
lxc2
lxc3
lxc4

我们发现数组的更改直接反应在了html上,这就是angular的优点,以前的话还要用jQuery再在html中加一个标签,现在这些事情angular都为我们做了,是不是很方便^-^

(2) ng-click

这个已经在上面的例子总用到了,可以为标签的点击事件绑定对应作用域中的一个方法,也是很方便

(3) ng-model

ng-model 指令可以将输入域的值与 AngularJS 创建的变量绑定。

1
2
3
4
<input ng-model='name' />   
<input value="{{name}}" />
<input ng-model='name' />
<div ng-bind="name"> </div>

运行之后我们发现在第一个input中输入值可以下面三个标签上显示,改变第三个其他三个也会跟着改变,但是改变第二个就只会他自己改变,这也很好解释,因为第二个input只是用来显示name的值,并没有进行双向绑定

下面我们看看在html中通过ng-model定义的name在js中可不可以访问

1
2
3
4
5
<input ng-model='name' />
<input value="{{name}}" />
<input ng-model='name' />
<div ng-bind="name"> </div>
<button ng-click='showevent()'>alert()</button>

1
2
3
$scope.showevent=function(){
alert($scope.name);
}

在input中输入一个值,点击按钮,发现浏览器正确的输出了name的值

(4) ng-options

1
2
3
<select ng-model='select' ng-change='selectchange()' ng-options='i.url as i.site for i in sites'>
<option value="">请选择</option>
</select>
1
2
3
4
5
6
7
8
$scope.sites = [
{site : "Google", url : "http://www.google.com"},
{site : "Baidu", url : "http://www.baidu.com"},
{site : "Taobao", url : "http://www.taobao.com"}
];
$scope.selectchange=function(){
alert($scope.select);
}

刷新界面发现select已经正确的显示出来了,我们去改变他,发现浏览器也能正确的输出改变后的值

在上面使用了ng-change,其实和正常的onchange事件类似,都是在值发生改变时触发

(5)ng-disabled

1
<button ng-disabled='1' ng-click='changeselect()' >changeselect</button>

这个很简单,当他的值为不等于0和false时,angular就会给button加上disabled="disabled"属性

当然其他指令还有很多,下次在介绍吧^-^

初次上传工程到github的方法(2)

此方法适用于远程仓库为空的前提下

1. 建立仓库

这个就不做介绍了,在github新建就行
新建后确保仓库为空

2. 进入项目文件夹

git init
git add .
git commit -m ‘first’
git remote add origin ———仓库地址
git push -u origin master

初次上传工程到github的方法(1)

此方法适用于远程仓库已经有文件的前提下

1. 建立仓库

这个就不做介绍了,在github新建就行

2. 克隆

git clone address.git

3. 进入项目文件夹

4. 将自己的要上传的文件复制进来

5. 同步

git add .
git commit -m ‘first’
git push

注意!

如果在add的时候报错类似:

LF will be replaced by CRLF | fatal: CRLF would be replaced by LF

是因为Git的换行符检查功能;

假如你正在Windows上写程序,又或者你正在和其他人合作,他们在Windows上编程,而你却在其他系统上,在这些情况下,你可能会遇到行尾结束符问题。这是因为Windows使用回车和换行两个字符来结束一行,而Mac和Linux只使用换行一个字符。虽然这是小问题,但它会极大地扰乱跨平台协作。

Git可以在你提交时自动地把行结束符CRLF转换成LF,而在签出代码时把LF转换成CRLF。用core.autocrlf来打开此项功能,如果是在Windows系统上,把它设置成true,这样当签出代码时,LF会被转换成CRLF:

$ git config –global core.autocrlf true

Linux或Mac系统使用LF作为行结束符,因此你不想 Git 在签出文件时进行自动的转换;当一个以CRLF为行结束符的文件不小心被引入时你肯定想进行修正,把core.autocrlf设置成input来告诉 Git 在提交时把CRLF转换成LF,签出时不转换:

$ git config –global core.autocrlf input

这样会在Windows系统上的签出文件中保留CRLF,会在Mac和Linux系统上,包括仓库中保留LF。

如果你是Windows程序员,且正在开发仅运行在Windows上的项目,可以设置false取消此功能,把回车符记录在库中:

$ git config –global core.autocrlf false

常见浏览器兼容性问题与解决方案

浏览器兼容问题一:不同浏览器的标签默认的外补丁和内补丁不同

问题症状:随便写几个标签,不加样式控制的情况下,各自的margin 和padding差异较大。
碰到频率:100%
解决方案:

1
*{margin:0;padding:0;}


备注:这个是最常见的也是最易解决的一个浏览器兼容性问题,几乎所有的CSS文件开头都会用通配符*来设置各个标签的内外补丁是0。

浏览器兼容问题二:块属性标签float后,又有横行的margin情况下,在IE6显示margin比设置的大

问题症状:随便写几个标签,不加样式控制的情况下,各自的margin 和padding差异较大。
问题症状:常见症状是IE6中后面的一块被顶到下一行 碰到频率:90%(稍微复杂点的页面都会碰到,float布局最常见的浏览器兼容问题)
解决方案:在float的标签样式控制中加入display:inline;将其转化为行内属性
备注:我们最常用的就是div+CSS布局了,而div就是一个典型的块属性标签,横向布局的时候我们通常都是用div float实现的,横向的间距设置如果用margin实现,这就是一个必然会碰到的兼容性问题。

浏览器兼容问题三:设置较小高度标签(一般小于10px),在IE6,IE7,遨游中高度超出自己设置高度

问题症状:IE6、7和遨游里这个标签的高度不受控制,超出自己设置的高度
碰到频率:60%
解决方案:给超出高度的标签设置overflow:hidden;或者设置行高line-height 小于你设置的高度。
备注:这种情况一般出现在我们设置小圆角背景的标签里。出现这个问题的原因是IE8之前的浏览器都会给标签一个最小默认的行高的高度。即使你的标签是空的,这个标签的高度还是会达到默认的行高。

浏览器兼容问题四:行内属性标签,设置display:block后采用float布局,又有横行的margin的情况,IE6间距bug

问题症状:IE6里的间距比超过设置的间距
碰到几率:20%
解决方案:在display:block;后面加入display:inline;display:table;
备注:行内属性标签,为了设置宽高,我们需要设置display:block;(除了input标签比较特殊)。在用float布局并有横向的margin后,在IE6下,他就具有了块属性float后的横向margin的bug。不过因为它本身就是行内属性标签,所以我们再加上display:inline的话,它的高宽就不可设了。这时候我们还需要在display:inline后面加入display:talbe。

浏览器兼容问题五:图片默认有间距

问题症状:几个img标签放在一起的时候,有些浏览器会有默认的间距,加了问题一中提到的通配符也不起作用。
碰到几率:20%
解决方案:使用float属性为img布局
备注:因为img标签是行内属性标签,所以只要不超出容器宽度,img标签都会排在一行里,但是部分浏览器的img标签之间会有个间距。去掉这个间距使用float是正道。(负margin,虽然能解决,但负margin本身就是容易引起浏览器兼容问题的用法,所以我禁止他们使用)

浏览器兼容问题六:标签最低高度设置min-height不兼容

问题症状:因为min-height本身就是一个不兼容的CSS属性,所以设置min-height时不能很好的被各个浏览器兼容
碰到几率:5%
解决方案:如果我们要设置一个标签的最小高度200px,需要进行的设置为:

1
2
3
4
5
6
{   
min-height:200px;
height:auto !important;
height:200px;
overflow:visible;
}

备注:在B/S系统前端开时,有很多情况下我们又这种需求。当内容小于一个值(如300px)时。容器的高度为300px;当内容高度大于这个值时,容器高度被撑高,而不是出现滚动条。这时候我们就会面临这个兼容性问题。

浏览器兼容问题七:透明度的兼容CSS设置

做兼容页面的方法是:每写一小段代码(布局中的一行或者一块)我们都要在不同的浏览器中看是否兼容,当然熟练到一定的程度就没这么麻烦了。建议经常会碰到兼容性问题的新手使用。很多兼容性问题都是因为浏览器对标签的默认属性解析不同造成的,只要我们稍加设置都能轻松地解决这些兼容问题。如果我们熟悉标签的默认属性的话,就能很好的理解为什么会出现兼容问题以及怎么去解决这些兼容问题。
/ CSS hack/
我很少使用hacker的,可能是个人习惯吧,我不喜欢写的代码IE不兼容,然后用hack来解决。不过hacker还是非常好用的。使用hacker我可以把浏览器分为3类:IE6 ;IE7和遨游;其他(IE8 chrome ff safari opera等)
◆IE6认识的hacker 是下划线_ 和星号
◆IE7 遨游认识的hacker是星号

比如这样一个CSS设置:
height:300px;height:200px;_height:100px;
IE6浏览器在读到height:300px的时候会认为高时300px;继续往下读,他也认识
heihgt, 所以当IE6读到height:200px的时候会覆盖掉前一条的相冲突设置,认为高度是200px。继续往下读,IE6还认识_height,所以他又会覆盖掉200px高的设置,把高度设置为100px;
IE7和遨游也是一样的从高度300px的设置往下读。当它们读到
height200px的时候就停下了,因为它们不认识_height。所以它们会把高度解析为200px,剩下的浏览器只认识第一个height:300px;所以他们会把高度解析为300px。因为优先级相同且想冲突的属性设置后一个会覆盖掉前一个,所以书写的次序是很重要的。

Javascript 数组操作

1.新建数组

1
2
3
4
5
6
7
//可以省略new
var colors = new Array();
var colors = new Array(20);
var colors = new Array("red", "blue", "green");

var colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组
var names = []; // 创建一个空数组

2.检测数组

1
2
3
4
5
6
7
8
if (value instanceof Array){
//对数组执行某些操作
}

//推荐下面这种方式
if (Array.isArray(value)){
//对数组执行某些操作
}

3.栈方法和队列方法

push() 方法可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度
pop() 方法则从数组末尾移除最后一项,减少数组的 length 值,然后返回移除的项
shift() 它能够移除数组中的第一个项并返回该项,同时将数组长度减 1
unshift()它能在数组前端添加任意个项并返回新数组的长度
例:

1
2
3
4
5
6
7
8
var colors = new Array(); // 创建一个数组
var count = colors.push("red", "green"); // 推入两项
alert(count); //2
count = colors.push("black"); // 推入另一项
alert(count); //3
var item = colors.pop(); // 取得最后一项
alert(item); //"black"
alert(colors.length); //2

4.转换方法

1
2
3
4
var colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组
alert(colors.toString()); // red,blue,green
alert(colors.valueOf()); // red,blue,green
alert(colors); // red,blue,green

alert() 会自动调用toString()方法

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var person1 = {
toLocaleString : function () {
return "Nikolaos";
},
toString : function() {
return "Nicholas";
}
};
var person2 = {
toLocaleString : function () {
return "Grigorios";
},
toString : function() {
return "Greg";
}
};
var people = [person1, person2];
alert(people); //Nicholas,Greg
alert(people.toString()); //Nicholas,Greg
alert(people.toLocaleString()); //Nikolaos,Grigorios

join()方法 可以使用不同的分隔符来构建这个字符串

1
2
3
var colors = ["red", "green", "blue"];
alert(colors.join(",")); //red,green,blue
alert(colors.join("||")); //red||green||blue

5.排序方法

reverse() 方法会反转数组项的顺序

1
2
3
ar values = [1, 2, 3, 4, 5];
values.reverse();
alert(values); //5,4,3,2,1

sort() 方法按升序排列数组项——即最小的值位于最前面,最大的值排在最后面。
为了实现排序, sort() 方法会调用每个数组项的 toString() 转型方法,然后比较得到的字符串,以确定如何排序。
即使数组中的每一项都是数值, sort() 方法比较的也是字符串

1
2
3
var values = [0, 1, 5, 10, 15];
values.sort();
alert(values); //0,1,10,15,5

sort()自定义排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function compare(value1, value2) {
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}

var values = [0, 1, 5, 10, 15];
values.sort(compare);
alert(values); //0,1,5,10,15

//数值型比较函数
function compare(value1, value2){
return value2 - value1;
}

6.操作方法

concat() 方法可以基于当前数组中的所有项创建一个新数组

1
2
3
4
var colors = ["red", "green", "blue"];
var colors2 = colors.concat("yellow", ["black", "brown"]);
alert(colors); //red,green,blue
alert(colors2); //red,green,blue,yellow,black,brown

slice() 它能够基于当前数组中的一或多个项创建一个新数组。
slice() 方法可以接受一或两个参数,即要返回项的起始和结束位置
在只有一个参数的情况下, slice() 方法返回从该
参数指定位置开始到当前数组末尾的所有项。如果有两个参数,该方法返回起始和结束位置之间的项—但不包括结束位置的项

1
2
3
4
5
var colors = ["red", "green", "blue", "yellow", "purple"];
var colors2 = colors.slice(1);
var colors3 = colors.slice(1,4);
alert(colors2); //green,blue,yellow,purple
alert(colors3); //green,blue,yellow

splice() 方法,这个方法恐怕要算是最强大的数组方法了,它有很多种用。
splice() 的主要用途是向数组的中部插入项,但使用这种方法的方式则有如下 3 种

删除:可以删除任意数量的项,只需指定 2 个参数:要删除的第一项的位置和要删除的项数。
例如, splice(0,2) 会删除数组中的前两项。
插入:可以向指定位置插入任意数量的项,只需提供 3 个参数:起始位置、0(要删除的项数)
和要插入的项。如果要插入多个项,可以再传入第四、第五,以至任意多个项。例如,
splice(2,0,”red”,”green”) 会从当前数组的位置 2 开始插入字符串 “red” 和 “green” 。
替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定 3 个参数:起始位置、要删除的项数和要插入的任意数量的项。插入的项数不必与删除的项数相等。例如,
splice (2,1,”red”,”green”) 会删除当前数组位置 2 的项,然后再从位置 2 开始插入字符串”red” 和 “green” 。
splice() 方法始终都会返回一个数组,该数组中包含从原始数组中删除的项(如果没有删除任何项,则返回一个空数组) 。下面的代码展示了上述 3 种使用 splice() 方法的方式

1
2
3
4
5
6
7
8
9
10
var colors = ["red", "green", "blue"];
var removed = colors.splice(0,1); // 删除第一项
alert(colors); // green,blue
alert(removed); // red,返回的数组中只包含一项
removed = colors.splice(1, 0, "yellow", "orange"); // 从位置 1 开始插入两项
alert(colors); // green,yellow,orange,blue
alert(removed); // 返回的是一个空数组
removed = colors.splice(1, 1, "red", "purple"); // 插入两项,删除一项
alert(colors); // green,red,purple,orange,blue
alert(removed); // yellow,返回的数组中只包含一项

7.位置方法

indexOf() 和 lastIndexOf() 。这两个方法都接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。其中,indexOf() 方法从数组的开头(位置0)开始向后查找,lastIndexOf() 方法则从数组的末尾开始向前查找

1
2
3
4
5
6
7
8
9
10
var numbers = [1,2,3,4,5,4,3,2,1];
alert(numbers.indexOf(4)); //3
alert(numbers.lastIndexOf(4)); //5
alert(numbers.indexOf(4, 4)); //5
alert(numbers.lastIndexOf(4, 4)); //3
var person = { name: "Nicholas" };
var people = [{ name: "Nicholas" }];
var morePeople = [person];
alert(people.indexOf(person)); //-1
alert(morePeople.indexOf(person)); //0

8.迭代方法

every() :对数组中的每一项运行给定函数,如果该函数对每一项都返回 true,则返回 true 。

filter() :对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的数组。

forEach() :对数组中的每一项运行给定函数。这个方法没有返回值。

map() :对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组

some() :对数组中的每一项运行给定函数,如果该函数对任一项返回 true ,则返回 true 。

every() 和 some() ,它们都用于查询数组中的项是否满足某个条件

1
2
3
4
5
6
7
8
9
var numbers = [1,2,3,4,5,4,3,2,1];
var everyResult = numbers.every(function(item, index, array){
return (item > 2);
});
alert(everyResult); //false
var someResult = numbers.some(function(item, index, array){
return (item > 2);
});
alert(someResult); //true

filter() 它利用指定的函数确定是否在返回的数组中包含某一项

1
2
3
4
5
var numbers = [1,2,3,4,5,4,3,2,1];
var filterResult = numbers.filter(function(item, index, array){
return (item > 2);
});
alert(filterResult); //[3,4,5,4,3]

map() 也返回一个数组,而这个数组的每一项都是在原始数组中的对应项上运行传入函数的结果

1
2
3
4
5
var numbers = [1,2,3,4,5,4,3,2,1];
var mapResult = numbers.map(function(item, index, array){
return item * 2;
});
alert(mapResult); //[2,4,6,8,10,8,6,4,2]

forEach() ,它只是对数组中的每一项运行传入的函数。这个方法没有返回值,
本质上与使用 for 循环迭代数组一样

1
2
3
4
var numbers = [1,2,3,4,5,4,3,2,1];
numbers.forEach(function(item, index, array){
//执行某些操作
});

9.归并方法

1
2
3
4
5
6
7
8
9
10
11
12
//使用 reduce() 方法可以执行求数组中所有值之和的操作,比如:
var values = [1,2,3,4,5];
var sum = values.reduce(function(prev, cur, index, array){
return prev + cur;
});
alert(sum); //15
//reduceRight() 的作用类似,只不过方向相反而已。来看下面这个例子。
var values = [1,2,3,4,5];
var sum = values.reduceRight(function(prev, cur, index, array){
return prev + cur;
});
alert(sum); //15

C#特性

1
2
3
4
5
6
7
///获取myclass的特性
System.Reflection.MemberInfo info = typeof(MyClass);
object[] attributes = info.GetCustomAttributes(true);
for (int i = 0; i < attributes.Length; i++)
{
System.Console.WriteLine((attributes[i] as HelpAttribute).Url);
}