关于dva.js:路由跳转BugReactdva数据流页面跳转路由发生变化但页面不刷新问题及解决方案相同路径不同参数

React+Dva.js我的项目中路由跳转胜利(浏览器url发生变化)但页面不从新加载1.问题形容:在最近我的项目中,遇到了这个问题,如下图,我的项目中采纳了和浏览器相似的多标签页面显示。当我点击红色框中的按钮,心愿实现页面跳转,并将被点击数据的序号作为参数,跳转传入下一个页面。 2.Bug形容:当我在标签页中点击序号为71的数据进行路由跳转,再将序号为71的数据跳转到页面敞开后,从新点击其余数据,能够失常渲染。然而当我页面没有敞开,从新点击除序号71以外的数据,路由产生跳转,但页面依然停留在序号为71数据跳转后的页面。 Bug起因:通过剖析发现当第一个数据Id页面未敞开,第二次chuan

April 17, 2022 · 1 min · jiezi

关于dva.js:Taro328-dva-搭建配置过程

创立我的项目的步骤我就不赘述了,官网有具体的步骤Taro应用文档提前下载微信开发者工具 前提初始化好了一个Taro我的项目 步骤1. 编译运行npm run dev:weapp在模拟器中预览 2. 配置dva2.1 装置 react-reduxnpm i --save react-redux2.2 装置 dva-core dva-loadingdva-core 封装了redux和redux-sage的一个插件dva-loading 治理页面的loading状态 npm install --save dva-core dva-loading2.3 src目录下创立models目录,并在models目录下的index.js返回我的项目中创立的所有model// dva须要挂载所有的modelsimport users from '../pages/index/model'export default [ users,];users为pages/index 上面新建的model其构造就和咱们dva中的model一样 export default { namespace: 'users', state: { title: 'Hello World' }, effects: {}, reducers: { save(state, { payload }) { return { ...state, ...payload }; }, },};2.4 在src目录下创立utils目录,并在utils目录里创立dva.js文件import { create } from 'dva-core';import createLoading from 'dva-loading';let app;let store;let dispatch;function createApp(opt){ // 创立利用,返回dva实例 app = create(opt); // 配置插件 createLoading是解决异步加载的过渡问题 app.use(createLoading({})); if(!global.registered){ // 注册model opt.models.forEach(model => app.model(model)); } global.registered = true; // 启动利用 app.start(); store = app._store; app.getStore = () => store; dispatch = store.dispatch; app.dispatch = dispatch; return app;}export default { createApp, getDispatch: () => { return app.dispatch; }}2.5 在入口文件app.js里应用dva.js返回的办法创立一个app获取store,并将store挂载到Provider容器外面import { Component } from 'react'import dva from './utils/dva'import { Provider } from 'react-redux'import models from './models'import './app.less'const dvaApp = dva.createApp({ initialState: {}, models})const store = dvaApp.getStore();class App extends Component { componentDidMount () {} componentDidShow () {} componentDidHide () {} componentDidCatchError () {} // this.props.children 是将要会渲染的页面 render () { return <Provider store={store}>{this.props.children}</Provider> }}export default App配置实现,检测是否配置胜利在pages / index / index.jsx中连贯model,并且输入this.props ...

August 23, 2021 · 2 min · jiezi

使用-HooX-管理-React-状态的若干个好处

