cef编译

之前用cef用的官方编译后的, 下载下载cmake一下, 然后生成visual studio解决方案, 就行了, 但是在最近的项目中, 需要用到MP3和MP4的支持, 网上下载的都是不全的, 现在想自己编译一下, 由于长城防火墙, 屏蔽了国外的技术网站, 导致了我一直不想从源代码开始编译。现在正式进入主题吧

我是用的亚马逊服务器
200g硬盘
1核,2g
windows server 2016
64位,英文环境
python2.7
git
svn
基础环境

CEF_ARCHIVE_FORMAT=tar.bz2
DEPOT_TOOLS_WIN_TOOLCHAIN=0
CEF_USE_GN=0
GYP_DEFINES=buildtype=Official
GYP_MSVS_VERSION=2013(根据你的VS版本设置)
GYP_GENERATORS=ninja,msvs-ninja
DEPOT_TOOLS_UPDATE=0

python automate-git.py –download-dir=c:cefsource –branch=2623 –no-build –no-distrib –force-clean

python automate-git.py –download-dir=c:cefcefsource –branch=2623 –no-update –no-debug-build –build-log-file –verbose-build –force-distrib –force-build

vs2013.5_ult_enu.iso
windows 10 sdk

交互:
https://www.bbsmax.com/A/xl56rALm5r/

ant design pro 笔记二

学习笔记(2)

摘要介绍

在学习Ant Design Pro的过程中遇到了很多的新知识。针对一些要点做简单的梳理和摘要介绍。

关于异步调用之Promise

以我对前端的粗浅理解,每每提到异步调用我一定会想到ajax,这是我最早接触异步调用/同步调用概念的地方。事实上在js中通常使用的做法是回掉函数(callback),目前在我看来,Promise就像是对callback的一重包装。

用定义来说就是:Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。(引自ECMAScript6入门)。接下来摘要说下Promise有关的要点:

  1. 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
  3. then:Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。
  4. catch:Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。
  5. all:Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
  6. race:Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
  7. resolve:有时需要将现有对象转为 Promise 对象,Promise.resolve方法就起到这个作用。
  8. reject:Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
  9. done:Promise 对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为 Promise 内部的错误不会冒泡到全局)。因此,我们可以提供一个done方法,总是处于回调链的尾端,保证抛出任何可能出现的错误。
  10. finally:finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。它与done方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。

再介绍几个Promise有关的QA:

  1. resolve后如何返回相关数据?直接resolve(result)就可以将结果返回。
  2. Promise是不是只跟前后台交互有关?首先Promise确实跟ajax相关,但Promise本身是一种异步编程的解决方案,可以用在前后台交互上,也可以用在页面交互逻辑上。

关于异步调用之加密请求示例

在进行前后台交互时,尤其是提交如:密码数据时,由于网络是不安全的传输,因此需要对密码数据进行加密。在实现中使用的是RSA加密算法,原理见这里这里。简单来说,客户端(浏览器)在进行需加密数据提交时,需要拿到公钥(publicKey),这需要提前发起一个申请公钥的过程。

浏览器需要提交加密数据:先请求pk、用收到的pk加密数据、再提交数据。再进一步分解:

  1. 浏览器:待加密数据、加密方法
  2. 请求pk:浏览器-(发起请求pk的请求)->服务器
  3. 收到pk:服务器-(pk)->浏览器
  4. 浏览器:待加密数据、pk、加密方法
  5. 加密数据:待加密数据-加密方法、pk->加密后数据
  6. 浏览器:加密后数据
  7. 提交数据:浏览器-加密后数据->服务器
  8. 确认提交:服务器-响应->浏览器

由此暴露出来的接口就是function encryptPost(url, pkEncrypt, options)。其中url就是真正的提交数据的url、pkEncrypt就是加密方法、options就是要提交的数据们(包括参数等)。

在实现的时候,requestPK(pkEncrypt,options).then((newOptions)=>request(url,newOptions));。其中requestPK就是请求pk并将参数进行加密,同时将加密后的数据封装在Promise中传递出来,在then中将新的参数传递给真正的请求。详细的封装过程参见源码request.js,注意options是一个json对象其结构大约是{method:’POST’,body:params},因此真正的参数在body中,因此在构造新的options时使用了{...options,body:pkEncrypt(pk.result,options.body)}写法。

再介绍pkEncrypt,这是加密函数,它有两个参数,第一个是pk,第二个是params,因此实现时需要根据需要进行相应的实现。

进一步介绍加密时的封装。先定义公钥对明文加密的原子方法pkEncryptPlain,其入参是pk和明文,返回值是密文
。这并不能直接放入encryptPost中的pkEncrypt位置,毕竟pkEncrypt的入参是pk和options.body,这里进一步对入参进行约束,将入参分成params和encryptFields两部分,其中params是真正的入参而encryptFields是需要加密的入参。这样一来就可以进一步封装出pkEncryptBody函数,配合入参payload就能够正常使用了。

总结来看,任何一个需要进行加密提交的请求都可以通过如下方式进行,定义入参payload,由两部分组成:params、encryptFields,同时指定post的url。以login为例,调用的范例就是:

export async function usernameLogin(payload){
  const {params,encryptFields} = payload.payload;
  return encryptPost(`/security/login.go`,
    (pk,body)=>pkEncryptBody(encryptFields,pk,body),
    {method:'POST',body:params,}
  );
}

