文章目录
介绍
CS 与 BS结合:像 React,Vue 此类框架,转移了部分服务器的功能到客户端。将CS 和 BS 加以结合。客户端只用请求一次服务器,服务器就将所有js代码返回给客户端,所有交互类操作都不再依赖服务器。 客户端只有在需要服务器的数据时才会使用json通信一下,其他时间都在客户端利用js操作、暂存数据这样就极大减轻了服务器压力。
React 特性
○ 虚拟DOM树
React 通过对 DOM 的模拟,最大限度地减少与DOM的交互。将网页的DOM树完全复制一份虚拟的 DOM 树放到内存中。
○ 数据驱动
维护虚拟 DOM 树,当发现某些节点发生变化时,并不一定修改原来的 DOM 树(在网页中看到的每一个区域),比如某些分支可能会发生该改变时,首先会修改虚拟树中的这几个节点,而后将这几个节点与原节点对比。才会对真正发生改变的节点做出修改。

○ JSX文件
为了构建交互式用户界面,使用了 JSX。JSX 的完整版本是一个JavaScript语法扩展,它极大地简化了组件的创建。它支持HTML引用并使子组件渲染更容易。它本质上是一组 React 编写快捷方式。使用带有一些规则的 createElement 可以使源代码更具可读性和直接性。首先写jsx文件,运行时会先将所编写的jsx编译为js,编译完之后,将js文件运行在浏览器。
React 作用
将语言框架通用和优化,React主要用于构建UI。可以在React里传递多种类型的参数,如声明代码,帮助你渲染出UI、也可以是静态的HTML DOM元素、也可以传递动态变量、甚至是可交互的应用组件,性能出众,代码逻辑简单。
Git BashNode.jscreate-react-appGit Bash,直接输入下面命令执行npm i -g create-react-app
create-react-app react-app # react-app可以替换为其它名称
npm start # 启动应用
组件类似于一个 class,将一些 html、data(数据)、事件函数组合在一起成为一个组件
定义组件:定义好组件之后,需要将组件渲染出来,index.js 就是所有 js 的入口, 并引入React与Component组件。
// box.js 文件
import React, { Component } from 'react'; // 快捷键:imrc
// 引入React原因:将jsx编译为js
// 通过React.createElement()
class Box extends Component { // 快捷键:CC
state = { } //局部变量
// component类的函数,用来返回当前组件最后的渲染html结构
// render方法只能返回一个标签及所包含内容
render() {
return (
<h1> Hello world</h1>
);
}
}
export default Box;
// index.js 文件
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import 'bootstrap/dist/css/bootstrap.css'; // 引入bootstrap库
import Box from './components/box'; // 引入box
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Box /> // Box组件,见box.js
</React.StrictMode>
);
React Fragment 介绍与使用:
(1) render()方法只能返回一个标签及所包含内容,想返回多个并列标签时需要包含在一个标签内。
(2) React.Fragment 组件能够在不额外创建 DOM 元素的情况下,让render()方法中返回多个元素。Fragments 允许将子列表分组,而无需向DOM添加额外标签。
理解起来就是在我们定义组件的时候,return里最外层包裹的div往往不想渲染到页面,那么就要用到Fragment组件了。
class Box extends Component {
state = { }
render() {
return (
<React.Fragment>
<h1> Hello world</h1>
<button>left</button>
<button>right</button>
</React.Fragment>
);
}
}
在 jsx 中写 js 与 html 标签
jsx 中可以在任意地方定义 html 标签,但要注意,jsx 里的 html 标签中写 js 代码时都需要加 {} 括起来,{} 中只能写表达式。
render() {
return (
<React.Fragment>
<h1>{this.toString()}</h1> // html 标签内写js
<button className="btn btn-primary">left</button>
<button>right</button>
</React.Fragment>
);
}
toString(){
return `x: ${this.state.x}`;
// 或者
// const {x} = this.state; //ES6写法相当于const xxx = this.state.xxx
// return `x:${x}`;
}
设置样式
jsx文件下的html 标签中设置类名以用于 css 样式时,需要将 class =" "写为 className。 由于 jsx 下 html 标签与 js 语句混合,写 class 可能会与实际js中的同名类产生冲突。
className
return (
<React.Fragment>
<h1>{this.toString()}</h1>
<button className="btn btn-primary m-2">left</button>
<button className="btn btn-success m-2">right</button>
</React.Fragment>
// m-2 为 bootstrap 中 margin=2 的简写方式
);
style
render() {
return (
<React.Fragment>
// style样式:第一层{}代表里面是表达式,第二层{}代表里面是对象,即样式变量的内容
<div style={{
width: "50px",
height: "50px",
backgroundColor: "pink",
color: "white",
textAlign: "center",
lineHeight: "50px",
borderRadius: "5px",
}}>{this.toString()}</div>
<button className="btn btn-primary m-2">left</button>
<button className="btn btn-success m-2">right</button>
</React.Fragment>
);
}
等价于:
styles = {
width: "50px",
height: "50px",
backgroundColor: "pink", // css中所有 - 命名均改为驼峰命名法
}
render() {
return ( // 标签内 style={this.styele} 即可
<React.Fragment>
<div style={this.styles}>{this.toString()}</div>
<button className="btn btn-primary m-2">left</button>
<button className="btn btn-success m-2">right</button>
</React.Fragment>
);
}
数据驱动改变 style
将 style 与 state(局部变量) 值相关联,通过改变 state 里的值来改变 style。当局部变量改变时,通过接口实现所有被这个值影响的组件都改变。
state = {
x: 1,
}
getStyles() {
let styles = {
width: "50px",
height: "50px",
backgroundColor: "pink",
color: "white",
textAlign: "center",
lineHeight: "50px",
borderRadius: "5px",
margin: '5px',
};
if (this.state.x === 0){
styles.backgroundColor = 'orange'; // 数据驱动改变style
}
return styles;
}
render() {
return (
// 直接调用 getStyles()函数
<React.Fragment>
<div style={this.getStyles()}>{this.toString()}</div>
<button className="btn btn-primary m-2">left</button>
<button className="btn btn-success m-2">right</button>
</React.Fragment>
);
}
渲染列表
使用 map 函数
遍历类写法需给每个标签元素定义唯一 key 属性,用来帮助React快速找到被修改的DOM元素
class Box extends Component {
state = {
x: 1,
colors: ['red','green','blue'], // 定义渲染列表,这里用来修改div元素内容
}
render() {
return (
<React.Fragment>
{this.state.colors.map(color => (
<div key={color}>{color}</div> // 这里建立div并将内容赋值为上述列表
))}
</React.Fragment>
);
}
}
Conditional Rendering
A && B && C ...:从前往后,返回第一个为 false 的表达式。( 如果全为 true,就返回最后一个 true 表达式)
A || B || C ...:从前往后,返回第一个为 true 的表达式。(如果全为 false,就返回最后一个 false 表达式。)
即利用逻辑表达式的短路原则:
render() {
return (
<React.Fragment>
{this.state.colors.length === 0 && <p> No colors</p>}
// 即当 colors 有元素时无操作, 即当 colors 无元素时显示 No color
{this.state.colors.map(color => (
<div key={color}>{color}</div>
))}
</React.Fragment>
);
}
绑定事件
e.q 给 button 按钮绑定事件:
class Box extends Component {
handleClickLeft(){
console.log("click left",this);
}
handleClickRight(){
console.log("click right",this);
}
render() {
//仅仅是绑定函数,而不是在渲染时就将返回值传过来,因此handleClickleft不加()
return (
<React.Fragment>
<div style={this.getStyles()}>{this.toString()}</div>
<button onClick={this.handleClickLeft} className="btn btn-primary m-2">left</button>
<button onClick={this.handleClickRight} className="btn btn-success m-2">right</button>
</React.Fragment>
}
}
此时,输出的 this 不是 Box 类,而是 undifind。
如何使得方法里的 this 仍属于 Box 类:
// 法一:箭头函数(推荐)
// 法二: bind 函数
代码如下:
handleClickLeft=()=>{ // 法一:箭头函数
console.log("click left",this);
}
handleClickRight(){
console.log("click right",this);
}
render() {
return (
<React.Fragment>
<div style={this.getStyles()}>{this.toString()}</div>
<button onClick={this.handleClickLeft} className="btn btn-primary m-2">left</button>
<button onClick={this.handleClickRight.bind(this)} className="btn btn-success m-2">right</button>
</React.Fragment> // 法二:bind函数
);
}
修改 state 里的值
直接 this.state.x-- 不会影响到页面div的显示的x值,
如果想要让 state 里的 x 的修改影响到render函数的话, 必须用 setState() 函数 (通过重新调用 render 修改 div 里 x 值)
class Box extends Component {
state = {
x: 1,
}
handleClickLeft = () => {
this.setState({ // setState() 函数
x: this.state.x - 1
});
}
handleClickRight = () => {
this.setState({ // setState() 函数
x: this.state.x + 1
});
}
render() {
return (
<React.Fragment>
<div style={this.getStyles()}>{this.toString()}</div>
<button onClick={this.handleClickLeft} className="btn btn-primary m-2">left</button>
<button onClick={this.handleClickRight} className="btn btn-success m-2">right</button>
</React.Fragment>
);
}
}
通过按钮修改css属性:
将 state 里的值赋值给某一样式的属性,通过按钮修改state 值从而修改 css 样式
class Box extends Component {
state = {
x: 10, // state值
}
handleClickLeft = () => {
this.setState({
x: this.state.x - 10 // setState() 修改 state值, 重新调用 render() 函数
});
}
handleClickRight = () => {
this.setState({
x: this.state.x + 10 // setState() 修改 state值,重新调用 render() 函数
});
}
render() {
return (
<React.Fragment>
<div style={this.getStyles()}>{this.toString()}</div>
<button onClick={this.handleClickLeft} className="btn btn-primary m-2">left</button>
<button onClick={this.handleClickRight} className="btn btn-success m-2">right</button>
</React.Fragment>
);
}
getStyles() {
let styles = {
width: "50px",
height: "50px",
backgroundColor: "pink",
color: "white",
textAlign: "center",
lineHeight: "50px",
borderRadius: "5px",
margin: '5px',
marginLeft: this.state.x, // state值赋值给 css 属性值
};
if (this.state.x === 0){
styles.backgroundColor = 'orange';
}
return styles;
}
}
给事件函数添加参数
handleClickRight = (step) => {
this.setState({
x: this.state.x + step
});
}
handleClickRightTmp = () => {
return this.handleClickRight(50);
}
render() {
return (
<React.Fragment>
<div style={this.getStyles()}>{this.toString()}</div>
<button onClick={this.handleClickLeft} className="btn btn-primary m-2">left</button>
<button onClick={this.handleClickRightTmp} className="btn btn-success m-2">right</button>
</React.Fragment>
);
}
将 handleClickRight() 函数写为箭头匿名函数后等价于:
render() {
// 绑定了一个调用含参 函数 handleClickLeft =(step)=>{ } 的匿名函数
return (
<React.Fragment>
<div style={this.getStyles()}>{this.toString()}</div>
<button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">left</button>
<button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">right</button>
</React.Fragment>
);
}
综上,Box组件的构建步骤:
state 变量,并使得数据驱动 style;setState() 函数改变 state 值;调用setState()能够重新加载 render 函数,才可以对里面的 div 显示进行修改onClick绑定所写函数。这里绑定了一个调用含参函数 handleClickLeft =(step)=>{ } 的匿名函数,()=>this.handleClickRight(10)class Box extends Component {
// 1. 定义 state,并使得数据驱动style
state = {
x: 10,
}
// 2. 通过 handleClickLeft =(step)=>{ } 带参函数 与 setState() 改变state值
// 并能够重新加载 render 函数来对里面的 div 显示进行操作
handleClickLeft = (step) => {
this.setState({
x: this.state.x - step
});
}
handleClickRight = (step) => {
this.setState({
x: this.state.x + step
});
}
render() {
// 3. 给事件绑定函数:通过 render 函数里,按钮事件绑定函数。
// 绑定了一个调用含参函数 handleClickLeft =(step)=>{ } 的匿名函数
return (
<React.Fragment>
<div style={this.getStyles()}>{this.toString()}</div>
<button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">left</button>
<button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">right</button>
</React.Fragment>
);
}
getStyles() {
let styles = {
width: "50px",
height: "50px",
backgroundColor: "pink",
color: "white",
textAlign: "center",
lineHeight: "50px",
borderRadius: "5px",
margin: '5px',
marginLeft: this.state.x, // 数据驱动 style
};
if (this.state.x === 0){
styles.backgroundColor = 'orange';
}
return styles;
}
}
【组合 Component 】组件的构建
组合多个上述定义的
Box组件,形成Boxes组件,并完成属性值的传递。
<注:多个相同子组件时,每个子组件需要有唯一 key 值>
○ 建立 Boxes 类组件,内含多个 Box组件
import React, { Component } from 'react';
import Box from './box';
class Boxes extends Component {
// 1. 设置 state 变量,包括 Box 组件的唯一 key 值与 x 坐标值。
state = {
boxes:[
{id: 1, x: 10},
{id: 2, x: 10},
{id: 3, x: 100},
{id: 4, x: 10},
{id: 5, x: 10},
]
}
// 2. render 函数返回多个 box 组件,通过 map 列表,逐一建立并赋值多个 Box 组件
// 将 box.id 赋值给组件唯一 key,将 box.x 赋值给 Box 组件的 x
render() {
return (
<React.Fragment>
{this.state.boxes.map((box)=>(
<Box
key = {box.id} // id
x = {box.x} // 这里会自动找到 Box 组件里的 x 赋值并存储在 props 中
// 但仅仅是修改了x,并不会改变前端的显示
/>
))}
</React.Fragment>
);
}
}
export default Boxes;
【注】在react组件之间的通信是通过props属性来完成的,比如父组件需要将数据传递给子组件,那么组件在渲染子组件的时候,直接将数据作为子组件的属性传参。
○ state 值传递:通过 props 将 Boxes定义的属性值返回传递给 Box的state
class Box extends Component {
state = {
// props类似于state,存储除key以外属于 box 的所有属性
// Boxes 建立的 Box 赋值的 x 存到了 props 里
// 通过 props 传递给了每个 Box
x: this.props.x,
}
handleClickLeft = (step) => {
this.setState({
x: this.state.x - step
});
}
handleClickRight = (step) => {
this.setState({
x: this.state.x + step
});
}
render() {
return (
<React.Fragment>
<div style={this.getStyles()}>{this.toString()}</div>
<button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">left</button>
<button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">right</button>
</React.Fragment>
);
}
getStyles() {
let styles = {
width: "50px",
height: "50px",
backgroundColor: "pink",
color: "white",
textAlign: "center",
lineHeight: "50px",
borderRadius: "5px",
margin: '5px',
marginLeft: this.state.x,
};
if (this.state.x === 0){
styles.backgroundColor = 'orange';
}
return styles;
}
toString(){
return `x: ${this.state.x}`;
}
}
export default Box;
○ 标签传递:通过 props 将Boxes 增加的Box.children子标签,传递给 Box
// Boxes.jsx文件
render() {
return (
<React.Fragment>
{this.state.boxes.map((box)=>(
<Box key = {box.id} x = {box.x}>
<p>Box :</p> // 将 Box 的闭合标签写为双标签
<div>{box.id}</div> // 可在 Box 标签内增加其它标签,属性名为 Box.children
</Box> // 并存储到了 props 中
))}
</React.Fragment>
);
}
// Box.jsx 文件
render() {
console.log(this.props);
return (
<React.Fragment>
{this.props.children[0]} // 通过 props 所存储的 children 将增加的标签传递给Box
<div style={this.getStyles()}>{this.toString()}</div>
<button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">left</button>
<button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">right</button>
</React.Fragment>
);
}
○ 方法传递1:React 子组件调用父组件的方法。
Box子组件调用Boxes父组件内的方法,依旧通过props。实现在 Box 组件中触发 onClick 事件后,在Boxes组件中删除key值对应的Box,即在Box标签内调用Boxes标签的方法
// Boxes.jsx 文件
// 1. Boxes.jsx 文件中写删除的方法
handleDelete = (id) => {
// filter: boxes列表的元素依次判断,若表达式为true则留下,否则删掉
// 即若id不等留下来,相等删除
const newboxes = this.state.boxes.filter(
(x)=>(x.id !== id)
);
this.setState({
boxes: newboxes
})
}
render() {
if(this.state.boxes.length === 0){
return <div className='alert alert-dark'>没有元素可以删除了!!!</div>
}
return (
// 2. 将所写删除方法定义为标签的 onDelete 属性传递给 Box(会存储在 props中)
<React.Fragment>
{this.state.boxes.map((box)=>(
<Box key = {box.id} id={box.id} x = {box.x} onDelete = {this.handleDelete}>
<p>Box :</p>
<div>{box.id}</div>
</Box>
))}
</React.Fragment>
);
}
// Box.jsx 文件
render() {
return (
<React.Fragment>
{this.props.children[0]}
<div style={this.getStyles()}>{this.toString()}</div>
<button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">left</button>
<button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">right</button>
// 3. Box 调用 Boxes 的删除方法 :
// Box 中的 Button 的 onClick 事件,绑定匿名函数来调用含参的删除方法
<button onClick={()=>this.props.onDelete(this.props.id)} className='btn btn-danger m-2'> Delete </button>
</React.Fragment>
);
}
○ 方法传递2:React 父组件调用子组件的方法。
(仅能调用一个子组件方法,无法调用列表子组件)
// 父组件
class Boxes extends Component {
// 1. Boxes 父组件中写入
setChildRef = (ref) => {
this.ChildRef = ref;
}
// 3. Boxes 父组件中写调用 Box 子组件的方法
handleReset = () =>{
this.ChildRef.handleRE()
}
render() {
return (
<React.Fragment>
// 4. 将父组件方法绑定onClick单击事件中,即可实现单击调用子组件的方法
<button onClick={this.handleReset} className='btn btn-primary'> Clear </button>
{this.state.boxes.map((box)=>(
// 2. Boxes 父组件的 Box 子组件标签内增加 ref 属性,并将 setChildRef 传递过来
<Box key = {box.id} ref={this.setChildRef} id={box.id} x = {box.x} onDelete = {this.handleDelete}>
<p>Box :</p>
<div>{box.id}</div>
</Box>
))}
</React.Fragment>
);
}
}
// 子组件
class Box extends Component {
state = {
x: this.props.x,
}
// 子组件中被调用的方法
handleRE = () =>{
this.setState({
x: 0
});
}
render() {
return (
<React.Fragment>
......
</React.Fragment>
);
}
}
Transformers开始在视频识别领域的“猪突猛进”,各种改进和魔改层出不穷。由此作者将开启VideoTransformer系列的讲解,本篇主要介绍了FBAI团队的TimeSformer,这也是第一篇使用纯Transformer结构在视频识别上的文章。如果觉得有用,就请点赞、收藏、关注!paper:https://arxiv.org/abs/2102.05095code(offical):https://github.com/facebookresearch/TimeSformeraccept:ICML2021author:FacebookAI一、前言Transformers(VIT)在图
我想开始使用“Sinatra”框架进行编码,但我找不到该框架的“MVC”模式。是“MVC-Sinatra”模式或框架吗? 最佳答案 您可能想查看Padrino这是一个围绕Sinatra构建的框架,可为您的项目提供更“类似Rails”的感觉,但没有那么多隐藏的魔法。这是使用Sinatra可以做什么的一个很好的例子。虽然如果您需要开始使用这很好,但我个人建议您将它用作学习工具,以对您来说最有意义的方式使用Sinatra构建您自己的应用程序。写一些测试/期望,写一些代码,通过测试-重复:)至于ORM,你还应该结帐Sequel其中(imho
我有一个将某些事件写入队列的Rails3应用。现在我想在服务器上创建一个服务,每x秒轮询一次队列,并按计划执行其他任务。除了创建ruby脚本并通过cron作业运行它之外,还有其他稳定的替代方案吗? 最佳答案 尽管启动基于Rails的持久任务是一种选择,但您可能希望查看更有序的系统,例如delayed_job或Starling管理您的工作量。我建议不要在cron中运行某些东西,因为启动整个Rails堆栈的开销可能很大。每隔几秒运行一次它是不切实际的,因为Rails上的启动时间通常为5-15秒,具体取决于您的硬件。不过,每天这样做几
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。我一直在Rails上做两个项目,它们运行良好,但在这个过程中重新发明了轮子,自来水(和热水)和止痛药,正如我随后了解到的那样,这些已经存在于框架中。那么基本上,正确了解框架中所有智能部分的最佳方法是什么,这将节省时间而不是自己构建已经实现的功能?从第1页开始阅读文档?是否有公开所有内容的特定示例应用程序?一个特定的开源项目?所有的rails交通?还是完全
关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。关闭4年前。Improvethisquestion我希望能够将模板化的YARD文档样式注释插入到我现有的Rails遗留应用程序中。目前它的评论很少。我想要具有指定参数的类header和方法header(通过从我假定的方法签名中提取)和返回值的占位符。在PHP代码中,我有一些工具可以检查代码并在适当的位置创建插入到代码中的文档header注释。在带有Ducktyping等的Ruby中,我确信诸如@params等类型之类
我尝试用Ruby设计一个基于Web的应用程序。我开发了一个简单的核心应用程序,在没有框架和数据库的情况下在六边形架构中实现DCI范例。核心六边形中有小六边形和网络,数据库,日志等适配器。每个六边形都在没有数据库和框架的情况下自行运行。在这种方法中,我如何提供与数据库模型和实体类的关系作为独立于数据库的关系。我想在将来将框架从Rails更改为Sinatra或数据库。事实上,我如何在这个核心Hexagon中实现完全隔离的rails和mongodb的数据库适配器或框架适配器。有什么想法吗? 最佳答案 ROM呢?(Ruby对象映射器)。还有
据我了解,Python的扭曲框架为网络通信提供了更高级别的抽象(?)。我正在寻找在Rails应用程序中使用与twisted等效的Ruby。 最佳答案 看看EventMachine.它不像Twisted那样广泛,但它是围绕事件驱动网络编程的相同概念构建的。 关于python-Ruby是否有相当于Python的扭曲框架作为网络抽象层?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/9
是否可以在单个事件机器中运行多个服务器?我的意思是,多个服务可以由一个客户端连接同时使用。例如,登录服务器对用户进行身份验证,然后用户可以同时使用聊天室和简单的游戏,例如带有单个客户端套接字的跳棋?或者我是否需要为每个服务使用多个事件机器react器? 最佳答案 我试过了,它正在工作:#!/usr/bin/envrubyrequire'eventmachine'moduleEchoServerdefpost_initputs"--someoneconnectedtotheechoserver!"enddefreceive_datad
我想使用比Rails(Sinatra/Ramaze/Camping)更轻的框架,但我担心这样做我将无法使用许多以插件形式为Rails定制的共享库.这是一个主要问题,还是这些插件中的大多数都可以跨不同的Ruby框架使用?使用Ruby框架而不是Rails是否还有其他潜在的缺点? 最佳答案 您仍然可以使用gems在你提到的所有框架中,很多东西都是可重用的。想要交换一个新的ORM,没问题。想要一个花哨的shmacy语法高亮,没问题。Rails一直在大力插入摆脱旧的插件模型,转而使用gems。如果其他框架之一符合您的需求,最好使用它。请记住,
我将以下代码放入RSpec测试中:it{shouldvalidate_format_of(:email).not_with('test@test')}并设置实际的类:validates:email,:presence=>true,:format=>/\b[A-Z0-9._%-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}\b/i当我运行测试时,我得到:失败:1)用户失败/错误:它{应该validate_format_of(:email).not_with('test@test')}当电子邮件设置为“test@test”时,预期错误包括“can'tbeblank”,得到错误