React现在最好最贵的智能手机版本能有多好

过渡 & 动画
可复用性 & 组合
对比其他框架
这个页面无疑是最难编写的,但我们认为它也是非常重要的。或许你曾遇到了一些问题并且已经用其他的框架解决了。你来这里的目的是看看 Vue 是否有更好的解决方案。这也是我们在此想要回答的。
客观来说,作为核心团队成员,显然我们会更偏爱 Vue,认为对于某些问题来讲用 Vue 解决会更好。如果没有这点信念,我们也就不会整天为此忙活了。但是在此,我们想尽可能地公平和准确地来描述一切。其他的框架也有显著的优点,例如 React 庞大的生态系统,或者像是 Knockout 对浏览器的支持覆盖到了 IE6。我们会尝试着把这些内容全部列出来。
我们也希望得到你的帮助,来使文档保持最新状态,因为 JavaScript 的世界进步的太快。如果你注意到一个不准确或似乎不太正确的地方,请让我们知道。
ReactReact 和 Vue 有许多相似之处,它们都有:
使用 Virtual DOM
提供了响应式 (Reactive) 和组件化 (Composable) 的视图组件。
将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库。
由于有着众多的相似处,我们会用更多的时间在这一块进行比较。这里我们不只保证技术内容的准确性,同时也兼顾了平衡的考量。我们需要承认 React 比 Vue 更好的地方,比如更丰富的生态系统。
React 社区为我们准确进行平衡的考量提供了,特别感谢来自 React 团队的 Dan Abramov 。他非常慷慨的花费时间来贡献专业知识来帮助我们完善这篇文档。
运行时性能React 和 Vue 都是非常快的,所以速度并不是在它们之中做选择的决定性因素。对于具体的数据表现,可以移步这个,它专注于渲染/更新非常简单的组件树的真实性能。
优化在 React 应用中,当某个组件的状态发生变化时,它会以该组件为根,重新渲染整个组件子树。
如要避免不必要的子组件的重渲染,你需要在所有可能的地方使用 PureComponent,或是手动实现 shouldComponentUpdate 方法。同时你可能会需要使用不可变的数据结构来使得你的组件更容易被优化。
然而,使用 PureComponent 和 shouldComponentUpdate 时,需要保证该组件的整个子树的渲染输出都是由该组件的 props 所决定的。如果不符合这个情况,那么此类优化就会导致难以察觉的渲染结果不一致。这使得 React 中的组件优化伴随着相当的心智负担。
在 Vue 应用中,组件的依赖是在渲染过程中自动追踪的,所以系统能精确知晓哪个组件确实需要被重渲染。你可以理解为每一个组件都已经自动获得了 shouldComponentUpdate,并且没有上述的子树问题限制。
Vue 的这个特点使得开发者不再需要考虑此类优化,从而能够更好地专注于应用本身。
HTML & CSS在 React 中,一切都是 JavaScript。不仅仅是 HTML 可以用 JSX 来表达,现在的潮流也越来越多地将 CSS 也纳入到 JavaScript 中来处理。这类方案有其优点,但也存在一些不是每个开发者都能接受的取舍。
Vue 的整体思想是拥抱经典的 Web 技术,并在其上进行扩展。我们下面会详细分析一下。
JSX vs Templates在 React 中,所有的组件的渲染功能都依靠 JSX。JSX 是使用 XML 语法编写 JavaScript 的一种语法糖。
JSX 说是手写的渲染函数有下面这些优势:
你可以使用完整的编程语言 JavaScript 功能来构建你的视图页面。比如你可以使用临时变量、JS 自带的流程控制、以及直接引用当前 JS 作用域中的值等等。
开发工具对 JSX 的支持相比于现有可用的其他 Vue 模板还是比较先进的 (比如,linting、类型检查、编辑器的自动完成)。
事实上 Vue 也提供了,甚至。然而,我们默认推荐的还是模板。任何合乎规范的 HTML 都是合法的 Vue 模板,这也带来了一些特有的优势:
对于很多习惯了 HTML 的开发者来说,模板比起 JSX 读写起来更自然。这里当然有主观偏好的成分,但如果这种区别会导致开发效率的提升,那么它就有客观的价值存在。
基于 HTML 的模板使得将已有的应用逐步迁移到 Vue 更为容易。
这也使得设计师和新人开发者更容易理解和参与到项目中。
你甚至可以使用其他模板预处理器,比如 Pug 来书写 Vue 的模板。
有些开发者认为模板意味着需要学习额外的 DSL (Domain-Specific Language 领域特定语言) 才能进行开发——我们认为这种区别是比较肤浅的。首先,JSX 并不是免费的——它是基于 JS 之上的一套额外语法,因此也有它自己的学习成本。同时,正如同熟悉 JS 的人学习 JSX 会很容易一样,熟悉 HTML 的人学习 Vue 的模板语法也是很容易的。最后,DSL 的存在使得我们可以让开发者用更少的代码做更多的事,比如 v-on 的各种修饰符,在 JSX 中实现对应的功能会需要多得多的代码。
更抽象一点来看,我们可以把组件区分为两类:一类是偏视图表现的 (presentational),一类则是偏逻辑的 (logical)。我们推荐在前者中使用模板,在后者中使用 JSX 或渲染函数。这两类组件的比例会根据应用类型的不同有所变化,但整体来说我们发现表现类的组件远远多于逻辑类组件。
组件作用域内的 CSS除非你把组件分布在多个文件上 (例如 ),CSS 作用域在 React 中是通过 CSS-in-JS 的方案实现的 (比如 、 和 )。这引入了一个新的面向组件的样式范例,它和普通的 CSS 撰写过程是有区别的。另外,虽然在构建时将 CSS 提取到一个单独的样式表是支持的,但 bundle 里通常还是需要一个运行时程序来让这些样式生效。当你能够利用 JavaScript 灵活处理样式的同时,也需要权衡 bundle 的尺寸和运行时的开销。
如果你是一个 CSS-in-JS 的爱好者,许多主流的 CSS-in-JS 库也都支持 Vue (比如
和 )。这里 React 和 Vue 主要的区别是,Vue 设置样式的默认方法是里类似 style 的标签。
让你可以在同一个文件里完全控制 CSS,将其作为组件代码的一部分。
@media (min-width: 250px) {
.list-container:hover {
background:
这个可选 scoped 属性会自动添加一个唯一的属性 (比如 data-v-21e5b78) 为组件内 CSS 指定作用域,编译的时候 .list-container:hover 会被编译成类似 .list-container[data-v-21e5b78]:hover。
最后,Vue 的单文件组件里的样式设置是非常灵活的。通过 ,你可以使用任意预处理器、后处理器,甚至深度集成 ——全部都在 &style& 标签内。
规模向上扩展Vue 和 React 都提供了强大的路由来应对大型应用。React 社区在状态管理方面非常有创新精神 (比如 Flux、Redux),而这些状态管理模式甚至 也可以非常容易的集成在 Vue 应用中。实际上,Vue 更进一步地采用了这种模式 (),更加深入集成 Vue 的状态管理解决方案 Vuex 相信能为你带来更好的开发体验。
两者另一个重要差异是,Vue 的路由库和状态管理库都是由官方维护支持且与核心库同步更新的。React 则是选择把这些问题交给社区维护,因此创建了一个更分散的生态系统。但相对的,React 的生态系统相比 Vue 更加繁荣。
最后,Vue 提供了 ,能让你非常容易地构建项目,包含了 ,,甚至 。React 在这方面也提供了 ,但是现在还存在一些局限性:
它不允许在项目生成时进行任何配置,而 Vue 支持 -like 定制。
它只提供一个构建单页面应用的单一模板,而 Vue 提供了各种用途的模板。
它不能用用户自建的模板构建项目,而自建模板对企业环境下预先建立协议是特别有用的。
而要注意的是这些限制是故意设计的,这有它的优势。例如,如果你的项目需求非常简单,你就不需要自定义生成过程。你能把它作为一个依赖来更新。如果阅读更多关于。
向下扩展React 学习曲线陡峭,在你开始学 React 前,你需要知道 JSX 和 ES2015,因为许多示例用的是这些语法。你需要学习构建系统,虽然你在技术上可以用 Babel 来实时编译代码,但是这并不推荐用于生产环境。
就像 Vue 向上扩展好比 React 一样,Vue 向下扩展后就类似于 jQuery。你只要把如下标签放到页面就可以运行:
&script src=&https://cdn.jsdelivr.net/npm/vue&&&/script&
然后你就可以编写 Vue 代码并应用到生产中,你只要用 min 版 Vue 文件替换掉就不用担心其他的性能问题。
由于起步阶段不需学 JSX,ES2015 以及构建系统,所以开发者只需不到一天的时间阅读就可以建立简单的应用程序。
原生渲染React Native 能使你用相同的组件模型编写有本地渲染能力的 APP (iOS 和 Android)。能同时跨多平台开发,对开发者是非常棒的。相应地,Vue 和
会进行官方合作,Weex 是阿里巴巴发起的跨平台用户界面开发框架,同时也正在 Apache 基金会进行项目孵化,Weex 允许你使用 Vue 语法开发不仅仅可以运行在浏览器端,还能被用于开发 iOS 和 Android 上的原生应用的组件。
在现在,Weex 还在积极发展,成熟度也不能和 React Native 相抗衡。但是,Weex 的发展是由世界上最大的电子商务企业的需求在驱动,Vue 团队也会和 Weex 团队积极合作确保为开发者带来良好的开发体验。
另一个 Vue 的开发者们很快就会拥有的选项是 ,这是一个。
MobXMobx 在 React 社区很流行,实际上在 Vue 也采用了几乎相同的反应系统。在有限程度上,React + Mobx 也可以被认为是更繁琐的 Vue,所以如果你习惯组合使用它们,那么选择 Vue 会更合理。
AngularJS (Angular 1)Vue 的一些语法和 AngularJS 的很相似 (例如 v-if vs ng-if)。因为 AngularJS 是 Vue 早期开发的灵感来源。然而,AngularJS 中存在的许多问题,在 Vue 中已经得到解决。
复杂性在 API 与设计两方面上 Vue.js 都比 AngularJS 简单得多,因此你可以快速地掌握它的全部特性并投入开发。
灵活性和模块化Vue.js 是一个更加灵活开放的解决方案。它允许你以希望的方式组织应用程序,而不是在任何时候都必须遵循 AngularJS 制定的规则,这让 Vue 能适用于各种项目。我们知道把决定权交给你是非常必要的。这也就是为什么我们提供 ,让你可以用几分钟,去选择是否启用高级特性,比如热模块加载、linting、CSS 提取等等。
数据绑定AngularJS 使用双向绑定,Vue 在不同组件间强制使用单向数据流。这使应用中的数据流更加清晰易懂。
指令与组件在 Vue 中指令和组件分得更清晰。指令只封装 DOM 操作,而组件代表一个自给自足的独立单元——有自己的视图和数据逻辑。在 AngularJS 中,每件事都由指令来做,而组件只是一种特殊的指令。
运行时性能Vue 有更好的性能,并且非常非常容易优化,因为它不使用脏检查。
在 AngularJS 中,当 watcher 越来越多时会变得越来越慢,因为作用域内的每一次变化,所有 watcher 都要重新计算。并且,如果一些 watcher 触发另一个更新,脏检查循环 (digest cycle) 可能要运行多次。AngularJS 用户常常要使用深奥的技术,以解决脏检查循环的问题。有时没有简单的办法来优化有大量 watcher 的作用域。
Vue 则根本没有这个问题,因为它使用基于依赖追踪的观察系统并且异步队列更新,所有的数据变化都是独立触发,除非它们之间有明确的依赖关系。
有意思的是,Angular 和 Vue 用相似的设计解决了一些 AngularJS 中存在的问题。
Angular (原本的 Angular 2)我们将新的 Angular 独立开来讨论,因为它是一个和 AngularJS 完全不同的框架。例如:它具有优秀的组件系统,并且许多实现已经完全重写,API 也完全改变了。
TypeScriptAngular 事实上必须用 TypeScript 来开发,因为它的文档和学习资源几乎全部是面向 TS 的。TS 有很多好处——静态类型检查在大规模的应用中非常有用,同时对于 Java 和 C# 背景的开发者也是非常提升开发效率的。
然而,并不是所有人都想用 TS——在中小型规模的项目中,引入 TS 可能并不会带来太多明显的优势。在这些情况下,用 Vue 会是更好的选择,因为在不用 TS 的情况下使用 Angular 会很有挑战性。
最后,虽然 Vue 和 TS 的整合可能不如 Angular 那么深入,我们也提供了官方的
和 ,并且知道有大量用户在生产环境中使用 Vue + TS 的组合。我们也和微软的 TS / VSCode 团队进行着积极的合作,目标是为 Vue + TS 用户提供更好的类型检查和 IDE 开发体验。
运行时性能这两个框架都很快,有非常类似的 benchmark 数据。你可以做更细粒度的对比,不过速度应该不是决定性的因素。
体积在体积方面,最近的 Angular 版本中在使用了
技术后使得最终的代码体积减小了许多。但即使如此,一个包含了 Vuex + Vue Router 的 Vue 项目 (gzip 之后 30kB) 相比使用了这些优化的 angular-cli 生成的默认项目尺寸 (~130kB) 还是要小得多。
灵活性Vue 相比于 Angular 更加灵活,Vue 官方提供了构建工具来协助你构建项目,但它并不限制你去如何组织你的应用代码。有人可能喜欢有严格的代码组织规范,但也有开发者喜欢更灵活自由的方式。
学习曲线要学习 Vue,你只需要有良好的 HTML 和 JavaScript 基础。有了这些基本的技能,你就可以非常快速地通过阅读
投入开发。
Angular 的学习曲线是非常陡峭的——作为一个框架,它的 API 面积比起 Vue 要大得多,你也因此需要理解更多的概念才能开始有效率地工作。当然,Angular 本身的复杂度是因为它的设计目标就是只针对大型的复杂应用;但不可否认的是,这也使得它对于经验不甚丰富的开发者相当的不友好。
EmberEmber 是一个全能框架。它提供了大量的约定,一旦你熟悉了它们,开发会变得很高效。不过,这也意味着学习曲线较高,而且并不灵活。这意味着在框架和库 (加上一系列松散耦合的工具) 之间做权衡选择。后者会更自由,但是也要求你做更多架构上的决定。
也就是说,我们最好比较的是 Vue 内核和 Ember 的与层:
Vue 在普通 JavaScript 对象上建立响应,提供自动化的计算属性。在 Ember 中需要将所有东西放在 Ember 对象内,并且手工为计算属性声明依赖。
Vue 的模板语法可以用全功能的 JavaScript 表达式,而 Handlebars 的语法和帮助函数相比来说非常受限。
在性能上,Vue 比 Ember ,即使是 Ember 2.x 的最新 Glimmer 引擎。Vue 能够自动批量更新,而 Ember 在性能敏感的场景时需要手动管理。
KnockoutKnockout 是 MVVM 领域内的先驱,并且追踪依赖。它的响应系统和 Vue 也很相似。它在以及其他方面的表现也是让人印象深刻的。它最低能支持到 IE6,而 Vue 最低只能支持到 IE9。
随着时间的推移,Knockout 的发展已有所放缓,并且略显有点老旧了。比如,它的组件系统缺少完备的生命周期事件方法,尽管这些在现在是非常常见的。以及相比于
调用子组件的接口它的方法显得有点笨重。
如果你有兴趣研究,你还会发现二者在接口设计的理念上是不同的。这可以通过各自创建的
体现出来。或许有点主观,但是很多人认为 Vue 的 API 接口更简单结构更优雅。
PolymerPolymer 是另一个由谷歌赞助的项目,事实上也是 Vue 的一个灵感来源。Vue 的组件可以粗略的类比于 Polymer 的自定义元素,并且两者具有相似的开发风格。最大的不同之处在于,Polymer 是基于最新版的 Web Components 标准之上,并且需要重量级的 polyfills 来帮助工作 (性能下降),浏览器本身并不支持这些功能。相比而言,Vue 在支持到 IE9 的情况下并不需要依赖 polyfills 来工作。
在 Polymer 1.0 版本中,为了弥补性能,团队非常有限的使用数据绑定系统。例如,在 Polymer 中唯一支持的表达式只有布尔值否定和单一的方法调用,它的 computed 方法的实现也并不是很灵活。
Polymer 自定义的元素是用 HTML 文件来创建的,这会限制使用 JavaScript/CSS (和被现代浏览器普遍支持的语言特性)。相比之下,Vue 的单文件组件允许你非常容易的使用 ES2015 和你想用的 CSS 预编译处理器。
在部署生产环境时,Polymer 建议使用 HTML Imports 加载所有资源。而这要求服务器和客户端都支持 Http 2.0 协议,并且浏览器实现了此标准。这是否可行就取决于你的目标用户和部署环境了。如果状况不佳,你必须用 Vulcanizer 工具来打包 Polymer 元素。而在这方面,Vue 可以结合异步组件的特性和 Webpack 的代码分割特性来实现懒加载 (lazy-loaded)。这同时确保了对旧浏览器的兼容且又能更快加载。
而 Vue 和 Web Component 标准进行深层次的整合也是完全可行的,比如使用 Custom Elements、Shadow DOM 的样式封装。然而在我们做出严肃的实现承诺之前,我们目前仍在等待相关标准成熟,进而再广泛应用于主流的浏览器中。
RiotRiot 3.0 提供了一个类似于基于组件的开发模型 (在 Riot 中称之为 Tag),它提供了小巧精美的 API。Riot 和 Vue 在设计理念上可能有许多相似处。尽管相比 Riot ,Vue 要显得重一点,Vue 还是有很多显著优势的:
更好的性能。Riot 使用了
而不是虚拟 DOM,但实际上用的还是脏检查机制,因此和 AngularJS 患有相同的性能问题。
更多成熟工具的支持。Vue 提供官方支持
和 ,而 Riot 是依靠社区来建立集成系统。
发现错误?想参与编辑?博主最新文章
博主热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)React Router v4 版本 完全指北 - WEB前端 - 伯乐在线
& React Router v4 版本 完全指北
事实上是React官方的标准路由库。当你在一个多视图的React应用中来回切换,你需要一个路由来管理那些URL。React Router 专注于此,同步保持你应用的UI和URL。
这个教程主要给你介绍React Router 的v4版本,以及你使用它可以做的大部分事情。
React 是一个很流行的库,用于在客户端渲染创建的单页应用(SPAs)。 一个SPA会有很多视图(也可以称为页面),不像传统的多页应用,视图之间的跳转不应该导致整个页面被重新加载。相反,我们希望视图就在当前页面里渲染。那些习惯于多页应用的最终用户,期望在一个SPA中应该包含以下特性:
应用中每个视图都应该有对应的唯一URL用来区分视图。以便用户可以在之后通过书签收藏的URL指向引用资源 – 例如:www.example.com/products。
浏览器的前进后退按钮应该正常工作。
动态生成的嵌套视图更应该有成对应的URL – 例如:example.com/products/shoes/101,101是产品id。
路由跳转是指在同步保持浏览器URL的过程中渲染页面中的视图。React Router 让你声明式的操作路由跳转。声明式路由方法,通过说“路由应该是这样的”,允许你控制应用中的数据流:
JavaScript
`&Route path="/about" component={About}/&`
`&Route path="/about" component={About}/&`
你可以把&Router&组件放在任意你想要路由渲染的地方。由于我们所需要接触的&Router&,&Link&以及其他React Router的API都只是组件,所以你可以非常方便的在React里使用路由。
写在开头。有一个常见的误区,大家都认为React Router是由facebook官方开发的一个路由解决方案。实际上,它是一个因其设计和简易性而流行的第三方库。如果你的需求只局限于路由的跳转,你可以无需太多麻烦,就可以从头开始实现一个自定义的路由。但是,了解React Router的基础知识可以让你更清楚的认识一个路由是怎么工作的。
本次教程分为几个部分。首先,我们使用npm安装好React和React Router,然后我们就开始React Router的基础部分。你将会看到React Router不同的代码示例的效果。本次教程涉及的例子包含:
基本路由跳转
带路径参数的嵌套路由
保护式路由
主要围绕构建这些路由所涉及的概念进行讨论。这个项目的全部代码在可以看到。当你进入一个单独的demo目录,执行npm install来安装依赖。要在本地服务器上运行这个应用,执行npm start,然后在浏览器打开http://localhost:3000/可以看到运行的demo。
让我们开始吧!
安装 React Router
假设你已经有一个React开发环境并已经运行了。如果没有,可以跳转到。或者,你可以使用来生成创建一个基本的React项目所需要的文件。这是Create React App生成的默认目录结构:
react-routing-demo-v4
├── .gitignore
├── package.json
├── public
├── favicon.ico
├── index.html
└── manifest.json
├── README.md
├── src
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
└── registerServiceWorker.js
└── yarn.lock
1234567891011121314151617
react-routing-demo-v4&&&&├── .gitignore&&&&├── package.json&&&&├── public&&&&│&& ├── favicon.ico&&&&│&& ├── index.html&&&&│&& └── manifest.json&&&&├── README.md&&&&├── src&&&&│&& ├── App.css&&&&│&& ├── App.js&&&&│&& ├── App.test.js&&&&│&& ├── index.css&&&&│&& ├── index.js&&&&│&& ├── logo.svg&&&&│&& └── registerServiceWorker.js&&&&└── yarn.lock
React Router库包含三个包: react-router, react-router-dom, 和 react-router-native。react-router是路由的核心包,而其他两个是基于特定环境的。如果你在开发一个网站,你应该使用react-router-dom,如果你在移动应用的开发环境使用React Native,你应该使用react-router-native。
使用npm安装react-router-dom:
`npm install --save react-router-dom`
`npm install --save react-router-dom`
React Router 基础
下面是路由的例子:
&route exact="" path="/" component="{Home}/"&
&route path="/category" component="{Category}/"&
&route path="/login" component="{Login}/"&
&route path="/products" component="{Products}/"/&
&&&router&&&&&&route exact="" path="/" component="{Home}/"&&&&&&&&route path="/category" component="{Category}/"&&&&&&&&&&route path="/login" component="{Login}/"&&&&&&&&&&&&route path="/products" component="{Products}/"/&&&&&&&&&&/route&&&&&&&&/route&&&&&&/route&&&&/router&
像上面的例子,你需要一个组件和一些组件来创建一个基本的路由。由于我们创建的是一个基于浏览器的应用,我们可以从React Router API中使用这两种类型的路由:
&BrowserRouter&
&HashRouter&
它们之间主要的区别,可以在它们所创建的URL明显看出:
http://example.com/about
http://example.com/#/about
// http://example.com/about&// http://example.com/#/about
&BrowserRouter&在两者中更为常用,原因是它使用了HTML5的history API来记录你的路由历史。而&HashRouter&则使用URL(window.location.hash)的hash部分来记录。如果你想兼容老式浏览器,你应该使用&HashRouter&。
使用&BrowserRouter&组件包裹App组件。
JavaScript
/* Import statements */
import React from 'react';
import ReactDOM from 'react-dom';
/* App is the entry point to the React code.*/
import App from './App';
/* import BrowserRouter from 'react-router-dom' */
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(
&BrowserRouter&
&/BrowserRouter&
, document.getElementById('root'));
123456789101112131415
/* Import statements */import React from 'react';import ReactDOM from 'react-dom';&/* App is the entry point to the React code.*/import App from './App';&/* import BrowserRouter from 'react-router-dom' */import { BrowserRouter } from 'react-router-dom';&ReactDOM.render(&&&&&BrowserRouter&&&&&&&&&&App /&&&&&&/BrowserRouter&&&&&, document.getElementById('root'));
注意:Router组件只能有一个子元素。子元素可以是HTML – 例如div – 也可以是一个react组件。
要让React Router工作,你需要从react-router-dom库引入相关的API。这里,我在index.js引入了BrowserRouter,也从App.js引入了App组件。App.js,如你所猜想的,是React组件的入口。
上述代码给我们整个App组件创建了一个history实例。接下来正式介绍下history。
history是一个让你轻松管理所有Javascript运行的会话记录的Javascript库。history提供了简洁的API,让你可以管理history堆栈,跳转,确认跳转,以及保持会话之间的状态。 – 来自
每个router组件创建了一个history对象,用来记录当前路径(history.location),上一步路径也存储在堆栈中。当前路径改变时,视图会重新渲染,给你一种跳转的感觉。当前路径又是如何改变的呢?history对象有history.push()和history.replace()这些方法来实现。当你点击组件会触发history.push(),使用则会调用history.replace()。其他方法 – 例如history.goBack()和history.goForward() – 用来根据页面的后退和前进来跳转history堆栈。
接下来,我们谈谈Links和Routes
Links and Routes
是React Router里最重要的组件。若当前路径匹配route的路径,它会渲染对应的UI。理想来说,应该有一个叫path的prop,当路径名跟当前路径匹配才会渲染。
另一方面,用来跳转页面。可以类比HTML的锚元素。然而,使用锚链接会导致浏览器的刷新,这不是我们想要的。所以,我们可以使用来跳转至具体的URL,并且视图重新渲染不会导致浏览器刷新。
我们已经介绍了创建一个基本的路由需要的所有东西。让我们试一个吧。
Demo 1: 基础路由
src/App.js
JavaScript
/* Import statements */
import React, { Component } from 'react';
import { Link, Route, Switch } from 'react-router-dom';
/* Home component */
const Home = () =& (
&h2&Home&/h2&
/* Category component */
const Category = () =& (
&h2&Category&/h2&
/* Products component */
const Products = () =& (
&h2&Products&/h2&
/* App component */
class App extends React.Component {
render() {
&nav className="navbar navbar-light"&
&ul className="nav navbar-nav"&
/* Link components are used for linking to other views */
&li&&Link to="/"&Homes&/Link&&/li&
&li&&Link to="/category"&Category&/Link&&/li&
&li&&Link to="/products"&Products&/Link&&/li&
/* Route components are rendered if the path prop matches the current URL */
&Route path="/" component={Home}/&
&Route path="/category" component={Category}/&
&Route path="/products" component={Products}/&
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
/* Import statements */import React, { Component } from 'react';import { Link, Route, Switch } from 'react-router-dom';&/* Home component */const Home = () =& (&&&div&&&&&&h2&Home&/h2&&&&/div&)&/* Category component */const Category = () =& (&&&div&&&&&&h2&Category&/h2&&&&/div&)&/* Products component */const Products = () =& (&&&div&&&&&&h2&Products&/h2&&&&/div&)&/* App component */class App extends React.Component {&&render() {&&&&return (&&&&&&&div&&&&&&&&&&nav className="navbar navbar-light"&&&&&&&&&&&&ul className="nav navbar-nav"&&&&&&&&&&&& /* Link components are used for linking to other views */&&&&&&&&&&&&&li&&Link to="/"&Homes&/Link&&/li&&&&&&&&&&&&&&li&&Link to="/category"&Category&/Link&&/li&&&&&&&&&&&&&&li&&Link to="/products"&Products&/Link&&/li&&&&&&&&&&&&&/ul&&&&&&&&& &/nav&&&&&&&&&&&&/* Route components are rendered if the path prop matches the current URL */&&&&&&&&&& &Route path="/" component={Home}/&&&&&&&&&&& &Route path="/category" component={Category}/&&&&&&&&&&& &Route path="/products" component={Products}/&&&&&&&&&/div&&&&&)&&}}
我们在App.js里定义了 Home,Category,和Products组件。尽管目前看起来没问题,当组件变得越来越臃肿,最好将每个组件分成单独的文件。根据经验,如果组件代码超过了10行,我通常会给它创建一个新的文件。从第二个demo开始,我会将App.js里面越来越多的组件分成单独的文件。
在App组件中,我们写了路由跳转的逻辑。 的路径与当前路径匹配,对应组件就会被渲染。对应渲染的组件传给了第二个prop–component。
在这里,/同时匹配/和/category。因此,所有路由都匹配并被渲染。我们该如何避免呢?应该给 path='/'的路由传递exact= {true}props:
JavaScript
`&Route exact={true} path="/" component={Home}/&`
`&Route exact={true} path="/" component={Home}/&`
若只想要路由在路径完全相同时渲染,你就可以使用exactprops。
创建嵌套路由之前,我们需要更深入的理解如何运行。开始吧。
&Route&有三个可以用来定义要渲染内容的props:
component.在上面我们已经看到了。当URL匹配时,router会将传递的组件使用React.createElement来生成一个React元素。
render. 适合行内渲染。在当前路径匹配路由路径时,renderprop期望一个函数返回一个元素。
children.childrenprop跟render很类似,也期望一个函数返回一个React元素。然而,不管路径是否匹配,children都会渲染。
Path and match
path用来标识路由匹配的URL部分。React Router使用了Path-to-RegExp库将路径字符串转为正则表达式。然后正则表达式会匹配当前路径。
当路由路径和当前路径成功匹配,会生成一个对象,我们叫它match。match对象有更多关于URL和path的信息。这些信息可以通过它的属性获取,如下所示:
match.url.返回URL匹配部分的字符串。对于创建嵌套的很有用。
match.path.返回路由路径字符串 – 就是。将用来创建嵌套的。
match.isExact.返回布尔值,如果准确(没有任何多余字符)匹配则返回true。
match.params.返回一个对象包含Path-to-RegExp包从URL解析的键值对。
现在我们完全了解了,开始创建一个嵌套路由吧。
Switch组件
在我们开始示例代码签,我想给你介绍下组件。当一起使用多个时,所有匹配的routes都会被渲染。根据demo1的代码,我添加一个新的route来验证为什么很有用。
JavaScript
&Route exact path="/" component={Home}/&
&Route path="/products" component={Products}/&
&Route path="/category" component={Category}/&
&Route path="/:id" render = {()=& (&p& I want this text to show up for all routes other than '/', '/products' and '/category' &/p&)}/&
&Route exact path="/" component={Home}/&&Route path="/products" component={Products}/&&Route path="/category" component={Category}/&&Route path="/:id" render = {()=& (&p& I want this text to show up for all routes other than '/', '/products' and '/category' &/p&)}/&
当URL为/products,所有匹配/products路径的route都会被渲染。所以,那个path为:id的&Route&会跟着Products组件一块渲染。设计就是如此。但是,若这不是你想要的结果,你应该给你的routes添加&Switch&组件。有&Switch&组件的话,只有第一个匹配路径的子&Route&会渲染。
Demo 2: 嵌套路由
之前,我们给/, /category and /products创建了路由。但如果我们想要/category/shoes这种形式的URL呢?
src/App.js
JavaScript
import React, { Component } from 'react';
import { Link, Route, Switch } from 'react-router-dom';
import Category from './Category';
class App extends Component {
render() {
&nav className="navbar navbar-light"&
&ul className="nav navbar-nav"&
&li&&Link to="/"&Homes&/Link&&/li&
&li&&Link to="/category"&Category&/Link&&/li&
&li&&Link to="/products"&Products&/Link&&/li&
&Route exact path="/" component={Home}/&
&Route path="/category" component={Category}/&
&Route path="/products" component={Products}/&
export default A
/* Code for Home and Products component omitted for brevity */
123456789101112131415161718192021222324252627282930
import React, { Component } from 'react';import { Link, Route, Switch } from 'react-router-dom';import Category from './Category';&class App extends Component {&&render() {&&&&&return (&&&&&&&div&&&&&&&&&&nav className="navbar navbar-light"&&&&&&&&&&&&ul className="nav navbar-nav"&&&&&&&&&&&&&&li&&Link to="/"&Homes&/Link&&/li&&&&&&&&&&&&&&li&&Link to="/category"&Category&/Link&&/li&&&&&&&&&&&&&&li&&Link to="/products"&Products&/Link&&/li&&&&&&&&&&&&/ul&&&&&&& &/nav&&&&&&&Switch&&&&&&&&Route exact path="/" component={Home}/&&&&&&&&Route path="/category" component={Category}/&&&&&&& &Route path="/products" component={Products}/&&&&&&/Switch&&&&&&&/div&&&&&);&&}}export default App;&/* Code for Home and Products component omitted for brevity */
不像React Router之前的版本,在版本4中,嵌套的最好放在父元素里面。所以,Category组件就是这里的父组件,我们将在父组件中定义category/:name路由。
src/Category.jsx
JavaScript
import React from 'react';
import { Link, Route } from 'react-router-dom';
const Category = ({ match }) =& {
return( &div& &ul&
&li&&Link to={`${match.url}/shoes`}&Shoes&/Link&&/li&
&li&&Link to={`${match.url}/boots`}&Boots&/Link&&/li&
&li&&Link to={`${match.url}/footwear`}&Footwear&/Link&&/li&
&Route path={`${match.path}/:name`} render= {({match}) =&( &div& &h3& {match.params.name} &/h3&&/div&)}/&
export default C
1234567891011121314
import React from 'react';import { Link, Route } from 'react-router-dom';&const Category = ({ match }) =& {return( &div& &ul&&&&&&li&&Link to={`${match.url}/shoes`}&Shoes&/Link&&/li&&&&&&li&&Link to={`${match.url}/boots`}&Boots&/Link&&/li&&&&&&li&&Link to={`${match.url}/footwear`}&Footwear&/Link&&/li&&&&&/ul&&&&Route path={`${match.path}/:name`} render= {({match}) =&( &div& &h3& {match.params.name} &/h3&&/div&)}/&&&&/div&)}export default Category;
首先,我们给嵌套路由定义了一些Link。之前提到过,match.url用来构建嵌套链接,match.path用来构建嵌套路由。如果你对match有不理解的概念,console.log(match)会提供一些有用的信息来帮助你了解它。
JavaScript
&Route path={`${match.path}/:name`}
render= {({match}) =&( &div& &h3& {match.params.name} &/h3&&/div&)}/&
&Route path={`${match.path}/:name`}&&render= {({match}) =&( &div& &h3& {match.params.name} &/h3&&/div&)}/&
这是我们首次尝试动态路由。不同于硬编码路由,我们给pathname使用了变量。:name是路径参数,获取category/之后到下一条斜杠之间的所有内容。所以,类似products/running-shoes的路径名会生成如下的一个params对象:
name: 'running-shoes'
{&&name: 'running-shoes'}
参数可以通过match.params或props.match.params来获取,取决于传递哪种props。另外有趣的是我们使用了renderprop。render props非常适合行内函数,这样不需要单独拆分组件。
Demo 3: 带Path参数的嵌套路由
我们让事情变得再复杂一些,可以吗?一个真实的路由应该是根据数据,然后动态展示。假设我们获取了从服务端API返回的product数据,如下所示。
src/Products.jsx
const productData = [
name: 'NIKE Liteforce Blue Sneakers',
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin molestie.',
status: 'Available'
name: 'Stylised Flip Flops and Slippers',
description: 'Mauris finibus, massa eu tempor volutpat, magna dolor euismod dolor.',
status: 'Out of Stock'
name: 'ADIDAS Adispree Running Shoes',
description: 'Maecenas condimentum porttitor auctor. Maecenas viverra fringilla felis, eu pretium.',
status: 'Available'
name: 'ADIDAS Mid Sneakers',
description: 'Ut hendrerit venenatis lacus, vel lacinia ipsum fermentum vel. Cras.',
status: 'Out of Stock'
1234567891011121314151617181920212223242526272829
const productData = [{&&id: 1,&&name: 'NIKE Liteforce Blue Sneakers',&&description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin molestie.',&&status: 'Available'&},{&&id: 2,&&name: 'Stylised Flip Flops and Slippers',&&description: 'Mauris finibus, massa eu tempor volutpat, magna dolor euismod dolor.',&&status: 'Out of Stock'&},{&&id: 3,&&name: 'ADIDAS Adispree Running Shoes',&&description: 'Maecenas condimentum porttitor auctor. Maecenas viverra fringilla felis, eu pretium.',&&status: 'Available'},{&&id: 4,&&name: 'ADIDAS Mid Sneakers',&&description: 'Ut hendrerit venenatis lacus, vel lacinia ipsum fermentum vel. Cras.',&&status: 'Out of Stock'},&];
我们需要根据下面这些路径创建路由:
/products. 这个路径应该展示产品列表。
/products/:productId.如果产品有:productId,这个页面应该展示该产品的数据,如果没有,就该展示一个错误信息。
src/Products.jsx
JavaScript
/* Import statements have been left out for code brevity */
const Products = ({ match }) =& {
const productsData = [
name: 'NIKE Liteforce Blue Sneakers',
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin molestie.',
status: 'Available'
//Rest of the data has been left out for code brevity
/* Create an array of `&li&` items for each product
var linkList = productsData.map( (product) =& {
&Link to={`${match.url}/${product.id}`}&
{product.name}
&h3& Products&/h3&
&ul& {linkList} &/ul&
&Route path={`${match.url}/:productId`}
render={ (props) =& &Product data= {productsData} {...props} /&}/&
&Route exact path={match.url}
render={() =& (
&div&Please select a product.&/div&
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
/* Import statements have been left out for code brevity */&const Products = ({ match }) =& {&&& const productsData = [&&&&{&&&&&&&&id: 1,&&&&&&&&name: 'NIKE Liteforce Blue Sneakers',&&&&&&&&description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin molestie.',&&&&&&&&status: 'Available'&&&&&},&&&&&//Rest of the data has been left out for code brevity&]; /* Create an array of `&li&` items for each product&&var linkList = productsData.map( (product) =& {&&&&return(&&&&&&&li&&&&&&&&&&Link to={`${match.url}/${product.id}`}&&&&&&&&&&&{product.name}&&&&&&&&&/Link&&&&&&&&/li&&&&&&&)&&&&&})&&&return(&&&&&div&&&&&&&&&&div&&&&&&&&& &div&&&&&&&&&&& &h3& Products&/h3&&&&&&&&&&& &ul& {linkList} &/ul&&&&&&&&& &/div&&&&&&&&&&/div&&&&&&&&&&&Route path={`${match.url}/:productId`}&&&&&&&&&&&&render={ (props) =& &Product data= {productsData} {...props} /&}/&&&&&&&&&&Route exact path={match.url}&&&&&&&&&&&&render={() =& (&&&&&&&&&&&&&div&Please select a product.&/div&&&&&&&&&&&&&)}&&&&&&&&/&&&&&&/div&&&)}
首先,我们通过productsData.id创建一列,并把它存储在linkList。路由从路径字符串根据匹配的对应产品id获取参数。
JavaScript
&Route path={`${match.url}/:productId`}
render={ (props) =& &Product data= {productsData} {...props} /&}/&
&Route path={`${match.url}/:productId`}&&render={ (props) =& &Product data= {productsData} {...props} /&}/&
你可能期望使用component = { Product }来替代行内render函数。问题是,我们不仅需要productsData,并顺带把剩余prop也传给Product组件。尽管你还有其他方法,不过我觉的这是最简单的方法了。{...props}使用ES6的 将所有prop传给组件。
这是Product组件的代码。
src/Product.jsx
JavaScript
/* Import statements have been left out for code brevity */
const Product = ({match,data}) =& {
var product= data.find(p =& p.id == match.params.productId);
var productD
if(product)
productData = &div&
&h3& {product.name} &/h3&
&p&{product.description}&/p&
&h4&{product.status}&/h4&
productData = &h2& Sorry. Product doesnt exist &/h2&;
{productData}
1234567891011121314151617181920212223
/* Import statements have been left out for code brevity */&const Product = ({match,data}) =& {&&var product= data.find(p =& p.id == match.params.productId);&&var productData;&&&if(product)&&&&productData = &div&&&&&&&&h3& {product.name} &/h3&&&&&&&&p&{product.description}&/p&&&&&&&&hr/&&&&&&&&h4&{product.status}&/h4&&&&/div&;&&else&&&&productData = &h2& Sorry. Product doesnt exist &/h2&;&&&return (&&&&&div&&&&&&&&div&&&&&&&&& {productData}&&&&&&&/div&&&&&&/div&&&)}
find方法用来查找数组中对象的id属性等于match.params.productId。如果product存在,productData就会展示,如果不存在,“Product不存在”的信息就会被渲染。
保护式路由
最后一个demo,我们将围绕保护式路由的技术进行讨论。那么,如果有人想进入/admin页面,他们会被要求先登录。然而,在我们保护路由之前还需要考虑一些事情。
类似服务端重定向,会将history堆栈的当前路径替换为新路径。新路径通过toprop传递。这是如何使用:
JavaScript
`&Redirect to={{pathname: '/login', state: {from: props.location}}}`
`&Redirect to={{pathname: '/login', state: {from: props.location}}}`
如果有人已经注销了账户,想进入/admin页面,他们会被重定向到/login页面。当前路径的信息是通过state传递的,若用户信息验证成功,用户会被重定向回初始路径。在子组件中,你可以通过this.props.location.state获取state的信息。
自定义路由
自定义路由最适合描述组件里嵌套的路由。如果我们需要确定一个路由是否应该渲染,最好的方法是写个自定义组件。下面是通过其他路由来定义自定义路由。
src/App.js
JavaScript
/* Add the PrivateRoute component to the existing Routes */
&Route exact path="/" component={Home} data={data}/&
&Route path="/category" component={Category}/&
&Route path="/login" component={Login}/&
&PrivateRoute authed={fakeAuth.isAuthenticated} path='/products' component = {Products} /&
/* Add the PrivateRoute component to the existing Routes */&Switch&&&&Route exact path="/" component={Home} data={data}/&&&&Route path="/category" component={Category}/&&&&Route path="/login" component={Login}/&&&&PrivateRoute authed={fakeAuth.isAuthenticated} path='/products' component = {Products} /&&/Switch&
若用户已登录,fakeAuth.isAuthenticated返回true,反之亦然。
这是PrivateRoute的定义。
src/App.js
JavaScript
/* PrivateRoute component definition */
const PrivateRoute = ({component: Component, authed, ...rest}) =& {
render={(props) =& authed === true
? &Component {...props} /&
: &Redirect to={{pathname: '/login', state: {from: props.location}}} /&} /&
12345678910
/* PrivateRoute component definition */const PrivateRoute = ({component: Component, authed, ...rest}) =& {&&return (&&&&&Route&&&&&&{...rest}&&&&&&render={(props) =& authed === true&&&&&&&&? &Component {...props} /&&&&&&&&&: &Redirect to={{pathname: '/login', state: {from: props.location}}} /&} /&&&)}
如果用户已登录,路由将渲染Admin组件。否则,用户将重定义到 /login登录页面。这样做的好处是,定义更明确,而且PrivateRoute可以复用。
最后,下面是Login组件的代码:
src/Login.jsx
JavaScript
import React from 'react';
import { Redirect } from 'react-router-dom';
class Login extends React.Component {
constructor() {
this.state = {
redirectToReferrer: false
// binding 'this'
this.login = this.login.bind(this);
fakeAuth.authenticate(() =& {
this.setState({ redirectToReferrer: true })
render() {
const { from } = this.props.location.state || { from: { pathname: '/' } }
const { redirectToReferrer } = this.
if (redirectToReferrer) {
&Redirect to={from} /&
&p&You must log in to view the page at {from.pathname}&/p&
&button onClick={this.login}&Log in&/button&
/* A fake authentication function */
export const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true
setTimeout(cb, 100)
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
import React from 'react';import { Redirect } from 'react-router-dom';&class Login extends React.Component {&&&constructor() {&&&&super();&&&&&this.state = {&&&&&&redirectToReferrer: false&&&&}&&&&// binding 'this'&&&&this.login = this.login.bind(this);&&}&&&login() {&&&&&fakeAuth.authenticate(() =& {&&&&&&this.setState({ redirectToReferrer: true })&&&&})&&}&&&render() {&&&&const { from } = this.props.location.state || { from: { pathname: '/' } }&&&&const { redirectToReferrer } = this.state;&&&&&if (redirectToReferrer) {&&&&&&return (&&&&&&&&&Redirect to={from} /&&&&&&&)&&&&}&&&&&return (&&&&&&&div&&&&&&&&&&p&You must log in to view the page at {from.pathname}&/p&&&&&&&&&&button onClick={this.login}&Log in&/button&&&&&&&&/div&&&&&)&&}}&/* A fake authentication function */export const fakeAuth = {&&&isAuthenticated: false,&&authenticate(cb) {&&&&this.isAuthenticated = true&&&& setTimeout(cb, 100)&&},}
下面这行是的示例,es6的特性之一。
`const { from } = this.props.location.state || { from: { pathname: '/' } }`
`const { from } = this.props.location.state || { from: { pathname: '/' } }`
让我们把所有片段拼凑到一块,好吗?这是我们使用React Router创建的应用最终效果:
Demo 4: 保护式路由
如你在本文中所看到的,React Router是一个帮助React构建更完美,更声明式的路由库。不像React Router之前的版本,在v4中,一切就“只是组件”。而且,新的设计模式也更完美的使用React的构建方式来实现。
在本次教程中,我们学到了:
如何配置和安装React Router
基础版路由,和一些基础组件,例如, 和
如何构建一个有导航功能的极简路由和嵌套路由
如何根据路径参数构建动态路由
最后,我们还学习了一些高级路由技巧,用来创建保护式路由的最终demo。
感谢作者:
可能感兴趣的话题
关于伯乐前端
伯乐前端分享Web前端开发,包括JavaScript,CSS和HTML5开发技术,前端相关的行业动态。
新浪微博:
推荐微信号
(加好友请注明来意)
– 好的话题、有启发的回复、值得信赖的圈子
– 分享和发现有价值的内容与观点
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 翻译传播优秀的外文文章
– 国内外的精选文章
– UI,网页,交互和用户体验
– 专注iOS技术分享
– 专注Android技术分享
– JavaScript, HTML5, CSS
– 专注Java技术分享
– 专注Python技术分享
& 2018 伯乐在线}

我要回帖

更多关于 win10哪个版本最贵 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信