常用的链接:

ant design pro 笔记

学习笔记

摘要介绍

Ant Design Pro 是一个基于Ant Design搭建起来的模板项目。它提供了两个主要布局:BasicLayoutUserLayout,在布局基础上制作了20多个基础页面,详情见模板介绍段落。

Ant Design Pro 主体代码使用ES2015+语法规则,因此在阅读源码时遇到不理解的语法,可前往相关主页进行查询。在学习笔记中,除非遇到关键的语法点,否则不对语法进行额外说明。

在接下来,将按照以下几个方面介绍项目的相关代码。
……

目录结构

├── mock                     # 本地模拟数据
├── node_modules             # 依赖库
├── public
│   ├── favicon.ico          # Favicon
│   └── index.html           # HTML 入口模板
├── src
│   ├── common               # 应用公用配置,如导航信息
│   ├── components           # 业务通用组件
│   ├── e2e                  # 集成测试用例
│   ├── layouts              # 通用布局
│   ├── models               # dva model
│   ├── routes               # 业务页面入口和常用模板
│   ├── services             # 后台接口服务
│   ├── utils                # 工具库
│   ├── g2.js                # 可视化图形配置
│   ├── polyfill.js          # 兼容性垫片
│   ├── theme.js             # 主题配置
│   ├── index.js             # 应用入口
│   ├── index.less           # 全局样式
│   └── router.js            # 路由入口
├── tests                    # 测试工具
├── .editorconfig            # 编辑器配置
├── .eslintrc                # js代码检测工具
├── .ga                      # 未知
├── .gitignore               # git版本配置
├── .roadhogrc               # roadhog配置
├── .roadhogrc.mock.js       # roadhog的模拟配置
├── .stylelintrc             # css代码审查配置
├── .travis.yml              # travis持续构建工具配置
├── package.json             # web前端项目配置文件
├── README.md
└──

项目中的几项配置文件

Q:为什么要用编辑器配置

A:当多人共同开发一个项目的时候,往往会出现大家用不同编辑器的情况。就前端开发者来说,有人喜欢 Sublime,有人喜欢 Webstorm , 也有人喜欢 Atom,还有人喜欢 Vim,HBuilder 等等。各种不同编程语言的开发者喜欢各种不同的编辑器。问题来了,如何让使用不同编辑器的开发者在共同开发一个项目时“无痛”地遵循编码规范(编码风格)?

_
Q:怎么用编辑器配置

A:在项目根创建一个名为 .editorconfig 的文件。安装与编辑器对应的 EditorConfig 插件。详情参考编辑器配置

_
Q代码检查工具是什么?

A:js的代码检查工具是ESLint,css的代码检查工具是StyleLint,相应的配置项保存在.eslintrc.stylelintrc文件中。一般来说,都不需要进行修订,深入学习可参考js代码检测工具css代码审查配置

_
Qgit是什么?

Agit是版本控制工具,类似的工具还有svn。有关.gitignore的配置参考git版本配置

_
Q:什么是持续集成系统

A:对个人而言,就是让你的代码在提交到远程——这里是GitHub——后,立即自动编译,并且在失败后可以自动给你发邮件的东西。当然,除了编译,还能做自动化测试、自动部署等。对团队或企业而言,这意味着更多的东西,是敏捷开发的一种践行。travis是一种持续集成工具,企业中还有用jenkins的。有关travis的更多介绍见travis持续构建工具配置

_
Q:什么是roadhog

A:这是一个 cli 工具,提供 serverbuildtest 三个命令,分别用于本地调试和构建。更多介绍见roadhog配置

_
Q:web前端项目的配置是package?

A:每个web前端项目的根目录下面,一般都有一个 package.json 文件,定义了这个项目所需要的各种模块,以及项目的配置信息(比如名称、版本、许可证等元数据)。更多介绍见web前端项目配置文件

_
Q:最关键,初学者应着重关心哪些

A:初学者应将精力放在package.jsonroadhog这两者上,其他的都可以忽略掉。

package.json摘要介绍

npm install命令根据这个package.json配置文件,自动下载所需的模块,也就是配置项目所需的运行和开发环境。package.json文件就是一个JSON对象,该对象的每一个成员就是当前项目的一项设置。

scripts指定了运行脚本命令的npm命令行缩写,比如start指定了运行npm run start时,所要执行的命令。

进行前端开发时可能经常用到的几个命令是:npm run start(进行开发调试,缩写成npm start)、npm run build(进行构建打包)。当然也可以尝试package.json中给出的其他命令,如:npm run analyzenpm run test等,甚至也可以自己在package.json中添加需要的命令。

dependencies字段指定了项目运行所依赖的模块,devDependencies指定项目开发所需要的模块。它们都指向一个对象。该对象的各个成员,分别由模块名和对应的版本要求组成,表示依赖的模块及其版本范围。更多信息参见web前端项目配置文件

babel配置项,其实是有关babel的配置内容,写在package.json中实际上是一种省略,也可以写在.babelrc文件中。

lint-staged是与代码检查有关的配置项,jest是与单元测试有关的配置项。这两项的配置可以参考jestlint。这两项与写业务逻辑代码没有直接关联。

roadhog摘要介绍

