其他章节请看:

react实战 系列

React 中的表单和路由的原理

React 中的表单是否简单好用,受控组件和非受控是指什么?

React 中的路由原理是什么,如何更好的理解 React 应用的路由?

请看下文:

简单的表单

你有见过在生成环境中没有涉及任何表单的应用吗?大多 web 应用都会涉及表单。比如登录、注册、提交信息。

表单由于难用有时名声不好,于是许多框架针对表单做了一些神奇的事情来减轻程序员的负担。

React 并未采用神奇的方法,但它却能让表单更容易使用。

在做实验测试 react 中表单是否真的容易使用之前,我们在稍微聊一下表单。

不同框架处理表单的方式都不尽相同,很难说一种比另一种要好。有的需要我们了解很多框架的内部实现,有的很容易使用但是可能不够灵活。

开发者需要有一个思维模型(针对表单),该模型能让开发者创建可维护的代码,并在 bug 出现时及时修复他们。

当涉及表单时,React 不会提供太多“魔法”,并且在过多了解表单和过少了解之间找到了一个中间带。React 中表单的思维模型其实是你已经了解的东西,并没有特别的 api。表单就是我们看到的东西。开发者使用组件、状态、属性来创建表单。

我们在回顾下 React 部分思维模式:

  • React 有两种主要处理数据的方式:状态属性
  • 组件是 js 类,除了 react 提供的生命周期钩子、render(),组件还可以拥有自定义的类方法,可以用来相应事件,或者做任何其他事
  • 与常规的 dom 元素一样,可以在 React 组件上注册事件,例如 onClick、onChange等
  • 父组件可以将回调函数作为属性传给子组件,使组件之间通信。

下面我们通过实验测试 react 中表单是否真的简单。

表单小示例

创建一个子组件 CreateCommentComponet,用户能通过它来提交评论。

<script type="text/babel">
class CreateCommentComponet extends React.Component {
constructor(props) {
super(props);
this.state = { text: "" };
this.onInputChange = this.onInputChange.bind(this);
} onInputChange(e) {
// e 是 React 合成事件,对用户来说就像原生的 event。
const text = e.target.value;
this.setState(() => ({ text: text })); // {1}
}
render() {
return <div className="CreateCommentComponet">
<p>您输入的评论是:{this.state.text}</p>
<textarea
value={this.state.text} /* {2} */
placeholder="请输入评论"
onChange={this.onInputChange}
/>
</div>
}
} ReactDOM.render(
<CreateCommentComponet />,
document.getElementById('root')
);
</script>

页面内容如下:

<div id="root">
<div class="CreateCommentComponet">
<p>您输入的评论是:</p>
<textarea placeholder="请输入评论"></textarea>
</div>
</div>

当我们在 textarea 中输入文字,例如 111,文字也会同步到 p 元素中。就像这样:

<div id="root">
<div class="CreateCommentComponet">
<p>您输入的评论是:111</p>
<textarea placeholder="请输入评论">111</textarea>
</div>
</div>

为什么我输入不了字符?

比如现在我们将 this.setState(行{1})注释,然后给 textarea 输入字符,页面什么也没发生。

初学者这时就很困惑,为什么我输不了字符,什么鬼?

其实这是正常的,也正是 React 尽职的表现。

React 保持虚拟 dom真实 dom 的同步,现在用户给 textarea 输入字符,尝试更改 dom,但用户并没有更新虚拟 dom,所以 React 也不会对用户做任何改变。

假如此时 textarea 变了,那岂不是又回到老的做事方式,由我们自己管理真实 dom。而非现在面向 React 编程,即通过声明组件在不同状态下的行为和外观,React 根据虚拟 DOM 生成和管理真实 dom。

如果注释 value={this.state.text}(行{2}),此刻就由受控组件变成非受控组件,也就是说 textarea 的值不在受 React 控制。

