关于vue.js:iview循环Form表单验证

<template> <div v-for="(AA, index2) in list"> <div v-for="(item, index) in AA.items"> <Form :ref="'formDynamic-'+index" :model="AA" :label-width="80" style="width: 300px"> <FormItem :label="'Item ' + item.index" :prop="'items.' + index + '.value'" :rules="{required: true, message: 'Item ' + item.index +' can not be empty', trigger: 'blur'}"> <Row> <Col span="18"> <Input type="text" v-model="item.value" placeholder="Enter something..."></Input> </Col> <Col span="4" offset="1"> <Button @click="handleRemove(index)">Delete</Button> </Col> </Row> </FormItem> <FormItem> <Button type="primary" @click="handleSubmit('formDynamic-'+index,index2)">Submit</Button> </FormItem> </Form> </div> </div> <Button type="dashed" long @click="handleAdd" icon="md-add">Add item</Button></template><script> export default { data() { return { index: 0, list: [ { items: [ { value: '', index: 1, status: 1 } ] }, ], } }, methods: { handleSubmit(name,index) { console.log(index) this.$refs[name][index].validate((valid) => { if (valid) { this.$Message.success('Success!'); } else { this.$Message.error('Fail!'); } }) }, handleReset(name) { this.$refs[name].resetFields(); }, handleAdd() { this.list.push( { items: [ { value: '', index: 1, status: 1 } ] } ); }, handleRemove(index) { this.formDynamic.items[index].status = 0 } } }</script>间接复制到这里查看成果https://run.iviewui.com/ ...

September 15, 2021 · 1 min · jiezi

关于vue.js:httpscloudstarpoolcnproducthtml

https://cloud.starpool.cn/pro...

September 15, 2021 · 1 min · jiezi

关于vue.js:Vue3-TypeScript-Gin-实现后台权限管理平台

最近始终在学习 Vue3 相干的技术栈,包含 CompositionAPI 、TypeScript、vite以及Element UI 对 Vue3 的反对版本 Element Plus。 因而想要应用 Vue3 写一个简略的 RBAC 的用户权限零碎。 之前始终是应用 MySQL 这个关系型数据库,正好最近在学习 Kong 网关的时候,接触到了 Postgres 这个关系型数据库,并且还看到了一句话,说是:“MySQL 是目前应用最宽泛的数据库,然而 Postgres 是目前最先进的数据库。” 当然这个最先进也是 Postgres 的开源组织本人标榜的。然而对我来说,Postgres 貌似的确要比 MySQL 用起来爽,最起码在数据类型的反对方面,就是一个十分不错的点。 我常常会应用 Json 或者 Array 这种字段,Postgres 就反对的十分不错,当然 MySQL 5.7 当前也是反对 Json 字段的,然而从性能和应用上来说,我还感觉还是 Postgres 用的更好一些,也不排除,是我本人对 MySQL 理解不深刻。 好了,废话不多说,咱们来理论看看我的项目吧。 本我的项目不论是前后端都不会进行适度的封装,该封装的封装,不该封装的就不会进行封装,不会为了装 X 而适度封装代码,缩小大家在看代码的工夫老本。 演示站点:http://fdevops.com:8088 github:https://github.com/lanyulei/sky , 如果感觉还能够,还请不要悭吝指尖的跳动,微微点上一个 star 。 Casbin 权限管制本零碎,应用 Casbin 作为接口的权限治理依赖,应用 RBAC 的形式进行治理,反对用户的多角色绑定。 Casbin 模型文件内容如下: [request_definition]r = sub, obj, act [policy_definition]p = sub, obj, act [role_definition]g = _, _ [policy_effect]e = some(where (p.eft == allow)) [matchers]m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act其实就是 Casbin 官网上给出的 RBAC 模型内容,如果须要应用自定义函数,可参考官网自行添加即可。 ...

September 15, 2021 · 2 min · jiezi

关于vue.js:cssrequireModuleExtension和css-Modules

