admin 发布的文章 - 青蓝鱼的博客-没有bug的代码是不完美的
admin 发布的文章 - 青蓝鱼的博客-没有bug的代码是不完美的
终于找到这篇文章了,感谢作者的分享!
https://fishfive.top/index.php/archives/29/
终于找到这篇文章了,感谢作者的分享!
受益匪浅,感谢博主。
该回复疑似异常,已被系统拦截!
1
111
666
# 图片回复
666
学到了
666
hello word
首页
关于
?
归档
留言
统计
导航
更多
github
友链
推荐
百度
搜 索
1
Nginx-Quic重新编译Nginx支持HTTP3
356 阅读
2
Centos7和Centos8网卡配置
269 阅读
3
六种好看的css按钮效果
189 阅读
4
node.js简单的web服务demo
159 阅读
5
JavaScript实现静态图片局部流动效果
156 阅读
默认分类
html
css
JavaScript
React
Vue
Git
centos
node.js
php
nginx
http
登录
搜 索
https://fishfive.top
累计撰写
27
篇文章
累计收到
16
条评论
首页
栏目
默认分类
html
css
JavaScript
React
Vue
Git
centos
node.js
php
nginx
http
页面
关于
归档
留言
统计
导航
github
友链
推荐
百度
用户登录
登录
找到
27
篇与
admin
相关的结果
2022-07-28
Vue组件通信方式
1.props2.$emit/$on3.$parent/$children4.ref5.$attrs / $listeners6.$root7.eventBus8.vuexprops父子组件通信1 props 父传子1.父组件以属性的方式传值给子组件2.子组件通过props方式接收数据父组件核心代码在父组件中引入子组件并绑定parentData自定义属性<Child:parentData="parentData"></Child > <script> import Child from '@/components/child' export default{ name:'Parent', components:, data(){ return{ parentData:'我是父组件向子组件传递的值-props方式' } } } </script>子组件核心代码1.在子组件中使用 props 接收父组件传递的数据,2.props 里的名字跟父组件定义的属性名一致<template> <div>我是父组件的数据:}</div> <div>我是父组件传递修改后的数据:}</div> </template> <script> export default{ name:'Child', props:{ parentData:{ type:String, default:'' } } data(){ mydata:'俺的小破站 '+ this.parentData }, watch:{ parentData(newVal){ this.mydata='俺的小破站 '+ newVal } }, } </script>Vue的单向数据流机制,子组件不能够直接去修改父组件传递的值修改的,否则能改的话那父组件的值就被污染了。但是子组件内想要修改父组件传过来的值却不“污染”父组件的话,可以在子组件内定义一个变量mydata去接收parentData数据,并使用 watch 监听parentData数据的变更$emit/$on 子传父1.子组件绑定自定义事件2.使用 $emit() 触发更改数据子组件核心代码<el-button @click="handleEmit">告诉父组件我要更改数据啦</el-button> <script> export default{ name:'Child', methods:{ handleEmit(){ this.$emit('triggerEmit','我是来自子组件的数据') } } } </script>父组件核心代码1.父组件定义并绑定子组件传递的triggerEmit事件2.triggerEmit事件名需跟子组件 $emit() 的事件名<Child @triggerEmit="changeData"></Child> <script> import Child from '@/components/child' export default{ name:'Parent', components:, methods:{ changeData(name){ console.log(name) // => 我是来自子组件的数据 } } } </script>$parent/$children1.子组件通过 $parent 获得父组件实例2.父组件通过 $children 获得子组件实例数组 子组件<template> <div>我是子组件</div> </template> <script> export default{ name:"Child", data(){ return{ childTitle: '我是子组件的数据' } }, methods:{ childHandle(){ console.log('我是子组件的方法') } }, created(){ console.log(this.$parent) console.log(this.$parent.parentTitle) // => 我是父组件的数据 this.$parent.parentHandle() // => 我是父组件的方法 } } </script>this.$parent可以获取到父组件的方法、data的数据等,并可以直接使用和执行。父组件<template> <div> <Child>我是父组件</Child> </div> </template> <script> import Child from './child.vue' export default{ name: 'parent', components:{ Child }, data(){ return{ parentTitle: '我是父组件的数据' } }, methods:{ parentHandle(){ console.log('我是父组件的方法') } }, mounted(){ console.log(this.$children) console.log(this.$children[0].childTitle) // => 我是子组件的数据 this.$children[0].childHandle() // => 我是子组件的方法 } } </script>注意钩子的使用父组件是在 mounted()生命周期中获取子组件实例的,并且获取的实例是一个数组形式层级发生变化的时候咋办呢 ???1.源码其实有更高层次的封装,在引用parent到时候抽象一个更高级的方法类似 dispatch,2.直接指定比较重要的父组件的类型或者名称,在循环查找的时候避免程序的脆弱性ref父组件使用 $refs 获得组件实例<template> <div> <Child ref="child"></Child > </div> </template> <script> import Child from './child.vue' export default{ name: 'parent', components:{ Child }, mounted(){ console.log(this.$refs.child ) /*组件实例*/ } } </script>1.注意 钩子 mounted父组件就可以直接使用this.$refs.xx获取子组件的实例$attrs/$listeners兄弟组件核心就是找共同点, 搭建桥梁,中间人,话事人的感觉$parent既然是兄弟往上找 总能找到共同的祖先不常用,可以参考文章上面 的 写法$root其实 根也是共享的eventBus也是都可以访问的创建一个Vue实例作为调度中心 eventBusimport Vue from "vue" export default new Vue()需要进行通信的组件中 引入<template> <div> <div>我是通信组件A</div> <el-button @click="changeName">修改</el-button> </div> </template <script> import from "../bus.js" export default{ data(){ return }, methods:{ changeName(){ EventBus.$emit("editName", '俺的小破站') } } } </script>监听事件<template> <div>我是通信组件B</div> </template <script> import from "../bus.js" export default{ data(){ return }, mounted:{ EventBus.$on('editName',(name)=>{ console.log(name) // 俺的小破站! }) } } </script>vuex1.Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。2.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测 的方式发生变化3.Vuex 也集成到 Vue 的官方调试工具npm 安装npm install vue --S需要 简单的配置import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex)组件 A 模板 重点< button @click="add()" >传给兄弟组件 -- }</button>组件 A 传值重要 的是创建了一个 中间 Vue 实例对象 用于周转这个就是 事件中心 event bus 创建一个空的vue实例对象,目的是实现兄弟组件之间的交互 var bus = new Vue(); add(){ this.num++; // /当组件需要给其他组件传值时,可以调用总线bus的$emit发射一个事件,需要接收这个传值的组件监听这个事件即可。 bus.$emit("changeData",this.num); },组件B核心代码 是监听 组件A 的事件 created(){ // 此时this指代自定义的f_view对象,记录该对象 var _this = this; console.log(this); // 接收传值的组件需要监听总线bus上的传值事件 // 通过bus.$on添加总线事件监听,第一个参数是监听的事件类型,第二个参数是事件绑定的函数,事件函数的参数就是发射事件时所传递的参数。 bus.$on("changeData",function(a){ // 此时的this指代vue实例tempVe对象 // a接收事件发射传递过来的num值 --- h_view中的数据源 console.log(a); _this.count = a; }); }并不是所有的项目都适合 用vuex,适合自己的才是最好的还有哪些可以变向管理状态或者说 页面共享数据datasetcookiestorage极端情况 window 挂载属性provide/inject父组件使用 provide 注入数据子组件使用 inject 使用数据父组件export default{ provide: { return{ provideName: '俺的小破站' } } }分析provideName这个变量可以提供给它其下的所有子组件,包括曾孙、孙子组件等,只需要使用 inject 就能获取数据子组件export default{ inject: ['provideName'], created () { console.log(this.provideName) // 俺的小破站 } }优缺点1.父组件不需要知道哪个组件使用它提供出去的数据2.子组件不需要知道这个数据从哪里来3.更加的原生,不会对第三方库产生依赖缺点:单向的;只能祖辈向子辈传值vue3 通信方式 props和emitsetup函数可以接受两个参数, prop 和 context ,其中context可以解构出emit slots attrs利用 emit实例来传参子组件 代码<template> <el-button @click="handle">子组件 ---点击</el-button> <div>我是父组件传过来的数据:}</div> </template> <script> export default { name:"Child", props: { name: { type: String, default: '' } }, setup(props,) { console.log(props.name) // yzs function handle() { emit('handleClick', 'Vue3 学起来') } return { handle } } } </script>简要分析Vue3中没有this的概念了,所以就不会有this.$emit存在,所以可以从setup传入的context解构出emit实例,从而派发事件给父组件 父组件<template> <Test name="yzs" @handleClick="myClick">父组件---点呀</Test> </template> <script> import Test from './index.vue' export default { name:"parent", components: , setup() { function myClick(name) { console.log(name)// Vue3 学起来 } return { myClick } } } </script>refVue3我们可以从Vue中导出 ref 方法,得到子组件的实例分析1.通过,在子组件声明ref属性,2.属性值必须和const btnRef = ref(null)这里声明的变量名一致,否则会报错3.拿到子组件实例后就可以直接调用组件的sendParent方法了父组件代码<template> <Test ref="btnRef"> <el-button @click="click">点我呀</el-button> </Test> </template> <script> import from "vue" import Test from './index.vue' export default { components: , setup() { const btnRef = ref(null) function click() { btnRef.value?.sendParent() // 我是给父组件调用的方法 } return { btnRef, click } } } </script>这里使用的btnRef.value?.是可选链操作符语法,代表?前面的值为true才继续执行后面的语句使用<template> <slot></slot> </template> <script> export default { setup() { function sendParent() { console.log("我是给父组件调用的方法") } return { sendParent } } } </script>provide/inject和vue2差不离儿父组件<template> <Test></Test> </template> <script> import from "vue" import Test from './index.vue' export default { components: , setup() { //我已经把数据注入到fromParent里面去了 provide('fromParent', '俺的小破站') return } } </script>子组件<template> <slot></slot> <div>我是父组件注入的数据:}</div> </template> <script> import from "vue" export default { setup() { //我来去父组件注入的数据 let parentData = inject('fromParent') return { parentData } } } </script>子孙组件使用inject获取到父组件注入的数区别其实主要就是 Vue3采用了 这个模块化所以inject 和 provide 需要单独导入nextTick 是什么在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。官方案例// 修改数据 vm.msg = 'Hello' // DOM 还没有更新 Vue.nextTick(function () { // DOM 更新了 }) // 作为一个 Promise 使用 (2.1.0 起新增,详见接下来的提示) Vue.nextTick() .then(function () { // DOM 更新了 })为什么 需要nextTick由于vue的异步更新策略导致我们对数据的修改不会立刻体现在dom变化上,此时如果想要立即获取更新后的dom状态,就需要使用这个方法vue在更新DOM时是异步执行的.只要侦听到数据变化,vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更.如果同一个watcher被多次触发,只会会被推入到队列中1次.这种在缓冲时取出重复数据对于避免不必要的计算和DOM操作是非常重要的.nextTick方法会在队列中加入一个回调函数,确保该函数在前面的DOM 操作完成后才调用所以当我们想在数据修改后立即看到都DOM执行结果就需要用到nextTick方法
2022年07月28日
69 阅读
0 评论
0 点赞
2022-07-28
Vue 指令
本文指令有:v-text,v-html,v-if,v-else,v-else-if,v-show,v-for,v-on,v-bind、v-model,v-pre,v-cloak,v-once插值表达式(以此引出指令)数据绑定最常见的形式就是 “Mustache”语法 (双大括号) 的文本插值,Mustache 标签将会被对应数据对象上属性的值替代。只要绑定的数据对象上属性发生了改变,插值处的内容都会更新。用过 vue 的盆友都清楚,message 是将数据解析成纯文本的,也就是说,就算 message 中含有了 html 标签,它也是只看做纯文本的,不能输出真正的 html。例如: <div id="app"> <p>}</p> </div> <script type="text/javascript"> var app = new Vue({ el: '#app', data: { message: '<h1>asc</h1>' } }) </script> <!--页面显示为: <h1>asc</h1>-->v-textv-text 简介:v-text 与插值表达式相同的地方是,它也是将数据解析成纯文本(解释 html 标签可能导致 xss 攻击)但它与花括号的区别是:使用 v-text 在页面加载时不会显示'message',解决了插值表达式闪烁问题,因为他是属性而不是插值表达式 <div id="app"> <p v-text="msg">这里的内容会被替换</p> </div> <script> var vm = new Vue({ el: "#app", data: { msg: "Hello! <h1>v-text</h1>", } }) </script> <!--页面显示为: Hello! <h1>v-text</h1>-->注: "页面闪烁" 指的是在加载插值表达式时,会先直接在页面显示'message',然后再编译对应的数据v-html简介:为了输出真正的 HTML,可以用 v-html 指令。它等同于 JS 的 innerHtml 属性,会将数据解析成 html 形式。 <div id="app"> <p v-text="msg">这里的内容会被替换</p> </div> <script> var vm = new Vue({ el: "#app", data: { msg: "Hello! <h1>v-html</h1>", } }) </script> <!--页面显示为: Hello! v-html -->v-if简介: v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。 <div id="app"> <p v-if="true"> v-if 为 true 时显示</p> <p v-if="false"> v-if 为 false 时显示</p> </div> <script> var vm = new Vue({ el: "#app", data: { true: true, false: false, } }) </script>v-if 是 “真正” 的条件渲染,因为它会确保在切换过程中 条件块内的事件监听器 和 子组件 适当地被销毁和重建。v-if 是惰性的,如果初始渲染时条件为 假,那么就什么也不用做,直到第一次条件变为 真,才开始渲染条件块。v-if 通常用于以下两种情况:多个元素之间,通过条件判断,来 展示 或者 隐藏 某个或多个元素。进行两个视图的切换。例如下面的代码分别实现了: ①type 不同值时元素的展示情况;②点击按钮切换视图<div id="app"> <div style="color:red">v-if的简单实用</div> <template> <div v-if="type == 'A'">A</div> <div v-else-if="type=='B'">B</div> <div v-else>C</div> </template> <div> <p>v-if的弹框切换</p> </div> <template v-if="loginType === 'username'"> <label>用户名: </label> <input placeholder="Enter your username" key="username-input"> </template> <template v-else> <label>密码: </label> <input placeholder="Enter your email address" key="email-input"> </template> <button @click="changeloginType">切换状态</button> </div> <script> var app = new Vue({ el: '#app', data: { type: 'C', loginType: 'username' }, methods: { changeloginType() { let self = this; if (self.loginType == 'username') { self.loginType = '' } else { self.loginType = 'username' } } } }) </script>v-else简介:个人感觉 v-else 并不像一个单独功能的指令,而是偏辅助功能的,而且 v-else 必须和 v-if 连用,否则会报错。 <div id="app"> <p v-if="msg1">The msg1 is true</p> <p v-else>The msg1 is false</p> <input type="button" @click="button" value="Button"/> </div> <script type="text/javascript"> var app = new Vue({ el: '#app', data: { msg1: true, }, methods: { button: function(){ this.msg1 = !this.msg1; } } }) </script>上述过程即为 v-if 与 v-else 连用,若去掉 v-if 部分,则报错为:编译模板时出错v-else-if简介:与 v-else 相同,v-else-if 也必须和 v-if 连用,其实道理很简单,类比 C 语言的 if、else、else if,这里的 v-else-if 也是用在 v-if 和 v-else 中间,实现多次判断 <div id="app"> <p v-if="10>20">v-if 赢了</p> <p v-else-if="10>5">v-else-if 赢了</p> <p v-else>好吧,v-else 赢了</p> </div> <script type="text/javascript"> var app = new Vue({ el: '#app', }) </script>如果没有 v-if,那么也会和上面的 v-else 一样报错:编译模板时出错v-show简介: v-show 也是用来 控制元素是否显示 的,其功能与 v-if 指令相似。<div id="app"> <p v-show="msg">显示成功</p> <input type="button" @click="show" value="Do you want to hide it?" /> </div> <script type="text/javascript"> var app = new Vue({ el: '#app', data: { msg: true, }, methods: { show: function(){ this.msg = !this.msg; } } }) </script>那么,v-show 与 v-if 有什么 联系 呢?v-show 与 v-if 都是通过 动态地操作 DOM 来实现显示与隐藏的v-show 与 v-if 的 区别 是什么呢?手段的不同: v-if 是动态地向 DOM 树内动态地添加或删除 DOM 元素,如果 v-if 的值是 false 就会在 DOM 中删除,相当于 visibility:hidden;,如果是 true 把元素克隆到相应的 DOM 树上去,支持加在 <template> 标签上;而 v-show 是动态地设置 DOM 元素的 display 属性,而不会删除 DOM 元素,是 display:none;,不支持加在 <template> 标签上。编译过程的不同: v-if 在切换时会有一个局部编译 / 卸载的过程,在切换时适当地销毁和重建内部的事件监听 和 子组件,因为 v-if 可能是数据绑定或者子组件;而 v-show 只是简单地进行 CSS 属性的切换。编译条件的不同: v-if 是真真正正的条件渲染,但它是惰性的,如果初始条件为假,则什么也不用做,只有在第一次条件变为真时才开始局部编译,编译会被缓存,之后等到再切换时再进行局部卸载;而 v-show 不管其值是否为真,都进行编译,然后编译被缓存,而且 DOM 元素也保留。性能消耗不同: v-if 有着更高的切换消耗,v-show 有着更高的初识渲染消耗由此,就引发出了使用问题:当组件的某块内容切换很少,那么切换消耗也就很少,此时用 v-if 来渲染更加合适如果频繁地切换着条件,那么就用 v-show 更加合适,实现一次渲染,多次切换为什么这么说呢,因为频繁操作 DOM 会很影响性能,如果频繁切换,就意味着频繁地创建、删除 DOM,为减少消耗就更该用 v-show;如果是要么显示要么隐藏的情况下,那么 v-if 更加合理,因为借助 v-if 的惰性,如果一开始它的值即为 false,那么甚至都不需要创建 DOM,如果其值为 true,则效果与 v-show 完全一样。补充: display:none 与 visibility:hidden 的区别:display:none 是彻底消失,不在文档流中占位,浏览器也不会解析该元素;visibility:hidden 可以理解为透明度为 0 的效果,它只是视觉上的消失,在文档流中是占位的,浏览器也会解析该元素。使用 visibility:hidden 要比 display:none 性能更好,用 display 切换时,页面会产生 回流 (当页面中的一部分元素需要改变规模尺寸、布局、显示隐藏等,页面需要 重新构建,即为回流。所有页面第一次加载时需要产生一次回流),而用 visibility 切换时不会产生回流。补充:若一个元素在 CSS 中已设置 display:none,则无法通过设置 v-if 或者 v-show 来让元素显示,因为切换时只会修改元素 element.style 的 “display” 属性为 “none” 或者 “block”,并不会覆盖或者修改已经存在的 CSS 属性v-forv-for 循环普通数组:<div id="app"> <p v-for="value in list">}</p> <p v-for="(value,i) in list">索引: } => 值: }</p> <!-- 注意: 值与索引的顺序为 : (value,i) --> </div> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { list: [1,2,3,4,5] } }) </script>v-for 循环对象<div id="app"> <p v-for="(value,key,i) in student"> 索引: } —— 键: } —— 值: } </p> </div> <script type="text/javascript"> var app = new Vue({ el: '#app', data: { student: { id: 0, name: 'Tom', gender: '男' } } }) </script>v-for 迭代数字<div id="app"> <p v-for="count in 10">这是第}次循环</p> </div> <script type="text/javascript"> var app = new Vue({ el: '#app', }) </script>v-for 中的 key 属性的使用: <div id="app"> <div> <label>Id: <input type="text" v-model="id" /> </label> <label>Name: <input type="text" v-model="name" /> </label> <input type="button" @click="add" value="添加" /> </div> <p v-for="item in list" :key='item.id'> <input type="checkbox" />} --- } </p> </div> <script type="text/javascript"> var app = new Vue({ el: '#app', data: { id: '', name: '', list: [ , , , ] }, methods: { add(){ this.list.unshift(); } } }) </script>v-on简介:它是来 绑定事件监听器 的,这样我们就可以进行一下交互。v-on 可简写为 @<div id="app"> <input type="button" v-on:click="click" value="点此弹出语句"/> </div> <script type="text/javascript"> var app = new Vue({ el: '#app', methods: { click(){ alert('Hello Vue!'); } } }) </script>v-bind简介: v-bind 指令主要用于 绑定属性,可以动态地绑定一个或多个属性,比如 class 属性、style 属性、value 属性、href 属性,只要是属性,就可以用 v-bind 进行绑定。它的语法是: v-bind:属性名 = "变量名",规定 v-bind 可以缩写为:,如语法可以缩写为:属性名 = "变量名"。绑定 HTML Class:对象语法:我们可以传给 v-bind:class 一个对象,以动态地切换 class 属性,<div v-bind:class=""></div> <script> var vm = new Vue({ el: '#app', data: { isActive: true //此时可以展示active } }) </script>上述表示 active 是否被展现取决于 isActive 是否为真。此外,可以在对象中传入多个属性来动态地切换 class,例如下面的模板 <div class="static" v-bind:class=""></div> <script> var vm = new Vue({ el: '#app', data: { isA: true, isB: false, } }) //结果渲染为: <div class="static isA"></div> </script>我们也可以绑定一个返回对象的计算属性,这是一个强大而且常用的模式 <div v-bind:class="classObject"></div> <script> var vm = new Vue({ el: '#app', data: { isA: true, isB: false, }, computed: { classObject: function () { return { active: this.isA && !this.isB, statiq: this.isB && this.isB.type === 'fatal' } } } }) </script>数组语法:我们可以把一个数组传给 v-bind:class,以应用一个 class 列表: <div id="app"> <div v-bind:class="[activeClass, errorClass]"></div> </div> <script> var vm = new Vue({ el: '#app', data: { activeClass: 'active', errorClass: 'text-danger' } }) //渲染为 <div class="active text-danger"></div> </script>v-model简介:这个指令用于 在表单上创建双向数据绑定,v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值,因为 v-model 选择 Vue 实例数据来作为具体的值。<div id="app"> <input v-model="somebody"> <p>hello }</p> </div> <script> var app = new Vue({ el: '#app', data: { somebody: '小明' } }) </script>v-pre简介: v-pre 会 按原样输出,标签中的内容是什么就输出什么,主要用来跳过这个元素以及它的子元素的编译过程,可以用来显示原始的 Mustache 标签。<div id="app"> <p v-pre>}</p> <p>}</p> </div> <script> var app = new Vue({ el: '#app', data: { msg:"'v-pre'是不会管我的..." } }) </script>v-cloak简介:还记得上文中提到的 页面闪烁 吗,我们也可以用 v-cloak 来解决这一问题。这个指令用来保持在元素上直到关联实例结束时进行编译。<div id="app" v-cloak> } </div> <script> var app = new Vue({ el: '#app', data: { context:'Hello Vue!' } }); //用'v-cloak'则不会出现闪烁 </script>v-oncev-once 关联的实例 只会渲染一次,执行 一次性地插值,当数据改变时,插值处的内容不会更新,v-once 所定义的元素或组件只会渲染一次,首次渲染后,不再随着数据的改变而重新渲染。若之后牵涉到重新渲染,那么 它关联的实例及其所有子节点 会被视为 静态内容 而被跳过,这可以用来优化更新性能。<div id="app"> <p v-once>}</p> <p>}</p> <p> <input type="text" v-model="msg" name=""> </p> </div> <script type="text/javascript"> var vm = new Vue({ el: '#app', data: { msg: "hello" } }); </script>
2022年07月28日
53 阅读
0 评论
0 点赞
2022-06-17
php模拟复现http错误码:499,500,502,504
Status Code 499、500、502、504也是后端Http服务经常返回的状态码,试想一下,对于每个状态码,如果你能通过一些修改或配置来人为复现它,是不是会更利于你去掌握它呢?本文就是通过动手复现的方式来学习它们。502,504在超时的场景下会比较像,经常有人不能区分它们。499产生的原因也常常会和504会有内在的关联,你都了解吗?本文不光复现它们,而且会循序渐进,在对比之中复现它们。下面所有复现的场景,修改nginx或者php-fpm的配置后,记得要重新启动。复现环境说明系统环境和软件环境为:Linux,Nginx,php-fpm。再来介绍一下本文复现会用到的Nginx和php-fpm的几个配置。#nginx超时相关配置: fastcgi_connect_timeout 5; # nginx连接fastcgi的超时时间 fastcgi_send_timeout 10; #nginx往fastcgi发送参数的超时时间 fastcgi_read_timeout 10; #nginx从fastcig获取数据的超时时间php-fpm配置: ; The timeout for serving a single request after which the worker process will ; be killed. This option should be used when the 'max_execution_time' ini option ; does not stop script execution for some reason. A value of '0' means 'off'. ; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) ; Default Value: 0 ; 翻译过来就是指一次请求的最长执行时间 request_terminate_timeout = 30s所有复现场景都是在nginx根目录下创建一个test.php文件,然后通过访问http://127.0.0.1/hello.php 来查看http响应code,test.php代码如下:<?php sleep(7); // 通过调整sleep秒数,来达成不同的复现 echo 'hello world'; ?>502定义502,Bad Gateway,网关错误,它往往表示网关从上游服务器中接收到的响应是无效的。先来了解一下网关是什么含义,从宏观定义上来说只要连接两个不同的网络的设备都可以叫网关,其实具体到应用层Http请求这一领域,网关就是指是转发其他服务器通信数据的服务器,对于本文的复现环境而言,当客户端请求数据到达nginx,nginx负责把请求转交给fastcgi(即php-fpm)进行处理,那么在这个场景中Nginx就是网关。502并不是指网关本身出了问题,而是从上游接收响应出了问题,比如由于上游服务自身超时导致不能产生响应数据,或者上游不按照协议约定来返回数据导致网关不能正常解析。复现路径1关闭php-fpm进程,返回502。这个比较容易理解,参照上面的定义,因为php-fpm进程关闭,nginx连接不上php-fpm,即nginx不能收从上层接收到响应数据。nginx 错误日志如下:connect() to unix:/home/work/server/php-cgi.sock failed (2: No such file or directory) while connecting to upstream复现路径2启动php-fpm进程,修改php-fpm.conf的request_terminate_timeout和php代码的sleep时间来复现。php代码: <?php sleep(7); echo 'hello world'; ?>php-fpm.conf配置:request_terminate_timeout=5nginx配置:fastcgi_read_timeout 10;php-fpm.conf设置的最大执行时间是5s,但是php脚本需要的执行时间大于7s,所以php-fpm进程执行5s时就回退出,此时php脚本没有正常执行完成,所以返回给网关Nginx的数据异常,于是导致502。php-fpm错误日志如下:script '/home/work/webroot/hello.php' (request: "GET /hello.php") execution timed out (5.161544 sec), terminatingnginx错误日志如下:recv() failed (104: Connection reset by peer) while reading response header from upstream504定义504,Gateway Timeout,网关超时。它表示网关没有从上游及时获取响应数据。注意它和502在超时场景下的区别,502是指上游php-fpm因为超过自身允许的执行时间而不能正常生成响应数据,而504是指在php-fpm还未执行完成的某一时刻,由于超过了nginx自身的超时时间,nginx则以为上游php-fpm没有按照设置时间返回响应数据就会返回504, 此时对于php-fpm而言还会继续执行下去,直到执行完成。复现路径php代码<?php sleep(7); echo 'hello world'; error_log("test", 3, "/tmp/test.log"); ?>php-fpm.conf配置:request_terminate_timeout=30 nginx配置fastcgi_read_timeout 5;test.php脚本执行时间需要7s,远小于php-fpm的一次请求的最大请求时间30s,所以php脚本可以正常完成执行,这个可以查看/tmp/test.log文件内容来得到证明。由于nginx从php-fpm读取数据的超时时间为5s,所以在5s的时科,nginx还未从php-fpm获取到响应数据,于是返回504。nginx错误日志如下:upstream timed out (110: Connection timed out) while reading response header from upstream499499, Client Closed Request, 客户端主动断开连接。是指一次http请求在客户端指定的时间内没有返回响应,此时,客户端会主动断开连接,此时表象为客户端无响应返回,而nginx的日志中会status code 为499。此状态码在浏览器请求时几乎不可见,因为浏览器默认的超时时间会很长。多见于服务之间的调用,在业务架构中常常会分层设计,拆分为不同的子系统或者微服务,这样系统之间就会常常通过http方式来请求,并且会设置每次请求的超时时间,当请求在请求时间内所调用的上游服务无返回,则会主动关闭连接,上游服务日志中会记录一条499。复现路径我们用上面504复现时相同的代码和配置。我们在linux终端使用curl命令来请求,-m 表示超时时间,单位为秒curl:(28) Operation timed out after 3004 milliseconds with 0 bytes receivednginx的access日志的code为499,如下:"HEAD /test.php HTTP/1.1" 499 0500定义500, Internal Server Error , 服务器内部错误,服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。日常开发中500错误几乎都是由于php脚本语法出现错误导致php-fpm无法正常执行。复现路径php代码:<?php echo 'hello ' echo ' world'; ?>由于php代码语法错误,php-fpm执行失败,然后告诉nginx这一结果,nginx则返回500。php错误日志:PHP Parse error: syntax error, unexpected 'echo' (T_ECHO), expecting ',' or ';' in /home/work/webroot/test.php on line 3总结499是由于超过客户端设置的请求超时时间,客户端主动关闭连接,服务器code为499。500多是由于代码语法错误,导致CGI执行错误并且会把错误结果通知服务器,服务器则报500。502是由于CGI由于在自身的执行时间要求内无法按时完成,则无法返回给服务器正常响应,此时服务器会返回502。504是CGI在服务器设置的超时时间内无法按时返回响应,服务器则返回504。499,502,504都会因为超时而产生,区别是超时超了谁的时,499是超了客户端本身的连接时间,502是超了CGI的执行时间,504是超了服务器本身的最大允许读取时间。
2022年06月17日
107 阅读
0 评论
0 点赞
2022-06-16
10 个最常见的 HTTP 状态代码
作为一个典型的互联网用户,没有什么比等待网页显示更令人沮丧的了,只收到”未找到页面”404错误状态代码。 当然,我们尝试重新加载页面,有时,让格林林斯开始工作,但大多数时候,问题是我们掌握的。 对于我们所有的典型用户,我们要么去下一件事,要么找到一个不同的网站。 在我们大多数人完全不知道的背景中发生了很多事情。 但是,对于 Web 开发人员来说,HTTP 状态代码错误可能令人讨厌。 据互联网工程特别工作组(IEFT)称,开发和推广互联网标准的组织有60多个不同的HTTP状态代码。 HTTP 状态代码分为以下五组:1xx 信息响应。 收到并理解的请求。 请求处理将继续。2xx 成功。 已成功接收、理解和接受该操作。3xx 重定向。 客户端必须采取进一步操作才能完成请求。4xx 客户端错误。 可能是客户端导致的错误。 请求包含错误的语法或无法实现。5xx 服务器错误。 服务器遇到错误,无法满足请求。请务必注意,并非所有这些状态代码都被视为”错误”,有些只是信息或操作的响应,并且不需要故障排除或修正。 下面是 10 个最常见的 HTTP 状态代码及其含义。通用 HTTP 状态代码状态代码 200 = 这是成功 HTTP 请求的标准”确定”状态代码。 返回的响应取决于请求。 例如,对于 GET 请求,响应将包含在消息正文中。 对于 PUT/POST 请求,响应将包括包含操作结果的资源。状态代码 201 – 这是确认请求成功并由此创建新资源的状态代码。 通常,这是在 POST/PUT 请求后发送的状态代码。状态代码 204 = 此状态代码确认服务器已满足请求,但不需要返回信息。 此状态代码的示例包括删除请求或请求是通过表单发送的,响应不应导致刷新表单或加载新页面。状态代码 304 = 用于浏览器缓存的状态代码。 如果响应尚未修改,则客户端/用户可以继续使用相同的响应/缓存版本。 例如,如果资源已自特定时间以来被修改,浏览器可以请求。 如果没有,则发送状态代码 304。 如果已修改,则发送状态代码 200 以及资源。状态代码 400 = 服务器由于客户端错误而无法理解和处理请求。 缺少数据、域验证和无效格式是导致发送状态代码 400 的一些示例。状态代码 401 = 当需要身份验证但失败或未提供身份验证时,将发生此状态代码请求。状态代码 403 – 与状态代码 401 非常相似,状态代码 403 在发送有效请求时发生,但服务器拒绝接受。 如果客户端/用户需要必要的权限,或者他们可能需要帐户来访问资源,则会发生这种情况。 与状态代码 401 不同,身份验证将不适用于此处。状态代码 404 = 普通用户将看到的最常见状态代码。 当请求有效,但无法在服务器上找到资源时,将发生状态代码 404。 即使这些代码分组在客户端错误”存储桶”中,它们通常是由于不正确的 URL 重定向造成的。状态代码 409 – 当请求与资源的当前状态冲突时,将发送状态代码 409。 这通常是同时更新或版本相互冲突的问题。状态代码 410 = 请求的资源不再可用,并且将不再可用。 了解网络错误 410。状态代码 500 – 用户通常看到的另一个状态代码,500 系列代码类似于 400 系列代码,因为它们是真正的错误代码。 当服务器由于意外问题无法完成请求时,将发生状态代码 500。 Web 开发人员通常必须对服务器日志进行梳理,以确定问题的确切问题来自何处。
2022年06月16日
45 阅读
0 评论
0 点赞
2022-05-30
Nginx 设置 ngx_pagespeed
重新编译#进入 nginx 源码同级目录,比如在 /usr/local/src #下载模块 wget -O ngx_pagespeed-release-1.9.32.6-beta.tar.gz https://codeload.github.com/pagespeed/ngx_pagespeed/tar.gz/release-1.9.32.6-beta #解压 tar zxvf ngx_pagespeed-release-1.9.32.6-beta.tar.gz #下载psol优化库 cd ngx_pagespeed-release-1.9.32.6-beta wget https://dl.google.com/dl/page-speed/psol/1.9.32.6.tar.gz #解压psol tar zxvf 1.9.32.6.tar.gz #查看现有nginx的编译参数 /usr/local/nginx/sbin/nginx -V #重新编译nginx,新增pagespeed模块 cd /usr/local/src/nginx-1.60 ./configure --add-module=../ngx_pagespeed-release-1.9.32.6-beta 后面接上上一步查询到的参数缓存文件夹用户存放 PageSpeed 优化图片、JS、CSS等文件后的临时文件,很多教程都选择挂载在 /var 目录下,如果是一些云服务器,我倒是建议放在挂载磁盘的目录下。例如我的服务器将云盘挂载在 /data 目录下,那么,创建缓存文件,chown —R 给予的权限和 Nginx 的用户一致:mkdir /data/ngx_pagespeed/chown -R www /data/ngx_pagespeed/修改配置编辑网站的 nginx 配置文件在 server 段内添加: # on 启用,off 关闭 pagespeed on; # 重置 http Vary 头 pagespeed RespectVary on; # html字符转小写 pagespeed LowercaseHtmlNames on; # 压缩带 Cache-Control: no-transform 标记的资源 pagespeed DisableRewriteOnNoTransform off; # 相对URL pagespeed PreserveUrlRelativity on; # X-Header 值,用于判断是否生效 pagespeed XHeaderValue "Powered By fishfive.top"; # 配置服务器缓存位置和自动清除触发条件(空间大小、时限) pagespeed FileCachePath "/data/ngx_pagespeed/"; pagespeed FileCacheSizeKb 2048000; pagespeed FileCacheCleanIntervalMs 43200000; pagespeed FileCacheInodeLimit 500000; # 过滤规则 pagespeed RewriteLevel PassThrough; # 过滤WordPress的后台(可选配置,可参考使用) pagespeed Disallow "*/wp-admin/*"; pagespeed Disallow "*/wp-login.php*"; # 移除不必要的url前缀,开启可能会导致某些自动加载功能失效 #pagespeed EnableFilters trim_urls; # 移除 html 空白 pagespeed EnableFilters collapse_whitespace; # 移除 html 注释 pagespeed EnableFilters remove_comments; # DNS 预加载 pagespeed EnableFilters insert_dns_prefetch; # 压缩CSS pagespeed EnableFilters rewrite_css; # 合并CSS pagespeed EnableFilters combine_css; # 重写CSS,优化加载渲染页面的CSS规则 pagespeed EnableFilters prioritize_critical_css; # google字体直接写入html 目的是减少浏览器请求和DNS查询 pagespeed EnableFilters inline_google_font_css; # 压缩js pagespeed EnableFilters rewrite_javascript; # 合并js pagespeed EnableFilters combine_javascript; # 优化内嵌样式属性 pagespeed EnableFilters rewrite_style_attributes; # 压缩图片 pagespeed EnableFilters rewrite_images; # 不加载显示区域以外的图片 pagespeed LazyloadImagesAfterOnload off; # 图片预加载 pagespeed EnableFilters inline_preview_images; # 移动端图片自适应重置 pagespeed EnableFilters resize_mobile_images; # 图片延迟加载 pagespeed EnableFilters lazyload_images; # 雪碧图片,图标很多的时候很有用 pagespeed EnableFilters sprite_images; # 扩展缓存 改善页面资源的可缓存性 pagespeed EnableFilters extend_cache; # 将 meta 转换为 header location ~ "\.pagespeed\.([a-z]\.)?[a-z]\.[^.]\.[^.]+" location ~ "^/ngx_pagespeed_static/" location ~ "^/ngx_pagespeed_beacon$" location /ngx_pagespeed_statistics location /ngx_pagespeed_message 更多的设置文档请翻看:https://modpagespeed.com/doc/外部缓存PageSpeed 还支持以 Memcached 和 Redis 作为外部缓存,来拓展性能。Redis 支持在配置中加入:pagespeed RedisServer "host:port";设置连接失败后的等待时间,默认 1s(可选):pagespeed RedisReconnectionDelayMs timeout_in_milliseconds;Memcached 支持在配置中加入:pagespeed MemcachedServers "host:port";设置连接失败后的等待时间,默认 1s(可选):pagespeed MemcachedTimeoutUs timeout_in_microseconds;重启生效先判断是否有错误, SSH 中输入如下值后回车:nginx -t如果返回:root@MF8.BIZ:~# nginx -t nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful即可,重启以生效 PageSpeed 的设置:service nginx restart 或 systemctl restart nginx清理缓存开启 PageSpeed 后,如果原来的 CSS 和 JS 发生变动话的,不清理缓存是不会生效的,需要清理缓存的时候:touch /data/pagespeed/cache.flush运行上述语句即可, 其中 /var/cache/pagespeed/ 为配置文件中,pagespeed FileCachePath "/var/cache/pagespeed/"; 设置的目录。
2022年05月30日
75 阅读
0 评论
0 点赞
1
...
3
4
5
6