通过事件和事件处理器更新状态来严格控制如何更新,按照这种设计的组件称为受控组件。因为我们严格控制了组件。非受控组件,组件保持自己的内部状态,不在使用 value 属性设置数据。

Tip:有关受控组件和非受控组件的介绍请看 这里

表单验证和清理

表单得加上前端校验,告诉用户提供的数据不能满足要求或无意义。

至于清理,笔者这里自定义了一个 Filter 类,用于清理冒犯性的内容,比如将 fuck 清理为 ****。

Tip:清理的功能,笔者最初想用 npm 包 bad-words,但它好像只支持 require 这种构建的环境。

<script type="text/babel">
/*
bad-words
自定义清理函数。
用法如下:
let filter = new Filter()
filter.clean('a b fuck c fuck') => a b **** c ****
*/
class Filter {
constructor() {
this.cleanWord = ['fuck']
this.placeHolder = '*'
}
// 增加过滤单词
addCleanWord(...words){
this.cleanWord = [...this.cleanWord, ...words]
}
clean(msg) {
this.cleanWord.forEach(
item => msg = msg.replace(new RegExp(item, 'g'),
new Array(item.length).fill(this.placeHolder).join('')))
return msg
}
}
class CreateCommentComponet extends React.Component {
constructor(props) {
super(props);
this.state = { text: "", valid: false };
this.handleSubmit = this.handleSubmit.bind(this)
this.onInputChange = this.onInputChange.bind(this);
}
handleSubmit = () => {
if (!this.state.valid) {
console.log('校验失败,不能提交')
return
}
console.log('提交')
}
// e 是 React 合成事件,对用户来说就像原生的 event。
onInputChange(e) {
// 清理输入。
const filter = new Filter()
const text = filter.clean(e.target.value);
this.setState(() => ({ text: text, valid: text.length <= 10 }));
}
render() {
return <div className="CreateCommentComponet">
<p>您输入的评论是:{this.state.text}</p>
<textarea
value={this.state.text} /* {2} */
placeholder="请输入评论"
onChange={this.onInputChange}
/>
<p><button onClick={this.handleSubmit}>submit</button></p>
</div>
}
} ReactDOM.render(
<CreateCommentComponet />,
document.getElementById('root')
);
</script>

当用户输入 1 2 fuc fuck 时,则会显示 您输入的评论是:1 2 fuc ****

最终版本

最后加上父组件,子组件将提交的评论发送给父组件,并重置自己。再由父组件提交评论到后端。

<script type="text/babel">
class CommentComponet extends React.Component {
// 默认没有评论
state = { comments: [] }
handleCommontSubmit = (commont) => {
// 本地模拟提交
this.setState({ comments: [...this.state.comments, commont] })
}
render() {
return <div>
<p>已发表评论有:</p>
{
this.state.comments.length === 0
? <p>暂无评论</p>
: <ul>{this.state.comments.map((item, i) => <li key={i}>{item}</li>)}</ul>
}
<CreateCommentComponet handleCommontSubmit={this.handleCommontSubmit} />
</div>
}
}
class CreateCommentComponet extends React.Component {
constructor(props) {
super(props);
this.state = { text: "", valid: false };
this.handleSubmit = this.handleSubmit.bind(this)
this.onInputChange = this.onInputChange.bind(this);
}
handleSubmit = () => {
if (!this.state.valid) {
console.log('校验失败,不能提交')
return
}
this.props.handleCommontSubmit(this.state.text)
// 重置
this.setState({ text: '' })
}
// e 是 React 合成事件,对用户来说就像原生的 event。
onInputChange(e) {
const text = e.target.value;
this.setState(() => ({ text: text, valid: text.length <= 10 })); // {1}
}
render() {
return <div className="CreateCommentComponet">
<p>您输入的评论是:{this.state.text}</p>
<textarea
value={this.state.text} /* {2} */
placeholder="请输入评论"
onChange={this.onInputChange}
/>
<p><button onClick={this.handleSubmit}>submit</button></p>
</div>
}
} ReactDOM.render(
<CommentComponet />,
document.getElementById('root')
);
</script>

