1.Typescript中的&理解
constructor(props: TagManagementStateProps & TagManagementOwnProps & TagManagementDispatchProps)
参数中的 & 表示props对象同时拥有了TagManagementStateProps、TagManagementOwnProps、TagManagementDispatchProps这三种类型的成员。交叉类型:将多个类型合并为一个类型,这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性
2.takeEvery
export default function* () {
yield takeEvery(TAG_LISTEN_QUERY, queryListSaga);
yield takeEvery(TAG_LISTEN_QUERY_INIT_DATA, queryInitDataSaga);
yield takeLatest(TAG_LISTEN_FETCH_MATERIAL_TAGS_SAGA, queryMaterialTagsSaga);
yield takeEvery(TAG_LISTEN_SAVE_QUESTION_TAGS, saveTagsSaga);
}
takeEvery:允许多个 fetchData 实例同时启动。在某个特定时刻,我 们 可以启动一个新的 fetchData 任务, 尽管之前还有一个或多个 fetchData 尚未结束
takeLatest:只允许执行一个 fetchData 任务,并且这个任务是最后被启动的那个,如果之前已经有一个任务在执行,那之前的这个任务会自动被取消。
3.由function*定义的generator(生成器)
generator(生成器)是ES6标准引入的新的数据类型。一个generator看上去像一个函数,但可以返回多次。 generator和函数不同的是,generator由function定义(注意多出的号),并且,除了return语句,还可以用yield返回多次。
4.encodeURI、encodeURIComponent、decodeURI、decodeURIComponent
-
这四个方法的用处
1、用来编码和解码URI的
统一资源标识符,或叫做 URI,是用来标识互联网上的资源(例如,网页或文件)和怎样访问这些资源的传输协议(例如,HTTP 或 FTP)的字符串。除了encodeURI、encodeURIComponent、decodeURI、decodeURIComponent四个用来编码和解码 URI 的函数之外 ECMAScript 语言自身不提供任何使用 URL 的支持。2、URI组成形式
一个 URI 是由组件分隔符分割的组件序列组成。其一般形式是:
Scheme : First / Second ; Third ? Fourth
其中斜体的名字代表组件;“:”, “/”, “;”,“?”是当作分隔符的保留字符。3、有和不同?
encodeURI 和 decodeURI 函数操作的是完整的 URI;这俩函数假定 URI 中的任何保留字符都有特殊意义,所有不会编码它们。
encodeURIComponent 和 decodeURIComponent 函数操作的是组成 URI 的个别组件;这俩函数假定任何保留字符都代表普通文本,所以必须编码它们,所以它们(保留字符)出现在一个完整 URI 的组件里面时不会被解释成保留字符了。
以上说明摘自ECMAScript标准,为了容易读懂做了点编辑加工。
4、图解四个函数的不同:
当 URI 里包含一个没在上面列出的字符或有时不想让给定的保留字符有特殊意义,那么必须编码这个字符。字符被转换成 UTF-8 编码,首先从 UTF-16 转换成相应的代码点值的替代。然后返回的字节序列转换为一个字符串,每个字节用一个“%xx”形式的转移序列表示。(具体转换规则可以参考抽象操作Encode和Decode的说明)
5.toUTCString
可根据世界时(UTC)把Data对象转换为字符串,并返回结果
6.Infinity:无穷大
7.@keyframes:创建动画(一般要注意浏览器的兼容性)
@keyframes animationname {keyframes-selector {css-styles;}}
animationname:声明动画的名称。
keyframes-selector:用来划分动画的时长,可以使用百分比形式,也可以使用 "from"和 "to"的形式。"from" 和 "to"的形式等价于 0% 和 100%建议始终使用百分比形式。
8.git clone时,报403错误
刚开始以为是项目组那边没有授权,但感觉又不对,找了一下发现是git 客户端缓存了错误的密码,解决方法:
1、增加远程地址的时候带上密码。(推荐)
git clone https://zhuyan.luo:[[email protected]](mailto:[email protected])/zhishinet/TeacherClient/
2、运行命令:rm ~/.git-credentials,删掉git config --global credential.helper store保存的账号和密码。回到每次输入用户名和密码。
~/.git-credentials
9.Object.assign()
[FROM_DRAFT_TO_PERSON]: (state, action: Action) => {
return Object.assign({},state,{
fromDraftsToPersonFlag: action.payload
})
},
Object.assign()用于把一个或多个源对象的可枚举属性复制到目标对象中,返回值为目标对象
10.JavaScript中的可枚举属性与不可枚举属性
在JavaScript中,对象的属性分为可枚举和不可枚举之分,它们是由属性的enumerable值决定的。可枚举性决定了这个属性能否被for…in查找遍历到。js中基本包装类型的原型属性是不可枚举的,如Object, Array, Number等,
11.count = (count + '') === '0' ? '0' : count; (count是id)
这样做可避免 !1=false,!0=true (!'0'=false)
12.Store
Store 是保存数据的地方,可以把它看成一个容器,整个应用只能有一个 Store。Redux 提供createStore这个函数来生成 Store。
import { createStore } from 'redux';
const store = createStore(fn);
上面代码中,createStore函数接受另一个函数作为参数,返回新生成的 Store 对象。
13.State
Store对象包含所有数据。如果想得到某个时点的数据,就要对 Store 生成快照。这种时点的数据集合,就叫做 State。
当前时刻的 State,可以通过store.getState()拿到。
14.box-shadow 阴影
用法:box-shadow: 水平阴影的位置(可为负值) 垂直阴影的位置(可为负值) [模糊距离] [阴影的尺寸] [阴影的颜色] [阴影类型 (outset、inset)];
实现鼠标hover时延迟出现阴影(结合transition-duration、transition-property两个属性使用)
#edit_card {
width: 100%;
height: auto;
transition-duration: .8s;
transition-property: box-shadow;
&:hover{
box-shadow: 0 0 12px rgb(153,153,153);
}
}
15.多重边框实现
1、 outline实现
不足之处:
2、 box-shadow实现
16.在入口文件app.tsx中引入了react-hot-loader中的AppContainer组件,这个组件下的所有子组件都会在发生变化时触发热更新
17. 在Typescript中Table组件使用fixed属性报错
解决方法:https://github.com/ant-design/ant-design/issues/3772#issuecomment-259358384
①定义columns时TableColumnConfig给any的类型:columns: TableColumnConfig
②给列定义fixed属性时写成:fixed: 'right' as 'right'
③或者写成下面这样
import React, { Component } from 'react';
import { Table } from 'antd';
import { TableColumnConfig } from 'antd/lib/table/Table';
interface Person {
name: string;
}
const columns: TableColumnConfig[] = [{
title: 'Name',
dataIndex: 'name',
fixed: 'right',
}];
const data: Person[] = [{
name: 'Jack',
}];
class PersonTable extends Table {}
class App extends Component {
render() {
return (
)
}
}
18.antd的Select组件,普通的在输入框后面都带有向下的箭头,并且不可输入,加上 mode="combobox" 属性后,下拉框就像一个输入框,既可以输入又能选择;加上showSearch optionFilterProp="children" 后可过滤搜索
19.antd在给组件添加默认值时,不在Form组件中直接使用 defaultValue 就行,但在Form表单中需要写成:initialValue
{getFieldDecorator('policyNumber',{
initialValue:fields.policyNumber?fields.policyNumber:''
})(
)}
20.table排序:
本地排序使用一个函数(参考 Array.sort 的 compareFunction),需要服务端排序可设为 true
21.新增的时候要传空对象过去,否则先点击编辑再点击新增,新增界面会和编辑界面一样(对象会被缓存)
const handleAdd = () => {
debugger;
onShowModal({
modalType: 'create',
currentItem: {},
});
}
const handleEdit = (item: any) => {
debugger;
onShowModal({
modalType: 'update',
currentItem: item,
});
};
22.Moment.js
日期转时间戳:Date.parse(this.props.form.getFieldValue('startDate')[0])
时间戳转日期:moment.unix(text).format('YYYY-MM-DD HH:mm')
23.需要全局使用的数据在 根RootContainer中获取
24.对于一个查询最好定义四个action
//获取排课表信息周\月数据量
export const COUNT_QUERY = 'scheduleValues/COUNT';
export const COUNT_QUERY_SUCCESS = 'scheduleValues/COUNT_SUCCESS';
export const COUNT_QUERY_ERROR = 'scheduleValues/COUNT_ERROR';
export const COUNT_QUERY_FAILED = 'scheduleValues/COUNT_FAILED';
25.对于请求返回的data的类型不同(对象、数组),要注意yield put(queryCountSuccess(data));,对象传data,数组传data.rows
//获取排课表信息周\月数据量
function* queryCount(action: Action) {
try {
let params = {};
const data = yield request({
url: countUrl,
method: 'get',
data: params,
});
if(data.success){
yield put(queryCountSuccess(data));
} else {
yield put(queryCountError(data.status));
}
} catch (e) {
yield put(queryCountFailed(e.message));
}
}
26.注意下这个 as Array,as Object
[LEVELS_QUERY_ERROR]: (state, action: Action) => {
return Object.assign({}, state, {
levelList: action.payload as Array,
});
},
[LEVELS_QUERY_FAILED]: (state, action: Action>) => {
return Object.assign({}, state, {
levelList: action.payload as Object,
});
},
27.下拉列表联动
studentSelect(value: any){
let school = null;
for(let i in this.props.studentList){
if(value === this.props.studentList[i].userId ){
school = this.props.studentList[i].school;
break;
}
}
this.props.form.setFieldsValue({school: school}) ;
}
28.根据接口返回数据的类型调用不同的请求方法,返回是json使用request; 返回字符串或者无返回值的使用autoRefreshTokenFetch,再将返回数据使用 .json()转化成json
//获取活跃人数 (接口只返回了 11)
function* queryActiveCount(action: Action) {
try {
const request = {
method: 'GET',
credentials: 'include',
headers: {
'Authorization': (window as GlobalDefinitions).authorization,
'Accept': 'application/json',
'Content-Type': 'application/json',
'Cache-Control':' no-cache'
},
url: activeCountUrl +'/TEACHER'
};
const data = (yield call(autoRefreshTokenFetch, request));
if(data.ok){
let json = yield data.json();
yield put(queryActiveCountSuccess(json));
} else {
yield put(queryActiveCountError(data.status));
}
} catch (e) {
yield put(queryActiveCountFailed(e.message));
}
}
//获取英语、数学人数(接口返回json {English: 8, Math: 3} )
function* querySubjectCount(action: Action) {
try {
let params = {};
const data = yield request({
url: subjectCountUrl,
method: 'get',
data: params,
});
if(data.success){
yield put(querySubjectCountSuccess(data));
} else {
yield put(querySubjectCountError(data.statusCode));
}
} catch (e) {
yield put(querySubjectCountFailed(e.message));
}
}
29.Select的value只能是String类型的数据,有时候数据库取出来的是Number的,想要利用initialValue初始显示在Form表单中的话需要将Number的转化成String,否则在下拉框中将会直接显示数字
initialValue: item.gender? item.gender.toString():'',
30.在Form表单中,想要使按Enter键的作用和点击某个按钮的作用一样,可以在那个按钮上加 htmlType="submit" 属性
比如在输入框输入完东西后想按enter键进行查询,那么可以在 查询 按钮上加该属性
31.省份provinceList和学校schoolList级联
componentWillMount(){
let provinceList = this.props.provinceList || [];
let schoolList = this.props.schoolList || [];
let options = [];
for(let i in provinceList){
debugger
let temp:any = {};
temp.value = provinceList[i].lookupCode;
temp.label = provinceList[i].lookupValue;
temp.children = [];
for(let j in schoolList){
if(schoolList[j].parentValue == provinceList[i].value){
let subTemp: any = {};
subTemp.value = schoolList[j].lookupCode;
subTemp.label = schoolList[j].lookupValue;
temp.children.push(subTemp);
}
}
options.push(temp);
}
this.setState({
options: options,
})
}
32.正则表达式
/^(?![0-9]+)[0-9A-Za-z]{6,20}) 预测该位置后面不全是数字
(?![a-zA-Z]+ 匹配行结尾位置
33.登录按钮loading状态做法
在点击按钮请求登录接口之前将loading置为true,请求登录接口成功(requestLoginSuccess),在成功那里将loading置为false,请求接口失败同理
34.返回上一页
window.location.reload(); //刷新
window.history.go(1); //前进
window.history.go(-1); //返回+刷新
window.history.forward(); //前进
window.history.back(); //返回
location.href=document.referrer; //document.referrer是获取上一页的url
history.length; //查看历史中的页面数
35./CourseManagement/course/schedule}> 返回
36.分页组件自定义每页条数pageSizeOptions
pagination: {
...pagination,
showSizeChanger: true,
showQuickJumper: true,
pageSizeOptions: ['10','20','50','100','200','500'],
},
37.Select要求value的类型要为string,将number转化为string的方法为:加个'',但要注意在初始化时也要给初始值加上'',否则显示不出value对应的值,同时后台需要number类型的数据时记得传过去的时候要用parseInt将字符串转成数字
{getFieldDecorator('userId', {
initialValue: ''+item.userId,
rules: [
{ required: true, message:'学生不能为空!',},
],
})(
)}
38.芝士网登录流程
userLogin: `${baseUrl}/api/zauth/v1/token/access`, //登录认证,获取token
refreshToken: `${baseUrl}/api/zauth/v1/token/refresh`, //token有过期时间的,过期了 需要通过 refreshToken 重新拿 token
userInfo: `${baseUrl}/api/HomePageAPI/GetUserInfo`, //传一个userId获取用户信息。(userId是在 知识网 登录页面登录成功后会把userId保存到localStorage里面,现在在我们的工程里没有存userId的过程只有取的过程,在app.tsx中可看到)
39.控制台报错
Cannot read property 'getFieldDecorator' of undefined
是因为使用了Form没有调用Form.create()
40.import一个组件时,如果组件导出时使用了关键字default,则导入时不需将组件用{},若没有default则需要
export const Login = Form.create()(LoginForm);
import {Login} from "./containers/CourseManagement/Login/RootContainer/index";
41.JS在1.6中为Array新增了几个方法map(),filter(),some(),every(),forEach(),也就是一共有这么多方法了。
map():返回一个新的Array,每个元素为调用func的结果
filter():返回一个符合func条件的元素数组
some():返回一个boolean,判断是否有元素是否符合func条件
every():返回一个boolean,判断每个元素是否符合func条件
forEach():没有返回值,只是针对每个元素调用func
42.Promise (https://www.cnblogs.com/lvdabao/p/es6-promise-1.html)
Promise是一个构造函数,自己身上有all、reject、resolve这几个眼熟的方法,原型上有then、catch等同样很眼熟的方法。这么说用Promise new出来的对象肯定就有then、catch方法。
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('执行完成');
resolve('随便什么数据');
}, 2000);
});
Promise的构造函数接收一个参数(是函数),这个参数又传入两个参数:resolve,reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。其实这里用“成功”和“失败”来描述并不准确,按照标准来讲,resolve是将Promise的状态置为fullfiled,reject是将Promise的状态置为rejected。
在上面的代码中,我们执行了一个异步操作,也就是setTimeout,2秒后,输出“执行完成”,并且调用resolve方法。
运行代码,会在2秒后输出“执行完成”。注意!我只是new了一个对象,并没有调用它,我们传进去的函数就已经执行了,这是需要注意的一个细节。所以我们用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数,如
function runAsync(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('执行完成');
resolve('随便什么数据');
}, 2000);
});
return p;
}
runAsync()
在我们包装好的函数最后,会return出Promise对象,也就是说,执行这个函数我们得到了一个Promise对象。还记得Promise对象上有then、catch方法吧?这就是强大之处了,看下面的代码
runAsync().then(function(data){
console.log(data);
//后面可以用传过来的数据做些其他操作
//......
});
在runAsync()的返回上直接调用then方法,then接收一个参数,是函数,并且会拿到我们在runAsync中调用resolve时传的的参数。运行这段代码,会在2秒后输出“执行完成”,紧接着输出“随便什么数据”。
这时候你应该有所领悟了,原来then里面的函数就跟我们平时的回调函数一个意思,能够在runAsync这个异步任务执行完成之后被执行。这就是Promise的作用了,简单来讲,就是能把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。
你可能会不屑一顾,那么牛逼轰轰的Promise就这点能耐?我把回调函数封装一下,给runAsync传进去不也一样吗,就像这样:
function runAsync(callback){
setTimeout(function(){
console.log('执行完成');
callback('随便什么数据');
}, 2000);
}
runAsync(function(data){
console.log(data);
});
效果也是一样的,还费劲用Promise干嘛。那么问题来了,有多层回调该怎么办?如果callback也是一个异步操作,而且执行完后也需要有相应的回调函数,该怎么办呢?总不能再定义一个callback2,然后给callback传进去吧。而Promise的优势在于,可以在then方法中继续写Promise对象并返回,然后继续调用then来进行回调操作。
(链式操作的用法)所以,从表面上看,Promise只是能够简化层层回调的写法,而实质上,Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活的多。所以使用Promise的正确场景是这样的:
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return runAsync3();
})
.then(function(data){
console.log(data);
});
这样能够按顺序,每隔两秒输出每个异步回调中的内容,在runAsync2中传给resolve的数据,能在接下来的then方法中拿到。
在then方法中,你也可以直接return数据而不是Promise对象,在后面的then中就可以接收到数据了,比如我们把上面的代码修改成这样:
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return '直接返回数据'; //这里直接返回数据
})
.then(function(data){
console.log(data);
});
(reject的用法)
到这里,你应该对“Promise是什么玩意”有了最基本的了解。那么我们接着来看看ES6的Promise还有哪些功能。我们光用了resolve,还没用reject呢,它是做什么的呢?事实上,我们前面的例子都是只有“执行成功”的回调,还没有“失败”的情况,reject的作用就是把Promise的状态置为rejected,这样我们在then中就能捕捉到,然后执行“失败”情况的回调。看下面的代码。
function getNumber(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
var num = Math.ceil(Math.random()*10); //生成1-10的随机数
if(num<=5){
resolve(num);
}
else{
reject('数字太大了');
}
}, 2000);
});
return p;
}
getNumber()
.then(
function(data){
console.log('resolved');
console.log(data);
},
function(reason, data){
console.log('rejected');
console.log(reason);
}
);
(catch的用法)
我们知道Promise对象除了then方法,还有一个catch方法,它是做什么用的呢?其实它和then的第二个参数一样,用来指定reject的回调,用法是这样:
getNumber()
.then(function(data){
console.log('resolved');
console.log(data);
})
.catch(function(reason){
console.log('rejected');
console.log(reason);
});
效果和写在then的第二个参数里面一样。不过它还有另外一个作用:在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。请看下面的代码:
getNumber()
.then(function(data){
console.log('resolved');
console.log(data);
console.log(somedata); //此处的somedata未定义
})
.catch(function(reason){
console.log('rejected');
console.log(reason);
});
在resolve的回调中,我们console.log(somedata);而somedata这个变量是没有被定义的。如果我们不用Promise,代码运行到这里就直接在控制台报错了,不往下运行了。但
也就是说进到catch方法里面去了,而且把错误原因传到了reason参数中。即便是有错误的代码也不会报错了,这与我们的try/catch语句有相同的功能。
(all的用法)
Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。我们仍旧使用上面定义好的runAsync1、runAsync2、runAsync3这三个函数,看下面的例子Promise
.all([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
console.log(results);
});
用Promise.all来执行,all接收一个数组参数,里面的值最终都算返回Promise对象。这样,三个异步操作的并行执行的,等到它们都执行完后才会进到then里面。那么,三个异步操作返回的数据哪里去了呢?都在then里面呢,all会把所有异步操作的结果放进一个数组中传给then,就是上面的results。
有了all,你就可以并行执行多个异步操作,并且在一个回调中处理所有的返回数据,是不是很酷?有一个场景是很适合用这个的,一些游戏类的素材比较多的应用,打开网页时,预先加载需要用到的各种资源如图片、flash以及各种静态文件。所有的都加载完后,我们再进行页面的初始化。
(race的用法)
all方法的效果实际上是「谁跑的慢,以谁为准执行回调」,那么相对的就有另一个方法「谁跑的快,以谁为准执行回调」,这就是race方法,这个词本来就是赛跑的意思。race的用法与all一样,我们把上面runAsync1的延时改为1秒来看一下:
Promise
.race([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
console.log(results);
});
这三个异步操作同样是并行执行的。结果你应该可以猜到,1秒后runAsync1已经执行完了,此时then里面的就执行了,你猜对了吗?不完全,是吧。在then里面的回调开始执行时,runAsync2()和runAsync3()并没有停止,仍旧再执行。于是再过1秒后,输出了他们结束的标志。
这个race有什么用呢?使用场景还是很多的,比如我们可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作,代码如下:
//请求某个图片资源function requestImg(){
var p = new Promise(function(resolve, reject){
var img = new Image();
img.onload = function(){
resolve(img);
}
img.src = 'xxxxxx';
});
return p;
}
//延时函数,用于给请求计时function timeout(){
var p = new Promise(function(resolve, reject){
setTimeout(function(){
reject('图片请求超时');
}, 5000);
});
return p;
}
Promise
.race([requestImg(), timeout()])
.then(function(results){
console.log(results);
})
.catch(function(reason){
console.log(reason);
});
requestImg函数会异步请求一张图片,我把地址写为"xxxxxx",所以肯定是无法成功请求到的。timeout函数是一个延时5秒的异步操作。我们把这两个返回Promise对象的函数放进race,于是他俩就会赛跑,如果5秒之内图片请求成功了,那么遍进入then方法,执行正常的流程。如果5秒钟图片还未成功返回,那么timeout就跑赢了,则进入catch,报出“图片请求超时”的信息。运行结果如下:
43.Array.from
ES6为Array增加了from函数用来将其他对象转换成数组。
当然,其他对象也是有要求,也不是所有的,可以将两种对象转换成数组。
1.部署了Iterator接口的对象,比如:Set,Map,Array。
2.类数组对象,什么叫类数组对象,就是一个对象必须有length属性,没有length,转出来的就是空数组。
44.dva/dynamic
解决组件动态加载问题的 util 方法。
比如:
import dynamic from 'dva/dynamic';
const UserPageComponent = dynamic({
app,
models: () => [
import('./models/users'),
],
component: () => import('./routes/UserPage'),
});
opts 包含:
app: dva 实例,加载 models 时需要
models: 返回 Promise 数组的函数,Promise 返回 dva model
component:返回 Promise 的函数,Promise 返回 React Component
45.dva API
* app = dva(opts) --创建应用,返回 dva 实例。(注:dva 支持多实例)
opts 包含:
*history*:指定给路由用的 history,默认是 hashHistory
*initialState*:指定初始数据,优先级高于 model 中的 state,默认是 {}
* app.use({}) --配置 hooks 或者注册插件
* app.model(model) --注册 model
* app.unmodel(namespace) --取消 model 注册,清理 reducers, effects 和 subscriptions。subscription 如果没有返回 unlisten 函数,使用 app.unmodel 会给予警告
* app.router(router) --注册路由表
* app.start('#root') --启动应用
46.react项目使用npm build命令(将开发完的前端代码,利用webpack打包成静态压缩文件)构建出静态文件,这些静态文件可以直接在浏览器打开,之前的财联邦项目发布就是使用的这个方式,同样还可以在服务器安装nodejs,然后把代码clone过去直接用npm start命令也可以启动项目访问页面(在本地开发的时候就是这样)
47.nodejs前端服务器的职责
* 作为静态文件服务器,当用户访问网站的时候,将index.html以及其引入的js、css、fonts以及图片返回给用户
* 负责将客户端发来的ajax请求转发给后台服务器
48.map()中的元素都需要属性key。在哪儿循环就在哪儿设置key
49.key用来作为React的观察点,但它们不会传递给组件。 如果你需要在组件中使用相同的值,则使用不同的名称显式地将它作为props传递:
const content = posts.map(post =>
);
使用上面的示例,Post组件可以读取props.id,但不能读取props.key
50.如果有几个组件需要反映相同的变化数据,建议将共享state提升到层级最近的,并且是共同的父组件上。
对于在React应用程序中更改的任何数据,都应该有一个唯一的“数据来源”,也就是state。通常,首先将state添加到需要渲染的组件。如果其他组件也需要它,你可以将其提升到它们层级最近的共同父级组件中。而不是尝试在不同组件之间去同步状态,总归就一句话:你应该依赖于自上而下的数据流
51.devtools
* eval 文档上解释的很明白,每个模块都封装到 eval 包裹起来,并在后面添加 //# sourceURL
* source-map 这是最原始的 source-map 实现方式,其实现是打包代码同时创建一个新的 sourcemap 文件, 并在打包文件的末尾添加 //# sourceURL 注释行告诉 JS 引擎文件在哪儿
* hidden-source-map 文档上也说了,就是 soucremap 但没注释,没注释怎么找文件呢?貌似只能靠后缀,譬如 xxx/bundle.js 文件,某些引擎会尝试去找 xxx/bundle.js.map
* inline-source-map 为每一个文件添加 sourcemap 的 DataUrl,注意这里的文件是打包前的每一个文件而不是最后打包出来的,同时这个 DataUrl 是包含一个文件完整 souremap 信息的 Base64 格式化后的字符串,而不是一个 url。
* eval-source-map 这个就是把 eval 的 sourceURL 换成了完整 souremap 信息的 DataUrl
* cheap-source-map 不包含列信息,不包含 loader 的 sourcemap,(譬如 babel 的 sourcemap)
* cheap-module-source-map 不包含列信息,同时 loader 的 sourcemap 也被简化为只包含对应行的。最终的 sourcemap 只有一份,它是 webpack 对 loader 生成的 sourcemap 进行简化,然后再次生成的。
* 这么多模式,到底该用哪个?
* cheap-module-eval-source-map 绝大多数情况下都会是最好的选择,这也是下版本 webpack 的默认选项。
* 相关解释:
* 大部分情况我们调试并不关心列信息,而且就算 sourcemap 没有列,有些浏览器引擎(例如 v8) 也会给出列信息,所以我们使用 cheap 模式可以大幅提高 souremap 生成的效率。
* 使用 eval 方式可大幅提高持续构建效率,参考 [webapck devtool 文档](https://webpack.github.io/docs/configuration.html#devtool) 下方速度对比表格,这对经常需要边改边调的前端开发而言,非常重要!
* 使用 module 可支持 babel 这种预编译工具(在 webpack 里做为 loader 使用)。
* eval-source-map 使用 DataUrl 本身包含完整 sourcemap 信息,并不需要像 sourceURL 那样,浏览器需要发送一个完整请求去获取 sourcemap 文件,这会略微提高点效率
- 52.React Developer Tools介绍
React Developer Tools 是一款由 facebook 开发的有用的 Chrome浏览器扩展。通过它我们可以查看应用程序的 React 组件分层结构,而不是更加神秘的浏览器 DOM 表示。
注意:该插件只对 ReactJS 开发有效。如果是 React Native 的话则无法使用这个插件调试。
安装完扩展程序后打开扩展程序管理页面。将 React Developer Tools 的“允许访问文件网址”勾选。
-
React Developer Tools使用说明
(1)首先使用 Chrome 打开需要调试的 React 页面,并打开“开发者工具”。
(2)在“开发者工具”上方工具栏最右侧会有个 react 标签。点击这个标签就可以看到当前应用的结构。
通过 React Developer Tools 我们可以很方便地看到各个组件之间的嵌套关系以及每个组件的事件、属性、状态等信息。
(3)React Developer Tools会自动检测React组件,不过在webpack-dev-server模式下,webpack会自动将React组件放入到iframe下,导致React组件检测失败,变通方法是webpack-dev-server配置在--inline模式下即可:
webpack-dev-server --inline
(5)修改某一处为错误,然后观察结果
56. this.props.children
this.props 对象的属性与组件的属性一一对应,但是有一个例外,就是 this.props.children 属性。它表示组件的所有子节点(查看 demo05)。
var NotesList = React.createClass({
render: function() {
return (
{
React.Children.map(this.props.children, function (child) {
return - {child}
;
})
}
);
}});
ReactDOM.render(
hello
world
,
document.body
);
上面代码的 NoteList 组件有两个 span 子节点,它们都可以通过 this.props.children 读取。
这里需要注意, this.props.children 的值有三种可能:如果当前组件没有子节点,它就是 undefined ;如果有一个子节点,数据类型是 object ;如果有多个子节点,数据类型就是 array 。所以,处理 this.props.children 的时候要小心。
React 提供一个工具方法 React.Children 来处理 this.props.children 。我们可以用 React.Children.map 来遍历子节点,而不用担心 this.props.children 的数据类型是 undefined 还是 object。更多的 React.Children 的方法,请参考官方文档。
props.children可以像任何其他props那样工作,它可以传递任何类型的数据,并不局限于那些告诉React应该如何渲染的东东。 例如,如果您有一个自定义组件,您可以将props.children作为一个回调函数:
-
布尔值、null、undefined在渲染时会被自动忽略
false,null,undefined和true是有效的子元素,不过他们从根本上讲是不参与渲染的。 这些JSX表达式将渲染处相同的东西:
如果你想要一个值如false,true,null或undefined出现在输出中,你必须先将它转换为字符串:
57.JSX会删除行的开始和结尾处的空格。 它也会删除中间的空行。 与标签相邻的空行被会被删除;
在字符串文本中间出现的空行会缩合成一个空格。
58.instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
object instanceof constructor (object--要检测的对象,constructor--某个构造函数)
- 59.window.scrollTo和window.scrollBy (to是绝对的意思(从整体而言),by是相对的意思(从原先的位置而言)
scrollTo:在窗体中如果有滚动条,将横向滚动条移动到相对于窗体宽度为x个
像素的位置,将纵向滚动条移动到相对于窗体高度为y个像素的位置
scrollBy:(要使此方法工作 window 滚动条的可见属性必须设置为true!)
如果有滚动条,将横向滚动条移动到相对于当前横向滚动条的x个像素的位置
(就是向左移动x像素),将纵向滚动条移动到相对于当前纵向滚动条高度为y个像素的位置(就是向下移动y像素)
60.npm --save-dev --save 的区别
npm install 在安装 npm 包时,有两种命令参数可以把它们的信息写入 package.json 文件,一个是npm install--save另一个是 npm install –save-dev,他们表面上的区别是--save 会把依赖包名称添加到 package.json 文件 dependencies 键下,--save-dev 则添加到 package.json 文件 devDependencies 键下,不过这只是它们的表面区别。它们真正的区别是,npm自己的文档说dependencies是运行时依赖,devDependencies是开发时的依赖。即devDependencies 下列出的模块,是我们开发时用的,比如 我们安装 js的压缩包gulp-uglify 时,我们采用的是 “npm install –save-dev gulp-uglify ”命令安装,因为我们在发布后用不到它,而只是在我们开发才用到它。dependencies 下的模块,则是我们发布后还需要依赖的模块,譬如像jQuery库或者Angular框架类似的,我们在开发完后后肯定还要依赖它们,否则就运行不了。
另外需要补充的是:
正常使用npm install时,会下载dependencies和devDependencies中的模块,当使用npm install –production或者注明NODE_ENV变量值为production时,只会下载dependencies中的模块
61.利用window.onscroll事件实现滚动条滚动分页
//滚动滑轮触发scrollFunc方法 //ie 谷歌
window.onscroll = () => {
this.loadMore();
};
//加载更多
loadMore = () => {
const {form: {getFieldValue}, chechData} = this.props;
let tempTotal = 0,page;
for (let index in chechData.groups) {
tempTotal += chechData.groups[index].homework.length;
}
page = Math.floor(tempTotal/10) + 1;
this.isVisible = this.isMoreVisible("#loadMore");
if (this.isVisible) {
let homewordParas: any = {
page: page,
pageSize: this.state.pageSize,
subjectId: this.props.subjectId,
sessionId: getFieldValue('classId')?getFieldValue('classId'):null,
timeRangeCode: getFieldValue('fromDate')?(getFieldValue('fromDate') === '近两周'?'TWO_WEEKS':getFieldValue('fromDate')):null,
assessmentTitle:getFieldValue('homeworkName')?getFieldValue('homeworkName'):null,
};
if (tempTotal < this.props.chechData.total ) {
this.setState({
page: homewordParas.page
});
this.props.getHttpCheckData(homewordParas);
} else {
this.props.chechData? (this.refs.loadMore as any).innerHTML = "已没有更多作业!":(this.refs.loadMore as any).innerHTML = "";
}
window.onscroll = null;
}
};
62.HTML5 的 Audio 标签
到今天为止,大多数的音频文件播放,是通过 Flash 来实现的。而 HTML5 定义了一个新元素「audio」,在播放音频上为我们提供了很多方便的功能。
在浏览器的支持上,
audio对象的方法和属性:
我们可以动态把一个audio元素插入到页面中,从而通过 JS 来获取这个对象,简单的方法如下:
事件:
63. addEventListener 用法 (https://www.cnblogs.com/Andyudd/p/5583563.html)
addEventListener 用于注册事件处理程序,IE 中为 attachEvent,我们为什么讲 addEventListener 而不讲 attachEvent 呢?一来 attachEvent 比较简单,二来 addEventListener 才是 DOM 中的标准内容。
addEventListener 为文档节点、document、window 或 XMLHttpRequest 注册事件处理程序,在以前我们一般是
语法:
target.addEventListener(type, listener, useCapture);
- target 文档节点、document、window 或 XMLHttpRequest。
- type 字符串,事件名称,不含“on”,比如“click”、“mouseover”、“keydown”等。
- listener 实现了 EventListener 接口或者是 JavaScript 中的函数。
- useCapture 是否使用捕捉,看了后面的事件流一节后就明白了,一般用 false。
64.addEventListener-事件流
当一个事件发生时,分为三个阶段:
捕获阶段-- 从根节点开始顺序而下,检测每个节点是否注册了事件处理程序。如果注册了事件处理程序,并且 useCapture 为 true,则调用该事件处理程序。(IE 中无此阶段。)
目标阶段-- 触发在目标对象本身注册的事件处理程序,也称正常事件派发阶段。
冒泡阶段-- 从目标节点到根节点,检测每个节点是否注册了事件处理程序,如果注册了事件处理程序,并且 useCapture 为 false,则调用该事件处理程序。
举例
如果在 d3 上点击鼠标,事件流是这样的:
捕获阶段 在 div1 处检测是否有 useCapture 为 true 的事件处理程序,若有,则执行该程序,然后再同样地处理 div2。
目标阶段 在 div3 处,发现 div3 就是鼠标点击的节点,所以这里为目标阶段,若有事件处理程序,则执行该程序,这里不论 useCapture 为 true 还是 false。
冒泡阶段 在 div2 处检测是否有 useCapture 为 false 的事件处理程序,若有,则执行该程序,然后再同样地处理 div1。
注意,上述捕获阶段和冒泡阶段中,实际上 div1 之上还应该有结点,比如有 body,但这里不讨论。
65. addEventListener-第三个参数 useCapture
Andyudd
addEventListener 用法
addEventListener 用于注册事件处理程序,IE 中为 attachEvent,我们为什么讲 addEventListener 而不讲 attachEvent 呢?一来 attachEvent 比较简单,二来 addEventListener 才是 DOM 中的标准内容。
- 简介
addEventListener 为文档节点、document、window 或 XMLHttpRequest 注册事件处理程序,在以前我们一般是
2.语法
target.addEventListener(type, listener, useCapture);
- target 文档节点、document、window 或 XMLHttpRequest。
- type 字符串,事件名称,不含“on”,比如“click”、“mouseover”、“keydown”等。
- listener 实现了 EventListener 接口或者是 JavaScript 中的函数。
- useCapture 是否使用捕捉,看了后面的事件流一节后就明白了,一般用 false。
3.示例
function Go()
{
//...
}
document.getElementById("testButton").addEventListener("click", Go, false);
或者 listener 直接就是函数
document.getElementById("testButton").addEventListener("click", function () { ... }, false);
addEventListener-事件流
说到 addEventListener 不得不说到事件流,先说事件流对后面的解释比较方便。
当一个事件发生时,分为三个阶段:
捕获阶段 从根节点开始顺序而下,检测每个节点是否注册了事件处理程序。如果注册了事件处理程序,并且 useCapture 为 true,则调用该事件处理程序。(IE 中无此阶段。)
目标阶段 触发在目标对象本身注册的事件处理程序,也称正常事件派发阶段。
冒泡阶段 从目标节点到根节点,检测每个节点是否注册了事件处理程序,如果注册了事件处理程序,并且 useCapture 为 false,则调用该事件处理程序。
举例
如果在 d3 上点击鼠标,事件流是这样的:
捕获阶段 在 div1 处检测是否有 useCapture 为 true 的事件处理程序,若有,则执行该程序,然后再同样地处理 div2。
目标阶段 在 div3 处,发现 div3 就是鼠标点击的节点,所以这里为目标阶段,若有事件处理程序,则执行该程序,这里不论 useCapture 为 true 还是 false。
冒泡阶段 在 div2 处检测是否有 useCapture 为 false 的事件处理程序,若有,则执行该程序,然后再同样地处理 div1。
注意,上述捕获阶段和冒泡阶段中,实际上 div1 之上还应该有结点,比如有 body,但这里不讨论。
addEventListener-第三个参数 useCapture
addEventListener 有三个参数:第一个参数表示事件名称(不含 on,如 "click");第二个参数表示要接收事件处理的函数;第三个参数为 useCapture,本文就讲解它。
请在此点击鼠标。
var outDiv = document.getElementById("outDiv");
var middleDiv = document.getElementById("middleDiv");
var inDiv = document.getElementById("inDiv");
var info = document.getElementById("info");
outDiv.addEventListener("click", function () { info.innerHTML += "outDiv" + "
"; }, false);
middleDiv.addEventListener("click", function () { info.innerHTML += "middleDiv" + "
"; }, false);
inDiv.addEventListener("click", function () { info.innerHTML += "inDiv" + "
"; }, false);
上述是我们测试的代码,根据 info 的显示来确定触发的顺序,有三个 addEventListener,而 useCapture 可选值为 true 和 false,所以 222,可以得出 8 段不同的程序。
全为 false 时,触发顺序为:inDiv、middleDiv、outDiv;
全为 true 时,触发顺序为:outDiv、middleDiv、inDiv;
outDiv 为 true,其他为 false 时,触发顺序为:outDiv、inDiv、middleDiv;
middleDiv 为 true,其他为 false 时,触发顺序为:middleDiv、inDiv、outDiv;
……
最终得出如下结论:true 的触发顺序总是在 false 之前;
如果多个均为 true,则外层的触发先于内层;
如果多个均为 false,则内层的触发先于外层。
66.react中样式通过className引入
①className={style["simple-audio-choice-wrap"]}
②className={style.ors_row_pc_list}
第一种方法的格式是因为样式名称使用了“-”,如果用第二种引入会报错
67.className引入多个样式
className={${style.a} ${style.b}
}
68.pop() 方法将删除 arrayObject 的最后一个元素,把数组长度减 1,并且返回它删除的元素的值。如果数组已经为空,则 pop() 不改变数组,并返回 undefined 值。
69.在package.json文件的webpack-dev-server.js 加--host 0.0.0.0,启动项目,可以使用手机输入电脑的ip对本地项目进行访问。这个配置的含义是,既可以再本地用http://localhost/yoursite的url访问项目,也可以用http://yourip/yoursite访问项目。
比如你的计算机IP地址是192.168.1.10,项目名称是app,那么你的访问url就可以是http://localhost/app,或者http://192.168.1.10/app。
这样设置的好处是,当你想用非本机访问项目进行测试时,由ip地址构建的url可以对项目进行访问,同时本地还可以用localhost进行访问。如果你将ip写死了,那么localhost就无法访问了;
70.reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
reduce() 可以作为一个高阶函数,用于函数的 compose。
注意: reduce() 对于空数组是不会执行回调函数的。
71.new Date().toLocaleString() toLocaleString() 方法可根据本地时间把 Date 对象转换为字符串,并返回结果。2018/3/19 下午8:45:14
72.cordova plugin add cordova-plugin-statusbar
Android 5.0以下 支持的接口比较少show()和hide() 等
1. StatusBar.isVisible 是否已经显示
2. StatusBar.hide(); 隐藏状态栏
3. StatusBar.show(); 显示状态栏
Android 5.0以上,支持设置背景
使用十六进制设置背景:
StatusBar.backgroundColorByHexString("#C0C0C0");
StatusBar.backgroundColorByHexString("#333"); // => #333333
StatusBar.backgroundColorByHexString("#FAB"); // => #FFAABB
使用名称设置背景:
StatusBar.backgroundColorByName("red");
document.addEventListener("deviceready", () => {
// set statusbar background color
if (cordova.platformId == 'android') {
StatusBar.backgroundColorByHexString("#f9f9f9");
}
}
73.typescript中使用Form报错但不影响运行
先这样写吧,这个类型暂时有点搞不定。
Form.create
74.改变当前组件的state值用setState,想改变props里面的值怎么做:
①当前组件如果是 connect 到 redux 就通过 dispatch 改。
第一: 就是 dispatch 一个 action 在 reduce 里面修改 store 的 state
第二:再组件加载完数据之后,把 props 的数据 放到组件的 state 里面,组件怎么变,就用setState 去让组件重新渲染。可以在
componentWillReceiveProps函数中把props 的数据 放到组件的 state中
②当前组件如果是 某个父组件 传递的 props 组件 的子组件, 子组件内部可以调用 this.props.onChange 通过事件的方式 把新的 props 传给父组件
然后父亲组件去改。Input 显示的值value就是 通过 props 传进去的,然后 Input 的值更新。需要改变 props的 value是直接通过 onChange 通知父组件 改变
75.Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。如果目标对象中的属性具有相同的键,则属性将被源中的属性覆盖。后来的源的属性将类似地覆盖早先的属性,但是Object.assign 不会跳过那些值为 null 或 undefined 的源对象
76.hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性
77.在saga文件里面获取store上的数据:
①可以通过select方法获取,如const cartData = yield select((state: any) => state.homeworkCartReducer.cartData);
②(window as GlobalDefinitions).store.getState().homeworkCenterReducer.subjectId