Node.js建站笔记-使用react和react-router取代Backbone

news/2024/5/11 14:14:26/文章来源:https://blog.csdn.net/weixin_33757609/article/details/85814017

斟酌之后,决定在《嗨猫》项目中引入react,整体项目偏重spa模式,舍弃部分server端的模板渲染,将一部分渲染工作交给前端react实现。

react拥有丰富的组件,虽然不如Backbone和underscore这对老基友成熟,但考虑到嗨猫的前端并不需要很多的MV*架构,目前使用到Backbone的地方只有hash路由而已,所以最终决定使用react-router取代Backbone,underscore也从项目依赖中移除。

下面就以登录&注册页为例,简单讲述整个替代过程。

1. 安装并二次编译react-router

因为项目前端仍然使用AMD规范,使用bower install react-router安装后的react-router是原始的ES6 module规范,不能兼容AMD规范。
react-router源码中提供了编译配置文件scripts/build.js,进入react-router根目录执行:

npm install

安装依赖工具之后执行:

node scripts/build.js

编译成功后生成lib和umd两个文件夹,lib目录下的是CommonJS规范的文件,umd目录下是UMD规范文件,项目中前端使用的是umd目录下的文件。

编译完毕之后配置/assets/global/js/dev/main.es中的requirejs的配置项:

paths: {"jquery": 'jquery/jquery.min',"requirejs": 'requirejs/require',"react": 'react/react','react-dom': 'react/react-dom',"react-router": "react-router/umd/ReactRouter.min","jqSlidejs": 'jquery-slide/jqSlide.min','jqValidate': 'jquery-validation/dist/jquery.validate.min'}

配置完毕后便可以在其他js文件中直接使用import关键字引入react-router组件。

2. 引入React并编写前端组件

以下改的均是在登录注册页的主要js文件/assets/components/passport/js/dev/main.es中进行。

2.1 首先引入react和react-dom。

React的新版本将react-dom分离出来专注于组件的render,原来的React.render函数被弃用。

import React from 'react'
import { render } from 'react-dom'
2.2 编写组件

将server端的swig模板进一步简化,除了logo区域之外的UI由react渲染,swig模板只提供一个外层容器:

<div class="hc_pwd_box"></div>

这个容器便是react组件的append dom的目标,我们首先将它储存起来:

let container = $('.hc_pwd_box')[0];

react的render方法不支持jquery对象,必须是原始的dom节点。

2.2.1 nav组件

595796-20160418130617554-1176382127.jpg

需要注意,nav是有状态的,tab文字下方的黑条指示当前的显示表单是注册还是登录,所以在编写nav组件是需要用到props,代码如下:

// nav 组件
const Nav = React.createClass({
render(){let mode = this.props.mode;let class_login = '',class_signup = '';if(mode === 'login'){class_login = 'box_nav_item box_nav_item_login box_nav_item-current';class_signup = 'box_nav_item box_nav_item_signup';}else{class_signup = 'box_nav_item box_nav_item_signup box_nav_item-current';class_login = 'box_nav_item box_nav_item_login';}return(<ul className="box_nav_list"><li className={class_signup}><a className="item_link" href="#signup">注册</a><div className="item_tray"></div></li><li className={class_login}><a className="item_link" href="#login">登录</a><div className="item_tray"></div></li></ul>);
}
});

jsx中class的声明必须使用className的写法。

上述代码中的this.props.mode是生成nav组件时传入的数据,然后组件内部根据这个数据判断显示哪个指示条。生成Nav组件的代码如下:

<Nav mode={'login'} />

Nav组件内部this.props.mode的值便是'login'

2.2.2 form表单的容器组件

容器组件最大的特性就是可以接收子节点,这里需要用到react中的this.props.children,它的作用于swig模板的block有相似之处,但是不能像block那样命名。FormBox组件代码如下:

// form容器组件
const FormBox = React.createClass({render(){let mode = this.props.mode;let current = 'box_form_container box_form_container_'+mode;return (<div className={current}><iframe style={{display:'none'}} name='target_ifr'></iframe>{this.props.children}</div>);}
});
  1. FormBox组件与Nav组件一样,是有状态的,根据状态值控制对应表单的display;
  2. {this.props.children}位置接收子节点,下文后讲解如何实现;
  3. jsx语法不能直接使用style='display:none'这种原始写法,必须写成上述代码中的格式,并且类似margin-top这种属性,必须写成与js语法相同的驼峰式marginTop