页面结构如下:

<div id="root">
<div>
<p>已发表评论有:</p>
<p>暂无评论</p>
<div class="CreateCommentComponet">
<p>您输入的评论是:</p><textarea placeholder="请输入评论"></textarea>
<p><button>submit</button></p>
</div>
</div>
</div>

当我们输入两条评论后,页面结构如下:

<div id="root">
<div>
<p>已发表评论有:</p>
<ul>
<li>评论1</li>
<li>评论2...</li>
</ul>
<div class="CreateCommentComponet">
<p>您输入的评论是:</p><textarea placeholder="请输入评论"></textarea>
<p><button>submit</button></p>
</div>
</div>
</div>

Tip:按照现在的写法,如果有 10 个 input,则需要定义 10 个 onInputChange 事件,其实是可以优化成一个,请看 这里

React 路由

根据前面两篇博文的学习,我们会创建 react 组件,也理解了 react 的数据流和生命周期。似乎还少点什么?

平时总说的 SPA(单页面应用)就是前后端分离的基础上,再加一层前端路由

Tip:在新的 Web 应用框架中,服务器最初会下发 html、css、js等资源,之后客户端应用“接管”工作,服务器只负责发送原始数据(通常是 json)。从这里开始,除非用户手动刷新页面,否则服务器只会下发 json 数据。

路由有许多含义和实现,对我们来说,它是一个资源导航系统。如果你使用浏览器,它会根据不同的 url(网址) 返回不同的页面(数据)。在服务端,路由着重将传入的请求路径匹配到源自数据库的资源。对于 React ,路由通常意味着将组件(人们想要的资源)匹配到 url(将用户想要的东西告诉系统的方式)

Tip:需要路由的原因有很多,例如:

  • 界面的不同部分需要。用户需要在浏览器历史中前进和后退
  • 网站的不同部分需要他们自己的 url,以便轻松的将人们路由到正确的地方
  • 按页面拆分代码有助于促进模块化,从而拆分应用

下面我们构建一个简单的路由,以便更好的理解 React 应用的路由。

比如之前学习 react 路由中有这么一段代码:

<Router>
<div>
<h2>About</h2>
<hr />
<ul>
<li>
<Link to="/about/article1">article1</Link>
</li>
<li>
<Link to="/about/article2">article2</Link>
</li> </ul>
<Switch>
<Route path="/about/article1">
文章1...
</Route>
<Route path="/about/article2">
文章2...
</Route>
</Switch>
</div>
</Router>

这里有 Router、Route、Link,为什么这就是一个嵌套路由,里面发生了什么?

自定义路由效果展示

Tip:为了方便,笔者就在开源项目 spug 中进行。用 react cli 创建的项目也都可以。

创建路由 Route.js

以下是 Route.js 的完整代码。功能很简单,就是作为 url 和组件映射的数据容器

import PropTypes from 'prop-types';
import React from 'react';
// package.json 没有,或许像 prop-types 自动已经引入了
import invariant from 'invariant'; /**
* Route 组件主要作为 url 和 组件映射的数据容器
* Route 不渲染任何东西,如果渲染,就报错。好奇怪!
* 其实这只是一种 React 可以理解,开发者也能通过它将路由和组件关联在一起的方式而已。
*
* 用法:<Route path="/home" component={Home} />。路径 `/home` 指向 `Home` 组件
*/
class Route extends React.Component {
static propTypes = {
path: PropTypes.string,
// React 元素或函数
component: PropTypes.oneOfType([PropTypes.element, PropTypes.func])
};
// 一旦被调用,我们就知道事情不对了。
render() {
return invariant(false, "<Route> elements are for config only and shouldn't be rendered");
}
} export default Route;