roadhog 是一个 cli 工具,提供 serverbuildtest 三个命令,分别用于本地调试和构建,并且提供了特别易用的 mock 功能。命令行体验和 create-react-app 一致,配置略有不同,比如默认开启 css modules,然后还提供了 JSON 格式的配置方式。

重点介绍roadhog有关的几个配置项,主要是在ant design pro的代码中用到了这些配置项。

entry

指定 webpack 入口文件,支持 glob 格式。

如果你的项目是多页类型,会希望把 src/pages 的文件作为入口。可以这样配:

"entry": "src/pages/\*.js"
env

针对特定的环境进行配置。server 的环境变量是 developmentbuild 的环境变量是 production

比如:

"extraBabelPlugins": ["transform-runtime"],
"env": {
  "development": {
    "extraBabelPlugins": ["dva-hmr"]
  }
}

这样,开发环境下的 extraBabelPlugins["transform-runtime", "dva-hmr"],而生产环境下是 ["transform-runtime"]

"env": {
  "development": {
    "extraBabelPlugins": [
      "dva-hmr",
      "transform-runtime",
      "transform-decorators-legacy",
      "transform-class-properties",
      ["import", { "libraryName": "antd", "style": true }]
    ]
  },
  "production": {
    "extraBabelPlugins": [
      "transform-runtime",
      "transform-decorators-legacy",
      "transform-class-properties",
      ["import", { "libraryName": "antd", "style": true }]
    ]
  }
}

在这段代码中,开发环境和生产环境分别配置,其中开发环境使用了dva-hmr插件,

externals

配置 webpackexternals 属性。

theme

配置主题,实际上是配 lessmodifyVars。支持 Object 和文件路径两种方式的配置。

比如:

"theme": {
  "@primary-color": "#1DA57A"
}

或者,

"theme": "./node_modules/abc/theme-config.js"

这里有 如何配置 antd theme 的例子

babel

上述多处提到了babel,因此有必要针对babel进行了解。用官方的用语:Babel 是一个 JavaScript 编译器。今天就来用下一代 JavaScript 语法写代码吧!。换句话说:Babel是一个广泛使用的转码器,可以将ES6代码转为ES5代码,从而在现有环境执行。

既然如此,接下来重点说说package.json.roadhogrc中有关babel的配置内容:

presets字段用来设定转码规则,如package.json

"babel": {
  "presets": [
    "env",
    "react"
  ],
  ...
}

就是设定了envreact两个转码规则。其中env指的是babel-preset-env,这是一个新的 preset,可以根据配置的目标运行环境(environment)自动启用需要的 babel 插件。react指的是babel-preset-react,主要是针对所有react插件的转码规则。

plugins配置项不用多说,从名称就能看出来,就是babel的一系列插件,与ant design propackage.json相关的两个插件在这里:transform-decorators-legacytransform-class-properties。而在.roadhogrc配置文件中也额外配置了babel的插件:transform-runtimedva-hmrbabel-plugin-import。其中唯一需要多说一点的是按需加载插件babel-plugin-import,它可以针对antd插件中的部分元素样式进行按需加载。

其实不用更多了解相关内容,只需知道如此配置之后,1.开发者可以使用最新的ES6书写代码;2.放心使用react相关插件;3.在进行run startrun build时,代码能够自动转换成ES5代码,从而在浏览器中执行。

项目中的几个目录

其中mock是本地模拟数据目录、node_modules是依赖库目录、public是入口目录、src是源码目录、tests是测试工具目录。在进行npm run build之后还会生成(默认情况下)dist目录,这是生成的生产环境下的运行代码目录。其他目录不必多说,只需要核心了解src的目录组成即可。

├── src
│   ├── common               # 应用公用配置,如导航信息
│   ├── components           # 业务通用组件
│   ├── e2e                  # 集成测试用例
│   ├── layouts              # 通用布局
│   ├── models               # dva model
│   ├── routes               # 业务页面入口和常用模板
│   ├── services             # 后台接口服务
│   ├── utils                # 工具库
│   ├── g2.js                # 可视化图形配置
│   ├── polyfill.js          # 兼容性垫片
│   ├── theme.js             # 主题配置
│   ├── index.js             # 应用入口
│   ├── index.less           # 全局样式
│   └── router.js            # 路由入口

index.js 应用入口

这个页面核心就是importconst这两个命令,至于其是否是ES6的暂且不管。

import

语法介绍见这里。在index.js这个文件中核心就是加载并执行了一系列module,并引入了dvamodels两个变量。

有关ES6的语法推荐查询阮一峰翻译的ECMAScript 6 入门,后续不再赘述。

const

语法介绍见这里。这里就是声明了一个变量app

其他

至于index页面中的const app = dva({})models.forEach((m)=>{app.model(m);});app.router(require('./router'));app.start('#root');这几句代码暂时不理会,待梳理调用流程时再进行解析。

简单说一下,由于这里是入口,所有这些内容都是用来进行全局设置的,至于其如何实现?如何调用?这些是细节内容,在概略浏览代码时,可以不关注这些点。

router.js 路由入口

这个页面核心就是定义路由策略。并引出了export命令。

export

语法介绍见这里。这里是将定义好的函数RouterConfig默认输出出去。

定义路由策略

