React开发问题总结(持续更新...) | 您所在的位置:网站首页 › react下载文件文件内容乱码 › React开发问题总结(持续更新...) |
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、图解四个函数的不同: 可根据世界时(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.StoreStore 是保存数据的地方,可以把它看成一个容器,整个应用只能有一个 Store。Redux 提供createStore这个函数来生成 Store。 import { createStore } from 'redux'; const store = createStore(fn);上面代码中,createStore函数接受另一个函数作为参数,返回新生成的 Store 对象。 13.StateStore对象包含所有数据。如果想得到某个时点的数据,就要对 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实现 解决方法: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}) ; } { this.props.studentList !== undefined && this.props.studentList!=="" && this.props.studentList.map((item: any) => {item.firstName} / {item.cellNumber} ) } 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]+ 在点击按钮请求登录接口之前将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. 返回 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:'学生不能为空!',}, ], })( { studentList !== undefined && studentList!=="" && studentList.map((item: any, i: any) => {item.firstName} / {item.cellNumber} {item.school} ) } )} 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 [ 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)修改某一处为错误,然后观察结果 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出现在输出中,你必须先将它转换为字符串: 在字符串文本中间出现的空行会缩合成一个空格。 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」,在播放音频上为我们提供了很多方便的功能。 在浏览器的支持上, 标签目前只支持 Internet Explorer 9+, Firefox, Opera, Chrome 和 Safari。也就是说,IE 9 以下是不支持的。 标签属性: * src: 要播放的音频的 URL。 * preload: 是否预加载,如果使用 "autoplay",则忽略该属性。 * autoplay: 是否自动播放。 * loop: 是否循环播放。 * controls: 是否显示浏览器自带的控制条,例如播放按钮。 html的写法: audio对象的方法和属性: 我们可以动态把一个audio元素插入到页面中,从而通过 JS 来获取这个对象,简单的方法如下: 事件: addEventListener 用于注册事件处理程序,IE 中为 attachEvent,我们为什么讲 addEventListener 而不讲 attachEvent 呢?一来 attachEvent 比较简单,二来 addEventListener 才是 DOM 中的标准内容。 addEventListener 为文档节点、document、window 或 XMLHttpRequest 注册事件处理程序,在以前我们一般是 #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()(LoginForm) 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 78.map只能遍历数组,其他用for...in...,或者排除掉那个非枚举属性再mapdefer 属性规定是否对脚本执行进行延迟,直到页面加载为止。 有的 javascript 脚本 document.write 方法来创建当前的文档内容,其他脚本就不一定是了。 如果您的脚本不会改变文档的内容,可将 defer 属性加入到 标签中,以便加快处理文档的速度。因为浏览器知道它将能够安全地读取文档的剩余部分而不用执行脚本,它将推迟对脚本的解释,直到文档已经显示给用户为止。 83.css怎么找到下面的ant-menu-item-selecte这个class? ①.main_left_content :global(.ant-menu-item-selected) ②.main_left_content>:global(.ant-menu-item-selected) 注意:>隔开表示 子集(向下一层);空格 隔开表示 子集或子孙集 (向下多层,范围更广) 84.&.el-row { margin-bottom: 20px; &:last-child { margin-bottom: 0; } } 注意:& 代表上一级选择器,实际编译成css就是 .el-row:last-child{} 85.覆盖antd样式不生效时,试着在需要覆盖的样式前加 :gloabalantd 的类都要加上 global ,要不然会被 webpack css modules 转换 class 的字符串 86.css选择器①将一个div设成间距,间距的颜色为div的背景色 ②给其中一个div设置border,border的宽度即为间距的宽度 ③利用box-shadow,将第一个和第三个参数都设置为0,第二个参数为间距的宽度,注意:同时要给margin值,投影到top方向就要给margin-top delete 操作符用于删除对象的某个属性;如果没有指向这个属性的引用,那它最终会被释放 ①.difference(array,[values]) 两个参数都是数组,array:要被检查/过滤的数组, array:要被检查/过滤的数组 ② .drop(array,number) 类似于原生js方法中的slice。从头开始删除number个数组元素。number不传的话默认按1处理 ③.without(array, [values]) 不同于difference方法。其values参数可以不是一个数组,而是接在array参数之后的零散参数。 ④.remove(array, [predicate=_.identity], [thisArg]) (对原数组操作) 第二个参数为function ⑤ .pull(array, [values]) (对原数组操作) (1)使用float .use-float>div:first-child{ width:100px; float:left;}.use-float>div:last-child{ overflow:hidden;}(2)使用table .use-table{ border-collapse:collapse; width:100%;}.use-table>tbody>tr>td:first-child{ width:100px;}(3)用div模拟table .use-mock-table{ display:table; width:100%;}.use-mock-table>div{ display:table-cell;}.use-mock-table>div:first-child{ width:100px;}(4)使用flex .use-flex{ display:flex;}.use-flex>div:first-child{ flex:none; width:100px;}.use-flex>div:last-child{ flex:1;} 94.数组去重① Set是ES6中新的对象。 利用它可以迅速为数组去重 简单来说,Set于Array的区别在于:Array中允许出现重复的元素,例如[1,2,2,3];而Set中的所有元素都是唯一的,只能是{1,2,3}。利用这一特性,我们就可以迅速地去掉数组中重复的元素。 ②lodash uniq/unique 数组去重 ③lodash union数组合并去重 ①推荐在循环对象属性的时候,使用for...in,在遍历数组的时候的时候使用for...of。 ②for...in循环出的是key,for...of循环出的是value ③注意,for...of是ES6新引入的特性。修复了ES5引入的for...in的不足 ④for...of不能循环普通的对象,需要通过和Object.keys()搭配使用 https://www.limitcode.com/detail/59a15b1a69e95702e0780249.html 回顾 npm install 命令最近在写Node程序的时候,突然对 npm install 的-save和-save-dev 这两个参数的使用比较混乱。其实博主在这之前对这两个参数的理解也是模糊的,各种查资料和实践后对它们之间的异同点略有理解。遂写下这篇文章避免自己忘记,同时也给node猿友一点指引。 我们在使用 npm install 安装模块的模块的时候 ,一般会使用下面这几种命令形式: npm install moduleName # 安装模块到项目目录下 npm install -g moduleName # -g 的意思是将模块安装到全局,具体安装到磁盘哪个位置,要看 npm config prefix 的位置。 npm install -save moduleName # -save 的意思是将模块安装到项目目录下,并在package文件的dependencies节点写入依赖。 npm install -save-dev moduleName # -save-dev 的意思是将模块安装到项目目录下,并在package文件的devDependencies节点写入依赖。那么问题来了,在项目中我们应该使用四个命令中的哪个呢?这个就要视情况而定了。下面对这四个命令进行对比,看完后你就不再这么问了。 npm install moduleName 命令 安装模块到项目node_modules目录下。 不会将模块依赖写入devDependencies或dependencies 节点。 运行 npm install 初始化项目时不会下载模块。 npm install -g moduleName 命令 安装模块到全局,不会在项目node_modules目录中保存模块包。 不会将模块依赖写入devDependencies或dependencies 节点。 运行 npm install 初始化项目时不会下载模块。 npm install -save * * moduleName 命令 安装模块到项目node_modules目录下。 会将模块依赖写入dependencies 节点。 运行 npm install 初始化项目时,会将模块下载到项目目录下。 运行npm install --production或者注明NODE_ENV变量值为production时,会自动下载模块到node_modules目录中。 npm install -save-dev moduleName 命令 安装模块到项目node_modules目录下。 会将模块依赖写入devDependencies 节点。 运行 npm install 初始化项目时,会将模块下载到项目目录下。 运行npm install --production或者注明NODE_ENV变量值为 production时,不会自动下载模块到node_modules目录中。总结 devDependencies 节点下的模块是我们在开发时需要用的,比如项目中使用的 gulp ,压缩css、js的模块。这些模块在我们的项目部署后是不需要的,所以我们可以使用 -save-dev 的形式安装。像 express 这些模块是项目运行必备的,应该安装在 dependencies 节点下,所以我们应该使用 -save 的形式安装。 96.XML DOM - XMLHttpRequest 对象同构应用:什么是前后端同构呢?就是前后端都可以使用同一套代码生成页面,页面既可以由前端动态生成,也可以由后端服务器直接渲染出来 单页面应用:就是只有一张Web页面的应用。单页应用程序 (SPA) 是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。 浏览器一开始会加载必需的HTML、CSS和JavaScript,所有的操作都在这张页面上完成,都由JavaScript来控制。直白一点就是只有一个html文件的应用。react,vue等做的都是单页面的 ① 要想更新 state 中的数据,你需要发起一个 action ②把 action 和 state 串起来,开发一些函数,这就是 reducer。reducer 只是一个接收 state 和 action,并返回新的 state 的函数 Redux三大原则 ①单一数据源: 整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。 可通过store.getState()取store上的数据 ②State 是只读的:唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。 ③使用纯函数来执行修改:为了描述 action 如何改变 state tree ,你需要编写 reducers。 99.在saga中使用setTimeouthttps://redux-saga-in-chinese.js.org/docs/api/index.html put 就是我们所说的一个调用 Effect 的例子 call 就像 put, 返回一个指示 middleware 以给定参数调用给定的函数的 Effect。 cancel 为了取消 fork 任务,使用的一个指定的 Effect 。 yield* 操作符来组合多个 Sagas,使得它们保持顺序。 这让你可以一种简单的程序风格来排列你的 宏观任务(macro-tasks)。 credentials: 'include' : 默认情况下,标准的跨域请求是不会发送cookie等用户认证凭据的。所以,当你访问远程api的时候,cookie是不会被带上的。【 当这个属性为true的时候,远程服务器也要作相应的处理。在响应头那里设置 Access-Control-Allow-Credentials: true 】 102.previousSibling属性返回元素节点之前的兄弟节点(包括文本节点、注释节点);previousElementSibling属性只返回元素节点之前的兄弟元素节点(不包括文本节点、注释节点); 103.前端文件下载 ①标签:下载 txt,png,jpg等这些浏览器支持直接打开的文件是不会执行下载任务的,而是会直接打开文件,这个时候就需要给a标签添加一个属性“download”; ②window.location.href = "..." 104.图片预览 使用106.对象中数组对象去重 1)import _ from "lodash"; for (let index in checkData.groups) { let homework = _.uniqWith(checkData.groups[index].homework, _.isEqual); checkData.groups[index].homework = homework; }2) /** * * @param {*} list 数组 * @param {*} fields 去重字段 */ function uniqeItem(list:any, fields:any){ function getUniquKey(item:any, fields:any){ let length = fields.length, key = []; while(length--){ key.push(item[fields[length]]); } return key.join('|'); } let newList = []; let keyMap = new Map(); for (let i = 0, j = list.length; i < j; i++) { let key = getUniquKey(list[i], fields); if (!keyMap.get(key)) { keyMap.set(key, true); newList.push(list[i]); } } return newList ; } uniqeItem([{a:1,b:2},{a:1,b:2}], ['a','b']); 107.8到20位的可输入半角符号,但不含空格并且至少包含一个数字和字母 /^(?=.*[0-9])(?=.*[a-zA-Z])[\x01-\x1f|\x21-\xff]{8,20}$/ 108.控制台报错解决1.message全局提示报错,之前以为是DatePicker组件的校验问题 定位问题到message组件:①通过下面的created by notification;createed by animate;②通过保存函数,应该弹出提示信息,但是没有弹出 2.Redirect to属性报错 定位问题:通过 '>' expected可以想到>是html片段符号,而文件后缀是ts,也就是文件类型应该是tsx。所以把文件后缀改为tsx,问题解决 3.DatePicker组件总是显示成英文 问题解决: 1.父组件将form传给子组件,子组件使用父组件传下来的form 2.使用官网API ①在父组件ExamCreation中不使用Form 1.Pagination组件显示成英文 解决方法: ①引入antd国际化组件LocaleProvider 和需要的语言包 ②用国际化组件将分页组件包裹起来 2.DatePicker组件显示成英文 解决方法:除了上面使用LocaleProvider 组件解决,DatePicker组件有自带的语言包 Redirect是一个组件,将组件嵌套在ts代码中,文件类型要改为tsx 113.浏览器在传递url的时候,会使用自己的编码格式对地址进行编码,如果浏览器所使用编码与服务器采用编码不一致,服务器接收到的参数就会出现乱码。在firefox,chrome下正常,ie下会出现乱码。解决方法:使用js encodeURI 对地址进行统一编码,encodeURI("article/detail?title=我是中文"); 114.阻止冒泡:e.stopPropagation(),IE使用e.cancelBubble = true if (e && e.stopPropagation) { e.stopPropagation(); } else if (window.event) { window.event.cancelBubble = true; } 115.日期格式转时间戳1.Date.parse(date) (注意浏览器之间的区别、兼容) IE: 注意:两种方法在三个浏览器都能起作用的日期格式是 / 分割的,所以要使用该方法的话使用 .replace()转化,如IE下的: https://blog.csdn.net/wulex/article/details/80402036 117.修改滚动条默认样式 ::-webkit-scrollbar { width: 6px; //height: 16px; background-color: #F5F5F5; } /*定义滚动条轨道 内阴影+圆角*/ ::-webkit-scrollbar-track { //-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); opacity: 0.5; border-radius: 10px; background-color: #F5F5F5; } /*定义滑块 内阴影+圆角*/ ::-webkit-scrollbar-thumb { border-radius: 10px; //-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3); background-color: #dddddd; } 118.Dva中container中的js文件第1,2个customer是在model中的命名空间,第3,4个是同一个 119.Classnames根据状态值动态添加或去除class. npm install classnames classNames('foo', 'bar'); // => 'foo bar' classNames('foo', { bar: true }); // => 'foo bar' classNames({ 'foo-bar': true }); // => 'foo-bar' classNames({ 'foo-bar': false }); // => '' classNames({ foo: true }, { bar: true }); // => 'foo bar' classNames({ foo: true, bar: true }); // => 'foo bar'可以看到键值为true的就返回键名,可以利用这个方法来动态控制键值的true/fale变化,从而控制是否返回键。(默认是返回的) // ES5 let buttonType = 'primary'; classNames({ [`btn-${buttonType}`]: true });在React中使用 var Button = React.createClass({ // ... render () { var btnClass = 'btn'; //根据点击的state来控制css if (this.state.isPressed) btnClass += ' btn-pressed'; else if (this.state.isHovered) btnClass += ' btn-over'; return {this.props.label}; } }); var classNames = require('classnames'); var Button = React.createClass({ // ... render () { var btnClass = classNames({ 'btn': true, 'btn-pressed': this.state.isPressed, 'btn-over': !this.state.isPressed && this.state.isHovered }); return {this.props.label}; } });如果是name和className进行了映射,可以使用bind方法 /* components/submit-button.js */ import { Component } from 'react'; import classNames from 'classnames/bind'; import styles from './submit-button.css'; let cx = classNames.bind(styles); export default class SubmitButton extends Component { render () { let text = this.props.store.submissionInProgress ? 'Processing...' : 'Submit';//text根据状态来动态加载 let className = cx({ base: true, inProgress: this.props.store.submissionInProgress,//样式的动态加载 error: this.props.store.errorOccurred, disabled: this.props.form.valid, }); return {text}; } }; 120.path-to-regexp使用path-to-regexp,我们可以在路径字符串中使用正则。如/:foo*/:bar?、/icon-:foo(\d+).png等。 直接调用构造函数使用,一个可能含某种匹配模式的路径字符串作为它的必选参数,它返回一个正则对象。 const pathToRegexp = require('path-to-regexp') var regexp_1 = pathToRegexp('/foo/:bar')// /^\/foo\/([^\/]+?)(?:\/)?$/i regexp_1.exec('/foo/barrrr')//匹配成功 =>RegExpExecArray [ '/foo/barrrr', 'barrrr', index: 0, input: '/foo/barrrr' ] regexp_1.exec('/bazzzz')//匹配失败 => null/foo/:bar中的/为分隔符,把多个匹配模式分隔开,这里就分成foo和:bar。像foo这种不带:前缀的,我们请求的路径需要和它完全匹配,而:bar这种,叫命名参数,就像个函数形参,可以传递任何请求路径字串给它。 在命名参数上,我们可以使用参数修饰符作为其后缀,有?、+、* “*”:表示我这个命名参数:bar可以接收随意个匹配模式,就好像参数数组长度[0,+∞) var regexp_2 = pathToRegexp('/foo/:bar*') regexp_2.exec('/foo/a/b/c')// => [ '/foo/a/b/c', 'a/b/c', index: 0, input: '/foo/a/b/c' ] regexp_2.exec('/foo')// => [ '/foo', undefined, index: 0, input: '/foo' ] “+”: 表示命名参数可以接收至少一个匹配模式,一个都没就匹配失败,[1,+∞) var regexp_3 = pathToRegexp('/foo/:bar+') regexp_3.exec('/foo/a/b/c')// => [ '/foo/a/b/c', 'a/b/c', index: 0, input: '/foo/a/b/c' ] regexp_3.exec('/foo')//匹配失败 => null “? ”:表示命名参数可以接收0个或1个匹配模式,多个失败,[0,1] var regexp_4 = pathToRegexp('/foo/:bar?') regexp_4.exec('/foo/a')// => [ '/foo/a', 'a', index: 0, input: '/foo/a' ] regexp_4.exec('/foo/a/b/c')// => null regexp_4.exec('/foo')// => [ '/foo', undefined, index: 0, input: '/foo' ]我们还可以为命名参数加上自定义的正则匹配模式 // 以下设置表示:foo只能是数字。 var regexp_5 = pathToRegexp('/icon-:foo(\\d+).png') regexp_5.exec('/icon-123.png')// => [ '/icon-123.png', '123', index: 0, input: '/icon-123.png' ] regexp_5.exec('/icon-abc.png')// null //不需要命名参数这个占位符,通过正则就能就能匹配。 var regexp_5 = pathToRegexp('/icon-(\\d+).png') regexp_5.exec('/icon-123.png')// => [ '/icon-123.png', '123', index: 0, input: '/icon-123.png' ] regexp_5.exec('/icon-abc.png')// null 121. Form 提交需注意把提交函数写在Form上会导致:表单提交后在地址栏会添一个“?”,界面刷新 Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象。“` @param: obj:需要定义属性的对象; prop:需要定义或修改的属性; descriptor:将被定义或修改属性的描述符对象里目前存在的属性描述符主要有两种形式: 数据描述符和存取描述符. 125.不更新的原因:更新src后,如果src与原来的相同,则浏览器回从缓存里获取图片而不会向后台发送新的请求 解决办法:在src之后加上一些没有意义的随机参数比如链接上“?time=new Date().getTime()”即获取当前时间的时间戳,这是浏览器会认为这是不同的url因此会重新发送请求加载新的图片。 改成如下样子会导致新的问题:每次进入界面都请求好几次 多次请求原因:props中有些变量值改变导致重新render,如loading由false变成true又变成false 解决方法:将url后的随机变量的值从models中传过来 特点: Provider 和 Consumer 必须来自同一次 React.createContext 调用。也就是说 NameContext.Provider 和 AgeContext.Consumer 是无法搭配使用的。 React.createContext 方法接收一个默认值作为参数。当 Consumer 外层没有对应的 Provider 时就会使用该默认值。 Provider 组件的 value prop 值发生变更时,其内部组件树中对应的 Consumer 组件会接收到新值并重新执行 children 函数。此过程不受 shouldComponentUpdete 方法的影响。前面的示例代码中,Hello 组件继承自 React.PureComponent 但页面依然能正确显示足以说明这一点。 Provider 组件利用 Object.is 检测 value prop 的值是否有更新。注意 Object.is 和 === 的行为不完全相同。具体细节请参考 Object.is 的 MDN 文档页。 Consumer 组件接收一个函数作为 children prop 并利用该函数的返回值生成组件树的模式被称为 Render Props 模式。详细介绍请参考相关 React 文档 127.在models层获取全局状态树数据 Redux下:const state =(window as GlobalDefinitions).store.getState().namespaceName Dva中: const state = yield select(st => st.namespaceName); namespaceName为要获取的那个状态树的命名空间 128.antd Table复选框选中控制选中控制一定要通过selectedRowKeys,不然会出现选中的key是空时,界面上还有勾 rowSelection={{ selectedRowKeys: selectedRows.map(i => i.docItemId), onChange: this.handleRowSelect, }} 129. 重新部署后页面js文件报404现象:react项目发布后, 需要用户手动刷新浏览器才能正常运行。有md5值, js每次更改代码后, 打包出的hash值也是不同的, 但用户必须刷新页面缓存才能正常使用 分析:检查 index.html 有没有在 Nginx 设置缓存;检查项目的 vendor.js common.js 是否有hash。 index.html被缓存了,被缓存的index.html引用的是旧版js、css资源,这些资源也被浏览器缓存过,所以加载的是上次访问的页面。 解决方法:修改下nginx配置就可以了 location / { index index.html; if ($uri ~* "html$") { add_header Cache-Control "no-cache, no-store, max-age=0, must-revalidate"; } }https://segmentfault.com/q/1010000007320935 130.查找nginx配置文件所在位置 whereis nginx --查看nginx安装路径 which nginx --查看nginx运行路径 /usr/sbin/nginx -t --在知晓了运行路径为/usr/sbin/nginx条件下,查看nginx配置文件的位置。nginx.conf、default.conf 131. 修改本地启动端口环境变量 PORT,在 compileStartEnv 里面。 如果没有这个文件,就在 package.json 里面 如果需要保存 就是 写在 环境变量的 地方; 如果不需要保存; 直接设置 当前环境变量就好了。 export PORT=8001 && yarn start // linux set PORT=8001 && yarn start // windows 132. pointer-events: none;指定在什么情况下 (如果有) 某个特定的图形元素可以成为鼠标事件 |
CopyRight 2018-2019 实验室设备网 版权所有 |