HooX  是一个基于 hook 的轻量级的 React 状态管理工具。使用它可方便的管理 React 应用的全局状态,概念简单,完美支持 TS。1. 更拥抱函数式组件从 React@16.8 的 hook 到 vue@3 的composition-api,基本可以断定,函数式组件是未来趋势。HooX提供了函数式组件下的状态管理方案,以及完全基于函数式写法的一系列 API,让用户更加的拥抱函数式组件,走向未来更进一步。 2. 简化纯 hook 写法带来的繁杂代码写过 hook 的同学肯定知道,hook 带来的逻辑抽象能力,让我们的代码变得更有条件。但是: useCallback/useMemo 真的是写的非常非常多由于作用域问题,一些方法内的 state 经常不知道到底对不对实际举个例子吧,比如一个列表,点击加载下一页,如果纯 hook 书写,会怎么样呢? import { useState, useEffect } from 'react'const fetchList = (...args) => fetch('./list-data', ...args)export default function SomeList() { const [list, setList] = useState([]) const [pageNav, setPageNav] = useState({ page: 1, size: 10 }) const { page, size } = pageNav // 初始化请求 useEffect(() => { fetchList(pageNav).then(data => { setList(data) }) // eslint-disable-next-line react-hooks/exhaustive-deps }, []) // 获取下一页内容 const nextPage = () => { const newPageNav = { page: page + 1, size } fetchList(newPageNav).then(data => { setList(data) setPageNav(newPageNav) }) } return ( <div> <div className="list"> {list.map((item, key) => ( <div className="item" key={key}> ... </div> ))} </div> <div className="nav"> {page}/{size} <div className="next" onClick={nextPage}> 下一页 </div> </div> </div> )}很常规的操作。现在,我希望给“下一页”这个方法,加个防抖,那应该怎么样呢?是这样吗? ...

November 5, 2019 · 3 min · jiezi

dva-如何异步获取接口数据

说在前面关于redux的框架有很多,这里我用到的是阿里云谦大大的dva(项目地址),这里主要记录下工作中是如何使用dva来异步获取接口数据的。文末更新至20190619更新(2019.0619)最近发现这篇文章虽然写的很烂,但是很多人都能搜到进来瞅两眼,想着再更新一点吧。主要补充一下dva的几个关键词的作用state的作用State 表示 Model 的状态数据,通常表现为一个 javascript 对象(当然它可以是任何值);操作的时候每次都要当作不可变数据(immutable data)来对待,保证每次都是全新对象,没有引用关系,这样才能保证 State 的独立性,便于测试和追踪变化。在每一个model中定义state,用于分模块管理全局状态effects的作用进行异步操作的地方(ajax...),底层引入了redux-sagas做异步流程控制,由于采用了generator的相关概念,所以将异步转成同步写法类似于vuex中的Action ,包含异步操作,在vuex中用于提交mutation,从而变更state,在dva中用于提交reducer,用于修改stateReducer的作用同步方法,唯一可以修改state的地方,通过effect通过actions传入的值修改state类似于vuex中mutation。Subscription的作用订阅一个数据源,然后根据条件 dispatch 需要的 action。数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。payload关键字一般作为参数含义,由UI层通过dispatch一个payload参数在model文件中使用call结合yield call发起ajax请求//getDetailDiscount 接口名//payload 参数yield call(getDetailDiscount, payload)put使用put关键词提交Reducer//doDiscounts 一个名字为doDiscounts的同步方法 修改stateyield put({type: 'doDiscounts', payload: response.data});select在effect中可以通过select获取model中state定义state state:{ num:1 }定义effect effect:{ *getNum({payload},{select}){ //获取state中的num const num = yield select(state => state.num) } }跨model获取state other字段为model的namespace effect:{ *getOtherNum({payload},{select}){ //获取state中的num const num = yield select(state => state.other.num) } }这里是正文第一步、定义modeldva里的model主要是用来开始处理数据和逻辑的。dva 通过 model 的概念把一个领域的模型管理起来,包含同步更新 state 的 reducers,处理异步逻辑的 effects,订阅数据源的 subscriptions 。 新建一个一个model/users.js ...

June 30, 2019 · 2 min · jiezi

基于ReactDvaJSUmiJSAntdesignexpressmongo搭建的CMS内容管理后台

项目地址前端部分:React+Ant-design+DvaJS+UmiJS - 地址(欢迎star&fork&issue)后端部分:express+mongoose+redsi(鉴权)+pm2 - 地址(欢迎star&fork&issue)项目简介此项目为初中级项目,主要为理解React+Ant-design+DvaJS+UmiJS搭建中后台系统结构后端项目为通用模型,包含用户管理,角色管理,内容管理,评论管理,内容分类管理,适用于任何内容型产品后台(博客、社区等,可以直接套个简易版CNode)项目预览在线演示地址

June 18, 2019 · 1 min · jiezi

长期更新记录一下近期工作中涉及到的内容DvaJs-Ant-Design

DvaJs dispatch在 dva 中,connect Model 的组件通过 props 可以访问到 dispatch,可以调用 Model 中的 Reducer 或者 Effects,常见的形式如: dispatch({ type: 'user/add', // 如果在 model 外调用,需要添加 namespace payload: {}, // 需要传递的信息}); 可以通过类似this.props.dispatch({type: 'siteConfig/eff_getMutantGene', // siteConfig为modal文件夹名,eff_getMutantGene为Effects方法。payload: {itemId: item[i].itemid}})调用Model中的Reducer或者Effects。 connect如果要发起一个 action 需要使用 dispatch 函数;需要注意的是 dispatch 是在组件 connect Models以后,通过 props 传入的。 Ant Design <From.Item />表单域表单一定会包含表单域,表单域可以是输入控件,标准表单域,标签,下拉菜单,文本域等。 这里我们封装了表单域 <Form.Item /> 。 <Form.Item {...props}>{children}</Form.Item> getPopupContainer getPopupContainer菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。 如果发现下拉菜单跟随页面滚动,或者需要在其他弹层中触发 Select,请尝试使用 getPopupContainer={triggerNode => triggerNode.parentNode}*将下拉弹层渲染节点固定在触发器的父元素中。getFieldDecorator.1经过 getFieldDecorator 包装的控件,表单控件会自动添加 value(或 valuePropName 指定的其他属性) onChange(或 trigger 指定的其他属性),数据同步将被 Form 接管,这会导致以下结果: ...

May 23, 2019 · 1 min · jiezi

如何使用ReactNative快速开发一个APP

从去年的10月份开始,我的大部分工作重心从传统的前端开发转向了使用ReactNative开发APP,在这个过程当中,走过了不少弯路,也遇到了一些技术相关的问题,但总算没有辜负那些对我信任的人。经历过痛苦和无助,终于坚持了下来,一个月的时间把产品成功部署上线了。想想这些日子其中不乏有一些经验,先愿意拿出来和大家交流交流,其中难免有一些不是最优的方法和方案,还望大家多提意见。 背景一开始为了快速的开展业务,我们决定把产品先通过H5的形式进行线上运行,终于在两周的高效率工作情况下,我们产品上线了,但相比较APP,使用H5开发,不能满足我们的产品需要,随后就决定开发APP,但公司这个时候没有APP开发的相关人员,我只好硬着头皮上了,通过学习ReactNative相关的基本知识,然后不断的向身边一些做过RN开发的朋友取经,使用RN开发APP的工作,才慢慢的走上正轨,起初,我打算IOS和安卓都用RN开发,但考虑到工作量和日常的一些事情,我一个人难免会耽误大家的进度。鉴于我平常使用的是window系统,就决定我只开发安卓客户端,IOS客户端我们又招了一个小伙伴。这样我的工作就有APP开发,H5开发,管理后台相关的支持等事情需要做。 基础工作开发APP的基础框架包,一开始使用create-react-app,再后来使用react-native-dva-starter作为基础的框架包。相比较create-react-app这个基础的框架,后者增加了dva和react-navigation模块,其中dva是一个基于redux和redux-saga的数据流方案,主要是为了管理我们项目当中的数据的,其中包括,数据请求,数据模型,数据存储,react-navigation是一套路由系统,可以帮助我们实现页面跳转,并管理历史跳转数据。数据的请求我们可以使用HTML5提供的fetch,也可以像通常开发H5页面那样使用Axios,毕竟请求数据这件事情,只不过是为了发起一个ajax请求,然后把数据拿回来就好,使用什么不太紧要,我在项目当中实际使用Axios来完成这部分的事情。准备好了上面相关的内容之后,我们最最基础的代码内容算是弄好了,后面就可以通过一些第三方的npm包,为你的项目加砖添瓦了;以下是我的项目当中用到的第三方包列表: react-native-splash-screen 开屏广告react-native-swiper 图片轮播react-native-pdf 支持显示PDF文件react-native-picker 列表选择react-native-root-tips toast提示框react-native-dialog dialog模态框react-native-checkbox-component checkbox组件react-native-linear-gradient 实现渐变react-native-version-number APP版本号管理react-native-device-info 获取设备信息react-native-contacts-wrapper-pro 获取用户联系人react-native-code-push APP热更新react-native-image-picker 通过图片列表和拍照选择图片以上不是全部,有些可能没有列出来,一个包的需不需要,往往是根据我们的需求来的,如果可以,你可以添加其他的包进来。 代码结构 以上是不完全的目录结构,具体的内容,各位看官可以去我的代码仓库中去下载,查看详细的内容。我会在文章的底部附上代码相关的地址。 预备知识和环境工欲善其事必先利其器,以上我忽略了一个重要的部分,就是环境搭建的过程。这部分工作说起来不容小觑,没有这一步的胜利,后面所有的事情,都是白搭。关于环境,我们需要一个安卓的模拟器和打包和运营的JAVA环境,以及开发安卓APP相关版本的SDK包。具体环境的搭建详情,大家可以去这里看,然后大家需要有react,webpack,redux的基础知识,以及对MVVM设计思想的初步了解,这样后续的事情,开展起来会顺利一些,不然就会一步三坑,看的一脸懵逼。对了,开发安卓APP,大家一定要了解安卓各个版本在现在的安卓手机中使用的情况,比如说,3年前我们安卓的客户端,最低只支持安卓4.0的系统,然后向上兼容,如果你现在用的是安卓手机,你可以查看下你自己的机器系统版本是多少。一般来讲,安卓8.0系统是这一两年市面上常用机型配置的系统。我的项目当中,是基于安卓8.0系统进行开发的,所以说说,创建安卓虚拟机的时候,我会下载相关版本的SDK,明白了这些,你在开发时候下载SDK的时候,就可以有选择了,不用一股脑的把所有版本的SDK下载到本地,毫不夸张的说,所有安卓版本的SDK资源的大小应该不会小于50G,而且这些资源是从国外那边下载的,如果你真的不小心下载了所有的SDK包,我相信,你会哭的。我配置的安卓模拟器是使用Android Studio中带的,下面是我配置的安卓模拟器的一些信息: 其他开发的过程当中,难免会遇到一些问题,建议大家多看看API文档,如果是第三方包,多看看他们的案例代码信息,如果实在解决不了,您也可以私聊我,我们一起探讨下。以下是APP产品的一些截图 代码地址:https://github.com/mmcai/reac...dva.js地址:https://dvajs.com/React-Native中文文档地址:https://reactnative.cn/react-navigation地址:https://reactnavigation.org/d...

May 15, 2019 · 1 min · jiezi

基于-Electron-React-的超高颜值喜马拉雅客户端-Mob-诞生记

前言最近一个月沉迷喜马拉雅无法自拔,听相声、段子、每日新闻,还有英语听力,摸鱼学习两不误。上班时候苦于没有桌面端,用网页版有些 bug,官方也不搞一个,只好自己动手了。样式参考了一下 Moon FM /t/555343,颜值还过得去,自我感觉挺好 ???????????? 简介Mob(モブ), 异能超能 100的男一号。 GitHub: zenghongtu/Mob基于 Electron, Umi, Dva, Antd 构建 功能及 UI 目前实现的功能有这些: 一个基本的音乐播放器每日必听推荐排行榜分类订阅听过下载声音搜索专辑技术选型技术栈: ElectronUmiDvaAntd之所以选择 Umi 是因为在之前项目中研究过其部分源码,开发体验感不错,而且 bug 也少。还有一个原因是我在找模板的过程中,看到这个大佬的模板wangtianlun/umi-electron-typescript,就直接拿来用了,大大减少了我搭建开发环境的时间,在此表示感谢~ 如果你对 Umi 和 Dva 不熟,墙裂建议去学一下,分分钟就可以上手,而且开发效率要提高的不要太多。开发篇React Hooks 使用问题在开发中,所有组件、页面都是使用 React Hooks 进行开发的。而让我觉得最难以琢磨的一个 hooks 非 useEffect 莫属。 // ...useEffect(() => { ipcRenderer.on("HOTKEY", handleGlobalShortcut); ipcRenderer.on("DOWNLOAD", handleDownloadStatus); return () => { ipcRenderer.removeListener("HOTKEY", handleGlobalShortcut); ipcRenderer.removeListener("DOWNLOAD", handleDownloadStatus); };}, [volume]);// ...const handleGlobalShortcut = (e, hotkey) => { switch (hotkey) { case "nextTrack": handleNext(); break; case "prevTrack": handlePrev(); break; case "volumeUp": const volumeUp = volume > 0.95 ? 1 : volume + 0.05; handleVolume(volumeUp * 100); break; case "volumeDown": const volumeDown = volume < 0.05 ? 0 : volume - 0.05; handleVolume(volumeDown * 100); break; case "changePlayState": handlePlayPause(); break; default: break; }};// ...为了减少渲染次数,我会设置了第二参数为 [volume],但这会导致一些出乎意料的情况,比如我触发了changePlayState,但却并没有得到意料中的值,这个时候设置为 [volume, playState] 就正常了。 ...

May 14, 2019 · 4 min · jiezi

(应用)企业后台系统敏捷开发-dva

1.未完待续

April 19, 2019 · 1 min · jiezi

解决react dva打包报错Unexpected token: operator (>)

问题描述近期在工作中,打包react项目时控制台报了以下错误:ERROR in index.js from UglifyJsUnexpected token: operator (>) [./~/query-string/index.js:8,0]问题解决我先初步推断了一下问题:Unexpected token: operator (>) 表示不识别 > 符号/query-string/index.js 表示这个文件里面的第8行第0列出现的问题我就找这个文件,发现是node_modules目录下的,安装的query-string插件,就打开看了一下第8行的内容。return (key, value, index) => { …}发现是箭头函数的 > 没有被识别,箭头函数是ES6的语法,由此可以发现是babel的问题。该项目用的是roadhog配置的babel,我就去查看了一下roadhog的配置,想把node_modules/query-string/index.js加入babel编译里面,结果在其官网看到了以下内容:把源码放到 src 目录下,因为非 src 目录下的文件不会走 babel 编译。roadhog默认只编译src目标录下的,所以我就去找query-string的版本,发现5.0.1的版本是用的es5写的,于是把它的版本降低,经过测试后解决了这个问题。

March 12, 2019 · 1 min · jiezi

又一轮子?Typescript+React+Redux,放弃saga,支持服务器渲染同构

你是原生Redux用户?有没有觉得写Redux太繁琐了?你是dvaJS用户?有没有觉得redux-saga概念太多,且yield无法返回TS类型?试试react-coat吧:项目地址:https://github.com/wooline/react-coat// 仅需一个类,搞定 action、reducer、effect、loadingclass ModuleHandlers extends BaseModuleHandlers { @reducer protected putCurUser(curUser: CurUser): State { return {…this.state, curUser}; } @reducer public putShowLoginPop(showLoginPop: boolean): State { return {…this.state, showLoginPop}; } @effect(“login”) // 使用自定义loading状态 public async login(payload: {username: string; password: string}) { const loginResult = await sessionService.api.login(payload); if (!loginResult.error) { this.updateState({curUser: loginResult.data}); Toast.success(“欢迎您回来!”); } else { alert(loginResult.error.message); } } // uncatched错误会触发@@framework/ERROR,监听并发送给后台 @effect(null) // 不需要loading,设置为null protected async ["@@framework/ERROR"](error: CustomError) { if (error.code === “401”) { this.dispatch(this.actions.putShowLoginPop(true)); } else if (error.code === “301” || error.code === “302”) { this.dispatch(this.routerActions.replace(error.detail)); } else { Toast.fail(error.message); await settingsService.api.reportError(error); } } // 监听自已的INIT Action,做一些异步数据请求 @effect() protected async “app/INIT” { const [projectConfig, curUser] = await Promise.all([ settingsService.api.getSettings(), sessionService.api.getCurUser() ]); this.updateState({ projectConfig, curUser, }); }}react-coat 特点集成 react、redux、react-router、history 等相关框架仅为以上框架的糖衣外套,不改变其基本概念,无强侵入与破坏性结构化前端工程、业务模块化,支持按需加载同时支持 SPA(单页应用)和 SSR(服务器渲染)使用 typescript 严格类型,更好的静态检查与智能提示开源微框架,源码不到千行,几乎不用学习即可上手与 Dva 的异同引入 ActionHandler 观察者模式,更优雅的处理模块之间的协作去除 redux-saga,使用 async、await 替代,简化代码的同时对 TS 类型支持更全面原生使用 typescript 组织和开发,更全面的类型安全路由组件化、无 Page 概念、更自然的 API 和更简单的组织结构更大的灵活性和自由度,不强封装脚手架等支持 SPA(单页应用)和 SSR(服务器渲染)快速切换,支持模块异步按需加载和同步加载快速切换差异示例:使用强类型组织所有 reducer 和 effect// Dva中常这样写dispatch({ type: ‘moduleA/query’, payload:{username:“jimmy”}} })//本框架中可直接利用ts类型反射和检查:this.dispatch(moduleA.actions.query({username:“jimmy”}))差异示例:State 和 Actions 支持继承// Dva不支持继承// 本框架可以直接继承class ModuleHandlers extends ArticleHandlers<State, PhotoResource> { constructor() { super({}, {api}); } @effect() protected async parseRouter() { const result = await super.parseRouter(); this.dispatch(this.actions.putRouteData({showComment: true})); return result; } @effect() protected async ModuleNames.photos + “/INIT” { await super.onInit(); }}差异示例:在 Dva 中,因为使用 redux-saga,假设在一个 effect 中使用 yield put 派发一个 action,以此来调用另一个 effect,虽然 yield 可以等待 action 的派发,但并不能等待后续 effect 的处理:// 在Dva中,updateState并不会等待otherModule/query的effect处理完毕了才执行effects: { * query (){ yield put({type: ‘otherModule/query’,payload:1}); yield put({type: ‘updateState’, payload: 2}); }}// 在本框架中,可使用awiat关键字, updateState 会等待otherModule/query的effect处理完毕了才执行class ModuleHandlers { async query (){ await this.dispatch(otherModule.actions.query(1)); this.dispatch(thisModule.actions.updateState(2)); }}差异示例:如果 ModuleA 进行某项操作成功之后,ModuleB 或 ModuleC 都需要 update 自已的 State,由于缺少 action 的观察者模式,所以只能将 ModuleB 或 ModuleC 的刷新动作写死在 ModuleA 中:// 在Dva中需要主动Put调用ModuleB或ModuleC的Actioneffects: { * update (){ … if(callbackModuleName===“ModuleB”){ yield put({type: ‘ModuleB/update’,payload:1}); }else if(callbackModuleName===“ModuleC”){ yield put({type: ‘ModuleC/update’,payload:1}); } }}// 在本框架中,可使用ActionHandler观察者模式:class ModuleB { //在ModuleB中兼听"ModuleA/update" action async [“ModuleA/update”] (){ …. }}class ModuleC { //在ModuleC中兼听"ModuleA/update" action async [“ModuleA/update”] (){ …. }}遵循规则:M 和 V 之间使用单向数据流整站保持单个 StoreStore 为 Immutability 不可变数据改变 Store 数据,必须通过 Reducer调用 Reducer 必须通过显式的 dispatch ActionReducer 必须为 pure function 纯函数有副作用的行为,全部放到 Effect 函数中每个 reducer 只能修改 Store 下的某个节点,但可以读取所有节点路由组件化,不使用集中式配置快速上手及 Demo本框架上手简单8 个新概念:Effect、ActionHandler、Module、ModuleState、RootState、Model、View、Component4 步创建:exportModel(), exportView(), exportModule(), createApp()3 个 Demo,循序渐进:入手:Helloworld进阶:SPA(单页应用)升级:SPA(单页应用)+SSR(服务器渲染) ...

February 28, 2019 · 2 min · jiezi

dva中组件的懒加载

组件的按需加载是提升首屏性能的重要手段。dva@2.0使用了react-router@4.0,相关的使用方式有变化,网上的讨论基本上都是旧的,不清楚的话会比较乱,这里做一下记录。dva@2.0以前dva@2.0以前的懒加载相关讨论有不少,可以参考dva-example-user-dashboard中的写法,徐飞大佬的文章使用 Dva 开发复杂 SPA,本质上借助的是webpack的require.ensure实现代码分割,参考代码分割 - 使用 require.ensure。具体实现形如:function RouterConfig({ history, app }) { const routes = [ { path: ‘/’, name: ‘IndexPage’, getComponent(nextState, cb) { require.ensure([], (require) => { registerModel(app, require(’./models/dashboard’)); cb(null, require(’./routes/IndexPage’)); }); }, }, { path: ‘/users’, name: ‘UsersPage’, getComponent(nextState, cb) { require.ensure([], (require) => { registerModel(app, require(’./models/users’)); cb(null, require(’./routes/Users’)); }); }, }, ]; return <Router history={history} routes={routes} />;}dva@2.0以后dva@2.0使用了react-router@4.0,其中的路由是组件式的,原来的方式就不太好搞。因此dva提供了一个dynamic函数来处理。在dva@2.0发布日志和dva api文档中有介绍。具体实现形如:import dynamic from ‘dva/dynamic’;function RouterConfig({ history,app }) { const UserPageComponent = dynamic({ app, models: () => [ import(’./models/users’), ], component: () => import(’./routes/UserPage’), }); return ( <Router history={history}> <Switch> <Route path="/user" component={UserPageComponent} /> <Route component={IndexPageComponent} /> </Switch> </Router> );}export default RouterConfig; ...

February 16, 2019 · 1 min · jiezi

roadhog+dva中环境变量的配置

有时候我们在代码里需要根据环境变量来决定一些逻辑。常见的比如,在测试环境访问的后端url跟正式环境是不一样的。不依赖框架的话,应当是基于webpack的define-plugin实现。如文档中所示的:new webpack.DefinePlugin({ PRODUCTION: JSON.stringify(true), VERSION: JSON.stringify(‘5fa3b9’), BROWSER_SUPPORTS_HTML5: true, TWO: ‘1+1’, ’typeof window’: JSON.stringify(‘object’)});回到dva。dva核心是个基于redux封装的数据流方案,也可以当成一个轻量级框架。从框架的角度来讲,它其实很轻很轻,几乎没怎么管数据流之外的事情,只是简单地集成了少许几个库形成一个框架。roadhog是个服务于框架的命令行工具,主要就是提供dev、build 和 test 等命令,屏蔽了webpack的复杂配置,提供了自己的相对简单的配置能力。显然,这里的变量配置应当由roadhog来处理。不要像我当初一样觉得dva是个框架就应该有相关功能_(:ゝ∠)_善用搜索引擎,从roadhog文档 - define和相关讨论容易找到方案。roadhog提供了define选项做DefinePlugin的事情。具体使用:编辑.webpacrc.jsexport default { define: { ‘process.env’: {}, ‘process.env.NODE_ENV’: process.env.NODE_ENV, ‘process.env.API_ENV’: process.env.API_ENV, },}

February 16, 2019 · 1 min · jiezi

(踩坑回忆录)Dva踩坑与解决方案

前言问题antd-pro组件的使用小结未完待续…持续追更…

January 11, 2019 · 1 min · jiezi

react+antd系列之Form表单(1):添加与删除

在用antd的时候,我们如果要对表单进行添加和删除该怎么弄呢,如下:import { connect } from ‘dva’;import { Form, Input, Button } from ‘antd’;import styles from ‘./eg1.css’;const FormItem = Form.Item;function Page(props) { const { form } = props; const { getFieldDecorator, getFieldValue } = form // 表单提交 const handleSubmit = (e) => { e.preventDefault(); form.validateFields((err, values) => { if (!err) { console.log(values); } }); } // 添加 const add = () => { const list = form.getFieldValue(’list’); const nextList = list.concat({}); form.setFieldsValue({ list: nextList, }); } // 删除 const deleteRow = (index) => { const list = form.getFieldValue(’list’); const content = form.getFieldValue(‘content’); if (list.length === 1) { return; } form.setFieldsValue({ list: list.filter((item, key) => key !== index), content: content.filter((item, key) => key !== index), }); } getFieldDecorator(’list’, { initialValue: [{}] }); const list = getFieldValue(’list’); const listContent = list.map((item, index) => { return ( <FormItem label=‘名称:’ key={index}> {getFieldDecorator(content[${index}].name, { rules: [{ required: true, message: “名称不能为空!”, }], })( <Input placeholder=“请输入名称” style={{ width: ‘60%’, marginRight: 8 }} /> )} {index > 0 ? ( <Button type=“primary” onClick={deleteRow.bind(this, index)}>删除</Button> ) : null} </FormItem> ); }); return ( <div className={styles.normal}> <Form onSubmit={handleSubmit}> {listContent} <FormItem> <Button type=“primary” htmlType=“submit”>提交</Button> <Button type=“primary” style={{ marginLeft: ‘10px’ }} onClick={add}>增加</Button> </FormItem> </Form> </div> );}const page = Form.create()(Page);export default connect()(page);这里不仅能对表单进行增加和删除,还能对表单进行验证,看是否有输入,以上是本身没有数据的情况,如果是有数据的情况如下:import React from ‘react’;import { connect } from ‘dva’;import { Form, Input, Button } from ‘antd’;import styles from ‘./eg2.css’;const FormItem = Form.Item;function Page(props) { const { form } = props; const { getFieldDecorator, getFieldValue } = form // 表单提交 const handleSubmit = (e) => { e.preventDefault(); form.validateFields((err, values) => { if (!err) { console.log(values); } }); } // 添加 const add = () => { const list = form.getFieldValue(’list’); const nextList = list.concat({}); form.setFieldsValue({ list: nextList, }); } // 删除 const deleteRow = (index) => { const list = form.getFieldValue(’list’); const content = form.getFieldValue(‘content’); if (list.length === 1) { return; } form.setFieldsValue({ list: list.filter((item, key) => key !== index), content: content.filter((item, key) => key !== index), }); } const slist = [{ id:‘0001’, name: ‘黎明’ }, { id:‘0002’, name: ‘晴天’ }] getFieldDecorator(’list’, { initialValue: slist }); const list = getFieldValue(’list’); const listContent = list.map((item, index) => { getFieldDecorator(content[${index}].id, {initialValue: item.id || ‘’}) return ( <FormItem label=‘名称:’ key={index}> {getFieldDecorator(content[${index}].name, { rules: [{ required: true, message: “名称不能为空!”, }], initialValue: item.name || ’’ })( <Input placeholder=“请输入名称” style={{ width: ‘60%’, marginRight: 8 }} /> )} {index > 0 ? ( <Button type=“primary” onClick={deleteRow.bind(this, index)}>删除</Button> ) : null} </FormItem> ); }); return ( <div className={styles.normal}> <Form onSubmit={handleSubmit}> {listContent} <FormItem> <Button type=“primary” htmlType=“submit”>提交</Button> <Button type=“primary” style={{ marginLeft: ‘10px’ }} onClick={add}>增加</Button> </FormItem> </Form> </div> );}const page = Form.create()(Page);export default connect()(page);一般如果本身有数据,都会有每行数据的id,但是这个id不显示,我们都会用getFieldDecorator给id声明,这样在我们提交表单的时候,就可以得到表单抓取到id的数据,有数据跟没有数据的差别就是,有数据需要在表单getFieldDecorator的时候给一个初始值,其他两者都一样具体代码下载地址:https://gitee.com/hope93/antd… ...

December 18, 2018 · 2 min · jiezi

初识React(9):dva简介

前言dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架。dva官网地址:https://dvajs.com/import dva from ‘dva’;// 1. Initializeconst app = dva();// 2. Pluginsapp.use({});// 3. Modelapp.model(require(’./models/example’).default);// 4. Routerapp.router(require(’./router’).default);// 5. Startapp.start(’#root’);dva仅有6个api,如下介绍:1. const app = dva(options)创建应用,返回dva实例options中包含:(1) history:默认为hashHistory,如果要配置history为browserHistory,则import createHistory from ‘history/createBrowserHistory’;const app = dva({ history: createHistory(),});(2) initialState: 指定初始数据,优先级高于model中的state,默认为{}(3) onError: 管理全局出错状态,如下:const app = dva({ onError(e){ console.log(e); }});(4) onAction(fn | fn[]): 在action被dispatch时触发,用于注册redux中间件,支持函数格式或者函数数组格式,如下通过redux-logger答应日志,如:import createLogger from ‘redux-logger’;const app = dva({ onAction: createLogger(opts),});(5) onStateChange(fn): state改变时触发,可用于同步state 到 localStorage,服务器端等(6) onReducer(fn): 封装 reducer 执行。比如借助 redux-undo 实现 redo/undo :import undoable from ‘redux-undo’;const app = dva({ onReducer: reducer => { return (state, action) => { const undoOpts = {}; const newState = undoable(reducer, undoOpts)(state, action); // 由于 dva 同步了 routing 数据,所以需要把这部分还原 return { …newState, routing: newState.present.routing }; }, },});(7) onEffect(fn): 封装 effect 执行。比如 dva-loading 基于此实现了自动处理 loading 状态。(8) onHmr(fn): 热替换相关,目前用于 babel-plugin-dva-hmr(9) extraReducers: 指定额外的 reducer,比如 redux-form 需要指定额外的 form reducerimport { reducer as formReducer } from ‘redux-form’const app = dva({ extraReducers: { form: formReducer, },});(10) extraEnhancers: 指定额外的 StoreEnhancer ,比如结合 redux-persist 的使用import { persistStore, autoRehydrate } from ‘redux-persist’;const app = dva({ extraEnhancers: [autoRehydrate()],});persistStore(app._store);2.app.use(hooks)配置 hooks 或者注册插件。(插件最终返回的是 hooks )比如注册 dva-loading 插件的例子:import createLoading from ‘dva-loading’;…app.use(createLoading(opts));hooks 包含2中(3)到(10)3.app.model(model)注册modelmodel 是 dva 中最重要的概念,以下是典型的例子:app.model({ namespace: ’todo’, state: [], reducers: { add(state, { payload: todo }) { // 保存数据到 state return […state, todo]; }, }, effects: { save({ payload: todo }, { put, call }) { // 调用 saveTodoToServer,成功后触发 add action 保存到 state yield call(saveTodoToServer, todo); yield put({ type: ‘add’, payload: todo }); }, }, subscriptions: { setup({ history, dispatch }) { // 监听 history 变化,当进入 / 时触发 load action return history.listen(({ pathname }) => { if (pathname === ‘/’) { dispatch({ type: ’load’ }); } }); }, },});model 包含 5 个属性:namespace: model 的命名空间,同时也是他在全局 state 上的属性,只能用字符串,不支持通过 . 的方式创建多层命名空间。state: 初始值,优先级低于传给 dva() 的 opts.initialState,如下:const app = dva({ initialState: { count: 1 },});app.model({ namespace: ‘count’, state: 0,});此时,在 app.start() 后 state.count 为 1 reducers: 以 key/value 格式定义 reducer。用于处理同步操作,唯一可以修改 state 的地方。由 action 触发,格式为 (state, action) => newState 或 [(state, action) => newState, enhancer]effects: 以 key/value 格式定义 effect。用于处理异步操作和业务逻辑,不直接修改 state。由 action 触发,可以触发 action,可以和服务器交互,可以获取全局 state 的数据等等。格式为(action, effects) => void 或 [*(action, effects) => void, { type }]。subscriptions: 以 key/value 格式定义 subscription。subscription 是订阅,用于订阅一个数据源,然后根据需要 dispatch 相应的 action。在 app.start() 时被执行,数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。格式为 ({ dispatch, history }, done) => unlistenFunction。注意:如果要使用 app.unmodel(),subscription 必须返回 unlisten 方法,用于取消数据订阅。4.app.unmodel(namespace)取消 model 注册,清理 reducers, effects 和 subscriptions。subscription 如果没有返回 unlisten 函数,使用 app.unmodel 会给予警告5.app.router(({ history, app }) => RouterConfig)注册路由表。通常是这样的:import { Router, Route } from ‘dva/router’;app.router(({ history }) => { return ( <Router history={history}> <Route path="/" component={App} /> <Router> );});推荐把路由信息抽成一个单独的文件,这样结合 babel-plugin-dva-hmr 可实现路由和组件的热加载,比如:app.router(require(’./router’));而有些场景可能不使用路由,比如多页应用,所以也可以传入返回 JSX 元素的函数。比如:app.router(() => <App />);6.app.start(selector)启动应用。selector 可选,如果没有 selector 参数,会返回一个返回 JSX 元素的函数。app.start(’#root’);那么什么时候不加 selector?常见场景有测试、node 端、react-native 和 i18n 国际化支持。比如通过 react-intl 支持国际化的例子:import { IntlProvider } from ‘react-intl’;…const App = app.start();ReactDOM.render(<IntlProvider><App /></IntlProvider>, htmlElement);本文参考官网:https://dvajs.com/api/#dva-api ...

December 18, 2018 · 2 min · jiezi

react+antd系列之Form表单(2):格式限制验证

格式限制antd中表单的功能很多,下面就为大家整理了一下antd中常用的几种表单输入格式验证:1. 输入框不能为空限制,如下: {getFieldDecorator(’name’, { rules: [{ required: true, message: ‘名称不能为空’, }], })( <Input placeholder=“请输入名称” /> )}2. 输入框字符限制,如下:字符长度范围限制: {getFieldDecorator(‘password’, { rules: [{ required: true, message: ‘密码不能为空’, }, { min:4, message: ‘密码不能少于4个字符’, }, { max:6, message: ‘密码不能大于6个字符’, }], })( <Input placeholder=“请输入密码” type=“password”/> )}字符长度限制: {getFieldDecorator(’nickname’, { rules: [{ required: true, message: ‘昵称不能为空’, }, { len: 4, message: ‘长度需4个字符’, }], })( <Input placeholder=“请输入昵称” /> )}3. 自定义校验 {getFieldDecorator(‘passwordcomfire’, { rules: [{ required: true, message: ‘请再次输入密码’, }, { validator: passwordValidator }], })( <Input placeholder=“请输入密码” type=“password”/> )} // 密码验证 const passwordValidator = (rule, value, callback) => { const { getFieldValue } = form; if (value && value !== getFieldValue(‘password’)) { callback(‘两次输入不一致!’) } // 必须总是返回一个 callback,否则 validateFields 无法响应 callback(); }validator属性自定义效验,必须返回一个callback4.whitespace空格报错 {getFieldDecorator(‘hobody’, { rules: [{ whitespace: true, message: ‘不能输入空格’, } ], })( <Input placeholder=“请输入昵称” /> )}若输入只有一个空格,则会报错5.pattern正则验证 {getFieldDecorator(‘qbc’, { rules: [{ message:‘只能输入数字’, pattern: /^[0-9]+$/ } ], })( <Input placeholder=“请输入ABC” />)}如果输入的不是数字,则提示错误完整代码地址:https://gitee.com/hope93/antd… ...

December 18, 2018 · 1 min · jiezi

小白在react+dva+antd中踩的一些坑

记录下来,一来给自己提醒,二来帮助一些朋友们解决问题。大部分都是很傻的坑,但就是踩了,orz样式方面1)、使用antd的table 想要改变td的样式,比如想要显示不完时省略 下图中2是官方文档的写法,当我们想要改变td样式时可以自己写一个render,见12.逻辑方面用的是dva,先简要概括一些model里各个的用法: namespace:唯一 state:初始化数据 subscriptions:路由变化,拿页面最初始数据 监听 effects:请求数据 处理异步action reducers: 更新数据 1)、想向后端发送一个请求,得到返回值后再提交另一个请求,像promise.then一样。 错误思路: 一开始很傻地直接前后写了两个dispatch,结果错误,虽然分前后顺序调用了接口(dispatch是同步执行的),可是并没有根据第一个dispatch的结果动态调用第二个接口。 解决方法: 在model里的effects中相关的函数里调用第二个接口所以使用effects的put来触发action。首先用dispatch向后端发起第一个请求,接着在effects里addAddressable函数里调用第二个请求model.js:2)、如何在effects里获取state里维护的值如上图,payload里需要sate里的deviceName、description等根据前面的图应该知道应该用select可是网上大概有两种方法:const todos = yield select(state => state.todos);const {id} = yield select(=>.storeIf) storeIf 是model的namespace我使用第一种获取不到,原因还不知道。所以如果法一获取不到,不妨用法二const {deviceName,description,templateDetail} = yield select(=>.device);3)、如何在render里动态渲染不同的div?在render中使用if else会报错解决:使用三元运算符 a?b:ca是一个触发不同div的条件,b是一个div,c是另一个div4)、根据后端数据动态渲染input数量,并且把输入值存到state中接受到的数据肯定是一个对象,类似:{{ a:1},{ b:2}}用map遍历得到a,b。但接下来一个问题是动态存入数据。解决:把后端可能要显示的字段都先在model的state中设置初始值。我这里是可能显示port或者address.在state写:{ address:"", port:0}通过[] 可以动态存入port或者address暂时遇到的就是这样了,其实准确来说也不是坑,是我在学习这个上的比较费时间的一些东西。发出来就是希望大家百度问题时看到,尽快解决,少花点时间。