通过function关键字定义了RouterConfig函数,其输入参数是{history}对象,返回值是一个页面,其由LocaleProviderRouterSwitchRouteRedirect标签(component)组成的。其中每个component从哪里来,怎么操作暂时先不去关注这些细节,在模仿实现的时候可以有样学样。

核心认识到通过/user路径可以访问到UserLayout,通过/可以访问到BasicLayout,默认情况下(Redirect),会跳转到/。具体代码就是如下这段:

其他文件

theme.js主要是主题配置、index.less主要是进行了全局样式设置、polyfill.js是兼容性垫片、g2.js是可视化图形的配置。一般情况下,如果不需要修改全局样式、主题修改、对g2进行配置,不进行额外的浏览器兼容性考虑,这些文件可以不进行深入了解。

其实对这些文件的加载过程是其使用的脚手架完成的,暂且不深入了解脚手架及webpack相关的实现机制。

dva

先来看看dva的介绍:dva 是基于现有应用架构 (redux + react-router + redux-saga 等)的一层轻量封装,没有引入任何新概念,全部代码不到 100 行。dva 是 framework,不是 library,类似 emberjs,会很明确地告诉你每个部件应该怎么写,这对于团队而言,会更可控。另外,除了 reactreact-dompeerDependencies 以外,dva 封装了所有其他依赖。dva 实现上尽量不创建新语法,而是用依赖库本身的语法,比如 router 的定义还是用 react-routerJSX 语法的方式(dynamic config 是性能的考虑层面,之后会支持)。 引用自这里,github的中文介绍在这里

这又引出了一系列新的概念:reduxreactreact-routerreact-dom等。暂且先不管这些新的概念是什么,只需要知道dva是一套框架(framework),尽量不引入新的语法,只是明确了每个部件该怎么写。

redux

由于dva是一层皮,其内涵是reduxreact等。那就了解下redux是什么?redux的中文文档从这里找到。用几句话概括就是:ReduxJavaScript 状态容器,提供可预测化的状态管理。可以让你构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。不仅于此,它还提供 超爽的开发体验,比如有一个时间旅行调试器可以编辑后实时预览。

从阅读其介绍就可以获知:Redux 试图让 state 的变化变得可预测,它就是管理state的解决方案。至于如何使用redux、它具有哪些核心概念、如何操作redux中的函数、使用它有哪些注意事项。可以通过学习redux获知。这应该是2天工作量的就可以完成的学习任务。

由于后续进行开发时会反复使用到redux相关的概念,这一章节必须进行扩展学习,可以安排后续的学习任务。但为不影响主线,暂且放下,进行整体思路梳理。

react及其他

React 是一个采用声明式,高效而且灵活的用来构建用户界面的框架。因此,有必要对react的基础逻辑进行理解,此处需要安排2天的学习任务,了解react的基础知识。中文的教程在这里

React Router 是完整的 React 路由解决方案。React Router 保持 UI 与 URL 同步。它拥有简单的 API 与强大的功能例如代码缓冲加载、动态路由匹配、以及建立正确的位置过渡处理。中文教程在这里

React Dom提供了针对dom的方法,具体参考这里

各目录及相互关系

common是应用公共配置,如导航信息;components是业务通用组件;layouts是通用布局;modelsdva modelroutes是业务页面入口和常用模板;services是后台接口服务;utils是工具库;e2e是集成测试用例。

从入口开始看起,一步步抽丝剥茧看看各个组件之间是怎样进行调用的。

首先在index.js中加载了models中的所有model并进行了注册。其中app.model()函数来自于dva的用法,而且model的概念也是dva中的概念,详见这里,而所有的model均来自于./models目录。之后又通过app.router()语句注册了路由表,详细介绍见这里index.js文件的最后调用了 app.start('#root');,这句话的含义是启动应用,详细介绍见这里

在启动应用之后,开发者可以通过浏览器向服务器发起请求访问,以获取页面和数据。不详细展开路由过程,依照路由字面含义。在访问/时,会被路由到BasicLayout布局页面(此处参见router.js)。

布局页面

布局页面在开始的位置import了一系列的组件(component),定义了一些const(常量),先不去管它。接下来出现了诡异的class关键字,这有中java代码乱入的感觉,没关系。这是ES6的新特性,参见这里,简单理解起来可以将之看作对象。这个class继承自React.PureComponent,在class中定义了静态变量、构造方法,以及一系列的内部方法,值得注意的是方法只有被调用时才会执行,因此class中的一系列方法都可以视为声明,而不会顺序执行。直到最后出现了一个render方法,这个方法会在渲染组件时被调用(此处请参考react组件相关),因此我们关心页面显示相关逻辑就主要是关注render这个方法。

在对render方法展开说之前,先把BasicLayout最后的export说完。export defaultES6相关语法,前面已有介绍,指默认输出。connectreact-redux中的一个api,是用来连接 React 组件与 Redux store的,其用法详情见这里,只需知道其返回的仍是原React组件(当前事例中是BasicLayout),并与已有的状态信息state相关联。

再展开说render。1.在render中,用到了一系列来自antd的组件如:LayoutMenu等,其相关的介绍不展开,可以到antd网站参考其相关教程。2.当然也需要用到dva封装好的有关路由的一系列组件,如:LinkRouteRedirectSwitch。3.同样也用到了ant design pro中创建的组件,如:HeaderSearchNoticeIconGlobalFooter。如果深入进去了解每个组件的实现原理,会不可避免陷入泥潭,对此只需阅读antddva的相关api,明确该组件是什么、怎么做就好,接下来就是实践中掌握。

