其他章节请看:

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. 即时通讯IM,是时代进步的逆流?看看JNPF怎么说

    JNPF快速开发平台所包含的第四个重要的开发框架是即时通讯沟通工具.即时沟通工具的目的是让各大企事业单位在各种业务工作流程环境下实现实时无缝协同办公,打破信息数据孤岛,形成高效的层级流转审批和各流程环 ...

  2. CentOS8设置国内镜像源(阿里云镜像)

    CentOS8设置国内镜像源(阿里云) 1.备份原有配置 [root@localhost ~]# mkdir /etc/yum.repos.d.bak [root@localhost ~]# mv / ...

  3. Spring框架系列(3) - 深入浅出Spring核心之控制反转(IOC)

    在Spring基础 - Spring简单例子引入Spring的核心中向你展示了IoC的基础含义,同时以此发散了一些IoC相关知识点; 本节将在此基础上进一步解读IOC的含义以及IOC的使用方式.@pd ...

  4. SpringCloudAlibaba学习(解决SpringBoot初始化以及Nginx启动出错问题)

    微服务强调每个服务都是单独的数据库 在不使用微服务的情况下可以采用分布式架构,通过Template来调用远程的Rest接口 但这种方式维护起来很麻烦,而且有很多弊端. 一.环境搭建 1.首先搭建Spr ...

  5. 物联网?快来看 Arduino 上云啦

    作者:HelloGitHub-Anthony 这里是 HelloGitHub 推出的讲解开源硬件开发平台 Arduino 的系列教程. 第一篇:Arduino 介绍和开发环境搭建 第二篇:制作温湿度显 ...

  6. 【RPA之家BluePrism手把手教程】2.3 多重计算

    2.3.1 添加除法运算计算框 2.3.2 设置除法运算计算属性 2.3.3 程序运行前初始值 2.3.4 程序运行后结果 使用多重计算框实现以上操作 2.3.5 添加多重选择框 2.3.6 设置多重 ...

  7. Docker安装Portainer管理工具

    1.下载镜像 docker pull portainer/portainer 2.启动 docker run -d -p 9000:9000 --restart=always -v /var/run/ ...

  8. docker容器内修改文件

    1.找到容器对应的ID 使用docker ps命令找到对应的镜像id 2.根据容器id进入到对应文件夹 执行命令:docker exec -it 镜像id /bin/bash 3.进入对应目录(以My ...

  9. 虚拟机启动时报’A start job is running for /etc/rc.local .. Compatibility错误。

    虚拟机启动时报'A start job is running for /etc/rc.local .. Compatibility错误. 问题已经存在很长时间了,但是不影响ssh登录,遂置之未理. 经 ...

  10. LEACH分簇算法实现和能量控制算法实现

    一.前言 1.在给定WSN的节点数目(100)前提下,节点随机分布,按照LEACH算法,实现每一轮对WSN的分簇.记录前K轮(k=10)时,网络的分簇情况,即每个节点的角色(簇头或簇成员).标记节点之 ...