[{"content":"前言 默认情况下，Transmission是同时监听IPV4和IPV6网络来实现BT/PT下载的。 现在遇到了一种使用场景是一台IPV6 Only的服务器使用Transmission来PT保种，同时因为其他业务需要，另外安装了Cloudflare Warp，使服务器同时拥有IPV4与IPV6的网络出口。按照PT的规则，Warp的IP是不可以作为PT做种使用的，因此现在需要让Transmission仅使用IPV6。查看了Transmission的Web端设置，并没有这方面可供修改。因此记录一下通过修改配置文件来实现上述目的的方法。\n具体操作 找到Transmission的配置目录，打开 settings.json 文件 找到其中 bind-address-ipv4 配置的位置，默认是：0.0.0.0。因为现在的需求是禁用掉IPV4监听，所以把这个0.0.0.0改成一个不存在的本地地址即可，比如 127.0.0.10 如果要禁用IPV6的话，则是修改 bind-address-ipv6 配置，默认是：:: ，比如可以修改为： ::10等 保存修改 重启Transmission，即可实现单栈网络监听 ","date":"2024-12-11T20:00:00+08:00","image":"/p/transmission-ipv6-only/1.jpg","permalink":"/p/transmission-ipv6-only/","title":"为Transmission设置IPV6 Only做种"},{"content":"需求 有两台机器A与B，需要都能访问 x.x.x.x:xx 地址。其中B机器能直接访问，A机器只能与B机器连通，无法访问 x.x.x.x:xx 地址。因此需要用Gost搭建端口转发，实现A访问B的特定端口，B再往x.x.x.x:xx 地址发送请求 为了隐私与安全性考量，B的特定端口需要有权限校验，防止任何人都能访问 准备工作 到这里下载最新版Gost到A、B两台机器上 将可执行文件放入 /usr/local/bin/gost 路径下 B机器配置 具体写法 使用 relay+tls模式监听本地8888端口，并将接收到的数据解密后发送到 x.x.x.x:xx\n1 /usr/local/bin/gost -L=relay+tls://user:pwd@:8888/0:0 -F=forward+tcp://x.x.x.x:xx 持续化运行 需要编写systemd配置信息将Gost放入systemd运行维护，创建 /etc/systemd/system/gost.service 文件：\n1 2 3 4 5 6 7 8 9 10 11 12 [Unit] Description=GO Simple Tunnel After=network.target Wants=network.target [Service] Type=simple ExecStart=/usr/local/bin/gost -L=relay+tls://user:pwd@:8888/0:0 -F=forward+tcp://x.x.x.x:xx Restart=always [Install] WantedBy=multi-user.target 执行：systemctl daemon-reload \u0026amp;\u0026amp; systemctl enable gost \u0026amp;\u0026amp; systemctl start gost 即可实现启动Gost并开机自启\nA机器配置 需要在本地监听一个端口（例如9999），使用与B机器相同配置的加密模式后转发到B机器的8888端口，A机器上访问 127.0.0.1:9999 即会被Gost转发到B机器后再转发到 x.x.x.x:xx 地址\n1 /usr/local/bin/gost -L=tcp://:9999/0:0 -F=relay+tls://user:pwd@B_address:8888/0:0 持续化运行 需要编写systemd配置信息将Gost放入systemd运行维护，创建 /etc/systemd/system/gost.service 文件：\n1 2 3 4 5 6 7 8 9 10 11 12 [Unit] Description=GO Simple Tunnel After=network.target Wants=network.target [Service] Type=simple ExecStart=/usr/local/bin/gost -L=tcp://:9999/0:0 -F=relay+tls://user:pwd@B_address:8888/0:0 Restart=always [Install] WantedBy=multi-user.target 执行：systemctl daemon-reload \u0026amp;\u0026amp; systemctl enable gost \u0026amp;\u0026amp; systemctl start gost 即可实现启动Gost并开机自启\n效果 A机器上访问 127.0.0.1:9999 地址，数据就会通过Gost的加密隧道到达B机器，再由B机器转发到 x.x.x.x:xx 地址\n","date":"2024-10-15T00:00:00+08:00","image":"/p/gost-forward/2.jpg","permalink":"/p/gost-forward/","title":"Gost使用加密隧道转发数据"},{"content":"1、基本理解和使用 函数式组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;UTF-8\u0026#34; /\u0026gt; \u0026lt;meta http-equiv=\u0026#34;X-UA-Compatible\u0026#34; content=\u0026#34;IE=edge\u0026#34; /\u0026gt; \u0026lt;meta name=\u0026#34;viewport\u0026#34; content=\u0026#34;width=device-width, initial-scale=1.0\u0026#34; /\u0026gt; \u0026lt;title\u0026gt;函数式组件\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div id=\u0026#34;test\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;!-- 引入核心库 --\u0026gt; \u0026lt;script src=\u0026#34;../js/react.development.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- 引入react-dom --\u0026gt; \u0026lt;script src=\u0026#34;../js/react-dom.development.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- 引入babel --\u0026gt; \u0026lt;script src=\u0026#34;../js/babel.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- 此处一定写babel --\u0026gt; \u0026lt;script type=\u0026#34;text/babel\u0026#34;\u0026gt; // 1.创建函数式组件 function MyComponent() { return \u0026lt;h2\u0026gt;我是用函数定义的组件（适用于【简单组件】的定义）\u0026lt;/h2\u0026gt; } // 2.渲染组件到页面 ReactDOM.render(\u0026lt;MyComponent /\u0026gt;, document.getElementById(\u0026#39;test\u0026#39;)) \u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 类式组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;UTF-8\u0026#34; /\u0026gt; \u0026lt;meta http-equiv=\u0026#34;X-UA-Compatible\u0026#34; content=\u0026#34;IE=edge\u0026#34; /\u0026gt; \u0026lt;meta name=\u0026#34;viewport\u0026#34; content=\u0026#34;width=device-width, initial-scale=1.0\u0026#34; /\u0026gt; \u0026lt;title\u0026gt;函数式组件\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div id=\u0026#34;test\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;!-- 引入核心库 --\u0026gt; \u0026lt;script src=\u0026#34;../js/react.development.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- 引入react-dom --\u0026gt; \u0026lt;script src=\u0026#34;../js/react-dom.development.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- 引入babel --\u0026gt; \u0026lt;script src=\u0026#34;../js/babel.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- 此处一定写babel --\u0026gt; \u0026lt;script type=\u0026#34;text/babel\u0026#34;\u0026gt; // 1.创建类式组件 class MyComponent extends React.Component { render() { return \u0026lt;h2\u0026gt;我是用类定义的组件（适用于【复杂组件】的定义）\u0026lt;/h2\u0026gt; } } // 2.渲染组件到页面 ReactDOM.render(\u0026lt;MyComponent /\u0026gt;, document.getElementById(\u0026#39;test\u0026#39;)) \u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 2、State state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)\n组件被称为\u0026quot;状态机\u0026quot;, 通过更新组件的state来更新对应的页面显示(重新渲染组件)\n组件中render方法中的this为组件实例对象\n组件自定义的方法中this为undefined，如何解决？\n强制绑定this: 通过函数对象的bind() 箭头函数 状态数据，不能直接修改或更新 ，用setState更新，是合并的动作\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 \u0026lt;!-- 此处一定写babel --\u0026gt; \u0026lt;script type=\u0026#34;text/babel\u0026#34;\u0026gt; // 1.创建类式组件 class MyComponent extends React.Component { constructor(props) { super(props) // 初始化状态 this.state = { isHot: true, } // 类组件获取this指向当前组件的方法一：将changeWeather绑定上this后生成新的方法重新赋值给changeWeather this.changeWeather = this.changeWeather.bind(this) } render() { return ( \u0026lt;h2 onClick={this.changeWeather}\u0026gt; 今天天气很{this.state.isHot ? \u0026#39;炎热\u0026#39; : \u0026#39;凉爽\u0026#39;} \u0026lt;/h2\u0026gt; ) } //changeweather放在哪里？——Weather的原型对象上，供实例使用 //由于changeleather是作为onClick的回调，所以不是通过实例调用的，是直接调用 //类中的方法默认开启了局部的严格模式，所以changeweather中的this为undefined changeWeather() { // 严重注意，state不可以直接更改，比如以下 // this.state.isHot = !this.state.isHot this.setState({ isHot: !this.state.isHot }) } } // 2.渲染组件到页面 ReactDOM.render(\u0026lt;MyComponent /\u0026gt;, document.getElementById(\u0026#39;test\u0026#39;)) \u0026lt;/script\u0026gt; setState引起组件的后续动作是异步的！！！！！！\n精简代码写法\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 \u0026lt;!-- 此处一定写babel --\u0026gt; \u0026lt;script type=\u0026#34;text/babel\u0026#34;\u0026gt; // 1.创建类式组件 class MyComponent extends React.Component { // 初始化状态 state = { isHot: true } render() { return ( \u0026lt;h2 onClick={this.changeWeather}\u0026gt; 今天天气很{this.state.isHot ? \u0026#39;炎热\u0026#39; : \u0026#39;凉爽\u0026#39;} \u0026lt;/h2\u0026gt; ) } // 自定义方法：赋值语句+箭头函数 changeWeather = () =\u0026gt; { this.setState({ isHot: !this.state.isHot }) } } // 2.渲染组件到页面 ReactDOM.render(\u0026lt;MyComponent /\u0026gt;, document.getElementById(\u0026#39;test\u0026#39;)) \u0026lt;/script\u0026gt; 2.1、setState setState更新状态的2种写法\n对象式的setState setState(stateChange, [callback])\nstateChange为状态改变对象(该对象可以体现出状态的更改) callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import React, {Component} from \u0026#39;react\u0026#39; export default class Demo extends Component { state = {count: 0} add = () =\u0026gt; { const {count} = this.state // 更新状态 this.setState({count: count + 1}, () =\u0026gt; { // 改完状态、render执行完的回调 console.log(this.state.count) }) } render() { return ( \u0026lt;div\u0026gt; \u0026lt;h1\u0026gt;当前求和为：{this.state.count}\u0026lt;/h1\u0026gt; \u0026lt;button onClick={this.add}\u0026gt;点我+1\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; ) } } 函数式的setState setState(updater, [callback])\nupdater为返回stateChange对象的函数 updater可以接收到state和props callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import React, {Component} from \u0026#39;react\u0026#39; export default class Demo extends Component { state = {count: 0} add = () =\u0026gt; { // 对象式的setState // const {count} = this.state // // 更新状态 // this.setState({count: count + 1}, () =\u0026gt; { // // 改完状态、render执行完的回调 // console.log(this.state.count) // }) // 函数式setState this.setState((state, props) =\u0026gt; { return {count: state.count + 1} }, () =\u0026gt; { // 改完状态、render执行完的回调 console.log(this.state.count) }) } render() { return ( \u0026lt;div\u0026gt; \u0026lt;h1\u0026gt;当前求和为：{this.state.count}\u0026lt;/h1\u0026gt; \u0026lt;button onClick={this.add}\u0026gt;点我+1\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; ) } } 总结 对象式的setState是函数式的setState的简写方式(语法糖) 使用原则： 如果新状态不依赖于原状态 ===\u0026gt; 使用对象方式 如果新状态依赖于原状态 ===\u0026gt; 使用函数方式 如果需要在setState()执行后获取最新的状态数据，要在第二个callback函数中读取 3、props props是只读的 标签体的内容也是可以通过props中的children属性拿到，是一种特殊的标签属性 3.1、基本使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \u0026lt;!-- 此处一定写babel --\u0026gt; \u0026lt;script type=\u0026#34;text/babel\u0026#34;\u0026gt; class Person extends React.Component { render() { return ( \u0026lt;ul\u0026gt; \u0026lt;li\u0026gt;作者：{this.props.name}\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;性别：{this.props.sex}\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;年龄：{this.props.age}\u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; } } // 2.渲染组件到页面 ReactDOM.render( \u0026lt;Person name=\u0026#34;tom\u0026#34; age=\u0026#34;18\u0026#34; sex=\u0026#34;女\u0026#34; /\u0026gt;, document.getElementById(\u0026#39;test\u0026#39;) ) \u0026lt;/script\u0026gt; 3.2、批量传递props 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 \u0026lt;!-- 此处一定写babel --\u0026gt; \u0026lt;script type=\u0026#34;text/babel\u0026#34;\u0026gt; class Person extends React.Component { render() { return ( \u0026lt;ul\u0026gt; \u0026lt;li\u0026gt;作者：{this.props.name}\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;性别：{this.props.sex}\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;年龄：{this.props.age}\u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; ) } } // 2.渲染组件到页面 const p = {name: \u0026#39;老刘\u0026#39;, age:18, sex: \u0026#39;女\u0026#39;} ReactDOM.render(\u0026lt;Person {...p} /\u0026gt;, document.getElementById(\u0026#39;test\u0026#39;)) \u0026lt;/script\u0026gt; 3.3、对props传值做类型限制 需要引入prop-types.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 \u0026lt;body\u0026gt; \u0026lt;div id=\u0026#34;test\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;!-- 引入核心库 --\u0026gt; \u0026lt;script src=\u0026#34;../js/react.development.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- 引入react-dom --\u0026gt; \u0026lt;script src=\u0026#34;../js/react-dom.development.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- 引入babel --\u0026gt; \u0026lt;script src=\u0026#34;../js/babel.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- 引入prop-types --\u0026gt; \u0026lt;script src=\u0026#34;../js/prop-types.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- 此处一定写babel --\u0026gt; \u0026lt;script type=\u0026#34;text/babel\u0026#34;\u0026gt; class Person extends React.Component { render() { return ( \u0026lt;ul\u0026gt; \u0026lt;li\u0026gt;作者：{this.props.name}\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;性别：{this.props.sex}\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;年龄：{this.props.age}\u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; ) } } // 设置入参格式 Person.propTypes = { // 限制只能字符串，且必传 name: PropTypes.string.isRequired, age: PropTypes.number, } // 设置默认值 Person.defaultProps = { sex: \u0026#39;无\u0026#39;, } // 2.渲染组件到页面 const p = { name: \u0026#39;老刘\u0026#39;, age: 18, sex: \u0026#39;女\u0026#39; } ReactDOM.render(\u0026lt;Person {...p} /\u0026gt;, document.getElementById(\u0026#39;test\u0026#39;)) \u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; props简写：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 \u0026lt;script type=\u0026#34;text/babel\u0026#34;\u0026gt; class Person extends React.Component { // 设置入参格式 static propTypes = { // 限制只能字符串，且必传 name: PropTypes.string.isRequired, age: PropTypes.number, } // 设置默认值 static defaultProps = { sex: \u0026#39;无\u0026#39;, } render() { return ( \u0026lt;ul\u0026gt; \u0026lt;li\u0026gt;作者：{this.props.name}\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;性别：{this.props.sex}\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;年龄：{this.props.age}\u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; ) } } // 2.渲染组件到页面 const p = { name: \u0026#39;老刘\u0026#39;, age: 18, sex: \u0026#39;女\u0026#39; } ReactDOM.render(\u0026lt;Person {...p} /\u0026gt;, document.getElementById(\u0026#39;test\u0026#39;)) \u0026lt;/script\u0026gt; 3.4、函数式组件使用props 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 \u0026lt;script type=\u0026#34;text/babel\u0026#34;\u0026gt; // 1.创建函数式组件 function Person(props) { return ( \u0026lt;ul\u0026gt; \u0026lt;li\u0026gt;作者：{props.name}\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;性别：{props.sex}\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;年龄：{props.age}\u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; ) } // 2.渲染组件到页面 ReactDOM.render( \u0026lt;Person name=\u0026#34;jery\u0026#34; age={18} sex=\u0026#34;女\u0026#34; /\u0026gt;, document.getElementById(\u0026#39;test\u0026#39;) ) \u0026lt;/script\u0026gt; 4、refs 组件内的标签可以定义ref属性来标识自己 4.1、字符串形式的ref（过时） 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 \u0026lt;script type=\u0026#34;text/babel\u0026#34;\u0026gt; // 创建组件 class Demo extends React.Component{ showData = () =\u0026gt; { alert(this.refs.input1.value) } showData2 = () =\u0026gt; { const {input2} = this.refs alert(input2.value) } render() { return ( \u0026lt;div\u0026gt; \u0026lt;input ref=\u0026#34;input1\u0026#34; type=\u0026#34;text\u0026#34; placeholder=\u0026#34;点击按钮提示数据\u0026#34; /\u0026gt; \u0026lt;button ref=\u0026#34;button1\u0026#34; onClick={this.showData}\u0026gt;点我提示左侧数据\u0026lt;/button\u0026gt;\u0026amp;nbsp; \u0026lt;input ref=\u0026#34;input2\u0026#34; onBlur={this.showData2} type=\u0026#34;text\u0026#34; placeholder=\u0026#34;失去焦点提示数据\u0026#34; /\u0026gt;\u0026amp;nbsp; \u0026lt;/div\u0026gt; ) } } // 2.渲染虚拟dom到页面 ReactDOM.render(\u0026lt;Demo/\u0026gt;, document.getElementById(\u0026#39;test\u0026#39;)) \u0026lt;/script\u0026gt; 4.2、回调形式的ref 如果ref回调函数是以内联函数的方式定义的，在更新过程中它会被执行两次，第一次传入参数null，然后第二次会传入参数DOM元素。这是因为在每次渲染时会创建一个新的函数实例，所以React 清空旧的ref并且设置新的。通过将ref的回调函数定义成class的绑定函数的方式可以避免上述问题，但是大多数情况下它是无关紧要的。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 \u0026lt;script type=\u0026#34;text/babel\u0026#34;\u0026gt; // 创建组件 class Demo extends React.Component{ showData = () =\u0026gt; { alert(this.input1.value) } showData2 = () =\u0026gt; { const {input2} = this alert(input2.value) } render() { return ( \u0026lt;div\u0026gt; {/* 内联回调函数方式获取ref */} \u0026lt;input ref={currentNode =\u0026gt; this.input1 = currentNode} type=\u0026#34;text\u0026#34; placeholder=\u0026#34;点击按钮提示数据\u0026#34; /\u0026gt; \u0026lt;button onClick={this.showData}\u0026gt;点我提示左侧数据\u0026lt;/button\u0026gt;\u0026amp;nbsp; \u0026lt;input ref={this.saveInput} onBlur={this.showData2} type=\u0026#34;text\u0026#34; placeholder=\u0026#34;失去焦点提示数据\u0026#34; /\u0026gt;\u0026amp;nbsp; \u0026lt;/div\u0026gt; ) } // class的绑定函数设置ref saveInput = (currentNode) =\u0026gt; { this.input2 = currentNode } } // 2.渲染虚拟dom到页面 ReactDOM.render(\u0026lt;Demo/\u0026gt;, document.getElementById(\u0026#39;test\u0026#39;)) \u0026lt;/script\u0026gt; 4.3、createRef使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 \u0026lt;script type=\u0026#34;text/babel\u0026#34;\u0026gt; // 创建组件 class Demo extends React.Component{ // React.createRef调用后可以返回一个容器，该容器可以存储被ref所标识的节点 // 但是该容器是专用的，只能放在一个标签上 myRef = React.createRef() showData = () =\u0026gt; { alert(this.myRef.current.value) } render() { return ( \u0026lt;div\u0026gt; \u0026lt;input ref={this.myRef} type=\u0026#34;text\u0026#34; placeholder=\u0026#34;点击按钮提示数据\u0026#34; /\u0026gt; \u0026lt;button onClick={this.showData}\u0026gt;点我提示左侧数据\u0026lt;/button\u0026gt;\u0026amp;nbsp; \u0026lt;/div\u0026gt; ) } } // 2.渲染虚拟dom到页面 ReactDOM.render(\u0026lt;Demo/\u0026gt;, document.getElementById(\u0026#39;test\u0026#39;)) \u0026lt;/script\u0026gt; 5、事件处理 通过onXxx属性指定事件处理函数(注意大小写) React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) 通过event.target得到发生事件的DOM元素对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 \u0026lt;script type=\u0026#34;text/babel\u0026#34;\u0026gt; // 创建组件 class Demo extends React.Component{ // 创建ref容器 myRef = React.createRef() showData = () =\u0026gt; { alert(this.myRef.current.value) } showData2 = (event) =\u0026gt; { alert(event.target.value) } render() { return ( \u0026lt;div\u0026gt; \u0026lt;input ref={this.myRef} type=\u0026#34;text\u0026#34; placeholder=\u0026#34;点击按钮提示数据\u0026#34; /\u0026gt; \u0026lt;button onClick={this.showData}\u0026gt;点我提示左侧数据\u0026lt;/button\u0026gt;\u0026amp;nbsp; \u0026lt;input onBlur={this.showData2} type=\u0026#34;text\u0026#34; placeholder=\u0026#34;失去焦点提示数据\u0026#34; /\u0026gt; \u0026lt;/div\u0026gt; ) } } // 2.渲染虚拟dom到页面 ReactDOM.render(\u0026lt;Demo/\u0026gt;, document.getElementById(\u0026#39;test\u0026#39;)) \u0026lt;/script\u0026gt; 6、高阶函数与函数柯里化 高阶函数：如果一个函数符合下面2个规范中的任何一个，那该函数就是高阶函数 若A函数，接收的参数是一个函数，那么A就可以称之为高阶函数 若A函数，调用的返回值依然是一个函数，那么A就可以称之为高阶函数 函数的柯里化：通过函数调用继续返回函数的方式，实现多次接收参数最后统一处理的函数编码形式 用函数柯里化实现：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 saveFormData = (dataType) =\u0026gt; { return (event) =\u0026gt; { this.setstate({[dataType]: event.target.value}) } } render(){ return( \u0026lt;form onSubmit={this.handleSubmit}\u0026gt; 用户名：\u0026lt;input onChange={this.saveFormData(\u0026#39;username\u0026#39;)} type=\u0026#34;text\u0026#34; name=\u0026#34;username\u0026#34;/\u0026gt; 密码：\u0026lt;input onChange={this.saveFormData(\u0026#39;password\u0026#39;)} type=\u0026#34;password\u0026#34; name=\u0026#34;password\u0026#34;/\u0026gt; \u0026lt;button\u0026gt;登录\u0026lt;/button\u0026gt; \u0026lt;/form\u0026gt; ) } 不用函数柯里化实现：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 //保存表单数据到状态中 saveFormData = (dataType,event)=\u0026gt;{ this.setState({[dataType]:event.target.value}) } render(){ return( \u0026lt;form onSubmit={this.handleSubmit}\u0026gt; 用户名：\u0026lt;input onChange={event =\u0026gt; this.saveFormData(\u0026#39;username\u0026#39;,event) } type=\u0026#34;text\u0026#34; name=\u0026#34;username\u0026#34;/\u0026gt; 密码：\u0026lt;input onChange={event =\u0026gt; this.saveFormData(\u0026#39;password\u0026#39;,event) } type=\u0026#34;password\u0026#34; name=\u0026#34;password\u0026#34;/\u0026gt; \u0026lt;button\u0026gt;登录\u0026lt;/button\u0026gt; \u0026lt;/form\u0026gt; ) } 7、组件的生命周期 7.1、生命周期流程图(旧) 初始化阶段: 由ReactDOM.render()触发\u0026mdash;初次渲染 constructor() componentWillMount() render() componentDidMount() 更新阶段: 由组件内部this.setSate()或父组件重新render触发 shouldComponentUpdate() componentWillUpdate() render() componentDidUpdate() 卸载组件: 由ReactDOM.unmountComponentAtNode()触发 componentWillUnmount() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 \u0026lt;script type=\u0026#34;text/babel\u0026#34;\u0026gt; /* 1. 初始化阶段: 由ReactDOM.render()触发---初次渲染 1.\tconstructor() 2.\tcomponentWillMount() 3.\trender() 4.\tcomponentDidMount() =====\u0026gt; 常用 一般在这个钩子中做一些初始化的事，例如：开启定时器、发送网络请求、订阅消息 2. 更新阶段: 由组件内部this.setSate()或父组件render触发 1.\tshouldComponentUpdate() 2.\tcomponentWillUpdate() 3.\trender() =====\u0026gt; 必须使用的一个 4.\tcomponentDidUpdate() 3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发 1.\tcomponentWillUnmount() =====\u0026gt; 常用 一般在这个钩子中做一些收尾的事，例如：关闭定时器、取消订阅消息 */ //创建组件 class Count extends React.Component{ //构造器 constructor(props){ console.log(\u0026#39;Count---constructor\u0026#39;); super(props) //初始化状态 this.state = {count:0} } //加1按钮的回调 add = ()=\u0026gt;{ //获取原状态 const {count} = this.state //更新状态 this.setState({count:count+1}) } //卸载组件按钮的回调 death = ()=\u0026gt;{ ReactDOM.unmountComponentAtNode(document.getElementById(\u0026#39;test\u0026#39;)) } //强制更新按钮的回调 force = ()=\u0026gt;{ this.forceUpdate() } //组件将要挂载的钩子 componentWillMount(){ console.log(\u0026#39;Count---componentWillMount\u0026#39;); } //组件挂载完毕的钩子 componentDidMount(){ console.log(\u0026#39;Count---componentDidMount\u0026#39;); } //组件将要卸载的钩子 componentWillUnmount(){ console.log(\u0026#39;Count---componentWillUnmount\u0026#39;); } //控制组件更新的“阀门” shouldComponentUpdate(){ console.log(\u0026#39;Count---shouldComponentUpdate\u0026#39;); return true } //组件将要更新的钩子 componentWillUpdate(){ console.log(\u0026#39;Count---componentWillUpdate\u0026#39;); } //组件更新完毕的钩子 componentDidUpdate(){ console.log(\u0026#39;Count---componentDidUpdate\u0026#39;); } render(){ console.log(\u0026#39;Count---render\u0026#39;); const {count} = this.state return( \u0026lt;div\u0026gt; \u0026lt;h2\u0026gt;当前求和为：{count}\u0026lt;/h2\u0026gt; \u0026lt;button onClick={this.add}\u0026gt;点我+1\u0026lt;/button\u0026gt; \u0026lt;button onClick={this.death}\u0026gt;卸载组件\u0026lt;/button\u0026gt; \u0026lt;button onClick={this.force}\u0026gt;不更改任何状态中的数据，强制更新一下\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; ) } } //父组件A class A extends React.Component{ //初始化状态 state = {carName:\u0026#39;奔驰\u0026#39;} changeCar = ()=\u0026gt;{ this.setState({carName:\u0026#39;奥拓\u0026#39;}) } render(){ return( \u0026lt;div\u0026gt; \u0026lt;div\u0026gt;我是A组件\u0026lt;/div\u0026gt; \u0026lt;button onClick={this.changeCar}\u0026gt;换车\u0026lt;/button\u0026gt; \u0026lt;B carName={this.state.carName}/\u0026gt; \u0026lt;/div\u0026gt; ) } } //子组件B class B extends React.Component{ //组件将要接收新的props的钩子 componentWillReceiveProps(props){ console.log(\u0026#39;B---componentWillReceiveProps\u0026#39;,props); } //控制组件更新的“阀门” shouldComponentUpdate(){ console.log(\u0026#39;B---shouldComponentUpdate\u0026#39;); return true } //组件将要更新的钩子 componentWillUpdate(){ console.log(\u0026#39;B---componentWillUpdate\u0026#39;); } //组件更新完毕的钩子 componentDidUpdate(){ console.log(\u0026#39;B---componentDidUpdate\u0026#39;); } render(){ console.log(\u0026#39;B---render\u0026#39;); return( \u0026lt;div\u0026gt;我是B组件，接收到的车是:{this.props.carName}\u0026lt;/div\u0026gt; ) } } //渲染组件 ReactDOM.render(\u0026lt;Count/\u0026gt;,document.getElementById(\u0026#39;test\u0026#39;)) \u0026lt;/script\u0026gt; 7.2、生命周期流程图(新，17以后) 初始化阶段: 由ReactDOM.render()触发\u0026mdash;初次渲染 constructor() getDerivedStateFromProps(props,state)：从props中得到派生的状态，也就是state的值在任何时候都取决于props时使用 render() componentDidMount() 更新阶段: 由组件内部this.setSate()或父组件重新render触发 getDerivedStateFromProps(props,state)：从props中得到派生的状态，也就是state的值在任何时候都取决于props时使用 shouldComponentUpdate() render() getSnapshotBeforeUpdate componentDidUpdate(preProps, preState)：preProps和preState都是更新前的数据 卸载组件: 由ReactDOM.unmountComponentAtNode()触发 componentWillUnmount() 若state的值在任何时候都取决于props，那么可以使用getDerivedStateFromProps getSnapshotBeforeUpdate(prevProps, prevState) 在最近一次渲染输出（提交到 DOM 节点）之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息（例如，滚动位置）。此生命周期方法的任何返回值将作为参数传递给 componentDidUpdate() 7.3、重要的勾子 render：初始化渲染或更新渲染调用 componentDidMount：开启监听, 发送ajax请求 componentWillUnmount：做一些收尾工作, 如: 清理定时器 7.4、即将废弃的勾子 componentWillMount componentWillReceiveProps componentWillUpdate 8、虚拟DOM与DOM Diffing算法 ","date":"2021-08-18T23:25:00+08:00","image":"/p/react-1/cover.jpg","permalink":"/p/react-1/","title":"React学习笔记（一）- 面向组件编程"},{"content":"前言 因为最近准备往树莓派上放一个基于OpenCV的项目，在PC上测试没有问题后放在树莓派上pip install -r requirements.txt的时候却提示：\n1 2 ERROR: Could not find a version that satisfies the requirement opencv-python (from versions: none) ERROR: No matching distribution found for opencv-python 再去网上搜索后才发现opencv-python这个库并没有提供ARM架构的二进制包。不过偶然发现还有一个专门为树莓派提供的第三方依赖库，可是这里目前只提供基于Python3.5且版本号太低无法满足我当前项目的要求，那接下来就只剩下源码自己独立编译这条路了。\n环境准备 Python3.7.4\n需要安装Numpy：pip install numpy\n扩展文件系统\n输入 sudo raspi-config\n选择 Advanced Options\n选择 Expand Filesystem\n扩充Swap\n编辑文件：sudo vi /etc/dphys-swapfile，将CONF_SWAPSIZE的值修改成2048\n重启dphys-swapfile：sudo /etc/init.d/dphys-swapfile restart\n更新系统的包\n1 sudo apt-get update 在系统中安装相关依赖\n1 2 3 4 5 6 7 8 sudo apt-get install build-essential cmake unzip pkg-config sudo apt-get install libjpeg-dev libpng-dev libtiff-dev sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev sudo apt-get install libxvidcore-dev libx264-dev sudo apt-get install libgtk-3-dev sudo apt-get install libcanberra-gtk* sudo apt-get install libatlas-base-dev gfortran sudo apt-get install python3-dev 下载源码并解压\n1 2 3 4 wget -O opencv.zip https://github.com/opencv/opencv/archive/4.1.1.zip wget -O opencv_contrib.zip https://codeload.github.com/opencv/opencv_contrib/zip/4.1.1 unzip opencv.zip unzip opencv_contrib.zip 编译安装 在opencv-4.1.1中创建build文件夹，用于存放源码编译后的产物\n1 2 3 cd opencv-4.1.1 mkdir build cd build 配置CMake参数\n1 2 3 4 5 6 7 8 9 10 sudo cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D OPENCV_EXTRA_MODULES_PATH=/home/pi/opencv/opencv_contrib-4.1.1/modules -D ENABLE_NEON=ON -D ENABLE_VFPV3=ON -D WITH_FFMPEG=ON -D INSTALL_PYTHON_EXAMPLES=ON -D WITH_GSTREAMER=ON -D OPENCV_EXTRA_EXE_LINKER_FLAGS=-latomic -D BUILD_EXAMPLES=ON .. 在CMake检查结束后，会打印出详细的信息，需要检查其中Python3部分的各个参数是否都有值。\n确认没问题后即开始编译OpenCV，建议可以在这时候使用screen命令创建一个可以在后台持续执行的会话。\n可以在make后增加-j2参数指定使用2个核心参与编译，这里使用单核编译，即不加任何参数。敲完命令后就需要等待大约七八小时后才能完成（Raspberry Pi 3B）\n1 sudo make 编译结束并且没有报错信息后，需要修改一下配置，modules/python3/CMakeFiles/opencv_python3.dir/link.txt中第一行增加-latomic参数\n1 vi build/modules/python3/CMakeFiles/opencv_python3.dir/link.txt 在第一行中的起始中 /usr/bin/c++ -fPIC -fsigned-char -W -Wall 修改为 /usr/bin/c++ -fPIC -latomic -fsigned-char -W -Wall\n安装OpenCV\n1 2 sudo make install sudo ldconfig 检查安装结果 1 2 3 4 5 6 7 $ python Python 3.7.4 (default, Sep 8 2019, 10:06:39) [GCC 8.3.0] on linux Type \u0026#34;help\u0026#34;, \u0026#34;copyright\u0026#34;, \u0026#34;credits\u0026#34; or \u0026#34;license\u0026#34; for more information. \u0026gt;\u0026gt;\u0026gt; import cv2 \u0026gt;\u0026gt;\u0026gt; cv2.__version__ \u0026#39;4.1.1\u0026#39; 参考链接 【树莓派】编译安装opencv+python调用\nInstall OpenCV 4 on your Raspberry Pi\n","date":"2019-09-09T22:00:00+08:00","image":"/p/raspberry-pi-compile-install-opencv4/cover.jpg","permalink":"/p/raspberry-pi-compile-install-opencv4/","title":"Raspberry Pi编译安装OpenCV4"},{"content":"公司的项目使用到了存储过程，由于此前在学校从未接触到，因此记录下学习笔记。\n什么是存储过程 存储过程（Stored Procedure）是在大型数据库系统中，一组为了完成特定功能的SQL 语句集，存储在数据库中，经过第一次编译后再次调用不需要再次编译，用户通过指定存储过程的名字并给出参数（如果该存储过程带有参数）来执行它。\n使用存储过程的优点 存储过程只在创造时进行编译，以后每次执行存储过程都不需再重新编译，而一般SQL语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度。\n当对数据库进行复杂操作时，可将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用。\n存储过程可以重复使用,可减少数据库开发人员的工作量。\n安全性高,可设定只有某些用户才具有对指定存储过程的使用权。\n简单例子 基本语法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 --------------创建存储过程----------------- CREATE [OR REPLACE] PROCEDURE procedure_name(参数列表) AS -- as不可以省略，也可以用is --PL/SQL体部分 END procedure_name; --------------调用存储过程----------------- EXECUTE Procedure_name \u0026#39;\u0026#39; --存储过程如果有参数，后面加参数格式为：@参数名=value，也可直接为参数值value --------------删除存储过程----------------- drop procedure procedure_name --在存储过程中能调用另外一个存储过程，而不能删除另外一个存储过程 存储过程或者存储函数，只能创建或者替换。 参数可以带也可以不带。 不带参数的存储过程 1 2 3 4 5 CREATE OR REPLACE procedure test as begin dbms_output.put_line(\u0026#39;HelloWorld\u0026#39;); end; 带参数的存储过程 给指定的员工涨100元工资,并且打印涨前和涨后的薪水:\n1 2 3 4 5 6 7 8 9 10 11 create or replace procedure test(pempno in emp.empno%type) as pename emp.ename%type; beforesal emp.sal%type; aftersal emp.sal%type; begin select ename,sal into pename,beforesal from emp where empno=pempno; aftersal:=beforesal+100; update emp set sal=aftersal where empno=pempno; dbms_output.put_line(\u0026#39;姓名: \u0026#39;||pename||\u0026#39; 涨前工资:\u0026#39;||beforesal||\u0026#39;涨后工资:\u0026#39;||aftersal); end; 注意\n要明确说明参数是输入参数（in)还是输出参数（out) 为保证调用多个存储过程中处在同一个事务中，所以一般不在存储过程或者存储函数中，commit或rollback 调用执行方式 exec命令 exec是SQLPlus命令，只能在SQLPlus中使用，exec可以直接跟存储过程名（可以省略括号） 1 2 SQL\u0026gt; set serveroutput on; SQL\u0026gt; exec test; 使用call命令 在SQL中使用call调用存储过程时，需要带上括号，不限制使用场景 1 2 SQL\u0026gt; set serveroutput on; SQL\u0026gt; call test(); 使用PLSQL语句调用 1 2 3 4 SQL\u0026gt; set serveroutput on; SQL\u0026gt; begin test; end; ","date":"2018-09-04T14:25:00+08:00","image":"/p/learn-procedure/cover.jpg","permalink":"/p/learn-procedure/","title":"存储过程"}]