BasicLayout布局的render中,核心布局就是左边栏(Sider,参考antdLayout.Sider)、顶部(Header,参考Layout.Header、内容区(Content,参考Layout.Content。在内容区除了整体的可更换的页面以外还有自创建的脚部区域(GlobalFooter),其中左边栏、顶部使用了ant design pro样例中自创建的组件,包括:搜索框(HeaderSearch)、通知提醒框(NoticeIcon),以及antd中已经封装好的通用模板:菜单栏(Menu)、下拉菜单(Dropdown)、头像(Avatar)、加载中(Spin)、图标(Icon)、标签(Tag)、全局提示(message)。如上所述,有关antddva中已经封装好的通用模板,应当阅读相关的api,而不应该重新造一遍轮子。

整个布局页面大体就是这个样子,在ant design pro中还提供了另一个页面布局就是UserLayout。如果实际开发中还需要其他的页面布局,就需要自己撰写,大体来说就要研究清楚BasicLayout页面中的各方面细节,并能够再依照需求完成创建。

Redirect 路由

BasicLayout页面布局中,Content区是通过

{
  getRouteData('BasicLayout').map(item =>
    (
      
    )
  )
}

完成页面加载。有关RedirectRoute的详细介绍在这里这里。先来看看getRouteData函数干了什么。

getRouteData函数来自于../utils/utils文件,通过对utils.js查看,可以看出该函数引用了../common/nav文件,也就是nav.js,结合nav.js文件进行梳理。综合三个位置可以分析得到如下过程:

  1. nav.js中通过import...from...引入了layoutsroutes中的布局页面和业务页面(常用模板)。由此可知 如需添加新的页面可以参照这个格式引入
  2. nav.js中定义了data,以json的格式定义了菜单目录结构,并将引入的页面(1.)以变量的形式引入进来。这个目录内在含义可以后续深入了解,需要注意的是:在添加新页面时应参照这个json格式添加
  3. 通过export default关键字把data输出出来。
  4. utils.js中,import navData from '../common/nav';引入了刚刚定义的data并重命名为navData。
  5. utils.js中的函数getRouteData中,用到了数组对象中的函数some官方中译文档解读参考)、filter官方中译文档解读参考)以及lodashcloneDeep
  6. 依照字面意思来理解,getRouteData做了两件事:1.对输入参数path,进行检查,如果检查不通过返回null(检查含义主要是确保path是一个有效layout,同时layoutchildren不为空);2.对检查通过的path,获取该布局下的所有数据,并将数据转换成数组,数组的每个元素是被称为item,该item具有path属性、exact属性、component属性、children属性。
  7. 解释下item的四个属性:path属性会赋值给外层调用中Routepath属性;component属性会赋值给外层调用中component属性;children属性是用来控制如何进行递归的;exact属性会赋值给外层调用中exact属性。
  8. getRouteData就获得了具有pathcomponent等属性的元素数组。
  9. BasicLayout中调用了getRouteData函数并对返回数组逐项拆解,组成Route
  10. 通过以上过程就将nav中的json格式配置转换成了页面中的跳转路由。当页面中的地址栏中出现/一级目录/二级目录时,就可以通过Route找到对应的component并进行加载。

这样一来,layoutsroutesnav.jsutils.js之间进行路由的链路就清晰了。由于我们是在实践中学习,对一些实现细节还不必过度深究,只需要知道页面是怎样串起来的,以及为什么改变nav.js中的目录样式就能够操作路由,同时能够结合实际的路由需要对路由进行必要整改就可以动手执行了。

Menu 导航菜单

导航菜单顾名思义就是左侧栏中的内容,在BasicLayout中就是这段代码


  {this.getNavMenuItems(this.menus)}

核心函数封装成了getNavMenuItems,其操作的对象是menus属性,而在构造方法中对menus的值进行了初始化。综合来看梳理成如下过程:

  1. BasicLayout的构造方法中,通过getNavData方法获取到nav.js中定义好的data
  2. data本身是数组,通过reduce官方中译文档解读参考)方法和concat(官方中译文档)方法将数组规约成一个新的数组。新的数组是原数组中每个元素的children数组再拼装在一起组成的。换句话说,nav中定义的data,第一层级是布局(layout),布局的children是页面集合(第二层级),页面集合的children是业务页面(第三层级)。此处的转换就是将layout去掉,拼装成页面集合(第二层级)的数组。
  3. getNavMenuItems函数中,对menus进行操作。对每一项(也就是页面集合)转换成一个Menu.Item。这是可以理解的。
  4. getNavMenuItems函数从上到下核心做了5件事:1.判断有没有可显示的name,如果没有将返回null;2.给item制定path,也就是跳转路径,采用父子路径拼装的思路完成;3.如果存在children且存在name不为空,构造子菜单SubMenu;4.明确可显示的图标;5.拼装成Menu.Item
  5. 在拼装Item过程中,涉及到了跳转路径path的构造、可显示的nameicon

结合路由和导航就可以理解整个页面左侧导航栏的动态显示、通过导航栏的点击实现页面的路由过程。

布局页面的其他关注点

BasicLayout中还引入了styles,这是一个less文件。