December 17, 2018 · 1 min · jiezi

从项目中由浅入深的学习vue,react,微信小程序和快应用(1)

**才见岭头云似盖,已惊岩下雪如尘。—《南秦雪》**前言这几天好多地方都下雪了,雪花真美呀,特地在网上搜上好看的图片和诗句写上。本文主要从template【模板】讲到一个demo,快速上手vue、react和微信小城序的项目开发。如果你不熟悉这中间的某一个技术栈,可以clone下来跑一跑。 如果全部能上手,中间有些细节耶可以看看。开撸1.template篇1.1 vue-template-pc1.效果图vue-template-pc项目,欢迎star2.技术栈vue+vue-router+vuex+axios+element-UI+iconfont(阿里)3.适配方案左侧固定宽度,右侧自适应左侧导航和右侧导航分别配置滚动条4.技能点分析技能点对应api常用指令@(v-on)绑定事件, v-if/v-show是否创建/和是否显示,v-for循环生命周期8个生命周期beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy和destroy观察计算computed和watchdata属性定义变量,同样变量使用必须先定义组件注册components局部注册,Vue.component()全局注册组件通讯子传父:this.$emit,父传子:props,平级组件:vuex或路由传参插件注册Vue.use()注册插件,如Vue.use(element)是调用element内部的install方法路由注册vue-router:Vue.use(router)也是调用内部的install方法,挂载到vue实例中生成route和router属性路由模式mode属性可以设置history和hash子路由children:[]可以配置子路由路由钩子router.beforeEach(实现导航钩子守卫)和router.afterEachvuex4个属性,state,getters, actions(异步获取数据)和mutations(同步获取数据)vuex4个辅助函数,mapState,mapGetters, mapActions和mapMutations,就是辅助处理commit或distapch方法axios拦截器,interceptors.request请求拦截器,interceptors.response响应拦截器axiosbaseUrl配置公共请求路径,必须符合http标准的链接,否则设置无效axios请求方法,get,post,put,delete等axios跨域,withCredentials: true,需要后端支持csssass,对应嵌套不超过三层,滚动条样式设置,文本两行超出build问题iconfont阿里字体图标,可以自定义icon5.那么问题来了?computed和watch的区别? 解析路由传参的方法? 解析vue.use,vue.install,mixins方法区别? 解析history和hash区别及实现原理? 区别解析原理解析vue-router官网使用history和hash模式部署服务器有什么问题?问题解析vuex的辅助函数和基本属性使用的区别?vuex官网axios原理?axios源码简单实现一个vue+vue-router+vuex的框架?1.2 react-pc-template1.效果图react-pc-template项目, 欢迎star2.技术栈dva+umi+ant-design-prodva:可拔插的react应用框架,基于react和reduxmui:集成react的router和reduxant-design-pro:基于react和ant-pc的中后台解决方案3.适配方案左侧固定宽度,右侧自适应右侧导航分别配置滚动条.控制整个page4.技能点分析技能点对应apiJSXreact是基于jSX语法生命周期实例化(5个):getDefaultProps,getInitialState,componentWillMount,render,componentDidMount生命周期更新:5个生命周期生命周期销毁:componentWillUnmout路由基于umi,里面有push,replace,go等方法状态管理dva里面的redux的封装,属性有state,effects,reducers组件传值父子:props,平级redux或umi的routermodel项目的model和dom是通过@connect()连接并将部分属性添加到props里登陆登陆是通过在入口js里面做路由判断5.那么问题来了?umi的router传参形式? 解析dva封装的redux和原生的redux使用有那些不同? dva使用解析redux使用解析umi里面router实现原理?umi源码对比vue和react在原理和使用上的区别?1.3 vue-mobile-template移动端代码见demo篇1.4 小程序模板由于小程序的IDE里面有生成的模板,mobile也是基于vue,所以只在demo篇展示demo1.5 快应用模板1.template代码实现官方template生成教程2.技能点分析技能点对应api布局基于弹性布局指令for:循环,if、show生命周期页面的生命周期:onInit、onReady、onShow、onHide、onDestroy、onBackPress、onMenuPressapp生命周期onCreate、onDestroy事件$on、$off、$emit、$emitElement路由配置manifest文件的router属性配置路由跳转router.page组件通讯父子组件:$emit,props,兄弟组件:通过 Publish/Subscribe 模型原生组件list,map,tabs和canvas消息机制websocket使用2.demo篇2.1 vue-demo(vue-pc-demo)1.效果图vue-demo项目地址, 欢迎star2.技术栈vue+vue-router+vuex+axios+element-UI+高德map+vue-split-table高德map:高德地图vue-split-table:表格拆分插件,vue-split-table插件3.适配方案同上4.技能点分析比template篇多了map的使用,高德使用手册实现axios的api模块化,并全局挂载api和axios所以由此可以看出只要有了template,后期开发so-easy,只是新加tab页2.2 react-pc-demo参考ant的ant-design-pro项目2.3 vue-mobile-demo1.效果图vue-mobile项目2.技术栈vue+vue-router+vuex+vant+rem+sass+iconfont(阿里)vant:有赞的电商mobile插件3.适配方案rem4.技能点分析iconfont的使用:官网配置icon,导出图标,引入assets目录下vant使用:详见vant官网全局配置rem:在index.html文件配置全局配置sass函数和mixin:在build/utils下面的scss的options属性配置static目录下面的函数和混入5.那么问题来了vue-cli生成的项目src下面的assets和根路径下面的static目录的区别?解析2.4 小程序demo1.效果min-program-demo项目,欢迎star2.技术栈weui+tabbar+分包+iconfont+自定义顶部导航+组件传值+wx.request封装weui:Tencent推出的小程序UI3.适配方案rpx:微信小程序的单位4.技能点分析技能点对应api常用指令bindtap绑定事件, wx:if/wx:show是否创建/和是否显示,wx:for循环生命周期1应用生命周期(app.js里):launch,show,hide生命周期2页面生命周期(page里):load,show,ready,hide,unload生命周期3组件周期(component里):created,attached,moved,detachedwx.requestajax请求背景音乐wx.getBackgroundAudioManager音频wx.createAudioContext图片wx.chooseImage文件wx.getFileInfo路由在app.json里面pages属性定义pages目录下面的文件路由切换wx.navigateTo,wx.navigateBack, wx.redirectTo,wx.switchTab,wx.reLaunch分包在app.json里面subPackages属性定义分包路由weui组件weui官网原生组件微信原生组件业务组件在json文件usingComponents注册组件通讯定义globalData,storage和路由5.那么问题来了小程序的生命周期执行顺序?page和应用生命周期 , component生命周期解释几种路由切换有什么不同?路由介绍小程序怎么实现watch监听数据变化?实现watch6.小程序框架wepy官网基于wepy的商城项目mpVue基于mpVue的项目分析:这两个框架都是通过预编译将对应风格的格式转化成小程序格式2.5 快应用demo类似书单项目的快应用3.结语对比下vue,react,微信小程序和快应用这几种技术栈开发,可以分为两类,一类是mvvm式的开发:vue,微信小程序和快应用一类是基于JSX的view开发

December 10, 2018 · 1 min · jiezi