N_Vue2 全家桶 N_Vue3 全家桶 N_Tmagic-editor 低代码二开 N_CSS 常用开发 N_Javascript N_Vue Pinia N_Electron 桌面开发 N_Formilyjs v1 低代码 N_Formilyjs 低代码 N_Node N_NodeRed N_React N_Threejs 3D开发 N_GIS 核心知识 N_GIS可视化开发 N_Wechat_小程序开发 N_Chrome 扩展开发

Chrome 调试技巧

https://mp.weixin.qq.com/s/a3FJxEtsWO65htzWuZyKuA

一键重新发起请求

在与后端接口联调或排查线上BUG时,你是不是也经常听到他们说这句话:你再发起一次请求试试,我这边看下为啥出错了!

重发请求,这有一种简单到发指的方式。

  1. 选中Network

  2. 点击Fetch/XHR

  3. 选择要重新发送的请求

  4. 右键选择Replay XHR

 复制JavaScript变量

假如你的代码经过计算会输出一个复杂的对象,且需要被复制下来发送给其他人,怎么办?

  1. 使用copy函数,将对象作为入参执行即可

控制台获取Elements面板选中的元素

调试网页时通过Elements面板选中元素后,如果想通过JS知道它的一些属性,如位置等怎么办呢?

  1. 通过Elements选择要调试的元素

  2. 控制台直接用$0访问

控制台引用上一次执行的结果

使用$_引用上一次操作的结果,不用每次都复制一遍

Memory 分析一则

使用chrome工具进行内存泄漏排查 https://www.yii666.com/blog/291805.html?action=onAll

Take Heap Snapshot 创建堆快照用来显示网页上的JS对象和相关的DOM节点的内存分布情况。利用该工具你可以创建JS的堆快照、内存分析图、对比堆快照以及定位内存泄漏问题。 选中Take Heap Snapshot,点击Take Snapshot按钮即可获取快照,在每一次获取快照前都会自动执行垃圾回收操作。 堆快照提供了不同的视角来进行查看:

进行一些操作后,再次点击Take heap snapshot生成一个快照,此时就有两个快照了,两个快照可以通过Comparison进行对比,查看多出了哪些内存占用,

Comparison: 列表中主要看New、Deleted、Delta三项, New 代表此次快照新增的内存占用数量, Deleted 代表此次快照销毁的内存占用数量, Delta 代表用来对比的快照增加或减少的内存占用数量,也就是New数量减去Deleted数量的结果; 点开可以看到引用层级

Containment: 该视图可以探测堆的具体内容,它提供了一个更适合的视图来查看对象结构,有助于分析对象的引用情况,使用它可以分析闭包和进行更深层次的对象分析。

Summary : 该视图按照构造函数进行分组,用它可以捕获对象和它们使用的内存情况,对于跟踪定位DOM节点的内存泄漏特别有用。 可以点击具体对象,查看其分配路径、引用关系以及占用的内存大小。 通过分析对象的引用关系,

Statistics : 统计视图。

Record Allocation Timeline从整个Heap角度记录内存的分配信息的时间轴信息,利用这个可以实现隔离内存泄漏问题。 选中Record Allocation Timeline按钮,点击Start按钮之后,执行你认为可能会引起内存泄漏的操作,操作之后点击左上角的停止按钮即可。 你可以在蓝色竖线上通过缩放来过滤构造器窗格来仅仅显示在指定的时间帧内的被分配的对象。 录制过程中,在时间线上会出现一些蓝色竖条,这些蓝色竖条代表一个新的内存分配,这个新的内存分配都可以会有潜在的内存泄露问题。 通过展开对象并点击它的值则可以在Object窗格中查看更多新分配的对象细节。

Shallow Size : 对象本身占用的内存 Retained Size : 对象本身及其引用总共占用的内存, 如果Shallow Size = Retained Size,说明基本没怎么泄漏。而如果Retained Size > Shallow Size,就需要多加注意了。 Distance :当前对象到根的引用层级距离 Alloc. Size : 新分配的内存 Freed Size : 释放的内存

Webpack ---------------------