Less 是一门 CSS 预处理语言,它扩展了 CSS 语言,增加了变量、Mixin、函数等特性,使 CSS 更易维护和扩展。

因此只需要把less当成是基本的样式对待就好。虽然它有不少新的特性,但大多数情况下,styles都是出现在某个组件的className位置,以引用的方式加入进来(css的外联)。

有关LinkIcon等框架提供的组件,需要自行查阅相关API,HeaderSearchNoticeIconGlobalFooter的用法在后续提到components的用法时再行展开。

动态获取数据

BasicLayout页面中进行动态加载数据就涉及到React组件生命周期,相关资料也可参考这里,总之在BasicLayout页面中用到的就是componentDidMount函数进行数据动态加载的。这个函数核心只是调用了dispatch

componentDidMount() {
  this.props.dispatch({
    type: 'user/fetchCurrent',
  });
}

此时就要引出前面提到的dvaredux相关的概念了。上文中提到了app.model()这个函数及其参考。接下来把整体逻辑串起来:

  1. 通过app.model()方法,对model进行注册,这个model概念来自于dva
  2. model中的effects部分,包装自redux-saga
  3. this.props.dispatch方法源自redux,见这里
  4. model注册时,其实就是注册了一系列的监听器,等待着被触发。
  5. dispatch时,会接受一个 Action 对象作为参数,将它发送出去。关于redux的数据流程参见这里
  6. 'user/fetchCurrent'为例,依照dva的逻辑会找到model中的user命名空间,并在其下找到fetchCurrent方法。因此,dispatch之后会触发models/user.js中的fetchCurrent方法。
  7. 又通过import加载了services/user.js中的queryCurrent异步调用方法。
  8. services/user.js中,importutils/request.js,并调用了其中的request请求。并传递请求路径为'/api/currentUser'
  9. 最终在utils/request.js中找到了request函数,其核心调用了dvafetch函数进行url请求。
  10. dva中的fetch包装自isomorphic-fetch,说明见这里
  11. 至此将发起fetch请求,向服务端请求相应的数据。以queryCurrent为例,将利用url:'/api/currentUser'
  12. .roadhogrc.mock.js中配置了代理,同时可以找到/api/currentUser请求返回的数据。后续不再赘述。

