Vue 中 强制组件重新渲染的正确方法
有时候,依赖 Vue 响应方式来更新数据是不够的,相反,我们需要手动重新渲染组件来更新数据。或者,我们可能只想抛开当前的DOM,重新开始。那么,如何让Vue以正确的方式重新呈现组件呢? 强制 Vue 重新渲染组件的最佳方法是在组件上设置:key。 当我们需要重新渲染组件时,只需更 key 的值,Vue 就会重新渲染组件。 这是一个非常简单的解决方案。 当然,你可能会对其他方式会更感兴趣: 简单粗暴的方式:重新加载整个页面 不妥的方式:使用 v-if 较好的方法:使用Vue的内置forceUpdate方法 最好的方法:在组件上进行 key 更改 简单粗暴的方式:重新加载整个页面 这相当于每次你想关闭应用程序时都要重新启动你的电脑。 这种方式或许有用,但这是一个非常糟糕的解决方案,不要这样做,我们来看看更好的方法。 不妥的方式:使用 v-if v-if指令,该指令仅在组件为true时才渲染。 如果为false,则该组件在DOM中不存在。 来看看,v-if 是怎么工作的,在template中,添加v-if指令:
1 2 3 |
<template> <my-component v-if="renderComponent" /> </template> |
在script 中,使用nextTick的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<script> export default { data() { return { renderComponent: true, }; }, methods: { forceRerender() { // 从 DOM 中删除 my-component 组件 this.renderComponent = false; this.$nextTick(() => { // 在 DOM 中添加 my-component 组件 this.renderComponent = true; }); } } }; </script> |
上面的过程大致如下: 刚开始 renderComponent设置为true,因此渲染 my-component 组件 当我们调用forceRerender时,我们立即将renderComponent设置为false 我们停止渲染my-component,因为v-if指令现在计算结果为false 在nextTick方法中将renderComponent设置回true 当v-if指令的计算结果为true时,再次渲染my-component 在这个过程中,有两个部分比较重要 首先,我们必须等到nextTick,否则我们不会看到任何变化。 在Vue中,一个 tick 是一个DOM更新周期。Vue将收集在同一 tick 中进行的所有更新,在 tick 结束时,它将根据这些更新来渲染 DOM 中的内容。如果我们不等到next tick,我们对renderComponent的更新就会自动取消,什么也不会改变。 其次,当我们第二次渲染时,Vue将创建一个全新的组件。 Vue 将销毁第一个,并创建一个新的,这意味着我们的新my-component将像正常情况一样经历其所有生命周期-created,mounted等。 另外,nextTick 可以与 promise 一起使用:
1 2 3 4 5 6 7 8 |
forceRerender() { // 从 DOM 中删除 my-component 组件 this.renderComponent = false; this.$nextTick().then(() => { this.renderComponent = true; }); } |
不过,这并不是一个很好的解决方案,所以,让我们做 Vue 想让我们做的 较好的方法:forceUpdate 方法 这是解决这个问题的两种最佳方法之一,这两种方法都得到了Vue的官方支持。 通常情况下,Vue 会通过更新视图来响应依赖项中的更改。然而,当我们调用forceUpdate时,也可以强制执行更新,即使所有依赖项实际上都没有改变。 下面是大多数人使用这种方法时所犯的最大错误。 如果 Vue 在事情发生变化时自动更新,为什么我们需要强制更新呢? 原因是有时候 Vue 的响应系统会让人感到困惑,我们认为Vue会对某个属性或变量的变化做出响应,但实际上并不是这样。在某些情况下,Vue的响应系统根本检测不到任何变化。 所以就像上一个方法,如果你需要这个来重新渲染你的组件,可能有一个更好的方法。 有两种不同的方法可以在组件实例本身和全局调用forceUpdate:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 全局 import Vue from 'vue'; Vue.forceUpdate(); // 使用组件实例 export default { methods: { methodThatForcesUpdate() { // ... this.$forceUpdate(); // ... } } } |
重要提示:这不会更新任何计算属性,调用forceUpdate仅仅强制重新渲染视图。 最好的方法:在组件上进行 key 更改 在许多情况下,我们需要重新渲染组件。 要正确地做到这一点,我们将提供一个key属性,以便 Vue 知道特定的组件与特定的数据片段相关联。如果key保持不变,则不会更改组件,但是如果key发生更改,Vue 就会知道应该删除旧组件并创建新组件。 正是我们需要的! 但是首先,我们需要绕一小段路来理解为什么在Vue中使用key。 为什么我们需要在 Vue 中使用 key 一旦你理解了这一点,那么这是了解如何以正确方式强制重新渲染的很小的一步。 假设我们要渲染具有以下一项或多项内容的组件列表: 有本地的状态 某种初始化过程,通常在created或mounted钩子中 […]
View Detailsgitlab 的 CI/CD 初次尝试
一、前言 ci/cd 持续集成持续交付,开发人员尝尝会遇到一些重复工作,比如一个asp.net的网站代码,写完之后要发布,然后再将发布内容移动到部署的服务器上再进行部署,就算你只改了一个页面上的一个字,也要将之前的发布部署过程重新做一遍。 CI 解决了这个重复性问题,开发人员只需git push 提交代码,剩下的工作交给CI , 做CI工作的有gitlab-ci/cd Jenkins k8s等,中小项目gitlab-ci比较合适,因为它更轻量级一些,本篇仅讨论gitlab的cicd功能。 二、环境 IP 角色 192.168.10.202(ubuntu) gitlab 192.168.10.208(centos) git,docker,gitlab-runner gitlab git docker 三者的安装 略。且本篇是以docker镜像方式部署 上图为gitlab-ci的过程,其实它只是省略了一些我们的手动发布部署过程,将其自动化,但原来的流程还是一个不能少。所以,我们要进行的第一步,就是手动实现一遍流程。 三、手动实现一遍 (gitlab服务器) 创建代码仓库—> (开发PC) git clone —> (开发pc) 修改代码,添加dockerfile —> (开发pc) git push —> (部署服务器) git pull —> (部署服务器) docker build ,docker run 1、创建代码仓库 在创建之前,需要在使用git的机器上设置好ssh,可以参考之前的https://www.cnblogs.com/hallejuayahaha/p/11996332.html 要注意的是,在部署机器上,gitlab-runner安装好后,需要添加一个gitlab-runner的用户,需要添加这个用的ssh,因为在ci自动化后,在部署服务器上的操作都是由这个用户来做的,所以不仅要添加它的ssh,也要docker 免去sudo的指令。 总之就是让gitlab-runner用户,在做部署过程中,不要受到权限的阻碍。 ******docker免去敲sudo******* 在安装完docker之后,也会出现一个docker用户组,将你要免去敲sudo的用户添加进去,就可以了
1 |
sudo usermod -aG docker gitlab-runner |
添加代码仓库 并将一个.netcore项目创建进去 添加dockerfile文件,仅需在项目上右键,docker支持,vs就可以自动添加。然后git push 这样开发pc端的代码就都提交到gitlab服务器上了, 2、部署端git pull ,docker build , docker run 镜像已经成功启动,打开浏览器,输入ip + 暴露出来的端口8090 PS: .NETCORE web中,startup.cs文件中,有 app.UseHsts(); app.UseHttpsRedirection(); 这两行要注释掉,不然网站回一直往https跳转,没法进入http的80端口 整个手动流程成功。 […]
View DetailsMySQL 截取部分日期
使用 DATE_FORMAT(get_date, '%Y-%m-%d') 函数截取。 其中:get_date 是需要截取的字段名;’%Y-%m-%d' 是截取后的日期格式。 select date_format('1997-10-04 22:23:00′,’%y %M %b %D %W %a %Y-%m-%d %H:%i:%s %r %T'); 结果:97 October Oct 4th Saturday Sat 1997-10-04 22:23:00 10:23:00 PM 22:23:00 get_date = "2006-12-07" SELECT count(*) FROM t_get_video_temp Where DATE_FORMAT(get_date, '%Y-%m-%d')=’2006-12-07′; SELECT count(*) FROM t_get_video_temp Where get_date like '2006%-07%'; from:https://blog.csdn.net/hlbt0112/article/details/50600191/
View Detailswebview调用ios或者安卓的方法(内嵌h5页面调用app原生方法)
1.配合使用调用app原生的方法(h5页面不需要回调和数据) 实例1
1 2 3 4 5 6 7 8 9 |
// 通知客户端,token失效 callTokenLostToApp(){ let boswer = vm.config.getBrowser() if(boswer == 'isiOS'){ window.webkit.messageHandlers.tokenExpiredTransmit.postMessage(1); }else if(boswer == 'isAndroid'){ window.tokenExpiredTransmit.jsMethod(1) } }, |
实例2
1 2 3 4 5 6 7 8 9 |
// 关闭页面 closePageApp(cb){ let boswer = vm.config.getBrowser() // IOS 关闭页面 if(boswer == 'isiOS'){ // 这段代码是固定的,必须要放到js中 window.webkit.messageHandlers.closePage.postMessage(1); // 安卓关闭页面 }else if(boswer == 'isAndroid'){ window.closePage.jsMethod(1) } }, |
2.配合使用调用app原生的方法(h5页面需要回调和数据) // 从App获取UUID(手机唯一标识)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
// 从App获取UUID(手机唯一标识) getSystemInfoFromApp(cb){ let boswer = vm.config.getBrowser() // IOS 获取UUID if(boswer == 'isiOS'){ // 这段代码是固定的,必须要放到js中 function setupWebViewJavascriptBridge(callback) { if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); } if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); } window.WVJBCallbacks = [callback]; var WVJBIframe = document.createElement('iframe'); WVJBIframe.style.display = 'none'; WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__'; document.documentElement.appendChild(WVJBIframe); setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0) } /*与OC交互的所有JS方法都要放在此处注册,才能调用通过JS调用OC或者让OC调用这里的JS*/ setupWebViewJavascriptBridge(function(bridge) { /*JS给ObjC提供公开的API,ObjC端通过注册,就可以在JS端调用此API时,得到回调。ObjC端可以在处理完成后,反馈给JS,这样写就是在载入页面完成时就先调用*/ bridge.callHandler('getSystemInfoFromApp', function(responseData) { if(cb && typeof cb == 'function'){ cb(responseData) }else{ return responseData } }) }) // 安卓获取UUID }else if(boswer == 'isAndroid'){ let systemInfo = window.AndroidWebView.getSystemInfoFromApp(); if(cb && typeof cb == 'function'){ cb(systemInfo) }else{ return systemInfo } } }, |
标红字段为调用的app端定义的方法名,需要app端定义方法配合 from:https://www.cnblogs.com/wendyAndJken/p/9318501.html
View Details精选!18个 VS Code 高效插件,献给需要的前端程序猿/媛
先写前面 俗话说 欲先善其事必先利器,作为一个21新世纪的打工人,怎么可以没有一个趁手的编辑器工具呢。今天的主角就是 VS Code ,一个炒鸡强大的编辑器,我们先来介绍一下这个编辑器。 VS Code 全称 Visual Studio Code 是由微软开发的一款免费、跨平台的轻量级代码编辑器。以功能强大、提示友好、不错的性能和颜值俘获了大量开发者的青睐,对 JavaScript 和 NodeJS 的支持非常好,自带很多功能,例如代码格式化,代码智能提示补全等。 再强大的IDE那也不可能面面俱到什么功能都塞进去,那样只会导致IDE本身太臃肿。功能嘛,按需索取,所以,vscode的很多强大功能都是基于插件实现的,IDE只提供一个最基本的框子和最基本功能,由插件来丰富和扩展它的功能。 美化插件 一个美美的编辑器,在我们的开发过程中是非常友好的,所以我们先来介绍一下用于美化的插件 1. Material Theme 插件 Material Theme 插件是一款用于美化主题的图标的插件,该插件包含两个子插件,分别是 Community Material Theme 该插件用于美化主题,还有一个 Material Theme Icons 用于美化图标的。 image-20201116212608537 2. VSCode Great Icons 插件 VSCode Great Icons 插件是 VS Code 图标插件,可以控制 VS Code 中的文件管理的树目录显示图标。不过如果安装上面那个插件这个插件也就不需要了。 image-20201116213037910 代码高亮插件 1. Color Highlight 插件 Color Highlight 插件是 VS Code 中的颜色高亮插件,可以在编辑器中看出其背景颜色。 image-20201116213858966 2. vscode-pigments 插件 该插件也是一款颜色高亮插件,同上一个插件类似,是我现在使用的一个颜色高亮的插件 image-20201116214057772 3. Bracket Pair Colorizer 插件 为不同的括号拥有不同的颜色,可以使嵌套结构表现特别明显,这个插件是我非常喜欢的一个插件,给我安排它。 image-20201116225424675 高效插件 1. Search node_modules 插件 Search node_modules 插件是一款高效的查找插件,当我们的文件太多时,需要找到某个定义的方法时,可以通过该插件在当前文件夹进行搜索内用 image-20201116214435807 2. Path Intellisense […]
View Detailsmysql中去重 distinct 用法
在使用mysql时,有时需要查询出某个字段不重复的记录,这时可以使用mysql提供的distinct这个关键字来过滤重复的记录,但是实际中我们往往用distinct来返回不重复字段的条数(count(distinct id)),其原因是distinct只能返回他的目标字段,而无法返回其他字段,例如有如下表user: 用distinct来返回不重复的用户名:select distinct name from user;,结果为: 这样只把不重复的用户名查询出来了,但是用户的id,并没有被查询出来:select distinct name,id from user;,这样的结果为: distinct name,id 这样的mysql 会认为要过滤掉name和id两个字段都重复的记录,如果sql这样写:select id,distinct name from user,这样mysql会报错,因为distinct必须放在要查询字段的开头。 所以一般distinct用来查询不重复记录的条数。 如果要查询不重复的记录,有时候可以用group by : select id,name from user group by name; from:https://www.cnblogs.com/lxwphp/p/11339949.html
View DetailsMYSQL把查询多个的结果用逗号隔开,放在一个列里
mysql关键字:GROUP_CONCAT select name from 表1 t1 INNER JOIN 表2 t2 on t1.id=t2.word_id(关联字段) 结果: 2.select GROUP_CONCAT(name) from word t1 INNER JOIN relation_word_customer t2 on t1.id=t2.word_id 结果 from:https://blog.csdn.net/my_sweetxue/article/details/106380267
View DetailsMixed Content: The page at 'xxx' was loaded over HTTPS, but requested an insecure resource 'xxx'.
HTTPS页面里动态的引入HTTP资源,比如引入一个js文件,会被直接block掉的.在HTTPS页面里通过AJAX的方式请求HTTP资源,也会被直接block掉的。 Mixed Content: The page at 'xxx' was loaded over HTTPS, but requested an insecure resource 'xxx'. This request has been blocked; the content must be served over HTTPS. 解决办法: 页面的head中加入: <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"> 意思是自动将http的不安全请求升级为https from:https://www.cnblogs.com/cici1989/p/11229810.html
View Details解决IDEA gradle项目执行main方法时自动运行gradle task问题
使用IDEA创建gradle项目后,执行main方法时会自动运行gradle的一些build task,导致启动很慢,如下图: 解决方法: 打开设置页面,进行如下修改: from:https://zhoujun.blog.csdn.net/article/details/103372893
View DetailsDO-DTO相互转换时的性能优化
一般情况下,DO是用来映射数据库记录的实体类,DTO是用来在网络上传输的实体类。两者的不同除了适用场景不同外还有就是DTO需要实现序列化接口。从DB查询到数据之后,ORM框架会把数据转换成DO对象,通常我们需要再把DO对象转换为DTO对象。同样的,插入数据到DB之前需要将DTO对象转换为DO对象然后交给ORM框架去执行JDBC。 通常用到的转换工具类BeanUtils是通过反射来实现的,实现源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
public static <T> T convertObject(Object sourceObj, Class<T> targetClz) { if (sourceObj == null) { return null; } if (targetClz == null) { throw new IllegalArgumentException("parameter clz shoud not be null"); } try { Object targetObj = targetClz.newInstance(); BeanUtils.copyProperties(sourceObj, targetObj); return (T) targetObj; } catch (Exception e) { throw new RuntimeException(e); } } private static void copyProperties(Object source, Object target, Class<?> editable, String[] ignoreProperties) throws BeansException { Assert.notNull(source, "Source must not be null"); Assert.notNull(target, "Target must not be null"); Class<?> actualEditable = target.getClass(); if (editable != null) { if (!editable.isInstance(target)) { throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]"); } actualEditable = editable; } PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable); List<String> ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null; for (PropertyDescriptor targetPd : targetPds) { if (targetPd.getWriteMethod() != null && (ignoreProperties == null || (!ignoreList.contains(targetPd.getName())))) { PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName()); if (sourcePd != null && sourcePd.getReadMethod() != null) { try { Method readMethod = sourcePd.getReadMethod(); if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { readMethod.setAccessible(true); } Object value = readMethod.invoke(source); Method writeMethod = targetPd.getWriteMethod(); if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { writeMethod.setAccessible(true); } writeMethod.invoke(target, value); } catch (Throwable ex) { throw new FatalBeanException("Could not copy properties from source to target", ex); } } } } } |
也可以通过mapstruct来实现,这种方式是在Mapper接口的包中生成一个对应mapper的实现类,实现类的源码如下。显然这种方式的实现更为普通,看起来没有BeanUtils的实现那么复杂。不过BeanUtils通过反射实现更为通用,可以为各种类型的DTO实现转换。而mapstruct只是帮我们生产了我们不想写的代码。
1 2 3 4 5 6 7 8 9 10 11 12 |
public Task doToDTO(TaskDO taskDO) { if (taskDO == null) { return null; } else { Task task = new Task(); task.setId(taskDO.getId()); //其他字段的set task.setGmtCreate(taskDO.getGmtCreate()); task.setGmtModified(taskDO.getGmtModified()); return task; } } |
对比以上两种方式,显然使用BeanUtils来进行转换时需要写的代码更少,内部的通过反射便可以进行get/set操作。而mapstruct实现上需要写的代码稍微多一点,但是这种方式的性能比通过反射实现更好。下面写一段代码来测试两种方式实现的性能差别。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public void testConvert() { System.out.println("####testConvert"); int num = 100000; TaskDO taskDO = new TaskDO(); long start = System.currentTimeMillis(); for (int i = 0; i < num; i ++) { Task task = ObjectConvertor.convertObject(taskDO, Task.class); } System.out.println(System.currentTimeMillis() - start); //--------------------------------------------- start = System.currentTimeMillis(); for (int i = 0; i < num; i ++) { Task task = taskMapper.doToDTO(taskDO); } System.out.println(System.currentTimeMillis() - start); } |
以上测试代码中分别使用两种方式对同一个DO对象进行n次转换,两次转换的耗时统计如下。单位:ms 次数 1 10 100 1000 10000 100000 1000000 10000000 Mapstruct 0 1 1 1 2 4 8 8 BeanUtil 9 7 11 26 114 500 1469 14586 可见当转换数量级增加时,使用BeanUtil的耗时急剧上升,而使用Mapstruct的耗时则保持在比较低的水平。 在一个系统中,ORM对DB的各种操作几乎都会涉及到DO和DTO之间的转换,参考以上表格的统计结果,更推荐使用Mapstruct。 from:https://www.cnblogs.com/umgsai/p/8570652.html
View Details