2.2.3 登录&注册表单组件

登录&注册form组件有以下几点注意:

  1. Login和Signup组件是render和react-router的入口,所以组件内部需要调用Nav和FormBox以及其他组件;
  2. 表单中的验证码图片需要请求接口获取。

首先贴上代码(以Login组件为例):

// 登录form组件
const Login = React.createClass({getInitialState(){return {verify_img: ""};},componentDidMount(){$.ajax({url: '/getverifycode',type: 'get',dataType: 'json'}).done(function(res) {if(this.isMounted()){this.setState({verify_img: res.img});}}.bind(this));},render(){return(<div><Nav mode={'login'} /><FormBox mode={'login'}><form action="/login" method="post" className="login_form" target='target_ifr'><div className="form_item form_item_input"><input type="text" name='signname' className="form_input hc_input hc_input_grey hc_input_borderdash" placeholder="用户名或邮箱" /><span className="form_input_placeholder"></span><div className="form_error"></div></div><div className="form_item form_item_input"><input type="password" name='password' className="form_input hc_input hc_input_grey hc_input_borderdash" placeholder="密码" /><span className="form_input_placeholder"></span><div className="form_error"></div></div><div className="form_item form_item_input"><input type="text" name='verifycode' className="form_input form_input_verifycode hc_input hc_input_grey hc_input_borderdash" placeholder="验证码"/><span className="form_input_placeholder"></span><img src={this.state.verify_img} alt="验证码" className="form_img_verifycode"/><div className="form_error"></div></div><div className="form_item form_item_submit"><button type="submit" className="hc_btn hc_btn_orange form_btn_submit">登录嗨猫</button></div></form></FormBox><Thirdparty /></div>);}
});
  1. getInitialState初始化Login组件的state;
  2. componentDidMount在组件绘制时触发,本例中使用jquery实现ajax请求;
  3. jsx中调用state的语法为{this.state.verify_img};
  4. FormBox组件调用时讲子节点写在其闭合标签内部,这一点与swig的block异曲同工。
  5. 另外需要注意的是,jsx中像img、input这类标签,必须不能遗漏闭合的斜杠/,否则会报语法错误。
2.3 配置react-router

react-router的配置比较简单,参照文档编写如下配置项:

const routeConf = [{path: '/',component: Pwd,indexRoute: {component: Signup},childRoutes: [{path: 'login',component: Login},{path: 'signup',component: Signup}]
}];
  1. 最外层的path指的是根目录,它调用的组件Pwd是一个空白得容器组件;
  2. indexRoute是进入页面默认的路由指向,本例中默认是注册表单;
  3. childRoutes是子路由的分发,path和component分别代表路径和对应的组件,上文提到的Nav组件中的两个a标签的href值分别对应childRoutes的path,本例中我们使用的是hash路由。

然后如下方式生产router:

render((<Router routes={routeConf}/>),container);

以上便是react-router替代Backbone的大概流程,目前遗留的问题有:

  1. 如何配合jquery validation实现表单验证?由于react-router每次的路由都是重新渲染dom节点,原来的dom节点被删除,导致jquery validation失效。
  2. 是否有比jquery validation更好的选择?

2015.12.2更新

3. 使用formsy-react取代jquery-validation

引入React的一个非常麻烦的事情是,react-router每次切换路径都会重绘dom,导致原来由jquery选定并保存的dom对象与重绘后的dom不一致。

如果是事件响应,可以使用dalegation处理,但是jquery validation插件并不支持类似dalegation的机制,这令两者的兼容面对一个死结。最终,奔着劲量减少耦合的目标(其实是没有研究出箭筒react-router和jquery validation的方案),决定使用react的表单验证组件formsy-react(下文简称为formsy)取代jquery validation,下文简单记录一下整个替代过程。

3.1 安装formsy-react并配置依赖

在项目根目录下执行:

bower install formsy-react --save

formsy安装在第三方库目录/assets/global/libs/下,formsy的目录结构如下:
595796-20160418130646616-774989449.jpg

src目录下是CommonJS规范的源文件,release目录下是编译后的umd规范文件,也是我们将在项目中引入的文件。在global/js/dev/main.es中的path中添加如下配置:

'formsy-react': 'formsy-react/release/formsy-react',

formsy安装成功后,便面临一个问题:前端react组件的重构。

3.2 react组件重构

使用formsy的前提是:form表单必须使用<Formsy.Form>生成,所以之前直接使用原始<form>生成的react组件必须重构为formsy格式。