通过上述分析,可见关键点在1.model的撰写和注册;2.dispatch的正确调用;3.后台业务请求的封装。这涉及到了modelsservices目录内容及utils/request.js(由于这个文件主要完成的是底层封装,基本可以不去关注。以添加新的后台请求为例,需要完成:1.在services目录下定义好请求的url以及函数名称;2.将该后台请求添加到对应的model中。以添加新的数据加载为例,需要完成:1.在models目录下中定义新的model,注意namespacestateeffectsreducerssubscriptions的合理使用;2.如果涉及到后台请求数据需要引入services;3.在合理的事件处发起dispatch。需要注意的是dispatch的目录要与modelnamespace相一致。

components目录

这个目录其实是抽象出来的一系列的业务通用组件。详细内容不再展开。包括BasicLayout中已经提到的HeaderSearchNoticeIconGlobalFooter组件,实际上都是自行封装的结果。

e2e目录

这个目录是端到端测试的相关目录,相关信息可以从这里了解一些。
由于不在主线学习范围之内暂不理会。

关于redux-auth-wrapper

这个项目的github地址在这里

通过对这个项目demo的初步体验,感觉到它是基于redux的state进行设计的。先来说说state,这是驻留在浏览器内存中的一个数据结构,可以讲起理解为记录在浏览器(客户端)中的用户最新状态信息。redux-auth-wrapper的作用位置是route时,就是利用state中的信息验证用户是否可以进行跳转。这是对用户体验的一重优化。

有关图标

扩展当前的图标

核心参考这篇扩展教程

  • Q:为什么需要用到扩展图标?
  • A:阿里框架所使用的图标库,不足以满足实际开发需要。
  • Q:阿里框架库中有哪些图标?
  • A:阿里框架中的所有图标可以在ant design的资源列表中下载到,点击这里
  • Q:为什么在下载的图标库中有些图标找不到?如:dashboard,它出现在ant design pro的项目中,但是在图标库中却没有。
  • A:因为ant design pro依赖的是ant-design-3.0.0-beta.5,阿里发布的图标库的版本号是2.10.x
  • Q:如何找到最新的图标库?
  • A:可以在ant-design代码库的tag中找到3.0.0-beta.5的代码中找到。其路径是componentsstylecoreiconfont.less中能够找到映射关系。当然阿里使用了一整套的图标(svg、ttf、woff、eot等多种格式,扫盲贴见这里),并用css和js完成适配映射,将图标映射成16进制的unicode,并指定相应的class代码。只要确保工程项目中使用到的图标库中存在某个图标,就可以直接在项目中使用相应的unicode代码或class代码。如果工程项目中使用到了第三方图标,可以对其扩展。
  • Q:如何依照阿里的icon框架进行图标扩展?
  • A:参考扩展教程
  • Q:还有哪些值得参考的与ant design图标有关的资料?
  • A:1.图标设计规范;2.本地部署 Iconfont 示例;3.antd图标库2.10.x;4.内网环境使用离线Icon图标资源
  • Q:在adp项目中应该怎么操作?
  • A:由于我们的工程打包环境是可以联网的,因此不需要进行离线Icon设置,也不需要完成Iconfont的本地部署。只需要按照扩展教程进行新组建的扩充即可。在扩充时用iconfont添加新的icon,然后构造成相应的图标库,在下载到对应位置即可。鉴于已完成基础性操作,后续添加新图标的流程:1.在iconfont的图标库中添加新的图标;2.将图标库下载下来覆盖项目图标目录/public/fonts/iconfont;3.在项目中使用图标代码。

React Developer Tools

  • Q:react的源代码通过编译组成了普通html、css代码。那怎样在浏览器中查看对应的源代码?
  • A:使用React Developer Tools可以辅助在chrome中查看源代码,参见这里
  • Q:相关链接
  • A:chrome插件下载地址

有关样式的几个注意事项

  • Q:如何在ant design的样式代码中使用css常用的’-‘?如:“font-size”
  • A:此处核心遇到的是js中的’.’取值与'[]’取值的区别,参考这里的解答
  • Q:ant design中推荐哪种方式编写样式?
  • A:ant design中推荐使用驼峰式的样式书写,如:’fontSize’。同时这个框架中大量使用了’.’操作符进行对象操作,使用import语法进行整个的代码组织。
  • Q:在引用样式时传统的写法可以使用多个classname,如class="csdn-toolbar csdn-toolbar-skin-black ",在ant design中怎样使用多个样式?
  • A:使用composes,参考这里Class 的组合章节。
  • Q:还有哪些注意事项?
  • A:1.在ant design的框架下推荐XXX.js文件与XXX.css(或XXX.less)文件放在一起,而不是使用分开的css、html、js目录分别管理不同类型的文件。这是源自react的模块化管理的思想。2.CSS Modules是ant design中默认使用。简要介绍见这里
  • Q:为什么样式会变成类似于class="globalFooter___1cM92"
  • A:因为CSS Modules定义了局部作用域的概念,为达到局部作用效果,构建工具会对class的name进行哈希操作,使类名变成独一无二。常用工具就是webpack,而使用的插件是css-loader

关于数据查询的方法

  • Q:如何制定模拟的数据请求?
  • A:ant design pro中使用了mock。可以在.roadhogrc.mock.js中进行数据请求配置。比如'GET /api/testapi'是新添加的模拟数据请求。可以尝试在浏览器中访问这个url:http://localhost:8000/api/testapi,确认该接口可以访问数据。
  • Q:在ant design pro中如何发起数据请求?
  • A:在services目录中的文件添加一个request请求。如:export async function queryTestData(){ return request('/api/testapi'); }就在js中定义了一个request,发起相应的request请求。因此,就可以在需要请求数据的位置直接调用queryTestData方法。
  • Q:可不可以在页面点击事件中直接调用queryTestData方法?ant design pro中为什么没用这种方法?
  • A:首先,这种调用方法是基础的调用,是可以在页面布局中引入相应的文件直接调用这个函数的。ant design pro中遵从了react和redux的设计理念,承认component的模块化设计理念,并加强浏览器缓存的管理。
  • Q:ant design pro设计框架中在哪里调用queryTestData方法?
  • A:在models中进行调用。
  • Q:那models有什么含义?
  • A:在浏览器的缓存中,每个component都有其自身的state(这是redux里的基础概念),也就是缓存。而models就是缓存进行对象化管理的抽象。因此可以把models中的model看作是不同的业务对象。而每个业务对象都可以按对象逻辑调用service方法。如在models/user.js中进行如下调用:
*fetchTestData(_, { call, put }){
  const response = yield call(queryTestData);
  yield put({
    type: 'returnTestData',
    payload:response,
  });
}
  • Q:fetchTestData函数书写有什么值得注意的地方?
  • A:1.这个函数写在model中,model的概念来自于dva,见这里,同时其概念解释见这里。2.要充分理解model的概念,才能更合理书写和调用相关函数。3.model的概念还整合了redux-saga的设计理念。4.model中的effects设计来源于redux-saga的设计,参考这里。5.effects部分中使用了星号和yield关键字,是源自于ES6中的Generator语法,以及异步调用,因此只需要认识到星号标识了这不是普通函数,是可以暂停执行的,加星号以示区别;yield标识此处可以暂停,并在获得执行权时从此处开始执行,调用方式参考文档里的next调用。6.call和put来自于redux-saga(是Redux异步操作的中间件),更全的redux接口见这里。7.call实际上就是创建了一条描述结果的信息(上例中的含义就是异步(yield)调用queryTestData,调用完成后(call)将结果赋值给response)。8.put实际上是用来创建dispatch Effect的(上例中的含义就是异步(yield)调用returnTestData,并传递参数response),可从这里这里以及这里了解更多的信息。
  • Q:model中如何改变相关状态?
  • A:model的状态存储在state对象中,在示例中我们添加了testdata:{},对象。依照redux的设计理念,所有对状态的修改都应该在reducer中完成。因此model中的reducers部分就是定义一系列修改state的函数,如:queryTestData函数。
  • Q:model中的state和action是什么?
  • A:其中state是函数调用前的状态,而action就是dispatch发出的action。
  • Q:model中的状态信息(state)怎么在页面中使用?
  • A:通过connect函数(来自于react-redux),其实connect设计的初衷就是把容器组件与redux相连接。其更多使用方法见这里。在示例中,就是通过
@connect(state => ({
  chart: state.chart,
  testdata: state.user.testdata,
}))

方法将redux中的state连接到了这个页面(容器组件)中。通过这句话testdata: state.user.testdata,就将全局state中命名空间(namespace)为user下的状态(state)属性testdata对象与容器组件中的testdata属性(存在于props中)相关联。

  • Q:怎样把容器组件中的属性与页面显示相关联?
  • A:通过const { chart,testdata } = this.props;这句话能够把属性(props)中的对象testdata解析出来。在页面上通过{testdata.name}将相应的值取出来。

例:Analysis页面折线图切换


  
    {
      offlineData.map(shop => (
        
          
            
          
        )
      )
    }
  
  • Q:怎样从接口中获取数据?
  • A:涉及到数据获取的部分有两个对象:offlineData、offlineChartData。这两个对象都是通过如下方法实现的数据调用,详细过程见’关于数据查询的方法’部分
  componentDidMount() {
    this.props.dispatch({
      type: 'chart/fetch',
    }).then(() => this.setState({ loading: false }));
  }
  • Q:currentTabKey怎样在页面实现数值传递?
  • A:先明确页面加载时会调用的几个函数:componentDidMountcomponentWillUnmountrender。这涉及到react组件的生命周期概念,参考这里的简介。现在只需要知道:
    componentDidMount()方法会在组件(所谓组件就是Component)挂在之后调用一次;

componentWillUnmount()方法会在组件被卸载时调用。;
render()有四种触发场景,详情不赘述,只需知道页面中调用this.setState方法是会触发当前组件的更新。
可以看出如下过程:
1.组件初始化时定义了currentTabKey变量,并赋值为”;
2.在初始化时最后调用了render方法,依据currentTabKeyactiveKey进行赋值;
3.在真正渲染时通过activeKey设置TabsactiveKey属性并传值给CustomTab组件;
4.在Tabs组件被点击时通过onChange方法回调handleTabChange函数;
5.在回调函数中通过this.setStatestate中的currentTabKey值修改;
6.this.setState 触发了render函数被再次调用。

  • Q:还有哪些是值得关注的点?
  • A:TabPane的变量通过const { TabPane } = Tabs;Tabs中析取出来,也就是说TabPane是定义在Tabs中的一个子组件;
    CustomTab是一个自定义组件,由于其在Tab的页签位置,同时使用了ReactNode来做页签,因此需要自行控制选中状态,由此theme={(currentKey !== data.name) && 'light'}color={(currentKey !== data.name) && '#BDE4FF'}就是在控制相应的样式。

相关链接整理

很有用的单独说一遍

dva:github项目
React中文文档
React 入门实例教程(阮一峰)
Redux 入门教程(一):基本用法(阮一峰)
Redux 中文文档
Redux-saga 中文文档
Redux 三重境
ECMAScript 6 入门
React Router 中文文档
React Router 使用教程(阮一峰)
Ant Design 指引

dva:github项目
dva:React+Redux最佳实践
dva:中文Readme
dva:中文API
一起学react (1) 10分钟 让你dva从入门到精通
初识 Dva
dva源码解析(一)
dva 入门:手把手教你写应用

React中文文档
React:State&生命周期
React:组件 & Props
React 入门实例教程(阮一峰)
React PureComponent源码解析
ReactJS分析之入口函数render
谈一谈创建React Component的几种方式
React Dom

Redux 入门教程(一):基本用法(阮一峰)
Redux 关于react-redux中的connect用法介绍及原理解析
Redux 中文文档
Redux 数据流
Redux-saga 中文文档
Redux-saga 实践总结
Redux 三重境
实例讲解基于 React+Redux 的前端开发流程

ECMAScript 6 入门
ES5中新增的Array方法详细说明
Ecma标准
Lodash 中文文档
exports 和 module.exports 的区别
ES6:export default 和 export 区别
fetch的用法
React 语法之let和const命令
ES6展开运算符

React Router
React Router 中文文档
React Router 使用教程(阮一峰)
React Router Redirect
path-to-regexp
Path-to-RegExp 使用

Ant Design 指引
Ant Design of React
Ant Design Pro教程
自定义主题开发中,修改theme.js需要重启
如何评价 Ant Design 这个项目(一个设计语言)?
Ant Design源码分析(一):Icon组件

roadhog
less 快速入门
Less简介及简单用法
Babel 入门教程(阮一峰,不知何故他已经在自己的博客中删除了,只能从archive找到)
stylelint初体验
JS/React 开发者的 Atom 终极配置
自动化e2e测试 — WebDriverJS,Jasmine和Protractor
Webpack单元测试,e2e测试
怎么为大中型的vue.js项目编写e2e测试?
Atom编辑器折腾记_(23)加快React开发的插件汇总
Fetch API
react&webpack使用css、less && 安装原则 — 从根本上解决问题。
less-loader
webpack-中文文档
React中的DOM操作
编辑器配置
js代码检测工具
css代码审查配置
git版本配置
travis持续构建工具配置
roadhog配置
web前端项目配置文件
jest
lint
如何配置 antd theme 的例子
dva-hmr
babel-plugin-import
babel-preset-env
babel-preset-react
transform-decorators-legacy
transform-class-properties
transform-runtime
antd