Tip:invariant 一种在开发中提供描述性错误但在生产中提供一般错误的方法。这里一旦调用了 render() 就会报错,我们就知道事情不对了。

var invariant = require('invariant');

invariant(someTruthyVal, 'This will not throw');
// No errors invariant(someFalseyVal, 'This will throw an error with this message');
// Error: Invariant Violation: This will throw an error with this message

第一个参数是假值就报错,真值不会报错。

创建路由器 Router.js

Router 用于管理路由。请看这段代码:

<Router location={this.state.location}>
<Route path="/" component={Home} />
<Route path="/test" component={Test} />
</<Router>

当 Router 的 location 是 /,则渲染 Home 组件。如果是 /test 则渲染 Test 组件。

大概思路是:通过一个变量 routes 来存储路由信息,比如 / 对应一个 Home,/test 对应 Test,借助 enroute(微型路由器),根据不同的 url 渲染出对应的组件。

完整代码如下:

import PropTypes from 'prop-types';
import React, { Component } from 'react';
// 微型路由器,使用它将路径匹配到组件上
import enroute from 'enroute';
import invariant from 'invariant'; export default class Router extends Component {
// 定义两个属性。必须有子元素 和 location。其中子元素至少有2个,否则就不是数组类型。
// 你换成其他规则也没问题
static propTypes = {
children: PropTypes.array.isRequired,
location: PropTypes.string.isRequired
};
constructor(props) {
super(props); /**
* 用来存储路由信息
* 例如:{/test: render(), /profile: render(), ...}
*/
this.routes = {}; // 添加路由
this.addRoutes(props.children); // 注册路由器。当匹配对应 url,则会调用对应的方法,比如匹配 /test,则调用相应的 render() 方法。render() 方法会返回相应的 React 组件
this.router = enroute(this.routes);
} // 向路由器中添加路由。需要两个东西:正确的 url 和 对应的组件
addRoute(element, parent) {
// Get the component, path, and children props from a given child
const { component, path, children } = element.props; // 没有 component 就会报错
invariant(component, `Route ${path} is missing the "path" property`);
// path 必须是字符串
invariant(typeof path === 'string', `Route ${path} is not a string`); // Set up Ccmponent to be rendered
// 返回组件。参考 enroute 的用法。
const render = (params, renderProps) => { // {1} // 如果匹配 <Route path="/test">,this 则是父组件 Router
const finalProps = Object.assign({ params }, this.props, renderProps); // Or, using the object spread operator (currently a candidate proposal for future versions of JavaScript)
// const finalProps = {
// ...this.props,
// ...renderProps,
// params,
// };
// finalProps 有父组件的 location、children 和 enroute 传来的 params
const children = React.createElement(component, finalProps);
// parent.render 父路由的 render(及行 {1} 定义的 render() 方法)
return parent ? parent.render(params, { children }) : children;
}; // 有父路由,则连接父路由
const route = this.normalizeRoute(path, parent); // If there are children, add those routes, too
if (children) {
// 注册路由
this.addRoutes(children, { route, render });
} // 将路由和 render 关联
this.routes[this.cleanPath(route)] = render;
} addRoutes(routes, parent) {
// 每个 routes 中的元素将调用一次回调函数(即下面的第二个实参)
// 下面这个 this 是什么?是这个组件的实例,箭头函数是没有 this 的。
React.Children.forEach(routes, route => this.addRoute(route, parent));
}
// 将// 替换成 /
cleanPath(path) {
return path.replace(/\/\//g, '/');
}
// 确保父路由和子路由返回正确的 url。例如:`/a` 和 `b` => `/a/b`
normalizeRoute(path, parent) {
// 绝对路由,直接返回
if (path[0] === '/') {
return path;
}
// 没有父路由,直接返回
if (!parent) {
return path;
}
// 连接父路由
return `${parent.route}/${path}`;
}
// 这里需要有 location 属性
// 将 url 对应的组件渲染出来
render() {
const { location } = this.props;
invariant(location, '<Router/> needs a location to work');
return this.router(location);
}
}

Router 组件说明:

  • render() - 将 url 对应的组件渲染出来
  • cleanPath()normalizeRoute() - 用于路径处理
  • addRoutes() - 依次注册子路由
  • addRoute() - 注册路由,最后存入变量 routes 中。例如 / 对应 / 的 render()/test 对应 /test 的 render()。对于嵌套路由,只会返回父路由对应的组件。
  • constructor() - 定义变量 routes 存储路由信息,通过 addRoutes 添加路由,最后利用 enroute 返回 this.router。于是 render() 就能将 url 对应的组件渲染出来。

TipReact.Children 提供了用于处理 this.props.children 不透明数据结构的实用方法。例如 forEach、map等

入口 App.js

最终测试的入口文件 App.js 代码如下:

import React, { Component } from 'react';
import Route from './myrouter/Route'
import Router from './myrouter/Router'
// 这个库能更改浏览器中的 url
import { history } from './myrouter/history'
// 链接
import Link from './myrouter/Link'
// 类似 404 的组件
import NotFound from 'myrouter/NotFound'; // 以下都是路由切换的组件(或子页面)
import Home from './myrouter/Home'
import Test from './myrouter/Test'
import Post from './myrouter/Post'
import Profile from './myrouter/Profile'
import EmailSetting from './myrouter/EmailSetting' class App extends Component {
componentDidMount() {
// 地址变化时触发
history.listen((location) => {
this.setState({ location: location.pathname })
});
}
// window.location.pathname,包含 URL 中路径部分的一个DOMString,开头有一个“/"。
// 例如 https://developer.mozilla.org/zh-CN/docs/Web/API/Location?a=3 的 pathname 是 /zh-CN/docs/Web/API/Location
state = { link: '', location: window.location.pathname } handleChange = (e) => {
this.setState({ link: e.target.value })
} handleClick = () => {
history.push(this.state.link)
} render() {
return ( <div style={{margin: 20}}>
<div style={{ border: '1px solid red', marginBottom: '20px' }}>
<h3>导航1</h3>
<p>请输入要跳转的导航(例如 /、/test、/posts/:postId、/profile/email、不存在的url):<br />
<input value={this.setState.link} onChange={this.handleChange} />
<button onClick={this.handleClick}>导航跳转</button></p>
</div>
<div style={{ border: '1px solid red', marginBottom: '20px' }}>
<h3>导航2</h3>
<p>
<Link to="/">主页</Link> <Link to="/test">测试</Link>
</p>
</div> <main style={{ border: '1px solid blue' }}>
<h3>不同的子页面:</h3>
{/* 有一个绑定到组件的路由组成的路由器 */}
<Router location={this.state.location}>
<Route path="/" component={Home} />
<Route path="/test" component={Test} />
<Route path="/posts/:postId" component={Post} />
<Route path="/profile" component={Profile}>
<Route path="email" component={EmailSetting} />
</Route>
{/* 都没有匹配到,就渲染 NotFound */}
<Route path="*" component={NotFound}/>
</Router>
</main>
</div>
);
}
} export default App;

Router 的 location 初始值是 window.location.pathname,点击导航跳转时调用会通过 history 更改浏览器的 url,接着会触发 history.listen,于是通过 this.setState 来更改 Router 的 location,React 则会渲染 url 相应的组件。

Tip:其他组件都在与 App.js 同级目录 myrouter 中。

Link.js

一个简单的封装。点击 a 时,调用 history.push() 方法。

import PropTypes from 'prop-types';
import React from 'react';
import { navigate } from './history'; function Link({ to, children }) {
return <a href={to} onClick={e => {
e.preventDefault()
navigate(to)
}}>{children}</a>
} Link.propTypes = {
to: PropTypes.string,
children: PropTypes.node
}; export default Link;

history.js

对 history 库简单处理:

import { createBrowserHistory } from "history";
const history = createBrowserHistory();
const navigate = to => history.push(to);
export {history, navigate}

NotFound.js

import React  from "react";
import Link from './Link'
export default function(){
return <div>
<p>404 !什么也没有。</p>
<Link to='/' children="主页"/>
</div>
}

其他组件

Home.js

// spug 的函数组件都有 `import React from 'react';`,尽管没有用到 React,奇怪!
import React from 'react';
class Home extends React.Component {
render() {
return (
<div className="home">
主页
</div>
);
}
} export default Home;

Post.js

import React from 'react';
class Post extends React.Component {
render() {
return (
<div className="post-component">
<p>post</p>
<p>postId:{this.props.params.postId}</p>
</div>
);
}
} export default Post;

Profile.js

import React from 'react';
class Profile extends React.Component {
render() {
return (
<div className="Profile-component">
<p>个人简介</p>
{this.props.children}
</div>
);
}
} export default Profile;

Tip: 嵌套路由笔者其实没有实现。比如 http://localhost:3000/profile 就会报错。

EmailSetting.js

import React from 'react';
class EmailSetting extends React.Component {
render() { return (
<div className="EmailSetting-component">
<p>个人简介 {'->'} 设置邮件</p>
</div>
);
}
} export default EmailSetting;

Test.js

用于测试 invariant、enroute 等库。

import React from 'react';
import invariant from 'invariant';
import enroute from 'enroute'; function edit(params, props){
// params {id: "3"}
console.log('params', params)
// props {additional: "props"}
console.log('props', props)
} const router = enroute({
'/users/new': function(){},
'/users/:id': function(){},
'/users/:id/edit': edit,
'*': function(){}
}) router('/users/3/edit', {additional: 'props'}) class Test extends React.Component {
render() {
this.addRoutes()
// import invariant from 'invariant';
// return invariant(false, '这个值是假值就会抛出错误')
return <p>测试页</p>
}
log(v){
console.log('v', v)
}
addRoutes() {
[...'abc'].forEach(item => {this.log(item)})
// [...'abc'].forEach(function(item){this.log(item)}, this)
}
} export default Test;

其他章节请看:

react实战 系列

react实战系列 —— React 中的表单和路由的原理的更多相关文章

  1. react实战 系列 —— React 的数据流和生命周期

    其他章节请看: react实战 系列 数据流和生命周期 如何处理 React 中的数据,组件之间如何通信,数据在 React 中如何流动? 常用的 React 生命周期方法以及开源项目 spug 中使 ...

  2. react实战系列 —— react 的第一个组件

    react 的第一个组件 写了 react 有一个半月,现在又有半个月没写了,感觉对其仍旧比较陌生. 本文分两部分,首先聊一下 react 的相关概念,然后不使用任何语法糖(包括 jsx)或可能隐藏底 ...

  3. React中的表单应用

    React中的表单应用 用户在表单填入的内容,属于用户跟组件的互动,所以不能用this.props读取. var Input = React.createClass({ //初始化组件数据 getIn ...

  4. react实战系列 —— 起步(mockjs、第一个模块、docusaurus)

    其他章节请看: react实战 系列 起步 本篇我们首先引入 mockjs ,然后进入 spug 系统,接着模仿"任务计划"模块实现一个类似的一级导航页面("My任务计划 ...

  5. react实战系列 —— 我的仪表盘(bizcharts、antd、moment)

    其他章节请看: react实战 系列 My Dashboard 上一篇我们在 spug 项目中模仿"任务计划"模块实现一个类似的一级导航页面("My任务计划") ...

  6. 七天接手react项目 系列 —— react 脚手架创建项目

    其他章节请看: 七天接手react项目 系列 react 脚手架创建项目 前面我们一直通过 script 的方式学习 react 基础知识,而真实项目通常是基于脚手架进行开发. 本篇首先通过 reac ...

  7. 七天接手react项目 系列 —— react 路由

    其他章节请看: 七天接手react项目 系列 react 路由 本篇首先讲解路由原理,接着以一个基础路由示例为起点讲述路由最基础的知识,然后讲解嵌套路由.路由传参,最后讲解路由组件和一般组件的区别,以 ...

  8. Python Django CMDB项目实战之-3创建form表单,并在前端页面上展示

    基于之前的项目代码 Python Django CMDB项目实战之-1如何开启一个Django-并设置base页.index页.文章页面 Python Django CMDB项目实战之-2创建APP. ...

  9. struts2官方 中文教程 系列五:处理表单

    先贴个本帖的地址,免得其它网站被爬去了struts2教程 官方系列五:处理表单  即 http://www.cnblogs.com/linghaoxinpian/p/6906298.html 下载本章 ...

随机推荐

  1. 『忘了再学』Shell流程控制 — 39、特殊流程控制语句

    目录 1.特殊流程控制语句介绍 2.exit语句 3.break语句 4.continue语句 1.特殊流程控制语句介绍 Shell程序或者说其他的程序,都是顺序执行的,也就是第一行执行完再执行第二行 ...

  2. 「快速学习系列」我熬夜整理了Vue3.x响应性API

    前言 Vue3.x正式版发布已经快半年了,相信大家也多多少少也用Vue3.x开发过项目.那么,我们今天就整理下Vue3.x中的响应性API.响应性APIreactive 作用: 创建一个响应式数据. ...

  3. WPF开发随笔收录-心电图曲线绘制

    一.前言 项目中之前涉及到胎儿心率图曲线的绘制,最近项目中还需要添加心电曲线和血样曲线的绘制功能.今天就来分享一下心电曲线的绘制方式: 二.正文 1.胎儿心率曲线的绘制是通过DrawingVisual ...

  4. 你真的懂Python命名吗?

    转载请注明出处️ 作者:测试蔡坨坨 原文链接:caituotuo.top/7417a7f0.html 大家好,我是测试蔡坨坨. 今天,我们来聊一下Python命名那些事儿. 名为万物之始,万物始于无名 ...

  5. C++库的随机数生成

    C++库为我们提供了很多生成随机数的方法. 使用C的随机数生成法 先学过C语言,或者仅仅用C++做算法的人.对rand()是非常熟悉了.这个函数没有参数,生成0到RAND_MAX的随机数(RAND_M ...

  6. NC20439 [SHOI2017]期末考试

    NC20439 [SHOI2017]期末考试 题目 题目描述 有 \(n\) 位同学,每位同学都参加了全部的 \(m\) 门课程的期末考试,都在焦急的等待成绩的公布.第 \(i\) 位同学希望在第 \ ...

  7. 【RocketMQ】消息的刷盘机制

    刷盘策略 CommitLog的asyncPutMessage方法中可以看到在写入消息之后,调用了submitFlushRequest方法执行刷盘策略: public class CommitLog { ...

  8. Tapdata Cloud 版本上新!率先支持数据校验、类型映射等6大新功能

    Tapdata Cloud cloud.tapdata.net Tapdata Cloud 是国内首家异构数据库实时同步云平台,目前支持 Oracle.MySQL.PG.SQL Server.Mong ...

  9. Ajax:异步的JS和XML

    1.Ajax1) AJAX 是 Asynchronous JavaScript And XML 的简称.直译为,异步的JS和XML.2) AJAX的实际意义是,不发生页面跳转.异步载入内容并改写页面内 ...

  10. Math类和函数定义

    Math这个类是java系统内部当中的一个类,他用来提供一些基本的数学操作,他也有些工具可以给我们用比如 :abs--算绝对值   pow--算幂次   random--随机数   round--四舍 ...