Webpack 是一个前端资源加载/打包工具;它将根据模块的依赖关系进行静态分析, 然后将这些模块按照指定的规则生成对应的静态资源;

webpack 根据模块的依赖关系进行静态分析,Webpack 会给每个模块分配一个唯一的 id 并通过这个 id 索引和访问模块;

loader

Webpack 本身只能处理 JavaScript 模块, 如果要处理其他类型的文件, 就需要使用 loader 进行转换;

所以如果我们需要在应用中添加 css 文件, 就需要使用到 css-loader 和 `style-loader 他们做两件不同的事情, css-loader 会遍历 CSS 文件, 然后找到 url() 表达式然后处理他们, style-loader 会把原来的 CSS 代码插入页面中的一个 style 标签中;

例如 .vue 单文件模板, 就是 vue-loader 这个loader处理的

...
  module: {
    rules: [
      ...(config.dev.useEslint ? [createLintingRule()]: []),
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
...

Web pack 中文文档\

Axios ---------------------

create方法

官方文档

var BASE_API = "http://127.0.0.1:8012"
// 创建axios实例
const service = axios.create({
  baseURL: BASE_API,//process.env.BASE_API, // api 的 base_url
  timeout: 20000 // 请求超时时间
})
 
// request拦截器
service.interceptors.request.use(
  config => {
    if (getToken()) {
      config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
    }
    config.headers['Content-Type'] = 'application/json'
    return config
  },
  error => {
    // Do something with request error
    console.log(error) // for debug
    Promise.reject(error)
  }
)
 
// response 拦截器
service.interceptors.response.use(
  response => {
    const code = response.status
    if (code < 200 || code > 300) {
      console.error(response);
      return Promise.reject('error')
    } else {
      return response.data
    }
  },
  error => {
    console.error(error);
    return Promise.reject(error)
  }
)
export default service

Axios 文件选择 上传

  1. 创建一个 input元素type为file类型
  2. 触发点击事件
  3. 可通过这个 input 元素获取文件对象
  4. 创建 FormData 把文件对象加入
export function readExcel(data) {
    return request({
      headers:{'Content-Type':'multipart/form-data'},
      url: 'api/excel/readTable',
      method: 'post',
      data:data
    })
  }
 
var inputObj= document.createElement("input");
inputObj.type="file";
inputObj.accept=".xls, .xlsx"
inputObj.click();
inputObj.addEventListener('change', event => {
let fileObj = inputObj.files[0]
if (fileObj) {
    let formData = new FormData()
    formData.append("file", fileObj); 
    readExcel(formData).then(res => {
        console.log(res);
    }).catch(() => {
                
    });
} 
})
 

Axios 文件下载

export function exportExcel(exportDescription,criteria) {
  return request({
    url: 'api/book/exportExcel',
    responseType: 'blob',//必须
    method: 'post',
    data:{"exportDescription":exportDescription,"criteria":criteria}
  })
}
 
exportExcel().then(res => {
    const content = res;
    const blob = new Blob([content]);
    const fileName = "export.xls";
    document.createElement("a"); // 非IE下载
    const elink = document.createElement("a");
    elink.download = fileName;
    elink.style.display = "none";
    elink.href = URL.createObjectURL(blob);
    document.body.appendChild(elink);
    elink.click();
    URL.revokeObjectURL(elink.href); // 释放URL 对象
    document.body.removeChild(elink);
});
 

Axios 数组参数

数组的URL拼接参数, 实际应该是 http://127.0.0.1:8002/api/book/queryReferenceList?refField=bookType&selects=id&selects=name

而Axios 会带[] http://127.0.0.1:8002/api/book/queryReferenceList?refField=bookType&selects[]=id&selects[]=name

 
import qs from 'qs';
///全局拦截
service.interceptors.request.use(
  config => {
    // ......其他逻辑代码
    if (config.method === 'get') {
      config.paramsSerializer = function(params) {
        return qs.stringify(params, { arrayFormat: 'repeat' });
      };
    }
    return config;
  }
);
 
///其二
export function del(keys) {
  keys = {"keys":keys}
  return request({
    url: 'api/online/',
    method: 'delete',
    params:keys,
    paramsSerializer: params => {
      return qs.stringify(params, { indices: false })
    }
  })
}
 
 

// paramsSerializer 是一个负责 params 序列化的函数

参考 axios 请求配置

Axios 同步获取数据 async

	async function syncFun (){
    let ret = await request({url: 'open/api/test'});
    //这里能够保证 获取到请求数据
		return ret;
  }
  
  //这里不是同步!! 参考上[ ES6 ]
  var components = syncFun();

ag-grid ---------------------

ag-grid是一款功能和性能强大外观漂亮的表格插件, ag-grid几乎能满足你对数据表格所有需求; 固定列, 拖动列大小和位置, 多表头, 自定义排序等等各种常用又必不可少功能;

官页 Git Hub

websocket ---------------------

WebSocket

Freemarker 模版引擎 ---------------------

官方 表达式手册

创建变量 assign

<#assign name in namespacehash>
  capture this
</#assign>

<#assign name1=value1 name2=value2 … nameN=valueN [in namespacehash]>,

使用该指令你可以创建一个新的变量, 或者替换一个已经存在的变量; 注意仅仅顶级变量可以被创建/替换 (也就是说你不能创建/替换 some_hash.subvar, 除了 some_hash);

name: 变量的名字; 它不是表达式; 而它可以写作是字符串, 如果变量名包含保留字符这是很有用的, 比如 <#assign “foo-bar” = 1>; 请注意这个字符串没有展开插值(如”${foo}”); 如果需要赋值一个动态创建的名字, 那么不得不使用 这个技巧; =: 赋值操作符; 它也可以是一个简写的赋值操作符(从 FreeMarker 2.3.23 版本开始): ++, —, +=, -=, *=, /= 或 %=; 比如 <#assign x++> 和 <#assign x = x + 1> 是一样的, 并且 <#assign x += 2> 和 <#assign x = x + 2> 是相同的; 请注意, ++ 通常意味着算术加法 (对于非数字将会失败), 不像 + 或 += 可以进行字符连接等重载操作; value: 存储的值; 是表达式; namespacehash: (通过 import) 为命名空间创建的哈希表; 是表达式; .

变量插值 <#assign s = "Hello ${user}!">

创建Map

在模板中指定一个哈希表, 就可以遍历用逗号分隔开的”键/值”对, 把列表放到花括号内即可; 键和值成对出现并以冒号分隔; 比如: { “name”: “green mouse”, “price”: 150 }

<#--遍历map 首选获取key的集合-->
<#list map?keys as key>
  <option value="${key}">${map[key]}</option>
</#list>
 

map 连接

像连接字符串那样, 也可以使用 + 号的方式来连接哈希表; 如果两个哈希表含有键相同的项, 那么在 + 号右侧的哈希表中的项优先;

<#assign ages = {“Joe”:23, “Fred”:25} + {“Joe”:30, “Julia”:18}>

如果要类似 map.put的操作 要这么干

<#assign __componentsMap = {"Joe":23, "Fred":25} />
<#assign __componentsMap = (__componentsMap + {"Joe":30, "Julia":18}) />

http://freemarker.foofun.cn/dgui_template_exp.html#dgui_template_exp_hashop

请注意, 不要在很多重复连接时使用序列连接操作, 比如在循环中往序列上追加项目, 而这样的使用是可以的:<#list users + admins as person>;

尽管序列连接的速度很快, 而且速度是和被连接序列的大小相独立的, 但是最终的结果序列的读取却比原先的两个序列慢那么一点; 通过这种方式进行的许多重复连接最终产生的序列读取的速度会慢;

处理不存在的变量

数据模型中经常会有可选的变量(也就是说有时并不存在); 除了一些典型的人为原因导致失误外, FreeMarker 绝不能容忍引用不存在的变量, 除非明确地告诉它当变量不存在时如何处理;

这部分对程序员而言: 一个不存在的变量和一个是 null 值的变量, 对于FreeMarker来说是一样的, 所以这里所指的”丢失”包含这两种情况;

通过在变量名后面跟着一个 !(叹号, 译者注)和默认值; 就像下面的这个例子, 当 user 不存在于数据模型时, 模板将会将 user 的值表示为字符串 “visitor”; (当 user 存在时, 模板就会表现出 ${user} 的值):

<h1>Welcome ${user.prop.name!"visitor"}!</h1>

当路径可能不存在时

<h1>Welcome ${(user.prop.name)!"visitor"}!</h1>

太xx难用了!!

一些素材网站 ---------------------

//图标 https://undraw.co/illustrations https://www.iconfont.cn/ 图标: iconstore, unDraw, ICONFINDER, ICONS8

矢量图/PSD模板: freepik, free-PSD-templates, 365psd 免抠图PNG: pngimage, CLEANPNG PPT模板: OfficePLUS, PPT超级市场, 51PPT模板, PPT汇, 优品PPT

图片: pixabay, Unsplash, Pexels, Foodiesfeed, CC零图片网, Logo神器, iconfont(图标, 矢量) 视频: Videezy, Videovo, mixkit, distill 音频(音乐, 音效): audionautix, Freepd, Freesound, 耳聆网, 淘声网 字体: 字由, 100font

注意商业授权

免费CDN ---------------------

https://www.qiniu.com/prices/qcdn

WebRTC

Web实时通信(WebRTC)是标准,协议和JavaScript API的集合,两者的组合可实现浏览器(对等)之间的对等音频,视频和数据共享。WebRTC无需依赖第三方插件或专有软件,而是将实时通信转变为任何Web应用程序都可以通过简单的JavaScript API加以利用的标准功能。

浏览器将这种复杂性的大部分从三个主要API中抽象出来:

MediaStream:获取音频和视频流 RTCPeerConnection:音频和视频数据的通信 RTCDataChannel:任意应用程序数据的通信

MDN - WebRTC API

jQuery ---------------------

JQ对象拷贝

$.extend( [deep ], target, object1 [, objectN ] )
 
$.extend(true, ret, this.list);
// 浅层复制(只复制顶层的非 object 元素)
var newObject = jQuery.extend({}, oldObject);
 
// 深层复制(一层一层往下复制直到最底层)
var newObject = jQuery.extend(true, {}, oldObject);
 

JQ元素是否可见

$("#xcode").is(":visible")

JQ复选框

if($(this).prop('checked')==false){
    $(this).prop('checked',true);
}
//单选
$("#wb-cnfigure-up1").find("input[name=enable]:checked").first().val();

JQ选择框

var opt=$("#myselect").val();//声明的获取id的值是select的值也就是option标签里的value
$("#select_id").change(function(){//code...}); //为Select添加事件, 当选择其中一项时触发
$("#select_id").append("<option value='Value'>Text</option>");
//select 
$("#wb-cnfigure-up1").find("select[name=time_per]").val();
 
, 设置text为pxx的项选中
 
    $(".selector").find("option[text='pxx']").attr("selected",true);

多文件上传

前端

<input hidden id="uploadexc" type="file" accept=".xls, .cvs" name="fileexc" multiple />
 
var formData = new FormData($("#uploadForm")[0]);
for(var i=0; i<files.length; i++){
    console.log("i="+i+", name="+files[i].name+", formName="+formName);
    formData.append(formName+"["+i+"]", files[i]);
}
$.ajax({
    type: "POST",
    url: ctx+"/exc/upread.htm",
    data: formData,
    async: false,
    processData: false,
    contentType: false ,
    success: function(data) {
       console.log("success: "+JSON.stringify(data));
    },
    error: function (data) {
        alert("error: 连接错误!");
        console.error("error: 连接错误!");
    }
});

后台

 
if (!(request instanceof DefaultMultipartHttpServletRequest)) {
        return  new ModelAndView("/dv/dv_dataset/upload_step1");
    }
    List<Map<String, Object>> excel = new ArrayList<Map<String, Object>>();
    DefaultMultipartHttpServletRequest multiFileRequest = (DefaultMultipartHttpServletRequest) request;
 
    Iterator<String> it = multiFileRequest.getFileNames();
    while (it.hasNext()) {
        List<MultipartFile> files = multiFileRequest.getFiles(it.next());
        for(MultipartFile file: files) {
            System.out.println(file.getOriginalFilename()+", size=" + file.getSize());
            Dv_datasetService.collect(file,excel);
        }
    }
 

IFrame 父子调用

  1. 父iframe 调用子iframe的方法 $("#sunPage")[0].contentWindow.sunMethod(); contentWindow 对象可以获取子iframe的window对象,兼容所有浏览器. sunMethod() 这是子iframe中的方法名. 如果iframe的id不知道, 比如用jbox 的open方法打开一个ifram.可以借助jquery的find方法找到iframe节点; 如: $(selector).find(“iframe”)[0].contentWondow.sunMethod();

  2. 父iframe 修改子iframe标签中的数据:如修改子iframe中的input的值. $('#sunPage').contents().find("#sunP").text("dsssssdd"); jQuery contents() 方法: http://www.w3school.com.cn/jquery/traversing_contents.asp 介绍 ID sunP 是子iframe中的input的id;

  3. 子iframe调用父iframe中的方法

window.parent.daoYo("asdadasds"); window.parent 直接调用window对象的parent daoYo(“asdadasds”); 父页面的方法名,可传参数; 4. 子iframe 修改父iframe标签中的数据:如修改父iframe中的input的值. 这个就不用说了. window.parent.$("#button3").text("ssssssssssss"); 5. 跳转 window.parent.location.href = “index.htm”

jQuery Ajax spring mvc

//ajax get
  $.get("demo_ajax_load.txt", function(result){
    $("div").html(result);
  });
 
spring mvc 接受复杂的json 
//需要 @RequestBody
//@RequestMapping(value = "modify_data")
//@ResponseBody
//public Map<String, String> modify_data(@RequestBody  DatesetDataList data)
 
前端传递的时候需要加上 contentType:application/json 和 JSON.stringify 转为字符串
contentType: "application/json",
data: JSON.stringify(test),
 
$.ajax({
    type: "POST",
    url: ctx+"/exc/upread.htm",
    data: {"key":"val"},
    contentType:"application/json",
    async: false, 
    success: function(data) {
        console.log("success: "+JSON.stringify(data));
        window.location.href = ctx+'/dv/dv_dataset/upload_step1.htm';
    },
    error: function (data) {
        alert("error: 连接错误!");
        console.error("error: 连接错误!");
    }
});
 
$.get("demo_test.asp",function(data,status){
    alert("Data: " + data + "\nStatus: " + status);
});
 

处理JQ的id选择器的特殊字符

function escapeJquery(srcString) {
    // 转义之后的结果
    var escapseResult = srcString;
    // javascript正则表达式中的特殊字符
    var jsSpecialChars = ["\\", "^", "$", "*", "?", ".", "+", "(", ")", "[", "]", "|", "{", "}"];
    // jquery中的特殊字符,不是正则表达式中的特殊字符
    var jquerySpecialChars = ["~", "`", "@", "#", "%", "&", "=", "'", "\"", ":", ";", "<", ">", ",", "/"];
    for (var i = 0; i < jsSpecialChars.length; i++) {
        escapseResult = escapseResult.replace(new RegExp("\\"
            + jsSpecialChars[i], "g"), "\\"
            + jsSpecialChars[i]);
    }
    for (var i = 0; i < jquerySpecialChars.length; i++) {
        escapseResult = escapseResult.replace(new RegExp(jquerySpecialChars[i],
            "g"), "\\" + jquerySpecialChars[i]);
    }
    return escapseResult;
}

lernajs ---------------------

lernajs

将大型代码仓库分割成多个独立版本化的软件包(package)对于代码共享来说非常有用;但是, 如果某些更改跨越了多个代码仓库的话将变得很麻烦并且难以跟踪, 并且, 跨越多个代码仓库的测试将迅速变得非常复杂;

为了解决这些(以及许多其它)问题, 某些项目会将 代码仓库分割成多个软件包(package), 并将每个软件包存放到独立的代码仓库中; 但是, 例如 Babel, React, Angular, Ember, Meteor, Jest 等项目以及许多其他项目则是在一个代码仓库中包含了多个软件包(package)并进行开发;

Lerna 是一种工具, 针对 使用 git 和 npm 管理多软件包代码仓库的工作流程进行优化