3.2.1 创建组件库

之所以在此时创建组件库,一方面是为了迎合formsy,但这并不是主要目的。随着项目规模的扩大,组件库是必须的一部分。

以formsy的需求为例,组件库的创建过程如下:

1.新建文件global/js/dev/UIComponents.es(目录不固定,暂时存于此);

2.引入依赖:

import React from 'react';
import Formsy from 'formsy-react';

3.创建formsy-react组件HCInput:

/**
* react-input组件
*/
export const HCInput = React.createClass({mixins: [Formsy.Mixin],changeValue(event) {this.setValue(event.currentTarget[this.props.type === 'checkbox' ? 'checked' : 'value']);},render(){const className = 'form_item form_item_input' + (this.props.className || ' ') + (this.showRequired() ? 'required' : this.showError() ? 'error' : null);const errorMessage = this.getErrorMessage();return(<div className={className}><input type={this.props.type || 'text'} name={this.props.name} className={this.props.inputClass} placeholder={this.props.placeholder} onChange={this.changeValue} value={this.getValue()} checked={this.props.type === 'checkbox' && this.getValue() ? 'checked' : null}/><span className="form_input_placeholder" style={{display:'none'}}></span>{this.props.children}<div className="form_error">{errorMessage}</div></div>)}
});

编写formsy组件时有几点需要注意(规范):

  • this.getErrorMessage() 获取的是调用组件时传入的validationError参数值;
  • onChange事件是不可缺少的,用来触发formsy的验证逻辑;

另外,根据项目需求,验证码部分需要在HCInput组件内安置验证码图片的dom,所以HCInput组件接受children组件this.props.children

4.将组件加入依赖配置

UIComponents组件将会成为项目中的基础依赖被多个场景使用,所以将它加入依赖配置文件,方便开发工作。在global/js/dev/main.es中的path中添加如下配置:

// 自定义组件
'UIComponents': './../js/prod/UIComponents'

3.2.2 Login组件重构

组件库创建完毕后,开始进行前端react组件的重构工作,以下内容以Login组件为例。

Login组件的render方法重构后的结构如下:

render(){return(<div><Nav mode={'login'} /><FormBox mode={'login'} ><Formsy.Form  action='/login' method="post" className="login_form" target='target_ifr' onSubmit={this.submit} mapping={this.mapInputs} onValid={this.enableSubimit} onInvalid={this.disableSubimit}><HCInputtype="text"name='signname'placeholder="用户名或邮箱"inputClass='form_input hc_input hc_input_grey hc_input_borderdash'validations='isSignname,isNotEmpty'validationErrors={{isSignname: '请输入正确的用户名或邮箱',isNotEmpty: !!this.state.emptyError ? '请输入用户名或邮箱': ''}}/><HCInputtype="password"name='password'placeholder="密码"inputClass='form_input hc_input hc_input_grey hc_input_borderdash'validations='isNotEmpty'validationErrors={{isNotEmpty: !!this.state.emptyError ? '请输入密码': ''}}/><HCInputtype="text"name='verifycode'placeholder="验证码"inputClass='form_input form_input_verifycode hc_input hc_input_grey hc_input_borderdash'validations='isNotEmpty'validationErrors={{isNotEmpty: !!this.state.emptyError ? '请输入验证码': '',isLength: '验证码不正确'}}><img src={this.state.verify_img} alt="验证码" className="form_img_verifycode"/></HCInput><div className="form_item form_item_submit"><button type="submit" className="hc_btn hc_btn_orange form_btn_submit">登录嗨猫</button></div></Formsy.Form></FormBox><Thirdparty /></div>);}

下面逐条讲解重构细节。

1.<Formsy.Form>

除了标签不同以外,其他语法与常规react组件相同,需要注意的是几个监听函数:

  • onSubmit:用于拦截表单默认的submit行为,这一点与jquery validation的submitHandler功能相同;
  • mapping:将表单中各个input元素映射为自定义的Object。mapping并不是必须的;
  • onValid:表单中各元素都验证通过后触发;
  • onInvalid:与onValid相反,表单中任何一个元素验证不通过就会触发onInvalid,一般与onValid配合控制submit开关。

2.submit开关控制

前文提到使用onInvalid和onValid对submit进行开关控制,需要配合React组件的State实现。

首先在Login组件的getInitialState()方法中添加canSubmit作为submit开关:

getInitialState(){return {verify_img: "",canSubmit: false};
}

