首页
统计
墙纸
留言
Search
1
PVE8优化
19 阅读
2
jenkins根据分支、文件夹打包
13 阅读
3
mysql创建数据库
12 阅读
4
vue-cli注册全局方法
7 阅读
5
开心的加班
7 阅读
web前端
Vue
CSS
javascript
React
那些年爬过过的坑
ES6
TypeScrippt
ES7
javascript图灵 - 总结
Node
面试总结
React-Native
Web优化
基础
AngularJS
拍摄
Flutter
Dart
Docker
Linux
mysql
PVE
登录
/
注册
Search
标签搜索
vue+elementui
Cicaba
累计撰写
146
篇文章
累计收到
13
条评论
首页
栏目
web前端
Vue
CSS
javascript
React
那些年爬过过的坑
ES6
TypeScrippt
ES7
javascript图灵 - 总结
Node
面试总结
React-Native
Web优化
基础
AngularJS
拍摄
Flutter
Dart
Docker
Linux
mysql
PVE
页面
统计
墙纸
留言
搜索到
137
篇与
的结果
2017-12-04
react的事件合成
在开发react项目中遇见在调用事件函数里的异步方法访问事件属性时报错.官方给出了解决方法.如果要以异步方式访问事件属性,应该对事件调用 event.persist() ,这将从池中删除合成事件,并允许用户代码保留对事件的引用。
2017年12月04日
0 阅读
0 评论
0 点赞
2017-11-29
那个坑爹的toString
在开发过程中,修改别人的BUG时发现报错无数,来看他代码if (curValue == null || curValue.toString().trim() == "") { this.queryParameter.dicCustomerTrans = ""; }我就想问了curValue=null,时会不会报错?再来let vehicleCode=this.detail.Vehicle_Number; if(vehicleCode!=null){ vehicleCode=vehicleCode.substring(0,vehicleCode.indexOf('_')); } 当vehicleCode=undefined时的会不会报错?总结在上面两种情况下都必须是字符串类型才能调用trim(),substring(),indexOf().在无法确认类型时,不要使用以上方法.还可以转换类型,String(),toString(),前者为类型强转,后者为Obj原型链上的方法,不可转null,undefined,NaN等
2017年11月29日
0 阅读
0 评论
0 点赞
2017-11-28
Redux
§ Store首先要区分 store 和 statestate 是应用的状态,一般本质上是一个普通对象例如,我们有一个 Web APP,包含 计数器 和 待办事项 两大功能那么我们可以为该应用设计出对应的存储数据结构(应用初始状态): /** 应用初始 state,本代码块记为 code-1 **/ { counter: 0, todos: [] }store 是应用状态 state 的管理者,包含下列四个函数:getState() # 获取整个 state dispatch(action) # ※ 触发 state 改变的【唯一途径】※ subscribe(listener) # 您可以理解成是 DOM 中的 addEventListener replaceReducer(nextReducer) # 一般在 Webpack Code-Splitting 按需加载的时候用 二者的关系是:state = store.getState()Redux 规定,一个应用只应有一个单一的 store,其管理着唯一的应用状态 stateRedux 还规定,不能直接修改应用的状态 state,也就是说,下面的行为是不允许的:var state = store.getState()state.counter = state.counter + 1 // 禁止在业务逻辑中直接修改 state若要改变 state,必须 dispatch 一个 action,这是修改应用状态的不二法门现在您只需要记住 action 只是一个包含 type 属性的普通对象即可例如 { type: 'INCREMENT' }上面提到,state 是通过 store.getState() 获取,那么 store 又是怎么来的呢?想生成一个 store,我们需要调用 Redux 的 createStore:import { createStore } from 'redux' ... const store = createStore(reducer, initialState) // store 是靠传入 reducer 生成的哦!现在您只需要记住 reducer 是一个 函数,负责更新并返回一个新的 state而 initialState 主要用于前后端同构的数据同步(详情请关注 React 服务端渲染)§ Action上面提到,action(动作)实质上是包含 type 属性的普通对象,这个 type 是我们实现用户行为追踪的关键例如,增加一个待办事项 的 action 可能是像下面一样:/** 本代码块记为 code-2 **/ { type: 'ADD_TODO', payload: { id: 1, content: '待办事项1', completed: false } }当然,action 的形式是多种多样的,唯一的约束仅仅就是包含一个 type 属性罢了也就是说,下面这些 action 都是合法的:/** 如下都是合法的,但就是不够规范 **/ { type: 'ADD_TODO', id: 1, content: '待办事项1', completed: false } { type: 'ADD_TODO', abcdefg: { id: 1, content: '待办事项1', completed: false } }虽说没有约束,但最好还是遵循规范如果需要新增一个代办事项,实际上就是将 code-2 中的 payload “写入” 到 state.todos 数组中(如何“写入”?在此留个悬念):/** 本代码块记为 code-3 **/ { counter: 0, todos: [{ id: 1, content: '待办事项1', completed: false }] }刨根问底,action 是谁生成的呢?⊙ Action CreatorAction Creator 可以是同步的,也可以是异步的顾名思义,Action Creator 是 action 的创造者,本质上就是一个函数,返回值是一个 action(对象)例如下面就是一个 “新增一个待办事项” 的 Action Creator:/** 本代码块记为 code-4 **/ var id = 1 function addTodo(content) { return { type: 'ADD_TODO', payload: { id: id++, content: content, // 待办事项内容 completed: false // 是否完成的标识 } } }将该函数应用到一个表单(假设 store 为全局变量,并引入了 jQuery ):<--! 本代码块记为 code-5 --> <input type="text" id="todoInput" /> <button id="btn">提交</button> <script> $('#btn').on('click', function() { var content = $('#todoInput').val() // 获取输入框的值 var action = addTodo(content) // 执行 Action Creator 获得 action store.dispatch(action) // 改变 state 的不二法门:dispatch 一个 action!!! }) </script> 在输入框中输入 “待办事项2” 后,点击一下提交按钮,我们的 state 就变成了: /** 本代码块记为 code-6 **/ { counter: 0, todos: [{ id: 1, content: '待办事项1', completed: false }, { id: 2, content: '待办事项2', completed: false }] }通俗点讲,Action Creator 用于绑定到用户的操作(点击按钮等),其返回值 action 用于之后的 dispatch(action)刚刚提到过,action 明明就没有强制的规范,为什么 store.dispatch(action) 之后,Redux 会明确知道是提取 action.payload,并且是对应写入到 state.todos 数组中?又是谁负责“写入”的呢?悬念即将揭晓...§ ReducerReducer 必须是同步的纯函数用户每次 dispatch(action) 后,都会触发 reducer 的执行reducer 的实质是一个函数,根据 action.type 来更新 state 并返回 nextState最后会用 reducer 的返回值 nextState 完全替换掉原来的 state注意:上面的这个 “更新” 并不是指 reducer 可以直接对 state 进行修改Redux 规定,须先复制一份 state,在副本 nextState 上进行修改操作例如,可以使用 lodash 的 cloneDeep,也可以使用 Object.assign / map / filter/ ... 等返回副本的函数在上面 Action Creator 中提到的 待办事项的 reducer 大概是长这个样子 (为了容易理解,在此不使用 ES6 / Immutable.js):/** 本代码块记为 code-7 **/ var initState = { counter: 0, todos: [] } function reducer(state, action) { // ※ 应用的初始状态是在第一次执行 reducer 时设置的 ※ if (!state) state = initState switch (action.type) { case 'ADD_TODO': var nextState = _.cloneDeep(state) // 用到了 lodash 的深克隆 nextState.todos.push(action.payload) return nextState default: // 由于 nextState 会把原 state 整个替换掉 // 若无修改,必须返回原 state(否则就是 undefined) return state } }通俗点讲,就是 reducer 返回啥,state 就被替换成啥§ 总结store 由 Redux 的 createStore(reducer) 生成state 通过 store.getState() 获取,本质上一般是一个存储着整个应用状态的对象action 本质上是一个包含 type 属性的普通对象,由 Action Creator (函数) 产生改变 state 必须 dispatch 一个 actionreducer 本质上是根据 action.type 来更新 state 并返回 nextState 的函数reducer 必须返回值,否则 nextState 即为 undefined实际上,state 就是所有 reducer 返回值的汇总(本教程只有一个 reducer,主要是应用场景比较简单)Action Creator => action => store.dispatch(action) => reducer(state, action) => 原 state state = nextState⊙ Redux 与传统后端 MVC 的对照Redux 传统后端 MVC store 数据库实例 state 数据库中存储的数据 dispatch(action) 用户发起请求 action: { type, payload } type 表示请求的 URL,payload 表示请求的数据 reducer 路由 + 控制器(handler) reducer 中的 switch-case 分支 路由,根据 action.type 路由到对应的控制器 reducer 内部对 state 的处理 控制器对数据库进行增删改操作 reducer 返回 nextState 将修改后的记录写回数据库
2017年11月28日
0 阅读
0 评论
0 点赞
2017-11-26
JS获取各种宽高
IE中: document.body.clientWidth ==> BODY对象宽度 document.body.clientHeight ==> BODY对象高度 document.documentElement.clientWidth ==> 可见区域宽度 document.documentElement.clientHeight ==> 可见区域高度 FireFox中: document.body.clientWidth ==> BODY对象宽度 document.body.clientHeight ==> BODY对象高度 document.documentElement.clientWidth ==> 可见区域宽度 document.documentElement.clientHeight ==> 可见区域高度 Opera中: document.body.clientWidth ==> 可见区域宽度 document.body.clientHeight ==> 可见区域高度 document.documentElement.clientWidth ==> 页面对象宽度(即BODY对象宽度加上Margin宽) document.documentElement.clientHeight ==> 页面对象高度(即BODY对象高度加上Margin高) 没有定义W3C的标准,则 IE为: document.documentElement.clientWidth ==> 0 document.documentElement.clientHeight ==> 0 FireFox为: document.documentElement.clientWidth ==> 页面对象宽度(即BODY对象宽度加上Margin宽) document.documentElement.clientHeight ==> 页面对象高度(即BODY对象高度加上Margin高) Opera为: document.documentElement.clientWidth ==> 页面对象宽度(即BODY对象宽度加上Margin宽) document.documentElement.clientHeight ==> 页面对象高度(即BODY对象高度加上Margin高) 网页可见区域宽: document.body.clientWidth 网页可见区域高: document.body.clientHeight 网页可见区域宽: document.body.offsetWidth (包括边线的宽) 网页可见区域高: document.body.offsetHeight (包括边线的高) 网页正文全文宽: document.body.scrollWidth 网页正文全文高: document.body.scrollHeight 网页被卷去的高: document.body.scrollTop 网页被卷去的左: document.body.scrollLeft 网页正文部分上: window.screenTop 网页正文部分左: window.screenLeft 屏幕分辨率的高: window.screen.height 屏幕分辨率的宽: window.screen.width 屏幕可用工作区高度: window.screen.availHeight 屏幕可用工作区宽度: window.screen.availWidth HTML精确定位:scrollLeft,scrollWidth,clientWidth,offsetWidth scrollHeight: 获取对象的滚动高度。 scrollLeft:设置或获取位于对象左边界和窗口中目前可见内容的最左端之间的距离 scrollTop:设置或获取位于对象最顶端和窗口中可见内容的最顶端之间的距离 scrollWidth:获取对象的滚动宽度 offsetHeight:获取对象相对于版面或由父坐标 offsetParent 属性指定的父坐标的高度 offsetLeft:获取对象相对于版面或由 offsetParent 属性指定的父坐标的计算左侧位置 offsetTop:获取对象相对于版面或由 offsetTop 属性指定的父坐标的计算顶端位置 event.clientX 相对文档的水平座标 event.clientY 相对文档的垂直座标 event.offsetX 相对容器的水平坐标 event.offsetY 相对容器的垂直坐标 document.documentElement.scrollTop 垂直方向滚动的值 event.clientX+document.documentElement.scrollTop 相对文档的水平座标+垂直方向滚动的量
2017年11月26日
4 阅读
0 评论
0 点赞
2017-11-25
React-Router
一、基本用法React Router 安装命令如下。$ npm install -S react-router使用时,路由器Router就是React的一个组件。import { Router } from 'react-router'; render(<Router/>, document.getElementById('app'));Router组件本身只是一个容器,真正的路由要通过Route组件定义。import { Router, Route, hashHistory } from 'react-router'; render(( <Router history={hashHistory}> <Route path="/" component={App}/> </Router> ), document.getElementById('app')); 上面代码中,用户访问根路由/(比如http://www.example.com/),组件APP就会加载到document.getElementById('app')。 你可能还注意到,Router组件有一个参数history,它的值hashHistory表示,路由的切换由URL的hash变化决定,即URL的#部分发生变化。举例来说,用户访问http://www.example.com/,实际会看到的是http://www.example.com/#/。 Route组件定义了URL路径与组件的对应关系。你可以同时使用多个Route组件。 <Router history={hashHistory}> <Route path="/" component={App}/> <Route path="/repos" component={Repos}/> <Route path="/about" component={About}/> </Router>上面代码中,用户访问/repos(比如http://localhost:8080/#/repos)时,加载Repos组件;访问/about(http://localhost:8080/#/about)时,加载About组件。二、嵌套路由Route组件还可以嵌套。<Router history={hashHistory}> <Route path="/" component={App}> <Route path="/repos" component={Repos}/> <Route path="/about" component={About}/> </Route> </Router>上面代码中,用户访问/repos时,会先加载App组件,然后在它的内部再加载Repos组件。<App> <Repos/> </App>App组件要写成下面的样子。export default React.createClass({ render() { return <div> {this.props.children} </div> } })上面代码中,App组件的this.props.children属性就是子组件。子路由也可以不写在Router组件里面,单独传入Router组件的routes属性。let routes = <Route path="/" component={App}> <Route path="/repos" component={Repos}/> <Route path="/about" component={About}/> </Route>; <Router routes={routes} history={browserHistory}/>三、 path 属性Route组件的path属性指定路由的匹配规则。这个属性是可以省略的,这样的话,不管路径是否匹配,总是会加载指定组件。请看下面的例子。<Route path="inbox" component={Inbox}> <Route path="messages/:id" component={Message} /> </Route>上面代码中,当用户访问/inbox/messages/:id时,会加载下面的组件。<Inbox> <Message/> </Inbox>如果省略外层Route的path参数,写成下面的样子。<Route component={Inbox}> <Route path="inbox/messages/:id" component={Message} /> </Route>现在用户访问/inbox/messages/:id时,组件加载还是原来的样子。<Inbox> <Message/> </Inbox>四、通配符path属性可以使用通配符。<Route path="/hello/:name"> // 匹配 /hello/michael // 匹配 /hello/ryan <Route path="/hello(/:name)"> // 匹配 /hello // 匹配 /hello/michael // 匹配 /hello/ryan <Route path="/files/*.*"> // 匹配 /files/hello.jpg // 匹配 /files/hello.html <Route path="/files/*"> // 匹配 /files/ // 匹配 /files/a // 匹配 /files/a/b <Route path="/**/*.jpg"> // 匹配 /files/hello.jpg // 匹配 /files/path/to/file.jpg通配符的规则如下。(1):paramName:paramName匹配URL的一个部分,直到遇到下一个/、?、#为止。这个路径参数可以通过this.props.params.paramName取出。(2)()()表示URL的这个部分是可选的。(3)**匹配任意字符,直到模式里面的下一个字符为止。匹配方式是非贪婪模式。(4) **** 匹配任意字符,直到下一个/、?、#为止。匹配方式是贪婪模式。path属性也可以使用相对路径(不以/开头),匹配时就会相对于父组件的路径,可以参考上一节的例子。嵌套路由如果想摆脱这个规则,可以使用绝对路由。路由匹配规则是从上到下执行,一旦发现匹配,就不再其余的规则了。上面代码中,路径/comments同时匹配两个规则,第二个规则不会生效。设置路径参数时,需要特别小心这一点。<Router> <Route path="/:userName/:id" component={UserPage}/> <Route path="/about/me" component={About}/> </Router>上面代码中,用户访问/about/me时,不会触发第二个路由规则,因为它会匹配/:userName/:id这个规则。因此,带参数的路径一般要写在路由规则的底部。此外,URL的查询字符串/foo?bar=baz,可以用this.props.location.query.bar获取。五、IndexRoute 组件下面的例子,你会不会觉得有一点问题?<Router> <Route path="/" component={App}> <Route path="accounts" component={Accounts}/> <Route path="statements" component={Statements}/> </Route> </Router>上面代码中,访问根路径/,不会加载任何子组件。也就是说,App组件的this.props.children,这时是undefined。因此,通常会采用{this.props.children || <Home/>}这样的写法。这时,Home明明是Accounts和Statements的同级组件,却没有写在Route中。IndexRoute就是解决这个问题,显式指定Home是根路由的子组件,即指定默认情况下加载的子组件。你可以把IndexRoute想象成某个路径的index.html。<Router> <Route path="/" component={App}> <IndexRoute component={Home}/> <Route path="accounts" component={Accounts}/> <Route path="statements" component={Statements}/> </Route> </Router>现在,用户访问/的时候,加载的组件结构如下。<App> <Home/> </App>这种组件结构就很清晰了:App只包含下级组件的共有元素,本身的展示内容则由Home组件定义。这样有利于代码分离,也有利于使用React Router提供的各种API。注意,IndexRoute组件没有路径参数path。六、Redirect 组件组件用于路由的跳转,即用户访问一个路由,会自动跳转到另一个路由。<Route path="inbox" component={Inbox}> {/* 从 /inbox/messages/:id 跳转到 /messages/:id */} <Redirect from="messages/:id" to="/messages/:id" /> </Route>现在访问/inbox/messages/5,会自动跳转到/messages/5。七、IndexRedirect 组件IndexRedirect组件用于访问根路由的时候,将用户重定向到某个子组件。<Route path="/" component={App}> <IndexRedirect to="/welcome" /> <Route path="welcome" component={Welcome} /> <Route path="about" component={About} /> </Route>上面代码中,用户访问根路径时,将自动重定向到子组件welcome。八、LinkLink组件用于取代元素,生成一个链接,允许用户点击后跳转到另一个路由。它基本上就是元素的React 版本,可以接收Router的状态。render() { return <div> <ul role="nav"> <li><Link to="/about">About</Link></li> <li><Link to="/repos">Repos</Link></li> </ul> </div> }如果希望当前的路由与其他路由有不同样式,这时可以使用Link组件的activeStyle属性。<Link to="/about" activeStyle={{color: 'red'}}>About</Link> <Link to="/repos" activeStyle={{color: 'red'}}>Repos</Link>上面代码中,当前页面的链接会红色显示。另一种做法是,使用activeClassName指定当前路由的Class。<Link to="/about" activeClassName="active">About</Link> <Link to="/repos" activeClassName="active">Repos</Link>上面代码中,当前页面的链接的class会包含active。在Router组件之外,导航到路由页面,可以使用浏览器的History API,像下面这样写。import { browserHistory } from 'react-router'; browserHistory.push('/some/path');九、IndexLink如果链接到根路由/,不要使用Link组件,而要使用IndexLink组件。这是因为对于根路由来说,activeStyle和activeClassName会失效,或者说总是生效,因为/会匹配任何子路由。而IndexLink组件会使用路径的精确匹配。<IndexLink to="/" activeClassName="active"> Home </IndexLink>上面代码中,根路由只会在精确匹配时,才具有activeClassName。另一种方法是使用Link组件的onlyActiveOnIndex属性,也能达到同样效果。<Link to="/" activeClassName="active" onlyActiveOnIndex={true}> Home </Link>实际上,IndexLink就是对Link组件的onlyActiveOnIndex属性的包装。十、histroy 属性Router组件的history属性,用来监听浏览器地址栏的变化,并将URL解析成一个地址对象,供 React Router 匹配。history属性,一共可以设置三种值。browserHistory hashHistory createMemoryHistory如果设为hashHistory,路由将通过URL的hash部分(#)切换,URL的形式类似example.com/#/some/path。import { hashHistory } from 'react-router' render( <Router history={hashHistory} routes={routes} />, document.getElementById('app') )如果设为browserHistory,浏览器的路由就不再通过Hash完成了,而显示正常的路径example.com/some/path,背后调用的是浏览器的History API。import { browserHistory } from 'react-router' render( <Router history={browserHistory} routes={routes} />, document.getElementById('app') )但是,这种情况需要对服务器改造。否则用户直接向服务器请求某个子路由,会显示网页找不到的404错误。如果开发服务器使用的是webpack-dev-server,加上--history-api-fallback参数就可以了。$ webpack-dev-server --inline --content-base . --history-api-fallbackcreateMemoryHistory主要用于服务器渲染。它创建一个内存中的history对象,不与浏览器URL互动。const history = createMemoryHistory(location)十一、表单处理Link组件用于正常的用户点击跳转,但是有时还需要表单跳转、点击按钮跳转等操作。这些情况怎么跟React Router对接呢?下面是一个表单。<form onSubmit={this.handleSubmit}> <input type="text" placeholder="userName"/> <input type="text" placeholder="repo"/> <button type="submit">Go</button> </form>第一种方法是使用browserHistory.pushimport { browserHistory } from 'react-router' // ... handleSubmit(event) { event.preventDefault() const userName = event.target.elements[0].value const repo = event.target.elements[1].value const path = `/repos/${userName}/${repo}` browserHistory.push(path) },第二种方法是使用context对象。export default React.createClass({ // ask for `router` from context contextTypes: { router: React.PropTypes.object }, handleSubmit(event) { // ... this.context.router.push(path) }, })十二、路由的钩子每个路由都有Enter和Leave钩子,用户进入或离开该路由时触发。<Route path="about" component={About} /> <Route path="inbox" component={Inbox}> <Redirect from="messages/:id" to="/messages/:id" /> </Route>上面的代码中,如果用户离开/messages/:id,进入/about时,会依次触发以下的钩子。/messages/:id的onLeave/inbox的onLeave/about的onEnter下面是一个例子,使用onEnter钩子替代组件。<Route path="inbox" component={Inbox}> <Route path="messages/:id" onEnter={ ({params}, replace) => replace(`/messages/${params.id}`) } /> </Route>onEnter钩子还可以用来做认证。const requireAuth = (nextState, replace) => { if (!auth.isAdmin()) { // Redirect to Home page if not an Admin replace({ pathname: '/' }) } } export const AdminRoutes = () => { return ( <Route path="/admin" component={Admin} onEnter={requireAuth} /> ) }下面是一个高级应用,当用户离开一个路径的时候,跳出一个提示框,要求用户确认是否离开。const Home = withRouter( React.createClass({ componentDidMount() { this.props.router.setRouteLeaveHook( this.props.route, this.routerWillLeave ) }, routerWillLeave(nextLocation) { // 返回 false 会继续停留当前页面, // 否则,返回一个字符串,会显示给用户,让其自己决定 if (!this.state.isSaved) return '确认要离开?'; }, }) )上面代码中,setRouteLeaveHook方法为Leave钩子指定routerWillLeave函数。该方法如果返回false,将阻止路由的切换,否则就返回一个字符串,提示用户决定是否要切换。文章来自阮一峰的网络日志!
2017年11月25日
4 阅读
0 评论
0 点赞
2017-11-25
去除右键菜单opendlg
未知文件类型,右键会多出一个opendlg的选项!下面是移除的方法:将下面的内容复制到记事本,并另存为XXX .reg,导入注册表即可!Windows Registry Editor Version 5.00 [-HKEY_CLASSES_ROOT\Unknown\shell\opendlg] [-HKEY_CLASSES_ROOT\Unknown\shell\opendlg\command] [-HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Folder\shell\pintostartscreen] [-HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Folder\shell\pintostartscreen\command]
2017年11月25日
2 阅读
0 评论
0 点赞
2017-11-24
那些年忘掉的React
在长期使用Vue开发项目的我,渐渐忘记如何使用React!!React 是一个 Facebook 和 Instagram 用来创建用户界面(UI)的 JavaScript 库。很人多认为 React 是 MVC 中的V(图)。1.React项目的搭建!使用yeoman脚手架搭建npm install -g yeomannpm install -g genreator-react-webpack新建一个文件夹yo react-webpack启动项目 npm start项目到处搭建完成!React的使用state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。而组件只能通过 props 来传递数据。 2.PropsTypeReact组件创建的时候,需要传入属性,我们可以使用使用PropTypes进行类型检查,您可以使用React.PropTypes在组件的道具上运行。React.PropTypes.array React.PropTypes.bool React.PropTypes.func React.PropTypes.number React.PropTypes.object React.PropTypes.string React.PropTypes.symbol React.PropTypes.node React.PropTypes.element React.PropTypes.instanceOf() React.PropTypes.oneOf() React.PropTypes.oneOfType() React.PropTypes.arrayOf() React.PropTypes.objectOf() React.PropTypes.shape() React.PropTypes.any 默认情况下,验证器将props视为可选属性。您可以使用isRequired确保在未提供道具时显示警告。 React.createClass({ propTypes: { // 可以声明 prop 为指定的 JS 基本类型。默认 // 情况下,这些 prop 都是可传可不传的。 optionalArray: React.PropTypes.array, optionalBool: React.PropTypes.bool, optionalFunc: React.PropTypes.func, optionalNumber: React.PropTypes.number, optionalObject: React.PropTypes.object, optionalString: React.PropTypes.string, // 所有可以被渲染的对象:数字, // 字符串,DOM 元素或包含这些类型的数组。 optionalNode: React.PropTypes.node, // React 元素 optionalElement: React.PropTypes.element, // 用 JS 的 instanceof 操作符声明 prop 为类的实例。 optionalMessage: React.PropTypes.instanceOf(Message), // 用 enum 来限制 prop 只接受指定的值。 optionalEnum: React.PropTypes.oneOf(['News', 'Photos']), // 指定的多个对象类型中的一个 optionalUnion: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.number, React.PropTypes.instanceOf(Message) ]), // 指定类型组成的数组 optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number), // 指定类型的属性构成的对象 optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number), // 特定形状参数的对象 optionalObjectWithShape: React.PropTypes.shape({ color: React.PropTypes.string, fontSize: React.PropTypes.number }), // 以后任意类型加上 `isRequired` 来使 prop 不可空。 requiredFunc: React.PropTypes.func.isRequired, // 不可空的任意类型 requiredAny: React.PropTypes.any.isRequired, // 自定义验证器。如果验证失败需要返回一个 Error 对象。不要直接 // 使用 `console.warn` 或抛异常,因为这样 `oneOfType` 会失效。 customProp: function(props, propName, componentName) { if (!/matchme/.test(props[propName])) { return new Error('Validation failed!'); } } }, /* ... */ });或者(ES6) MyComponent.propTypes = { name:React.PropTypes.string };静态方法(属性)属于类级别的特征,所有实例都自动获取类级别的静态特征。React生命周期//用代码说明 class LifeCycle extends React.Component { constructor(props) { //React初始化 super(props); //调用父主件(constructor)构造器,就是继承constructor this.state = {str: "hello"};//定义初始状态 } componentWillMount() { //主件即将挂载阶段 alert("componentWillMount"); } componentDidMount() { //主件渲染完成 alert("componentDidMount"); } componentWillReceiveProps(nextProps) { //props发生变化时触发 alert("componentWillReceiveProps"); } shouldComponentUpdate() { //props和state变化后是否重新渲染主件 alert("shouldComponentUpdate"); return true; // 记得要返回true } componentWillUpdate() { //主件即将更新 alert("componentWillUpdate"); } componentDidUpdate() { //主件更新完成 alert("componentDidUpdate"); } componentWillUnmount() { //主件销毁阶段 alert("componentWillUnmount"); } setTheState() { let s = "hello"; if (this.state.str === s) { s = "HELLO"; } this.setState({ str: s }); } forceItUpdate() { this.forceUpdate(); } render() { //主件渲染阶段 alert("render"); return( <div> <span>{"Props:"}<h2>{parseInt(this.props.num)}</h2></span> <br /> <span>{"State:"}<h2>{this.state.str}</h2></span> </div> ); } } class Container extends React.Component { constructor(props) { super(props); this.state = { num: Math.random() * 100 }; } propsChange() { this.setState({ num: Math.random() * 100 }); } setLifeCycleState() { this.refs.rLifeCycle.setTheState(); } forceLifeCycleUpdate() { this.refs.rLifeCycle.forceItUpdate(); } unmountLifeCycle() { // 这里卸载父组件也会导致卸载子组件 React.unmountComponentAtNode(document.getElementById("container")); } parentForceUpdate() { this.forceUpdate(); } render() { return ( <div> <a href="javascript:;" className="weui_btn weui_btn_primary" onClick={this.propsChange.bind(this)}>propsChange</a> <a href="javascript:;" className="weui_btn weui_btn_primary" onClick={this.setLifeCycleState.bind(this)}>setState</a> <a href="javascript:;" className="weui_btn weui_btn_primary" onClick={this.forceLifeCycleUpdate.bind(this)}>forceUpdate</a> <a href="javascript:;" className="weui_btn weui_btn_primary" onClick={this.unmountLifeCycle.bind(this)}>unmount</a> <a href="javascript:;" className="weui_btn weui_btn_primary" onClick={this.parentForceUpdate.bind(this)}>parentForceUpdateWithoutChange</a> <LifeCycle ref="rLifeCycle" num={this.state.num}></LifeCycle> </div> ); } } ReactDom.render( <Container></Container>, document.getElementById('container') );
2017年11月24日
6 阅读
0 评论
0 点赞
2017-11-24
Webpack dev服务器代理
Webpack dev服务器利用http-proxy-middleware可选地将请求代理到单独的,可能是外部的后端服务器。示例配置如下。proxy: { '/api': { target: 'https://other-server.example.com', secure: false } } // In webpack.config.js { devServer: { proxy: { '/api': { target: 'https://other-server.example.com', secure: false } } } } // Multiple entry proxy: [ { context: ['/api-v1/**', '/api-v2/**'], target: 'https://other-server.example.com', secure: false } ]有关可用配置,请参阅http-proxy-middleware选项文档。代理一些URL可用于各种配置。一个例子是从本地开发服务器提供JavaScript文件和其他静态资源,但仍然向外部后端开发服务器发送API请求。另一个例子是在两个独立的后端服务器之间拆分请求,例如认证后端和应用程序后端。绕过代理(在v1.13.0中增加)代理可以根据函数的返回来选择性的绕过。该函数可以检查HTTP请求,响应和任何给定的代理选项。它必须返回一个false或者一个将被服务的URL路径,而不是继续代理请求。例如,下面的配置不会代理源自浏览器的HTTP请求。这与historyApiFallback选项类似:浏览器请求将正常接收HTML文件,但API请求将代理到后端服务器。proxy: { '/some/path': { target: 'https://other-server.example.com', secure: false, bypass: function(req, res, proxyOptions) { if (req.headers.accept.indexOf('html') !== -1) { console.log('Skipping proxy for browser request.'); return '/index.html'; } } } }重写代理请求的URL(在v1.15.0中增加)对代理的请求可以通过提供一个函数来选择性的重写。该功能可以检查和更改HTTP请求。例如,下面的配置将重写HTTP请求以删除/apiURL开头的部分。proxy: { '/api': { target: 'https://other-server.example.com', pathRewrite: {'^/api' : ''} } }请注意,这pathRewrite是来自http-proxy-middleware的一项功能,因此请查看他们的文档以获取更多配置。代理本地虚拟主机看起来,http-proxy-middleware预解析本地主机名localhost,你需要以下配置来修复代理请求:var server = new webpackDevServer(compiler, { quiet: false, stats: { colors: true }, proxy: { "/api": { "target": { "host": "action-js.dev", "protocol": 'http:', "port": 80 }, ignorePath: true, changeOrigin: true, secure: false } } }); server.listen(8080);
2017年11月24日
3 阅读
0 评论
0 点赞
2017-11-24
new Date() 兼容性问题
一. 无参//无参 var dateTime = new Date();所有浏览器都兼容,GOOD二. 日期参数//日期参数 格式1 var dateTime = new Date("2016-05-20");a. IE > IE9-(不兼容) > IE9+(兼容,包含IE9) b. 火狐(兼容)c. 谷歌 (兼容)//日期参数 格式2 var dateTime = new Date("2016/05/20");所有浏览器都兼容,GOOD三. 日期时间参数//日期时间参数 格式1 var dateTime = new Date("2016-05-20 23:41:00");a. IE (不兼容,不管哪个版本)b. 火狐 (不兼容)c. 谷歌 (兼容)//日期时间参数 格式2 var dateTime = new Date("2016/05/20 23:42:00");所有浏览器都兼容,GOOD//日期时间参数 格式3 var dateTime = new Date("2016-05-20T23:42:00");a. IE > IE9- (不兼容) > IE9+(兼容) > IE9(半兼容) 半兼容是个什么鬼?哈哈。 半兼容就是可以得到日期时间,但是时间是错误的。比如上面我们的时间是 23:42:00 。那么它会自动在此基础上加上8个小时,因此我们得到的 dateTime 的值将会是: 2016-05-21 07:42:00b. 火狐 (兼容)c. 谷歌 (半兼容)同IE9 时间多加了8小时//日期时间参数 格式4 var dateTime = new Date("2016/05/20T23:42:00");a. IE (半兼容) 所有版本时间都多加了 1 个小时,即得到的 dateTime 的值为: 2016-05-21 00:42:00b. 火狐 (不兼容)c. 谷歌 (不兼容)根据上面测试的结果,所有浏览器都支持的格式为: var dateTime = new Date("2016/05/20 23:42:00");
2017年11月24日
1 阅读
0 评论
0 点赞
2017-11-23
webpack开发和生产两个环境的配置
webpack.base.conf.jswebpack基本配置var path = require('path') var config = require('../config') var utils = require('./utils') var projectRoot = path.resolve(__dirname, '../') var env = process.env.NODE_ENV // check env & config/index.js to decide whether to enable CSS source maps for the // various preprocessor loaders added to vue-loader at the end of this file var cssSourceMapDev = (env === 'development' && config.dev.cssSourceMap) var cssSourceMapProd = (env === 'production' && config.build.productionSourceMap) var useCssSourceMap = cssSourceMapDev || cssSourceMapProd // 配置文件的内容需要通过module.exports暴露 module.exports = { // 配置需要打包的入口文件,值可以是字符串、数组、对象。 // 1. 字符串: entry: './entry' // 2. 字符串: entry:[ './entry1','entry2'] (多入口) // 3. 对象: entry: {alert/index': path.resolve(pagesDir, `./alert/index/page`)} // 多入口书写的形式应为object,因为object,的key在webpack里相当于此入口的name, entry: { app: './src/main.js' }, output: { // 输出文件配置,output 输出有自己的一套规则,常用的参数基本就是这三个 // path: 表示生成文件的根目录 需要一个**绝对路径** path仅仅告诉Webpack结果存储在哪里 path: config.build.assetsRoot, // publicPath 参数表示的是一个URL 路径(指向生成文件的跟目录),用于生成css/js/图片/字体文件 // 等资源的路径以确保网页能正确地加载到这些资源. // “publicPath”项则被许多Webpack的插件用于在生产模式下更新内嵌到css、html文件里的url值. // 例如,在localhost(即本地开发模式)里的css文件中边你可能用“./test.png”这样的url来加载图片, // 但是在生产模式下“test.png”文件可能会定位到CDN上并且你的Node.js服务器可能是运行在HeroKu上边的。 // 这就意味着在生产环境你必须手动更新所有文件里的url为CDN的路径。 //开发环境:Server和图片都是在localhost(域名)下 //.image { // background-image: url('./test.png'); //} // 生产环境:Server部署下HeroKu但是图片在CDN上 //.image { // background-image: url('https://someCDN/test.png'); //} publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath, // filename 属性表示的是如何命名出来的入口文件,规则是一下三种: // [name] 指代入口文件的name,也就是上面提到的entry参数的key,因此,我们可以在name里利用/,即可达到控制文件目录结构的效果。 // [hash],指代本次编译的一个hash版本,值得注意的是,只要是在同一次编译过程中生成的文件,这个[hash].js //的值就是一样的;在缓存的层面来说,相当于一次全量的替换。 filename: '[name].js' }, // 用来配置依赖文件的匹配,如依赖文件的别名配置、模块的查找目录、默认查找的 // 文件后缀名 // resolve.root 该选型用来制定模块查找的根路径,必须为**绝对路径**,值可以 // 是路径字符串或者路径数组若是数组,则会依次查找 resolve: { extensions: ['', '.js', '.vue', '.json'], fallback: [path.join(__dirname, '../node_modules')], // 用来配置依赖文件的别名,值是一个对,该对象的键是别名,值是实际路径 alias: { 'vue$': 'vue/dist/vue.common.js', 'src': path.resolve(__dirname, '../src'), 'assets': path.resolve(__dirname, '../src/assets'), 'components': path.resolve(__dirname, '../src/components') } }, resolveLoader: { fallback: [path.join(__dirname, '../node_modules')] }, // 用来进行模块加载相关的配置 module: { preLoaders: [ { test: /\.vue$/, loader: 'eslint', include: projectRoot, exclude: /node_modules/ }, { test: /\.js$/, loader: 'eslint', include: projectRoot, exclude: /node_modules/ } ], loaders: [ // webpack拥有一个类似于插件的机制,名为Loader,通过Loader,webpack能够针对每一种特定的资源做出相应的处理 // 1.test参数用来指示当前配置项针对哪些资源,该值应是一个条件值(condition)。 // 2.exclude参数用来剔除掉需要忽略的资源,该值应是一个条件值(condition)。 // 3.include参数用来表示本loader配置仅针对哪些目录/文件,该值应是一个条件值(condition)。 // 而include参数则用来指示目录;注意同时使用这两者的时候,实际上是and的关系。 // 4.loader/loaders参数,用来指示用哪个或哪些loader来处理目标资源,这俩货 // 表达的其实是一个意思,只是写法不一样,我个人推荐用loader写成一行,多个 // loader间使用!分割,这种形式类似于管道的概念,又或者说是函数式编程。形 // 如loader: 'css?!postcss!less',可以很明显地看出,目标资源先经less-loader // 处理过后将结果交给postcss-loader作进一步处理,然后最后再交给css-loader。 { test: /\.vue$/, loader: 'vue' }, { test: /\.js$/, loader: 'babel', include: projectRoot, exclude: /node_modules/ }, { test: /\.json$/, loader: 'json' }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url', query: { limit: 10000, name: utils.assetsPath('img/[name].[hash:7].[ext]') } }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url', query: { limit: 10000, name: utils.assetsPath('fonts/[name].[hash:7].[ext]') } }, // expose-loader,这个loader的作用是,将指定js模块export的变量声明为全局变量 { test: require.resolve('jquery'), // 此loader配置项的目标是NPM中的jquery loader: 'expose?$!expose?jQuery', // 先把jQuery对象声明成为全局变量`jQuery`,再通过管道进一步又声明成为全局变量`$` }, ] }, eslint: { formatter: require('eslint-friendly-formatter') }, vue: { loaders: utils.cssLoaders({ sourceMap: useCssSourceMap }), // 解决.vue中文件style的部分一些特性解析,比如scoped postcss: [ require('autoprefixer')({ browsers: ['last 2 versions'] }) ] } }webpack.dev.conf.jsvar config = require('../config') var webpack = require('webpack') var merge = require('webpack-merge') var utils = require('./utils') var baseWebpackConfig = require('./webpack.base.conf') var HtmlWebpackPlugin = require('html-webpack-plugin') // add hot-reload related code to entry chunks Object.keys(baseWebpackConfig.entry).forEach(function (name) { baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) }) module.exports = merge(baseWebpackConfig, { module: { loaders: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) }, // eval-source-map is faster for development devtool: '#eval-source-map', plugins: [ // DefinePlugin 是webpack 的内置插件,该插件可以在打包时候替换制定的变量 // new webpack.DefinePlugin({ 'process.env': config.dev.env }), // https://github.com/glenjamin/webpack-hot-middleware#installation--usage new webpack.optimize.OccurrenceOrderPlugin(), new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin(), // https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ filename: 'index.html', template: 'index.html', inject: true }), // 可以自动加载当前模块依赖的其他模块并已制定别名注入到当前的模块中,引入jq // 在网上看到的文章,救了我的命 ProvidePlugin + expose-loader 引入jq // // 如果你把jQuery看做是一个普通的js模块来加载(要用到jQuery的模块统统先require // 后再使用),那么,当你加载老式jQuery插件时,往往会提示找不到jQuery实例 // 有时候是提示找不到$),这是为啥呢? // 要解释这个问题,就必须先稍微解释一下jQuery插件的机制:jQuery插件是通过 // jQuery提供的jQuery.fn.extend(object)和jQuery.extend(object)这俩方法,来 // 把插件本身实现的方法挂载到jQuery(也即$)这个对象上的。传统引用jQuery及 // 其插件的方式是先用<script>加载jQuery本身,然后再用同样的方法来加载其插件; // jQuery会把jQuery对象设置为全局变量(当然也包括了$),既然是全局变量,那么 // 插件们很容易就能找到jQuery对象并挂载自身的方法了。 // // 而webpack作为一个遵从模块化原则的构建工具,自然是要把各模块的上下文环境给 // 分隔开以减少相互间的影响;而jQuery也早已适配了AMD/CMD等加载方式,换句话说, // 我们在require jQuery的时候,实际上并不会把jQuery对象设置为全局变量。说到 // 这里,问题也很明显了,jQuery插件们找不到jQuery对象了,因为在它们各自的上下 // 文环境里,既没有局部变量jQuery(因为没有适配AMD/CMD,所以就没有相应的requi // re语句了),也没有全局变量jQuery。 // // A: ProvidePlugin的机制是:当webpack加载到某个js模块里,出现了未定义且名称符合 // (字符串完全匹配)配置中key的变量时,会自动require配置中value所指定的js模块 // expose-loader,这个loader的作用是,将指定js模块export的变量声明为全局变量。 // // B:externals 调用jq // externals是webpack配置中的一项,用来将某个全局变量“伪装”成某个js模块的exports, // 如下面这个配置: // externals: {'jquery': 'window.jQuery',}, // 那么,当某个js模块显式地调用var $ = require('jquery')的时候,就会把window, // jQuery返回给它,与上述ProvidePlugin + expose-loader的方案相反,此方案是先用 // <script>加载的jQuery满足老式jQuery插件的需要,再通过externals将其转换成符合 // 模块化要求的exports。 new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery", "window.jQuery": "jquery", 'window.$': 'jquery', }) ] })webpack.prod.conf.jsvar path = require('path') var config = require('../config') var utils = require('./utils') var webpack = require('webpack') var merge = require('webpack-merge') var baseWebpackConfig = require('./webpack.base.conf') var ExtractTextPlugin = require('extract-text-webpack-plugin') var HtmlWebpackPlugin = require('html-webpack-plugin') var env = config.build.env var webpackConfig = merge(baseWebpackConfig, { module: { loaders: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true }) }, devtool: config.build.productionSourceMap ? '#source-map' : false, output: { path: config.build.assetsRoot, filename: utils.assetsPath('js/[name].[chunkhash].js'), chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') }, vue: { loaders: utils.cssLoaders({ sourceMap: config.build.productionSourceMap, extract: true }) }, // webpack插件位置,有固定的用法 // 1. 利用Plugin的初始方法并传入Plugin预设的参数进行初始化,生成一个实例。 // 2. 将此实例插入到webpack配置文件中的plugins参数(数组类型)里即可。 // // 1. plugins: [ // http://vuejs.github.io/vue-loader/en/workflow/production.html new webpack.DefinePlugin({ 'process.env': env }), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }), new webpack.optimize.OccurrenceOrderPlugin(), // extract css into its own file new ExtractTextPlugin(utils.assetsPath('css/[name].[contenthash].css')), // generate dist index.html with correct asset hash for caching. // you can customize output by editing /index.html // see https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ // filename 生成网页的HTML名字,可以使用/来控制文件文件的目录结构,最 // 终生成的路径是基于webpac配置的output.path的 filename: config.build.index, template: 'index.html', inject: true, // inject,指示把加载js文件用的<script>插入到哪里,默认是插到<body> // 的末端,如果设置为'head',则把<script>插入到<head>里。 minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: true // more options: // https://github.com/kangax/html-minifier#options-quick-reference }, // necessary to consistently work with multiple chunks via CommonsChunkPlugin chunksSortMode: 'dependency' }), // 如果文件是多入口的文件,可能存在,重复代码,把公共代码提取出来,又不会重复下载公共代码了 // (多个页面间会共享此文件的缓存) // CommonsChunkPlugin的初始化常用参数有解析? // name: 这个给公共代码的chunk唯一的标识 // filename,如何命名打包后生产的js文件,也是可以用上[name]、[hash]、[chunkhash] // minChunks,公共代码的判断标准:某个js模块被多少个chunk加载了才算是公共代码 new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: function (module, count) { // any required modules inside node_modules are extracted to vendor return ( module.resource && /\.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, '../node_modules') ) === 0 ) } }), // extract webpack runtime and module manifest to its own file in order to // prevent vendor hash from being updated whenever app bundle is updated new webpack.optimize.CommonsChunkPlugin({ name: 'manifest', chunks: ['vendor'] }) ] }) if (config.build.productionGzip) { var CompressionWebpackPlugin = require('compression-webpack-plugin') webpackConfig.plugins.push( new CompressionWebpackPlugin({ asset: '[path].gz[query]', algorithm: 'gzip', test: new RegExp( '\\.(' + config.build.productionGzipExtensions.join('|') + ')$' ), threshold: 10240, minRatio: 0.8 }) ) } module.exports = webpackConfig
2017年11月23日
4 阅读
0 评论
0 点赞
1
...
11
12
13
14