因为之前对css Modules不太理解,意识它还得从一个bug说起。 缘起本人搭的一个vue+ts我的项目,应用到了swiper,版本号为 "swiper": "^5.4.5","vue-awesome-swiper": "^4.1.1",按官网文档在main.ts中引入 import VueAwesomeSwiper from 'vue-awesome-swiper';import 'swiper/css/swiper.css'Vue.use(VueAwesomeSwiper /* { default options with global component } */);demo也是间接copy <template> <div class="hello"> hello <swiper ref="mySwiper" :options="swiperOptions"> <swiper-slide>Slide 1</swiper-slide> <swiper-slide>Slide 2</swiper-slide> <swiper-slide>Slide 3</swiper-slide> <swiper-slide>Slide 4</swiper-slide> <swiper-slide>Slide 5</swiper-slide> <div class="swiper-pagination" slot="pagination"></div> </swiper> </div></template><script lang="ts">import { Component, Prop, Vue } from 'vue-property-decorator';@Componentexport default class HelloWorld extends Vue { swiperOptions = { pagination: { el: '.swiper-pagination', }, cssMode: false, // Some Swiper option/callback... };}</script>而后发现swiper光有js成果,没有css款式。 于是在资源中查看 ...

September 14, 2021 · 1 min · jiezi

关于vue.js:rnish加速web的设计使用案例消息

当有客户端进行拜访varnish,varnish第一次拜访和nginx一样,进行代理转发,而后返回给客户端,然而这个时候会在varnish服务器上保留一份缓存,星池starpool 如果下次有客户端在次访问这个内容,将间接从varnish代理服务器上间接返回给客户端,而不会再次去转发。不提供给内部试⽤;SpringCloud 开发了⼀个⽀付零碎 -》做成⼀个镜像 (操作系统+软件运⾏环境+⽤户程序)创立新模块 选中模块 鼠标右键 增加框架反对 web勾选。 NAP是因特网的路由抉择档次体系中的通信替换点。https://www.starpool.cn 每个网络接入点都由一个共享替换零碎或者局域网组成,用来替换业务量。通达因特网主干线的点。

September 14, 2021 · 1 min · jiezi

关于vue.js:Antdesignvue-DatePicker-日期选择框时间选择范围限定

办法一:预选前解决(1)disableDate禁选日期 原理:当点击开展工夫选择器,会默认遍历选择器中显示的所以日期,通过date返回,咱们能够return(true/false)来判断是否可选 disableDate(date){return date<this.$moment(this.$moment(new Date()).format("YYYY-MM-DD")); //返回明天当前的日期}(2)disabledTime禁选工夫 原理:因为禁选工夫分为时、分、秒,所以须要各自返回禁选数组,time为以后抉择工夫 disabledEndTime(time){let resultlet startValue = this.form.getFieldValue(‘startTime’)function returnarray(max){var tem = [];for (var i = 0; i < max; i++){tem.push(i)}return tem}if(startValue){let hours = this.$moment(startValue).format("HH");let minutes = this.$moment(startValue).format('mm')result = {disabledHours:() => returnarray(hours)}//若选中工夫后,动静返回可选工夫(解决与开始工夫同一时的状况)if(time){let hourschosen = this.$moment(time._d).format("HH")if(hourschosen === hours){result = {disabledHours:() => returnarray(hours)disabledMinutes:() => returnarray(parseInt(minutes, 0) + 1)}} }return result;}}办法二:选中后判断watch:{endValue(val){if(this.startValue && val) {if(this.startValue > val){let vue = new Vue()this.endValue = nullvue.$message.warning("完结工夫不能小于开始工夫")}}}}

September 14, 2021 · 1 min · jiezi

关于vue.js:antdesignvue使用menu选中菜单刷新之后默认展开和选中

1.开发环境 vue2.电脑系统 windows10专业版3.在应用ant-design-vue开发的过程中,咱们在应用到menu组件的时候,选中菜单的时候刷新之后,没有默认开展和选中,上面我来分享一下办法。4.废话不多说,间接上操作: // template<a-menu :open-keys="openKeys" //要害代码 @openChange="onOpenChange" //要害代码 :default-selected-keys="defaultselectedkeys" //要害代码 :default-open-keys="defaultopenkeys" // 要害代码 mode="inline" theme="dark" :inline-collapsed="collapsed" @click="ClickMenu" //要害代码 > <template v-for="item in MenuList"> <a-menu-item v-if="!item.children" :key="item.code"> <a-icon type="pie-chart" /> <span>{{ item.name }}</span> </a-menu-item> <sub-menu v-else :key="item.code" :menu-info="item" /> </template> </a-menu>// returnrootSubmenuKeys: [], //所有路由数据(路由的name) openKeys: [], // 选中示意父级列表 路由 name defaultselectedkeys: [], // 默认选中 路由 name defaultopenkeys: [], // 选中的菜单 id// methodsClickMenu({ key}) { // console.log(key); this.defaultopenkeys[0] = "" + key; this.defaultselectedkeys[0] = key; // sessionStorage.setItem( "defaultopenkeys", JSON.stringify(this.defaultopenkeys) ); sessionStorage.setItem( "defaultselectedkeys", JSON.stringify(this.defaultselectedkeys[0]) ); this.$router.push({ name: key, }, (onComplete) => {} );},onOpenChange(openKeys) { // console.log(openKeys); console.log(this.openKeys); const latestOpenKey = openKeys.find( (key) => this.openKeys.indexOf(key) === -1 ); if (this.rootSubmenuKeys.indexOf(latestOpenKey) === -1) { console.log(openKeys); this.openKeys = openKeys; sessionStorage.setItem("openKeys", JSON.stringify(this.openKeys)); } else { console.log(openKeys); this.openKeys = latestOpenKey ? [latestOpenKey] : []; sessionStorage.setItem("openKeys", JSON.stringify(this.openKeys)); }},5.成果如下: ...

September 14, 2021 · 1 min · jiezi

关于vue.js:JS-中的变量提升的数据案例有哪些

大括号中出线了 function/let/const 等关键字,则会造成一个全新的块级公有上下文变态机制:创,遇到代码块里的 function foo,创立一个全新的块级上下文,星池starpool 申明并定义 foo如之前所说的变态机制,要把对 foo 所有操作映射到全局,所以第一步在全局上下文中申明的 foo 被定义到块级上下文中 foo 所关联的函数。前辈举荐我应用electron进行桌面利用开发,因为electron利用具备跨平台性,而且electron开发只须要应用html+css+js这一套,学习老本比拟低。 肯定要弄明确package.json这个文件。一个node我的项目的所有参数配置都在这个文件中,最好是每个标签都弄明确,开发过程中有一部分奇怪的问题都是这个文件导致的。然而electron builder打包较为简单,能设置的参数很多,网上也没有较为敌对的教程。https://www.starpool.cn 打包的时候因为网络问题也始终失败。然而electron builder打包较为简单,能设置的参数很多,网上也没有较为敌对的教程。打包的时候因为网络问题也始终失败。

September 14, 2021 · 1 min · jiezi

关于vue.js:10分钟快速实现数据双向绑定

概念双向绑定概念其实很简略,就是视图(View)的变动能实时让数据模型(Model)发生变化,而数据的变动也能实时更新到视图层。咱们所说的单向数据绑定就是从数据到视图这一方向的关系。 剖析1、响应式数据应用Object.defineProperty、Proxy对数据进行监听拦挡。 //obj:必须。指标对象//prop:必须。需定义或批改的属性的名字//descriptor:必须。指标属性所领有的个性Object.defineProperty(obj, prop, descriptor)vue3.0 开始 Proxy代替Object.defineProperty let p = new Proxy(target, handler);2、input事件监听绑定事件处理函数,实时批改数据。 3、相干dom操作将数据与相干dom节点绑定在一起,批改数据的时候对应的dom节点也应一起扭转。 实现1、html页面<!DOCTYPE html><html> <head> <meta charset="utf-8"> <title>v-model</title> </head> <body> <div id="app"> <input type="text" v-model="name" placeholder="姓名"/> <input type="text" v-model="age" placeholder="年龄"/> <div> <p> 姓名:<span>{{name}}</span> </p> <p> 年龄:<span>{{age}}</span> </p> <p> <p> 年龄:<span>{{age}}</span> </p> </p> </div> <button id="btn">批改名字</button> </div> </body> <script src="./VModel.js"></script> <script> const app = new VModel('#app',{ name:'', age:'' }); document.getElementById('btn').addEventListener('click',function() { app.setData('name','名字扭转了'); }) </script></html> 2、VModel.js(1)结构VModelclass VModel { constructor(el,data) { this.el = document.querySelector(el); //存放数据对象 this._data = data; //寄存绑定数据的dom节点 this.domPoll = {}; this.init(); }}(2)初始化数据对象应用Object.definePropertyinitData () { const _this = this; this.data = {}; for(let key in this._data){ Object.defineProperty(this.data,key,{ get(){ console.log("获取数据",key,_this._data[key]); return _this._data[key]; }, set(newVal){ console.log("设置数据",key,newVal); _this.domPoll[key].innerText = newVal; _this._data[key] = newVal; } }); }}应用ProxyinitData () { const _this = this; this.data = {}; this.data = new Proxy(this.data,{ get(target,key){ return Reflect.get(target,key); }, set(target,key,value){ // _this.domPoll[key].innerText = value; _this.domPoll[key].forEach(item => { item.innerText = value; }) return Reflect.set(target,key,value); } })}(3)绑定dom节点bindDom(el){ const childNodes = el.childNodes; childNodes.forEach(item => { //nodeType为3时该dom节点为文本节点 if(item.nodeType === 3){ const _value = item.nodeValue; if(_value.trim().length){ //匹配是否有两个花括号包裹的数据 let _isValid = /\{\{(.+?)\}\}/.test(_value); if(_isValid){ const _key = _value.match(/\{\{(.+?)\}\}/)[1].trim(); // this.domPoll[_key] = item.parentNode; //一个数据能够被多个dom节点绑定,所以应该用数组来进行保留 //未定义时先初始化 if(!this.domPoll[_key]) this.domPoll[_key] = []; this.domPoll[_key].push(item.parentNode); //替换绑定的值 item.parentNode.innerText = this.data[_key] || undefined; } } } //递归遍历子节点 item.childNodes && this.bindDom(item); })}(4)输入框数据绑定bindInput(el){ //获取input所有元素节点 const _allInput = el.querySelectorAll('input'); _allInput.forEach(input => { const _vModel = input.getAttribute('v-model'); //判断是否有v-model属性 if(_vModel){//监听输出事件 input.addEventListener('keyup',this.handleInput.bind(this,_vModel,input),false); } })}handleInput(key,input){ const _value = input.value; //数据变动的时候会同步批改dom节点绑定的数据。 this.data[key] = _value;}(4)残缺代码class VModel { constructor(el,data) { this.el = document.querySelector(el); this._data = data; this.domPoll = {}; this.init(); } init(){ this.initData(); this.initDom(); } initDom(){ this.bindDom(this.el); this.bindInput(this.el); console.log('domPoll',this.domPoll); } initData () { const _this = this; this.data = {}; // for(let key in this._data){ // Object.defineProperty(this.data,key,{ // get(){ // console.log("获取数据",key,_this._data[key]); // return _this._data[key]; // }, // set(newVal){ // console.log("设置数据",key,newVal); // _this.domPoll[key].innerText = newVal; // _this._data[key] = newVal; // } // }); // } this.data = new Proxy(this.data,{ get(target,key){ return Reflect.get(target,key); }, set(target,key,value){ // _this.domPoll[key].innerText = value; _this.domPoll[key].forEach(item => { item.innerText = value; }) return Reflect.set(target,key,value); } }) } bindDom(el){ const childNodes = el.childNodes; childNodes.forEach(item => { if(item.nodeType === 3){ const _value = item.nodeValue; if(_value.trim().length){ let _isValid = /\{\{(.+?)\}\}/.test(_value); if(_isValid){ const _key = _value.match(/\{\{(.+?)\}\}/)[1].trim(); // this.domPoll[_key] = item.parentNode; if(!this.domPoll[_key]) this.domPoll[_key] = []; this.domPoll[_key].push(item.parentNode); item.parentNode.innerText = this.data[_key] || undefined; } } } item.childNodes && this.bindDom(item); }) } bindInput(el){ const _allInput = el.querySelectorAll('input'); _allInput.forEach(input => { const _vModel = input.getAttribute('v-model'); if(_vModel){ input.addEventListener('keyup',this.handleInput.bind(this,_vModel,input),false); } }) } handleInput(key,input){ const _value = input.value; this.data[key] = _value; // console.log(this.data); } setData(key,value){ this.data[key] = value; }} ...

September 13, 2021 · 2 min · jiezi

关于vue.js:知识付费对接微信公众号订阅消息

订阅告诉是一个用户被动订阅、服务号按需下发的告诉能力。应用过程请恪守《微信公众平台服务协定》《微信公众平台经营标准》。 用户在前述场景被动订阅后,服务号可通过接口向用户发送订阅告诉,如信用卡动账揭示、物流到货告诉等。订阅告诉分为一次性订阅和长期订阅,一次性订阅是指用户订阅一次,服务号可不限工夫公开发一条对应的订阅告诉;长期订阅是指用户订阅一次,服务号可长期屡次下发告诉,长期订阅告诉仅向政务民生、医疗等公共服务畛域凋谢。微信订阅音讯和微信模版音讯的区别在于模版音讯能够忽视用户的主观意识,间接发给用户;而订阅音讯则必须用户批准零碎能力发给用户。这样防止了给用户带来的骚扰。因为微信有打算应用订阅音讯来取代模版音讯,所以常识付费新增了订阅音讯性能,常识付费零碎能够抉择应用模版音讯还是订阅音讯。上面咱们就来看看常识付费是如何加订阅音讯性能的。通过微信文档,咱们能够看到微信公众号的订阅音讯是通过wx-open-subscribe标签来调起抉择的。如下事例:<wx-open-subscrib template="TenvU22BA1jCp4YHfYEpRuESXYReQyDuhs4vbdWA99I" id="subscribe-btn"> <script type="text/wxtag-template" slot="style"> <style> .subscribe-btn { color: #fff; background-color: #07c160; }</style></script> <script type="text/wxtag-template"> <button class="subscribe-btn"> 一次性模版音讯订阅 </button></script></wx-open-subscribe><script>var btn = document.getElementById('subscribe-btn');btn.addEventListener('success', function (e) { console.log('success', e.detail);}); btn.addEventListener('error',function (e) { console.log('fail', e.detail);});</script>依据事例咱们能够理解应用wx-open-subscribe标签给template属性传入你须要弹出的订阅音讯模板ID即可,多个以英文逗号分隔。常识付费是如何实现的呢?首先是前端显示,在常识付费调起领取,抉择微信领取而后调起微信订阅音讯的弹窗,我的项目根目录中public/wap/first/zsff/components/payment下的index.html中咱们退出订阅音讯的标签,传入须要的模板ID。<wx-open-subscribe v-if="isWechat && templateId":template="templateId"@success="onSuccess"@error="onError"<script type="text/wxtag-template" slot="style"> <style> button { display: block; width: 100%; height: 40px; border-radius: 20px; border: none; background-color: #2c8eff; font-family: inherit; font-weight: normal; font-size: 14px; color: #fff; } </style></script><script type="text/wxtag-template"> <button>立刻领取</button></script></wx-open-subscribe> 用户抉择实现后执行订单性能,后盾依照用户的抉择后果发送相应的订阅音讯。 常识付费根目录下extend/service中的RoutineTemplateService类中加有对于微信公众号订阅音讯的全副接口,sendTemplate办法就是给用户发送订阅音讯的办法,通过这个办法咱们能够发送各种订阅音讯。常识付费根目录application\wap\model\routine中的RoutineTemplate类里是各个订阅音讯的发送办法。如下是专题购买胜利发送的订阅音讯,其中$data中的所有索引要和微信公众号平台中加的订阅音讯内容统一。$data['character_string1']['value'] = $orderId;$data['amount3']['value'] = $order['pay_price'];$data'time2' = date('Y-m-d H:i:s',time());$data'thing6' = '您购买的专题已领取胜利!';RoutineTemplate::sendOrderSuccess($data,$order['uid'],$site_url . Url::build('wap/special/grade_list')); ...

September 13, 2021 · 1 min · jiezi

关于vue.js:前后端分离-Vue-Eggjs-Mysql-的JS全栈实践动态菜单RBAC权限模型WebSocket实现站内信

Beehive前言Beehive 是一个我的项目管理系统。参考于Teambetion、PearProject,实现局部性能。 这是一个Vue+Node.js的js全栈我的项目。基于RBAC模型做权限管制,动静配置菜单,前端实现页面元素级别的权限管制。通过WebSocket实现站内信性能,工作看板中,实现更新同步推送。一旦其余我的项目成员有对咱们以后查看的我的项目工作做任何的操作,页面都将立刻同步更新,并向此工作的所有参与者(除了操作者)发送音讯告诉。注册和找回明码须要通过邮箱验证码验证,能够通过github受权登陆(不是很稳固)。 Node.js框架选用的是Egg.js,配合sequelize,本人写了一个小工具。能够通过填写表字段的配置,执行npm run generator-entity主动生成一整套文件,包含Swagger、数据校验validate、Sequelize须要的model、controller、service、router。并主动创立数据库表,包含每个字段的类型、长度、是否能为空、默认值、正文、索引、甚至是外键都能搞定。因为加了权限管制,所以还要到前端的资源管理中增加一下新增的资源,并在角色中点选调配一下,就实现了一张表的CRUD了,包含新增、批改、详情、批量删除、分页列表。当然这还是有很多能够优化的空间的,但也根本够用了。为了优化鉴权耗费,以及满足WebSocket的可靠性设计须要,零碎引入Redis做缓存。 明码是加盐存储的,且在传输过程中应用了RSA做了非对称加密。Jwt认证应用Access Token + Refresh Token,配合黑名单。 成果演示预公布环境:超级管理员账号:test-super ,明码:test-super123 预公布环境地址:beehives.imfdj.top 预公布环境:普通用户账号:test-user ,明码:test-user123 生产环境:普通用户账号:test-user ,明码:test-user123 生产环境地址:beehive.imfdj.top 技术栈前端:Vue2全家桶 + Element-ui + Axios + Vue-socket.io + Sass 前端我的项目github地址 后端:Egg.js + Sequelize + Jwt + Mysql + Redis + Socket + Swagger 后端我的项目github地址 阐明如果对您有帮忙,您能够点右上角 "Star" 反对一下 谢谢! ^_^ 或者您能够 "follow" 一下,我会一直开源更多的乏味的我的项目。如:Vue3 + NestJS + TypeScript ✨ 如有问题请间接在 Issues 中提,或者您发现问题并有十分好的解决方案,欢送 PR 指标性能[x] 登录、注册 -- 实现[x] github受权登录 -- 实现[x] 找回明码 -- 实现[x] 滑块验证 -- 实现[x] 邮箱验证 -- 实现[x] 动静首页 -- 实现[x] 集体设置 -- 实现[x] 用户治理 -- 实现[x] 角色治理 -- 实现[x] 菜单治理 -- 实现[x] 资源管理 -- 实现[x] 操作日志 -- 实现[x] 动静菜单 -- 实现[x] 部门治理 -- 实现[x] 我的项目列表 -- 实现[x] 工作看板 -- 实现[x] 工作列表 -- 实现[x] 我的项目文件 -- 实现[x] 我的项目概览 -- 实现[x] 我的项目成员 -- 实现[x] 我的项目邀请 -- 实现[x] 我的项目设置 -- 实现[x] 我的项目回收站 -- 实现[x] 工作筛选 -- 实现[x] 工作详情 -- 实现[x] 工作标签 -- 实现[x] 工作参与者 -- 实现[x] 工作动静 -- 实现[x] 工作工时 -- 实现[x] 工作关联文件 -- 实现[x] 工作更新即时同步 -- 实现[x] 公开我的项目的业务权限管制(非我的项目成员不可编辑我的项目) -- 实现[x] 我的项目模板 -- 实现[x] 音讯揭示 -- 实现[x] 工作台 -- 实现[x] 站内信 -- 实现[x] 页面元素权限管制 -- 实现[ ] 我的项目版本 -- 待开发[ ] 我的项目日程 -- 待开发局部截图 ...

September 13, 2021 · 2 min · jiezi

关于vue.js:VueExpressMysql全栈项目之增删改查分页排序导出表格功能

本文记录一下实现一个全栈小我的项目,前端应用vue框架、后端应用express框架、数据库应用mysql。产品需要剖析产品经理说,我须要做一个web人员治理我的项目,我的项目蕴含以下性能: 用户能够在页面上新建数据,新建的数据内容有,人名、年龄、他乡、以及此人的备注用户能够删除之前新建的人员信息,删除只做逻辑删除,不做物理删除(不删数据库数据)用户能够批改之前新建的人员信息,人名、年龄、他乡、备注均能够批改页面中要有一个输入框做含糊搜寻性能,比方用户搜寻一个“海”这个字,所有和这个字关联的数据都要出现筛选进去。无论人名、他乡、还是备注中蕴含了这个字。用户能够勾选行,做excel导出性能应用表格出现数据,做分页治理、所有字段都反对排序说白了,就是增删改查分页排序导出性能。咱们理解需要当前,就能够设计数据库表、以及字段了预览一下效果图开始设计数据库之前,咱们先预览最终的效果图,最终的前后端代码以及mysql表,文末会给到Gitee的地址,欢送大家下载瞅瞅看看 数据库设计这里因为性能比较简单,所以咱们设计一张表即可,在一张表里存储我的项目中须要的字段即可。当然建表之前,咱们要先新建一个数据库。新建数据库之前,咱们要连贯本人的MySql数据库。这里我用的是Navicat工具治理Mysql数据库。 连贯数据库 新建数据库数据库的名字叫做person_manage,后续express中配置数据库连接池须要用到。 新建表(新建字段)新建的这张表的名字是people_table 这里就不赘述,Mysql的下载和Navcat的下载了。大家谷歌(百度)一下,有很多下载安装的教程的。前端页面开发前端应用vue我的项目,所以咱们从0到1简略搭建一个vue我的项目。 提前装置好node软件、node中自带npm node -v和npm-v别离查看对应版本号,我的版本别离是v12.18.0和6.14.4配置淘宝镜像 npm install -g cnpm –registry=https://registry.npm.taobao.org,这样下载包的时候会快一些应用vue-cli疾速生成一个vue我的项目架构。其实vue-cli脚手架其实也是一个npm包,所以要应用npm下载vue-cli脚手架,执行命令npm install -g @vue/cli全局下载vue-cli脚手架,执行命令vue -V能够查看相应的vue-cli的版本号最初执行vue create 我的项目名创立一个我的项目,这里我创立的是mydemo我的项目,即vue create mydemo我的项目创立好了当前,咱们还须要下载axios用法发申请,下载vue-router用来做路由,下载element-ui用来疾速开发,下载nprogress做个进度条等相干插件包。而后引入并应用这些包,最初咱们依照我的项目须要,批改一下我的项目的目录,最终是上面的构造。 前端我的项目结构图 接着贴出各个文件夹代码api文件夹局部axios的二次封装(api.js)// 引入axios包,并创立一个axios实例import axios from "axios"; const http = axios.create({})import NProgress from 'nprogress' // 引入NProgress 当然,要提前npm下载一下import 'nprogress/nprogress.css' // 引入NProgress和对应的款式NProgress.configure({ showSpinner: false }) // 暗藏右侧的旋转进度条// 给这个axios实例加上申请拦截器和相应拦截器//申请拦挡http.interceptors.request.use( (config) => { NProgress.start() // 开启进度条 return config; }, (err) => { return Promise.reject(err); })//响应拦挡http.interceptors.response.use( (res) => { NProgress.done() // 敞开进度条 return res.data }, (error) => { NProgress.done() // 敞开进度条 return Promise.reject(error); })/* 裸露一个函数,在函数中应用刚创立的且加上拦截器的axios实例去发申请。 因为发申请须要指定申请形式、申请地址、申请参数、申请头、响应类型等相干信息 所以,须要接管相应的method、url、data、headers、responseType等参数 这里最终裸露的api函数是留给lss/index.js文件中定义的接口函数应用的*/ export default (method, url, data = null, headers = 'application/json;charset=UTF-8', responseType) => { if (method == "post") { return http({ method: 'post', url: url, data: data, headers: { 'Content-Type': headers, }, responseType: responseType }); } else if (method == "get") { return http({ method: 'get', url: url, params: data, headers: { 'Content-Type': headers }, responseType: responseType }); } else if (method == "put"){ // ...... return; } else if (method == "delete"){ // ..... return; } }axios的二次封装当前,咱们须要在接口函数中引入,并定义接口函数接口函数的定义(lss/index.js)// 导入http中创立的axios实例import http from '../api';export const getTableData = (params) => http("get", `/api/getTableData`,params)//分页查问获取人物信息export const getTotalCount = () => http("get", `/api/getTotalCount`)//分页查问获取人物信息总条目数export const deleteData = (params) => http("get", `/api/deleteData`,params)//删除人物信息export const addData = (params) => http("post", `/api/addData`,params)//新增人物信息export const editData = (params) => http("post", `/api/editData`,params)//编辑人物信息export const exportExcel = (params) => http("post", `/api/exportExcel`,params,'application/json; charset=UTF-8',"arraybuffer")//导出表格数据接口函数定义了当前,咱们在(api/index.js)文件中做对立治理,裸露进来,在main.js文件中再引入最终注册到Vue的原型链下来。接口函数的对立治理并裸露(api/index.js)// 对立治理理一下申请接口import { getTableData, getTotalCount, deleteData, addData, editData, exportExcel,} from './lss/index'export default { getTableData, getTotalCount, deleteData, addData, editData, exportExcel,}main.js中将接口函数注册到Vue原型上看倒数第五行、倒数第六行import Vue from 'vue'import App from './App.vue'Vue.config.productionTip = false // 敞开提醒import "./assets/css/reset.css"; //引入重置样式表import ElementUI from 'element-ui'; //引入饿了么包import 'element-ui/lib/theme-chalk/index.css'; //引入饿了么款式Vue.use(ElementUI); // 应用饿了么UIimport router from './router/index' // 引入路由import api from "./api/index" // 引入封装的axios的接口函数Vue.prototype.$api = api // 将封装的axios接口函数注册到原型链上new Vue({ render: h => h(App), router // 挂载路由}).$mount('#app')assets文件夹局部assets文件夹寄存的个别是动态资源文件,比方重置样式表,比方404页面的图片等。当然重置样式表,也是要在main.js中引入并应用的。这里就不赘述了 ...

September 12, 2021 · 7 min · jiezi

关于vue.js:Vuev2614源码解毒二初始化和挂载

初始化流程new Vue咱们在应用 Vue 的时候,首页就是先 new Vue(...) ;在上一章中通过剖析构建流程,咱们得出入口文件 src/platforms/web/entry-runtime-with-compiler.js ,通过入口文件,咱们一步一步找到 Vue 构造函数定义所在: // src/platforms/web/entry-runtime-with-compiler.js// ...import Vue from './runtime/index'// ...// src/platforms/web/runtime/index.jsimport Vue from 'core/index'// ...// src/core/index.jsimport Vue from './instance/index'import { initGlobalAPI } from './global-api/index'import { isServerRendering } from 'core/util/env'import { FunctionalRenderContext } from 'core/vdom/create-functional-component'// 初始化全局 APIinitGlobalAPI(Vue)// ...// src/core/instance/index.jsimport { initMixin } from './init'import { stateMixin } from './state'import { renderMixin } from './render'import { eventsMixin } from './events'import { lifecycleMixin } from './lifecycle'import { warn } from '../util/index'// Vue 构造函数function Vue (options) {  if (process.env.NODE_ENV !== 'production' &&    !(this instanceof Vue)  ) {    warn('Vue is a constructor and should be called with the `new` keyword')  }  // 调用 Vue.prototype_init 办法,该办法是在 initMixin 中定义的  this._init(options)}// 定义 Vue.prototype_init 办法initMixin(Vue)/** * 定义: *   Vue.prototype.$data *   Vue.prototype.$props *   Vue.prototype.$set *   Vue.prototype.$delete *   Vue.prototype.$watch */stateMixin(Vue)/** * 定义 事件相干的 办法: *   Vue.prototype.$on *   Vue.prototype.$once *   Vue.prototype.$off *   Vue.prototype.$emit */eventsMixin(Vue)/** * 定义: *   Vue.prototype._update *   Vue.prototype.$forceUpdate *   Vue.prototype.$destroy */lifecycleMixin(Vue)/** * 定义: *   Vue.prototype.$nextTick *   Vue.prototype._render */renderMixin(Vue)export default Vue_init// src/core/instance/init.jsexport function initMixin (Vue: Class<Component>) {  Vue.prototype._init = function (options?: Object) {    const vm: Component = this    // 每个实例都保留一个 _uid    vm._uid = uid++    let startTag, endTag    /* istanbul ignore if */    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {      startTag = `vue-perf-start:${vm._uid}`      endTag = `vue-perf-end:${vm._uid}`      mark(startTag)    }    // a flag to avoid this being observed    vm._isVue = true    // 解决组件配置项    if (options && options._isComponent) {      // 每个子组件初始化时走这里,这里只做了一些性能优化      // 将组件配置对象上的一些深层次属性放到 vm.$options 选项中,以进步代码的执行效率      initInternalComponent(vm, options)    } else {      // 合并选项,合并默认选项和自定义选项      vm.$options = mergeOptions(        resolveConstructorOptions(vm.constructor),        options || {},        vm      )    }    // 设置代理,将 vm 实例上的属性代理到 vm._renderProxy    /* istanbul ignore else */    if (process.env.NODE_ENV !== 'production') {      initProxy(vm)    } else {      vm._renderProxy = vm    }    // expose real self    vm._self = vm    // 初始化实例关系属性,$parent、$children、$refs、$root等    initLifecycle(vm)    // 初始化自定义事件,解决父组件传递的事件和回调    initEvents(vm)    // 解析组件的插槽信息,失去 vm.$slot,解决渲染函数(_render),失去 vm.$createElement 办法,即 h 函数    initRender(vm)    // 调用 beforeCreate 钩子函数    callHook(vm, 'beforeCreate')    // 初始化组件的 inject 配置项,失去 result[key] = val 模式的配置对象,而后对后果数据进行响应式解决,并代理每个 key 到 vm 实例    initInjections(vm)    // 数据响应式外围,解决 props、methods、data、computed、watch    initState(vm)    // 解析组件配置项上的 provide 对象,将其挂载到 vm._provided 属性上    initProvide(vm) // resolve provide after data/props    // 调用 created 钩子函数    callHook(vm, 'created')    /* istanbul ignore if */    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {      vm._name = formatComponentName(vm, false)      mark(endTag)      measure(`vue ${vm._name} init`, startTag, endTag)    }    if (vm.$options.el) {      vm.$mount(vm.$options.el)    }  }}下面代码很清晰的看出初始化都做了哪些事件,在初始化的最初,如果有 el 属性,则会主动调用 vm.$mount 进行挂载,否则咱们就须要手动调用 $mount。接下里就进入了挂载阶段。 Vue 实例挂载$mount入口文件 src/platforms/web/entry-runtime-with-compiler.js : /* @flow */import config from 'core/config'import { warn, cached } from 'core/util/index'import { mark, measure } from 'core/util/perf'import Vue from './runtime/index'import { query } from './util/index'import { compileToFunctions } from './compiler/index'import { shouldDecodeNewlines, shouldDecodeNewlinesForHref } from './util/compat'const idToTemplate = cached(id => {  const el = query(id)  return el && el.innerHTML})/** * 编译器的入口 * 进行预编译,最终将模版编译成 render 函数 */// 缓存原型上的办法const mount = Vue.prototype.$mount// 从新定义该办法Vue.prototype.$mount = function (  el?: string | Element,  hydrating?: boolean): Component {  el = el && query(el)  // 不能挂载在 body、html 这样的根节点上  if (el === document.body || el === document.documentElement) {    process.env.NODE_ENV !== 'production' && warn(      `Do not mount Vue to <html> or <body> - mount to normal elements instead.`    )    return this  }  const options = this.$options  /**   *   若没有 render办法,则解析 template 和 el,并转换为 render 函数   *   优先级:render > template > el   */  if (!options.render) {    let template = options.template    // template    if (template) {      if (typeof template === 'string') {        if (template.charAt(0) === '#') {          // { template: '#app' },以 id 为 ‘app’ 的节点,作为挂载节点          template = idToTemplate(template)          /* istanbul ignore if */          if (process.env.NODE_ENV !== 'production' && !template) {            warn(              `Template element not found or is empty: ${options.template}`,              this            )          }        }      } else if (template.nodeType) {        // template 是一个失常的元素,获取其 innerHtml 作为模版        template = template.innerHTML      } else {        if (process.env.NODE_ENV !== 'production') {          warn('invalid template option:' + template, this)        }        return this      }    } else if (el) {      // el      template = getOuterHTML(el)    }    if (template) {      /* istanbul ignore if */      if (process.env.NODE_ENV !== 'production' && config.performance && mark) {        mark('compile')      }      // 编译模版,失去动静渲染函数和动态渲染函数      const { render, staticRenderFns } = compileToFunctions(template, {        // 在非生产环境下,编译时记录标签属性在模版字符串中开始和完结的地位索引        outputSourceRange: process.env.NODE_ENV !== 'production',        shouldDecodeNewlines,        shouldDecodeNewlinesForHref,        // 界定符,默认 {{}}        delimiters: options.delimiters,        // 是否保留正文        comments: options.comments      }, this)      // 将两个渲染函数放到 this.$options 上      options.render = render      options.staticRenderFns = staticRenderFns      /* istanbul ignore if */      if (process.env.NODE_ENV !== 'production' && config.performance && mark) {        mark('compile end')        measure(`vue ${this._name} compile`, 'compile', 'compile end')      }    }  }  // 调用原型上办法挂载  return mount.call(this, el, hydrating)}/** * Get outerHTML of elements, taking care * of SVG elements in IE as well. */function getOuterHTML (el: Element): string {  if (el.outerHTML) {    return el.outerHTML  } else {    const container = document.createElement('div')    container.appendChild(el.cloneNode(true))    return container.innerHTML  }}Vue.compile = compileToFunctionsexport default Vue从下面代码能够看出,不论定义 render 办法还是 el 和 template 属性,最终的目标就是失去 render 渲染函数。而后保留在 options 上。 编译模板,失去 render 渲染函数,通过调用 compileToFunctions 办法,这个到编译器的时候再一块看。 最初调用原型上的 $mount ,定义在 src/platform/web/runtime/index.js 中 Vue.prototype.$mount = function (  el?: string | Element,  hydrating?: boolean): Component {  el = el && inBrowser ? query(el) : undefined  return mountComponent(this, el, hydrating)}理论调用 mountComponent ,定义在 src/core/instance/lifecycle.js mountComponent// src/core/instance/lifecycle.jsexport function mountComponent (  vm: Component,  el: ?Element,  hydrating?: boolean): Component {  vm.$el = el  if (!vm.$options.render) {    vm.$options.render = createEmptyVNode    if (process.env.NODE_ENV !== 'production') {      /* istanbul ignore if */      if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||        vm.$options.el || el) {        warn(          'You are using the runtime-only build of Vue where the template ' +          'compiler is not available. Either pre-compile the templates into ' +          'render functions, or use the compiler-included build.',          vm        )      } else {        warn(          'Failed to mount component: template or render function not defined.',          vm        )      }    }  }  callHook(vm, 'beforeMount')  let updateComponent  /* istanbul ignore if */  if (process.env.NODE_ENV !== 'production' && config.performance && mark) {    updateComponent = () => {      const name = vm._name      const id = vm._uid      const startTag = `vue-perf-start:${id}`      const endTag = `vue-perf-end:${id}`      mark(startTag)      const vnode = vm._render()      mark(endTag)      measure(`vue ${name} render`, startTag, endTag)      mark(startTag)      vm._update(vnode, hydrating)      mark(endTag)      measure(`vue ${name} patch`, startTag, endTag)    }  } else {    // 执行 vm._render() 函数,失去 虚构 DOM,并将 vnode 传递给 _update 办法,接下来就该到 patch 阶段了    updateComponent = () => {      vm._update(vm._render(), hydrating)    }  }  // we set this to vm._watcher inside the watcher's constructor  // since the watcher's initial patch may call $forceUpdate (e.g. inside child  // component's mounted hook), which relies on vm._watcher being already defined  new Watcher(vm, updateComponent, noop, {    before () {      if (vm._isMounted && !vm._isDestroyed) {        callHook(vm, 'beforeUpdate')      }    }  }, true /* isRenderWatcher */)  hydrating = false  // vm.$vnode 示意 Vue 实例的父虚构 Node,所以它为 Null 则示意以后是根 Vue 的实例  if (vm.$vnode == null) {    vm._isMounted = true    callHook(vm, 'mounted')  }  return vm}mountComponent 内定义了 updateComponent 办法,而后实例化一个Watcher,同时将 updateComponent 作为参数传入,在 Watcher 的回调函数中被调用。Watcher 在这里次要是初始化和数据变动时,执行回调函数。 ...

September 12, 2021 · 1 min · jiezi

关于vue.js:Vue微信开发中微信分享的优雅实现

前言微信分享次要是能够把咱们做的网页分享给好友或者分享到朋友圈,在发送给好友时,展现进去的音讯不是一段很丑的网址,而是带着图文形容的非凡模板音讯,很多流传性质比拟强的网页都会借助这个个性晋升流传性。要实现这个性能,须要咱们接入微信的JS-SDK,JS-SDK是什么呢?官网文档介绍如下: 微信JS-SDK是微信公众平台 面向网页开发者提供的基于微信内的网页开发工具包。通过应用微信JS-SDK,网页开发者可借助微信高效地应用拍照、选图、语音、地位等手机零碎的能力,同时能够间接应用微信分享、扫一扫、卡券、领取等微信特有的能力,为微信用户提供更优质的网页体验。能够看到,JS-SDK能做很多事件,那么明天,咱们先探讨对于微信分享的细节。如果你还须要实现微信受权登录相干的性能,那么能够查看笔者的这篇分享:Vue微信开发中受权登录的优雅实现 筹备第一步,不必多说,同样是先熟读一遍官网文档,文档地址如下:微信开发JS-SDK应用阐明文档这里须要非凡阐明的是,在开发之前,须要先登录微信公众平台进入“公众号设置”的“性能设置”里填写“JS接口平安域名”。这个工作肯定不能忘,切记切记。应用JS-SDK还有一个要害的环节,那就是通过config接口注入权限验证配置,而配置中有个signature参数是须要借助服务端获取的,所以咱们开发时仍然须要跪舔一波后端搭档给予反对(全栈的看官就当我没说)。看完文档的看官们,应该都能梳理进去咱们接入微信分享的具体流程了,笔者梳理如下: 引入JS-SDK;通过调用后端接口获取签名,签名算法见该页;调用wx.config办法注入相干配置;调用相干的分享的api,传入对应的分享信息; 实现这里笔者以实现一个微信漂流瓶性能为例,分享一下编码过程;技术栈采纳的是Vue3+typescript,Vue2的开发者还请依据状况做适当调整。 引入JS-SDK官网文档的形容是引入js-sdk文件,即通过script形式引入,但咱们当初是Vue利用,那么能不能通过npm仓库装置通过ESModule形式引入呢?当然是能够的,装置命令如下: // js版本yarn add weixin-js-sdk// ts版本yarn add weixin-js-sdk-ts笔者这里装置的是ts版本, 装置完后,package.json中显示了装置的版本 封装模块本着性能解耦准则和不便复用,笔者决定独自新建一个文件,专门封装js-sdk相干的性能。在vue3中,当然是封装成hook啦。所以,咱们在hooks目录下,新建了一个useWxSDK.ts文件;而后,开始封装第一个办法,即wx.config /** * 初始化设置 */function initConfig(configInfo) { return new Promise((resolve) => { wx.config({ debug: false, appId: configInfo.appId, timestamp: configInfo.timestamp, nonceStr: configInfo.nonceStr, signature: configInfo.signature, jsApiList: configInfo.jsApiList ?? [ 'chooseImage', 'uploadImage', 'previewImage', 'onMenuShareTimeline', 'onMenuShareAppMessage', 'chooseWXPay', ], openTagList: [], }) wx.ready(() => { resolve(true) }) })}这里阐明一下wx.config办法,为啥要封装为Promise呢?其实能够看到JS-SDK的API设计是比拟原始的,咱们须要通过wx.ready去注册配置胜利后的回调函数,笔者在这里用Promise封装,在ready回调函数里调用Promise.resolve,那么咱们在用的时候,就能够通过优雅的then语法来实现配置胜利后的操作啦! 接下来封装分享好友和微信朋友圈的办法: /** 设置微信分享 */function setShareInfo(shareInfo,onSuccess, onCancel) { wx.onMenuShareTimeline({ title: shareInfo.title, // 分享题目 link: shareInfo.link, // 分享链接,能够不是以后页面,该链接域名或门路必须与当前页面对应的公众号JS平安域名统一 imgUrl: shareInfo.imgUrl, success: function () { // 用户确认分享后执行的回调函数 onSuccess() }, cancel: function () { onCancel() // 用户勾销分享后执行的回调函数 }, }) wx.onMenuShareAppMessage({ title: shareInfo.title, // 分享题目 desc: shareInfo.desc, link: shareInfo.link, // 分享链接,能够不是以后页面,该链接域名或门路必须与当前页面对应的公众号JS平安域名统一 imgUrl: shareInfo.imgUrl, type: 'link', // 分享类型,music、video或link,不填默认为link success: function () { // 用户确认分享后执行的回调函数 onSuccess() }, cancel: function () { // 用户勾销分享后执行的回调函数 onCancel() }, })}接下来,开始搭积木。因为思考到分享这个操作,也是会在多个页面复用,那么有必要再形象一个分享的办法,这样在须要用到微信分享的中央间接调用该办法就行。因而,笔者又新建了一个useWxShare.ts文件 ...

September 11, 2021 · 2 min · jiezi

关于vue.js:echarts折线统计图实现虚实结合

1、需要场景: 实现折线统计图,如果蕴含明天,明天用虚线示意,其余用实线2、实现思路: 将一条线进行切割,如明天是20210909,从20210901到20210909的区间将其宰割为两段为20210901-20210908,20210908-202109093、具体代码实现: //明天设为虚线的办法//data须要解决的y轴显示数据,dateData为x轴数据-日期setDottedLine(data,dateData){ //明天 let nowTime = new Date() let chooseYear = nowTime.getFullYear() let chooseMonth = nowTime.getMonth() + 1 let chooseDate = nowTime.getDate() if (chooseMonth < 10) chooseMonth = '0' + chooseMonth if (chooseDate < 10) chooseDate = '0' + chooseDate let today = `${chooseYear}-${chooseMonth}-${chooseDate}`; //昨天 let oneday = new Date(nowTime - 1 * 24 * 3600 * 1000); let chooseYearonedayago = oneday.getFullYear() let chooseMonthonedayago = oneday.getMonth() + 1 let chooseDateonedayago = oneday.getDate() if (chooseMonthonedayago < 10) chooseMonthonedayago = '0' + chooseMonthonedayago if (chooseDateonedayago < 10) chooseDateonedayago = '0' + chooseDateonedayago let yesterday = `${chooseYearonedayago}-${chooseMonthonedayago}-${chooseDateonedayago}`; if(dateData.includes(today)){ let newObj = {} let arraySolid = []; let arrayDotted = []; for(let i=0; i<dateData.length; i++){ if(dateData[i]==yesterday){ arraySolid.push(data[i]) arrayDotted.push(data[i]) }else if(dateData[i]==today){ arraySolid.push(null) arrayDotted.push(data[i]) }else{ arraySolid.push(data[i]) arrayDotted.push(null) } } this.$set(newObj,'arraySolid',arraySolid) //实线 this.$set(newObj,'arrayDotted',arrayDotted) //虚线 return newObj }else{ return data }}//调用 ...

September 10, 2021 · 2 min · jiezi

关于vue.js:Vue-插件引用-elementui-sass

element-ui装置: npm i element-ui -S /* main.js */import ElementUI from 'element-ui';import 'element-ui/lib/theme-chalk/index.css';Vue.use(ElementUI);sass装置指定版本: npm install sass-loader --save-dev装置node-sass(指定版本): npm i -D node-sass@4.12.0sass-loade卸载: npm uninstall sass-loade 如果报错:gyp verb check python checking for Python executable "python2" in the PATHgyp verb check python checking for Python executable "python" in the PATH 次要是windows平台短少编译环境 先运行: npm install -g node-gyp而后运行:运行 npm install --global --production windows-build-tools 能够主动装置跨平台的编译器:gym还是报错 就看看版本是不是对应的上 装置指定版本 npm i -D node-sass@4.12.0npm i -D sass-loader@7.1.0装置后应用要在build/webpack.base.conf.jsde里的在module的rules中插入以下代码增加配置: ...

September 10, 2021 · 1 min · jiezi

关于vue.js:Vue3-预览图片和视频

我的项目中遇到一组数据既有可能是图片,也有可能是视频,须要同时预览的状况,搜了一下,找到了vue-gallery,试了一下之后发现没法在VUE3下没法用,不晓得是真的齐全没法用,还是因为我用的Composition API才没法用,没去纠结。 没找到其余的,只好自力更生,然而也没有齐全自力更生。我留意到了Element Plus的Image组件是能够大图预览的,毕竟Element Plus是开源的,只有略微改一下,对图片和视频资源做一个判断,而后别离显示img和video不就能够了。于是我找到了Element Plus的image-viewer的源码,做了一下批改,外围的批改中央如下面所说的,加了判断和video <div class="el-image-viewer__canvas"> <img v-for="(url, i) in urlList" v-show="i === index && isImage" ref="media" :key="url" :src="url" :style="mediaStyle" class="el-image-viewer__img" @load="handleMediaLoad" @error="handleMediaError" @mousedown="handleMouseDown" /> <video controls="controls" v-for="(url, i) in urlList" v-show="i === index && isVideo" ref="media" :key="url" :src="url" :style="mediaStyle" class="el-image-viewer__img" @load="handleMediaLoad" @error="handleMediaError" @mousedown="handleMouseDown" ></video></div>而后把图片预览的相干操作比方放大放大旋转等工具条在视频的时候给暗藏,把Element Plus的局部ts语法改成js,局部工具函数给拿进去,事件函数on和off给重写下,就完事了,残缺代码如下 <template> <transition name="viewer-fade"> <div ref="wrapper" :tabindex="-1" class="el-image-viewer__wrapper" :style="{ zIndex }" > <div class="el-image-viewer__mask" @click.self="hideOnClickModal && hide()" ></div> <!-- CLOSE --> <span class="el-image-viewer__btn el-image-viewer__close" @click="hide" > <i class="el-icon-close"></i> </span> <!-- ARROW --> <template v-if="!isSingle"> <span class="el-image-viewer__btn el-image-viewer__prev" :class="{ 'is-disabled': !infinite && isFirst }" @click="prev" > <i class="el-icon-arrow-left"></i> </span> <span class="el-image-viewer__btn el-image-viewer__next" :class="{ 'is-disabled': !infinite && isLast }" @click="next" > <i class="el-icon-arrow-right"></i> </span> </template> <!-- ACTIONS --> <div v-if="isImage" class="el-image-viewer__btn el-image-viewer__actions" > <div class="el-image-viewer__actions__inner"> <i class="el-icon-zoom-out" @click="handleActions('zoomOut')" ></i> <i class="el-icon-zoom-in" @click="handleActions('zoomIn')" ></i> <i class="el-image-viewer__actions__divider"></i> <i :class="mode.icon" @click="toggleMode"></i> <i class="el-image-viewer__actions__divider"></i> <i class="el-icon-refresh-left" @click="handleActions('anticlocelise')" ></i> <i class="el-icon-refresh-right" @click="handleActions('clocelise')" ></i> </div> </div> <!-- CANVAS --> <div class="el-image-viewer__canvas"> <img v-for="(url, i) in urlList" v-show="i === index && isImage" ref="media" :key="url" :src="url" :style="mediaStyle" class="el-image-viewer__img" @load="handleMediaLoad" @error="handleMediaError" @mousedown="handleMouseDown" /> <video controls="controls" v-for="(url, i) in urlList" v-show="i === index && isVideo" ref="media" :key="url" :src="url" :style="mediaStyle" class="el-image-viewer__img" @load="handleMediaLoad" @error="handleMediaError" @mousedown="handleMouseDown" ></video> </div> </div> </transition></template><script>import { computed, ref, onMounted, watch, nextTick } from 'vue'const EVENT_CODE = { tab: 'Tab', enter: 'Enter', space: 'Space', left: 'ArrowLeft', // 37 up: 'ArrowUp', // 38 right: 'ArrowRight', // 39 down: 'ArrowDown', // 40 esc: 'Escape', delete: 'Delete', backspace: 'Backspace',}const isFirefox = function () { return !!window.navigator.userAgent.match(/firefox/i)}const rafThrottle = function (fn) { let locked = false return function (...args) { if (locked) return locked = true window.requestAnimationFrame(() => { fn.apply(this, args) locked = false }) }}const Mode = { CONTAIN: { name: 'contain', icon: 'el-icon-full-screen', }, ORIGINAL: { name: 'original', icon: 'el-icon-c-scale-to-original', },}const mousewheelEventName = isFirefox() ? 'DOMMouseScroll' : 'mousewheel'const CLOSE_EVENT = 'close'const SWITCH_EVENT = 'switch'export default { name: 'MediaViewer', props: { urlList: { type: Array, default: () => [], }, zIndex: { type: Number, default: 2000, }, initialIndex: { type: Number, default: 0, }, infinite: { type: Boolean, default: true, }, hideOnClickModal: { type: Boolean, default: false, }, }, emits: [CLOSE_EVENT, SWITCH_EVENT], setup(props, { emit }) { let _keyDownHandler = null let _mouseWheelHandler = null let _dragHandler = null const loading = ref(true) const index = ref(props.initialIndex) const wrapper = ref(null) const media = ref(null) const mode = ref(Mode.CONTAIN) const transform = ref({ scale: 1, deg: 0, offsetX: 0, offsetY: 0, enableTransition: false, }) const isSingle = computed(() => { const { urlList } = props return urlList.length <= 1 }) const isFirst = computed(() => { return index.value === 0 }) const isLast = computed(() => { return index.value === props.urlList.length - 1 }) const currentMedia = computed(() => { return props.urlList[index.value] }) const isVideo = computed(() => { const currentUrl = props.urlList[index.value] return currentUrl.endsWith('.mp4') }) const isImage = computed(() => { const currentUrl = props.urlList[index.value] return currentUrl.endsWith('.jpg') || currentUrl.endsWith('.png') }) const mediaStyle = computed(() => { const { scale, deg, offsetX, offsetY, enableTransition } = transform.value const style = { transform: `scale(${scale}) rotate(${deg}deg)`, transition: enableTransition ? 'transform .3s' : '', marginLeft: `${offsetX}px`, marginTop: `${offsetY}px`, } if (mode.value.name === Mode.CONTAIN.name) { style.maxWidth = style.maxHeight = '100%' } return style }) function hide() { deviceSupportUninstall() emit(CLOSE_EVENT) } function deviceSupportInstall() { _keyDownHandler = rafThrottle((e) => { switch (e.code) { // ESC case EVENT_CODE.esc: hide() break // SPACE case EVENT_CODE.space: toggleMode() break // LEFT_ARROW case EVENT_CODE.left: prev() break // UP_ARROW case EVENT_CODE.up: handleActions('zoomIn') break // RIGHT_ARROW case EVENT_CODE.right: next() break // DOWN_ARROW case EVENT_CODE.down: handleActions('zoomOut') break } }) _mouseWheelHandler = rafThrottle((e) => { const delta = e.wheelDelta ? e.wheelDelta : -e.detail if (delta > 0) { handleActions('zoomIn', { zoomRate: 0.015, enableTransition: false, }) } else { handleActions('zoomOut', { zoomRate: 0.015, enableTransition: false, }) } }) document.addEventListener('keydown', _keyDownHandler, false) document.addEventListener( mousewheelEventName, _mouseWheelHandler, false ) } function deviceSupportUninstall() { document.removeEventListener('keydown', _keyDownHandler, false) document.removeEventListener( mousewheelEventName, _mouseWheelHandler, false ) _keyDownHandler = null _mouseWheelHandler = null } function handleMediaLoad() { loading.value = false } function handleMediaError(e) { loading.value = false } function handleMouseDown(e) { if (loading.value || e.button !== 0) return const { offsetX, offsetY } = transform.value const startX = e.pageX const startY = e.pageY const divLeft = wrapper.value.clientLeft const divRight = wrapper.value.clientLeft + wrapper.value.clientWidth const divTop = wrapper.value.clientTop const divBottom = wrapper.value.clientTop + wrapper.value.clientHeight _dragHandler = rafThrottle((ev) => { transform.value = { ...transform.value, offsetX: offsetX + ev.pageX - startX, offsetY: offsetY + ev.pageY - startY, } }) document.addEventListener('mousemove', _dragHandler, false) document.addEventListener( 'mouseup', (e) => { const mouseX = e.pageX const mouseY = e.pageY if ( mouseX < divLeft || mouseX > divRight || mouseY < divTop || mouseY > divBottom ) { reset() } document.removeEventListener( 'mousemove', _dragHandler, false ) }, false ) e.preventDefault() } function reset() { transform.value = { scale: 1, deg: 0, offsetX: 0, offsetY: 0, enableTransition: false, } } function toggleMode() { if (loading.value) return const modeNames = Object.keys(Mode) const modeValues = Object.values(Mode) const currentMode = mode.value.name const index = modeValues.findIndex((i) => i.name === currentMode) const nextIndex = (index + 1) % modeNames.length mode.value = Mode[modeNames[nextIndex]] reset() } function prev() { if (isFirst.value && !props.infinite) return const len = props.urlList.length index.value = (index.value - 1 + len) % len } function next() { if (isLast.value && !props.infinite) return const len = props.urlList.length index.value = (index.value + 1) % len } function handleActions(action, options = {}) { if (loading.value) return const { zoomRate, rotateDeg, enableTransition } = { zoomRate: 0.2, rotateDeg: 90, enableTransition: true, ...options, } switch (action) { case 'zoomOut': if (transform.value.scale > 0.2) { transform.value.scale = parseFloat( (transform.value.scale - zoomRate).toFixed(3) ) } break case 'zoomIn': transform.value.scale = parseFloat( (transform.value.scale + zoomRate).toFixed(3) ) break case 'clocelise': transform.value.deg += rotateDeg break case 'anticlocelise': transform.value.deg -= rotateDeg break } transform.value.enableTransition = enableTransition } watch(currentMedia, () => { nextTick(() => { const $media = media.value if (!$media.complete) { loading.value = true } }) }) watch(index, (val) => { reset() emit(SWITCH_EVENT, val) }) onMounted(() => { deviceSupportInstall() // add tabindex then wrapper can be focusable via Javascript // focus wrapper so arrow key can't cause inner scroll behavior underneath wrapper.value?.focus?.() }) return { index, wrapper, media, isSingle, isFirst, isLast, currentMedia, isImage, isVideo, mediaStyle, mode, handleActions, prev, next, hide, toggleMode, handleMediaLoad, handleMediaError, handleMouseDown, } },}</script>应用 ...

September 10, 2021 · 5 min · jiezi

关于vue.js:vue使用libflexible和postcsspxtorempx转rem进行适配

1.开发环境 vue2.电脑系统 windows10专业版3.在开发的过程中,咱们须要做适配,上面我来分享一下办法,心愿对你有所帮忙。4.废话不多说,间接上操作: //装置 lib-flexiblenpm install lib-flexible --save-dev5.批改lib-flexible配置 // 文件在node_modules>lib-flexible>flexible5-1.在mian.ts中引入lib-flexible // 导入 lib-flexibleimport "lib-flexible";6.装置postcss-pxtorem(px转rem) npm install postcss-pxtorem -D6-1.在根目录下新建.postcssrc.js(和package同级) module.exports = { "plugins": { "autoprefixer": {}, 'postcss-pxtorem': { rootValue: 19.2, // 75示意750设计稿,37.5示意375设计稿 propList: ['*'] } }}6-2.你可能会遇到以下报错6-3.剖析起因 //最高版本是6,找不到8的版本6-4.解决办法如下: npm install postcss-pxtorem@5 -D7.本期的分享到了这里就完结啦,心愿对你有所帮忙,让咱们一起致力走向巅峰。

September 9, 2021 · 1 min · jiezi

关于vue.js:Vue3样式绑定

小编明天和大家分享对于Vue中的款式和class的绑定,首先申明,这篇文章呈现的class不是类申明的关键字,而是标签外部的属性,用于实现款式在原生html中,咱们给一个元素增加款式的时候,大略有这么两种形式 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <div style="color:red">我是一个红色的小可爱</div></body></html>第二种形式就是在head标签之间,增加style标签,将款式对立写在style之间,而后在标签中定义对应的class <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> .red_color{ color:red }</style></head><body> <div class="red_color">我同样是一个红色的小可爱</div></body></html>对于Vue来说,有以下四种形式定义款式和class一、通过Vue提供的v-bind形式绑定字符串,就像这样 <html> <head> <style> .red_color{ color:red } .green_color{ color:green } .yellow_color{ color:yellow } </style> </head> <body> <script> const app = Vue.createApp({ data(){ return { classString:'red_color' } }, template: `<div :class="classString">Hello World</div>` }) const vm = app.mount('#root') </script> </body></html>最初渲染进去的DOM长这样 <div class="red_color">Hello World</div>二、class绑定的还能够是个Object,这个Object比拟特地,key为定义好的class,value为Boolean,为true的时候,增加该class,否则不增加该class <html> <head> <style> .red_color{ color:red } .green_color{ color:green } .yellow_color{ color:yellow }</style> </head> <body> <script> const app = Vue.createApp({ data(){ return { classObj:{ red_color: true, green_color: true, yellow_color: false } } }, template: `<div :class="classObj">Hello World</div>` }) const vm = app.mount('#root')</script> </body></html>最初渲染进去的DOM长这样 ...

September 9, 2021 · 2 min · jiezi

关于vue.js:Cloud与领域驱动的详细案例分析

咱们心愿畛域对象可能精确地表白出业务用意,然而少数时候,咱们看到的却是充斥getter和setter的畛域对象。此时的畛域对象曾经不是畛域对象了,星池starpool 它们只是个数据载体,也就是Martin Fowler所说的贫血对象。这种做法会导致畛域特定业务逻辑扩散在一堆service层中,软件架构随业务开发长年累积横蛮成长,从而糜烂,无奈保护。 在微服务设计中应该首先辨认出DDD中的聚合根(Aggregate Root);还有在微服务之间集成时应该采纳DDD中的防腐层(Anti-Corruption Layer, ACL)。咱们甚至能够说DDD和微服务有着天生的默契。咱们心愿畛域对象可能精确地表白出业务用意,然而少数时候,咱们看到的却是充斥getter和setter的畛域对象。https://www.starpool.cn 此时的畛域对象曾经不是畛域对象了,它们只是个数据载体,也就是Martin Fowler所说的贫血对象。这种做法会导致畛域特定业务逻辑扩散在一堆service层中,软件架构随业务开发长年累积横蛮成长,从而糜烂,无奈保护。

September 8, 2021 · 1 min · jiezi

关于vue.js:VUE第一篇-elementUI表格-点击按钮修改表格单行数据关闭弹窗修改表格单行数据

问题:点击表格按钮关上弹窗,同时批改按钮的disabled=true,敞开弹窗,批改按钮的disabled=false。知识点:1、this.$set(item,'INdisabled',false):应用$set,这样增加的属性才是响应式,否则无论怎么批改,都不会扭转曾经展现的数据!2、深拷贝,浅拷贝 <el-table :data="invoiceRecord" border stripe ref="multipleTable" tooltip-effect="dark" style="width: 100%; overfolow: hidden" > <el-table-column label="编号" type="index" align="center" width="65"> </el-table-column> <el-table-column prop="address" label="操作" fixed="right" align="center" > <template slot-scope="scope"> <el-button :disabled="scope.row.INdisabled" v-if="scope.row.SM_ID == 0 " class="upload" size="mini" type="danger" @click="getContractInvoiceId(scope.row)" >上传</el-button> </template> </el-table-column> </el-table>注:加载表格数据invoiceRecord的时候,手动增加了INdisabled属性 this.invoiceRecord.find((item, index) => {//留神应用$set,这样增加的属性才是响应式,否则不论用!//item.INdisabled=false;//这种写法不具备响应式,即便表格数据批改,表格展现的还是原来的数据 this.$set(item,'INdisabled',false) if(item.IIM_IsFlag==0){ this.$set(item,'INdisabled',true); } })//上传点击事件 getContractInvoiceId(item){ this.$set(item,'INdisabled',true); // item.INdisabled=true; this.uploadInvoice=item;//浅拷贝,用于敞开弹窗批改disabled },下面点击事件,给变量uploadInvoice赋值,是用于弹窗敞开的时候,能动静批改编辑行按钮的状态。这里波及到一个深拷贝,浅拷贝的问题,浅拷贝复制的是援用(批改uploadInvoice,item也跟着变动);深拷贝复制的是实体(uploadInvoice是一个独立的个体,批改uploadInvoice,item不变),写法JSON.parse(JSON.stringify(item)),网上是这么说的,我也没试过。 //弹窗敞开事件 uploadInvoice当初是援用的item,因而item.INdisabled也会扭转changeVisible(){ this.$set(this.uploadInvoice,'INdisabled',false);//按钮复原状态 },

September 8, 2021 · 1 min · jiezi

关于vue.js:vue可拖动水平进度条组件

须要一个可拖动并能设置最大最小值及步长的进度条,所以问了度娘,参考了很多网友写的进度条的代码,终于搞出了这个进度条组件,pc端和挪动端均可用。 代码: <template> <div class="progress-bar"> <div class="num">{{ num }}</div> <div class="mask"> <div class="bar"><div class="point"></div></div> </div> </div></template><script>export default { name: "progress-bar", props: { min: { //最小值 type: Number, default: 0 }, max: { //最大值 type: Number, default: 100 }, step: { //每步的值为多少 type: Number, default: 1 } }, data() { return { num: this.value, test: "" }; }, mounted() { let dragSlide = () => { document.addEventListener( "touchmove", function(e) { e.preventDefault(); }, { passive: false } ); //勾销挪动端手势长按弹出提示框的操作 document.addEventListener("contextmenu", function(e) { e.preventDefault(); }); this.point = document.querySelector(".point"); //管制进度条的点 this.pointWidth = parseInt( window.getComputedStyle(this.point, null).width ); this.bar = document.querySelector(".bar"); //进度条色彩层 this.mask = document.querySelector(".mask"); //进度条底层 let lastX = null; //判断鼠标挪动方向,解决向左侧滑动时候的bug let move = e => { let x = e.touches[0].pageX; let direction = ""; if (lastX == null) { lastX = x; return; } if (x > lastX) { direction = "right"; } else if (x < lastX) { direction = "left"; } else { direction = ""; } let mask_left = this.getPosition(this.mask).left; let point_left = x - mask_left; if (point_left >= this.mask.offsetWidth - this.pointWidth) { //控制点能够滑动的最大值就是进度条的宽度减去控制点的宽度; point_left = this.mask.offsetWidth - this.pointWidth; } if (point_left < 0) { //控制点滑动的最小值不能为负 point_left = 0; } this.point.style.left = point_left + "px"; this.bar.style.width = point_left + this.pointWidth + "px"; //计算百分比 let percent = (point_left / (this.mask.offsetWidth - this.pointWidth)) * 100; if (percent < 0.5 && direction == "right") { percent = Math.ceil(percent); } else if (percent > 0.5 && direction == "right") { percent = Math.floor(percent); } else { percent = Math.ceil(percent); } let value = (percent / 100) * (this.max - this.min) + this.min; let v = Math.floor(value / this.step); value = v * this.step; this.num = value; }; this.point.addEventListener("touchmove", move); }; let drag0 = new dragSlide(); }, methods: { getPosition(node) { //获取元素的相对地位,工具函数 let left = node.offsetLeft; //获取元素绝对于其父元素的left值var left let top = node.offsetTop; let current = node.offsetParent; // 获得元素的offsetParent // 始终循环直到根元素 while (current != null) { left += current.offsetLeft; top += current.offsetTop; current = current.offsetParent; } return { left: left, top: top }; } }};</script><style lang="scss">* { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;}.progress-bar { text-align: left; .mask { width: 320px; height: 20px; background-color: #dddddd; border-radius: 10px; position: absolute; } .bar { height: 20px; background-color: #1069db; position: absolute; z-index: 2; border-radius: 10px; bottom: 0; left: 0; } .point { width: 24px; height: 24px; border-radius: 50%; border: 1px solid #1069db; position: absolute; bottom: -3px; left: 0; z-index: 3; background-color: white; } .num { text-align: center; padding-bottom: 20px; }}</style>组件应用办法: ...

September 8, 2021 · 2 min · jiezi

关于vue.js:Vuev2614源码解毒预手写一个简易版Vue

MVVM 设计模式,是由 MVC、MVP 等设计模式进化而来,M - 数据模型(Model),VM - 视图模型(ViewModel),V - 视图层(View)。MVVM 的外围是 ViewModel 层,它就像是一个中转站(value converter),负责转换 Model 中的数据对象来让数据变得更容易治理和应用,该层向上与视图层进行双向数据绑定,向下与 Model 层通过接口申请进行数据交互,起呈上启下作用。如下图所示: Vue中的MVVM思维应用 MVVM 设计模式的前端框架很多,其中渐进式框架 Vue 是典型的代表,深得宽广前端开发者的青眼。 从上图中能够看出MVVM次要分为这么几个局部: 模板编译(Compile)数据劫持(Observer)订阅-公布(Dep)观察者(Watcher)咱们来看一个 vue 的实例: <!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <meta http-equiv="X-UA-Compatible" content="IE=edge">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <title>vue</title></head><body>  <div id="app">    <p>{{ number }}</p>  </div>  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>  <script>    const VM = new Vue({      el: '#app',      data: {        number: 0      },    })    setInterval(() => {      VM.number++    }, 1000)  </script></body></html>咱们对原理进行剖析一下: 首先 new Vue() 执行初始化,通过 Observer 对 data 上的属性执行响应式解决。也就是 Object.defineProperty 对数据属性进行劫持。通过Compile 进行模板编译,对模板里动静绑定的数据,应用 data 数据进行初始化。在模板初始化时,在触发Object.defineProperty 内的 getter 时,创立更新函数和 Watcher 类。同一属性在模板中可能呈现屡次,就会创立多个Watcher ,就须要Dep 来对立治理。当数据发生变化时,找到属性对应的 dep ,告诉所有 Watcher 执行更新函数。创立Vue类// 创立 Vue 类class Vue {  constructor(options) {    // 保留选项    this.$options = options;    this.$data = options.data;    // 响应式解决    observe(this.$data)    // 将数据代理到实例上    proxyData(this, '$data')    // 用数据和元素进行编译    new Compiler(options.el, this)  }}// 代理数据的办法function proxyData (vm, sourceKey) {  Object.keys(vm[sourceKey]).forEach(key => {    Object.defineProperty(vm, key, {      get () {        return vm[sourceKey][key]      },      set (newVal) {        vm[sourceKey][key] = newVal      }    })  })}下面代码创立了一个 Vue 类和 proxyData 办法,Vue 类接管 options 参数,外部调用 observe 办法对传入参数 options.data 数据,递归进行响应式解决。应用 proxyData 办法把数据代理到实例上,让咱们获取和批改数据的时候能够间接通过 this 或 this.$data, 如: this.number 或 this.$data.number 。最初应用 Compiler 对模板进行编译,初始化动静绑定的数据。 ...

September 7, 2021 · 1 min · jiezi

关于vue.js:UDP的学习步骤有哪些方法呢

听之前要进行创立套接字->绑定ip::端口号->监听。咱们能够在bind之前应用setsockopt函数,设置套接字选项,星池starpool其中就包含REUSEADDR这个选项,表明多个过程能够复用bind函数中指定的地址和端口号。发送方通过非凡的算法计算得出,在传递到接管方之后,还要在从新计算。如果某个数据报在传输过程中被第三方篡改或者因为线路乐音等起因受到损坏,发送和接管方的校验计算值将不会相符,由此UDP协定能够测验是否出错。 也就是说一个UDP能传输的数据的最大长度是64K(蕴含UDP首部)。然而64K在当今的互联网环境下,是一个十分小的数字。https://www.starpool.cn 如果咱们须要传输的数据超过64K,就须要应用层手动的分包,屡次发送,并在接收端拼装

September 7, 2021 · 1 min · jiezi

关于vue.js:CARRY4原理分析的文档指导

行了一次异或操作(LUT查找表实现的,异或就是加法不进位)。星池starpool数据在进入CARRY4之前曾经进行了加法运算,只是还没有判断进位标记。这样一看,大略理解了,CARRY4的原理过程了。如果对应位是1,加上,否则右移。留神b二进制右移一位,a就要乘2。因为二进制每一位权值对应2。构造函数中调用父类构造函数。因为毕竟函数就是在特定上下文中执行代码的简略对象,所以能够应用apply()和call()办法以新创建的对象为上下文执行构造函数。 它能解决原型链继承中传参数和援用类型属性抵触。还是间接看例子:留神ans初始化为0,因为0加任何数都不变。构造函数中调用父类构造函数。星池starpool因为毕竟函数就是在特定上下文中执行代码的简略对象,所以能够应用apply()和call()办法以新创建的对象为上下文执行构造函数。它能解决原型链继承中传参数和援用类型属性抵触。还是间接看例子:*

September 6, 2021 · 1 min · jiezi

关于vue.js:关于指针的C学习方法指导

如果所有未应用的指针都被赋予空值,同时防止应用空指针,就能够避免误用一个未初始化的指针。很多时候,未初始化的变量存有一些垃圾值,导致程序难以调试。星池starpool样编译器就不晓得应该调用哪一个函数。因而,C++将NULL定义成了(int)0,解决了函数重载的匹配问题,但却引入了另一个问题,还是这段代码:果输出有n个元素,最初一个元素的下标就是n-1。所以数组的下标如果小于0,或者大于n-1,就是数组越界拜访了,超出了数组非法空间的拜访。C语言自身是不做数组下标的越界查看,编译 那咱们找一下问题,星池starpool调试之后能够看到 bubble_sort 函数外部的 sz ,是1。难道数组作为函数参数的时候,不是把整个数组的传递过来?

September 6, 2021 · 1 min · jiezi

关于vue.js:vue自定义工具类

1.封装 /src /utils /xx.js export function formatWord(data){ return data.replace(/<span style=\'color:red;font-weight:bold;\'>/ig,'').replace(/<\/span>/ig,'')}2页面调用 this.formInline.zhaobdw = formatWord(row.biddingUnit) import{formatWord}from '@/utils/zfczz.js'

September 6, 2021 · 1 min · jiezi

关于vue.js:关于vue3-compositionAPI

setup函数的参数props,context setup(props, context) {},props:个别通过父组件传递过去的属性会放到props对象里,应用时可间接通过props获取context:外面蕴含三个属性attrs:所有非prop的attribute;slots:父组件传递过去的的插槽;emit:当咱们组件外部emit时会用到 setup不可应用this!官网文档这里有写 ref的应用将一个原始数据类型(primitive data type)转换成一个带有响应式个性的数据类型,原始数据类型共有7个,别离是: **StringNumberBigIntBooleanSymbolNullUndefined** ref会浅层解包,在templete上间接用{{test}}就好了 setup() { let test = ref(0) let tobj = ref({a: 1}); // test.value = 1; const testFun = () => { test.value++; tobj.a.vaue = 'b'; console.log(tobj.a) }; return { tobj, robj, test, testFun, } },假如咱们在定义了用ref定义了tobj是对象,当咱们在testFun扭转值时,会报错 reactive的应用reactive (等价于Vue2中的Vue.observable() )来赋予对象(Object) 响应式的个性的。 假如咱们这么写 <template> <div class="home"> <h1>{{a}}</h1> <button @click="handleClick">1</button> </div></template><script>import { defineComponent } from 'vue';import { ref, reactive } from 'vue'export default defineComponent({ setup() { let robj = reactive({ a: 'a', handleClick: () => { robj.a = 'c'; console.log(robj); }, }); return { ...robj } },});</script>这时候咱们发现,页面并没有发生变化!事实上,这样写没有成果的起因就在于一个响应型对象(reactive object) 一旦被销毁或开展,其响应式个性(reactivity)就会失落。通过查看咱们能够晓得,尽管robj.a的值的确产生了变动,但robj.ae的类型只是一个一般的字符串(String) ,并不具备响应式个性(reactivity),故而页面也没有随着其值的变动而从新渲染。 ...

September 6, 2021 · 1 min · jiezi

关于vue.js:个人开源典范的-Vuejs-可被模仿吗|Benefit-from-Open-Source

通过剖析 VueJS 开源各个阶段的社区经营于技术演进思路,为想要开发集体开源我的项目的开发者提供借鉴。 https://www.bilibili.com/vide...

September 5, 2021 · 1 min · jiezi

关于vue.js:Vuejs-报错-Cannot-read-property-validate-of-undefined

报错提示信息:TypeError: Cannot read property 'validate' of undefined ** 解决:** 解析: 谬误导致的起因是 el-form表单中的 ref="dataForm"和表单提交时this.$refs.dataForm.validate()名字交的不统一,或者是ref这个就没有定义. 我是因为名字起的不统一而导致报错的,下次要仔细啦

September 5, 2021 · 1 min · jiezi

关于vue.js:vue-router-v4x-路由的动态加载实现-完整代码

前言最近就业中没人要,终日呆在家里总想写点什么,顺便增强一下代码程度,在 router4.0 中增加路由的办法从 addRoutes() 变成 addRoute(),以前实现的形式就产生了变动,不过也只是小改变。 为什么不举荐间接写路由表?如果把路由表固定写在前端页面中,用户就能够拜访所有页面,后端就须要一份跟前端一样的路由表来配置权限,对页面进行比对,依据不同角色返回相应的页面权限。如果前端路由表批改了,那么后端同时也须要批改,这样就须要同时保护两端,不仅麻烦,前端只能改源码,还有可能因为遗记批改而导致bug。 动静路由如果路由表是由后端获取的,那么你拜访了没有权限的页面会返回 404 谬误,并且只需后端保护,权限管制更加残缺。 具体实现在本例中没有应用 Vuex 来存储后端传来的路由列表数据,我感觉没必要,间接应用 sessionStorage 来存储就能够了,因为一旦页面刷新了,Vuex 中的数据就会隐没,那就得从新从新申请数据,会影响页面的加载速度。 问题剖析在什么时候加载路由表?路由表加载完应该做什么?刷新如何从新加载?用户切换账号会有什么问题?踩坑(答复以上问题)在用户登录后跳转的 router.beforeEach 钩子外面异步加载 router.beforeEach((to, from, next) => { // 注册动静路由 registerRoutes().then(() => { // 跳转事件 }).catch(() => { // 解决异样事件 })});进行路由重定向,因为之前跳转的时候地址还不存在路由表中,如果间接 next() 会找不到页面,所以须要重定向,这里还须要做一个判断,不然会进入死循环。 if (routeFlag) { next();} else { // 注册动静路由 registerRoutes().then(() => { routeFlag = true; next({ ...to, replace: true }); }).catch(() => { // 解决异样事件 })}首先判断用户 token 是否登录,如果已登录,获取 sessionStorage 存储的路由表,进入 beforeEach 会主动从新注册路由。应该把 sessionStorage 存储的路由表移除,不然切换账号后会获取到上一个登录账号的路由表。残缺代码Vue3 + Router4 + TypeScript ...

September 4, 2021 · 3 min · jiezi

关于vue.js:怎样捕获Vue-组件的错误信息

简述了解:errorCaptured 是组件外部钩子,当捕捉一个来自子孙组件的谬误时被调用,接管 error、 vm、info 三个参数,return false 后能够阻止谬误持续向上抛出 errorHandler 为全局钩子,应用 Vue.config.errorHandler 配置,接管参数与 errorCaptured 一 致,2.6 后可捕获 v-on 与 promise 链的谬误,可用于对立错误处理与谬误兜底 具体看看:一、谬误类型任何一个框架,对于谬误的解决都是一种必备的能力 在Vue 中,则是定义了一套对应的错误处理规定给到使用者,且在源代码级别,对局部必要的过程做了肯定的错误处理。 次要的谬误起源包含: 1.后端接口谬误 2.代码中自身逻辑谬误 二、后端接口谬误申请实现后捕捉谬误---------- 封装axios申请,通过catch捕捉谬误。 axios({ method:'get', url:'地址',}).then(res=>{ //胜利解决}).catch(err=>{ //捕捉谬误})申请返回后通过拦截器捕捉谬误-----------通过axios的interceptor实现网络申请的response先进行一层拦挡 // 增加响应拦截器axios.interceptors.response.use(function (response) { // 对响应数据做点什么 return response; }, function (error) { // 对响应谬误做点什么 return Promise.reject(error); });三、代码逻辑问题1、设置全局谬误处理函数 Vue.config.errorHandler = function (err, vm, info) { // handle error // `info` 是 Vue 特定的错误信息,比方谬误所在的生命周期钩子 // 只在 2.2.0+ 可用}2、生命周期钩子 ...

September 4, 2021 · 1 min · jiezi

关于vue.js:vue子组件改父组件的值-调用父方法

子组件把数据传入父组件子组件 this.$emit('changeInput1', this.formInline.xmmc)父组件 <sc v-show="condition == 'sc'" ref="sc" @changeInput1="changeInput1"></sc> changeInput1(e) { this.input3 = e },子组件调用父组件中的办法1.简略状况this.$parent.a(); 2.$emit触发一个事件,在父组件监听到这个事件 调用外部办法父 @shijianName="fName"fName(){console.log(1)}子 <button @click="a()"/>a(){this.$emit('shijianName')}3父组件把办法传入子组件 子组件间接调用父:name="name" 办法定义不写了子 props:{name:{type:Function,default:null}}而后,你把它当做子组件的办法间接调用就行

September 4, 2021 · 1 min · jiezi

关于vue.js:vue组件传值

父组件传递自定义事件给子组件, 子组件显示调用的两种形式 父组件应用 v-bind(:属性传递)父组件 <child :mockParent="handleParentEvet"></child>子组件需接管props props:{ mockParent:{ type: Function }},methods:{ handle(){ this.mockParent('param from child') // 不能应用 this.$emit('mockParent','sssss') }}父组件应用 v-on/@(事件传递),子组件调用时应用边界状况父组件 <child @test="parentTest" @update="parentUpdate"></child>子组件中无需接管props methods:{ handle(){ this.$listeners.test('param from child test') // OK this.$listeners.update('param from child update') // OK this.$emit('update','param from child update') // OK }}

September 3, 2021 · 1 min · jiezi

关于vue.js:vue中mockjs的使用

src目录下新建index.js和response目录,response 用于注册各个模块的接口编写index.js import Mock from 'mockjs'let moduleList = require.context('./response', true, /.js$/)moduleList.keys().forEach(key => { let el = moduleList(key) for (const i in el) { if (Object.hasOwnProperty.call(el, i)) el[i]() }})Mock.setup({ timeout: '0-100' // 也反对具体数字})export default Mock3.编写示例 response目录下新建user.js,内容如下 import Mock from 'mockjs'const Random = Mock.Randomexport const getUserList = () => { Mock.mock(/user\/getUserList/, 'get', { 'data|1-10': [ { 'id|+1':0 email: '@email', name: '@cname', address: '@county(true)' } ] })}4.应用 // 间接调用接口import { getUserList } from '@/api/user'{ methods: { getUserList(){ getUserList().then(res => console.log(res)) } }}

September 3, 2021 · 1 min · jiezi

关于vue.js:前端面试每日-31-第870天

明天的知识点 (2021.09.02) —— 第870天 (我也要出题)[html] 实现左中右三栏布局有哪些办法?[css] z-index在CSS3中失效范畴有哪些?[js] 写一个办法去除对象中值为空('',' ',null,undefined)的属性[软技能] 你喜爱养小动物吗?《论语》,曾子曰:“吾日三省吾身”(我每天屡次检查本人)。前端面试每日3+1题,以面试题来驱动学习,每天提高一点!让致力成为一种习惯,让奋斗成为一种享受!置信 保持 的力量!!! 欢送在 Issues 和敌人们一起探讨学习! 我的项目地址:前端面试每日3+1【举荐】欢送跟 jsliang 一起折腾前端,零碎整顿前端常识,目前正在折腾 LeetCode,打算买通算法与数据结构的任督二脉。GitHub 地址 微信公众号欢送大家前来探讨,如果感觉对你的学习有肯定的帮忙,欢送点个Star, 同时欢送微信扫码关注 前端剑解 公众号,并退出 “前端学习每日3+1” 微信群互相交换(点击公众号的菜单:交换)。 学习不打烊,充电加油只为遇到更好的本人,365天无节假日,每天早上5点纯手工公布面试题(死磕本人,愉悦大家)。心愿大家在这虚夸的前端圈里,放弃沉着,保持每天花20分钟来学习与思考。在这变幻无穷,类库层出不穷的前端,倡议大家不要等到找工作时,才狂刷题,提倡每日学习!(不忘初心,html、css、javascript才是基石!)欢送大家到Issues交换,激励PR,感激Star,大家有啥好的倡议能够加我微信一起交换探讨! 心愿大家每日去学习与思考,这才达到来这里的目标!!!(不要为了谁而来,要为本人而来!)交换探讨欢送大家前来探讨,如果感觉对你的学习有肯定的帮忙,欢送点个[Star]

September 2, 2021 · 1 min · jiezi

关于vue.js:Vue中的Vuex二

小编明天持续和大家一起学习和探讨Vuex,咱们书接上回,持续摸索Vuex的其余个性三、Mutation在上一篇文章中,咱们曾经晓得,对于Vuex中的数据,不能像data中的数据一样间接批改,那要对State中的数据批改的时候,要怎么做呢,Vuex提供了Mutation形式进行对立批改,并且应用state作为第一个参数,就像这样 const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment (state) { // 变更状态 state.count++ } }})要触发这个函数,只须要这样就能够 store.commit('increment')同样,咱们一样能够在提交的时候,多传递一个参数,也就是官网上说的提交载荷(Payload),就像这样 // ...mutations: { increment (state, n) { state.count += n }}store.commit('increment', 10)实际上,咱们在应用Mutation第二个参数的时候,传递个对象,是平时更罕用的形式,同时,也能够传递更大量的数据信息,就像这样 // ...mutations: { increment (state, payload) { state.count += payload.amount }}store.commit('increment', { amount: 10})同样,官网还提出了“对象提交格调”,通过type作为作为提交对象的key,对应触发Mutation中对应的办法,就像这样 store.commit({ type: 'increment', amount: 10})mutations: { increment (state, payload) { state.count += payload.amount }}小编在这多说一点,针对对象,js在新的语法中作了很多扩大,其中扩大运算符就是一个,能够合并属性一样的值,同样的,在Linux的一些配置文件中,应该也是应用了相似的思维。 state.obj = { ...state.obj, newProp: 123 }在理论我的项目中,对于字符串,咱们很容易写错,在之前的文章中,小编提供了两种计划,一种是能够应用es6中的Symbol来实现,一种是能够定义常量来实现,这些在Vuex中也有相应的影子。 ...

September 2, 2021 · 2 min · jiezi

关于vue.js:知识付费如何二开分销功能

首先咱们要晓得什么是分销零碎? 分销零碎指通过互联网将供应商与经销商有机地分割在一起,为企业的业务经营及与贸易搭档的单干提供了一种全新的模式。供应商、分支机构和经销商之间能够实现实时地提交业务单据、查问产品供给和库存情况、并取得市场、销售信息及客户反对,实现了供应商、分支机构与经销商之间端到端的供应链治理,无效地缩短了供销链。 常识付费的分销性能次要体现在专题课程及会员模块;用户购买专题课程、会员后会给用户的下级和上下级返佣。那么分销零碎是如何实现的呢?常识付费零碎中在用户进入零碎时若是带有下级的信息,那么这个用户就是别的用户倒退的上级,咱们就须要记录该用户的下级信息。数据库用户表eb_user表中有字段spread_uid(推广员id),该字段记录的是用户的下级id。首先确定什么时候执行分销性能?分销性能执行必定是须要订单完结后,这样能够防止大多数用户退款后的佣金返还问题。而后就是分销代码的书写了,常识付费分销分为人人分销和指定分销;人人分销是指零碎中的所有用户均可参加分销流动,并且在上级用户购买后能够取得佣金。指定分销是指只有零碎设置推广权限的用户能力在上级用户购买后取得佣金。因为常识付费的分销是二级分销,所有咱们是间接写了一级返佣和二级返佣的办法,在执行完一级返佣后执行二级返佣,这是分销的最简略逻辑了。以专题课程为例,在专题课程购买胜利后执行返佣。(注:以下代码为局部代码,具体见常识付费https://gitee.com/ZhongBangKe...)/** //TODO 专题领取胜利后@param $orderId@param $notify@return bool */public static function paySuccess($orderId){ $order = self::where('order_id', $orderId)->where('type',0)->find();if(!$order) return false;User::bcInc($order['uid'], 'pay_count', 1, 'uid');$res = self::where('order_id', $orderId)->where('type',0)->update(['paid' => 1, 'pay_time' => time()]);try { //专题返佣 User::backOrderBrokerage($order);} catch (\Throwable $e) {}StoreOrderStatus::status($order->id, 'pay_success', '用户付款胜利');return false !== $res;} /** 一级推广 专题@param $orderInfo@return bool */public static function backOrderBrokerage($orderInfo){ $userInfo = User::getUserInfo($orderInfo['uid']);if (!$userInfo || !$userInfo['spread_uid']) return true;$course_distribution_switch = SystemConfigService::get('course_distribution_switch');//课程分销开关if($course_distribution_switch==0) return true;$storeBrokerageStatu = SystemConfigService::get('store_brokerage_statu') ?: 1;//获取后盾分销类型if ($storeBrokerageStatu == 1) { if (!User::be(['uid' => $userInfo['spread_uid'], 'is_promoter' => 1])) return true;}$brokerageRatio = bcdiv(SystemConfigService::get('store_brokerage_ratio'),100,2);if ($brokerageRatio <= 0) return true;$brokeragePrice = bcmul($orderInfo['pay_price'], $brokerageRatio, 2);if ($brokeragePrice <= 0) return true;$mark = '一级推广人' .$userInfo['nickname'] . '生产' . floatval($orderInfo['pay_price']) . '元购买专题,处分推广佣金' . floatval($brokeragePrice);self::beginTrans();$res1 = UserBill::income('购买专题返佣', $userInfo['spread_uid'], 'now_money', 'brokerage', $brokeragePrice, $orderInfo['id'], 0, $mark);$res2 = self::bcInc($userInfo['spread_uid'], 'brokerage_price', $brokeragePrice, 'uid');$res = $res1 && $res2;self::checkTrans($res);if ($res) self::backOrderBrokerageTwo($orderInfo);return $res;} ...

September 2, 2021 · 2 min · jiezi

关于vue.js:文件云存储系统之文件下载功能实现

文件存储系统中有一个必不可少的性能就是文件的下载性能,下载的应该反对各种格局的文件。本我的项目中客户端应用的是vue3、axios和vuex,服务端应用的是Gin框架,下边就介绍文件下载局部的代码和次要步骤。 一、Gin框架局部:前两个函数的调用都是依据参数查找files表和user_files表中是否存在文件记录,如果文件不存在,则返回404,如果存在再执行后边的操作。第二局部是Header的配置,这里重点说三个配置信息。 c.Header("Access-Control-Expose-Headers", "Content-Disposition"):Content-Disposition中配置有文件名信息,通过Content-Disposition能够把文件名传递给客户端,客户端在取出文件名作为下载文件的名称。然而这一项默认是不公开的,如果不设置c.Header("Access-Control-Expose-Headers", "Content-Disposition")的话content-type就裸露不进来,客户端也接管不了,这一项配置的作用就是裸露该项,也是必须配置的。c.Header("response-type", "blob"):如果客服端是以文件流的模式下载文件,服务端必须配置该项,否则下载下来的文件会报如下谬误:文件格式不正确或已损坏。c.File(fm.FileAddress):参数是文件的存储地址,这一项也是必须的,否则下载的文件是空的。申请的形式post和get都能够。// DownloadHandler文件下载接口func DownloadHandler(c *gin.Context) { fileSha1 := c.Request.FormValue("file_sha1") userId, _ := strconv.ParseInt(c.Request.FormValue("user_id"), 10, 64) userFile, err := se.FindUserFileByUserAndSha1(c, userId, fileSha1) if err != nil { fmt.Printf("Failed to find file, err is %s\n", err.Error()) c.JSON(http.StatusNotFound, gin.H{ "code": 0, "msg": "找不到文件", "data": nil, }) return } fm, err := se.FindFileMetaByFileSha1(c, fileSha1) if err != nil { fmt.Printf("Failed to find file, err is %s\n", err.Error()) c.JSON(http.StatusNotFound, gin.H{ "code": 0, "msg": "找不到文件", "data": nil, }) return } c.Header("Content-Type", "application/octet-stream") // 强制浏览器下载 c.Header("Content-Disposition", "attachment;filename=\""+userFile.FileName+"\"") // 浏览器下载或预览 c.Header("Content-Disposition", "inline;filename=\""+userFile.FileName+"\"") c.Header("Content-Transfer-Encoding", "binary") c.Header("Cache-Control", "no-cache") c.Header("Access-Control-Expose-Headers", "Content-Disposition") c.Header("response-type", "blob") // 以流的模式下载必须设置这一项,否则前端下载下来的文件会呈现格局不正确或已损坏的问题 c.File(fm.FileAddress)}二、vue配置vue应用的是3版本,因为下载文件的图标和文件夹下的文件列表不在一个vue文件中,须要传递全局变量,所以对变量的状态治理应用vuex来进行,很不便。 ...

September 2, 2021 · 2 min · jiezi

关于vue.js:Vue-Node-MongoDB-项目在CentOS82服务器上部署到宝塔Linux面板流程

一、背景分享wallBlog我的项目部署上线的流程。以下是部署须要的工具以及环境: mac零碎操作一台零碎为CentOS 8.2服务器宝塔Linux面板治理、运维我的项目Node v14.15.1Npm v6.14.8PM2管理器 v5.2.0MongoDB v4.4.6Nginx v1.21.0wallBlog线上地址:http://www.rasblog.comGithub地址:https://github.com/Sujb-sus/wallBlog码云地址:https://gitee.com/Sujb/wall-blog我这里仅是大略介绍一下流程,具体的服务器部署介绍能够参考该视频:https://www.bilibili.com/video/av70120365/ 二、服务器近程连贯从阿里云买完一台CentOS 8.2服务器,拿到服务器的公网ip,就能够在终端进行服务器近程连贯: 输出命令:ssh root@xx.xx.xx.xx(公网ip地址);输出服务器明码,可在阿里云控制台外面重置明码;连贯上之后能够新增一个用户,就不必始终用root用户登陆了。 1、adduser wall // 增加用户名wall2、passwd wall // 从新设置明码3、whereis sudoers // 查问sudo权限者门路4、chmod -v u+w /etc/sudoers // 给该门路下的sudoers者增加读写权限5、vim /etc/sudoers // 编辑该文件,在外面增加sudoers,在root All=(ALL) ALL上面增加一行 wall All=(ALL) ALL,就能保留了vim文件会波及一些Linux根本命令: 1、 i // 光标落地,可编辑文件2、 Esc // 收起光标,不可编辑了3、 :q // 不保留退出文件4、 :wq // 保留且退出文件5、 :wq! // 强制保留且退出文件三、更换阿里的yum源且更新1、备份你的原镜像文件,免得出错后能够复原。 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup2、下载新的CentOS-Base.repo到/etc/yum.repos.d/。我这边是CentOs8.2版本,所以对应下载是阿里云的Centos-8.repo版本,依据服务器版本下载对应repo版本,这个须要留神一下。 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-8.repo3、运行yum makecache生成缓存。 yum makecache4、查看零碎所有可用源。 yum repolist enabled5、更新yum。 yum -y update更新完yum之后,就能够下载宝塔Linux面板了。 留神:下载宝塔Linux面板须要一个洁净的零碎,不然下载不了。所以连贯上了服务器,先更新一下yum,而后间接下载宝塔Linux面板,不做多余的事。如果零碎曾经装置了其余货色,能够去阿里云重置一下零碎,把零碎清空洁净即可。 四、下载宝塔Linux面板去宝塔官网 https://www.bt.cn/bbs/thread-19376-1-1.html 下载,连贯服务器时须要用root用户连贯,因为只有root用户才可下载;下载完之后,控制台会显示登陆面板的账号、明码、拜访门路,这个须要存起来,登陆要用;在拜访前须要去阿里云的网络安全组还有防火墙,放开8888端口能力进行拜访;拜访门路:xx.xx.xx.xx:8888 (公网ip地址 + 8888端口);输出账号密码就能登进去了,能够在面板重置账号、明码、拜访门路。五、部署前端Vue我的项目将打包后的Vue我的项目的资源放在宝塔里的文件目录下,自行定义门路;在宝塔里的软件商店下载Nginx;下完之后就能够在外面配置Vue文件资源指向以及ip地址。 ...

September 2, 2021 · 1 min · jiezi

关于vue.js:Vue-Node-MongoDB-前后端分离搭建一款简约的个人博客系统

wallBlog一款简洁的集体博客零碎 wallBlog 线上地址:http://dzblog.cn/article/5a69609c3c04164b0bd4b964Gitub地址:https://github.com/Sujb-sus/wallBlog次要性能客户端文章性能:文章内容反对Markdown语法且代码高亮展现;标签性能:通过标签分类来检索文章数据;侧边栏性能:点击排行、站长举荐和标签分类等;搜寻性能:通过关键词检索文章的题目和摘要,并反对搜寻高亮;留言性能:可点赞、回复评论,统计评论以及回复总数,反对Emoji表情;其余性能:图片懒加载、分页、侧边栏吸顶以及一键置顶等;治理端权限治理:CRUD 管理员,可调配权限;文章治理:CRUD 文章,文章封面反对本地上传、文章内容反对Markdown语法编辑;标签治理:CRUD 标签,标签背景色反对用Vue-Color插件自定义抉择;留言治理:RD 评论以及回复;我的项目构造 技术利用前端:Vue、Element-UI、ES6、Webpack、Less、Axios、Markdown-Editor、Highlight、Vue-Color、Vue-Lazyload等;后端:Node、Koa2、Jwt、MongoDB、Busboy、Log4、Node-Notifier、Ora、Chalk、Rimraf等;运维:宝塔 Linux 面板、Nginx、PM2管理工具等;注意事项:我的项目启动前,须要在本地装置好MongoDB服务,请自行装置;启动本地的 mongo 服务,开始给数据库初始化一些必要数据;数据库名可自行批改,然而须要跟code/server/config.js文件同步上;登录治理后盾时,须要给数据库创立users汇合,并注册一个账号进行登录;> mongo // 开启mongo服务> show dbs // 显示数据库列表> use wallBlog // 新建一个wallBlog数据库> db.createUser({user:"wall",pwd:"123456",roles:[{role:"readWrite",db:'wallBlog'}]}) // 在wallBlog数据库创立一个wall用户,明码为123456> show users // 展现该库有哪些用户> db.auth("wall", "123456"); // 数据库认证一下用户、明码> db.users.insert({ // 往该库的users汇合插入一条数据,账号:admin 明码:123456 "pwd" : "e10adc3949ba59abbe56e057f20f883e", "username" : "admin", "roles" : [ "admin" ]})> show collections // 查问该库下的汇合(相似于mysql的表)> db.users.find() // 查问users汇合下的所有数据初始化好本地的MongoDB数据库,就能够在code/server/config.js文件下配置账号、明码以及数据库名称;npm run dev:server开启后盾接口服务;npm run dev:admin启动治理后盾界面,用下面注册的账号密码登录,并录入文章数据;npm run dev:client启动客户端页面;脚本命令本我的项目是基于 Webpack5.5 来构建与打包的。 npm run dev:admin // 本地开发治理端页面npm run dev:client // 本地开发客户端页面npm run build:admin // 我的项目打包 - 治理端npm run build:client // 我的项目打包 - 客户端npm run analyz // 查看打包信息npm run server // 启动后盾接口服务

September 1, 2021 · 1 min · jiezi

关于vue.js:在-Vue-中created-和-mounted-的区别

简略了解区别 : 1.created办法是在初始化页面之前对dom的操作。 2.mounted办法是在初始化页面之后对dom的操作。 为什么要有这个辨别? 因为有些需要就是要在页面加载之后能力申请到的,比方id,js中用document.getElementById("xxx") 一张表格来具体理解一下生命周期钩子函数 生命周期钩子组件状态最佳实际beforeCreate实例初始化之后,this指向创立的实例,不能拜访到data、computed、watch、methods上的办法和数据罕用于初始化非响应式变量created实例创立实现,可拜访data、computed、watch、methods上的办法和数据,未挂载到DOM,不能拜访到$el属性,$ref属性内容为空数组罕用于简略的ajax申请,页面的初始化beforeMount在挂载开始之前被调用,beforeMount之前,会找到对应的template,并编译成render函数-mounted实例挂载到DOM上,此时能够通过DOM API获取到DOM节点,$ref属性能够拜访罕用于获取VNode信息和操作,ajax申请beforeupdate响应式数据更新时调用,产生在虚构DOM打补丁之前适宜在更新之前拜访现有的DOM,比方手动移除已增加的事件监听器updated虚构 DOM 从新渲染和打补丁之后调用,组件DOM曾经更新,可执行依赖于DOM的操作防止在这个钩子函数中操作数据,可能陷入死循环beforeDestroy实例销毁之前调用。这一步,实例依然齐全可用,this仍能获取到实例罕用于销毁定时器、解绑全局事件、销毁插件对象等操作destroyed实例销毁后调用,调用后,Vue 实例批示的所有货色都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁-

September 1, 2021 · 1 min · jiezi

关于vue.js:Vue中的Vuex一

小编在我的项目中,常常会应用到Vuex,其实小编本人只是晓得Vuex是为了解决什么痛点,然而具体这个货色是怎么回事,小编本人有点迷迷糊糊,昨天小编痛下决心,认认真真的把Vuex的官网(https://vuex.vuejs.org/zh/)读了一遍,播种还是很多的,顺便拿进去与大家分享,也心愿能在大家今后的面试中,减少一些筹码和信念。 Vuex 是一个专为 Vue.js 利用程序开发的状态管理模式。它采纳集中式存储管理利用的所有组件的状态,并以相应的规定保障状态以一种可预测的形式发生变化。Vuex 也集成到 Vue 的官网调试工具 devtools extension (opens new window),提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试性能。这个是官网对Vuex的形容,小编之前的我的项目常常会用Vuex存储用户信息,比方以后用户的id,用户所在部门,头像等等,这些信息都是通过数据接口取得的数据,其实存储在用户浏览器的Cookie和LocalStorage都是能够的,用的时候,再从本地的相应地位获取,用户退出的时候,清空对应的信息,同样也能够较少屡次申请,并且也解决了多个组件共享数据的痛点。然而很多成型的admin都在应用Vuex,同样对于另一个前端框架赫赫有名的React也在用相似的计划(Redux),面临前端突飞猛进的倒退,学会应用Vuex就显得更为重要。Vuex次要分为四个局部,State、Getters、Mutations、Actions,小编上面就联合实例一一的解释一下。 在正式进入Vuex的世界之前,咱们须要先装置Vuex npm install vuex --save在咱们应用脚手架构建我的项目的时候,须要显示的在我的项目入口文件main.js中引入Vuex,并通过Vue.use()来装置 import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)Vuex依赖Promise,还不理解Promise的,能够查看小编之前的文章《ES6中的Promise》一、StateVuex外围就是一个store,store中的数据都寄存在State中,能够将State了解成一个大仓库,大仓库中存着咱们须要共享的数据。在组件中,能够通过this.$store获取State中的数据,为了保证数据的一致性,State中数据是不容许间接批改的,扭转 store 中的状态的惟一路径就是显式地提交 (commit) 上面咱们来创立一个最简略的store import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }})当初,你能够通过 store.state 来获取状态对象,以及通过 store.commit 办法触发状态变更: store.commit('increment')console.log(store.state.count) // -> 1最初,咱们须要把store注入到创立的Vue实例中,就像这样 new Vue({ el: '#app', store // 这个地位因为对象的key和value一样,应用es6语法能够简写成这样})那么咱们如何在 Vue 组件中展现状态呢?因为 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简略的办法就是在计算属性(computed)中返回某个状态:// 创立一个 Counter 组件const Counter = { template: `<div>{{ count }}</div>`, computed: { count () { return store.state.count } }}咱们还能够这样const Counter = { template: `<div>{{ count }}</div>`, computed: { count () { return this.$store.state.count } }}实际上以上内容,咱们就能够失常读取store中的值了,官网还提供咱们一种更简便的形式:mapState ...

September 1, 2021 · 2 min · jiezi

关于vue.js:Vue2源码解读五指令和生命周期

前言:这篇文章的外围是Vue2的指令和申明周期的架构,其立足于模板引擎、虚构Dom与Diff算法、数据响应式原理、形象语法树之上,这就像要盖一座房子,所须要的砖,水泥,钢筋都筹备好了,那么接下来就是怎么把它们组合起来施展各自的作用让这个房子的架子先搭起来呢? Vue类的创立在此简化模仿本人手写一个Vue类: Vue.jsexport default class Vue { constructor(options) { // todo }}这个Vue构造函数中传入的对象就是咱们日常实例化Vue类时的el,data,methods等等 index.htmlvar vm = new Vue({ el: '#app', data: { a: 1, b: { c: 2 } }, watch: { a() { console.log('a扭转') } }});这时候就把options存为$options,目标是为了让用户也能够应用,而后让数据变为响应式的,尔后就要进行模板编译,调用Compile类,把options中的el和上下文(vue这个实例)传过来: Vue.jsexport default class Vue { constructor(options) { // 把参数options对象存为$options this.$options = options || {} // 数据 this._data = options.data || undefined; // 数据变为响应式的,这里就是生命周期 ... // 模板编译 new Compile(options.el, this) }}Compile类-模板编译Compile.jsexport default class Compile { constructor(el, vue) { // vue实例 this.$vue = vue; // 挂载点 this.$el = document.querySelector(el); // 如果用户传入了挂载点 if (this.$el) { // 调用函数,让节点变为fragment(片段),相似于mustache种的tokens。实际上用的是AST,这里就是轻量级的,fragment let $fragment = this.node2Fragment(this.$el); // 编译 this.compile($fragment) // 替换好的内容要上树 this.$el.appendChild($fragment) } }}fragment(片段)的生成在Compile类中,创立node2Fragment()办法,目标是让所有dom节点,都进入fragment ...

September 1, 2021 · 3 min · jiezi

关于vue.js:Vue实现动态tab标签滚动定位跟随动画

只管目前大多数 UI 框架都有 tab 组件,然而有时候并不能满足需要,或者须要对组件进行二次开发,思考到总总起因,还是决定本人亲手写一个算了。 Element.getBoundingClientRect()实现其实不难,这里只需应用 getBoundingClientRect 这个函数就能够,依据文档的介绍,该办法返回元素的大小及其绝对于视窗的地位。 看图后应该不难理解,图中 0,0 指定是浏览器中窗口的左上角,因而应用该函数后元素返回的 top、bottom、left、right 都是绝对窗口左上角的地位。 设计剖析因为 tab 的数量不是固定的,很有可能超出元素边界,所以须要用外层来包含 tablist,并且是 overflow: hidden,滚动成果通过扭转 css 中的 translate 来实现。 <template> <div> <button class="cat-button" type="button" @click="addTab">增加标签</button> </div> <div class="cat-tabbar"> <div class="cat-tabbar__arrow" @click="scrollTabbar('prev')" v-if="showArrow">后退</div> <div ref="containerElement" class="cat-tabbar__container"> <ul ref="tabbarElement" class="cat-tabbar__list"> <template v-for="(item, index) in data" :key="index"> <li :class="['cat-tabbar__item', activeName === item.name && 'is-active']" @click="changeTab(index)"> <div class="tab-text">{{item.title}}</div> <div class="tab-close" v-if="data.length > 1"> <span @click.stop="removeTab(index)">X</span> </div> </li> </template> </ul> </div> <div class="cat-tabbar__arrow" @click="scrollTabbar('next')" v-if="showArrow">后退</div> </div></template>实现剖析如何计算滚动地位?只有通过 getBoundingClientRect 获得各元素的地位后,再判断 tab 是否超出父级元素的边界,而后依据以后选中的 tab 地位计算出向前或向后须要滚动多少像素。 ...

September 1, 2021 · 3 min · jiezi

关于vue.js:vue30配置vuei18n

我的项目装置vue-i18n须要装置最新版的vue-i18n npm install vue-i18n@next --save或者用yarn yarn add vue-i18n@next --save装置实现之后,能够看一下package.json文件;看是否是最新版的vue-i18n,我装置的是9.1.7。 在我的项目src文件夹中新建lang文件夹新建lang文件夹,在lang文件夹中新建三个js文件: zh-CN.js module.exports = { header:{ text:'学习' }}zh-TW.js module.exports = { header:{ text:'學習' }}index.js import { createI18n } from 'vue-i18n'import zh_CN from './zh-CN'import zh_TW from './zh-TW'// 语言库const messages = { 'zh-CN': zh_CN, 'zh-TW': zh_TW}// 默认语言// const langDefault = 'zh-CN'const langDefault = 'zh-TW'const i18n = createI18n({ locale: langDefault, //默认显示的语言 messages})export default i18n; // 将i18n裸露进来,在main.js中引入挂载将i18n裸露进来,在main.js中引入挂载main.js import i18n from './lang'import { createApp } from "vue";import App from "./App.vue";const app = createApp(App); // 创立实例app.use(i18n);app.mount("#app");页面中应用在 HTML 模板中应用 template: ...

September 1, 2021 · 1 min · jiezi

关于vue.js:Vue-中-route和router-的区别是什么

先想一下,$router是不是看着特地眼生,比方咱们罕用的传递参数的办法就是应用$router外面的history,router其实是VueRouter的一个实例,所以它是一个全局对象,蕴含了所有的子对象和属性而route是正在跳转的这个路由的部分对象,能够获取这个正在跳转的路由的name,path,params,query等等

August 31, 2021 · 1 min · jiezi

关于vue.js:Vue2源码解读三数据变化侦测数据响应式原理

Vue2.X官网文档中曾经论述了深刻响应式原理,简略来讲就是数据批改之后,被es5里边Object .defineProperty,setter拦挡到了,告诉watcher,watcher对函数进行渲染,这个过程种要创立新的虚构dom节点,比照旧的虚构dom节点,比照完之后做成一个补丁,把补丁打在实在dom构造中,实在dom再更新,视图产生扭转。 Object.defineProperty()数据劫持/数据代理利用javascript引擎赋予的性能,检测对象属性变动Object.defineProperty()办法会间接在一个对象上定义一个新属性,或者批改一个对象的现有属性,并返回此对象。 var obj = {};Object.defineProperty(obj, 'a', { value: 3})Object.defineProperty(obj, 'b', { value: 5})console.log(obj) // {a:3,b:5}console.log(obj.a, obj.b) // 3 5Object.defineProperty()能够设置额定暗藏的属性 Object.defineProperty(obj, 'a', { // value: 3, get(){}, // 是否可写 writable: true})Object.defineProperty()真正对数据的操作是他它本身的getter函数(读取)和setter函数(设置)来进行的: Object.defineProperty(obj, 'a', { // getter函数 get(){ console.log(ole.log('拜访a属性'); return 7; }, // setter函数 set(nVal) { console.log('批改a属性为'+nVal) }})console.log(obj.a); // 7obj.a = 10;console.log(obj.a); // 7由以上示例可知,当拜访obj的a属性时,值为7,当批改a属性的值之后,从打印后果看出,setter函数的确执行了,然而新值并没有赋给getter的返回值,此时的getter和setter短少了一个连贯的桥梁:变量,所以下面的代码稍作改变: var temp = '';Object.defineProperty(obj, 'a', { // getter函数 get(){ console.log(ole.log('拜访a属性'); return temp; }, // setter函数 set(nVal) { console.log('批改a属性为'+nVal); temp = nVal; }})console.log(obj.a); // 7obj.a = 10;console.log(obj.a); // 10到这里Object.defineProperty()的用法就曾经很分明了,接下来要做的就是怎么让它更好看优雅~ ...

August 31, 2021 · 3 min · jiezi

关于vue.js:js中的事件委托代理

小编明天在做我的项目的时候,在我的项目的正文中发现了对于事件委托的正文(PS:尽管上面代码写的不是事件委托的代码图片),小编作为一个干了前端三四年的小菜鸟,仍然不晓得事件委托到底是个啥,想想羞愧,特意在Vue3这个专栏两头插了这个。既然谈到了事件委托,就不能不提冒泡,那什么是事件冒泡呢?小编特意在网上找了这样一张图片上面的泡泡都很小,越往上泡泡越大。图中的泡泡能够看成是html中的DOM构造。那到底什么是冒泡呢?比方这样一个构造 <div onclick="handleDivClick()"> <ul onclick="handleUlClick()"> <li onclick="handleLiClick()">第一个元素</li> <li onclick="handleLiClick()">第二个元素</li> <li onclick="handleLiClick()">第三个元素</li> </ul></div>同样,咱们再定义三个对应的函数 function handleDivClick(){ alert('handleDivClick')}function handleUlClick(){ alert('handleUlClick')}function handleLiClick(){ alert('handleLiClick')}这个时候,咱们再点击li元素的时候,除了会执行自身的点击函数之外,还会执行ul和div元素的点击函数,也就是点击一次,会呈现三次弹窗。这个就是小编了解的冒泡,同样,在原生js中,提供了e.stopPropagation()(W3C)和e.cancelBubble = true(IE)形式阻止事件冒泡,对于Vue,同样提供了.stop修饰符。 以下内容转载自 https://www.cnblogs.com/lauzh... 那什么叫事件委托呢?它还有一个名字叫事件代理,JavaScript高级程序设计上讲:事件委托就是利用事件冒泡,只指定一个事件处理程序,就能够治理某一类型的所有事件。那这是什么意思呢?网上的各位大牛们讲事件委托基本上都用了同一个例子,就是取快递来解释这个景象,我认真琢磨了一下,这个例子还真是失当,我就不去想别的例子来解释了,借花献佛,我摘过去,大家认真体会一下事件委托到底是一个什么原理: 有三个共事预计会在周一收到快递。为签收快递,有两种方法:一是三个人在公司门口等快递;二是委托给前台MM代为签收。事实当中,咱们大都采纳委托的计划(公司也不会容忍那么多员工站在门口就为了等快递)。前台MM收到快递后,她会判断收件人是谁,而后依照收件人的要求签收,甚至代为付款。这种计划还有一个劣势,那就是即便公司里来了新员工(不论多少),前台MM也会在收到寄给新员工的快递后核实并代为签收。这里其实还有2层意思的:第一,当初委托前台的共事是能够代为签收的,即程序中的现有的dom节点是有事件的;第二,新员工也是能够被前台MM代为签收的,即程序中新增加的dom节点也是有事件的。为什么要用事件委托:一般来说,dom须要有事件处理程序,咱们都会间接给它设事件处理程序就好了,那如果是很多的dom须要增加事件处理呢?比方咱们有100个li,每个li都有雷同的click点击事件,可能咱们会用for循环的办法,来遍历所有的li,而后给它们增加事件,那这么做会存在什么影响呢?在JavaScript中,增加到页面上的事件处理程序数量将间接关系到页面的整体运行性能,因为须要一直的与dom节点进行交互,拜访dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会缩短整个页面的交互就绪工夫,这就是为什么性能优化的次要思维之一就是缩小DOM操作的起因;如果要用事件委托,就会将所有的操作放到js程序外面,与dom的操作就只须要交互一次,这样就能大大的缩小与dom的交互次数,进步性能;每个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率就越大,天然性能就越差了(内存不够用,是硬伤,哈哈),比方下面的100个li,就要占用100个内存空间,如果是1000个,10000个呢,那只能说呵呵了,如果用事件委托,那么咱们就能够只对它的父级(如果只有一个父级)这一个对象进行操作,这样咱们就须要一个内存空间就够了,是不是省了很多,天然性能就会更好。事件委托的原理:事件委托是利用事件的冒泡原理来实现的,何为事件冒泡呢?就是事件从最深的节点开始,而后逐渐向上流传事件,举个例子:页面上有这么一个节点树,div>ul>li>a;比方给最外面的a加一个click点击事件,那么这个事件就会一层一层的往外执行,执行程序a>li>ul>div,有这样一个机制,那么咱们给最里面的div加点击事件,那么外面的ul,li,a做点击事件的时候,都会冒泡到最外层的div上,所以都会触发,这就是事件委托,委托它们父级代为执行事件。事件委托怎么实现:终于到了本文的外围局部了,哈哈,在介绍事件委托的办法之前,咱们先来看一段个别办法的例子:子节点实现雷同的性能: <ul id="ul1"> <li>111</li> <li>222</li> <li>333</li> <li>444</li></ul>实现性能是点击li,弹出123: window.onload = function(){ var oUl = document.getElementById("ul1"); var aLi = oUl.getElementsByTagName('li'); for(var i=0;i<aLi.length;i++){ aLi[i].onclick = function(){ alert(123); } }}下面的代码的意思很简略,置信很多人都是这么实现的,咱们看看有多少次的dom操作,首先要找到ul,而后遍历li,而后点击li的时候,又要找一次指标的li的地位,能力执行最初的操作,每次点击都要找一次li;那么咱们用事件委托的形式做又会怎么样呢? window.onload = function(){ var oUl = document.getElementById("ul1"); oUl.onclick = function(){ alert(123); }}这里用父级ul做事件处理,当li被点击时,因为冒泡原理,事件就会冒泡到ul上,因为ul上有点击事件,所以事件就会触发,当然,这里当点击ul的时候,也是会触发的,那么问题就来了,如果我想让事件代理的成果跟间接给节点的事件成果一样怎么办,比如说只有点击li才会触发,不怕,咱们有绝招:Event对象提供了一个属性叫target,能够返回事件的指标节点,咱们成为事件源,也就是说,target就能够示意为以后的事件操作的dom,然而不是真正操作dom,当然,这个是有兼容性的,规范浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了以后节点的地位,并不知道是什么节点名称,这里咱们用nodeName来获取具体是什么标签名,这个返回的是一个大写的,咱们须要转成小写再做比拟(习惯问题): window.onload = function(){ var oUl = document.getElementById("ul1"); oUl.onclick = function(ev){ var ev = ev || window.event; var target = ev.target || ev.srcElement; if(target.nodeName.toLowerCase() == 'li'){ alert(123); alert(target.innerHTML); } }}这样改下就只有点击li会触发事件了,且每次只执行一次dom操作,如果li数量很多的话,将大大减少dom的操作,优化的性能可想而知!下面的例子是说li操作的是同样的成果,要是每个li被点击的成果都不一样,那么用事件委托还有用吗? ...

August 30, 2021 · 2 min · jiezi

关于vue.js:SpringBootVue3-项目实战打造企业级在线办公系统

download:SpringBoot+Vue3 我的项目实战,打造企业级在线办公零碎vue.js简介Vue.js读音 /vju/, 相似于 view Vue.js是前端三大新框架:Angular.js、React.js、Vue.js之一,Vue.js目前的应用和关注水平在三大框架中略微胜出,并且它的热度还在递增。 Vue.js能够作为一个js库来应用,也能够用它全套的工具来构建零碎界面,这些能够依据我的项目的须要灵便抉择,所以说,Vue.js是一套构建用户界面的渐进式框架。 Vue的外围库只关注视图层,Vue的指标是通过尽可能简略的 API 实现响应的数据绑定,在这一点上Vue.js相似于后盾的模板语言。 Vue也能够将界面拆分成一个个的组件,通过组件来构建界面,而后用自动化工具来生成单页面(SPA - single page application)零碎。 Vue.js应用文档及下载Vue.jsVue.js应用文档曾经写的很齐备和具体了,通过以下地址能够查看: https://cn.vuejs.org/v2/guide/ vue.js如果当成一个库来应用,能够通过上面地址下载: https://cn.vuejs.org/v2/guide... Vue.js基本概念首先通过将vue.js作为一个js库来应用,来学习vue的一些基本概念,咱们下载了vue.js后,须要在页面上通过script标签引入vue.js,开发中能够应用开发版本vue.js,产品上线要换成vue.min.js。 <script type="text/javascript" src="js/vue.min.js"></script>Vue实例每个 Vue 利用都是通过实例化一个新的 Vue对象开始的: window.onload = function(){ var vm = new Vue({ el:'#app', data:{message:'hello world!'}});} ...... <div id="app">{{ message }}</div>其中,el属性对应一个标签,当vue对象创立后,这个标签内的区域就被vue对象接管,在这个区域内就能够应用vue对象中定义的属性和办法。 数据与办法当一个 Vue 实例被创立时,它向 Vue 的响应式零碎中退出了其data对象中能找到的所有的属性。当这些属性的值产生扭转时,视图将会产生“响应”,即匹配更新为新的值。还能够在Vue实例中定义方法,通过办法来扭转实例中data对象中的数据,数据扭转了,视图中的数据也扭转。 window.onload = function(){ var vm = new Vue({ el:'#app', data:{message:'hello world!'}, methods:{ fnChangeMsg:function(){ this.message = 'hello Vue.js!'; } }});} ...... <div id="app"> <p>{{ message }}</p><button @click="fnChangeMsg">扭转数据和视图</button></div> ...

August 30, 2021 · 1 min · jiezi

关于vue.js:Vue源码解读四AST抽象语法树

本篇内容是在模板引擎的根底上,联合虚构DOM进行的探讨,基于三者之间的关系,总结出下图示意: graph TD A[模板语法] -->|解析| B(形象语法树AST) B --> |调用|C[渲染函数即h函数] C -->|生成| D[虚构节点diff/patch] D --> |更新| E(界面)1. 形象语法树(AST)是什么?形象语法树,Abstract Syntax Tree(简称:AST)实质上就是一个js对象。模板语法先变成形象语法树,而后再将语法树(次要起过渡作用)编译为失常的HTML语法。vue 中应用模板语法所写的html构造,不是真正的dom,vue底层视作字符串,逐字审查字符串,解析为JS对象。 2. 形象语法树和虚构节点的关系?如前文图示,模板语法->形象语法树AST->渲染函数(h函数)->虚构节点-(diff/patch)>界面那既然曾经理解了AST是由模板语法失去的,那这个处理过程到底是怎么进行的呢?先从与此相关的最根本算法说起~ 3. 相干算法储备1). 指针思维(JS中的指针只是字符串或者数组的一个下标地位,不同于C语言中的指针,多说一句,C语言中的指针是能够操作内存的)在此列一个算法例子,领会指针在js中的利用:找出字符串aaaaaabbbbbbbcccccccccccccddddd'中,间断反复呈现次数最多的字符。在解这个算法的时候,既然有“最多”,那必然是有比拟,而比拟的对象至多得有两个,因而首先就要想到咱们须要设置两个指针,而这两个指针的地位如何确定呢?再一看题目是“间断”,所以两指针的初始地位必然是相邻的,如下图:确定了i,j两指针地位后,就开始进行比拟了,比拟过程中如果i,j指向的字符雷同,则i不动,j后移;否则将j此刻的值赋给i,j后移一位,阐明在赋值之前,i,j之前的字符都是雷同的;开启新一轮i,j是否雷同的比拟,比拟的完结条件是i不小于这个字符串的长度length。剖析完解题思路,代码就好写了: // 给定一个字符串var str = 'aaaaaabbbbbbbcccccccccccccddddd';// 设置两指针var i = 0, j = 1;// 记录反复最多次数var maxRepeat = 0;// 记录反复最多的字符;var maxRepeatStr= '';// 当i还在范畴内的时候,应该持续寻找while(i <= str.length - 1) { // 看i指向的字符和j指向的字符是不是不雷同 if (str[i] ==== str[j]) { j++; } else { // console.log(i+'和'+j+'之间的文字间断雷同,字母'+str[i]+'反复了'+ (j - i) + '次') // 和以后反复次数最多的进行比拟 if (j - i > maxRepeat) { // 如果以后文字反复次数(j-i)超过了此时的最大值 // 就让它成为最大值 maxRepeat = j - i; maxRepeatStr = str[i] } i = j; j++; }}// 循环完结之后,就能够输入答案了console.log('maxRepeatChar', maxRepeatChar)2)递归深刻-即规定复现同上列举一个例子,领会递归的运算效率:试输入斐波那契数列的前10项,即1、1、2、3、5、8、13、21、34、55。而后请思考,代码是否有大量反复的计算?应该如何解决反复计算?察看推理得出,这一列数字从第三项起都是本身得前两项之和,所以每次只有前两项相加即可,高级代码如下: ...

August 30, 2021 · 4 min · jiezi

关于vue.js:vfor循环中使用require或import关键字引入本地图片

问题形容咱们做我的项目中,经常须要把图片出现到页面上,一般来说有以下几种形式 形式一(后端返回图片URL)这种形式就是后端返回图片的url地址,咱们间接img标签的src属性绑定imgUrl即可。如下代码: <div class="item" v-for="(item, index) in apiArr" :key="index"> <!-- apiArr是后端返回的数据,其中的每一项中都有一个imgUrl属性,存储的是图片的url地址 --> <img :src="item.imgUrl" alt=""></div>形式二(前端应用require)第二种形式,把图片文件存储到前端里,后端只返回图片的名字(或不返回图片数据),代码举例如下: 代码附上<template> <div class="wrap"> <div class="item" v-for="(item, index) in apiArr" :key="index"> <div class="imgWrap"> <!-- require引入图片文件模块 --> <img :src="require(`@/assets/img/${item.imgTitle}.png`)" alt="" /> <!-- 最初就变成这样的了就能失常显示了 <img src="@/assets/img/first.png" alt=""> --> </div> <div class="infoWrap"> <div><span class="bloder">名次:</span> {{ item.title }}</div> <div><span class="bloder">得分:</span> {{ item.score }}</div> </div> </div> </div></template><script>export default { data() { return { apiArr: [], }; }, mounted() { // 假如apiArr是咱们发申请后端返回的数据,外面的imgTitle属性存储的是图片的名字 // 通过require关键字引入,会主动到指定门路下的文件中寻找对应的图片文件加载进去 this.apiArr = [ { title: "冠军", score: "98.8", imgTitle: "first", }, { title: "亚军", score: "97.9", imgTitle: "second", }, { title: "季军", score: "96.2", imgTitle: "third", }, ]; },};</script>效果图如下 ...

August 30, 2021 · 1 min · jiezi

关于vue.js:超详细VueRouter手把手教程

最近在重温vue全家桶,再看一遍感觉记忆更粗浅,所以专门记录一下(本文vue-router版本为v3.x)。 1,router-view<router-view>是一个功能性组件,用于渲染门路匹配到的视图组件。能够配合<transition>和<keep-alive>应用。如果两个一起用,要确保在内层应用<keep-alive>。 <router-view></router-view><!--或--><router-view name="footer"></router-view>如果 <router-view>设置了名称,则会渲染对应的路由配置中 components下的相应组件。 2,router-link<router-link>标签反对用户在具备路由性能的利用中(点击)导航。 属性类型阐明toString/Object指标路由/指标地位的对象 replaceBoolean不留下导航记录appendBoolean在以后门路后加门路 /a => /a/btagString指定渲染成何种标签active-classString激活时应用的Class<router-link :to="{ path: '/login'}" replace tag="span"></router-link>3,重定向redirect根路由重定向到login const router = new VueRouter({ routes: [ { path: '/', redirect: '/login' } ]})动静返回重定向指标 const router = new VueRouter({ routes: [ { path: '/a', redirect: to => { // 办法接管 指标路由 作为参数 // return 重定向的 字符串门路/门路对象 }} ]})4,路由别名路由拜访/b时,URL会放弃为/b,然而路由匹配则为/a const router = new VueRouter({ routes: [ { path: '/a', component: A, alias: '/b' } ]})5,路由传参props应用props,防止和$route适度耦合,这样就能够间接在组件中应用props接管参数 ...

August 30, 2021 · 3 min · jiezi

关于vue.js:简述封装-Vue-组件过程及组件的命名规范

明天简略来说一下Vue相干的最根底的知识点 1.简述 Vue 封装的过程首先,组件能够晋升整个我的项目的开发效率。可能把页面形象成多个绝对独立的模块,解决了咱们传统我的项目开发的毛病:效率低,难保护,复用性等问题; 而后,应用Vue.extend办法创立一个组件,而后应用 Vue.component发放注册组件。子组件须要数据,能够在props中承受定义。而子组件批改好数据后,想把数据传递给父组件。则能够采纳emit办法 2.组件的命名标准给组件命名有两种形式: 1.应用链式命名 : my-component; // 在摸板字符串中<my-component></my-component>2.应用大驼峰命名 : My-Component // 在摸板字符串中<My-Component></My-Component>注 : 在非字符串模板中举荐应用形式 2 ,因为要遵循 W3C 标准中的自定义组件名(字母全小写且必须蕴含一个连字符) 防止和以后以及将来的 HTML 元素相冲突.

August 28, 2021 · 1 min · jiezi

关于vue.js:一文让你看懂什么是vuex

一文让你看懂什么是vuex为什么会有Vuex ? Vuex 是一个专为 Vue.js 利用程序开发的状态管理模式。它采纳集中式存储管理利用的所有组件的状态,并以相应的规定保障状态以一种可预测的形式发生变化。 vuex是采纳集中式治理组件依赖的共享数据的一个工具,能够解决不同组件数据共享问题。论断 批改state状态必须通过mutationsmutations只能执行同步代码,相似ajax,定时器之类的代码不能在mutations中执行执行异步代码,要通过actions,而后将数据提交给mutations才能够实现state的状态即共享数据能够在组件中援用组件中能够调用actionvuex根底-初始化性能建设一个新的脚手架我的项目, 在我的项目中利用vuex$ vue create demo开始vuex的初始化建设,抉择模式时,抉择默认模式初始化: 第一步:npm i vuex --save => 装置到运行时依赖 => 我的项目上线之后仍然应用的依赖 ,开发时依赖 => 开发调试时应用开发时依赖 就是开开发的时候,须要的依赖,运行时依赖,我的项目上线运行时仍然须要的第二步: 在main.js中 import Vuex from 'vuex'第三步:在main.js中 Vue.use(Vuex) => 调用了 vuex中的 一个install办法第四步:const store = new Vuex.Store({...配置项})第五步:在根实例配置 store 选项指向 store 实例对象import Vue from 'vue'import Vuex from 'vuex'Vue.use(vuex)const store = new Vuex.Store({})new Vue({ el: '#app', store})vuex根底-statestate是搁置所有公共状态的属性,如果你有一个公共状态数据 , 你只须要定义在 state对象中 定义state // 初始化vuex对象const store = new Vuex.Store({ state: { // 治理数据 count: 0 }})如何在组件中获取count?原始模式- 插值表达式 ...

August 28, 2021 · 2 min · jiezi

关于vue.js:vue3-axios封装接口请求

1、在我的项目中装置axiosvue add axios2、main.jsimport './plugins/axios' // 装置axios结束主动新增此行3、./plugins/axios.jsimport axios from 'axios'import qs from 'qs'import { ElMessage} from 'element-plus';const http = {}const Axios = axios.create({ timeout: 200000, withCredentials: true, // 主动携带cookie baseURL: '', // 接口地址 headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' }, validateStatus(status) { return status >= 200 && status < 300 }});// 申请拦挡Axios.interceptors.request.use(config => { // 头部如需带上token,在此处配置 return config}, err => { return Promise.reject(err)})// 响应拦挡Axios.interceptors.response.use(res => { return res;}, err => { switch (err.response.status) { case 400: ElMessage({ showClose: true, message: '申请出错', // 此处能够换成接口返回的报错信息 type: 'error' }); break; case 401: ElMessage({ showClose: true, message: '受权失败,请从新登录', // 此处能够换成接口返回的报错信息 type: 'error' }); return; case 422: ElMessage({ showClose: true, message: '参数谬误,请查看填写的参数', // 此处能够换成接口返回的报错信息 type: 'error' }); return; case 403: ElMessage({ showClose: true, message: '回绝拜访', // 此处能够换成接口返回的报错信息 type: 'error' }); break; case 404: ElMessage({ showClose: true, message: '申请谬误,未找到该资源', // 此处能够换成接口返回的报错信息 type: 'error' }); break; case 500: ElMessage({ showClose: true, message: '请从新登录', // 此处能够换成接口返回的报错信息 type: 'error' }); break; } return Promise.reject(err);})http.get = function(url, data = {}) { return new Promise((resolve, reject) => { Axios.get(url, { params: data }).then(res => { resolve(res); }).catch(err => { reject(err); }) })}http.del = function(url, data = {}) { return new Promise((resolve, reject) => { Axios.delete(url, { params: data }).then(res => { resolve(res); }).catch(err => { reject(err); }) })}http.post = function(url, data) { return new Promise((resolve, reject) => { Axios.post(url, qs.stringify(data)).then(res => { resolve(res); }).catch(err => { reject(err); }) })}export default http4、封装申请接口url.js:申请的门路index.js: 封装的接口办法url.js ...

August 28, 2021 · 2 min · jiezi

关于vue.js:动态菜单栏

要求依据接口数据动静设置菜单,层级层列都不确定。同时获取到菜单栏之后,每个栏位还须要独自二次申请接口获取栏位的数量。父节点数量须要前端解决,将所有子节点栏位加起来。 三个未知:栏位未知,层级未知,详情页面未知。 问题菜单栏层级列数都不确定,须要依据后端数据渲染,怎么解决? 这边须要用到组件递归。我的项目用的是element的el-menu。菜单栏层级列数都不确定,每个栏位须要申请不同的接口获取数据,且要渲染到对应栏位展现。不确定的多层栏位怎么申请接口。设置了一个变量,将菜单栏赋值。而后变量递归,依据取得的接口数据对变量参数进行批改。且怎么更新多不确定的多层栏位的组件上? 对组件进行ref设置。递归组件的refs,顺次强制更新。点击菜单栏跳转不同详情页面,栏位数量未知那详情页面也地位,页面怎么解决能力实时依据不同菜单展现数据? 和后端确定,栏位信息设置type,且type雷同须要跳转雷同模版页面,依据携带的数据不同,页面展现不同数据。router-view应用keep-alive,将路由信息设置为key。这样路由信息不同页面会从新actived。再设置几套页面模板。依据type不同跳转不同模板进行页面渲染。详情页面的信息都在菜单栏上,怎么在点击的时候能获得这些栏位信息。能够放在路由meta上,点击菜单的时候动静设置meta。详情页进去的时候,钩子函数监听获取这些数据。层级未知,父节点数量须要将所有子节点栏位加起来,怎么相加?应用computed,传入你以后节点的数据,递归数据以后节点数据。这样能够计算你上面所有子节点的数据。

August 28, 2021 · 1 min · jiezi

关于vue.js:Vue动态路由路由参数改变视图不更新问题的解决

1.开发环境 vue2.电脑系统 windows10专业版3.在开发的过程中,咱们常常会应用路由传参,还会传参到同一个路由,然而会遇到批改了页面参数,然而视图仍旧是之前的内容,没有进行刷新,上面我来分享一下解决办法。4.废话不多说,间接上效果图: 起因剖析:当 应用路由参数时,例如从/EnterpriseRegisterManagement/Enterpriserealname?state=0到/EnterpriseRegisterManagement/Enterpriserealname?state=1,原来的组件实例会被复用.因为两个路由都渲染同个组件,比起销毁再创立,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再次被调用。复用组件时,绝对路由参数的变动作出响应的话,你能够简略的watch(监听变动)$route 对象5.办法一:watch(监听变动) $route 对象: watch: { // 监听路由的变动 参数变动时更新公布订阅数据 $route(to, from) { // console.log(to); // console.log(to.query.state); // console.log(from); console.log(from.query.state); if (to.query.state) { this.$nextTick(() => { this.state = to.query.state; this.$forceUpdate(); }); } }, },6.办法二:应用路由导航守卫: beforeRouteUpdate (to, from, next) { // 做一些想要做的解决... next() // 肯定要有next },7.本期的分享到了这里就完结啦,心愿对你有所帮忙,让咱们一起致力走向巅峰。

August 28, 2021 · 1 min · jiezi

关于vue.js:深入-Vue3-源码学习响应式原理

Vue2 响应式原理学过 Vue2 的话应该晓得响应式原理是由 Object.defineProperty 对数据进行劫持,再加上订阅公布,实现数据的响应的。 Object.defineProperty 存在以下几个方面的毛病。 初始化的时候须要遍历对象的所有属性进行劫持,如果对象存在嵌套还须要进行递归。导致初始化的时候须要耗费一些资源用于递归遍历。从下面能够推导出 Vue2 对于新增、删减对象属性是无奈进行劫持,须要通过 Vue.set、Vue.delete 进行操作。每个调用者会生成一个 Watcher,造成内存占用。无奈劫持 Set、Map 对象。Vue3 响应式原理针对以上问题,Vue3 改用了 ES6 原生的 Proxy 对数据进行代理。 Proxy 根本用法如下: const reactive = (target) => {  return new Proxy(target, {    get(target, key) {      console.log("get: ", key);      // return Reflect.get(target, key);      return target[key];    },    set(target, key, value) {      console.log("set: ", key, " = ", value);      // Reflect.set(target, key, value);      target[key] = value;      return value;    },  });};var a = reactive({ count: 1 });console.log(a.count);a.count = 2;console.log(a.count);// log 输入// get:  count// 1// set:  count  =  2// get:  count// 2如此便可检测到数据的变动。接下来只需在 get 进行收集依赖,set 告诉依赖更新。 接下来还需借助 effect、track 和 trigger 办法。 effect 函数传入一个回调函数,回调函数会立刻执行,并主动与响应式数据建设依赖关系。 track 在 proxy get 中执行,建设依赖关系。 trigger 响应式数据发生变化时,依据依赖关系找到对应函数进行执行。 代码实现如下: const reactive = (target) => {  return new Proxy(target, {    get(target, key) {      console.log("[proxy get] ", key);      track(target, key);      // return Reflect.get(target, key);      return target[key];    },    set(target, key, value) {      console.log("[proxy set]  ", key, " = ", value);      // Reflect.set(target, key, value);      target[key] = value;      trigger(target, key);      return value;    },  });};// 用于寄存 effect 传入的 fn,便于 track 时找到对应 fnconst effectStack = [];// 用于保留 响应式对象 和 fn 的关系// {//   target: {//     key: [fn, fn];//   }// }const targetMap = {};const track = (target, key) => {  let depsMap = targetMap[target];  if (!depsMap) {    targetMap[target] = depsMap = {};  }  let dep = depsMap[key];  if (!dep) {    depsMap[key] = dep = [];  }  // 建设依赖关系  const activeEffect = effectStack[effectStack.length - 1];  dep.push(activeEffect);};const trigger = (target, key) => {  const depsMap = targetMap[target];  if (!depsMap) return;  const deps = depsMap[key];  // 依据依赖关系,找出 fn 并从新执行  deps.map(fn => {    fn();  });};const effect = (fn) => {  try {    effectStack.push(fn);    fn();  } catch (error) {    effectStack.pop(fn);  }};var a = reactive({ count: 1 });effect(() => {  console.log("[effect] ", a.count);});a.count = 2;// log 输入// [proxy get]  count// [effect]  1// [proxy set]   count  =  2// [proxy get]  count// [effect]  2以上代码并不是 Vue3 的源码,而是 Vue3 响应式的原理,相比起 Vue2 要更加简略。 执行程序为 调用 reactive 代理响应式对象;调用 effect ,会将 fn 保留至 effectStack,在执行 fn 时会触发 Proxy 的 get;从 Proxy 的 get 触发 track,将数据与 fn 建设关系;批改响应式数据,触发 Proxy 的 set;从 Proxy 的 set 触发 trigger,从而找出对应的 fn 并执行。弄清楚原理再去看源码会简略很多,上面咱们一起去看下源码。 ...

August 28, 2021 · 2 min · jiezi

关于vue.js:vueroute-切换-视图不更新

问题:在应用了雷同组件之后,切换路由,不执行created 和 mounted 办法,无奈获取数据例如:从 /gameInfo 切换到 /gameInfo/details 只有第一次切换的时候,会执行 mounted,之后就不会执行 mounted了。 解决:1,通过 watch 监听 以后页面的 route 变动 watch: { '$route': function() { // 首页刷新之后,第一次没有监听 route ,第二次之后,才开始执行 console.log('route',this.$route) } },2,在 <router-view> 增加 key 属性 <keep-alive> <router-view :key="$route.path" /></keep-alive>以上是网上的解决办法。但我遇到的问题,仍旧没有解决,最初排查发现,是 <router-view> 的地位有问题。 // layout/index.vue<template> <div> <top-menu /> <game-menu v-if="$route.path != '/home'" /> <transition name="fade-transform" mode="out-in"> <keep-alive> <router-view :key="$route.path" /> </keep-alive> </transition> </div></template>此时 <router-view> 监听的是 里面的导航,所以,当我点击 <game-menu /> 组件里的导航栏,页面并不会更新。最初将 <router-view> 放到了 <game-menu /> 里,问题解决,每次切换路由,都会执行 mounted 办法了。 ...

August 28, 2021 · 1 min · jiezi

关于vue.js:Vue生命周期函数

明天小编持续和大家一起钻研Vue,明天要一起探讨的是生命周期函数,在小编的了解中,生命周期就是在肯定工夫主动执行的函数,就相似原生js中的window.onload。小编先拷贝一张来自Vue官网文档中的一张图其实在这张图中,曾经残缺的阐明了Vue中每个生命周期中的函数的执行工夫,然而小编还是要用代码联合正文,更加具体的阐明生命周期。 const app = Vue.createApp({ data(){ return { message: 'helllo world' } }, methods:{ handllItemClick(){ // 绑定点击事件。性能就是点击DOM元素的时候,更新data中的数据 this.message = '666' } }, // 在实例生成之前会主动执行的函数 beforeCreate(){ console.log('beforeCreate') }, // 在实例生成之后会主动执行的函数 created(){ console.log('created') }, // 在模板曾经被变成函数之后立刻执行的函数(在组件内容被渲染到组件之前主动执行的函数) beforeMount(){ console.log(document.getElementById('root').innerHTML) // 空白 console.log('beforeMount') }, // 在组件内容被渲染到页面之后主动执行的函数 mounted(){ console.log(document.getElementById('root').innerHTML) // <div>hello world</div> console.log('mounted') }, // 当data中的函数变动会立刻主动执行的函数 beforeUpdate(){ console.log(document.getElementById('root').innerHTML) // <div>hello world</div> console.log('beforeUpdate') }, // 当data中的函数变动,同时页面实现更新后会主动执行的函数 updated(){ console.log(document.getElementById('root').innerHTML) // <div>666</div> console.log('updated') }, // 当Vue利用生效时,主动执行的函数 beforeUnmounted(){ console.log(document.getElementById('root').innerHTML) // <div>hello world</div> console.log('beforeUnmounted') }, // 当Vue利用生效时,且 data 数据齐全销毁(DOM齐全销毁)之后,主动执行的函数 unmounted(){ console.log(document.getElementById('root').innerHTML) // 空白 console.log('unmounted') }, template: '<div v-on:click="handllItemClick">{{ message }}</div>'})const vm = app.mount('#root')至于生命周期在理论我的项目中的应用,每个生命周期适宜做哪些事件,小编会在今后的文章陆续更新,明天的目标就是要和生命周期函数混个脸熟。一起加油!大家还能够扫描二维码,关注我的微信公众号,蜗牛全栈 ...

August 27, 2021 · 1 min · jiezi

关于vue.js:Vue源码解读二虚拟DOM与Diff算法

什么是虚构DOM? virtual DOM,用一般js对象来形容DOM构造,因为不是实在DOM,所以称之为虚构DOM。虚构DOM在Vue中的利用Vue的编译器在编译模板之后,会把这些模板编译成一个渲染函数。而函数被调用的时候就会渲染并且返回一个虚构DOM的树。Vue的Virtual DOM Patching算法是基于Snabbdom的实现。当咱们有了这个虚构的树之后,再交给一个Patch函数,负责把这些虚构DOM真正施加到实在的DOM上。在这个过程中,Vue有本身的响应式零碎来侦测在渲染过程中所依赖到的数据起源。在渲染过程中,侦测到数据起源之后就能够准确感知数据源的变动。到时候就能够依据须要从新进行渲染。当从新进行渲染之后,会生成一个新的树,将新的树与旧的树进行比照,就能够最终得出应施加到实在DOM上的改变。最初再通过Patch函数施加改变。简略点讲,在Vue的底层实现上,Vue将模板编译成虚构DOM渲染函数。联合Vue自带的响应零碎,在应该状态扭转时,Vue可能智能地计算出从新渲染组件的最小代价并应到DOM操作上。Snabbdom虚构DOM实现思路解读以此html页面内容为例 body> <button id="btn">点击我扭转</button> <div id="container"></div> <script src="/xuni/bundle.js"></script></body>const container = document.getElementById('container');const btn = document.getElementById('btn')const myVnode1 = h('ul', {}, [ h('li', {key: 'A'}, 'A'), h('li', {key: 'B'}, 'B'), h('li', {key: 'C'}, 'C'), h('li', {key: 'D'}, 'D'), h('li', {key: 'E'}, 'E')]);patch(container, myVnode1)解读h函数h函数就是vue中的createElement办法,它是用来创立虚构DOM,追踪DOM变动,最终返回虚构DOM,即js封装好的节点对象。h函数的简略剖析如下: /** 低配版本的h函数,这个函数必须接管3个参数,缺一不可 相当于它的重载性能较弱 也就是说,调用的时候状态必须是以下三种之一: 状态①:h('div', {}, '文字') 状态二:h('div', {}, []) 状态三:h('div', {}, h())*/export default function(sel, data, c) { // 查看参数的个数 if (arguments.length !== 3) { throw new Error('h函数必须传入3个参数,低配版h函数'); } // 查看参数c的类型 if (typeof c === 'string' || typeof c === 'number') { // 阐明当初调用h函数就是状态① return vnode(sel, data, undefined, c, undefined); } else if (Array.isArray(c)) { // 阐明当初调用H函数就是状态② let children = [] // 遍历c,收集children for (let i = 0; i < c.length; i++) { // c[i] 必须是一个对象,如果不满足 if (!(typeof c[i] === 'object' && c[i].hasOwnProperty('sel'))) { throw new Error('传入的数组参数中有项不是h函数') } // 这里不必执行c[i],因为测试语句中曾经调用了,既是执行h函数了 //此时只须要收集好就能够了 children.push(c[i]) } return vnode(sel, data,children, undefined, undefined) } else if (typeof c === 'object' && c.hasOwnProperty('sel')) { // 阐明是调用h函数状态③ // 即传入的c是惟一的孩子,间接存入children let children = [c]; return vnode(sel, data, children, undefined, undefined) } else { throw new Error('传入的第三个参数类型不对') }}vNode函数就是用来标准h中传入的参数,将传入的参数data中的属性key提出来,作为返回值对象中的一个属性,返回整合之后的对象 ...

August 27, 2021 · 4 min · jiezi

关于vue.js:vue调用支付宝支付接口返回form

1.开发环境 vue2.电脑系统 windows10专业版3.在开发的过程中,咱们在领取的时候会抉择领取形式,上面我来分享一下调用后端接口(后端接口支付宝的领取接口)返回form,前端怎么解决呢?办法如下。4-1:在template中增加如下代码: <div class="Graphicdetailsfooter" v-html="this.ConfirmOrderobj.alipayPayObj.cont">{{ this.ConfirmOrderobj.alipayPayObj.cont }}</div>4-2.在抉择领取形式的办法中,增加如下代码: const div = document.createElement("divform"); div.innerHTML = this.ConfirmOrderobj.alipayPayObj.cont; document.body.appendChild(div); document.forms[0].acceptCharset = "GBK"; // 放弃与支付宝默认编码格局统一,如果不统一将会呈现:调试谬误,请回到申请起源地,从新发动申请,错误代码 invalid-signature 谬误起因: 验签出错,倡议查看签名字符串或签名私钥与利用公钥是否匹配 document.forms[0].submit();//留神:this.ConfirmOrderobj.alipayPayObj.cont 就是后端返回的支付宝的form5.成果如下:6.本期的分享到了这里就完结啦,心愿对你有所帮忙,让咱们一起致力走向巅峰。

August 27, 2021 · 1 min · jiezi

关于vue.js:vue解决跨域方法

1.什么是跨域跨域指浏览器不容许以后页面的所在的源去申请另一个源的数据。源指协定,端口,域名。只有这个3个中有一个不同就是跨域。 举例: 以后页面url 被申请页面url 是否跨域 起因http://www.test.com/ http://www.test.com/index.html 否 同源(协定、域名、端口号雷同)http://www.test.com/ https://www.test.com/index.html 跨域 协定不同(http/https)http://www.test.com/ http://www.baidu.com/ 跨域 主域名不同(test/baidu)http://www.test.com/ http://blog.test.com/ 跨域 子域名不同(www/blog)http://www.test.com:8080/ http://www.test.com:7001/ 跨域 端口号不同(8080/7001)2.跨域解决办法应用proxy代理: 首先创立一个 vue.config.js文件 module.exports = { publicPath: './', outputDir: 'dist', assetsDir: 'static', lintOnSave: process.env.NODE_ENV === 'development', productionSourceMap: false, devServer: { port: port, open: true, overlay: { warnings: false, errors: true }, // 代理跨域的配置 proxy: { // 当咱们的本地的申请 有/api的时候,就会代理咱们的申请地址向另外一个服务器发出请求 // 这里的api 示意如果咱们的申请地址有/api的时候,就出触发代理机制 // localhost:8888/api/abc => 代理给另一个服务器 '/api': { target: 'www.baidu.com', // 跨域申请的地址 changeOrigin: true, // 只有这个值为true的状况下 才示意开启跨域 secure: false, // 如果是https接口,须要配置这个参数 // 门路重写 pathRewrite: { // 从新路由 localhost:8888/api/login => www.baidu.com/api/login '^/api': '' // 假如咱们想把 localhost:8888/api/login 变成www.baidu.com/login 就须要这么做 } } } }}// 留神:批改了配置文件后肯定要重启才会失效3.小结: 代理跨域的次要形式是利用服务器申请服务器的形式避过跨域问题来实现的.大略的流程: 浏览器===>代理服务器===>指标服务器. ...

August 27, 2021 · 1 min · jiezi

关于vue.js:Vue-怎么实现组件之间数据互传

父组件数据传给子组件 HTML构造 : <!--操作div的就是父组件--><div id="example"> <parent message="message"></parent> <!--子级组件--> </div>实现父传子 1. 通过 props 自定义属性 父级组件 : 要设置传进去的属性 ( message ) <parent message="message"></parent>子组件 : 须要在子组件内用props自定义属性接管 // 子组件 export default { name:'Children', props:['message'] } 应用的办法 {{message}} , v-bind 2. $attrs (能够代替props父传子)子组件能够间接应用 ( $attrs.父级传递过去的属性值) <!--在子组件内--> <div id="Children"> <p>{{$attrs.message}}</p> </div>props和$attrs的区别是什么 ?1. props是父类向子类传递并且须要子类被动接管的属性 2. $attrs 默认是父类传递到字类根元素的属性,字类不必被动接管会间接放在子类根元素上 父组件如何操作子组件的元素?**HTML构造 :**<!--操作div的就是父组件--><div id="example"> <parent ref="parent"></parent> <!--子级组件--> </div>1. ref$refs 能够间接获取到子类的元素和值 this.$refs.parent.'要操作的子级元素' 2. $childrenthis.$children[0].'要操作的子级元素'区别 $refs能够间接获取到增加了ref属性的元素 $children 获取到的是所有子元素,然而(子元素是无序的,不会保障程序) 先人和后辈之间传值 因为嵌套层数过多,传递props不切实际,vue提供了 provide/inject API 来实现该工作 provide/inject : 可能实现先人给后辈传值 ...

August 27, 2021 · 1 min · jiezi

关于vue.js:深入-Vue3-源码学习初始化流程

搭建调试环境为了弄清楚 Vue3 的初始化,倡议先克隆 Vue3 到本地。 git clone https://github.com/vuejs/vue-next.git装置依赖 npm install批改 package.json,将 dev 命令加上 --sourcemap 不便调试,并运行 npm run dev // package.json..."scripts": { "dev": "node scripts/dev.js --sourcemap", ...}...在 packages/vue 目录下减少 index.html,内容如下 <!-- index.html --><div id="app"> {{ count }}</div><script src="./dist/vue.global.js"></script><script> Vue.createApp({ setup() { const count = Vue.ref(0); return { count }; } }).mount('#app');</script>在浏览器关上 index.html,程序失常运行则能够开始进行下一步调试。 进行调试如果在上面的流程中迷失了方向,倡议先看一下结尾的总结,再回过头来看这一段。 在 createApp 的地位打上断点,而后刷新页面进断点,开始调试。 具体大家能够自行调试,我在这里就大略形容一下初始化流程。 createApp进入 createApp 外部,会跳转到 packages/runtime-dom/src/index.ts 的 createApp,执行实现返回 app 实例。 // packages/runtime-dom/src/index.tsexport const createApp = ((...args) => { const app = ensureRenderer().createApp(...args) ... return app}) as CreateAppFunction<Element>接下来持续看 ensureRenderer 的实现。 ...

August 27, 2021 · 4 min · jiezi

关于vue.js:leafletjs-实现离线地图

在地图Gis模块开发常见需要性能点如下: 打点相干:地图打点、海量点加载、点聚合点击点,自定义窗口显示点位详细信息。区域绘制-省市区边界款式批改、3D暗影等框线点-中心点画圆框选、多边形绘制框选判断点是否在选框范畴之内。场景:在前端需要里实现以上地图相干性能时,咱们经常能够应用百度地图、高德地图、谷歌地图、天地图等相干在线地图服务及api疾速实现地图相干性能。然而对于有些客户是政务零碎,只能应用内网不能提供外网的时候,以上地图服务就不能应用了,这个时候就须要用到离线地图服务。 leaflet.js的应用1.下载地图瓦片,搭建本地地图服务,引入服务js 初始化地图 3.绘制省市区边线 4.地图打点 //list为地图点位数组对象,每一项蕴含经纬度坐标点等信息。 function setMakers(list){ markers = L.markerClusterGroup(); for (var i = 0; i < list.length; i++) { var a = list[i]; var data = a var marker = L.marker(new L.LatLng(a.lat, a.lon), {title: '' , icon :getIcon(a.index)}); let showItem = [ { key: '摄像头名称', val: data.nodeName }, // 0是未知,1失常,2离线 { key: '状态', val: data.onlineStatus === 2 ? '离线' : data.onlineStatus === 1 ? '失常' : '未知' }, { key: '类型', val: getDeviceType(data.deviceType) }, { key: '经度', val: data.lon }, { key: '纬度', val: data.lat } ] let basicInfo = '' for (let i = 0; i < showItem.length; i++) { basicInfo += '<div class="info_row"><span class="info_lable">' + showItem[i].key + ':</span>' + showItem[i].val + '</div>' } let myTitle = '<div class="info_title">视频详情</div>' window.currentData = data window.basicDom = '<div class="beauty-scroll">' + myTitle + basicInfo + '<div id="map_video_box" class="video_play_block">' + '<img class="map_play_btn" src="./src/assets/img/play.png" onclick="playVideo(currentData)">'+ `<div id="play_video_block" style="width:100%;height:99%;"></div>` + '</div>' + '</div>' marker.bindPopup(basicDom); markers.addLayer(marker); mapMarkers.push(marker) } map.addLayer(markers); }6、点击框选范畴按钮,绘制框选多边形 ...

August 27, 2021 · 2 min · jiezi

关于vue.js:vueantdesignvue实现导航菜单循环遍历

demo地址:https://gitee.com/shjin/menu_...1.创立vue我的项目 vue create menu_demo2.装置ant-design-vue yarn add ant-design-vue3.main.js文件引入ant-design-vue import Antd from 'ant-design-vue';import 'ant-design-vue/dist/antd.css';Vue.use(Antd);4.菜单index.js import Menu from "ant-design-vue/es/menu";import Icon from "ant-design-vue/es/icon";const { Item, SubMenu } = Menu;export default { name: "SMenu", props: { menu: { type: Array, required: true }, theme: { type: String, required: false, default: "light" }, mode: { type: String, required: false, default: "inline" }, collapsed: { type: Boolean, required: false, default: false }, }, data() { return { openKeys: [], cachedOpenKeys: [], selectedKeys:['user1'] }; }, computed: { rootSubmenuKeys: vm => { const keys = []; vm.menu.forEach(item => keys.push(item.path)); return keys; } }, mounted() { this.updateMenu(); }, watch: { collapsed(val) { if (val) { this.cachedOpenKeys = this.openKeys.concat(); this.openKeys = []; } else { this.openKeys = this.cachedOpenKeys; } }, $route: function() { this.updateMenu(); } }, methods: { // select menu item onOpenChange(openKeys) { // 在程度模式下时执行,并且不再执行后续 if (this.mode === "horizontal") { this.openKeys = openKeys; return; } // 非程度模式时 const latestOpenKey = openKeys.find(key => !this.openKeys.includes(key)); if (!this.rootSubmenuKeys.includes(latestOpenKey)) { this.openKeys = openKeys; } else { this.openKeys = latestOpenKey ? [latestOpenKey] : []; } }, updateMenu() { const routes = this.$route.matched.concat(); const { hidden } = this.$route.meta; if (routes.length >= 3 && hidden) { routes.pop(); this.selectedKeys = [routes[routes.length - 1].path]; } else { this.selectedKeys = [routes.pop().path]; } const openKeys = []; if (this.mode === "inline") { routes.forEach(item => { openKeys.push(item.path); }); } //update-begin-author:taoyan date:20190510 for:online表单菜单点击开展的一级目录不对 if (!this.selectedKeys || this.selectedKeys[0].indexOf(":") < 0) { this.collapsed ? (this.cachedOpenKeys = openKeys) : (this.openKeys = openKeys); } //update-end-author:taoyan date:20190510 for:online表单菜单点击开展的一级目录不对 }, // render renderItem(menu) { if (!menu.hidden) { return menu.children && !menu.alwaysShow ? this.renderSubMenu(menu) : this.renderMenuItem(menu); } return null; }, renderMenuItem(menu) { let props = { to: { name: menu.name } }; const attrs = { target: menu.target }; return ( <Item {...{ key: menu.key }}> <div {...{ props, attrs }} > {this.renderIcon(menu.icon)} <span>{menu.title}</span> </div> </Item> ); }, renderSubMenu(menu) { const itemArr = []; if (!menu.alwaysShow) { menu.children.forEach(item => itemArr.push(this.renderItem(item))); } return ( <SubMenu {...{ key: menu.key }}> <span slot="title"> {this.renderIcon(menu.icon)} <span>{menu.title}</span> </span> {itemArr} </SubMenu> ); }, renderIcon(icon) { if (icon === "none" || icon === undefined) { return null; } const props = {}; typeof icon === "object" ? (props.component = icon) : (props.type = icon); return <Icon {...{ props }} />; } }, render() { const { mode, theme, menu } = this; const props = { mode: mode, theme: theme, openKeys: this.openKeys }; const on = { select: obj => { this.selectedKeys = obj.selectedKeys; this.$emit("select", obj); }, openChange: this.onOpenChange }; const menuTree = menu.map(item => { if (item.hidden) { return null; } return this.renderItem(item); }); return ( <Menu vModel={this.selectedKeys} {...{ props, on: on }}> {menuTree} </Menu> ); }};5.联合应用 ...

August 27, 2021 · 4 min · jiezi

关于vue.js:Vue3组件

上一篇文章小编用Vue的语法编写了一个简略的todoList,实现的逻辑比较简单,然而在理论我的项目中,会波及到一些比较复杂的逻辑,比方在item中增加各种各样的标签,再加上甲方爸爸提出各种各样的需要,最初可能把咱们搞到解体。所以引入了在组件化的概念。在组件化之前,小编先跟大家补充一些基础知识。一、数据绑定和差值表达式上一篇文章小编要实现的成果是这样的 这个时候,可能有这样的需要,比方我想将文本框内输出的内容作为button标签的title,或者想把文本框的内容实时的显示在按钮外面。这时候,咱们须要将代码批改成这样: Vue.createApp({ data() { return { list: ['item1', 'item2', 'item3', 'item4'], inputVal: "" } }, methods: { handleAddItem() { this.list.push(this.inputVal) this.inputVal = '' } }, template: ` <div> <input v-model="inputVal" /> // v-bind能够实现将data中的数据绑定在指定属性上,包含html原生标签属性和后续组件传值 // v-bind:title="inputVal"能够简写成:title="inputVal" <button v-bind:title="inputVal" v-on:click="handleAddItem">减少{{ inputVal }}</button> <ul> // {{}} 叫做差值表达式,能够实现与data中的数据动静绑定, // 能够实现【把文本框的内容实时的显示在按钮外面】 <li v-for="(item,index) of list">{{item}}</li> </ul> </div> `}).mount('#root')接下来,咱们可能不仅仅要在ul中增加li,可能两头还要增加各种各样的标签,比方span、div...这个时候,咱们须要引入组件的概念,组件在小编看来,能够了解成一个iframe,至于iframe外面的货色咱们并不关怀。然而组件比iframe好的中央是能够互相通信和数据传输。 const app = Vue.createApp({ // 定义Vue实例 data() { return { list: ['item1', 'item2', 'item3', 'item4'], inputVal: "" } }, methods: { handleAddItem() { this.list.push(this.inputVal) this.inputVal = '' } }, template: `<div> <input v-model="inputVal" /> <button v-bind:title="inputVal" v-on:click="handleAddItem">减少{{ inputVal }}</button> <ul> // 通过v-bind:content="item"将父页面的值通过自定义属性content传递到子组件中 <todo-item v-for="item of list" v-bind:content="item" /> </ul></div>`}).mount('#root')app.component('todo-item',{ // 注册组件 props:['content'], // 子组件承受父组件传递过去的值 template: '<div>{{ content }}</div>'})又是前端提高的一天,大家一起加油!大家还能够扫描二维码,关注我的微信公众号,蜗牛全栈 ...

August 26, 2021 · 1 min · jiezi

关于vue.js:Vue页面前进刷新后退缓存动画解决方案vcathistory轻量级插件

vcat-history反对vue3反对TypeScript后退后退动画后退刷新后退缓存无代码净化 插件装置 # 目前仅反对vue3我的项目npm i vcat-history问题形容APP我的项目中页面的后退后退是最根本的性能,那么在Vue我的项目中如何实现? 动画实现的形式大多数都是通过监听 popstate 事件和 router 来判断页面后退或后退,配合 transition 实现动画成果。这是我在最后开发中实现的形式,不过在理论我的项目中体验并不完满,偶然会有bug,尽管不影响应用。 缓存为了让用户有最佳的体验,在用户关上新页面的时候,新页面上的数据是最新的(刷新),而当用户后退到上个页面的时候,上个页面须要复原到之前的状态(包含数据和滚动条的地位),这其中就须要用到 keep-alive 来对页面进行缓存。 <keep-alive> <router-view> <!-- 被缓存的视图 --> </router-view></keep-alive>解决在最新的计划中,我的想法是应用vuex来治理路由的历史状态,因为页面是依照肯定程序关上的,例如 A > B > C 这样的程序关上页面,那就依照程序增加到历史状态中 [A, B, C] ,在每次插入记录之前都会查看列表中是否存在雷同的记录,如果下个页面关上的是D,因为D不存在,所以列表是 [A, B, C, D] ,阐明页面是后退的,如果下个页面关上的是B,因为B存在,所以是后退操作,最初列表更新 [A, B] 。 依照这样的逻辑,后退的页面都进行 keep-alive ,后退的页面放入一个 exclude 列表中,这样就实现了后退后退动画,后退刷新后退缓存的成果。 应用// 在 router 中援用import { initRouter } from "vcat-history";// 替换原理的 createRouterconst router = initRouter({ history: createWebHashHistory(), routes})<!-- App.vue --><template> <router-view v-slot="{ Component }"> <transition :name="transitionName"> <keep-alive :exclude="excludePages"> <component :is="Component" :key="$route.fullPath" /> </keep-alive> </transition> </router-view></template><script lang="ts">import { defineComponent, computed } from "vue";import { state } from "vcat-history";export default defineComponent({ name: "App", setup() { const transitionName = computed(() => state.transitionName); const excludePages = computed(() => state.excludePages); return { transitionName, excludePages, }; },});</script><!-- 默认动画款式,.slide-left后退,.slide-right后退 -->如果感觉好用,请举荐给别人!有问题请留言,感激反对!Demo链接 ...

August 26, 2021 · 1 min · jiezi

关于vue.js:Vue源码解读一模板引擎

什么是模板引擎?模板引擎是为了使用户界面与业务数据(内容)拆散而产生的,它能够生成特定格局的文档,用于网站的模板引擎就会生成一个规范的文档,就是将模板文件和数据通过模板引擎生成一个HTML代码。 本篇内容须要的js常量及dom构造<body> <ul id="list"></ul> <script> var arr = [ {name: '小明', age:23}, {name: '小红', age:25}, {name: '小强', age: 27} ] </script></body>模板引擎的倒退纯dom法-创立节点法var list = document.getElementById('list')for (var i = 0; i < arr.length; i++) { // 每遍历一项,都要用DOM办法创立li标签 var oli = document.createElement('li'); var hdDiv = document.createElement('div'); hdDiv.className = 'hd'; hdDiv.innerText = arr[i].name + '根本信息'; var dbDiv = document.createElement('div'); dbDiv.className = 'db'; dbDiv.innerText = arr[i].name + '的根本信息'; var p1 = document.createElement('p') p1.innerText = '姓名' + arr[i].name // 创立的节点是孤儿节点,必须上树能力被用户看见 dbDiv.appendChild(p1) oli.appendChild(hdDiv) oli.appendChild(dbDiv) list.appendChild(oli)}这种形式内存开销大,繁冗简短。 ...

August 26, 2021 · 4 min · jiezi

关于vue.js:实现nm布局模板

在页面布局模板的开发场景中,咱们经常须要实现22,33,44等布局款式,能够对立形象为 n行m列的布局。那如何实现一个nm的布局款式呢?本篇文章向大家分享一些我的想法。 1、提供两个输入框给用户,让用户本人输出行和列的数量。2、获取用户输出的行列,将行、列参数传给模板组件3、模板组件内应用双层for循环渲染页面的每一个盒子。应用百分比布局动静计算盒子对应的宽、高占比。宽:Math.round(100 / this.row),高:Math.round(100 / this.column)。给style属性绑定动静的宽高百分比。依据for循环渲染的index,设置每个盒子的惟一标识。具体循环代码如下: <div v-for="(item, ind) in column" :key="ind" class="item_block_row" :style="{ width: '100%', height: '100%' }" > <div v-for="(item2, ind2) in row" :key="ind2" :style="{ width: itemWidth - 1 + '%', height: itemHeight2 - 1 + '%' }" class="item_block" > <i v-show=" list2 && list2.length && playDomName !== 'wasm_player_block_' + ind + '_' + ind2 " class="el-icon-caret-right template_play_btn" @click="playVideo(ind, ind2)" ></i> <i v-if="list2 && list2.length" class="wasm_player_block" :class="'wasm_player_block_' + ind + '_' + ind2" :id="'play_video_' + ind + '_' + ind2" style="width:100%;height:100%;" ></i> </div> </div>4、在每一个盒子外部,如果咱们须要动态显示数组的每一项,能够将一维数组解决为二维数组,二维数组的每一项正好对应布局页面的每一个盒子。即可进行数据的展现。 ...

August 26, 2021 · 1 min · jiezi

关于vue.js:Vue3实现列表循环

明天小编和大家一起在Vue的路上摸索,要实现的性能是这样的。现将默认 数组内的渲染到页面上,而后点击按钮之后,将文本框内的数据增加到列表上,成果如下。 源码是这样的,上面我就联合代码中的正文来阐明一下外围的代码 Vue.createApp({ data(){ return { list:['item1','item2','item3','item4'], // 绑定v-for循环的列表 inputVal:"" // 绑定文本框的内容 } }, methods:{ handleAddItem(){ // 绑定按钮的点击事件 this.list.push(this.inputVal) // 批改绑定的数据,在数组中减少数据 this.inputVal = '' // 对绑定的值从新赋值,清空文本框内的值 } }, template:` <div> // 通过v-model绑定表单元素的值,也就是常说的数据双向绑定 <input v-model="inputVal" /> // v-on:click是绑定按钮的点击事件,能够通过@click语法糖来代替 <button v-on:click="handleAddItem">减少</button> <ul> // v-for和上一篇文章的v-if相似,都叫做指令 // v-for用作数据的循环 // v-for能够承受两个参数,第一个是数组中元素,第二个元素是下标 // 能够通过in和of两个关键字链接绑定的变量 // 不倡议将v-if和v-for用在同一个元素上。非要用能够在里面嵌套div或者其余元素 // 依据官网文档,同时应用的时候,v-for的优先级会高于v-if <li v-for="(item,index) of list">{{item}}</li> </ul> </div> `}).mount('#root')之前有过根底或者仔细的小伙伴会发现,小编在应用v-for的时候,前面用的是of关键字,那对于v-for来说,尽管官网文档给的是in,那么of和in有什么不一样呢?在原生js中,对于循环来说,能够应用关键字in和for来循环,失去的后果是不一样的,就像这样 let arr = [ {name:'lilei',age:12}, {name:'hanmeimei',age:20}]for(let item of arr){ console.log(item) // {{name:'lilei',age:12} {name:'hanmeimei',age:20}}for(let key in arr){ console.log(item) // 0 1}小编在查问了一些材料后,失去这样的论断,遍历数组的时候,举荐应用of,语法为(item,index)。在遍历对象的时候,举荐应用in,语法为(item,name,index)上面是实例 ...

August 25, 2021 · 1 min · jiezi

关于vue.js:前端面试每日-31-第862天

明天的知识点 (2021.08.25) —— 第862天 (我也要出题)[html] html5中的meta标签revised有什么作用?[css] 应用css实现一个条纹边框[js] 解释下({} + [] == [] + {})的后果[软技能] 你理解超分辨率吗?《论语》,曾子曰:“吾日三省吾身”(我每天屡次检查本人)。前端面试每日3+1题,以面试题来驱动学习,每天提高一点!让致力成为一种习惯,让奋斗成为一种享受!置信 保持 的力量!!! 欢送在 Issues 和敌人们一起探讨学习! 我的项目地址:前端面试每日3+1【举荐】欢送跟 jsliang 一起折腾前端,零碎整顿前端常识,目前正在折腾 LeetCode,打算买通算法与数据结构的任督二脉。GitHub 地址 微信公众号欢送大家前来探讨,如果感觉对你的学习有肯定的帮忙,欢送点个Star, 同时欢送微信扫码关注 前端剑解 公众号,并退出 “前端学习每日3+1” 微信群互相交换(点击公众号的菜单:交换)。 学习不打烊,充电加油只为遇到更好的本人,365天无节假日,每天早上5点纯手工公布面试题(死磕本人,愉悦大家)。心愿大家在这虚夸的前端圈里,放弃沉着,保持每天花20分钟来学习与思考。在这变幻无穷,类库层出不穷的前端,倡议大家不要等到找工作时,才狂刷题,提倡每日学习!(不忘初心,html、css、javascript才是基石!)欢送大家到Issues交换,激励PR,感激Star,大家有啥好的倡议能够加我微信一起交换探讨! 心愿大家每日去学习与思考,这才达到来这里的目标!!!(不要为了谁而来,要为本人而来!)交换探讨欢送大家前来探讨,如果感觉对你的学习有肯定的帮忙,欢送点个[Star]

August 25, 2021 · 1 min · jiezi

关于vue.js:vue项目多环境配置env的实现

"scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "build:bsy": "vue-cli-service build --mode bsy", "build:kthz": "vue-cli-service build --mode kthz", "serve:bsy": "vue-cli-service serve --mode bsy", "serve:kthz": "vue-cli-service serve --mode kthz", },--mode bsy 对应.env.bsy 文件,其余模式相似,如--mode kthz 对应.env.kthz 文件

August 25, 2021 · 1 min · jiezi

关于vue.js:Vue源码-模板编译解析

vue源码 模板编译模板编译的次要目标是将模板(template)转换为渲染函数(render)模板编译作用 Vue2.x应用VNode形容视图以及各种交互,用户本人编写VNode 比较复杂用户只须要编写相似 HTML的代码 - Vue.js模板,通过编译器将模板转换为返回 VNode 的 render函数.vue文件会被webpack在构建的过程中转换成 render函数 (外部通过vue-loader)运行时版本,vuecli默认应用构建时版本,体积大,构建速度慢查看模板编译后果 <div id="app"> <h1>Vue<span>模板编译</span></h1> <p>{{msg}}</p> <comp></comp></div>Vue.component("comp",{ template:'<div>I am Comp</div>'})const vm = new Vue({ el:"#app", data:{ msg:'Hello Compiler' }, methods:{ handler(){ console.log('test') } }})console.log(vm.$options.render)获取render输入后果 with作用是示意上面_的办法都是vue实例的办法 (function anonymous() {with(this) { return _c( 'div', // tag标签 {attrs:{"id":"app"}}, // data用于形容tag [ // children形容tag子节点 _m(0), // 模板中的动态内容,对应下面的h1标签 _v(" "), // 创立空白文本,两个标签之间的空白 _c('p',[_v(_s(msg))]), // 创立p标签对应的vnode,参数二是文本 _v(" "), _c('comp') ], 1 // children 拍平 )}})_c() 定义在 src/core/instance/render.js (h函数,用于创立vnode)_m() / _v() / _s() 定义在 src/core/instance/render-helpers/index.js将html模板转化为render渲染函数 ...

August 25, 2021 · 2 min · jiezi

关于vue.js:Vue编译过程的-AST语法树

形象语法树形象语法树简称AST (Abstract Syntax Tree)应用对象的模式形容树形的代码构造此处的形象语法树是用来形容树形构造的 HTML字符串、为什么要应用 形象语法树模板字符串转换成AST后,能够通过AST 对模板做优化解决标记模板中的动态内容,在patch 的时候间接跳过动态内容在patch的过程中动态内容不须要比照和从新渲染

August 25, 2021 · 1 min · jiezi

关于vue.js:Vue源码虚拟-Dom解析

什么是虚构DOMVirtual DOM 是应用 JS 对象形容实在 DOM (一般的JS 对象,形容DOM 构造)Vue 中的 虚构Dom 借鉴 Snabbdom, 并增加了Vue.js 的个性。(借鉴模块机制,钩子函数,diff算法等,增加例如指令,组件机制新个性)为什么要应用 虚构DOM防止间接操作 DOM, 进步开发效率 (只关注业务代码实现,不须要关注 dom 浏览器兼容性问题)作为一个中间层能够跨平台 (Web Weex挪动端平台 SSR渲染)虚构 DOM 不肯定能够进步性能 首次渲染时会减少开销 (须要保护一层额定的虚构dom)简单视图状况下晋升渲染性能 (频繁dom操作,通过diff算法,比照新旧两个虚构dom树差别,并更新差别)h函数vm = new Vue({ el:"#app", render(h){ // h(tag, data, children) // tag元素标签, data元素属性, children 数组则示意子元素,字符串示意元素内容 // return h('h1',this.msg) // 省略 data // return h('h1', {domProps:{ innerHTML:this.msg }}) // dom属性 // return h('h1', {attrs:{ id: "title" }}, this.msg) // 标签属性和dom内容 let vnode = h('h1', {attrs:{ id: "title" }}, this.msg) console.log(vnode) return vnode; }, data:{ msg:"hello world" }})输入后果 ...

August 25, 2021 · 2 min · jiezi

关于vue.js:Vue源码中对Watcher执行的分析

触发更新时watcher的执行watcher分为三种,Computed Watcher, 用户Watcher侦听器,渲染Watcher前两种 initState 时初始化渲染watcher在 core/instance/lifecycle.js 中 mountComponent,后执行当调用 dep.notify 时,会对watcher排序,而后顺次更新watcher.update对于不同类型 watcher, update解决形式不同渲染watcher会判断 以后组件watcher是否放入队列中,未放入 则找到地位插入到队列中调用 flushSchedulerQueue 办法 先更新父组件后更新子组件(父组件先创立)用户watchers在渲染watcher之前运行,(用户或计算属性在initState中创立,在mountComponent之前)如果一个组件在父组件执行期间被销毁,则跳过以后组件遍历队列中的 watcher,调用 watcher.run()run 办法内 调用get 办法, get办法调用 了 this.getter.call(vm, vm)this.getter 就是 updateComponent 办法更新完结后重置状态调用 activated,updated两个钩子 notify () {// copyconst subs = this.subs.slice()if (process.env.NODE_ENV !== 'production' && !config.async) { // 按watcher的创立程序排序 subs.sort((a, b) => a.id - b.id)}// 调用 watcher的 更新for (let i = 0, l = subs.length; i < l; i++) { subs[i].update()}}update () {// 三种watcher update 渲染watcher 走elseif (this.lazy) { this.dirty = true} else if (this.sync) { this.run()} else { queueWatcher(this)}}export function queueWatcher (watcher: Watcher) {const id = watcher.id// has是个对象,避免 watcher被反复解决if (has[id] == null) { has[id] = true // flushing = true 示意 watcher对象正在被解决 // 把watcher放入队列中 if (!flushing) { queue.push(watcher) } else { let i = queue.length - 1 // 队列未解决完,从后向前取watcher和以后组件watcher比拟,确定i while (i > index && queue[i].id > watcher.id) { i-- } // 把组件watcher插入对应地位 queue.splice(i + 1, 0, watcher) } // waiting = true 示意 以后队列正在执行 if (!waiting) { waiting = true if (process.env.NODE_ENV !== 'production' && !config.async) { // 遍历所有watcher,调用 watcher.run() flushSchedulerQueue() return } // 生产环境会传入nextTick, nextTick(flushSchedulerQueue) }}}function flushSchedulerQueue () {... // 把watcher插入队列// 遍历队列内的watcher执行 runwatcher.run()...}run () {...this.get() // this指watcher...}get () { pushTarget(this)...// this.getter 为 updateComponentvalue = this.getter.call(vm,vm)}

August 25, 2021 · 1 min · jiezi

关于vue.js:Vue对数组响应式处理

对数组的响应式解决在 src/observer/index.js 中将原生批改原数组的办法,进行重写,保留性能,并能够在数组扭转时触发 notify 告诉 watcher 更新视图并未解决 索引 和 length,尽管数组批改了,然而未触发 notify因为咱们只解决了数组中的元素,而非属性(属性蕴含下标,length等等),不解决因为性能问题代替计划:通过 vm.arr.splice(0,1,100)Vue.set()this.$updateForce() 源码局部 if (Array.isArray(value)) { // hasProt用来判断 以后浏览器是否反对 对象原型 __proto__ // 修补数组中的原型办法,如果数组发生变化调用 notify更新视图 if (hasProto) { protoAugment(value, arrayMethods) } else { copyAugment(value, arrayMethods, arrayKeys) } // 遍历数组所有元素,是对象的元素会转化为响应式对象,而非所有属性!!!! // 下标和length是属性,无奈通过 vm.msg[0]更新,因为性能问题 // 能够应用 vm.msg.splice(0,1,100) 代替 this.observeArray(value)} else { // 遍历对象中的每个属性,转换成 setter/getter this.walk(value)}const arrayProto = Array.prototype// 创立一个对象,对象原型指向 传入的参数 (此对象的原型指向Array.prototype)export const arrayMethods = Object.create(arrayProto)// 批改原数组元素的办法 (批改原数据会调用dep.notify,告诉watcher去更新视图,然而数组原生办法不会,须要修补)const methodsToPatch = [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']

August 25, 2021 · 1 min · jiezi

关于vue.js:Vue对对象响应式原理分析

数据响应式解决通过查看源码解决问题 vm.msg = {count:0},从新给属性 msg 赋值,是否是响应式的?(是)vm.arr[0] = 4,给数组元素赋值,视图是否会更新?(不是)vm.arr.length = 0, 批改数组的length,视图是否会更新?(不是)vm.arr.push(4),视图是否会更新?(是)响应式解决的入口 响应式解决入口 在 initState 中initState办法在 src/core/instance/state.js 中实现办法中重要办法为 observer ...// 参数:options中的data 参数2示意根数据(根数据额定解决)observe(data,true /* asRootData */)...解析observe办法 observe办法定义在 src/core/observer/index.js如果以后 data 有 observer 则间接返回,没有给属性创立一个observer,并返回对 data对象进行判断,如果有 __ob__ 属性,返回 observer。没有,则 new Observer(data) (外部把它变成响应式,注册getter/setter),再返回在observe中的getter会收集依赖,setter会派发更新 (首次编译会触发getter)Dep.target在watcher类中定义,参考 mountComponent办法,办法中会new Watcher export function observe (value: any, asRootData: ?boolean): Observer | void {// 判断value是否是对象或 VNode实例if (!isObject(value) || value instanceof VNode) { return}let ob: Observer | void// 如果 value 有 __ob__(Observer 类的属性) 完结// 做过响应式就不必再操作了,相当于加了个缓存if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { ob = value.__ob__} else if ( shouldObserve && !isServerRendering() && (Array.isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue) { // 创立一个observer对象, ob = new Observer(value)}// 根数据data,会额定++标记if (asRootData && ob) { ob.vmCount++}// 返回一个Obserrver类return ob }解析Observer类 ...

August 25, 2021 · 2 min · jiezi

关于vue.js:Vue首次渲染解析

Vue 首次渲染源码在初始化完结会调用 $mount进行页面挂载Vue.prototype._init = function (options?: Object) { ... if (vm.$options.el) { // 挂载页面 vm.$mount(vm.$options.el) }}如果是带编译器的版本,会跳转到 $mount的重写办法,多了将模板编译成render函数的操作,详见// # platforms/web/entry-runtime-with-compiler.jsVue.prototype.$mount = function (){ ... // 调用 web/runtime/index.js下定义的$mount 办法 return mount.call(this, el, hydrating)}原始定义的 $mount办法,解决了el,并调用了 mountComponent// # platforms/web/runtime/index.jsVue.prototype.$mount = function ( el?: string | Element, hydrating?: boolean): Component { // 为什么这里 && 一下? // 如果带编译器版本,会重写 $mount办法,此时el在重写办法内曾经初始化 // 如果不带编译器版本,浏览器环境下,须要把el转化成 dom,获取dom.innerHTML作为模板 el = el && inBrowser ? query(el) : undefined return mountComponent(this, el, hydrating)}mountComponent办法定义在 core/instance/lifecycle.jsmountComponent办法解析 ...

August 25, 2021 · 1 min · jiezi

关于vue.js:Vue源码解析入口解析

Vue入口文件查找查看 dist/vue.js 的构建过程当运行 npm run dev 时指定了配置文件,从配置文件 script/config.js 动手web-full-dev (web蕴含编译器和运行时的开发版)script/config.js 基于 node 的模块 "dev": "rollup -w -c scripts/config.js -m --environment TARGET:web-full-dev"配置文件 config.js ...// 判断是否有环境变量 targetif (process.env.TARGET) { module.exports = genConfig(process.env.TARGET)} else { exports.getBuild = genConfig exports.getAllBuilds = () => Object.keys(builds).map(genConfig)}genrator config 生成配置文件 function genConfig (name) { const opts = builds[name] ... return config}咱们传入的环境变量指向 build 中的 带编译器的 开发版本 const builds = { 'web-full-dev': { // resolve把前面的门路转化为绝对路径 // 理论获取的是 src/platforms/web/entry-runtime-with-compiler.js // resolve办法会找到下面的门路,有别名操作 entry: resolve('web/entry-runtime-with-compiler.js'), dest: resolve('dist/vue.js'), format: 'umd', env: 'development', alias: { he: './entity-decoder' }, // 打包出的文件的文件头 banner:banner函数 banner }}问题: 如果同时设置 template和 render 会执行谁?const vm = new Vue({ el:"#app", template:"<h3>Hello Template</h3>", render(h) { return h('h4',"Hello Render") }})解析 entry 所对应的入口文件,并答复下面的问题从 入口文件中,src/platforms/web/entry-runtime-with-compiler.js ...

August 25, 2021 · 1 min · jiezi

关于vue.js:Vue构建版本理解

前言提到 Vue 的构建版本,对 Vue 初学者而言,很多人都是似懂非懂。官文对于 构建版本的解释放在了,前言局部,以表格的模式对其进行介绍,官文跳转 图解构建版本 如图 完整版:同时蕴含 编译器 和 运行时 的版本 编译器:用来将模板字符串编译成为 JavaScript 渲染函数的代码,体积大、效率低运行时:用来创立 Vue 实例、渲染并解决虚构 DOM 等的代码,体积小、效率高。能够解释为不蕴含编译器的版本。(编译器源码3000+lines)UMD:UMD 版本通用的模块版本,反对多种模块形式。 vue.js 默认文件就是运行时 + 编译器的UMD 版本CommonJS(cjs):CommonJS 版本用来配合老的打包工具比方 Browserify 或webpack1。ES Module:从 2.6 开始 Vue 会提供两个 ES Modules (ESM) 构建文件,为古代打包工具提供的版本。 ESM 格局被设计为能够被动态剖析(在编译时解决解析模块依赖而不是运行时),所以打包工具能够利用这一点来进行“tree-shaking”并将用不到的代码排除出最终的包。ES6 模块与 CommonJS 模块的差别<div id="app"> Hello World</div><script src="vue.runtime.js"></script> // 运行时UMD版本,会报错,无奈编译template模板<script> new Vue({ el:"#app", template:'<h1>{{ msg }}</h1>', data:{ msg:"HelloVue" } })</script> vue-cli中默认导入 vue.runtime.esm.js 查看vue-cli 创立的我的项目的 vue导入版本 yarn global add @vue/cli (vuecli目前最新4.5)vue create xxx (用cli创立时选Vue2)生成的我的项目中cli 对 webpack进行了封装,导致我的项目中看不到 main.js 下的咱们写的 import Vue from 'vue' 是从哪里引入的通过命令行工具 vue inspect 查看配置文件,发现vue默认应用 esm运行时版本的vue// 操作方法,生成output文件vue inspect > output.js ...

August 25, 2021 · 1 min · jiezi

关于vue.js:Vue3实现字符串反转和内容隐藏

通过一周工夫的致力,小编明天持续和大家学习Vue3,明天还是以理论例子为主,先让小小白感受一下Vue的魅力,让另外一些小小白领会一下不必脚手架是一种什么体验,当然了,也为了接下来的工作内容筹备筹备。上一篇对于Vue的文章,通过Vue,实现了一个累加的性能。源码是这样的 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="https://unpkg.com/vue@next"></script></head><body> <div id="root"></div> <script> Vue.createApp({ data(){ return { content: 1 // 绑定的数据 } }, mounted(){ // 页面渲染之后,主动执行的函数 setInterval(() => { this.content += 1 },1000) }, template:'<div>{{ content }}</div>' // 模版 }).mount('#root') // 挂在点为id="root"的DOM节点 </script> </body></html>上面咱们来看看明天的第一个例子:字符串反转,先看源码:为了节俭空间,我这里就只粘js局部的代码 Vue.createApp({ data(){ return { content: "hello world" } }, methods:{ handleBtnClick(){ const newContent = this.content.split("").reverse().join('') this.content = newContent } }, template:` <div> {{ content }} <button v-on:click="handleBtnClick">反转</button> </div>`}).mount('#root')没接触过Vue的小伙伴,看到这串字母可能是一脸懵圈,那就跟小编,一起搭上Vue这趟车吧下面的代码实现的性能就是将字符串反转反转再反转,上面小编就联合正文一起阐明一下代码的作用 ...

August 24, 2021 · 1 min · jiezi

关于vue.js:在疫情期间微分销商城如何运营

当初疫情还没完结,商家们也亟需更多的订单来维持店铺运行。那么微分销商城在疫情期间要怎么经营呢?上面,就由小编来通知大家吧!一、一直保护和减少性能随着微分销商城一直地发展壮大,最开始的那几个性能必定是不够的,为了不妨碍微分销商城的倒退,商家要减少一些有可能用失去的性能,还要对以前的性能进行保护和降级,这样能力让性能适宜现阶段的微分销商城,并且不便商家在分销零碎进行治理。而且性能一直减少和欠缺还能让用户有更好的生产体验,留下用户的可能性大大增加。二、减少营销流动次数许多人前几年巴不得春节假期缩短,往年终于是如愿以偿了,但有些人却却开心不起来了。因为这次的疫情尽管带来了超长的假期,但却不能随便出门,特地是一些被重点关照的地区,一家人好几天只能派一个人进来洽购日常用品,这让很多用户苦不堪言。然而这种状况却让微分销商城营销流动的参加人数急剧减少,很多用户不能进来购物就只好在网络上购买本人须要的产品。所以商家应该减少流动的次数,毕竟被困在家里的用户有数亿之多,若是商家能吸引到一小部分的用户就能够赚很多,微分销商城多举办几次营销流动从哪方面来说都是一件坏事,不会给商家带来害处,因而商家要器重这个方面。三、借网络热点宣传网络传递信息的速度有多快我置信很多商家都晓得,特地是这段非凡的期间,很多人都没有外出玩耍,在家无聊的时候就刷刷手机。而且除了疫情之外还有很多的网络热点呈现,商家要将这些网络热点利用起来,借助它们宣传本人的微分销商城和产品,比方在百度上发表和热点相干的文章,在其中夹杂一些微商城或者产品的信息,这样用户在观看文章的时候就会看见广告,起到很好的宣传成果。四、留住新用户这段时间内少数微分销商城都会吸引来大量的新用户,为了让微分销商城倒退得更好,商家应该争取让这些新用户留下。比方商家能够通知用户,在微分销商城购买的产品达到肯定的数量就能失去相应的现金处分或产品折扣券,这样用户才会有盼头,持续生产的可能性才会更大。如果您想领有本人的商城零碎,可抉择CRMEB,CRMEB专一于挪动互联网软件设计、研发、销售为一体的高新技术企业,咱们将竭诚为您服务。

August 24, 2021 · 1 min · jiezi

关于vue.js:Tomcat负载均衡的案例设计

指Ngnix每次都将同一用户的所有申请转发至同一台服务器上,星池云服务器及Nginx的 IP_hash。ession复制(简直不必):每次session发生变化,就播送给集群中的服务器,使所有的服务器上的session雷同。session共享:缓存session至内存数据库中,应用redis,memcached实现。(能够设置过期工夫,过期主动清理。 如果后盾服务连贯超时,Nginx是自身是有机制的,如果呈现一个节点down掉的时候,Nginx会更据你具体负载平衡的设置,将申请转移到其余的节点上,然而,如果后盾服务连贯没有down掉,而是返回了谬误异样码 如:504、502、500,该怎么办? 开发人员查看定位问题看日志,如星池云服务器果不能提供一个可视化的日志查看平台,将非常苦楚!请务必确保 net.ipv4.ip_forward=1 ,默认他是0,如果是则ip4转发性能将限度, 很可能本机上 docker版本的nginx 全副抛出502谬误 ,启动docker端口映射则会抛出“ IPv4 forwarding is disabled. Networking will not work” 谬误!则ip4转发性能将限度, 很可能本机上 docker版本的nginx 全副抛出502谬误 ,https://www.starpool.cn 启动docker端口映射则会抛出“ IPv4 forwarding is disabled. Networking will not work” 谬误!

August 24, 2021 · 1 min · jiezi

关于vue.js:js面试题的设计方法的案例有哪些

单线程、非阻塞IO、V8虚拟机、事件驱动什么是回调函数‘回调函数,星池云服务器就是放在另外一个函数(如 parent)的参数列表中,作为参数传递给这个 parent,而后在 parent 函数体的某个地位执行,Node.js 异步编程的间接体现就是回调。又称网域,是由一串用点分隔的名字组成的Internet上某一台计算机或计算机组的名称,用于在数据传输时对计算机的定位标识(有时也指地理位置)IP是Internet Protocol(网际互连协定)的缩写,是TCP/IP体系中的网络层协定。 它是浏览器最外围也最根本的平安性能,所谓的同源,指的是协定,域名,端口雷同。浏览器处于平安方面的思考,只容许本域名下的接口交互,不同源的客户端脚本,星池云服务器在没有明确受权的状况下,不能读写对方的资源。 然而它能够使你理解一个人的在Node.js方面的教训。这种类型的问题并不能通知你面试者的思维形式和工作习惯。其次,显示生存中的问题更可能展现应聘者的常识——咱们喜爱和员工进行结对编程。你不应该间接应用Node监听80端口(在*nix零碎中),https://www.starpool.cn 这样做须要root权限,对于运行程序来说这不是一个好主见。不过,你能够使Node监听1024以上的端口,而后在Node后面部署nginx反向代理。

August 24, 2021 · 1 min · jiezi

关于vue.js:我们团队在-Vue-3-Dev-Tools-的帮助下调试效率有了质的飞跃

有幻想,有干货,微信搜寻 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。 本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试残缺考点、材料以及我的系列文章。 Vue3 曾经进去了, Vue3 Devtools 正式版也快进去了,目前咱们在用的是 beta 版本,当初咱们来看看 Vue3 Devtoolls 有哪些新的性能。 装置关上谷利用商店,搜寻 vue devtools,抉择 beat 的标识装置,如下所示: 留神,在应用 Vue3 devtools 时,要把 Vue2 devtools 的关掉,免得造成混同。 第一步实现,如果商店关上不了的,自行百度,办法反正你们必定比我多。 开始应用装置实现了,咱们关上控制台就有一个 Vue 的 tab,如果下所示: multi-app (多利用视图)多利用视图,意思就是咱们能够查看多个利用,比方我在我的项目中增加多个 createApp 如下所示: import { createApp } from 'vue'import App from './App.vue'import App2 from './App2.vue'import App3 from './App3.vue'createApp(App).mount('#app')createApp(App2).mount('#app1')createApp(App3).mount('#app2')控制台关上查看: 在有多个Vue应用程序的浏览器页面中,能够在它们之间疾速替换,并有能力查看在iframe内的Vue应用程序。 inspector tab (查看器选项卡)咱们能够通过查看器查看每个组件的状态,这个查看器就是罗盘状的图标。 组件操作图标当抉择一个组件时,会看到右上方有一组三个不同的图标。 第一个眼睛的图标的示意 Scroll to component。当点击这个图标时,浏览器将会滚动到组件所在的地位。 ...

August 24, 2021 · 1 min · jiezi

关于vue.js:小程序电商的趋势告诉你小程序到底强在哪

当初很多商家都在寻找一种新的获客形式,对于互联网时代,其实是不缺流量的,只是没有找到一种适宜本人的引流形式。对于大多数商家而言,高额的推广费用或者一些广告费用他们是不想承当的,工夫久了也很难接受。互联网时代,每天都有不同的货色诞生,对于流量,是有数商家关注的点。小程序是很多人十分认可的一个引流工具,成果好,流量大,很多数据撑持着他的口碑。从刚开始到当初日活正在一直上涨,各项数据也一直上涨。充分说明他在这个时代有很高的引流价值,对于商家来说作用很大。但不管怎样,还是有很多人会持着狐疑的态度,对性能和成果都有狐疑的中央,明天就为大家解说一下对于小程序到底有哪些性能是值得一提,也可能阐明他的引流价值很高的。首先,小程序电商的趋势置信大家都明确,传统电商的流量正在一直转移,小程序是电商的一个重要突破点。咱们就一个景象来看,那就是当初购物的渠道有多少,置信大家不仅仅局限于某个平台了吧,当初来看,太多了,随着社交电商的风靡,小程序占有很大的劣势,比方某信小程序,它就是建设在咱们最大的社交软件下面的,流量微小。咱们发朋友圈就会发现,很多人做社交电商,通过分享链接或者图片海报等,某些平台只能分享缩略图或者其余的,对于朋友圈来说,体验不会太好,所以成果也不会太好。对于小程序来说就不一样了,当初小程序都反对分享到朋友圈了,能够进行海报分享或者小程序分享,在朋友圈外面的视觉体验也十分好,成果天然是没得说的。朋友圈自身是一个流量池,咱们须要提供好的文案或者图片去撑持起这个圈子,这样能力实现好的转化。还有一个很重要的点,那就是小程序下面资质和货色须要通过平台严格的审核,经营范围须要统一,不能平心而论,这一点能够让消费者足够的释怀,产品的可靠性,能让消费者会始终成为你的粉丝。

August 23, 2021 · 1 min · jiezi

关于vue.js:谈谈你对方法的理解有哪些案例吗

办法其实是具备肯定性能的代码块,咱们能够把须要屡次应用的性能提取成一个办法,这样屡次应用的时候也不须要把这些反复的代码写屡次造成代码的冗余。星池云服务器格局办法定义的格局:修饰符 返回值类型 办法名(参数列表){办法体}办法签名:办法名(参数列表) 注意事项留神:咱们这里说的是返回值类型而不是返回值,如果一个办法有返回值,那么返回值类型必须设置为与返回值雷同的类型,并且返回值须要应用return关键字来返回。只是格局上须要定义,然而调用办法时起不到限度的作用形参:定义方法的时候的参数列表实参:应用办法的时候传入的数据 重载的意义:星池云服务器是为了不便外界对办法进行调用,什么样的参数程序都能够找到对应的办法来执行,体现的是程序的灵活性子类办法的返回值类型 <= 父类办法的返回值类型【这个大小是继承关系,不是值的大小】子类办法抛出的异样类型 <= 父类办法抛出的异样类型【这个还没学,不必管】留神:如果父类办法的返回值类型是void,https://www.starpool.cn 子类保持一致即可

August 23, 2021 · 1 min · jiezi

关于vue.js:Vue-32-发布了那尤雨溪是怎么发布-Vuejs-的

1. 前言大家好,我是若川。欢送关注我的公众号:若川视线,最近组织了源码共读流动,感兴趣的能够加我微信 ruochuan12,长期交流学习。 之前写的《学习源码整体架构系列》 蕴含jQuery、underscore、lodash、vuex、sentry、axios、redux、koa、vue-devtools、vuex4十篇源码文章。 写绝对很难的源码,消耗了本人的工夫和精力,也没播种多少浏览点赞,其实是一件挺受打击的事件。从浏览量和读者受害方面来看,不能促成作者继续输入文章。 所以转变思路,写一些绝对通俗易懂的文章。其实源码也不是设想的那么难,至多有很多看得懂。 最近尤雨溪公布了3.2版本。小版本曾经是3.2.4了。本文来学习下尤大是怎么公布vuejs的,学习源码为本人所用。 本文波及到的 vue-next/scripts/release.js文件,整个文件代码行数尽管只有 200 余行,但十分值得咱们学习。 歌德曾说:读一本好书,就是在和崇高的人谈话。 同理可得:读源码,也算是和作者的一种学习交换的形式。 浏览本文,你将学到: 1. 相熟 vuejs 公布流程2. 学会调试 nodejs 代码3. 入手优化公司我的项目公布流程环境筹备之前,咱们先预览下vuejs的公布流程。 2. 环境筹备关上 vue-next,开源我的项目个别都能在 README.md 或者 .github/contributing.md 找到奉献指南。 而奉献指南写了很多对于参加我的项目开发的信息。比方怎么跑起来,我的项目目录构造是怎么的。怎么投入开发,须要哪些常识储备等。 你须要确保 Node.js 版本是 10+, 而且 yarn 的版本是 1.x Yarn 1.x。 你装置的 Node.js 版本很可能是低于 10。最简略的方法就是去官网重新安装。也能够应用 nvm等治理Node.js版本。 node -v# v14.16.0# 全局装置 yarn# 克隆我的项目git clone https://github.com/vuejs/vue-next.gitcd vue-next# 或者克隆我的我的项目git clone https://github.com/lxchuan12/vue-next-analysis.gitcd vue-next-analysis/vue-next# 装置 yarnnpm install --global yarn# 装置依赖yarn # install the dependencies of the project# yarn release2.1 严格校验应用 yarn 装置依赖接着咱们来看下 vue-next/package.json 文件。 ...

August 23, 2021 · 7 min · jiezi

关于vue.js:超详细Vuex手把手教程

1,前言最近在重温vue全家桶,再看一遍感觉记忆更粗浅,所以专门记录一下(本文vuex版本为v3.x)。 2,Vuex 是什么Vuex是专为Vue.js开发的状态管理模式。它采纳集中式存储,治理所有组件的状态,并以相应的规定保障状态以一种可预测的形式发生变化(我的了解就是全局变量)。 3,5大属性阐明state 对象类型,相似于实例的 data属性,存放数据 getters 对象类型,相似于实例的计算属性 computed mutations 对象类型,相似于实例的 methods,然而不能解决异步办法 actions 对象类型,相似于实例的 methods,能够解决异步办法 modules 对象类型,当state内容比拟多时,通过该属性宰割成小模块,每个模块都领有本人的 state、mutation、action、getter 4,state存储在state中的数据和Vue实例中的data遵循雷同的规定,必须是纯正的对象。 4.1 间接拜访this.$store.state.xxx4.1 应用mapState映射<template> <div id="communication"> <p>计数:{{ getCount }}</p> <p>学校:{{ getSchool('我是参数') }}</p> </div></template><script>import { mapState } from 'vuex'export default { name: 'Vuex', data() { return { date: 1998 } }, computed: { ...mapState({ // mapState默认会把state当第一个参数传进来 getCount: state => state.count, getSchool(state) { return (val) => { return state.school + val + this.date } } }) }, mounted() { // 间接取值 console.log(this.$store.state.count) }}</script>5,gettersgetter的返回值会依据它的依赖被缓存起来,且只有当它的依赖值产生了扭转才会被从新计算,并且默认承受state作为其第一个参数,也能够承受其余getter作为第二个参数(如下例) ...

August 23, 2021 · 6 min · jiezi

关于vue.js:Vue-什么是watch侦听器

在官方网站上,对于 watch 有具体的介绍,大家能够去好好考究一下官网文档,上面是我集体对 watch侦听器的总结 什么是watch侦听器?watch 侦听器: 实质上是一个函数, 监督数据变动, 针对数据的变动做特定的操作.(数据名作为办法名即可) (注: 所有的侦听器,都应该被定义在 watch 节点下 ) const vm = new Vue({ el: '#app', data: { username: '' }, watch: { // 监听 username 值的变动 // newVal 是"变动后的新值", oldVal 是"变动之前的旧值" username(newVal. oldVal) { console.log(newVal. oldVal) } }}) immediate 选项默认状况下,组件在首次加载结束后不会调用 watch 侦听器.若想让 watch 侦听器立刻被调用,则须要用immediate选项. watch: { username: { // handler 是固定写法,示意当 username 的值变动时,// 主动调用 handler 处理函数 handler: async function (newVal) {if (newVal === '') return const { data: res } = await axios.get('https://www.escook.cn/api/finduser/' + newVal)console.log(res) }, // 示意页面首次渲染好之后,就立刻触发以后的 watch 侦听器 //immediate 选项的默认值是 false //immediate 的作用是: 管制侦听器是否主动触发一次!immediate: true } } deep 选项如果 watch 侦听一个对象, 若对象中的属性值产生了变动,则无奈被监听到.此时须要应用deep 选项 ...

August 22, 2021 · 1 min · jiezi

关于vue.js:vue源码03-watch-侦听属性-初始化和更新

导航[[深刻01] 执行上下文](https://juejin.im/post/684490...) [[深刻02] 原型链](https://juejin.im/post/684490...) [[深刻03] 继承](https://juejin.im/post/684490...) [[深刻04] 事件循环](https://juejin.im/post/684490...) [[深刻05] 柯里化 偏函数 函数记忆](https://juejin.im/post/684490...) [[深刻06] 隐式转换 和 运算符](https://juejin.im/post/684490...) [[深刻07] 浏览器缓存机制(http缓存机制)](https://juejin.im/post/684490...) [[深刻08] 前端平安](https://juejin.im/post/684490...) [[深刻09] 深浅拷贝](https://juejin.im/post/684490...) [[深刻10] Debounce Throttle](https://juejin.im/post/684490...) [[深刻11] 前端路由](https://juejin.im/post/684490...) [[深刻12] 前端模块化](https://juejin.im/post/684490...) [[深刻13] 观察者模式 公布订阅模式 双向数据绑定](https://juejin.im/post/684490...) [[深刻14] canvas](https://juejin.im/post/684490...) [[深刻15] webSocket](https://juejin.im/post/684490...) [[深刻16] webpack](https://juejin.im/post/684490...) [[深刻17] http 和 https](https://juejin.im/post/684490...) [[深刻18] CSS-interview](https://juejin.im/post/684490...) [[深刻19] 手写Promise](https://juejin.im/post/684490...) [[深刻20] 手写函数](https://juejin.im/post/684490...) [[react] Hooks](https://juejin.im/post/684490...) [[部署01] Nginx](https://juejin.im/post/684490...) [[部署02] Docker 部署vue我的项目](https://juejin.im/post/684490...) [[部署03] gitlab-CI](https://juejin.im/post/684490...) [[源码-webpack01-前置常识] AST形象语法树](https://juejin.im/post/684490...) [[源码-webpack02-前置常识] Tapable](https://juejin.im/post/684490...) [[源码-webpack03] 手写webpack - compiler简略编译流程](https://juejin.im/post/684490...) [[源码] Redux React-Redux01](https://juejin.im/post/684490...) [[源码] axios ](https://juejin.im/post/684490...) [[源码] vuex ](https://juejin.im/post/684490...) [[源码-vue01] data响应式 和 初始化渲染 ](https://juejin.im/post/684490...) [[源码-vue02] computed 响应式 - 初始化,拜访,更新过程 ](https://juejin.im/post/684490...) [[源码-vue03] watch 侦听属性 - 初始化和更新 ](https://juejin.im/post/684490...) [[源码-vue04] Vue.set 和 vm.$set ](https://juejin.im/post/684490...) [[源码-vue05] Vue.extend ](https://juejin.im/post/684490...) ...

August 21, 2021 · 10 min · jiezi

关于vue.js:Vue-组件的生命周期

生命周期(Life Cycle) 是指一个组件从 创立 => 运行 => 销毁 的整个阶段; 强调的是一个时间段.生命周期函数: 是由vue框架提供的内置函数,会随同这组件的生命周期动静按秩序执行留神: 生命周期强调的是时间段,生命周期函数强工夫点.创立阶段 beforeCreate (组件的props|data|methods 尚未被创立,都处于不可用的状态created (组件的props| data | methods | 曾经创立好,都处于可用状态, 但组件的模板构造尚未生成 => 发送Ajax最早的机会,申请数据beforeMount 将要把内存中编译好的HTML构造渲染到浏览器中. 此时对浏览器中还没有以后组件的 DOM 构造mounted组件第一次被渲染到浏览器中 | 操作 DOM 最早的机会 (此时曾经把内存中的HTML 构造胜利的渲染到浏览器中)运行阶段 beforeUpdate (将要依据变动后,最新的数据,从新渲染组件的模板构造)updated 曾经根最新的数据,实现了组件 DOM 构造的从新渲染 =>可能操作到最新的DOM 元素销毁阶段 beforeDestroy 将要销毁此组件,此时尚未销毁,组件还处于失常工作的状态destroyed 组件曾经被销毁,此组件在浏览器中对应的DOM构造已被齐全移除!

August 20, 2021 · 1 min · jiezi

关于vue.js:vueelementUI多级表头展示

输出指标和输入指标作为一级表头,输出指标和输入指标的每一小项作为二级表头,二级表头是动静生成的,效果图如下: 返回数据解释:数组-->对象-->对象,第二个对象中的每一个属性是二级表头的属性名字和值,格局如下: [ { "executionDetailId": "945f4744-bde9-4c84-9f29-b6b3a18c7b18", "executionId": "90a7fe92-370a-49f5-a836-816f6048c002", "subjectId": "joys-01", "subjectCode": "joys-01", "subjectName": "公司01", "execTimestamp": "2021-07-22T09:08:56.000+00:00", "status": "SUCCEEDED", "creationTs": "2021-07-22 17:08:56", "creatorId": "8c0e93bb-db21-4480-8d28-bf91cbff2003", "tenantId": "74a03e75-c696-4ba6-a4c8-30e0ce48f526", "subjectProps": {}, "inputIndicatorSet": { "近1个月内企业动产融资记录数": 1, "近6个月内企业投资人股权变更记录数": 3 }, "outputIndicatorSet": { "经营异样": 20 }, "errorMessage": null }, { "executionDetailId": "b8fe23d0-f640-4661-9063-987b2e49fe22", "executionId": "90a7fe92-370a-49f5-a836-816f6048c002", "subjectId": "joys-02", "subjectCode": "joys-02", "subjectName": "公司02", "execTimestamp": "2021-07-22T09:08:56.000+00:00", "status": "FAILED", "creationTs": "2021-07-22 17:08:56", "creatorId": "8c0e93bb-db21-4480-8d28-bf91cbff2003", "tenantId": "74a03e75-c696-4ba6-a4c8-30e0ce48f526", "subjectProps": {}, "inputIndicatorSet": { "近1个月内企业动产融资记录数": 4, "近6个月内企业投资人股权变更记录数": 2 }, "outputIndicatorSet": { "经营异样": 20 }, "errorMessage": null }, { "executionDetailId": "f5d144de-1792-451e-aa58-c6897e8e9301", "executionId": "90a7fe92-370a-49f5-a836-816f6048c002", "subjectId": "joys-03", "subjectCode": "joys-03", "subjectName": "公司03", "execTimestamp": "2021-07-22T09:08:56.000+00:00", "status": "SUCCEEDED", "creationTs": "2021-07-22 17:08:56", "creatorId": "8c0e93bb-db21-4480-8d28-bf91cbff2003", "tenantId": "74a03e75-c696-4ba6-a4c8-30e0ce48f526", "subjectProps": {}, "inputIndicatorSet": { "近1个月内企业动产融资记录数": 3, "近6个月内企业投资人股权变更记录数": 4 }, "outputIndicatorSet": { "经营异样": 20 }, "errorMessage": null }]生成二级表头:以后业务场景中,每一条数据的inputIndicatorSet和outputIndicatorSet都是雷同的,所以咱们定义两个变量,取出第一个对象的属性:inputIndicatorSet和outputIndicatorSet,代码如下: ...

August 20, 2021 · 1 min · jiezi

关于vue.js:前端websocket-使用

1.结构WebSocket实例 var ws = new WebSocket('ws://localhost:8080');2.以后实例的readyState 状态 WebSocket的四种状态CONNECTING:值为0,示意正在连接。OPEN:值为1,示意连贯胜利,能够通信了。CLOSING:值为2,示意连贯正在敞开。CLOSED:值为3,示意连贯曾经敞开,或者关上连贯失败。3.实例属性回调函数 ws.onopen = (e)=> { console.log("连贯胜利")}ws.onmessage= (e)=> { console.log("接管音讯胜利")}ws.onoerror = (e)=> { console.log("连贯失败")}ws.onclose = (e)=> { console.log("连贯敞开")}4. 重连 //重连 reConnection(){ console.log("从新连贯") if(this.lockReconnect){ return } this.lockReconnect = true if(this.timerReconnect) { clearTimeout(this.timerReconnect) } //没连上会始终重连, 设置迟延,防止申请过多 this.timerReconnect = setTimeout(() => { //setTimeout 到点了执行 this.createWebSocket() this.lockReconnect = false }, 5000); }5. 监测心跳 heartCheck(){ console.log("监测心跳") if(this.timerHeart){ clearTimeout(this.timerHeart) } if(this.timerServerHeart){ clearTimeout(this.timerServerHeart) } this.timerHeart = setTimeout(() => { this.ws.send("are you weak") this.timerServerHeart = setTimeout(() => { // 断了 this.ws.close() }, 5000); this.lockReconnect = false }, 2000); }6.残缺的封装代码———待欠缺后更新 ...

August 20, 2021 · 1 min · jiezi

关于vue.js:vue父组件调用子组件方法

1.开发环境 vue2.电脑系统 windows10专业版3.在开发的过程中,咱们一些状况的时候,须要在 父组件中应用子组件的办法,上面我来分享一下应用办法。4.废话不多说,间接上代码: // 父组件代码<Ca :UpLoad="OrderList.UpLoad" ref="child" />// 子组件代码:GetCa() { return this.Ca;},5.在父组件中调用子组件办法 this.$refs.child.GetCa();6.本期的分享到了这里就完结啦,心愿对你有所帮忙,让咱们一起致力走向巅峰。

August 20, 2021 · 1 min · jiezi

关于vue.js:简述-Vue-与-jQuery-的区别

区别:jQuery >>> 只是对原生 JS 的 API 选择器等进行了封装,不便操作 DOM , 实质还是操作 DOM 实现逻辑, 数据和界面还是连贯在一起的. 实用于须要操作 DOM 的业务: 如, 动画, 交互成果, 页面特效. Vue>>> MVVM 模型, 将数据层和视图层齐全分来到, 不仅对 API 进行封装, 还提供了一系列的解决方案, 这是一个思维的转变. 数据驱动的机制, 次要操作的是数据而不是频繁操作 DOM (导致页面频繁重绘). 实用的业务: 数据相干的解决以及操作

August 19, 2021 · 1 min · jiezi