然后再onValid和onInvalid对应的响应函数中添加开关逻辑:

enableSubimit(){this.setState({canSubmit: true});
},
disableSubimit(){this.setState({canSubmit: false});
}

最后,在onSubmit对应的响应函数中根据开关判断是否提交表单:

submit(data){//开关off时不提交if(!this.state.canSubmit){return;}// ajax提交表单}

3.扩展formsy的验证规则

formsy自带的验证规则并不能完全满足项目的需求,比如登录时支持用户名和邮箱共享一个input,然后通过正则分发。formsy并没有这种混合验证的需求,所以我们需要对其验证规则进行扩展。

formsy提供了addValidationRule()API用来扩展验证规则。以signname为例,简单讲述一下扩展方法。

之前使用jquery validation已经完成了isSignname的验证规则制定,现在我们将它迁移到formsy,在UIComponents.es中添加代码如下:

/**
* @desc 登录名判断 - 纯文字或邮箱
*/
Formsy.addValidationRule('isSignname',function(values,value){let reg_isemail = /[@]/,reg_email =/^[a-z]([a-z0-9]*[-_]?[a-z0-9]+)*@([a-z0-9]*[-_]?[a-z0-9]+)+[\.][a-z]{2,3}([\.][a-z]{2})?$/i;return !reg_isemail.test(value) || (reg_isemail.test(value) &&reg_email.test(value));
});

这样,在Login组件内便可以使用isSignname验证规则:

<HCInputtype="text"name='signname'placeholder="用户名或邮箱"inputClass='form_input hc_input hc_input_grey hc_input_borderdash'validations='isSignname,isNotEmpty'validationErrors={{isSignname: '请输入正确的用户名或邮箱',isNotEmpty: !!this.state.emptyError ? '请输入用户名或邮箱': ''}}
/>

上述代码中的isNotEmpty也是我们自定义的验证规则,随后将会详细讲解为何不使用formsy自带的required验证规则。

formsy多个验证规则可以按上述代码搬使用逗号分隔,也可以写成类似validationErrors的格式
存在多个validation错误提示文案是必须使用validationErrors,注意是复数形式,如果写成validationError会解析出错。

下面解释一下为何需要自定义的isNotEmpty替代formsy自带的required规则。

  1. isNotEmpty规则的应用场景

简单来说,isNotEmpty规则存在的唯一目的,是保证进入页面之后初始状态没有错误提示信息。因为formsy的在表单创建成功之后立即进行验证。这样的规则之下,每次进入页面或者进行hash路由后,在用户输入信息之前便会显示错误提示信息,这显然是不合理的。

isNotEmpty的验证规则非常简单:

Formsy.addValidationRule('isNotEmpty',function(values,value){let isNotEmpty = !!value;return isNotEmpty;
});

isNotEmpty的关键逻辑在Login组件State的转换上面。参照本节最初Login组件的完整代码,将isNotEmpty的错误提示文案取值为this.state.emptyError,验证流程如下:

  1. 进入页面或切换hash路由之后,formsy立即对表单进行验证,此时isNotEmpty规则返回false,显示isNotEmpty错误提示文案,但是我们不想让用户看到这个提示,所以将次文案设置为空字符串,这就是为何this.state.emptyError初始值为''的原因;
  2. 用户输入信息之后点击submit按钮,触发submit函数中emptyError的设置逻辑this.setState({emptyError: '不能为空'});,在此之后,所有的验证逻辑便可以正常进行。

可能会有人疑惑为何this.setState({emptyError: '不能为空'});没有触发组件的重绘?经本人验证,只有在组件中state以某个属性直接使用(比如className={{this.state.emptyError}}这种)的情况下,setState才会触发重绘。formsy组件中,state作为formsy组件的某个配置参数,而不是直接使用,换句换说,this.state.emptyError只是作为值传入,而不是引用传入,并没有绑定关系。这种情况下setState是不会触发重绘的。

目前暂时采用的isNotEmpty方案并不是最优解,并且交互逻辑仍然有细微的问题,后续会深入研究formsy是否有原生可支持场景需求的方案。


2015.12.07更新

去除isNotEmpty验证规则,使用formsy isPristine API弥补空白验证缺陷

前文提到使用isNotEmpty配合组件的state避免hash路由切换后自动进行表单验证,导致初始进入表达后便显示错误提示。isNotEmpty规则配合state虽然能够解决这个问题,但是实现方式有些tricky。仔细研究formsy的API之后发现了isPristine,这个API的作用如下:

By default all formsy input elements are pristine, which means they are not "touched". As soon as the setValue method is run it will no longer be pristine.

默认情况下formsy的input控件都是原始值,换句话说就是它们还没有被触及。当formsy组件的setValue被调用后,input控件便不再是原始的了。

根据这个API的说明,我们可以进行这样的判断:如果input控件是原始的,那么它的错误提示便是空白的,用户便看不到错误提示。一旦组件的setValue被调用,便将错误提示替换为正常的值。根据这个规则,我们去掉与isNotEmpty规则匹配的state操作,修改HCInput控件的错误提示为:

<div className="form_error">{this.isPristine() ? '':errorMessage}</div>

这样,我们就省去了繁琐的state操作,直接使用formsy内部机制实现项目的需求了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_702138.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Win2008R2配置WebDeploy发布网站

一、配置服务器 1.安装管理服务 2.点击管理服务进行配置 二.安装WebDeploy 2.1通过离线安装包方式安装&#xff1a; https://www.iis.net/downloads/microsoft/web-deploy离线包没有WebDeploy3.6版本的&#xff0c;不过影响不大2.2通过Web平台安装程序安装&#xff1a; 我当时…

网站迁移的一般步骤、常见问题及解决措施

前言 最近在做网站迁移的工作&#xff0c;在此过程中遇到了很多问题&#xff0c;本文将网站迁移的一般步骤&#xff0c;常见问题和解决办法记录下来&#xff0c;希望帮到大家&#xff0c;少走弯路。 网站迁移的一般步骤、常见问题及解决措施前言一、网站迁移的一般步骤1、备份…

使用GitHub免费搭建属于自己的网站

1、创建仓库 在自己的GitHub上新建自己的仓库&#xff0c;仓库名格式必须为username.github.io&#xff0c;username与自己的GitHub一致&#xff0c;例如下图这般&#xff0c;若已经有这个仓库&#xff0c;会有图中的提示。 2、克隆此仓库&#xff08;本文使用的Ubuntu&…

tcp时间戳 引起的网站不能访问

目录 问题现象 TCP时间戳说明 解决方法 什么情况下出现这个问题 问题现象 访问一个我们新接入的业务接口&#xff0c;能ping通他们的站点&#xff0c;第一次telnet对端接口会通&#xff0c;第二次、第三次... 一直做下去就不通了&#xff0c;但是连接时超时服务器没有任何…

大型网站架构演变

第二遍阅读《大型网站技术架构》&#xff0c;真心觉得书中的内容很好&#xff0c;推荐大家去阅读。下面是书中对大型网站架构演变过程的介绍。 对于一个小型企业级网站,通常的架构如下图&#xff1a; 随着用户数量的增多&#xff0c;可以将应用服务与数据服务分离以提高性能&am…

百度技术沙龙精英汇聚 探秘大型网站数据库架构设计与性能优化

云计算浪潮汹涌而至&#xff0c;来自四面八方的海量数据已呈爆炸之势&#xff0c;如何给前端用户提供稳定、顺畅、安全、高速的体验服务&#xff0c;这既要求后台数据库系统顺应业务模式的变革不断升级&#xff0c;又能满足数据量爆增所带来的各种存储、查询、分析、挖掘等各种…

笔记本电脑当服务器部署网站,笔记本当云服务器

笔记本当云服务器 内容精选换一换如果在创建弹性云服务器时未设置密码&#xff0c;或密码丢失、过期&#xff0c;可以参见本节操作重置密码。密码丢失或过期前&#xff0c;已安装密码重置插件。公共镜像创建的弹性云服务器默认已安装一键重置密码插件。私有镜像创建的云服务器且…

大型网站系统架构

前言 一个成熟的大型网站&#xff08;如淘宝、京东等&#xff09;的系统架构并不是开始设计就具备完整的高性能、高可用、安全等特性&#xff0c;它总是随着用户量的增加&#xff0c;业务功能的扩展逐渐演变完善的&#xff0c;在这个过程中&#xff0c;开发模式、技术架构、设…

给网站增加https的简单方法

为什么80%的码农都做不了架构师&#xff1f;>>> https://certbot.eff.org/ 转载于:https://my.oschina.net/fox99/blog/1621009

支持全球探测点的新一代网站监控

2019独角兽企业重金招聘Python工程师标准>>> 摘要&#xff1a; 当你的网站开发完成了&#xff0c;你是不是想知道全国各地到服务器的网络访问情况。 当你的网站上线了&#xff0c;你是不是不想变成熊猫眼24小时盯着网站运行情况。 当你的网站商业化了&#xff0c;你…

Windows 08R2 IIS网站架设

目录 目录配置和安装IIS环境设置安装IIS服务器网站的站点目录和欢迎页面 配置和安装IIS IIS是Windows的网站服务器&#xff0c;所以配置IIS服务的前提是需要一个网址、和DNS域名并添加主机记录。 环境设置 我们以下图架构搭建一个类似的IIS服务。我们将DNS服务和IIS服务ALL-…

SEO优化:网站优化的五大步骤

我们在做SEO优化的过程中&#xff0c;不能毫无头绪得去做&#xff0c;不然取得的效果很差&#xff0c;做任何事情&#xff0c;都要有一个好的策划&#xff0c;完整的方案才能做的更好。今天搜客大伟讲讲做网站优化的五大步骤&#xff1a; 1、关键词分析 这是进行SEO优化最重要的…

ThinkSNS积分商城系统 一站式解决企业商城建站需求

2019独角兽企业重金招聘Python工程师标准>>> 什么是ThinkSNS ThinkSNS(简称TS)&#xff0c;一款全平台综合性社交系统&#xff0c;为国内外大中小企业和创业者提供社会化软件研发及技术解决方案&#xff0c;目前最新版本为ThinkSNS。 积分商城体验PC端地址 http://d…

3000+ NLP资源一网打尽,只需用这个分类检索网站 | 免费

子豪 发自 凹非寺量子位 报道 | 公众号 QbitAINLP资源免费搜索神器来了~有了它&#xff0c;就能一键直达arXiv论文PDF、相关研究的关联网、GitHub存储库&#xff0c;以及Colab&#xff0c;再也不用逐项搜索&#xff01;这是一个NLP资源检索网站&#xff0c;在reddit上获得了460…

php ucwords,WordPress博客程序中Platinum SEO Pack SEO插件设置图解介绍

Automatically do 301 redirects for permalink changes&#xff1a;301转向&#xff1a;当日志固定链接结构改变后会因为找不到页面而显示404错误&#xff0c;启用此选项后&#xff0c;旧URL会被自动转向到新的URL。Home Title:这个是显示在浏览器标题栏的首页标题&#xff0c…

前端里面如何进行搜索引擎优化(SEO)

如何进行SEO优化&#xff1a; (1) 避免head标签js堵塞&#xff1a; 所有放在head标签里面的js和css都会堵塞渲染&#xff1b;如果这些css和js需要加载很久的话&#xff0c;那么页面就空白了&#xff1b; 解决办法&#xff1a;一是把script放到body后面&#xff0c;这也是很多网…

html中怎么给网页添加音乐播放器,怎么给网站或网页添加音乐

一定程度上学建站网不赞同在网站中插入背景音乐&#xff0c;就自己而言&#xff0c;如果我在听着音乐时打开你的网站&#xff0c;我会毫不犹豫的叉掉你的网页&#xff0c;因为“有碍视听”&#xff0c;不过事无绝对&#xff0c;如果没有听音乐&#xff0c;一遍浏览网站文章一遍…

十周后,62%的PHP网站将运行在一个不受支持的PHP版本上

根据W3Techs的统计数据&#xff0c;目前约有78.9&#xff05;的网站使用PHP开发。\\但是&#xff0c;PHP 5.6.x的安全支持将在2018年12月31日正式停止&#xff0c;这标志着对古老的PHP 5.x分支版本的支持都将结束。\\也就是说&#xff0c;从明年开始&#xff0c;大约62&#xf…

使用 acme.sh 获取网站证书并配置https访问

目录 1. 安装 acme.sh 2. 生成证书 1. 安装 acme.sh 安装很简单, 一个命令: curl https://get.acme.sh | sh普通用户和 root 用户都可以安装使用. 安装过程进行了以下几步: 1.把 acme.sh 安装到你的 home 目录下: ~/.acme.sh/并创建 一个 bash 的 alias, 方便你的使用: …

【Docker实战之入门】Dockerfile详细分析:构建docker镜像(4)构建动态网站WordPress...

代码文件 [rootOptimus /]# cd docker-training/ [rootOptimus docker-training]# ls centos7 mysql php-fpm README.md wordpress [rootOptimus docker-training]# cd wordpress/ [rootOptimus wordpress]# ls Dockerfile init.sh readme.html wp-admin …